/* ================================================================== * BasicSecurityPolicy.java - 9/10/2016 8:01:18 AM * * Copyright 2007-2016 SolarNetwork.net Dev Team * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA * 02111-1307 USA * ================================================================== */ package net.solarnetwork.central.security; import java.io.Serializable; import java.util.Collections; import java.util.EnumSet; import java.util.HashMap; import java.util.HashSet; import java.util.LinkedHashSet; import java.util.Map; import java.util.Set; import com.fasterxml.jackson.databind.annotation.JsonDeserialize; import com.fasterxml.jackson.databind.annotation.JsonSerialize; import net.solarnetwork.central.domain.Aggregation; import net.solarnetwork.central.domain.LocationPrecision; /** * Basic implementation of {@link SecurityPolicy}. * * @author matt * @version 1.1 */ @JsonDeserialize(builder = net.solarnetwork.central.security.BasicSecurityPolicy.Builder.class) @JsonSerialize(using = SecurityPolicySerializer.class) public class BasicSecurityPolicy implements SecurityPolicy, Serializable { private static final long serialVersionUID = -7842690205136252446L; /** * A builder for {@link BasicSecurityPolicy} instances. * * Configure properties on instances of this class, then call * {@link #build()} to get a {@link BasicSecurityPolicy} instance. */ public static class Builder { private static final Map<Aggregation, Set<Aggregation>> MAX_AGGREGATION_CACHE = new HashMap<Aggregation, Set<Aggregation>>( 16); private static final Map<LocationPrecision, Set<LocationPrecision>> MAX_LOCATION_PRECISION_CACHE = new HashMap<LocationPrecision, Set<LocationPrecision>>( 16); private Set<Long> nodeIds; private Set<String> sourceIds; private Set<Aggregation> aggregations; private Set<LocationPrecision> locationPrecisions; private Aggregation minAggregation; private LocationPrecision minLocationPrecision; private Set<String> nodeMetadataPaths; private Set<String> userMetadataPaths; public Builder withPolicy(SecurityPolicy policy) { if ( policy != null ) { return this.withAggregations(policy.getAggregations()) .withMinAggregation(policy.getMinAggregation()) .withLocationPrecisions(policy.getLocationPrecisions()) .withMinLocationPrecision(policy.getMinLocationPrecision()) .withNodeIds(policy.getNodeIds()).withSourceIds(policy.getSourceIds()) .withNodeMetadataPaths(policy.getNodeMetadataPaths()) .withUserMetadataPaths(policy.getUserMetadataPaths()); } return this; } public Builder withMergedPolicy(SecurityPolicy policy) { if ( policy != null ) { Builder b = this.withMergedAggregations(policy.getAggregations()) .withMergedLocationPrecisions(policy.getLocationPrecisions()) .withMergedNodeIds(policy.getNodeIds()) .withMergedSourceIds(policy.getSourceIds()) .withMergedNodeMetadataPaths(policy.getNodeMetadataPaths()) .withMergedUserMetadataPaths(policy.getUserMetadataPaths()); if ( policy.getMinAggregation() != null ) { b = b.withMinAggregation(policy.getMinAggregation()); } if ( policy.getMinLocationPrecision() != null ) { b = b.withMinLocationPrecision(policy.getMinLocationPrecision()); } return b; } return this; } public Builder withNodeIds(Set<Long> nodeIds) { this.nodeIds = (nodeIds == null || nodeIds.isEmpty() ? null : Collections.unmodifiableSet(nodeIds)); return this; } public Builder withNodeMetadataPaths(Set<String> nodeMetadataPaths) { this.nodeMetadataPaths = (nodeMetadataPaths == null || nodeMetadataPaths.isEmpty() ? null : Collections.unmodifiableSet(nodeMetadataPaths)); return this; } public Builder withUserMetadataPaths(Set<String> userMetadataPaths) { this.userMetadataPaths = (userMetadataPaths == null || userMetadataPaths.isEmpty() ? null : Collections.unmodifiableSet(userMetadataPaths)); return this; } public Builder withSourceIds(Set<String> sourceIds) { this.sourceIds = (sourceIds == null || sourceIds.isEmpty() ? null : Collections.unmodifiableSet(sourceIds)); return this; } public Builder withAggregations(Set<Aggregation> aggregations) { this.aggregations = aggregations; return this; } public Builder withLocationPrecisions(Set<LocationPrecision> locationPrecisions) { this.locationPrecisions = locationPrecisions; return this; } public Builder withMergedNodeIds(Set<Long> nodeIds) { Set<Long> set = nodeIds; if ( this.nodeIds != null && !this.nodeIds.isEmpty() ) { set = new LinkedHashSet<Long>(this.nodeIds); if ( nodeIds != null ) { set.addAll(nodeIds); } } return withNodeIds(set); } public Builder withMergedNodeMetadataPaths(Set<String> nodeMetadataPaths) { Set<String> set = nodeMetadataPaths; if ( this.nodeMetadataPaths != null && !this.nodeMetadataPaths.isEmpty() ) { set = new LinkedHashSet<String>(this.nodeMetadataPaths); if ( nodeMetadataPaths != null ) { set.addAll(nodeMetadataPaths); } } return withNodeMetadataPaths(set); } public Builder withMergedUserMetadataPaths(Set<String> userMetadataPaths) { Set<String> set = userMetadataPaths; if ( this.userMetadataPaths != null && !this.userMetadataPaths.isEmpty() ) { set = new LinkedHashSet<String>(this.userMetadataPaths); if ( userMetadataPaths != null ) { set.addAll(userMetadataPaths); } } return withUserMetadataPaths(set); } public Builder withMergedSourceIds(Set<String> sourceIds) { Set<String> set = sourceIds; if ( this.sourceIds != null && !this.sourceIds.isEmpty() ) { set = new LinkedHashSet<String>(this.sourceIds); if ( sourceIds != null ) { set.addAll(sourceIds); } } return withSourceIds(set); } public Builder withMergedAggregations(Set<Aggregation> aggregations) { Set<Aggregation> set = aggregations; if ( this.aggregations != null && !this.aggregations.isEmpty() ) { if ( aggregations != null ) { set = new LinkedHashSet<Aggregation>(this.aggregations); set.addAll(aggregations); } else { set = this.aggregations; } } return withAggregations(set); } public Builder withMergedLocationPrecisions(Set<LocationPrecision> locationPrecisions) { Set<LocationPrecision> set = locationPrecisions; if ( this.locationPrecisions != null && !this.locationPrecisions.isEmpty() ) { if ( locationPrecisions != null ) { set = new LinkedHashSet<LocationPrecision>(this.locationPrecisions); set.addAll(locationPrecisions); } else { set = this.locationPrecisions; } } return withLocationPrecisions(set); } public Builder withMinAggregation(Aggregation minAggregation) { this.minAggregation = minAggregation; return this; } private Set<Aggregation> buildAggregations() { if ( minAggregation == null && aggregations != null && !aggregations.isEmpty() ) { return Collections.unmodifiableSet(aggregations); } else if ( minAggregation == null ) { return null; } Set<Aggregation> result = MAX_AGGREGATION_CACHE.get(minAggregation); if ( result != null ) { return result; } result = new HashSet<Aggregation>(16); for ( Aggregation agg : Aggregation.values() ) { if ( agg.compareLevel(minAggregation) > -1 ) { result.add(agg); } } result = Collections.unmodifiableSet(EnumSet.copyOf(result)); MAX_AGGREGATION_CACHE.put(minAggregation, result); return result; } /** * Treat the configured {@code locationPrecisions} set as a single * minimum value or a list of exact values. * * By default if {@code locationPrecisions} is configured with a single * value it will be treated as a <em>minimum</em> value, and any * {@link LocationPrecision} with a * {@link LocationPrecision#getPrecision()} equal to or higher than that * value's level will be included in the generated * {@link BasicSecurityPolicy#getLocationPrecisions()} set. Set this to * {@code false} to disable that behavior and treat * {@code locationPrecisions} as the exact values to include in the * generated {@link BasicSecurityPolicy#getLocationPrecisions()} set. * * @param minLocationPrecision * {@code false} to treat configured location precision values * as-is, {@code true} to treat as a minimum threshold * @return The builder. */ public Builder withMinLocationPrecision(LocationPrecision minLocationPrecision) { this.minLocationPrecision = minLocationPrecision; return this; } private Set<LocationPrecision> buildLocationPrecisions() { if ( minLocationPrecision == null && locationPrecisions != null && !locationPrecisions.isEmpty() ) { return Collections.unmodifiableSet(locationPrecisions); } else if ( minLocationPrecision == null ) { return null; } Set<LocationPrecision> result = MAX_LOCATION_PRECISION_CACHE.get(minLocationPrecision); if ( result != null ) { return result; } result = new HashSet<LocationPrecision>(16); for ( LocationPrecision agg : LocationPrecision.values() ) { if ( agg.comparePrecision(minLocationPrecision) > -1 ) { result.add(agg); } } result = Collections.unmodifiableSet(EnumSet.copyOf(result)); MAX_LOCATION_PRECISION_CACHE.put(minLocationPrecision, result); return result; } public BasicSecurityPolicy build() { return new BasicSecurityPolicy(nodeIds, sourceIds, buildAggregations(), minAggregation, buildLocationPrecisions(), minLocationPrecision, nodeMetadataPaths, userMetadataPaths); } } private final Set<Long> nodeIds; private final Set<String> sourceIds; private final Set<Aggregation> aggregations; private final Set<LocationPrecision> locationPrecisions; private final Aggregation minAggregation; private final LocationPrecision minLocationPrecision; private final Set<String> nodeMetadataPaths; private final Set<String> userMetadataPaths; /** * Constructor. * * @param nodeIds * The node IDs to restrict to, or {@code null} for no restriction. * @param sourceIds * The source ID to restrict to, or {@code null} for no restriction. * @param aggregations * The aggregations to restrict to, or {@code null} for no * restriction. * @param minAggregation * If specified, a minimum aggregation level that is allowed. * @param locationPrecisions * The location precisions to restrict to, or {@code null} for no * restriction. * @param minALocationPrecision * If specified, a minimum location precision that is allowed. * @param nodeMetadataPaths * The {@code SolarNodeMetadata} paths to restrict to, or * {@code null} for no restriction. * @param userMetadataPaths * The {@code UserNodeMetadata} paths to restrict to, or {@code null} * for no restriction. */ public BasicSecurityPolicy(Set<Long> nodeIds, Set<String> sourceIds, Set<Aggregation> aggregations, Aggregation minAggregation, Set<LocationPrecision> locationPrecisions, LocationPrecision minLocationPrecision, Set<String> nodeMetadataPaths, Set<String> userMetadataPaths) { super(); this.nodeIds = nodeIds; this.sourceIds = sourceIds; this.aggregations = aggregations; this.minAggregation = minAggregation; this.locationPrecisions = locationPrecisions; this.minLocationPrecision = minLocationPrecision; this.nodeMetadataPaths = nodeMetadataPaths; this.userMetadataPaths = userMetadataPaths; } @Override public Set<Long> getNodeIds() { return nodeIds; } @Override public Set<String> getSourceIds() { return sourceIds; } @Override public Set<Aggregation> getAggregations() { return aggregations; } @Override public Set<LocationPrecision> getLocationPrecisions() { return locationPrecisions; } @Override public Aggregation getMinAggregation() { return minAggregation; } @Override public LocationPrecision getMinLocationPrecision() { return minLocationPrecision; } @Override public Set<String> getNodeMetadataPaths() { return nodeMetadataPaths; } @Override public Set<String> getUserMetadataPaths() { return userMetadataPaths; } @Override public int hashCode() { final int prime = 31; int result = 1; result = prime * result + ((aggregations == null) ? 0 : aggregations.hashCode()); result = prime * result + ((locationPrecisions == null) ? 0 : locationPrecisions.hashCode()); result = prime * result + ((minAggregation == null) ? 0 : minAggregation.hashCode()); result = prime * result + ((minLocationPrecision == null) ? 0 : minLocationPrecision.hashCode()); result = prime * result + ((nodeIds == null) ? 0 : nodeIds.hashCode()); result = prime * result + ((sourceIds == null) ? 0 : sourceIds.hashCode()); result = prime * result + ((nodeMetadataPaths == null) ? 0 : nodeMetadataPaths.hashCode()); result = prime * result + ((userMetadataPaths == null) ? 0 : userMetadataPaths.hashCode()); return result; } @Override public boolean equals(Object obj) { if ( this == obj ) { return true; } if ( obj == null ) { return false; } if ( !(obj instanceof BasicSecurityPolicy) ) { return false; } BasicSecurityPolicy other = (BasicSecurityPolicy) obj; if ( aggregations == null ) { if ( other.aggregations != null ) { return false; } } else if ( !aggregations.equals(other.aggregations) ) { return false; } if ( locationPrecisions == null ) { if ( other.locationPrecisions != null ) { return false; } } else if ( !locationPrecisions.equals(other.locationPrecisions) ) { return false; } if ( minAggregation != other.minAggregation ) { return false; } if ( minLocationPrecision != other.minLocationPrecision ) { return false; } if ( nodeIds == null ) { if ( other.nodeIds != null ) { return false; } } else if ( !nodeIds.equals(other.nodeIds) ) { return false; } if ( sourceIds == null ) { if ( other.sourceIds != null ) { return false; } } else if ( !sourceIds.equals(other.sourceIds) ) { return false; } if ( nodeMetadataPaths == null ) { if ( other.nodeMetadataPaths != null ) { return false; } } else if ( !nodeMetadataPaths.equals(other.nodeMetadataPaths) ) { return false; } if ( userMetadataPaths == null ) { if ( other.userMetadataPaths != null ) { return false; } } else if ( !userMetadataPaths.equals(other.userMetadataPaths) ) { return false; } return true; } }