]> git.aero2k.de Git - tmp/jakarta-migration.git/blob
62898cd810a36471d2ed057fe98bf34958b96ef7
[tmp/jakarta-migration.git] /
1 /**
2  *  This document is a part of the source code and related artifacts
3  *  for CollectionSpace, an open source collections management system
4  *  for museums and related institutions:
5
6  *  http://www.collectionspace.org
7  *  http://wiki.collectionspace.org
8
9  *  Copyright 2009 University of California at Berkeley
10
11  *  Licensed under the Educational Community License (ECL), Version 2.0.
12  *  You may not use this file except in compliance with this License.
13
14  *  You may obtain a copy of the ECL 2.0 License at
15
16  *  https://source.collectionspace.org/collection-space/LICENSE.txt
17
18  *  Unless required by applicable law or agreed to in writing, software
19  *  distributed under the License is distributed on an "AS IS" BASIS,
20  *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
21  *  See the License for the specific language governing permissions and
22  *  limitations under the License.
23  */
24 package org.collectionspace.services.batch.nuxeo;
25
26 import java.util.ArrayList;
27 import java.util.HashSet;
28 import java.util.List;
29 import java.util.Set;
30
31 import javax.ws.rs.core.Response;
32
33 import org.collectionspace.authentication.AuthN;
34 import org.collectionspace.services.account.AccountResource;
35 import org.collectionspace.services.authorization.AuthZ;
36 import org.collectionspace.services.authorization.CSpaceResource;
37 import org.collectionspace.services.authorization.PermissionException;
38 import org.collectionspace.services.authorization.URIResourceImpl;
39 import org.collectionspace.services.authorization.perms.ActionType;
40 import org.collectionspace.services.nuxeo.client.java.NuxeoDocumentModelHandler;
41 import org.collectionspace.services.nuxeo.client.java.CoreSessionInterface;
42 import org.collectionspace.services.nuxeo.client.java.NuxeoRepositoryClientImpl;
43 import org.collectionspace.services.batch.BatchCommon;
44 import org.collectionspace.services.batch.BatchCommon.ForDocTypes;
45 import org.collectionspace.services.batch.BatchCommon.ForRoles;
46 import org.collectionspace.services.batch.BatchInvocable;
47 import org.collectionspace.services.batch.ResourceActionGroup;
48 import org.collectionspace.services.batch.ResourceActionGroupList;
49 import org.collectionspace.services.client.PoxPayloadIn;
50 import org.collectionspace.services.client.PoxPayloadOut;
51 import org.collectionspace.services.common.ResourceMap;
52 import org.collectionspace.services.common.authorization_mgt.ActionGroup;
53 import org.collectionspace.services.common.context.ServiceContext;
54 import org.collectionspace.services.common.document.BadRequestException;
55 import org.collectionspace.services.common.document.DocumentException;
56 import org.collectionspace.services.common.invocable.Invocable;
57 import org.collectionspace.services.common.invocable.InvocationContext;
58 import org.collectionspace.services.common.invocable.InvocationResults;
59 import org.collectionspace.services.common.invocable.Invocable.InvocationError;
60
61 import org.jboss.resteasy.spi.ResteasyProviderFactory;
62 import org.slf4j.Logger;
63 import org.slf4j.LoggerFactory;
64
65 public class BatchDocumentModelHandler extends NuxeoDocumentModelHandler<BatchCommon> {
66         private final Logger logger = LoggerFactory.getLogger(BatchDocumentModelHandler.class);
67
68         protected final int BAD_REQUEST_STATUS = Response.Status.BAD_REQUEST.getStatusCode();
69         
70         /**
71          * Return true if the batch job supports the requested mode.
72          * @param invocationCtx
73          * @param batchCommon
74          * @return
75          * @throws BadRequestException 
76          */
77         protected boolean supportsInvokationMode(InvocationContext invocationCtx, BatchCommon batchCommon) throws BadRequestException {
78                 boolean result = false;
79                 
80                 String invocationMode = invocationCtx.getMode().toLowerCase();
81                 if (BatchInvocable.INVOCATION_MODE_SINGLE.equalsIgnoreCase(invocationMode)) {
82                         result = batchCommon.isSupportsSingleDoc(); //BatchJAXBSchema.SUPPORTS_SINGLE_DOC;
83                 } else if (BatchInvocable.INVOCATION_MODE_LIST.equalsIgnoreCase(invocationMode)) {
84                         result = batchCommon.isSupportsDocList(); //BatchJAXBSchema.SUPPORTS_DOC_LIST;
85                 } else if (BatchInvocable.INVOCATION_MODE_GROUP.equalsIgnoreCase(invocationMode)) {
86                         result = batchCommon.isSupportsGroup(); //BatchJAXBSchema.SUPPORTS_GROUP;
87                 } else if (Invocable.INVOCATION_MODE_NO_CONTEXT.equalsIgnoreCase(invocationMode)) {
88                         result = batchCommon.isSupportsNoContext(); //InvocableJAXBSchema.SUPPORTS_NO_CONTEXT;
89                 } else {
90                         String msg = String.format("BatchResource: Unknown invocation mode '%s' requested trying to invoke batch job '%s'.",
91                                         invocationMode, batchCommon.getName());
92                         throw new BadRequestException(msg);
93                 }
94                 
95                 return result;
96         }
97         
98         /**
99          * Returns true if we found any required permissions.
100          * 
101          * @param batchCommon
102          * @return
103          */
104         private boolean hasRequiredPermissions(BatchCommon batchCommon) {
105                 boolean result = false;
106                 
107                 try {
108                         result = batchCommon.getResourceActionGroupList().getResourceActionGroup().size() > 0;
109                 } catch (NullPointerException e) {
110                         // ignore exception, we're just testing to see if we have any list elements
111                 }
112                 
113                 return result;
114         }
115         
116         /**
117          * Returns true if we found any required roles.
118          * 
119          * @param batchCommon
120          * @return
121          */
122         private boolean hasRequiredRoles(BatchCommon batchCommon) {
123                 boolean result = false;
124                 
125                 try {
126                         result = batchCommon.getForRoles().getRoleDisplayName().size() > 0;
127                 } catch (NullPointerException e) {
128                         // ignore exception, we're just testing to see if we have any list elements
129                 }
130                 
131                 return result;
132         }
133
134         /**
135          * The current user is authorized to run the batch job if:
136          *      1. No permissions or roles are specified in the batch job
137          *  2. No roles are specified, but permissions are specified and the current user has those permissions
138          *  3. Roles are specified and the current user is a member of at least one of the roles.
139          * 
140          * @param batchCommon
141          * @return
142          */
143         protected boolean isAuthoritzed(BatchCommon batchCommon) {
144                 boolean result = true;
145                 
146                 if (hasRequiredRoles(batchCommon)) { 
147                         result = isAuthorizedWithRoles(batchCommon);
148                 } else if (hasRequiredPermissions(batchCommon)) {
149                         result = isAuthoritzedWithPermissions(batchCommon);
150                 }
151                                 
152                 return result;
153         }
154         
155         protected boolean isAuthorizedWithRoles(BatchCommon batchCommon) {
156                 boolean result = false;
157                 
158                 ForRoles forRolesList = batchCommon.getForRoles();
159                 if (forRolesList != null) {
160                         AccountResource accountResource = new AccountResource();
161                         List<String> roleDisplayNameList = accountResource.getAccountRoles(AuthN.get().getUserId(), AuthN.get().getCurrentTenantId());
162                         for (String target : forRolesList.getRoleDisplayName()) {
163                                 if (roleDisplayNameList.contains(target)) {
164                                         result = true;
165                                         break;
166                                 }
167                         }
168                 }
169                 
170                 return result;
171         }
172         
173         /**
174          * Check to see if the current user is authorized to run/invoke this batch job.  If the batch job
175          * did not specify any permissions, we assume that the current user is authorized to run the job.
176          * @param batchCommon
177          * @return
178          */
179         protected boolean isAuthoritzedWithPermissions(BatchCommon batchCommon) {
180                 boolean result = true;
181                 
182                 ResourceActionGroupList resourceActionGroupList = batchCommon.getResourceActionGroupList();
183                 if (resourceActionGroupList != null) {
184                         String tenantId = AuthN.get().getCurrentTenantId();
185                         for (ResourceActionGroup resourceActionGroup: resourceActionGroupList.getResourceActionGroup()) {
186                                 String resourceName = resourceActionGroup.getResourceName();
187                                 ActionGroup actionGroup = ActionGroup.creatActionGroup(resourceActionGroup.getActionGroup());
188                                 for (ActionType actionType: actionGroup.getActions()) {
189                                         CSpaceResource res = new URIResourceImpl(tenantId, resourceName, AuthZ.getMethod(actionType));
190                                         if (AuthZ.get().isAccessAllowed(res) == false) {
191                                                 return false;
192                                         }
193                                 }
194                         }
195                 }
196                 
197                 return result;
198         }
199         
200         /**
201          * Returns a copy of the incoming list of strings all lower-cased.  Also removes any duplicates.
202          * 
203          * @param listOfStrings
204          * @return
205          */
206         private List<String> toLowerCase(List<String> listOfStrings) {
207                 List<String> result = null;
208                 
209                 if (listOfStrings != null) {
210                         Set<String> stringSet = new HashSet<String>();
211                         for (String s : listOfStrings) {
212                                 stringSet.add(s.toLowerCase());
213                         }
214                         result = new ArrayList<String>(stringSet);
215                 }
216                 
217                 return result;
218         }
219
220         public InvocationResults invokeBatchJob(ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx, String csid,
221                         ResourceMap resourceMap, InvocationContext invocationCtx, BatchCommon batchCommon) throws Exception {
222                 CoreSessionInterface repoSession = null;
223                 boolean releaseRepoSession = false;
224
225                 NuxeoRepositoryClientImpl repoClient = (NuxeoRepositoryClientImpl) this.getRepositoryClient(ctx);
226                 if (repoSession == null) {
227                         repoSession = repoClient.getRepositorySession(ctx);
228                         releaseRepoSession = true;
229                 }
230
231                 // Get properties from the batch docModel, and release the session
232                 try {
233                         //
234                         // Ensure the current user has permission to run this batch job
235                         if (isAuthoritzed(batchCommon) == false) {
236                                 String msg = String.format("BatchResource: The user '%s' does not have permission to run the batch job '%s' CSID='%s'", 
237                                                 AuthN.get().getUserId(), batchCommon.getName(), csid);
238                                 throw new PermissionException(msg);
239                         }
240                         
241                         //
242                         // Ensure the batch job supports the requested invocation context's mode type
243                         if (supportsInvokationMode(invocationCtx, batchCommon) == false) {
244                                 String msg = String.format("BatchResource: The batch job '%s' CSID='%s' does not support the invocation mode '%s'.", 
245                                                 batchCommon.getName(), csid, invocationCtx.getMode());
246                                 throw new BadRequestException(msg);
247                         }
248                         
249                         //
250                         // Ensure the batch job supports the requested invocation context's document type
251                         if (!Invocable.INVOCATION_MODE_NO_CONTEXT.equalsIgnoreCase(invocationCtx.getMode())) {
252                                 ForDocTypes forDocTypes = batchCommon.getForDocTypes();
253                                 if (forDocTypes != null) {
254                                         List<String> forDocTypeList = toLowerCase(forDocTypes.getForDocType()); // convert all strings to lowercase.
255                                         if (forDocTypeList == null || !forDocTypeList.contains(invocationCtx.getDocType().toLowerCase())) {
256                                                 String msg = String.format("BatchResource: The batch job '%s' CSID='%s' does not support the invocation document type '%s'.", 
257                                                                 batchCommon.getName(), csid, invocationCtx.getDocType());
258                                                 throw new BadRequestException(msg);
259                                         }
260                                 }
261                         }
262
263                         //
264                         // Now that we've ensure all the prerequisites have been met, let's try to
265                         // instantiate and run the batch job.
266                         //
267                         
268                         String className = batchCommon.getClassName().trim();
269                         ClassLoader tccl = Thread.currentThread().getContextClassLoader();
270                         Class<?> c = tccl.loadClass(className);
271                         tccl.setClassAssertionStatus(className, true);
272                         if (!BatchInvocable.class.isAssignableFrom(c)) {
273                                 throw new RuntimeException("BatchResource: Class: " + className + " does not implement BatchInvocable!");
274                         }
275         
276                         BatchInvocable batchInstance = (BatchInvocable) c.newInstance();
277                         List<String> modes = batchInstance.getSupportedInvocationModes();
278                         if (!modes.contains(invocationCtx.getMode().toLowerCase())) {
279                                 String msg = String.format("BatchResource: Invoked with unsupported mode '%s'.  Batch class '%s' supports these modes: %s.",
280                                                 invocationCtx.getMode().toLowerCase(), className, modes.toString());
281                                 throw new BadRequestException(msg);
282                         }
283         
284                         batchInstance.setInvocationContext(invocationCtx);
285                         batchInstance.setServiceContext(ctx);
286                         
287                         if (resourceMap != null) {
288                                 batchInstance.setResourceMap(resourceMap);
289                         } else {
290                                 resourceMap = ResteasyProviderFactory.getContextData(ResourceMap.class);
291                                 if (resourceMap != null) {
292                                         batchInstance.setResourceMap(resourceMap);
293                                 } else {
294                                         logger.warn("BatchResource.invoke did not get a resourceMapHolder in context!");
295                                 }
296                         }
297         
298                         batchInstance.run();
299                         int status = batchInstance.getCompletionStatus();
300                         if (status == Invocable.STATUS_ERROR) {
301                                 InvocationError error = batchInstance.getErrorInfo();
302                                 if (error.getResponseCode() == BAD_REQUEST_STATUS) {
303                                         throw new BadRequestException("BatchResouce: batchProcess encountered error: "
304                                                         + batchInstance.getErrorInfo());
305                                 } else {
306                                         throw new RuntimeException("BatchResouce: batchProcess encountered error: "
307                                                         + batchInstance.getErrorInfo());
308         
309                                 }
310                         }
311         
312                         InvocationResults results = batchInstance.getResults();
313                         return results;
314                 } catch (PermissionException e) {
315                         if (logger.isDebugEnabled()) {
316                                 logger.debug("BatchResource: Caught exception ", e);
317                         }
318                         throw e;
319                 } catch (Exception e) {
320                         if (logger.isDebugEnabled()) {
321                                 logger.debug("BatchResource: Caught exception ", e);
322                         }
323                         throw new DocumentException(e);
324                 } finally {
325                         if (releaseRepoSession && repoSession != null) {
326                                 repoClient.releaseRepositorySession(ctx, repoSession);
327                         }
328                 }
329         }
330 }