001package org.apache.archiva.redback.role.template; 002 003/* 004 * Licensed to the Apache Software Foundation (ASF) under one 005 * or more contributor license agreements. See the NOTICE file 006 * distributed with this work for additional information 007 * regarding copyright ownership. The ASF licenses this file 008 * to you under the Apache License, Version 2.0 (the 009 * "License"); you may not use this file except in compliance 010 * with the License. You may obtain a copy of the License at 011 * 012 * http://www.apache.org/licenses/LICENSE-2.0 013 * 014 * Unless required by applicable law or agreed to in writing, 015 * software distributed under the License is distributed on an 016 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 017 * KIND, either express or implied. See the License for the 018 * specific language governing permissions and limitations 019 * under the License. 020 */ 021 022import org.apache.archiva.redback.rbac.Operation; 023import org.apache.archiva.redback.rbac.Permission; 024import org.apache.archiva.redback.rbac.RbacManagerException; 025import org.apache.archiva.redback.rbac.Resource; 026import org.apache.archiva.redback.rbac.Role; 027import org.apache.archiva.redback.rbac.RBACManager; 028import org.apache.archiva.redback.role.PermanentRoleDeletionInvalid; 029import org.apache.archiva.redback.role.RoleExistsException; 030import org.apache.archiva.redback.role.RoleManagerException; 031import org.apache.archiva.redback.role.RoleNotFoundException; 032import org.apache.archiva.redback.role.model.ModelApplication; 033import org.apache.archiva.redback.role.model.ModelOperation; 034import org.apache.archiva.redback.role.model.ModelPermission; 035import org.apache.archiva.redback.role.model.ModelResource; 036import org.apache.archiva.redback.role.model.ModelRole; 037import org.apache.archiva.redback.role.model.ModelTemplate; 038import org.apache.archiva.redback.role.model.RedbackRoleModel; 039import org.apache.archiva.redback.role.util.RoleModelUtils; 040import org.slf4j.Logger; 041import org.slf4j.LoggerFactory; 042import org.springframework.stereotype.Service; 043 044import javax.inject.Inject; 045import javax.inject.Named; 046import java.util.ArrayList; 047import java.util.Collections; 048import java.util.Iterator; 049import java.util.List; 050 051/** 052 * DefaultRoleTemplateProcessor: inserts the components of a template into the rbac manager 053 * 054 * @author: Jesse McConnell 055 */ 056@Service("roleTemplateProcessor") 057public class DefaultRoleTemplateProcessor 058 implements RoleTemplateProcessor 059{ 060 private Logger log = LoggerFactory.getLogger( DefaultRoleTemplateProcessor.class ); 061 062 @Inject 063 @Named(value = "rbacManager#default") 064 private RBACManager rbacManager; 065 066 @Override 067 @SuppressWarnings("unchecked") 068 public String create( final RedbackRoleModel model, final String templateId, final String resource ) 069 throws RoleManagerException 070 { 071 for ( ModelApplication application : model.getApplications() ) 072 { 073 for ( ModelTemplate template : application.getTemplates() ) 074 { 075 if ( templateId.equals( template.getId() ) ) 076 { 077 // resource can be special 078 processResource( template, resource ); 079 080 // templates are roles that have yet to be paired with a resource for creation 081 return processTemplate( model, template, resource ); 082 083 } 084 } 085 } 086 087 throw new RoleNotFoundException( "unknown template '" + templateId + "'" ); 088 } 089 090 @Override 091 @SuppressWarnings("unchecked") 092 public void remove( RedbackRoleModel model, String templateId, String resource ) 093 throws RoleManagerException 094 { 095 for ( ModelApplication application : model.getApplications() ) 096 { 097 for ( ModelTemplate template : application.getTemplates() ) 098 { 099 if ( templateId.equals( template.getId() ) ) 100 { 101 removeTemplatedRole( model, template, resource ); 102 return; 103 } 104 } 105 } 106 107 throw new RoleManagerException( "unknown template '" + templateId + "'" ); 108 } 109 110 private void removeTemplatedRole( RedbackRoleModel model, ModelTemplate template, String resource ) 111 throws RoleManagerException 112 { 113 String roleId = getRoleId( template.getId( ), resource ); 114 115 try 116 { 117 Role role = rbacManager.getRoleById( roleId ); 118 119 if ( !role.isPermanent() ) 120 { 121 // remove the role 122 rbacManager.removeRole( role ); 123 124 // remove the permissions 125 // todo, do this in a better way too, permissions can be shared across multiple roles and that could blow chunks here. 126 //for ( Iterator i = template.getPermissions().iterator(); i.hasNext(); ) 127 //{ 128 // ModelPermission permission = (ModelPermission) i.next(); 129 // if ( !permission.isPermanent() ) 130 // { 131 // rbacManager.removePermission( permission.getName() + template.getDelimiter() 132 // + resolvePermissionResource( model, permission, resolvePermissionResource( model, permission, resource ) ) ); 133 // } 134 //} 135 136 // check if we want to remove the resources 137 Resource rbacResource = rbacManager.getResource( resource ); 138 139 //if ( !rbacResource.isPermanent() ) 140 //{ 141 //todo we need a better way of finding if a resource is unused anymore...probably a cleaning process in the db or something 142 //rbacManager.removeResource( rbacResource ); 143 //} 144 145 // todo find dangling child role references and smoke 146 } 147 else 148 { 149 throw new PermanentRoleDeletionInvalid( "Unable to remove role, it is flagged permanent: "+roleId ); 150 } 151 } 152 catch ( RbacManagerException e ) 153 { 154 throw new RoleManagerException( "Unable to remove templated role: " + roleId, e ); 155 } 156 //catch ( RoleTemplateProcessorException e ) 157 //{ 158 // throw new RoleManagerException( "unable to remove templated role, error resolving resource: Role:" + roleName + " Resource: " + resource, e ); 159 //} 160 } 161 162 private void processResource( ModelTemplate template, String resource ) 163 throws RoleManagerException 164 { 165 if ( !rbacManager.resourceExists( resource ) ) 166 { 167 try 168 { 169 Resource res = rbacManager.createResource( resource ); 170 res.setPermanent( template.isPermanentResource() ); 171 rbacManager.saveResource( res ); 172 } 173 catch ( RbacManagerException e ) 174 { 175 throw new RoleManagerException( "error creating resource '" + resource + "'", e ); 176 } 177 } 178 } 179 180 @Override 181 public String getRoleId( String templateId, String resource) { 182 return RoleModelUtils.getRoleId( templateId, resource ); 183 } 184 185 @SuppressWarnings("unchecked") 186 private String processTemplate( RedbackRoleModel model, ModelTemplate template, String resource ) 187 throws RoleManagerException 188 { 189 final String templateName = template.getNamePrefix() + template.getDelimiter() + resource; 190 final String roleId = getRoleId( template.getId( ), resource ); 191 192 List<Permission> permissions = processPermissions( model, template, resource ); 193 194 boolean roleExists = false; 195 196 try 197 { 198 roleExists = rbacManager.roleExists( templateName ); 199 } 200 catch ( RbacManagerException e ) 201 { 202 throw new RoleExistsException( e.getMessage(), e ); 203 } 204 205 if ( !roleExists ) 206 { 207 try 208 { 209 Role role = rbacManager.createRole( templateName ); 210 role.setId( roleId ); 211 role.setModelId( template.getId() ); 212 role.setResource( resource ); 213 role.setTemplateInstance( true ); 214 role.setDescription( template.getDescription() ); 215 role.setPermanent( template.isPermanent() ); 216 role.setAssignable( template.isAssignable() ); 217 218 // add any permissions associated with this role 219 for ( Iterator<Permission> j = permissions.iterator(); j.hasNext(); ) 220 { 221 Permission permission = j.next(); 222 223 role.addPermission( permission ); 224 } 225 226 // add child roles to this role 227 if ( template.getChildRoles() != null ) 228 { 229 for ( String childRoleId : template.getChildRoles() ) 230 { 231 ModelRole childRoleProfile = RoleModelUtils.getModelRole( model, childRoleId ); 232 role.addChildRoleName( childRoleProfile.getName() ); 233 role.addChildRoleId( childRoleProfile.getId() ); 234 } 235 } 236 237 // add child templates to this role, be nice and make them if they don't exist 238 if ( template.getChildTemplates() != null ) 239 { 240 for ( String childTemplateId : template.getChildTemplates() ) 241 { 242 ModelTemplate childModelTemplate = RoleModelUtils.getModelTemplate( model, childTemplateId ); 243 244 if ( childModelTemplate == null ) 245 { 246 throw new RoleManagerException( 247 "error obtaining child template from model: template " + templateName 248 + " # child template: " + childTemplateId ); 249 } 250 251 String childRoleName = 252 childModelTemplate.getNamePrefix() + childModelTemplate.getDelimiter() + resource; 253 254 // check if the role exists, if it does then add it as a child, otherwise make it and add it 255 // this should be safe since validation should protect us from template cycles 256 if ( rbacManager.roleExists( childRoleName ) ) 257 { 258 role.addChildRoleName( childRoleName ); 259 role.addChildRoleId( getRoleId( childTemplateId, resource ) ); 260 } 261 else 262 { 263 processTemplate( model, childModelTemplate, resource ); 264 265 role.addChildRoleName( childRoleName ); 266 role.addChildRoleId( getRoleId( childTemplateId, resource ) ); 267 } 268 } 269 } 270 271 // this role needs to be saved since it now needs to be added as a child role by 272 // another role 273 if ( !rbacManager.roleExists( role.getName() ) ) 274 { 275 role = rbacManager.saveRole( role ); 276 } 277 278 // add link from parent roles to this new role 279 if ( template.getParentRoles() != null ) 280 { 281 for ( String parentRoleId : template.getParentRoles() ) 282 { 283 ModelRole parentModelRole = RoleModelUtils.getModelRole( model, parentRoleId ); 284 Role parentRole = rbacManager.getRole( parentModelRole.getName() ); 285 parentRole.addChildRole( role ); 286 rbacManager.saveRole( parentRole ); 287 } 288 } 289 290 // add child templates to this role, be nice and make them if they don't exist 291 if ( template.getParentTemplates() != null ) 292 { 293 for ( String parentTemplateId : template.getParentTemplates() ) 294 { 295 ModelTemplate parentModelTemplate = RoleModelUtils.getModelTemplate( model, parentTemplateId ); 296 297 if ( parentModelTemplate == null ) 298 { 299 throw new RoleManagerException( 300 "error obtaining parent template from model: template " + templateName 301 + " # child template: " + parentTemplateId ); 302 } 303 304 String parentRoleName = 305 parentModelTemplate.getNamePrefix() + parentModelTemplate.getDelimiter() + resource; 306 307 // check if the role exists, if it does then add it as a child, otherwise make it and add it 308 // this should be safe since validation should protect us from template cycles 309 if ( rbacManager.roleExists( parentRoleName ) ) 310 { 311 Role parentRole = rbacManager.getRole( parentRoleName ); 312 313 parentRole.addChildRole( role ); 314 rbacManager.saveRole( parentRole ); 315 } 316 else 317 { 318 processTemplate( model, parentModelTemplate, resource ); 319 320 Role parentRole = rbacManager.getRole( parentRoleName ); 321 322 parentRole.addChildRole( role ); 323 rbacManager.saveRole( parentRole ); 324 } 325 } 326 } 327 328 } 329 catch ( RbacManagerException e ) 330 { 331 throw new RoleManagerException( "error creating role '" + templateName + "'", e ); 332 } 333 } 334 else 335 { 336 try 337 { 338 Role role = rbacManager.getRole( templateName ); 339 340 boolean changed = false; 341 for ( Permission permission : permissions ) 342 { 343 if ( !role.getPermissions().contains( permission ) ) 344 { 345 log.info( "Adding new permission '{}' to role '{}'", 346 permission.getName(), role.getName() ); 347 role.addPermission( permission ); 348 changed = true; 349 } 350 } 351 352 // Copy list to avoid concurrent modifications 353 List<Permission> oldPermissions = new ArrayList<Permission>( role.getPermissions() ); 354 for ( Permission permission : oldPermissions ) 355 { 356 if ( !permissions.contains( permission ) ) 357 { 358 log.info( "Removing old permission '{}' from role '{}'", permission.getName(), role.getName() ); 359 role.removePermission( permission ); 360 changed = true; 361 } 362 } 363 if ( changed ) 364 { 365 rbacManager.saveRole( role ); 366 } 367 } 368 catch ( RbacManagerException e ) 369 { 370 throw new RoleManagerException( "error updating role '" + templateName + "'", e ); 371 } 372 } 373 return roleId; 374 } 375 376 @SuppressWarnings("unchecked") 377 private List<Permission> processPermissions( RedbackRoleModel model, ModelTemplate template, String resource ) 378 throws RoleManagerException 379 { 380 381 if ( template.getPermissions() != null ) 382 { 383 // copy list to avoid concurrent modifications 384 List<ModelPermission> templatePermissions = new ArrayList<ModelPermission>( template.getPermissions() ); 385 List<Permission> rbacPermissions = new ArrayList<Permission>( templatePermissions.size() ); 386 for ( ModelPermission profilePermission : templatePermissions ) 387 { 388 try 389 { 390 String permissionName = 391 profilePermission.getName() + template.getDelimiter() + resolvePermissionResource( model, 392 profilePermission, 393 resource ); 394 395 if ( !rbacManager.permissionExists( permissionName ) ) 396 { 397 398 Permission permission = rbacManager.createPermission( permissionName ); 399 400 ModelOperation modelOperation = 401 RoleModelUtils.getModelOperation( model, profilePermission.getOperation() ); 402 Operation rbacOperation = rbacManager.getOperation( modelOperation.getName() ); 403 404 String permissionResource = resolvePermissionResource( model, profilePermission, resource ); 405 406 Resource rbacResource = rbacManager.getResource( permissionResource ); 407 408 permission.setOperation( rbacOperation ); 409 permission.setResource( rbacResource ); 410 permission.setPermanent( profilePermission.isPermanent() ); 411 permission.setDescription( profilePermission.getDescription() ); 412 413 permission = rbacManager.savePermission( permission ); 414 415 rbacPermissions.add( permission ); 416 417 } 418 else 419 { 420 421 rbacPermissions.add( rbacManager.getPermission( permissionName ) ); 422 423 } 424 } 425 catch ( RbacManagerException e ) 426 { 427 throw new RoleManagerException( "unable to generate templated role: " + e.getMessage(), e ); 428 } 429 catch ( RoleTemplateProcessorException e ) 430 { 431 throw new RoleManagerException( "unable to resolve resource: " + resource, e ); 432 } 433 } 434 return rbacPermissions; 435 } 436 437 return Collections.emptyList(); 438 } 439 440 private String resolvePermissionResource( RedbackRoleModel model, ModelPermission permission, String resource ) 441 throws RoleTemplateProcessorException 442 { 443 String permissionResource = permission.getResource(); 444 445 // if permission's resource is ${resource}, return the resource passed in 446 if ( permissionResource.startsWith( "${" ) ) 447 { 448 String tempStr = permissionResource.substring( 2, permissionResource.indexOf( '}' ) ); 449 450 if ( "resource".equals( tempStr ) ) 451 { 452 return resource; 453 } 454 } 455 456 // check if the resource resolves to declared operation 457 String declaredResource = resolveResource( model, permission.getResource() ); 458 if ( declaredResource != null ) 459 { 460 return declaredResource; 461 } 462 else 463 { 464 // either niether of the above apply, then its the resource. 465 return resource; 466 } 467 } 468 469 private String resolveResource( RedbackRoleModel model, String resource ) 470 throws RoleTemplateProcessorException 471 { 472 ModelResource resolvedResource = RoleModelUtils.getModelResource( model, resource ); 473 474 if ( resolvedResource != null ) 475 { 476 return resolvedResource.getName(); 477 } 478 else 479 { 480 return null; 481 } 482 } 483 484 public RBACManager getRbacManager() 485 { 486 return rbacManager; 487 } 488 489 public void setRbacManager( RBACManager rbacManager ) 490 { 491 this.rbacManager = rbacManager; 492 } 493}