View Javadoc

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