]> git.aero2k.de Git - tmp/jakarta-migration.git/blob
d618ea45389763ada0a4bb89fa5e26f45dd4cbdd
[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.authorization.storage;
25
26 import java.util.ArrayList;
27 import java.util.List;
28 import java.util.UUID;
29
30 import javax.persistence.EntityExistsException;
31 import javax.persistence.NoResultException;
32
33 import org.collectionspace.services.client.PermissionClient;
34 import org.collectionspace.services.client.PermissionClient.ActionCompare;
35
36 import org.collectionspace.services.authorization.perms.ActionType;
37 import org.collectionspace.services.authorization.CSpaceAction;
38 import org.collectionspace.services.authorization.perms.Permission;
39 import org.collectionspace.services.authorization.perms.PermissionAction;
40 import org.collectionspace.services.authorization.perms.PermissionsList;
41 import org.collectionspace.services.authorization.URIResourceImpl;
42
43 import org.collectionspace.services.common.context.ServiceContext;
44 import org.collectionspace.services.common.document.BadRequestException;
45 import org.collectionspace.services.common.document.DocumentException;
46 import org.collectionspace.services.common.document.DocumentFilter;
47 import org.collectionspace.services.common.document.DocumentWrapper;
48 import org.collectionspace.services.common.document.JaxbUtils;
49 import org.collectionspace.services.common.document.TransactionException;
50 import org.collectionspace.services.common.security.SecurityUtils;
51 import org.collectionspace.services.common.storage.jpa.JPATransactionContext;
52 import org.collectionspace.services.common.storage.jpa.JpaDocumentHandler;
53 import org.collectionspace.services.common.storage.jpa.JpaStorageUtils;
54
55 import org.slf4j.Logger;
56 import org.slf4j.LoggerFactory;
57
58 /**
59  * Document handler for Permission
60  * @author 
61  */
62 @SuppressWarnings("rawtypes")
63 public class PermissionDocumentHandler
64                 extends JpaDocumentHandler<Permission, PermissionsList, Permission, List<Permission>> {
65
66     private final Logger logger = LoggerFactory.getLogger(PermissionDocumentHandler.class);
67     private Permission permission;
68     private PermissionsList permissionsList;
69     
70     public CSpaceAction getAction(ActionType action) {
71         if (ActionType.CREATE.name().equals(action.name())) {
72             return CSpaceAction.CREATE;
73         } else if (ActionType.READ.equals(action)) {
74             return CSpaceAction.READ;
75         } else if (ActionType.UPDATE.equals(action)) {
76             return CSpaceAction.UPDATE;
77         } else if (ActionType.DELETE.equals(action)) {
78             return CSpaceAction.DELETE;
79         } else if (ActionType.SEARCH.equals(action)) {
80             return CSpaceAction.SEARCH;
81         } else if (ActionType.ADMIN.equals(action)) {
82             return CSpaceAction.ADMIN;
83         } else if (ActionType.START.equals(action)) {
84             return CSpaceAction.START;
85         } else if (ActionType.STOP.equals(action)) {
86             return CSpaceAction.STOP;
87         }
88         //
89         // We could not find a match, so we need to throw an exception.
90         //
91         throw new IllegalArgumentException("action = " + action.toString());
92     }
93     
94     /*
95      * Add the ACE hashed ID to the permission action so we can map the permission to the Spring Security
96      * tables.
97      */
98     private void handlePermissionActions(Permission perm) throws DocumentException {
99         //
100         // Verify the permission actions.  If the action group is missing, create it from the action list and vice versa.
101         //
102         ActionCompare compareResult = PermissionClient.validatePermActions(perm);
103         switch (compareResult) {
104                 case ACTIONS_MISSING:
105                         String msg = "Permission resource encountered with missing action group and action list.";
106                         throw new DocumentException(msg);
107                         
108                         case ACTION_GROUP_EMPTY:
109                                 String actionGroup = PermissionClient.getActionGroup(perm.getAction());
110                                 perm.setActionGroup(actionGroup);
111                                 break;
112                                 
113                         case ACTION_LIST_EMPTY:
114                                 List<PermissionAction> actionList = PermissionClient.getActionList(perm.getActionGroup());
115                                 perm.setAction(actionList);
116                                 break;
117                                 
118                         case MATCHES:
119                                 // all good
120                                 break;
121                                 
122                         case MISMATCHES:
123                                 msg = String.format("Permission has mismatching action group and action list.  Action group='%s' and Action list = '%s'.",
124                                                 perm.getActionGroup(), PermissionClient.getActionGroup(perm.getAction()));
125                                 throw new DocumentException(msg);
126         }
127         
128         List<PermissionAction> permActions = perm.getAction();
129         for (PermissionAction permAction : permActions) {
130             CSpaceAction action = getAction(permAction.getName());
131             URIResourceImpl uriRes = new URIResourceImpl(perm.getTenantId(), perm.getResourceName(), action);
132             permAction.setObjectIdentity(uriRes.getHashedId().toString());
133             permAction.setObjectIdentityResource(uriRes.getId());
134             //PermissionActionUtil.update(perm, permAction);
135         }
136     }
137     
138         private Permission findExistingPermission(Permission perm) throws TransactionException {
139         Permission result = null;
140         
141                 ServiceContext ctx = getServiceContext();
142                 String tenantId = ctx.getTenantId(); // we need a tenant ID 
143                 JPATransactionContext jpaTransactionContext = (JPATransactionContext)ctx.openConnection();
144                 try {
145                 result = (Permission)JpaStorageUtils.getEntityByDualKeys(jpaTransactionContext, 
146                                 Permission.class.getName(),
147                                 PermissionStorageConstants.RESOURCE_NAME, perm.getResourceName(), 
148                                 PermissionStorageConstants.ACTION_GROUP, perm.getActionGroup(),
149                                 tenantId);
150                 } catch (NoResultException e) {
151                         if (logger.isTraceEnabled()) {
152                                 String msg = String.format("Looked for but could not find permission with resource name = '%s', action group = '%s', tenat ID = '%s'.",
153                                                 perm.getResourceName(), perm.getActionGroup(), tenantId);
154                                 logger.trace(msg);
155                         }
156                 } finally {
157                         ctx.closeConnection();
158                 }
159
160         return result;
161     }
162
163     @Override
164     public void handleCreate(DocumentWrapper<Permission> wrapDoc) throws EntityExistsException, DocumentException {
165         //
166         // First check to see if an equivalent permission exists
167         //
168         Permission permission = wrapDoc.getWrappedObject();     
169         Permission existingPermission = findExistingPermission(permission);
170
171         if (existingPermission == null) {
172                 String id = UUID.randomUUID().toString();        
173                 permission.setCsid(id);
174                 setTenant(permission);
175                 handlePermissionActions(permission);
176         } else {
177                 String msg = String.format("Found existing permission with resource name = '%s', action group = '%s', and tenant ID = '%s'.",
178                                 existingPermission.getResourceName(), existingPermission.getActionGroup(), existingPermission.getTenantId());
179                 wrapDoc.resetWrapperObject(existingPermission); // update the wrapped document with the existing permission instance
180                 throw new EntityExistsException(msg);
181         }
182     }
183
184     @Override
185     public void completeCreate(DocumentWrapper<Permission> wrapDoc) throws Exception {
186     }
187
188     @Override
189     public void handleUpdate(DocumentWrapper<Permission> wrapDoc) throws Exception {
190         Permission permissionFound = wrapDoc.getWrappedObject();
191         Permission permissionReceived = getCommonPart();
192         merge(permissionReceived, permissionFound);
193     }
194
195     /**
196      * merge manually merges the from from to the to permission
197      * -this method is created due to inefficiency of JPA EM merge
198      * @param from
199      * @param to
200      * @return merged permission
201      */
202     private Permission merge(Permission from, Permission to) throws Exception {
203         if (!(from.getResourceName().equalsIgnoreCase(to.getResourceName()))) {
204             String msg = "Resource name cannot be changed " + to.getResourceName();
205             logger.error(msg);
206             throw new BadRequestException(msg);
207         }
208         //resource name, attribute  cannot be changed
209
210         if (from.getDescription() != null) {
211             to.setDescription(from.getDescription());
212         }
213         if (from.getEffect() != null) {
214             to.setEffect(from.getEffect());
215         }
216         List<PermissionAction> fromActions = from.getAction();
217         if (!fromActions.isEmpty()) {
218             // Override the whole list, no reconciliation by design
219             to.setAction(fromActions);
220             // Update the actionGroup field to reflect the new action list
221             to.setActionGroup(PermissionClient.getActionGroup(fromActions));
222         }
223
224         if (logger.isDebugEnabled()) {
225             logger.debug("merged permission=" + JaxbUtils.toString(to, Permission.class));
226         }
227
228         handlePermissionActions(to);
229         return to;
230     }
231
232     @SuppressWarnings("unchecked")
233         @Override
234     public void completeUpdate(DocumentWrapper<Permission> wrapDoc) throws Exception {
235         Permission upAcc = wrapDoc.getWrappedObject();
236         getServiceContext().setOutput(upAcc);
237         sanitize(upAcc);
238         //FIXME update lower-layer authorization (acls)
239         //will require deleting old permissions for this resource and adding
240         //new based on new actions and effect
241     }
242
243     @SuppressWarnings("unchecked")
244         @Override
245     public void handleGet(DocumentWrapper<Permission> wrapDoc) throws Exception {
246         setCommonPart(extractCommonPart(wrapDoc));
247         sanitize(getCommonPart());
248         getServiceContext().setOutput(permission);
249     }
250
251     @SuppressWarnings("unchecked")
252         @Override
253     public void handleGetAll(DocumentWrapper<List<Permission>> wrapDoc) throws Exception {
254         PermissionsList permissionsList = extractCommonPartList(wrapDoc);
255         setCommonPartList(permissionsList);
256         getServiceContext().setOutput(getCommonPartList());
257     }
258
259     @Override
260     public void completeDelete(DocumentWrapper<Permission> wrapDoc) throws Exception {
261     }
262
263     /*
264      * See https://issues.collectionspace.org/browse/DRYD-181
265      * 
266      * For backward compatibility, we could not change the permission list to be a child class of AbstractCommonList.  This
267      * would have change the result payload and would break existing API clients.  So the best we can do, it treat
268      * the role list payload as a special case and return the paging information.
269      * 
270      */
271         protected PermissionsList extractPagingInfoForPerms(PermissionsList permList, DocumentWrapper<List<Permission>> wrapDoc)
272             throws Exception {
273
274         DocumentFilter docFilter = this.getDocumentFilter();
275         long pageSize = docFilter.getPageSize();
276         long pageNum = pageSize != 0 ? docFilter.getOffset() / pageSize : pageSize;
277         // set the page size and page number
278         permList.setPageNum(pageNum);
279         permList.setPageSize(pageSize);
280         List<Permission> docList = wrapDoc.getWrappedObject();
281         // Set num of items in list. this is useful to our testing framework.
282         permList.setItemsInPage(docList.size());
283         // set the total result size
284         permList.setTotalItems(docFilter.getTotalItemsResult());
285
286         return permList;
287     }
288         
289     @Override
290     public Permission extractCommonPart(
291             DocumentWrapper<Permission> wrapDoc)
292             throws Exception {
293         return wrapDoc.getWrappedObject();
294     }
295
296     @Override
297     public void fillCommonPart(Permission obj, DocumentWrapper<Permission> wrapDoc)
298             throws Exception {
299         throw new UnsupportedOperationException("operation not relevant for AccountDocumentHandler");
300     }
301
302     @Override
303     public PermissionsList extractCommonPartList(
304             DocumentWrapper<List<Permission>> wrapDoc)
305             throws Exception {
306
307         PermissionsList permissionsList = extractPagingInfoForPerms(new PermissionsList(), wrapDoc);
308         List<Permission> list = new ArrayList<Permission>();
309         permissionsList.setPermission(list);
310         for (Object obj : wrapDoc.getWrappedObject()) {
311             Permission permission = (Permission) obj;
312             sanitize(permission);
313             list.add(permission);
314         }
315         
316         return permissionsList;
317     }
318
319     @Override
320     public Permission getCommonPart() {
321         return permission;
322     }
323
324     @Override
325     public void setCommonPart(Permission permission) {
326         this.permission = permission;
327     }
328
329     @Override
330     public PermissionsList getCommonPartList() {
331         return permissionsList;
332     }
333
334     @Override
335     public void setCommonPartList(PermissionsList permissionsList) {
336         this.permissionsList = permissionsList;
337     }
338
339     @Override
340     public String getQProperty(
341             String prop) {
342         return null;
343     }
344
345     @Override
346     public DocumentFilter createDocumentFilter() {
347         DocumentFilter filter = new PermissionJpaFilter(this.getServiceContext());
348         return filter;
349     }
350
351     /**
352      * Sanitize removes data not needed to be sent to the consumer
353      * @param permission
354      */
355     private void sanitize(Permission permission) {
356         if (!SecurityUtils.isCSpaceAdmin()) {
357             // permission.setTenantId(null); // REM - Why are we removing the tenant ID from the payload? Commenting out this line for now.
358         }
359     }
360
361     private void setTenant(Permission permission) {
362         //set tenant only if not available from input
363         if (permission.getTenantId() == null || permission.getTenantId().isEmpty()) {
364             permission.setTenantId(getServiceContext().getTenantId());
365         }
366     }
367 }