]> git.aero2k.de Git - tmp/jakarta-migration.git/commitdiff
DRYD-861: Allow a user to retrieve own roles without having read perms on accounts...
authorRay Lee <ray.lee@lyrasis.org>
Tue, 2 Jun 2020 19:00:13 +0000 (15:00 -0400)
committerRay Lee <ray.lee@lyrasis.org>
Tue, 2 Jun 2020 19:00:13 +0000 (15:00 -0400)
services/account/service/src/main/java/org/collectionspace/services/account/AccountResource.java
services/authorization/service/src/main/java/org/collectionspace/services/authorization/AuthZ.java
services/common/src/main/java/org/collectionspace/services/common/security/SecurityInterceptor.java
services/common/src/main/java/org/collectionspace/services/common/security/SecurityUtils.java
services/common/src/main/java/org/collectionspace/services/common/storage/jpa/JpaStorageUtils.java

index 424e8095a503e041296fd794cf41742821db0b92..cd06e8bd4108c1b90742c4ff11220b8ae2dc7710 100644 (file)
@@ -133,9 +133,9 @@ public class AccountResource extends SecurityResourceBase<AccountsCommon, Accoun
     @Path("{csid}")
     public AccountsCommon getAccount(@Context UriInfo ui, @PathParam("csid") String csid) {
        AccountsCommon result = null;
-       
+
         result = (AccountsCommon)get(ui, csid, AccountsCommon.class);
-       
+
        return result;
     }
 
@@ -224,14 +224,14 @@ public class AccountResource extends SecurityResourceBase<AccountsCommon, Accoun
     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.
@@ -243,8 +243,8 @@ public class AccountResource extends SecurityResourceBase<AccountsCommon, Accoun
      *
      * @param ui
      * @return
-     * @throws  
-     * @throws IOException 
+     * @throws
+     * @throws IOException
      */
     @POST
     @Path(PROCESS_PASSWORD_RESET_PATH)
@@ -466,13 +466,13 @@ public class AccountResource extends SecurityResourceBase<AccountsCommon, Accoun
 
        return result;
     }
-    
+
        @DELETE
     @Path("{csid}")
     public Response deleteAccount(@Context UriInfo uriInfo, @PathParam("csid") String csid) {
         logger.debug("deleteAccount with csid=" + csid);
         ensureCSID(csid, ServiceMessages.DELETE_FAILED);
-        
+
         try {
                AccountsCommon account = (AccountsCommon)get(csid, AccountsCommon.class);
                //
@@ -512,7 +512,7 @@ public class AccountResource extends SecurityResourceBase<AccountsCommon, Accoun
             throw bigReThrow(e, ServiceMessages.DELETE_FAILED, csid);
         }
 
-        return Response.status(HttpResponseCodes.SC_OK).build();        
+        return Response.status(HttpResponseCodes.SC_OK).build();
     }
 
        @POST
@@ -529,7 +529,7 @@ public class AccountResource extends SecurityResourceBase<AccountsCommon, Accoun
         }
         logger.debug("createAccountRole with accCsid=" + accCsid);
         ensureCSID(accCsid, ServiceMessages.POST_FAILED+ "accountroles account ");
-        
+
         try {
                AccountsCommon account = (AccountsCommon)get(accCsid, AccountsCommon.class);
             // If marked as immutable, fail.
@@ -538,7 +538,7 @@ public class AccountResource extends SecurityResourceBase<AccountsCommon, Accoun
                        Response.status(Response.Status.FORBIDDEN).entity("Roles for Account: "+accCsid+" are immutable.").type("text/plain").build();
                 return response;
             }
-            
+
             ServiceContext<AccountsCommon, AccountsCommon> ctx = createServiceContext((AccountsCommon) null, AccountsCommon.class, uriInfo);
             ctx.openConnection();
             try {
@@ -576,29 +576,25 @@ public class AccountResource extends SecurityResourceBase<AccountsCommon, Accoun
             throw bigReThrow(e, ServiceMessages.GET_FAILED, accCsid);
         }
         checkResult(result, accCsid, ServiceMessages.GET_FAILED);
-        
+
         return result;
     }
 
     @GET
     @Path("{csid}/accountroles")
-    public AccountRole getAccountRole(
-            @PathParam("csid") String accCsid) {
+    public AccountRole getAccountRole(@PathParam("csid") String accCsid) {
         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(ctx, accCsid, SubjectType.ROLE);
+            result = JpaStorageUtils.getAccountRoles(accCsid);
         } catch (Exception e) {
             throw bigReThrow(e, ServiceMessages.GET_FAILED, accCsid);
         }
-        
         checkResult(result, accCsid, ServiceMessages.GET_FAILED);
+
         return result;
     }
 
@@ -606,7 +602,7 @@ public class AccountResource extends SecurityResourceBase<AccountsCommon, Accoun
     @Path("{csid}/accountperms")
     public AccountPermission getAccountPerm(@PathParam("csid") String accCsid) {
         logger.debug("getAccountPerm with accCsid=" + accCsid);
-        
+
         ensureCSID(accCsid, ServiceMessages.GET_FAILED+ "getAccountPerm account ");
         AccountPermission result = null;
         try {
@@ -615,14 +611,14 @@ public class AccountResource extends SecurityResourceBase<AccountsCommon, Accoun
             throw bigReThrow(e, ServiceMessages.GET_FAILED, accCsid);
         }
         checkResult(result, accCsid, ServiceMessages.GET_FAILED);
-        
+
         return result;
     }
 
        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
@@ -631,7 +627,7 @@ public class AccountResource extends SecurityResourceBase<AccountsCommon, Accoun
                        Response.status(Response.Status.FORBIDDEN).entity("Roles for Account: "+accCsid+" are immutable.").type("text/plain").build();
                 return response;
             }
-            
+
             ServiceContext<AccountsCommon, AccountsCommon> ctx = createServiceContext((AccountsCommon) null,
                     AccountsCommon.class, (UriInfo) null);
             ctx.openConnection();
