]> git.aero2k.de Git - tmp/jakarta-migration.git/blob
9a66032ebda93a684ff68a40954333fec043cc6e
[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.common.authorization_mgt;
25
26 import java.util.ArrayList;
27 import java.util.HashMap;
28 import java.util.List;
29 import javax.persistence.NoResultException;
30
31 import org.collectionspace.services.common.document.DocumentException;
32 import org.collectionspace.services.common.document.DocumentNotFoundException;
33 import org.collectionspace.services.common.document.JaxbUtils;
34 import org.collectionspace.services.common.api.Tools;
35 import org.collectionspace.services.common.context.ServiceContext;
36 import org.collectionspace.services.common.context.ServiceContextProperties;
37 import org.collectionspace.services.common.storage.jpa.JPATransactionContext;
38 import org.collectionspace.services.common.storage.jpa.JpaStorageUtils;
39
40 import org.collectionspace.services.client.RoleClient;
41 import org.collectionspace.authentication.AuthN;
42
43 import org.collectionspace.services.authorization.perms.ActionType;
44 import org.collectionspace.services.authorization.perms.EffectType;
45 import org.collectionspace.services.authorization.perms.Permission;
46 import org.collectionspace.services.authorization.perms.PermissionAction;
47 import org.collectionspace.services.authorization.storage.PermissionStorageConstants;
48 import org.collectionspace.services.authorization.storage.RoleStorageConstants;
49 import org.collectionspace.services.authorization.PermissionResource;
50 import org.collectionspace.services.authorization.PermissionRole;
51 import org.collectionspace.services.authorization.PermissionRoleRel;
52 import org.collectionspace.services.authorization.PermissionValue;
53 import org.collectionspace.services.authorization.Role;
54 import org.collectionspace.services.authorization.RoleValue;
55 import org.collectionspace.services.authorization.SubjectType;
56
57 import org.slf4j.Logger;
58 import org.slf4j.LoggerFactory;
59
60 // TODO: Auto-generated Javadoc
61 /**
62  * The Class PermissionRoleUtil.
63  *
64  * @author
65  */
66 public class PermissionRoleUtil {
67
68     static final Logger logger = LoggerFactory.getLogger(PermissionRoleUtil.class);
69
70     /**
71      * Gets the relation subject.
72      *
73      * @param ctx the ctx
74      * @return the relation subject
75      */
76     static public SubjectType getRelationSubject(ServiceContext<?, ?> ctx) {
77         Object o = ctx.getProperty(ServiceContextProperties.SUBJECT);
78         if (o == null) {
79             throw new IllegalArgumentException(ServiceContextProperties.SUBJECT
80                     + " property is missing in context "
81                     + ctx.toString());
82         }
83         return (SubjectType) o;
84     }
85
86     /**
87      * Gets the relation subject.
88      *
89      * @param ctx the ctx
90      * @param pr the pr
91      * @return the relation subject
92      */
93     static public SubjectType getRelationSubject(ServiceContext<?, ?> ctx, PermissionRole pr) {
94         SubjectType subject = pr.getSubject();
95         if (subject == null) {
96             //it is not required to give subject as URI determines the subject
97             subject = getRelationSubject(ctx);
98         }
99         return subject;
100     }
101
102     /**
103      * buildPermissionRoleRel builds persistent relationship entities from given
104      * permissionrole.
105      *
106      * @param pr permissionrole
107      * @param subject the subject
108      * @param permRoleRelationshipList persistent entities built are inserted into this list
109      * @param toDelete the to delete
110      */
111     static public void buildPermissionRoleRel(JPATransactionContext jpaTransactionContext, 
112                 PermissionRole pr,
113                 SubjectType subject,
114                 List<PermissionRoleRel> permRoleRelationshipList,
115                 boolean handleDelete,
116                 String tenantId) throws Exception {
117         
118         if (subject.equals(SubjectType.ROLE)) {
119                 List<PermissionValue> permissionValues = pr.getPermission();
120                 if (permissionValues != null && permissionValues.size() == 1) {
121                     PermissionValue pv = permissionValues.get(0);
122                     for (RoleValue rv : pr.getRole()) {
123                         PermissionRoleRel permRoleRelationship = buildPermissonRoleRel(jpaTransactionContext, pv, rv, subject, handleDelete, tenantId);
124                         permRoleRelationshipList.add(permRoleRelationship);
125                     }
126                 } else {
127                         String msg = "There must be one and only one Permission supplied in the payload when creating this Permission-Roles relationshiop.";
128                         throw new DocumentException(msg);
129                 }
130         } else if (subject.equals(SubjectType.PERMISSION)) {
131                 List<RoleValue> roleValues = pr.getRole();
132                 if (roleValues != null && roleValues.size() == 1) {
133                     RoleValue rv = roleValues.get(0);
134                     for (PermissionValue pv : pr.getPermission()) {
135                         PermissionRoleRel prr = buildPermissonRoleRel(jpaTransactionContext, pv, rv, subject, handleDelete, tenantId);
136                         permRoleRelationshipList.add(prr);
137                     }
138                 } else {
139                         String msg = "There must be one and only one Role supplied in the payload when creating this Role-Permissions relationshiop.";
140                         throw new DocumentException(msg);
141                 }
142         }
143     }
144     
145     static public void buildPermissionRoleRel(
146                 ServiceContext<?, ?> ctx,
147                 PermissionRole pr,
148                 SubjectType subject,
149                 List<PermissionRoleRel> prrl,
150                 boolean handleDelete,
151                 String tenantId) throws Exception {
152         
153         JPATransactionContext jpaTransactionContext = (JPATransactionContext)ctx.openConnection();
154         try {
155             jpaTransactionContext.beginTransaction();            
156             buildPermissionRoleRel(jpaTransactionContext, pr, subject, prrl, handleDelete, tenantId);
157             jpaTransactionContext.commitTransaction();
158         } catch (Exception e) {
159                 jpaTransactionContext.markForRollback();
160             if (logger.isDebugEnabled()) {
161                 logger.debug("Caught exception ", e);
162             }
163             throw e;
164         } finally {
165             ctx.closeConnection();
166         }
167     }    
168
169     /*
170      * Try to find a persisted Permission record using a PermissionValue instance.
171      *
172      */
173     static private Permission lookupPermission(JPATransactionContext jpaTransactionContext, PermissionValue permissionValue, String tenantId) throws DocumentException {
174         Permission result = null;
175         
176         String actionGroup = permissionValue.getActionGroup() != null ? permissionValue.getActionGroup().trim() : null;
177         String resourceName = permissionValue.getResourceName() != null ? permissionValue.getResourceName().trim() : null;
178         String permissionId = permissionValue.getPermissionId() != null ? permissionValue.getPermissionId().trim() : null;
179         //
180         // If we have a permission ID, use it to try to lookup the persisted permission
181         //
182         if (permissionId != null && !permissionId.isEmpty()) {
183                 try {
184                         result = (Permission)JpaStorageUtils.getEntityByDualKeys(
185                                         jpaTransactionContext, 
186                                         Permission.class.getName(),
187                                         PermissionStorageConstants.ID, permissionId, 
188                                         PermissionStorageConstants.TENANT_ID, tenantId);
189                 } catch (Throwable e) {
190                         String msg = String.format("Searched for but couldn't find a permission with CSID='%s'.",
191                                         permissionId);
192                         logger.trace(msg);
193                 }
194         } else if (Tools.notBlank(resourceName) && Tools.notBlank(actionGroup)) {
195                 //
196                 // If there was no permission ID, then we can try to find the permission with the resource name and action group tuple
197                 //
198                 try {
199                         result = (Permission)JpaStorageUtils.getEntityByDualKeys(
200                                         jpaTransactionContext, 
201                                         Permission.class.getName(),
202                                         PermissionStorageConstants.RESOURCE_NAME, permissionValue.getResourceName(), 
203                                         PermissionStorageConstants.ACTION_GROUP, permissionValue.getActionGroup(),
204                                         tenantId);
205                 } catch (NoResultException e) {
206                         String msg = String.format("Searched for but couldn't find a permission for resource='%s', action group='%s', and tenant ID='%s'.",
207                                         permissionValue.getResourceName(), permissionValue.getActionGroup(), tenantId);
208                         logger.trace(msg);
209                 }
210         } else {
211                 String errMsg = String.format("Couldn't perform lookup of permission.  Not enough information provided.  Lookups requires a permission CSID or a resource name and action group tuple.  The provided information was permission ID='%s', resourceName='%s', and actionGroup='%s'.",
212                                 permissionId, resourceName, actionGroup);
213                 throw new DocumentException(errMsg);
214         }
215         
216         if (result == null) {
217                 throw new DocumentNotFoundException(String.format("Could not find Permission resource with CSID='%s', actionGroup='%s', resourceName='%s'.", 
218                                 permissionId, actionGroup, resourceName));
219         }
220         
221         return result;
222     }
223     /**
224      * Ensure the Role's permission relationships can be changed.
225      * 
226      * @param role
227      * @return
228      */
229     static private boolean canRoleRelatedTo(Role role) {
230         boolean result = true;
231         
232         if (RoleClient.IMMUTABLE.equals(role.getPermsProtection()) && !AuthN.get().isSystemAdmin()) {
233                 result = false;
234         }
235         
236         return result;
237     }
238     
239     /**
240      * Builds a permisson role relationship for either 'create' or 'delete'
241      *
242      * @param pv the pv (currently using only the ID)
243      * @param rv the rv (currently using only the ID)
244      * @param handleDelete the handle delete
245      * @return the permission role rel
246      * @throws DocumentException 
247      */
248     static private PermissionRoleRel buildPermissonRoleRel(JPATransactionContext jpaTransactionContext, PermissionValue permissionValue,
249                 RoleValue roleValue,
250                 SubjectType subject,
251                 boolean handleDelete,  // if 'true' then we're deleting not building a permission-role record
252                 String tenantId) throws DocumentException {
253
254         PermissionRoleRel result = null;
255         Role role = lookupRole(jpaTransactionContext, roleValue, tenantId);
256         //
257         // Ensure we can change the Role's permissions-related relationships.
258         //
259         if (canRoleRelatedTo(role) == false) {
260                 String msg = String.format("Role with CSID='%s' cannot have its associated permissions changed.", role.getCsid());
261                 throw new DocumentException(msg);
262         }
263         //
264         // Get the permission info
265         //
266         Permission permission = lookupPermission(jpaTransactionContext, permissionValue, tenantId);
267         //
268         // If we couldn't find an existing permission and we're not processing a DELETE request, we need to create
269         // a new permission.
270         //
271         if (permission == null && handleDelete == false) {
272                 permission = new Permission();
273                 permission.setResourceName(permissionValue.getResourceName());
274                 permission.setActionGroup(permissionValue.getActionGroup());
275                 permission.setEffect(EffectType.PERMIT); // By default, CollectionSpace currently (11/2017) supports only PERMIT
276                 List<PermissionAction> actionList = createPermActionList(permissionValue.getActionGroup());
277                 permission.setAction(actionList);
278                 permission = createPermission(jpaTransactionContext, permission);
279                 if (permission == null) {
280                         String errMsg = "Could not create new permission for new permission-role relationship.";
281                         throw new DocumentException(errMsg);
282                 }
283         } else if (permission == null && handleDelete == true) {
284                 String msg = String.format("Could not find an existing permission that matches this: %s", 
285                                 JaxbUtils.toString(permissionValue, PermissionValue.class));
286                 throw new DocumentException(msg);
287         }
288         
289         //
290         // Since our permissionValue may not have been supplied by the client with an ID, we need
291         // to add it now.
292         //
293         if (permissionValue.getPermissionId() == null || permissionValue.getPermissionId().trim().isEmpty()) {
294                 permissionValue.setPermissionId(permission.getCsid());
295         }
296         
297         //
298         // Create the permission-role to persist
299         //
300         result = new PermissionRoleRel();
301         result.setPermissionId(permission.getCsid());
302         result.setPermissionResource(permission.getResourceName());
303         result.setActionGroup(permission.getActionGroup());
304         result.setRoleId(roleValue.getRoleId());
305         result.setRoleName(roleValue.getRoleName());
306         
307         //
308         // For 'delete' we need to set the hjid of the existing relstionship
309         //
310         String relationshipId = null;
311         if (subject.equals(SubjectType.ROLE) == true) {
312                 relationshipId = roleValue.getRoleRelationshipId();
313         } else if (subject.equals(SubjectType.PERMISSION) == true) {
314                 relationshipId = permissionValue.getPermRelationshipId();
315         }
316         if (relationshipId != null && handleDelete == true) {
317                 result.setHjid(Long.parseLong(relationshipId));  // set this so we can convince JPA to del the relation
318         }
319         
320         return result;
321     }
322     
323     public static RoleValue fetchRoleValue(ServiceContext<?, ?> ctx, String roleId) throws DocumentNotFoundException {
324         RoleValue result = null;
325         
326         JPATransactionContext jpaTransactionContext = (JPATransactionContext) ctx.getCurrentTransactionContext();
327         Role role = lookupRole(jpaTransactionContext, roleId, ctx.getTenantId());
328         result = AuthorizationRoleRel.buildRoleValue(role);
329         
330         return result;
331     }
332
333     private static Role lookupRole(JPATransactionContext jpaTransactionContext, RoleValue roleValue, String tenantId) throws DocumentNotFoundException {
334         return lookupRole(jpaTransactionContext, roleValue.getRoleId(), tenantId);
335         }
336     
337     private static Role lookupRole(JPATransactionContext jpaTransactionContext, String roleId, String tenantId) throws DocumentNotFoundException {
338         Role result = null;
339         
340         try {
341                 result = (Role)JpaStorageUtils.getEntityByDualKeys(
342                                                 jpaTransactionContext,
343                                         Role.class.getName(),
344                                         RoleStorageConstants.ROLE_ID, roleId, 
345                                         RoleStorageConstants.ROLE_TENANT_ID, tenantId);
346         } catch (Throwable e) {
347                 String msg = String.format("Searched for but couldn't find a role with CSID='%s'.",
348                                 roleId);
349                 logger.trace(msg);
350         }
351         
352         if (result == null) {
353                 String msg = String.format("Could not find Role resource with CSID='%s'", roleId);
354                 throw new DocumentNotFoundException(msg);
355         }
356         
357         return result;
358         }
359     
360
361         private static Permission createPermission(JPATransactionContext jpaTransactionContext, Permission permission) {
362                 Permission result = null;
363                 
364                 PermissionResource permissionResource = new PermissionResource();  // Get the PermissionResource singleton instance (RESTEasy ensures it is a singleton)
365                 result = permissionResource.createPermissionFromInstance(jpaTransactionContext, permission);
366                 
367                 return result;
368         }
369
370         private static List<PermissionAction> createPermActionList(String actionGroup) throws DocumentException {
371         ArrayList<PermissionAction> result = new ArrayList<PermissionAction>();
372         
373         for (char c : actionGroup.toUpperCase().toCharArray()) {
374                 PermissionAction permAction = new PermissionAction();
375                 switch (c) {
376                         case 'C':
377                                 permAction.setName(ActionType.CREATE);
378                                 break;
379                                 
380                         case 'R':
381                                 permAction.setName(ActionType.READ);
382                                 break;
383                                 
384                         case 'U':
385                                 permAction.setName(ActionType.UPDATE);
386                                 break;
387                                 
388                         case 'D':
389                                 permAction.setName(ActionType.DELETE);
390                                 break;
391                                 
392                         case 'L':
393                                 permAction.setName(ActionType.SEARCH);
394                                 break;
395                                 
396                         default:
397                                 String errMsg = String.format("Illegal action group token '%c' in permission action group '%s'.",
398                                                 c, actionGroup);
399                                 throw new DocumentException(errMsg);
400                 }
401                 
402                 if (result.add(permAction) == false) {
403                         String warnMsg = String.format("Illegal or duplicate action group token '%c' in permission action group '%s'.",
404                                         c, actionGroup);
405                         logger.warn(warnMsg);
406                 }
407         }
408         
409                 return result;
410         }
411         
412         static public boolean isEmpty(PermissionRole permRole) {
413                 boolean result = true;
414                 
415                 if (permRole != null && !Tools.isEmpty(permRole.getPermission()) 
416                                 && !Tools.isEmpty(permRole.getRole()) && permRole.getSubject() != null) {
417                         result = false;
418                 }
419                 
420                 return result;
421         }
422
423         /**
424      * Checks if is invalid tenant.
425      *
426      * @param tenantId the tenant id
427      * @param msgBldr the msg bldr
428      * @return true, if is invalid tenant
429      */
430     static boolean isInvalidTenant(String tenantId, StringBuilder msgBldr) {
431         boolean invalid = false;
432
433         if (tenantId == null || tenantId.isEmpty()) {
434             invalid = true;
435             msgBldr.append("\n tenant : tenantId is missing");
436         }
437         String whereClause = "where id = :id";
438         HashMap<String, Object> params = new HashMap<String, Object>();
439         params.put("id", tenantId);
440
441         Object tenantFound = JpaStorageUtils.getEntity(
442                 "org.collectionspace.services.account.Tenant", whereClause, params);
443         if (tenantFound == null) {
444             invalid = true;
445             msgBldr.append("\n tenant : tenantId=" + tenantId
446                     + " not found");
447         }
448         return invalid;
449     }
450 }