001package org.apache.archiva.redback.rbac.jpa; 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.AbstractRBACManager; 023import org.apache.archiva.redback.rbac.Operation; 024import org.apache.archiva.redback.rbac.Permission; 025import org.apache.archiva.redback.rbac.RBACObjectAssertions; 026import org.apache.archiva.redback.rbac.RbacManagerException; 027import org.apache.archiva.redback.rbac.RbacObjectInvalidException; 028import org.apache.archiva.redback.rbac.RbacObjectNotFoundException; 029import org.apache.archiva.redback.rbac.RbacPermanentException; 030import org.apache.archiva.redback.rbac.Resource; 031import org.apache.archiva.redback.rbac.Role; 032import org.apache.archiva.redback.rbac.UserAssignment; 033import org.apache.archiva.redback.rbac.jpa.model.JpaOperation; 034import org.apache.archiva.redback.rbac.jpa.model.JpaPermission; 035import org.apache.archiva.redback.rbac.jpa.model.JpaResource; 036import org.apache.archiva.redback.rbac.jpa.model.JpaRole; 037import org.apache.archiva.redback.rbac.jpa.model.JpaUserAssignment; 038import org.apache.archiva.redback.rbac.jpa.model.RoleId; 039import org.springframework.stereotype.Service; 040 041import javax.persistence.EntityManager; 042import javax.persistence.NoResultException; 043import javax.persistence.PersistenceContext; 044import javax.persistence.Query; 045import javax.persistence.TypedQuery; 046import javax.transaction.Transactional; 047import java.util.ArrayList; 048import java.util.Collection; 049import java.util.List; 050import java.util.Map; 051import java.util.concurrent.atomic.AtomicBoolean; 052 053/** 054 * Created by martin on 20.09.16. 055 */ 056@Service("rbacManager#jpa") 057public class JpaRbacManager extends AbstractRBACManager { 058 059 060 @PersistenceContext(unitName = "redback-jpa") 061 EntityManager em; 062 063 064 private AtomicBoolean initialized = new AtomicBoolean(false); 065 066 067 public void setEntityManager(EntityManager em) { 068 this.em = em; 069 } 070 071 072 @Override 073 public Role createRole( String id, String name ) 074 { 075 JpaRole role = new JpaRole( ); 076 role.setId( id ); 077 role.setName( name ); 078 return role; 079 } 080 081 @Override 082 public boolean roleExistsById( String id ) throws RbacManagerException 083 { 084 final EntityManager em = getEm(); 085 TypedQuery<Long> q = em.createQuery("SELECT COUNT(r) FROM JpaRole r WHERE r.id = :roleid", Long.class); 086 q.setParameter("roleid",id); 087 Long num; 088 try { 089 num = q.getSingleResult(); 090 } catch (NoResultException ex) { 091 return false; 092 } 093 return num>0; 094 } 095 096 @Override 097 public boolean roleExists( String name ) throws RbacManagerException 098 { 099 final EntityManager em = getEm(); 100 TypedQuery<Long> q = em.createQuery("SELECT COUNT(r) FROM JpaRole r WHERE r.name = :rolename", Long.class); 101 q.setParameter("rolename",name); 102 Long num; 103 try { 104 num = q.getSingleResult(); 105 } catch (NoResultException ex) { 106 return false; 107 } 108 return num>0; 109 } 110 111 @Override 112 public boolean roleExists( Role role ) throws RbacManagerException 113 { 114 return this.roleExistsById( role.getId() ); 115 } 116 117 @Transactional 118 @Override 119 public Role saveRole(Role role) throws RbacObjectInvalidException, RbacManagerException { 120 RBACObjectAssertions.assertValid( role ); 121 final EntityManager em = getEm(); 122 Role mergedRole = em.merge(role); 123 fireRbacRoleSaved(mergedRole); 124 for (Permission perm : mergedRole.getPermissions()) { 125 fireRbacPermissionSaved(perm); 126 } 127 return mergedRole; 128 } 129 130 @Transactional 131 @Override 132 public Map<String, List<? extends Permission>> getAssignedPermissionMap(String principal) throws RbacManagerException { 133 return super.getAssignedPermissionMap(principal); 134 } 135 136 @Transactional 137 @Override 138 public Map<String, ? extends Role> getChildRoleNames( Role role) throws RbacManagerException { 139 return super.getChildRoleNames(role); 140 } 141 142 @Transactional 143 @Override 144 public Map<String, ? extends Role> getChildRoleIds( Role role ) throws RbacManagerException 145 { 146 return super.getChildRoleIds( role ); 147 } 148 149 @Transactional 150 @Override 151 public void addChildRole(Role role, Role childRole) throws RbacObjectInvalidException, RbacManagerException { 152 super.addChildRole(role, childRole); 153 } 154 155 @Transactional 156 @Override 157 public void saveRoles(Collection<Role> roles) throws RbacObjectInvalidException, RbacManagerException { 158 if ( roles == null ) 159 { 160 // Nothing to do. 161 return; 162 } 163 164 final EntityManager em = getEm(); 165 List<Role> merged = new ArrayList<>( ); 166 for (Role role : roles ) { 167 RBACObjectAssertions.assertValid(role); 168 merged.add(em.merge(role)); 169 } 170 for (Role role : merged) { 171 fireRbacRoleSaved(role); 172 } 173 } 174 175 176 @Override 177 public Role getRole(String roleName) throws RbacObjectNotFoundException, RbacManagerException { 178 final EntityManager em = getEm(); 179 TypedQuery<JpaRole> q = em.createQuery("SELECT r FROM JpaRole r WHERE r.name = :rolename", JpaRole.class); 180 q.setParameter("rolename",roleName); 181 Role role; 182 try { 183 role = q.getSingleResult(); 184 } catch (NoResultException ex) { 185 log.warn("Role {} not found", roleName); 186 throw new RbacObjectNotFoundException("Role not found "+roleName); 187 } 188 return role; 189 } 190 191 @Override 192 public Role getRoleById( String id ) throws RbacObjectNotFoundException, RbacManagerException 193 { 194 final EntityManager em = getEm(); 195 TypedQuery<JpaRole> q = em.createQuery("SELECT r FROM JpaRole r WHERE r.id = :roleid", JpaRole.class); 196 q.setParameter("roleid",id); 197 Role role; 198 try { 199 role = q.getSingleResult(); 200 } catch (NoResultException ex) { 201 log.warn("Role {} not found", id); 202 throw new RbacObjectNotFoundException("Role not found "+id); 203 } 204 return role; 205 } 206 207 @Override 208 public List<? extends Role> getAllRoles() throws RbacManagerException { 209 final EntityManager em = getEm(); 210 TypedQuery<JpaRole> q = em.createQuery("SELECT r FROM JpaRole r", JpaRole.class); 211 return q.getResultList(); 212 } 213 214 @Transactional 215 @Override 216 public void removeRole(Role role) throws RbacObjectNotFoundException, RbacObjectInvalidException, RbacManagerException { 217 RBACObjectAssertions.assertValid(role); 218 if (!(role instanceof JpaRole)) { 219 throw new RbacObjectInvalidException("Role object is not instance of JpaRole"); 220 } 221 if ( role.isPermanent() ) 222 { 223 throw new RbacPermanentException( "Unable to delete permanent role [" + role.getName() + "]" ); 224 } 225 final EntityManager em = getEm(); 226 JpaRole myRole = em.find(JpaRole.class, new RoleId( role.getId(), role.getName())); 227 if (myRole == null) { 228 throw new RbacObjectNotFoundException("Role not found "+role.getName()); 229 } 230 myRole.setPermissions( new ArrayList<>( )); 231 em.remove(myRole); 232 fireRbacRoleRemoved(myRole); 233 } 234 235 @Override 236 public Permission createPermission(String name) throws RbacManagerException { 237 JpaPermission permission = new JpaPermission(); 238 permission.setName(name); 239 return permission; 240 } 241 242 @Override 243 public Permission createPermission(String name, String operationName, String resourceIdentifier) throws RbacManagerException { 244 JpaPermission permission = new JpaPermission(); 245 permission.setName(name); 246 Operation op; 247 try { 248 op = getOperation(operationName); 249 } catch (RbacObjectNotFoundException ex) { 250 op = createOperation(operationName); 251 } 252 permission.setOperation(op); 253 Resource res; 254 try { 255 res = getResource(resourceIdentifier); 256 } catch (RbacObjectNotFoundException ex) { 257 res = createResource(resourceIdentifier); 258 } 259 permission.setResource(res); 260 return permission; 261 } 262 263 @Transactional 264 @Override 265 public Permission savePermission(Permission permission) throws RbacObjectInvalidException, RbacManagerException { 266 RBACObjectAssertions.assertValid(permission); 267 if (!(permission instanceof JpaPermission)) { 268 throw new RbacObjectInvalidException("The permission object ist not instance of JpaPermission"); 269 } 270 final EntityManager em = getEm(); 271 Permission savedPermission = em.merge(permission); 272 fireRbacPermissionSaved(savedPermission); 273 return savedPermission; 274 } 275 276 @Override 277 public Permission getPermission(String permissionName) throws RbacObjectNotFoundException, RbacManagerException { 278 final EntityManager em = getEm(); 279 TypedQuery<Permission> q = em.createQuery("SELECT p FROM JpaPermission p WHERE p.name=:name", Permission.class); 280 q.setParameter("name",permissionName); 281 Permission res = q.getSingleResult(); 282 if (res==null) { 283 throw new RbacObjectNotFoundException("Permission "+permissionName+" not found"); 284 } 285 return res; 286 } 287 288 @Override 289 public List<? extends Permission> getAllPermissions() throws RbacManagerException { 290 final EntityManager em = getEm(); 291 TypedQuery<JpaPermission> q = em.createQuery("SELECT p FROM JpaPermission p",JpaPermission.class); 292 return q.getResultList(); 293 } 294 295 @Transactional 296 @Override 297 public void removePermission(Permission permission) throws RbacObjectNotFoundException, RbacObjectInvalidException, RbacManagerException { 298 RBACObjectAssertions.assertValid(permission); 299 if (!(permission instanceof JpaPermission)) { 300 throw new RbacObjectInvalidException("The permission object is not JpaPermission object"); 301 } 302 if ( permission.isPermanent() ) 303 { 304 throw new RbacPermanentException( "Unable to delete permanent permission [" + permission.getName() + "]" ); 305 } 306 final EntityManager em = getEm(); 307 JpaPermission p = em.find(JpaPermission.class, permission.getName()); 308 if (p == null) { 309 throw new RbacObjectNotFoundException("Permission " + permission.getName() + " not found"); 310 } 311 em.remove(p); 312 fireRbacPermissionRemoved(p); 313 } 314 315 @Override 316 public Operation createOperation(String name) throws RbacManagerException { 317 JpaOperation op = new JpaOperation(); 318 op.setName(name); 319 return op; 320 } 321 322 @Transactional 323 @Override 324 public Operation saveOperation(Operation operation) throws RbacObjectInvalidException, RbacManagerException { 325 RBACObjectAssertions.assertValid(operation); 326 if (!(operation instanceof JpaOperation)) { 327 throw new RbacObjectInvalidException("Operation is not JpaOperation object"); 328 } 329 final EntityManager em = getEm(); 330 return em.merge(operation); 331 } 332 333 @Override 334 public Operation getOperation(String operationName) throws RbacObjectNotFoundException, RbacManagerException { 335 final EntityManager em = getEm(); 336 Operation op = em.find(JpaOperation.class,operationName); 337 if(op==null) { 338 throw new RbacObjectNotFoundException("Operation "+operationName+" not found"); 339 } 340 return op; 341 } 342 343 @Override 344 public List<? extends Operation> getAllOperations() throws RbacManagerException { 345 final EntityManager em = getEm(); 346 TypedQuery<JpaOperation> q = em.createQuery("SELECT o FROM JpaOperation o", JpaOperation.class); 347 return q.getResultList(); 348 } 349 350 @Transactional 351 @Override 352 public void removeOperation(Operation operation) throws RbacObjectNotFoundException, RbacObjectInvalidException, RbacManagerException { 353 RBACObjectAssertions.assertValid(operation); 354 if (!(operation instanceof JpaOperation)) { 355 throw new RbacObjectInvalidException("Operation is not JpaOperation object"); 356 } 357 if ( operation.isPermanent() ) 358 { 359 throw new RbacPermanentException( "Unable to delete permanent operation [" + operation.getName() + "]" ); 360 } 361 final EntityManager em = getEm(); 362 JpaOperation op = em.find(JpaOperation.class, operation.getName()); 363 if (op==null) { 364 throw new RbacObjectNotFoundException("Operation not found "+operation.getName()); 365 } 366 em.remove(op); 367 } 368 369 @Override 370 public Resource createResource(String identifier) throws RbacManagerException { 371 JpaResource resource = new JpaResource(); 372 resource.setIdentifier(identifier); 373 return resource; 374 } 375 376 @Transactional 377 @Override 378 public Resource saveResource(Resource resource) throws RbacObjectInvalidException, RbacManagerException { 379 RBACObjectAssertions.assertValid(resource); 380 if (!(resource instanceof JpaResource)) { 381 throw new RbacObjectInvalidException("Resource is not JpaResource"); 382 } 383 final EntityManager em = getEm(); 384 return em.merge(resource); 385 } 386 387 // Overriding to add the transactional attribute here 388 @Transactional 389 @Override 390 public Resource getGlobalResource() 391 throws RbacManagerException 392 { 393 return super.getGlobalResource(); 394 } 395 396 @Override 397 public Resource getResource(String resourceIdentifier) throws RbacObjectNotFoundException, RbacManagerException { 398 final EntityManager em = getEm(); 399 Resource r = em.find(JpaResource.class,resourceIdentifier); 400 if (r==null) { 401 throw new RbacObjectNotFoundException("Resource "+resourceIdentifier+" not found"); 402 } 403 return r; 404 } 405 406 @Override 407 public List<? extends Resource> getAllResources() throws RbacManagerException { 408 final EntityManager em = getEm(); 409 TypedQuery<JpaResource> q = em.createQuery("SELECT r FROM JpaResource r",JpaResource.class); 410 return q.getResultList(); 411 } 412 413 @Transactional 414 @Override 415 public void removeResource(Resource resource) throws RbacObjectNotFoundException, RbacObjectInvalidException, RbacManagerException { 416 RBACObjectAssertions.assertValid(resource); 417 if (!(resource instanceof JpaResource)) { 418 throw new RbacObjectInvalidException("Resource is not JpaResource"); 419 } 420 if (resource.isPermanent()) { 421 throw new RbacObjectInvalidException("Unable to delete permanent resource ["+resource.getIdentifier()+ "]"); 422 } 423 final EntityManager em = getEm(); 424 JpaResource res = em.find(JpaResource.class, resource.getIdentifier()); 425 if (res==null) { 426 throw new RbacObjectNotFoundException("Resource "+resource.getIdentifier()+" not found"); 427 } 428 em.remove(res); 429 } 430 431 @Override 432 public UserAssignment createUserAssignment(String principal) throws RbacManagerException { 433 JpaUserAssignment ua = new JpaUserAssignment(); 434 ua.setPrincipal(principal); 435 return ua; 436 } 437 438 @Transactional 439 @Override 440 public UserAssignment saveUserAssignment(UserAssignment userAssignment) throws RbacObjectInvalidException, RbacManagerException { 441 RBACObjectAssertions.assertValid(userAssignment); 442 if (!(userAssignment instanceof JpaUserAssignment)) { 443 throw new RbacObjectInvalidException("Cannto save object that is not JpaUserAssignment"); 444 } 445 final EntityManager em = getEm(); 446 UserAssignment savedAssignment = em.merge(userAssignment); 447 fireRbacUserAssignmentSaved(savedAssignment); 448 return savedAssignment; 449 } 450 451 @Override 452 public UserAssignment getUserAssignment(String principal) throws RbacObjectNotFoundException, RbacManagerException { 453 final EntityManager em = getEm(); 454 UserAssignment ua = em.find(JpaUserAssignment.class, principal); 455 if (ua==null) { 456 throw new RbacObjectNotFoundException("User assignment not found "+principal); 457 } 458 return ua; 459 } 460 461 @Override 462 public List<? extends UserAssignment> getAllUserAssignments() throws RbacManagerException { 463 final EntityManager em = getEm(); 464 TypedQuery<JpaUserAssignment> q = em.createQuery("SELECT ua FROM JpaUserAssignment ua", JpaUserAssignment.class); 465 return q.getResultList(); 466 } 467 468 @Override 469 public List<? extends UserAssignment> getUserAssignmentsForRoles(Collection<String> roleIds ) throws RbacManagerException { 470 try { 471 final EntityManager em = getEm(); 472 TypedQuery<JpaUserAssignment> q = em.createQuery("SELECT ua FROM JpaUserAssignment ua WHERE ua.roleIds IN :roles", JpaUserAssignment.class); 473 q.setParameter("roles", roleIds ); 474 return q.getResultList(); 475 } catch (Exception ex) { 476 log.error("Query failed: {}",ex.getMessage(),ex); 477 if (log.isDebugEnabled()) { 478 ex.printStackTrace(); 479 } 480 throw new RbacManagerException(ex.getMessage(),ex); 481 } 482 } 483 484 @Transactional 485 @Override 486 public void removeUserAssignment(UserAssignment userAssignment) throws RbacObjectNotFoundException, RbacObjectInvalidException, RbacManagerException { 487 RBACObjectAssertions.assertValid(userAssignment); 488 if (userAssignment.isPermanent()) { 489 throw new RbacObjectInvalidException("Cannot remove permanent object "+userAssignment.getPrincipal()); 490 } 491 final EntityManager em = getEm(); 492 JpaUserAssignment ua = em.find(JpaUserAssignment.class, userAssignment.getPrincipal()); 493 if (ua==null) { 494 throw new RbacObjectNotFoundException("User assignment not found "+userAssignment.getPrincipal()); 495 } 496 em.remove(ua); 497 fireRbacUserAssignmentRemoved(userAssignment); 498 } 499 500 @Transactional 501 @Override 502 public void eraseDatabase() { 503 final EntityManager em = getEm(); 504 // Deletion is a bit tricky, because the JPA bulk delete queries do not cascade 505 // or keep foreign keys into account. 506 TypedQuery<JpaPermission> tqp = em.createQuery("SELECT r FROM JpaPermission r",JpaPermission.class); 507 for(JpaPermission p : tqp.getResultList()) { 508 p.setOperation(null); 509 p.setResource(null); 510 } 511 TypedQuery<JpaRole> tqr = em.createQuery("SELECT r FROM JpaRole r",JpaRole.class); 512 for (JpaRole r : tqr.getResultList()) { 513 r.getPermissions().clear(); 514 } 515 em.flush(); 516 TypedQuery<JpaOperation> tqo = em.createQuery("SELECT o FROM JpaOperation o",JpaOperation.class); 517 for(JpaOperation o : tqo.getResultList()) { 518 em.remove(o); 519 } 520 TypedQuery<JpaResource> tqre = em.createQuery("SELECT re FROM JpaResource re",JpaResource.class); 521 for(JpaResource re : tqre.getResultList()) { 522 em.remove(re); 523 } 524 for (JpaPermission p : tqp.getResultList()) { 525 em.remove(p); 526 } 527 for (JpaRole r : tqr.getResultList()) { 528 em.remove(r); 529 } 530 TypedQuery<JpaUserAssignment> tqu = em.createQuery("SELECT ua FROM JpaUserAssignment ua", JpaUserAssignment.class); 531 for(JpaUserAssignment ua : tqu.getResultList()) { 532 em.remove(ua); 533 } 534 em.flush(); 535 em.clear(); 536 537 } 538 539 @Override 540 public String getDescriptionKey() { 541 return "archiva.redback.rbacmanager.jpa"; 542 } 543 544 @Override 545 public boolean isReadOnly() { 546 return false; 547 } 548 549 private EntityManager getEm() { 550 if (initialized.compareAndSet(false, true)) { 551 Query q = em.createQuery("SELECT COUNT(r.name) FROM JpaRole r"); 552 boolean dbInit = q.getFirstResult()==0; 553 fireRbacInit(dbInit); 554 } 555 return em; 556 } 557 558 @Override 559 public boolean isFinalImplementation() { 560 return true; 561 } 562}