]> git.aero2k.de Git - tmp/jakarta-migration.git/commitdiff
DRYD-221: Roles and perms refactoring of transaction handling.
authorremillet <remillet@yahoo.com>
Sat, 13 Jan 2018 11:51:11 +0000 (03:51 -0800)
committerremillet <remillet@yahoo.com>
Sat, 13 Jan 2018 11:51:11 +0000 (03:51 -0800)
80 files changed:
services/IntegrationTests/src/main/java/org/collectionspace/services/IntegrationTests/xmlreplay/ServiceResult.java
services/IntegrationTests/src/main/java/org/collectionspace/services/IntegrationTests/xmlreplay/XmlReplay.java
services/IntegrationTests/src/test/resources/test-data/xmlreplay/batch/accountroles-post-bigbird.xml
services/IntegrationTests/src/test/resources/test-data/xmlreplay/batch/permissionroles-post-bird.xml
services/IntegrationTests/src/test/resources/test-data/xmlreplay/batch/role-create-bird.xml
services/IntegrationTests/src/test/resources/test-data/xmlreplay/security.xml
services/IntegrationTests/src/test/resources/test-data/xmlreplay/security/1-bigbird-permission.xml
services/IntegrationTests/src/test/resources/test-data/xmlreplay/security/1-vocab.xml [new file with mode: 0644]
services/IntegrationTests/src/test/resources/test-data/xmlreplay/security/10-permissionroles-elmo.xml
services/IntegrationTests/src/test/resources/test-data/xmlreplay/security/11-bigbird-permission-CRU.xml
services/IntegrationTests/src/test/resources/test-data/xmlreplay/security/11-permissionroles-bigbird-CRU.xml
services/IntegrationTests/src/test/resources/test-data/xmlreplay/security/12-bigbird-permission-R.xml
services/IntegrationTests/src/test/resources/test-data/xmlreplay/security/12-permissionroles-bigbird-R.xml
services/IntegrationTests/src/test/resources/test-data/xmlreplay/security/13-permissionroles-bigbird.xml
services/IntegrationTests/src/test/resources/test-data/xmlreplay/security/14-permissionroles-bogus.xml [new file with mode: 0644]
services/IntegrationTests/src/test/resources/test-data/xmlreplay/security/3-role-test-cm.xml
services/IntegrationTests/src/test/resources/test-data/xmlreplay/security/3a-update-role-test-cm.xml
services/IntegrationTests/src/test/resources/test-data/xmlreplay/security/4-role-intern.xml
services/IntegrationTests/src/test/resources/test-data/xmlreplay/security/7-accountroles-bigbird.xml
services/IntegrationTests/src/test/resources/test-data/xmlreplay/security/8-account-roles-elmo.xml
services/IntegrationTests/src/test/resources/test-data/xmlreplay/security/9-permissionroles-bigbird.xml
services/IntegrationTests/src/test/resources/test-data/xmlreplay/security/create-account-grover.xml [new file with mode: 0644]
services/IntegrationTests/src/test/resources/test-data/xmlreplay/security/res/get1-loansin-CRUDL.res.xml [new file with mode: 0644]
services/IntegrationTests/src/test/resources/test-data/xmlreplay/security/slipOutPerm-update.xml [new file with mode: 0644]
services/IntegrationTests/src/test/resources/test-data/xmlreplay/security/slipOutPerm.xml [new file with mode: 0644]
services/IntegrationTests/src/test/resources/test-data/xmlreplay/security/slipOutRole.xml [new file with mode: 0644]
services/IntegrationTests/src/test/resources/test-data/xmlreplay/security/slipOutVocab-1.xml [new file with mode: 0644]
services/IntegrationTests/src/test/resources/test-data/xmlreplay/security/slipOutVocab-2.xml [new file with mode: 0644]
services/account/jaxb/src/main/resources/accounts_common.xsd
services/account/pstore/src/main/resources/db/postgresql/account.sql
services/account/service/src/main/java/org/collectionspace/services/account/AccountResource.java
services/account/service/src/main/java/org/collectionspace/services/account/AccountRoleSubResource.java
services/account/service/src/main/java/org/collectionspace/services/account/storage/AccountDocumentHandler.java
services/account/service/src/main/java/org/collectionspace/services/account/storage/AccountRoleDocumentHandler.java
services/account/service/src/main/java/org/collectionspace/services/account/storage/AccountStorageClient.java
services/account/service/src/main/java/org/collectionspace/services/account/storage/csidp/TokenStorageClient.java
services/authentication/service/src/main/java/org/collectionspace/authentication/AuthN.java
services/authorization-mgt/client/src/main/java/org/collectionspace/services/client/PermissionClient.java
services/authorization-mgt/client/src/main/java/org/collectionspace/services/client/RoleClient.java
services/authorization-mgt/client/src/test/java/org/collectionspace/services/authorization/client/test/PermissionRoleServiceTest.java
services/authorization-mgt/client/src/test/java/org/collectionspace/services/authorization/client/test/RolePermissionServiceTest.java
services/authorization-mgt/client/src/test/java/org/collectionspace/services/authorization/client/test/RoleServiceTest.java
services/authorization-mgt/import/src/main/java/org/collectionspace/services/authorization/driver/AuthorizationSeedDriver.java
services/authorization-mgt/import/src/main/java/org/collectionspace/services/authorization/importer/AuthorizationGen.java
services/authorization-mgt/import/src/main/java/org/collectionspace/services/authorization/importer/AuthorizationSeed.java
services/authorization-mgt/service/src/main/java/org/collectionspace/services/authorization/RoleResource.java
services/authorization-mgt/service/src/main/java/org/collectionspace/services/authorization/storage/PermissionDocumentHandler.java.txt
services/authorization-mgt/service/src/main/java/org/collectionspace/services/authorization/storage/PermissionValidatorHandler.java
services/authorization-mgt/service/src/main/java/org/collectionspace/services/authorization/storage/RoleDocumentHandler.java
services/authorization-mgt/service/src/main/java/org/collectionspace/services/authorization/storage/RoleValidatorHandler.java
services/authorization/pstore/src/main/resources/db/postgresql/authorization.sql
services/authorization/service/src/main/java/org/collectionspace/services/authorization/AuthZ.java
services/authorization/service/src/main/java/org/collectionspace/services/authorization/spi/CSpacePermissionManager.java
services/authorization/service/src/main/java/org/collectionspace/services/authorization/spring/SpringPermissionManager.java
services/common-api/src/main/java/org/collectionspace/services/common/api/Tools.java
services/common/src/main/java/org/collectionspace/services/authorization/PermissionResource.java
services/common/src/main/java/org/collectionspace/services/authorization/PermissionRoleSubResource.java
services/common/src/main/java/org/collectionspace/services/authorization/storage/AuthorizationDelegate.java
services/common/src/main/java/org/collectionspace/services/authorization/storage/PermissionDocumentHandler.java
services/common/src/main/java/org/collectionspace/services/authorization/storage/PermissionRoleDocumentHandler.java
services/common/src/main/java/org/collectionspace/services/authorization/storage/PermissionStorageConstants.java
services/common/src/main/java/org/collectionspace/services/authorization/storage/RoleStorageConstants.java
services/common/src/main/java/org/collectionspace/services/common/SecurityResourceBase.java
services/common/src/main/java/org/collectionspace/services/common/authorization_mgt/AuthorizationCommon.java
services/common/src/main/java/org/collectionspace/services/common/authorization_mgt/AuthorizationRoleRel.java
services/common/src/main/java/org/collectionspace/services/common/authorization_mgt/PermissionRoleUtil.java
services/common/src/main/java/org/collectionspace/services/common/context/RemoteServiceContextImpl.java
services/common/src/main/java/org/collectionspace/services/common/context/ServiceContext.java
services/common/src/main/java/org/collectionspace/services/common/document/AbstractDocumentHandlerImpl.java
services/common/src/main/java/org/collectionspace/services/common/document/InconsistentStateException.java [new file with mode: 0644]
services/common/src/main/java/org/collectionspace/services/common/document/JaxbUtils.java
services/common/src/main/java/org/collectionspace/services/common/document/TransactionException.java
services/common/src/main/java/org/collectionspace/services/common/storage/StorageClient.java
services/common/src/main/java/org/collectionspace/services/common/storage/TransactionContext.java
services/common/src/main/java/org/collectionspace/services/common/storage/jpa/JPATransactionContext.java
services/common/src/main/java/org/collectionspace/services/common/storage/jpa/JpaRelationshipStorageClient.java
services/common/src/main/java/org/collectionspace/services/common/storage/jpa/JpaStorageClientImpl.java
services/common/src/main/java/org/collectionspace/services/common/storage/jpa/JpaStorageUtils.java
services/common/src/main/java/org/collectionspace/services/nuxeo/client/java/RepositoryClientImpl.java
services/hyperjaxb/src/main/resources/permissions.xsd

