Java tutorial
/* * ==================================================================== * This file is part of the ebXML Registry by Icar Cnr v3.2 * ("eRICv32" in the following disclaimer). * * "eRICv32" 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 3 of the License, or * (at your option) any later version. * * "eRICv32" 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 Version 3 * along with "eRICv32". If not, see <http://www.gnu.org/licenses/>. * * eRICv32 is a forked, derivative work, based on: * - freebXML Registry, a royalty-free, open source implementation of the ebXML Registry standard, * which was published under the "freebxml License, Version 1.1"; * - ebXML OMAR v3.2 Edition, published under the GNU GPL v3 by S. Krushe & P. Arwanitis. * * All derivative software changes and additions are made under * * Copyright (C) 2013 Ing. Antonio Messina <messina@pa.icar.cnr.it> * * This software consists of voluntary contributions made by many * individuals on behalf of the freebxml Software Foundation. For more * information on the freebxml Software Foundation, please see * "http://www.freebxml.org/". * * This product includes software developed by the Apache Software * Foundation (http://www.apache.org/). * * ==================================================================== */ package it.cnr.icar.eric.server.security.authorization; import it.cnr.icar.eric.client.xml.registry.infomodel.PersonNameImpl; import it.cnr.icar.eric.common.BindingUtility; import it.cnr.icar.eric.common.ReferenceInfo; import it.cnr.icar.eric.common.exceptions.AuthorizationException; import it.cnr.icar.eric.common.exceptions.ObjectNotFoundException; import it.cnr.icar.eric.common.exceptions.UnauthorizedRequestException; import it.cnr.icar.eric.server.common.RegistryProperties; import it.cnr.icar.eric.server.common.ServerRequestContext; import it.cnr.icar.eric.server.persistence.PersistenceManager; import it.cnr.icar.eric.server.persistence.PersistenceManagerFactory; import it.cnr.icar.eric.server.security.authentication.AuthenticationServiceImpl; import it.cnr.icar.eric.server.util.ServerResourceBundle; import java.net.URI; import java.net.URISyntaxException; import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Set; import java.util.StringTokenizer; import javax.xml.registry.JAXRException; import javax.xml.registry.RegistryException; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.oasis.ebxml.registry.bindings.lcm.SubmitObjectsRequest; import org.oasis.ebxml.registry.bindings.lcm.UpdateObjectsRequest; import org.oasis.ebxml.registry.bindings.query.AdhocQueryRequest; import org.oasis.ebxml.registry.bindings.query.ResponseOptionType; import org.oasis.ebxml.registry.bindings.query.ResponseOptionType.ReturnType; import org.oasis.ebxml.registry.bindings.rim.AssociationType1; import org.oasis.ebxml.registry.bindings.rim.AuditableEventType; import org.oasis.ebxml.registry.bindings.rim.FederationType; import org.oasis.ebxml.registry.bindings.rim.IdentifiableType; import org.oasis.ebxml.registry.bindings.rim.RegistryObjectType; import org.oasis.ebxml.registry.bindings.rim.RegistryType; import org.oasis.ebxml.registry.bindings.rim.UserType; import org.oasis.ebxml.registry.bindings.rs.RegistryRequestType; import com.sun.xacml.EvaluationCtx; import com.sun.xacml.PDP; import com.sun.xacml.PDPConfig; import com.sun.xacml.attr.AnyURIAttribute; import com.sun.xacml.attr.AttributeDesignator; import com.sun.xacml.attr.AttributeValue; import com.sun.xacml.attr.BagAttribute; import com.sun.xacml.attr.StringAttribute; import com.sun.xacml.cond.EvaluationResult; import com.sun.xacml.cond.Function; import com.sun.xacml.cond.FunctionFactory; import com.sun.xacml.ctx.Attribute; import com.sun.xacml.ctx.RequestCtx; import com.sun.xacml.ctx.ResponseCtx; import com.sun.xacml.ctx.Result; import com.sun.xacml.ctx.Status; import com.sun.xacml.ctx.Subject; import com.sun.xacml.finder.AttributeFinder; import com.sun.xacml.finder.AttributeFinderModule; import com.sun.xacml.finder.PolicyFinder; import com.sun.xacml.finder.PolicyFinderModule; import com.sun.xacml.finder.impl.CurrentEnvModule; /** * AuthorizationService implementation for the ebxml Registry. * * @author <a href="mailto:Farrukh.Najmi@Sun.COM">Farrukh S. Najmi</a> */ public class AuthorizationServiceImpl { /** * @link * @shapeType PatternLink * @pattern Singleton * @supplierRole Singleton factory */ /* # private AuthorizationServiceImpl _ authorizationServiceImpl; */ private static AuthorizationServiceImpl instance = null; /** * The prefix for all access control attribute ids as defined by ebRIM. */ public static final String REGISTRY_ACP_RPREFIX = "urn:oasis:names:tc:ebxml-regrep:3.0:rim:acp:"; /** * The prefix for all resource attribute designators as defined by ebRIM. */ public static final String RESOURCE_ATTRIBUTE_PREFIX = REGISTRY_ACP_RPREFIX + "resource:"; /** * The prefix for all subject attribute designators as defined by ebRIM. */ public static final String SUBJECT_ATTRIBUTE_PREFIX = REGISTRY_ACP_RPREFIX + "subject:"; /** * The prefix for all action attribute designators as defined by ebRIM. * TODO: Add it to ebRIM. */ public static final String ACTION_ATTRIBUTE_PREFIX = REGISTRY_ACP_RPREFIX + "action:"; /** * The prefix for all environment attribute designators (impl specific). */ public static final String ENVIRONMENT_ATTRIBUTE_PREFIX = REGISTRY_ACP_RPREFIX + "environment:"; /** The owner resource attribute from V3 spec. */ public static final String RESOURCE_ATTRIBUTE_OWNER = RESOURCE_ATTRIBUTE_PREFIX + "owner"; /** The owner resource attribute from V3 spec. */ public static final String RESOURCE_ATTRIBUTE_SELECTOR = RESOURCE_ATTRIBUTE_PREFIX + "selector"; /** The owner resource attribute (impl specific). */ public static final String RESOURCE_ATTRIBUTE_RESOURCE = RESOURCE_ATTRIBUTE_PREFIX + "resource"; /** * The request-context environment attribute specific to ebxmlrr (not from * V3 spec). */ public static final String ENVIRONMENT_ATTRIBUTE_REQUEST_CONTEXT = ENVIRONMENT_ATTRIBUTE_PREFIX + "request-context"; /** * The subject-id subject attribute from XACML 1.0 spec. Should be in XACML * impl?? */ public static final String SUBJECT_ATTRIBUTE_ID = "urn:oasis:names:tc:xacml:1.0:subject:subject-id"; /** The role subject attribute from V3 spec. */ public static final String SUBJECT_ATTRIBUTE_ROLES = SUBJECT_ATTRIBUTE_PREFIX + "roles"; /** The role subject attribute from V3 spec. */ public static final String SUBJECT_ATTRIBUTE_GROUPS = SUBJECT_ATTRIBUTE_PREFIX + "groups"; /** The user subject attribute specific to ebxmlrr (not from V3 spec). */ public static final String SUBJECT_ATTRIBUTE_USER = SUBJECT_ATTRIBUTE_PREFIX + "user"; /** The action-id action attribute from V3 spec. */ public static final String ACTION_ATTRIBUTE_ID = "urn:oasis:names:tc:xacml:1.0:action:action-id"; /** The reference-source action attribute. Needs to be added to V3 specs. */ public static final String ACTION_ATTRIBUTE_REFERENCE_SOURCE = ACTION_ATTRIBUTE_PREFIX + "reference-source"; /** * The reference-source-attribute action attribute. Needs to be added to V3 * specs. */ public static final String ACTION_ATTRIBUTE_REFERENCE_SOURCE_ATTRIBUTE = ACTION_ATTRIBUTE_PREFIX + "reference-source-attribute"; /** * The reference-source-attribute-filter action attribute prefix. Suffix is * an attribute name in RIM class for reference-source. Needs to be added to * V3 specs. */ public static final String ACTION_ATTRIBUTE_REFERENCE_SOURCE_ATTRIBUTE_FILTER_PREFIX = ACTION_ATTRIBUTE_PREFIX + "reference-source-attribute-filter:"; public static final String PROP_REGISTRY_REQUEST = "it.cnr.icar.eric.server.security.authorization.RegistryRequest"; /** The standard namespace where all the ebRIM spec-defined functions live */ public static final String FUNCTION_NS = "urn:oasis:names:tc:ebxml-regrep:3.0:rim:acp:function:"; private static BindingUtility bu = BindingUtility.getInstance(); private PDP pdp = null; private static Log log = LogFactory.getLog(AuthorizationServiceImpl.class); String idForDefaultACP = RegistryProperties.getInstance().getProperty("eric.security.authorization.defaultACP"); private AuthenticationServiceImpl ac = null; /** * Class Constructor. Protected and only used by getInstance() * */ protected AuthorizationServiceImpl() { // Load the functions Set<Function> customFunctions = loadCustomFunctions(); Iterator<Function> funcIter = customFunctions.iterator(); while (funcIter.hasNext()) { Function func = funcIter.next(); FunctionFactory.addTargetFunction(func); } // Add any custom PolicyFinderModules to XACML engine Set<PolicyFinderModule> policyFinders = loadCustomPolicyFinderModules(); PolicyFinder policyFinder = new PolicyFinder(); policyFinder.setModules(policyFinders); // Add any custom AttributeFinderModules to XACML engine Set<AttributeFinderModule> customAttributeFinderModules = loadCustomAttributeFinderModules(); CurrentEnvModule envModule = new CurrentEnvModule(); ArrayList<AttributeFinderModule> attrModules = new ArrayList<AttributeFinderModule>(); attrModules.addAll(customAttributeFinderModules); attrModules.add(envModule); AttributeFinder attrFinder = new AttributeFinder(); attrFinder.setModules(attrModules); pdp = new PDP(new PDPConfig(attrFinder, policyFinder, null)); ac = AuthenticationServiceImpl.getInstance(); } /** * Gets the singleton instance as defined by Singleton pattern. * * @return the singleton instance * */ public synchronized static AuthorizationServiceImpl getInstance() { if (instance == null) { instance = new AuthorizationServiceImpl(); } return instance; } /** * * @return the Set of custom AttributeFinder modules for use with the XACML * engine. */ private Set<AttributeFinderModule> loadCustomAttributeFinderModules() { HashSet<AttributeFinderModule> customAFMs = new HashSet<AttributeFinderModule>(); String customAFMList = RegistryProperties.getInstance() .getProperty("eric.security.authorization.customAttributeFinderModules"); if (customAFMList != null) { StringTokenizer st = new StringTokenizer(customAFMList, ","); while (st.hasMoreTokens()) { String customAFMClassName = st.nextToken(); try { AttributeFinderModule afm = (AttributeFinderModule) Class.forName(customAFMClassName) .newInstance(); customAFMs.add(afm); log.debug("Loaded custom attribute finder module '" + customAFMClassName + "'"); } catch (Throwable t) { log.warn(ServerResourceBundle.getInstance().getString( "message.FailedToLoadCustomAttributeFinderModuleException", new Object[] { customAFMClassName, t.getMessage() })); } } } return customAFMs; } /** * * @return the Set of custom PolicyFinder modules for use with the XACML * engine. */ private Set<PolicyFinderModule> loadCustomPolicyFinderModules() { HashSet<PolicyFinderModule> customPFMs = new HashSet<PolicyFinderModule>(); String customPFMList = RegistryProperties.getInstance() .getProperty("eric.security.authorization.customPolicyFinderModules"); if (customPFMList != null) { StringTokenizer st = new StringTokenizer(customPFMList, ","); while (st.hasMoreTokens()) { String customPFMClassName = st.nextToken(); try { PolicyFinderModule pfm = (PolicyFinderModule) Class.forName(customPFMClassName).newInstance(); customPFMs.add(pfm); log.debug("Loaded custom policy finder module '" + customPFMClassName + "'"); } catch (Throwable t) { log.warn(ServerResourceBundle.getInstance().getString( "message.FailedToLoadCustomPolicyFinderModuleException", new Object[] { customPFMClassName, t.getMessage() })); } } } return customPFMs; } /** * * @return the Set of custom Functions for use with the XACML engine. */ private Set<Function> loadCustomFunctions() { HashSet<Function> customFuncs = new HashSet<Function>(); String customFuncList = RegistryProperties.getInstance() .getProperty("eric.security.authorization.customFunctions"); if (customFuncList != null) { StringTokenizer st = new StringTokenizer(customFuncList, ","); while (st.hasMoreTokens()) { String customFuncClassName = st.nextToken(); try { Function func = (Function) Class.forName(customFuncClassName).newInstance(); customFuncs.add(func); log.debug("Loaded custom attribute finder module '" + customFuncClassName + "'"); } catch (Throwable t) { log.warn(ServerResourceBundle.getInstance().getString( "message.FailedToLoadCustomFunctionException", new Object[] { customFuncClassName, t.getMessage() })); } } } return customFuncs; } /** * Check if user is authorized to perform specified request using V3 * specification. * <p> * Check if the specified User (requestor) is authorized to make this * request or not. The initial subject lists contains the object in the * request is a resource. The primary action is determined by the type of * request. In addition * * <ul> * <li> * <b><i>AdhocQueryRequest: </i></b> Process query as normal and then filter * out objects that should not be visible to the client.</li> * <li> * <b><i>ApproveObjectRequest: </i></b> Check if subject is authorized for * the approve action.</li> * <li> * <b><i>Deprecate/UndeprecateRequest: </i></b> Check if subject is * authorized for the deprecate/undeprecate action.</li> * <li> * <b><i>RemoveObjectRequest: </i></b> Check if subject is authorized for * the delete action.</li> * <li> * <b><i>SubmitObjectsRequest/UpdateObjectsRequest: </i></b> Check if * subject authorized for the create action. Check any referenced objects * and see if their policies allows reference action.</li> * </ul> * * @todo Do we need any new Attribute types by Extending AttributeValue * (have string URI etc.)?? * @todo Do we need any new functions?? * * @throws RegistryException */ @SuppressWarnings({ "static-access", "deprecation" }) public AuthorizationResult checkAuthorization(ServerRequestContext context) throws RegistryException { try { RegistryRequestType registryRequest = context.getCurrentRegistryRequest(); if (null == context.getUser()) { // Set context user as RegistryGuest built in context.setUser(ac.registryGuest); } String userId = context.getUser().getId(); AuthorizationResult authRes = new AuthorizationResult(userId); boolean isAdmin = context.isRegistryAdministrator(); if (isAdmin) { // Allow RegistryAdmin role all privileges return authRes; } Set<Subject> subjects = new HashSet<Subject>(); Set<Attribute> actions = new HashSet<Attribute>(); Set<Attribute> environment = new HashSet<Attribute>(); Attribute actionAttr = null; boolean readOnly = false; String action = bu.getActionFromRequest(registryRequest); actionAttr = new Attribute(new URI(ACTION_ATTRIBUTE_ID), new URI(StringAttribute.identifier), null, null, new StringAttribute(action)); // Determine the action attributes. if (registryRequest instanceof AdhocQueryRequest) { readOnly = true; } actions.add(actionAttr); // Init subject attributes HashSet<Attribute> userSubjectAttributes = new HashSet<Attribute>(); Attribute idSubjectAttr = new Attribute(new URI(SUBJECT_ATTRIBUTE_ID), new URI(AnyURIAttribute.identifier), null, null, new AnyURIAttribute(new URI(userId))); userSubjectAttributes.add(idSubjectAttr); Attribute userSubjectAttr = new Attribute(new URI(SUBJECT_ATTRIBUTE_USER), new URI(ObjectAttribute.identifier), null, null, new ObjectAttribute(context.getUser())); userSubjectAttributes.add(userSubjectAttr); Subject userSubject = new Subject(new URI(AttributeDesignator.SUBJECT_CATEGORY_DEFAULT), userSubjectAttributes); subjects.add(userSubject); // Pass RequestContext as an environment attribute Attribute requestEnvAttr = new Attribute(new URI(ENVIRONMENT_ATTRIBUTE_REQUEST_CONTEXT), new URI(ObjectAttribute.identifier), null, null, new ObjectAttribute(context)); environment.add(requestEnvAttr); // Iterate over each resource and see if action is authorized on the // resource by the subject ArrayList<String> ids = new ArrayList<String>(); if (registryRequest instanceof AdhocQueryRequest) { // For AdhocQueryRequest query is already done and result is in // queryResults. Now do access control check on queryResults. Iterator<?> iter = context.getQueryResults().iterator(); while (iter.hasNext()) { IdentifiableType ro = (IdentifiableType) iter.next(); ids.add(ro.getId()); } } else { ids.addAll(bu.getIdsFromRequest(registryRequest)); } // Optimization: Get ownersMap in a single query and cache it @SuppressWarnings("unused") HashMap<String, String> ownersMap = getOwnersMap(context, ids); Iterator<String> idsIter = ids.iterator(); while (idsIter.hasNext()) { String id = idsIter.next(); if (id != null) { if ((!readOnly) && (id.equals(idForDefaultACP))) { // Auth check for defaultACP is special and requires // that // it is submitted by RegistryAdministrator role. // Note this will be generalized when we have better // Role Based Access Control (RBAC) support if (!isAdmin) { String msg = getExceptionMessage( "message.error.authorization.allowedOnlyToAdmin.defineDefaultACP", id, context.getUser(), getActionString(actions)); throw new UnauthorizedRequestException(id, context.getUser().getId(), getActionString(actions), msg); } } else { try { checkAuthorizationForResource(context, id, subjects, actions, environment); authRes.addPermittedResource(id); } catch (UnauthorizedRequestException ure) { authRes.addDeniedResourceException(ure); } catch (RegistryException re) { if (re.getCause() instanceof UnauthorizedRequestException) { authRes.addDeniedResourceException((UnauthorizedRequestException) re.getCause()); } else { throw re; } } } } else { @SuppressWarnings("unused") int i = 0; } } log.debug("userId=" + userId + " is " + (authRes.getResult() == AuthorizationResult.PERMIT_NONE ? "not " : "") + "allowed to perform the requested operation."); return authRes; } catch (URISyntaxException e) { throw new RegistryException(e); } catch (AuthorizationException e) { throw e; } catch (JAXRException e) { throw new RegistryException(e); } } /** * Check if subject is authorized to perform action on the resource * RegistryObject. * * @param id * id of the resource being accessed. * @param subjects * A list of xacml subject Attributes representing the subject * making the request. * @param actions * A list of xacml action Attributes representing the action * being requested. * * @throws RegistryException */ @SuppressWarnings("deprecation") private void checkAuthorizationForResource(ServerRequestContext context, String id, Set<Subject> subjects, Set<Attribute> actions, Set<Attribute> environment) throws RegistryException { boolean isAdmin = context.isRegistryAdministrator(); RegistryObjectType ebRegistryObjectType = getRegistryObject(context, id, false); if (ebRegistryObjectType == null) { String msg = getExceptionMessage("message.error.authorization.ObjectNotFound", id, context.getUser(), getActionString(actions)); throw new AuthorizationException(msg); } // Only allow non RegistryAdministrator roles to READ Federation and // Registry types if (((ebRegistryObjectType instanceof FederationType) || (ebRegistryObjectType instanceof RegistryType)) && (!(isAdmin))) { String actionName = getActionAttributeValue(actions, ACTION_ATTRIBUTE_ID); if (!((actionName.equalsIgnoreCase("read")) || (actionName.equalsIgnoreCase("reference")))) { String msg = getExceptionMessage("message.error.authorization.allowedOnlyToAdmin.defineFederation", id, context.getUser(), getActionString(actions)); throw new UnauthorizedRequestException(id, context.getUser().getId(), getActionString(actions), msg); } } String ownerId = getRegistryObjectOwnerId(context, id); try { // Resource Attributes Attribute idResourceAttr = new Attribute(new URI(EvaluationCtx.RESOURCE_ID), new URI(AnyURIAttribute.identifier), null, null, new AnyURIAttribute(new URI(id))); Attribute resourceResourceAttr = new Attribute(new URI(RESOURCE_ATTRIBUTE_RESOURCE), new URI(ObjectAttribute.identifier), null, null, new ObjectAttribute(ebRegistryObjectType)); Attribute ownerResourceAttr = new Attribute(new URI(RESOURCE_ATTRIBUTE_OWNER), new URI(AnyURIAttribute.identifier), null, null, new AnyURIAttribute(new URI(ownerId))); Set<Attribute> resourceAttributes = new HashSet<Attribute>(); resourceAttributes.add(idResourceAttr); resourceAttributes.add(ownerResourceAttr); resourceAttributes.add(resourceResourceAttr); int decision = Result.DECISION_DENY; Status status = null; if (context.getConfirmationAssociations().containsKey(id)) { // Bypass auth check for confirm ass decision = Result.DECISION_PERMIT; } else { RequestCtx req = new RequestCtx(subjects, resourceAttributes, actions, environment); // PolicyDecisionPoint ResponseCtx resp = pdp.evaluate(req); Set<?> results = resp.getResults(); // Expecting only one Result Result result = (Result) results.iterator().next(); status = result.getStatus(); decision = result.getDecision(); } if (!(decision == Result.DECISION_PERMIT)) { String statusMsg = status.getMessage(); if (statusMsg == null) { statusMsg = ServerResourceBundle.getInstance().getString("message.NoInfoAvailable"); } String msg = getExceptionMessage("message.UnauthorizedRequestDenied", id, context.getUser(), getActionString(actions), statusMsg); throw new UnauthorizedRequestException(id, context.getUser().getId(), getActionString(actions), msg); } // The action is authorized for the resource. // However, some actions on a resource may result in other actions // on other resources // Check authorization for such special secondary actions next RegistryRequestType registryRequest = context.getCurrentRegistryRequest(); if ((registryRequest instanceof SubmitObjectsRequest) || (registryRequest instanceof UpdateObjectsRequest)) { // Check access control for all refrences from this object checkObjectReferencesInResource(context, id, subjects, environment); } } catch (URISyntaxException e) { throw new RegistryException(e); } } private void checkObjectReferencesInResource(ServerRequestContext context, String id, Set<Subject> subjects, Set<Attribute> environment) throws RegistryException { try { // Check if object being submitted has any reference actions that // need to be checked. RegistryObjectType ro = (RegistryObjectType) context.getSubmittedObjectsMap().get(id); Set<ReferenceInfo> refInfos = bu.getObjectRefsInRegistryObject(ro, context.getIdMap(), new HashSet<RegistryObjectType>(), 1); Iterator<ReferenceInfo> iter = refInfos.iterator(); while (iter.hasNext()) { ReferenceInfo refInfo = iter.next(); Set<Attribute> actionAttributes = getReferenceActionAttributes(refInfo); if ((actionAttributes != null) && (actionAttributes.size() > 0)) { try { checkAuthorizationForResource(context, refInfo.targetObject, subjects, actionAttributes, environment); } catch (UnauthorizedRequestException e) { String msg = getExceptionMessage( "message.error.authorization.referenceToDeprecatedObjectDenied", id, context.getUser(), getActionString(actionAttributes)); // ?? This is a JAXR spec bug that we do not send an // UnauthorizedRequestException // IllegalStateException constructor changed to not // include the cause, since that won't compile under JDK // 1.4 // TODO: Use reflection to issue this call to account // for differences between JDK 1.4 and 1.5 constructor // parameters? // throw new IllegalStateException(msg, e); throw new IllegalStateException(msg); } } if (ro instanceof AssociationType1) { AssociationType1 ass = (AssociationType1) ro; String assocType = ass.getAssociationType(); // Check to prevent multiple AccessControlFileFor // associations with same targetObject if ((assocType != null) && assocType.equalsIgnoreCase( BindingUtility.CANONICAL_ASSOCIATION_TYPE_ID_AccessControlPolicyFor)) { checkAccessControlPolicyForAssociation(context, ass); } // Check to prevent HasFederationMember assoc to be // submitted by non-admin if ((assocType != null) && assocType .equalsIgnoreCase(BindingUtility.CANONICAL_ASSOCIATION_TYPE_ID_HasFederationMember)) { if (!(context.isRegistryAdministrator())) { String msg = getExceptionMessage( "message.error.authorization.allowedOnlyToAdmin.defineFederation", id, context.getUser(), getActionString(actionAttributes)); throw new UnauthorizedRequestException(id, context.getUser().getId(), getActionString(actionAttributes), msg); } } } } } catch (AuthorizationException e) { throw e; } catch (JAXRException e) { throw new RegistryException(e); } } private void checkAccessControlPolicyForAssociation(ServerRequestContext context, AssociationType1 ass) throws RegistryException { // Make sure that only owner or admin is submitting // AccessControlPolicyFor Association String protectedResourceId = ass.getTargetObject(); String protectedResourceOwnerId = getRegistryObjectOwnerId(context, protectedResourceId); if (!(protectedResourceOwnerId.equals(context.getUser().getId()))) { String msg = getExceptionMessage("message.error.authorization.allowedOnlyToOwnerOrAdmin.assignACP", protectedResourceId, context.getUser()); throw new UnauthorizedRequestException(protectedResourceId, context.getUser().getId(), "", msg); } // Check and prevent multiple AccessControlPolicyFor Associations for // same targetObject ResponseOptionType ebResponseOptionType = BindingUtility.getInstance().queryFac.createResponseOptionType(); ebResponseOptionType.setReturnType(ReturnType.LEAF_CLASS); ebResponseOptionType.setReturnComposedObjects(true); String query = "SELECT ass.* from Association ass WHERE ass.targetObject = '" + protectedResourceId + "' AND ass.associationType = '" + BindingUtility.CANONICAL_ASSOCIATION_TYPE_ID_AccessControlPolicyFor + "' AND ass.id != '" + ass.getId() + "' "; List<IdentifiableType> oldAsses = PersistenceManagerFactory.getInstance().getPersistenceManager() .executeSQLQuery(context, query, ebResponseOptionType, "Association", new ArrayList<Object>()); if (oldAsses.size() > 0) { String msg = getExceptionMessage("message.error.authorization.AccessControlPolicyForAlreadyExists", protectedResourceId, context.getUser()); throw new AuthorizationException(msg); } } @SuppressWarnings({ "deprecation", "static-access" }) private Set<Attribute> getReferenceActionAttributes(ReferenceInfo refInfo) throws RegistryException { Set<Attribute> actionAttributes = new HashSet<Attribute>(); try { // The id for the "reference" action Attribute idAttr = new Attribute(new URI(ACTION_ATTRIBUTE_ID), new URI(StringAttribute.identifier), null, null, new StringAttribute(bu.ACTION_REFERENCE)); actionAttributes.add(idAttr); // The sourceObject for the "reference" action Attribute refSourceAttr = new Attribute(new URI(ACTION_ATTRIBUTE_REFERENCE_SOURCE), new URI(AnyURIAttribute.identifier), null, null, new AnyURIAttribute(new URI(refInfo.sourceObject))); actionAttributes.add(refSourceAttr); // The attribute within the source object for the "reference" // action. Attribute refSourceAttributeAttr = new Attribute(new URI(ACTION_ATTRIBUTE_REFERENCE_SOURCE_ATTRIBUTE), new URI(StringAttribute.identifier), null, null, new StringAttribute(refInfo.attributeName)); actionAttributes.add(refSourceAttributeAttr); } catch (URISyntaxException e) { throw new RegistryException(e); } return actionAttributes; } private String getActionString(Set<Attribute> actions) { String actionStr = ""; Iterator<Attribute> actionsIter = actions.iterator(); while (actionsIter.hasNext()) { Attribute actionAttr = actionsIter.next(); String attributeIdStr = actionAttr.getId().toString(); String attributeValueStr = actionAttr.getValue().encode(); if (attributeIdStr.equals(ACTION_ATTRIBUTE_ID)) { actionStr += " id=" + attributeValueStr; } else if (attributeIdStr.equals(ACTION_ATTRIBUTE_REFERENCE_SOURCE)) { actionStr += " sourceObject=" + attributeValueStr; } else if (attributeIdStr.equals(ACTION_ATTRIBUTE_REFERENCE_SOURCE_ATTRIBUTE)) { actionStr += " sourceAttribute=" + attributeValueStr; } else if (attributeIdStr.startsWith(ACTION_ATTRIBUTE_REFERENCE_SOURCE_ATTRIBUTE_FILTER_PREFIX)) { actionStr += " filter=" + attributeValueStr; } } return actionStr; } /** * Gets the AttributeValue matching specified Attribute id. */ private String getActionAttributeValue(Set<Attribute> actions, String attributeName) { String attributeValue = ""; Iterator<Attribute> actionsIter = actions.iterator(); while (actionsIter.hasNext()) { Attribute actionAttr = actionsIter.next(); String attributeIdStr = actionAttr.getId().toString(); String attributeValueStr = actionAttr.getValue().encode(); if (attributeIdStr.equals(attributeName)) { attributeValue = attributeValueStr; } } return attributeValue; } /** * Gets the RegistryObject with the specified id, if any. Use context cache * for fetched objects. NOTE: If 'requireExisting' is set to false AND * request is submission/update, object is returned from request. Otherwise * it must come from registry. * * @param context * The RequestContext, used for caching. * @param id * The UUID of a RegistryObject. * @param requireExisting * boolean flag to require object from registry (not from * submission) * @return The UUID of the user who owns the registry object. * @throws RegistryException */ RegistryObjectType getRegistryObject(ServerRequestContext context, String id, boolean requireExisting) throws RegistryException { return context.getRegistryObject(id, "RegistryObject", requireExisting); } /** * Gets the id of the user who owns the registry object with the specified * id. * * @param id * The UUID of a RegistryObject. * @return The UUID of the user who owns the registry object. * @throws RegistryException * Thrown if no owner can be found for the registry object. */ @SuppressWarnings("static-access") private HashMap<String, String> getOwnersMap(ServerRequestContext context, List<String> ids) throws RegistryException { PersistenceManager pm = PersistenceManagerFactory.getInstance().getPersistenceManager(); HashMap<String, String> ownersMap = pm.getOwnersMap(context, ids); // If Object not it registry and If request is submit or update then use // caller as owner // but only if caller is not RegistryGuest Iterator<String> iter = ids.iterator(); while (iter.hasNext()) { String id = iter.next(); if (!(ownersMap.containsKey(id))) { // Object has no owner yet. Must not be in registry if ((context.getCurrentRegistryRequest() instanceof SubmitObjectsRequest) || (context.getCurrentRegistryRequest() instanceof UpdateObjectsRequest)) { // Request is submit or update if (!(context.getUser().getId().equals(ac.ALIAS_REGISTRY_GUEST))) { String ownerId = context.getUser().getId(); ownersMap.put(id, ownerId); } else { // throw exception, cannot continue or will return null String msg = getExceptionMessage("message.error.authorization.unauthenticatedUser", id.toString()); throw new UnauthorizedRequestException(id, context.getUser().getId(), "", msg); } } } } // Update cache context.getFetchedOwners().putAll(ownersMap); return ownersMap; } /** * Gets the id of the user who owns the registry object with the specified * id. * * @param id * The UUID of a RegistryObject. * @return The UUID of the user who owns the registry object. * @throws RegistryException * Thrown if no owner can be found for the registry object. */ @SuppressWarnings("static-access") private String getRegistryObjectOwnerId(ServerRequestContext context, String id) throws RegistryException { // Check cache first String ownerId = context.getFetchedOwners().get(id); if (ownerId != null) { return ownerId; } // Check if object is already in Registry RegistryObjectType ro = null; try { ro = getRegistryObject(context, id, true); } catch (ObjectNotFoundException e) { // Not in registry. ro = null; } if (ro == null) { // Object not it registry. If request is submit or update then use // caller as owner if ((context.getCurrentRegistryRequest() instanceof SubmitObjectsRequest) || (context.getCurrentRegistryRequest() instanceof UpdateObjectsRequest)) { // Dont let RegistryGuest be owner // Without this extra check RegistryGuest was able to save if (!(context.getUser().getId().equals(ac.ALIAS_REGISTRY_GUEST))) { ownerId = context.getUser().getId(); return ownerId; } else { // throw exception, cannot continue or will return null String msg = getExceptionMessage("message.error.authorization.unauthenticatedUser", id.toString()); throw new UnauthorizedRequestException(id, context.getUser().getId(), "", msg); } } } // Object SHOULD be in registry already. Get owner from registry. // Note that RemoveObjectsRequest for an object that is not in registry // is a case where object may not be in the registry. List<String> ids = new ArrayList<String>(); ids.add(id); PersistenceManager pm = PersistenceManagerFactory.getInstance().getPersistenceManager(); HashMap<String, String> ownersMap = pm.getOwnersMap(context, ids); ownerId = ownersMap.get(id); // Special case for getting RegistryOperator as owner of // AuditableEvents. if ((ownerId == null) && (ro != null)) { @SuppressWarnings("unused") String className = "Unknown"; className = ro.getClass().getName(); // AuditableEvents owner is undefined and is implicitly // RegistryOperator if (ro instanceof AuditableEventType) { ownerId = ac.ALIAS_REGISTRY_OPERATOR; } else { // throw new // RegistryException(ServerResourceBundle.getInstance().getString("message.ownersNotFound", // new Object[]{id,className})); // This can happen for transient objects created and returned by // special queries such as Export Query ownerId = ac.ALIAS_REGISTRY_GUEST; } } // Update cache context.getFetchedOwners().put(id, ownerId); return ownerId; } /* * Utility method to get ServerRequestContext from xacml EValuationContext */ static ServerRequestContext getRequestContext(EvaluationCtx context) { Object obj = null; try { if (context != null) { EvaluationResult result = context.getEnvironmentAttribute(new URI(ObjectAttribute.identifier), new URI(AuthorizationServiceImpl.ENVIRONMENT_ATTRIBUTE_REQUEST_CONTEXT), null); AttributeValue attrValue = result.getAttributeValue(); BagAttribute bagAttr = (BagAttribute) attrValue; if (bagAttr.size() == 1) { Iterator<?> iter = bagAttr.iterator(); ObjectAttribute objAttr = (ObjectAttribute) iter.next(); if (objAttr != null) { obj = objAttr.getValue(); } } } else { // This path is only used by unit tests like // AssociationExistsFunctionTest which do not have an // EvaluationCtx to pass. obj = new ServerRequestContext( "it.cnr.icar.eric.server.security.authorization:AuthorizationServiceImpl:getRequestContext", null); } } catch (Exception e) { log.error(e.getMessage(), e); } return (ServerRequestContext) obj; } private String getExceptionMessage(String messageId, String objectId) { return getExceptionMessage(messageId, objectId, null, null, null); } private String getExceptionMessage(String messageId, String objectId, UserType user) { return getExceptionMessage(messageId, objectId, user, null, null); } private String getExceptionMessage(String messageId, String objectId, UserType user, String action) { return getExceptionMessage(messageId, objectId, user, action, null); } private String getExceptionMessage(String messageId, String objectId, UserType user, String action, String info) { ArrayList<String> params = new ArrayList<String>(); if (objectId != null) { params.add(objectId); } if (user != null) { params.add(getUserInfo(user)); } if (action != null) { params.add(action); } if (info != null) { params.add(info); } String msg = ServerResourceBundle.getInstance().getString(messageId, params.toArray()); return msg; } private String getUserInfo(UserType user) { String userInfo = " id=" + user.getId(); try { PersonNameImpl personName = new PersonNameImpl(null, null, user.getPersonName()); userInfo += " name=" + personName.getFormattedName(); } catch (Exception e) { log.warn(e, e); } return userInfo; } }