View Javadoc
1   /**
2    * Copyright 2005-2015 The Kuali Foundation
3    *
4    * Licensed under the Educational Community License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    * http://www.opensource.org/licenses/ecl2.php
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  package org.kuali.rice.kim.impl.permission;
17  
18  import static org.kuali.rice.core.api.criteria.PredicateFactory.equal;
19  import static org.kuali.rice.core.api.criteria.PredicateFactory.in;
20  
21  import java.util.ArrayList;
22  import java.util.Collection;
23  import java.util.Collections;
24  import java.util.Comparator;
25  import java.util.HashMap;
26  import java.util.List;
27  import java.util.Map;
28  import java.util.concurrent.CopyOnWriteArrayList;
29  
30  import javax.xml.namespace.QName;
31  
32  import org.apache.commons.collections.CollectionUtils;
33  import org.apache.commons.lang.StringUtils;
34  import org.apache.commons.lang.exception.ExceptionUtils;
35  import org.apache.log4j.Logger;
36  import org.kuali.rice.core.api.cache.CacheKeyUtils;
37  import org.kuali.rice.core.api.criteria.QueryByCriteria;
38  import org.kuali.rice.core.api.criteria.QueryResults;
39  import org.kuali.rice.core.api.exception.RiceIllegalArgumentException;
40  import org.kuali.rice.core.api.exception.RiceIllegalStateException;
41  import org.kuali.rice.core.api.membership.MemberType;
42  import org.kuali.rice.core.api.resourceloader.GlobalResourceLoader;
43  import org.kuali.rice.kim.api.KimConstants;
44  import org.kuali.rice.kim.api.common.assignee.Assignee;
45  import org.kuali.rice.kim.api.common.delegate.DelegateType;
46  import org.kuali.rice.kim.api.common.template.Template;
47  import org.kuali.rice.kim.api.common.template.TemplateQueryResults;
48  import org.kuali.rice.kim.api.identity.principal.Principal;
49  import org.kuali.rice.kim.api.permission.Permission;
50  import org.kuali.rice.kim.api.permission.PermissionQueryResults;
51  import org.kuali.rice.kim.api.permission.PermissionService;
52  import org.kuali.rice.kim.api.role.RoleMembership;
53  import org.kuali.rice.kim.api.role.RoleService;
54  import org.kuali.rice.kim.api.services.KimApiServiceLocator;
55  import org.kuali.rice.kim.api.type.KimType;
56  import org.kuali.rice.kim.api.type.KimTypeInfoService;
57  import org.kuali.rice.kim.framework.permission.PermissionTypeService;
58  import org.kuali.rice.kim.impl.common.attribute.AttributeTransform;
59  import org.kuali.rice.kim.impl.common.attribute.KimAttributeDataBo;
60  import org.kuali.rice.kim.impl.role.RolePermissionBo;
61  import org.kuali.rice.krad.data.DataObjectService;
62  import org.kuali.rice.krad.util.KRADPropertyConstants;
63  import org.springframework.cache.Cache;
64  import org.springframework.cache.CacheManager;
65  import org.springframework.cache.support.NoOpCacheManager;
66  
67  public class PermissionServiceImpl implements PermissionService {
68      private static final Logger LOG = Logger.getLogger( PermissionServiceImpl.class );
69  
70  	protected RoleService roleService;
71  	protected PermissionTypeService defaultPermissionTypeService;
72  	protected KimTypeInfoService kimTypeInfoService;
73  	protected DataObjectService dataObjectService;
74  	protected CacheManager cacheManager;
75  
76   	private final CopyOnWriteArrayList<Template> allTemplates = new CopyOnWriteArrayList<Template>();
77  
78      // --------------------
79      // Authorization Checks
80      // --------------------
81  
82      public PermissionServiceImpl() {
83          this.cacheManager = new NoOpCacheManager();
84      }
85  
86      protected PermissionTypeService getPermissionTypeService(Template permissionTemplate) {
87      	if ( permissionTemplate == null ) {
88      		throw new IllegalArgumentException( "permissionTemplate may not be null" );
89      	}
90      	KimType kimType = kimTypeInfoService.getKimType( permissionTemplate.getKimTypeId() );
91      	String serviceName = kimType.getServiceName();
92      	// if no service specified, return a default implementation
93      	if ( StringUtils.isBlank( serviceName ) ) {
94      		return defaultPermissionTypeService;
95      	}
96      	try {
97  	    	Object service = GlobalResourceLoader.getService(QName.valueOf(serviceName));
98  	    	// if we have a service name, it must exist
99  	    	if ( service == null ) {
100 				throw new RuntimeException("null returned for permission type service for service name: " + serviceName);
101 	    	}
102 	    	// whatever we retrieved must be of the correct type
103 	    	if ( !(service instanceof PermissionTypeService)  ) {
104 	    		throw new RuntimeException( "Service " + serviceName + " was not a PermissionTypeService.  Was: " + service.getClass().getName() );
105 	    	}
106 	    	return (PermissionTypeService)service;
107     	} catch( Exception ex ) {
108     		// sometimes service locators throw exceptions rather than returning null, handle that
109     		throw new RuntimeException( "Error retrieving service: " + serviceName + " from the KimImplServiceLocator.", ex );
110     	}
111     }
112 
113     @Override
114     public boolean hasPermission(String principalId, String namespaceCode,
115                                  String permissionName) throws RiceIllegalArgumentException  {
116         incomingParamCheck(principalId, "principalId");
117         incomingParamCheck(namespaceCode, "namespaceCode");
118         incomingParamCheck(permissionName, "permissionName");
119 
120         return isAuthorized( principalId, namespaceCode, permissionName, Collections.<String, String>emptyMap() );
121     }
122 
123     @Override
124     public boolean isAuthorized(String principalId, String namespaceCode,
125                                 String permissionName, Map<String, String> qualification ) throws RiceIllegalArgumentException {
126         incomingParamCheck(principalId, "principalId");
127         incomingParamCheck(namespaceCode, "namespaceCode");
128         incomingParamCheck(permissionName, "permissionName");
129         incomingParamCheck(qualification, "qualification");
130 
131         if ( LOG.isDebugEnabled() ) {
132             logAuthorizationCheck("Permission", principalId, namespaceCode, permissionName, qualification);
133         }
134 
135         List<String> roleIds = getRoleIdsForPermission(namespaceCode, permissionName);
136     	if ( roleIds.isEmpty() ) {
137     		if ( LOG.isDebugEnabled() ) {
138     			LOG.debug( "Result: false");
139     		}
140     		return false;
141     	}
142 
143     	boolean isAuthorized = roleService.principalHasRole(principalId, roleIds, qualification);
144         
145         if ( LOG.isDebugEnabled() ) {
146             LOG.debug( "Result: " + isAuthorized );
147         }
148         return isAuthorized;
149 
150     }
151     @Override
152     public boolean hasPermissionByTemplate(String principalId, String namespaceCode, String permissionTemplateName,
153             Map<String, String> permissionDetails) throws RiceIllegalArgumentException {
154         incomingParamCheck(principalId, "principalId");
155         incomingParamCheck(namespaceCode, "namespaceCode");
156         incomingParamCheck(permissionTemplateName, "permissionTemplateName");
157 
158         return isAuthorizedByTemplate(principalId, namespaceCode, permissionTemplateName, permissionDetails,
159                 Collections.<String, String>emptyMap());
160     }
161     @Override
162     public boolean isAuthorizedByTemplate(String principalId, String namespaceCode, String permissionTemplateName,
163             Map<String, String> permissionDetails, Map<String, String> qualification) throws RiceIllegalArgumentException {
164         incomingParamCheck(principalId, "principalId");
165         incomingParamCheck(namespaceCode, "namespaceCode");
166         incomingParamCheck(permissionTemplateName, "permissionTemplateName");
167         incomingParamCheck(qualification, "qualification");
168 
169         if ( LOG.isDebugEnabled() ) {
170             logAuthorizationCheckByTemplate("Perm Templ", principalId, namespaceCode, permissionTemplateName, permissionDetails, qualification);
171         }
172 
173         List<String> roleIds = getRoleIdsForPermissionTemplate(namespaceCode, permissionTemplateName, permissionDetails);
174     	if (roleIds.isEmpty()) {
175             if (LOG.isDebugEnabled()) {
176                 LOG.debug("Result: false");
177             }
178     		return false;
179     	}
180         boolean isAuthorized = roleService.principalHasRole(principalId, roleIds, qualification);
181         if (LOG.isDebugEnabled()) {
182             LOG.debug( "Result: " + isAuthorized );
183         }
184 		return isAuthorized;
185     	
186     }
187     @Override
188     public List<Permission> getAuthorizedPermissions( String principalId,
189             String namespaceCode, String permissionName,
190             Map<String, String> qualification ) throws RiceIllegalArgumentException {
191         incomingParamCheck(principalId, "principalId");
192         incomingParamCheck(namespaceCode, "namespaceCode");
193         incomingParamCheck(permissionName, "permissionName");
194         incomingParamCheck(qualification, "qualification");
195 
196         // get all the permission objects whose name match that requested
197     	List<Permission> permissions = getPermissionsByName(namespaceCode, permissionName);
198     	// now, filter the full list by the detail passed
199     	List<Permission> applicablePermissions = getMatchingPermissions( permissions, null );
200         List<Permission> permissionsForUser = getPermissionsForUser(principalId, applicablePermissions, qualification);
201     	return permissionsForUser;
202     }
203     @Override
204     public List<Permission> getAuthorizedPermissionsByTemplate(String principalId, String namespaceCode,
205             String permissionTemplateName, Map<String, String> permissionDetails, Map<String, String> qualification) throws RiceIllegalArgumentException {
206         incomingParamCheck(principalId, "principalId");
207         incomingParamCheck(namespaceCode, "namespaceCode");
208         incomingParamCheck(permissionTemplateName, "permissionTemplateName");
209         incomingParamCheck(qualification, "qualification");
210 
211         // get all the permission objects whose name match that requested
212     	List<Permission> permissions = getPermissionsByTemplateName(namespaceCode, permissionTemplateName);
213     	// now, filter the full list by the detail passed
214     	List<Permission> applicablePermissions = getMatchingPermissions( permissions, permissionDetails );
215 
216         return getPermissionsForUser(principalId, applicablePermissions, qualification);
217     }
218     
219     /**
220      * Checks the list of permissions against the principal's roles and returns a subset of the list which match.
221      */
222     protected List<Permission> getPermissionsForUser( String principalId, List<Permission> permissions,
223             Map<String, String> qualification ) {
224     	List<Permission> results = new ArrayList<Permission>();
225     	for ( Permission perm : permissions ) {
226     		List<String> roleIds = getRoleIdsForPermissions( Collections.singletonList(perm) );
227     		if ( roleIds != null && !roleIds.isEmpty() ) {
228     			if ( roleService.principalHasRole( principalId, roleIds, qualification) ) {
229     				results.add( perm );
230     			}
231     		}
232     	}
233     	return Collections.unmodifiableList(results);
234     }
235 
236     protected Map<String,PermissionTypeService> getPermissionTypeServicesByTemplateId( Collection<Permission> permissions ) {
237     	Map<String,PermissionTypeService> permissionTypeServices = new HashMap<String, PermissionTypeService>( permissions.size() );
238     	for (Permission perm : permissions) {
239             if(!permissionTypeServices.containsKey(perm.getTemplate().getId())) {
240                 permissionTypeServices.put(perm.getTemplate().getId(), getPermissionTypeService(perm.getTemplate()));
241             }
242     	}
243     	return permissionTypeServices;
244     }
245     
246 	protected Map<String,List<Permission>> groupPermissionsByTemplate(Collection<Permission> permissions) {
247     	Map<String,List<Permission>> results = new HashMap<String,List<Permission>>();
248     	for (Permission perm : permissions) {
249     		List<Permission> perms = results.get(perm.getTemplate().getId());
250     		if (perms == null) {
251     			perms = new ArrayList<Permission>();
252     			results.put(perm.getTemplate().getId(), perms);
253     		}
254     		perms.add(perm);
255     	}
256     	return results;
257     }
258     
259 	/**
260      * Compare each of the passed in permissions with the given permissionDetails.  Those that
261      * match are added to the result list.
262      */
263     protected List<Permission> getMatchingPermissions( List<Permission> permissions, Map<String, String> permissionDetails ) {
264         List<String> permissionIds = new ArrayList<String>(permissions.size());
265         for (Permission permission : permissions) {
266             permissionIds.add(permission.getId());
267         }
268         String cacheKey =  new StringBuilder("{getMatchingPermissions}")
269                 .append("permissionIds=").append(CacheKeyUtils.key(permissionIds)).append("|")
270                 .append("permissionDetails=").append(CacheKeyUtils.mapKey(permissionDetails)).toString();
271         Cache.ValueWrapper cachedValue = cacheManager.getCache(Permission.Cache.NAME).get(cacheKey);
272         if (cachedValue != null && cachedValue.get() instanceof List) {
273             return ((List<Permission>)cachedValue.get());
274         }
275 
276     	List<Permission> applicablePermissions = new ArrayList<Permission>();    	
277     	if ( permissionDetails == null || permissionDetails.isEmpty() ) {
278     		// if no details passed, assume that all match
279     		for ( Permission perm : permissions ) {
280     			applicablePermissions.add(perm);
281     		}
282     	} else {
283     		// otherwise, attempt to match the permission details
284     		// build a map of the template IDs to the type services
285     		Map<String,PermissionTypeService> permissionTypeServices = getPermissionTypeServicesByTemplateId(permissions);
286     		// build a map of permissions by template ID
287     		Map<String, List<Permission>> permissionMap = groupPermissionsByTemplate(permissions);
288     		// loop over the different templates, matching all of the same template against the type
289     		// service at once
290     		for ( Map.Entry<String,List<Permission>> entry : permissionMap.entrySet() ) {
291     			PermissionTypeService permissionTypeService = permissionTypeServices.get( entry.getKey() );
292     			List<Permission> permissionList = entry.getValue();
293 				applicablePermissions.addAll( permissionTypeService.getMatchingPermissions( permissionDetails, permissionList ) );    				
294     		}
295     	}
296         applicablePermissions = Collections.unmodifiableList(applicablePermissions);
297         cacheManager.getCache(Permission.Cache.NAME).put(cacheKey, applicablePermissions);
298         return applicablePermissions;
299     }
300 
301 
302     @Override
303     public List<Assignee> getPermissionAssignees( String namespaceCode, String permissionName,
304             Map<String, String> qualification ) throws RiceIllegalArgumentException {
305         incomingParamCheck(namespaceCode, "namespaceCode");
306         incomingParamCheck(permissionName, "permissionName");
307         incomingParamCheck(qualification, "qualification");
308 
309     	List<String> roleIds = getRoleIdsForPermission( namespaceCode, permissionName);
310     	if ( roleIds.isEmpty() ) {
311     		return Collections.emptyList();
312     	}
313     	Collection<RoleMembership> roleMembers = roleService.getRoleMembers( roleIds,qualification );
314     	List<Assignee> results = new ArrayList<Assignee>();
315         for ( RoleMembership rm : roleMembers ) {
316 			List<DelegateType.Builder> delegateBuilderList = new ArrayList<DelegateType.Builder>();
317 			if (!rm.getDelegates().isEmpty()) {
318     			for (DelegateType delegate : rm.getDelegates()){
319                     delegateBuilderList.add(DelegateType.Builder.create(delegate));
320     			}
321 			}
322     		if ( MemberType.PRINCIPAL.equals(rm.getType()) ) {
323     			results.add (Assignee.Builder.create(rm.getMemberId(), null, delegateBuilderList).build());
324     		} else if ( MemberType.GROUP.equals(rm.getType()) ) {
325     			results.add (Assignee.Builder.create(null, rm.getMemberId(), delegateBuilderList).build());
326     		}
327     	}
328 
329     	return Collections.unmodifiableList(results);
330     }
331 
332     @Override
333     public List<Assignee> getPermissionAssigneesByTemplate(String namespaceCode, String permissionTemplateName,
334             Map<String, String> permissionDetails, Map<String, String> qualification) throws RiceIllegalArgumentException {
335         incomingParamCheck(namespaceCode, "namespaceCode");
336         incomingParamCheck(permissionTemplateName, "permissionTemplateName");
337         incomingParamCheck(qualification, "qualification");
338 
339     	List<String> roleIds = getRoleIdsForPermissionTemplate( namespaceCode, permissionTemplateName, permissionDetails);
340     	if ( roleIds.isEmpty() ) {
341     		return Collections.emptyList();
342     	}
343     	Collection<RoleMembership> roleMembers = roleService.getRoleMembers( roleIds,qualification);
344     	List<Assignee> results = new ArrayList<Assignee>();
345         for ( RoleMembership rm : roleMembers ) {
346 			List<DelegateType.Builder> delegateBuilderList = new ArrayList<DelegateType.Builder>();
347 			if (!rm.getDelegates().isEmpty()) {
348     			for (DelegateType delegate : rm.getDelegates()){
349                     delegateBuilderList.add(DelegateType.Builder.create(delegate));
350     			}
351 			}
352     		if ( MemberType.PRINCIPAL.equals(rm.getType()) ) {
353     			results.add (Assignee.Builder.create(rm.getMemberId(), null, delegateBuilderList).build());
354     		} else { // a group membership
355     			results.add (Assignee.Builder.create(null, rm.getMemberId(), delegateBuilderList).build());
356     		}
357     	}
358     	return Collections.unmodifiableList(results);
359     }
360 
361     @Override
362     public boolean isPermissionDefined( String namespaceCode, String permissionName ) throws RiceIllegalArgumentException {
363         incomingParamCheck(namespaceCode, "namespaceCode");
364         incomingParamCheck(permissionName, "permissionName");
365 
366     	// get all the permission objects whose name match that requested
367     	List<Permission> permissions = getPermissionsByName(namespaceCode, permissionName);
368     	// now, filter the full list by the detail passed
369     	return !getMatchingPermissions(permissions, null).isEmpty();
370     }
371 
372     @Override
373     public boolean isPermissionDefinedByTemplate(String namespaceCode, String permissionTemplateName,
374             Map<String, String> permissionDetails) throws RiceIllegalArgumentException {
375 
376         incomingParamCheck(namespaceCode, "namespaceCode");
377         incomingParamCheck(permissionTemplateName, "permissionTemplateName");
378 
379     	// get all the permission objects whose name match that requested
380     	List<Permission> permissions = getPermissionsByTemplateName(namespaceCode, permissionTemplateName);
381     	// now, filter the full list by the detail passed
382     	return !getMatchingPermissions(permissions, permissionDetails).isEmpty();
383     }
384 
385     @Override
386     public List<String> getRoleIdsForPermission(String namespaceCode, String permissionName) throws RiceIllegalArgumentException {
387         incomingParamCheck(namespaceCode, "namespaceCode");
388         incomingParamCheck(permissionName, "permissionName");
389         // note...this method is cached at the RoleService interface level using an annotation, but it's called quite
390         // a bit internally, so we'll reproduce the caching here using the same key to help optimize
391         String cacheKey =  new StringBuilder("{RoleIds}")
392                 .append("namespaceCode=").append(namespaceCode).append("|")
393                 .append("name=").append(permissionName).toString();
394         Cache.ValueWrapper cachedValue = cacheManager.getCache(Permission.Cache.NAME).get(cacheKey);
395         if (cachedValue != null && cachedValue.get() instanceof List) {
396             return ((List<String>)cachedValue.get());
397         }
398         // get all the permission objects whose name match that requested
399         List<Permission> permissions = getPermissionsByName(namespaceCode, permissionName);
400         // now, filter the full list by the detail passed
401         List<Permission> applicablePermissions = getMatchingPermissions(permissions, null);
402         List<String> roleIds = getRoleIdsForPermissions(applicablePermissions);
403         cacheManager.getCache(Permission.Cache.NAME).put(cacheKey, roleIds);
404         return roleIds;
405     }
406 
407     protected List<String> getRoleIdsForPermissionTemplate(String namespaceCode,
408             String permissionTemplateName,
409             Map<String, String> permissionDetails) {
410         String cacheKey =  new StringBuilder("{getRoleIdsForPermissionTemplate}")
411                 .append("namespaceCode=").append(namespaceCode).append("|")
412                 .append("permissionTemplateName=").append(permissionTemplateName).append("|")
413                 .append("permissionDetails=").append(CacheKeyUtils.mapKey(permissionDetails)).toString();
414         Cache.ValueWrapper cachedValue = cacheManager.getCache(Permission.Cache.NAME).get(cacheKey);
415         if (cachedValue != null && cachedValue.get() instanceof List) {
416             return ((List<String>)cachedValue.get());
417         }
418     	// get all the permission objects whose name match that requested
419     	List<Permission> permissions = getPermissionsByTemplateName(namespaceCode, permissionTemplateName);
420     	// now, filter the full list by the detail passed
421     	List<Permission> applicablePermissions = getMatchingPermissions(permissions, permissionDetails);
422     	List<String> roleIds = getRoleIdsForPermissions(applicablePermissions);
423         cacheManager.getCache(Permission.Cache.NAME).put(cacheKey, roleIds);
424         return roleIds;
425     }
426 
427     // --------------------
428     // Permission Data
429     // --------------------
430     
431     @Override
432     public Permission getPermission(String permissionId) throws RiceIllegalArgumentException {
433         incomingParamCheck(permissionId, "permissionId");
434         PermissionBo impl = getPermissionImpl(permissionId);
435     	if (impl != null) {
436     		return PermissionBo.to(impl);
437     	}
438     	return null;
439     }
440     
441     @Override
442     public List<Permission> findPermissionsByTemplate(String namespaceCode, String permissionTemplateName) throws RiceIllegalArgumentException {
443         incomingParamCheck(namespaceCode, "namespaceCode");
444         incomingParamCheck(permissionTemplateName, "permissionTemplateName");
445 
446         List<Permission> perms = getPermissionsByTemplateName(namespaceCode, permissionTemplateName);
447     	List<Permission> results = new ArrayList<Permission>(perms.size());
448     	for (Permission perm : perms) {
449     	    results.add(perm);
450     	}
451     	return Collections.unmodifiableList(results);
452     }
453 
454 	protected PermissionBo getPermissionImpl(String permissionId) throws RiceIllegalArgumentException {
455     	incomingParamCheck(permissionId, "permissionId");
456 
457         return dataObjectService.find( PermissionBo.class, permissionId );
458     }
459     
460     protected List<Permission> getPermissionsByTemplateName( String namespaceCode, String permissionTemplateName ){
461         String cacheKey =  new StringBuilder("{getPermissionsByTemplateName}")
462                 .append("namespaceCode=").append(namespaceCode).append("|")
463                 .append("permissionTemplateName=").append(permissionTemplateName).toString();
464         Cache.ValueWrapper cachedValue = cacheManager.getCache(Permission.Cache.NAME).get(cacheKey);
465         if (cachedValue != null && cachedValue.get() instanceof List) {
466             return ((List<Permission>)cachedValue.get());
467         }
468         HashMap<String,Object> criteria = new HashMap<String,Object>(3);
469         criteria.put("template.namespaceCode", namespaceCode);
470         criteria.put("template.name", permissionTemplateName);
471         criteria.put("template.active", Boolean.TRUE);
472         criteria.put(KRADPropertyConstants.ACTIVE, Boolean.TRUE);
473         List<Permission> permissions =
474                 toPermissions(dataObjectService.findMatching(PermissionBo.class, QueryByCriteria.Builder.andAttributes(criteria).build()).getResults());
475         cacheManager.getCache(Permission.Cache.NAME).put(cacheKey, permissions);
476         return permissions;
477     }
478 
479     protected List<Permission> getPermissionsByName( String namespaceCode, String permissionName ) {
480         String cacheKey =  new StringBuilder("{getPermissionsByName}")
481                 .append("namespaceCode=").append(namespaceCode).append("|")
482                 .append("permissionName=").append(permissionName).toString();
483         Cache.ValueWrapper cachedValue = cacheManager.getCache(Permission.Cache.NAME).get(cacheKey);
484         if (cachedValue != null && cachedValue.get() instanceof List) {
485             return ((List<Permission>)cachedValue.get());
486         }
487         HashMap<String,Object> criteria = new HashMap<String,Object>(3);
488         criteria.put(KimConstants.UniqueKeyConstants.NAMESPACE_CODE, namespaceCode);
489         criteria.put(KimConstants.UniqueKeyConstants.PERMISSION_NAME, permissionName);
490         criteria.put(KRADPropertyConstants.ACTIVE, Boolean.TRUE);
491         List<Permission> permissions =
492                 toPermissions(dataObjectService.findMatching( PermissionBo.class, QueryByCriteria.Builder.andAttributes(criteria).build() ).getResults());
493         cacheManager.getCache(Permission.Cache.NAME).put(cacheKey, permissions);
494         return permissions;
495     }
496 	
497     @Override
498 	public Template getPermissionTemplate(String permissionTemplateId) throws RiceIllegalArgumentException {
499         incomingParamCheck(permissionTemplateId, "permissionTemplateId");
500 
501         PermissionTemplateBo impl = dataObjectService.find( PermissionTemplateBo.class, permissionTemplateId );
502 		if ( impl != null ) {
503 			return PermissionTemplateBo.to(impl);
504 		}
505 		return null;
506 	}
507 
508     @Override
509 	public Template findPermTemplateByNamespaceCodeAndName(String namespaceCode,
510             String permissionTemplateName) throws RiceIllegalArgumentException {
511 		incomingParamCheck(namespaceCode, "namespaceCode");
512         incomingParamCheck(permissionTemplateName, "permissionTemplateName");
513 
514         Map<String,String> criteria = new HashMap<String,String>(2);
515 		criteria.put( KimConstants.UniqueKeyConstants.NAMESPACE_CODE, namespaceCode );
516 		criteria.put( KimConstants.UniqueKeyConstants.PERMISSION_TEMPLATE_NAME, permissionTemplateName );
517 		QueryResults<PermissionTemplateBo> results = dataObjectService.findMatching( PermissionTemplateBo.class, QueryByCriteria.Builder.andAttributes(criteria).build() );
518         if ( results.getResults().isEmpty() ) {
519             return null;
520         }
521         return PermissionTemplateBo.to(results.getResults().get(0));
522 	}
523 
524     @Override
525 	public List<Template> getAllTemplates() {
526 		if ( allTemplates.isEmpty() ) {
527 			Map<String,Object> criteria = new HashMap<String,Object>(1);
528 			criteria.put(KRADPropertyConstants.ACTIVE, Boolean.TRUE);
529 			
530 			QueryResults<PermissionTemplateBo> impls = dataObjectService.findMatching( PermissionTemplateBo.class, QueryByCriteria.Builder.andAttributes(criteria).build() );
531 			List<Template> infos = new ArrayList<Template>( impls.getResults().size() );
532 			for ( PermissionTemplateBo impl : impls.getResults() ) {
533 				infos.add( PermissionTemplateBo.to(impl) );
534 			}
535 			Collections.sort(infos, new Comparator<Template>() {
536 				@Override public int compare(Template tmpl1,
537 						Template tmpl2) {
538 
539 					int result = tmpl1.getNamespaceCode().compareTo(tmpl2.getNamespaceCode());
540 					if ( result != 0 ) {
541 						return result;
542 					}
543 					result = tmpl1.getName().compareTo(tmpl2.getName());
544 					return result;
545 				}
546 			});
547 			allTemplates.addAll(infos);
548 		}
549 		return Collections.unmodifiableList(allTemplates);
550     }
551 
552 
553 	@Override
554 	public Permission createPermission(Permission permission)
555 			throws RiceIllegalArgumentException, RiceIllegalStateException {
556         incomingParamCheck(permission, "permission");
557 
558         if (StringUtils.isNotBlank(permission.getId()) && getPermission(permission.getId()) != null) {
559             throw new RiceIllegalStateException("the permission to create already exists: " + permission);
560         }
561         List<PermissionAttributeBo> attrBos = Collections.emptyList();
562         if (permission.getTemplate() != null) {
563             attrBos = KimAttributeDataBo.createFrom(PermissionAttributeBo.class, permission.getAttributes(), permission.getTemplate().getKimTypeId());
564         }
565         PermissionBo bo = PermissionBo.from(permission);
566         if (bo.getTemplate() == null && bo.getTemplateId() != null) {
567             bo.setTemplate(PermissionTemplateBo.from(getPermissionTemplate(bo.getTemplateId())));
568         }
569         bo.setAttributeDetails(attrBos);
570         return PermissionBo.to(dataObjectService.save(bo));
571 	}
572 
573 	@Override
574 	public Permission updatePermission(Permission permission)
575 			throws RiceIllegalArgumentException, RiceIllegalStateException {
576         incomingParamCheck(permission, "permission");
577 
578         PermissionBo oldPermission = getPermissionImpl(permission.getId());
579         if (StringUtils.isBlank(permission.getId()) || oldPermission == null) {
580             throw new RiceIllegalStateException("the permission does not exist: " + permission);
581         }
582 
583         //List<PermissionAttributeBo> attrBos = KimAttributeDataBo.createFrom(PermissionAttributeBo.class, permission.getAttributes(), permission.getTemplate().getKimTypeId());
584 
585         List<PermissionAttributeBo> oldAttrBos = oldPermission.getAttributeDetails();
586         //put old attributes in map for easier updating
587         Map<String, PermissionAttributeBo> oldAttrMap = new HashMap<String, PermissionAttributeBo>();
588         for (PermissionAttributeBo oldAttr : oldAttrBos) {
589             oldAttrMap.put(oldAttr.getKimAttribute().getAttributeName(), oldAttr);
590         }
591         List<PermissionAttributeBo> newAttrBos = new ArrayList<PermissionAttributeBo>();
592         for (String key : permission.getAttributes().keySet()) {
593             if (oldAttrMap.containsKey(key)) {
594                 PermissionAttributeBo updatedAttr = oldAttrMap.get(key);
595                 updatedAttr.setAttributeValue(permission.getAttributes().get(key));
596                 newAttrBos.add(updatedAttr);
597             } else { //new attribute
598                 newAttrBos.addAll(KimAttributeDataBo.createFrom(PermissionAttributeBo.class, Collections.singletonMap(key, permission.getAttributes().get(key)), permission.getTemplate().getKimTypeId()));
599             }
600         }
601         PermissionBo bo = PermissionBo.from(permission);
602         if (CollectionUtils.isNotEmpty(newAttrBos)) {
603             if(null!= bo.getAttributeDetails())  {
604                 bo.getAttributeDetails().clear();
605             }
606             bo.setAttributeDetails(newAttrBos);
607         }
608         if (bo.getTemplate() == null && bo.getTemplateId() != null) {
609             bo.setTemplate(PermissionTemplateBo.from(getPermissionTemplate(bo.getTemplateId())));
610         }
611 
612         return PermissionBo.to(dataObjectService.save(bo));		
613 	}
614 	
615     @Override
616     public Permission findPermByNamespaceCodeAndName(String namespaceCode, String permissionName)
617             throws RiceIllegalArgumentException {
618         incomingParamCheck(namespaceCode, "namespaceCode");
619         incomingParamCheck(permissionName, "permissionName");
620 
621         PermissionBo permissionBo = getPermissionBoByName(namespaceCode, permissionName);
622         if (permissionBo != null) {
623             return PermissionBo.to(permissionBo);
624         }
625         return null;
626     }
627     
628     protected PermissionBo getPermissionBoByName(String namespaceCode, String permissionName) {
629         if (StringUtils.isBlank(namespaceCode)
630                 || StringUtils.isBlank(permissionName)) {
631             return null;
632         }
633         Map<String, Object> criteria = new HashMap<String, Object>(3);
634         criteria.put(KimConstants.UniqueKeyConstants.NAMESPACE_CODE, namespaceCode);
635         criteria.put(KimConstants.UniqueKeyConstants.NAME, permissionName);
636         criteria.put(KRADPropertyConstants.ACTIVE, Boolean.TRUE);
637         // while this is not actually the primary key - there will be at most one row with these criteria
638         QueryResults<PermissionBo> results = dataObjectService.findMatching(PermissionBo.class, QueryByCriteria.Builder.andAttributes(criteria).build());
639         if ( results.getResults().isEmpty() ) {
640             return null;
641         }
642         return results.getResults().get(0);
643     }
644 
645     @Override
646     public PermissionQueryResults findPermissions(final QueryByCriteria queryByCriteria)
647             throws RiceIllegalArgumentException {
648         incomingParamCheck(queryByCriteria, "queryByCriteria");
649 
650         QueryResults<PermissionBo> results = dataObjectService.findMatching(PermissionBo.class,
651                 AttributeTransform.getInstance().apply(queryByCriteria));
652 
653         PermissionQueryResults.Builder builder = PermissionQueryResults.Builder.create();
654         builder.setMoreResultsAvailable(results.isMoreResultsAvailable());
655         builder.setTotalRowCount(results.getTotalRowCount());
656 
657         final List<Permission.Builder> ims = new ArrayList<Permission.Builder>();
658         for (PermissionBo bo : results.getResults()) {
659             ims.add(Permission.Builder.create(bo));
660         }
661 
662         builder.setResults(ims);
663         return builder.build();
664     }
665 
666     @Override
667     public TemplateQueryResults findPermissionTemplates(final QueryByCriteria queryByCriteria)
668             throws RiceIllegalArgumentException {
669         incomingParamCheck(queryByCriteria, "queryByCriteria");
670 
671         QueryResults<PermissionTemplateBo> results = dataObjectService.findMatching(PermissionTemplateBo.class, queryByCriteria);
672 
673         TemplateQueryResults.Builder builder = TemplateQueryResults.Builder.create();
674         builder.setMoreResultsAvailable(results.isMoreResultsAvailable());
675         builder.setTotalRowCount(results.getTotalRowCount());
676 
677         final List<Template.Builder> ims = new ArrayList<Template.Builder>();
678         for (PermissionTemplateBo bo : results.getResults()) {
679             ims.add(Template.Builder.create(bo));
680         }
681 
682         builder.setResults(ims);
683         return builder.build();
684     }
685 
686     private List<String> getRoleIdsForPermissions( Collection<Permission> permissions ) {
687         if (CollectionUtils.isEmpty(permissions)) {
688             return Collections.emptyList();
689         }
690         List<String> ids = new ArrayList<String>();
691         for (Permission p : permissions) {
692             ids.add(p.getId());
693         }
694 
695         return getRoleIdsForPermissionIds(ids);
696     }
697 
698     private List<String> getRoleIdsForPermissionIds(Collection<String> permissionIds) {
699         if (CollectionUtils.isEmpty(permissionIds)) {
700             return Collections.emptyList();
701         }
702         String cacheKey =  new StringBuilder("{getRoleIdsForPermissionIds}")
703                 .append("permissionIds=").append(CacheKeyUtils.key(permissionIds)).toString();
704         Cache.ValueWrapper cachedValue = cacheManager.getCache(Permission.Cache.NAME).get(cacheKey);
705         if (cachedValue != null && cachedValue.get() instanceof List) {
706             return ((List<String>)cachedValue.get());
707         }
708         QueryByCriteria query = QueryByCriteria.Builder.fromPredicates(equal("active", Boolean.TRUE), in("permissionId", permissionIds.toArray(new String[]{})));
709         QueryResults<RolePermissionBo> results = dataObjectService.findMatching(RolePermissionBo.class, query);
710         List<String> roleIds = new ArrayList<String>();
711         for (RolePermissionBo bo : results.getResults()) {
712             roleIds.add(bo.getRoleId());
713         }
714         roleIds = Collections.unmodifiableList(roleIds);
715         cacheManager.getCache(Permission.Cache.NAME).put(cacheKey, roleIds);
716         return roleIds;
717     }
718 	
719 	/**
720      * Sets the kimTypeInfoService attribute value.
721      *
722      * @param kimTypeInfoService The kimTypeInfoService to set.
723      */
724 	public void setKimTypeInfoService(KimTypeInfoService kimTypeInfoService) {
725 		this.kimTypeInfoService = kimTypeInfoService;
726 	}
727 	
728 	/**
729      * Sets the defaultPermissionTypeService attribute value.
730      *
731      * @param defaultPermissionTypeService The defaultPermissionTypeService to set.
732      */
733 	public void setDefaultPermissionTypeService(PermissionTypeService defaultPermissionTypeService) {
734     	this.defaultPermissionTypeService = defaultPermissionTypeService;
735 	}
736 	
737 	/**
738      * Sets the roleService attribute value.
739      *
740      * @param roleService The roleService to set.
741      */
742 	public void setRoleService(RoleService roleService) {
743 		this.roleService = roleService;
744 	}
745 
746     /**
747      * Sets the dataObjectService attribute value.
748      *
749      * @param dataObjectService The dataObjectService to set.
750      */
751     public void setDataObjectService(final DataObjectService dataObjectService) {
752         this.dataObjectService = dataObjectService;
753     }
754 
755     /**
756      * Sets the cache manager which this service implementation can for internal caching.
757      * Calling this setter is optional, though the value passed to it must not be null.
758      *
759      * @param cacheManager the cache manager to use for internal caching, must not be null
760      * @throws IllegalArgumentException if a null cache manager is passed
761      */
762     public void setCacheManager(final CacheManager cacheManager) {
763         if (cacheManager == null) {
764             throw new IllegalArgumentException("cacheManager must not be null");
765         }
766         this.cacheManager = cacheManager;
767     }
768 
769     private List<Permission> toPermissions(Collection<PermissionBo> permissionBos) {
770         if (CollectionUtils.isEmpty(permissionBos)) {
771             return new ArrayList<Permission>();
772         }
773         List<Permission> permissions = new ArrayList<Permission>(permissionBos.size());
774         for (PermissionBo permissionBo : permissionBos) {
775             permissions.add(PermissionBo.to(permissionBo));
776         }
777         return permissions;
778     }
779     
780     protected void logAuthorizationCheck(String checkType, String principalId, String namespaceCode, String permissionName, Map<String, String> qualification ) {
781         StringBuilder sb = new StringBuilder();
782         sb.append(  '\n' );
783         sb.append( "Is AuthZ for " ).append( checkType ).append( ": " ).append( namespaceCode ).append( "/" ).append( permissionName ).append( '\n' );
784         sb.append( "             Principal:  " ).append( principalId );
785         if ( principalId != null ) {
786             Principal principal = KimApiServiceLocator.getIdentityService().getPrincipal(principalId);
787             if ( principal != null ) {
788                 sb.append( " (" ).append( principal.getPrincipalName() ).append( ')' );
789             }
790         }
791         sb.append( '\n' );
792         sb.append( "             Qualifiers:\n" );
793         if ( qualification != null && !qualification.isEmpty() ) {
794             sb.append( qualification );
795         } else {
796             sb.append( "                         [null]\n" );
797         }
798         if (LOG.isTraceEnabled()) {
799             LOG.trace( sb.append(ExceptionUtils.getStackTrace(new Throwable())));
800         } else {
801             LOG.debug(sb.toString());
802         }
803     }
804     
805     protected void logAuthorizationCheckByTemplate(String checkType, String principalId, String namespaceCode, String permissionName, 
806                                                    Map<String, String> permissionDetails, Map<String, String> qualification ) {
807         StringBuilder sb = new StringBuilder();
808         sb.append(  '\n' );
809         sb.append( "Is AuthZ for " ).append( checkType ).append( ": " ).append( namespaceCode ).append( "/" ).append( permissionName ).append( '\n' );
810         sb.append( "             Principal:  " ).append( principalId );
811         if ( principalId != null ) {
812             Principal principal = KimApiServiceLocator.getIdentityService().getPrincipal(principalId);
813             if ( principal != null ) {
814                 sb.append( " (" ).append( principal.getPrincipalName() ).append( ')' );
815             }
816         }
817         sb.append( '\n' );
818         sb.append( "             Details:\n" );
819         if ( permissionDetails != null ) {
820             sb.append( permissionDetails );
821         } else {
822             sb.append( "                         [null]\n" );
823         }
824         sb.append( "             Qualifiers:\n" );
825         if ( qualification != null && !qualification.isEmpty() ) {
826             sb.append( qualification );
827         } else {
828             sb.append( "                         [null]\n" );
829         }
830         if (LOG.isTraceEnabled()) {
831             LOG.trace( sb.append(ExceptionUtils.getStackTrace(new Throwable())));
832         } else {
833             LOG.debug(sb.toString());
834         }
835     }
836     
837     private void incomingParamCheck(Object object, String name) {
838         if (object == null) {
839             throw new RiceIllegalArgumentException(name + " was null");
840         } else if (object instanceof String
841                 && StringUtils.isBlank((String) object)) {
842             throw new RiceIllegalArgumentException(name + " was blank");
843         }
844     }
845 
846 
847 }