index e4e602e02170642e38fcdd9d8d6e968fa6923f00..4736f5da4cf76f3ba2aca8151a8c3aaf070e8500 100644 (file)
@@ -54,6 +54,7 @@ public class ServiceResult {
     public String error = "";
     public String fromTestID = "";
     public String auth = "";
+    public String adminAuth = "";
     public String boundary = "";
     public String payloadStrictness = "";
     public long contentLength = 0;
index 865ffd49ae0f29fff7ae7a2048ccb93e212ab92b..b9ae62c5c34c082656cb21a8ef5e6351e6fd2aaa 100644 (file)
@@ -16,6 +16,7 @@ import java.util.*;
  *   See example usage in calling class XmlReplayTest in services/IntegrationTests, and also in main() in this class.
  *   @author Laramie Crocker
  */
+@SuppressWarnings("unchecked")
 public class XmlReplay {
 
     public XmlReplay(String basedir, String reportsDir){
@@ -93,7 +94,8 @@ public class XmlReplay {
         return reportsList;
     }
 
-    public String toString(){
+    @Override
+       public String toString(){
         return "XmlReplay{"+this.basedir+", "+this.defaultAuthsMap+", "+this.dump+", "+this.reportsDir+'}';
     }
 
@@ -151,7 +153,7 @@ public class XmlReplay {
 
     /** Creates new instances of XmlReplay, one for each controlFile specified in the master,
      *  and setting defaults from this instance, but not sharing ServiceResult objects or maps. */
-    public List<List<ServiceResult>> runMaster(String masterFilename, boolean readOptionsFromMaster) throws Exception {
+       public List<List<ServiceResult>> runMaster(String masterFilename, boolean readOptionsFromMaster) throws Exception {
         List<List<ServiceResult>> list = new ArrayList<List<ServiceResult>>();
         org.dom4j.Document document;
         if (readOptionsFromMaster){
@@ -229,8 +231,8 @@ public class XmlReplay {
         return runTests(testGroupID, "");
     }
 
-    public List<ServiceResult>  autoDelete(String logName){
-        return autoDelete(this.serviceResultsMap, logName, 0);
+    public List<ServiceResult> autoDelete(String logName){
+        return autoDelete(this.defaultAuthsMap, this.serviceResultsMap, logName, 0);
     }
 
     /** Use this method to clean up resources created on the server that returned CSIDs, if you have
@@ -238,14 +240,14 @@ public class XmlReplay {
      * @param serviceResultsMap a Map of ServiceResult objects, which will contain ServiceResult.deleteURL.
      * @return a List<String> of debug info about which URLs could not be deleted.
      */
-    private static List<ServiceResult> autoDelete(Map<String, ServiceResult> serviceResultsMap, String logName, int reattempt) {
+    private static List<ServiceResult> autoDelete(AuthsMap defaultAuths, Map<String, ServiceResult> serviceResultsMap, String logName, int reattempt) {
         List<ServiceResult> results = new ArrayList<ServiceResult>();
         HashMap<String, ServiceResult> reattemptList = new HashMap<String, ServiceResult>();
         int deleteFailures = 0;
         for (ServiceResult pr : serviceResultsMap.values()) {
             try {
                 if (pr.autoDelete == true && Tools.notEmpty(pr.deleteURL)){
-                    ServiceResult deleteResult = XmlReplayTransport.doDELETE(pr.deleteURL, pr.auth, pr.testID, "[autodelete:"+logName+"]");
+                    ServiceResult deleteResult = XmlReplayTransport.doDELETE(pr.deleteURL, defaultAuths.getDefaultAuth(), 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()
                     }
@@ -276,7 +278,7 @@ public class XmlReplay {
         // our MAX_REATTEMPTS limit.
         //
         if (reattemptList.size() > 0 && reattempt < MAX_REATTEMPTS) {
-               return autoDelete(reattemptList, logName, ++reattempt); // recursive call
+               return autoDelete(defaultAuths, reattemptList, logName, ++reattempt); // recursive call
         }
         
         return results;
@@ -288,7 +290,8 @@ public class XmlReplay {
         public String getDefaultAuth(){
             return map.get(defaultID);
         }
-        public String toString(){
+        @Override
+               public String toString(){
             return "AuthsMap: {default='"+defaultID+"'; "+map.keySet()+'}';
         }
     }
@@ -314,7 +317,8 @@ public class XmlReplay {
         public boolean payloads = false;
         //public static final ServiceResult.DUMP_OPTIONS dumpServiceResultOptions = ServiceResult.DUMP_OPTIONS;
         public ServiceResult.DUMP_OPTIONS dumpServiceResult = ServiceResult.DUMP_OPTIONS.minimal;
-        public String toString(){
+        @Override
+               public String toString(){
             return "payloads: "+payloads+" dumpServiceResult: "+dumpServiceResult;
         }
     }
@@ -426,7 +430,7 @@ public class XmlReplay {
         byte[] b = FileUtils.readFileToByteArray(new File(expectedResponseParts.responseFilename));
         String expectedPartContent = new String(b);
         Map<String,String> vars = expectedResponseParts.varsList.get(0);  //just one part, so just one varsList.  Must be there, even if empty.
-        expectedPartContent = evalStruct.eval(expectedPartContent, serviceResultsMap, vars, evalStruct.jexl, evalStruct.jc);
+        expectedPartContent = XmlReplayEval.eval(expectedPartContent, serviceResultsMap, vars, evalStruct.jexl, evalStruct.jc);
         serviceResult.expectedContentExpanded = expectedPartContent;
         String label = "NOLABEL";
         String leftID  = "{from expected part, label:"+label+" filename: "+expectedResponseParts.responseFilename+"}";
@@ -556,7 +560,7 @@ public class XmlReplay {
             } else {
                 tests = testgroup.selectNodes("test");
             }
-            String authForTest = "";
+            String authForTest = ""; // reset auth for each test group
             int testElementIndex = -1;
 
             for (Node testNode : tests) {
@@ -587,12 +591,10 @@ public class XmlReplay {
                     
                     if (Tools.notEmpty(authIDForTest)){
                         currentAuthForTest = authsMap.map.get(authIDForTest);
-                    }
-                    else {
+                    } else {
                         String tokenAuthExpression = testNode.valueOf("@tokenauth");
-                        
                         if (Tools.notEmpty(tokenAuthExpression)){
-                            currentAuthForTest = "Bearer " + evalStruct.eval(tokenAuthExpression, serviceResultsMap, null, jexl, jc);
+                            currentAuthForTest = "Bearer " + XmlReplayEval.eval(tokenAuthExpression, serviceResultsMap, null, jexl, jc);
                         }
                     }
                     
@@ -604,7 +606,7 @@ public class XmlReplay {
                     }
 
                     if (uri.indexOf("$")>-1){
-                        uri = evalStruct.eval(uri, serviceResultsMap, null, jexl, jc);
+                        uri = XmlReplayEval.eval(uri, serviceResultsMap, null, jexl, jc);
                     }
                     fullURL = fixupFullURL(fullURL, protoHostPort, uri);
 
@@ -723,17 +725,19 @@ public class XmlReplay {
                         hasError = ! serviceResult.gotExpectedResult();
                     }
 
-                    boolean doingAuto = (dump.dumpServiceResult == ServiceResult.DUMP_OPTIONS.auto);
-                    String serviceResultRow = serviceResult.dump(dump.dumpServiceResult, hasError)+"; time:"+(System.currentTimeMillis()-startTime);
-                    String leader = (dump.dumpServiceResult == ServiceResult.DUMP_OPTIONS.detailed) ? "XmlReplay:"+testIDLabel+": ": "";
-
                     report.addTestResult(serviceResult);
 
-                    if (   (dump.dumpServiceResult == ServiceResult.DUMP_OPTIONS.detailed)
-                        || (dump.dumpServiceResult == ServiceResult.DUMP_OPTIONS.full)         ){
+                    if ((dump.dumpServiceResult == ServiceResult.DUMP_OPTIONS.detailed) || (dump.dumpServiceResult == ServiceResult.DUMP_OPTIONS.full)) {
                         System.out.println("\r\n#---------------------#");
                     }
-                    System.out.println(timeString()+" "+leader+serviceResultRow+"\r\n");
+                    
+                    String leader = (dump.dumpServiceResult == ServiceResult.DUMP_OPTIONS.detailed) ? "XmlReplay:" + testIDLabel +": ": "";                    
+                    String serviceResultRow = serviceResult.dump(dump.dumpServiceResult, hasError) + "; time:" + (System.currentTimeMillis()-startTime);                
+                    //System.out.println(timeString() + "\n" + leader + serviceResultRow + "\r\n");
+                    System.out.println(String.format("Time: %s\nLeader: %s\nAuth: %s\nResult: %s\r\n", 
+                               timeString(), testIDLabel, Tools.notEmpty(authIDForTest) ? authIDForTest : "<inferred>", serviceResultRow));
+                    
+                    boolean doingAuto = (dump.dumpServiceResult == ServiceResult.DUMP_OPTIONS.auto);                    
                     if (dump.payloads || (doingAuto&&hasError) ) {
                         if (Tools.notBlank(serviceResult.requestPayload)){
                             System.out.println("\r\n========== request payload ===============");
@@ -759,7 +763,7 @@ public class XmlReplay {
                 }
             }
             if (Tools.isTrue(autoDeletePOSTS) && param_autoDeletePOSTS){
-                autoDelete(serviceResultsMap, "default", 0);
+                autoDelete(defaultAuths, serviceResultsMap, "default", 0);
             }
         }
 
@@ -823,7 +827,7 @@ public class XmlReplay {
         return result;
     }
 
-    public static void main(String[]args) throws Exception {
+       public static void main(String[]args) throws Exception {
         Options options = createOptions();
         //System.out.println("System CLASSPATH: "+prop.getProperty("java.class.path", null));
         CommandLineParser parser = new GnuParser();
index 834f757521c5397cb422cf2ce034c456c4ad53d7..2fd065a4dbc9b386239d3f8144245e50aad33170 100644 (file)
@@ -8,6 +8,6 @@
     </account>
     <role>
         <roleId>${createRole.CSID}</roleId>
-        <roleName>ROLE_1_BIRD</roleName>
+        <roleName>xROLE_1_BIRD</roleName>
     </role>
 </ns2:account_role>
index c5e3482bf4abd827ca86ae483c24d95da5d5402d..82223f2df3a46465c4c5f7a0e61be615dd3a1dfa 100644 (file)
@@ -10,6 +10,6 @@
     </permission>
     <role>
         <roleId>${createRole.CSID}</roleId>
-        <roleName>ROLE_1_BIRD</roleName>
+        <roleName>xROLE_1_BIRD</roleName>
     </role>
 </ns2:permission_role>
index bd12dd5c41b35f9504de386302a06a7d5c3fdd1f..c7adf9a3c0f961e62fcb35c4a1f412a3e5acb7b4 100644 (file)
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
 <ns2:role xmlns:ns2="http://collectionspace.org/services/authorization">
-    <roleName>ROLE_1_BIRD</roleName>
+    <roleName>xROLE_1_BIRD</roleName>
     <description>Role for testing batch invoke permissions</description>
 </ns2:role>
index 8c9316c25853a999f65db80031f1c0ca8d73c1ba..5074eeaa4b877805263606c9b30cc3096dce7bcd 100644 (file)
@@ -6,19 +6,72 @@
         <auth ID="user1@museum1">dXNlcjFAbXVzZXVtMS5vcmc6dXNlcjFAbXVzZXVtMS5vcmc=</auth>
         <auth ID="bigbird2010">YmlnYmlyZDIwMTA6YmlnYmlyZDIwMTA=</auth>
         <auth ID="elmo2010">ZWxtbzIwMTA6ZWxtbzIwMTA=</auth>
+        <auth ID="grover2018">Z3JvdmVyMjAxODpncm92ZXIyMDE4</auth>
+        
     </auths>
     
+    <testGroup ID="slipOut" autoDeletePOSTS="true">
+        <test ID="slipOutPerm">
+            <method>POST</method>
+            <uri>/cspace-services/authorization/permissions</uri>
+            <filename>security/slipOutPerm.xml</filename>
+        </test>
+        <test ID="slipOutRole">
+            <method>POST</method>
+            <uri>/cspace-services/authorization/roles</uri>
+            <filename>security/slipOutRole.xml</filename>
+        </test>
+        <test ID="accountGrover">
+            <method>POST</method>
+            <uri>/cspace-services/accounts</uri>
+            <filename>security/create-account-grover.xml</filename>
+        </test>
+        <test ID="slipOutVocab-1" auth="grover2018">
+            <method>POST</method>
+            <uri>/cspace-services/vocabularies</uri>
+            <filename>security/slipOutVocab-1.xml</filename>
+        </test>
+        <test ID="update-slipOutPerm" auth="admin@core.collectionspace.org">
+            <method>PUT</method>
+            <uri>/cspace-services/authorization/permissions/${slipOutPerm.CSID}</uri>
+            <filename>security/slipOutPerm-update.xml</filename>
+        </test>
+        <test ID="slipOutVocab-2" auth="grover2018">
+            <expectedCodes>403</expectedCodes>
+            <method>POST</method>
+            <uri>/cspace-services/vocabularies</uri>
+            <filename>security/slipOutVocab-2.xml</filename>
+        </test>
+    </testGroup>
+    
+    <testGroup ID="DefaultPermmissions" autoDeletePOSTS="true">
+        <test ID="get1-loansin-CRUDL">
+            <method>GET</method>
+            <uri>/cspace-services/authorization/permissions/1-loansin-CRUDL</uri>
+            <response>
+                <expected level="TEXT" />
+                <filename>security/res/get1-loansin-CRUDL.res.xml</filename>
+                <label>permission</label>
+            </response>
+        </test>
+        <test ID="get1-loansin-CRUDLpermRoles">
+            <method>GET</method>
+            <uri>/cspace-services/authorization/permissions/1-loansin-CRUDL/permroles</uri>
+        </test>
+    </testGroup>
     
     <testGroup ID="deleteBug" autoDeletePOSTS="false">
+
         <test ID="permElmo">
             <method>POST</method>
             <uri>/cspace-services/authorization/permissions</uri>
             <filename>security/2-elmo-permission.xml</filename>
         </test>
         <test ID="elmoPermroles">
+            <expectedCodes>400</expectedCodes>
             <method>POST</method>
             <uri>/cspace-services/authorization/permissions/${permElmo.CSID}/permroles</uri>
-            <filename>security/10-permissionroles-elmo.xml</filename>
+            <filename>security/14-permissionroles-bogus.xml</filename>
         </test>
         <test ID="accountElmo">
             <method>POST</method>
@@ -41,6 +94,7 @@
             <fromTestID>dimension1</fromTestID>
         </test>
         <test ID="deleteElmoPermroles">
+            <expectedCodes>404</expectedCodes>
             <method>DELETE</method>
             <uri>/cspace-services/authorization/permissions/${permElmo.CSID}/permroles</uri>
         </test>
                        <filename>dimension/2-put.xml</filename>
         </test>
 
-
-
        <test ID="dimensionBigbird_GET">
             <method>GET</method>
             <uri>/cspace-services/dimensions/</uri>
         <!-- CLEANUP -->
         
         <test ID="deletePermrolesBigbird" auth="admin@core.collectionspace.org">
-            <msg>Deleting permroles from bigbird2010</msg>
+            <msg>Deleting permroles already deleted in test ID="deletePermrolesBigbird"</msg>
+            <expectedCodes>404</expectedCodes>
             <method>DELETE</method>
             <uri>/cspace-services/authorization/permissions/${permBigbird.CSID}/permroles</uri>
         </test>
index 3f7afdb7cfa886ca0de3a0492a81248fc4f2652e..dc7e16c0094984ac87564cadecd7d0ae8f016b34 100644 (file)
@@ -1,18 +1,17 @@
 <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
 <ns2:permission xmlns:ns2="http://collectionspace.org/services/authorization/perms">
-  <resourceName>dimensions</resourceName>
-  <action>
-    <name>CREATE</name>
-  </action>
-  <action>
-    <name>READ</name>
-  </action>
-  <action>
-    <name>UPDATE</name>
-  </action>
-  <action>
-    <name>DELETE</name>
-  </action>
-  <effect>PERMIT</effect>
+    <resourceName>dimensions</resourceName>
+    <action>
+        <name>CREATE</name>
+    </action>
+    <action>
+        <name>READ</name>
+    </action>
+    <action>
+        <name>UPDATE</name>
+    </action>
+    <action>
+        <name>DELETE</name>
+    </action>
+    <effect>PERMIT</effect>
 </ns2:permission>
-
diff --git a/services/IntegrationTests/src/test/resources/test-data/xmlreplay/security/1-vocab.xml b/services/IntegrationTests/src/test/resources/test-data/xmlreplay/security/1-vocab.xml
new file mode 100644 (file)
index 0000000..01d878d
--- /dev/null
@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<document name="vocabularies">
+  <ns3:vocabularies_common xmlns:ns3="http://collectionspace.org/services/vocabulary"
+  xmlns:ns2="http://collectionspace.org/services/jaxb">
+    <displayName>TestOrder Vocabulary</displayName>
+    <shortIdentifier>TestOrderVocab</shortIdentifier>
+    <refName>
+    urn:cspace:org.collectionspace.demo:vocabulary:name(TestOrderVocab)'TestOrder Vocabulary'</refName>
+    <vocabType>enum</vocabType>
+    <description>This is a test vocabulary</description>
+    <source>Some mythical book</source>
+ </ns3:vocabularies_common>
+</document>
+
index af52b93e4b73077762530cd9241b2a98ca335e6b..bf6b9a426272bea94f17a92c8edfc0c824ffa921 100644 (file)
@@ -7,7 +7,7 @@ xmlns:ns2="http://collectionspace.org/services/authorization">
   </permission>
   <role>
     <roleId>${roleIntern.CSID}</roleId>
-    <roleName>ROLE_TEST_INTERN</roleName>
+    <roleName>xROLE_TEST_INTERN</roleName>
   </role>
 </ns2:permission_role>
 
index bb7ae56072b24d85e1b992f6e866b171b9687f28..65a686aeae0919174e2eeac355c4508cdf0a032e 100644 (file)
@@ -1,16 +1,14 @@
 <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
-<ns2:permission
-xmlns:ns2="http://collectionspace.org/services/authorization">
-  <resourceName>dimensions</resourceName>
-  <action>
-    <name>CREATE</name>
-  </action>
-  <action>
-    <name>READ</name>
-  </action>
-  <action>
-    <name>UPDATE</name>
-  </action>
-  <effect>PERMIT</effect>
+<ns2:permission xmlns:ns2="http://collectionspace.org/services/authorization/perms">
+    <resourceName>dimensions</resourceName>
+    <action>
+        <name>CREATE</name>
+    </action>
+    <action>
+        <name>READ</name>
+    </action>
+    <action>
+        <name>UPDATE</name>
+    </action>
+    <effect>PERMIT</effect>
 </ns2:permission>
-
index df8889fb6f84ec871e07dabd85c4565f961dbda0..93b097eb8dbef7afd10dc49805f777a2b6657410 100644 (file)
@@ -7,7 +7,7 @@ xmlns:ns2="http://collectionspace.org/services/authorization">
   </permission>
   <role>
     <roleId>${roleTestCM.CSID}</roleId>
-    <roleName>ROLE_TEST_CM</roleName>
+    <roleName>xROLE_TEST_CM</roleName>
   </role>
 </ns2:permission_role>
 
index 6d4da79f574e4c2a8b8012b15bb2e493e76c9424..895abc518aae308d4394774bec381677a48231fe 100644 (file)
@@ -1,10 +1,8 @@
 <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
-<ns2:permission
-xmlns:ns2="http://collectionspace.org/services/authorization">
-  <resourceName>dimensions</resourceName>
-  <action>
-    <name>READ</name>
-  </action>
-  <effect>PERMIT</effect>
+<ns2:permission xmlns:ns2="http://collectionspace.org/services/authorization/perms">
+    <resourceName>dimensions</resourceName>
+    <action>
+        <name>READ</name>
+    </action>
+    <effect>PERMIT</effect>
 </ns2:permission>
-
index f4517f2d9cd11eef2533daf706d122fcc40a1857..7c262ebff5b0770f1458c12dc7c7886c3851c6db 100644 (file)
@@ -7,7 +7,7 @@ xmlns:ns2="http://collectionspace.org/services/authorization">
   </permission>
   <role>
     <roleId>${roleTestCM.CSID}</roleId>
-    <roleName>ROLE_TEST_CM</roleName>
+    <roleName>xROLE_TEST_CM</roleName>
   </role>
 </ns2:permission_role>
 
index f4517f2d9cd11eef2533daf706d122fcc40a1857..7c262ebff5b0770f1458c12dc7c7886c3851c6db 100644 (file)
@@ -7,7 +7,7 @@ xmlns:ns2="http://collectionspace.org/services/authorization">
   </permission>
   <role>
     <roleId>${roleTestCM.CSID}</roleId>
-    <roleName>ROLE_TEST_CM</roleName>
+    <roleName>xROLE_TEST_CM</roleName>
   </role>
 </ns2:permission_role>
 
diff --git a/services/IntegrationTests/src/test/resources/test-data/xmlreplay/security/14-permissionroles-bogus.xml b/services/IntegrationTests/src/test/resources/test-data/xmlreplay/security/14-permissionroles-bogus.xml
new file mode 100644 (file)
index 0000000..740a305
--- /dev/null
@@ -0,0 +1,13 @@
+<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
+<ns2:permission_role
+xmlns:ns2="http://collectionspace.org/services/authorization">
+  <permission>
+    <permissionId>bogus-perm</permissionId>
+    <resourceName>dimensions</resourceName>
+  </permission>
+  <role>
+    <roleId>bogus-role</roleId>
+    <roleName>xROLE_TEST_INTERN</roleName>
+  </role>
+</ns2:permission_role>
+
index ccd7034d6c415c02fde1683a624f0a902bd98130..38356e74886a9df140ca3aa0debe8cf7bad53337 100644 (file)
@@ -1,28 +1,23 @@
 <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
 <ns2:role xmlns:ns2="http://collectionspace.org/services/authorization">
-    <roleName>ROLE_TEST_CM</roleName>
+    <roleName>xROLE_TEST_CM</roleName>
     <description>role for ROLE_TEST_CM</description>
     <permission>
-        <permRelationshipId>4381</permRelationshipId>
         <permissionId>1-vocabularies-RL</permissionId>
         <resourceName>vocabularies</resourceName>
         <actionGroup>RL</actionGroup>
     </permission>
     <permission>
-        <permRelationshipId>4382</permRelationshipId>
         <permissionId>1-groups-RL</permissionId>
         <resourceName>groups</resourceName>
         <actionGroup>RL</actionGroup>
     </permission>
     <permission>
-        <permRelationshipId>4381</permRelationshipId>
-        <permissionId>1-vocabularies-CRUL</permissionId>
+        <permissionId>1-vocabularies-CRUDL</permissionId>
         <resourceName>vocabularies</resourceName>
         <actionGroup>CRUL</actionGroup>
     </permission>
     <permission>
-        <permRelationshipId>4382</permRelationshipId>
-        <permissionId>1-groups-CRUL</permissionId>
         <resourceName>groups</resourceName>
         <actionGroup>CRUL</actionGroup>
     </permission>
index 2178ce6d931251d7e4c8832fd028c20138f9dee3..4b7ce62fa0cc25a4e0dff64715171f38bf02929c 100644 (file)
@@ -1,6 +1,6 @@
 <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
 <ns2:role xmlns:ns2="http://collectionspace.org/services/authorization">
-    <roleName>ROLE_TEST_CM</roleName>
+    <roleName>xROLE_TEST_CM</roleName>
     <description>role for ROLE_TEST_CM</description>
     <permission>
         <permissionId>1-vocabularies-RL</permissionId>
index b16fc0503ac3b8dd51e28ab9f8c775ba3c2ec8c5..529dda4845e0a20e3ef4e7cec56840ee540f5d92 100644 (file)
@@ -1,6 +1,6 @@
 <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
 <ns2:role xmlns:ns2="http://collectionspace.org/services/authorization">
-    <roleName>ROLE_TEST_INTERN</roleName>
+    <roleName>xROLE_TEST_INTERN</roleName>
     <description>role for ROLE_TEST_INTERN</description>
     <permission>
         <permRelationshipId>4381</permRelationshipId>
index a1d518c049750b7adf32361e2713acee9d73372e..4f669059653a10bac1cdf5ab25fdd17b80424bf7 100644 (file)
@@ -9,7 +9,7 @@ xmlns:ns2="http://collectionspace.org/services/authorization">
   </account>
   <role>
     <roleId>${roleTestCM.CSID}</roleId>
-    <roleName>ROLE_TEST_CM</roleName>
+    <roleName>xROLE_TEST_CM</roleName>
   </role>
 </ns2:account_role>
 
index 5c321065e91de7b25cc44301a1183263a9b0fc9a..aa6165d54f4bc2f52a7b65255ebfa47dfa969ddc 100644 (file)
@@ -9,7 +9,7 @@ xmlns:ns2="http://collectionspace.org/services/authorization">
   </account>
   <role>
     <roleId>${roleIntern.CSID}</roleId>
-    <roleName>ROLE_TEST_INTERN</roleName>
+    <roleName>xROLE_TEST_INTERN</roleName>
   </role>
 </ns2:account_role>
 
index 08df47170befb02498eafc6c3a8d15401f578ed4..555819a7c903e914c5cec9bc1ec6bbbea4799020 100644 (file)
@@ -7,7 +7,7 @@ xmlns:ns2="http://collectionspace.org/services/authorization">
   </permission>
   <role>
     <roleId>${roleTestCM.CSID}</roleId>
-    <roleName>ROLE_TEST_CM</roleName>
+    <roleName>xROLE_TEST_CM</roleName>
   </role>
 </ns2:permission_role>
 
diff --git a/services/IntegrationTests/src/test/resources/test-data/xmlreplay/security/create-account-grover.xml b/services/IntegrationTests/src/test/resources/test-data/xmlreplay/security/create-account-grover.xml
new file mode 100644 (file)
index 0000000..6ca2535
--- /dev/null
@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
+<ns2:accounts_common xmlns:ns2="http://collectionspace.org/services/account" xmlns:ns3="http://collectionspace.org/services/hyperjaxb">
+    <screenName>grover2018</screenName>
+    <personRefName>grover2018</personRefName>
+    <email>grover@cspace.org</email>
+    <phone>1234567890</phone>
+    <userId>grover2018</userId>
+    <!-- Pass word is grover2018, base64 encoded -->
+    <password>Z3JvdmVyMjAxOA==</password>
+    <tenants>
+        <tenant_id>1</tenant_id>
+    </tenants>
+    <role>
+        <roleId>${slipOutRole.CSID}</roleId> <!-- It should be ok that role name is missing -->
+    </role>
+</ns2:accounts_common>
diff --git a/services/IntegrationTests/src/test/resources/test-data/xmlreplay/security/res/get1-loansin-CRUDL.res.xml b/services/IntegrationTests/src/test/resources/test-data/xmlreplay/security/res/get1-loansin-CRUDL.res.xml
new file mode 100644 (file)
index 0000000..ef5478e
--- /dev/null
@@ -0,0 +1,37 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ns2:permission xmlns:ns2="http://collectionspace.org/services/authorization/perms" csid="1-loansin-CRUDL">
+    <description>Generated admin permission.</description>
+    <resourceName>loansin</resourceName>
+    <actionGroup>CRUDL</actionGroup>
+    <action>
+        <name>CREATE</name>
+        <objectIdentity>1011301984</objectIdentity>
+        <objectIdentityResource>1:loansin#create</objectIdentityResource>
+    </action>
+    <action>
+        <name>READ</name>
+        <objectIdentity>1927741434</objectIdentity>
+        <objectIdentityResource>1:loansin#read</objectIdentityResource>
+    </action>
+    <action>
+        <name>UPDATE</name>
+        <objectIdentity>1524749869</objectIdentity>
+        <objectIdentityResource>1:loansin#update</objectIdentityResource>
+    </action>
+    <action>
+        <name>DELETE</name>
+        <objectIdentity>1028137743</objectIdentity>
+        <objectIdentityResource>1:loansin#delete</objectIdentityResource>
+    </action>
+    <action>
+        <name>SEARCH</name>
+        <objectIdentity>1457259276</objectIdentity>
+        <objectIdentityResource>1:loansin#search</objectIdentityResource>
+    </action>
+    <effect>PERMIT</effect>
+    <metadataProtection>immutable</metadataProtection>
+    <actionsProtection>immutable</actionsProtection>
+    <tenant_id>1</tenant_id>
+</ns2:permission>
+
+
diff --git a/services/IntegrationTests/src/test/resources/test-data/xmlreplay/security/slipOutPerm-update.xml b/services/IntegrationTests/src/test/resources/test-data/xmlreplay/security/slipOutPerm-update.xml
new file mode 100644 (file)
index 0000000..2d6d290
--- /dev/null
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
+<ns3:permission xmlns:ns3="http://collectionspace.org/services/authorization/perms">
+    <resourceName>vocabularies</resourceName>
+    <action>
+        <name>READ</name>
+    </action>
+    <effect>PERMIT</effect>
+</ns3:permission>
diff --git a/services/IntegrationTests/src/test/resources/test-data/xmlreplay/security/slipOutPerm.xml b/services/IntegrationTests/src/test/resources/test-data/xmlreplay/security/slipOutPerm.xml
new file mode 100644 (file)
index 0000000..58830e5
--- /dev/null
@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
+<ns3:permission xmlns:ns3="http://collectionspace.org/services/authorization/perms">
+    <resourceName>vocabularies</resourceName>
+    <action>
+        <name>CREATE</name>
+    </action>
+    <action>
+        <name>READ</name>
+    </action>
+    <effect>PERMIT</effect>
+</ns3:permission>
diff --git a/services/IntegrationTests/src/test/resources/test-data/xmlreplay/security/slipOutRole.xml b/services/IntegrationTests/src/test/resources/test-data/xmlreplay/security/slipOutRole.xml
new file mode 100644 (file)
index 0000000..3a58e27
--- /dev/null
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
+<ns2:role xmlns:ns2="http://collectionspace.org/services/authorization">
+    <roleName>SLIP_OUT_ROLE1</roleName>
+    <permission>
+        <permissionId>${slipOutPerm.CSID}</permissionId>
+    </permission>
+</ns2:role>
diff --git a/services/IntegrationTests/src/test/resources/test-data/xmlreplay/security/slipOutVocab-1.xml b/services/IntegrationTests/src/test/resources/test-data/xmlreplay/security/slipOutVocab-1.xml
new file mode 100644 (file)
index 0000000..563e052
--- /dev/null
@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<document name="vocabularies">
+  <ns3:vocabularies_common xmlns:ns3="http://collectionspace.org/services/vocabulary"
+  xmlns:ns2="http://collectionspace.org/services/jaxb">
+    <displayName>SlipRole Vocabulary</displayName>
+    <shortIdentifier>sliprole1</shortIdentifier>
+    <refName>
+        urn:cspace:org.collectionspace.demo:vocabulary:name(sliprole1)'SlipRole Vocabulary'</refName>
+    <vocabType>enum</vocabType>
+    <description>This is a test vocabulary</description>
+    <source>Some rug book</source>
+ </ns3:vocabularies_common>
+</document>
+
diff --git a/services/IntegrationTests/src/test/resources/test-data/xmlreplay/security/slipOutVocab-2.xml b/services/IntegrationTests/src/test/resources/test-data/xmlreplay/security/slipOutVocab-2.xml
new file mode 100644 (file)
index 0000000..2dc75cb
--- /dev/null
@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<document name="vocabularies">
+  <ns3:vocabularies_common xmlns:ns3="http://collectionspace.org/services/vocabulary"
+  xmlns:ns2="http://collectionspace.org/services/jaxb">
+    <displayName>SlipRole Vocabulary</displayName>
+    <shortIdentifier>sliprole2</shortIdentifier>
+    <refName>
+        urn:cspace:org.collectionspace.demo:vocabulary:name(sliprole2)'SlipRole Vocabulary'</refName>
+    <vocabType>enum</vocabType>
+    <description>This is a test vocabulary</description>
+    <source>Some rug book</source>
+ </ns3:vocabularies_common>
+</document>
+
index 51a1e908381f054fa23b6b0aa764fee967ad5c1a..0fab3f3905e5bf4125d7b78b3d537d7dbc115a76 100644 (file)
                             </hj:basic>
                         </xs:appinfo>
                     </xs:annotation>
-                                                               </xs:element>
-                                                               <xs:element name="createdAt" type="xs:dateTime">
+                               </xs:element>
+                               <xs:element name="createdAt" type="xs:dateTime">
                     <xs:annotation>
                         <xs:appinfo>
                             <hj:basic>
index 307e9b0e0fd5c9030c662f9f35051585bc85a570..7055839bf7dd0b19f37b9342d83d293c16f6301d 100644 (file)
@@ -3,8 +3,12 @@ DROP TABLE IF EXISTS accounts_common CASCADE;
 DROP TABLE IF EXISTS accounts_tenants CASCADE;
 DROP TABLE IF EXISTS tenants CASCADE;
 DROP SEQUENCE IF EXISTS hibernate_sequence;
-create table accounts_common (csid varchar(128) not null, created_at timestamp not null, email varchar(255) not null, mobile varchar(255), person_ref_name varchar(255), phone varchar(255), screen_name varchar(128) not null, status varchar(15) not null, updated_at timestamp, userid varchar(128) not null, metadata_protection varchar(255), roles_protection varchar(255), primary key (csid), unique (userid));
-create table accounts_tenants (HJID int8 not null, tenant_id varchar(128) not null, TENANTS_ACCOUNTS_COMMON_CSID varchar(128), primary key (HJID));
+create table accounts_common (csid varchar(128) not null, created_at timestamp not null, email varchar(255) not null, mobile varchar(255), person_ref_name varchar(255), phone varchar(255), screen_name varchar(128) not null, 
+       status varchar(15) not null, updated_at timestamp, userid varchar(128) not null, 
+       metadata_protection varchar(255), roles_protection varchar(255), 
+       primary key (csid), unique (userid));
+
+       create table accounts_tenants (HJID int8 not null, tenant_id varchar(128) not null, TENANTS_ACCOUNTS_COMMON_CSID varchar(128), primary key (HJID));
 create table tenants (id varchar(128) not null, created_at timestamp not null, name varchar(255) not null, config_md5hash varchar(255), authorities_initialized boolean not null, disabled boolean not null, updated_at timestamp, primary key (id));
 alter table accounts_tenants add constraint FKFDA649B05A9CEEB5 foreign key (TENANTS_ACCOUNTS_COMMON_CSID) references accounts_common;
 create sequence hibernate_sequence;
index d7a86fb48d876ae3ddccd00af637e03c38d0d14b..d664d8c8833c3955b66c8fb3f0ab5c8d6b73aefd 100644 (file)
@@ -48,6 +48,7 @@ import org.collectionspace.services.common.context.ServiceContextFactory;
 import org.collectionspace.services.common.document.DocumentNotFoundException;
 import org.collectionspace.services.common.query.UriInfoImpl;
 import org.collectionspace.services.common.storage.StorageClient;
+import org.collectionspace.services.common.storage.TransactionContext;
 import org.collectionspace.services.common.storage.jpa.JpaStorageUtils;
 import org.collectionspace.services.config.tenant.EmailConfig;
 import org.collectionspace.services.config.tenant.TenantBindingType;
@@ -56,12 +57,9 @@ import org.jboss.resteasy.util.HttpResponseCodes;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
-import java.io.IOException;
-import java.io.UnsupportedEncodingException;
 import java.net.URI;
 import java.net.URISyntaxException;
 import java.nio.charset.StandardCharsets;
-import java.security.NoSuchAlgorithmException;
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.List;
@@ -88,7 +86,7 @@ import javax.xml.bind.DatatypeConverter;
 @Path(AccountClient.SERVICE_PATH)
 @Consumes("application/xml")
 @Produces("application/xml")
-public class AccountResource extends SecurityResourceBase {
+public class AccountResource extends SecurityResourceBase<AccountsCommon, AccountsCommon> {
 
        final Logger logger = LoggerFactory.getLogger(AccountResource.class);
     final StorageClient storageClient = new AccountStorageClient();
@@ -216,9 +214,17 @@ public class AccountResource extends SecurityResourceBase {
 
     @PUT
     @Path("{csid}")
-    public AccountsCommon updateAccount(@Context UriInfo ui, @PathParam("csid") String csid,AccountsCommon theUpdate) {
+    public AccountsCommon updateAccount(@Context UriInfo ui, @PathParam("csid") String csid, AccountsCommon theUpdate) {
         return (AccountsCommon)update(ui, csid, theUpdate, AccountsCommon.class);
     }
+    
+    /*
+     * Use this when you have an existing and active ServiceContext.
+     */
+    public AccountsCommon updateAccount(ServiceContext<AccountsCommon, AccountsCommon> parentContext, UriInfo ui, String csid, AccountsCommon theUpdate) {
+        return (AccountsCommon)update(parentContext, ui, csid, theUpdate, AccountsCommon.class);
+    }
+    
 
     /**
      * Resets an accounts password.
@@ -230,13 +236,12 @@ public class AccountResource extends SecurityResourceBase {
      *
      * @param ui
      * @return
-     * @throws UnsupportedEncodingException 
-     * @throws DocumentNotFoundException 
+     * @throws  
      * @throws IOException 
      */
     @POST
     @Path(PROCESS_PASSWORD_RESET_PATH)
-    synchronized public Response processPasswordReset(Passwordreset passwordreset, @Context UriInfo ui) throws UnsupportedEncodingException, DocumentNotFoundException {
+    synchronized public Response processPasswordReset(Passwordreset passwordreset, @Context UriInfo ui) {
        Response response = null;
 
        //
@@ -300,31 +305,45 @@ public class AccountResource extends SecurityResourceBase {
                response = Response.status(Response.Status.BAD_REQUEST).entity(errMsg).type("text/plain").build();
                return response;
         }
-
-        //
         //
+        // Finally, try to update the account with the new password.
         //
         String tenantId = token.getTenantId();
        TenantBindingType tenantBindingType = ServiceMain.getInstance().getTenantBindingConfigReader().getTenantBinding(tenantId);
        EmailConfig emailConfig = tenantBindingType.getEmailConfig();
        if (emailConfig != null) {
                try {
-                               if (AuthorizationCommon.hasTokenExpired(emailConfig, token) == false) {
-                                       AccountsCommon accountUpdate = new AccountsCommon();
-                                       accountUpdate.setUserId(targetAccount.getUserId());
-                                       accountUpdate.setPassword(password.getBytes());
-                                       updateAccount(ui, targetAccount.getCsid(), accountUpdate);
-                                       TokenStorageClient.update(tokenId, false); // disable the token so it can't be used again.
-                                       String msg = String.format("Successfully reset password using token ID='%s'.",
-                                                       token.getId());
-                               response = Response.status(Response.Status.OK).entity(msg).type("text/plain").build();
-                       } else {
-                               String errMsg = String.format("Could not reset password using token with ID='%s'. Password reset token has expired.",
-                                                       token.getId());
-                               response = Response.status(Response.Status.INTERNAL_SERVER_ERROR).entity(errMsg).type("text/plain").build();
-                       }
-                       } catch (NoSuchAlgorithmException e) {
-                               String errMsg = String.format("Could not reset password for using token ID='%s'. Error: '%s'",
+                       ServiceContext<AccountsCommon, AccountsCommon> ctx = createServiceContext((AccountsCommon) null, AccountsCommon.class, ui);
+                   TransactionContext transactionCtx = ctx.openConnection();
+                       try {
+                                       if (AuthorizationCommon.hasTokenExpired(emailConfig, token) == false) {
+                                               transactionCtx.beginTransaction();
+                                               AccountsCommon accountUpdate = new AccountsCommon();
+                                               accountUpdate.setUserId(targetAccount.getUserId());
+                                               accountUpdate.setPassword(password.getBytes());
+                                               updateAccount(ctx, ui, targetAccount.getCsid(), accountUpdate);
+                                               TokenStorageClient.update(transactionCtx, tokenId, false); // disable the token so it can't be used again.
+                                               transactionCtx.commitTransaction();
+                                               //
+                                               // Success!
+                                               //
+                                               String msg = String.format("Successfully reset password using token ID='%s'.", token.getId());
+                                       response = Response.status(Response.Status.OK).entity(msg).type("text/plain").build();
+                               } else {
+                                       String errMsg = String.format("Could not reset password using token with ID='%s'. Password reset token has expired.",
+                                                               token.getId());
+                                       response = Response.status(Response.Status.INTERNAL_SERVER_ERROR).entity(errMsg).type("text/plain").build();
+                               }
+                               } catch (Throwable t) {
+                                       transactionCtx.markForRollback();
+                                       String errMsg = String.format("Could not reset password using token ID='%s'. Error: '%s'",
+                                                       t.getMessage(), token.getId());
+                               response = Response.status(Response.Status.BAD_REQUEST).entity(errMsg).type("text/plain").build();
+                               } finally {
+                                       ctx.closeConnection();
+                               }
+               } catch (Exception e) {
+                               String errMsg = String.format("Could not reset password using token ID='%s'. Error: '%s'",
                                                e.getMessage(), token.getId());
                        response = Response.status(Response.Status.BAD_REQUEST).entity(errMsg).type("text/plain").build();
                        }
@@ -440,7 +459,7 @@ public class AccountResource extends SecurityResourceBase {
 
        return result;
     }
-
+    
        @DELETE
     @Path("{csid}")
     public Response deleteAccount(@Context UriInfo uriInfo, @PathParam("csid") String csid) {
@@ -449,8 +468,10 @@ public class AccountResource extends SecurityResourceBase {
         
         try {
                AccountsCommon account = (AccountsCommon)get(csid, AccountsCommon.class);
+               //
             // If marked as metadata immutable, do not delete
-            if (AccountClient.IMMUTABLE.equals(account.getMetadataProtection())) {
+            //
+               if (AccountClient.IMMUTABLE.equals(account.getMetadataProtection())) {
                 Response response =
                        Response.status(Response.Status.FORBIDDEN).entity("Account: "+csid+" is immutable.").type("text/plain").build();
                 return response;
@@ -461,11 +482,22 @@ public class AccountResource extends SecurityResourceBase {
             //
             ServiceContext<AccountsCommon, AccountsCommon> ctx = createServiceContext((AccountsCommon) null,
                     AccountsCommon.class, uriInfo);
-            ctx.openConnection();
+            TransactionContext transactionContext = ctx.openConnection();
             try {
+               transactionContext.beginTransaction();
+               //
+               // Delete all the account-role relationships
+               //
                    AccountRoleSubResource subResource = new AccountRoleSubResource("accounts/accountroles");
                    subResource.deleteAccountRole(ctx, csid, SubjectType.ROLE);
+                   //
+                   // Now delete the account.
+                   //
                    getStorageClient(ctx).delete(ctx, csid);
+                   transactionContext.commitTransaction();
+            } catch (Throwable t) {
+               transactionContext.markForRollback();
+               throw t;
             } finally {
                ctx.closeConnection();
             }
@@ -476,9 +508,11 @@ public class AccountResource extends SecurityResourceBase {
         return Response.status(HttpResponseCodes.SC_OK).build();        
     }
 
-    @POST
+       @POST
     @Path("{csid}/accountroles")
-    public Response createAccountRole(@QueryParam("_method") String method,
+    public Response createAccountRole(
+               @Context UriInfo uriInfo,
+               @QueryParam("_method") String method,
             @PathParam("csid") String accCsid,
             AccountRole input) {
         if (method != null) {
@@ -488,21 +522,29 @@ public class AccountResource extends SecurityResourceBase {
         }
         logger.debug("createAccountRole with accCsid=" + accCsid);
         ensureCSID(accCsid, ServiceMessages.POST_FAILED+ "accountroles account ");
+        
         try {
                AccountsCommon account = (AccountsCommon)get(accCsid, AccountsCommon.class);
-            // If marked as roles immutable, do not create
+            // If marked as immutable, fail.
             if (AccountClient.IMMUTABLE.equals(account.getRolesProtection())) {
                 Response response =
                        Response.status(Response.Status.FORBIDDEN).entity("Roles for Account: "+accCsid+" are immutable.").type("text/plain").build();
                 return response;
             }
-            AccountRoleSubResource subResource =
-                    new AccountRoleSubResource(AccountRoleSubResource.ACCOUNT_ACCOUNTROLE_SERVICE);
-            String accrolecsid = subResource.createAccountRole((ServiceContext)null, input, SubjectType.ROLE);
-            UriBuilder path = UriBuilder.fromResource(AccountResource.class);
-            path.path(accCsid + "/accountroles/" + accrolecsid);
-            Response response = Response.created(path.build()).build();
-            return response;
+            
+            ServiceContext<AccountsCommon, AccountsCommon> ctx = createServiceContext((AccountsCommon) null, AccountsCommon.class, uriInfo);
+            ctx.openConnection();
+            try {
+                   AccountRoleSubResource subResource =
+                           new AccountRoleSubResource(AccountRoleSubResource.ACCOUNT_ACCOUNTROLE_SERVICE);
+                   String accrolecsid = subResource.createAccountRole(ctx, input, SubjectType.ROLE);
+                   UriBuilder path = UriBuilder.fromResource(AccountResource.class);
+                   path.path(accCsid + "/accountroles/" + accrolecsid);
+                   Response response = Response.created(path.build()).build();
+                   return response;
+            } finally {
+               ctx.closeConnection();
+            }
          } catch (Exception e) {
             throw bigReThrow(e, ServiceMessages.POST_FAILED, accCsid);
         }
@@ -516,15 +558,18 @@ public class AccountResource extends SecurityResourceBase {
         logger.debug("getAccountRole with accCsid=" + accCsid);
         ensureCSID(accCsid, ServiceMessages.GET_FAILED+ "accountroles account ");
         AccountRoleRel result = null;
+        ServiceContext<AccountsCommon, AccountsCommon> ctx = null;
+
         try {
             AccountRoleSubResource subResource =
                     new AccountRoleSubResource(AccountRoleSubResource.ACCOUNT_ACCOUNTROLE_SERVICE);
             //get relationships for an account
-            result = subResource.getAccountRoleRel((ServiceContext)null, accCsid, SubjectType.ROLE, accrolecsid);
+            result = subResource.getAccountRoleRel(ctx,        accCsid, SubjectType.ROLE, accrolecsid);
          } catch (Exception e) {
             throw bigReThrow(e, ServiceMessages.GET_FAILED, accCsid);
         }
         checkResult(result, accCsid, ServiceMessages.GET_FAILED);
+        
         return result;
     }
 
@@ -535,12 +580,13 @@ public class AccountResource extends SecurityResourceBase {
         logger.debug("getAccountRole with accCsid=" + accCsid);
         ensureCSID(accCsid, ServiceMessages.GET_FAILED+ "accountroles account ");
         AccountRole result = null;
+        ServiceContext<AccountsCommon, AccountsCommon> ctx = null;
         
         try {
             AccountRoleSubResource subResource =
                     new AccountRoleSubResource(AccountRoleSubResource.ACCOUNT_ACCOUNTROLE_SERVICE);
             //get relationships for an account
-            result = subResource.getAccountRole((ServiceContext)null, accCsid, SubjectType.ROLE);
+            result = subResource.getAccountRole(ctx, accCsid, SubjectType.ROLE);
         } catch (Exception e) {
             throw bigReThrow(e, ServiceMessages.GET_FAILED, accCsid);
         }
@@ -566,22 +612,30 @@ public class AccountResource extends SecurityResourceBase {
         return result;
     }
 
-    public Response deleteAccountRole(String accCsid, AccountRole input) {
+       public Response deleteAccountRole(String accCsid, AccountRole input) {
         logger.debug("deleteAccountRole with accCsid=" + accCsid);
         ensureCSID(accCsid, ServiceMessages.DELETE_FAILED+ "accountroles account ");
         
         try {
                AccountsCommon account = (AccountsCommon)get(accCsid, AccountsCommon.class);
             // If marked as roles immutable, do not delete
-            if(AccountClient.IMMUTABLE.equals(account.getRolesProtection())) {
+            if (AccountClient.IMMUTABLE.equals(account.getRolesProtection())) {
                 Response response =
                        Response.status(Response.Status.FORBIDDEN).entity("Roles for Account: "+accCsid+" are immutable.").type("text/plain").build();
                 return response;
             }
-            AccountRoleSubResource subResource =
-                    new AccountRoleSubResource(AccountRoleSubResource.ACCOUNT_ACCOUNTROLE_SERVICE);
-            //delete all relationships for an account
-            subResource.deleteAccountRole((ServiceContext)null, accCsid, SubjectType.ROLE, input);
+            
+            ServiceContext<AccountsCommon, AccountsCommon> ctx = createServiceContext((AccountsCommon) null,
+                    AccountsCommon.class, (UriInfo) null);
+            ctx.openConnection();
+            try {
+                   AccountRoleSubResource subResource =
+                           new AccountRoleSubResource(AccountRoleSubResource.ACCOUNT_ACCOUNTROLE_SERVICE);
+                   //delete all relationships for an account
+                   subResource.deleteAccountRole(ctx, accCsid, SubjectType.ROLE, input);
+            } finally {
+               ctx.closeConnection();
+            }
         } catch (Exception e) {
             throw bigReThrow(e, ServiceMessages.DELETE_FAILED, accCsid);
         }
@@ -591,7 +645,8 @@ public class AccountResource extends SecurityResourceBase {
 
     @DELETE
     @Path("{csid}/accountroles")
-    public Response deleteAccountRole(@PathParam("csid") String accCsid) {
+    public Response deleteAccountRole(@Context UriInfo uriInfo, @PathParam("csid") String accCsid) {
+       
         logger.debug("deleteAccountRole: All roles related to account with accCsid=" + accCsid);
         ensureCSID(accCsid, ServiceMessages.DELETE_FAILED+ "accountroles account ");
         
@@ -603,10 +658,17 @@ public class AccountResource extends SecurityResourceBase {
                        Response.status(Response.Status.FORBIDDEN).entity("Roles for Account: "+accCsid+" are immutable.").type("text/plain").build();
                 return response;
             }
-            AccountRoleSubResource subResource =
-                    new AccountRoleSubResource(AccountRoleSubResource.ACCOUNT_ACCOUNTROLE_SERVICE);
-            //delete all relationships for an account
-            subResource.deleteAccountRole((ServiceContext)null, accCsid, SubjectType.ROLE);
+            
+            ServiceContext<AccountsCommon, AccountsCommon> ctx = createServiceContext((AccountsCommon) null, AccountsCommon.class, uriInfo);
+            ctx.openConnection();
+            try {
+                   AccountRoleSubResource subResource =
+                           new AccountRoleSubResource(AccountRoleSubResource.ACCOUNT_ACCOUNTROLE_SERVICE);
+                   //delete all relationships for an account
+                   subResource.deleteAccountRole(ctx, accCsid, SubjectType.ROLE);
+            } finally {
+               ctx.closeConnection();
+            }
         } catch (Exception e) {
             throw bigReThrow(e, ServiceMessages.DELETE_FAILED, accCsid);
         }
index 9d3dce195650dfba7a2ad4cae2d5aaac37955d2d..3ad10062b8017e3bf548c9fd4bebaf99bf23c4a1 100644 (file)
@@ -175,23 +175,23 @@ public class AccountRoleSubResource
      */
     public String createAccountRole(ServiceContext parentCtx, AccountRole input, SubjectType subject)
             throws Exception {
-       
        //
        // We need to associate every new account with the Spring Security Admin role so we can make
        // changes to the Spring Security ACL tables.  The Spring Security Admin role has NO CollectionSpace
        // specific permissions.  It is an internal/private role that service consumers and end-users NEVER see.
        //
        
-       //Preserve the original incoming list of roles
+       // Preserve the original incoming list of roles
        List<RoleValue> inputRoleValues = input.getRole();
 
-       //Change the role list to be just the Spring role
-       List<RoleValue> springRoles = new ArrayList<RoleValue>();
-       input.setRole(springRoles);
-       RoleValue springAdminRole = new RoleValue();
-       springRoles.add(springAdminRole);
-       springAdminRole.setRoleId(AuthN.ROLE_SPRING_ADMIN_ID);
-       springAdminRole.setRoleName(AuthN.ROLE_SPRING_ADMIN_NAME);
+       // Temporarily change the role list to be just the Spring role
+       List<RoleValue> springRoleValueList = new ArrayList<RoleValue>();
+       input.setRole(springRoleValueList);
+       
+       RoleValue springAdminRoleValue = new RoleValue();
+       springRoleValueList.add(springAdminRoleValue);
+       springAdminRoleValue.setRoleId(AuthN.ROLE_SPRING_ADMIN_ID);
+       springAdminRoleValue.setRoleName(AuthN.ROLE_SPRING_ADMIN_NAME);
 
        // The Spring role relationship may already exist, if it does then we'll get a PersistenceException that
        // we'll just ignore.
@@ -210,7 +210,7 @@ public class AccountRoleSubResource
        }
        
        //
-       // Now we'll add the account relationships for the original incoming roles.
+       // Now we'll add the actual account-role relationships for the original incoming payload.
        //
        input.setRole(inputRoleValues);
         ServiceContext<AccountRole, AccountRole> ctx = createServiceContext(parentCtx, input, subject);
index daa2245cc241e12ed00d6e096fa143a12e834f72..eddd79470e49206787113562ddb66e0166c27cf8 100644 (file)
@@ -41,6 +41,7 @@ import org.collectionspace.services.account.RoleValue;
 import org.collectionspace.services.client.AccountClient;
 import org.collectionspace.services.client.AccountFactory;
 import org.collectionspace.services.client.AccountRoleFactory;
+import org.collectionspace.services.common.storage.TransactionContext;
 import org.collectionspace.services.common.storage.jpa.JpaDocumentHandler;
 import org.collectionspace.services.common.context.ServiceContext;
 import org.collectionspace.services.common.document.DocumentFilter;
@@ -146,13 +147,23 @@ public class AccountDocumentHandler
     }
 
     @Override
+    /**
+     * If the create payload included a list of role, relate them to the account.
+     */
     public void completeCreate(DocumentWrapper<AccountsCommon> wrapDoc) throws Exception {
        AccountsCommon accountsCommon = wrapDoc.getWrappedObject();
        List<RoleValue> roleValueList = account.getRole();
        if (roleValueList != null && roleValueList.size() > 0) {
+               //
+               // To prevent new Accounts being created (especially low-level Spring Security accounts/SIDs), we'll first flush the current
+               // JPA context to ensure our Account can be successfully persisted.
+               //
+               TransactionContext jpaTransactionContext = this.getServiceContext().getCurrentTransactionContext();
+               jpaTransactionContext.flush();
+
                AccountRoleSubResource subResource = new AccountRoleSubResource(AccountRoleSubResource.ACCOUNT_ACCOUNTROLE_SERVICE);
                AccountRole accountRole = AccountRoleFactory.createAccountRoleInstance(accountsCommon, roleValueList, true, true);
-                       String accountRoleCsid = subResource.createAccountRole(this.getServiceContext(), accountRole, SubjectType.ROLE);
+                       subResource.createAccountRole(this.getServiceContext(), accountRole, SubjectType.ROLE);
        }
     }
     
index 9ea09060917ab084da3490891cb0fdf0ce77065c..442fd1dd074869e805526066904404d35705511d 100644 (file)
@@ -27,6 +27,7 @@ import java.util.ArrayList;
 import java.util.List;
 
 import org.collectionspace.services.common.authorization_mgt.AuthorizationRoleRel;
+import org.collectionspace.services.common.authorization_mgt.PermissionRoleUtil;
 import org.collectionspace.services.authorization.AccountRole;
 import org.collectionspace.services.authorization.AccountRoleRel;
 import org.collectionspace.services.authorization.AccountValue;
@@ -35,9 +36,11 @@ import org.collectionspace.services.authorization.SubjectType;
 import org.collectionspace.services.common.context.ServiceContext;
 import org.collectionspace.services.common.storage.jpa.JpaDocumentFilter;
 import org.collectionspace.services.common.storage.jpa.JpaDocumentHandler;
+import org.collectionspace.services.common.document.DocumentException;
 import org.collectionspace.services.common.document.DocumentFilter;
 import org.collectionspace.services.common.document.DocumentWrapper;
 import org.collectionspace.services.common.context.ServiceContextProperties;
+
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -104,7 +107,8 @@ public class AccountRoleDocumentHandler
     /* (non-Javadoc)
      * @see org.collectionspace.services.common.document.AbstractDocumentHandlerImpl#handleGet(org.collectionspace.services.common.document.DocumentWrapper)
      */
-    @Override
+    @SuppressWarnings({ "unchecked" })
+       @Override
     public void handleGet(DocumentWrapper<List<AccountRoleRel>> wrapDoc) throws Exception {
        AccountRole output = extractCommonPart(wrapDoc);
         setCommonPart(output);
@@ -207,33 +211,54 @@ public class AccountRoleDocumentHandler
        // Ensure the roles exist
     }
     
-    public void fillCommonPart(AccountRole ar,
+    /**
+     * Populate the incoming wrapDoc (AccountRoleRel) object with account-role data.
+     * 
+     * @param accountRole
+     * @param wrapDoc
+     * @param handleDelete
+     * @throws Exception
+     */
+    public void fillCommonPart(AccountRole accountRole,
                DocumentWrapper<List<AccountRoleRel>> wrapDoc,
                boolean handleDelete)
                throws Exception {
-        List<AccountRoleRel> arrl = wrapDoc.getWrappedObject();
-        SubjectType subject = ar.getSubject();
+        List<AccountRoleRel> accountRoleRelList = wrapDoc.getWrappedObject();
+        SubjectType subject = accountRole.getSubject();
         if (subject == null) {
             //it is not required to give subject as URI determines the subject
             subject = getSubject(getServiceContext());
-        } else {
-            //subject mismatch should have been checked during validation
         }
+        
         if (subject.equals(SubjectType.ROLE)) {
-            //FIXME: potential index out of bounds exception...negative test needed
-            AccountValue av = ar.getAccount().get(0);
-
-            for (RoleValue rv : ar.getRole()) {
-                AccountRoleRel arr = buildAccountRoleRel(av, rv, handleDelete);
-                arrl.add(arr);
-            }
+               if (accountRole.getAccount() != null &&  accountRole.getAccount().size() == 1) {
+                   AccountValue av = accountRole.getAccount().get(0); // Since ROLE is the subject, they'll be just one account
+                   for (RoleValue rv : accountRole.getRole()) {
+                       if (rv.getRoleName() == null) { // We need the role name, so get it if the payload just includes the role CSID
+                               rv = PermissionRoleUtil.fetchRoleValue(getServiceContext(), rv.getRoleId());
+                       }
+                       AccountRoleRel arr = buildAccountRoleRel(av, rv, handleDelete);
+                       accountRoleRelList.add(arr);
+                   }
+               } else {
+                       String msg = "There must be one (and only one) account supplied in an account-role relationship where the subject is the role.";
+                       throw new DocumentException(msg);
+               }
         } else if (SubjectType.ACCOUNT.equals(subject)) {
-            //FIXME: potential index out of bounds exception...negative test needed
-               RoleValue rv = ar.getRole().get(0);
-            for (AccountValue av : ar.getAccount()) {
-                AccountRoleRel arr = buildAccountRoleRel(av, rv, handleDelete);
-                arrl.add(arr);
-            }
+               if (accountRole.getRole() != null &&  accountRole.getRole().size() == 1) {              
+                       RoleValue rv = accountRole.getRole().get(0);
+                   for (AccountValue av : accountRole.getAccount()) {
+                       AccountRoleRel arr = buildAccountRoleRel(av, rv, handleDelete);
+                       accountRoleRelList.add(arr);
+                   }
+               } else {
+                       String msg = "There must be one (and only one) role supplied in an account-role relationship where the subject is the account.";
+                       throw new DocumentException(msg);
+               }
+        } else {
+               String msg = String.format("Unknown subject '%s' encounted when working with an AccountRole object",
+                               subject.value());
+               throw new DocumentException(msg);
         }
     }
     
@@ -355,18 +380,18 @@ public class AccountRoleDocumentHandler
      * @return the account role rel
      */
     private AccountRoleRel buildAccountRoleRel(AccountValue av, RoleValue rv, boolean handleDelete) {
-        AccountRoleRel arr = new AccountRoleRel();
-        arr.setAccountId(av.getAccountId());
-        arr.setUserId(av.getUserId());
-        arr.setScreenName(av.getScreenName());
-        arr.setRoleId(rv.getRoleId());
-        arr.setRoleName(rv.getRoleName());
+        AccountRoleRel accountRoleRel = new AccountRoleRel();
+        accountRoleRel.setAccountId(av.getAccountId());
+        accountRoleRel.setUserId(av.getUserId());
+        accountRoleRel.setScreenName(av.getScreenName());
+        accountRoleRel.setRoleId(rv.getRoleId());
+        accountRoleRel.setRoleName(rv.getRoleName());
         
         String relationshipId = rv.getRoleRelationshipId();
         if (relationshipId != null && handleDelete == true) {
-               arr.setHjid(Long.parseLong(relationshipId));  // set this so we can convince JPA to del the relation
+               accountRoleRel.setHjid(Long.parseLong(relationshipId));  // set this so we can convince JPA to delete the relationship
         }        
-        return arr;
+        return accountRoleRel;
     }
 
     /**
@@ -375,7 +400,7 @@ public class AccountRoleDocumentHandler
      * @param ctx the ctx
      * @return the subject
      */
-    static SubjectType getSubject(ServiceContext ctx) {
+    static SubjectType getSubject(ServiceContext<?, ?> ctx) {
         Object o = ctx.getProperty(ServiceContextProperties.SUBJECT);
         if (o == null) {
             throw new IllegalArgumentException(ServiceContextProperties.SUBJECT
index ffdf5398de82321a842b2acd0cb86dca8cb70c6a..0977ce61c8fc6d0b8730c5333f9e5cf2e97b6a22 100644 (file)
@@ -73,21 +73,24 @@ public class AccountStorageClient extends JpaStorageClientImpl {
         try {
             account = (AccountsCommon) handler.getCommonPart();
             handler.prepare(Action.CREATE);
-            DocumentWrapper<AccountsCommon> wrapDoc =
-                    new DocumentWrapperImpl<AccountsCommon>(account);
+            DocumentWrapper<AccountsCommon> wrapDoc = new DocumentWrapperImpl<AccountsCommon>(account);
             handler.handle(Action.CREATE, wrapDoc);
             jpaConnectionContext.beginTransaction();
             //
-            // If userid and password are given, add to default id provider
+            // If userid and password are given, add to default ID provider -i.e., add it to the Spring Security account list
             //
             if (account.getUserId() != null && isForCSpaceIdentityProvider(account.getPassword())) {
                 User user = userStorageClient.create(account.getUserId(), account.getPassword());
-                   jpaConnectionContext.persist(user);         
+                   jpaConnectionContext.persist(user);
             }
-
+            //
+            // Now add the account to the CSpace list of accounts
+            //
             account.setCreatedAtItem(new Date());
             jpaConnectionContext.persist(account);             
-
+            //
+            // Finish creating related resources -e.g., account-role relationships
+            //
             handler.complete(Action.CREATE, wrapDoc);
             jpaConnectionContext.commitTransaction();
 
index 2836fda5a987b538d0624bc7953ab0d3ad35dced..747ee40ef1ac8697f45a62dd2f749b73f4e74a8c 100644 (file)
@@ -40,7 +40,10 @@ import org.collectionspace.services.common.document.BadRequestException;
 import org.collectionspace.services.common.document.DocumentException;
 import org.collectionspace.services.common.document.DocumentNotFoundException;
 import org.collectionspace.services.common.document.JaxbUtils;
+import org.collectionspace.services.common.document.TransactionException;
 import org.collectionspace.services.common.security.SecurityUtils;
+import org.collectionspace.services.common.storage.TransactionContext;
+import org.collectionspace.services.common.storage.jpa.JPATransactionContext;
 import org.collectionspace.services.common.storage.jpa.JpaStorageUtils;
 
 import org.slf4j.Logger;
@@ -84,75 +87,65 @@ public class TokenStorageClient {
        }
 
     /**
-     * Get token for given ID
-     * @param em EntityManager
+     * Update a token for given an id
      * @param id
+     * @param enabledFlag
+     * @throws TransactionException 
      */
-    static public Token get(String id) throws DocumentNotFoundException {
-        EntityManagerFactory emf = JpaStorageUtils.getEntityManagerFactory();
+    static public void update(TransactionContext transactionContext, String id, boolean enabledFlag) throws DocumentNotFoundException, TransactionException {
         Token tokenFound = null;
         
-        try {
-            EntityManager em = emf.createEntityManager();
-            tokenFound = get(em, id);
-        } finally {
-            if (emf != null) {
-                JpaStorageUtils.releaseEntityManagerFactory(emf);
+        tokenFound = get((JPATransactionContext)transactionContext, id);
+        if (tokenFound != null) {
+            tokenFound.setEnabled(enabledFlag);
+            tokenFound.setUpdatedAtItem(new Date());
+            if (logger.isDebugEnabled()) {
+                logger.debug("Updated token=" + JaxbUtils.toString(tokenFound, Token.class));
             }
+        } else {
+               String msg = String.format("Could not find token with id='%s'", id);
+               throw new DocumentNotFoundException(msg);
         }
-        
-        return tokenFound;
     }
 
     /**
-     * Update a token for given an id
+     * Get token for given ID
+     * @param em EntityManager
      * @param id
-     * @param enabledFlag
-     */
-    static public void update(String id, boolean enabledFlag) throws DocumentNotFoundException {
-        EntityManagerFactory emf = JpaStorageUtils.getEntityManagerFactory();
-        EntityManager em = null;
+     */    
+    public static Token get(JPATransactionContext jpaTransactionContext, String id) throws DocumentNotFoundException, TransactionException {
+        Token tokenFound = null;
         
+        tokenFound = (Token) jpaTransactionContext.find(Token.class, id);        
+        if (tokenFound == null) {
+            String msg = "Could not find token with ID=" + id;
+            logger.error(msg);
+            throw new DocumentNotFoundException(msg);
+        }
+
+        return tokenFound;
+    }
+    
+    static public Token get(String id) throws DocumentNotFoundException {
         Token tokenFound = null;
+        EntityManagerFactory emf = JpaStorageUtils.getEntityManagerFactory();
+
         try {
-            em = emf.createEntityManager();
-               tokenFound = get(em, id);
-               if (tokenFound != null) {
-                   em.getTransaction().begin();
-                   tokenFound.setEnabled(enabledFlag);
-                   tokenFound.setUpdatedAtItem(new Date());
-                   if (logger.isDebugEnabled()) {
-                       logger.debug("Updated token=" + JaxbUtils.toString(tokenFound, Token.class));
-                   }
-                   em.getTransaction().commit();
-               } else {
-                       String msg = String.format("Could not find token with id='%s'", id);
-                       throw new DocumentNotFoundException(msg);
-               }
+            EntityManager em = emf.createEntityManager();
+            tokenFound = (Token) em.find(Token.class, id);        
+            if (tokenFound == null) {
+                String msg = "Could not find token with ID=" + id;
+                logger.error(msg);
+                throw new DocumentNotFoundException(msg);
+            }
         } finally {
-               if (em != null && em.isOpen()) {
-                       em.close();
-               }
             if (emf != null) {
                 JpaStorageUtils.releaseEntityManagerFactory(emf);
             }
-        }        
-    }
-
-    public static Token get(EntityManager em, String id) throws DocumentNotFoundException {
-        Token tokenFound = null;
-        
-        em.getTransaction().begin();
-        tokenFound = em.find(Token.class, id);
-        em.getTransaction().commit();
-        if (tokenFound == null) {
-            String msg = "Could not find token with ID=" + id;
-            logger.error(msg);
-            throw new DocumentNotFoundException(msg);
         }
         
         return tokenFound;
-    }
+    }    
 
        /**
      * Deletes the token with given id
index 5d8f71ca197b947150a1a359bdf3394db7ae63c7..81f8a5e6b8b3fb958a4aad88c73c0eba80a970db 100644 (file)
@@ -90,6 +90,7 @@ public class AuthN {
     
     public static final String ROLE_SPRING_ADMIN_ID = "-1";
     public static final String ROLE_SPRING_ADMIN_NAME = "ROLE_SPRING_ADMIN";
+    public static final String ROLE_SPRING_GROUP_NAME = "Spring Security Administrator";
 
     // Define a special account value for the tenantManager. Yes, this is a hack, but
     // less troublesome than the alternatives.
@@ -100,6 +101,17 @@ public class AuthN {
         //FIXME initialize with the help of configuration meta data
         authnContext = new SpringAuthNContext();
     }
+    
+    public boolean isSystemAdmin() {
+       boolean result = false;
+       
+       String currentUserId = this.getUserId();
+       if (currentUserId.equals(AuthN.SPRING_ADMIN_USER) || currentUserId.equals(AuthN.ADMIN_TENANT_NAME)) {
+               result = true;
+       }
+       
+       return result;
+    }
 
     public final static AuthN get() {
         return self;
index 49d4f627bfde58d754692f9479eda93dfdf3ab32..5bb5c4bc28bb4fae5cc69dc620a1d2183fa8ee2b 100644 (file)
@@ -49,6 +49,8 @@ public class PermissionClient extends AbstractServiceClientImpl<PermissionsList,
        public static final String SERVICE_PATH_COMPONENT = SERVICE_NAME;       
        public static final String SERVICE_PATH = "/" + SERVICE_PATH_COMPONENT;
        public static final String SERVICE_PATH_PROXY = SERVICE_PATH + "/";
+    public final static String IMMUTABLE = "immutable";
+    public static final String PERMISSION_UPDATE_CSID = "PERMISSION_UPDATE_CSID";
        
        public enum ActionCompare {
            ACTION_GROUP_EMPTY, ACTION_LIST_EMPTY, ACTIONS_MISSING, MATCHES, MISMATCHES
index aa5732b2dfbd192e9d6ad062d16875ae2fc517f8..45edbe65e920ace074a890f313ed4a0c8502c872 100644 (file)
@@ -45,10 +45,11 @@ public class RoleClient extends AbstractServiceClientImpl<RolesList, Role, Role,
        public static final String SERVICE_PATH_PROXY = SERVICE_PATH + "/";     
        public final static String IMMUTABLE = "immutable";
        public final static String INCLUDE_PERMS_QP = "showPerms";
-       
+
        //
        // Used to qualify backend role name
-       private final static String BACKEND_ROLE_PREFIX = "ROLE_";      
+       //
+       public final static String BACKEND_ROLE_PREFIX = "ROLE_";       
 
     public RoleClient() throws Exception {
                super();
@@ -57,7 +58,7 @@ public class RoleClient extends AbstractServiceClientImpl<RolesList, Role, Role,
     public RoleClient(String clientPropertiesFilename) throws Exception {
                super(clientPropertiesFilename);
        }
-    
+        
     /**
      * Creates a backend (Spring Security as of v4.5) role name.
      * @param roleDisplayName
index d0c8f260a5595c12c94b41283c9e69379406abc0..c444233ed1129985ee0383ba18d0bfe687f1649f 100644 (file)
@@ -124,14 +124,14 @@ public class PermissionRoleServiceTest extends AbstractServiceTestImpl<Permissio
 //        pvi.setPermissionId(iPermId);
 //        permValues.put(pvi.getResourceName(), pvi);
 
-        String rn1 = "ROLE_CO1" + TEST_MARKER;
+        String rn1 = "xROLE_CO1" + TEST_MARKER;
         String r1RoleId = createRole(rn1);
         RoleValue rv1 = new RoleValue();
         rv1.setRoleId(r1RoleId);
         rv1.setRoleName(rn1);
         roleValues.put(rv1.getRoleName(), rv1);
 
-        String rn2 = "ROLE_CO2" + TEST_MARKER;
+        String rn2 = "xROLE_CO2" + TEST_MARKER;
         String r2RoleId = createRole(rn2);
         RoleValue rv2 = new RoleValue();
         rv2.setRoleId(r2RoleId);
index b92fc8a3ec83655c660318cf2acc14553069fb46..7a54eb992ad41fc6c8ff18106233ebe3ea407e7c 100644 (file)
@@ -68,7 +68,7 @@ public class RolePermissionServiceTest extends AbstractServiceTestImpl<Permissio
     private final static Logger logger = LoggerFactory.getLogger(CLASS_NAME);
     // Instance variables specific to this test.
     final private static String TEST_MARKER = "_RolePermissionServiceTest";
-    final private static String TEST_ROLE_NAME = "ROLE";
+    final private static String TEST_ROLE_NAME = "xROLE";
     final private static String NO_REL_SUFFIX = "-no-rel";
     /** The perm values. */
     private Hashtable<String, PermissionValue> permValues = new Hashtable<String, PermissionValue>();
index 9ac0801ea8d492e77354f1d38f2f29eb5104cc3f..5a70fb614720c4b4fabbefbcdfc748d36b202d6a 100644 (file)
@@ -61,18 +61,18 @@ public class RoleServiceTest extends AbstractServiceTestImpl<RolesList, Role, Ro
     // Used to create unique identifiers
     static private final Random random = new Random(System.currentTimeMillis());
     
-       private static final String PERM_1_RL_RESOURCE = "ROLE_TEST_PERMVALUE_RESOURCE_1";
+       private static final String PERM_1_RL_RESOURCE = "xROLE_TEST_PERMVALUE_RESOURCE_1";
        private static final String PERM_1_RL_ACTIONGROUP = "RL";
-       private static final String PERM_2_RL_RESOURCE = "ROLE_TEST_PERMVALUE_RESOURCE_2";
+       private static final String PERM_2_RL_RESOURCE = "xROLE_TEST_PERMVALUE_RESOURCE_2";
        private static final String PERM_2_RL_ACTIONGROUP = "CRUL";
-       private static final String PERM_3_RL_RESOURCE = "ROLE_TEST_PERMVALUE_RESOURCE_3";
+       private static final String PERM_3_RL_RESOURCE = "xROLE_TEST_PERMVALUE_RESOURCE_3";
        private static final String PERM_3_RL_ACTIONGROUP = "CRUDL";
 
 
     // Instance variables specific to this test.
     /** The known resource id. */
-    private String knownRoleName = "ROLE_USERS_MOCK-1";
-    private String knownRoleDisplayName = "ROLE_DISPLAYNAME_USERS_MOCK-1";
+    private String knownRoleName = "xROLE_USERS_MOCK-1";
+    private String knownRoleDisplayName = "xROLE_DISPLAYNAME_USERS_MOCK-1";
     private String verifyResourceId = null;
     private String verifyRoleName = "collections_manager_mock-1";
     //
@@ -498,7 +498,7 @@ public class RoleServiceTest extends AbstractServiceTestImpl<RolesList, Role, Ro
                res.close();
         }
 
-        Role role2 = createRoleInstance("ROLE_COLLECTIONS_CURATOR_TEST",
+        Role role2 = createRoleInstance("xROLE_COLLECTIONS_CURATOR_TEST",
                 "collections curator", true);
         res = client.create(role2);
         try {
@@ -512,7 +512,7 @@ public class RoleServiceTest extends AbstractServiceTestImpl<RolesList, Role, Ro
                res.close();
         }
 
-        Role role3 = createRoleInstance("ROLE_MOVINGIMAGE_ADMIN_TEST",
+        Role role3 = createRoleInstance("xROLE_MOVINGIMAGE_ADMIN_TEST",
                 "moving image admin", true);
         res = client.create(role3);
         try {
@@ -615,7 +615,7 @@ public class RoleServiceTest extends AbstractServiceTestImpl<RolesList, Role, Ro
                Assert.assertNotNull(output);
        
                //FIXME: Tenant ID of "1" should not be hard coded
-               String roleNameToVerify = "ROLE_" +
+               String roleNameToVerify = RoleClient.BACKEND_ROLE_PREFIX +
                        "1_" +
                        verifyRoleName.toUpperCase();
                Assert.assertEquals(output.getRoleName(), roleNameToVerify,
@@ -949,7 +949,7 @@ public class RoleServiceTest extends AbstractServiceTestImpl<RolesList, Role, Ro
         // Note: The ID used in this 'create' call may be arbitrary.
         // The only relevant ID may be the one used in updateRole(), below.
         RoleClient client = new RoleClient();
-        Role role = createRoleInstance("ROLE_XXX",
+        Role role = createRoleInstance("xROLE_XXX",
                 "xxx",
                 true);
         Response res = client.update(NON_EXISTENT_ID, role);
index b54b8e50d8c18ac29cf54c5162b6bfb3829b432a..7b9a3d50923d86fea57e4298eb75a964c611a8a9 100644 (file)
@@ -115,6 +115,7 @@ public class AuthorizationSeedDriver {
 
     public void generate(JPATransactionContext jpaTransactionContext) {
         try {
+               login();
             authzGen = new AuthorizationGen();
             authzGen.initialize(tenantBindingFile);
             authzGen.createDefaultRoles(jpaTransactionContext);
@@ -129,16 +130,21 @@ public class AuthorizationSeedDriver {
         } catch (Exception ex) {
             logger.error("AuthorizationSeedDriver caught an exception: ", ex);
             throw new RuntimeException(ex);
+        } finally {
+               logout();
         }
     }
 
     public void seed(JPATransactionContext jpaTransactionContext) {
         TransactionStatus status = null;
         try {
+            login();
+            //
                // Push all the authz info into the cspace DB tables -this include default roles, permissions, and permroles
+            //
             store();
-
-            setupSpring();
+            
+            setupSpringSecurity();
             status = beginTransaction("seedData");
             AuthorizationSeed authzSeed = new AuthorizationSeed();
             authzSeed.seedPermissions(jpaTransactionContext, authzGen.getDefaultPermissions(), authzGen.getDefaultPermissionRoles());
@@ -164,10 +170,9 @@ public class AuthorizationSeedDriver {
     /**
      * Setup of Spring Security context
      */
-    private void setupSpring() {
+    private void setupSpringSecurity() {
         ClassPathXmlApplicationContext appContext = new ClassPathXmlApplicationContext(
                 new String[]{SPRING_SECURITY_METADATA});
-        login();
         System.setProperty("spring-beans-config", SPRING_SECURITY_METADATA);
         //
         // authZ local not used but call to AuthZ.get() has side-effect of initializing our Spring Security context
index 23510b009f7fdb274983608ac474fcca33dca95f..a5cef46519aabf787d7557884016cc706ad96ad1 100644 (file)
@@ -56,7 +56,7 @@ import org.collectionspace.services.config.tenant.TenantBindingType;
  * @author 
  */
 public class AuthorizationGen {
-
+    final Logger logger = LoggerFactory.getLogger(AuthorizationGen.class);
     //
     // Should the base resource act as a proxy for its sub-resources for AuthZ purposes
     //
@@ -66,18 +66,21 @@ public class AuthorizationGen {
     
        private static final boolean USE_APP_GENERATED_BINDINGS = true;
     
-    final Logger logger = LoggerFactory.getLogger(AuthorizationGen.class);
+    private List<Permission> readWritePermList = new ArrayList<Permission>();
     private List<Permission> tenantMgmntPermList = new ArrayList<Permission>();
     private List<PermissionRole> tenantMgmntPermRoleList = new ArrayList<PermissionRole>();
+    
     private List<Permission> adminPermList = new ArrayList<Permission>();
     private List<PermissionRole> adminPermRoleList = new ArrayList<PermissionRole>();
+    
     private List<Permission> readerPermList = new ArrayList<Permission>();
     private List<PermissionRole> readerPermRoleList = new ArrayList<PermissionRole>();
+    
     private List<Role> adminRoles = new ArrayList<Role>();
     private List<Role> readerRoles = new ArrayList<Role>();
+    
     private Role cspaceTenantMgmntRole;
-    private Hashtable<String, TenantBindingType> tenantBindings =
-            new Hashtable<String, TenantBindingType>();
+    private Hashtable<String, TenantBindingType> tenantBindings = new Hashtable<String, TenantBindingType>();
        //
     // Store the list of default roles, perms, and roleperms
     //
@@ -112,11 +115,14 @@ public class AuthorizationGen {
      */
     public void createDefaultPermissions(JPATransactionContext jpaTransactionContext) {
         for (String tenantId : tenantBindings.keySet()) {
-            List<Permission> adminPerms = createDefaultAdminPermissions(tenantId, AUTHZ_IS_ENTITY_PROXY);
+            List<Permission> adminPerms = createDefaultAdminPermissions(tenantId, AUTHZ_IS_ENTITY_PROXY); // CRUDL perms
             adminPermList.addAll(adminPerms);
 
-            List<Permission> readerPerms = createDefaultReaderPermissions(tenantId, AUTHZ_IS_ENTITY_PROXY);
+            List<Permission> readerPerms = createDefaultReaderPermissions(tenantId, AUTHZ_IS_ENTITY_PROXY); // RL perms
             readerPermList.addAll(readerPerms);
+            
+            List<Permission> readWritePerms = createDefaultReadWritePermissions(tenantId, AUTHZ_IS_ENTITY_PROXY); // CRUL perms
+            readWritePermList.addAll(readWritePerms);
         }
         
         List<Permission> tenantMgmntPerms = createDefaultTenantMgmntPermissions();
@@ -178,19 +184,48 @@ public class AuthorizationGen {
        return perm;
     }
 
-    private Permission buildAdminPermission(String tenantId, String resourceName) {
-       String description = "Generated admin permission.";
-       return AuthorizationCommon.createPermission(tenantId, resourceName, description, AuthorizationCommon.ACTIONGROUP_CRUDL_NAME);
-    }
+    /**
+     * createDefaultReadWritePermissions creates read-write (CRUL) permissions for all services
+     * used by the given tenant
+     * @param tenantId
+     * @return
+     */
+    public List<Permission> createDefaultReadWritePermissions(String tenantId, boolean isEntityProxy) {
+        ArrayList<Permission> apcList = new ArrayList<Permission>();
 
+        TenantBindingType tbinding = tenantBindings.get(tenantId);
+        for (ServiceBindingType sbinding : tbinding.getServiceBindings()) {
+            //add permissions for the main path
+               String resourceName = sbinding.getName().toLowerCase().trim();
+               if (isEntityProxy == true) {
+                       resourceName = SecurityUtils.getResourceEntity(resourceName);
+               }
+            Permission perm = buildReadWritePermission(tbinding.getId(), resourceName);
+            apcList.add(perm);
+
+            //add permissions for alternate paths
+            if (isEntityProxy == false) {
+                   List<String> uriPaths = sbinding.getUriPath();
+                   for (String uriPath : uriPaths) {
+                       perm = buildReadWritePermission(tbinding.getId(), uriPath.toLowerCase());
+                       apcList.add(perm);
+                   }
+            }
+        }
+
+        return apcList;
+    }
+    
     /**
      * createDefaultReaderPermissions creates read only permissions for all services
      * used by the given tenant
+     * 
      * @param tenantId
      * @return
      */
     public List<Permission> createDefaultReaderPermissions(String tenantId, boolean isEntityProxy) {
         ArrayList<Permission> apcList = new ArrayList<Permission>();
+        
         TenantBindingType tbinding = tenantBindings.get(tenantId);
         for (ServiceBindingType sbinding : tbinding.getServiceBindings()) {
             //add permissions for the main path
@@ -198,27 +233,35 @@ public class AuthorizationGen {
                if (isEntityProxy == true) {
                        resourceName = SecurityUtils.getResourceEntity(resourceName);
                }               
-            Permission perm = buildReaderPermission(tbinding.getId(),
-                    resourceName);
+            Permission perm = buildReaderPermission(tbinding.getId(), resourceName);
             apcList.add(perm);
 
             //add permissions for alternate paths
             if (isEntityProxy == false) {
                    List<String> uriPaths = sbinding.getUriPath();
                    for (String uriPath : uriPaths) {
-                       perm = buildReaderPermission(tbinding.getId(),
-                               uriPath.toLowerCase());
+                       perm = buildReaderPermission(tbinding.getId(), uriPath.toLowerCase());
                        apcList.add(perm);
                    }
             }
         }
+        
         return apcList;
-
     }
 
+    private Permission buildAdminPermission(String tenantId, String resourceName) {
+       String description = "Generated admin permission.";
+       return AuthorizationCommon.createPermission(tenantId, resourceName, description, AuthorizationCommon.ACTIONGROUP_CRUDL_NAME, true);
+    }
+    
     private Permission buildReaderPermission(String tenantId, String resourceName) {
-       String description = "Generated read-only permission.";
-       return AuthorizationCommon.createPermission(tenantId, resourceName, description, AuthorizationCommon.ACTIONGROUP_RL_NAME);      
+       String description = "Generated read-only (RL) permission.";
+       return AuthorizationCommon.createPermission(tenantId, resourceName, description, AuthorizationCommon.ACTIONGROUP_RL_NAME, true);        
+    }
+    
+    private Permission buildReadWritePermission(String tenantId, String resourceName) {
+       String description = "Generated read-write (CRUL) permission.";
+       return AuthorizationCommon.createPermission(tenantId, resourceName, description, AuthorizationCommon.ACTIONGROUP_CRUL_NAME, true);      
     }
 
     public List<Permission> getDefaultPermissions() {
@@ -226,6 +269,7 @@ public class AuthorizationGen {
                allPermList = new ArrayList<Permission>();
                allPermList.addAll(adminPermList);
                allPermList.addAll(readerPermList);
+               allPermList.addAll(readWritePermList);          
                allPermList.addAll(tenantMgmntPermList);
        }
         return allPermList;
@@ -315,7 +359,8 @@ public class AuthorizationGen {
         /* for (Permission p : adminPermList) {
             PermissionRole permCAdmRole = associatePermissionRoles(p, roles, false);
             adminPermRoleList.add(permCAdmRole);
-        }  */       
+        }  */
+        
         // Now associate the tenant management perms to the role
         for (Permission p : tenantMgmntPermList) {
                // Note we enforce tenant, as should all be tenant 0 (the special one)
index bd849dc15ee98fcc6ec46c6831ec272afa84a9b1..7ac92c819254fe45ca8d38b3e773f9520df88347 100644 (file)
@@ -106,7 +106,7 @@ public class AuthorizationSeed {
                }
             for (PermissionRole pr : permRoleList) {
                 if (pr.getPermission().get(0).getPermissionId().equals(p.getCsid())) {
-                       AuthorizationCommon.addPermissionsForUri(p, pr);
+                       AuthorizationCommon.addPermissionsForUri(jpaTransactionContext, p, pr);
                 }
             }
         }
index 0ae23e998207f8190eaafda8aabfac1a51903c4e..17bd0f7b53d91282cc10943d03d009ecdac78101 100644 (file)
@@ -30,6 +30,7 @@ import org.collectionspace.services.common.ServiceMessages;
 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.document.DocumentNotFoundException;
 import org.collectionspace.services.common.storage.StorageClient;
 import org.collectionspace.services.common.storage.TransactionContext;
 import org.collectionspace.services.common.storage.jpa.JpaStorageClientImpl;
@@ -57,7 +58,7 @@ import javax.ws.rs.core.UriInfo;
 @Consumes("application/xml")
 @Produces("application/xml")
 @SuppressWarnings("unchecked")
-public class RoleResource extends SecurityResourceBase {
+public class RoleResource extends SecurityResourceBase<Role, Role> {
 
     final Logger logger = LoggerFactory.getLogger(RoleResource.class);
     final StorageClient storageClient = new JpaStorageClientImpl();
@@ -78,12 +79,12 @@ public class RoleResource extends SecurityResourceBase {
     }
 
     @Override
-    public ServiceContextFactory getServiceContextFactory() {
+    public ServiceContextFactory<Role, Role> getServiceContextFactory() {
         return RemoteServiceContextFactory.get();
     }
 
     @Override
-    public StorageClient getStorageClient(ServiceContext ctx) {
+    public StorageClient getStorageClient(ServiceContext<Role, Role> ctx) {
         //FIXME use ctx to identify storage client
         return storageClient;
     }
@@ -114,7 +115,7 @@ public class RoleResource extends SecurityResourceBase {
             AccountRoleSubResource subResource =
                     new AccountRoleSubResource(AccountRoleSubResource.ACCOUNT_ACCOUNTROLE_SERVICE);
             //get relationships for a role
-            result = subResource.getAccountRole((ServiceContext)null, accCsid, SubjectType.ACCOUNT);
+            result = subResource.getAccountRole((ServiceContext<Role, Role>)null, accCsid, SubjectType.ACCOUNT);
         } catch (Exception e) {
             throw bigReThrow(e, ServiceMessages.GET_FAILED, accCsid);
         }
@@ -164,15 +165,19 @@ public class RoleResource extends SecurityResourceBase {
             // If marked as metadata immutable, do not delete
             if (RoleClient.IMMUTABLE.equals(role.getMetadataProtection())) {
                 Response response = 
-                       Response.status(Response.Status.FORBIDDEN).entity("Role: "+csid+" is immutable.").type("text/plain").build();
+                       Response.status(Response.Status.FORBIDDEN).entity("Role: "+csid+" is immutable.").type("text/plain").build(); // FIXME: Should be status code 423 (resource locked)
                 return response;
             }
             //
-            // delete all the permission/role relationships
+            // delete all the permission/role relationships (if any)
             //
             PermissionRoleSubResource permRoleResource =
                     new PermissionRoleSubResource(PermissionRoleSubResource.ROLE_PERMROLE_SERVICE);
-            permRoleResource.deletePermissionRole(ctx, csid, SubjectType.PERMISSION);
+            try {
+               permRoleResource.deletePermissionRole(ctx, csid, SubjectType.PERMISSION);
+            } catch (DocumentNotFoundException dnf) {
+               // consume exception, not a problem.  Just means no relationships exist
+            }
             //
             //delete all the account/role relationships associate with this role
             //
@@ -182,7 +187,7 @@ public class RoleResource extends SecurityResourceBase {
             //
             //finally, delete the role itself
             //
-            ((JpaStorageClientImpl) getStorageClient(ctx)).deleteWhere(ctx, csid);
+            ((JpaStorageClientImpl) getStorageClient(ctx)).deleteWhere(ctx, csid); // FIXME: We should/could get rid the SID in Spring Security table as well
             transactionContext.commitTransaction();
         } catch(Exception e) {
                transactionContext.markForRollback();
@@ -198,26 +203,31 @@ public class RoleResource extends SecurityResourceBase {
     @Path("{csid}/permroles")
     public Response createRolePermission(@QueryParam("_method") String method, @PathParam("csid") String roleCsid,
             PermissionRole input) {
-        if (method != null) {
+        if (method != null) { // FIXME: Not sure how method could every be "delete"
             if ("delete".equalsIgnoreCase(method)) {
                 return deleteRolePermission(roleCsid, input);
             }
         }
-        
+                
         logger.debug("createRolePermission with roleCsid=" + roleCsid);
         ensureCSID(roleCsid, ServiceMessages.PUT_FAILED + "permroles role ");
         Response response = null;
         try {
                Role role = (Role)get(roleCsid, Role.class);
-            // If marked as metadata immutable, do not delete
+               //
+            // If marked as metadata immutable, do not change
+               //
             if (RoleClient.IMMUTABLE.equals(role.getPermsProtection())) {
                 response = 
                        Response.status(Response.Status.FORBIDDEN).entity("Role: "+roleCsid+" is immutable.").type("text/plain").build();
                 return response;
             }
+            //
+            // Create new role-permission relationships
+            //
             PermissionRoleSubResource subResource =
                     new PermissionRoleSubResource(PermissionRoleSubResource.ROLE_PERMROLE_SERVICE);
-            String permrolecsid = subResource.createPermissionRole((ServiceContext)null, input, SubjectType.PERMISSION);
+            String permrolecsid = subResource.createPermissionRole((ServiceContext<Role, Role>)null, input, SubjectType.PERMISSION);
             UriBuilder path = UriBuilder.fromResource(RoleResource.class);
             path.path(roleCsid + "/permroles/" + permrolecsid);
             response = Response.created(path.build()).build();
@@ -240,7 +250,7 @@ public class RoleResource extends SecurityResourceBase {
             PermissionRoleSubResource subResource =
                     new PermissionRoleSubResource(PermissionRoleSubResource.ROLE_PERMROLE_SERVICE);
             //get relationships for a role
-            result = subResource.getPermissionRole((ServiceContext)null, roleCsid, SubjectType.PERMISSION);
+            result = subResource.getPermissionRole((ServiceContext<Role, Role>)null, roleCsid, SubjectType.PERMISSION);
         } catch (Exception e) {
             throw bigReThrow(e, ServiceMessages.GET_FAILED, roleCsid);
         }
@@ -262,7 +272,7 @@ public class RoleResource extends SecurityResourceBase {
             PermissionRoleSubResource subResource =
                     new PermissionRoleSubResource(PermissionRoleSubResource.ROLE_PERMROLE_SERVICE);
             //get relationships for a role
-            result = subResource.getPermissionRoleRel((ServiceContext)null, roleCsid, SubjectType.PERMISSION, permrolecsid);
+            result = subResource.getPermissionRoleRel((ServiceContext<Role, Role>)null, roleCsid, SubjectType.PERMISSION, permrolecsid);
         } catch (Exception e) {
             throw bigReThrow(e, ServiceMessages.GET_FAILED, roleCsid);
         }
@@ -287,7 +297,7 @@ public class RoleResource extends SecurityResourceBase {
             PermissionRoleSubResource subResource =
                     new PermissionRoleSubResource(PermissionRoleSubResource.ROLE_PERMROLE_SERVICE);
             //delete all relationships for a permission
-            subResource.deletePermissionRole((ServiceContext)null, roleCsid, SubjectType.PERMISSION, input);
+            subResource.deletePermissionRole((ServiceContext<Role, Role>)null, roleCsid, SubjectType.PERMISSION, input);
             result = Response.status(HttpResponseCodes.SC_OK).build();
         } catch (Exception e) {
             throw bigReThrow(e, ServiceMessages.DELETE_FAILED, roleCsid);
@@ -314,7 +324,7 @@ public class RoleResource extends SecurityResourceBase {
             PermissionRoleSubResource subResource =
                     new PermissionRoleSubResource(PermissionRoleSubResource.ROLE_PERMROLE_SERVICE);
             //delete all relationships for a permission
-            subResource.deletePermissionRole((ServiceContext)null, roleCsid, SubjectType.PERMISSION);            
+            subResource.deletePermissionRole((ServiceContext<Role, Role>)null, roleCsid, SubjectType.PERMISSION);            
         } catch (Exception e) {
             throw bigReThrow(e, ServiceMessages.DELETE_FAILED, roleCsid);
         }
index 6c0ea8e81210a28f057c1de70c0e65942b876d57..9be3c7b6e270380a3b007f9795c1e8b6e6fc1b33 100644 (file)
@@ -1,262 +1 @@
-/**
- *  This document is a part of the source code and related artifacts
- *  for CollectionSpace, an open source collections management system
- *  for museums and related institutions:
-
- *  http://www.collectionspace.org
- *  http://wiki.collectionspace.org
-
- *  Copyright 2009 University of California at Berkeley
-
- *  Licensed under the Educational Community License (ECL), Version 2.0.
- *  You may not use this file except in compliance with this License.
-
- *  You may obtain a copy of the ECL 2.0 License at
-
- *  https://source.collectionspace.org/collection-space/LICENSE.txt
-
- *  Unless required by applicable law or agreed to in writing, software
- *  distributed under the License is distributed on an "AS IS" BASIS,
- *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- *  See the License for the specific language governing permissions and
- *  limitations under the License.
- */
-package org.collectionspace.services.authorization.storage;
-
-import java.util.ArrayList;
-import java.util.List;
-import java.util.UUID;
-
-import org.collectionspace.services.authorization.perms.ActionType;
-import org.collectionspace.services.authorization.CSpaceAction;
-import org.collectionspace.services.authorization.perms.Permission;
-import org.collectionspace.services.authorization.perms.PermissionAction;
-import org.collectionspace.services.authorization.perms.PermissionsList;
-import org.collectionspace.services.authorization.URIResourceImpl;
-
-import org.collectionspace.services.common.document.BadRequestException;
-import org.collectionspace.services.common.document.DocumentFilter;
-import org.collectionspace.services.common.document.DocumentWrapper;
-import org.collectionspace.services.common.document.JaxbUtils;
-import org.collectionspace.services.common.security.SecurityUtils;
-import org.collectionspace.services.common.storage.jpa.JpaDocumentHandler;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-/**
- * Document handler for Permission
- * @author 
- */
-public class PermissionDocumentHandler
-               extends JpaDocumentHandler<Permission, PermissionsList, Permission, List> {
-
-    private final Logger logger = LoggerFactory.getLogger(PermissionDocumentHandler.class);
-    private Permission permission;
-    private PermissionsList permissionsList;
-    
-    public CSpaceAction getAction(ActionType action) {
-        if (ActionType.CREATE.name().equals(action.name())) {
-            return CSpaceAction.CREATE;
-        } else if (ActionType.READ.equals(action)) {
-            return CSpaceAction.READ;
-        } else if (ActionType.UPDATE.equals(action)) {
-            return CSpaceAction.UPDATE;
-        } else if (ActionType.DELETE.equals(action)) {
-            return CSpaceAction.DELETE;
-        } else if (ActionType.SEARCH.equals(action)) {
-            return CSpaceAction.SEARCH;
-        } else if (ActionType.ADMIN.equals(action)) {
-            return CSpaceAction.ADMIN;
-        } else if (ActionType.START.equals(action)) {
-            return CSpaceAction.START;
-        } else if (ActionType.STOP.equals(action)) {
-            return CSpaceAction.STOP;
-        }
-        //
-        // We could not find a match, so we need to throw an exception.
-        //
-        throw new IllegalArgumentException("action = " + action.toString());
-    }
-    
-    /*
-     * Add the ACE hashed ID to the permission action so we can map the permission to the Spring Security
-     * tables.
-     */
-    private void handlePermissionActions(Permission perm) {
-       //FIXME: REM - Having Java class loader issues with ActionType class.  Not sure of the cause.
-       try {
-               List<PermissionAction> permActions = perm.getAction();
-               for (PermissionAction permAction : permActions) {
-                   CSpaceAction action = getAction(permAction.getName());
-                   URIResourceImpl uriRes = new URIResourceImpl(perm.getTenantId(),
-                           perm.getResourceName(), action);
-                   permAction.setObjectIdentity(uriRes.getHashedId().toString());
-                   permAction.setObjectIdentityResource(uriRes.getId());
-                   //PermissionActionUtil.update(perm, permAction);
-               }
-       } catch (Exception x) {
-               x.printStackTrace();
-       }
-    }
-
-    @Override
-    public void handleCreate(DocumentWrapper<Permission> wrapDoc) throws Exception {
-        String id = UUID.randomUUID().toString();
-        Permission permission = wrapDoc.getWrappedObject();
-        permission.setCsid(id);
-        setTenant(permission);
-        handlePermissionActions(permission);
-    }
-
-    @Override
-    public void completeCreate(DocumentWrapper<Permission> wrapDoc) throws Exception {
-    }
-
-    @Override
-    public void handleUpdate(DocumentWrapper<Permission> wrapDoc) throws Exception {
-        Permission permissionFound = wrapDoc.getWrappedObject();
-        Permission permissionReceived = getCommonPart();
-        merge(permissionReceived, permissionFound);
-    }
-
-    /**
-     * merge manually merges the from from to the to permission
-     * -this method is created due to inefficiency of JPA EM merge
-     * @param from
-     * @param to
-     * @return merged permission
-     */
-    private Permission merge(Permission from, Permission to) throws Exception {
-        if (!(from.getResourceName().equalsIgnoreCase(to.getResourceName()))) {
-            String msg = "Resource name cannot be changed " + to.getResourceName();
-            logger.error(msg);
-            throw new BadRequestException(msg);
-        }
-        //resource name, attribute  cannot be changed
-
-        if (from.getDescription() != null) {
-            to.setDescription(from.getDescription());
-        }
-        if (from.getEffect() != null) {
-            to.setEffect(from.getEffect());
-        }
-        List<PermissionAction> fromActions = from.getAction();
-        if (!fromActions.isEmpty()) {
-            //override the whole list, no reconcilliation by design
-            to.setAction(fromActions);
-        }
-
-        if (logger.isDebugEnabled()) {
-            logger.debug("merged permission=" + JaxbUtils.toString(to, Permission.class));
-        }
-
-        handlePermissionActions(to);
-        return to;
-    }
-
-    @Override
-    public void completeUpdate(DocumentWrapper<Permission> wrapDoc) throws Exception {
-        Permission upAcc = wrapDoc.getWrappedObject();
-        getServiceContext().setOutput(upAcc);
-        sanitize(upAcc);
-        //FIXME update lower-layer authorization (acls)
-        //will require deleting old permissions for this resource and adding
-        //new based on new actions and effect
-    }
-
-    @Override
-    public void handleGet(DocumentWrapper<Permission> wrapDoc) throws Exception {
-        setCommonPart(extractCommonPart(wrapDoc));
-        sanitize(getCommonPart());
-        getServiceContext().setOutput(permission);
-    }
-
-    @Override
-    public void handleGetAll(DocumentWrapper<List> wrapDoc) throws Exception {
-        PermissionsList permissionsList = extractCommonPartList(wrapDoc);
-        setCommonPartList(permissionsList);
-        getServiceContext().setOutput(getCommonPartList());
-    }
-
-    @Override
-    public void completeDelete(DocumentWrapper<Permission> wrapDoc) throws Exception {
-    }
-
-    @Override
-    public Permission extractCommonPart(
-            DocumentWrapper<Permission> wrapDoc)
-            throws Exception {
-        return wrapDoc.getWrappedObject();
-    }
-
-    @Override
-    public void fillCommonPart(Permission obj, DocumentWrapper<Permission> wrapDoc)
-            throws Exception {
-        throw new UnsupportedOperationException("operation not relevant for AccountDocumentHandler");
-    }
-
-    @Override
-    public PermissionsList extractCommonPartList(
-            DocumentWrapper<List> wrapDoc)
-            throws Exception {
-
-        PermissionsList permissionsList = new PermissionsList();
-        List<Permission> list = new ArrayList<Permission>();
-        permissionsList.setPermission(list);
-        for (Object obj : wrapDoc.getWrappedObject()) {
-            Permission permission = (Permission) obj;
-            sanitize(permission);
-            list.add(permission);
-        }
-        return permissionsList;
-    }
-
-    @Override
-    public Permission getCommonPart() {
-        return permission;
-    }
-
-    @Override
-    public void setCommonPart(Permission permission) {
-        this.permission = permission;
-    }
-
-    @Override
-    public PermissionsList getCommonPartList() {
-        return permissionsList;
-    }
-
-    @Override
-    public void setCommonPartList(PermissionsList permissionsList) {
-        this.permissionsList = permissionsList;
-    }
-
-    @Override
-    public String getQProperty(
-            String prop) {
-        return null;
-    }
-
-    @Override
-    public DocumentFilter createDocumentFilter() {
-        DocumentFilter filter = new PermissionJpaFilter(this.getServiceContext());
-        return filter;
-    }
-
-    /**
-     * sanitize removes data not needed to be sent to the consumer
-     * @param permission
-     */
-    private void sanitize(Permission permission) {
-        if (!SecurityUtils.isCSpaceAdmin()) {
-            permission.setTenantId(null);
-        }
-    }
-
-    private void setTenant(Permission permission) {
-        //set tenant only if not available from input
-        if (permission.getTenantId() == null || permission.getTenantId().isEmpty()) {
-            permission.setTenantId(getServiceContext().getTenantId());
-        }
-    }
-}
+The PermissionDocumentHandler.java file has been move to the "org.collectionspace.services.common" module.
\ No newline at end of file
index c9628487ca5d5f83dcf959208af44b71322e5dd7..0e453481389793a346db045d7c868a9e922fba42 100644 (file)
@@ -26,6 +26,8 @@ package org.collectionspace.services.authorization.storage;
 
 import java.util.List;
 
+import javax.xml.bind.JAXBElement;
+
 import org.collectionspace.services.authorization.perms.Permission;
 import org.collectionspace.services.authorization.perms.PermissionAction;
 import org.collectionspace.services.client.PermissionClient;
@@ -33,6 +35,7 @@ import org.collectionspace.services.common.ServiceMessages;
 import org.collectionspace.services.common.context.ServiceContext;
 import org.collectionspace.services.common.document.DocumentHandler.Action;
 import org.collectionspace.services.common.document.InvalidDocumentException;
+import org.collectionspace.services.common.document.JaxbUtils;
 import org.collectionspace.services.common.document.ValidatorHandler;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -41,12 +44,12 @@ import org.slf4j.LoggerFactory;
  * PermissionValidatorHandler executes validation rules for permission
  * @author 
  */
-public class PermissionValidatorHandler implements ValidatorHandler {
+public class PermissionValidatorHandler implements ValidatorHandler<Permission, Permission> {
 
     final Logger logger = LoggerFactory.getLogger(PermissionValidatorHandler.class);
 
     @Override
-    public void validate(Action action, ServiceContext ctx)
+    public void validate(Action action, ServiceContext<Permission, Permission> ctx)
             throws InvalidDocumentException {
         if (logger.isDebugEnabled()) {
             logger.debug("validate() action=" + action.name());
@@ -57,21 +60,27 @@ public class PermissionValidatorHandler implements ValidatorHandler {
             boolean invalid = false;
 
             if (action.equals(Action.CREATE)) {
-                //create specific validation here
                 if (permission.getResourceName() == null || permission.getResourceName().isEmpty()) {
                     invalid = true;
                     msgBldr.append("\nThe resource name for creating a new permission resource is missing or empty.");
-                } else {
-                       invalid = !validateActionFields(permission);
                 }
+               if (validateActionFields(action, permission) == false) {
+                       invalid = true;
+                    msgBldr.append("\nAction info is missing or inconsistent.");
+                }
+               if (permission.getEffect() == null) {
+                       invalid = true;
+                    msgBldr.append("\n'effect' elment is missing from the payload or is not set to either PERMIT or DENY.");
+               }
             } else if (action.equals(Action.UPDATE)) {
-                //update specific validation here
                 if (permission.getResourceName() == null || permission.getResourceName().isEmpty()) {
                     invalid = true;
                     msgBldr.append("\nThe resource name for updating an existing permission is missing or empty.");
-                } else {
-                       invalid = !validateActionFields(permission);
                 }
+               if (validateActionFields(action, permission) == false) {
+                       invalid = true;
+                    msgBldr.append("\nAction info is missing or inconsistent.");
+                }                
             }
             
             if (invalid) {
@@ -86,7 +95,7 @@ public class PermissionValidatorHandler implements ValidatorHandler {
         }
     }
 
-       private boolean validateActionFields(Permission permission) {
+       private boolean validateActionFields(Action action, Permission permission) {
                boolean result = true;
                
                List<PermissionAction> permActionList = permission.getAction();
@@ -106,7 +115,15 @@ public class PermissionValidatorHandler implements ValidatorHandler {
                        // if the action list field is not set, but the action group is set then set the action actionL
                        permission.setAction(PermissionClient.getActionList(permActionGroup));
                } else {
-                       // both action fields are not set, we don't care.
+                       if (action.equals(Action.CREATE)) {
+                               result = false;
+                               org.collectionspace.services.authorization.perms.ObjectFactory objectFactory = 
+                                               new org.collectionspace.services.authorization.perms.ObjectFactory();
+                               JAXBElement<Permission> permJaxbElement = objectFactory.createPermission(permission);
+                               String msg = String.format("Either (or both) the 'action' or 'actiongroup' element needs to be set: %s",
+                                               JaxbUtils.toString(permJaxbElement, Permission.class));                 
+                               logger.error(msg);
+                       }
                }
                
                return result;
index cd777062fac709394b6c5688f6319e02431bd33d..8e436a4fd6eeac79a88f0b5625db43c308302434 100644 (file)
@@ -45,6 +45,7 @@ import org.collectionspace.services.common.document.DocumentFilter;
 import org.collectionspace.services.common.document.DocumentWrapper;
 import org.collectionspace.services.common.document.JaxbUtils;
 import org.collectionspace.services.common.security.SecurityUtils;
+import org.collectionspace.services.common.storage.TransactionContext;
 import org.collectionspace.services.common.storage.jpa.JpaDocumentHandler;
 
 import org.slf4j.Logger;
@@ -164,6 +165,13 @@ public class RoleDocumentHandler
         //
        List<PermissionValue> permValueList = role.getPermission();
        if (permValueList != null && permValueList.size() > 0) {
+               //
+               // To prevent new Permissions being created (especially low-level Spring Security perms), we'll first flush the current
+               // JPA context to ensure our Role can be successfully persisted.
+               //
+               TransactionContext jpaTransactionContext = this.getServiceContext().getCurrentTransactionContext();
+               jpaTransactionContext.flush();
+               
                // create and persist a permrole instance
                // The caller of this method needs to ensure a valid and active EM (EntityManager) instance is in the Service context
                RoleValue roleValue = RoleFactory.createRoleValueInstance(role);
index 4e239b32dde75fb08b9482805a2aa04d02e7e1d1..75b542bafb60312a1436415f8295a7043285e45b 100644 (file)
@@ -26,6 +26,7 @@
 package org.collectionspace.services.authorization.storage;
 
 import org.collectionspace.services.authorization.Role;
+import org.collectionspace.services.client.RoleClient;
 import org.collectionspace.services.common.ServiceMessages;
 import org.collectionspace.services.common.context.ServiceContext;
 import org.collectionspace.services.common.document.DocumentHandler.Action;
@@ -38,35 +39,38 @@ import org.slf4j.LoggerFactory;
  * RoleValidatorHandler executes validation rules for role
  * @author 
  */
-public class RoleValidatorHandler implements ValidatorHandler {
+public class RoleValidatorHandler implements ValidatorHandler<Role, Role> {
 
     final Logger logger = LoggerFactory.getLogger(RoleValidatorHandler.class);
 
     @Override
-    public void validate(Action action, ServiceContext ctx)
+    public void validate(Action action, ServiceContext<Role, Role> ctx)
             throws InvalidDocumentException {
         if (logger.isDebugEnabled()) {
             logger.debug("validate() action=" + action.name());
         }
         try {
-            Role role = (Role) ctx.getInput();
+            Role role = ctx.getInput();
             StringBuilder msgBldr = new StringBuilder(ServiceMessages.VALIDATION_FAILURE);
             boolean invalid = false;
 
             if (action.equals(Action.CREATE)) {
-
-                //create specific validation here
                 if (role.getRoleName() == null || role.getRoleName().isEmpty()) {
                     invalid = true;
                     msgBldr.append("\nroleName : missing or empty");
+                } else {
+                       if (role.getRoleName().startsWith(RoleClient.BACKEND_ROLE_PREFIX)) {
+                        invalid = true;
+                        msgBldr.append(String.format("\nroleName : cannot beging with '%s'", RoleClient.BACKEND_ROLE_PREFIX));
+                       }
                 }
             } else if (action.equals(Action.UPDATE)) {
-                //update specific validation here
                 if (role.getRoleName() == null || role.getRoleName().isEmpty()) {
                     invalid = true;
                     msgBldr.append("\nroleName : cannot be missing or empty");
                 }
             }
+            
             if (invalid) {
                 String msg = msgBldr.toString();
                 logger.error(msg);
index c5cb4b10f04b9e95a30d90d0f7e8bb4acf3fdca8..eb9f87fc1715df70dbe1ca8e2027dc73d7f3445b 100644 (file)
@@ -5,10 +5,24 @@ DROP TABLE IF EXISTS permissions_actions CASCADE;
 DROP TABLE IF EXISTS permissions_roles CASCADE;
 DROP TABLE IF EXISTS roles CASCADE;
 DROP SEQUENCE IF EXISTS hibernate_sequence;
-create table accounts_roles (HJID int8 not null, account_id varchar(128) not null, created_at timestamp not null, role_id varchar(128) not null, role_name varchar(255), screen_name varchar(255), user_id varchar(128) not null, primary key (HJID), unique (account_id, role_id));
-create table permissions (csid varchar(128) not null, action_group varchar(128), attribute_name varchar(128), created_at timestamp not null, description varchar(255), effect varchar(32) not null, resource_name varchar(128) not null, tenant_id varchar(128) not null, updated_at timestamp, primary key (csid), unique (resource_name, action_group, tenant_id));
-create table permissions_actions (HJID int8 not null, name varchar(128) not null, objectIdentity varchar(128) not null, objectIdentityResource varchar(128) not null, ACTION__PERMISSION_CSID varchar(128), primary key (HJID));
-create table permissions_roles (HJID int8 not null, actionGroup varchar(255), created_at timestamp not null, permission_id varchar(128) not null, permission_resource varchar(255), role_id varchar(128) not null, role_name varchar(255), primary key (HJID), unique (permission_id, role_id));
-create table roles (csid varchar(128) not null, created_at timestamp not null, description varchar(255), displayname varchar(200) not null, rolegroup varchar(255), rolename varchar(200) not null, tenant_id varchar(128) not null, metadata_protection varchar(255), perms_protection varchar(255), updated_at timestamp, primary key (csid), unique (rolename, tenant_id), unique (displayname, tenant_id));
+
+create table accounts_roles (HJID int8 not null, account_id varchar(128) not null, created_at timestamp not null, role_id varchar(128) not null, 
+       role_name varchar(255) not null, screen_name varchar(255), user_id varchar(128) not null, primary key (HJID), unique (account_id, role_id));
+
+create table permissions (csid varchar(128) not null, action_group varchar(128), attribute_name varchar(128), created_at timestamp not null, description varchar(255), effect varchar(32) not null, 
+       metadata_protection varchar(255), actions_protection varchar(255), 
+       resource_name varchar(128) not null, tenant_id varchar(128) not null,
+       updated_at timestamp, primary key (csid));
+
+create table permissions_actions (HJID int8 not null, name varchar(128) not null, objectIdentity varchar(128) not null, objectIdentityResource varchar(128) not null, 
+       ACTION__PERMISSION_CSID varchar(128), primary key (HJID));
+
+       create table permissions_roles (HJID int8 not null, actionGroup varchar(255), created_at timestamp not null, permission_id varchar(128) not null, permission_resource varchar(255), role_id varchar(128) not null, role_name varchar(255), primary key (HJID), unique (permission_id, role_id));
+
+create table roles (csid varchar(128) not null, created_at timestamp not null, description varchar(255), displayname varchar(200) not null, rolegroup varchar(255), 
+       rolename varchar(200) not null, tenant_id varchar(128) not null, 
+       metadata_protection varchar(255), perms_protection varchar(255), 
+       updated_at timestamp, primary key (csid), unique (rolename, tenant_id), unique (displayname, tenant_id));
+
 alter table permissions_actions add constraint FK85F82042E2DC84FD foreign key (ACTION__PERMISSION_CSID) references permissions;
 create sequence hibernate_sequence;
index 58e7ac8dee6a0dfab92f9a03f00e49f375730c70..016954d53a2b11957393d19bbe69babc025449a6 100644 (file)
@@ -28,12 +28,13 @@ import java.util.HashSet;
 
 import org.apache.commons.logging.Log;
 import org.apache.commons.logging.LogFactory;
+
 import org.collectionspace.authentication.AuthN;
 import org.collectionspace.authentication.CSpaceTenant;
 import org.collectionspace.authentication.CSpaceUser;
-import org.collectionspace.authentication.spi.AuthNContext;
 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;
 import org.springframework.security.core.Authentication;
@@ -107,8 +108,7 @@ public class AuthZ {
         if (logger.isDebugEnabled()) {
             logger.debug("reading beanConfig=" + beanConfig);
         }
-        ClassPathXmlApplicationContext appContext = new ClassPathXmlApplicationContext(
-                new String[]{beanConfig});
+        ClassPathXmlApplicationContext appContext = new ClassPathXmlApplicationContext(new String[]{beanConfig}); // FIXME: This is never used.  Keep it for debugging?
         provider = (CSpaceAuthorizationProvider) appContext.getBean("cspaceAuthorizationProvider");
         if (logger.isDebugEnabled()) {
             logger.debug("initialized the authz provider");
@@ -127,7 +127,7 @@ public class AuthZ {
         try {
             for (CSpaceResource res : resources) {
                 CSpaceAction action = res.getAction();
-               addPermissions(res, action, principals, grant);
+               addPermission(res, action, principals, grant);
             }
                provider.commitTransaction(status);
         } catch (Throwable t) {
@@ -143,7 +143,7 @@ public class AuthZ {
      * @param principals
      * @param grant true to grant false to deny
      */
-    private void addPermissions(CSpaceResource res, CSpaceAction action, String[] principals, boolean grant)
+    private void addPermission(CSpaceResource res, CSpaceAction action, String[] principals, boolean grant)
             throws PermissionException {
         provider.getPermissionManager().addPermissionsToRoles(res, action, principals, grant);
         provider.clearAclCache();
@@ -156,14 +156,14 @@ public class AuthZ {
      * @param res
      * @param principals
      */
-    public void deletePermissionsFromRoles(CSpaceResource[] resources, String[] principals)
+    public void deletePermissionsFromRoles(CSpaceResource[] resources, String[] principals) // FIXME: # Can tx move one level up?
             throws PermissionNotFoundException, PermissionException {
        
         TransactionStatus status = provider.beginTransaction("deletePermssions");
         try {
                for (CSpaceResource res : resources) {
                        CSpaceAction action = res.getAction();
-                       deletePermissionsFromRoles(res, action, principals);
+                       deletePermissionFromRoles(res, action, principals);
             }
                provider.commitTransaction(status);
            } catch (Throwable t) {
@@ -179,9 +179,9 @@ public class AuthZ {
      * @param action
      * @param principals
      */
-    private void deletePermissionsFromRoles(CSpaceResource res, CSpaceAction action, String[] principals)
+    private void deletePermissionFromRoles(CSpaceResource res, CSpaceAction action, String[] principals)
             throws PermissionNotFoundException, PermissionException {
-        provider.getPermissionManager().deletePermissionsFromRoles(res, action, principals);
+        provider.getPermissionManager().deletePermissionFromRoles(res, action, principals);
         provider.clearAclCache();
     }
 
index b8f0f30de1b4d4bcb6952800a559fb9dea06a23f..c0dd99aa36a6c02140b7c0ef89b15b124d9e15a0 100644 (file)
@@ -57,7 +57,7 @@ public interface CSpacePermissionManager {
      * @see CSpaceResource
      * @see CSpaceAction
      */
-    public void deletePermissionsFromRoles(CSpaceResource res, CSpaceAction action, String[] principals)
+    public void deletePermissionFromRoles(CSpaceResource res, CSpaceAction action, String[] principals)
             throws PermissionNotFoundException, PermissionException;
 
     /**
index 70e5f0b6254dd95f7fd8259a3f60061ff75633fc..ef2b1074f1ae94a2330ba17f47b978ec99efe3c3 100644 (file)
@@ -137,7 +137,7 @@ public class SpringPermissionManager implements CSpacePermissionManager {
      * @throws PermissionException
      */
     @Override
-    public void deletePermissionsFromRoles(CSpaceResource res, CSpaceAction action, String[] principals)
+    public void deletePermissionFromRoles(CSpaceResource res, CSpaceAction action, String[] principals)
             throws PermissionNotFoundException, PermissionException {
         ObjectIdentity oid = SpringAuthorizationProvider.getObjectIdentity(res);
         Sid[] sids = SpringAuthorizationProvider.getSids(principals);
@@ -181,14 +181,6 @@ public class SpringPermissionManager implements CSpacePermissionManager {
                 throw new PermissionException(msg, ex);
             }
         }
-        if (log.isDebugEnabled()) {
-            log.debug("deletedpermissions(res,action,prin[]), success for "
-                    + " res=" + res.toString()
-                    + " action=" + action.toString()
-                    + " oid=" + oid.toString()
-                    + " perm=" + p.toString()
-                    + " sids=" + sids.toString());
-        }
     }
 
     /**
@@ -360,7 +352,7 @@ public class SpringPermissionManager implements CSpacePermissionManager {
                     + " found " + aces + " aces");
         }
         ArrayList<Integer> foundAces = new ArrayList<Integer>();
-        Iterator iter = acel.listIterator();
+        Iterator<AccessControlEntry> iter = acel.listIterator();
         //not possible to delete while iterating
         while (iter.hasNext()) {
             AccessControlEntry ace = (AccessControlEntry) iter.next();
@@ -376,11 +368,17 @@ public class SpringPermissionManager implements CSpacePermissionManager {
             }
             i++;
         }
+        
+        boolean updateNeeded = false;
         for (int j = foundAces.size() - 1; j >= 0; j--) {
             //the following operation does not work while iterating in the while loop
             acl.deleteAce(foundAces.get(j)); //autobox
+            updateNeeded = true;
+        }
+        
+        if (updateNeeded) {
+               provider.getProviderAclService().updateAcl(acl);
         }
-        provider.getProviderAclService().updateAcl(acl);
 
         if (log.isDebugEnabled()) {
             log.debug("deletePermissions: for acl oid=" + oid.toString()
index 427228b76c6dab3fe226e110f45664437ea23fb2..43746e8532362aac42729ad4a07424e59a76d18e 100644 (file)
@@ -25,8 +25,10 @@ package org.collectionspace.services.common.api;
 
 import java.io.File;
 import java.io.InputStream;
+import java.util.List;
 import java.util.Properties;
 import  java.util.regex.Pattern;
+
 import java.util.regex.Matcher;
 
 /** General utility methods.
@@ -402,4 +404,12 @@ public class Tools {
                
                return result;
        }
+
+       public static boolean isEmpty(List<?> theList) {
+               if (theList != null && theList.size() > 0) {
+                       return false;
+               } else {
+                       return true;
+               }
+       }
 }
index f331ef5f2fc15d55af6fcd6acff9c926c44205a4..d137750529a09e7bb23a7ee4a129141964834ac3 100644 (file)
@@ -27,24 +27,35 @@ import org.collectionspace.services.authorization.PermissionRole;
 import org.collectionspace.services.authorization.PermissionRoleRel;
 import org.collectionspace.services.authorization.SubjectType;
 import org.collectionspace.services.authorization.perms.Permission;
+import org.collectionspace.services.authorization.perms.PermissionAction;
 import org.collectionspace.services.authorization.perms.PermissionsList;
-import org.collectionspace.services.authorization.storage.AuthorizationDelegate;
+import org.collectionspace.services.authorization.storage.PermissionDocumentHandler;
 import org.collectionspace.services.client.CollectionSpaceClientUtils;
 import org.collectionspace.services.client.PayloadOutputPart;
 import org.collectionspace.services.client.PermissionClient;
+import org.collectionspace.services.common.CSWebApplicationException;
 import org.collectionspace.services.common.SecurityResourceBase;
 import org.collectionspace.services.common.ServiceMessages;
+import org.collectionspace.services.common.api.Tools;
+import org.collectionspace.services.common.authorization_mgt.PermissionRoleUtil;
 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.document.DocumentException;
+import org.collectionspace.services.common.document.DocumentHandler;
+import org.collectionspace.services.common.document.DocumentHandler.Action;
+import org.collectionspace.services.common.document.DocumentNotFoundException;
 import org.collectionspace.services.common.storage.StorageClient;
 import org.collectionspace.services.common.storage.TransactionContext;
 import org.collectionspace.services.common.storage.jpa.JPATransactionContext;
 import org.collectionspace.services.common.storage.jpa.JpaStorageClientImpl;
+
 import org.jboss.resteasy.util.HttpResponseCodes;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+import java.util.List;
+
 import javax.ws.rs.Consumes;
 import javax.ws.rs.DELETE;
 import javax.ws.rs.GET;
@@ -62,7 +73,7 @@ import javax.ws.rs.core.UriInfo;
 @Path(PermissionClient.SERVICE_PATH)
 @Consumes("application/xml")
 @Produces("application/xml")
-public class PermissionResource extends SecurityResourceBase {
+public class PermissionResource extends SecurityResourceBase<Permission, Permission> {
 
     final Logger logger = LoggerFactory.getLogger(PermissionResource.class);
     final StorageClient storageClient = new JpaStorageClientImpl();
@@ -89,7 +100,7 @@ public class PermissionResource extends SecurityResourceBase {
     }
 
     @Override
-    public StorageClient getStorageClient(@SuppressWarnings("rawtypes") ServiceContext ctx) {
+    public StorageClient getStorageClient(ServiceContext<Permission, Permission> ctx) {
         //FIXME use ctx to identify storage client
         return storageClient;
     }
@@ -110,7 +121,7 @@ public class PermissionResource extends SecurityResourceBase {
        Response response = createPermission(jpaTransactionContext, input);
        if (response.getStatus() == Response.Status.CREATED.getStatusCode()) {
                permCsid = CollectionSpaceClientUtils.extractId(response);
-               result = (Permission)get(jpaTransactionContext, permCsid, Permission.class);
+               result = (Permission)get(jpaTransactionContext, permCsid, Permission.class); // return the result of a full lookup of what we just persisted
        }
                
        return result;
@@ -134,42 +145,187 @@ public class PermissionResource extends SecurityResourceBase {
        return result;
     }
 
+    /**
+     * Updates a permission by first deleting it and all of it's relationships (with roles, and perm-actions) and then
+     * recreating it.  Unfortunately, we can't seem to be able to just update the perm-actions because of an issue with JPA.
+     * 
+     * @param ui
+     * @param csid
+     * @param theUpdate
+     * @return
+     * @throws Exception
+     */
     @PUT
     @Path("{csid}")
-    public Permission updatePermission(@PathParam("csid") String csid,Permission theUpdate) {
-         return (Permission)update(csid, theUpdate, Permission.class);
+    synchronized public Permission updatePermission(@Context UriInfo ui, @PathParam("csid") String csid, Permission theUpdate) throws Exception {
+       Permission result = null;
+        ensureCSID(csid, ServiceMessages.UPDATE_FAILED + "permission ");
+        
+               ServiceContext<Permission, Permission> ctx = createServiceContext(null, Permission.class);
+        PermissionDocumentHandler docHandler = (PermissionDocumentHandler) createDocumentHandler(ctx);
+
+        TransactionContext transactionContext = ctx.openConnection();
+        try {
+               transactionContext.beginTransaction();
+               //
+               // Get a copy of the currently persisted resource
+               //
+               Permission original = (Permission) get(transactionContext, csid, Permission.class);
+               if (original == null) {
+                       throw new DocumentNotFoundException(String.format("The Permission resource CSID=%s could not be found.", csid));
+               } else if (isImmutable(original) == true) {
+                               String msg = String.format("Permission resource CSID=%s is immutable and cannot be updated.", csid);
+                               throw new DocumentException(msg);
+               }
+
+               Permission perm = copyForUpdate(original);  // If we upgrade to JPA 2.0, we could just "detach" the original instead of needing to copy it.
+               
+               //
+               // Before we start the update process, verify the payload is valid
+               //
+               ctx.setInput(theUpdate);
+               docHandler.prepare(Action.UPDATE);
+               
+               //
+               // Get a copy of the permission-role relationships
+               //
+               PermissionRole permRole = getPermissionRole(ctx, csid);
+
+               //
+               // Delete the Permission resource (and related perm-actions, and perm-roles) from storage and remove from current JPA context
+               //
+            Response deletedRes = deletePermission(ctx, csid);
+            if (deletedRes.getStatus() != Response.Status.OK.getStatusCode()) {
+                       throw new DocumentException(String.format("Could not update Permission resource CSID=%s", csid));
+            }
+            
+            //
+            // Merge the "update" payload with the corresponding Permission resource payload
+            //
+            perm = docHandler.merge(perm, theUpdate);
+            
+               //
+               // Recreate the Permission resource (and related perm-actions) using the same CSID and updated Permission object
+               //
+               ctx.setInput(perm);
+               ctx.setProperty(PermissionClient.PERMISSION_UPDATE_CSID, csid);
+               Response res = create(ctx, perm);
+               if (res.getStatus() != Response.Status.CREATED.getStatusCode()) {
+                       throw new DocumentException(String.format("Could not update Permission resource CSID=%s", csid));
+               }
+               
+               //
+               // Recreate the permission-role relationships
+               //
+               if (PermissionRoleUtil.isEmpty(permRole) == false) {
+                       Response permRoleRes = createPermissionRole(ctx, csid, permRole);
+                   if (permRoleRes.getStatus() != Response.Status.CREATED.getStatusCode()) {
+                               throw new DocumentException(String.format("Could not update Permission resource CSID=%s", csid));
+                   }
+               }
+            
+            transactionContext.commitTransaction();
+            result = perm;
+        } catch (Exception e) {
+               transactionContext.markForRollback();
+            throw bigReThrow(e, ServiceMessages.UPDATE_FAILED, csid);
+        } finally {
+               if (result == null) {
+                       //
+                       // 
+                       //
+               }
+               ctx.closeConnection();
+        }
+        
+        return result;
     }
 
-    @SuppressWarnings("unchecked")
+    /**
+     * Return true if the permission is immutable.
+     * 
+     * @param original
+     * @return
+     */
+       private boolean isImmutable(Permission original) {
+               boolean result = false;
+
+               if ((!Tools.isEmpty(original.getMetadataProtection()) && original.getMetadataProtection().equals(PermissionClient.IMMUTABLE))
+                               || (!Tools.isEmpty(original.getActionsProtection())     && original.getActionsProtection().equals(PermissionClient.IMMUTABLE))) {
+                       result = true;
+               }
+
+               return result;
+       }
+
+       private Permission copyForUpdate(Permission theOriginal) throws DocumentException {
+               Permission result = null;
+               
+               if (theOriginal != null) {
+                       result = new Permission();
+                       result.setAttributeName(theOriginal.getAttributeName());
+                       result.setDescription(theOriginal.getDescription());
+                       result.setEffect(theOriginal.getEffect());
+                       result.setResourceName(theOriginal.getResourceName());
+                       result.setTenantId(theOriginal.getTenantId());
+                       result.setActionGroup(theOriginal.getActionGroup());
+                       
+                       for (PermissionAction permissionAction : theOriginal.getAction()) {
+                               result.getAction().add(copyForUpdate(permissionAction));
+                       }
+               }
+               
+               return result;
+       }
+
+       private PermissionAction copyForUpdate(PermissionAction permissionAction) {
+               PermissionAction result = new PermissionAction();
+               
+               result.setName(permissionAction.getName());
+               result.setObjectIdentity(permissionAction.getObjectIdentity());
+               result.setObjectIdentityResource(permissionAction.getObjectIdentityResource());
+               
+               return result;
+       }
+
+       /**
+     * Deletes the Permission resource and its relationship(s) with any role(s).  Does not delete the actual low-level permission-action tuples.
+     * See https://issues.collectionspace.org/browse/DRYD-223
+     * 
+     * @param csid
+     * @return
+     * @throws Exception
+     */
        @DELETE
     @Path("{csid}")
-    synchronized public Response deletePermission(@PathParam("csid") String csid) throws Exception {
+    public Response deletePermission(@PathParam("csid") String csid) throws Exception {
         logger.debug("deletePermission with csid=" + csid);
         ensureCSID(csid, ServiceMessages.DELETE_FAILED + "permission ");
 
                ServiceContext<Permission, Permission> ctx = createServiceContext((Permission) null, Permission.class);
+               return deletePermission(ctx, csid);
+    }
+
+    synchronized public Response deletePermission(ServiceContext<Permission, Permission> ctx, String csid) throws Exception {
+        DocumentHandler docHandler = createDocumentHandler(ctx);
+
         TransactionContext transactionContext = ctx.openConnection();
         try {
                transactionContext.beginTransaction();
                //
                // First, delete the relationships between the Permission resource and any Role resources.
                //
-            PermissionRoleSubResource subResource =
-                    new PermissionRoleSubResource(PermissionRoleSubResource.PERMISSION_PERMROLE_SERVICE);
-            subResource.deletePermissionRole(ctx, csid, SubjectType.ROLE);
-            //
-            // Next, delete the low-level (Spring Security) permissions.
-            //
-            // NOTE: For deletePermission() in the authz provider at the PermissionRoleSubResource/DocHandler level, there is no visibility
-            // if permission is deleted, so do it here.
-            //
-            // WARNING: This operation deletes the Spring ACL (not the ACEs). It's possible the ACL might be needed for other ACEs roles...
-            //
-            AuthorizationDelegate.deletePermissions((JPATransactionContext)transactionContext, csid); // Deletes the low-level (Spring Security) permissions
+               try {
+                   PermissionRoleSubResource subResource =
+                           new PermissionRoleSubResource(PermissionRoleSubResource.PERMISSION_PERMROLE_SERVICE);
+                   subResource.deletePermissionRole(ctx, csid, SubjectType.ROLE);
+               } catch (DocumentNotFoundException dnf) {
+                       // ignore, just means we didn't find any relationships to delete
+               }
             //
             // Lastly, delete the Permission resource itself and commit the transaction
             //
-            getStorageClient(ctx).delete(ctx, csid);
+            getStorageClient(ctx).delete(ctx, csid, docHandler);
             transactionContext.commitTransaction();
         } catch (Exception e) {
                transactionContext.markForRollback();
@@ -180,31 +336,40 @@ public class PermissionResource extends SecurityResourceBase {
         
         return Response.status(HttpResponseCodes.SC_OK).build();
     }
-
+    
        @POST
     @Path("{csid}/permroles")
-    public Response createPermissionRole(@QueryParam("_method") String method,
+    public Response createPermissionRole(
+               @QueryParam("_method") String method,
             @PathParam("csid") String permCsid,
-            PermissionRole input) {
-                if (method != null) {
-            if ("delete".equalsIgnoreCase(method)) {
-                return deletePermissionRole(permCsid, input);
-            }
-        }
-        logger.debug("createPermissionRole with permCsid=" + permCsid);
-        ensureCSID(permCsid, ServiceMessages.POST_FAILED + "permroles permission ");
-        try {
-            PermissionRoleSubResource subResource =
-                    new PermissionRoleSubResource(PermissionRoleSubResource.PERMISSION_PERMROLE_SERVICE);
-            String permrolecsid = subResource.createPermissionRole((ServiceContext<Permission, Permission>)null, input, SubjectType.ROLE);
-            UriBuilder path = UriBuilder.fromResource(PermissionResource.class);
-            path.path(permCsid + "/permroles/" + permrolecsid);
-            Response response = Response.created(path.build()).build();
-            return response;
-        } catch (Exception e) {
-            throw bigReThrow(e, ServiceMessages.POST_FAILED, permCsid);
-        }
-    }
+                       PermissionRole input) {
+               if (method != null) {
+                       if ("delete".equalsIgnoreCase(method)) { // FIXME: How could 'method' ever equal "delete"
+                               return deletePermissionRole(permCsid, input);
+                       }
+               }
+               logger.debug("createPermissionRole with permCsid=" + permCsid);
+               ensureCSID(permCsid, ServiceMessages.POST_FAILED + "permroles permission ");
+                               
+               return createPermissionRole((ServiceContext<Permission, Permission>)null, permCsid, input);
+       }
+       
+    protected Response createPermissionRole(
+               ServiceContext<Permission, Permission> ctx,
+               String permCsid,
+                       PermissionRole input) {                         
+               try {
+                       PermissionRoleSubResource subResource = new PermissionRoleSubResource(
+                                       PermissionRoleSubResource.PERMISSION_PERMROLE_SERVICE);
+                       String permrolecsid = subResource.createPermissionRole(ctx, input, SubjectType.ROLE);
+                       UriBuilder path = UriBuilder.fromResource(PermissionResource.class);
+                       path.path(permCsid + "/permroles/" + permrolecsid);
+                       Response response = Response.created(path.build()).build();
+                       return response;
+               } catch (Exception e) {
+                       throw bigReThrow(e, ServiceMessages.POST_FAILED, permCsid);
+               }
+       }       
 
        @GET
     @Path("{csid}/permroles/{id}")
@@ -228,24 +393,37 @@ public class PermissionResource extends SecurityResourceBase {
 
        @GET
     @Path("{csid}/permroles")
-    public PermissionRole getPermissionRole(
-            @PathParam("csid") String permCsid) {
+    public PermissionRole getPermissionRole(@PathParam("csid") String permCsid) {
         logger.debug("getPermissionRole with permCsid=" + permCsid);
         ensureCSID(permCsid, ServiceMessages.GET_FAILED + "permroles permission ");
+
+        PermissionRole result = getPermissionRole((ServiceContext<Permission, Permission>)null, permCsid);
+        
+        if (PermissionRoleUtil.isEmpty(result)) {
+               String msg = String.format("Could not find any permission-role relationships for Permission resource CSID=%s", permCsid);
+            Response response = Response.status(Response.Status.NOT_FOUND).entity(msg).type("text/plain").build();
+            throw new CSWebApplicationException(response);
+        }
+        
+        return result;
+    }
+       
+    private PermissionRole getPermissionRole(ServiceContext<Permission, Permission> ctx, String permCsid) {
+        ensureCSID(permCsid, ServiceMessages.GET_FAILED + "permroles permission ");
         PermissionRole result = null;
+
         try {
             PermissionRoleSubResource subResource =
                     new PermissionRoleSubResource(PermissionRoleSubResource.PERMISSION_PERMROLE_SERVICE);
-            //get relationships for a permission
-            result = subResource.getPermissionRole((ServiceContext<Permission, Permission>)null, permCsid, SubjectType.ROLE);
+            result = subResource.getPermissionRole(ctx, permCsid, SubjectType.ROLE);
         } catch (Exception e) {
             throw bigReThrow(e, ServiceMessages.GET_FAILED, permCsid);
         }
-        checkResult(result, permCsid, ServiceMessages.GET_FAILED);
+        
         return result;
-    }
-
-    public Response deletePermissionRole(String permCsid, PermissionRole input) {
+    }  
+       
+    private Response deletePermissionRole(String permCsid, PermissionRole input) {
         logger.debug("Delete payload of permrole relationships with permission permCsid=" + permCsid);
         ensureCSID(permCsid, ServiceMessages.DELETE_FAILED + "permroles permission ");
         try {
index cc38e6a0188cb0acc0a2edfb455828e065364b17..887962d5880dfcf6c8692731bbff634365dc1d4f 100644 (file)
@@ -30,6 +30,7 @@ import org.collectionspace.services.authorization.SubjectType;
 import org.collectionspace.services.authorization.perms.Permission;
 import org.collectionspace.services.authorization.storage.PermissionRoleDocumentHandler;
 import org.collectionspace.services.common.AbstractCollectionSpaceResourceImpl;
+import org.collectionspace.services.common.authorization_mgt.PermissionRoleUtil;
 import org.collectionspace.services.common.context.RemoteServiceContextFactory;
 import org.collectionspace.services.common.context.ServiceContext;
 import org.collectionspace.services.common.context.ServiceContextFactory;
@@ -51,6 +52,8 @@ import org.slf4j.LoggerFactory;
 @SuppressWarnings("rawtypes")
 public class PermissionRoleSubResource
         extends AbstractCollectionSpaceResourceImpl<PermissionRole, PermissionRole> {
+    /** The logger. */
+    final Logger logger = LoggerFactory.getLogger(PermissionRoleSubResource.class);
 
     public final static String ROLE_PERMROLE_SERVICE = "authorization/roles/permroles";
     public final static String PERMISSION_PERMROLE_SERVICE = "authorization/permissions/permroles";
@@ -58,8 +61,6 @@ public class PermissionRoleSubResource
     //service name to identify binding
     /** The service name. */
     private String serviceName = "authorization/permroles";
-    /** The logger. */
-    final Logger logger = LoggerFactory.getLogger(PermissionRoleSubResource.class);
     /** The storage client. */
     final StorageClient storageClient = new JpaRelationshipStorageClient<PermissionRole>();
     /**
@@ -247,15 +248,32 @@ public class PermissionRoleSubResource
         if (logger.isDebugEnabled()) {
             logger.debug("deletePermissionRole with csid=" + csid);
         }
+        
         PermissionRole permRole = getPermissionRole(parentCtx, csid, subject);
-        if (permRole != null) {
+        if (PermissionRoleUtil.isEmpty(permRole) == false) {
                deletePermissionRole(parentCtx, csid, subject, permRole);
         } else {
-               String msg = String.format("The permission CSID=%s is missing or not related to any roles.", csid);
+               String msg = String.format("The %s CSID=%s is missing or not related to any objects.",
+                               getInverse(subject).toString().toLowerCase(), csid);
                throw new DocumentNotFoundException(msg);
         }
     }
-
+    
+    /*
+     * Returns the inverse of the subject (the object)
+     */
+    private SubjectType getInverse(SubjectType subject) {
+       SubjectType result;
+       
+       if (subject.equals(SubjectType.PERMISSION)) {
+               result = SubjectType.ROLE;
+       } else {
+               result = SubjectType.PERMISSION;
+       }
+       
+       return result;
+    }
+    
     /**
      * deletePermissionRole deletes permission-role relationships using given
      * csid of object (permission/role) and subject (role/permission)
index ddc9c5584d5d6ec97c611eeffc9d6b4dd4ba4285..812ddb488b67c5955be5b1049703d2fda0858505 100644 (file)
@@ -27,6 +27,7 @@ import java.util.ArrayList;
 import java.util.List;
 
 import org.collectionspace.authentication.AuthN;
+
 import org.collectionspace.services.authorization.perms.ActionType;
 import org.collectionspace.services.authorization.AuthZ;
 import org.collectionspace.services.authorization.CSpaceAction;
@@ -34,18 +35,20 @@ import org.collectionspace.services.authorization.CSpaceResource;
 import org.collectionspace.services.authorization.perms.EffectType;
 import org.collectionspace.services.authorization.perms.Permission;
 import org.collectionspace.services.authorization.perms.PermissionAction;
-import org.collectionspace.services.authorization.PermissionException;
 import org.collectionspace.services.authorization.PermissionRole;
 import org.collectionspace.services.authorization.PermissionValue;
 import org.collectionspace.services.authorization.Role;
 import org.collectionspace.services.authorization.RoleValue;
 import org.collectionspace.services.authorization.SubjectType;
 import org.collectionspace.services.authorization.URIResourceImpl;
+
 import org.collectionspace.services.common.authorization_mgt.PermissionRoleUtil;
 import org.collectionspace.services.common.context.ServiceContext;
+import org.collectionspace.services.common.document.DocumentException;
 import org.collectionspace.services.common.document.DocumentNotFoundException;
 import org.collectionspace.services.common.storage.jpa.JPATransactionContext;
 import org.collectionspace.services.common.storage.jpa.JpaStorageUtils;
+
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -60,13 +63,14 @@ public class AuthorizationDelegate {
     private static final Logger logger = LoggerFactory.getLogger(AuthorizationDelegate.class);
 
     /**
-     * addPermissions add permissions represented given PermissionRole
+     * Add low-level Spring permissions represented by the given PermissionRole instance
+     * 
      * @param ctx
      * @param pr permission role
      * @throws Exception
      * @see PermissionRole
      */
-    public static void addRelationships(ServiceContext ctx, PermissionRole pr) throws Exception {
+    public static void addRelationships(ServiceContext<?, ?> ctx, PermissionRole pr) throws Exception {
        JPATransactionContext jpaTransactionContext = (JPATransactionContext) ctx.getCurrentTransactionContext();
        
         SubjectType subject = PermissionRoleUtil.getRelationSubject(ctx, pr);
@@ -83,6 +87,7 @@ public class AuthorizationDelegate {
             String[] roles = getRoles(jpaTransactionContext, pr.getRole());
             boolean grant = permission.getEffect().equals(EffectType.PERMIT) ? true : false;
             authz.addPermissions(resources, roles, grant);
+               jpaTransactionContext.setAclTablesUpdateFlag(true); // Tell the containing JPA transaction that we've committed changes to the Spring Tables
         } else if (SubjectType.PERMISSION.equals(subject)) {
             RoleValue rv = pr.getRole().get(0);
             Role role = getRole(jpaTransactionContext, rv.getRoleId());
@@ -97,14 +102,13 @@ public class AuthorizationDelegate {
             for (PermissionValue pv : pr.getPermission()) {
                 Permission p = getPermission(jpaTransactionContext, pv.getPermissionId());
                 if (p == null) {
-                    String msg = "addPermissions: No permission resource found for csid=" + pv.getPermissionId();
-                    logger.error(msg);
-                    //TODO: would be nice contiue to still send 400 back
-                    continue;
+                    String msg = "addRelationships: No permission resource found for csid=" + pv.getPermissionId();
+                    throw new DocumentException(msg);
                 }
                 CSpaceResource[] resources = getResources(p);
                 boolean grant = p.getEffect().equals(EffectType.PERMIT) ? true : false;
                 authz.addPermissions(resources, roles, grant);
+               jpaTransactionContext.setAclTablesUpdateFlag(true); // Tell the containing JPA transaction that we've committed changes to the Spring Tables
             }
         }
     }
@@ -112,53 +116,57 @@ public class AuthorizationDelegate {
     /**
      * deletePermissions delete all permissions associated with given permission role
      * @param ctx
-     * @param pr permissionrole
+     * @param permRole permissionrole
      * @throws Exception
      */
-    public static void deletePermissionsFromRoles(ServiceContext ctx, PermissionRole pr)
+    public static void deletePermissionsFromRoles(ServiceContext<?, ?> ctx, PermissionRole permRole)
             throws Exception {
        JPATransactionContext jpaTransactionContext = (JPATransactionContext) ctx.getCurrentTransactionContext();
 
-        SubjectType subject = PermissionRoleUtil.getRelationSubject(ctx, pr);
+        SubjectType subject = PermissionRoleUtil.getRelationSubject(ctx, permRole);
         AuthZ authz = AuthZ.get();
         if (subject.equals(SubjectType.ROLE)) {
-               List<PermissionValue> permissionValues = pr.getPermission();
-               if (permissionValues != null & permissionValues.size() > 0) {
-                   PermissionValue pv = permissionValues.get(0);
-                   Permission p = getPermission(jpaTransactionContext, pv.getPermissionId());
+               List<PermissionValue> permissionValues = permRole.getPermission();
+               if (permissionValues != null && permissionValues.size() == 1) {
+                   PermissionValue permValue = permissionValues.get(0);
+                   Permission p = getPermission(jpaTransactionContext, permValue.getPermissionId());
                    if (p == null) {
-                       String msg = "deletePermissions: No permission found for id=" + pv.getPermissionId();
+                       String msg = "deletePermissions: No permission found for id=" + permValue.getPermissionId();
                        logger.error(msg);
                        throw new DocumentNotFoundException(msg);
                    }
                    CSpaceResource[] resources = getResources(p);
-                   String[] roles = getRoles(jpaTransactionContext, pr.getRole());
+                   String[] roles = getRoles(jpaTransactionContext, permRole.getRole());
                 authz.deletePermissionsFromRoles(resources, roles);
+               jpaTransactionContext.setAclTablesUpdateFlag(true); // Tell the containing JPA transaction that we've committed changes to the Spring Tables
+               } else {
+                       throw new DocumentException("When the subject of a permrole is ROLE, there should be only ONE permission specified.");
                }
         } else if (SubjectType.PERMISSION.equals(subject)) {
-               List<RoleValue> roleValues = pr.getRole();
-               if (roleValues != null && roleValues.size() > 0) {
-                   RoleValue rv = roleValues.get(0);
-                   Role r = getRole(jpaTransactionContext, rv.getRoleId());
-                   if (r == null) {
-                       String msg = "deletePermissions: No role found for id=" + rv.getRoleId();
+               List<RoleValue> roleValues = permRole.getRole();
+               if (roleValues != null && roleValues.size() == 1) {
+                   RoleValue roleValue = roleValues.get(0);
+                   Role role = getRole(jpaTransactionContext, roleValue.getRoleId());
+                   if (role == null) {
+                       String msg = "deletePermissions: No role found for id=" + roleValue.getRoleId();
                        logger.error(msg);
                        throw new DocumentNotFoundException(msg);
                    }
-                   //using r not rv ensures we're getting the "ROLE" prefix/qualified name
+                   // Using role not roleValue ensures we're getting the "ROLE" prefix/qualified name
                    // This needs to use the qualified name, not the display name
-                   String[] roles = {r.getRoleName()}; 
-                   for (PermissionValue pv : pr.getPermission()) {
-                       Permission p = getPermission(jpaTransactionContext, pv.getPermissionId());
-                       if (p == null) {
+                   String[] roles = {role.getRoleName()}; 
+                   for (PermissionValue pv : permRole.getPermission()) {
+                       Permission perm = getPermission(jpaTransactionContext, pv.getPermissionId());
+                       if (perm == null) {
                            String msg = "deletePermissions: No permission found for id=" + pv.getPermissionId();
-                           logger.error(msg);
-                           //TODO: would be nice contiue to still send 400 back
-                           continue;
+                           throw new DocumentException(msg);
                        }
-                       CSpaceResource[] resources = getResources(p);
+                       CSpaceResource[] resources = getResources(perm);
                     authz.deletePermissionsFromRoles(resources, roles);
+                       jpaTransactionContext.setAclTablesUpdateFlag(true); // Tell the containing JPA transaction that we've committed changes to the Spring Tables                    
                    }
+               } else {
+                       throw new DocumentException("When the subject of a permrole is PERMISSION, there should be only ONE role specified.");
                }
         }
     }
@@ -182,6 +190,7 @@ public class AuthorizationDelegate {
 
         CSpaceResource[] resources = getResources(p);
         AuthZ.get().deletePermissions(resources);
+        jpaTransactionContext.setAclTablesUpdateFlag(true); // Tell the containing JPA transaction that we've committed changes to the Spring Tables
     }
 
     /**
index d618ea45389763ada0a4bb89fa5e26f45dd4cbdd..28e43efe22a8548e972241b25ff2c4d55f2b13c9 100644 (file)
@@ -39,7 +39,7 @@ import org.collectionspace.services.authorization.perms.Permission;
 import org.collectionspace.services.authorization.perms.PermissionAction;
 import org.collectionspace.services.authorization.perms.PermissionsList;
 import org.collectionspace.services.authorization.URIResourceImpl;
-
+import org.collectionspace.services.common.api.Tools;
 import org.collectionspace.services.common.context.ServiceContext;
 import org.collectionspace.services.common.document.BadRequestException;
 import org.collectionspace.services.common.document.DocumentException;
@@ -63,7 +63,7 @@ import org.slf4j.LoggerFactory;
 public class PermissionDocumentHandler
                extends JpaDocumentHandler<Permission, PermissionsList, Permission, List<Permission>> {
 
-    private final Logger logger = LoggerFactory.getLogger(PermissionDocumentHandler.class);
+       private final Logger logger = LoggerFactory.getLogger(PermissionDocumentHandler.class);
     private Permission permission;
     private PermissionsList permissionsList;
     
@@ -131,7 +131,6 @@ public class PermissionDocumentHandler
             URIResourceImpl uriRes = new URIResourceImpl(perm.getTenantId(), perm.getResourceName(), action);
             permAction.setObjectIdentity(uriRes.getHashedId().toString());
             permAction.setObjectIdentityResource(uriRes.getId());
-            //PermissionActionUtil.update(perm, permAction);
         }
     }
     
@@ -165,11 +164,18 @@ public class PermissionDocumentHandler
        //
        // First check to see if an equivalent permission exists
        //
+       ServiceContext<Permission, Permission> ctx = getServiceContext();
        Permission permission = wrapDoc.getWrappedObject();     
        Permission existingPermission = findExistingPermission(permission);
 
        if (existingPermission == null) {
-               String id = UUID.randomUUID().toString();        
+               //
+               // If our call originates from an UPDATE/PUT request, then we can find a CSID in the service context
+               //
+               String id = (String)ctx.getProperty(PermissionClient.PERMISSION_UPDATE_CSID);
+               if (Tools.isEmpty(id) == true) {
+                       id = UUID.randomUUID().toString();
+               }
                permission.setCsid(id);
                setTenant(permission);
                handlePermissionActions(permission);
@@ -185,59 +191,63 @@ public class PermissionDocumentHandler
     public void completeCreate(DocumentWrapper<Permission> wrapDoc) throws Exception {
     }
 
+    /**
+     * Not used.  Due to an issue with the JPA 1.0 update mechanism, we had to perform the update process
+     * in the PermissionResource class.  Look there for more details. 
+     */
+    @Deprecated
     @Override
     public void handleUpdate(DocumentWrapper<Permission> wrapDoc) throws Exception {
-        Permission permissionFound = wrapDoc.getWrappedObject();
-        Permission permissionReceived = getCommonPart();
-        merge(permissionReceived, permissionFound);
     }
 
-    /**
-     * merge manually merges the from from to the to permission
-     * -this method is created due to inefficiency of JPA EM merge
-     * @param from
-     * @param to
-     * @return merged permission
+    /*
+     * Merge two Permission resources for an update/put request.
      */
-    private Permission merge(Permission from, Permission to) throws Exception {
-        if (!(from.getResourceName().equalsIgnoreCase(to.getResourceName()))) {
-            String msg = "Resource name cannot be changed " + to.getResourceName();
-            logger.error(msg);
-            throw new BadRequestException(msg);
+    public Permission merge(Permission perm, Permission theUpdate) throws DocumentException {
+       Permission result = perm;
+       
+        if (!Tools.isEmpty(theUpdate.getResourceName()) && !theUpdate.getResourceName().equalsIgnoreCase(perm.getResourceName())) {
+               String msg = String.format("Failed attempt to change Permission's (CSID='%S') resource name from '%s' to '%s'.",
+                               perm.getCsid(), perm.getResourceName(), theUpdate.getResourceName());
+            throw new DocumentException(msg);
         }
-        //resource name, attribute  cannot be changed
 
-        if (from.getDescription() != null) {
-            to.setDescription(from.getDescription());
+        if (theUpdate.getDescription() != null) {
+            perm.setDescription(theUpdate.getDescription());
         }
-        if (from.getEffect() != null) {
-            to.setEffect(from.getEffect());
+        if (theUpdate.getEffect() != null) {
+            perm.setEffect(theUpdate.getEffect());
         }
-        List<PermissionAction> fromActions = from.getAction();
-        if (!fromActions.isEmpty()) {
-            // Override the whole list, no reconciliation by design
-            to.setAction(fromActions);
-            // Update the actionGroup field to reflect the new action list
-            to.setActionGroup(PermissionClient.getActionGroup(fromActions));
+        //
+        // Override the whole perm-action list, no reconciliation by design. We've
+        // already cleaned-up and removed all the old perm-role relationships
+        //
+        // If the update didn't provide any new perm-actions, then we leave the
+        // existing ones alone.
+        //
+        if (Tools.isEmpty(theUpdate.getAction()) == false) {
+               perm.setAction(theUpdate.getAction());
+               perm.setActionGroup(PermissionClient.getActionGroup(theUpdate.getAction()));
         }
-
+        
         if (logger.isDebugEnabled()) {
-            logger.debug("merged permission=" + JaxbUtils.toString(to, Permission.class));
+            logger.debug("merged permission=" + JaxbUtils.toString(perm, Permission.class));
         }
-
-        handlePermissionActions(to);
-        return to;
+        
+        return result;
     }
-
+    
+    /**
+     * Because of issues with JPA 1.0 not being able to propagate updates from the 'permissions' table to the related 'permissions_actions'
+     * table, we need to handle updates in the PermissionResource class by deleting and creating the Permission resource
+     */
     @SuppressWarnings("unchecked")
        @Override
+       @Deprecated
     public void completeUpdate(DocumentWrapper<Permission> wrapDoc) throws Exception {
-        Permission upAcc = wrapDoc.getWrappedObject();
-        getServiceContext().setOutput(upAcc);
-        sanitize(upAcc);
-        //FIXME update lower-layer authorization (acls)
-        //will require deleting old permissions for this resource and adding
-        //new based on new actions and effect
+        Permission updatedPerm = wrapDoc.getWrappedObject();
+        getServiceContext().setOutput(updatedPerm);
+        sanitize(updatedPerm);
     }
 
     @SuppressWarnings("unchecked")
index a3614ef1bdf2663869fe56f3e35898bcafefc088..0e6ea03d9308592ac88fdc685268acf8f9dd2cd8 100644 (file)
@@ -38,8 +38,11 @@ import org.collectionspace.services.authorization.SubjectType;
 import org.collectionspace.services.common.authorization_mgt.AuthorizationRoleRel;
 import org.collectionspace.services.common.authorization_mgt.PermissionRoleUtil;
 import org.collectionspace.services.common.context.ServiceContext;
+import org.collectionspace.services.common.document.DocumentException;
 import org.collectionspace.services.common.document.DocumentFilter;
+import org.collectionspace.services.common.document.DocumentNotFoundException;
 import org.collectionspace.services.common.document.DocumentWrapper;
+import org.collectionspace.services.common.document.JaxbUtils;
 import org.collectionspace.services.common.document.TransactionException;
 import org.collectionspace.services.common.storage.jpa.JPATransactionContext;
 import org.collectionspace.services.common.storage.jpa.JpaDocumentFilter;
@@ -204,50 +207,64 @@ public class PermissionRoleDocumentHandler
         AuthorizationDelegate.deletePermissionsFromRoles(getServiceContext(), pr);
     }
 
-    /* (non-Javadoc)
+    /*
+     * Turns a list of permission-role rows from the database into a PermissionRole object.  The list of rows
+     * was the result of a query where the subject was either a Role or a Permission.
+     * 
+     * (non-Javadoc)
      * @see org.collectionspace.services.common.document.AbstractDocumentHandlerImpl#extractCommonPart(org.collectionspace.services.common.document.DocumentWrapper)
      */
     @Override
-    public PermissionRole extractCommonPart(
-            DocumentWrapper<List<PermissionRoleRel>> wrapDoc)
+    public PermissionRole extractCommonPart(DocumentWrapper<List<PermissionRoleRel>> wrapDoc)
             throws Exception {
-        List<PermissionRoleRel> prrl = wrapDoc.getWrappedObject();
-        PermissionRole pr = new PermissionRole();
-        SubjectType subject = PermissionRoleUtil.getRelationSubject(getServiceContext());
-        if (prrl.size() == 0) {
-            return pr;
+        PermissionRole result = new PermissionRole();
+
+        List<PermissionRoleRel> permissionRoleRel = wrapDoc.getWrappedObject();
+        if (permissionRoleRel.size() == 0) {
+            return result;
         }
-        PermissionRoleRel prr0 = prrl.get(0);
+        
+        SubjectType subject = PermissionRoleUtil.getRelationSubject(getServiceContext());
+        result.setSubject(subject);
+        
+        PermissionRoleRel prr0 = permissionRoleRel.get(0);
         if (SubjectType.ROLE.equals(subject)) {
-
-            List<PermissionValue> pvs = new ArrayList<PermissionValue>();
-            pr.setPermission(pvs);
+               //
+               // Since ROLE is the subject, they'll be just one Permission
+               //
+            List<PermissionValue> permissionValueList = new ArrayList<PermissionValue>();
+            result.setPermission(permissionValueList);
             PermissionValue pv = AuthorizationRoleRel.buildPermissionValue(prr0);
-            pvs.add(pv);
-
-            //add roles
-            List<RoleValue> rvs = new ArrayList<RoleValue>();
-            pr.setRole(rvs);
-            for (PermissionRoleRel prr : prrl) {
+            permissionValueList.add(pv);
+            //
+            // Add role values
+            //
+            List<RoleValue> roleValueList = new ArrayList<RoleValue>();
+            result.setRole(roleValueList);
+            for (PermissionRoleRel prr : permissionRoleRel) {
                 RoleValue rv = AuthorizationRoleRel.buildRoleValue(prr);
-                rvs.add(rv);
+                roleValueList.add(rv);
             }
         } else if (SubjectType.PERMISSION.equals(subject)) {
-
-            List<RoleValue> rvs = new ArrayList<RoleValue>();
-            pr.setRole(rvs);
+               //
+               // Since PERMISSION is the subject, they'll be just one Role and one or more Permissions
+               //
+            List<RoleValue> roleValueList = new ArrayList<RoleValue>();
+            result.setRole(roleValueList);
             RoleValue rv = AuthorizationRoleRel.buildRoleValue(prr0);
-            rvs.add(rv);
-
-            //add permssions
-            List<PermissionValue> pvs = new ArrayList<PermissionValue>();
-            pr.setPermission(pvs);
-            for (PermissionRoleRel prr : prrl) {
+            roleValueList.add(rv);
+            //
+            // Add permssions values
+            //
+            List<PermissionValue> permissionValueList = new ArrayList<PermissionValue>();
+            result.setPermission(permissionValueList);
+            for (PermissionRoleRel prr : permissionRoleRel) {
                 PermissionValue pv = AuthorizationRoleRel.buildPermissionValue(prr);
-                pvs.add(pv);
+                permissionValueList.add(pv);
             }
         }
-        return pr;
+        
+        return result;
     }
 
     /**
@@ -274,7 +291,13 @@ public class PermissionRoleDocumentHandler
         
         ServiceContext ctx = this.getServiceContext();
         String tenantId = ctx.getTenantId();
-        PermissionRoleUtil.buildPermissionRoleRel(ctx, pr, subject, prrl, handleDelete, tenantId);
+        try {
+               PermissionRoleUtil.buildPermissionRoleRel(ctx, pr, subject, prrl, handleDelete, tenantId);
+        } catch (DocumentNotFoundException dnf) {
+               String msg = String.format("The following perm-role payload references permissions and/or roles that do not exist: \n%s",
+                               JaxbUtils.toString(pr, PermissionRole.class));
+               throw new DocumentException(msg);
+        }
     }
     
     /* (non-Javadoc)
index 0924ff63daa1de2b4b75f97008c787fb5c62c12e..dd722d2b51f68504148381ba0efdbedc97022d27 100644 (file)
@@ -34,6 +34,9 @@ public class PermissionStorageConstants {
     final public static String Q_RESOURCE_NAME = "res";
     final public static String Q_ACTION_GROUP = "actGrp";
 
+    
+    final public static String ID = "csid";
+    final public static String TENANT_ID = "tenant_id";
     final public static String RESOURCE_NAME = "resourceName";
     final public static String ACTION_GROUP = "actionGroup";
        public static final String PERMREL_ROLE_ID = "roleId";
index ffc216f1c5d4d1273b2146781ef4e94269fe431e..8d428063d4ab54692780bbae5dd683fcedec1629 100644 (file)
@@ -25,7 +25,7 @@
 package org.collectionspace.services.authorization.storage;
 
 /**
- * RoleStorageConstants declares query params, etc.
+ * RoleStorageConstants declares JPA query params, etc. See JAX-B file roles.xsd for details
  * @author
  */
 public class RoleStorageConstants {
@@ -33,6 +33,8 @@ public class RoleStorageConstants {
     final public static String Q_ROLE_NAME = "r";
 
     final public static String ROLE_NAME = "roleName";
+    final public static String ROLE_ID = "csid";
+    final public static String ROLE_TENANT_ID = "tenant_id";
 
     final public static String PERM_ROLE_REL_ROLE_ID = "roleId";
     final public static String PERM_ROLE_REL_PERM_ID = "permissionId";
index 05f0c893df22c4cd58693a071201d296eafe0493..df5a9fa786970295e18216eed1e9600ebcd60a6e 100644 (file)
@@ -3,6 +3,7 @@ package org.collectionspace.services.common;
 import org.collectionspace.services.common.context.ServiceContext;
 import org.collectionspace.services.common.document.DocumentFilter;
 import org.collectionspace.services.common.document.DocumentHandler;
+import org.collectionspace.services.common.storage.TransactionContext;
 import org.collectionspace.services.common.storage.jpa.JPATransactionContext;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -12,16 +13,29 @@ import javax.ws.rs.core.Response;
 import javax.ws.rs.core.UriBuilder;
 import javax.ws.rs.core.UriInfo;
 
-@SuppressWarnings({ "unchecked", "rawtypes" })
-public abstract class SecurityResourceBase extends AbstractCollectionSpaceResourceImpl {
+@SuppressWarnings("rawtypes")
+public abstract class SecurityResourceBase<IT, OT> extends AbstractCollectionSpaceResourceImpl<IT, OT> {
 
-    final Logger logger = LoggerFactory.getLogger(SecurityResourceBase.class);
+    @SuppressWarnings("hiding")
+       final Logger logger = LoggerFactory.getLogger(SecurityResourceBase.class);
 
-    public Response create(Object input) {
+    public Response create(IT input) {
+       Response response = null;
+       
+        try {
+            ServiceContext<IT, OT> ctx = createServiceContext(input, input.getClass());
+            response = create(ctx, input);
+        } catch (Exception e) {
+            throw bigReThrow(e, ServiceMessages.POST_FAILED+"create in "+this.getClass().getName());
+        }
+        
+        return response;
+    }
+    
+    protected Response create(ServiceContext<IT, OT> ctx, IT input) {
        Response response = null;
        
         try {
-            ServiceContext ctx = createServiceContext(input, input.getClass());
             DocumentHandler handler = createDocumentHandler(ctx);
             String csid = getStorageClient(ctx).create(ctx, handler);
             UriBuilder path = UriBuilder.fromResource(this.getClass());
@@ -34,11 +48,11 @@ public abstract class SecurityResourceBase extends AbstractCollectionSpaceResour
         return response;
     }
     
-    public Response create(JPATransactionContext jpaTransactionContext, Object input) {
+    public Response create(JPATransactionContext jpaTransactionContext, IT input) {
        Response response = null;
        
         try {
-            ServiceContext ctx = createServiceContext(jpaTransactionContext, input, input.getClass());
+            ServiceContext<IT, OT> ctx = createServiceContext(jpaTransactionContext, input, input.getClass());
             DocumentHandler handler = createDocumentHandler(ctx);
             String csid = getStorageClient(ctx).create(ctx, handler);
             UriBuilder path = UriBuilder.fromResource(this.getClass());
@@ -51,9 +65,9 @@ public abstract class SecurityResourceBase extends AbstractCollectionSpaceResour
         return response;
     }    
 
-    private ServiceContext createServiceContext(JPATransactionContext jpaTransactionContext, Object input,
+    private ServiceContext<IT, OT> createServiceContext(JPATransactionContext jpaTransactionContext, IT input,
                        Class<? extends Object> clazz) throws Exception {
-        ServiceContext result = createServiceContext(input, clazz);
+        ServiceContext<IT, OT> result = createServiceContext(input, clazz);
         
         if (jpaTransactionContext != null) {
                        result.setTransactionContext(jpaTransactionContext);
@@ -71,7 +85,7 @@ public abstract class SecurityResourceBase extends AbstractCollectionSpaceResour
         ensureCSID(csid, ServiceMessages.GET_FAILED + "csid");
         Object result = null;
         try {
-            ServiceContext ctx = createServiceContext((Object) null, objectClass, ui);            
+            ServiceContext<IT, OT> ctx = createServiceContext((IT) null, objectClass, ui);            
             DocumentHandler handler = createDocumentHandler(ctx);
             getStorageClient(ctx).get(ctx, csid, handler);
             result = ctx.getOutput();
@@ -82,12 +96,13 @@ public abstract class SecurityResourceBase extends AbstractCollectionSpaceResour
         return result;
     }
     
-    public Object get(JPATransactionContext jpaTransactionContext, String csid, Class objectClass) {
+    protected Object get(TransactionContext transactionContext, String csid, Class<?> objectClass) {
         logger.debug("get with csid=" + csid);
+        JPATransactionContext jpaTransactionContext = (JPATransactionContext)transactionContext;
         ensureCSID(csid, ServiceMessages.GET_FAILED + "csid");
         Object result = null;
         try {
-            ServiceContext ctx = createServiceContext(jpaTransactionContext, (Object) null, objectClass);   
+            ServiceContext<IT, OT> ctx = createServiceContext(jpaTransactionContext, (IT) null, objectClass);   
             DocumentHandler handler = createDocumentHandler(ctx);
             getStorageClient(ctx).get(ctx, csid, handler);
             result = ctx.getOutput();
@@ -100,7 +115,7 @@ public abstract class SecurityResourceBase extends AbstractCollectionSpaceResour
 
        public Object getList(UriInfo ui, Class objectClass) {
         try {
-            ServiceContext ctx = createServiceContext((Object) null, objectClass, ui);
+            ServiceContext<IT, OT> ctx = createServiceContext((IT) null, objectClass, ui);
             DocumentHandler handler = createDocumentHandler(ctx);
             MultivaluedMap<String, String> queryParams = (ui != null ? ui.getQueryParameters() : null);
             DocumentFilter myFilter = handler.createDocumentFilter();
@@ -116,17 +131,17 @@ public abstract class SecurityResourceBase extends AbstractCollectionSpaceResour
         }
     }
 
-    public Object update(String csid, Object theUpdate, Class<?> objectClass) {
+    public Object update(String csid, IT theUpdate, Class<?> objectClass) {
        return update((UriInfo)null, csid, theUpdate, objectClass);
     }
     
-       public Object update(UriInfo ui, String csid, Object theUpdate, Class objectClass) {
+       public Object update(UriInfo ui, String csid, IT theUpdate, Class objectClass) {
         if (logger.isDebugEnabled()) {
             logger.debug("updateRole with csid=" + csid);
         }
         ensureCSID(csid, ServiceMessages.PUT_FAILED + this.getClass().getName());
         try {
-            ServiceContext ctx = createServiceContext(theUpdate, objectClass, ui);
+            ServiceContext<IT, OT> ctx = createServiceContext(theUpdate, objectClass, ui);
             DocumentHandler handler = createDocumentHandler(ctx);
             getStorageClient(ctx).update(ctx, csid, handler);
             return ctx.getOutput();
@@ -134,4 +149,37 @@ public abstract class SecurityResourceBase extends AbstractCollectionSpaceResour
             throw bigReThrow(e, ServiceMessages.PUT_FAILED, csid);
         }
     }
+       
+       public Object update(ServiceContext<?, ?> parentCtx, UriInfo ui, String csid, IT theUpdate, Class objectClass) {
+        if (logger.isDebugEnabled()) {
+            logger.debug("updateRole with csid=" + csid);
+        }
+        ensureCSID(csid, ServiceMessages.PUT_FAILED + this.getClass().getName());
+        
+        try {
+            ServiceContext<IT, OT> ctx = createServiceContext(parentCtx, theUpdate, objectClass, ui);
+            DocumentHandler handler = createDocumentHandler(ctx);
+            getStorageClient(ctx).update(ctx, csid, handler);
+            return ctx.getOutput();
+        } catch (Exception e) {
+            throw bigReThrow(e, ServiceMessages.PUT_FAILED, csid);
+        }
+    }
+
+    protected ServiceContext<IT, OT> createServiceContext(
+               ServiceContext<?, ?> parentCtx,
+               IT input,
+               Class<?> theClass,
+               UriInfo uriInfo) throws Exception {
+       ServiceContext<IT, OT> ctx = createServiceContext(input, theClass, uriInfo);
+       JPATransactionContext parentTransactionContext = parentCtx != null ? (JPATransactionContext)parentCtx.getCurrentTransactionContext() : null;
+       //
+       // If the parent context has an active JPA connection then we'll use it.
+       //
+       if (parentTransactionContext != null) {
+               ctx.setTransactionContext(parentTransactionContext);
+       }
+       
+       return ctx;
+    }  
 }
index 3ba8a36dc95ce67dcf58e44d8d64637537f4b187..66e3acfd56d39f8cb868e511f4768f4c65d72bfe 100644 (file)
@@ -14,10 +14,7 @@ import java.util.Hashtable;
 import java.util.List;
 import java.util.Map;
 import java.util.UUID;
-
 import javax.naming.NamingException;
-import javax.persistence.EntityManager;
-import javax.persistence.EntityManagerFactory;
 
 import org.collectionspace.authentication.AuthN;
 import org.collectionspace.services.account.AccountListItem;
@@ -38,7 +35,7 @@ import org.collectionspace.services.authorization.perms.ActionType;
 import org.collectionspace.services.authorization.perms.EffectType;
 import org.collectionspace.services.authorization.perms.Permission;
 import org.collectionspace.services.authorization.perms.PermissionAction;
-
+import org.collectionspace.services.client.PermissionClient;
 import org.collectionspace.services.client.Profiler;
 import org.collectionspace.services.client.RoleClient;
 import org.collectionspace.services.client.workflow.WorkflowClient;
@@ -46,6 +43,7 @@ import org.collectionspace.services.client.workflow.WorkflowClient;
 import org.collectionspace.services.common.config.ServiceConfigUtils;
 import org.collectionspace.services.common.config.TenantBindingConfigReaderImpl;
 import org.collectionspace.services.common.context.ServiceBindingUtils;
+import org.collectionspace.services.common.document.DocumentException;
 import org.collectionspace.services.common.document.DocumentHandler;
 import org.collectionspace.services.common.security.SecurityUtils;
 import org.collectionspace.services.common.storage.DatabaseProductType;
@@ -65,7 +63,6 @@ import org.collectionspace.services.lifecycle.TransitionDefList;
 //import org.mortbay.log.Log;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
-import org.springframework.security.acls.model.AlreadyExistsException;
 
 
 public class AuthorizationCommon {
@@ -76,8 +73,6 @@ public class AuthorizationCommon {
        // For token generation and password reset
        //
        final private static String DEFAULT_PASSWORD_RESET_EMAIL_MESSAGE = "Hello {{greeting}},\n\r\n\rYou've started the process to reset your CollectionSpace account password. To finish resetting your password, go to the Reset Password page {{link}} on CollectionSpace.\n\r\n\rIf clicking the link doesn't work, copy and paste the following link into your browser address bar and click Go.\n\r\n\r{{link}}\n\r Thanks,\n\r\n\r CollectionSpace Administrator\n\r\n\rPlease do not reply to this email. This mailbox is not monitored and you will not receive a response. For assistance, contact your CollectionSpace Administrator directly.";
-       final private static String tokensalt = "74102328UserDetailsReset";
-       final private static int TIME_SCALAR = 100000;
        private static final String DEFAULT_PASSWORD_RESET_EMAIL_SUBJECT = "Password reset for CollectionSpace account";
        
        //
@@ -89,14 +84,18 @@ public class AuthorizationCommon {
     // ActionGroup labels/constants
     //
        
-       // for READ-WRITE
+       // for READ-WRITE-DELETE
     final public static String ACTIONGROUP_CRUDL_NAME = "CRUDL";
     final public static ActionType[] ACTIONSET_CRUDL = {ActionType.CREATE, ActionType.READ, ActionType.UPDATE, ActionType.DELETE, ActionType.SEARCH};
+       // for READ-WRITE
+    final public static String ACTIONGROUP_CRUL_NAME = "CRUL";
+    final public static ActionType[] ACTIONSET_CRUL = {ActionType.CREATE, ActionType.READ, ActionType.UPDATE, ActionType.SEARCH};
     // for READ-ONLY
     final public static String ACTIONGROUP_RL_NAME = "RL";
     final public static ActionType[] ACTIONSET_RL = {ActionType.READ, ActionType.SEARCH};
        
        static ActionGroup ACTIONGROUP_CRUDL;
+       static ActionGroup ACTIONGROUP_CRUL;
        static ActionGroup ACTIONGROUP_RL;
        
        // A static block to initialize the predefined action groups
@@ -109,7 +108,10 @@ public class AuthorizationCommon {
                ACTIONGROUP_RL = new ActionGroup();
                ACTIONGROUP_RL.name = ACTIONGROUP_RL_NAME;
                ACTIONGROUP_RL.actions = ACTIONSET_RL;
-
+               // For read-write
+               ACTIONGROUP_CRUL = new ActionGroup();
+               ACTIONGROUP_CRUL.name = ACTIONGROUP_CRUL_NAME;
+               ACTIONGROUP_CRUL.actions = ACTIONSET_CRUL;
        }
        
     final static Logger logger = LoggerFactory.getLogger(AuthorizationCommon.class);
@@ -163,17 +165,7 @@ public class AuthorizationCommon {
        public static String setTenantConfigMD5Hash(String tenantId, String md5hash) {
                return tenantConfigMD5HashTable.put(tenantId, md5hash);
        }       
-       
-       @Deprecated
-    public static Role xgetRole(String tenantId, String displayName) {
-       Role role = null;
-       
-       String roleName = AuthorizationCommon.getQualifiedRoleName(tenantId, displayName);
-       //role = AuthorizationStore.getRoleByName(roleName, tenantId);
-        
-        return role;
-    }
-    
+           
     public static Role getRole(JPATransactionContext jpaTransactionContext, String tenantId, String displayName) {
        Role role = null;
        
@@ -183,11 +175,15 @@ public class AuthorizationCommon {
         return role;
     }
     
-    
-    public static Role createRole(String tenantId, String name, String description) {
-       return createRole(tenantId, name, description, false /* mutable by default */);
-    }
-    
+    /**
+     * Create a new role instance to be persisted later.
+     * 
+     * @param tenantId
+     * @param name
+     * @param description
+     * @param immutable
+     * @return
+     */
     public static Role createRole(String tenantId, String name, String description, boolean immutable) {
        Role role = new Role();
        
@@ -212,7 +208,8 @@ public class AuthorizationCommon {
      * with assumption that resource is of type URI
      * @param permission configuration
      */
-    public static void addPermissionsForUri(Permission perm,
+    public static void addPermissionsForUri(JPATransactionContext jpaTransactionContext,
+               Permission perm,
             PermissionRole permRole) throws PermissionException {
        //
        // First check the integrity of the incoming arguments.
@@ -238,6 +235,7 @@ public class AuthorizationCommon {
             resources.add(uriRes);
         }
         AuthZ.get().addPermissions(resources.toArray(new CSpaceResource[0]), principals.toArray(new String[0]), grant); // CSPACE-4967
+        jpaTransactionContext.setAclTablesUpdateFlag(true); // Tell the containing JPA transaction that we've committed changes to the Spring Tables
     }
     
     private static Connection getConnection(String databaseName) throws NamingException, SQLException {
@@ -267,6 +265,8 @@ public class AuthorizationCommon {
                result = ACTIONGROUP_CRUDL;
        } else if (actionGroupStr.equalsIgnoreCase(ACTIONGROUP_RL_NAME)) {
                result = ACTIONGROUP_RL;
+       } else if (actionGroupStr.equalsIgnoreCase(ACTIONGROUP_CRUL_NAME)) {
+               result = ACTIONGROUP_CRUL;
        }
        
        return result;
@@ -275,11 +275,12 @@ public class AuthorizationCommon {
     public static Permission createPermission(String tenantId,
                String resourceName,
                String description,
-               String actionGroupStr) {
+               String actionGroupStr,
+               boolean immutable) {
        Permission result = null;
        
        ActionGroup actionGroup = getActionGroup(actionGroupStr);
-       result = createPermission(tenantId, resourceName, description, actionGroup);
+       result = createPermission(tenantId, resourceName, description, actionGroup, immutable);
        
        return result;
     }
@@ -287,7 +288,8 @@ public class AuthorizationCommon {
     private static Permission createPermission(String tenantId,
                String resourceName,
                String description,
-               ActionGroup actionGroup) {
+               ActionGroup actionGroup,
+               boolean immutable) {
         String id = tenantId
                        + "-" + resourceName.replace('/', '_') // Remove the slashes so the ID can be used in a URI/URL
                        + "-" + actionGroup.name;
@@ -307,13 +309,19 @@ public class AuthorizationCommon {
                pas.add(permAction);
         }
         
+        if (immutable) {
+               perm.setMetadataProtection(PermissionClient.IMMUTABLE);
+               perm.setActionsProtection(PermissionClient.IMMUTABLE);
+        }
+        
         return perm;
     }
     
     private static Permission createWorkflowPermission(TenantBindingType tenantBinding,
                ServiceBindingType serviceBinding,
                String transitionVerb,
-               ActionGroup actionGroup)
+               ActionGroup actionGroup,
+               boolean immutable)
     {
        Permission result = null;
        String workFlowServiceSuffix;
@@ -332,7 +340,7 @@ public class AuthorizationCommon {
                        + workFlowServiceSuffix
                        + transitionName;
        String description = "A generated workflow permission for actiongroup " + actionGroup.name;
-       result = createPermission(tenantId, resourceName, description, actionGroup);
+       result = createPermission(tenantId, resourceName, description, actionGroup, immutable);
        
        if (logger.isDebugEnabled() == true) {
                logger.debug("Generated a workflow permission: "
@@ -348,15 +356,18 @@ public class AuthorizationCommon {
     private static PermissionRole createPermissionRole(
                Permission permission,
                Role role,
-               boolean enforceTenancy) throws Exception
+               boolean enforceTenancy) throws DocumentException
     {
        PermissionRole permRole = new PermissionRole();
+       
+       //
        // Check to see if the tenant ID of the permission and the tenant ID of the role match
+       //
        boolean tenantIdsMatch = role.getTenantId().equalsIgnoreCase(permission.getTenantId());
        if (tenantIdsMatch == false && enforceTenancy == false) {
                tenantIdsMatch = true; // If we don't need to enforce tenancy then we'll just consider them matched.
        }
-                       
+                               
                if (tenantIdsMatch == true) {
                permRole.setSubject(SubjectType.ROLE);
                //
@@ -382,13 +393,13 @@ public class AuthorizationCommon {
                } else {
                String errMsg = "The tenant ID of the role: " + role.getTenantId()
                                + " did not match the tenant ID of the permission: " + permission.getTenantId();
-               throw new Exception(errMsg);
+               throw new DocumentException(errMsg);
                }
        
        return permRole;
     }
     
-    private static Hashtable<String, String> getTenantNamesFromConfig(TenantBindingConfigReaderImpl tenantBindingConfigReader) {
+       private static Hashtable<String, String> getTenantNamesFromConfig(TenantBindingConfigReaderImpl tenantBindingConfigReader) {
 
        // Note that this only handles tenants not marked as "createDisabled"
        Hashtable<String, TenantBindingType> tenantBindings =
@@ -664,84 +675,92 @@ public class AuthorizationCommon {
        }
     }
     
-    
+    /**
+     * Creates the default Admin and Reader roles for all the configured tenants.
+     * 
+     * Returns the CSID of the Spring Admin role.
+     * 
+     * @param conn
+     * @param tenantInfo
+     * @param tenantAdminRoleCSIDs
+     * @param tenantReaderRoleCSIDs
+     * @return
+     * @throws SQLException
+     * @throws Exception
+     */
     private static String findOrCreateDefaultRoles(Connection conn, Hashtable<String, String> tenantInfo,
                Hashtable<String, String> tenantAdminRoleCSIDs, Hashtable<String, String> tenantReaderRoleCSIDs) 
                        throws SQLException, Exception {
-       // Fifth, fetch and save the default roles
+
                String springAdminRoleCSID = null;
        Statement stmt = null;
        PreparedStatement pstmt = null;
        try {
-               final String querySpringRole = 
-                               "SELECT csid from roles WHERE rolename='"+AuthN.ROLE_SPRING_ADMIN_NAME+"'";
+               //
+               // Look for the Spring Security admin role.  If not found, create it.
+               //
+               final String querySpringRole = String.format("SELECT csid from roles WHERE rolename='%s'", AuthN.ROLE_SPRING_ADMIN_NAME);
                stmt = conn.createStatement();
                ResultSet rs = stmt.executeQuery(querySpringRole);
-               if(rs.next()) {
+               if (rs.next()) {
                        springAdminRoleCSID = rs.getString(1);
                        if (logger.isDebugEnabled()) {
-                               logger.debug("createDefaultAccounts found Spring Admin role: "
-                                               +springAdminRoleCSID);
+                               logger.debug("createDefaultAccounts found Spring Admin role: " + springAdminRoleCSID);
                        }
                } else {
-                       final String insertSpringAdminRoleSQL =
-                                       "INSERT INTO roles (csid, rolename, displayName, rolegroup, created_at, tenant_id) "
-                                                       + "VALUES ('-1', 'ROLE_SPRING_ADMIN', 'SPRING_ADMIN', 'Spring Security Administrator', now(), '0')";
+                       final String insertSpringAdminRoleSQL = String.format(
+                                       "INSERT INTO roles (csid, rolename, displayName, rolegroup, created_at, tenant_id) VALUES ('%s', '%s', '%s', '%s', now(), '%s')",
+                                       AuthN.ROLE_SPRING_ADMIN_ID, AuthN.ROLE_SPRING_ADMIN_NAME, AuthN.SPRING_ADMIN_USER, AuthN.ROLE_SPRING_GROUP_NAME, AuthN.ADMIN_TENANT_ID);
                        stmt.executeUpdate(insertSpringAdminRoleSQL);
-                       springAdminRoleCSID = "-1";
-                       if (logger.isDebugEnabled()) {
-                               logger.debug("createDefaultAccounts CREATED Spring Admin role: "
-                                               +springAdminRoleCSID);
-                       }
+                       springAdminRoleCSID = AuthN.ROLE_SPRING_ADMIN_ID;
                }
                rs.close();
-               final String getRoleCSIDSql =
-                               "SELECT csid from roles WHERE tenant_id=? and rolename=?";
-               pstmt = conn.prepareStatement(getRoleCSIDSql); // create a statement
                rs = null;
-               for(String tId : tenantInfo.keySet()) {
-                       pstmt.setString(1, tId);                                                // set tenant_id param
-                       pstmt.setString(2, getDefaultAdminRole(tId));   // set rolename param
+               
+               //
+               // Look for and save each tenants default Admin and Reader roles
+               //
+               final String getRoleCSIDSql = "SELECT csid from roles WHERE tenant_id=? and rolename=?";
+               pstmt = conn.prepareStatement(getRoleCSIDSql); // create a statement
+               for (String tenantId : tenantInfo.keySet()) {
+                       //
+                       // Look for the default Admin role
+                       //
+                       pstmt.setString(1, tenantId);
+                       pstmt.setString(2, getDefaultAdminRole(tenantId));
                        rs = pstmt.executeQuery();
                        // extract data from the ResultSet
-                       if(!rs.next()) {
-                               throw new RuntimeException("Cannot find role: "+getDefaultAdminRole(tId)
-                                               +" for tenant id: "+tId+" in roles!");
-                       }
-                       String tenantAdminRoleCSID = rs.getString(1);
-                       if (logger.isDebugEnabled()) {
-                               logger.debug("createDefaultAccounts found role: "
-                                               +getDefaultAdminRole(tId)+"("+tenantAdminRoleCSID
-                                               +") for tenant id: "+tId);
+                       if (!rs.next()) {
+                               throw new RuntimeException("Cannot find role: " + getDefaultAdminRole(tenantId)
+                                       + " for tenant id: " + tenantId + " in roles!");
                        }
-                       tenantAdminRoleCSIDs.put(tId, tenantAdminRoleCSID);
-                       pstmt.setString(1, tId);                                                // set tenant_id param
-                       pstmt.setString(2, getDefaultReaderRole(tId));  // set rolename param
+                       String tenantAdminRoleCSID = rs.getString(1); // First column (#1) is the CSID
+                       tenantAdminRoleCSIDs.put(tenantId, tenantAdminRoleCSID);
                        rs.close();
+                       rs = null;
+                       //
+                       // Look for the default Reader role
+                       //
+                       pstmt.setString(1, tenantId);                                           // set tenant_id param
+                       pstmt.setString(2, getDefaultReaderRole(tenantId));     // set rolename param
                        rs = pstmt.executeQuery();
                        // extract data from the ResultSet
-                       if(!rs.next()) {
-                               throw new RuntimeException("Cannot find role: "+getDefaultReaderRole(tId)
-                                               +" for tenant id: "+tId+" in roles!");
+                       if (!rs.next()) {
+                               throw new RuntimeException("Cannot find role: " + getDefaultReaderRole(tenantId)
+                                               + " for tenant id: " + tenantId + " in roles!");
                        }
                        String tenantReaderRoleCSID = rs.getString(1);
-                       if (logger.isDebugEnabled()) {
-                               logger.debug("createDefaultAccounts found role: "
-                                               +getDefaultReaderRole(tId)+"("+tenantReaderRoleCSID
-                                               +") for tenant id: "+tId);
-                       }
-                       tenantReaderRoleCSIDs.put(tId, tenantReaderRoleCSID);
+                       tenantReaderRoleCSIDs.put(tenantId, tenantReaderRoleCSID);
                        rs.close();
                }
                pstmt.close();
        } catch(Exception e) {
                throw e;
        } finally {
-               if(stmt!=null)
-                       stmt.close();
-               if(pstmt!=null)
-                       pstmt.close();
+               if (stmt != null) stmt.close();
+               if (pstmt != null) pstmt.close();
        }
+       
        return springAdminRoleCSID;
     }
 
@@ -786,55 +805,38 @@ public class AuthorizationCommon {
        PreparedStatement pstmt = null;
        try {
                String insertAccountRoleSQL;
-               if (databaseProductType == DatabaseProductType.MYSQL) {
-                       insertAccountRoleSQL = INSERT_ACCOUNT_ROLE_SQL_MYSQL;
-               } else if (databaseProductType == DatabaseProductType.POSTGRESQL) {
+               if (databaseProductType == DatabaseProductType.POSTGRESQL) {
                        insertAccountRoleSQL = INSERT_ACCOUNT_ROLE_SQL_POSTGRES;
                } else {
                        throw new Exception("Unrecognized database system.");
                }
-               if (logger.isDebugEnabled()) {
-                       logger.debug("createDefaultAccounts binding accounts to roles with SQL:\n"
-                                       +insertAccountRoleSQL);
-               }
+               
                pstmt = conn.prepareStatement(insertAccountRoleSQL); // create a statement
-               for(String tId : tenantInfo.keySet()) {
-                       String adminUserId =  getDefaultAdminUserID(tenantInfo.get(tId));
-                       if(!usersInRepo.contains(adminUserId)) {
+               for (String tId : tenantInfo.keySet()) {
+                       String adminUserId = getDefaultAdminUserID(tenantInfo.get(tId));
+                       if (!usersInRepo.contains(adminUserId)) {
                                String adminAcct = tenantAdminAcctCSIDs.get(tId);
                                String adminRoleId = tenantAdminRoleCSIDs.get(tId);
                                pstmt.setString(1, adminAcct);          // set acct CSID param
                                pstmt.setString(2, adminUserId);        // set user_id param
                                pstmt.setString(3, adminRoleId);        // set role_id param
                                pstmt.setString(4, getDefaultAdminRole(tId));   // set rolename param
-                               if (logger.isDebugEnabled()) {
-                                       logger.debug("createDefaultAccounts binding account: "
-                                                       +adminUserId+" to Admin role("+adminRoleId
-                                                       +") for tenant id: "+tId);
-                               }
                                pstmt.executeUpdate();
+                               //
                                // Now add the Spring Admin Role to the admin accounts
+                               //
                                pstmt.setString(3, springAdminRoleCSID);        // set role_id param
                                pstmt.setString(4, AuthN.ROLE_SPRING_ADMIN_NAME);               // set rolename param
-                               if (logger.isDebugEnabled()) {
-                                       logger.debug("createDefaultAccounts binding account: "
-                                                       +adminUserId+" to Spring Admin role: "+springAdminRoleCSID);
-                               }
                                pstmt.executeUpdate();
                        }
                        String readerUserId = getDefaultReaderUserID(tenantInfo.get(tId));
-                       if(!usersInRepo.contains(readerUserId)) {
+                       if (!usersInRepo.contains(readerUserId)) {
                                String readerAcct = tenantReaderAcctCSIDs.get(tId);
                                String readerRoleId = tenantReaderRoleCSIDs.get(tId);
                                pstmt.setString(1, readerAcct);         // set acct CSID param
                                pstmt.setString(2, readerUserId);       // set user_id param
                                pstmt.setString(3, readerRoleId);       // set role_id param
                                pstmt.setString(4, getDefaultReaderRole(tId));  // set rolename param
-                               if (logger.isDebugEnabled()) {
-                                       logger.debug("createDefaultAccounts binding account: "
-                                                       +readerUserId+" to Reader role("+readerRoleId
-                                                       +") for tenant id: "+tId);
-                               }
                                pstmt.executeUpdate();
                        }
                }
@@ -842,8 +844,9 @@ public class AuthorizationCommon {
        } catch(Exception e) {
                throw e;
        } finally {
-               if(pstmt!=null)
+               if (pstmt!=null) {
                        pstmt.close();
+               }
        }
     }
     
@@ -869,20 +872,18 @@ public class AuthorizationCommon {
                pstmt.setString(2, tenantManagerUserID);        // set user_id param
                pstmt.setString(3, tenantManagerRoleID);        // set role_id param
                pstmt.setString(4, tenantManagerRoleName);      // set rolename param
-               if (logger.isDebugEnabled()) {
-                       logger.debug("bindTenantManagerAccountRole binding user: "
-                                       +tenantManagerUserID+" to Admin role("+tenantManagerRoleName+")");
-               }
                pstmt.executeUpdate();
+               
                /* At this point, tenant manager should not need the Spring Admin Role
-               pstmt.setString(3, springAdminRoleCSID);        // set role_id param
-               pstmt.setString(4, SPRING_ADMIN_ROLE);          // set rolename param
-               if (logger.isDebugEnabled()) {
-                       logger.debug("createDefaultAccounts binding account: "
-                                       +adminUserId+" to Spring Admin role: "+springAdminRoleCSID);
-               }
-               pstmt.executeUpdate();
+                       pstmt.setString(3, springAdminRoleCSID);        // set role_id param
+                       pstmt.setString(4, SPRING_ADMIN_ROLE);          // set rolename param
+                       if (logger.isDebugEnabled()) {
+                               logger.debug("createDefaultAccounts binding account: "
+                                               +adminUserId+" to Spring Admin role: "+springAdminRoleCSID);
+                       }
+                       pstmt.executeUpdate();
                */
+               
                pstmt.close();
        } catch(Exception e) {
                throw e;
@@ -914,8 +915,9 @@ public class AuthorizationCommon {
                        throw e;
                } finally {
                        try {
-                               if (conn != null)
+                               if (conn != null) {
                                        conn.close();
+                               }
                        } catch (SQLException sqle) {
                                if (logger.isDebugEnabled()) {
                                        logger.debug("SQL Exception closing statement/connection: " + sqle.getLocalizedMessage());
@@ -971,7 +973,7 @@ public class AuthorizationCommon {
                                tenantAdminAcctCSIDs, tenantReaderAcctCSIDs);
                
                boolean createdTenantMgrAccount = findOrCreateTenantManagerUserAndAccount(conn);
-               if(createdTenantMgrAccount) {
+               if (createdTenantMgrAccount) {
                        // If we created the account, we need to create the bindings. Otherwise, assume they
                        // are all set (from previous initialization).
                        String tenantManagerRoleCSID = findTenantManagerRole(conn);
@@ -984,8 +986,9 @@ public class AuthorizationCommon {
                throw e;
                } finally {
                        try {
-                               if (conn != null)
+                               if (conn != null) {
                                        conn.close();
+                               }
                        } catch (SQLException sqle) {
                                if (logger.isDebugEnabled()) {
                                        logger.debug("SQL Exception closing statement/connection: " + sqle.getLocalizedMessage());
@@ -995,7 +998,7 @@ public class AuthorizationCommon {
     }
     
     private static String getDefaultAdminRole(String tenantId) {
-       return ROLE_PREFIX+tenantId+TENANT_ADMIN_ROLE_SUFFIX;
+       return ROLE_PREFIX + tenantId + TENANT_ADMIN_ROLE_SUFFIX;
     }
     
     private static String getDefaultReaderRole(String tenantId) {
@@ -1003,14 +1006,14 @@ public class AuthorizationCommon {
     }
     
     private static String getDefaultAdminUserID(String tenantName) {
-       return TENANT_ADMIN_ACCT_PREFIX+tenantName;
+       return TENANT_ADMIN_ACCT_PREFIX + tenantName;
     }
     
     private static String getDefaultReaderUserID(String tenantName) {
-       return TENANT_READER_ACCT_PREFIX+tenantName;
+       return TENANT_READER_ACCT_PREFIX + tenantName;
     }
     
-       static public PermissionAction createPermissionAction(Permission perm,
+       static private PermissionAction createPermissionAction(Permission perm,
                        ActionType actionType) {
         PermissionAction pa = new PermissionAction();
 
@@ -1023,18 +1026,6 @@ public class AuthorizationCommon {
            
            return pa;
        }
-
-       static public PermissionAction update(Permission perm, PermissionAction permAction) {
-        PermissionAction pa = new PermissionAction();
-
-           CSpaceAction action = URIResourceImpl.getAction(permAction.getName());
-           URIResourceImpl uriRes = new URIResourceImpl(perm.getTenantId(),
-                   perm.getResourceName(), action);
-           pa.setObjectIdentity(uriRes.getHashedId().toString());
-           pa.setObjectIdentityResource(uriRes.getId());
-           
-           return pa;
-       }
        
        private static HashSet<String> getTransitionVerbList(TenantBindingType tenantBinding, ServiceBindingType serviceBinding) {
                HashSet<String> result = new HashSet<String>();
@@ -1053,7 +1044,9 @@ public class AuthorizationCommon {
                TransitionDefList result = null;
                try {
                        String serviceObjectName = serviceBinding.getObject().getName();
-               DocumentHandler docHandler = ServiceConfigUtils.createDocumentHandlerInstance(
+                       
+               @SuppressWarnings("rawtypes")
+                       DocumentHandler docHandler = ServiceConfigUtils.createDocumentHandlerInstance(
                                tenantBinding, serviceBinding);
                Lifecycle lifecycle = docHandler.getLifecycle(serviceObjectName);
                if (lifecycle != null) {
@@ -1083,6 +1076,7 @@ public class AuthorizationCommon {
        }
        
        /**
+        * Creates the immutable workflow permission sets for the default admin and reader roles.
         * 
         * @param tenantBindingConfigReader
         * @param databaseProductType
@@ -1093,7 +1087,7 @@ public class AuthorizationCommon {
                JPATransactionContext jpaTransactionContext,
                TenantBindingConfigReaderImpl tenantBindingConfigReader,
                DatabaseProductType databaseProductType, 
-               String cspaceDatabaseName) throws Exception //FIXME: REM - 4/11/2012 - Rename to createWorkflowPermissions
+               String cspaceDatabaseName) throws Exception
     {
        java.util.logging.Logger logger = java.util.logging.Logger.getAnonymousLogger();
 
@@ -1122,16 +1116,15 @@ public class AuthorizationCommon {
                                if (prop == null ? true : Boolean.parseBoolean(prop)) {
                                        try {
                                                jpaTransactionContext.beginTransaction();
-                                               TransitionDefList transitionDefList = getTransitionDefList(tenantBinding, serviceBinding);
                                                HashSet<String> transitionVerbList = getTransitionVerbList(tenantBinding, serviceBinding);
                                                for (String transitionVerb : transitionVerbList) {
                                                        //
                                                        // Create the permission for the admin role
-                                                       Permission adminPerm = createWorkflowPermission(tenantBinding, serviceBinding, transitionVerb, ACTIONGROUP_CRUDL);
+                                                       Permission adminPerm = createWorkflowPermission(tenantBinding, serviceBinding, transitionVerb, ACTIONGROUP_CRUDL, true);
                                                        persist(jpaTransactionContext, adminPerm, adminRole, true, ACTIONGROUP_CRUDL);
                                                        //
                                                        // Create the permission for the read-only role
-                                                       Permission readonlyPerm = createWorkflowPermission(tenantBinding, serviceBinding, transitionVerb, ACTIONGROUP_RL);                                                      
+                                                       Permission readonlyPerm = createWorkflowPermission(tenantBinding, serviceBinding, transitionVerb, ACTIONGROUP_RL, true);                                                        
                                                        persist(jpaTransactionContext, readonlyPerm, readonlyRole, true, ACTIONGROUP_RL); // Persist/store the permission and permrole records and related Spring Security info
                                                }
                                                jpaTransactionContext.commitTransaction();
@@ -1188,7 +1181,6 @@ public class AuthorizationCommon {
     public static String getPersistedMD5Hash(String tenantId, String cspaceDatabaseName) throws Exception {
        String result = null;
        
-       ArrayList<String> existingTenants = new ArrayList<String>();
        // First find or create the tenants
        final String queryTenantSQL = String.format("SELECT id, name, config_md5hash FROM tenants WHERE id = '%s'", tenantId);
        
@@ -1263,7 +1255,7 @@ public class AuthorizationCommon {
                        Profiler profiler = new Profiler(AuthorizationCommon.class, 2);
                        profiler.start();
                        // Add a corresponding entry in the Spring Security Tables
-                       addPermissionsForUri(permission, permRole);
+                       addPermissionsForUri(jpaTransactionContext, permission, permRole);
                        profiler.stop();
                        logger.debug("Finished full perm generation for "
                                        + ":" + permission.getTenantId()
index 80759dead51bb5246f48565e5d647777f61fbe6c..9ce462ee0c6336c3a4e5d1d1f051955b4f5e90c2 100644 (file)
@@ -5,6 +5,7 @@ import org.collectionspace.services.authorization.PermissionRoleRel;
 import org.collectionspace.authentication.AuthN;
 import org.collectionspace.services.authorization.AccountRoleRel;
 import org.collectionspace.services.authorization.PermissionValue;
+import org.collectionspace.services.authorization.Role;
 import org.collectionspace.services.authorization.RoleValue;
 
 public class AuthorizationRoleRel {
@@ -23,6 +24,24 @@ public class AuthorizationRoleRel {
         return av;
     }
        
+    /**
+     * Builds the role value.
+     *
+     * @param arr the arr
+     * @return the role account value
+     */
+    static public RoleValue buildRoleValue(Role role) {
+       RoleValue rv = null;
+       
+       rv = new RoleValue();
+        rv.setRoleId(role.getCsid());
+        rv.setRoleName(role.getRoleName());
+        rv.setDisplayName(role.getDisplayName());
+        rv.setTenantId(role.getTenantId());
+
+        return rv;
+    }
+    
     /**
      * Builds the role value.
      *
index d73f37656f94ff7e7d2ee2c9166d86ee41b1be97..9a66032ebda93a684ff68a40954333fec043cc6e 100644 (file)
@@ -26,27 +26,31 @@ package org.collectionspace.services.common.authorization_mgt;
 import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.List;
-import javax.persistence.EntityManager;
-import javax.persistence.EntityManagerFactory;
 import javax.persistence.NoResultException;
 
 import org.collectionspace.services.common.document.DocumentException;
 import org.collectionspace.services.common.document.DocumentNotFoundException;
-import org.collectionspace.services.common.document.TransactionException;
+import org.collectionspace.services.common.document.JaxbUtils;
+import org.collectionspace.services.common.api.Tools;
 import org.collectionspace.services.common.context.ServiceContext;
 import org.collectionspace.services.common.context.ServiceContextProperties;
 import org.collectionspace.services.common.storage.jpa.JPATransactionContext;
 import org.collectionspace.services.common.storage.jpa.JpaStorageUtils;
 
+import org.collectionspace.services.client.RoleClient;
+import org.collectionspace.authentication.AuthN;
+
 import org.collectionspace.services.authorization.perms.ActionType;
 import org.collectionspace.services.authorization.perms.EffectType;
 import org.collectionspace.services.authorization.perms.Permission;
 import org.collectionspace.services.authorization.perms.PermissionAction;
 import org.collectionspace.services.authorization.storage.PermissionStorageConstants;
+import org.collectionspace.services.authorization.storage.RoleStorageConstants;
 import org.collectionspace.services.authorization.PermissionResource;
 import org.collectionspace.services.authorization.PermissionRole;
 import org.collectionspace.services.authorization.PermissionRoleRel;
 import org.collectionspace.services.authorization.PermissionValue;
+import org.collectionspace.services.authorization.Role;
 import org.collectionspace.services.authorization.RoleValue;
 import org.collectionspace.services.authorization.SubjectType;
 
@@ -69,7 +73,7 @@ public class PermissionRoleUtil {
      * @param ctx the ctx
      * @return the relation subject
      */
-    static public SubjectType getRelationSubject(ServiceContext ctx) {
+    static public SubjectType getRelationSubject(ServiceContext<?, ?> ctx) {
         Object o = ctx.getProperty(ServiceContextProperties.SUBJECT);
         if (o == null) {
             throw new IllegalArgumentException(ServiceContextProperties.SUBJECT
@@ -86,7 +90,7 @@ public class PermissionRoleUtil {
      * @param pr the pr
      * @return the relation subject
      */
-    static public SubjectType getRelationSubject(ServiceContext ctx, PermissionRole pr) {
+    static public SubjectType getRelationSubject(ServiceContext<?, ?> ctx, PermissionRole pr) {
         SubjectType subject = pr.getSubject();
         if (subject == null) {
             //it is not required to give subject as URI determines the subject
@@ -101,39 +105,45 @@ public class PermissionRoleUtil {
      *
      * @param pr permissionrole
      * @param subject the subject
-     * @param prrl persistent entities built are inserted into this list
+     * @param permRoleRelationshipList persistent entities built are inserted into this list
      * @param toDelete the to delete
      */
     static public void buildPermissionRoleRel(JPATransactionContext jpaTransactionContext, 
                PermissionRole pr,
                SubjectType subject,
-               List<PermissionRoleRel> prrl,
+               List<PermissionRoleRel> permRoleRelationshipList,
                boolean handleDelete,
                String tenantId) throws Exception {
        
         if (subject.equals(SubjectType.ROLE)) {
                List<PermissionValue> permissionValues = pr.getPermission();
-               if (permissionValues != null && permissionValues.size() > 0) {
+               if (permissionValues != null && permissionValues.size() == 1) {
                    PermissionValue pv = permissionValues.get(0);
                    for (RoleValue rv : pr.getRole()) {
-                       PermissionRoleRel prr = buildPermissonRoleRel(jpaTransactionContext, pv, rv, subject, handleDelete, tenantId);
-                       prrl.add(prr);
+                       PermissionRoleRel permRoleRelationship = buildPermissonRoleRel(jpaTransactionContext, pv, rv, subject, handleDelete, tenantId);
+                       permRoleRelationshipList.add(permRoleRelationship);
                    }
+               } else {
+                       String msg = "There must be one and only one Permission supplied in the payload when creating this Permission-Roles relationshiop.";
+                       throw new DocumentException(msg);
                }
         } else if (subject.equals(SubjectType.PERMISSION)) {
                List<RoleValue> roleValues = pr.getRole();
-               if (roleValues != null && roleValues.size() > 0) {
+               if (roleValues != null && roleValues.size() == 1) {
                    RoleValue rv = roleValues.get(0);
                    for (PermissionValue pv : pr.getPermission()) {
                        PermissionRoleRel prr = buildPermissonRoleRel(jpaTransactionContext, pv, rv, subject, handleDelete, tenantId);
-                       prrl.add(prr);
+                       permRoleRelationshipList.add(prr);
                    }
+               } else {
+                       String msg = "There must be one and only one Role supplied in the payload when creating this Role-Permissions relationshiop.";
+                       throw new DocumentException(msg);
                }
         }
     }
     
     static public void buildPermissionRoleRel(
-               ServiceContext ctx,
+               ServiceContext<?, ?> ctx,
                PermissionRole pr,
                SubjectType subject,
                List<PermissionRoleRel> prrl,
@@ -160,7 +170,7 @@ public class PermissionRoleUtil {
      * Try to find a persisted Permission record using a PermissionValue instance.
      *
      */
-    static private Permission lookupPermission(JPATransactionContext jpaTransactionContext, PermissionValue permissionValue, String tenantId) throws TransactionException {
+    static private Permission lookupPermission(JPATransactionContext jpaTransactionContext, PermissionValue permissionValue, String tenantId) throws DocumentException {
        Permission result = null;
        
        String actionGroup = permissionValue.getActionGroup() != null ? permissionValue.getActionGroup().trim() : null;
@@ -171,19 +181,23 @@ public class PermissionRoleUtil {
        //
        if (permissionId != null && !permissionId.isEmpty()) {
                try {
-                       result = (Permission)JpaStorageUtils.getEntity(jpaTransactionContext, permissionId, Permission.class);
+                       result = (Permission)JpaStorageUtils.getEntityByDualKeys(
+                                       jpaTransactionContext, 
+                                       Permission.class.getName(),
+                                       PermissionStorageConstants.ID, permissionId, 
+                                       PermissionStorageConstants.TENANT_ID, tenantId);
                } catch (Throwable e) {
                        String msg = String.format("Searched for but couldn't find a permission with CSID='%s'.",
                                        permissionId);
                        logger.trace(msg);
                }
-       } else if ((resourceName != null && !resourceName.isEmpty()) && 
-                       (actionGroup != null && !actionGroup.isEmpty())) {
+       } else if (Tools.notBlank(resourceName) && Tools.notBlank(actionGroup)) {
                //
                // If there was no permission ID, then we can try to find the permission with the resource name and action group tuple
                //
                try {
-                       result = (Permission)JpaStorageUtils.getEntityByDualKeys(jpaTransactionContext, 
+                       result = (Permission)JpaStorageUtils.getEntityByDualKeys(
+                                       jpaTransactionContext, 
                                        Permission.class.getName(),
                                        PermissionStorageConstants.RESOURCE_NAME, permissionValue.getResourceName(), 
                                        PermissionStorageConstants.ACTION_GROUP, permissionValue.getActionGroup(),
@@ -196,11 +210,31 @@ public class PermissionRoleUtil {
        } else {
                String errMsg = String.format("Couldn't perform lookup of permission.  Not enough information provided.  Lookups requires a permission CSID or a resource name and action group tuple.  The provided information was permission ID='%s', resourceName='%s', and actionGroup='%s'.",
                                permissionId, resourceName, actionGroup);
-               logger.warn(errMsg);
+               throw new DocumentException(errMsg);
+       }
+       
+       if (result == null) {
+               throw new DocumentNotFoundException(String.format("Could not find Permission resource with CSID='%s', actionGroup='%s', resourceName='%s'.", 
+                               permissionId, actionGroup, resourceName));
        }
        
        return result;
     }
+    /**
+     * Ensure the Role's permission relationships can be changed.
+     * 
+     * @param role
+     * @return
+     */
+    static private boolean canRoleRelatedTo(Role role) {
+       boolean result = true;
+       
+        if (RoleClient.IMMUTABLE.equals(role.getPermsProtection()) && !AuthN.get().isSystemAdmin()) {
+               result = false;
+        }
+       
+       return result;
+    }
     
     /**
      * Builds a permisson role relationship for either 'create' or 'delete'
@@ -218,8 +252,18 @@ public class PermissionRoleUtil {
                String tenantId) throws DocumentException {
 
        PermissionRoleRel result = null;
+       Role role = lookupRole(jpaTransactionContext, roleValue, tenantId);
+       //
+       // Ensure we can change the Role's permissions-related relationships.
+       //
+        if (canRoleRelatedTo(role) == false) {
+               String msg = String.format("Role with CSID='%s' cannot have its associated permissions changed.", role.getCsid());
+               throw new DocumentException(msg);
+        }
+       //
+        // Get the permission info
+        //
        Permission permission = lookupPermission(jpaTransactionContext, permissionValue, tenantId);
-       
        //
        // If we couldn't find an existing permission and we're not processing a DELETE request, we need to create
        // a new permission.
@@ -236,6 +280,10 @@ public class PermissionRoleUtil {
                        String errMsg = "Could not create new permission for new permission-role relationship.";
                        throw new DocumentException(errMsg);
                }
+       } else if (permission == null && handleDelete == true) {
+               String msg = String.format("Could not find an existing permission that matches this: %s", 
+                               JaxbUtils.toString(permissionValue, PermissionValue.class));
+               throw new DocumentException(msg);
        }
        
        //
@@ -271,8 +319,46 @@ public class PermissionRoleUtil {
        
         return result;
     }
+    
+    public static RoleValue fetchRoleValue(ServiceContext<?, ?> ctx, String roleId) throws DocumentNotFoundException {
+       RoleValue result = null;
+       
+       JPATransactionContext jpaTransactionContext = (JPATransactionContext) ctx.getCurrentTransactionContext();
+       Role role = lookupRole(jpaTransactionContext, roleId, ctx.getTenantId());
+       result = AuthorizationRoleRel.buildRoleValue(role);
+       
+       return result;
+    }
 
-    private static Permission createPermission(JPATransactionContext jpaTransactionContext, Permission permission) {
+    private static Role lookupRole(JPATransactionContext jpaTransactionContext, RoleValue roleValue, String tenantId) throws DocumentNotFoundException {
+       return lookupRole(jpaTransactionContext, roleValue.getRoleId(), tenantId);
+       }
+    
+    private static Role lookupRole(JPATransactionContext jpaTransactionContext, String roleId, String tenantId) throws DocumentNotFoundException {
+       Role result = null;
+       
+       try {
+               result = (Role)JpaStorageUtils.getEntityByDualKeys(
+                                               jpaTransactionContext,
+                                       Role.class.getName(),
+                                       RoleStorageConstants.ROLE_ID, roleId, 
+                                       RoleStorageConstants.ROLE_TENANT_ID, tenantId);
+       } catch (Throwable e) {
+               String msg = String.format("Searched for but couldn't find a role with CSID='%s'.",
+                               roleId);
+               logger.trace(msg);
+       }
+       
+       if (result == null) {
+               String msg = String.format("Could not find Role resource with CSID='%s'", roleId);
+               throw new DocumentNotFoundException(msg);
+       }
+       
+       return result;
+       }
+    
+
+       private static Permission createPermission(JPATransactionContext jpaTransactionContext, Permission permission) {
                Permission result = null;
                
                PermissionResource permissionResource = new PermissionResource();  // Get the PermissionResource singleton instance (RESTEasy ensures it is a singleton)
@@ -322,6 +408,17 @@ public class PermissionRoleUtil {
        
                return result;
        }
+       
+       static public boolean isEmpty(PermissionRole permRole) {
+               boolean result = true;
+               
+               if (permRole != null && !Tools.isEmpty(permRole.getPermission()) 
+                               && !Tools.isEmpty(permRole.getRole()) && permRole.getSubject() != null) {
+                       result = false;
+               }
+               
+               return result;
+       }
 
        /**
      * Checks if is invalid tenant.
index d10ab1caf3ae8d509ac06cdfa83902319bcfc5de..17ba6aea3c26245042c85cd85acfd4992e1224f4 100644 (file)
@@ -55,6 +55,7 @@ public class RemoteServiceContextImpl<IT, OT>
 
     /** The logger. */
     final Logger logger = LoggerFactory.getLogger(RemoteServiceContextImpl.class);
+    
     //input stores original content as received over the wire
     /** The input. */
     private IT input;    
@@ -163,12 +164,12 @@ public class RemoteServiceContextImpl<IT, OT>
      */
     @Override
     public void setInput(IT input) {
-        //for security reasons, do not allow to set input again (from handlers)
-        if (this.input != null) {
-            String msg = "Resetting or changing an context's input is not allowed.";
-            logger.error(msg);
-            throw new IllegalStateException(msg);
-        }
+       if (logger.isDebugEnabled()) {
+               if (this.input != null) {
+                   String msg = "\n#\n# Resetting or changing an context's input is not advised.\n#";
+                   logger.warn(msg);
+               }
+       }
         this.input = input;
     }
 
@@ -195,7 +196,8 @@ public class RemoteServiceContextImpl<IT, OT>
      * @return
      * @throws Exception 
      */
-    public CollectionSpaceResource<IT, OT> getResource(ServiceContext<?, ?> ctx) throws Exception {
+    @SuppressWarnings("unchecked")
+       public CollectionSpaceResource<IT, OT> getResource(ServiceContext<?, ?> ctx) throws Exception {
        CollectionSpaceResource<IT, OT> result = null;
        
        ResourceMap resourceMap = ctx.getResourceMap();
@@ -232,7 +234,8 @@ public class RemoteServiceContextImpl<IT, OT>
     /* (non-Javadoc)
      * @see org.collectionspace.services.common.context.RemoteServiceContext#getLocalContext(java.lang.String)
      */
-    @Override
+    @SuppressWarnings("unchecked")
+       @Override
     public ServiceContext<IT, OT> getLocalContext(String localContextClassName) throws Exception {
         ClassLoader cloader = Thread.currentThread().getContextClassLoader();
         Class<?> ctxClass = cloader.loadClass(localContextClassName);
@@ -278,8 +281,10 @@ public class RemoteServiceContextImpl<IT, OT>
                        //
                        // If it's a shared connection, we can't close it.  Just reduce the refcount by 1
                        //
-                       String warnMsg = "Attempted to release a shared storage connection.  Only the originator can release the connection";
-                       logger.warn(warnMsg);
+                       if (logger.isTraceEnabled()) {
+                               String traceMsg = "Attempted to release a shared storage connection.  Only the originator can release the connection";
+                               logger.trace(traceMsg);
+                       }
                        transactionConnectionRefCount--;
                } else {
                        TransactionContext transactionCtx = getCurrentTransactionContext();
index 36bb6a2a6f47fa4307e45b5928571eac04df2fe9..5adce86dff23cc71f0cab1258b59c1bd8c4c05ce 100644 (file)
@@ -38,7 +38,7 @@ import org.collectionspace.services.common.document.TransactionException;
 import org.collectionspace.services.common.document.ValidatorHandler;
 import org.collectionspace.services.common.security.SecurityContext;
 import org.collectionspace.services.common.storage.TransactionContext;
-import org.collectionspace.services.common.storage.jpa.JPATransactionContext;
+
 import org.collectionspace.services.config.ClientType;
 import org.collectionspace.services.config.service.ObjectPartType;
 import org.collectionspace.services.config.service.ServiceBindingType;
index 770289760dbc6f40c09a774130c519899724223c..f6af6159bc384cab988a228bada7776b1bec3669 100644 (file)
@@ -45,6 +45,7 @@ import org.slf4j.LoggerFactory;
  * @param <WT> 
  * @param <WTL> 
  */
+@SuppressWarnings({"unchecked", "rawtypes"})
 public abstract class AbstractDocumentHandlerImpl<T, TL, WT, WTL>
         implements DocumentHandler<T, TL, WT, WTL> {
 
@@ -77,7 +78,7 @@ public abstract class AbstractDocumentHandlerImpl<T, TL, WT, WTL>
     /* (non-Javadoc)
      * @see org.collectionspace.services.common.document.DocumentHandler#getServiceContext()
      */
-    @Override
+       @Override
     public ServiceContext getServiceContext() {
         return serviceContext;
     }
@@ -311,7 +312,7 @@ public abstract class AbstractDocumentHandlerImpl<T, TL, WT, WTL>
     /* (non-Javadoc)
      * @see org.collectionspace.services.common.document.DocumentHandler#complete(org.collectionspace.services.common.document.DocumentHandler.Action, org.collectionspace.services.common.document.DocumentWrapper)
      */
-    @Override
+       @Override
     final public void complete(Action action, DocumentWrapper<?> wrapDoc) throws Exception {
         switch (action) {
             case CREATE:
diff --git a/services/common/src/main/java/org/collectionspace/services/common/document/InconsistentStateException.java b/services/common/src/main/java/org/collectionspace/services/common/document/InconsistentStateException.java
new file mode 100644 (file)
index 0000000..fb333de
--- /dev/null
@@ -0,0 +1,23 @@
+package org.collectionspace.services.common.document;
+
+public class InconsistentStateException extends TransactionException {
+       private static final long serialVersionUID = 11L;
+
+       public InconsistentStateException() {
+        super(TRANSACTION_FAILED_MSG);
+        setErrorCode(HTTP_CODE);
+       }
+
+       public InconsistentStateException(String msg) {
+               super(msg);
+       }
+
+       public InconsistentStateException(String message, Throwable cause) {
+               super(message, cause);
+       }
+
+       public InconsistentStateException(Throwable cause) {
+               super(cause);
+       }
+
+}
index 0c8d4b377e5dd688656f480156c29966cbef94b7..366634d278e870dfc7efabf6e52fafe8b7d817d4 100644 (file)
@@ -32,8 +32,12 @@ import java.io.StringWriter;
 import java.lang.reflect.InvocationTargetException;
 import java.lang.reflect.Method;
 import javax.xml.bind.JAXBContext;
+import javax.xml.bind.JAXBElement;
+import javax.xml.bind.JAXBException;
 import javax.xml.bind.Marshaller;
 import javax.xml.bind.Unmarshaller;
+import javax.xml.namespace.QName;
+
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -51,17 +55,42 @@ public class JaxbUtils {
      * @param clazz class of the jaxb object
      * @return
      */
-    public static String toString(Object o, Class clazz) {
+    public static String toString(Object o, Class<?> clazz) {          
         StringWriter sw = new StringWriter();
+        
         try {
             JAXBContext jc = JAXBContext.newInstance(clazz);
             Marshaller m = jc.createMarshaller();
-            m.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT,
-                    Boolean.TRUE);
+            m.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE);
             m.marshal(o, sw);
-        } catch (Exception e) {
-            e.printStackTrace();
-        }
+        } catch (javax.xml.bind.MarshalException e) {
+               //
+               // If the JAX-B object we're trying to marshal doesn't have an @XmlRootElement, then we need another
+               // approach.
+               //
+            return marshalWithoutRoot(o, clazz);
+        } catch (JAXBException e) {
+                       logger.error(e.getMessage());
+               }
+        
+        return sw.toString();
+    }
+    
+    /*
+     * Use this to marshal a JAX-B object that has no @XmlRootElement
+     */
+    private static String marshalWithoutRoot(Object o, Class<?> clazz) {
+        StringWriter sw = new StringWriter();
+
+       try {
+            JAXBContext jc = JAXBContext.newInstance(clazz);
+               Marshaller marshaller = jc.createMarshaller();
+               marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE);
+               marshaller.marshal(new JAXBElement(new QName("uri","local"), clazz, o), sw);
+       } catch (Exception e) {
+               logger.debug(e.getMessage());
+       }
+       
         return sw.toString();
     }
 
index 7f979b16409c57cd8d0489e0f5c141010d680190..a2c099f7fcb8714deb8f2d3f44d80d427ea29b9c 100644 (file)
@@ -22,10 +22,6 @@ package org.collectionspace.services.common.document;
  * 
  */
 public class TransactionException extends DocumentException {
-
-    /**
-        * 
-        */
        private static final long serialVersionUID = 1L;
 
        // Custom HTTP status code, per the extensibility offered via RFC-2616
index d39fbb9702bcc95175075fcd3b8e9c1842e593d5..168f64d711b2fa6314b611ee5f03af575eb22dff 100644 (file)
@@ -161,4 +161,7 @@ public interface StorageClient {
                        throws DocumentNotFoundException, TransactionException,
                        DocumentException;
 
+       boolean delete(ServiceContext ctx, Object entityFound, DocumentHandler handler)
+                       throws DocumentNotFoundException, DocumentException;
+
 }
index 09f3516a0da9e84b345baa0e425b0e2f582ce103..bbcf4bb0e3203d8bd0301571c04f5340a8123351 100644 (file)
@@ -36,4 +36,7 @@ public abstract class TransactionContext {
        abstract public void remove(Object entity);
 
        abstract public Object merge(Object entity);
+
+       abstract public void flush();
+
 }
index 5954593e9c4757dba25feccd89fd392e240c2242..dbdf2483a961d65002041f8fc9b6b34396e7be5f 100644 (file)
@@ -3,10 +3,13 @@ package org.collectionspace.services.common.storage.jpa;
 import javax.persistence.EntityManager;
 import javax.persistence.EntityManagerFactory;
 import javax.persistence.Query;
+import javax.persistence.RollbackException;
 
 import org.collectionspace.services.common.context.ServiceContext;
+import org.collectionspace.services.common.document.InconsistentStateException;
 import org.collectionspace.services.common.document.TransactionException;
 import org.collectionspace.services.common.storage.TransactionContext;
+
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -16,7 +19,7 @@ public class JPATransactionContext extends TransactionContext {
     private final Logger logger = LoggerFactory.getLogger(TransactionContext.class);
 
        private int transactionRefCount = 0;
-       private Boolean commitSuccessful = null;
+       private boolean aclTablesUpdatedFlag = false;
        
        EntityManagerFactory emf;
        EntityManager em;
@@ -39,6 +42,23 @@ public class JPATransactionContext extends TransactionContext {
        protected EntityManager getEntityManager() {
                return em;
        }
+               
+       /**
+        * Set to 'true' if (and only if) a change has been made AND successfully committed to the Spring Security tables.
+        * 
+        * Since we can't include Spring Security table changes and JPA changes in a single transaction, we
+        * keep track of changes to the Spring Security tables here.  We'll use this flag to log a critical error if
+        * we think there is a chance the JPA tables and Spring Security tables get out of sync.
+        * 
+        * @param flag
+        */
+       public void setAclTablesUpdateFlag(boolean flag) {
+               aclTablesUpdatedFlag = flag;
+       }
+       
+       protected boolean getAclTablesUpdateFlag() {
+               return aclTablesUpdatedFlag;
+       }       
        
        @Override
        public ServiceContext getServiceContext() {
@@ -58,13 +78,40 @@ public class JPATransactionContext extends TransactionContext {
        @Override
        public void close() throws TransactionException  {
                if (em.getTransaction().isActive() == true && em.getTransaction().getRollbackOnly() == true) {
-                       em.getTransaction().rollback();
+                       if (getAclTablesUpdateFlag() == false) {
+                               //
+                               // Since there were no changes committed to the Spring Security tables, we can just rollback and continue
+                               //
+                               em.getTransaction().rollback();
+                       } else {
+                               String msg = handleInconsistentState();
+                               throw new InconsistentStateException(msg);
+                       }
        } else if (em.getTransaction().isActive() == true) {
-               throw new JPATransactionException("There is an active transaction.  You must commit the active transaction prior to calling this close method.");
+               markForRollback();
+               close(); // NOTE: Recursive call.
+               throw new JPATransactionException("There was an active transaction.  You must commit the active transaction prior to calling this close method.");
        }
-       
+               
+               em.close();
+        JpaStorageUtils.releaseEntityManagerFactory(emf);              
+       }
+       
+       private String handleInconsistentState() throws InconsistentStateException {
+               //
+               // If we've modified the Spring Tables and need to rollback this JPA transaction, we now have a potentially critical inconsistent state in the system
+               //
+               String msg = "\n#\n# CRITICAL: The Spring Security tables just became inconsistent with CollectionSpace JPA AuthN and AuthZ tables.  Contact your CollectionSpace administrator immediately.\n#";
+
+               //
+               // Finish by rolling back the JPA transaction, closing the connection, and throwing an exception
+               //
+               logger.error(msg);
+               em.getTransaction().rollback();
                em.close();
         JpaStorageUtils.releaseEntityManagerFactory(emf);
+        
+        return msg;
        }
 
        @Override
@@ -85,11 +132,13 @@ public class JPATransactionContext extends TransactionContext {
                return em.merge(entity);
        }
        
+       @SuppressWarnings("unchecked")
        @Override
        public Object find(Class entityClass, Object primaryKey) {
                return em.find(entityClass, primaryKey);
        }
        
+       @SuppressWarnings("unchecked")
        @Override
        public Object find(Class entityClass, String id) {
                return em.find(entityClass, id);
@@ -104,6 +153,16 @@ public class JPATransactionContext extends TransactionContext {
     public void remove(Object entity) {
                em.remove(entity);
        }
+       
+       @Override
+       public boolean isTransactionActive() {
+               return em.getTransaction().isActive();
+       }
+       
+       @Override
+       public void flush() {
+               em.flush();
+       }
 
        @Override
        public void commitTransaction() throws TransactionException {
@@ -112,12 +171,6 @@ public class JPATransactionContext extends TransactionContext {
                }
                if (--transactionRefCount == 0) {
                        em.getTransaction().commit();
-                       commitSuccessful = Boolean.TRUE;
                }
        }
-
-       @Override
-       public boolean isTransactionActive() {
-               return em.getTransaction().isActive();
-       }
 }
index 49bc98036a4b995e4d0f085b1b348fd9503a6dfd..d2a92aa74f4076e0e7f1dbfe339bf169ba794e6e 100644 (file)
@@ -66,21 +66,6 @@ import org.slf4j.LoggerFactory;
 public class JpaRelationshipStorageClient<T> extends JpaStorageClientImpl {
 
     private final Logger logger = LoggerFactory.getLogger(JpaRelationshipStorageClient.class);
-
-    public static PermissionValue createPermissionValue(Permission permission) {
-       PermissionValue result = new PermissionValue();
-       result.setPermissionId(permission.getCsid());
-       result.setResourceName(permission.getResourceName());
-       result.setActionGroup(permission.getActionGroup());
-       return result;
-    }
-    
-    public static RoleValue createRoleValue(Role role) {
-       RoleValue result = new RoleValue();
-       result.setRoleId(role.getCsid());
-       result.setRoleName(role.getRoleName());
-       return result;
-    }
     
     public JpaRelationshipStorageClient() {
        //empty
@@ -107,30 +92,28 @@ public class JpaRelationshipStorageClient<T> extends JpaStorageClientImpl {
         try {
             jpaTransactionContext.beginTransaction();
             handler.prepare(Action.CREATE);
-            List<T> rl = new ArrayList<T>();
-            DocumentWrapper<List<T>> wrapDoc =
-                    new DocumentWrapperImpl<List<T>>(rl);
+            List<T> relationshipList = new ArrayList<T>();
+            DocumentWrapper<List<T>> wrapDoc = new DocumentWrapperImpl<List<T>>(relationshipList);
             handler.handle(Action.CREATE, wrapDoc);
-            for (T r : rl) {
-                JaxbUtils.setValue(r, "setCreatedAtItem", Date.class, new Date());
-                jpaTransactionContext.persist(r);
+            for (T relationship : relationshipList) {
+                JaxbUtils.setValue(relationship, "setCreatedAtItem", Date.class, new Date());
+                jpaTransactionContext.persist(relationship);
             }
-            handler.complete(Action.CREATE, wrapDoc);
+            handler.complete(Action.CREATE, wrapDoc); 
             jpaTransactionContext.commitTransaction();
-            result = "-1"; // meaningless result
+            result = "0"; // meaningless result
         } catch (BadRequestException bre) {
-               jpaTransactionContext.markForRollback();
             throw bre;
         } catch (PersistenceException pe) {
-               jpaTransactionContext.markForRollback();
                throw pe;
+        } catch (DocumentException de) {
+               throw de;
         } catch (Exception e) {
-               jpaTransactionContext.markForRollback();
-            if (logger.isDebugEnabled()) {
-                logger.debug("Caught exception ", e);
-            }
             throw new DocumentException(e);
         } finally {
+               if (result == null) {
+               jpaTransactionContext.markForRollback();  // If result == null, we failed and must mark the current tx for rollback
+               }
             ctx.closeConnection();
         }
         
@@ -159,7 +142,6 @@ public class JpaRelationshipStorageClient<T> extends JpaStorageClientImpl {
                }
                
                String objectId = getObjectId(ctx);
-               Class objectClass = getObjectClass(ctx);
                DocumentFilter docFilter = handler.getDocumentFilter();
                if (docFilter == null) {
                    docFilter = handler.createDocumentFilter();
@@ -193,19 +175,9 @@ public class JpaRelationshipStorageClient<T> extends JpaStorageClientImpl {
             try {
                 relList = q.getResultList();
             } catch (NoResultException nre) {
-                String msg = "get(1): " + " could not find relationships for object class="
-                        + objectClass.getName() + " id=" + id;
-                if (logger.isDebugEnabled()) {
-                    logger.debug(msg, nre);
-                }
-            }
-            if (relList.size() == 0) {
-                String msg = "get(2): " + " could not find relationships for object class="
-                        + objectClass.getName() + " id=" + id;
-                if (logger.isDebugEnabled()) {
-                    logger.debug(msg);
-                }
+               // Quietly consume.  relList will just be an empty list
             }
+
             DocumentWrapper<List<T>> wrapDoc = new DocumentWrapperImpl<List<T>>(relList);
             handler.handle(Action.GET, wrapDoc);
             handler.complete(Action.GET, wrapDoc);
@@ -354,14 +326,14 @@ public class JpaRelationshipStorageClient<T> extends JpaStorageClientImpl {
         try {
                jpaTransactionContext.beginTransaction();
             handler.prepare(Action.DELETE);
-            List<T> rl = new ArrayList<T>();
-            DocumentWrapper<List<T>> wrapDoc = new DocumentWrapperImpl<List<T>>(rl);
+            List<T> relationshipList = new ArrayList<T>();
+            DocumentWrapper<List<T>> wrapDoc = new DocumentWrapperImpl<List<T>>(relationshipList);
             handler.handle(Action.DELETE, wrapDoc);
             //
             //the following could be much more efficient if done with a single sql/jql
             //
-            for (T r : rl) {
-               jpaTransactionContext.remove(getRelationship(jpaTransactionContext, r));
+            for (T relationship : relationshipList) {
+               jpaTransactionContext.remove(getRelationship(jpaTransactionContext, relationship));
             }
             handler.complete(Action.DELETE, wrapDoc); // Delete from the Spring Security tables.  Would be better if this was part of the earlier transaction.
             jpaTransactionContext.commitTransaction();
index 613e7d0177c595e7d5d7991b363785047200b802..b0665fc8d836732dcb9effe2e45b95bc1e8458a8 100644 (file)
@@ -115,8 +115,8 @@ public class JpaStorageClientImpl implements StorageClient {
             try {
                handler.handle(Action.CREATE, wrapDoc);
                    JaxbUtils.setValue(entity, "setCreatedAtItem", Date.class, new Date());
-                   jpaConnectionContext.persist(entity);               
-            } catch (EntityExistsException ee) {
+                   jpaConnectionContext.persist(entity);
+            } catch (EntityExistsException ee) { // FIXME: No, don't allow duplicates
                //
                // We found an existing matching entity in the store, so we don't need to create one.  Just update the transient 'entity' instance with the existing persisted entity we found.
                // An entity's document handler class will throw this exception only if attempting to create (but not actually creating) duplicate is ok -e.g., Permission records.
@@ -410,28 +410,55 @@ public class JpaStorageClientImpl implements StorageClient {
      * cost: a get before delete
      * @see org.collectionspace.services.common.storage.StorageClient#delete(org.collectionspace.services.common.context.ServiceContext, java.lang.String)
      */
-    @SuppressWarnings({ "rawtypes", "unchecked" })
+    @SuppressWarnings({ "rawtypes" })
        @Override
     public boolean delete(ServiceContext ctx, String id, DocumentHandler handler)
             throws DocumentNotFoundException, DocumentException {
-       boolean result = true;
+       boolean result = false;
        
        JPATransactionContext jpaConnectionContext = (JPATransactionContext)ctx.openConnection(); 
         try {
             jpaConnectionContext.beginTransaction();
-            handler.prepare(Action.DELETE);
             Object entityFound = getEntity(ctx, id);
             if (entityFound == null) {
-               jpaConnectionContext.markForRollback();
                 String msg = "delete(ctx, ix, handler) could not find entity with id=" + id;
                 logger.error(msg);
                 throw new DocumentNotFoundException(msg);
             }
-            DocumentWrapper<Object> wrapDoc = new DocumentWrapperImpl<Object>(entityFound);
+            result = delete(ctx, entityFound, handler);
+            jpaConnectionContext.commitTransaction();
+        } catch (DocumentException de) {
+               jpaConnectionContext.markForRollback();
+               throw de;
+        } catch (Exception e) {
+            if (logger.isDebugEnabled()) {
+                logger.debug("delete(ctx, ix, handler): Caught exception ", e);
+            }
+            jpaConnectionContext.markForRollback();
+            throw new DocumentException(e);
+        } finally {
+               ctx.closeConnection();
+        }
+        
+        return result;
+    }
+    
+    @SuppressWarnings({ "rawtypes", "unchecked" })
+       @Override
+    public boolean delete(ServiceContext ctx, Object entity, DocumentHandler handler)
+            throws DocumentNotFoundException, DocumentException {
+       boolean result = false;
+       
+       JPATransactionContext jpaConnectionContext = (JPATransactionContext)ctx.openConnection(); 
+        try {
+            jpaConnectionContext.beginTransaction();
+            handler.prepare(Action.DELETE);
+            DocumentWrapper<Object> wrapDoc = new DocumentWrapperImpl<Object>(entity);
             handler.handle(Action.DELETE, wrapDoc);
-            jpaConnectionContext.remove(entityFound);
+            jpaConnectionContext.remove(entity);
             handler.complete(Action.DELETE, wrapDoc);
             jpaConnectionContext.commitTransaction();
+            result = true;
         } catch (DocumentException de) {
                jpaConnectionContext.markForRollback();
                throw de;
@@ -446,7 +473,7 @@ public class JpaStorageClientImpl implements StorageClient {
         }
         
         return result;
-    }
+    }    
 
     /**
      * Gets the entityReceived name.
@@ -501,9 +528,9 @@ public class JpaStorageClientImpl implements StorageClient {
             throws DocumentNotFoundException, TransactionException {
        Object entityFound = null;
        
-       JPATransactionContext jpaTransactionConnection = (JPATransactionContext)ctx.openConnection();
+       JPATransactionContext jpaTransactionContext = (JPATransactionContext)ctx.openConnection();
        try {
-               entityFound = JpaStorageUtils.getEntity(jpaTransactionConnection.getEntityManager(), id, entityClazz);
+               entityFound = JpaStorageUtils.getEntity(jpaTransactionContext, id, entityClazz); // FIXME: # Should be qualifying with the tenant ID
                if (entityFound == null) {
                    String msg = "could not find entity of type=" + entityClazz.getName()
                            + " with id=" + id;
index 56f7f791b545403001dab38779688057e7159695..705cd756eb94d70d606f9d9fc688521a5651855c 100644 (file)
@@ -440,6 +440,7 @@ public class JpaStorageUtils {
         if (useTenantId == true) {
             q.setParameter("tenantId", tenantId);
         }
+        
         result = q.getSingleResult();
 
         return result;
index 2293bb966c5ebe9e213577477b2579efb853a96c..e95c958daade81a58a9c422130b5675a1804f2f7 100644 (file)
@@ -2221,4 +2221,13 @@ public class RepositoryClientImpl implements RepositoryClient<PoxPayloadIn, PoxP
                repoSession.setTransactionRollbackOnly();
        }
     }
+
+    /**
+     * Should never get called.
+     */
+       @Override
+       public boolean delete(ServiceContext ctx, Object entityFound, DocumentHandler handler)
+                       throws DocumentNotFoundException, DocumentException {
+               throw new UnsupportedOperationException();
+       }
 }
index 7b3051febede698d57ad9d8482d47fa5c8f050d2..1bb5fd50fea7adeea21589fdf70a05a50f2bdc6a 100644 (file)
             </xs:documentation>
                        <xs:appinfo>
                                <hj:entity>
-                                       <orm:table name="permissions">
-                                               <orm:unique-constraint>
-                            <!-- combined length should be < 1000 bytes -->
-                            <orm:column-name>resource_name</orm:column-name>
-                            <orm:column-name>action_group</orm:column-name>
-                            <orm:column-name>tenant_id</orm:column-name>
-                           </orm:unique-constraint>
-                    </orm:table>
+                                       <orm:table name="permissions" />
                                </hj:entity>
                        </xs:appinfo>
                </xs:annotation>
@@ -81,8 +74,7 @@
                                        </xs:appinfo>
                                </xs:annotation>
                        </xs:element>
-                       <xs:element name="actionGroup" type="xs:string" minOccurs="0"
-                               maxOccurs="1">
+                       <xs:element name="actionGroup" type="xs:string" minOccurs="0" maxOccurs="1">
                                <xs:annotation>
                                        <xs:appinfo>
                                                <hj:basic>
                                        </xs:appinfo>
                                </xs:annotation>
                        </xs:element>
-                       <xs:element name="action" type="permission_action"
-                               minOccurs="1" maxOccurs="unbounded" />
-                       <xs:element name="effect" type="effect_type" minOccurs="1"
-                               maxOccurs="1">
+                       <xs:element name="action" type="permission_action" minOccurs="1" maxOccurs="unbounded" />
+                       <xs:element name="effect" type="effect_type" minOccurs="1" maxOccurs="1">
                                <xs:annotation>
                                        <xs:appinfo>
                                                <hj:basic>
                                        </xs:appinfo>
                                </xs:annotation>
                        </xs:element>
+                   <xs:element name="metadataProtection" type="xs:string" minOccurs="0" maxOccurs="1">
+                   <xs:annotation>
+                       <xs:appinfo>
+                           <hj:basic>
+                               <orm:column name="metadata_protection" nullable="true"/>
+                           </hj:basic>
+                       </xs:appinfo>
+                   </xs:annotation>
+            </xs:element>
+                       <xs:element name="actionsProtection" type="xs:string" minOccurs="0" maxOccurs="1">
+                               <xs:annotation>
+                                       <xs:appinfo>
+                                               <hj:basic>
+                                                       <orm:column name="actions_protection" nullable="true"/>
+                                               </hj:basic>
+                                       </xs:appinfo>
+                               </xs:annotation>
+                       </xs:element>
                        <xs:element name="tenant_id" type="xs:string" minOccurs="1">
                                <xs:annotation>
                                        <xs:appinfo>