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.kuali.rice.core.api.criteria.CriteriaLookupService;
21  import org.kuali.rice.core.api.criteria.GenericQueryResults;
22  import org.kuali.rice.core.api.criteria.LookupCustomizer;
23  import org.kuali.rice.core.api.criteria.QueryByCriteria;
24  import org.kuali.rice.core.api.exception.RiceIllegalArgumentException;
25  import org.kuali.rice.core.api.exception.RiceIllegalStateException;
26  import org.kuali.rice.core.api.membership.MemberType;
27  import org.kuali.rice.core.api.resourceloader.GlobalResourceLoader;
28  import org.kuali.rice.kim.api.KimConstants;
29  import org.kuali.rice.kim.api.common.assignee.Assignee;
30  import org.kuali.rice.kim.api.common.delegate.DelegateType;
31  import org.kuali.rice.kim.api.common.template.Template;
32  import org.kuali.rice.kim.api.common.template.TemplateQueryResults;
33  import org.kuali.rice.kim.api.permission.Permission;
34  import org.kuali.rice.kim.api.permission.PermissionQueryResults;
35  import org.kuali.rice.kim.api.permission.PermissionService;
36  import org.kuali.rice.kim.api.role.RoleMembership;
37  import org.kuali.rice.kim.api.role.RoleService;
38  import org.kuali.rice.kim.api.type.KimType;
39  import org.kuali.rice.kim.api.type.KimTypeInfoService;
40  import org.kuali.rice.kim.framework.permission.PermissionTypeService;
41  import org.kuali.rice.kim.impl.common.attribute.AttributeTransform;
42  import org.kuali.rice.kim.impl.common.attribute.KimAttributeDataBo;
43  import org.kuali.rice.kim.impl.role.RolePermissionBo;
44  import org.kuali.rice.krad.service.BusinessObjectService;
45  import org.kuali.rice.krad.util.KRADPropertyConstants;
46  
47  import java.util.ArrayList;
48  import java.util.Collection;
49  import java.util.Collections;
50  import java.util.Comparator;
51  import java.util.HashMap;
52  import java.util.List;
53  import java.util.Map;
54  import java.util.concurrent.CopyOnWriteArrayList;
55  
56  import static org.kuali.rice.core.api.criteria.PredicateFactory.*;
57  
58  public class PermissionServiceImpl implements PermissionService {
59  	private RoleService roleService;
60      private PermissionTypeService defaultPermissionTypeService;
61      private KimTypeInfoService kimTypeInfoService;
62  	private BusinessObjectService businessObjectService;
63  	private CriteriaLookupService criteriaLookupService;
64  
65   	private final CopyOnWriteArrayList<Template> allTemplates = new CopyOnWriteArrayList<Template>();
66  
67      // --------------------
68      // Authorization Checks
69      // --------------------
70  
71      protected PermissionTypeService getPermissionTypeService( PermissionTemplateBo permissionTemplate ) {
72      	if ( permissionTemplate == null ) {
73      		throw new IllegalArgumentException( "permissionTemplate may not be null" );
74      	}
75      	KimType kimType = kimTypeInfoService.getKimType( permissionTemplate.getKimTypeId() );
76      	String serviceName = kimType.getServiceName();
77      	// if no service specified, return a default implementation
78      	if ( StringUtils.isBlank( serviceName ) ) {
79      		return defaultPermissionTypeService;
80      	}
81      	try {
82  	    	Object service = GlobalResourceLoader.getService(serviceName);
83  	    	// if we have a service name, it must exist
84  	    	if ( service == null ) {
85  				throw new RuntimeException("null returned for permission type service for service name: " + serviceName);
86  	    	}
87  	    	// whatever we retrieved must be of the correct type
88  	    	if ( !(service instanceof PermissionTypeService)  ) {
89  	    		throw new RuntimeException( "Service " + serviceName + " was not a PermissionTypeService.  Was: " + service.getClass().getName() );
90  	    	}
91  	    	return (PermissionTypeService)service;
92      	} catch( Exception ex ) {
93      		// sometimes service locators throw exceptions rather than returning null, handle that
94      		throw new RuntimeException( "Error retrieving service: " + serviceName + " from the KimImplServiceLocator.", ex );
95      	}
96      }
97  
98      @Override
99      public boolean hasPermission(String principalId, String namespaceCode,
100                                  String permissionName) throws RiceIllegalArgumentException  {
101         incomingParamCheck(principalId, "principalId");
102         incomingParamCheck(namespaceCode, "namespaceCode");
103         incomingParamCheck(permissionName, "permissionName");
104 
105         return isAuthorized( principalId, namespaceCode, permissionName, Collections.<String, String>emptyMap() );
106     }
107 
108     @Override
109     public boolean isAuthorized(String principalId, String namespaceCode,
110                                 String permissionName, Map<String, String> qualification ) throws RiceIllegalArgumentException {
111         incomingParamCheck(principalId, "principalId");
112         incomingParamCheck(namespaceCode, "namespaceCode");
113         incomingParamCheck(permissionName, "permissionName");
114         incomingParamCheck(qualification, "qualification");
115 
116         List<String> roleIds = getRoleIdsForPermission( namespaceCode, permissionName );
117     	if ( roleIds.isEmpty() ) {
118     		return false;
119     	}
120 
121 		return roleService.principalHasRole( principalId, roleIds, qualification);
122 
123     }
124     @Override
125     public boolean hasPermissionByTemplate(String principalId, String namespaceCode, String permissionTemplateName,
126             Map<String, String> permissionDetails) throws RiceIllegalArgumentException {
127         incomingParamCheck(principalId, "principalId");
128         incomingParamCheck(namespaceCode, "namespaceCode");
129         incomingParamCheck(permissionTemplateName, "permissionTemplateName");
130 
131         return isAuthorizedByTemplate(principalId, namespaceCode, permissionTemplateName, permissionDetails,
132                 Collections.<String, String>emptyMap());
133     }
134     @Override
135     public boolean isAuthorizedByTemplate(String principalId, String namespaceCode, String permissionTemplateName,
136             Map<String, String> permissionDetails, Map<String, String> qualification) throws RiceIllegalArgumentException {
137         incomingParamCheck(principalId, "principalId");
138         incomingParamCheck(namespaceCode, "namespaceCode");
139         incomingParamCheck(permissionTemplateName, "permissionTemplateName");
140         incomingParamCheck(qualification, "qualification");
141 
142         List<String> roleIds = getRoleIdsForPermissionTemplate( namespaceCode, permissionTemplateName, permissionDetails );
143     	if ( roleIds.isEmpty() ) {
144     		return false;
145     	}
146     	return roleService.principalHasRole( principalId, roleIds, qualification);
147     }
148     @Override
149     public List<Permission> getAuthorizedPermissions( String principalId,
150             String namespaceCode, String permissionName,
151             Map<String, String> qualification ) throws RiceIllegalArgumentException {
152         incomingParamCheck(principalId, "principalId");
153         incomingParamCheck(namespaceCode, "namespaceCode");
154         incomingParamCheck(permissionName, "permissionName");
155         incomingParamCheck(qualification, "qualification");
156 
157         // get all the permission objects whose name match that requested
158     	List<PermissionBo> permissions = getPermissionImplsByName( namespaceCode, permissionName );
159     	// now, filter the full list by the detail passed
160     	List<Permission> applicablePermissions = getMatchingPermissions( permissions, null );
161     	return getPermissionsForUser(principalId, applicablePermissions, qualification);
162     }
163     @Override
164     public List<Permission> getAuthorizedPermissionsByTemplate(String principalId, String namespaceCode,
165             String permissionTemplateName, 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         // get all the permission objects whose name match that requested
172     	List<PermissionBo> permissions = getPermissionImplsByTemplateName( namespaceCode, permissionTemplateName );
173     	// now, filter the full list by the detail passed
174     	List<Permission> applicablePermissions = getMatchingPermissions( permissions, permissionDetails );
175     	return getPermissionsForUser(principalId, applicablePermissions, qualification);
176     }
177     
178     /**
179      * Checks the list of permissions against the principal's roles and returns a subset of the list which match.
180      */
181     protected List<Permission> getPermissionsForUser( String principalId, List<Permission> permissions,
182             Map<String, String> qualification ) {
183     	ArrayList<Permission> results = new ArrayList<Permission>();
184     	List<Permission> tempList = new ArrayList<Permission>(1);
185     	for ( Permission perm : permissions ) {
186     		tempList.clear();
187     		tempList.add( perm );
188     		List<String> roleIds = getRoleIdsForPermissions( tempList );
189     		// TODO: This could be made a little better by collecting the role IDs into
190     		// a set and then processing the distinct list rather than a check
191     		// for every permission
192     		if ( roleIds != null && !roleIds.isEmpty() ) {
193     			if ( roleService.principalHasRole( principalId, roleIds, qualification) ) {
194     				results.add( perm );
195     			}
196     		}
197     	}
198     	
199     	return Collections.unmodifiableList(results);
200     }
201 
202     protected Map<String,PermissionTypeService> getPermissionTypeServicesByTemplateId( Collection<PermissionBo> permissions ) {
203     	Map<String,PermissionTypeService> permissionTypeServices = new HashMap<String, PermissionTypeService>( permissions.size() );
204     	for ( PermissionBo perm : permissions ) {
205     		permissionTypeServices.put(perm.getTemplateId(), getPermissionTypeService( perm.getTemplate() ) );    				
206     	}
207     	return permissionTypeServices;
208     }
209     
210     @SuppressWarnings("static-access")
211 	protected Map<String,List<Permission>> groupPermissionsByTemplate( Collection<PermissionBo> permissions ) {
212     	Map<String,List<Permission>> results = new HashMap<String,List<Permission>>();
213     	for ( PermissionBo perm : permissions ) {
214     		List<Permission> perms = results.get( perm.getTemplateId() );
215     		if ( perms == null ) {
216     			perms = new ArrayList<Permission>();
217     			results.put( perm.getTemplateId(), perms );
218     		}
219     		perms.add( PermissionBo.to(perm) );
220     	}
221     	return results;
222     }
223     
224 	/**
225      * Compare each of the passed in permissions with the given permissionDetails.  Those that
226      * match are added to the result list.
227      */
228     protected List<Permission> getMatchingPermissions( List<PermissionBo> permissions, Map<String, String> permissionDetails ) {
229     	List<Permission> applicablePermissions = new ArrayList<Permission>();    	
230     	if ( permissionDetails == null || permissionDetails.isEmpty() ) {
231     		// if no details passed, assume that all match
232     		for ( PermissionBo perm : permissions ) {
233     			applicablePermissions.add( PermissionBo.to(perm) );
234     		}
235     	} else {
236     		// otherwise, attempt to match the permission details
237     		// build a map of the template IDs to the type services
238     		Map<String,PermissionTypeService> permissionTypeServices = getPermissionTypeServicesByTemplateId( permissions );
239     		// build a map of permissions by template ID
240     		Map<String,List<Permission>> permissionMap = groupPermissionsByTemplate( permissions );
241     		// loop over the different templates, matching all of the same template against the type
242     		// service at once
243     		for ( Map.Entry<String,List<Permission>> entry : permissionMap.entrySet() ) {
244     			PermissionTypeService permissionTypeService = permissionTypeServices.get( entry.getKey() );
245     			List<Permission> permissionList = entry.getValue();
246 				applicablePermissions.addAll( permissionTypeService.getMatchingPermissions( permissionDetails, permissionList ) );    				
247     		}
248     	}
249     	return applicablePermissions;
250     }
251     @Override
252     public List<Assignee> getPermissionAssignees( String namespaceCode, String permissionName,
253             Map<String, String> qualification ) throws RiceIllegalArgumentException {
254         incomingParamCheck(namespaceCode, "namespaceCode");
255         incomingParamCheck(permissionName, "permissionName");
256         incomingParamCheck(qualification, "qualification");
257 
258 
259     	List<String> roleIds = getRoleIdsForPermission( namespaceCode, permissionName);
260     	if ( roleIds.isEmpty() ) {
261     		return Collections.emptyList();
262     	}
263     	Collection<RoleMembership> roleMembers = roleService.getRoleMembers( roleIds,qualification );
264     	List<Assignee> results = new ArrayList<Assignee>();
265         for ( RoleMembership rm : roleMembers ) {
266 			List<DelegateType.Builder> delegateBuilderList = new ArrayList<DelegateType.Builder>();
267 			if (!rm.getDelegates().isEmpty()) {
268     			for (DelegateType delegate : rm.getDelegates()){
269                     delegateBuilderList.add(DelegateType.Builder.create(delegate));
270     			}
271 			}
272     		if ( MemberType.PRINCIPAL.equals(rm.getType()) ) {
273     			results.add (Assignee.Builder.create(rm.getMemberId(), null, delegateBuilderList).build());
274     		} else if ( MemberType.GROUP.equals(rm.getType()) ) {
275     			results.add (Assignee.Builder.create(null, rm.getMemberId(), delegateBuilderList).build());
276     		}
277     	}
278     	return Collections.unmodifiableList(results);
279     }
280 
281     @Override
282     public List<Assignee> getPermissionAssigneesByTemplate(String namespaceCode, String permissionTemplateName,
283             Map<String, String> permissionDetails, Map<String, String> qualification) throws RiceIllegalArgumentException {
284         incomingParamCheck(namespaceCode, "namespaceCode");
285         incomingParamCheck(permissionTemplateName, "permissionTemplateName");
286         incomingParamCheck(qualification, "qualification");
287 
288     	List<String> roleIds = getRoleIdsForPermissionTemplate( namespaceCode, permissionTemplateName, permissionDetails);
289     	if ( roleIds.isEmpty() ) {
290     		return Collections.emptyList();
291     	}
292     	Collection<RoleMembership> roleMembers = roleService.getRoleMembers( roleIds,qualification);
293     	List<Assignee> results = new ArrayList<Assignee>();
294         for ( RoleMembership rm : roleMembers ) {
295 			List<DelegateType.Builder> delegateBuilderList = new ArrayList<DelegateType.Builder>();
296 			if (!rm.getDelegates().isEmpty()) {
297     			for (DelegateType delegate : rm.getDelegates()){
298                     delegateBuilderList.add(DelegateType.Builder.create(delegate));
299     			}
300 			}
301     		if ( MemberType.PRINCIPAL.equals(rm.getType()) ) {
302     			results.add (Assignee.Builder.create(rm.getMemberId(), null, delegateBuilderList).build());
303     		} else { // a group membership
304     			results.add (Assignee.Builder.create(null, rm.getMemberId(), delegateBuilderList).build());
305     		}
306     	}
307     	return Collections.unmodifiableList(results);
308     }
309 
310     @Override
311     public boolean isPermissionDefined( String namespaceCode, String permissionName ) throws RiceIllegalArgumentException {
312         incomingParamCheck(namespaceCode, "namespaceCode");
313         incomingParamCheck(permissionName, "permissionName");
314 
315     	// get all the permission objects whose name match that requested
316     	List<PermissionBo> permissions = getPermissionImplsByName( namespaceCode, permissionName );
317     	// now, filter the full list by the detail passed
318     	return !getMatchingPermissions( permissions, null ).isEmpty();
319     }
320 
321     @Override
322     public boolean isPermissionDefinedByTemplate(String namespaceCode, String permissionTemplateName,
323             Map<String, String> permissionDetails) throws RiceIllegalArgumentException {
324 
325         incomingParamCheck(namespaceCode, "namespaceCode");
326         incomingParamCheck(permissionTemplateName, "permissionTemplateName");
327 
328     	// get all the permission objects whose name match that requested
329     	List<PermissionBo> permissions = getPermissionImplsByTemplateName( namespaceCode, permissionTemplateName );
330     	// now, filter the full list by the detail passed
331     	return !getMatchingPermissions( permissions, permissionDetails ).isEmpty();
332     }
333 
334     @Override
335     public List<String> getRoleIdsForPermission(String namespaceCode, String permissionName) throws RiceIllegalArgumentException {
336         incomingParamCheck(namespaceCode, "namespaceCode");
337         incomingParamCheck(permissionName, "permissionName");
338 
339         // get all the permission objects whose name match that requested
340         List<PermissionBo> permissions = getPermissionImplsByName(namespaceCode, permissionName);
341         // now, filter the full list by the detail passed
342         List<Permission> applicablePermissions = getMatchingPermissions(permissions, null);
343         return getRoleIdsForPermissions(applicablePermissions);
344     }
345 
346     protected List<String> getRoleIdsForPermissionTemplate( String namespaceCode, String permissionTemplateName, Map<String, String> permissionDetails ) {
347     	// get all the permission objects whose name match that requested
348     	List<PermissionBo> permissions = getPermissionImplsByTemplateName( namespaceCode, permissionTemplateName );
349     	// now, filter the full list by the detail passed
350     	List<Permission> applicablePermissions = getMatchingPermissions( permissions, permissionDetails );
351     	return getRoleIdsForPermissions( applicablePermissions );
352     }
353 
354     // --------------------
355     // Permission Data
356     // --------------------
357     
358     @Override
359     public Permission getPermission(String permissionId) throws RiceIllegalArgumentException {
360         incomingParamCheck(permissionId, "permissionId");
361 
362         PermissionBo impl = getPermissionImpl( permissionId );
363     	if ( impl != null ) {
364     		return PermissionBo.to(impl);
365     	}
366     	return null;
367     }
368     
369     @Override
370     public List<Permission> findPermissionsByTemplate(String namespaceCode, String permissionTemplateName) throws RiceIllegalArgumentException {
371         incomingParamCheck(namespaceCode, "namespaceCode");
372         incomingParamCheck(permissionTemplateName, "permissionTemplateName");
373 
374         List<PermissionBo> impls = getPermissionImplsByTemplateName( namespaceCode, permissionTemplateName );
375     	List<Permission> results = new ArrayList<Permission>(impls.size());
376     	for (PermissionBo impl : impls) {
377     	    results.add(PermissionBo.to(impl));
378     	}
379     	return Collections.unmodifiableList(results);
380     }
381 
382 	protected PermissionBo getPermissionImpl(String permissionId) throws RiceIllegalArgumentException {
383     	incomingParamCheck(permissionId, "permissionId");
384 
385         HashMap<String,Object> pk = new HashMap<String,Object>( 1 );
386         pk.put( KimConstants.PrimaryKeyConstants.PERMISSION_ID, permissionId );
387         return businessObjectService.findByPrimaryKey( PermissionBo.class, pk );
388     }
389     
390     protected List<PermissionBo> getPermissionImplsByTemplateName( String namespaceCode, String permissionTemplateName ){
391         HashMap<String,Object> pk = new HashMap<String,Object>( 3 );
392         pk.put( "template.namespaceCode", namespaceCode );
393         pk.put( "template.name", permissionTemplateName );
394         pk.put( KRADPropertyConstants.ACTIVE, "Y" );
395         return ((List<PermissionBo>) businessObjectService.findMatching( PermissionBo.class, pk ));
396     }
397 
398     protected List<PermissionBo> getPermissionImplsByName( String namespaceCode, String permissionName ) {
399         HashMap<String,Object> pk = new HashMap<String,Object>( 3 );
400         pk.put( KimConstants.UniqueKeyConstants.NAMESPACE_CODE, namespaceCode );
401         pk.put( KimConstants.UniqueKeyConstants.PERMISSION_NAME, permissionName );
402         pk.put( KRADPropertyConstants.ACTIVE, "Y" );
403         
404         return ((List<PermissionBo>) businessObjectService.findMatching( PermissionBo.class, pk ));
405     }
406 	
407     @Override
408 	public Template getPermissionTemplate(String permissionTemplateId) throws RiceIllegalArgumentException {
409         incomingParamCheck(permissionTemplateId, "permissionTemplateId");
410 
411         PermissionTemplateBo impl = businessObjectService.findBySinglePrimaryKey( PermissionTemplateBo.class, permissionTemplateId );
412 		if ( impl != null ) {
413 			return PermissionTemplateBo.to(impl);
414 		}
415 		return null;
416 	}
417 
418     @Override
419 	public Template findPermTemplateByNamespaceCodeAndName(String namespaceCode,
420             String permissionTemplateName) throws RiceIllegalArgumentException {
421 		incomingParamCheck(namespaceCode, "namespaceCode");
422         incomingParamCheck(permissionTemplateName, "permissionTemplateName");
423 
424         Map<String,String> criteria = new HashMap<String,String>(2);
425 		criteria.put( KimConstants.UniqueKeyConstants.NAMESPACE_CODE, namespaceCode );
426 		criteria.put( KimConstants.UniqueKeyConstants.PERMISSION_TEMPLATE_NAME, permissionTemplateName );
427 		PermissionTemplateBo impl = businessObjectService.findByPrimaryKey( PermissionTemplateBo.class, criteria );
428 		if ( impl != null ) {
429 			return PermissionTemplateBo.to(impl);
430 		}
431 		return null;
432 	}
433 
434     @Override
435 	public List<Template> getAllTemplates() {
436 		if ( allTemplates.isEmpty() ) {
437 			Map<String,String> criteria = new HashMap<String,String>(1);
438 			criteria.put( KRADPropertyConstants.ACTIVE, "Y" );
439 			List<PermissionTemplateBo> impls = (List<PermissionTemplateBo>) businessObjectService.findMatching( PermissionTemplateBo.class, criteria );
440 			List<Template> infos = new ArrayList<Template>( impls.size() );
441 			for ( PermissionTemplateBo impl : impls ) {
442 				infos.add( PermissionTemplateBo.to(impl) );
443 			}
444 			Collections.sort(infos, new Comparator<Template>() {
445 				@Override public int compare(Template tmpl1,
446 						Template tmpl2) {
447 
448 					int result = tmpl1.getNamespaceCode().compareTo(tmpl2.getNamespaceCode());
449 					if ( result != 0 ) {
450 						return result;
451 					}
452 					result = tmpl1.getName().compareTo(tmpl2.getName());
453 					return result;
454 				}
455 			});
456 			allTemplates.addAll(infos);
457 		}
458 		return Collections.unmodifiableList(allTemplates);
459     }
460 
461 
462 	@Override
463 	public Permission createPermission(Permission permission)
464 			throws RiceIllegalArgumentException, RiceIllegalStateException {
465         incomingParamCheck(permission, "permission");
466 
467         if (StringUtils.isNotBlank(permission.getId()) && getPermission(permission.getId()) != null) {
468             throw new RiceIllegalStateException("the permission to create already exists: " + permission);
469         }
470         List<PermissionAttributeBo> attrBos = Collections.emptyList();
471         if (permission.getTemplate() != null) {
472             attrBos = KimAttributeDataBo.createFrom(PermissionAttributeBo.class, permission.getAttributes(), permission.getTemplate().getKimTypeId());
473         }
474         PermissionBo bo = PermissionBo.from(permission);
475         if (bo.getTemplate() == null && bo.getTemplateId() != null) {
476             bo.setTemplate(PermissionTemplateBo.from(getPermissionTemplate(bo.getTemplateId())));
477         }
478         bo.setAttributeDetails(attrBos);
479         return PermissionBo.to(businessObjectService.save(bo));
480 	}
481 
482 	@Override
483 	public Permission updatePermission(Permission permission)
484 			throws RiceIllegalArgumentException, RiceIllegalStateException {
485         incomingParamCheck(permission, "permission");
486 
487         PermissionBo oldPermission = getPermissionImpl(permission.getId());
488         if (StringUtils.isBlank(permission.getId()) || oldPermission == null) {
489             throw new RiceIllegalStateException("the permission does not exist: " + permission);
490         }
491 
492         //List<PermissionAttributeBo> attrBos = KimAttributeDataBo.createFrom(PermissionAttributeBo.class, permission.getAttributes(), permission.getTemplate().getKimTypeId());
493 
494         List<PermissionAttributeBo> oldAttrBos = oldPermission.getAttributeDetails();
495         //put old attributes in map for easier updating
496         Map<String, PermissionAttributeBo> oldAttrMap = new HashMap<String, PermissionAttributeBo>();
497         for (PermissionAttributeBo oldAttr : oldAttrBos) {
498             oldAttrMap.put(oldAttr.getKimAttribute().getAttributeName(), oldAttr);
499         }
500         List<PermissionAttributeBo> newAttrBos = new ArrayList<PermissionAttributeBo>();
501         for (String key : permission.getAttributes().keySet()) {
502             if (oldAttrMap.containsKey(key)) {
503                 PermissionAttributeBo updatedAttr = oldAttrMap.get(key);
504                 updatedAttr.setAttributeValue(permission.getAttributes().get(key));
505                 newAttrBos.add(updatedAttr);
506             } else { //new attribute
507                 newAttrBos.addAll(KimAttributeDataBo.createFrom(PermissionAttributeBo.class, Collections.singletonMap(key, permission.getAttributes().get(key)), permission.getTemplate().getKimTypeId()));
508             }
509         }
510         PermissionBo bo = PermissionBo.from(permission);
511         if (CollectionUtils.isNotEmpty(newAttrBos)) {
512             if(null!= bo.getAttributeDetails())  {
513                 bo.getAttributeDetails().clear();
514             }
515             bo.setAttributeDetails(newAttrBos);
516         }
517         if (bo.getTemplate() == null && bo.getTemplateId() != null) {
518             bo.setTemplate(PermissionTemplateBo.from(getPermissionTemplate(bo.getTemplateId())));
519         }
520 
521         return PermissionBo.to(businessObjectService.save(bo));		
522 	}
523 	
524     @Override
525     public Permission findPermByNamespaceCodeAndName(String namespaceCode, String permissionName)
526             throws RiceIllegalArgumentException {
527         incomingParamCheck(namespaceCode, "namespaceCode");
528         incomingParamCheck(permissionName, "permissionName");
529 
530         PermissionBo permissionBo = getPermissionBoByName(namespaceCode, permissionName);
531         if (permissionBo != null) {
532             return PermissionBo.to(permissionBo);
533         }
534         return null;
535     }
536     
537     protected PermissionBo getPermissionBoByName(String namespaceCode, String permissionName) {
538         if (StringUtils.isBlank(namespaceCode)
539                 || StringUtils.isBlank(permissionName)) {
540             return null;
541         }
542         Map<String, String> criteria = new HashMap<String, String>();
543         criteria.put(KimConstants.UniqueKeyConstants.NAMESPACE_CODE, namespaceCode);
544         criteria.put(KimConstants.UniqueKeyConstants.NAME, permissionName);
545         criteria.put(KRADPropertyConstants.ACTIVE, "Y");
546         // while this is not actually the primary key - there will be at most one row with these criteria
547         return businessObjectService.findByPrimaryKey(PermissionBo.class, criteria);
548     }
549 
550     @Override
551     public PermissionQueryResults findPermissions(final QueryByCriteria queryByCriteria)
552             throws RiceIllegalArgumentException {
553         incomingParamCheck(queryByCriteria, "queryByCriteria");
554 
555         LookupCustomizer.Builder<PermissionBo> lc = LookupCustomizer.Builder.create();
556         lc.setPredicateTransform(AttributeTransform.getInstance());
557 
558         GenericQueryResults<PermissionBo> results = criteriaLookupService.lookup(PermissionBo.class, queryByCriteria, lc.build());
559 
560         PermissionQueryResults.Builder builder = PermissionQueryResults.Builder.create();
561         builder.setMoreResultsAvailable(results.isMoreResultsAvailable());
562         builder.setTotalRowCount(results.getTotalRowCount());
563 
564         final List<Permission.Builder> ims = new ArrayList<Permission.Builder>();
565         for (PermissionBo bo : results.getResults()) {
566             ims.add(Permission.Builder.create(bo));
567         }
568 
569         builder.setResults(ims);
570         return builder.build();
571     }
572 
573     @Override
574     public TemplateQueryResults findPermissionTemplates(final QueryByCriteria queryByCriteria)
575             throws RiceIllegalArgumentException {
576         incomingParamCheck(queryByCriteria, "queryByCriteria");
577 
578         GenericQueryResults<PermissionTemplateBo> results = criteriaLookupService.lookup(PermissionTemplateBo.class, queryByCriteria);
579 
580         TemplateQueryResults.Builder builder = TemplateQueryResults.Builder.create();
581         builder.setMoreResultsAvailable(results.isMoreResultsAvailable());
582         builder.setTotalRowCount(results.getTotalRowCount());
583 
584         final List<Template.Builder> ims = new ArrayList<Template.Builder>();
585         for (PermissionTemplateBo bo : results.getResults()) {
586             ims.add(Template.Builder.create(bo));
587         }
588 
589         builder.setResults(ims);
590         return builder.build();
591     }
592 
593     private List<String> getRoleIdsForPermissions( Collection<Permission> permissions ) {
594         if (CollectionUtils.isEmpty(permissions)) {
595             return Collections.emptyList();
596         }
597         List<String> ids = new ArrayList<String>();
598         for (Permission p : permissions) {
599             ids.add(p.getId());
600         }
601 
602         QueryByCriteria query = QueryByCriteria.Builder.fromPredicates(equal("active", "true"), in("permissionId", ids.toArray(new String[]{})));
603 
604         GenericQueryResults<RolePermissionBo> results = criteriaLookupService.lookup(RolePermissionBo.class, query);
605         List<String> roleIds = new ArrayList<String>();
606         for (RolePermissionBo bo : results.getResults()) {
607             roleIds.add(bo.getRoleId());
608         }
609         return Collections.unmodifiableList(roleIds);
610     }
611 	
612 	/**
613      * Sets the kimTypeInfoService attribute value.
614      *
615      * @param kimTypeInfoService The kimTypeInfoService to set.
616      */
617 	public void setKimTypeInfoService(KimTypeInfoService kimTypeInfoService) {
618 		this.kimTypeInfoService = kimTypeInfoService;
619 	}
620 	
621 	/**
622      * Sets the defaultPermissionTypeService attribute value.
623      *
624      * @param defaultPermissionTypeService The defaultPermissionTypeService to set.
625      */
626 	public void setDefaultPermissionTypeService(PermissionTypeService defaultPermissionTypeService) {
627     	this.defaultPermissionTypeService = defaultPermissionTypeService;
628 	}
629 	
630 	/**
631      * Sets the roleService attribute value.
632      *
633      * @param roleService The roleService to set.
634      */
635 	public void setRoleService(RoleService roleService) {
636 		this.roleService = roleService;
637 	}
638 
639     /**
640      * Sets the businessObjectService attribute value.
641      *
642      * @param businessObjectService The businessObjectService to set.
643      */
644     public void setBusinessObjectService(final BusinessObjectService businessObjectService) {
645         this.businessObjectService = businessObjectService;
646     }
647 
648     /**
649      * Sets the criteriaLookupService attribute value.
650      *
651      * @param criteriaLookupService The criteriaLookupService to set.
652      */
653     public void setCriteriaLookupService(final CriteriaLookupService criteriaLookupService) {
654         this.criteriaLookupService = criteriaLookupService;
655     }
656 
657     private void incomingParamCheck(Object object, String name) {
658         if (object == null) {
659             throw new RiceIllegalArgumentException(name + " was null");
660         } else if (object instanceof String
661                 && StringUtils.isBlank((String) object)) {
662             throw new RiceIllegalArgumentException(name + " was blank");
663         }
664     }
665 }