]> git.aero2k.de Git - tmp/jakarta-migration.git/commitdiff
DRYD-732: Initial support for adding 'invoke' subresource for batch and report resources.
authorRichard Millet <remillet@gmail.com>
Tue, 22 Oct 2019 23:56:13 +0000 (16:56 -0700)
committerRichard Millet <remillet@gmail.com>
Tue, 22 Oct 2019 23:56:13 +0000 (16:56 -0700)
services/authorization/service/src/main/java/org/collectionspace/services/authorization/AuthZ.java
services/common/src/main/cspace/config/services/tenants/tenant-bindings-proto-unified.xml
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/report/service/src/main/java/org/collectionspace/services/report/ReportResource.java

index 1d18a994e40969b4d39c492d6350c01b6335e7c4..18fcffa3f0440d7b831a11b679d9406be463d90e 100644 (file)
@@ -59,6 +59,17 @@ 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 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";     
 
     /**
      *
index c3543c8e076d157a86788951dcf5b77d78d31963..9a62f51a77f47ed22fbc573919931708bb1b174d 100644 (file)
                </tenant:serviceBindings>
                <!-- end blob service meta-data -->
 
+               <!-- begin batch-invoke service meta-data -->
+               <tenant:serviceBindings id="batch/invoke" merge:matcher="id" name="batch/invoke"
+                       type="utility" version="1.0">
+                       <!-- other URI paths through which this service could be accessed -->
+                       <!-- <service:uriPath xmlns:service='http://collectionspace.org/services/config/service'> /batch/*/invoke </service:uriPath> -->
+                       <service:documentHandler xmlns:service="http://collectionspace.org/services/config/service">org.collectionspace.services.batch.nuxeo.BatchDocumentModelHandler
+                       </service:documentHandler>
+                       <!-- <service:validatorHandler xmlns:service='http://collectionspace.org/services/config/service'> org.collectionspace.services.batch.nuxeo.BatchDocumentModelHandler
+                               </service:validatorHandler> -->
+                       <service:object xmlns:service="http://collectionspace.org/services/config/service" name="BatchInvoke"
+                               version="1.0">
+                               <service:part id="0" control_group="Managed" versionable="true" auditable="false" label="batchinvoke_system"
+                                       updated="" order="0">
+                                       <service:content contentType="application/xml">
+                                               <service:xmlContent namespaceURI="http://collectionspace.org/services/config/system"
+                                                       schemaLocation="http://collectionspace.org/services/config/system http://collectionspace.org/services/config/system/system-response.xsd" />
+                                       </service:content>
+                               </service:part>
+                       </service:object>
+               </tenant:serviceBindings>
+               <!-- end batch-invoke service meta-data -->
+
                <!-- begin batch service meta-data -->
                <tenant:serviceBindings id="Batch" merge:matcher="id" name="Batch" type="utility" version="1.0">
                        <service:repositoryDomain xmlns:service="http://collectionspace.org/services/config/service">default-domain</service:repositoryDomain>
                </tenant:serviceBindings>
                <!-- end Index service meta-data -->
 
+               <!-- begin reports-invoke service meta-data -->
+               <tenant:serviceBindings id="reports/invoke" merge:matcher="id" name="reports/invoke"
+                       type="utility" version="1.0">
+                       <!-- other URI paths through which this service could be accessed -->
+                       <!-- <service:uriPath xmlns:service='http://collectionspace.org/services/config/service'> /reports/*/invoke </service:uriPath> -->
+                       <service:documentHandler xmlns:service="http://collectionspace.org/services/config/service">org.collectionspace.services.report.nuxeo.ReportDocumentModelHandler
+                       </service:documentHandler>
+                       <!-- <service:validatorHandler xmlns:service='http://collectionspace.org/services/config/service'> org.collectionspace.services.report.nuxeo.ReportDocumentModelHandler
+                               </service:validatorHandler> -->
+                       <service:object xmlns:service="http://collectionspace.org/services/config/service" name="ReportsInvoke"
+                               version="1.0">
+                               <service:part id="0" control_group="Managed" versionable="true" auditable="false" label="reportsinvoke_system"
+                                       updated="" order="0">
+                                       <service:content contentType="application/xml">
+                                               <service:xmlContent namespaceURI="http://collectionspace.org/services/config/system"
+                                                       schemaLocation="http://collectionspace.org/services/config/system http://collectionspace.org/services/config/system/system-response.xsd" />
+                                       </service:content>
+                               </service:part>
+                       </service:object>
+               </tenant:serviceBindings>
+               <!-- end reports-invoke service meta-data -->
+               
                <!-- begin report service meta-data -->
                <tenant:serviceBindings id="Reports" merge:matcher="id" name="Reports" type="utility" version="1.0">
                        <!-- other URI paths through which this service could be accessed -->
index 73a0997ffafb3800a39872d7fb45a8a8f66f3958..de25f34412f17f01c91519b65dcb4b5424084a12 100644 (file)
@@ -84,11 +84,7 @@ public class SecurityInterceptor implements PreProcessInterceptor, PostProcessIn
 
        /** The Constant logger. */
        private static final Logger logger = LoggerFactory.getLogger(SecurityInterceptor.class);
