]> git.aero2k.de Git - tmp/jakarta-migration.git/blob
a6979ae43002af7fb4983dc00cc9feee9b638a2d
[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.api.Tools;
53 import org.collectionspace.services.common.authorization_mgt.ActionGroup;
54 import org.collectionspace.services.common.context.ServiceContext;
55 import org.collectionspace.services.common.document.BadRequestException;
56 import org.collectionspace.services.common.document.DocumentException;
57 import org.collectionspace.services.common.invocable.Invocable;
58 import org.collectionspace.services.common.invocable.InvocationContext;
59 import org.collectionspace.services.common.invocable.InvocationResults;
60 import org.collectionspace.services.common.invocable.Invocable.InvocationError;
61
62 import org.jboss.resteasy.spi.ResteasyProviderFactory;
63 import org.slf4j.Logger;
64 import org.slf4j.LoggerFactory;
65
66 public class BatchDocumentModelHandler extends NuxeoDocumentModelHandler<BatchCommon> {
67         private final Logger logger = LoggerFactory.getLogger(BatchDocumentModelHandler.class);
68
69         protected final int BAD_REQUEST_STATUS = Response.Status.BAD_REQUEST.getStatusCode();
70
71         /**
72          * Return true if the batch job supports the requested mode.
73          * @param invocationCtx
74          * @param batchCommon
75          * @return
76          * @throws BadRequestException
77          */
78         protected boolean supportsInvokationMode(InvocationContext invocationCtx, BatchCommon batchCommon) throws BadRequestException {
79                 boolean result = false;
80
81                 String invocationMode = invocationCtx.getMode().toLowerCase();
82                 if (BatchInvocable.INVOCATION_MODE_SINGLE.equalsIgnoreCase(invocationMode)) {
83                         result = batchCommon.isSupportsSingleDoc(); //BatchJAXBSchema.SUPPORTS_SINGLE_DOC;
84                 } else if (BatchInvocable.INVOCATION_MODE_LIST.equalsIgnoreCase(invocationMode)) {
85                         result = batchCommon.isSupportsDocList(); //BatchJAXBSchema.SUPPORTS_DOC_LIST;
86                 } else if (BatchInvocable.INVOCATION_MODE_GROUP.equalsIgnoreCase(invocationMode)) {
87                         result = batchCommon.isSupportsGroup(); //BatchJAXBSchema.SUPPORTS_GROUP;
88                 } else if (Invocable.INVOCATION_MODE_NO_CONTEXT.equalsIgnoreCase(invocationMode)) {
89                         result = batchCommon.isSupportsNoContext(); //InvocableJAXBSchema.SUPPORTS_NO_CONTEXT;
90                 } else {
91                         String msg = String.format("BatchResource: Unknown invocation mode '%s' requested trying to invoke batch job '%s'.",
92                                         invocationMode, batchCommon.getName());
93                         throw new BadRequestException(msg);
94                 }
95
96                 return result;
97         }
98
99         /**
100          * Returns true if we found any required permissions.
101          *
102          * @param batchCommon
103          * @return
104          */
105         private boolean hasRequiredPermissions(BatchCommon batchCommon) {
106                 boolean result = false;
107
108                 try {
109                         result = batchCommon.getResourceActionGroupList().getResourceActionGroup().size() > 0;
110                 } catch (NullPointerException e) {
111                         // ignore exception, we're just testing to see if we have any list elements
112                 }
113
114                 return result;
115         }
116
117         /**
118          * Returns true if we found any required roles.
119          *
120          * @param batchCommon
121          * @return
122          */
123         private boolean hasRequiredRoles(BatchCommon batchCommon) {
124                 boolean result = false;
125
126                 try {
127                         result = batchCommon.getForRoles().getRoleDisplayName().size() > 0;
128                 } catch (NullPointerException e) {
129                         // ignore exception, we're just testing to see if we have any list elements
130                 }
131
132                 return result;
133         }
134
135         /**
136          * The current user is authorized to run the batch job if:
137          *      1. No permissions or roles are specified in the batch job
138          *  2. No roles are specified, but permissions are specified and the current user has those permissions
139          *  3. Roles are specified and the current user is a member of at least one of the roles.
140          *
141          * @param batchCommon
142          * @return
143          */
144         protected boolean isAuthoritzed(BatchCommon batchCommon) {
145                 boolean result = true;
146
147                 if (hasRequiredRoles(batchCommon)) {
148                         result = isAuthorizedWithRoles(batchCommon);
149                 } else if (hasRequiredPermissions(batchCommon)) {
150                         result = isAuthoritzedWithPermissions(batchCommon);
151                 }
152
153                 return result;
154         }
155
156         protected boolean isAuthorizedWithRoles(BatchCommon batchCommon) {
157                 boolean result = false;
158
159                 ForRoles forRolesList = batchCommon.getForRoles();
160                 if (forRolesList != null) {
161                         AccountResource accountResource = new AccountResource();
162                         List<String> roleDisplayNameList = accountResource.getAccountRoleDisplayNames(AuthN.get().getUserId(), AuthN.get().getCurrentTenantId());
163                         for (String target : forRolesList.getRoleDisplayName()) {
164                                 if (Tools.listContainsIgnoreCase(roleDisplayNameList, target)) {
165                                         result = true;
166                                         break;
167                                 }
168                         }
169                 }
170
171                 return result;
172         }
173
174         /**
175          * Check to see if the current user is authorized to run/invoke this batch job.  If the batch job
176          * did not specify any permissions, we assume that the current user is authorized to run the job.
177          * @param batchCommon
178          * @return
179          */
180         protected boolean isAuthoritzedWithPermissions(BatchCommon batchCommon) {
181                 boolean result = true;
182
183                 ResourceActionGroupList resourceActionGroupList = batchCommon.getResourceActionGroupList();
184                 if (resourceActionGroupList != null) {
185                         String tenantId = AuthN.get().getCurrentTenantId();
186                         for (ResourceActionGroup resourceActionGroup: resourceActionGroupList.getResourceActionGroup()) {
187                                 String resourceName = resourceActionGroup.getResourceName();
188                                 ActionGroup actionGroup = ActionGroup.creatActionGroup(resourceActionGroup.getActionGroup());
189                                 for (ActionType actionType: actionGroup.getActions()) {
190                                         CSpaceResource res = new URIResourceImpl(tenantId, resourceName, AuthZ.getMethod(actionType));
191                                         if (AuthZ.get().isAccessAllowed(res) == false) {
192                                                 return false;
193                                         }
194                                 }
195                         }
196                 }
197
198                 return result;
199         }
200
201         /**
202          * Returns a copy of the incoming list of strings all lower-cased.  Also removes any duplicates.
203          *
204          * @param listOfStrings
205          * @return
206          */
207         private List<String> toLowerCase(List<String> listOfStrings) {
208                 List<String> result = null;
209
210                 if (listOfStrings != null) {
211                         Set<String> stringSet = new HashSet<String>();
212                         for (String s : listOfStrings) {
213                                 stringSet.add(s.toLowerCase());
214                         }
215                         result = new ArrayList<String>(stringSet);
216                 }
217
218                 return result;
219         }
220
221         public InvocationResults invokeBatchJob(ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx, String csid,
222                         ResourceMap resourceMap, InvocationContext invocationCtx, BatchCommon batchCommon) throws Exception {
223                 CoreSessionInterface repoSession = null;
224                 boolean releaseRepoSession = false;
225
226                 NuxeoRepositoryClientImpl repoClient = (NuxeoRepositoryClientImpl) this.getRepositoryClient(ctx);
227                 if (repoSession == null) {
228                         repoSession = repoClient.getRepositorySession(ctx);
229                         releaseRepoSession = true;
230                 }
231
232                 // Get properties from the batch docModel, and release the session
233                 try {
234                         //
235                         // Ensure the current user has permission to run this batch job
236                         if (isAuthoritzed(batchCommon) == false) {
237                                 String msg = String.format("BatchResource: The user '%s' does not have permission to run the batch job '%s' CSID='%s'",
238                                                 AuthN.get().getUserId(), batchCommon.getName(), csid);
239                                 throw new PermissionException(msg);
240                         }
241
242                         //
243                         // Ensure the batch job supports the requested invocation context's mode type
244                         if (supportsInvokationMode(invocationCtx, batchCommon) == false) {
245                                 String msg = String.format("BatchResource: The batch job '%s' CSID='%s' does not support the invocation mode '%s'.",
246                                                 batchCommon.getName(), csid, invocationCtx.getMode());
247                                 throw new BadRequestException(msg);
248                         }
249
250                         //
251                         // Ensure the batch job supports the requested invocation context's document type
252                         if (!Invocable.INVOCATION_MODE_NO_CONTEXT.equalsIgnoreCase(invocationCtx.getMode())) {
253                                 ForDocTypes forDocTypes = batchCommon.getForDocTypes();
254                                 if (forDocTypes != null) {
255                                         List<String> forDocTypeList = toLowerCase(forDocTypes.getForDocType()); // convert all strings to lowercase.
256                                         if (forDocTypeList == null || !forDocTypeList.contains(invocationCtx.getDocType().toLowerCase())) {
257                                                 String msg = String.format("BatchResource: The batch job '%s' CSID='%s' does not support the invocation document type '%s'.",
258                                                                 batchCommon.getName(), csid, invocationCtx.getDocType());
259                                                 throw new BadRequestException(msg);
260                                         }
261                                 }
262                         }
263
264                         //
265                         // Now that we've ensure all the prerequisites have been met, let's try to
266                         // instantiate and run the batch job.
267                         //
268
269                         String className = batchCommon.getClassName().trim();
270                         ClassLoader tccl = Thread.currentThread().getContextClassLoader();
271                         Class<?> c = tccl.loadClass(className);
272                         tccl.setClassAssertionStatus(className, true);
273                         if (!BatchInvocable.class.isAssignableFrom(c)) {
274                                 throw new RuntimeException("BatchResource: Class: " + className + " does not implement BatchInvocable!");
275                         }
276
277                         BatchInvocable batchInstance = (BatchInvocable) c.newInstance();
278                         List<String> modes = batchInstance.getSupportedInvocationModes();
279                         if (!modes.contains(invocationCtx.getMode().toLowerCase())) {
280                                 String msg = String.format("BatchResource: Invoked with unsupported mode '%s'.  Batch class '%s' supports these modes: %s.",
281                                                 invocationCtx.getMode().toLowerCase(), className, modes.toString());
282                                 throw new BadRequestException(msg);
283                         }
284
285                         batchInstance.setInvocationContext(invocationCtx);
286                         batchInstance.setServiceContext(ctx);
287
288                         if (resourceMap != null) {
289                                 batchInstance.setResourceMap(resourceMap);
290                         } else {
291                                 resourceMap = ResteasyProviderFactory.getContextData(ResourceMap.class);
292                                 if (resourceMap != null) {
293                                         batchInstance.setResourceMap(resourceMap);
294                                 } else {
295                                         logger.warn("BatchResource.invoke did not get a resourceMapHolder in context!");
296                                 }
297                         }
298
299                         try {
300                                 batchInstance.run(batchCommon);
301                         } catch (UnsupportedOperationException t) {
302                                 // Support for run() will be deprecated in a future release.  See DRYD-878
303                                 logger.debug(t.getMessage());
304                                 batchInstance.run();
305                         }
306
307                         int status = batchInstance.getCompletionStatus();
308                         if (status == Invocable.STATUS_ERROR) {
309                                 InvocationError error = batchInstance.getErrorInfo();
310                                 if (error.getResponseCode() == BAD_REQUEST_STATUS) {
311                                         throw new BadRequestException("BatchResouce: batchProcess encountered error: "
312                                                         + batchInstance.getErrorInfo());
313                                 } else {
314                                         throw new RuntimeException("BatchResouce: batchProcess encountered error: "
315                                                         + batchInstance.getErrorInfo());
316
317                                 }
318                         }
319
320                         InvocationResults results = batchInstance.getResults();
321                         return results;
322                 } catch (PermissionException e) {
323                         if (logger.isDebugEnabled()) {
324                                 logger.debug("BatchResource: Caught exception ", e);
325                         }
326                         throw e;
327                 } catch (Exception e) {
328                         if (logger.isDebugEnabled()) {
329                                 logger.debug("BatchResource: Caught exception ", e);
330                         }
331                         throw new DocumentException(e);
332                 } finally {
333                         if (releaseRepoSession && repoSession != null) {
334                                 repoClient.releaseRepositorySession(ctx, repoSession);
335                         }
336                 }
337         }
338 }