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