-       private static final String REPORTS_MIME_OUTPUTS = "reports/mimetypes";
-       private static final String ACCOUNT_PERMISSIONS = "accounts/*/accountperms";
-       private static final String STRUCTURED_DATE_REQUEST = "structureddate";
-       private static final String PASSWORD_RESET = "accounts/requestpasswordreset";
-       private static final String PROCESS_PASSWORD_RESET = "accounts/processpasswordreset";
+       
        private static final String SYSTEM_INFO = SystemInfoClient.SERVICE_NAME;
        private static final String NUXEO_ADMIN = null;
     //
@@ -107,8 +103,8 @@ public class SecurityInterceptor implements PreProcessInterceptor, PostProcessIn
        
                String resName = SecurityUtils.getResourceName(request.getUri()).toLowerCase();
                switch (resName) {
-                       case PASSWORD_RESET:
-                       case PROCESS_PASSWORD_RESET:
+                       case AuthZ.PASSWORD_RESET:
+                       case AuthZ.PROCESS_PASSWORD_RESET:
                        case SYSTEM_INFO:
                                return true;
                }
@@ -139,9 +135,9 @@ public class SecurityInterceptor implements PreProcessInterceptor, PostProcessIn
        // STRUCTURED_DATE_REQUEST: All user can request the parsing of a structured date string.
        //
        switch (resName) {
-               case STRUCTURED_DATE_REQUEST:
-               case ACCOUNT_PERMISSIONS:
-               case REPORTS_MIME_OUTPUTS:
+               case AuthZ.STRUCTURED_DATE_REQUEST:
+               case AuthZ.ACCOUNT_PERMISSIONS:
+               case AuthZ.REPORTS_MIME_OUTPUTS:
                        result = false;
                        break;
                default:
@@ -186,14 +182,24 @@ public class SecurityInterceptor implements PreProcessInterceptor, PostProcessIn
                        String resEntity = SecurityUtils.getResourceEntity(resName);
                        
                        //
-                       // If the resource entity is acting as a proxy then all sub-resource will map to the resource itself.
-                       // This essentially means that the sub-resource inherit all the authz permissions of the entity.
+                       // 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.
                        //
-                       if (SecurityUtils.isEntityProxy() == true && !resName.equalsIgnoreCase(ACCOUNT_PERMISSIONS)) {
+                       if (SecurityUtils.isResourceProxied(resName) == true) {
                                resName = resEntity;
+                       } else {
+                               //
+                               // If our resName is not proxied, we may need to tweak it.
+                               //
+                               switch (resName) {
+                                       case AuthZ.REPORTS_INVOKE:
+                                       case AuthZ.BATCH_INVOKE: {
+                                               resName = resName.replace("/*/", "/");
+                                       }
+                               }
                        }
                        //
-                       // Make sure the account is current and active
+                       // Make sure the account of the user making the request is current and active
                        //
                        checkActive();
                        
index 37d7c6cd3e6abd6891555b57b5a0f1c522d0bcf8..0f20ebcf2069559ccd96aaffb2cbc69e2be0de27 100644 (file)
@@ -86,6 +86,7 @@ class CSpacePasswordEncoder extends BasePasswordEncoder {
 public class SecurityUtils {
 
     private static final Logger logger = LoggerFactory.getLogger(SecurityUtils.class);
+
     public static final String URI_PATH_SEPARATOR = "/";
     public static final int MIN_PASSWORD_LENGTH = 8;
     public static final int MAX_PASSWORD_LENGTH = 24;
@@ -231,6 +232,7 @@ public class SecurityUtils {
                
                uriPath = uriPath.replace("//", "/"); // replace duplicate '/' characters
                uriPath = uriPath.startsWith("/") ? uriPath.substring(1) : uriPath; // if present, strip the leading '/' character
+                               
                return uriPath;
        }
     
@@ -309,10 +311,18 @@ public class SecurityUtils {
      *
      * @return true, if is entity proxy is acting as a proxy for all sub-resources
      */
-    public static final boolean isEntityProxy() {
-       //
-       // should be getting this information from  the cspace config settings (tenent bindings file).
-       return true;
+    public static final boolean isResourceProxied(String resName) {
+       boolean result = true;
+       
+       switch (resName) {
+               case AuthZ.REPORTS_INVOKE:
+               case AuthZ.BATCH_INVOKE:
+               case AuthZ.ACCOUNT_PERMISSIONS:
+                       result = false;
+                       break;
+       }
+       
+       return result;
     }
 
     
index c87c2cfba5f15605ab4b87478539ceafbf230eef..cbf26cd96f8ed22f1549defe3eb925e6d5d7c6b8 100644 (file)
@@ -29,6 +29,10 @@ import java.util.List;
 import org.collectionspace.services.jaxb.AbstractCommonList;
 import org.collectionspace.services.report.nuxeo.ReportDocumentModelHandler;
 import org.collectionspace.services.publicitem.PublicitemsCommon;
+import org.collectionspace.services.authorization.AuthZ;
+import org.collectionspace.services.authorization.CSpaceResource;
+import org.collectionspace.services.authorization.URIResourceImpl;
+import org.collectionspace.services.authorization.perms.ActionType;
 import org.collectionspace.services.client.IQueryManager;
 import org.collectionspace.services.client.PayloadPart;
 import org.collectionspace.services.client.PoxPayloadIn;
@@ -62,6 +66,7 @@ import javax.ws.rs.core.MultivaluedMap;
 import javax.ws.rs.core.Response;
 import javax.ws.rs.core.UriInfo;
 import javax.ws.rs.core.Response.ResponseBuilder;
+import javax.ws.rs.core.Response.Status;
 
 @Path(ReportClient.SERVICE_PATH)
 @Consumes("application/xml")
@@ -77,6 +82,7 @@ public class ReportResource extends NuxeoBasedResource {
     private static String REPORTS_STD_GROUPCSID_PARAM = "groupcsid";
     private static String REPORTS_STD_CSIDLIST_PARAM = "csidlist";
     private static String REPORTS_STD_TENANTID_PARAM = "tenantid";
+    private static String REPORT_INVOKE_RESNAME = "reports/invoke";
 
     @Override
     protected String getVersionString() {
@@ -188,30 +194,98 @@ public class ReportResource extends NuxeoBasedResource {
             StringBuffer outMimeType = new StringBuffer();
             StringBuffer outReportFileName = new StringBuffer();
             ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx = createServiceContext();
-            InputStream reportInputStream = invokeReport(ctx, csid, invContext, outMimeType, outReportFileName);
-            response = PublicItemUtil.publishToRepository(
-                       (PublicitemsCommon)null,
-                       resourceMap,
-                       uriInfo,
-                       getRepositoryClient(ctx),
-                       ctx,
-                       reportInputStream,
-                       outReportFileName.toString());
+            
+            if (isAuthorizedToInvokeReports(ctx) == true) {
+                   InputStream reportInputStream = invokeReport(ctx, csid, invContext, outMimeType, outReportFileName);
+                   response = PublicItemUtil.publishToRepository(
+                               (PublicitemsCommon)null,
+                               resourceMap,
+                               uriInfo,
+                               getRepositoryClient(ctx),
+                               ctx,
+                               reportInputStream,
+                               outReportFileName.toString());
+            } else {
+                               ResponseBuilder builder = Response.status(Status.FORBIDDEN);
+                       response = builder.build();
+            }
         } catch (Exception e) {
             throw bigReThrow(e, ServiceMessages.POST_FAILED);
         }
 
         return response;
     }
+    
+    /*
+     * This method allows backward compatibility with the old API for running reports.
+     */
+    private boolean isAuthorizedToInvokeReports(ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx) {
+       boolean result = true;
+                       
+               //
+               // Until we enforce a user having POST perms on "/reports/*/invoke", we will continue to allow users with
+               // POST perms on "/reports" to run reports -see JIRA issue https://collectionspace.atlassian.net/browse/DRYD-732
+       //
+       // To start enforcing POST perms on "/reports/*/invoke", uncomment the following block of code
+               //
+
+       /*
+       CSpaceResource res = new URIResourceImpl(ctx.getTenantId(), REPORT_INVOKE_RESNAME, AuthZ.getMethod(ActionType.CREATE));
+               if (AuthZ.get().isAccessAllowed(res) == false) {
+                       result = false;
+               }
+               */
+
+               return result;
+    }
 
+    /**
+     * This method is deprecated at of CollectionSpace v5.3.
+     * @param ui
+     * @param csid
+     * @param invContext
+     * @return
+     */
     @POST
     @Path("{csid}")
+    @Deprecated
     public Response invokeReport(
                @Context UriInfo ui,
                @PathParam("csid") String csid,
                InvocationContext invContext) {
        Response response = null;
 
+        try {
+            StringBuffer outMimeType = new StringBuffer();
+            StringBuffer outFileName = new StringBuffer();
+            ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx = createServiceContext();
+            
+            if (isAuthorizedToInvokeReports(ctx) == true) {
+                   InputStream reportInputStream = invokeReport(ctx, csid, invContext, outMimeType, outFileName);
+                               // Need to set response type for what is requested...
+                               ResponseBuilder builder = Response.ok(reportInputStream, outMimeType.toString());
+                               builder = builder.header("Content-Disposition","inline;filename=\""+ outFileName.toString() +"\"");
+                       response = builder.build();
+            } else {
+                               ResponseBuilder builder = Response.status(Status.FORBIDDEN);
+                       response = builder.build();
+            }
+        } catch (Exception e) {
+               String msg = e.getMessage();
+            throw bigReThrow(e, ServiceMessages.POST_FAILED + msg != null ? msg : "");
+        }
+
+        return response;
+    }
+    
+    @POST
+    @Path("{csid}/invoke")
+    public Response invokeReportNew(
+               @Context UriInfo ui,
+               @PathParam("csid") String csid,
+               InvocationContext invContext) {
+       Response response = null;
+
         try {
             StringBuffer outMimeType = new StringBuffer();
             StringBuffer outFileName = new StringBuffer();