@@ -646,17 +642,17 @@ public class AccountResource extends SecurityResourceBase<AccountsCommon, Accoun
         } catch (Exception e) {
             throw bigReThrow(e, ServiceMessages.DELETE_FAILED, accCsid);
         }
-        
+
         return Response.status(HttpResponseCodes.SC_OK).build();
     }
 
     @DELETE
     @Path("{csid}/accountroles")
     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 ");
-        
+
         try {
             // If marked as roles immutable, do not delete
                AccountsCommon account = (AccountsCommon)get(accCsid, AccountsCommon.class);
@@ -665,7 +661,7 @@ public class AccountResource extends SecurityResourceBase<AccountsCommon, Accoun
                        Response.status(Response.Status.FORBIDDEN).entity("Roles for Account: "+accCsid+" are immutable.").type("text/plain").build();
                 return response;
             }
-            
+
             ServiceContext<AccountsCommon, AccountsCommon> ctx = createServiceContext((AccountsCommon) null, AccountsCommon.class, uriInfo);
             ctx.openConnection();
             try {
@@ -679,7 +675,7 @@ public class AccountResource extends SecurityResourceBase<AccountsCommon, Accoun
         } catch (Exception e) {
             throw bigReThrow(e, ServiceMessages.DELETE_FAILED, accCsid);
         }
-        
+
         return Response.status(HttpResponseCodes.SC_OK).build();
 
     }
index 18fcffa3f0440d7b831a11b679d9406be463d90e..662d07494a011e533200f3b7535a00923c849e18 100644 (file)
@@ -59,17 +59,18 @@ public class AuthZ {
     private AuthZ() {
         setupProvider();
     }
-    
+
        //
        // URI paths that require special handling
        //
     public static final String REPORTS_MIME_OUTPUTS = "reports/mimetypes";
     public static final String ACCOUNT_PERMISSIONS = "accounts/*/accountperms";
+    public static final String ACCOUNT_ROLES = "accounts/*/accountroles";
     public static final String STRUCTURED_DATE_REQUEST = "structureddate";
     public static final String PASSWORD_RESET = "accounts/requestpasswordreset";
     public static final String PROCESS_PASSWORD_RESET = "accounts/processpasswordreset";
        public static final String REPORTS_INVOKE = "reports/*/invoke";
-       public static final String BATCH_INVOKE = "batch/*/invoke";     
+       public static final String BATCH_INVOKE = "batch/*/invoke";
 
     /**
      *
index de25f34412f17f01c91519b65dcb4b5424084a12..87aca18f764a6fc593958e75f12d09209b7e6df2 100644 (file)
@@ -77,14 +77,14 @@ import org.slf4j.LoggerFactory;
 @ServerInterceptor
 @Provider
 public class SecurityInterceptor implements PreProcessInterceptor, PostProcessInterceptor {
-       
+
        static {
                System.err.println("Static initialization of: " + SecurityInterceptor.class.getCanonicalName());
        }
 
        /** The Constant logger. */
        private static final Logger logger = LoggerFactory.getLogger(SecurityInterceptor.class);
-       
+
        private static final String SYSTEM_INFO = SystemInfoClient.SERVICE_NAME;
        private static final String NUXEO_ADMIN = null;
     //
@@ -96,11 +96,11 @@ public class SecurityInterceptor implements PreProcessInterceptor, PostProcessIn
     // Error messages
     //
     private static final String ERROR_NUXEO_LOGOUT = "Attempt to logout when Nuxeo login context was null.";
-    private static final String ERROR_UNBALANCED_LOGINS = "The number of Logins vs Logouts to the Nuxeo framework was unbalanced.";    
-           
+    private static final String ERROR_UNBALANCED_LOGINS = "The number of Logins vs Logouts to the Nuxeo framework was unbalanced.";
+
     private boolean isAnonymousRequest(HttpRequest request, ResourceMethodInvoker resourceMethodInvoker) { // see C:\dev\src\cspace\services\services\JaxRsServiceProvider\src\main\webapp\WEB-INF\applicationContext-security.xml
        boolean result = false;
-       
+
                String resName = SecurityUtils.getResourceName(request.getUri()).toLowerCase();
                switch (resName) {
                        case AuthZ.PASSWORD_RESET:
@@ -108,7 +108,7 @@ public class SecurityInterceptor implements PreProcessInterceptor, PostProcessIn
                        case SYSTEM_INFO:
                                return true;
                }
-                       
+
                Class<?> resourceClass = resourceMethodInvoker.getResourceClass();
                try {
                        CollectionSpaceResource resourceInstance = (CollectionSpaceResource)resourceClass.newInstance();
@@ -121,32 +121,34 @@ public class SecurityInterceptor implements PreProcessInterceptor, PostProcessIn
 
        return result;
     }
-        
-    /*
-     * Check to see if the resource required authorization to access
-     * 
-     */
-    private boolean requiresAuthorization(String resName) {
-       boolean result = true;
-               //
-       // ACCOUNT_PERMISSIONS: All active users are allowed to see the *their* (we enforce this) current list of permissions.  If this is not
-               // the request, then we'll do a full AuthZ check.
-       //
-       // STRUCTURED_DATE_REQUEST: All user can request the parsing of a structured date string.
-       //
-       switch (resName) {
-               case AuthZ.STRUCTURED_DATE_REQUEST:
-               case AuthZ.ACCOUNT_PERMISSIONS:
-               case AuthZ.REPORTS_MIME_OUTPUTS:
-                       result = false;
-                       break;
-               default:
-                       result = true;
-       }
-       
-       return result;
-    }
-    
+
+               /*
+                       * Check to see if the resource required authorization to access
+                       *
+                       */
+               private boolean requiresAuthorization(String resName) {
+                       boolean result = true;
+                       //
+                       // ACCOUNT_PERMISSIONS, ACCOUNT_ROLES: All active users are allowed to see the *their*
+                       // (we enforce this) current list of permissions and roles.  If this is not the request, then
+                       // we'll do a full AuthZ check.
+                       //
+                       // STRUCTURED_DATE_REQUEST: All user can request the parsing of a structured date string.
+                       //
+                       switch (resName) {
+                               case AuthZ.STRUCTURED_DATE_REQUEST:
+                               case AuthZ.ACCOUNT_PERMISSIONS:
+                               case AuthZ.ACCOUNT_ROLES:
+                               case AuthZ.REPORTS_MIME_OUTPUTS:
+                                       result = false;
+                                       break;
+                               default:
+                                       result = true;
+                       }
+
+                       return result;
+               }
+
        /* (non-Javadoc)
         * @see org.jboss.resteasy.spi.interception.PreProcessInterceptor#preProcess(org.jboss.resteasy.spi.HttpRequest, org.jboss.resteasy.core.ResourceMethod)
         */
@@ -155,21 +157,21 @@ public class SecurityInterceptor implements PreProcessInterceptor, PostProcessIn
                        throws Failure, CSWebApplicationException {
                ServerResponse result = null; // A null value essentially means success for this method
                Method resourceMethod = resourceMethodInvoker.getMethod();
-               
+
                try {
                        if (isAnonymousRequest(request, resourceMethodInvoker) == true) {
                                // We don't need to check credentials for anonymous requests.  Just login to Nuxeo and
                                // exit
                                nuxeoPreProcess(request, resourceMethodInvoker); // We login to Nuxeo only after we've checked authorization
-       
+
                                return result;
                        }
-                       
+
                        final String servicesResource = "/cspace-services/"; // HACK - this is configured in war, get this from tomcat instead
                        final int servicesResourceLen = servicesResource.length();
                        String httpMethod = request.getHttpMethod();
                        String uriPath = request.getUri().getPath();
-                       
+
                        if (logger.isDebugEnabled()) {
                                String fullRequest = request.getUri().getRequestUri().toString();
                                int servicesResourceIdx = fullRequest.indexOf(servicesResource);
@@ -177,10 +179,10 @@ public class SecurityInterceptor implements PreProcessInterceptor, PostProcessIn
                                                                                                        : fullRequest.substring(servicesResourceIdx+servicesResourceLen);
                                logger.debug("received " + httpMethod + " on " + relativeRequest);
                        }
-                       
+
                        String resName = SecurityUtils.getResourceName(request.getUri());
                        String resEntity = SecurityUtils.getResourceEntity(resName);
-                       
+
                        //
                        // If the resource entity is acting as a proxy then all sub-resources will map to the resource itself.
                        // This essentially means sub-resources inherit all the authz permissions of the entity.
@@ -202,7 +204,7 @@ public class SecurityInterceptor implements PreProcessInterceptor, PostProcessIn
                        // Make sure the account of the user making the request is current and active
                        //
                        checkActive();
-                       
+
                        if (requiresAuthorization(resName) == true) { //see comment immediately above
                                AuthZ authZ = AuthZ.get();
                                CSpaceResource res = new URIResourceImpl(AuthN.get().getCurrentTenantId(), resName, httpMethod);
@@ -244,7 +246,7 @@ public class SecurityInterceptor implements PreProcessInterceptor, PostProcessIn
                                // Login to Nuxeo
                                //
                                nuxeoPreProcess(request, resourceMethodInvoker); // We login to Nuxeo only after we've checked authorization
-                               
+
                                //
                                // We've passed all the checks.  Now just log the results
                                //
@@ -260,25 +262,25 @@ public class SecurityInterceptor implements PreProcessInterceptor, PostProcessIn
                        }
                        throw t;
                }
-               
+
                return result;
        }
-       
+
        @Override
        public void postProcess(ServerResponse arg0) {
                //
                // Log out of the Nuxeo framework
                //
                nuxeoPostProcess(arg0);
-       }       
+       }
 
        /**
         * checkActive check if account is active
-        * @throws CSWebApplicationException 
+        * @throws CSWebApplicationException
         */
        private void checkActive() throws CSWebApplicationException {
                String userId = AuthN.get().getUserId();
-                               
+
                try {
                        //
                        // Need to ensure that user's tenant is not disabled
@@ -290,18 +292,18 @@ public class SecurityInterceptor implements PreProcessInterceptor, PostProcessIn
                                                userId, tenantBindingType.getDisplayName());
                                Response response = Response.status(
                                                Response.Status.CONFLICT).entity(errMsg).type("text/plain").build();
-                               throw new CSWebApplicationException(response);                          
+                               throw new CSWebApplicationException(response);
                        }
                } catch (IllegalStateException ise) {
                        String errMsg = "User's account is not associated to any active tenants, userId=" + userId;
                        // Note the RFC on return types:
-                       // If the request already included Authorization credentials, then the 401 response 
+                       // If the request already included Authorization credentials, then the 401 response
                        // indicates that authorization has been refused for those credentials.
                        Response response = Response.status(
                                        Response.Status.UNAUTHORIZED).entity(errMsg).type("text/plain").build();
                        throw new CSWebApplicationException(ise, response);
                }
