]> git.aero2k.de Git - tmp/jakarta-migration.git/commitdiff
DRYD-186: POSTs to the Role resource can now declare a set of permissions to associat...
authorremillet <remillet@yahoo.com>
Thu, 7 Dec 2017 19:55:38 +0000 (11:55 -0800)
committerremillet <remillet@yahoo.com>
Thu, 7 Dec 2017 19:55:38 +0000 (11:55 -0800)
29 files changed:
services/IntegrationTests/src/main/java/org/collectionspace/services/IntegrationTests/xmlreplay/XmlReplay.java
services/IntegrationTests/src/main/java/org/collectionspace/services/IntegrationTests/xmlreplay/XmlReplayTransport.java
services/IntegrationTests/src/test/resources/test-data/xmlreplay/security.xml
services/IntegrationTests/src/test/resources/test-data/xmlreplay/security/3-role-test-cm.xml
services/IntegrationTests/src/test/resources/test-data/xmlreplay/security/4-role-intern.xml
services/account/client/src/test/java/org/collectionspace/services/account/client/test/AccountRoleServiceTest.java
services/authorization-mgt/client/pom.xml
services/authorization-mgt/client/src/main/java/org/collectionspace/services/client/PermissionClient.java
services/authorization-mgt/client/src/main/java/org/collectionspace/services/client/PermissionProxy.java
services/authorization-mgt/client/src/main/java/org/collectionspace/services/client/PermissionRoleFactory.java
services/authorization-mgt/client/src/main/java/org/collectionspace/services/client/RoleFactory.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/service/src/main/java/org/collectionspace/services/authorization/storage/RoleDocumentHandler.java
services/authorization/jaxb/src/main/resources/roles.xsd
services/client/src/main/java/org/collectionspace/services/client/test/BaseServiceTest.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/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/storage/StorageClient.java
services/common/src/main/java/org/collectionspace/services/common/storage/TransactionContext.java [new file with mode: 0644]
services/common/src/main/java/org/collectionspace/services/common/storage/jpa/JPATransactionContext.java [new file with mode: 0644]
services/common/src/main/java/org/collectionspace/services/common/storage/jpa/JPATransactionException.java [new file with mode: 0644]
services/common/src/main/java/org/collectionspace/services/common/storage/jpa/JpaStorageClientImpl.java
services/security/client/src/test/java/org/collectionspace/services/security/client/test/AuthorizationServiceTest.java
services/security/client/src/test/java/org/collectionspace/services/security/client/test/MultiTenancyTest.java

