diff --git a/pom.xml b/pom.xml index fa30ae55..3e218ee0 100644 --- a/pom.xml +++ b/pom.xml @@ -78,6 +78,8 @@ UTF-8 true 3.0.3 + 2.4.1 + + + + redback-common + org.apache.archiva.redback + 2.5-SNAPSHOT + + 4.0.0 + + redback-common-jpa + jar + Redback :: JPA Common Package + + + + org.apache.archiva.redback + redback-rbac-model + + + org.apache.archiva.redback + redback-users-api + + + + org.apache.openjpa + openjpa + ${openjpa.version} + + + org.hsqldb + hsqldb + test + + + + + \ No newline at end of file diff --git a/redback-common/redback-common-jpa/src/main/resources/META-INF/persistence-hsqldb.xml b/redback-common/redback-common-jpa/src/main/resources/META-INF/persistence-hsqldb.xml new file mode 100644 index 00000000..a78444e0 --- /dev/null +++ b/redback-common/redback-common-jpa/src/main/resources/META-INF/persistence-hsqldb.xml @@ -0,0 +1,41 @@ + + + + + org.apache.openjpa.persistence.PersistenceProviderImpl + java:comp/env/jdbc/redbackjpa + org.apache.archiva.redback.users.jpa.model.JpaUser + org.apache.archiva.redback.rbac.jpa.model.JpaOperation + org.apache.archiva.redback.rbac.jpa.model.JpaResource + org.apache.archiva.redback.rbac.jpa.model.JpaPermission + org.apache.archiva.redback.rbac.jpa.model.JpaRole + org.apache.archiva.redback.rbac.jpa.model.JpaUserAssignment + + + + + + + + + + + diff --git a/redback-common/redback-common-jpa/src/main/resources/META-INF/persistence.xml b/redback-common/redback-common-jpa/src/main/resources/META-INF/persistence.xml new file mode 100644 index 00000000..46878e88 --- /dev/null +++ b/redback-common/redback-common-jpa/src/main/resources/META-INF/persistence.xml @@ -0,0 +1,36 @@ + + + + + org.apache.openjpa.persistence.PersistenceProviderImpl + java:comp/env/jdbc/redbackjpa + org.apache.archiva.redback.users.jpa.model.JpaUser + org.apache.archiva.redback.rbac.jpa.model.JpaOperation + org.apache.archiva.redback.rbac.jpa.model.JpaResource + org.apache.archiva.redback.rbac.jpa.model.JpaPermission + org.apache.archiva.redback.rbac.jpa.model.JpaRole + org.apache.archiva.redback.rbac.jpa.model.JpaUserAssignment + + + + + + diff --git a/redback-rbac/redback-rbac-providers/pom.xml b/redback-rbac/redback-rbac-providers/pom.xml index 0edb4970..a94d451d 100644 --- a/redback-rbac/redback-rbac-providers/pom.xml +++ b/redback-rbac/redback-rbac-providers/pom.xml @@ -32,5 +32,6 @@ redback-rbac-memory redback-rbac-cached redback-rbac-ldap + redback-rbac-jpa diff --git a/redback-rbac/redback-rbac-providers/redback-rbac-jpa/pom.xml b/redback-rbac/redback-rbac-providers/redback-rbac-jpa/pom.xml new file mode 100644 index 00000000..77045ab9 --- /dev/null +++ b/redback-rbac/redback-rbac-providers/redback-rbac-jpa/pom.xml @@ -0,0 +1,70 @@ + + + + + redback-rbac-providers + org.apache.archiva.redback + 2.5-SNAPSHOT + + 4.0.0 + + redback-rbac-jpa + jar + Redback :: RBAC Provider :: JPA + + + + org.apache.archiva.redback + redback-rbac-model + + + org.springframework + spring-context-support + + + javax.annotation + jsr250-api + + + org.apache.archiva.redback + redback-rbac-tests + test + + + org.apache.archiva.redback + redback-common-jpa + + + + org.apache.openjpa + openjpa + ${openjpa.version} + + + org.hsqldb + hsqldb + test + + + + + \ No newline at end of file diff --git a/redback-rbac/redback-rbac-providers/redback-rbac-jpa/src/main/java/org/apache/archiva/redback/rbac/jpa/JpaRbacManager.java b/redback-rbac/redback-rbac-providers/redback-rbac-jpa/src/main/java/org/apache/archiva/redback/rbac/jpa/JpaRbacManager.java new file mode 100644 index 00000000..ced9104b --- /dev/null +++ b/redback-rbac/redback-rbac-providers/redback-rbac-jpa/src/main/java/org/apache/archiva/redback/rbac/jpa/JpaRbacManager.java @@ -0,0 +1,452 @@ +package org.apache.archiva.redback.rbac.jpa; + +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import org.apache.archiva.redback.rbac.*; +import org.apache.archiva.redback.rbac.jpa.model.*; +import org.apache.openjpa.persistence.Type; +import org.springframework.stereotype.Service; + +import javax.annotation.PostConstruct; +import javax.inject.Inject; +import javax.persistence.*; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.concurrent.atomic.AtomicBoolean; + +/** + * Created by martin on 20.09.16. + */ +@Service("rbacManager#jpa") +public class JpaRbacManager extends AbstractRBACManager { + + + @PersistenceContext(unitName = "redback-jpa") + EntityManager em; + + + private AtomicBoolean initialized = new AtomicBoolean(false); + + + public void setEntityManager(EntityManager em) { + this.em = em; + } + + + + @Override + public Role createRole(String name) { + JpaRole role = new JpaRole(); + role.setName(name); + return role; + } + + @Override + public Role saveRole(Role role) throws RbacObjectInvalidException, RbacManagerException { + RBACObjectAssertions.assertValid( role ); + final EntityManager em = getEm(); + em.getTransaction().begin(); + em.persist(role); + + em.getTransaction().commit(); + fireRbacRoleSaved(role); + for (Permission perm : role.getPermissions()) { + fireRbacPermissionSaved(perm); + } + return role; + } + + @Override + public void saveRoles(Collection roles) throws RbacObjectInvalidException, RbacManagerException { + if ( roles == null ) + { + // Nothing to do. + return; + } + + final EntityManager em = getEm(); + em.getTransaction().begin(); + for (Role role : roles ) { + RBACObjectAssertions.assertValid(role); + em.persist(role); + } + em.getTransaction().commit(); + for (Role role : roles) { + fireRbacRoleSaved(role); + } + } + + @Override + public Role getRole(String roleName) throws RbacObjectNotFoundException, RbacManagerException { + final EntityManager em = getEm(); + TypedQuery q = em.createQuery("SELECT r FROM JpaRole r WHERE r.name = :rolename", JpaRole.class); + q.setParameter("rolename",roleName); + return q.getSingleResult(); + } + + @Override + public List getAllRoles() throws RbacManagerException { + final EntityManager em = getEm(); + Query q = em.createQuery("SELECT r FROM JpaRole r"); + return q.getResultList(); + } + + @Override + public void removeRole(Role role) throws RbacObjectNotFoundException, RbacObjectInvalidException, RbacManagerException { + RBACObjectAssertions.assertValid(role); + if (!(role instanceof JpaRole)) { + throw new RbacObjectInvalidException("Role object is not instance of JpaRole"); + } + if ( role.isPermanent() ) + { + throw new RbacPermanentException( "Unable to delete permanent role [" + role.getName() + "]" ); + } + final EntityManager em = getEm(); + em.getTransaction().begin(); + JpaRole myRole = em.find(JpaRole.class, role.getName()); + if (myRole == null) { + throw new RbacObjectNotFoundException("Role not found "+role.getName()); + } + myRole.setPermissions(new ArrayList()); + em.remove(myRole); + em.getTransaction().commit(); + fireRbacRoleRemoved(myRole); + } + + @Override + public Permission createPermission(String name) throws RbacManagerException { + JpaPermission permission = new JpaPermission(); + permission.setName(name); + return permission; + } + + @Override + public Permission createPermission(String name, String operationName, String resourceIdentifier) throws RbacManagerException { + JpaPermission permission = new JpaPermission(); + permission.setName(name); + Operation op; + try { + op = getOperation(operationName); + } catch (RbacObjectNotFoundException ex) { + op = createOperation(operationName); + } + permission.setOperation(op); + Resource res; + try { + res = getResource(resourceIdentifier); + } catch (RbacObjectNotFoundException ex) { + res = createResource(resourceIdentifier); + } + permission.setResource(res); + return permission; + } + + @Override + public Permission savePermission(Permission permission) throws RbacObjectInvalidException, RbacManagerException { + RBACObjectAssertions.assertValid(permission); + if (!(permission instanceof JpaPermission)) { + throw new RbacObjectInvalidException("The permission object ist not instance of JpaPermission"); + } + final EntityManager em = getEm(); + em.getTransaction().begin(); + em.persist(permission); + em.getTransaction().commit(); + fireRbacPermissionSaved(permission); + return permission; + } + + @Override + public Permission getPermission(String permissionName) throws RbacObjectNotFoundException, RbacManagerException { + final EntityManager em = getEm(); + TypedQuery q = em.createQuery("SELECT p FROM JpaPermission p WHERE p.name=:name", Permission.class); + q.setParameter("name",permissionName); + Permission res = q.getSingleResult(); + if (res==null) { + throw new RbacObjectNotFoundException("Permission "+permissionName+" not found"); + } + return res; + } + + @Override + public List getAllPermissions() throws RbacManagerException { + final EntityManager em = getEm(); + TypedQuery q = em.createQuery("SELECT p FROM JpaPermission p",JpaPermission.class); + return (List)(List)q.getResultList(); + } + + @Override + public void removePermission(Permission permission) throws RbacObjectNotFoundException, RbacObjectInvalidException, RbacManagerException { + RBACObjectAssertions.assertValid(permission); + if (!(permission instanceof JpaPermission)) { + throw new RbacObjectInvalidException("The permission object is not JpaPermission object"); + } + if ( permission.isPermanent() ) + { + throw new RbacPermanentException( "Unable to delete permanent permission [" + permission.getName() + "]" ); + } + final EntityManager em = getEm(); + em.getTransaction().begin(); + Permission p = em.find(JpaPermission.class, permission.getName()); + if (p == null) { + throw new RbacObjectNotFoundException("Permission " + permission.getName() + " not found"); + } + em.remove(p); + em.getTransaction().commit(); + fireRbacPermissionRemoved(p); + } + + @Override + public Operation createOperation(String name) throws RbacManagerException { + JpaOperation op = new JpaOperation(); + op.setName(name); + return op; + } + + @Override + public Operation saveOperation(Operation operation) throws RbacObjectInvalidException, RbacManagerException { + RBACObjectAssertions.assertValid(operation); + if (!(operation instanceof JpaOperation)) { + throw new RbacObjectInvalidException("Operation is not JpaOperation object"); + } + final EntityManager em = getEm(); + em.getTransaction().begin(); + em.persist(operation); + em.getTransaction().commit(); + return operation; + } + + @Override + public Operation getOperation(String operationName) throws RbacObjectNotFoundException, RbacManagerException { + final EntityManager em = getEm(); + Operation op = em.find(JpaOperation.class,operationName); + if(op==null) { + throw new RbacObjectNotFoundException("Operation "+operationName+" not found"); + } + return op; + } + + @Override + public List getAllOperations() throws RbacManagerException { + final EntityManager em = getEm(); + Query q = em.createQuery("SELECT o FROM JpaOperation o"); + return q.getResultList(); + } + + @Override + public void removeOperation(Operation operation) throws RbacObjectNotFoundException, RbacObjectInvalidException, RbacManagerException { + RBACObjectAssertions.assertValid(operation); + if (!(operation instanceof JpaOperation)) { + throw new RbacObjectInvalidException("Operation is not JpaOperation object"); + } + if ( operation.isPermanent() ) + { + throw new RbacPermanentException( "Unable to delete permanent operation [" + operation.getName() + "]" ); + } + final EntityManager em = getEm(); + em.getTransaction().begin(); + Operation op = em.find(JpaOperation.class, operation.getName()); + if (op==null) { + throw new RbacObjectNotFoundException("Operation not found "+operation.getName()); + } + em.remove(op); + em.getTransaction().commit(); + + } + + @Override + public Resource createResource(String identifier) throws RbacManagerException { + JpaResource resource = new JpaResource(); + resource.setIdentifier(identifier); + return resource; + } + + @Override + public Resource saveResource(Resource resource) throws RbacObjectInvalidException, RbacManagerException { + RBACObjectAssertions.assertValid(resource); + if (!(resource instanceof JpaResource)) { + throw new RbacObjectInvalidException("Resource is not JpaResource"); + } + final EntityManager em = getEm(); + em.getTransaction().begin(); + em.persist(resource); + em.getTransaction().commit(); + return resource; + } + + @Override + public Resource getResource(String resourceIdentifier) throws RbacObjectNotFoundException, RbacManagerException { + final EntityManager em = getEm(); + Resource r = em.find(JpaResource.class,resourceIdentifier); + if (r==null) { + throw new RbacObjectNotFoundException("Resource "+resourceIdentifier+" not found"); + } + return r; + } + + @Override + public List getAllResources() throws RbacManagerException { + final EntityManager em = getEm(); + TypedQuery q = em.createQuery("SELECT r FROM JpaResource r",JpaResource.class); + return (List)(List)q.getResultList(); + } + + @Override + public void removeResource(Resource resource) throws RbacObjectNotFoundException, RbacObjectInvalidException, RbacManagerException { + RBACObjectAssertions.assertValid(resource); + if (!(resource instanceof JpaResource)) { + throw new RbacObjectInvalidException("Resource is not JpaResource"); + } + if (resource.isPermanent()) { + throw new RbacObjectInvalidException("Unable to delete permanent resource ["+resource.getIdentifier()+ "]"); + } + final EntityManager em = getEm(); + em.getTransaction().begin(); + Resource res = em.find(JpaResource.class, resource.getIdentifier()); + if (res==null) { + throw new RbacObjectNotFoundException("Resource "+resource.getIdentifier()+" not found"); + } + em.remove(res); + em.getTransaction().commit(); + } + + @Override + public UserAssignment createUserAssignment(String principal) throws RbacManagerException { + JpaUserAssignment ua = new JpaUserAssignment(); + ua.setPrincipal(principal); + return ua; + } + + @Override + public UserAssignment saveUserAssignment(UserAssignment userAssignment) throws RbacObjectInvalidException, RbacManagerException { + RBACObjectAssertions.assertValid(userAssignment); + if (!(userAssignment instanceof JpaUserAssignment)) { + throw new RbacObjectInvalidException("Cannto save object that is not JpaUserAssignment"); + } + final EntityManager em = getEm(); + em.getTransaction().begin(); + em.persist(userAssignment); + em.getTransaction().commit(); + fireRbacUserAssignmentSaved(userAssignment); + return userAssignment; + } + + @Override + public UserAssignment getUserAssignment(String principal) throws RbacObjectNotFoundException, RbacManagerException { + final EntityManager em = getEm(); + UserAssignment ua = em.find(JpaUserAssignment.class, principal); + if (ua==null) { + throw new RbacObjectNotFoundException("User assignment not found "+principal); + } + return ua; + } + + @Override + public List getAllUserAssignments() throws RbacManagerException { + final EntityManager em = getEm(); + Query q = em.createQuery("SELECT ua FROM JpaUserAssignment ua"); + return q.getResultList(); + } + + @Override + public List getUserAssignmentsForRoles(Collection roleNames) throws RbacManagerException { + final EntityManager em = getEm(); + Query q = em.createQuery("SELECT ua FROM JpaUserAssignment ua, ua.roleNames rn WHERE rn IN :rolenames"); + q.setParameter("rolenames",roleNames); + return q.getResultList(); + } + + @Override + public void removeUserAssignment(UserAssignment userAssignment) throws RbacObjectNotFoundException, RbacObjectInvalidException, RbacManagerException { + RBACObjectAssertions.assertValid(userAssignment); + if (userAssignment.isPermanent()) { + throw new RbacObjectInvalidException("Cannot remove permanent object "+userAssignment.getPrincipal()); + } + final EntityManager em = getEm(); + em.getTransaction().begin(); + UserAssignment ua = em.find(UserAssignment.class, userAssignment.getPrincipal()); + if (ua==null) { + throw new RbacObjectNotFoundException("User assignment not found "+userAssignment.getPrincipal()); + } + em.remove(ua); + em.getTransaction().commit(); + fireRbacUserAssignmentRemoved(userAssignment); + } + + @Override + public void eraseDatabase() { + final EntityManager em = getEm(); + // Deletion is a bit tricky, because the JPA bulk delete queries do not cascade + // or keep foreign keys into account. + em.getTransaction().begin(); + TypedQuery tqp = em.createQuery("SELECT r FROM JpaPermission r",JpaPermission.class); + for(JpaPermission p : tqp.getResultList()) { + p.setOperation(null); + p.setResource(null); + } + TypedQuery tqr = em.createQuery("SELECT r FROM JpaRole r",JpaRole.class); + for (JpaRole r : tqr.getResultList()) { + r.getPermissions().clear(); + } + em.flush(); + TypedQuery tqo = em.createQuery("SELECT o FROM JpaOperation o",JpaOperation.class); + for(JpaOperation o : tqo.getResultList()) { + em.remove(o); + } + TypedQuery tqre = em.createQuery("SELECT re FROM JpaResource re",JpaResource.class); + for(JpaResource re : tqre.getResultList()) { + em.remove(re); + } + for (JpaPermission p : tqp.getResultList()) { + em.remove(p); + } + for (JpaRole r : tqr.getResultList()) { + em.remove(r); + } + TypedQuery tqu = em.createQuery("SELECT ua FROM JpaUserAssignment ua", JpaUserAssignment.class); + for(JpaUserAssignment ua : tqu.getResultList()) { + em.remove(ua); + } + em.getTransaction().commit(); + + + } + + @Override + public String getDescriptionKey() { + return "archiva.redback.rbacmanager.jpa"; + } + + @Override + public boolean isReadOnly() { + return false; + } + + private EntityManager getEm() { + if (initialized.compareAndSet(false, true)) { + Query q = em.createQuery("SELECT COUNT(r.name) FROM JpaRole r"); + boolean dbInit = q.getFirstResult()==0; + fireRbacInit(dbInit); + } + return em; + } + + +} diff --git a/redback-rbac/redback-rbac-providers/redback-rbac-jpa/src/main/java/org/apache/archiva/redback/rbac/jpa/model/JpaOperation.java b/redback-rbac/redback-rbac-providers/redback-rbac-jpa/src/main/java/org/apache/archiva/redback/rbac/jpa/model/JpaOperation.java new file mode 100644 index 00000000..66a41a15 --- /dev/null +++ b/redback-rbac/redback-rbac-providers/redback-rbac-jpa/src/main/java/org/apache/archiva/redback/rbac/jpa/model/JpaOperation.java @@ -0,0 +1,91 @@ +package org.apache.archiva.redback.rbac.jpa.model; + +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import org.apache.archiva.redback.rbac.Operation; + +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.Id; +import javax.persistence.Table; +import java.io.Serializable; + +/** + * Created by martin on 25.09.16. + */ +@Entity +@Table(name="SECURITY_OPERATIONS") +public class JpaOperation implements Operation, Serializable { + + @Id + @Column(name="NAME") + private String name; + @Column(name="DESCRIPTION") + private String description; + @Column(name="PERMANENT") + private boolean permanent; + + + @Override + public String getName() { + return name; + } + + @Override + public void setName(String name) { + this.name = name; + } + + @Override + public String getDescription() { + return description; + } + + @Override + public void setDescription(String description) { + this.description = description; + } + + @Override + public boolean isPermanent() { + return permanent; + } + + @Override + public void setPermanent(boolean permanent) { + this.permanent = permanent; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + JpaOperation that = (JpaOperation) o; + + return name.equals(that.name); + + } + + @Override + public int hashCode() { + return name.hashCode(); + } +} diff --git a/redback-rbac/redback-rbac-providers/redback-rbac-jpa/src/main/java/org/apache/archiva/redback/rbac/jpa/model/JpaPermission.java b/redback-rbac/redback-rbac-providers/redback-rbac-jpa/src/main/java/org/apache/archiva/redback/rbac/jpa/model/JpaPermission.java new file mode 100644 index 00000000..4ac69896 --- /dev/null +++ b/redback-rbac/redback-rbac-providers/redback-rbac-jpa/src/main/java/org/apache/archiva/redback/rbac/jpa/model/JpaPermission.java @@ -0,0 +1,123 @@ +package org.apache.archiva.redback.rbac.jpa.model; + +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import org.apache.archiva.redback.rbac.Operation; +import org.apache.archiva.redback.rbac.Permission; +import org.apache.archiva.redback.rbac.Resource; +import org.apache.archiva.redback.rbac.jpa.JpaRbacManager; + +import javax.persistence.*; +import java.io.Serializable; + +/** + * Created by martin on 25.09.16. + */ +@Entity +@Table(name="SECURITY_PERMISSIONS") +public class JpaPermission implements Permission,Serializable { + + @Id + @Column(name="NAME") + private String name; + @Column(name="DESCRIPTION") + private String description; + @Column(name="PERMANENT") + private boolean permanent; + @ManyToOne(cascade = CascadeType.PERSIST) + @JoinColumn( + name="OPERATION_NAME_OID", + referencedColumnName = "NAME" + ) + private JpaOperation operation; + @ManyToOne(cascade = CascadeType.PERSIST) + @JoinColumn( + name="RESOURCE_IDENTIFIER_OID", + referencedColumnName = "IDENTIFIER" + ) + private JpaResource resource; + + + @Override + public String getName() { + return name; + } + + @Override + public void setName(String name) { + this.name = name; + } + + @Override + public String getDescription() { + return description; + } + + @Override + public void setDescription(String description) { + this.description = description; + } + + @Override + public boolean isPermanent() { + return permanent; + } + + @Override + public void setPermanent(boolean permanent) { + this.permanent = permanent; + } + + @Override + public Operation getOperation() { + return operation; + } + + @Override + public void setOperation(Operation operation) { + this.operation = (JpaOperation)operation; + } + + @Override + public Resource getResource() { + return resource; + } + + @Override + public void setResource(Resource resource) { + this.resource = (JpaResource)resource; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + JpaPermission that = (JpaPermission) o; + + return name.equals(that.name); + + } + + @Override + public int hashCode() { + return name.hashCode(); + } +} diff --git a/redback-rbac/redback-rbac-providers/redback-rbac-jpa/src/main/java/org/apache/archiva/redback/rbac/jpa/model/JpaResource.java b/redback-rbac/redback-rbac-providers/redback-rbac-jpa/src/main/java/org/apache/archiva/redback/rbac/jpa/model/JpaResource.java new file mode 100644 index 00000000..fc7ee777 --- /dev/null +++ b/redback-rbac/redback-rbac-providers/redback-rbac-jpa/src/main/java/org/apache/archiva/redback/rbac/jpa/model/JpaResource.java @@ -0,0 +1,91 @@ +package org.apache.archiva.redback.rbac.jpa.model; + +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import org.apache.archiva.redback.rbac.Resource; + +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.Id; +import javax.persistence.Table; +import java.io.Serializable; + +/** + * Created by martin on 25.09.16. + */ +@Entity +@Table(name="SECURITY_RESOURCES") +public class JpaResource implements Resource, Serializable { + + @Id + @Column(name="IDENTIFIER") + private String identifier; + @Column(name="PATTERN") + private boolean pattern; + @Column(name="PERMANENT") + private boolean permanent; + + + @Override + public String getIdentifier() { + return identifier; + } + + @Override + public void setIdentifier(String identifier) { + this.identifier = identifier; + } + + @Override + public boolean isPattern() { + return pattern; + } + + @Override + public void setPattern(boolean pattern) { + this.pattern = pattern; + } + + @Override + public boolean isPermanent() { + return permanent; + } + + @Override + public void setPermanent(boolean permanent) { + this.permanent = permanent; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + JpaResource that = (JpaResource) o; + + return identifier.equals(that.identifier); + + } + + @Override + public int hashCode() { + return identifier.hashCode(); + } +} diff --git a/redback-rbac/redback-rbac-providers/redback-rbac-jpa/src/main/java/org/apache/archiva/redback/rbac/jpa/model/JpaRole.java b/redback-rbac/redback-rbac-providers/redback-rbac-jpa/src/main/java/org/apache/archiva/redback/rbac/jpa/model/JpaRole.java new file mode 100644 index 00000000..1f8a62c6 --- /dev/null +++ b/redback-rbac/redback-rbac-providers/redback-rbac-jpa/src/main/java/org/apache/archiva/redback/rbac/jpa/model/JpaRole.java @@ -0,0 +1,172 @@ +package org.apache.archiva.redback.rbac.jpa.model; + +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import org.apache.archiva.redback.rbac.AbstractRole; +import org.apache.archiva.redback.rbac.Permission; + +import javax.persistence.*; +import java.io.Serializable; +import java.util.ArrayList; +import java.util.List; + +/** + * Created by martin on 25.09.16. + */ +@Entity +@Table( + name="SECURITY_ROLES" +) +public class JpaRole extends AbstractRole implements Serializable { + + @Id + @Column(name="NAME") + private String name; + @Column(name="DESCRIPTION") + private String description; + @Column(name="ASSIGNABLE") + private boolean assignable; + @Column(name="PERMANENT") + private boolean permanent; + @ManyToMany(cascade = CascadeType.PERSIST) + @JoinTable( + name="SECURITY_ROLE_PERMISSION_MAP", + joinColumns={ @JoinColumn(name="NAME_OID", referencedColumnName="NAME") }, + inverseJoinColumns = { + @JoinColumn(name="NAME_EID",referencedColumnName = "NAME") + } + ) + List permissions = new ArrayList(); + + @ElementCollection + @CollectionTable( + name="SECURITY_ROLE_CHILDROLE_MAP", + joinColumns = { + @JoinColumn(name="NAME_OID",referencedColumnName = "NAME") + } + ) + List childRoleNames = new ArrayList(); + + + + @Override + public void addPermission(Permission permission) { + if (permission instanceof JpaPermission) { + this.permissions.add((JpaPermission) permission); + } + + } + + @Override + public void addChildRoleName(String name) { + this.childRoleNames.add(name); + } + + @Override + public List getChildRoleNames() { + return childRoleNames; + } + + @Override + public String getDescription() { + return description; + } + + @Override + public String getName() { + return name; + } + + @Override + public List getPermissions() { + // Maybe better to create a new list? + return (List)(List)permissions; + } + + @Override + public boolean isAssignable() { + return assignable; + } + + @Override + public void removePermission(Permission permission) { + this.permissions.remove(permission); + } + + @Override + public void setAssignable(boolean assignable) { + this.assignable=assignable; + } + + @Override + public void setChildRoleNames(List names) { + this.childRoleNames.clear(); + this.childRoleNames.addAll(names); + } + + @Override + public void setDescription(String description) { + this.description=description; + + } + + @Override + public void setName(String name) { + this.name=name; + + } + + @Override + public void setPermissions(List permissions) { + this.permissions.clear(); + for (Permission p : permissions) { + if (p instanceof JpaPermission) { + permissions.add(p); + } + } + } + + @Override + public boolean isPermanent() { + return permanent; + } + + @Override + public void setPermanent(boolean permanent) { + this.permanent=permanent; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + if (!super.equals(o)) return false; + + JpaRole jpaRole = (JpaRole) o; + + return name.equals(jpaRole.name); + + } + + @Override + public int hashCode() { + return name.hashCode(); + } +} diff --git a/redback-rbac/redback-rbac-providers/redback-rbac-jpa/src/main/java/org/apache/archiva/redback/rbac/jpa/model/JpaUserAssignment.java b/redback-rbac/redback-rbac-providers/redback-rbac-jpa/src/main/java/org/apache/archiva/redback/rbac/jpa/model/JpaUserAssignment.java new file mode 100644 index 00000000..39e0b2d3 --- /dev/null +++ b/redback-rbac/redback-rbac-providers/redback-rbac-jpa/src/main/java/org/apache/archiva/redback/rbac/jpa/model/JpaUserAssignment.java @@ -0,0 +1,97 @@ +package org.apache.archiva.redback.rbac.jpa.model; + +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import org.apache.archiva.redback.rbac.AbstractUserAssignment; + +import javax.persistence.*; +import java.io.Serializable; +import java.util.ArrayList; +import java.util.List; + +/** + * Created by Martin Stockhammer on 26.09.16. + */ +@Entity +@Table(name="SECURITY_USER_ASSIGNMENTS") +public class JpaUserAssignment extends AbstractUserAssignment implements Serializable { + + + @Id + @Column(name="PRINCIPAL") + private String principal; + @ElementCollection + @Column(name="STRING_ELE") + @CollectionTable( + name="SECURITY_USERASSIGNMENT_MAP", + joinColumns = { + @JoinColumn(name = "PRINCIPAL_OID", referencedColumnName = "PRINCIPAL") + } + ) + private List roleNames = new ArrayList(); + @Column(name="PERMANENT") + private boolean permanent = false; + + @Override + public String getPrincipal() { + return principal; + } + + @Override + public void setPrincipal(String principal) { + this.principal = principal; + } + + @Override + public List getRoleNames() { + return roleNames; + } + + @Override + public void setRoleNames(List roleNames) { + this.roleNames = roleNames; + } + + @Override + public boolean isPermanent() { + return permanent; + } + + @Override + public void setPermanent(boolean permanent) { + this.permanent = permanent; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + JpaUserAssignment that = (JpaUserAssignment) o; + + return principal.equals(that.principal); + + } + + @Override + public int hashCode() { + return principal.hashCode(); + } +} diff --git a/redback-rbac/redback-rbac-providers/redback-rbac-jpa/src/test/java/org/apache/archiva/redback/rbac/jpa/JpaRbacManagerTest.java b/redback-rbac/redback-rbac-providers/redback-rbac-jpa/src/test/java/org/apache/archiva/redback/rbac/jpa/JpaRbacManagerTest.java new file mode 100644 index 00000000..3c5bc9fa --- /dev/null +++ b/redback-rbac/redback-rbac-providers/redback-rbac-jpa/src/test/java/org/apache/archiva/redback/rbac/jpa/JpaRbacManagerTest.java @@ -0,0 +1,132 @@ +package org.apache.archiva.redback.rbac.jpa; + +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import org.apache.archiva.redback.common.jdo.test.StoreManagerDebug; +import org.apache.archiva.redback.rbac.RbacManagerException; +import org.apache.archiva.redback.tests.AbstractRbacManagerTestCase; +import org.junit.Before; +import org.springframework.test.annotation.DirtiesContext; + +import javax.inject.Inject; +import javax.inject.Named; +import javax.persistence.EntityManagerFactory; +import javax.persistence.Persistence; +import java.io.InputStream; +import java.util.Properties; + +/** + * JdoRbacManagerTest: + * + * @author Jesse McConnell + * @author Joakim Erdfelt + */ +@DirtiesContext(classMode = DirtiesContext.ClassMode.AFTER_EACH_TEST_METHOD) +public class JpaRbacManagerTest + extends AbstractRbacManagerTestCase +{ + + @Inject + @Named(value = "rbacManager#jpa") + JpaRbacManager rbacManager; + + public static int EVENTCOUNT = 2; + + @Override + public void assertEventCount() + { + assertEquals( EVENTCOUNT, eventTracker.initCount ); + } + + /** + * Creates a new RbacStore which contains no data. + */ + @Before + public void setUp() + throws Exception + { + + super.setUp(); + Properties props = new Properties(); + InputStream is = Thread.currentThread().getContextClassLoader().getResourceAsStream("test.properties"); + assert is!=null; + props.load(is); + is.close(); + EntityManagerFactory emf = Persistence.createEntityManagerFactory("redback-jpa",props); + + log.info("test setup"); + rbacManager.setEntityManager(emf.createEntityManager()); + super.setRbacManager(rbacManager); + assertNotNull(rbacManager); + log.info("injected rbac manager "+rbacManager); + + } + + + @Override + public void testGetAssignedRoles() + throws RbacManagerException + { + super.testGetAssignedRoles(); + } + + @Override + public void testGetAssignedPermissionsDeep() + throws RbacManagerException + { + super.testGetAssignedPermissionsDeep(); + } + + @Override + protected void afterSetup() + { + super.afterSetup(); + } + + @Override + public void testLargeApplicationInit() + throws RbacManagerException + { + this.clearCache(); + super.testLargeApplicationInit(); + } + + @Override + public void testGetRolesDeep() + throws RbacManagerException + { + this.clearCache(); + super.testGetRolesDeep(); + } + + + @Override + public void testStoreInitialization() + throws Exception + { + this.clearCache(); + rbacManager.eraseDatabase(); + eventTracker.rbacInit( true ); + super.testStoreInitialization(); + assertEquals( EVENTCOUNT, eventTracker.initCount ); + } + + +} diff --git a/redback-rbac/redback-rbac-providers/redback-rbac-jpa/src/test/resources/spring-context.xml b/redback-rbac/redback-rbac-providers/redback-rbac-jpa/src/test/resources/spring-context.xml new file mode 100644 index 00000000..abee0b5a --- /dev/null +++ b/redback-rbac/redback-rbac-providers/redback-rbac-jpa/src/test/resources/spring-context.xml @@ -0,0 +1,40 @@ + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/redback-rbac/redback-rbac-providers/redback-rbac-jpa/src/test/resources/test.properties b/redback-rbac/redback-rbac-providers/redback-rbac-jpa/src/test/resources/test.properties new file mode 100644 index 00000000..df848c45 --- /dev/null +++ b/redback-rbac/redback-rbac-providers/redback-rbac-jpa/src/test/resources/test.properties @@ -0,0 +1,22 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +openjpa.ConnectionURL=jdbc:hsqldb:mem:reback-jpa +openjpa.ConnectionDriverName=org.hsqldb.jdbcDriver +openjpa.ConnectionUserName=sa +openjpa.ConnectionPassword= +openjp.Log=DefaultLevel=WARN,Tool=INFO +openjpa.jdbc.SynchronizeMappings=buildSchema(ForeignKeys=true) \ No newline at end of file diff --git a/redback-users/redback-users-providers/pom.xml b/redback-users/redback-users-providers/pom.xml index 9ddab02c..4a906482 100644 --- a/redback-users/redback-users-providers/pom.xml +++ b/redback-users/redback-users-providers/pom.xml @@ -39,5 +39,6 @@ redback-users-jdo redback-users-ldap redback-users-configurable + redback-users-jpa diff --git a/redback-users/redback-users-providers/redback-users-jpa/pom.xml b/redback-users/redback-users-providers/redback-users-jpa/pom.xml new file mode 100644 index 00000000..2606b712 --- /dev/null +++ b/redback-users/redback-users-providers/redback-users-jpa/pom.xml @@ -0,0 +1,68 @@ + + + + + redback-users-providers + org.apache.archiva.redback + 2.5-SNAPSHOT + + 4.0.0 + + redback-users-jpa + jar + Redback :: Users Provider :: JPA + + + + org.springframework + spring-context-support + + + javax.annotation + jsr250-api + + + org.apache.archiva.redback + redback-policy + + + org.apache.archiva.redback + redback-common-jpa + + + org.apache.openjpa + openjpa + ${openjpa.version} + + + + org.hsqldb + hsqldb + test + + + org.apache.archiva.redback + redback-users-tests + test + + + \ No newline at end of file diff --git a/redback-users/redback-users-providers/redback-users-jpa/src/main/java/org/apache/archiva/redback/users/jpa/JpaUserManager.java b/redback-users/redback-users-providers/redback-users-jpa/src/main/java/org/apache/archiva/redback/users/jpa/JpaUserManager.java new file mode 100644 index 00000000..c4c83f60 --- /dev/null +++ b/redback-users/redback-users-providers/redback-users-jpa/src/main/java/org/apache/archiva/redback/users/jpa/JpaUserManager.java @@ -0,0 +1,307 @@ +package org.apache.archiva.redback.users.jpa; + +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import org.apache.archiva.redback.policy.UserSecurityPolicy; +import org.apache.archiva.redback.users.*; +import org.apache.archiva.redback.users.jpa.model.JpaUser; +import org.apache.commons.lang.StringUtils; + +import javax.annotation.PostConstruct; +import javax.inject.Inject; +import javax.persistence.*; +import javax.persistence.criteria.CriteriaBuilder; +import javax.persistence.criteria.CriteriaQuery; +import java.util.List; +import java.util.concurrent.atomic.AtomicBoolean; + +/** + * Created by martin on 20.09.16. + */ +@org.springframework.stereotype.Service("userManager#jpa") +public class JpaUserManager extends AbstractUserManager { + + + @PersistenceContext(unitName = "redback-jpa") + EntityManager em; + + @Inject + private UserSecurityPolicy userSecurityPolicy; + + // JpaUserManager is a singleton and initialization should be thread safe + private AtomicBoolean initialized = new AtomicBoolean(false); + + + public void setEntityManager(EntityManager em) { + this.em = em; + } + + @Override + public boolean isReadOnly() { + return false; + } + + @Override + public String getId() { + return "jpa"; + } + + private EntityManager getEm() { + if (initialized.compareAndSet(false,true)) { + Query q = em.createQuery("SELECT COUNT(u.username) FROM JpaUser u"); + boolean dbInit = q.getFirstResult()==0; + fireUserManagerInit(dbInit); + } + return em; + } + + + @Override + public User createUser(String username, String fullName, String emailAddress) throws UserManagerException { + + JpaUser user = new JpaUser(); + user.setUsername(username); + user.setFullName(fullName); + user.setEmail(emailAddress); + return user; + } + + @Override + public UserQuery createUserQuery() { + return new JpaUserQuery(); + } + + @Override + public List getUsers() throws UserManagerException { + final EntityManager em = getEm(); + Query q= em.createQuery("SELECT x from JpaUser x"); + return q.getResultList(); + } + + @Override + public List getUsers(boolean orderAscending) throws UserManagerException { + final EntityManager em = getEm(); + final String orderFlag = orderAscending ? "ASC" : "DESC"; + Query q = em.createQuery("SELECT u FROM JpaUser u ORDER BY u.username "+orderFlag); + return q.getResultList(); + } + + @Override + public User addUser(User user) throws UserManagerException { + EntityManager em = getEm(); + if ( !( user instanceof JpaUser ) ) + { + throw new UserManagerException( "Unable to Add User. User object " + user.getClass().getName() + + " is not an instance of " + JpaUser.class.getName() ); + } + + if ( StringUtils.isEmpty( user.getUsername() ) ) + { + throw new IllegalStateException( + Messages.getString( "user.manager.cannot.add.user.without.username" ) ); //$NON-NLS-1$ + } + + userSecurityPolicy.extensionChangePassword( user ); + + fireUserManagerUserAdded( user ); + + // TODO: find a better solution + // workaround for avoiding the admin from providing another password on the next login after the + // admin account has been created + // extensionChangePassword by default sets the password change status to false + if ( "admin".equals( user.getUsername() ) ) + { + user.setPasswordChangeRequired( false ); + } + else + { + user.setPasswordChangeRequired( true ); + } + em.getTransaction().begin(); + em.persist((JpaUser)user); + em.getTransaction().commit(); + return user; + } + + @Override + public User updateUser(User user) throws UserNotFoundException, UserManagerException { + return updateUser(user, false); + } + + @Override + public User findUser(String username) throws UserNotFoundException, UserManagerException { + if (username==null) { + throw new UserNotFoundException("Username was "); + } + final EntityManager em = getEm(); + TypedQuery q = em.createQuery("SELECT u FROM JpaUser u WHERE LOWER(u.username)=:uname", JpaUser.class); + q.setParameter("uname",username.toLowerCase()); + User result; + try { + result = q.getSingleResult(); + } catch (NoResultException ex ) { + throw new UserNotFoundException(ex); + } + return result; + } + + @Override + public User findUser(String username, boolean useCache) throws UserNotFoundException, UserManagerException { + return findUser(username); + } + + @Override + public List findUsersByUsernameKey(String usernameKey, boolean orderAscending) throws UserManagerException { + return findUsers("username",usernameKey,"username",orderAscending); + } + + @Override + public List findUsersByFullNameKey(String fullNameKey, boolean orderAscending) throws UserManagerException { + return findUsers("fullName",fullNameKey,"username",orderAscending); + } + + @Override + public List findUsersByEmailKey(String emailKey, boolean orderAscending) throws UserManagerException { + return findUsers("email",emailKey,"username", orderAscending); + } + + @Override + public List findUsersByQuery(final UserQuery queryParam) throws UserManagerException { + final EntityManager em = getEm(); + final JpaUserQuery query = (JpaUserQuery)queryParam; + String orderByAttribute = ""; + if (UserQuery.ORDER_BY_EMAIL.equals(query.getOrderBy())) { + orderByAttribute="email"; + } else if (UserQuery.ORDER_BY_FULLNAME.equals(query.getOrderBy())) { + orderByAttribute="fullName"; + } else if (UserQuery.ORDER_BY_USERNAME.equals(query.getOrderBy())) { + orderByAttribute="username"; + } else { + throw new IllegalArgumentException("Unknown order attribute "+query.getOrderBy()); + } + StringBuilder sb = new StringBuilder("SELECT u FROM JpaUser u "); + if (query.hasUsername()||query.hasFullName()||query.hasEmail()) { + sb.append("WHERE "); + } + boolean checkBefore = false; + if (query.hasUsername()) { + sb.append("LOWER(u.username) LIKE :username "); + checkBefore=true; + } + if (query.hasEmail()) { + if (checkBefore) { + sb.append("AND "); + } + checkBefore=true; + sb.append("LOWER(u.email) LIKE :email "); + } + if (query.hasFullName()) { + if (checkBefore) { + sb.append("AND "); + } + sb.append("LOWER(u.fullName) LIKE :fullname "); + } + if (query.getOrderBy()!=null && !"".equals(query.getOrderBy())) { + sb.append("ORDER BY u.").append(orderByAttribute).append(query.isAscending() ? " ASC" : " DESC"); + } + TypedQuery q = em.createQuery(sb.toString(), User.class); + if (query.hasUsername()) { + q.setParameter("username", "%"+query.getUsername().toLowerCase()+"%"); + } + if (query.hasEmail()) { + q.setParameter("email", "%"+query.getEmail().toLowerCase()+"%"); + } + if (query.hasFullName()) { + q.setParameter("fullname", "%"+query.getFullName().toLowerCase()+"%"); + } + q.setFirstResult((int)query.getFirstResult()).setMaxResults((int)query.getMaxResults()); + return q.getResultList(); + } + + private List findUsers(final String attribute, final String pattern, + final String orderAttribute, final boolean orderAscending) { + final EntityManager em = getEm(); + StringBuilder sb = new StringBuilder("SELECT u FROM JpaUser u WHERE LOWER(u."); + sb.append(attribute).append(") LIKE :patternvalue ORDER BY u.").append(orderAttribute); + sb.append(orderAscending ? " ASC" : " DESC"); + TypedQuery q = em.createQuery(sb.toString(),User.class); + q.setParameter("patternvalue","%"+pattern.toLowerCase()+"%"); + return q.getResultList(); + } + + @Override + public boolean userExists(String principal) throws UserManagerException { + EntityManager em = getEm(); + JpaUser user = em.find(JpaUser.class, principal); + return user != null; + } + + + + @Override + public void deleteUser(String username) throws UserNotFoundException, UserManagerException { + final EntityManager em = getEm(); + User u = findUser(username); + if (u.isPermanent()) { + throw new PermanentUserException("User "+username+" cannot be deleted"); + } + em.getTransaction().begin(); + em.remove(u); + em.getTransaction().commit(); + fireUserManagerUserRemoved(u); + } + + @Override + public void addUserUnchecked(User user) throws UserManagerException { + + } + + @Override + public void eraseDatabase() { + EntityManager em = getEm(); + em.getTransaction().begin(); + Query q = em.createQuery("DELETE FROM JpaUser u"); + q.executeUpdate(); + em.getTransaction().commit(); + } + + @Override + public User updateUser(User user, boolean passwordChangeRequired) throws UserNotFoundException, UserManagerException { + if ( StringUtils.isNotEmpty( user.getPassword() ) ) + { + userSecurityPolicy.extensionChangePassword( user, passwordChangeRequired ); + } + final EntityManager em = getEm(); + em.getTransaction().begin(); + em.persist((JpaUser)user); + em.getTransaction().commit(); + fireUserManagerUserUpdated(user); + return user; + } + + @Override + public String getDescriptionKey() { + return null; + } + + + + +} diff --git a/redback-users/redback-users-providers/redback-users-jpa/src/main/java/org/apache/archiva/redback/users/jpa/JpaUserQuery.java b/redback-users/redback-users-providers/redback-users-jpa/src/main/java/org/apache/archiva/redback/users/jpa/JpaUserQuery.java new file mode 100644 index 00000000..d48a59ce --- /dev/null +++ b/redback-users/redback-users-providers/redback-users-jpa/src/main/java/org/apache/archiva/redback/users/jpa/JpaUserQuery.java @@ -0,0 +1,123 @@ +package org.apache.archiva.redback.users.jpa; + +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import org.apache.archiva.redback.users.UserQuery; + +import java.util.Arrays; + +/** + * Created by martin on 23.09.16. + */ +public class JpaUserQuery implements UserQuery { + + private String username; + private String email; + private String fullName; + private long firstResult=0; + private long maxResults=Integer.MAX_VALUE; + private boolean ascending=true; + + private String orderBy="username"; + + @Override + public String getUsername() { + return username; + } + + @Override + public void setUsername(String username) { + this.username = username; + } + + public boolean hasUsername() { + return username != null && !"".equals(username); + } + + @Override + public String getEmail() { + return email; + } + + @Override + public void setEmail(String email) { + this.email = email; + } + + public boolean hasEmail() { + return email!=null && !"".equals(email); + } + + @Override + public String getFullName() { + return fullName; + } + + @Override + public void setFullName(String fullName) { + this.fullName = fullName; + } + + public boolean hasFullName() { + return fullName!=null && !"".equals(fullName); + } + + @Override + public long getFirstResult() { + return firstResult; + } + + public void setFirstResult(int firstResult) { + this.firstResult = firstResult; + } + + @Override + public long getMaxResults() { + return maxResults; + } + + public void setMaxResults(int maxResults) { + this.maxResults = maxResults; + } + + @Override + public boolean isAscending() { + return ascending; + } + + @Override + public void setAscending(boolean ascending) { + this.ascending = ascending; + } + + + @Override + public String getOrderBy() { + return orderBy; + } + + @Override + public void setOrderBy(String orderBy) { + if (!UserQuery.ALLOWED_ORDER_FIELDS.contains(orderBy)) { + throw new IllegalArgumentException("Order attribute not allowed: "+orderBy); + } + this.orderBy = orderBy; + } +} diff --git a/redback-users/redback-users-providers/redback-users-jpa/src/main/java/org/apache/archiva/redback/users/jpa/model/JpaUser.java b/redback-users/redback-users-providers/redback-users-jpa/src/main/java/org/apache/archiva/redback/users/jpa/model/JpaUser.java new file mode 100644 index 00000000..04f441e0 --- /dev/null +++ b/redback-users/redback-users-providers/redback-users-jpa/src/main/java/org/apache/archiva/redback/users/jpa/model/JpaUser.java @@ -0,0 +1,206 @@ +package org.apache.archiva.redback.users.jpa.model; + +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import javax.persistence.ElementCollection; +import javax.persistence.Entity; +import javax.persistence.Id; +import javax.persistence.Table; +import java.util.ArrayList; +import java.util.Date; +import java.util.List; + +/** + * Created by martin on 20.09.16. + */ +@Entity +@Table(name="JDOUSER") +public class JpaUser implements org.apache.archiva.redback.users.User { + + @Id + private String username; + + private String fullName; + private String email; + private String encodedPassword; + private Date lastPasswordChange; + @ElementCollection + private List previousEncodedPasswords = new ArrayList(); + private boolean permanent; + private boolean locked; + private boolean passwordChangeRequired; + private boolean validated; + private int countFailedLoginAttempts; + private Date accountCreationDate; + private Date lastLoginDate; + private String rawPassword; + + + @Override + public String getUsername() { + return username; + } + + @Override + public void setUsername(String name) { + this.username = name; + } + + @Override + public String getFullName() { + return fullName; + } + + @Override + public void setFullName(String name) { + this.fullName = name; + } + + @Override + public String getEmail() { + return email; + } + + @Override + public void setEmail(String address) { + this.email = address; + } + + @Override + public String getPassword() { + return rawPassword; + } + + @Override + public void setPassword(String rawPassword) { + this.rawPassword = rawPassword; + } + + @Override + public String getEncodedPassword() { + return encodedPassword; + } + + @Override + public void setEncodedPassword(String encodedPassword) { + this.encodedPassword = encodedPassword; + } + + @Override + public Date getLastPasswordChange() { + return lastPasswordChange; + } + + @Override + public void setLastPasswordChange(Date passwordChangeDate) { + this.lastPasswordChange = lastPasswordChange; + } + + @Override + public List getPreviousEncodedPasswords() { + return previousEncodedPasswords; + } + + @Override + public void setPreviousEncodedPasswords(List encodedPasswordList) { + this.previousEncodedPasswords.clear(); + this.previousEncodedPasswords.addAll(encodedPasswordList); + } + + @Override + public void addPreviousEncodedPassword(String encodedPassword) { + this.previousEncodedPasswords.add(encodedPassword); + } + + @Override + public boolean isPermanent() { + return permanent; + } + + @Override + public void setPermanent(boolean permanent) { + this.permanent = permanent; + } + + @Override + public boolean isLocked() { + return locked; + } + + @Override + public void setLocked(boolean locked) { + this.locked = locked; + } + + @Override + public boolean isPasswordChangeRequired() { + return passwordChangeRequired; + } + + @Override + public void setPasswordChangeRequired(boolean changeRequired) { + this.passwordChangeRequired = changeRequired; + } + + @Override + public boolean isValidated() { + return validated; + } + + @Override + public void setValidated(boolean valid) { + this.validated = valid; + } + + @Override + public int getCountFailedLoginAttempts() { + return countFailedLoginAttempts; + } + + @Override + public void setCountFailedLoginAttempts(int count) { + this.countFailedLoginAttempts = count; + } + + @Override + public Date getAccountCreationDate() { + return accountCreationDate; + } + + @Override + public void setAccountCreationDate(Date date) { + this.accountCreationDate = date; + } + + @Override + public Date getLastLoginDate() { + return lastLoginDate; + } + + @Override + public void setLastLoginDate(Date date) { + this.lastLoginDate = date; + } + + @Override + public String getUserManagerId() { + return null; + } +} diff --git a/redback-users/redback-users-providers/redback-users-jpa/src/test/java/org/apache/archiva/redback/users/jpa/JpaUserManagerTest.java b/redback-users/redback-users-providers/redback-users-jpa/src/test/java/org/apache/archiva/redback/users/jpa/JpaUserManagerTest.java new file mode 100644 index 00000000..4556755f --- /dev/null +++ b/redback-users/redback-users-providers/redback-users-jpa/src/test/java/org/apache/archiva/redback/users/jpa/JpaUserManagerTest.java @@ -0,0 +1,86 @@ +package org.apache.archiva.redback.users.jpa; + +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import org.apache.archiva.redback.policy.UserSecurityPolicy; +import org.apache.archiva.redback.users.User; +import org.apache.archiva.redback.users.UserManager; +import org.apache.archiva.redback.users.provider.test.AbstractUserManagerTestCase; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.junit.Before; +import org.junit.Test; + +import javax.inject.Inject; +import javax.inject.Named; +import javax.persistence.EntityManagerFactory; +import javax.persistence.Persistence; +import java.io.InputStream; +import java.util.HashMap; +import java.util.Map; +import java.util.Properties; + +/** + * Created by martin on 21.09.16. + */ + +public class JpaUserManagerTest extends AbstractUserManagerTestCase { + + Log log = LogFactory.getLog(JpaUserManagerTest.class); + + @Inject + @Named("userManager#jpa") + JpaUserManager jpaUserManager; + + + @Inject + private UserSecurityPolicy securityPolicy; + + @Before + @Override + public void setUp() throws Exception { + + super.setUp(); + Properties props = new Properties(); + InputStream is = Thread.currentThread().getContextClassLoader().getResourceAsStream("test.properties"); + assert is!=null; + props.load(is); + is.close(); + EntityManagerFactory emf = Persistence.createEntityManagerFactory("redback-jpa",props); + + log.info("test setup"); + jpaUserManager.setEntityManager(emf.createEntityManager()); + super.setUserManager(jpaUserManager); + assertNotNull(jpaUserManager); + log.info("injected usermanager "+jpaUserManager); + + // create the factory defined by the "openjpa" entity-manager entry + + } + + @Test + public void testInit() { + jpaUserManager.initialize(); + } + + + + +} diff --git a/redback-users/redback-users-providers/redback-users-jpa/src/test/resources/spring-context.xml b/redback-users/redback-users-providers/redback-users-jpa/src/test/resources/spring-context.xml new file mode 100644 index 00000000..4d7802ed --- /dev/null +++ b/redback-users/redback-users-providers/redback-users-jpa/src/test/resources/spring-context.xml @@ -0,0 +1,50 @@ + + + + + + + + + + + + + + + + + + + ]]> + + + + + + + \ No newline at end of file diff --git a/redback-users/redback-users-providers/redback-users-jpa/src/test/resources/test.properties b/redback-users/redback-users-providers/redback-users-jpa/src/test/resources/test.properties new file mode 100644 index 00000000..df848c45 --- /dev/null +++ b/redback-users/redback-users-providers/redback-users-jpa/src/test/resources/test.properties @@ -0,0 +1,22 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +openjpa.ConnectionURL=jdbc:hsqldb:mem:reback-jpa +openjpa.ConnectionDriverName=org.hsqldb.jdbcDriver +openjpa.ConnectionUserName=sa +openjpa.ConnectionPassword= +openjp.Log=DefaultLevel=WARN,Tool=INFO +openjpa.jdbc.SynchronizeMappings=buildSchema(ForeignKeys=true) \ No newline at end of file diff --git a/redback-users/redback-users-tests/src/main/java/org/apache/archiva/redback/users/provider/test/AbstractUserManagerTestCase.java b/redback-users/redback-users-tests/src/main/java/org/apache/archiva/redback/users/provider/test/AbstractUserManagerTestCase.java index 6da8ac8d..d293bda5 100644 --- a/redback-users/redback-users-tests/src/main/java/org/apache/archiva/redback/users/provider/test/AbstractUserManagerTestCase.java +++ b/redback-users/redback-users-tests/src/main/java/org/apache/archiva/redback/users/provider/test/AbstractUserManagerTestCase.java @@ -97,10 +97,12 @@ public class AbstractUserManagerTestCase throws UserManagerException { + assertNotNull(userManager); getUserManager().eraseDatabase(); getEventTracker().userManagerInit( true ); assertNotNull( getUserManager() ); + assertNotNull(userManager.getUsers()); assertEquals( "New UserManager should contain no users. " + userManager.getUsers(), 0, userManager.getUsers().size() ); }