-               
+
                try {
                        //can't use JAXB here as this runs from the common jar which cannot
                        //depend upon the account service
@@ -349,10 +351,10 @@ public class SecurityInterceptor implements PreProcessInterceptor, PostProcessIn
                                        Response.Status.INTERNAL_SERVER_ERROR).entity(msg).type("text/plain").build();
                        throw new CSWebApplicationException(e, response);
                }
-               
+
                return null;
        }
-       
+
        public void nuxeoPostProcess(ServerResponse arg0) {
                try {
                        nuxeoLogout();
@@ -360,8 +362,8 @@ public class SecurityInterceptor implements PreProcessInterceptor, PostProcessIn
                        String msg = "Unable to logout of the Nuxeo framework.";
                        logger.error(msg, e);
                }
-       }       
-    
+       }
+
     private void logLoginContext(LoginContext loginContext) {
        if (!logger.isTraceEnabled()) return;
 
@@ -374,10 +376,10 @@ public class SecurityInterceptor implements PreProcessInterceptor, PostProcessIn
                        logger.trace("[" + principal.getName() + "]");
                }
     }
-    
+
     private void logLogoutContext(LoginContext loginContext) {
        if (!logger.isTraceEnabled()) return;
-       
+
        if (loginContext != null) {
                        logger.trace("CollectionSpace services now logging out of Nuxeo with LoginContext: "
                                        + loginContext);
@@ -391,8 +393,8 @@ public class SecurityInterceptor implements PreProcessInterceptor, PostProcessIn
                logger.trace("Logged out.");
        }
     }