index 76217e33b4a1b79fb01991268948acb005acadb4..865ffd49ae0f29fff7ae7a2048ccb93e212ab92b 100644 (file)
@@ -656,8 +656,8 @@ public class XmlReplay {
                         fullURL = fixupFullURL(fullURL, protoHostPort, uri);
                     } else if (method.equalsIgnoreCase("DELETE")){
                         String fromTestID = testNode.valueOf("fromTestID");
-                        ServiceResult pr = serviceResultsMap.get(fromTestID);
-                        if (pr!=null){
+                        ServiceResult pr = Tools.notBlank(fromTestID) ? serviceResultsMap.get(fromTestID) : null;
+                        if (pr != null) {
                             serviceResult = XmlReplayTransport.doDELETE(pr.deleteURL, authForTest, testIDLabel, fromTestID);
                             serviceResult.fromTestID = fromTestID;
                             if (expectedCodes.size()>0){
index efd9cbed4a51cb33805774fa60f1d85f67c53b66..31dd26038b0585eeeee931ed5e4aba4c9669ed3d 100644 (file)
@@ -110,7 +110,9 @@ public class XmlReplayTransport {
         deleteMethod.setRequestHeader("Accept", "multipart/mixed");
         deleteMethod.addRequestHeader("Accept", "application/xml");
         deleteMethod.setRequestHeader("Authorization", formatAuth(authForTest));
-        deleteMethod.setRequestHeader("X-XmlReplay-fromTestID", fromTestID);
+        if (Tools.notBlank(fromTestID)) {
+               deleteMethod.setRequestHeader("X-XmlReplay-fromTestID", fromTestID);
+        }
         int statusCode1 = 0;
         String res = "";
         try {
index 013d72b5ce73aebe3ba683e8da203080790a8262..5946eca88a0d24460ee3c4129d5888c2a06386f8 100644 (file)
             <filename>dimension/1.xml</filename>
         </test>
         <test ID="dimensionBigbirdPUTAfterPermrolesDeleted">
-            <expectedCodes>404</expectedCodes>
+            <expectedCodes>403</expectedCodes>
             <method>PUT</method>
             <uri>/cspace-services/dimensions/${dimension1.CSID}</uri>
             <filename>dimension/2-put.xml</filename>
index 8575b3a78da43cdadb1984e3e5b4eb00f0a0ca41..ccd7034d6c415c02fde1683a624f0a902bd98130 100644 (file)
@@ -1,6 +1,29 @@
 <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
-<ns2:role
-xmlns:ns2="http://collectionspace.org/services/authorization">
-  <roleName>ROLE_TEST_CM</roleName>
-  <description>role for ROLE_TEST_CM</description>
+<ns2:role xmlns:ns2="http://collectionspace.org/services/authorization">
+    <roleName>ROLE_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>
+        <resourceName>vocabularies</resourceName>
+        <actionGroup>CRUL</actionGroup>
+    </permission>
+    <permission>
+        <permRelationshipId>4382</permRelationshipId>
+        <permissionId>1-groups-CRUL</permissionId>
+        <resourceName>groups</resourceName>
+        <actionGroup>CRUL</actionGroup>
+    </permission>
 </ns2:role>
index d74115ed29a66e7b322fd43e84940c80db184b44..b16fc0503ac3b8dd51e28ab9f8c775ba3c2ec8c5 100644 (file)
@@ -1,5 +1,17 @@
 <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
 <ns2:role xmlns:ns2="http://collectionspace.org/services/authorization">
-  <roleName>ROLE_TEST_INTERN</roleName>
-  <description>role for ROLE_TEST_INTERN</description>
+    <roleName>ROLE_TEST_INTERN</roleName>
+    <description>role for ROLE_TEST_INTERN</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>
 </ns2:role>
index 2fba8111e55007cad2c46ab5ffc0822c16580e36..a6b7b8a2f39e91fdb0c183564b7c6ae0c5222d46 100644 (file)
@@ -573,7 +573,7 @@ public class AccountRoleServiceTest extends AbstractServiceTestImpl<AccountRole,
         RoleClient roleClient = new RoleClient();
         Role role = RoleFactory.createRoleInstance(roleName,
                        roleName, //the display name
-                "role for " + roleName, true);
+                "role for " + roleName, true, RoleFactory.EMPTY_PERMVALUE_LIST);
         setupCreate();
         Response res = roleClient.create(role);
         try {
index 781c576dd0769761f94699cae4869ab636d1760a..58d3f70f98b7516e059db88f94f9585640bee055 100644 (file)
             <artifactId>org.collectionspace.services.authorization.jaxb</artifactId>
             <version>${project.version}</version>
         </dependency>
+        <dependency>
+            <groupId>org.collectionspace.services</groupId>
+            <artifactId>org.collectionspace.services.hyperjaxb</artifactId>
+            <version>${project.version}</version>
+        </dependency>
         <dependency>
             <groupId>org.collectionspace.services</groupId>
             <artifactId>org.collectionspace.services.client</artifactId>
index 9ff74c0ec9d780a4e4dab170ac9c747c93f2ef10..49d4f627bfde58d754692f9479eda93dfdf3ab32 100644 (file)
@@ -31,8 +31,8 @@ import java.util.HashMap;
 import java.util.List;
 
 import javax.ws.rs.core.Response;
-
 import org.apache.http.HttpStatus;
+
 import org.collectionspace.services.authorization.perms.ActionType;
 import org.collectionspace.services.authorization.perms.Permission;
 import org.collectionspace.services.authorization.perms.PermissionAction;
@@ -96,7 +96,6 @@ public class PermissionClient extends AbstractServiceClientImpl<PermissionsList,
 
     public Response readSearchList(String resourceName) {
         return getProxy().readSearchList(resourceName);
-
     }
 
     /**
@@ -108,6 +107,30 @@ public class PermissionClient extends AbstractServiceClientImpl<PermissionsList,
     public Response read(String csid) {
         return getProxy().read(csid);
     }
+    
+    /*
+     * We expect a single result.  An resourceName/actionGroup tuple should uniquely identify the permission resource
+     */
+    public Permission read(String resourceName, String actionGroup) {
+       Permission result = null;
+       
+       Response res = getProxy().read(resourceName, actionGroup);
+       try {
+               if (res != null && res.getStatus() == Response.Status.OK.getStatusCode()) {
+                       PermissionsList permsListElement = res.readEntity(PermissionsList.class);
+                       if (permsListElement != null && permsListElement.getPermission() != null) {
+                               List<Permission> permsList = permsListElement.getPermission();
+                               if (permsList.size() == 1) {
+                                       result = permsList.get(0);
+                               }
+                       }
+               }
+       } finally {
+               res.close();
+       }
+       
+       return result;
+    }
 
     /**
      * @param permission
index 1341263dfad3de730b3fd4c2b274194796a12e39..a67f3270e10aeb2ea8c5f8862dd4974ab7219861 100644 (file)
@@ -47,7 +47,8 @@ import org.collectionspace.services.authorization.perms.PermissionsList;
 @Consumes({"application/xml"})
 public interface PermissionProxy extends CollectionSpaceProxy<PermissionsList> {
 
-    @GET
+    @Override
+       @GET
     @Produces({"application/xml"})
     Response readList();
 
@@ -62,7 +63,11 @@ public interface PermissionProxy extends CollectionSpaceProxy<PermissionsList> {
     @GET
     @Path("/{csid}")
     Response read(@PathParam("csid") String csid);
-
+    
+    //(R)read
+    @GET
+    Response read(@QueryParam("res") String resourceName, @QueryParam("actGrp") String actionGroup);
+    
     //(U)pdate
     @PUT
     @Path("/{csid}")
index 94563b5f4317c3ee6efada50445ab1a22e3a38fd..a04c5fe284255ba6c94480163b1d754b518512a7 100644 (file)
@@ -29,6 +29,7 @@ import java.util.List;
 import org.collectionspace.services.authorization.PermissionRole;
 import org.collectionspace.services.authorization.PermissionValue;
 import org.collectionspace.services.authorization.RoleValue;
+import org.collectionspace.services.authorization.SubjectType;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -84,18 +85,28 @@ public class PermissionRoleFactory {
             boolean useRoleId) {
 
         PermissionRole permRole = new PermissionRole();
-        //service consume is not required to provide subject as it is determined
-        //from URI used
-//        permRole.setSubject(SubjectType.ROLE);
         if (useRoleId) {
             ArrayList<RoleValue> rvs = new ArrayList<RoleValue>();
             rvs.add(rv);
             permRole.setRole(rvs);
         }
+        
         if (usePermId) {
             permRole.setPermission(pvs);
         }
 
         return permRole;
     }
+    
+    public static PermissionRole createPermissionRoleInstance(SubjectType subjectType,
+               RoleValue rv,
+            List<PermissionValue> pvs,
+            boolean usePermId,
+            boolean useRoleId) {
+
+        PermissionRole permRole = createPermissionRoleInstance(rv, pvs, usePermId, useRoleId);
+        permRole.setSubject(subjectType);
+        return permRole;
+    }
+    
 }
index 05a902886586fef297bf7fccddc80b49db831271..d5f912823f66897b7b87d3eeb32e066d92198f49 100644 (file)
 package org.collectionspace.services.client;
 
 
+import java.util.List;
+
+import org.collectionspace.services.authorization.PermissionValue;
 import org.collectionspace.services.authorization.Role;
+import org.collectionspace.services.authorization.RoleValue;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -61,6 +65,9 @@ import org.slf4j.LoggerFactory;
 public class RoleFactory {
 
     static private final Logger logger = LoggerFactory.getLogger(RoleFactory.class);
+    
+       public static final List<PermissionValue> EMPTY_PERMVALUE_LIST = null;
+
     /**
      * create role instance
      * @param roleName
@@ -71,7 +78,8 @@ public class RoleFactory {
     public static Role createRoleInstance(String roleName,
                String displayName,
             String description,
-            boolean useRoleName) {
+            boolean useRoleName,
+            List<PermissionValue> permValueList) {
 
         Role role = new Role();
         if (useRoleName == true) {
@@ -79,7 +87,20 @@ public class RoleFactory {
         }
         role.setDisplayName(displayName);
         role.setDescription(description);
+        role.setPermission(permValueList);
+        
         return role;
 
     }
+    
+    public static RoleValue createRoleValueInstance(Role role) {
+       RoleValue result = new RoleValue();
+       
+       result.setDisplayName(role.getDisplayName());
+       result.setRoleId(role.getCsid());
+       result.setRoleName(role.getRoleName());
+       result.setTenantId(role.getTenantId());
+       
+       return result;
+    }
 }
index b2a81d262427abd4e57ba388909a9608e4bc93e7..d0c8f260a5595c12c94b41283c9e69379406abc0 100644 (file)
@@ -650,7 +650,7 @@ public class PermissionRoleServiceTest extends AbstractServiceTestImpl<Permissio
 
         Role role = RoleFactory.createRoleInstance(roleName,
                        roleName, //the display name
-                "role for " + roleName, true);
+                "role for " + roleName, true, RoleFactory.EMPTY_PERMVALUE_LIST);
         Response res = null;
         String id = null;
         try {
index 7ed710e0417f323db7c8f02fc1faaa3cada8486e..b92fc8a3ec83655c660318cf2acc14553069fb46 100644 (file)
@@ -668,7 +668,7 @@ public class RolePermissionServiceTest extends AbstractServiceTestImpl<Permissio
 
         Role role = RoleFactory.createRoleInstance(roleName,
                        roleName, //the display name
-                "role for " + roleName, true);
+                "role for " + roleName, true, RoleFactory.EMPTY_PERMVALUE_LIST);
         role.setRoleGroup("something");
         Response res = null;
         String id = null;
index 5d37e19cdb83a99621ff33e527f2c69dd708fd2e..b6a9b0c6f941e6ad289995231b712bd9237b4e4c 100644 (file)
  */
 package org.collectionspace.services.authorization.client.test;
 
-//import java.util.ArrayList;
-//import java.util.List;
+import java.util.ArrayList;
+import java.util.List;
 import java.util.Random;
-
 import javax.ws.rs.core.Response;
 
 import org.collectionspace.services.client.CollectionSpaceClient;
+import org.collectionspace.services.client.PermissionClient;
 import org.collectionspace.services.client.RoleClient;
+import org.collectionspace.services.authorization.PermissionValue;
 import org.collectionspace.services.authorization.Role;
 import org.collectionspace.services.authorization.RolesList;
+import org.collectionspace.services.authorization.perms.Permission;
 import org.collectionspace.services.client.RoleFactory;
 import org.collectionspace.services.client.test.AbstractServiceTestImpl;
+
 import org.testng.Assert;
+import org.testng.annotations.AfterClass;
+import org.testng.annotations.BeforeClass;
 import org.testng.annotations.Test;
+
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -54,6 +60,14 @@ 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_ACTIONGROUP = "RL";
+       private static final String PERM_2_RL_RESOURCE = "ROLE_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_ACTIONGROUP = "CRUDL";
+
 
     // Instance variables specific to this test.
     /** The known resource id. */
@@ -61,7 +75,10 @@ public class RoleServiceTest extends AbstractServiceTestImpl<RolesList, Role, Ro
     private String knownRoleDisplayName = "ROLE_DISPLAYNAME_USERS_MOCK-1";
     private String verifyResourceId = null;
     private String verifyRoleName = "collections_manager_mock-1";
-//    private List<String> allResourceIdsCreated = new ArrayList<String>();
+    //
+    // Permission values
+    //
+    private List<PermissionValue> permissionValues = RoleFactory.EMPTY_PERMVALUE_LIST;
 
     @Override
     public String getServiceName() { 
@@ -91,9 +108,87 @@ public class RoleServiceTest extends AbstractServiceTestImpl<RolesList, Role, Ro
        @Override
        protected CollectionSpaceClient getClientInstance(String clientPropertiesFilename) throws Exception {
         return new RoleClient(clientPropertiesFilename);
-       }       
+       }
+       
+    /**
+     * Seed data.
+     * @throws Exception 
+     */
+    @BeforeClass(alwaysRun = true)
+    public void seedData() throws Exception {
+       permissionValues = new ArrayList<PermissionValue>();
+       permissionValues.add(createPermissionValueInstance(PERM_1_RL_RESOURCE, PERM_1_RL_ACTIONGROUP));
+       permissionValues.add(createPermissionValueInstance(PERM_2_RL_RESOURCE, PERM_2_RL_ACTIONGROUP));
+       permissionValues.add(createPermissionValueInstance(PERM_3_RL_RESOURCE, PERM_3_RL_ACTIONGROUP));
+    }
     
-    /* (non-Javadoc)
+    private PermissionValue createPermissionValueInstance(String resource, String actionGroup) {
+               PermissionValue permValue = new PermissionValue();
+               permValue.setResourceName(resource);
+               permValue.setActionGroup(actionGroup);
+               return permValue;
+       }
+
+       /**
+     * Clean up.
+     * @throws Exception 
+     */
+    @AfterClass(alwaysRun = true)
+    @Override
+    public void cleanUp() throws Exception {
+        String noTest = System.getProperty("noTestCleanup");
+        if (Boolean.TRUE.toString().equalsIgnoreCase(noTest)) {
+            if (logger.isDebugEnabled()) {
+                logger.debug("Skipping Cleanup phase ...");
+            }
+            return;
+        }
+        if (logger.isDebugEnabled()) {
+            logger.debug("Cleaning up temporary resources created for testing ...");
+        }
+        //
+        // Delete the permissions we indirectly created with when we created via the "createRoleWithPerms()" test 
+        //
+        for (PermissionValue pv : permissionValues) {
+            deletePermission(pv);
+        }
+        
+        //
+        // Call our parent cleanup method.
+        //
+        super.cleanUp();
+    }
+    
+
+    /*
+     * Use a resource name and action group value to find and delete a permission record/resource.
+     */
+    private void deletePermission(PermissionValue permissionValue) throws Exception {
+       int statusCode = Response.Status.OK.getStatusCode();
+        PermissionClient client = new PermissionClient();
+        Permission permission = client.read(permissionValue.getResourceName(), permissionValue.getActionGroup());
+       if (permission != null) {
+            Response res = client.delete(permission.getCsid());
+            try {
+               statusCode = res.getStatus();
+            } finally {
+               res.close();
+            }
+       } else {
+               //
+               // Something bad happened.
+               //
+               statusCode = Response.Status.BAD_REQUEST.getStatusCode();               
+       }
+       
+       if (statusCode != Response.Status.OK.getStatusCode()) {
+               String msg = String.format("Could not delete test Permission record: resource name='%s', actionGroup='%s'.",
+                               permissionValue.getResourceName(), permissionValue.getActionGroup());
+               logger.error(msg);              
+       }
+       }
+
+       /* (non-Javadoc)
      * @see org.collectionspace.services.client.test.AbstractServiceTestImpl#readPaginatedList(java.lang.String)
      */
     @Override
@@ -116,7 +211,7 @@ public class RoleServiceTest extends AbstractServiceTestImpl<RolesList, Role, Ro
         // Submit the request to the service and store the response.
         RoleClient client = new RoleClient();
         Role role = createRoleInstance(knownRoleName, "All users are required to be in this role",
-                true);
+                true, RoleFactory.EMPTY_PERMVALUE_LIST);
         Response res = client.create(role);
         try {
                int statusCode = res.getStatus();
@@ -145,6 +240,81 @@ public class RoleServiceTest extends AbstractServiceTestImpl<RolesList, Role, Ro
         }
     }
     
+    @Test(dataProvider = "testName", 
+               dependsOnMethods = {"CRUDTests"})
+    public void createRoleWithPerms(String testName) throws Exception {
+        // Perform setup, such as initializing the type of service request
+        // (e.g. CREATE, DELETE), its valid and expected status codes, and
+        // its associated HTTP method name (e.g. POST, DELETE).
+        setupCreate();
+
+        // Submit the request to the service and store the response.
+        RoleClient client = new RoleClient();
+        Role role = createRoleInstance("CREATE_ROLE_WITH_PERMS" + System.currentTimeMillis(), "A role created with perms.",
+                true, permissionValues);
+        Response res = client.create(role);
+        try {
+               int statusCode = res.getStatus();
+               // Check the status code of the response: does it match
+               // the expected response(s)?
+               //
+               // Specifically:
+               // Does it fall within the set of valid status codes?
+               // Does it exactly match the expected status code?
+               if (logger.isDebugEnabled()) {
+                   logger.debug(testName + ": status = " + statusCode);
+               }
+               Assert.assertTrue(testRequestType.isValidStatusCode(statusCode),
+                       invalidStatusCodeMessage(testRequestType, statusCode));
+               Assert.assertEquals(statusCode, testExpectedStatusCode);
+       
+               // Store the ID returned from this create operation
+               // for additional tests below.
+               String csid = extractId(res);
+               allResourceIdsCreated.add(csid);
+               if (logger.isDebugEnabled()) {
+                   logger.debug(testName + ": role with perms ID=" + csid);
+               }
+        } finally {
+               res.close();
+        }
+    }    
+    
+    @Test(dataProvider = "testName", 
+               dependsOnMethods = {"CRUDTests"})    
+    public void createDupliateRole(String testName) throws Exception {
+        // Perform setup, such as initializing the type of service request
+        // (e.g. CREATE, DELETE), its valid and expected status codes, and
+        // its associated HTTP method name (e.g. POST, DELETE).
+        setupDuplicate();
+
+        // Submit the request to the service and store the response.
+        RoleClient client = new RoleClient();
+        Role role = createRoleInstance(knownRoleName, "This is a duplicate of an existing role.",
+                true, RoleFactory.EMPTY_PERMVALUE_LIST);
+        Response res = client.create(role);
+        try {
+               int statusCode = res.getStatus();
+               if (statusCode == 201) {
+                       // We expected NOT to create a new role, so if we did then we need to keep track of it and eventually delete it.
+                       String duplicateRole = extractId(res);
+                       allResourceIdsCreated.add(duplicateRole);
+               }
+               // Check the status code of the response: does it match
+               // the expected response(s)?
+               //
+               // Specifically:
+               // Does it exactly match the expected status code?
+               if (logger.isDebugEnabled()) {
+                   logger.debug(testName + ": status = " + statusCode);
+               }
+               Assert.assertEquals(statusCode, testExpectedStatusCode);
+       
+        } finally {            
+               res.close();
+        }
+    }
+    
     @Test(dataProvider = "testName", 
                dependsOnMethods = {"CRUDTests"})
     public void createWithDisplayname(String testName) throws Exception {
@@ -880,22 +1050,31 @@ public class RoleServiceTest extends AbstractServiceTestImpl<RolesList, Role, Ro
      */
     public Role createRoleInstance(String roleName,
             String description,
-            boolean useRoleName) {
+            boolean useRoleName,
+            List<PermissionValue> permValueList) {
 
         Role role = RoleFactory.createRoleInstance(roleName,
                        roleName, //the display name
                        description,
-                useRoleName);
+                useRoleName,
+                permValueList);
+        
         if (logger.isDebugEnabled()) {
             logger.debug("to be created, role");
             org.collectionspace.services.authorization.ObjectFactory objectFactory = new org.collectionspace.services.authorization.ObjectFactory();            
             logger.debug(objectAsXmlString(objectFactory.createRole(role),
                     Role.class));
         }
+        
         return role;
-
     }
-
+    
+    public Role createRoleInstance(String roleName,
+            String description,
+            boolean useRoleName) {
+       return this.createRoleInstance(roleName, description, useRoleName, RoleFactory.EMPTY_PERMVALUE_LIST);
+    }
+    
     /**
      * Prints the list.
      *
index a7f2b2fc1665856509edff904db2f0834ad2806f..13637c6520479bfc52b9b749596fbee224bf5a7e 100644 (file)
@@ -27,10 +27,18 @@ import java.util.ArrayList;
 import java.util.List;
 import java.util.UUID;
 
+import org.collectionspace.services.authorization.PermissionRole;
+import org.collectionspace.services.authorization.PermissionRoleSubResource;
+import org.collectionspace.services.authorization.PermissionValue;
 import org.collectionspace.services.authorization.Role;
+import org.collectionspace.services.authorization.RoleValue;
 import org.collectionspace.services.authorization.RolesList;
+import org.collectionspace.services.authorization.SubjectType;
 
+import org.collectionspace.services.client.PermissionRoleFactory;
 import org.collectionspace.services.client.RoleClient;
+import org.collectionspace.services.client.RoleFactory;
+
 import org.collectionspace.services.common.document.BadRequestException;
 import org.collectionspace.services.common.document.DocumentFilter;
 import org.collectionspace.services.common.document.DocumentWrapper;
@@ -73,6 +81,22 @@ public class RoleDocumentHandler
         role.setMetadataProtection(null);
         role.setPermsProtection(null);
     }
+    
+    @Override
+    public void completeCreate(DocumentWrapper<Role> wrapDoc) throws Exception {
+       Role role = wrapDoc.getWrappedObject();
+       List<PermissionValue> permValueList = role.getPermission();
+       if (permValueList != null && permValueList.size() > 0) {
+               // 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);
+               PermissionRole permRole = PermissionRoleFactory.createPermissionRoleInstance(SubjectType.PERMISSION, roleValue,
+                               permValueList, true, true);
+            PermissionRoleSubResource subResource =
+                    new PermissionRoleSubResource(PermissionRoleSubResource.ROLE_PERMROLE_SERVICE);
+            String permrolecsid = subResource.createPermissionRole(permRole, SubjectType.PERMISSION);
+       }
+    }
 
     @Override
     public void handleUpdate(DocumentWrapper<Role> wrapDoc) throws Exception {
index e9d9492abfc3ca9cb45b1896638bf1f4270c63f5..ddeffd3d37ba944bb8295e7437cd9a4aad35cc2e 100644 (file)
@@ -13,6 +13,8 @@
                jaxb:extensionBindingPrefixes="hj orm xjc"
     >
 
+    <xs:include schemaLocation="authorization_common.xsd"/>
+
     <!--
     see http://weblogs.java.net/blog/2006/03/03/why-does-jaxb-put-xmlrootelement-sometimes-not-always
     for more details behind xjc:simple
                     </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="permsProtection" type="xs:string" minOccurs="0" maxOccurs="1">
-                                                               <xs:annotation>
-                                                                               <xs:appinfo>
-                                                                                               <hj:basic>
-                                                                                                               <orm:column name="perms_protection"  nullable="true"/>
-                                                                                               </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="permsProtection" type="xs:string" minOccurs="0" maxOccurs="1">
+                                       <xs:annotation>
+                                                       <xs:appinfo>
+                                                                       <hj:basic>
+                                                                                       <orm:column name="perms_protection"  nullable="true"/>
+                                                                       </hj:basic>
+                                                       </xs:appinfo>
+                                       </xs:annotation>
+                       </xs:element>
             <xs:element name="createdAt" type="xs:dateTime">
                 <xs:annotation>
                     <xs:appinfo>
                     </xs:appinfo>
                 </xs:annotation>
             </xs:element>
+            <xs:element name="permission" type="ns:permission_value" minOccurs="1" maxOccurs="unbounded"/>
         </xs:sequence>
         <xs:attribute name="csid" type="xs:string">
             <xs:annotation>
                 </xs:appinfo>
             </xs:annotation>
         </xs:attribute>
+        
     </xs:complexType>
 </xs:schema>
 
index e4b41f4bb71be1feeb0b215029ec7f3c0f4ba441..035f164cf592fba85d87e293504ffb3f8d90e1f5 100644 (file)
@@ -290,6 +290,15 @@ public abstract class BaseServiceTest<CLT> {
         testSetup(testExpectedStatusCode, testRequestType);
     }
     
+    /**
+     * Sets up create duplicate request.
+     */
+    protected void setupDuplicate() {
+        testExpectedStatusCode = STATUS_BAD_REQUEST;
+        testRequestType = ServiceRequestType.CREATE;
+        testSetup(testExpectedStatusCode, testRequestType);
+    }    
+    
     /**
      * Initializes setup values for a given test.
      *
index c394a0eee6d90beb23c3001e5e3ff9105e7a309d..990c732013fb78e2155f12155f113d58dcb4da21 100644 (file)
@@ -26,19 +26,24 @@ package org.collectionspace.services.authorization.storage;
 import java.util.ArrayList;
 import java.util.List;
 
+import javax.persistence.EntityManager;
+import javax.persistence.EntityManagerFactory;
+import javax.persistence.NoResultException;
+
 import org.collectionspace.services.authorization.PermissionRole;
 import org.collectionspace.services.authorization.PermissionRoleRel;
 import org.collectionspace.services.authorization.PermissionValue;
 import org.collectionspace.services.authorization.PermissionsRolesList;
 import org.collectionspace.services.authorization.RoleValue;
 import org.collectionspace.services.authorization.SubjectType;
-
+import org.collectionspace.services.authorization.perms.Permission;
 import org.collectionspace.services.common.authorization_mgt.AuthorizationRoleRel;
 import org.collectionspace.services.common.authorization_mgt.PermissionRoleUtil;
 import org.collectionspace.services.common.document.DocumentFilter;
 import org.collectionspace.services.common.document.DocumentWrapper;
 import org.collectionspace.services.common.storage.jpa.JpaDocumentFilter;
 import org.collectionspace.services.common.storage.jpa.JpaDocumentHandler;
+import org.collectionspace.services.common.storage.jpa.JpaStorageUtils;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -79,6 +84,69 @@ public class PermissionRoleDocumentHandler
     @Override
     public void handleCreate(DocumentWrapper<List<PermissionRoleRel>> wrapDoc) throws Exception {
         fillCommonPart(getCommonPart(), wrapDoc);
+        filterOutExisting(wrapDoc);
+    }
+    
+    private boolean permRoleRelExists(EntityManager em, PermissionRoleRel permRoleRel) {
+       boolean result = false;
+       
+       PermissionRoleRel queryResult = null;
+       try {
+               queryResult = (PermissionRoleRel) JpaStorageUtils.getEntityByDualKeys(em, 
+                               PermissionRoleRel.class.getName(),
+                               PermissionStorageConstants.PERMREL_ROLE_ID, permRoleRel.getRoleId(), 
+                               PermissionStorageConstants.PERMREL_PERM_ID, permRoleRel.getPermissionId());
+       } catch (NoResultException e) {
+               // Ignore exception.  Just means the permission hasn't been stored/persisted yet.
+       }
+       
+       if (queryResult != null) {
+               result = true;
+       }
+       
+       return result;
+    }
+    
+    /**
+     * Find the existing (already persisted) 
+     * @param wrapDoc
+     * @throws Exception
+     */
+    private void filterOutExisting(DocumentWrapper<List<PermissionRoleRel>> wrapDoc) throws Exception {
+       List<PermissionRoleRel> permRoleRelList = wrapDoc.getWrappedObject();
+       
+        EntityManagerFactory emf = null;
+        EntityManager em = null;
+        try {
+            emf = JpaStorageUtils.getEntityManagerFactory(JpaStorageUtils.CS_PERSISTENCE_UNIT);
+            em = emf.createEntityManager();
+            em.getTransaction().begin();
+            
+            for (PermissionRoleRel permRoleRel : permRoleRelList) {
+               if (permRoleRelExists(em, permRoleRel) == true) {
+                       //
+                       // Remove the item from the list since it already exists
+                       //
+                       permRoleRelList.remove(permRoleRel);
+               }
+            }
+            
+            em.getTransaction().commit();
+               em.close();            
+        } catch (Exception e) {
+            if (em != null && em.getTransaction().isActive()) {
+                em.getTransaction().rollback();
+            }
+            if (logger.isDebugEnabled()) {
+                logger.debug("Caught exception ", e);
+            }
+            throw e;
+        } finally {
+            if (em != null) {
+                JpaStorageUtils.releaseEntityManagerFactory(emf);
+            }
+        }
+
     }
 
     /* (non-Javadoc)
index e3fc393f817da38a5b124db7c4b3ac66c8d4cab4..0924ff63daa1de2b4b75f97008c787fb5c62c12e 100644 (file)
@@ -36,5 +36,7 @@ public class PermissionStorageConstants {
 
     final public static String RESOURCE_NAME = "resourceName";
     final public static String ACTION_GROUP = "actionGroup";
+       public static final String PERMREL_ROLE_ID = "roleId";
+       public static final String PERMREL_PERM_ID = "permissionId";
 
 }
index 125662130217659081f769b676e91c4aa6b3448e..27c374cdbc2ed8f31d07b613a0ef12aaba9c6e2e 100644 (file)
@@ -244,6 +244,14 @@ public class PermissionRoleUtil {
                }
        }
        
+       //
+       // Since our permissionValue may not have been supplied by the client with an ID, we need
+       // to add it now.
+       //
+       if (permissionValue.getPermissionId() == null || permissionValue.getPermissionId().trim().isEmpty()) {
+               permissionValue.setPermissionId(permission.getCsid());
+       }
+       
        //
        // Create the permission-role to persist
        //
index 0b1a66a5bca571aad5ed1e33cc7135cd109eb920..a1c1ebd7c14231e22f4fc5401293d997d48dd916 100644 (file)
@@ -32,9 +32,14 @@ import org.collectionspace.services.common.ResourceMap;
 import org.collectionspace.services.common.ServiceMain;
 import org.collectionspace.services.common.config.ConfigUtils;
 import org.collectionspace.services.common.config.TenantBindingConfigReaderImpl;
+import org.collectionspace.services.common.document.TransactionException;
 import org.collectionspace.services.common.security.UnauthorizedException;
+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.config.service.ServiceBindingType;
 import org.collectionspace.services.config.tenant.TenantBindingType;
+
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -245,4 +250,71 @@ public class RemoteServiceContextImpl<IT, OT>
                // TODO Auto-generated method stub
                throw new RuntimeException("Unimplemented method.");
        }
+       
+       //
+       // Transaction management methods
+       //
+       
+       private TransactionContext getCurrentTransactionContext() {
+               return (TransactionContext) this.getProperty(StorageClient.SC_TRANSACTION_CONTEXT_KEY);
+       }
+
+       @Override
+       public void releaseConnection() throws TransactionException {
+               if (isTransactionContextShared() == true) {
+                       throw new TransactionException("Attempted to release a shared storage connection.  Only the originator can release the connection");
+               }
+               
+               TransactionContext transactionCtx = getCurrentTransactionContext();             
+               if (transactionCtx != null) {
+                       transactionCtx.close();
+               this.setProperty(StorageClient.SC_TRANSACTION_CONTEXT_KEY, null);
+               } else {
+                       throw new TransactionException("Attempted to release a non-existent storage connection.  Transaction context missing from service context.");
+               }
+       }
+
+       @Override
+       public TransactionContext openConnection() throws TransactionException {
+               TransactionContext result = getCurrentTransactionContext();
+               if (result != null) {
+                       throw new TransactionException("Attempted to open a new connection when a current connection is still part of the current service context.  The current connection must be closed with the releaseConnection() method.");
+               }
+
+               result = new JPATransactionContext(this);
+        this.setProperty(StorageClient.SC_TRANSACTION_CONTEXT_KEY, result);
+
+               return result;
+       }
+
+       @Override
+       public void setTransactionContext(TransactionContext transactionCtx) {
+               // TODO Auto-generated method stub
+               
+       }
+
+       /**
+        * Returns true if the TransactionContext is shared with another ServiceContext instance
+        * @throws TransactionException 
+        */
+       @Override
+       public boolean isTransactionContextShared() throws TransactionException {
+               boolean result = true;
+               
+               TransactionContext transactionCtx = getCurrentTransactionContext();
+               if (transactionCtx != null) {
+                       if (transactionCtx.getServiceContext() == this) {  // check to see if the service context used to create the connection is the same as the current service context
+                               result = false;
+                       }
+               } else {
+                       throw new TransactionException("Transaction context missing from service context.");
+               }
+               
+               return result;
+       }
+
+       @Override
+       public boolean hasActiveConnection() {
+               return getCurrentTransactionContext() != null;
+       }
 }
index 5027ac75ef369338702f2626ce873293f0abcb1e..ccdf5c3b8414e4f0184c46739f7ee982a16ee140 100644 (file)
@@ -34,8 +34,11 @@ import org.collectionspace.services.client.CollectionSpaceClient;
 import org.collectionspace.services.common.CollectionSpaceResource;
 import org.collectionspace.services.common.ResourceMap;
 import org.collectionspace.services.common.document.DocumentHandler;
+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;
@@ -406,6 +409,31 @@ public interface ServiceContext<IT, OT> {
         * @return The JAX-RS request information
         */
        Request getRequestInfo();
+       
+       /**
+        * 
+        */
+       public TransactionContext openConnection() throws TransactionException; // Only 1 active connection at a time
+       
+       /**
+        * 
+        */
+       public boolean hasActiveConnection();
+       
+       /**
+        * 
+        */
+       public void releaseConnection() throws TransactionException; // Assumes there's been a call to getConnection.
+       
+       /**
+        * 
+        */
+       public void setTransactionContext(TransactionContext transactionCtx); // For sharing a transaction context with another service context.
+       
+       /**
+        * 
+        */
+       public boolean isTransactionContextShared() throws TransactionException;
 }
 
 
index 6b9e51aaf81a6098fafcbbf4fdcf4e3c7ad07d8b..d39fbb9702bcc95175075fcd3b8e9c1842e593d5 100644 (file)
@@ -35,6 +35,8 @@ import org.collectionspace.services.lifecycle.TransitionDef;
  */
 public interface StorageClient {
 
+       public static final String SC_TRANSACTION_CONTEXT_KEY = "SC_ACTIVE_TRANSACTION_KEY";
+       
     /**
      * create entity in the persistence store
      * @param ctx service context under which this method is invoked
diff --git a/services/common/src/main/java/org/collectionspace/services/common/storage/TransactionContext.java b/services/common/src/main/java/org/collectionspace/services/common/storage/TransactionContext.java
new file mode 100644 (file)
index 0000000..41bf9a3
--- /dev/null
@@ -0,0 +1,25 @@
+package org.collectionspace.services.common.storage;
+
+import org.collectionspace.services.common.context.ServiceContext;
+import org.collectionspace.services.common.document.TransactionException;
+
+@SuppressWarnings("rawtypes")
+public abstract class TransactionContext {
+
+       protected ServiceContext ctx;
+       
+       public ServiceContext getServiceContext() {
+               // TODO Auto-generated method stub
+               return ctx;
+       }
+
+       abstract public void markForRollback();
+
+       abstract public void close() throws TransactionException;
+       
+       abstract public void beginTransaction() throws TransactionException;
+       
+       abstract public void commitTransaction() throws TransactionException;
+
+       abstract public boolean isTransactionActive() throws TransactionException;
+}
diff --git a/services/common/src/main/java/org/collectionspace/services/common/storage/jpa/JPATransactionContext.java b/services/common/src/main/java/org/collectionspace/services/common/storage/jpa/JPATransactionContext.java
new file mode 100644 (file)
index 0000000..6e747f3
--- /dev/null
@@ -0,0 +1,70 @@
+package org.collectionspace.services.common.storage.jpa;
+
+import javax.persistence.EntityManager;
+import javax.persistence.EntityManagerFactory;
+
+import org.collectionspace.services.common.context.ServiceContext;
+import org.collectionspace.services.common.document.TransactionException;
+import org.collectionspace.services.common.storage.TransactionContext;
+
+@SuppressWarnings("rawtypes")
+public class JPATransactionContext extends TransactionContext {
+       EntityManagerFactory emf;
+       EntityManager em;
+       
+       @SuppressWarnings("unused")
+       private JPATransactionContext() {
+               // Don't allow anyone to create an empty instance
+       }
+       
+       public JPATransactionContext(ServiceContext ctx) {
+        emf = JpaStorageUtils.getEntityManagerFactory();            
+        em = emf.createEntityManager();
+        this.ctx = ctx;
+       }
+
+       protected EntityManagerFactory getEntityManagerFactory() {
+               return emf;
+       }
+       
+       protected EntityManager getEntityManager() {
+               return em;
+       }
+       
+       @Override
+       public ServiceContext getServiceContext() {
+               return ctx;
+       }
+       
+       @Override
+       public void markForRollback() {
+               em.getTransaction().setRollbackOnly();
+       }
+       
+       @Override
+       public void close() throws TransactionException  {
+               if (em.getTransaction().isActive() == true && em.getTransaction().getRollbackOnly() == true) {
+                       em.getTransaction().rollback();
+       } 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.");
+       }
+       
+               em.close();
+        JpaStorageUtils.releaseEntityManagerFactory(emf);
+       }
+
+       @Override
+       public void beginTransaction() {
+        em.getTransaction().begin();    
+       }
+
+       @Override
+       public void commitTransaction() {
+        em.getTransaction().commit();
+       }
+
+       @Override
+       public boolean isTransactionActive() {
+               return em.getTransaction().isActive();
+       }
+}
diff --git a/services/common/src/main/java/org/collectionspace/services/common/storage/jpa/JPATransactionException.java b/services/common/src/main/java/org/collectionspace/services/common/storage/jpa/JPATransactionException.java
new file mode 100644 (file)
index 0000000..2b8e89d
--- /dev/null
@@ -0,0 +1,15 @@
+package org.collectionspace.services.common.storage.jpa;
+
+import org.collectionspace.services.common.document.TransactionException;
+
+public class JPATransactionException extends TransactionException {
+
+       /**
+        * 
+        */
+       private static final long serialVersionUID = 2018758347488796620L;
+
+    public JPATransactionException(String msg) {
+       super(msg);
+    }
+}
index 334ce0650666ed3fc184b90f4f193030b551852c..2e25788becea69538d9e296e84aa5fd753d552fd 100644 (file)
@@ -135,6 +135,7 @@ public class JpaStorageClientImpl implements StorageClient {
                    } catch (EntityExistsException ee) {
                        //
                        // 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.
                        //
                        entity = wrapDoc.getWrappedObject(); // the handler should have reset the wrapped transient object with the existing persisted entity we just found.
                    }
index f05b7d83019d0d2e6201dfeec04d04545f6521ca..dab407726495d3831102713a0ca2c9b5ec656aeb 100644 (file)
@@ -690,7 +690,7 @@ public class AuthorizationServiceTest extends BaseServiceTest<AbstractCommonList
                Role role = RoleFactory.createRoleInstance(roleName, roleName, // the
                                                                                                                                                // display
                                                                                                                                                // name
-                               "role for " + roleName, true);
+                               "role for " + roleName, true, RoleFactory.EMPTY_PERMVALUE_LIST);
                Response res = roleClient.create(role);
                try {
                        assertStatusCode(res, "CreateRole");
index e9b4efc2ce0caaeb20e3fe8420e97a22a09b5a62..017b7a83291b1a6d08dc9d9ec8c81f69f25d188a 100644 (file)
@@ -611,7 +611,7 @@ public class MultiTenancyTest extends BaseServiceTest<AbstractCommonList> {
         roleClient.setAuth(true, ui.userName, true, ui.password, true);
         Role role = RoleFactory.createRoleInstance(roleName,
                        roleName, //the display name
-                "role for " + roleName, true);
+                "role for " + roleName, true, RoleFactory.EMPTY_PERMVALUE_LIST);
         role.setTenantId(tenantId);
         Response res = roleClient.create(role);
         try {