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 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("template.active", "Y");
476         criteria.put(KRADPropertyConstants.ACTIVE, "Y");
477         List<Permission> permissions =
478                 toPermissions(businessObjectService.findMatching(PermissionBo.class, criteria));
479         cacheManager.getCache(Permission.Cache.NAME).put(cacheKey, permissions);
480         return permissions;
481     }
482 
483     protected List<Permission> getPermissionsByName( String namespaceCode, String permissionName ) {
484         String cacheKey =  new StringBuilder("{getPermissionsByName}")
485                 .append("namespaceCode=").append(namespaceCode).append("|")
486                 .append("permissionName=").append(permissionName).toString();
487         Cache.ValueWrapper cachedValue = cacheManager.getCache(Permission.Cache.NAME).get(cacheKey);
488         if (cachedValue != null && cachedValue.get() instanceof List) {
489             return ((List<Permission>)cachedValue.get());
490         }
491         HashMap<String,Object> criteria = new HashMap<String,Object>(3);
492         criteria.put(KimConstants.UniqueKeyConstants.NAMESPACE_CODE, namespaceCode);
493         criteria.put(KimConstants.UniqueKeyConstants.PERMISSION_NAME, permissionName);
494         criteria.put(KRADPropertyConstants.ACTIVE, "Y");
495         List<Permission> permissions =
496                 toPermissions(businessObjectService.findMatching( PermissionBo.class, criteria ));
497         cacheManager.getCache(Permission.Cache.NAME).put(cacheKey, permissions);
498         return permissions;
499     }
500 	
501     @Override
502 	public Template getPermissionTemplate(String permissionTemplateId) throws RiceIllegalArgumentException {
503         incomingParamCheck(permissionTemplateId, "permissionTemplateId");
504 
505         PermissionTemplateBo impl = businessObjectService.findBySinglePrimaryKey( PermissionTemplateBo.class, permissionTemplateId );
506 		if ( impl != null ) {
507 			return PermissionTemplateBo.to(impl);
508 		}
509 		return null;
510 	}
511 
512     @Override
513 	public Template findPermTemplateByNamespaceCodeAndName(String namespaceCode,
514             String permissionTemplateName) throws RiceIllegalArgumentException {
515 		incomingParamCheck(namespaceCode, "namespaceCode");
516         incomingParamCheck(permissionTemplateName, "permissionTemplateName");
517 
518         Map<String,String> criteria = new HashMap<String,String>(2);
519 		criteria.put( KimConstants.UniqueKeyConstants.NAMESPACE_CODE, namespaceCode );
520 		criteria.put( KimConstants.UniqueKeyConstants.PERMISSION_TEMPLATE_NAME, permissionTemplateName );
521 		PermissionTemplateBo impl = businessObjectService.findByPrimaryKey( PermissionTemplateBo.class, criteria );
522 		if ( impl != null ) {
523 			return PermissionTemplateBo.to(impl);
524 		}
525 		return null;
526 	}
527 
528     @Override
529 	public List<Template> getAllTemplates() {
530 		if ( allTemplates.isEmpty() ) {
531 			Map<String,String> criteria = new HashMap<String,String>(1);
532 			criteria.put( KRADPropertyConstants.ACTIVE, "Y" );
533 			List<PermissionTemplateBo> impls = (List<PermissionTemplateBo>) businessObjectService.findMatching( PermissionTemplateBo.class, criteria );
534 			List<Template> infos = new ArrayList<Template>( impls.size() );
535 			for ( PermissionTemplateBo impl : impls ) {
536 				infos.add( PermissionTemplateBo.to(impl) );
537 			}
538 			Collections.sort(infos, new Comparator<Template>() {
539 				@Override public int compare(Template tmpl1,
540 						Template tmpl2) {
541 
542 					int result = tmpl1.getNamespaceCode().compareTo(tmpl2.getNamespaceCode());
543 					if ( result != 0 ) {
544 						return result;
545 					}
546 					result = tmpl1.getName().compareTo(tmpl2.getName());
547 					return result;
548 				}
549 			});
550 			allTemplates.addAll(infos);
551 		}
552 		return Collections.unmodifiableList(allTemplates);
553     }
554 
555 
556 	@Override
557 	public Permission createPermission(Permission permission)
558 			throws RiceIllegalArgumentException, RiceIllegalStateException {
559         incomingParamCheck(permission, "permission");
560 
561         if (StringUtils.isNotBlank(permission.getId()) && getPermission(permission.getId()) != null) {
562             throw new RiceIllegalStateException("the permission to create already exists: " + permission);
563         }
564         List<PermissionAttributeBo> attrBos = Collections.emptyList();
565         if (permission.getTemplate() != null) {
566             attrBos = KimAttributeDataBo.createFrom(PermissionAttributeBo.class, permission.getAttributes(), permission.getTemplate().getKimTypeId());
567         }
568         PermissionBo bo = PermissionBo.from(permission);
569         if (bo.getTemplate() == null && bo.getTemplateId() != null) {
570             bo.setTemplate(PermissionTemplateBo.from(getPermissionTemplate(bo.getTemplateId())));
571         }
572         bo.setAttributeDetails(attrBos);
573         return PermissionBo.to(businessObjectService.save(bo));
574 	}
575 
576 	@Override
577 	public Permission updatePermission(Permission permission)
578 			throws RiceIllegalArgumentException, RiceIllegalStateException {
579         incomingParamCheck(permission, "permission");
580 
581         PermissionBo oldPermission = getPermissionImpl(permission.getId());
582         if (StringUtils.isBlank(permission.getId()) || oldPermission == null) {
583             throw new RiceIllegalStateException("the permission does not exist: " + permission);
584         }
585 
586         //List<PermissionAttributeBo> attrBos = KimAttributeDataBo.createFrom(PermissionAttributeBo.class, permission.getAttributes(), permission.getTemplate().getKimTypeId());
587 
588         List<PermissionAttributeBo> oldAttrBos = oldPermission.getAttributeDetails();
589         //put old attributes in map for easier updating
590         Map<String, PermissionAttributeBo> oldAttrMap = new HashMap<String, PermissionAttributeBo>();
591         for (PermissionAttributeBo oldAttr : oldAttrBos) {
592             oldAttrMap.put(oldAttr.getKimAttribute().getAttributeName(), oldAttr);
593         }
594         List<PermissionAttributeBo> newAttrBos = new ArrayList<PermissionAttributeBo>();
595         for (String key : permission.getAttributes().keySet()) {
596             if (oldAttrMap.containsKey(key)) {
597                 PermissionAttributeBo updatedAttr = oldAttrMap.get(key);
598                 updatedAttr.setAttributeValue(permission.getAttributes().get(key));
599                 newAttrBos.add(updatedAttr);
600             } else { //new attribute
601                 newAttrBos.addAll(KimAttributeDataBo.createFrom(PermissionAttributeBo.class, Collections.singletonMap(key, permission.getAttributes().get(key)), permission.getTemplate().getKimTypeId()));
602             }
603         }
604         PermissionBo bo = PermissionBo.from(permission);
605         if (CollectionUtils.isNotEmpty(newAttrBos)) {
606             if(null!= bo.getAttributeDetails())  {
607                 bo.getAttributeDetails().clear();
608             }
609             bo.setAttributeDetails(newAttrBos);
610         }
611         if (bo.getTemplate() == null && bo.getTemplateId() != null) {
612             bo.setTemplate(PermissionTemplateBo.from(getPermissionTemplate(bo.getTemplateId())));
613         }
614 
615         return PermissionBo.to(businessObjectService.save(bo));		
616 	}
617 	
618     @Override
619     public Permission findPermByNamespaceCodeAndName(String namespaceCode, String permissionName)
620             throws RiceIllegalArgumentException {
621         incomingParamCheck(namespaceCode, "namespaceCode");
622         incomingParamCheck(permissionName, "permissionName");
623 
624         PermissionBo permissionBo = getPermissionBoByName(namespaceCode, permissionName);
625         if (permissionBo != null) {
626             return PermissionBo.to(permissionBo);
627         }
628         return null;
629     }
630     
631     protected PermissionBo getPermissionBoByName(String namespaceCode, String permissionName) {
632         if (StringUtils.isBlank(namespaceCode)
633                 || StringUtils.isBlank(permissionName)) {
634             return null;
635         }
636         Map<String, String> criteria = new HashMap<String, String>();
637         criteria.put(KimConstants.UniqueKeyConstants.NAMESPACE_CODE, namespaceCode);
638         criteria.put(KimConstants.UniqueKeyConstants.NAME, permissionName);
639         criteria.put(KRADPropertyConstants.ACTIVE, "Y");
640         // while this is not actually the primary key - there will be at most one row with these criteria
641         return businessObjectService.findByPrimaryKey(PermissionBo.class, criteria);
642     }
643 
644     @Override
645     public PermissionQueryResults findPermissions(final QueryByCriteria queryByCriteria)
646             throws RiceIllegalArgumentException {
647         incomingParamCheck(queryByCriteria, "queryByCriteria");
648 
649         LookupCustomizer.Builder<PermissionBo> lc = LookupCustomizer.Builder.create();
650         lc.setPredicateTransform(AttributeTransform.getInstance());
651 
652         GenericQueryResults<PermissionBo> results = criteriaLookupService.lookup(PermissionBo.class, queryByCriteria, lc.build());
653 
654         PermissionQueryResults.Builder builder = PermissionQueryResults.Builder.create();
655         builder.setMoreResultsAvailable(results.isMoreResultsAvailable());
656         builder.setTotalRowCount(results.getTotalRowCount());
657 
658         final List<Permission.Builder> ims = new ArrayList<Permission.Builder>();
659         for (PermissionBo bo : results.getResults()) {
660             ims.add(Permission.Builder.create(bo));
661         }
662 
663         builder.setResults(ims);
664         return builder.build();
665     }
666 
667     @Override
668     public TemplateQueryResults findPermissionTemplates(final QueryByCriteria queryByCriteria)
669             throws RiceIllegalArgumentException {
670         incomingParamCheck(queryByCriteria, "queryByCriteria");
671 
672         GenericQueryResults<PermissionTemplateBo> results = criteriaLookupService.lookup(PermissionTemplateBo.class, queryByCriteria);
673 
674         TemplateQueryResults.Builder builder = TemplateQueryResults.Builder.create();
675         builder.setMoreResultsAvailable(results.isMoreResultsAvailable());
676         builder.setTotalRowCount(results.getTotalRowCount());
677 
678         final List<Template.Builder> ims = new ArrayList<Template.Builder>();
679         for (PermissionTemplateBo bo : results.getResults()) {
680             ims.add(Template.Builder.create(bo));
681         }
682 
683         builder.setResults(ims);
684         return builder.build();
685     }
686 
687     private List<String> getRoleIdsForPermissions( Collection<Permission> permissions ) {
688         if (CollectionUtils.isEmpty(permissions)) {
689             return Collections.emptyList();
690         }
691         List<String> ids = new ArrayList<String>();
692         for (Permission p : permissions) {
693             ids.add(p.getId());
694         }
695 
696         return getRoleIdsForPermissionIds(ids);
697     }
698 
699     private List<String> getRoleIdsForPermissionIds(Collection<String> permissionIds) {
700         if (CollectionUtils.isEmpty(permissionIds)) {
701             return Collections.emptyList();
702         }
703         String cacheKey =  new StringBuilder("{getRoleIdsForPermissionIds}")
704                 .append("permissionIds=").append(CacheKeyUtils.key(permissionIds)).toString();
705         Cache.ValueWrapper cachedValue = cacheManager.getCache(Permission.Cache.NAME).get(cacheKey);
706         if (cachedValue != null && cachedValue.get() instanceof List) {
707             return ((List<String>)cachedValue.get());
708         }
709         QueryByCriteria query = QueryByCriteria.Builder.fromPredicates(equal("active", "true"), in("permissionId", permissionIds.toArray(new String[]{})));
710         GenericQueryResults<RolePermissionBo> results = criteriaLookupService.lookup(RolePermissionBo.class, query);
711         List<String> roleIds = new ArrayList<String>();
712         for (RolePermissionBo bo : results.getResults()) {
713             roleIds.add(bo.getRoleId());
714         }
715         roleIds = Collections.unmodifiableList(roleIds);
716         cacheManager.getCache(Permission.Cache.NAME).put(cacheKey, roleIds);
717         return roleIds;
718     }
719 	
720 	/**
721      * Sets the kimTypeInfoService attribute value.
722      *
723      * @param kimTypeInfoService The kimTypeInfoService to set.
724      */
725 	public void setKimTypeInfoService(KimTypeInfoService kimTypeInfoService) {
726 		this.kimTypeInfoService = kimTypeInfoService;
727 	}
728 	
729 	/**
730      * Sets the defaultPermissionTypeService attribute value.
731      *
732      * @param defaultPermissionTypeService The defaultPermissionTypeService to set.
733      */
734 	public void setDefaultPermissionTypeService(PermissionTypeService defaultPermissionTypeService) {
735     	this.defaultPermissionTypeService = defaultPermissionTypeService;
736 	}
737 	
738 	/**
739      * Sets the roleService attribute value.
740      *
741      * @param roleService The roleService to set.
742      */
743 	public void setRoleService(RoleService roleService) {
744 		this.roleService = roleService;
745 	}
746 
747     /**
748      * Sets the businessObjectService attribute value.
749      *
750      * @param businessObjectService The businessObjectService to set.
751      */
752     public void setBusinessObjectService(final BusinessObjectService businessObjectService) {
753         this.businessObjectService = businessObjectService;
754     }
755 
756     /**
757      * Sets the criteriaLookupService attribute value.
758      *
759      * @param criteriaLookupService The criteriaLookupService to set.
760      */
761     public void setCriteriaLookupService(final CriteriaLookupService criteriaLookupService) {
762         this.criteriaLookupService = criteriaLookupService;
763     }
764 
765     /**
766      * Sets the cache manager which this service implementation can for internal caching.
767      * Calling this setter is optional, though the value passed to it must not be null.
768      *
769      * @param cacheManager the cache manager to use for internal caching, must not be null
770      * @throws IllegalArgumentException if a null cache manager is passed
771      */
772     public void setCacheManager(final CacheManager cacheManager) {
773         if (cacheManager == null) {
774             throw new IllegalArgumentException("cacheManager must not be null");
775         }
776         this.cacheManager = cacheManager;
777     }
778 
779     private List<Permission> toPermissions(Collection<PermissionBo> permissionBos) {
780         if (CollectionUtils.isEmpty(permissionBos)) {
781             return new ArrayList<Permission>();
782         }
783         List<Permission> permissions = new ArrayList<Permission>(permissionBos.size());
784         for (PermissionBo permissionBo : permissionBos) {
785             permissions.add(PermissionBo.to(permissionBo));
786         }
787         return permissions;
788     }
789     
790     protected void logAuthorizationCheck(String checkType, String principalId, String namespaceCode, String permissionName, Map<String, String> qualification ) {
791         StringBuilder sb = new StringBuilder();
792         sb.append(  '\n' );
793         sb.append( "Is AuthZ for " ).append( checkType ).append( ": " ).append( namespaceCode ).append( "/" ).append( permissionName ).append( '\n' );
794         sb.append( "             Principal:  " ).append( principalId );
795         if ( principalId != null ) {
796             Principal principal = KimApiServiceLocator.getIdentityService().getPrincipal(principalId);
797             if ( principal != null ) {
798                 sb.append( " (" ).append( principal.getPrincipalName() ).append( ')' );
799             }
800         }
801         sb.append( '\n' );
802         sb.append( "             Qualifiers:\n" );
803         if ( qualification != null && !qualification.isEmpty() ) {
804             sb.append( qualification );
805         } else {
806             sb.append( "                         [null]\n" );
807         }
808         if (LOG.isTraceEnabled()) {
809             LOG.trace( sb.append(ExceptionUtils.getStackTrace(new Throwable())));
810         } else {
811             LOG.debug(sb.toString());
812         }
813     }
814     
815     protected void logAuthorizationCheckByTemplate(String checkType, String principalId, String namespaceCode, String permissionName, 
816                                                    Map<String, String> permissionDetails, Map<String, String> qualification ) {
817         StringBuilder sb = new StringBuilder();
818         sb.append(  '\n' );
819         sb.append( "Is AuthZ for " ).append( checkType ).append( ": " ).append( namespaceCode ).append( "/" ).append( permissionName ).append( '\n' );
820         sb.append( "             Principal:  " ).append( principalId );
821         if ( principalId != null ) {
822             Principal principal = KimApiServiceLocator.getIdentityService().getPrincipal(principalId);
823             if ( principal != null ) {
824                 sb.append( " (" ).append( principal.getPrincipalName() ).append( ')' );
825             }
826         }
827         sb.append( '\n' );
828         sb.append( "             Details:\n" );
829         if ( permissionDetails != null ) {
830             sb.append( permissionDetails );
831         } else {
832             sb.append( "                         [null]\n" );
833         }
834         sb.append( "             Qualifiers:\n" );
835         if ( qualification != null && !qualification.isEmpty() ) {
836             sb.append( qualification );
837         } else {
838             sb.append( "                         [null]\n" );
839         }
840         if (LOG.isTraceEnabled()) {
841             LOG.trace( sb.append(ExceptionUtils.getStackTrace(new Throwable())));
842         } else {
843             LOG.debug(sb.toString());
844         }
845     }
846     
847     private void incomingParamCheck(Object object, String name) {
848         if (object == null) {
849             throw new RiceIllegalArgumentException(name + " was null");
850         } else if (object instanceof String
851                 && StringUtils.isBlank((String) object)) {
852             throw new RiceIllegalArgumentException(name + " was blank");
853         }
854     }
855 
856 
857 }