-    
-    
+
+
     /*
      * Login to Nuxeo and save the LoginContext instance in a thread local variable
      */
@@ -407,9 +409,9 @@ public class SecurityInterceptor implements PreProcessInterceptor, PostProcessIn
                                        Thread.currentThread(), threadLocalLoginContext));
                }
        }
-       
+
        LoginContext loginContext = threadLocalLoginContext.get();
-       
+
        if (loginContext == null) {
                loginContext = Framework.loginAs(user);
                frameworkLogins++;
@@ -434,9 +436,9 @@ public class SecurityInterceptor implements PreProcessInterceptor, PostProcessIn
                frameworkLogins++;
        }
     }
-    
+
     private synchronized void nuxeoLogout() throws LoginException {
-       LoginContext loginContext = threadLocalLoginContext != null ? threadLocalLoginContext.get() : null; 
+       LoginContext loginContext = threadLocalLoginContext != null ? threadLocalLoginContext.get() : null;
         if (loginContext != null) {
                        logLogoutContext(loginContext);
             loginContext.logout();
@@ -451,7 +453,7 @@ public class SecurityInterceptor implements PreProcessInterceptor, PostProcessIn
                        logger.warn(ERROR_NUXEO_LOGOUT);  // If we get here, it means our login/logout bookkeeping has failed.
                }
         }
-        
+
         if (frameworkLogins == 0) {
                if (threadLocalLoginContext != null) {
                        logger.trace(String.format("Thread ID %s: Clearing ThreadLocal instance %s - %s ",
@@ -459,5 +461,5 @@ public class SecurityInterceptor implements PreProcessInterceptor, PostProcessIn
                }
                threadLocalLoginContext = null; //Clear the ThreadLocal to void Tomcat warnings associated with thread pools.
         }
-    }  
+    }
 }
