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}