From 1875756bf22ba34de004eecc9669bb704db055c5 Mon Sep 17 00:00:00 2001 From: remillet Date: Fri, 24 Mar 2017 17:32:59 -0700 Subject: [PATCH] CSPACE-5985:Adding support for authorization checks for invoking batch jobs. --- .../7.10-HF17/config/vcsconfig.sql.txt | 12 + services/IntegrationTests/pom.xml | 5 + .../xmlreplay/ServiceResult.java | 1 + .../IntegrationTests/xmlreplay/XmlReplay.java | 11 +- .../xmlreplay/batch/batch-create-Template.xml | 37 ++ .../batch/batch-create-permissions.xml | 27 ++ .../batch-create-updateobjloc-nocontext.xml | 28 +- .../xmlreplay/batch/batch-invoke-Template.xml | 24 ++ ...nvContext.xml => batch-invoke-gargabe.xml} | 0 ...xtList.xml => batch-invoke-list-empty.xml} | 0 ...vContextList.xml => batch-invoke-list.xml} | 0 .../batch/batch-invoke-nocontext.xml | 7 + ...InvContext.xml => batch-invoke-single.xml} | 0 .../test-data/xmlreplay/batch/batch.xml | 18 +- .../xmlreplay/batch/batch1 - Copy (2).xml | 36 ++ .../xmlreplay/batch/batch1 - Copy (3).xml | 36 ++ .../xmlreplay/batch/batch1 - Copy (4).xml | 36 ++ .../xmlreplay/batch/batch1 - Copy (5).xml | 36 ++ .../test-data/xmlreplay/batch/batch1.xml | 16 - .../test-data/xmlreplay/batch/batch2.xml | 14 + .../xmlreplay/batch/batchInvContextSingle.xml | 10 - .../CollectionSpaceJaxRsApplication.java | 2 +- .../src/main/resources/accounts_common.xsd | 1 + .../services/account/AccountResource.java | 82 ++++- .../storage/AccountDocumentHandler.java | 1 + .../services/client/RoleClient.java | 55 ++- .../PermissionRoleSubResource.java | 3 - .../services/authorization/RoleResource.java | 1 - .../storage/RoleDocumentHandler.java | 14 +- .../services/authorization/AuthZ.java | 31 ++ .../schemas/batch_common-template.xml | 40 +++ .../main/resources/schemas/batch_common.xsd | 49 ++- .../src/main/resources/schemas/instance1.xml | 4 + .../services/client/BatchClient.java | 3 +- services/batch/jaxb/pom.xml | 6 +- .../main/resources/batch_common-template.xml | 40 +++ .../jaxb/src/main/resources/batch_common.xsd | 39 +- .../main/resources/batch_common_document.xsd | 63 ++++ .../jaxb/src/main/resources/instance1.xml | 34 ++ services/batch/service/pom.xml | 22 ++ .../batch/AbstractBatchInvocable.java | 20 +- .../services/batch/BatchResource.java | 32 +- .../nuxeo/BatchDocumentModelHandler.java | 335 +++++++++++++----- .../batch/nuxeo/BatchValidatorHandler.java | 116 +++++- .../nuxeo/CreateAndLinkLoanOutBatchJob.java | 112 +----- .../services/batch/nuxeo/TestBatchJob.java | 20 ++ .../nuxeo/UpdateObjectLocationBatchJob.java | 12 +- .../services/client/PoxPayload.java | 20 +- .../services/common/api/Tools.java | 33 +- .../AbstractCollectionSpaceResourceImpl.java | 11 +- .../common/CollectionSpaceResource.java | 2 +- .../services/common/NuxeoBasedResource.java | 2 +- .../services/common/ResourceMap.java | 2 +- .../services/common/ResourceMapImpl.java | 5 +- .../services/common/ServiceMain.java | 6 +- .../common/authorization_mgt/ActionGroup.java | 200 +++++++++++ .../AuthorizationCommon.java | 18 +- .../context/RemoteServiceContextImpl.java | 16 +- .../services/common/invocable/Invocable.java | 9 +- .../services/common/query}/UriInfoImpl.java | 2 +- .../common/security/SecurityInterceptor.java | 2 +- .../security/UnauthorizedException.java | 6 +- .../storage/jpa/JpaDocumentHandler.java | 6 +- .../java/RemoteDocumentModelHandlerImpl.java | 9 + .../src/main/resources/invocationContext.xsd | 75 ++-- 65 files changed, 1492 insertions(+), 423 deletions(-) create mode 100644 services/IntegrationTests/src/test/resources/test-data/xmlreplay/batch/batch-create-Template.xml create mode 100644 services/IntegrationTests/src/test/resources/test-data/xmlreplay/batch/batch-create-permissions.xml create mode 100644 services/IntegrationTests/src/test/resources/test-data/xmlreplay/batch/batch-invoke-Template.xml rename services/IntegrationTests/src/test/resources/test-data/xmlreplay/batch/{batchBadInvContext.xml => batch-invoke-gargabe.xml} (100%) rename services/IntegrationTests/src/test/resources/test-data/xmlreplay/batch/{batchBadInvContextList.xml => batch-invoke-list-empty.xml} (100%) rename services/IntegrationTests/src/test/resources/test-data/xmlreplay/batch/{batchInvContextList.xml => batch-invoke-list.xml} (100%) create mode 100644 services/IntegrationTests/src/test/resources/test-data/xmlreplay/batch/batch-invoke-nocontext.xml rename services/IntegrationTests/src/test/resources/test-data/xmlreplay/batch/{batch1InvContext.xml => batch-invoke-single.xml} (100%) create mode 100644 services/IntegrationTests/src/test/resources/test-data/xmlreplay/batch/batch1 - Copy (2).xml create mode 100644 services/IntegrationTests/src/test/resources/test-data/xmlreplay/batch/batch1 - Copy (3).xml create mode 100644 services/IntegrationTests/src/test/resources/test-data/xmlreplay/batch/batch1 - Copy (4).xml create mode 100644 services/IntegrationTests/src/test/resources/test-data/xmlreplay/batch/batch1 - Copy (5).xml delete mode 100644 services/IntegrationTests/src/test/resources/test-data/xmlreplay/batch/batch1.xml create mode 100644 services/IntegrationTests/src/test/resources/test-data/xmlreplay/batch/batch2.xml delete mode 100644 services/IntegrationTests/src/test/resources/test-data/xmlreplay/batch/batchInvContextSingle.xml create mode 100644 services/batch/3rdparty/nuxeo-platform-cs-batch/src/main/resources/schemas/batch_common-template.xml create mode 100644 services/batch/3rdparty/nuxeo-platform-cs-batch/src/main/resources/schemas/instance1.xml create mode 100644 services/batch/jaxb/src/main/resources/batch_common-template.xml create mode 100644 services/batch/jaxb/src/main/resources/batch_common_document.xsd create mode 100644 services/batch/jaxb/src/main/resources/instance1.xml create mode 100644 services/batch/service/src/main/java/org/collectionspace/services/batch/nuxeo/TestBatchJob.java create mode 100644 services/common/src/main/java/org/collectionspace/services/common/authorization_mgt/ActionGroup.java rename services/{batch/service/src/main/java/org/collectionspace/services/batch => common/src/main/java/org/collectionspace/services/common/query}/UriInfoImpl.java (99%) diff --git a/3rdparty/nuxeo/nuxeo-server/7.10-HF17/config/vcsconfig.sql.txt b/3rdparty/nuxeo/nuxeo-server/7.10-HF17/config/vcsconfig.sql.txt index 275fc1d65..aef9cbb49 100644 --- a/3rdparty/nuxeo/nuxeo-server/7.10-HF17/config/vcsconfig.sql.txt +++ b/3rdparty/nuxeo/nuxeo-server/7.10-HF17/config/vcsconfig.sql.txt @@ -1,6 +1,7 @@ # # A place to modify the Nuxeo database with SQL statements. # See https://doc.nuxeo.com/display/ADMINDOC/VCS+Configuration#VCSConfiguration-DatabaseCreationOption +# or https://doc.nuxeo.com/nxdoc/repository-configuration/#page-title # #CATEGORY: afterTableCreation @@ -15,3 +16,14 @@ SELECT constraint_name FROM information_schema.constraint_column_usage WHERE tab #IF: emptyResult ALTER TABLE reports_common add CONSTRAINT reportname_unique UNIQUE (name); + +# +# Add a unique constraint to the batch job 'name' column of the batch_common table. +# +LOG.INFO Adding a unique constraint to the batch 'name' column of the batch_common table + +#TEST: +SELECT constraint_name FROM information_schema.constraint_column_usage WHERE table_name = 'batch_common' AND constraint_name = 'batchname_unique'; + +#IF: emptyResult +ALTER TABLE batch_common add CONSTRAINT batchname_unique UNIQUE (name); diff --git a/services/IntegrationTests/pom.xml b/services/IntegrationTests/pom.xml index 4b58877f0..0c8bf9a1b 100644 --- a/services/IntegrationTests/pom.xml +++ b/services/IntegrationTests/pom.xml @@ -42,6 +42,11 @@ org.collectionspace.services.collectionobject.jaxb ${project.version} + + org.collectionspace.services + org.collectionspace.services.batch.jaxb + ${project.version} + org.collectionspace.services org.collectionspace.services.collectionobject.client diff --git a/services/IntegrationTests/src/main/java/org/collectionspace/services/IntegrationTests/xmlreplay/ServiceResult.java b/services/IntegrationTests/src/main/java/org/collectionspace/services/IntegrationTests/xmlreplay/ServiceResult.java index 548a9b4d8..e4e602e02 100644 --- a/services/IntegrationTests/src/main/java/org/collectionspace/services/IntegrationTests/xmlreplay/ServiceResult.java +++ b/services/IntegrationTests/src/main/java/org/collectionspace/services/IntegrationTests/xmlreplay/ServiceResult.java @@ -37,6 +37,7 @@ import java.util.Map; * $LastChangedDate: $ */ public class ServiceResult { + public boolean autoDelete = true; public String testID = ""; public String testGroupID = ""; public String fullURL = ""; diff --git a/services/IntegrationTests/src/main/java/org/collectionspace/services/IntegrationTests/xmlreplay/XmlReplay.java b/services/IntegrationTests/src/main/java/org/collectionspace/services/IntegrationTests/xmlreplay/XmlReplay.java index 2a58de2cc..76217e33b 100644 --- a/services/IntegrationTests/src/main/java/org/collectionspace/services/IntegrationTests/xmlreplay/XmlReplay.java +++ b/services/IntegrationTests/src/main/java/org/collectionspace/services/IntegrationTests/xmlreplay/XmlReplay.java @@ -244,7 +244,7 @@ public class XmlReplay { int deleteFailures = 0; for (ServiceResult pr : serviceResultsMap.values()) { try { - if (Tools.notEmpty(pr.deleteURL)){ + if (pr.autoDelete == true && Tools.notEmpty(pr.deleteURL)){ ServiceResult deleteResult = XmlReplayTransport.doDELETE(pr.deleteURL, pr.auth, pr.testID, "[autodelete:"+logName+"]"); if (deleteResult.gotExpectedResult() == false || deleteResult.responseCode != 200) { reattemptList.put(REATTEMPT_KEY + deleteFailures++, pr); // We need to try again after our dependents have been deleted. cow() @@ -564,6 +564,14 @@ public class XmlReplay { try { testElementIndex++; String testID = testNode.valueOf("@ID"); + // + // Figure out if we will auto delete resources + boolean autoDelete = param_autoDeletePOSTS; + String autoDeleteValue = testNode.valueOf("@autoDeletePOSTS"); + if (autoDeleteValue != null && !autoDeleteValue.trim().isEmpty()) { + autoDelete = Boolean.valueOf(autoDeleteValue).booleanValue(); + } + String testIDLabel = Tools.notEmpty(testID) ? (testGroupID+'.'+testID) : (testGroupID+'.'+testElementIndex); String method = testNode.valueOf("method"); String contentType = testNode.valueOf("contentType"); @@ -637,6 +645,7 @@ public class XmlReplay { vars = parts.varsList.get(0); } serviceResult = XmlReplayTransport.doPOST_PUTFromXML(parts.responseFilename, vars, protoHostPort, uri, method, contentType, evalStruct, authForTest, testIDLabel); + serviceResult.autoDelete = autoDelete; if (vars!=null) { serviceResult.addVars(vars); } diff --git a/services/IntegrationTests/src/test/resources/test-data/xmlreplay/batch/batch-create-Template.xml b/services/IntegrationTests/src/test/resources/test-data/xmlreplay/batch/batch-create-Template.xml new file mode 100644 index 000000000..00cbca7d9 --- /dev/null +++ b/services/IntegrationTests/src/test/resources/test-data/xmlreplay/batch/batch-create-Template.xml @@ -0,0 +1,37 @@ + + + name0 + notes0 + + CollectionObject + Intake + + + TENANT_READER + TENANT_ADMINISTRATOR + + + + blobs + CRUL + + + media + CRUL + + + concepts + RL + + + collectionobjects + RL + + + false + false + false + false + false + className0 + diff --git a/services/IntegrationTests/src/test/resources/test-data/xmlreplay/batch/batch-create-permissions.xml b/services/IntegrationTests/src/test/resources/test-data/xmlreplay/batch/batch-create-permissions.xml new file mode 100644 index 000000000..1ff6409d2 --- /dev/null +++ b/services/IntegrationTests/src/test/resources/test-data/xmlreplay/batch/batch-create-permissions.xml @@ -0,0 +1,27 @@ + + + + batch-create-permissions + A batch resource for testing the Batch service's permission enforcement before invoking batch jobs. + + + blobs + CRUL + + + media + CRUL + + + concepts + RL + + + collectionobjects + RL + + + true + org.collectionspace.services.batch.nuxeo.TestBatchJob + + diff --git a/services/IntegrationTests/src/test/resources/test-data/xmlreplay/batch/batch-create-updateobjloc-nocontext.xml b/services/IntegrationTests/src/test/resources/test-data/xmlreplay/batch/batch-create-updateobjloc-nocontext.xml index 8f0cfd627..ea4154928 100644 --- a/services/IntegrationTests/src/test/resources/test-data/xmlreplay/batch/batch-create-updateobjloc-nocontext.xml +++ b/services/IntegrationTests/src/test/resources/test-data/xmlreplay/batch/batch-create-updateobjloc-nocontext.xml @@ -2,16 +2,40 @@ + xmlns:ns3="http://collectionspace.org/services/jaxb" + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://collectionspace.org/services/batch file:batch_common.xsd"> TestUpdateObjectLocationBatchJob CollectionObject + + TENANT_READER + TENANT_ADMINISTRATOR + + + + blobs + CRUL + + + media + CRUL + + + concepts + RL + + + collectionobjects + RL + + true true true true false - org.collectionspace.services.batch.nuxeo.UpdateObjectLocationBatchJob + org.collectionspace.services.batch.nuxeo.TestBatchJob diff --git a/services/IntegrationTests/src/test/resources/test-data/xmlreplay/batch/batch-invoke-Template.xml b/services/IntegrationTests/src/test/resources/test-data/xmlreplay/batch/batch-invoke-Template.xml new file mode 100644 index 000000000..743d38e1d --- /dev/null +++ b/services/IntegrationTests/src/test/resources/test-data/xmlreplay/batch/batch-invoke-Template.xml @@ -0,0 +1,24 @@ + + + + mode0 + updateCoreValues0 + docType0 + singleCSID0 + groupCSID0 + + csid0 + csid1 + csid2 + + + + + + + + + + diff --git a/services/IntegrationTests/src/test/resources/test-data/xmlreplay/batch/batchBadInvContext.xml b/services/IntegrationTests/src/test/resources/test-data/xmlreplay/batch/batch-invoke-gargabe.xml similarity index 100% rename from services/IntegrationTests/src/test/resources/test-data/xmlreplay/batch/batchBadInvContext.xml rename to services/IntegrationTests/src/test/resources/test-data/xmlreplay/batch/batch-invoke-gargabe.xml diff --git a/services/IntegrationTests/src/test/resources/test-data/xmlreplay/batch/batchBadInvContextList.xml b/services/IntegrationTests/src/test/resources/test-data/xmlreplay/batch/batch-invoke-list-empty.xml similarity index 100% rename from services/IntegrationTests/src/test/resources/test-data/xmlreplay/batch/batchBadInvContextList.xml rename to services/IntegrationTests/src/test/resources/test-data/xmlreplay/batch/batch-invoke-list-empty.xml diff --git a/services/IntegrationTests/src/test/resources/test-data/xmlreplay/batch/batchInvContextList.xml b/services/IntegrationTests/src/test/resources/test-data/xmlreplay/batch/batch-invoke-list.xml similarity index 100% rename from services/IntegrationTests/src/test/resources/test-data/xmlreplay/batch/batchInvContextList.xml rename to services/IntegrationTests/src/test/resources/test-data/xmlreplay/batch/batch-invoke-list.xml diff --git a/services/IntegrationTests/src/test/resources/test-data/xmlreplay/batch/batch-invoke-nocontext.xml b/services/IntegrationTests/src/test/resources/test-data/xmlreplay/batch/batch-invoke-nocontext.xml new file mode 100644 index 000000000..340e4d989 --- /dev/null +++ b/services/IntegrationTests/src/test/resources/test-data/xmlreplay/batch/batch-invoke-nocontext.xml @@ -0,0 +1,7 @@ + + + + nocontexty + diff --git a/services/IntegrationTests/src/test/resources/test-data/xmlreplay/batch/batch1InvContext.xml b/services/IntegrationTests/src/test/resources/test-data/xmlreplay/batch/batch-invoke-single.xml similarity index 100% rename from services/IntegrationTests/src/test/resources/test-data/xmlreplay/batch/batch1InvContext.xml rename to services/IntegrationTests/src/test/resources/test-data/xmlreplay/batch/batch-invoke-single.xml diff --git a/services/IntegrationTests/src/test/resources/test-data/xmlreplay/batch/batch.xml b/services/IntegrationTests/src/test/resources/test-data/xmlreplay/batch/batch.xml index 59981205a..c7d195cc4 100644 --- a/services/IntegrationTests/src/test/resources/test-data/xmlreplay/batch/batch.xml +++ b/services/IntegrationTests/src/test/resources/test-data/xmlreplay/batch/batch.xml @@ -3,7 +3,23 @@ YWRtaW5AY29yZS5jb2xsZWN0aW9uc3BhY2Uub3JnOkFkbWluaXN0cmF0b3I= + cmVhZGVyQGNvcmUuY29sbGVjdGlvbnNwYWNlLm9yZzpyZWFkZXI= + YWRtaW5AY29sbGVjdGlvbnNwYWNlLm9yZzpBZG1pbmlzdHJhdG9y + + + + POST + /cspace-services/batch/ + batch/batch-create-permissions.xml + + + POST + /cspace-services/batch/${createBatchPermissions.CSID} + batch/batch-invoke-nocontext.xml + + + POST @@ -13,7 +29,7 @@ POST /cspace-services/batch/ - batch/batch1.xml + batch/batch2.xml POST diff --git a/services/IntegrationTests/src/test/resources/test-data/xmlreplay/batch/batch1 - Copy (2).xml b/services/IntegrationTests/src/test/resources/test-data/xmlreplay/batch/batch1 - Copy (2).xml new file mode 100644 index 000000000..5864169f3 --- /dev/null +++ b/services/IntegrationTests/src/test/resources/test-data/xmlreplay/batch/batch1 - Copy (2).xml @@ -0,0 +1,36 @@ + + + + TestBatchJob + A benign batch job for testing the Batch service. + + TENANT_READER + TENANT_ADMINISTRATOR + + + + blobs + CRUL + + + media + CRUL + + + concepts + RL + + + collectionobjects + RL + + + false + false + false + false + false + org.collectionspace.services.batch.nuxeo.TestBatchJob + + diff --git a/services/IntegrationTests/src/test/resources/test-data/xmlreplay/batch/batch1 - Copy (3).xml b/services/IntegrationTests/src/test/resources/test-data/xmlreplay/batch/batch1 - Copy (3).xml new file mode 100644 index 000000000..5864169f3 --- /dev/null +++ b/services/IntegrationTests/src/test/resources/test-data/xmlreplay/batch/batch1 - Copy (3).xml @@ -0,0 +1,36 @@ + + + + TestBatchJob + A benign batch job for testing the Batch service. + + TENANT_READER + TENANT_ADMINISTRATOR + + + + blobs + CRUL + + + media + CRUL + + + concepts + RL + + + collectionobjects + RL + + + false + false + false + false + false + org.collectionspace.services.batch.nuxeo.TestBatchJob + + diff --git a/services/IntegrationTests/src/test/resources/test-data/xmlreplay/batch/batch1 - Copy (4).xml b/services/IntegrationTests/src/test/resources/test-data/xmlreplay/batch/batch1 - Copy (4).xml new file mode 100644 index 000000000..5864169f3 --- /dev/null +++ b/services/IntegrationTests/src/test/resources/test-data/xmlreplay/batch/batch1 - Copy (4).xml @@ -0,0 +1,36 @@ + + + + TestBatchJob + A benign batch job for testing the Batch service. + + TENANT_READER + TENANT_ADMINISTRATOR + + + + blobs + CRUL + + + media + CRUL + + + concepts + RL + + + collectionobjects + RL + + + false + false + false + false + false + org.collectionspace.services.batch.nuxeo.TestBatchJob + + diff --git a/services/IntegrationTests/src/test/resources/test-data/xmlreplay/batch/batch1 - Copy (5).xml b/services/IntegrationTests/src/test/resources/test-data/xmlreplay/batch/batch1 - Copy (5).xml new file mode 100644 index 000000000..5864169f3 --- /dev/null +++ b/services/IntegrationTests/src/test/resources/test-data/xmlreplay/batch/batch1 - Copy (5).xml @@ -0,0 +1,36 @@ + + + + TestBatchJob + A benign batch job for testing the Batch service. + + TENANT_READER + TENANT_ADMINISTRATOR + + + + blobs + CRUL + + + media + CRUL + + + concepts + RL + + + collectionobjects + RL + + + false + false + false + false + false + org.collectionspace.services.batch.nuxeo.TestBatchJob + + diff --git a/services/IntegrationTests/src/test/resources/test-data/xmlreplay/batch/batch1.xml b/services/IntegrationTests/src/test/resources/test-data/xmlreplay/batch/batch1.xml deleted file mode 100644 index bc3cc1093..000000000 --- a/services/IntegrationTests/src/test/resources/test-data/xmlreplay/batch/batch1.xml +++ /dev/null @@ -1,16 +0,0 @@ - - - - TestCreateAndLinkLoanOutBatchJob - This should be interesting - - CollectionObject - - true - true - true - org.collectionspace.services.batch.nuxeo.CreateAndLinkLoanOutBatchJob - - diff --git a/services/IntegrationTests/src/test/resources/test-data/xmlreplay/batch/batch2.xml b/services/IntegrationTests/src/test/resources/test-data/xmlreplay/batch/batch2.xml new file mode 100644 index 000000000..577db7e29 --- /dev/null +++ b/services/IntegrationTests/src/test/resources/test-data/xmlreplay/batch/batch2.xml @@ -0,0 +1,14 @@ + + + + TestBatchJob + A second benign batch job for testing the Batch service. + false + false + false + false + false + org.collectionspace.services.batch.nuxeo.TestBatchJob + + diff --git a/services/IntegrationTests/src/test/resources/test-data/xmlreplay/batch/batchInvContextSingle.xml b/services/IntegrationTests/src/test/resources/test-data/xmlreplay/batch/batchInvContextSingle.xml deleted file mode 100644 index dc262c48f..000000000 --- a/services/IntegrationTests/src/test/resources/test-data/xmlreplay/batch/batchInvContextSingle.xml +++ /dev/null @@ -1,10 +0,0 @@ - - - single - CollectionObject - ${CollObj1} - - - diff --git a/services/JaxRsServiceProvider/src/main/java/org/collectionspace/services/jaxrs/CollectionSpaceJaxRsApplication.java b/services/JaxRsServiceProvider/src/main/java/org/collectionspace/services/jaxrs/CollectionSpaceJaxRsApplication.java index d1a5cd6d2..dbc8f68c3 100644 --- a/services/JaxRsServiceProvider/src/main/java/org/collectionspace/services/jaxrs/CollectionSpaceJaxRsApplication.java +++ b/services/JaxRsServiceProvider/src/main/java/org/collectionspace/services/jaxrs/CollectionSpaceJaxRsApplication.java @@ -90,7 +90,7 @@ public class CollectionSpaceJaxRsApplication extends Application private Set singletons = new HashSet(); private Set> empty = new HashSet>(); - private ResourceMap resourceMap = new ResourceMapImpl(); + private ResourceMap resourceMap = new ResourceMapImpl(); private ServletContext servletContext = null; public CollectionSpaceJaxRsApplication() { diff --git a/services/account/jaxb/src/main/resources/accounts_common.xsd b/services/account/jaxb/src/main/resources/accounts_common.xsd index ec2a18d6c..63371d416 100644 --- a/services/account/jaxb/src/main/resources/accounts_common.xsd +++ b/services/account/jaxb/src/main/resources/accounts_common.xsd @@ -229,6 +229,7 @@ + diff --git a/services/account/service/src/main/java/org/collectionspace/services/account/AccountResource.java b/services/account/service/src/main/java/org/collectionspace/services/account/AccountResource.java index bb2e159e2..988468509 100644 --- a/services/account/service/src/main/java/org/collectionspace/services/account/AccountResource.java +++ b/services/account/service/src/main/java/org/collectionspace/services/account/AccountResource.java @@ -27,20 +27,30 @@ import org.collectionspace.services.account.storage.AccountStorageClient; import org.collectionspace.services.authorization.AccountPermission; import org.collectionspace.services.authorization.AccountRole; import org.collectionspace.services.authorization.AccountRoleRel; +import org.collectionspace.services.authorization.RoleValue; import org.collectionspace.services.authorization.SubjectType; import org.collectionspace.services.client.AccountClient; import org.collectionspace.services.client.PayloadOutputPart; +import org.collectionspace.services.client.RoleClient; import org.collectionspace.services.common.SecurityResourceBase; import org.collectionspace.services.common.ServiceMessages; +import org.collectionspace.services.common.UriInfoWrapper; import org.collectionspace.services.common.context.RemoteServiceContextFactory; import org.collectionspace.services.common.context.ServiceContext; import org.collectionspace.services.common.context.ServiceContextFactory; +import org.collectionspace.services.common.query.UriInfoImpl; import org.collectionspace.services.common.storage.StorageClient; import org.collectionspace.services.common.storage.jpa.JpaStorageUtils; import org.jboss.resteasy.util.HttpResponseCodes; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import java.net.URI; +import java.net.URISyntaxException; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + import javax.ws.rs.Consumes; import javax.ws.rs.DELETE; import javax.ws.rs.GET; @@ -51,6 +61,7 @@ import javax.ws.rs.PathParam; import javax.ws.rs.Produces; import javax.ws.rs.QueryParam; import javax.ws.rs.core.Context; +import javax.ws.rs.core.PathSegment; import javax.ws.rs.core.Response; import javax.ws.rs.core.UriBuilder; import javax.ws.rs.core.UriInfo; @@ -105,7 +116,8 @@ public class AccountResource extends SecurityResourceBase { @GET @Produces("application/xml") public AccountsCommonList getAccountList(@Context UriInfo ui) { - AccountsCommonList result = (AccountsCommonList)getList(ui, AccountsCommon.class); + UriInfoWrapper uriInfoWrapper = new UriInfoWrapper(ui); + AccountsCommonList result = (AccountsCommonList)getList(uriInfoWrapper, AccountsCommon.class); if(logger.isTraceEnabled()) { PayloadOutputPart ppo = new PayloadOutputPart(AccountsCommonList.class.getSimpleName(), result); @@ -113,6 +125,73 @@ public class AccountResource extends SecurityResourceBase { } return result; } + + protected UriInfo createUriInfo() throws URISyntaxException { + return createUriInfo(""); + } + + private UriInfo createUriInfo(String queryString) throws URISyntaxException { + URI absolutePath = new URI(""); + URI baseUri = new URI(""); + return new UriInfoImpl(absolutePath, baseUri, "", queryString, Collections.emptyList()); + } + + /** + * Perform a search off the accounts for using a user ID + * @param userId + * @return + */ + private String getAccountCsid(String userId) { + String result = null; + + try { + UriInfo uriInfo = createUriInfo(String.format("uid=%s", userId)); + AccountsCommonList accountsCommonList = getAccountList(uriInfo); + if (accountsCommonList != null && accountsCommonList.getAccountListItem() != null) { + for (AccountListItem accountListItem: accountsCommonList.getAccountListItem()) { + if (accountListItem.getUserid().equalsIgnoreCase(userId)) { + result = accountListItem.getCsid(); + break; + } + } + } + } catch (URISyntaxException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + + return result; + } + + /** + * Return the list of roles (display name) for a user id. + * + * @param userId + * @return + */ + public List getAccountRoles(String userId, String tenantId) { + List result = null; + + String accountCsid = getAccountCsid(userId); + if (accountCsid != null) { + AccountRole accountRole = getAccountRole(accountCsid); + if (accountRole != null && accountRole.getRole() != null) { + List roleValueList = accountRole.getRole(); + if (roleValueList.isEmpty() == false) { + result = new ArrayList(); + for (RoleValue roleValue: roleValueList) { + String displayName = roleValue.getDisplayName(); + if (displayName == null) { + displayName = RoleClient.inferDisplayName(roleValue.getRoleName(), tenantId); + } + result.add(displayName); + } + } + } + } + + return result; + } @PUT @Path("{csid}") @@ -225,7 +304,6 @@ public class AccountResource extends SecurityResourceBase { logger.debug("getAccountPerm with accCsid=" + accCsid); ensureCSID(accCsid, ServiceMessages.GET_FAILED+ "getAccountPerm account "); AccountPermission result = null; - String userId = "undefined"; try { result = JpaStorageUtils.getAccountPermissions(accCsid); } catch (Exception e) { diff --git a/services/account/service/src/main/java/org/collectionspace/services/account/storage/AccountDocumentHandler.java b/services/account/service/src/main/java/org/collectionspace/services/account/storage/AccountDocumentHandler.java index 8a0111804..09a344483 100644 --- a/services/account/service/src/main/java/org/collectionspace/services/account/storage/AccountDocumentHandler.java +++ b/services/account/service/src/main/java/org/collectionspace/services/account/storage/AccountDocumentHandler.java @@ -163,6 +163,7 @@ public class AccountDocumentHandler AccountsCommon account = (AccountsCommon) obj; AccountListItem accListItem = new AccountListItem(); accListItem.setScreenName(account.getScreenName()); + accListItem.setUserid(account.getUserId()); accListItem.setEmail(account.getEmail()); accListItem.setStatus(account.getStatus()); String id = account.getCsid(); diff --git a/services/authorization-mgt/client/src/main/java/org/collectionspace/services/client/RoleClient.java b/services/authorization-mgt/client/src/main/java/org/collectionspace/services/client/RoleClient.java index 4a9755b60..7694e7b7a 100644 --- a/services/authorization-mgt/client/src/main/java/org/collectionspace/services/client/RoleClient.java +++ b/services/authorization-mgt/client/src/main/java/org/collectionspace/services/client/RoleClient.java @@ -28,16 +28,7 @@ package org.collectionspace.services.client; import javax.ws.rs.core.Response; - - - - - - - - import org.apache.http.HttpStatus; -import org.collectionspace.services.authorization.AccountRole; import org.collectionspace.services.authorization.Role; import org.collectionspace.services.authorization.RolesList; import org.collectionspace.services.description.ServiceDescription; @@ -53,6 +44,7 @@ public class RoleClient extends AbstractServiceClientImpl(); - // - private String permissionRoleCsid = null; - /** * Instantiates a new permission role sub resource. * diff --git a/services/authorization-mgt/service/src/main/java/org/collectionspace/services/authorization/RoleResource.java b/services/authorization-mgt/service/src/main/java/org/collectionspace/services/authorization/RoleResource.java index 96a948c08..d07d6c2ec 100644 --- a/services/authorization-mgt/service/src/main/java/org/collectionspace/services/authorization/RoleResource.java +++ b/services/authorization-mgt/service/src/main/java/org/collectionspace/services/authorization/RoleResource.java @@ -47,7 +47,6 @@ import javax.ws.rs.Path; import javax.ws.rs.PathParam; import javax.ws.rs.Produces; import javax.ws.rs.QueryParam; -import javax.ws.rs.WebApplicationException; import javax.ws.rs.core.Context; import javax.ws.rs.core.Response; import javax.ws.rs.core.UriBuilder; diff --git a/services/authorization-mgt/service/src/main/java/org/collectionspace/services/authorization/storage/RoleDocumentHandler.java b/services/authorization-mgt/service/src/main/java/org/collectionspace/services/authorization/storage/RoleDocumentHandler.java index 5526957ce..daeb5ecd7 100644 --- a/services/authorization-mgt/service/src/main/java/org/collectionspace/services/authorization/storage/RoleDocumentHandler.java +++ b/services/authorization-mgt/service/src/main/java/org/collectionspace/services/authorization/storage/RoleDocumentHandler.java @@ -67,8 +67,7 @@ public class RoleDocumentHandler } setTenant(role); - role.setRoleName(fixRoleName(role.getRoleName(), - role.getTenantId())); + role.setRoleName(RoleClient.getBackendRoleName(role.getRoleName(), role.getTenantId())); role.setCsid(id); // We do not allow creation of locked roles through the services. role.setMetadataProtection(null); @@ -81,7 +80,7 @@ public class RoleDocumentHandler Role roleReceived = getCommonPart(); // If marked as metadata immutable, do not do update if(!RoleClient.IMMUTABLE.equals(roleFound.getMetadataProtection())) { - roleReceived.setRoleName(fixRoleName(roleReceived.getRoleName(), + roleReceived.setRoleName(RoleClient.getBackendRoleName(roleReceived.getRoleName(), roleFound.getTenantId())); merge(roleReceived, roleFound); } @@ -211,15 +210,6 @@ public class RoleDocumentHandler } } - private String fixRoleName(String role, String tenantId) { - String roleName = role.toUpperCase(); - String rolePrefix = "ROLE_" + tenantId + "_"; - if (!roleName.startsWith(rolePrefix)) { - roleName = rolePrefix + roleName; - } - return roleName; - } - private void setTenant(Role role) { //set tenant only if not available from input if (role.getTenantId() == null || role.getTenantId().isEmpty()) { diff --git a/services/authorization/service/src/main/java/org/collectionspace/services/authorization/AuthZ.java b/services/authorization/service/src/main/java/org/collectionspace/services/authorization/AuthZ.java index cef1fb3d9..9dd09df36 100644 --- a/services/authorization/service/src/main/java/org/collectionspace/services/authorization/AuthZ.java +++ b/services/authorization/service/src/main/java/org/collectionspace/services/authorization/AuthZ.java @@ -28,6 +28,7 @@ import java.util.HashSet; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; +import org.collectionspace.services.authorization.perms.ActionType; import org.collectionspace.services.authorization.spi.CSpaceAuthorizationProvider; import org.springframework.context.support.ClassPathXmlApplicationContext; import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; @@ -61,6 +62,36 @@ public class AuthZ { return self; } + public static String getMethod(ActionType actionType) { + String result = null; + + switch (actionType) { + case CREATE: + result = "POST"; + break; + case READ: + result = "GET"; + break; + case UPDATE: + result = "PUT"; + break; + case DELETE: + result = "DELETE"; + break; + case RUN: + result = "RUN"; + break; + case SEARCH: + result = "READ"; + break; + default: + throw new RuntimeException(String.format("Encountered unexpected action type '%s'.", + actionType.value())); + } + + return result; + } + private void setupProvider() { String beanConfig = "applicationContext-authorization.xml"; //system property is only set in test environment diff --git a/services/batch/3rdparty/nuxeo-platform-cs-batch/src/main/resources/schemas/batch_common-template.xml b/services/batch/3rdparty/nuxeo-platform-cs-batch/src/main/resources/schemas/batch_common-template.xml new file mode 100644 index 000000000..53890a8fc --- /dev/null +++ b/services/batch/3rdparty/nuxeo-platform-cs-batch/src/main/resources/schemas/batch_common-template.xml @@ -0,0 +1,40 @@ + + + name0 + notes0 + + CollectionObject + Intake + + + TENANT_READER + TENANT_ADMINISTRATOR + + + + blobs + CRUL + + + media + CRUL + + + concepts + RL + + + collectionobjects + RL + + + false + false + false + false + false + className0 + diff --git a/services/batch/3rdparty/nuxeo-platform-cs-batch/src/main/resources/schemas/batch_common.xsd b/services/batch/3rdparty/nuxeo-platform-cs-batch/src/main/resources/schemas/batch_common.xsd index 0b670e4e2..d0e5c1729 100644 --- a/services/batch/3rdparty/nuxeo-platform-cs-batch/src/main/resources/schemas/batch_common.xsd +++ b/services/batch/3rdparty/nuxeo-platform-cs-batch/src/main/resources/schemas/batch_common.xsd @@ -1,14 +1,12 @@ - - - - + + + + + + + + + + - + - - - - - - - + + + + + + + + + + + + + + + + + + + + + + diff --git a/services/batch/3rdparty/nuxeo-platform-cs-batch/src/main/resources/schemas/instance1.xml b/services/batch/3rdparty/nuxeo-platform-cs-batch/src/main/resources/schemas/instance1.xml new file mode 100644 index 000000000..bb247a74d --- /dev/null +++ b/services/batch/3rdparty/nuxeo-platform-cs-batch/src/main/resources/schemas/instance1.xml @@ -0,0 +1,4 @@ + +className0 diff --git a/services/batch/client/src/main/java/org/collectionspace/services/client/BatchClient.java b/services/batch/client/src/main/java/org/collectionspace/services/client/BatchClient.java index 097bd972f..1a107f6ac 100644 --- a/services/batch/client/src/main/java/org/collectionspace/services/client/BatchClient.java +++ b/services/batch/client/src/main/java/org/collectionspace/services/client/BatchClient.java @@ -29,7 +29,8 @@ public class BatchClient extends AbstractCommonListPoxServiceClientImpl 4.0.0 - org.collectionspace.services org.collectionspace.services.batch.jaxb services.batch.jaxb @@ -19,6 +18,11 @@ org.collectionspace.services.jaxb ${project.version} + + org.collectionspace.services + org.collectionspace.services.hyperjaxb + ${project.version} + diff --git a/services/batch/jaxb/src/main/resources/batch_common-template.xml b/services/batch/jaxb/src/main/resources/batch_common-template.xml new file mode 100644 index 000000000..53890a8fc --- /dev/null +++ b/services/batch/jaxb/src/main/resources/batch_common-template.xml @@ -0,0 +1,40 @@ + + + name0 + notes0 + + CollectionObject + Intake + + + TENANT_READER + TENANT_ADMINISTRATOR + + + + blobs + CRUL + + + media + CRUL + + + concepts + RL + + + collectionobjects + RL + + + false + false + false + false + false + className0 + diff --git a/services/batch/jaxb/src/main/resources/batch_common.xsd b/services/batch/jaxb/src/main/resources/batch_common.xsd index 7b9eebd44..dd1437fa5 100644 --- a/services/batch/jaxb/src/main/resources/batch_common.xsd +++ b/services/batch/jaxb/src/main/resources/batch_common.xsd @@ -21,26 +21,45 @@ - + - + - + - - - - + + + + + + + + + + + + - - + + + + + + + + + + + + + + diff --git a/services/batch/jaxb/src/main/resources/batch_common_document.xsd b/services/batch/jaxb/src/main/resources/batch_common_document.xsd new file mode 100644 index 000000000..15b11183c --- /dev/null +++ b/services/batch/jaxb/src/main/resources/batch_common_document.xsd @@ -0,0 +1,63 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/services/batch/jaxb/src/main/resources/instance1.xml b/services/batch/jaxb/src/main/resources/instance1.xml new file mode 100644 index 000000000..aa3a12cb8 --- /dev/null +++ b/services/batch/jaxb/src/main/resources/instance1.xml @@ -0,0 +1,34 @@ + + + + name0 + notes0 + + forDocType0 + forDocType1 + + + roleDisplayName0 + roleDisplayName1 + + + + resourceName0 + actionGroup0 + + + resourceName1 + actionGroup1 + + + false + false + false + false + false + className0 + + diff --git a/services/batch/service/pom.xml b/services/batch/service/pom.xml index 5f95b8820..c881fd723 100644 --- a/services/batch/service/pom.xml +++ b/services/batch/service/pom.xml @@ -22,6 +22,28 @@ org.collectionspace.services org.collectionspace.services.common + + org.collectionspace.services + org.collectionspace.services.authentication.service + ${project.version} + provided + + + org.collectionspace.services + org.collectionspace.services.account.service + ${project.version} + provided + + + org.collectionspace.services + org.collectionspace.services.authorization.jaxb + ${project.version} + + + org.collectionspace.services + org.collectionspace.services.authorization.service + ${project.version} + org.collectionspace.services org.collectionspace.services.jaxb diff --git a/services/batch/service/src/main/java/org/collectionspace/services/batch/AbstractBatchInvocable.java b/services/batch/service/src/main/java/org/collectionspace/services/batch/AbstractBatchInvocable.java index d5f0b7491..c487489a7 100644 --- a/services/batch/service/src/main/java/org/collectionspace/services/batch/AbstractBatchInvocable.java +++ b/services/batch/service/src/main/java/org/collectionspace/services/batch/AbstractBatchInvocable.java @@ -3,6 +3,9 @@ package org.collectionspace.services.batch; import java.util.Collections; import java.util.List; import javax.ws.rs.core.Response; + +import org.collectionspace.services.client.PoxPayloadIn; +import org.collectionspace.services.client.PoxPayloadOut; import org.collectionspace.services.common.ResourceMap; import org.collectionspace.services.common.context.ServiceContext; import org.collectionspace.services.common.invocable.InvocationContext; @@ -26,6 +29,7 @@ import org.slf4j.LoggerFactory; * - ADR 2013-01-04 */ public abstract class AbstractBatchInvocable implements BatchInvocable { + final Logger logger = LoggerFactory.getLogger(AbstractBatchInvocable.class); public final int OK_STATUS = Response.Status.OK.getStatusCode(); public final int CREATED_STATUS = Response.Status.CREATED.getStatusCode(); @@ -33,15 +37,15 @@ public abstract class AbstractBatchInvocable implements BatchInvocable { public final int INT_ERROR_STATUS = Response.Status.INTERNAL_SERVER_ERROR.getStatusCode(); protected final String CSID_VALUES_NOT_PROVIDED_IN_INVOCATION_CONTEXT = "Could not find required CSID values in the invocation context for this batch job."; + + private ServiceContext ctx; private List invocationModes; private ResourceMap resourceMap; - private InvocationContext invocationCtx; - private ServiceContext ctx; - private int completionStatus; - private InvocationResults results; - private InvocationError errorInfo; - final Logger logger = LoggerFactory.getLogger(AbstractBatchInvocable.class); + protected InvocationContext invocationCtx; + protected int completionStatus; + protected InvocationResults results; + protected InvocationError errorInfo; public AbstractBatchInvocable() { init(); @@ -75,12 +79,12 @@ public abstract class AbstractBatchInvocable implements BatchInvocable { } @Override - public void setServiceContext(ServiceContext context) { + public void setServiceContext(ServiceContext context) { this.ctx = context; } @Override - public ServiceContext getServiceContext() { + public ServiceContext getServiceContext() { return ctx; } diff --git a/services/batch/service/src/main/java/org/collectionspace/services/batch/BatchResource.java b/services/batch/service/src/main/java/org/collectionspace/services/batch/BatchResource.java index 41322dbf6..aa38ece22 100644 --- a/services/batch/service/src/main/java/org/collectionspace/services/batch/BatchResource.java +++ b/services/batch/service/src/main/java/org/collectionspace/services/batch/BatchResource.java @@ -27,6 +27,7 @@ import org.collectionspace.services.BatchJAXBSchema; import org.collectionspace.services.batch.nuxeo.BatchDocumentModelHandler; import org.collectionspace.services.client.BatchClient; import org.collectionspace.services.client.IQueryManager; +import org.collectionspace.services.client.PayloadPart; import org.collectionspace.services.client.PoxPayloadIn; import org.collectionspace.services.client.PoxPayloadOut; import org.collectionspace.services.common.NuxeoBasedResource; @@ -72,11 +73,7 @@ public class BatchResource extends NuxeoBasedResource { @Override //public Class getCommonPartClass() { public Class getCommonPartClass() { - try { - return Class.forName("org.collectionspace.services.batch.BatchCommon");//.class; - } catch (ClassNotFoundException e){ - return null; - } + return BatchCommon.class; } /** @@ -105,8 +102,7 @@ public class BatchResource extends NuxeoBasedResource { return list; } - private AbstractCommonList batchSearch(UriInfo ui, - String docType, String mode) { + private AbstractCommonList batchSearch(UriInfo ui, String docType, String mode) { try { ServiceContext ctx = createServiceContext(ui); DocumentHandler handler = createDocumentHandler(ctx); @@ -158,10 +154,18 @@ public class BatchResource extends NuxeoBasedResource { } return ptClause; } - - - + private BatchCommon getBatchCommon(String csid) throws Exception { + BatchCommon result = null; + + ServiceContext ctx = createServiceContext(); + PoxPayloadOut ppo = get(csid, ctx); + PayloadPart batchCommonPart = ppo.getPart(BatchClient.SERVICE_COMMON_PART_NAME); + result = (BatchCommon)batchCommonPart.getBody(); + + return result; + } + @POST @Path("{csid}") public InvocationResults invokeBatchJob( @@ -169,13 +173,15 @@ public class BatchResource extends NuxeoBasedResource { @Context UriInfo ui, @PathParam("csid") String csid, InvocationContext invContext) { + try { ServiceContext ctx = createServiceContext(ui); BatchDocumentModelHandler handler = (BatchDocumentModelHandler)createDocumentHandler(ctx); - - return handler.invokeBatchJob(ctx, csid, resourceMap, invContext); + return handler.invokeBatchJob(ctx, csid, resourceMap, invContext, getBatchCommon(csid)); } catch (Exception e) { - throw bigReThrow(e, ServiceMessages.POST_FAILED); + String msg = String.format("%s Could not invoke batch job with CSID='%s'.", + ServiceMessages.POST_FAILED, csid); + throw bigReThrow(e, msg); } } } diff --git a/services/batch/service/src/main/java/org/collectionspace/services/batch/nuxeo/BatchDocumentModelHandler.java b/services/batch/service/src/main/java/org/collectionspace/services/batch/nuxeo/BatchDocumentModelHandler.java index 5509ebf0c..52f75556d 100644 --- a/services/batch/service/src/main/java/org/collectionspace/services/batch/nuxeo/BatchDocumentModelHandler.java +++ b/services/batch/service/src/main/java/org/collectionspace/services/batch/nuxeo/BatchDocumentModelHandler.java @@ -23,32 +23,42 @@ */ package org.collectionspace.services.batch.nuxeo; +import java.util.ArrayList; +import java.util.HashSet; import java.util.List; +import java.util.Set; import javax.ws.rs.core.Response; -import org.collectionspace.services.BatchJAXBSchema; -import org.collectionspace.services.jaxb.InvocableJAXBSchema; +import org.collectionspace.authentication.AuthN; +import org.collectionspace.services.account.AccountResource; +import org.collectionspace.services.authorization.AuthZ; +import org.collectionspace.services.authorization.CSpaceResource; +import org.collectionspace.services.authorization.PermissionException; +import org.collectionspace.services.authorization.URIResourceImpl; +import org.collectionspace.services.authorization.perms.ActionType; import org.collectionspace.services.nuxeo.client.java.NuxeoDocumentModelHandler; import org.collectionspace.services.nuxeo.client.java.CoreSessionInterface; import org.collectionspace.services.nuxeo.client.java.RepositoryClientImpl; -import org.collectionspace.services.nuxeo.util.NuxeoUtils; import org.collectionspace.services.batch.BatchCommon; +import org.collectionspace.services.batch.BatchCommon.ForDocTypes; +import org.collectionspace.services.batch.BatchCommon.ForRoles; import org.collectionspace.services.batch.BatchInvocable; +import org.collectionspace.services.batch.ResourceActionGroup; +import org.collectionspace.services.batch.ResourceActionGroupList; import org.collectionspace.services.client.PoxPayloadIn; import org.collectionspace.services.client.PoxPayloadOut; import org.collectionspace.services.common.ResourceMap; +import org.collectionspace.services.common.authorization_mgt.ActionGroup; import org.collectionspace.services.common.context.ServiceContext; import org.collectionspace.services.common.document.BadRequestException; import org.collectionspace.services.common.document.DocumentException; -import org.collectionspace.services.common.document.DocumentWrapper; import org.collectionspace.services.common.invocable.Invocable; import org.collectionspace.services.common.invocable.InvocationContext; import org.collectionspace.services.common.invocable.InvocationResults; import org.collectionspace.services.common.invocable.Invocable.InvocationError; + import org.jboss.resteasy.spi.ResteasyProviderFactory; -import org.nuxeo.ecm.core.api.DocumentModel; -import org.nuxeo.ecm.core.api.model.PropertyException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -56,28 +66,161 @@ public class BatchDocumentModelHandler extends NuxeoDocumentModelHandler ctx, String csid, - ResourceMap resourceMap, InvocationContext invocationCtx) throws Exception { - - CoreSessionInterface repoSession = null; - boolean releaseRepoSession = false; - - String invocationMode = invocationCtx.getMode(); - String modeProperty = null; - boolean checkDocType = true; + + /** + * Return true if the batch job supports the requested mode. + * @param invocationCtx + * @param batchCommon + * @return + * @throws BadRequestException + */ + protected boolean supportsInvokationMode(InvocationContext invocationCtx, BatchCommon batchCommon) throws BadRequestException { + boolean result = false; + + String invocationMode = invocationCtx.getMode().toLowerCase(); if (BatchInvocable.INVOCATION_MODE_SINGLE.equalsIgnoreCase(invocationMode)) { - modeProperty = BatchJAXBSchema.SUPPORTS_SINGLE_DOC; + result = batchCommon.isSupportsSingleDoc(); //BatchJAXBSchema.SUPPORTS_SINGLE_DOC; } else if (BatchInvocable.INVOCATION_MODE_LIST.equalsIgnoreCase(invocationMode)) { - modeProperty = BatchJAXBSchema.SUPPORTS_DOC_LIST; + result = batchCommon.isSupportsDocList(); //BatchJAXBSchema.SUPPORTS_DOC_LIST; } else if (BatchInvocable.INVOCATION_MODE_GROUP.equalsIgnoreCase(invocationMode)) { - modeProperty = BatchJAXBSchema.SUPPORTS_GROUP; + result = batchCommon.isSupportsGroup(); //BatchJAXBSchema.SUPPORTS_GROUP; } else if (Invocable.INVOCATION_MODE_NO_CONTEXT.equalsIgnoreCase(invocationMode)) { - modeProperty = InvocableJAXBSchema.SUPPORTS_NO_CONTEXT; - checkDocType = false; + result = batchCommon.isSupportsNoContext(); //InvocableJAXBSchema.SUPPORTS_NO_CONTEXT; } else { - throw new BadRequestException("BatchResource: unknown Invocation Mode: " + invocationMode); + String msg = String.format("BatchResource: Unknown invocation mode '%s' requested trying to invoke batch job '%s'.", + invocationMode, batchCommon.getName()); + throw new BadRequestException(msg); + } + + return result; + } + + /** + * Returns true if we found any required permissions. + * + * @param batchCommon + * @return + */ + private boolean hasRequiredPermissions(BatchCommon batchCommon) { + boolean result = false; + + try { + result = batchCommon.getResourceActionGroupList().getResourceActionGroup().size() > 0; + } catch (NullPointerException e) { + // ignore exception, we're just testing to see if we have any list elements + } + + return result; + } + + /** + * Returns true if we found any required roles. + * + * @param batchCommon + * @return + */ + private boolean hasRequiredRoles(BatchCommon batchCommon) { + boolean result = false; + + try { + result = batchCommon.getForRoles().getRoleDisplayName().size() > 0; + } catch (NullPointerException e) { + // ignore exception, we're just testing to see if we have any list elements + } + + return result; + } + + /** + * The current user is authorized to run the batch job if: + * 1. No permissions or roles are specified in the batch job + * 2. No roles are specified, but permissions are specified and the current user has those permissions + * 3. Roles are specified and the current user is a member of at least one of the roles. + * + * @param batchCommon + * @return + */ + protected boolean isAuthoritzed(BatchCommon batchCommon) { + boolean result = true; + + if (hasRequiredRoles(batchCommon)) { + result = isAuthorizedWithRoles(batchCommon); + } else if (hasRequiredPermissions(batchCommon)) { + result = isAuthoritzedWithPermissions(batchCommon); + } + + return result; + } + + protected boolean isAuthorizedWithRoles(BatchCommon batchCommon) { + boolean result = false; + + ForRoles forRolesList = batchCommon.getForRoles(); + if (forRolesList != null) { + AccountResource accountResource = new AccountResource(); + List roleDisplayNameList = accountResource.getAccountRoles(AuthN.get().getUserId(), AuthN.get().getCurrentTenantId()); + for (String target : forRolesList.getRoleDisplayName()) { + if (roleDisplayNameList.contains(target)) { + result = true; + break; + } + } + } + + return result; + } + + /** + * Check to see if the current user is authorized to run/invoke this batch job. If the batch job + * did not specify any permissions, we assume that the current user is authorized to run the job. + * @param batchCommon + * @return + */ + protected boolean isAuthoritzedWithPermissions(BatchCommon batchCommon) { + boolean result = true; + + ResourceActionGroupList resourceActionGroupList = batchCommon.getResourceActionGroupList(); + if (resourceActionGroupList != null) { + String tenantId = AuthN.get().getCurrentTenantId(); + for (ResourceActionGroup resourceActionGroup: resourceActionGroupList.getResourceActionGroup()) { + String resourceName = resourceActionGroup.getResourceName(); + ActionGroup actionGroup = ActionGroup.creatActionGroup(resourceActionGroup.getActionGroup()); + for (ActionType actionType: actionGroup.getActions()) { + CSpaceResource res = new URIResourceImpl(tenantId, resourceName, AuthZ.getMethod(actionType)); + if (AuthZ.get().isAccessAllowed(res) == false) { + return false; + } + } + } + } + + return result; + } + + /** + * Returns a copy of the incoming list of strings all lower-cased. Also removes any duplicates. + * + * @param listOfStrings + * @return + */ + private List toLowerCase(List listOfStrings) { + List result = null; + + if (listOfStrings != null) { + Set stringSet = new HashSet(); + for (String s : listOfStrings) { + stringSet.add(s.toLowerCase()); + } + result = new ArrayList(stringSet); } + + return result; + } + + public InvocationResults invokeBatchJob(ServiceContext ctx, String csid, + ResourceMap resourceMap, InvocationContext invocationCtx, BatchCommon batchCommon) throws Exception { + CoreSessionInterface repoSession = null; + boolean releaseRepoSession = false; RepositoryClientImpl repoClient = (RepositoryClientImpl) this.getRepositoryClient(ctx); repoSession = this.getRepositorySession(); @@ -86,37 +229,97 @@ public class BatchDocumentModelHandler extends NuxeoDocumentModelHandler wrapper = repoClient.getDoc(repoSession, ctx, csid); - DocumentModel docModel = wrapper.getWrappedObject(); - Boolean supports = (Boolean) NuxeoUtils.getProperyValue(docModel, modeProperty); - if (!supports) { - throw new BadRequestException("BatchResource: This Batch Job does not support Invocation Mode: " - + invocationMode); + // + // Ensure the current user has permission to run this batch job + if (isAuthoritzed(batchCommon) == false) { + String msg = String.format("BatchResource: The user '%s' does not have permission to run the batch job '%s' CSID='%s'", + AuthN.get().getUserId(), batchCommon.getName(), csid); + throw new PermissionException(msg); } - if (checkDocType) { - List forDocTypeList = (List) NuxeoUtils.getProperyValue(docModel, BatchJAXBSchema.FOR_DOC_TYPES); //docModel.getPropertyValue(BatchJAXBSchema.FOR_DOC_TYPES); - if (forDocTypeList == null || !forDocTypeList.contains(invocationCtx.getDocType())) { - throw new BadRequestException("BatchResource: Invoked with unsupported document type: " - + invocationCtx.getDocType()); + + // + // Ensure the batch job supports the requested invocation context's mode type + if (supportsInvokationMode(invocationCtx, batchCommon) == false) { + String msg = String.format("BatchResource: The batch job '%s' CSID='%s' does not support the invocation mode '%s'.", + batchCommon.getName(), csid, invocationCtx.getMode()); + throw new BadRequestException(msg); + } + + // + // Ensure the batch job supports the requested invocation context's document type + if (!Invocable.INVOCATION_MODE_NO_CONTEXT.equalsIgnoreCase(invocationCtx.getMode())) { + ForDocTypes forDocTypes = batchCommon.getForDocTypes(); + if (forDocTypes != null) { + List forDocTypeList = toLowerCase(forDocTypes.getForDocType()); // convert all strings to lowercase. + if (forDocTypeList == null || !forDocTypeList.contains(invocationCtx.getDocType().toLowerCase())) { + String msg = String.format("BatchResource: The batch job '%s' CSID='%s' does not support the invocation document type '%s'.", + batchCommon.getName(), csid, invocationCtx.getDocType()); + throw new BadRequestException(msg); + } } } - className = (String) NuxeoUtils.getProperyValue(docModel, BatchJAXBSchema.BATCH_CLASS_NAME); //docModel.getPropertyValue(BatchJAXBSchema.BATCH_CLASS_NAME); - } catch (PropertyException pe) { - if (logger.isDebugEnabled()) { - logger.debug("Property exception getting batch values: ", pe); + + // + // Now that we've ensure all the prerequisites have been met, let's try to + // instantiate and run the batch job. + // + + String className = batchCommon.getClassName().trim(); + ClassLoader tccl = Thread.currentThread().getContextClassLoader(); + Class c = tccl.loadClass(className); + tccl.setClassAssertionStatus(className, true); + if (!BatchInvocable.class.isAssignableFrom(c)) { + throw new RuntimeException("BatchResource: Class: " + className + " does not implement BatchInvocable!"); + } + + BatchInvocable batchInstance = (BatchInvocable) c.newInstance(); + List modes = batchInstance.getSupportedInvocationModes(); + if (!modes.contains(invocationCtx.getMode().toLowerCase())) { + String msg = String.format("BatchResource: Invoked with unsupported mode '%s'. Batch class '%s' supports these modes: %s.", + invocationCtx.getMode().toLowerCase(), className, modes.toString()); + throw new BadRequestException(msg); + } + + batchInstance.setInvocationContext(invocationCtx); + batchInstance.setServiceContext(ctx); + + if (resourceMap != null) { + batchInstance.setResourceMap(resourceMap); + } else { + resourceMap = ResteasyProviderFactory.getContextData(ResourceMap.class); + if (resourceMap != null) { + batchInstance.setResourceMap(resourceMap); + } else { + logger.warn("BatchResource.invoke did not get a resourceMapHolder in context!"); + } + } + + batchInstance.run(); + int status = batchInstance.getCompletionStatus(); + if (status == Invocable.STATUS_ERROR) { + InvocationError error = batchInstance.getErrorInfo(); + if (error.getResponseCode() == BAD_REQUEST_STATUS) { + throw new BadRequestException("BatchResouce: batchProcess encountered error: " + + batchInstance.getErrorInfo()); + } else { + throw new RuntimeException("BatchResouce: batchProcess encountered error: " + + batchInstance.getErrorInfo()); + + } } - throw pe; - } catch (DocumentException de) { + + InvocationResults results = batchInstance.getResults(); + return results; + } catch (PermissionException e) { if (logger.isDebugEnabled()) { - logger.debug("Problem getting batch doc: ", de); + logger.debug("BatchResource: Caught exception ", e); } - throw de; + throw e; } catch (Exception e) { if (logger.isDebugEnabled()) { - logger.debug("Caught exception ", e); + logger.debug("BatchResource: Caught exception ", e); } throw new DocumentException(e); } finally { @@ -124,51 +327,5 @@ public class BatchDocumentModelHandler extends NuxeoDocumentModelHandler c = tccl.loadClass(className); - // enable validation assertions - tccl.setClassAssertionStatus(className, true); - if (!BatchInvocable.class.isAssignableFrom(c)) { - throw new RuntimeException("BatchResource: Class: " + className + " does not implement BatchInvocable!"); - } - - BatchInvocable batchInstance = (BatchInvocable) c.newInstance(); - List modes = batchInstance.getSupportedInvocationModes(); - if (!modes.contains(invocationMode)) { - throw new BadRequestException("BatchResource: Invoked with unsupported context mode: " + invocationMode); - } - - batchInstance.setInvocationContext(invocationCtx); - batchInstance.setServiceContext(ctx); - - if (resourceMap != null) { - batchInstance.setResourceMap(resourceMap); - } else { - resourceMap = ResteasyProviderFactory.getContextData(ResourceMap.class); - if (resourceMap != null) { - batchInstance.setResourceMap(resourceMap); - } else { - logger.warn("BatchResource.invoke did not get a resourceMapHolder in Context!"); - } - } - - batchInstance.run(); - int status = batchInstance.getCompletionStatus(); - if (status == Invocable.STATUS_ERROR) { - InvocationError error = batchInstance.getErrorInfo(); - if (error.getResponseCode() == BAD_REQUEST_STATUS) { - throw new BadRequestException("BatchResouce: batchProcess encountered error: " - + batchInstance.getErrorInfo()); - } else { - throw new RuntimeException("BatchResouce: batchProcess encountered error: " - + batchInstance.getErrorInfo()); - - } - } - - InvocationResults results = batchInstance.getResults(); - return results; } } diff --git a/services/batch/service/src/main/java/org/collectionspace/services/batch/nuxeo/BatchValidatorHandler.java b/services/batch/service/src/main/java/org/collectionspace/services/batch/nuxeo/BatchValidatorHandler.java index 4598b146d..c864a662e 100644 --- a/services/batch/service/src/main/java/org/collectionspace/services/batch/nuxeo/BatchValidatorHandler.java +++ b/services/batch/service/src/main/java/org/collectionspace/services/batch/nuxeo/BatchValidatorHandler.java @@ -1,17 +1,119 @@ package org.collectionspace.services.batch.nuxeo; -import org.collectionspace.services.common.context.ServiceContext; +import org.collectionspace.services.batch.BatchCommon; +import org.collectionspace.services.batch.BatchInvocable; +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.ValidatorHandler; -import org.collectionspace.services.common.document.DocumentHandler.Action; +import org.collectionspace.services.common.document.ValidatorHandlerImpl; -public class BatchValidatorHandler implements ValidatorHandler { +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class BatchValidatorHandler extends ValidatorHandlerImpl { + + final Logger logger = LoggerFactory.getLogger(BatchValidatorHandler.class); + + // + // Error Strings + // + private static final String VALIDATION_ERROR = "The batch record payload was invalid. See log file for more details."; + private static final String NAME_NULL_ERROR = "The batch record field \"name\" cannot be empty or missing."; + private static final String MISSING_CLASS_ERROR = "The Java class '%s' (fully qualified with package name) for the batch job named '%s' cannot be found."; + + @Override + protected Class getCommonPartClass() { + return BatchCommon.class; + } + + @Override + protected void handleCreate() throws InvalidDocumentException { + try { + BatchCommon batchCommon = (BatchCommon) getCommonPart(); + validateBatchCommon(batchCommon); + } catch (AssertionError e) { + if (logger.isErrorEnabled() == true) { + logger.error(e.getMessage(), e); + } + throw new InvalidDocumentException(VALIDATION_ERROR, e); + } + } + + @Override + protected void handleGet() throws InvalidDocumentException { + // TODO Auto-generated method stub + + } + + @Override + protected void handleGetAll() throws InvalidDocumentException { + // TODO Auto-generated method stub + + } + + @Override + protected void handleUpdate() throws InvalidDocumentException { + try { + BatchCommon batchCommon = (BatchCommon) getCommonPart(); + validateBatchCommon(batchCommon); + } catch (AssertionError e) { + if (logger.isErrorEnabled() == true) { + logger.error(e.getMessage(), e); + } + throw new InvalidDocumentException(VALIDATION_ERROR, e); + } + } @Override - public void validate(Action action, ServiceContext ctx) - throws InvalidDocumentException { + protected void handleDelete() throws InvalidDocumentException { // TODO Auto-generated method stub - //System.out.println("BatchValidatorHandler executed."); + } + // + // Private Methods + // + private boolean canFindClass(String className) { + boolean result = false; + + try { + ClassLoader tccl = Thread.currentThread().getContextClassLoader(); + Class c = tccl.loadClass(className); + tccl.setClassAssertionStatus(className, true); + if (!BatchInvocable.class.isAssignableFrom(c)) { + throw new RuntimeException("BatchResource: Class: " + className + " does not implement BatchInvocable!"); + } + result = true; + } catch (Exception e) { + String msg = String.format("Could not find load batch class named '%s'", + className); + logger.debug(msg, e); + } + + return result; + } + + private void validateBatchCommon(BatchCommon batchCommon) { + CS_ASSERT(batchCommon != null); + + // + // Ensure a batch name + String batchName = batchCommon.getName(); + CS_ASSERT(batchName != null, NAME_NULL_ERROR); + CS_ASSERT(batchName.isEmpty() == false, NAME_NULL_ERROR); + + // + // Ensure a batch class + String batchClassName = batchCommon.getClassName(); + CS_ASSERT(batchName != null, NAME_NULL_ERROR); + CS_ASSERT(batchName.isEmpty() == false, NAME_NULL_ERROR); + + // + // Ensure we can find and load the batch Java class + if (canFindClass(batchClassName) == false) { + String msg = String.format(MISSING_CLASS_ERROR, batchClassName, batchCommon.getName()); + CS_ASSERT(false, batchClassName); + } + } + } diff --git a/services/batch/service/src/main/java/org/collectionspace/services/batch/nuxeo/CreateAndLinkLoanOutBatchJob.java b/services/batch/service/src/main/java/org/collectionspace/services/batch/nuxeo/CreateAndLinkLoanOutBatchJob.java index 1781753f1..4ac26db5b 100644 --- a/services/batch/service/src/main/java/org/collectionspace/services/batch/nuxeo/CreateAndLinkLoanOutBatchJob.java +++ b/services/batch/service/src/main/java/org/collectionspace/services/batch/nuxeo/CreateAndLinkLoanOutBatchJob.java @@ -1,102 +1,38 @@ package org.collectionspace.services.batch.nuxeo; -import java.util.ArrayList; +import java.util.Arrays; import java.util.List; import javax.ws.rs.core.Response; -import org.collectionspace.services.batch.BatchInvocable; +import org.collectionspace.services.batch.AbstractBatchInvocable; import org.collectionspace.services.client.CollectionSpaceClientUtils; import org.collectionspace.services.common.NuxeoBasedResource; -import org.collectionspace.services.common.ResourceMap; import org.collectionspace.services.common.api.GregorianCalendarDateTimeUtils; -import org.collectionspace.services.common.context.ServiceContext; import org.collectionspace.services.common.invocable.InvocationContext; -import org.collectionspace.services.common.invocable.InvocationResults; import org.collectionspace.services.client.LoanoutClient; import org.collectionspace.services.client.RelationClient; -public class CreateAndLinkLoanOutBatchJob implements BatchInvocable { +public class CreateAndLinkLoanOutBatchJob extends AbstractBatchInvocable { - private static ArrayList invocationModes = null; - private InvocationContext invocationCtx; - private ServiceContext ctx; - private int completionStatus; - private ResourceMap resourceMap; - private InvocationResults results; - private InvocationError errorInfo; private final String RELATION_TYPE = "affects"; private final String LOAN_DOCTYPE = "LoanOut"; private final String RELATION_PREDICATE_DISP = "affects"; - protected final int CREATED_STATUS = Response.Status.CREATED.getStatusCode(); - protected final int BAD_REQUEST_STATUS = Response.Status.BAD_REQUEST.getStatusCode(); - protected final int INT_ERROR_STATUS = Response.Status.INTERNAL_SERVER_ERROR.getStatusCode(); public CreateAndLinkLoanOutBatchJob() { - CreateAndLinkLoanOutBatchJob.setupClassStatics(); - invocationCtx = null; - completionStatus = STATUS_UNSTARTED; - resourceMap = null; - results = new InvocationResults(); - errorInfo = null; + setSupportedInvocationModes(Arrays.asList(INVOCATION_MODE_SINGLE, INVOCATION_MODE_LIST)); } - - private static void setupClassStatics() { - if(invocationModes == null ) { - invocationModes = new ArrayList(1); - invocationModes.add(INVOCATION_MODE_SINGLE); - invocationModes.add(INVOCATION_MODE_LIST); - } - } - - /** - * @return a set of modes that this plugin can support on invocation. Must be non-empty. - */ - public List getSupportedInvocationModes() { - return CreateAndLinkLoanOutBatchJob.invocationModes; - } - - @Override - public void setServiceContext(ServiceContext context) { - this.ctx = context; - } - - @Override - public ServiceContext getServiceContext() { - return ctx; - } - - @Override - public InvocationContext getInvocationContext() { - return invocationCtx; - } - /** - * Sets the invocation context for the batch job. Called before run(). - * @param context an instance of InvocationContext. - */ - @Override - public void setInvocationContext(InvocationContext context) { - this.invocationCtx = context; - } - - /** - * Sets the invocation context for the batch job. Called before run(). - * @param invocationCtx an instance of InvocationContext. - */ - public void setResourceMap(ResourceMap resourceMap) { - this.resourceMap = resourceMap; - } - /** * The main work logic of the batch job. Will be called after setContext. */ + @Override public void run() { completionStatus = STATUS_MIN_PROGRESS; try { // First, create the Loanout - if(createLoan() != STATUS_ERROR) { + if (createLoan() != STATUS_ERROR) { if(INVOCATION_MODE_SINGLE.equalsIgnoreCase(invocationCtx.getMode())) { if(createRelation(results.getPrimaryURICreated(), invocationCtx.getSingleCSID()) != STATUS_ERROR) { @@ -151,8 +87,8 @@ public class CreateAndLinkLoanOutBatchJob implements BatchInvocable { // First, create the Loanout // We fetch the resource class by service name - NuxeoBasedResource resource = (NuxeoBasedResource) resourceMap.get( LoanoutClient.SERVICE_NAME); - Response response = resource.create(resourceMap, null, loanoutPayload); + NuxeoBasedResource resource = (NuxeoBasedResource) getResourceMap().get( LoanoutClient.SERVICE_NAME); + Response response = resource.create(getResourceMap(), null, loanoutPayload); if(response.getStatus() != CREATED_STATUS) { completionStatus = STATUS_ERROR; errorInfo = new InvocationError(INT_ERROR_STATUS, @@ -177,8 +113,8 @@ public class CreateAndLinkLoanOutBatchJob implements BatchInvocable { + ""+RELATION_TYPE+"" + ""+RELATION_PREDICATE_DISP+"" + ""; - NuxeoBasedResource resource = (NuxeoBasedResource) resourceMap.get(RelationClient.SERVICE_NAME); - Response response = resource.create(resourceMap, null, relationPayload); + NuxeoBasedResource resource = (NuxeoBasedResource) getResourceMap().get(RelationClient.SERVICE_NAME); + Response response = resource.create(getResourceMap(), null, relationPayload); if(response.getStatus() != CREATED_STATUS) { completionStatus = STATUS_ERROR; errorInfo = new InvocationError(INT_ERROR_STATUS, @@ -187,32 +123,4 @@ public class CreateAndLinkLoanOutBatchJob implements BatchInvocable { } return completionStatus; } - - /** - * @return one of the STATUS_* constants, or a value from 1-99 to indicate progress. - * Implementations need not support partial completion (progress) values, and can transition - * from STATUS_MIN_PROGRESS to STATUS_COMPLETE. - */ - public int getCompletionStatus() { - return completionStatus; - } - - /** - * @return information about the batch job actions and results - */ - public InvocationResults getResults() { - if(completionStatus != STATUS_COMPLETE) - return null; - return results; - } - - /** - * @return a user-presentable note when an error occurs in batch processing. Will only - * be called if getCompletionStatus() returns STATUS_ERROR. - */ - public InvocationError getErrorInfo() { - return errorInfo; - } - - } diff --git a/services/batch/service/src/main/java/org/collectionspace/services/batch/nuxeo/TestBatchJob.java b/services/batch/service/src/main/java/org/collectionspace/services/batch/nuxeo/TestBatchJob.java new file mode 100644 index 000000000..0358cf9f1 --- /dev/null +++ b/services/batch/service/src/main/java/org/collectionspace/services/batch/nuxeo/TestBatchJob.java @@ -0,0 +1,20 @@ +package org.collectionspace.services.batch.nuxeo; + +import java.util.Arrays; + +import org.collectionspace.services.batch.AbstractBatchInvocable; + +public class TestBatchJob extends AbstractBatchInvocable { + + public TestBatchJob() { + super(); + setSupportedInvocationModes(Arrays.asList(INVOCATION_MODE_SINGLE, INVOCATION_MODE_LIST, + INVOCATION_MODE_GROUP, INVOCATION_MODE_NO_CONTEXT)); + } + + @Override + public void run() { + // An empty batch job used just for testing. + } + +} diff --git a/services/batch/service/src/main/java/org/collectionspace/services/batch/nuxeo/UpdateObjectLocationBatchJob.java b/services/batch/service/src/main/java/org/collectionspace/services/batch/nuxeo/UpdateObjectLocationBatchJob.java index b4ea9c4e5..add7e158e 100644 --- a/services/batch/service/src/main/java/org/collectionspace/services/batch/nuxeo/UpdateObjectLocationBatchJob.java +++ b/services/batch/service/src/main/java/org/collectionspace/services/batch/nuxeo/UpdateObjectLocationBatchJob.java @@ -14,7 +14,6 @@ import javax.ws.rs.core.PathSegment; import javax.ws.rs.core.UriInfo; import org.collectionspace.services.batch.AbstractBatchInvocable; -import org.collectionspace.services.batch.UriInfoImpl; import org.collectionspace.services.client.AbstractCommonListUtils; import org.collectionspace.services.client.CollectionObjectClient; import org.collectionspace.services.client.IClientQueryParams; @@ -27,6 +26,7 @@ import org.collectionspace.services.common.ResourceMap; import org.collectionspace.services.common.api.RefNameUtils; import org.collectionspace.services.common.api.Tools; import org.collectionspace.services.common.invocable.InvocationResults; +import org.collectionspace.services.common.query.UriInfoImpl; import org.collectionspace.services.jaxb.AbstractCommonList; import org.dom4j.DocumentException; //import org.jboss.resteasy.specimpl.UriInfoImpl; @@ -137,7 +137,6 @@ public class UpdateObjectLocationBatchJob extends AbstractBatchInvocable { ResourceMap resourcemap = getResourceMap(); NuxeoBasedResource collectionObjectResource = (NuxeoBasedResource) resourcemap.get(CollectionObjectClient.SERVICE_NAME); NuxeoBasedResource movementResource = (NuxeoBasedResource) resourcemap.get(MovementClient.SERVICE_NAME); - String computedCurrentLocation; long numUpdated = 0; long processed = 0; @@ -397,8 +396,11 @@ public class UpdateObjectLocationBatchJob extends AbstractBatchInvocable { } UriInfo uriInfo = this.setupQueryParamForUpdateRecords(); // Determines if we'll updated the updateAt and updatedBy core values - byte[] response = collectionObjectResource.update(resourcemap, uriInfo, collectionObjectCsid, - collectionObjectUpdatePayload); + if (logger.isDebugEnabled()) { + byte[] responseBytes = collectionObjectResource.update(resourcemap, uriInfo, collectionObjectCsid, + collectionObjectUpdatePayload); + logger.debug(String.format("Batch resource: Resonse from collectionobject (cataloging record) update: %s", new String(responseBytes))); + } numUpdated++; if (logger.isTraceEnabled()) { @@ -635,7 +637,6 @@ public class UpdateObjectLocationBatchJob extends AbstractBatchInvocable { } } - private List getMemberCsidsFromGroup(String serviceName, String groupCsid) throws URISyntaxException, DocumentException { ResourceMap resourcemap = getResourceMap(); NuxeoBasedResource resource = (NuxeoBasedResource) resourcemap.get(serviceName); @@ -659,7 +660,6 @@ public class UpdateObjectLocationBatchJob extends AbstractBatchInvocable { boolean morePages = true; long currentPage = 0; - long totalItems = 0; long pageSize = DEFAULT_PAGE_SIZE; List noContextCsids = new ArrayList(); diff --git a/services/client/src/main/java/org/collectionspace/services/client/PoxPayload.java b/services/client/src/main/java/org/collectionspace/services/client/PoxPayload.java index 76577afe9..af3648d4c 100644 --- a/services/client/src/main/java/org/collectionspace/services/client/PoxPayload.java +++ b/services/client/src/main/java/org/collectionspace/services/client/PoxPayload.java @@ -63,11 +63,11 @@ public abstract class PoxPayload { private void setDomDocument(Document dom) throws DocumentException { this.domDocument = dom; String label = domDocument.getRootElement().getName(); - if (label != null) { + if (label != null && label.equalsIgnoreCase("document")) { this.payloadName = label; - } else if (logger.isWarnEnabled() == true) { - logger.warn("Incoming message payload is missing a name/label."); - logger.warn(this.xmlPayload); + } else { + String msg = "The following incoming request payload is missing the root element or is otherwise malformed. For example valid payloads, see https://wiki.collectionspace.org/display/DOC/Common+Services+REST+API+documentation"; + throw new DocumentException(msg + '\n' + this.xmlPayload); } parseParts(); } @@ -295,7 +295,7 @@ public abstract class PoxPayload { } /** - * Attempts to marshal a DOM4j element (for a part) into an instance of a JAXB object + * Attempts to unmarshal a DOM4j element (for a part) into an instance of a JAXB object * * @param elementInput the element input * @return the object @@ -310,9 +310,9 @@ public abstract class PoxPayload { result = um.unmarshal( new StreamSource(new StringReader(elementInput.asXML()))); } catch (Exception e) { - if (logger.isTraceEnabled() == true) { - logger.trace(e.getMessage()); - } + String msg = String.format("Could not unmarshal XML payload '%s' into a JAXB object.", + elementInput.getName()); + logger.warn(msg); } return result; @@ -345,7 +345,9 @@ public abstract class PoxPayload { Document doc = DocumentHelper.parseText(text); result = doc.getRootElement(); //FIXME: REM - call .detach() to free the element } catch (Exception e) { - e.printStackTrace(); //FIXME: REM - Please use proper logger.isWarning() statement + String msg = String.format("Could not marshal JAXB object '%s' to an XML element.", + jaxbObject.toString()); + logger.error(msg); } return result; diff --git a/services/common-api/src/main/java/org/collectionspace/services/common/api/Tools.java b/services/common-api/src/main/java/org/collectionspace/services/common/api/Tools.java index 16d356ee8..427228b76 100644 --- a/services/common-api/src/main/java/org/collectionspace/services/common/api/Tools.java +++ b/services/common-api/src/main/java/org/collectionspace/services/common/api/Tools.java @@ -241,24 +241,25 @@ public class Tools { * presentation of error messages to clients. * @param includeLines if zero, return all lines of stack trace, otherwise return number of lines from top. */ - public static String errorToString(Throwable e, boolean stackTraceOnException, int includeLines){ - if (e==null){ - return ""; - } - String s = e.toString() + "\r\n -- message: " + e.getMessage(); - - StringBuffer causeBuffer = new StringBuffer(); - Throwable cause = e.getCause(); - while (cause != null){ - causeBuffer.append(cause.getClass().getName()+"::"+cause.getMessage()+"\r\n"); - cause = cause.getCause(); - } - if (causeBuffer.length()>0) s = s + "\r\n -- Causes: "+causeBuffer.toString(); + public static String errorToString(Throwable e, boolean stackTraceOnException, int includeLines) { + if (e == null) { + return ""; + } + String s = "\r\n -- Exception: " + e.getClass().getCanonicalName() + "\r\n -- Message: " + e.getMessage(); + StringBuffer causeBuffer = new StringBuffer(); + Throwable cause = e.getCause(); + while (cause != null) { + causeBuffer.append(cause.getClass().getName() + "::" + cause.getMessage() + "\r\n"); + cause = cause.getCause(); + } + if (causeBuffer.length() > 0) { + s = s + "\r\n -- Causes: " + causeBuffer.toString(); + } - s = s + "\r\n -- Stack Trace: \r\n -- " + getStackTrace(e, includeLines); - return s; - } + s = s + "\r\n -- Stack Trace: \r\n -- " + getStackTrace(e, includeLines); + return s; + } /** * Return a set of properties from a properties file. diff --git a/services/common/src/main/java/org/collectionspace/services/common/AbstractCollectionSpaceResourceImpl.java b/services/common/src/main/java/org/collectionspace/services/common/AbstractCollectionSpaceResourceImpl.java index 1a5dde637..d89d51bbc 100644 --- a/services/common/src/main/java/org/collectionspace/services/common/AbstractCollectionSpaceResourceImpl.java +++ b/services/common/src/main/java/org/collectionspace/services/common/AbstractCollectionSpaceResourceImpl.java @@ -37,9 +37,8 @@ import javax.ws.rs.core.Request; import javax.ws.rs.core.Response; import javax.ws.rs.core.UriInfo; +import org.collectionspace.services.authorization.PermissionException; import org.collectionspace.services.client.CollectionSpaceClient; -import org.collectionspace.services.client.PoxPayloadIn; -import org.collectionspace.services.client.PoxPayloadOut; import org.collectionspace.services.common.CSWebApplicationException; import org.collectionspace.services.common.api.Tools; import org.collectionspace.services.common.config.ServiceConfigUtils; @@ -501,6 +500,10 @@ public abstract class AbstractCollectionSpaceResourceImpl response = Response.status(Response.Status.UNAUTHORIZED).entity(serviceMsg + e.getMessage()).type("text/plain").build(); result = new CSWebApplicationException(e, response); + } else if (e instanceof PermissionException) { + response = Response.status(Response.Status.FORBIDDEN).entity(serviceMsg + e.getMessage()).type("text/plain").build(); + result = new CSWebApplicationException(e, response); + } else if (e instanceof DocumentNotFoundException) { // // Don't log this error unless we're in 'trace' mode @@ -534,6 +537,10 @@ public abstract class AbstractCollectionSpaceResourceImpl // return new WebApplicationException(e, code); result = new CSWebApplicationException(e, response); + } else if (e instanceof org.dom4j.DocumentException) { + int code = Response.Status.BAD_REQUEST.getStatusCode(); + response = Response.status(code).entity(serviceMsg + e.getMessage()).type("text/plain").build(); + result = new CSWebApplicationException(e, response); } else if (e instanceof CSWebApplicationException) { // subresource may have already thrown this exception // so just pass it on diff --git a/services/common/src/main/java/org/collectionspace/services/common/CollectionSpaceResource.java b/services/common/src/main/java/org/collectionspace/services/common/CollectionSpaceResource.java index 2c06dd3e4..dd7b84db3 100644 --- a/services/common/src/main/java/org/collectionspace/services/common/CollectionSpaceResource.java +++ b/services/common/src/main/java/org/collectionspace/services/common/CollectionSpaceResource.java @@ -62,7 +62,7 @@ public interface CollectionSpaceResource { * @param ctx the ctx * @return the repository client */ - public RepositoryClient getRepositoryClient(ServiceContext ctx); + public RepositoryClient getRepositoryClient(ServiceContext ctx); /** * Gets the storage client. diff --git a/services/common/src/main/java/org/collectionspace/services/common/NuxeoBasedResource.java b/services/common/src/main/java/org/collectionspace/services/common/NuxeoBasedResource.java index 37508b209..ccfc4344a 100644 --- a/services/common/src/main/java/org/collectionspace/services/common/NuxeoBasedResource.java +++ b/services/common/src/main/java/org/collectionspace/services/common/NuxeoBasedResource.java @@ -158,7 +158,7 @@ public abstract class NuxeoBasedResource @Context UriInfo uriInfo, String xmlPayload) { uriInfo = new UriInfoWrapper(uriInfo); - return this.create(null, resourceMap, uriInfo, xmlPayload); + return this.create(null, resourceMap, uriInfo, xmlPayload); } public Response create(ServiceContext parentCtx, // REM: 8/13/2012 - Some sub-classes will override this method -e.g., MediaResource does. diff --git a/services/common/src/main/java/org/collectionspace/services/common/ResourceMap.java b/services/common/src/main/java/org/collectionspace/services/common/ResourceMap.java index d11f69c73..259baf684 100644 --- a/services/common/src/main/java/org/collectionspace/services/common/ResourceMap.java +++ b/services/common/src/main/java/org/collectionspace/services/common/ResourceMap.java @@ -5,6 +5,6 @@ import java.util.Map; /* * Maps service names to Resource instances. Use the Service Client Class to get the service name. */ -public interface ResourceMap extends Map> { +public interface ResourceMap extends Map> { } diff --git a/services/common/src/main/java/org/collectionspace/services/common/ResourceMapImpl.java b/services/common/src/main/java/org/collectionspace/services/common/ResourceMapImpl.java index 85407e904..9c31fab1e 100644 --- a/services/common/src/main/java/org/collectionspace/services/common/ResourceMapImpl.java +++ b/services/common/src/main/java/org/collectionspace/services/common/ResourceMapImpl.java @@ -2,10 +2,7 @@ package org.collectionspace.services.common; import java.util.HashMap; -import org.collectionspace.services.client.PoxPayloadIn; -import org.collectionspace.services.client.PoxPayloadOut; - -public class ResourceMapImpl extends HashMap> implements ResourceMap { +public class ResourceMapImpl extends HashMap> implements ResourceMap { /** * diff --git a/services/common/src/main/java/org/collectionspace/services/common/ServiceMain.java b/services/common/src/main/java/org/collectionspace/services/common/ServiceMain.java index 9821be5f7..d7fa1a585 100644 --- a/services/common/src/main/java/org/collectionspace/services/common/ServiceMain.java +++ b/services/common/src/main/java/org/collectionspace/services/common/ServiceMain.java @@ -822,10 +822,10 @@ public class ServiceMain { */ private synchronized void populateUriTemplateRegistry() { if (uriTemplateRegistry.isEmpty()) { - CollectionSpaceResource resource = null; + CollectionSpaceResource resource = null; ResourceMap resourceMap = getJaxRSResourceMap(); - Set> entrySet = resourceMap.entrySet(); - for (Map.Entry entry : entrySet) { + Set>> entrySet = resourceMap.entrySet(); + for (Map.Entry> entry : entrySet) { resource = entry.getValue(); Map entries = resource.getUriRegistryEntries(); diff --git a/services/common/src/main/java/org/collectionspace/services/common/authorization_mgt/ActionGroup.java b/services/common/src/main/java/org/collectionspace/services/common/authorization_mgt/ActionGroup.java new file mode 100644 index 000000000..ea86045d6 --- /dev/null +++ b/services/common/src/main/java/org/collectionspace/services/common/authorization_mgt/ActionGroup.java @@ -0,0 +1,200 @@ +package org.collectionspace.services.common.authorization_mgt; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +import org.collectionspace.services.authorization.perms.ActionType; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class ActionGroup { + final static Logger logger = LoggerFactory.getLogger(ActionGroup.class); + + String name; + ActionType[] actions = {}; + + static private String toString(ActionType[] actionTypes) { + String result = null; + + if (actionTypes.length > 0) { + result = new String(); + for (ActionType actionType: actionTypes) { + result = result + actionType.value() + ':'; + } + } + + return result; + } + + static private String toString(List charList) { + String result = null; + + if (charList.isEmpty() == false) { + result = new String(); + for (Character c: charList) { + result = result + c; + } + } + + return result; + } + + static int valueOf(Character c) { + int result = 0; + + switch (c) { + case 'C': + result = 1; + break; + case 'R': + result = 2; + break; + case 'U': + result = 3; + break; + case 'D': + result = 4; + break; + case 'I': + result = 5; + break; + case 'L': + result = 6; + break; + default: + result = 0; + } + + return result; + } + + static int valueOf(ActionType actionType) { + int result = 0; + + switch (actionType) { + case CREATE: + result = 1; + break; + case READ: + result = 2; + break; + case UPDATE: + result = 3; + break; + case DELETE: + result = 4; + break; + case RUN: + result = 5; + break; + case SEARCH: + result = 6; + break; + default: + result = 0; + } + + return result; + } + + /** + * Factory method to create an ActionGroup from an action string -i.e., "CRUDL", "RL", "CRUL", etc. + * @param actionString + * @return + */ + static public ActionGroup creatActionGroup(String actionString) { + ActionGroup result = null; + + Set actionCharSet = new HashSet(); + Set actionTypeSet = new HashSet(); + for (char c : actionString.toCharArray()) { + switch (c) { + case 'C': + actionTypeSet.add(ActionType.CREATE); + actionCharSet.add(c); + break; + + case 'R': + actionTypeSet.add(ActionType.READ); + actionCharSet.add(c); + break; + + case 'U': + actionTypeSet.add(ActionType.UPDATE); + actionCharSet.add(c); + break; + + case 'D': + actionTypeSet.add(ActionType.DELETE); + actionCharSet.add(c); + break; + + case 'I': + actionTypeSet.add(ActionType.RUN); + actionCharSet.add(c); + break; + + case 'L': + actionTypeSet.add(ActionType.SEARCH); + actionCharSet.add(c); + break; + + default: + System.out.println(String.format("Unknown action character '%c'.", c)); + } + } + + if (actionTypeSet.size() > 0) { + // sort for readability + ArrayList actionCharList = new ArrayList(actionCharSet); + Collections.sort(actionCharList, new Comparator() { + @Override + public int compare(Character c1, Character c2) { + if (valueOf(c1) > valueOf(c2)) { + return 1; + } else if (valueOf(c1) < valueOf(c2)) { + return -1; + } else { + return 0; + } + } + }); + + // sort for readability + ArrayList actionTypeList = new ArrayList(actionTypeSet); + Collections.sort(actionTypeList, new Comparator() { + @Override + public int compare(ActionType a1, ActionType a2) { + if (valueOf(a1) > valueOf(a2)) { + return 1; + } else if (valueOf(a1) < valueOf(a2)) { + return -1; + } else { + return 0; + } + } + }); + + result = new ActionGroup(); + result.name = toString(actionCharList); + result.actions = actionTypeList.toArray(result.actions); + } + + logger.trace(String.format("Create new action group containing these actions name:'%s' actions:'%s'", + result.name, toString(result.actions))); + + return result; + } + + public String getName() { + return name; + } + + public ActionType[] getActions() { + return this.actions; + } +} diff --git a/services/common/src/main/java/org/collectionspace/services/common/authorization_mgt/AuthorizationCommon.java b/services/common/src/main/java/org/collectionspace/services/common/authorization_mgt/AuthorizationCommon.java index 01c03ceb5..014c460e6 100644 --- a/services/common/src/main/java/org/collectionspace/services/common/authorization_mgt/AuthorizationCommon.java +++ b/services/common/src/main/java/org/collectionspace/services/common/authorization_mgt/AuthorizationCommon.java @@ -68,32 +68,18 @@ public class AuthorizationCommon { // for READ-ONLY final public static String ACTIONGROUP_RL_NAME = "RL"; final public static ActionType[] ACTIONSET_RL = {ActionType.READ, ActionType.SEARCH}; - - - /* - * Inner class to deal with predefined ADMIN and READER action groupds - */ - public class ActionGroup { - String name; - ActionType[] actions; - - public String getName() { - return name; - } - } static ActionGroup ACTIONGROUP_CRUDL; static ActionGroup ACTIONGROUP_RL; // A static block to initialize the predefined action groups static { - AuthorizationCommon ac = new AuthorizationCommon(); // For admin - ACTIONGROUP_CRUDL = ac.new ActionGroup(); + ACTIONGROUP_CRUDL = new ActionGroup(); ACTIONGROUP_CRUDL.name = ACTIONGROUP_CRUDL_NAME; ACTIONGROUP_CRUDL.actions = ACTIONSET_CRUDL; // For reader - ACTIONGROUP_RL = ac.new ActionGroup(); + ACTIONGROUP_RL = new ActionGroup(); ACTIONGROUP_RL.name = ACTIONGROUP_RL_NAME; ACTIONGROUP_RL.actions = ACTIONSET_RL; diff --git a/services/common/src/main/java/org/collectionspace/services/common/context/RemoteServiceContextImpl.java b/services/common/src/main/java/org/collectionspace/services/common/context/RemoteServiceContextImpl.java index e9dd05744..0b1a66a5b 100644 --- a/services/common/src/main/java/org/collectionspace/services/common/context/RemoteServiceContextImpl.java +++ b/services/common/src/main/java/org/collectionspace/services/common/context/RemoteServiceContextImpl.java @@ -117,7 +117,8 @@ public class RemoteServiceContextImpl /* * Returns the name of the service's acting repository. Gets this from the tenant and service bindings files */ - public String getRepositoryName() throws Exception { + @Override + public String getRepositoryName() throws Exception { String result = null; TenantBindingConfigReaderImpl tenantBindingConfigReader = ServiceMain.getInstance().getTenantBindingConfigReader(); @@ -181,10 +182,10 @@ public class RemoteServiceContextImpl * @return * @throws Exception */ - public CollectionSpaceResource getResource(ServiceContext ctx) throws Exception { + public CollectionSpaceResource getResource(ServiceContext ctx) throws Exception { CollectionSpaceResource result = null; - ResourceMap resourceMap = ctx.getResourceMap(); + ResourceMap resourceMap = ctx.getResourceMap(); String resourceName = ctx.getClient().getServiceName(); result = (CollectionSpaceResource) resourceMap.get(resourceName); @@ -208,7 +209,8 @@ public class RemoteServiceContextImpl /** * @param map the map of service names to resource instances. */ - public void setResourceMap(ResourceMap map) { + @Override + public void setResourceMap(ResourceMap map) { this.resourceMap = map; } @@ -218,7 +220,7 @@ public class RemoteServiceContextImpl * @see org.collectionspace.services.common.context.RemoteServiceContext#getLocalContext(java.lang.String) */ @Override - public ServiceContext getLocalContext(String localContextClassName) throws Exception { + public ServiceContext getLocalContext(String localContextClassName) throws Exception { ClassLoader cloader = Thread.currentThread().getContextClassLoader(); Class ctxClass = cloader.loadClass(localContextClassName); if (!ServiceContext.class.isAssignableFrom(ctxClass)) { @@ -226,8 +228,8 @@ public class RemoteServiceContextImpl + " implementation of " + ServiceContext.class.getName()); } - Constructor ctor = ctxClass.getConstructor(java.lang.String.class); - ServiceContext ctx = (ServiceContext) ctor.newInstance(getServiceName()); + Constructor ctor = ctxClass.getConstructor(java.lang.String.class); + ServiceContext ctx = (ServiceContext) ctor.newInstance(getServiceName()); return ctx; } diff --git a/services/common/src/main/java/org/collectionspace/services/common/invocable/Invocable.java b/services/common/src/main/java/org/collectionspace/services/common/invocable/Invocable.java index efd24294d..f0863c139 100644 --- a/services/common/src/main/java/org/collectionspace/services/common/invocable/Invocable.java +++ b/services/common/src/main/java/org/collectionspace/services/common/invocable/Invocable.java @@ -24,6 +24,9 @@ package org.collectionspace.services.common.invocable; import org.collectionspace.services.common.invocable.InvocationContext; import java.util.List; + +import org.collectionspace.services.client.PoxPayloadIn; +import org.collectionspace.services.client.PoxPayloadOut; import org.collectionspace.services.common.api.Tools; import org.collectionspace.services.common.context.ServiceContext; @@ -64,11 +67,13 @@ public interface Invocable { return (Tools.notBlank(message)) ? message : "No error message provided"; } } + public String INVOCATION_MODE_SINGLE = "single"; public String INVOCATION_MODE_LIST = "list"; public String INVOCATION_MODE_GROUP = "group"; public String INVOCATION_MODE_NO_CONTEXT = "nocontext"; //public String INVOCATION_MODE_QUERY = "query"; NYI + public final int STATUS_ERROR = -1; public final int STATUS_UNSTARTED = 0; public final int STATUS_MIN_PROGRESS = 1; @@ -113,9 +118,9 @@ public interface Invocable { /* * Save a handle to the JAX-RS related service context */ - void setServiceContext(ServiceContext context); + void setServiceContext(ServiceContext context); - ServiceContext getServiceContext(); + ServiceContext getServiceContext(); InvocationContext getInvocationContext(); } diff --git a/services/batch/service/src/main/java/org/collectionspace/services/batch/UriInfoImpl.java b/services/common/src/main/java/org/collectionspace/services/common/query/UriInfoImpl.java similarity index 99% rename from services/batch/service/src/main/java/org/collectionspace/services/batch/UriInfoImpl.java rename to services/common/src/main/java/org/collectionspace/services/common/query/UriInfoImpl.java index 237c156e4..90e2be1a1 100644 --- a/services/batch/service/src/main/java/org/collectionspace/services/batch/UriInfoImpl.java +++ b/services/common/src/main/java/org/collectionspace/services/common/query/UriInfoImpl.java @@ -1,4 +1,4 @@ -package org.collectionspace.services.batch; +package org.collectionspace.services.common.query; import org.jboss.resteasy.specimpl.MultivaluedMapImpl; import org.jboss.resteasy.specimpl.PathSegmentImpl; 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 f1958f9ba..30182024f 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 @@ -159,7 +159,7 @@ public class SecurityInterceptor implements PreProcessInterceptor, PostProcessIn checkActive(); // - // All active users are allowed to the *their* (we enforce this) current list of permissions. If this is not + // All active users are allowed to see the *their* (we enforce this) current list of permissions. If this is not // the request, then we'll do a full AuthZ check. // if (resName.equalsIgnoreCase(ACCOUNT_PERMISSIONS) != true) { //see comment immediately above diff --git a/services/common/src/main/java/org/collectionspace/services/common/security/UnauthorizedException.java b/services/common/src/main/java/org/collectionspace/services/common/security/UnauthorizedException.java index 730a7d2cb..353bbeec4 100644 --- a/services/common/src/main/java/org/collectionspace/services/common/security/UnauthorizedException.java +++ b/services/common/src/main/java/org/collectionspace/services/common/security/UnauthorizedException.java @@ -33,7 +33,11 @@ import org.collectionspace.services.common.ServiceException; */ public class UnauthorizedException extends ServiceException { - final public static int HTTP_CODE = 401; + /** + * + */ + private static final long serialVersionUID = 1L; + final public static int HTTP_CODE = 401; /** * Creates a new instance of UnauthorizedException without detail message. diff --git a/services/common/src/main/java/org/collectionspace/services/common/storage/jpa/JpaDocumentHandler.java b/services/common/src/main/java/org/collectionspace/services/common/storage/jpa/JpaDocumentHandler.java index 9ce64108e..c2d77bd06 100644 --- a/services/common/src/main/java/org/collectionspace/services/common/storage/jpa/JpaDocumentHandler.java +++ b/services/common/src/main/java/org/collectionspace/services/common/storage/jpa/JpaDocumentHandler.java @@ -35,7 +35,8 @@ public abstract class JpaDocumentHandler * @return the tL * @throws Exception the exception */ - public TL extractPagingInfo(TL theCommonList, DocumentWrapper wrapDoc) + @Override + public TL extractPagingInfo(TL theCommonList, DocumentWrapper wrapDoc) throws Exception { AbstractCommonList commonList = (AbstractCommonList) theCommonList; @@ -54,7 +55,8 @@ public abstract class JpaDocumentHandler return (TL) commonList; } - public Lifecycle getLifecycle(String docTypeName) { + @Override + public Lifecycle getLifecycle(String docTypeName) { Lifecycle result = new Lifecycle(); result.setName("Life cycles are not supported by the JPA-based services."); return result; // NOTE: As of 3/2012, none of the JPA-based services support a life cycle type. 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 4baecf273..68c64bbcd 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 @@ -388,6 +388,7 @@ public abstract class RemoteDocumentModelHandlerImpl Map partsMetaMap = getServiceContext().getPartsMetadata(); //iterate over parts received and fill those parts + boolean werePartsFilled = false; List inputParts = input.getParts(); for (PayloadInputPart part : inputParts) { @@ -404,6 +405,14 @@ public abstract class RemoteDocumentModelHandlerImpl continue; } fillPart(part, docModel, partMeta, action, ctx); + werePartsFilled = true; + } + + if (werePartsFilled == false) { + String msg = String.format("%s request failed because there were no XML payload parts in the request.", + action.toString()); + logger.error(msg); + throw new BadRequestException(msg); } } diff --git a/services/jaxb/src/main/resources/invocationContext.xsd b/services/jaxb/src/main/resources/invocationContext.xsd index 1665c8ab2..b4f234722 100644 --- a/services/jaxb/src/main/resources/invocationContext.xsd +++ b/services/jaxb/src/main/resources/invocationContext.xsd @@ -8,45 +8,38 @@ $LastChangedRevision: 2316 $ $LastChangedDate: 2010-06-02 16:03:51 -0700 (Wed, 02 Jun 2010) $ --> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + -- 2.47.3