/**
* Copyright (c) Codice Foundation
* <p>
* This is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser
* General Public License as published by the Free Software Foundation, either version 3 of the
* License, or any later version.
* <p>
* 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
* Lesser General Public License for more details. A copy of the GNU Lesser General Public License
* is distributed along with this program and can be found at
* <http://www.gnu.org/licenses/lgpl.html>.
*/
package ddf.security.permission;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import org.apache.commons.lang.StringUtils;
import org.apache.shiro.authz.Permission;
import org.apache.shiro.authz.permission.WildcardPermission;
/**
* Permission class comprised of a key and a list of corresponding values. Contains the logic to
* determine if this key/value permission can imply another key/value permission.
*/
public class KeyValuePermission implements Permission {
private String key;
private Set<String> values;
/**
* Creates a new KeyValuePermission with the specified key and an empty list of values.
*
* @param key the key to be used for this permission
*/
public KeyValuePermission(String key) {
this(key, new HashSet<>());
}
/**
* Creates a new KeyValuePermission with the specified key and corresponding list of values.
*
* @param key the key to be used for this permission
* @param values the list of values to be used for this permission
* @throws IllegalArgumentException if the key is null - a valid key is required
* @deprecated
*/
public KeyValuePermission(String key, List<String> values) {
if (key == null) {
throw new IllegalArgumentException(
"Incoming key cannot be null, could not create permission.");
}
this.key = key;
if (values == null) {
this.values = new HashSet<>();
} else {
this.values = new HashSet<>(values);
}
}
/**
* Creates a new KeyValuePermission with the specified key and corresponding list of values.
*
* @param key the key to be used for this permission
* @param values the list of values to be used for this permission
* @throws IllegalArgumentException if the key is null - a valid key is required
*/
public KeyValuePermission(String key, Set<String> values) {
if (key == null) {
throw new IllegalArgumentException(
"Incoming key cannot be null, could not create permission.");
}
this.key = key;
if (values == null) {
this.values = new HashSet<>();
} else {
this.values = values;
}
}
public String getKey() {
return key;
}
public Set<String> getValues() {
return Collections.unmodifiableSet(values);
}
/**
* Adds an additional value to the existing values.
*
* @param value new value to be added to the existing values for this key/value pair
*/
public void addValue(String value) {
values.add(value);
}
/**
* Returns {@code true} if this current instance <em>implies</em> all the functionality and/or
* resource access described by the specified {@code Permission} argurment, {@code false}
* otherwise.
* <p>
* That is, this current instance must be exactly equal to or a <em>superset</em> of the
* functionality and/or resource access described by the given {@code Permission} argument. Yet
* another way of saying this would be:
* <p>
* If "permission1 implies permission2", i.e.
* <code>permission1.implies(permission2)</code> , then any Subject granted {@code permission1}
* would have ability greater than or equal to that defined by {@code permission2}.
* <p>
* For KeyValuePermission objects this is determined as follows:
* <p>
* If the keys of each permission are equal and if the values from this object implies the
* values from the passed in permission, then this permission will imply the passed in
* permission.
*
* @param p permission to checked to see if this permission implies p
* @return {@code true} if this current instance <em>implies</em> all the functionality and/or
* resource access described by the specified {@code Permission} argument, {@code false}
* otherwise.
*/
@Override
public boolean implies(Permission p) {
if (p instanceof KeyValuePermission) {
if (getKey().equals(((KeyValuePermission) p).getKey())) {
WildcardPermission thisWildCard = buildWildcardFromKeyValue(this);
WildcardPermission implied = buildWildcardFromKeyValue((KeyValuePermission) p);
return thisWildCard.implies(implied);
}
} else if (p instanceof KeyValueCollectionPermission) {
WildcardPermission thisWildCard = buildWildcardFromKeyValue(this);
List<KeyValuePermission> permissionList = ((KeyValueCollectionPermission) p)
.getKeyValuePermissionList();
for (KeyValuePermission keyValuePermission : permissionList) {
if (getKey().equals(keyValuePermission.getKey())) {
WildcardPermission implied = buildWildcardFromKeyValue(keyValuePermission);
return thisWildCard.implies(implied);
}
}
} else if (p instanceof MatchOneCollectionPermission) {
MatchOneCollectionPermission matchOneCollectionPermission = (MatchOneCollectionPermission) p;
return matchOneCollectionPermission.implies(this);
} else if (p instanceof WildcardPermission) {
WildcardPermission thisWildCard = buildWildcardFromKeyValue(this);
return thisWildCard.implies(p);
}
return false;
}
/**
* Returns a {@link org.apache.shiro.authz.permission.WildcardPermission} representing a
* {@link KeyValuePermission}
*
* @param perm the permission to convert.
* @return new equivalent permission
*/
private WildcardPermission buildWildcardFromKeyValue(KeyValuePermission perm) {
StringBuilder wildcardString = new StringBuilder();
wildcardString.append(perm.getKey());
wildcardString.append(":");
for (String value : perm.getValues()) {
wildcardString.append(value);
wildcardString.append(",");
}
return new WildcardPermission(
wildcardString.toString().substring(0, wildcardString.length() - 1));
}
/**
* Creates a string representation of this key/value permission object.
*
* @return a string representation of this key/value permission object
*/
@Override
public String toString() {
return key + " : " + StringUtils.join(values, ",");
}
}