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}