/**
* Copyright (c) Members of the EGEE Collaboration. 2006-2009.
* See http://www.eu-egee.org/partners/ for details on the copyright holders.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.glite.authz.pap.common.xacml.wizard;
import java.util.LinkedList;
import java.util.List;
import org.glite.authz.pap.common.utils.Utils;
import org.glite.authz.pap.common.xacml.impl.PolicyTypeString;
import org.glite.authz.pap.common.xacml.impl.TypeStringUtils;
import org.glite.authz.pap.common.xacml.utils.DescriptionTypeHelper;
import org.glite.authz.pap.common.xacml.utils.ObligationsHelper;
import org.glite.authz.pap.common.xacml.utils.PolicyHelper;
import org.glite.authz.pap.common.xacml.utils.XMLObjectHelper;
import org.glite.authz.pap.common.xacml.wizard.exceptions.PolicyWizardException;
import org.glite.authz.pap.common.xacml.wizard.exceptions.UnsupportedPolicyException;
import org.glite.authz.pap.common.xacml.wizard.exceptions.UnsupportedPolicySetWizardException;
import org.opensaml.xacml.policy.EffectType;
import org.opensaml.xacml.policy.ObligationType;
import org.opensaml.xacml.policy.ObligationsType;
import org.opensaml.xacml.policy.PolicyType;
import org.opensaml.xacml.policy.RuleType;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* This class allows to render an action (of the simplified policy language) into an OpenSAML object
* and vice versa. The XACML policy this class produces (or recognizes) has a <i>Target</i> with one
* <i>ActionMatch</i> and a set of rules (see {@link RuleWizard}.
* <p>
* The {@link PolicyType} object (and the DOM) is build only if it is used the constructor
* {@link PolicyWizard#PolicyWizard(PolicyType)} or the method {@link PolicyWizard#getXACML()}.<br>
* In order to cut on memory usage release the PolicyType object when it's not needed by using the
* helper method {@link TypeStringUtils#releaseUnneededMemory(Object)}.
* <p>
* The {@link PolicyWizard#getXACML()} method returns an instance of {@link PolicyTypeString}.
* Therefore, if you don't have to do specific operations on it (e.g. just giving it to Axis), call
* the {@link TypeStringUtils#releaseUnneededMemory(Object)} method on it to keep only the string
* representation and save a lot of memory.
*/
public class PolicyWizard extends XACMLWizard {
private static final Logger log = LoggerFactory.getLogger(PolicyWizard.class);
/** Prefix used in the policyId to set a policy as private */
protected static final String VISIBILITY_PRIVATE_PREFIX = "private";
/** Prefix used in the policyId to set a policy as public */
protected static final String VISIBILITY_PUBLIC_PREFIX = "public";
protected final String actionValue;
/** The action attribute on which this policy is based */
protected static final AttributeWizardType attributeWizardType = AttributeWizardTypeConfiguration.getInstance()
.getActionAttributeWizard();
protected final List<ObligationWizard> obligationWizardList = new LinkedList<ObligationWizard>();
protected final List<RuleWizard> ruleWizardList = new LinkedList<RuleWizard>();
protected final TargetWizard targetWizard;
protected String description = null;
protected boolean isPrivate = false;
protected PolicyTypeString policy = null;
protected String policyId = null;
protected String policyIdUniqueNumber;
protected String version = null;
/**
* Constructor that renders an action of the simplified policy language to XACML.
*
* @param attributeWizard the action attribute defining the <i>Target</i> of the policy.
*
* @throws UnsupportedPolicyException if the given attribute is not an action attribute as the
* one given by {@link AttributeWizardTypeConfiguration#getActionAttributeWizard()}.
*/
public PolicyWizard(AttributeWizard attributeWizard) {
if (attributeWizardType != attributeWizard.getAttributeWizardType()) {
throw new UnsupportedPolicyException("Attribute not supported: " + attributeWizard.getId());
}
actionValue = attributeWizard.getValue();
targetWizard = new TargetWizard(attributeWizard);
policyId = generatePolicyId();
version = "1";
}
/**
* Constructor the renders an XACML policy to an action of the simplified policy language.
*
* @param policy the XACML policy.
*
* @throws UnsupportedPolicyException if the given XACML policy cannot be rendered to an action
* of the simplified policy language.
*/
public PolicyWizard(PolicyType policy) {
if (!(policy instanceof PolicyTypeString)) {
throw new PolicyWizardException("Argument is not PolicyTypeString");
}
targetWizard = new TargetWizard(policy.getTarget());
List<AttributeWizard> targetAttributeWizardList = targetWizard.getAttributeWizardList();
validateTargetAttributewizardList(targetAttributeWizardList);
policyId = policy.getPolicyId();
decomposePolicyId(policy.getPolicyId());
actionValue = targetAttributeWizardList.get(0).getValue();
if (policy.getDescription() != null) {
description = policy.getDescription().getValue();
}
try {
version = policy.getVersion();
new Integer(version);
} catch (NumberFormatException e) {
throw new UnsupportedPolicyException("Wrong version format", e);
}
if (policy.getObligations() != null) {
List<ObligationType> obligationList = policy.getObligations().getObligations();
for (ObligationType obligation : obligationList) {
obligationWizardList.add(new ObligationWizard(obligation));
}
}
for (RuleType rule : policy.getRules()) {
ruleWizardList.add(new RuleWizard(rule));
}
this.policy = (PolicyTypeString) policy;
}
/**
* Return the action value of the given policy.
*
* @param policy
* @return the action value of the given policy.
*
* @throws UnsupportedPolicyException if the given policy is not an action policy.
*/
public static String getActionValue(PolicyType policy) {
TargetWizard targetWizard = new TargetWizard(policy.getTarget());
List<AttributeWizard> targetAttributeWizardList = targetWizard.getAttributeWizardList();
validateTargetAttributewizardList(targetAttributeWizardList);
return targetAttributeWizardList.get(0).getValue();
}
/**
* Increases by one the version of the given policy. If the existing version of thepolicy is
* missing or not recognized then it is set to 1.
*
* @param policy the policy to increase the verion number.
*/
public static void increaseVersion(PolicyType policy) {
int version;
try {
version = (new Integer(policy.getVersion())).intValue();
version++;
} catch (NumberFormatException e) {
log.error("Unrecognized version format, setting version to 1. PolicySetId=" + policy.getPolicyId());
version = 1;
}
policy.setVersion(Integer.toString(version));
}
/**
* Checks if the prefix of the given policy id is equal to
* {@link PolicyWizard#VISIBILITY_PUBLIC_PREFIX}.
*
* @param policyId the policy id to check.
* @return <code>true</code> if the prefix of the given policy id is equal to
* {@link PolicyWizard#VISIBILITY_PUBLIC_PREFIX}, <code>false</code> otherwise.
*/
public static boolean isPrivate(String policyId) {
return !isPublic(policyId);
}
/**
* Checks if the prefix of the given policy id is not equal to
* {@link PolicyWizard#VISIBILITY_PRIVATE_PREFIX} or missing.
*
* @param policyId the policy id to check.
* @return <code>true</code> if the prefix of the given policy id is not equal to
* {@link PolicyWizard#VISIBILITY_PRIVATE_PREFIX} or missing, <code>false</code>
* otherwise.
*/
public static boolean isPublic(String policyId) {
String[] idTokens = policyId.split("_");
if (idTokens.length == 0) {
throw new UnsupportedPolicyException("Unrecognized policyId: " + policyId);
}
if (idTokens.length == 1) {
return true;
}
if (VISIBILITY_PRIVATE_PREFIX.equals(idTokens[0])) {
return false;
}
return true;
}
/**
* The policy id is composed by two tokens: a prefix and an UUID. This method returns the UUID
* part.
*
* @param policyId the policy id.
* @return the UUID part of the given policy id.
*
* @throws UnsupportedPolicyException if the given policy id is not recognized.
*/
private static String getIdUniqueNumber(String policyId) {
String[] idTokens = policyId.split("_");
if (idTokens.length == 0) {
throw new UnsupportedPolicyException("Unrecognized policyId: " + policyId);
}
if (idTokens.length == 1) {
return idTokens[0];
}
if (idTokens.length == 2) {
if ((VISIBILITY_PRIVATE_PREFIX.equals(idTokens[0])) || (VISIBILITY_PUBLIC_PREFIX.equals(idTokens[0]))) {
return idTokens[1];
}
}
throw new UnsupportedPolicyException("Unrecognized policyId: " + policyId);
}
/**
* Append an obligation to the list of obligations.
*
* @param obligationWizard the obligation to add.
*/
public void addObligation(ObligationWizard obligationWizard) {
obligationWizardList.add(obligationWizard);
invalidatePolicyType();
}
/**
* Add a rule which <i>Target</i> is defined by the given attribute and with the given effect.
*
* @param attribute the attribute used to build the Target of the rule.
* @param effect the effect of the rule.
*/
public void addRule(AttributeWizard attribute, EffectType effect) {
addRule(new RuleWizard(attribute, effect));
}
public void addRule(int index, AttributeWizard attribute, EffectType effect) {
addRule(index, new RuleWizard(attribute, effect));
}
public void addRule(int index, List<AttributeWizard> targetAttributeList, EffectType effect) {
addRule(new RuleWizard(targetAttributeList, effect));
}
public void addRule(int index, RuleWizard ruleWizard) {
ruleWizardList.add(index, ruleWizard);
invalidatePolicyType();
}
public void addRule(List<AttributeWizard> targetAttributeList, EffectType effect) {
addRule(new RuleWizard(targetAttributeList, effect));
}
public void addRule(RuleWizard ruleWizard) {
ruleWizardList.add(ruleWizard);
invalidatePolicyType();
}
public boolean denyRuleForAttributeExists(AttributeWizard attributeWizard) {
for (RuleWizard ruleWizard : ruleWizardList) {
if (ruleWizard.deniesAttribute(attributeWizard)) {
return true;
}
}
return false;
}
public String getDescription() {
return description;
}
public int getNumberOfRules() {
return ruleWizardList.size();
}
public String getPolicyId() {
return policyId;
}
public String getPolicyIdPrefix() {
if (isPrivate) {
return VISIBILITY_PRIVATE_PREFIX;
} else {
return VISIBILITY_PUBLIC_PREFIX;
}
}
public String getTagAndValue() {
return String.format("%s \"%s\"", attributeWizardType.getId(), actionValue);
}
public int getVersion() {
return Integer.valueOf(version);
}
public String getVersionString() {
return version;
}
public PolicyType getXACML() {
initPolicyTypeIfNotSet();
return policy;
}
public void increaseVersion() {
setVersion(getVersion() + 1);
}
public boolean isDOMReleased() {
return (policy == null);
}
public boolean isEquivalent(PolicyType policy) {
if (!(targetWizard.isEquivalent(policy.getTarget()))) {
return false;
}
List<RuleType> ruleList = policy.getRules();
if (ruleList.size() != ruleWizardList.size()) {
return false;
}
for (int i = 0; i < ruleWizardList.size(); i++) {
if (!(ruleWizardList.get(i).isEquivalent(ruleList.get(i)))) {
return false;
}
}
if (description != null) {
if (!(description.equals(policy.getDescription()))) {
return false;
}
} else {
if (policy.getDescription() != null) {
return false;
}
}
return true;
}
public boolean isPrivate() {
return isPrivate;
}
public boolean isPublic() {
return !isPrivate;
}
public void releaseChildrenDOM() {
targetWizard.releaseChildrenDOM();
targetWizard.releaseDOM();
for (ObligationWizard obligationWizard : obligationWizardList) {
obligationWizard.releaseChildrenDOM();
obligationWizard.releaseDOM();
}
for (RuleWizard ruleWizard : ruleWizardList) {
ruleWizard.releaseChildrenDOM();
ruleWizard.releaseDOM();
}
}
public void releaseDOM() {
if (policy != null) {
policy.releaseDOM();
policy = null;
}
}
public boolean removeDenyRuleForAttribute(AttributeWizard attributeWizard) {
for (int i = 0; i < ruleWizardList.size(); i++) {
RuleWizard ruleWizard = ruleWizardList.get(i);
if (ruleWizard.deniesAttribute(attributeWizard)) {
if (policy != null) {
PolicyHelper.removeRule(policy, ruleWizard.getRuleId());
}
ruleWizardList.remove(i);
return true;
}
}
return false;
}
public void setDescription(String value) {
this.description = value;
if (policy != null) {
policy.setDescription(DescriptionTypeHelper.build(value));
}
}
public void setPolicyId(String policyId) {
this.policyId = policyId;
if (policy != null) {
policy.setPolicyId(policyId);
}
}
public void setPrivate(boolean isPrivate) {
this.isPrivate = isPrivate;
policyId = composeId();
if (policy != null) {
policy.setPolicyId(policyId);
}
}
public void setVersion(int version) {
this.version = Integer.toString(version);
if (policy != null) {
policy.setVersion(this.version);
}
}
public String toFormattedString() {
return toFormattedString(false);
}
public String toFormattedString(boolean printPolicyId) {
return toFormattedString(0, 4, printPolicyId, false);
}
public String toFormattedString(int baseIndentation, int internalIndentation) {
return toFormattedString(baseIndentation, internalIndentation, false, false);
}
public String toFormattedString(int baseIndentation, int internalIndentation, boolean printPolicyId, boolean printRuleIds) {
String baseIndentString = Utils.fillWithSpaces(baseIndentation);
String indentString = Utils.fillWithSpaces(baseIndentation + internalIndentation);
StringBuffer sb = new StringBuffer();
if (printPolicyId) {
sb.append(String.format("%sid=%s\n", baseIndentString, policyId));
}
if (isPrivate()) {
sb.append(String.format("%sprivate\n", baseIndentString));
}
sb.append(String.format("%saction \"%s\" {\n", baseIndentString, actionValue));
if (description != null) {
sb.append(String.format("%sdescription=\"%s\"\n", indentString, description));
}
for (ObligationWizard obligationWizard : obligationWizardList) {
sb.append(obligationWizard.toFormattedString(baseIndentation + internalIndentation, internalIndentation));
sb.append('\n');
}
for (RuleWizard ruleWizard : ruleWizardList) {
if (printRuleIds) {
sb.append('\n');
}
sb.append(ruleWizard.toFormattedString(baseIndentation + internalIndentation, internalIndentation, printRuleIds));
sb.append('\n');
}
sb.append(baseIndentString + "}");
return sb.toString();
}
public String toXACMLString() {
initPolicyTypeIfNotSet();
return XMLObjectHelper.toString(policy);
}
/**
* Return the effective policy id by composing the prefix and the UUID.
*
* @return the effective policy id.
*/
private String composeId() {
return getPolicyIdPrefix() + "_" + policyIdUniqueNumber;
}
/**
* Set the members {@link PolicyWizard#isPrivate}, {@link PolicyWizard#policyIdUniqueNumber} and
* {@link PolicyWizard#policyIdVisibilityPrefix} from the given policy id.
*
* @param policyId the policy id to decompose.
*
* @throws UnsupportedPolicyException if the given policy id format is not recognized.
*/
private void decomposePolicyId(String policyId) {
isPrivate = isPrivate(policyId);
policyIdUniqueNumber = getIdUniqueNumber(policyId);
}
/**
* Return a new generated policy id.
*
* @return a new generated policy id.
*/
private String generatePolicyId() {
policyIdUniqueNumber = WizardUtils.generateId(null);
return composeId();
}
/**
* Build the PolicyType object if it doesn't already exits.
*/
private void initPolicyTypeIfNotSet() {
if (policy == null) {
log.debug("Initializing policyType");
setPolicyType();
} else {
log.debug("policyType already initialized");
}
}
/**
* Release the memory used for the PolicyType object.
*/
private void invalidatePolicyType() {
releaseChildrenDOM();
releaseDOM();
}
/**
* Build (or re-build if already exists) the PolicyType object.
*/
private void setPolicyType() {
releaseDOM();
policy = new PolicyTypeString(PolicyHelper.build(policyId, PolicyHelper.RULE_COMBALG_FIRST_APPLICABLE));
if (description != null) {
policy.setDescription(DescriptionTypeHelper.build(description));
}
policy.setTarget(targetWizard.getXACML());
policy.setVersion(version);
if (obligationWizardList.size() > 0) {
ObligationsType obligations = ObligationsHelper.build();
List<ObligationType> obligationList = obligations.getObligations();
for (ObligationWizard obligationWizard : obligationWizardList) {
obligationList.add(obligationWizard.getXACML());
}
policy.setObligations(obligations);
}
for (RuleWizard ruleWizard : ruleWizardList) {
policy.getRules().add(ruleWizard.getXACML());
}
}
/**
* Checks if the given target attribute wizard list can be used to build an action of the
* simplified policy language.
*
* @param targetAttributeWizardList the list of wizard attributes to validate.
*
* @throws UnsupportedPolicyException if the validation fails.
*/
private static void validateTargetAttributewizardList(List<AttributeWizard> targetAttributeWizardList) {
if (targetAttributeWizardList == null) {
throw new UnsupportedPolicySetWizardException("targetAttributeWizardList is null");
}
if (targetAttributeWizardList.size() != 1) {
throw new UnsupportedPolicySetWizardException("Only one action attribute is supported (found "
+ targetAttributeWizardList.size() + " attributes)");
}
AttributeWizard aw = targetAttributeWizardList.get(0);
if (aw.getAttributeWizardType() != attributeWizardType) {
throw new UnsupportedPolicySetWizardException("Only one action attribute is supported");
}
}
}