From 183dab9c0c55efecbc4b108e16573eeccd8f5d78 Mon Sep 17 00:00:00 2001 From: Richard Millet Date: Wed, 6 Apr 2011 04:59:50 +0000 Subject: [PATCH] CSPACE-3764, CSPACE-3710, CSPACE-3709: AuthZ support for Dimensions service as a first proof-of-implemenation. --- .../importer/AuthorizationGen.java | 81 ++++++++++----- .../client/AbstractServiceClientImpl.java | 2 +- .../services/client/CollectionSpaceProxy.java | 5 +- .../services/client/PayloadOutputPart.java | 2 +- .../client/test/AbstractServiceTestImpl.java | 71 +++++++++++--- .../client/workflow/WorkflowClient.java | 3 +- .../collectionspace/tenant-bindings.xml | 11 ++- ...tMultiPartCollectionSpaceResourceImpl.java | 8 +- .../context/MultipartServiceContext.java | 3 + .../context/MultipartServiceContextImpl.java | 13 ++- .../common/security/SecurityInterceptor.java | 20 ++++ .../common/storage/jpa/JpaStorageUtils.java | 27 ++++- .../nuxeo/WorkflowDocumentModelHandler.java | 6 +- .../java/RemoteDocumentModelHandlerImpl.java | 17 ++++ .../client/test/DimensionServiceTest.java | 98 +++++++++++++++++++ ...rkflows_common.xsd => workflow_common.xsd} | 2 +- .../resources/OSGI-INF/core-types-contrib.xml | 2 +- ...rkflows_common.xsd => workflow_common.xsd} | 0 .../client/test/WorkflowServiceTest.java | 90 ++++++++--------- .../nuxeo/WorkflowValidatorHandler.java | 6 +- 20 files changed, 354 insertions(+), 113 deletions(-) rename services/jaxb/src/main/resources/{workflows_common.xsd => workflow_common.xsd} (97%) rename services/workflow/3rdparty/nuxeo-platform-cs-workflow/src/main/resources/schemas/{workflows_common.xsd => workflow_common.xsd} (100%) diff --git a/services/authorization-mgt/import/src/main/java/org/collectionspace/services/authorization/importer/AuthorizationGen.java b/services/authorization-mgt/import/src/main/java/org/collectionspace/services/authorization/importer/AuthorizationGen.java index 450b0f3c0..c203f80df 100644 --- a/services/authorization-mgt/import/src/main/java/org/collectionspace/services/authorization/importer/AuthorizationGen.java +++ b/services/authorization-mgt/import/src/main/java/org/collectionspace/services/authorization/importer/AuthorizationGen.java @@ -63,11 +63,16 @@ public class AuthorizationGen { final public static String ROLE_TENANT_ADMINISTRATOR = "TENANT_ADMINISTRATOR"; final public static String ROLE_TENANT_READER = "TENANT_READER"; final public static String ROLE_ADMINISTRATOR_ID = "0"; + final public static String ADMINISTRATOR_TENANT_ID = "0"; // // ActionGroup labels/constants // final public static String ACTIONGROUP_CRUDL = "CRUDL"; final public static String ACTIONGROUP_RL = "RL"; + // + // Should the base resource act as a proxy for its sub-resources for AuthZ purposes + // + final public static boolean AUTHZ_IS_ENTITY_PROXY = false; final Logger logger = LoggerFactory.getLogger(AuthorizationGen.class); private List adminPermList = new ArrayList(); @@ -100,10 +105,10 @@ public class AuthorizationGen { */ public void createDefaultPermissions() { for (String tenantId : tenantBindings.keySet()) { - List adminPerms = createDefaultAdminPermissions(tenantId); + List adminPerms = createDefaultAdminPermissions(tenantId, AUTHZ_IS_ENTITY_PROXY); adminPermList.addAll(adminPerms); - List readerPerms = createDefaultReaderPermissions(tenantId); + List readerPerms = createDefaultReaderPermissions(tenantId, AUTHZ_IS_ENTITY_PROXY); readerPermList.addAll(readerPerms); } } @@ -114,14 +119,14 @@ public class AuthorizationGen { * @param tenantId * @return */ - public List createDefaultAdminPermissions(String tenantId) { + public List createDefaultAdminPermissions(String tenantId, boolean isEntityProxy) { ArrayList apcList = new ArrayList(); TenantBindingType tbinding = tenantBindings.get(tenantId); for (ServiceBindingType sbinding : tbinding.getServiceBindings()) { //add permissions for the main path String resourceName = sbinding.getName().toLowerCase().trim(); - if (SecurityUtils.isEntityProxy() == true) { + if (isEntityProxy == true) { resourceName = SecurityUtils.getResourceEntity(resourceName); } Permission perm = buildAdminPermission(tbinding.getId(), @@ -129,7 +134,7 @@ public class AuthorizationGen { apcList.add(perm); //add permissions for alternate paths - if (SecurityUtils.isEntityProxy() == false) { + if (isEntityProxy == false) { List uriPaths = sbinding.getUriPath(); for (String uriPath : uriPaths) { perm = buildAdminPermission(tbinding.getId(), @@ -180,13 +185,13 @@ public class AuthorizationGen { * @param tenantId * @return */ - public List createDefaultReaderPermissions(String tenantId) { + public List createDefaultReaderPermissions(String tenantId, boolean isEntityProxy) { ArrayList apcList = new ArrayList(); TenantBindingType tbinding = tenantBindings.get(tenantId); for (ServiceBindingType sbinding : tbinding.getServiceBindings()) { //add permissions for the main path String resourceName = sbinding.getName().toLowerCase().trim(); - if (SecurityUtils.isEntityProxy() == true) { + if (isEntityProxy == true) { resourceName = SecurityUtils.getResourceEntity(resourceName); } Permission perm = buildReaderPermission(tbinding.getId(), @@ -194,7 +199,7 @@ public class AuthorizationGen { apcList.add(perm); //add permissions for alternate paths - if (SecurityUtils.isEntityProxy() == false) { + if (isEntityProxy == false) { List uriPaths = sbinding.getUriPath(); for (String uriPath : uriPaths) { perm = buildReaderPermission(tbinding.getId(), @@ -298,12 +303,12 @@ public class AuthorizationGen { public void associateDefaultPermissionsRoles() { for (Permission p : adminPermList) { - PermissionRole permAdmRole = associatePermissionRoles(p, adminRoles); + PermissionRole permAdmRole = associatePermissionRoles(p, adminRoles, true); adminPermRoleList.add(permAdmRole); } for (Permission p : readerPermList) { - PermissionRole permRdrRole = associatePermissionRoles(p, readerRoles); + PermissionRole permRdrRole = associatePermissionRoles(p, readerRoles, true); readerPermRoleList.add(permRdrRole); } @@ -311,23 +316,33 @@ public class AuthorizationGen { List roles = new ArrayList(); roles.add(cspaceAdminRole); for (Permission p : adminPermList) { - PermissionRole permCAdmRole = associatePermissionRoles(p, roles); + PermissionRole permCAdmRole = associatePermissionRoles(p, roles, false); adminPermRoleList.add(permCAdmRole); } } - public List associatePermissionsRoles(List perms, List roles) { + public List associatePermissionsRoles(List perms, List roles, boolean enforceTenancy) { + List result = null; + List permRoles = new ArrayList(); for (Permission perm : perms) { - PermissionRole permRole = associatePermissionRoles(perm, roles); - permRoles.add(permRole); + PermissionRole permRole = associatePermissionRoles(perm, roles, enforceTenancy); + if (permRole != null) { + permRoles.add(permRole); + } + } + + if (permRoles.isEmpty() == false) { + result = permRoles; } - return permRoles; + + return result; } private PermissionRole associatePermissionRoles(Permission perm, - List roles) { - + List roles, boolean enforceTenancy) { + PermissionRole result = null; + PermissionRole pr = new PermissionRole(); pr.setSubject(SubjectType.ROLE); List permValues = new ArrayList(); @@ -340,15 +355,32 @@ public class AuthorizationGen { List roleValues = new ArrayList(); for (Role role : roles) { - RoleValue rv = new RoleValue(); - // This needs to use the qualified name, not the display name - rv.setRoleName(role.getRoleName().toUpperCase()); - rv.setRoleId(role.getCsid()); - roleValues.add(rv); + boolean tenantIdsMatched = true; + if (enforceTenancy == true) { + tenantIdsMatched = role.getTenantId().equals(perm.getTenantId()); + } + if (tenantIdsMatched == true) { + RoleValue rv = new RoleValue(); + // This needs to use the qualified name, not the display name + rv.setRoleName(role.getRoleName().toUpperCase()); + rv.setRoleId(role.getCsid()); + roleValues.add(rv); + } else { + if (logger.isDebugEnabled() == true) { + logger.debug("Role and Permission tenant ID did not match."); //FIXME: REM - Remove this debug statement. + } + } + } + // + // If 'roleValues' is not empty, then associate it with the incoming 'perm' values + // otherwise, return null; + // + if (roleValues.isEmpty() == false) { + pr.setRoles(roleValues); + result = pr; } - pr.setRoles(roleValues); - return pr; + return result; } public List getDefaultPermissionRoles() { @@ -371,6 +403,7 @@ public class AuthorizationGen { role.setDisplayName(ROLE_ADMINISTRATOR); role.setRoleName(ROLE_PREFIX + role.getDisplayName()); role.setCsid(ROLE_ADMINISTRATOR_ID); + role.setTenantId(ADMINISTRATOR_TENANT_ID); return role; } diff --git a/services/client/src/main/java/org/collectionspace/services/client/AbstractServiceClientImpl.java b/services/client/src/main/java/org/collectionspace/services/client/AbstractServiceClientImpl.java index dbd105c4d..84e01ee70 100644 --- a/services/client/src/main/java/org/collectionspace/services/client/AbstractServiceClientImpl.java +++ b/services/client/src/main/java/org/collectionspace/services/client/AbstractServiceClientImpl.java @@ -37,7 +37,7 @@ import org.apache.commons.httpclient.auth.AuthScope; //import org.collectionspac import org.collectionspace.services.common.authorityref.AuthorityRefList; import org.collectionspace.services.jaxb.AbstractCommonList; -import org.collectionspace.services.workflow.WorkflowsCommon; +import org.collectionspace.services.workflow.WorkflowCommon; import org.jboss.resteasy.client.ClientResponse; //import org.collectionspace.services.common.context.ServiceContext; import org.jboss.resteasy.client.ProxyFactory; import org.jboss.resteasy.client.core.executors.ApacheHttpClientExecutor; diff --git a/services/client/src/main/java/org/collectionspace/services/client/CollectionSpaceProxy.java b/services/client/src/main/java/org/collectionspace/services/client/CollectionSpaceProxy.java index d751eb5f3..fd1f667ba 100644 --- a/services/client/src/main/java/org/collectionspace/services/client/CollectionSpaceProxy.java +++ b/services/client/src/main/java/org/collectionspace/services/client/CollectionSpaceProxy.java @@ -36,6 +36,7 @@ import javax.ws.rs.Produces; import javax.ws.rs.QueryParam; import javax.ws.rs.core.Response; +import org.collectionspace.services.client.workflow.WorkflowClient; import org.collectionspace.services.common.authorityref.AuthorityRefList; import org.collectionspace.services.jaxb.AbstractCommonList; @@ -61,13 +62,13 @@ public interface CollectionSpaceProxy { @GET @Produces({"application/xml"}) @Consumes({"application/xml"}) - @Path("{csid}/workflow") + @Path("{csid}" + WorkflowClient.SERVICE_PATH) ClientResponse getWorkflow(@PathParam("csid") String csid); @PUT @Produces({"application/xml"}) @Consumes({"application/xml"}) - @Path("{csid}/workflow") + @Path("{csid}" + WorkflowClient.SERVICE_PATH) ClientResponse updateWorkflow(@PathParam("csid") String csid, byte[] xmlPayload); /* diff --git a/services/client/src/main/java/org/collectionspace/services/client/PayloadOutputPart.java b/services/client/src/main/java/org/collectionspace/services/client/PayloadOutputPart.java index 96e1f1af8..bd8c50caa 100644 --- a/services/client/src/main/java/org/collectionspace/services/client/PayloadOutputPart.java +++ b/services/client/src/main/java/org/collectionspace/services/client/PayloadOutputPart.java @@ -8,7 +8,7 @@ public class PayloadOutputPart extends PayloadPart { // // Constructors // - PayloadOutputPart(String label, Object body) { + public PayloadOutputPart(String label, Object body) { super(label, body); } diff --git a/services/client/src/main/java/org/collectionspace/services/client/test/AbstractServiceTestImpl.java b/services/client/src/main/java/org/collectionspace/services/client/test/AbstractServiceTestImpl.java index c871478f8..9f2a66869 100644 --- a/services/client/src/main/java/org/collectionspace/services/client/test/AbstractServiceTestImpl.java +++ b/services/client/src/main/java/org/collectionspace/services/client/test/AbstractServiceTestImpl.java @@ -28,7 +28,13 @@ import java.util.ArrayList; import java.util.List; import org.collectionspace.services.jaxb.AbstractCommonList; +import org.collectionspace.services.workflow.WorkflowCommon; +import org.collectionspace.services.client.AbstractPoxServiceClientImpl; import org.collectionspace.services.client.CollectionSpaceClient; +import org.collectionspace.services.client.PayloadOutputPart; +import org.collectionspace.services.client.PoxPayloadIn; +import org.collectionspace.services.client.PoxPayloadOut; +import org.collectionspace.services.client.workflow.WorkflowClient; import org.jboss.resteasy.client.ClientResponse; import org.slf4j.Logger; @@ -39,6 +45,7 @@ import org.testng.annotations.AfterClass; import org.testng.annotations.Test; import javax.activation.MimetypesFileTypeMap; +import javax.ws.rs.core.Response; /** @@ -595,17 +602,59 @@ public abstract class AbstractServiceTestImpl extends BaseServiceTest implements totalItems);//expected total num of items } } - -// @Override -// protected CollectionSpaceClient getClientInstance() { -// throw new UnsupportedOperationException(); //FIXME: REM - See http://issues.collectionspace.org/browse/CSPACE-3498 -// } -// -// @Override -// protected String getServiceName() { -// throw new UnsupportedOperationException(); //FIXME: REM - See http://issues.collectionspace.org/browse/CSPACE-3498 -// } - + + @SuppressWarnings("rawtypes") + protected void updateLifeCycleState(String testName, String resourceId, String lifeCycleState) throws Exception { + // + // Read the existing object + // + CollectionSpaceClient client = this.getClientInstance(); + ClientResponse res = client.getWorkflow(resourceId); + assertStatusCode(res, testName); + logger.debug("Got object to update life cycle state with ID: " + resourceId); + PoxPayloadIn input = new PoxPayloadIn(res.getEntity()); + WorkflowCommon workflowCommons = (WorkflowCommon) extractPart(input, WorkflowClient.SERVICE_COMMONPART_NAME, WorkflowCommon.class); + Assert.assertNotNull(workflowCommons); + // + // Mark it for a soft delete. + // + logger.debug("Current workflow state:" + objectAsXmlString(workflowCommons, WorkflowCommon.class)); + workflowCommons.setCurrentLifeCycleState(lifeCycleState); + PoxPayloadOut output = new PoxPayloadOut(WorkflowClient.SERVICE_PAYLOAD_NAME); + PayloadOutputPart commonPart = output.addPart(WorkflowClient.SERVICE_COMMONPART_NAME, workflowCommons); + // + // Perform the update + // + res = client.updateWorkflow(resourceId, output); + assertStatusCode(res, testName); + input = new PoxPayloadIn(res.getEntity()); + WorkflowCommon updatedWorkflowCommons = (WorkflowCommon) extractPart(input, WorkflowClient.SERVICE_COMMONPART_NAME, WorkflowCommon.class); + Assert.assertNotNull(updatedWorkflowCommons); + // + // Read the updated object and make sure it was updated correctly. + // + res = client.getWorkflow(resourceId); + assertStatusCode(res, testName); + logger.debug("Got workflow state of updated object with ID: " + resourceId); + input = new PoxPayloadIn(res.getEntity()); + updatedWorkflowCommons = (WorkflowCommon) extractPart(input, WorkflowClient.SERVICE_COMMONPART_NAME, WorkflowCommon.class); + Assert.assertNotNull(workflowCommons); + Assert.assertEquals(updatedWorkflowCommons.getCurrentLifeCycleState(), lifeCycleState); + } + + /* + * Sub-classes must override for the workflow tests. + */ + + protected String createWorkflowTarget(String testName) throws Exception { + logger.warn("Sub-class test clients should override this method"); + throw new UnsupportedOperationException(); + } + + protected String createTestObject(String testName) throws Exception { + logger.warn("Sub-class test clients should override this method"); + throw new UnsupportedOperationException(); + } } diff --git a/services/client/src/main/java/org/collectionspace/services/client/workflow/WorkflowClient.java b/services/client/src/main/java/org/collectionspace/services/client/workflow/WorkflowClient.java index bbbcc5b34..108009054 100644 --- a/services/client/src/main/java/org/collectionspace/services/client/workflow/WorkflowClient.java +++ b/services/client/src/main/java/org/collectionspace/services/client/workflow/WorkflowClient.java @@ -27,11 +27,12 @@ import org.collectionspace.services.client.AbstractPoxServiceClientImpl; * */ public class WorkflowClient extends AbstractPoxServiceClientImpl { - public static final String SERVICE_NAME = "workflows"; + public static final String SERVICE_NAME = "workflow"; public static final String SERVICE_PATH_COMPONENT = SERVICE_NAME; public static final String SERVICE_PATH = "/" + SERVICE_PATH_COMPONENT; public static final String SERVICE_PAYLOAD_NAME = SERVICE_NAME; public static final String SERVICE_COMMONPART_NAME = SERVICE_NAME + PART_LABEL_SEPARATOR + PART_COMMON_LABEL; + public static final String SERVICE_AUTHZ_SUFFIX = "/*/" + SERVICE_PATH_COMPONENT + "/"; // // Workflow states // diff --git a/services/common/src/main/cspace/config/services/tenants/collectionspace/tenant-bindings.xml b/services/common/src/main/cspace/config/services/tenants/collectionspace/tenant-bindings.xml index bf4136a52..aa0ff82d6 100644 --- a/services/common/src/main/cspace/config/services/tenants/collectionspace/tenant-bindings.xml +++ b/services/common/src/main/cspace/config/services/tenants/collectionspace/tenant-bindings.xml @@ -686,7 +686,7 @@ - + default-domain @@ -700,7 +700,7 @@ xmlns:service='http://collectionspace.org/services/common/service'> + label="workflow-system" updated="" order="0"> + label="workflow_common" updated="" order="1"> + schemaLocation="http://collectionspace.org/services/workflow http://services.collectionspace.org/movement/workflow_common.xsd"> @@ -1500,6 +1500,9 @@ + + /dimensions/*/workflow/ + default-domain diff --git a/services/common/src/main/java/org/collectionspace/services/common/AbstractMultiPartCollectionSpaceResourceImpl.java b/services/common/src/main/java/org/collectionspace/services/common/AbstractMultiPartCollectionSpaceResourceImpl.java index 20fe2241b..5f8e5ae87 100644 --- a/services/common/src/main/java/org/collectionspace/services/common/AbstractMultiPartCollectionSpaceResourceImpl.java +++ b/services/common/src/main/java/org/collectionspace/services/common/AbstractMultiPartCollectionSpaceResourceImpl.java @@ -46,7 +46,7 @@ import org.collectionspace.services.common.document.DocumentHandler; import org.collectionspace.services.common.document.DocumentNotFoundException; import org.collectionspace.services.common.security.UnauthorizedException; import org.collectionspace.services.common.workflow.service.nuxeo.WorkflowDocumentModelHandler; -import org.collectionspace.services.workflow.WorkflowsCommon; +import org.collectionspace.services.workflow.WorkflowCommon; import org.jboss.resteasy.client.ClientResponse; import org.slf4j.Logger; @@ -162,7 +162,7 @@ public abstract class AbstractMultiPartCollectionSpaceResourceImpl extends WorkflowDocumentModelHandler docHandler = (WorkflowDocumentModelHandler)createDocumentHandler(ctx, WorkflowClient.SERVICE_COMMONPART_NAME, - WorkflowsCommon.class); + WorkflowCommon.class); return docHandler; } @@ -172,7 +172,7 @@ public abstract class AbstractMultiPartCollectionSpaceResourceImpl extends */ @GET - @Path("{csid}/workflow") + @Path("{csid}" + WorkflowClient.SERVICE_PATH) public byte[] getWorkflow( @PathParam("csid") String csid) { PoxPayloadOut result = null; @@ -194,7 +194,7 @@ public abstract class AbstractMultiPartCollectionSpaceResourceImpl extends } @PUT - @Path("{csid}/workflow") + @Path("{csid}" + WorkflowClient.SERVICE_PATH) public byte[] updateWorkflow(@PathParam("csid") String csid, String xmlPayload) { PoxPayloadOut result = null; try { diff --git a/services/common/src/main/java/org/collectionspace/services/common/context/MultipartServiceContext.java b/services/common/src/main/java/org/collectionspace/services/common/context/MultipartServiceContext.java index cf7c4ce99..694f5c91f 100644 --- a/services/common/src/main/java/org/collectionspace/services/common/context/MultipartServiceContext.java +++ b/services/common/src/main/java/org/collectionspace/services/common/context/MultipartServiceContext.java @@ -26,6 +26,7 @@ package org.collectionspace.services.common.context; import java.io.IOException; import java.io.InputStream; +import org.collectionspace.services.client.PayloadOutputPart; import org.collectionspace.services.client.PoxPayloadIn; import org.collectionspace.services.client.PoxPayloadOut; import org.dom4j.Element; @@ -110,5 +111,7 @@ public interface MultipartServiceContext */ public void addOutputPart(String label, Element doc, String contentType) throws Exception; + public void addOutputPart(PayloadOutputPart outputPart) throws Exception; + public void setRespositoryWorkspaceName(String workspaceName); } diff --git a/services/common/src/main/java/org/collectionspace/services/common/context/MultipartServiceContextImpl.java b/services/common/src/main/java/org/collectionspace/services/common/context/MultipartServiceContextImpl.java index 67dc80d73..53e1d654d 100644 --- a/services/common/src/main/java/org/collectionspace/services/common/context/MultipartServiceContextImpl.java +++ b/services/common/src/main/java/org/collectionspace/services/common/context/MultipartServiceContextImpl.java @@ -159,8 +159,17 @@ public class MultipartServiceContextImpl if (logger.isTraceEnabled() == true) { logger.trace("Adding part:" + label + " to " + getOutput().getName() + " document."); - } - } + } + } + + @Override + public void addOutputPart(PayloadOutputPart outputPart) throws Exception { + PayloadOutputPart part = getOutput().addPart(outputPart); + if (logger.isTraceEnabled() == true) { + logger.trace("Adding part:" + part.getLabel() + + " to " + getOutput().getName() + " document."); + } + } /* (non-Javadoc) * @see org.collectionspace.services.common.context.RemoteServiceContextImpl#getLocalContext(java.lang.String) diff --git a/services/common/src/main/java/org/collectionspace/services/common/security/SecurityInterceptor.java b/services/common/src/main/java/org/collectionspace/services/common/security/SecurityInterceptor.java index be23afc4d..455e1d433 100644 --- a/services/common/src/main/java/org/collectionspace/services/common/security/SecurityInterceptor.java +++ b/services/common/src/main/java/org/collectionspace/services/common/security/SecurityInterceptor.java @@ -48,6 +48,7 @@ import org.collectionspace.authentication.AuthN; import org.collectionspace.services.authorization.AuthZ; import org.collectionspace.services.authorization.CSpaceResource; import org.collectionspace.services.authorization.URIResourceImpl; +import org.collectionspace.services.client.workflow.WorkflowClient; import org.collectionspace.services.common.document.JaxbUtils; import org.collectionspace.services.common.storage.jpa.JpaStorageUtils; import org.collectionspace.services.common.security.SecurityUtils; @@ -104,7 +105,26 @@ public class SecurityInterceptor implements PreProcessInterceptor { Response response = Response.status( Response.Status.FORBIDDEN).entity(uriPath + " " + httpMethod).type("text/plain").build(); throw new WebApplicationException(response); + } else { + // + // They passed the first round of security checks, so now let's check to see if they're trying + // to perform a workflow state change and make sure they are allowed to to this. + // + if (uriPath.endsWith(WorkflowClient.SERVICE_PATH_COMPONENT) == true) { + String workflowSubResName = getResourceName(request.getUri()); + res = new URIResourceImpl(workflowSubResName, httpMethod); + if (!authZ.isAccessAllowed(res)) { + logger.error("Access to " + resName + ":" + res.getId() + " is NOT allowed to " + + " user=" + AuthN.get().getUserId()); + Response response = Response.status( + Response.Status.FORBIDDEN).entity(uriPath + " " + httpMethod).type("text/plain").build(); + throw new WebApplicationException(response); + } + } } + // + // We've passed all the checks. Now just log the results + // if (logger.isDebugEnabled()) { logger.debug("Access to " + res.getId() + " is allowed to " + " user=" + AuthN.get().getUserId() + diff --git a/services/common/src/main/java/org/collectionspace/services/common/storage/jpa/JpaStorageUtils.java b/services/common/src/main/java/org/collectionspace/services/common/storage/jpa/JpaStorageUtils.java index 9c7efaf0b..4323b6ac1 100644 --- a/services/common/src/main/java/org/collectionspace/services/common/storage/jpa/JpaStorageUtils.java +++ b/services/common/src/main/java/org/collectionspace/services/common/storage/jpa/JpaStorageUtils.java @@ -67,7 +67,7 @@ public class JpaStorageUtils { /** The Constant CS_PERSISTENCE_UNIT. */ public final static String CS_PERSISTENCE_UNIT = "org.collectionspace.services"; private final static String CS_AUTHZ_PERSISTENCE_UNIT = "org.collectionspace.services.authorization"; - private final static String CS_CURRENT_USER = "0"; + public final static String CS_CURRENT_USER = "0"; /** * getEntity for given id and class @@ -166,11 +166,20 @@ public class JpaStorageUtils { return result; } - //FIXME: This method should be moved to the AccountPermissionDocumemntHandler + //FIXME: REM - This method should probably be moved to the AccountPermissionDocumemntHandler /* * This is a prototype for the /accounts/{csid}/permissions GET service call. */ public static AccountPermission getAccountPermissions(String csid) + throws UnauthorizedException, DocumentNotFoundException { + return getAccountPermissions(csid, null, null); + } + + //FIXME: REM - This method should probably be moved to the AccountPermissionDocumemntHandler + /* + * This is a prototype for the /accounts/{csid}/permissions GET service call. + */ + public static AccountPermission getAccountPermissions(String csid, String currentResource, String permissionResource) throws UnauthorizedException, DocumentNotFoundException { // // Make sure the user asking for this list has the correct @@ -198,8 +207,18 @@ public class JpaStorageUtils { try { StringBuilder queryStrBldr = new StringBuilder("SELECT ar, pr FROM " + AccountRoleRel.class.getName() + " ar, " + PermissionRoleRel.class.getName() + " pr" + - " WHERE ar.roleId = pr.roleId and ar.userId=" + "'" + userId + "'" + - "group by pr.permissionId"); + " WHERE ar.roleId = pr.roleId and ar.userId=" + "'" + userId + "'"); + // + // Filter by the permissionResource param if it is set to something + // + if (permissionResource != null && currentResource != null) { + queryStrBldr.append(" and (pr.permissionResource = " + "'" + currentResource + "'" + + " or pr.permissionResource = " + "'" + permissionResource + "'" + ")"); + } + // + // Add group by clause + // + queryStrBldr.append(" group by pr.permissionId"); emf = getEntityManagerFactory(); em = emf.createEntityManager(); diff --git a/services/common/src/main/java/org/collectionspace/services/common/workflow/service/nuxeo/WorkflowDocumentModelHandler.java b/services/common/src/main/java/org/collectionspace/services/common/workflow/service/nuxeo/WorkflowDocumentModelHandler.java index 61a39bbfe..8e39057d4 100644 --- a/services/common/src/main/java/org/collectionspace/services/common/workflow/service/nuxeo/WorkflowDocumentModelHandler.java +++ b/services/common/src/main/java/org/collectionspace/services/common/workflow/service/nuxeo/WorkflowDocumentModelHandler.java @@ -43,14 +43,14 @@ import org.collectionspace.services.common.service.ObjectPartType; import org.collectionspace.services.common.workflow.jaxb.WorkflowJAXBSchema; import org.collectionspace.services.nuxeo.client.java.DocHandlerBase; import org.collectionspace.services.nuxeo.client.java.RemoteDocumentModelHandlerImpl; -import org.collectionspace.services.workflow.WorkflowsCommon; +import org.collectionspace.services.workflow.WorkflowCommon; import org.dom4j.Element; import org.nuxeo.ecm.core.api.DocumentModel; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class WorkflowDocumentModelHandler - extends DocHandlerBase { + extends DocHandlerBase { /** The logger. */ private final Logger logger = LoggerFactory.getLogger(WorkflowDocumentModelHandler.class); @@ -120,7 +120,7 @@ public class WorkflowDocumentModelHandler ObjectPartType partMeta, Action action, ServiceContext ctx) throws Exception { - WorkflowsCommon workflowsCommon = (WorkflowsCommon)part.getBody(); + WorkflowCommon workflowsCommon = (WorkflowCommon)part.getBody(); docModel.followTransition(getTransitionFromState(workflowsCommon.getCurrentLifeCycleState())); } diff --git a/services/common/src/main/java/org/collectionspace/services/nuxeo/client/java/RemoteDocumentModelHandlerImpl.java b/services/common/src/main/java/org/collectionspace/services/nuxeo/client/java/RemoteDocumentModelHandlerImpl.java index 50db6a4d7..a5f302f2d 100644 --- a/services/common/src/main/java/org/collectionspace/services/nuxeo/client/java/RemoteDocumentModelHandlerImpl.java +++ b/services/common/src/main/java/org/collectionspace/services/nuxeo/client/java/RemoteDocumentModelHandlerImpl.java @@ -35,19 +35,26 @@ import javax.ws.rs.WebApplicationException; import javax.ws.rs.core.MediaType; import javax.ws.rs.core.Response; +import org.collectionspace.services.authorization.AccountPermission; import org.collectionspace.services.jaxb.AbstractCommonList; import org.collectionspace.services.client.PayloadInputPart; +import org.collectionspace.services.client.PayloadOutputPart; import org.collectionspace.services.client.PoxPayloadIn; import org.collectionspace.services.client.PoxPayloadOut; +import org.collectionspace.services.client.workflow.WorkflowClient; import org.collectionspace.services.common.authorityref.AuthorityRefList; +import org.collectionspace.services.common.authorization_mgt.AuthorizationCommon; import org.collectionspace.services.common.context.MultipartServiceContext; import org.collectionspace.services.common.context.ServiceContext; import org.collectionspace.services.common.document.BadRequestException; +import org.collectionspace.services.common.document.DocumentNotFoundException; import org.collectionspace.services.common.document.DocumentUtils; import org.collectionspace.services.common.document.DocumentWrapper; import org.collectionspace.services.common.document.DocumentFilter; import org.collectionspace.services.common.document.DocumentHandler.Action; +import org.collectionspace.services.common.security.UnauthorizedException; import org.collectionspace.services.common.service.ObjectPartType; +import org.collectionspace.services.common.storage.jpa.JpaStorageUtils; import org.collectionspace.services.common.vocabulary.RefNameUtils; import org.dom4j.Element; @@ -182,6 +189,16 @@ public abstract class RemoteDocumentModelHandlerImpl Map unQObjectProperties = extractPart(docModel, schema, partMeta); addOutputPart(unQObjectProperties, schema, partMeta); } + addAccountPermissionsPart(); + } + + private void addAccountPermissionsPart() throws Exception { + MultipartServiceContext ctx = (MultipartServiceContext) getServiceContext(); + String currentServiceName = ctx.getServiceName(); + AccountPermission accountPermission = JpaStorageUtils.getAccountPermissions(JpaStorageUtils.CS_CURRENT_USER, + currentServiceName, "/" + currentServiceName + WorkflowClient.SERVICE_AUTHZ_SUFFIX); + PayloadOutputPart accountPermissionPart = new PayloadOutputPart("account_permission", accountPermission); + ctx.addOutputPart(accountPermissionPart); } /* (non-Javadoc) diff --git a/services/dimension/client/src/test/java/org/collectionspace/services/client/test/DimensionServiceTest.java b/services/dimension/client/src/test/java/org/collectionspace/services/client/test/DimensionServiceTest.java index a33a5d7f3..9437bd290 100644 --- a/services/dimension/client/src/test/java/org/collectionspace/services/client/test/DimensionServiceTest.java +++ b/services/dimension/client/src/test/java/org/collectionspace/services/client/test/DimensionServiceTest.java @@ -34,9 +34,11 @@ import org.collectionspace.services.client.PayloadInputPart; import org.collectionspace.services.client.PayloadOutputPart; import org.collectionspace.services.client.PoxPayloadIn; import org.collectionspace.services.client.PoxPayloadOut; +import org.collectionspace.services.client.workflow.WorkflowClient; import org.collectionspace.services.dimension.DimensionsCommon; import org.collectionspace.services.dimension.DimensionsCommonList; import org.collectionspace.services.jaxb.AbstractCommonList; +import org.collectionspace.services.workflow.WorkflowCommon; import org.jboss.resteasy.client.ClientResponse; @@ -755,4 +757,100 @@ public class DimensionServiceTest extends AbstractServiceTestImpl { return multipart; } + + @Override + protected String createWorkflowTarget(String testName) throws Exception { + String result = null; + + result = createTestObject(testName); + + return result; + } + + @Override + protected String createTestObject(String testName) throws Exception { + String result = null; + + DimensionClient client = new DimensionClient(); + String identifier = createIdentifier(); + PoxPayloadOut multipart = createDimensionInstance(client.getCommonPartName(), + identifier); + ClientResponse res = client.create(multipart); + + int statusCode = res.getStatus(); + Assert.assertEquals(statusCode, STATUS_CREATED); + + result = extractId(res); + allResourceIdsCreated.add(result); + + return result; + } + + /* + * This test assumes that no objects exist yet. + * + * http://localhost:8180/cspace-services/intakes?wf_deleted=false + */ + @Test(dataProvider = "testName", dataProviderClass = AbstractServiceTestImpl.class, dependsOnMethods = {"update"}) + public void readWorkflowList(String testName) throws Exception { + // + // Create 3 new objects + // + final int OBJECTS_TOTAL = 3; + for (int i = 0; i < OBJECTS_TOTAL; i++) { + this.createWorkflowTarget(testName); + } + // + // Mark one as soft deleted + // + int currentTotal = allResourceIdsCreated.size(); + String csid = allResourceIdsCreated.get(currentTotal - 1); //0-based index to get the last one added + this.setupUpdate(); + this.updateLifeCycleState(testName, csid, WorkflowClient.WORKFLOWSTATE_DELETED); + // + // Read the list back. The deleted item should not be in the list + // +// int updatedTotal = readIncludeDeleted(testName, Boolean.FALSE); +// Assert.assertEquals(updatedTotal, currentTotal - 1, "Deleted items seem to be returned in list results."); + } + + protected void updateLifeCycleState(String testName, String resourceId, String lifeCycleState) throws Exception { + // + // Read the existing object + // + DimensionClient client = new DimensionClient(); + ClientResponse res = client.getWorkflow(resourceId); + assertStatusCode(res, testName); + logger.debug("Got object to update life cycle state with ID: " + resourceId); + PoxPayloadIn input = new PoxPayloadIn(res.getEntity()); + WorkflowCommon workflowCommons = (WorkflowCommon) extractPart(input, WorkflowClient.SERVICE_COMMONPART_NAME, WorkflowCommon.class); + Assert.assertNotNull(workflowCommons); + // + // Mark it for a soft delete. + // + logger.debug("Current workflow state:" + objectAsXmlString(workflowCommons, WorkflowCommon.class)); + workflowCommons.setCurrentLifeCycleState(lifeCycleState); + PoxPayloadOut output = new PoxPayloadOut(WorkflowClient.SERVICE_PAYLOAD_NAME); + PayloadOutputPart commonPart = output.addPart(workflowCommons, MediaType.APPLICATION_XML_TYPE); + commonPart.setLabel(WorkflowClient.SERVICE_COMMONPART_NAME); + // + // Perform the update + // + res = client.updateWorkflow(resourceId, output); + assertStatusCode(res, testName); + input = new PoxPayloadIn(res.getEntity()); + WorkflowCommon updatedWorkflowCommons = (WorkflowCommon) extractPart(input, WorkflowClient.SERVICE_COMMONPART_NAME, WorkflowCommon.class); + Assert.assertNotNull(updatedWorkflowCommons); + // + // Read the updated object and make sure it was updated correctly. + // + res = client.getWorkflow(resourceId); + assertStatusCode(res, testName); + logger.debug("Got workflow state of updated object with ID: " + resourceId); + input = new PoxPayloadIn(res.getEntity()); + updatedWorkflowCommons = (WorkflowCommon) extractPart(input, WorkflowClient.SERVICE_COMMONPART_NAME, WorkflowCommon.class); + Assert.assertNotNull(workflowCommons); + Assert.assertEquals(updatedWorkflowCommons.getCurrentLifeCycleState(), lifeCycleState); + } + } diff --git a/services/jaxb/src/main/resources/workflows_common.xsd b/services/jaxb/src/main/resources/workflow_common.xsd similarity index 97% rename from services/jaxb/src/main/resources/workflows_common.xsd rename to services/jaxb/src/main/resources/workflow_common.xsd index 35655b7a1..3ba78d7d4 100644 --- a/services/jaxb/src/main/resources/workflows_common.xsd +++ b/services/jaxb/src/main/resources/workflow_common.xsd @@ -24,7 +24,7 @@ See http://weblogs.java.net/blog/kohsuke/archive/2006/03/why_does_jaxb_p.html --> - + diff --git a/services/workflow/3rdparty/nuxeo-platform-cs-workflow/src/main/resources/OSGI-INF/core-types-contrib.xml b/services/workflow/3rdparty/nuxeo-platform-cs-workflow/src/main/resources/OSGI-INF/core-types-contrib.xml index 8b34a1e80..c8de9573c 100644 --- a/services/workflow/3rdparty/nuxeo-platform-cs-workflow/src/main/resources/OSGI-INF/core-types-contrib.xml +++ b/services/workflow/3rdparty/nuxeo-platform-cs-workflow/src/main/resources/OSGI-INF/core-types-contrib.xml @@ -1,6 +1,6 @@ - + diff --git a/services/workflow/3rdparty/nuxeo-platform-cs-workflow/src/main/resources/schemas/workflows_common.xsd b/services/workflow/3rdparty/nuxeo-platform-cs-workflow/src/main/resources/schemas/workflow_common.xsd similarity index 100% rename from services/workflow/3rdparty/nuxeo-platform-cs-workflow/src/main/resources/schemas/workflows_common.xsd rename to services/workflow/3rdparty/nuxeo-platform-cs-workflow/src/main/resources/schemas/workflow_common.xsd diff --git a/services/workflow/client/src/test/java/org/collectionspace/services/client/test/WorkflowServiceTest.java b/services/workflow/client/src/test/java/org/collectionspace/services/client/test/WorkflowServiceTest.java index f1e7a91c3..30f23585b 100644 --- a/services/workflow/client/src/test/java/org/collectionspace/services/client/test/WorkflowServiceTest.java +++ b/services/workflow/client/src/test/java/org/collectionspace/services/client/test/WorkflowServiceTest.java @@ -33,7 +33,7 @@ import org.collectionspace.services.client.PoxPayloadIn; import org.collectionspace.services.client.PoxPayloadOut; import org.collectionspace.services.jaxb.AbstractCommonList; -import org.collectionspace.services.workflow.WorkflowsCommon; +import org.collectionspace.services.workflow.WorkflowCommon; import org.collectionspace.services.client.DimensionClient; import org.collectionspace.services.client.workflow.WorkflowClient; import org.collectionspace.services.dimension.DimensionsCommon; @@ -98,7 +98,21 @@ public class WorkflowServiceTest extends AbstractServiceTestImpl { /* * Create a Dimension instance to use as our test target. */ - public void createTestObject(String testName) throws Exception { + @Override + protected String createWorkflowTarget(String testName) throws Exception { + String result = null; + + result = createTestObject(testName); + + return result; + } + + /* + * Create a Dimension instance to use as our test target. + */ + protected String createTestObject(String testName) throws Exception { + String result = null; + logger.debug(testBanner(testName, CLASS_NAME)); setupCreate(); DimensionClient client = new DimensionClient(); @@ -109,7 +123,10 @@ public class WorkflowServiceTest extends AbstractServiceTestImpl { knownResourceId = extractId(res); // Store the ID returned from the first resource created for additional tests below. logger.debug(testName + ": knownResourceId=" + knownResourceId); } - allResourceIdsCreated.add(extractId(res)); // Store the IDs from every resource created by tests so they can be deleted after tests have been run. + result = extractId(res); + allResourceIdsCreated.add(result); // Store the IDs from every resource created by tests so they can be deleted after tests have been run. + + return result; } @Override @@ -126,7 +143,7 @@ public class WorkflowServiceTest extends AbstractServiceTestImpl { ClientResponse res = client.getWorkflow(knownResourceId); assertStatusCode(res, testName); PoxPayloadIn input = new PoxPayloadIn(res.getEntity()); - WorkflowsCommon workflowsCommon = (WorkflowsCommon) extractPart(input, WorkflowClient.SERVICE_COMMONPART_NAME, WorkflowsCommon.class); + WorkflowCommon workflowsCommon = (WorkflowCommon) extractPart(input, WorkflowClient.SERVICE_COMMONPART_NAME, WorkflowCommon.class); if (logger.isDebugEnabled() == true) { logger.debug("Workflow payload is: " + input.getXmlPayload()); } @@ -162,44 +179,6 @@ public class WorkflowServiceTest extends AbstractServiceTestImpl { updateLifeCycleState(testName, knownResourceId, WorkflowClient.WORKFLOWSTATE_APPROVED); } - private void updateLifeCycleState(String testName, String resourceId, String lifeCycleState) throws Exception { - // - // Read the existing object - // - DimensionClient client = new DimensionClient(); - ClientResponse res = client.getWorkflow(resourceId); - assertStatusCode(res, testName); - logger.debug("Got object to update life cycle state with ID: " + resourceId); - PoxPayloadIn input = new PoxPayloadIn(res.getEntity()); - WorkflowsCommon workflowCommons = (WorkflowsCommon) extractPart(input, WorkflowClient.SERVICE_COMMONPART_NAME, WorkflowsCommon.class); - Assert.assertNotNull(workflowCommons); - // - // Mark it for a soft delete. - // - logger.debug("Current workflow state:" + objectAsXmlString(workflowCommons, WorkflowsCommon.class)); - workflowCommons.setCurrentLifeCycleState(lifeCycleState); - PoxPayloadOut output = new PoxPayloadOut(WorkflowClient.SERVICE_PAYLOAD_NAME); - PayloadOutputPart commonPart = output.addPart(workflowCommons, MediaType.APPLICATION_XML_TYPE); - commonPart.setLabel(WorkflowClient.SERVICE_COMMONPART_NAME); - // - // Perform the update - // - res = client.updateWorkflow(resourceId, output); - assertStatusCode(res, testName); - input = new PoxPayloadIn(res.getEntity()); - WorkflowsCommon updatedWorkflowCommons = (WorkflowsCommon) extractPart(input, WorkflowClient.SERVICE_COMMONPART_NAME, WorkflowsCommon.class); - Assert.assertNotNull(updatedWorkflowCommons); - // - // Read the updated object and make sure it was updated correctly. - // - res = client.getWorkflow(resourceId); - assertStatusCode(res, testName); - logger.debug("Got workflow state of updated object with ID: " + resourceId); - input = new PoxPayloadIn(res.getEntity()); - updatedWorkflowCommons = (WorkflowsCommon) extractPart(input, WorkflowClient.SERVICE_COMMONPART_NAME, WorkflowsCommon.class); - Assert.assertNotNull(workflowCommons); - Assert.assertEquals(updatedWorkflowCommons.getCurrentLifeCycleState(), lifeCycleState); - } @Override // @Test(dataProvider = "testName", dataProviderClass = AbstractServiceTestImpl.class, dependsOnMethods = {"update", "testSubmitRequest"}) @@ -348,33 +327,42 @@ public class WorkflowServiceTest extends AbstractServiceTestImpl { return result; } + @Override + public void readList(String testName) throws Exception { + } + /* * This test assumes that no objects exist yet. * * http://localhost:8180/cspace-services/intakes?wf_deleted=false */ - @Override @Test(dataProvider = "testName", dataProviderClass = AbstractServiceTestImpl.class, dependsOnMethods = {"update"}) - public void readList(String testName) throws Exception { + public void readWorkflow(String testName) throws Exception { + // + // Get the total count of non-deleted existing records + // + int existingRecords = readIncludeDeleted(testName, Boolean.FALSE); + // // Create 3 new objects // - final int OBJECTS_TOTAL = 3; - for (int i = 0; i < OBJECTS_TOTAL; i++) { - this.createTestObject(testName); + final int OBJECTS_TO_CREATE = 3; + for (int i = 0; i < OBJECTS_TO_CREATE; i++) { + this.createWorkflowTarget(testName); } + // // Mark one as soft deleted // - int currentTotal = allResourceIdsCreated.size(); - String csid = allResourceIdsCreated.get(currentTotal - 1); //0-based index to get the last one added + int existingTestCreated = allResourceIdsCreated.size(); // assumption is that no other test created records were soft deleted + String csid = allResourceIdsCreated.get(existingTestCreated - 1); //0-based index to get the last one added this.setupUpdate(); this.updateLifeCycleState(testName, csid, WorkflowClient.WORKFLOWSTATE_DELETED); // - // Read the list back. The deleted item should not be in the list + // Read the list of existing non-deleted records // int updatedTotal = readIncludeDeleted(testName, Boolean.FALSE); - Assert.assertEquals(updatedTotal, currentTotal - 1, "Deleted items seem to be returned in list results."); + Assert.assertEquals(updatedTotal, existingRecords + OBJECTS_TO_CREATE - 1, "Deleted items seem to be returned in list results."); } @Override diff --git a/services/workflow/service/src/main/java/org/collectionspace/services/workflow/nuxeo/WorkflowValidatorHandler.java b/services/workflow/service/src/main/java/org/collectionspace/services/workflow/nuxeo/WorkflowValidatorHandler.java index 8ba0fe378..b77010381 100644 --- a/services/workflow/service/src/main/java/org/collectionspace/services/workflow/nuxeo/WorkflowValidatorHandler.java +++ b/services/workflow/service/src/main/java/org/collectionspace/services/workflow/nuxeo/WorkflowValidatorHandler.java @@ -4,7 +4,7 @@ import org.collectionspace.services.client.PoxPayloadIn; import org.collectionspace.services.client.PoxPayloadOut; import org.collectionspace.services.common.document.InvalidDocumentException; import org.collectionspace.services.common.document.ValidatorHandlerImpl; -import org.collectionspace.services.workflow.WorkflowsCommon; +import org.collectionspace.services.workflow.WorkflowCommon; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -20,13 +20,13 @@ public class WorkflowValidatorHandler extends ValidatorHandlerImpl getCommonPartClass() { - return WorkflowsCommon.class; + return WorkflowCommon.class; } @Override protected void handleCreate() throws InvalidDocumentException { try { - WorkflowsCommon intakesCommon = (WorkflowsCommon)getCommonPart(); + WorkflowCommon intakesCommon = (WorkflowCommon)getCommonPart(); assert(intakesCommon != null); } catch (AssertionError e) { if (logger.isErrorEnabled() == true) { -- 2.47.3