index 0f20ebcf2069559ccd96aaffb2cbc69e2be0de27..062f25f72db253fd0b5c85f47776522fec13d067 100644 (file)
@@ -81,7 +81,7 @@ class CSpacePasswordEncoder extends BasePasswordEncoder {
 
 /**
  *
- * @author 
+ * @author
  */
 public class SecurityUtils {
 
@@ -90,12 +90,12 @@ public class SecurityUtils {
     public static final String URI_PATH_SEPARATOR = "/";
     public static final int MIN_PASSWORD_LENGTH = 8;
     public static final int MAX_PASSWORD_LENGTH = 24;
-    
+
     public static final String BASE64_ENCODING = "BASE64";
     public static final String BASE16_ENCODING = "HEX";
     public static final String RFC2617_ENCODING = "RFC2617";
     private static char MD5_HEX[] = "0123456789abcdef".toCharArray();
-    
+
     /**
      * createPasswordHash creates password has using configured digest algorithm
      * and encoding
@@ -123,7 +123,7 @@ public class SecurityUtils {
             logger.error(msg);
             throw new IllegalArgumentException(msg);
         }
-        if (password.length() < MIN_PASSWORD_LENGTH 
+        if (password.length() < MIN_PASSWORD_LENGTH
                        || password.length() > MAX_PASSWORD_LENGTH) {
             String msg = "Bad password: '"+password+"': length should be >= "
                        + MIN_PASSWORD_LENGTH + " and <= " + MAX_PASSWORD_LENGTH;
@@ -134,11 +134,11 @@ public class SecurityUtils {
 
     public static String getWorkflowResourceName(HttpRequest request) {
        String result = null;
-                       
+
        UriInfo uriInfo = request.getUri();
        String workflowSubResName = SecurityUtils.getResourceName(uriInfo);
        String resEntity = SecurityUtils.getResourceEntity(workflowSubResName);
-       
+
                MultivaluedMap<String, String> pathParams = uriInfo.getPathParameters();
                String workflowTransition = pathParams.getFirst(WorkflowClient.TRANSITION_PARAM_JAXRS);
                if (workflowTransition != null) {
@@ -147,17 +147,17 @@ public class SecurityUtils {
                        // e.g., intakes/workflow or intakes/*/workflow
                        result = resEntity;
                }
-       
+
        return result;
     }
-    
+
     public static String getIndexResourceName(HttpRequest request) {
        String result = null;
-                       
+
        UriInfo uriInfo = request.getUri();
        String indexSubResName = SecurityUtils.getResourceName(uriInfo);
        String resEntity = SecurityUtils.getResourceEntity(indexSubResName);
-       
+
                MultivaluedMap<String, String> pathParams = uriInfo.getPathParameters();
                String indexId = pathParams.getFirst(IndexClient.INDEX_ID_PARAM);
                if (indexId != null  && pathParams.containsKey("csid")) {
@@ -165,12 +165,12 @@ public class SecurityUtils {
                result = resEntity + "/*/" + IndexClient.SERVICE_NAME + "/" + indexId;
                } else if (indexId != null) {
                        // e.g., intakes/index/fulltext
-               result = resEntity + "/" + IndexClient.SERVICE_NAME + "/" + indexId;                    
+               result = resEntity + "/" + IndexClient.SERVICE_NAME + "/" + indexId;
                } else {
                        // e.g., intakes
                        result = resEntity;
                }
-               
+
                //
                // Overriding the result from above.
                //
@@ -178,10 +178,10 @@ public class SecurityUtils {
                // we're just going to return the index resource name.
                //
                result = IndexClient.SERVICE_NAME;
-       
+
        return result;
-    }    
-    
+    }
+
        /**
         * Gets the resource name.
         *
@@ -192,7 +192,7 @@ public class SecurityUtils {
                String uriPath = uriInfo.getPath();
 
                MultivaluedMap<String, String> pathParams = uriInfo.getPathParameters();
-               
+
                for (String pathParamName : pathParams.keySet()) {
                        //assumption : path params for csid for any entity has substring csid in name
                        String pathParamValue = pathParams.get(pathParamName).get(0);
@@ -216,9 +216,9 @@ public class SecurityUtils {
                        if ((pathParamName.toLowerCase().indexOf("indexid") > -1)) {
                                //replace indexid with wildcard
                                uriPath = uriPath.replace(pathParamValue, "*");
-                       }                       
+                       }
                }
-               
+
                // FIXME: REM
                // Since the hjid (HyperJaxb3 generated IDs) are not unique strings in URIs that also have a CSID,
                // we need to replace hjid last.  We can fix this by having HyperJaxb3 generate UUID.
@@ -229,13 +229,13 @@ public class SecurityUtils {
                        String hjidValue = hjidValueList.get(0); //should be just one value, so get the first.
                        uriPath = uriPath.replace(hjidValue, "*");
                }
-               
+
                uriPath = uriPath.replace("//", "/"); // replace duplicate '/' characters
                uriPath = uriPath.startsWith("/") ? uriPath.substring(1) : uriPath; // if present, strip the leading '/' character
-                               
+
                return uriPath;
        }
-    
+
        /**
         * Gets the resource name.
         *
@@ -244,16 +244,16 @@ public class SecurityUtils {
         */
        public static String getResourceEntity(UriInfo uriInfo) {
                String result = null;
-               
+
                result = getResourceEntity(uriInfo.getPath());
 //             List<PathSegment> pathSegmentList = uriInfo.getPathSegments();
 //             if (pathSegmentList.isEmpty() == false) {
 //                     result = pathSegmentList.get(0).getPath();
 //             }
-               
+
                return result;
        }
-       
+
        /**
         * Gets the resource entity by returning the first segment of the resource path
         *
@@ -264,12 +264,12 @@ public class SecurityUtils {
        public static String getResourceEntity(String relativePath)
        {
                String result = "";
-               
+
            StringTokenizer strTok = new StringTokenizer(relativePath, URI_PATH_SEPARATOR);
            String pathSegment = null;
            while (strTok.hasMoreTokens() == true) {
                pathSegment = strTok.nextToken();
-               if (pathSegment.equals("*") || 
+               if (pathSegment.equals("*") ||
                                pathSegment.equals(IndexClient.SERVICE_PATH_COMPONENT) || pathSegment.equals(CollectionSpaceClient.SERVICE_DESCRIPTION_PATH)) {  // Strip off subresource paths since they inherit their parent's permissions
                        //
                        // leave the loop if we hit a wildcard character or the "index" subresource
@@ -285,15 +285,15 @@ public class SecurityUtils {
            // Special case for the "index" services since "index" is also a subresource for some of the other services.
            //
            if (Tools.isEmpty(result) && pathSegment.equals(IndexClient.SERVICE_PATH_COMPONENT)) {
-               result = IndexClient.SERVICE_PATH_COMPONENT; 
+               result = IndexClient.SERVICE_PATH_COMPONENT;
            }
-               
+
                return result;
        }
-    
+
        public static List<ServiceBindingType> getReadableServiceBindingsForCurrentUser(
                        List<ServiceBindingType> serviceBindings) {
-               ArrayList<ServiceBindingType> readableList = 
+               ArrayList<ServiceBindingType> readableList =
                                new ArrayList<ServiceBindingType>(serviceBindings.size());
                AuthZ authZ = AuthZ.get();
        for(ServiceBindingType binding:serviceBindings) {
@@ -305,7 +305,7 @@ public class SecurityUtils {
        }
        return readableList;
        }
-    
+
     /**
      * Checks if is entity is action as a proxy for all sub-resources.
      *
@@ -313,19 +313,20 @@ public class SecurityUtils {
      */
     public static final boolean isResourceProxied(String resName) {
        boolean result = true;
-       
+
        switch (resName) {
                case AuthZ.REPORTS_INVOKE:
                case AuthZ.BATCH_INVOKE:
-               case AuthZ.ACCOUNT_PERMISSIONS:
+                               case AuthZ.ACCOUNT_PERMISSIONS:
+                               case AuthZ.ACCOUNT_ROLES:
                        result = false;
                        break;
        }
-       
+
        return result;
     }
 
-    
+
     /**
      * isCSpaceAdmin check if authenticated user is a CSpace administrator
      * @param tenantId
@@ -333,24 +334,24 @@ public class SecurityUtils {
      */
     public static boolean isCSpaceAdmin() {
        boolean result = false;
-       
+
        String tenantId = null;
        try {
                tenantId = AuthN.get().getCurrentTenantId();
        } catch (Throwable e) {
                tenantId = AuthN.ADMIN_TENANT_ID;
        }
-       
+
         if (tenantId != null) {
             if (AuthN.ADMIN_TENANT_ID.equals(tenantId) == true ||
                        AuthN.ANONYMOUS_TENANT_ID.equals(tenantId)) {
                 result = true;
             }
         }
-        
+
         return result;
     }
-    
+
     public static String createPasswordHash(String hashAlgorithm, String hashEncoding, String hashCharset,
                String username, String password, String salt)
     {
@@ -362,7 +363,7 @@ public class SecurityUtils {
     {
        CSpacePasswordEncoder passwordEncoder = new CSpacePasswordEncoder();
        String saltedPassword = passwordEncoder.mergePasswordAndSalt(password, salt); //
-         
+
         String passwordHash = null;
         byte passBytes[];
         try
@@ -463,5 +464,5 @@ public class SecurityUtils {
         return Base64Utils.fromb64(str);
     }
 
-    
+
 }
index 109ca4dbc015a1b3e347232299c7be215311aacc..0b70abee930399288538b03619505533a26087b6 100644 (file)
@@ -39,12 +39,14 @@ import javax.persistence.Query;
 
 import org.collectionspace.authentication.AuthN;
 import org.collectionspace.services.authorization.AccountPermission;
-import org.collectionspace.services.authorization.AccountValue;
+import org.collectionspace.services.authorization.AccountRole;
 import org.collectionspace.services.authorization.AccountRoleRel;
+import org.collectionspace.services.authorization.AccountValue;
 import org.collectionspace.services.authorization.AuthZ;
 import org.collectionspace.services.authorization.CSpaceResource;
 import org.collectionspace.services.authorization.PermissionRoleRel;
 import org.collectionspace.services.authorization.PermissionValue;
+import org.collectionspace.services.authorization.RoleValue;
 import org.collectionspace.services.authorization.URIResourceImpl;
 import org.collectionspace.services.common.api.Tools;
 import org.collectionspace.services.common.authorization_mgt.AuthorizationRoleRel;
@@ -60,32 +62,32 @@ import org.slf4j.LoggerFactory;
 
 /**
  * Utilities for JpaStorage
- * @author 
+ * @author
  */
 @SuppressWarnings({"rawtypes", "unchecked"})
 public class JpaStorageUtils {
 
     final private static Logger logger = LoggerFactory.getLogger(JpaStorageUtils.class);
-    
+
     /** The Constant CS_PERSISTENCE_UNIT. */
     public final static String CS_PERSISTENCE_UNIT = "org.collectionspace.services";
     public static String CS_AUTHZ_PERSISTENCE_UNIT = "org.collectionspace.services.authorization";
     public final static String CS_CURRENT_USER = "0";
-    
+
     // This is the column name for ID field of all the JPA objects
     public static final String CSID_LABEL = "csid";
-    
+
     private static Map<String, EntityManagerFactory> entityManagerFactoryCache = new HashMap<String, EntityManagerFactory>();
 
     private static boolean useTenantId(String tenantId) {
        boolean result = true;
-       
+
         boolean csAdmin = SecurityUtils.isCSpaceAdmin();
         if (csAdmin == true) {
                logger.trace("Running as the CSAdmin user.");
                //Thread.dumpStack();
         }
-        
+
        if (tenantId == null) {
                result = false;
                logger.trace("Ignoring tenant ID during .");
@@ -94,7 +96,7 @@ public class JpaStorageUtils {
 
        return result;
     }
-       
+
        @Deprecated
        public static Object getEntity(String id, Class entityClazz) {
         EntityManagerFactory emf = null;
@@ -114,7 +116,7 @@ public class JpaStorageUtils {
         }
         return entityFound;
     }
-    
+
 
     public static Object getEntity(long id, Class entityClazz)
                throws DocumentNotFoundException {
@@ -136,7 +138,7 @@ public class JpaStorageUtils {
 
     /**
      * getEntity with given id and class using given entity manager
-     * 
+     *
      * @param em
      * @param id
      * @param entityClazz
@@ -152,7 +154,7 @@ public class JpaStorageUtils {
         //FIXME: it would be nice to verify tenantid as well
         return em.find(entityClazz, id);
     }
-        
+
     public static Object getEntity(JPATransactionContext jpaTransactionContext, String id, Class entityClazz) throws DocumentNotFoundException {
         if (entityClazz == null) {
             String msg = "Not constructed with JpaStorageClientImpl(entityClazz) ctor";
@@ -161,8 +163,8 @@ public class JpaStorageUtils {
         }
         //FIXME: it would be nice to verify tenantid as well
         return jpaTransactionContext.find(entityClazz, id);
-    }    
-    
+    }
+
     @Deprecated
     private static String getUserId(String csid)
                throws DocumentNotFoundException  {
@@ -173,7 +175,7 @@ public class JpaStorageUtils {
        if (csid.equals(CS_CURRENT_USER) == true) {
                return AuthN.get().getUserId();
        }
-       
+
        //FIXME: Why can't the common jar depend on the account service?  Can we move the account
        //jaxb classes to the common "jaxb" module?
        try {
@@ -182,7 +184,7 @@ public class JpaStorageUtils {
                        String whereClause = "where csid = :csid";
                        HashMap<String, Object> params = new HashMap<String, Object>();
                        params.put("csid", csid);
-       
+
                        Object account = JpaStorageUtils.getEntity(
                                        "org.collectionspace.services.account.AccountsCommon", whereClause, params);
                        if (account == null) {
@@ -195,12 +197,12 @@ public class JpaStorageUtils {
                        result = (String)JaxbUtils.getValue(account, "getUserId");
        } catch (Exception e) {
                        String msg = "User's account is in invalid state, csid=" + csid;
-                       throw new DocumentNotFoundException(msg);               
+                       throw new DocumentNotFoundException(msg);
        }
-               
+
        return result;
     }
-    
+
     private static AccountValue getAccountValue(String csid)
        throws DocumentNotFoundException  {
 
@@ -234,11 +236,11 @@ public class JpaStorageUtils {
                return av;
        } catch (Exception e) {
                String msg = "User's account is in invalid state, csid=" + csid;
-               throw new DocumentNotFoundException(msg);               
+               throw new DocumentNotFoundException(msg);
        }
     }
 
-    //FIXME: REM - This method should probably be moved to the AccountPermissionDocumemntHandler
+    //FIXME: REM - This method should probably be moved to the AccountPermissionDocumentHandler
     /*
      * This is a prototype for the /accounts/{csid}/permissions GET service call.
      */
@@ -246,8 +248,8 @@ public class JpaStorageUtils {
                throws UnauthorizedException, DocumentNotFoundException {
        return getAccountPermissions(csid, null, null);
     }
-    
-    //FIXME: REM - This method should probably be moved to the AccountPermissionDocumemntHandler    
+
+    //FIXME: REM - This method should probably be moved to the AccountPermissionDocumentHandler
     /*
      * This is a prototype for the /accounts/{csid}/permissions GET service call.
      */
@@ -260,7 +262,7 @@ public class JpaStorageUtils {
         //
        AccountValue account = getAccountValue(csid);
        String userId = account.getUserId();
-       String currentUserId = AuthN.get().getUserId(); 
+       String currentUserId = AuthN.get().getUserId();
         if (currentUserId.equalsIgnoreCase(userId) == false) {
                        CSpaceResource res = new URIResourceImpl(AuthN.get().getCurrentTenantId(), "accounts", "GET");
                        if (AuthZ.get().isAccessAllowed(res) == false) {
@@ -272,7 +274,7 @@ public class JpaStorageUtils {
                                throw new UnauthorizedException(msg);
                        }
         }
-        
+
         AccountPermission result = new AccountPermission();
        EntityManagerFactory emf = null;
         EntityManager em = null;
@@ -284,9 +286,9 @@ public class JpaStorageUtils {
 
             emf = getEntityManagerFactory();
             em = emf.createEntityManager();
-           
+
             StringBuilder permQueryStrBldr = new StringBuilder(
-                       "SELECT DISTINCT pr FROM " + AccountRoleRel.class.getName() + " ar, " 
+                       "SELECT DISTINCT pr FROM " + AccountRoleRel.class.getName() + " ar, "
                        + PermissionRoleRel.class.getName() + " pr"
                        + " WHERE ar.roleId = pr.roleId and ar.userId=" + "'" + userId + "'");
             //
@@ -297,7 +299,7 @@ public class JpaStorageUtils {
                                " or pr.permissionResource = " + "'" + permissionResource + "'" + ")");
             }
             String queryStr = permQueryStrBldr.toString(); //for debugging
-            Query q = em.createQuery(queryStr);            
+            Query q = em.createQuery(queryStr);
             resultList = q.getResultList().iterator();
 
             if (resultList.hasNext()) {
@@ -330,41 +332,120 @@ public class JpaStorageUtils {
         }
         return result;
     }
-    
+
+       //FIXME: REM - This method should probably be moved to the AccountPermissionDocumentHandler
+       public static AccountRole getAccountRoles(String csid)
+               throws UnauthorizedException, DocumentNotFoundException {
+               return getAccountRoles(csid, null, null);
+       }
+
+       //FIXME: REM - This method should probably be moved to the AccountPermissionDocumentHandler
+       public static AccountRole getAccountRoles(String csid, String currentResource, String permissionResource)
+               throws UnauthorizedException, DocumentNotFoundException {
+               //
+               // Make sure the user asking for this list has the correct
+               // permission -that is, the csid's userId match the currently logged in userId or
+               // that they have read access to the "accounts" resource.
+               //
+               AccountValue account = getAccountValue(csid);
+               String userId = account.getUserId();
+               String currentUserId = AuthN.get().getUserId();
+               if (currentUserId.equalsIgnoreCase(userId) == false) {
+                       CSpaceResource res = new URIResourceImpl(AuthN.get().getCurrentTenantId(), "accounts", "GET");
+                       if (AuthZ.get().isAccessAllowed(res) == false) {
+                               String msg = "Access to the permissions for the account with csid = " + csid + " is NOT allowed for " +
+                                       " user=" + currentUserId;
+                               if (logger.isDebugEnabled() == true) {
+                                       logger.debug(msg);
+                               }
+                               throw new UnauthorizedException(msg);
+                       }
+               }
+
+               AccountRole result = new AccountRole();
+               EntityManagerFactory emf = null;
+               EntityManager em = null;
+               Iterator<Object> resultList = null;
+               try {
+                       List<AccountValue> accountValues = new ArrayList<AccountValue>();
+                       accountValues.add(account);
+                       result.setAccount(accountValues);
+
+                       emf = getEntityManagerFactory();
+                       em = emf.createEntityManager();
+
+                       String queryStr =
+                                       "SELECT DISTINCT ar FROM " + AccountRoleRel.class.getName() + " ar "
+                                       + "WHERE ar.userId=" + "'" + userId + "'";
+
+                       Query q = em.createQuery(queryStr);
+                       resultList = q.getResultList().iterator();
+
+                       if (resultList.hasNext()) {
+                               List<RoleValue> roleValues = new ArrayList<RoleValue>();
+                               while (resultList.hasNext()) {
+                                       AccountRoleRel accountRolRel = (AccountRoleRel)resultList.next();
+                                       roleValues.add(AuthorizationRoleRel.buildRoleValue(accountRolRel));
+                               }
+                               result.setRole(roleValues);
+                       }
+               } catch (NoResultException nre) {
+                       if (em != null && em.getTransaction().isActive()) {
+                               em.getTransaction().rollback();
+                       }
+                       if (logger.isDebugEnabled()) {
+                               logger.debug("could not find entity with id=" + userId, nre);
+                       }
+                       //returns null
+               } catch (Exception e) {
+                       if (em != null && em.getTransaction().isActive()) {
+                               em.getTransaction().rollback();
+                       }
+                       if (logger.isDebugEnabled()) {
+                               logger.debug("could not find entity(2) with id=" + userId, e);
+                       }
+               } finally {
+                       if (em != null) {
+                               releaseEntityManagerFactory(emf);
+                       }
+               }
+               return result;
+       }
+
     public static Object getEnityByKey(
                JPATransactionContext jpaTransactionContext,
-               String entityName, 
-               String key, 
+               String entityName,
+               String key,
                String value,
             String tenantId) throws TransactionException {
        return getEnityByKey(jpaTransactionContext, (DocumentFilter)null, entityName, key, value, tenantId);
     }
-    
+
     public static Object getEnityByKey(
                JPATransactionContext jpaTransactionContext,
                DocumentFilter docFilter,
                String entityName, String key, String value,
             String tenantId) throws TransactionException {
         Object result = null;
-        
+
         try {
             boolean useTenantId = useTenantId(tenantId);
             StringBuilder queryStrBldr = new StringBuilder("SELECT a FROM ");
             queryStrBldr.append(entityName);
             queryStrBldr.append(" a");
-            
+
             if (docFilter != null) {
                    String joinFetch = docFilter.getJoinFetchClause();
                    if (Tools.notBlank(joinFetch)) {
                        queryStrBldr.append(" " + joinFetch);
                    }
             }
-            
+
             queryStrBldr.append(" WHERE a." + key + " = :" + key);
             if (useTenantId == true) {
                 queryStrBldr.append(" AND a.tenantId = :tenantId");
             }
-            String queryStr = queryStrBldr.toString(); //for debugging            
+            String queryStr = queryStrBldr.toString(); //for debugging
             Query q = jpaTransactionContext.createQuery(queryStr);
             q.setParameter(key, value);
             if (useTenantId == true) {
@@ -382,12 +463,12 @@ public class JpaStorageUtils {
             }
             //returns null
         }
-        
+
         return result;
     }
 
     /**
-     * 
+     *
      * @param em
      * @param entityName
      * @param key1
@@ -395,18 +476,18 @@ public class JpaStorageUtils {
      * @param key2
      * @param value2
      * @return
-     * @throws TransactionException 
+     * @throws TransactionException
      */
     public static Object getEntityByDualKeys(
                JPATransactionContext jpaTransactionContext,
-               String entityName, 
+               String entityName,
                String key1, String value1,
                String key2, String value2) throws TransactionException {
        return getEntityByDualKeys(jpaTransactionContext, entityName, key1, value1, key2, value2, null);
     }
-    
+
     /**
-     * 
+     *
      * @param em
      * @param entityName
      * @param key1
@@ -415,16 +496,16 @@ public class JpaStorageUtils {
      * @param value2
      * @param tenantId
      * @return
-     * @throws TransactionException 
+     * @throws TransactionException
      */
        public static Object getEntityByDualKeys(
                JPATransactionContext jpaTransactionContext,
-               String entityName, 
+               String entityName,
                String key1, String value1,
                String key2, String value2,
             String tenantId) throws TransactionException {
        Object result = null;
-       
+
         boolean useTenantId = useTenantId(tenantId);
         StringBuilder queryStrBldr = new StringBuilder("SELECT a FROM ");
         queryStrBldr.append(entityName);
@@ -441,12 +522,12 @@ public class JpaStorageUtils {
         if (useTenantId == true) {
             q.setParameter("tenantId", tenantId);
         }
-        
+
         result = q.getSingleResult();
 
         return result;
     }
-       
+
        public static List getEntityListByDualKeys(JPATransactionContext jpaTransactionContext, String entityName,
                        String key1, String value1, String key2, String value2, String tenantId) throws TransactionException {
                List result = null;
@@ -472,15 +553,15 @@ public class JpaStorageUtils {
 
                return result;
        }
-    
+
     /**
-     * 
+     *
      * @param ctx
      * @param entityName
      * @param id
      * @param tenantId
      * @return
-     * @throws TransactionException 
+     * @throws TransactionException
      */
     public static Object getEntity(
                JPATransactionContext jpaTransactionContext,
@@ -492,7 +573,7 @@ public class JpaStorageUtils {
     }
 
     /**
-     * getEntity 
+     * getEntity
      * @param entityName fully qualified entity name
      * @param id
      * @param tenantId
@@ -518,7 +599,7 @@ public class JpaStorageUtils {
             queryStrBldr.append(entityName);
             queryStrBldr.append(" a");
             queryStrBldr.append(" WHERE csid = :csid");
-            
+
             boolean useTenantId = useTenantId(tenantId);
             if (useTenantId == true) {
                 queryStrBldr.append(" AND tenantId = :tenantId");
@@ -559,7 +640,7 @@ public class JpaStorageUtils {
     public static Object getEntity(JPATransactionContext jpaTransactionContext, String entityName,
             String whereClause, HashMap<String, Object> paramBindings) {
         Object result = null;
-        
+
         StringBuilder queryStrBldr = new StringBuilder("SELECT a FROM ");
         queryStrBldr.append(entityName);
         queryStrBldr.append(" a");
@@ -570,20 +651,20 @@ public class JpaStorageUtils {
         for (String paramName : paramBindings.keySet()) {
             q.setParameter(paramName, paramBindings.get(paramName));
         }
-        
+
                result = q.getSingleResult();
-        
+
         if (result == null) {
                logger.debug("Call to getEntity() returned empty set.");
         }
-        
+
         return result;
     }
-    
+
     public static Object getEntity(EntityManager em, String entityName,
             String whereClause, HashMap<String, Object> paramBindings) {
         Object result = null;
-        
+
         StringBuilder queryStrBldr = new StringBuilder("SELECT a FROM ");
         queryStrBldr.append(entityName);
         queryStrBldr.append(" a");
@@ -594,16 +675,16 @@ public class JpaStorageUtils {
         for (String paramName : paramBindings.keySet()) {
             q.setParameter(paramName, paramBindings.get(paramName));
         }
-        
+
         result = q.getSingleResult();
-        
+
         if (result == null) {
                logger.debug("Call to getEntity() returned empty set.");
         }
-        
+
         return result;
     }
-    
+
     /**
      * getEntity using given where clause with given param bindings
      * @param entityName
@@ -652,7 +733,7 @@ public class JpaStorageUtils {
     public static EntityManagerFactory getEntityManagerFactory() {
        EntityManagerFactory result = null;
        PersistenceException persistenceException = null;
-       
+
        try {
                result = getEntityManagerFactory(CS_PERSISTENCE_UNIT);
        } catch (PersistenceException e) {
@@ -671,11 +752,11 @@ public class JpaStorageUtils {
                        persistenceException = e;
                }
        }
-               
+
         if (result == null) {
                throw persistenceException;
         }
-        
+
         return result;
     }
 
@@ -691,14 +772,14 @@ public class JpaStorageUtils {
        EntityManagerFactory result = null;
 
         result = getCachedEntityManagerFactory(persistenceUnit);
-       
+
                //
                // Try using a backup persistence unit if the specified one is not available and log a warning
                //
         if (result == null && !persistenceUnit.equalsIgnoreCase(CS_PERSISTENCE_UNIT)) {
             result = getCachedEntityManagerFactory(CS_PERSISTENCE_UNIT);
         }
-       
+
        //
        // One more try.
        //
@@ -742,7 +823,7 @@ public class JpaStorageUtils {
         // are cached and re-used, they should not be closed after each use.
         // Instead, releaseEntityManagerFactories() should be called when the
         // services layer is stopped.
-        
+
         // if (emf != null) {
         //     emf.close();
         // }
@@ -752,8 +833,7 @@ public class JpaStorageUtils {
         for (EntityManagerFactory emf : entityManagerFactoryCache.values()) {
             emf.close();
         }
-        
+
         entityManagerFactoryCache.clear();
     }
 }
-