Adding JPA implementation of users and rbac provider

Uses OpenJPA


Project: http://git-wip-us.apache.org/repos/asf/archiva-redback-core/repo
Commit: 
http://git-wip-us.apache.org/repos/asf/archiva-redback-core/commit/62efc70b
Tree: http://git-wip-us.apache.org/repos/asf/archiva-redback-core/tree/62efc70b
Diff: http://git-wip-us.apache.org/repos/asf/archiva-redback-core/diff/62efc70b

Branch: refs/heads/jpa
Commit: 62efc70bd27c0ddb8bd01c0f67b0c96646aaaea5
Parents: d17c6ff
Author: Martin Stockhammer <marti...@apache.org>
Authored: Tue Sep 27 23:13:03 2016 +0200
Committer: Martin Stockhammer <marti...@apache.org>
Committed: Tue Sep 27 23:13:03 2016 +0200

----------------------------------------------------------------------
 pom.xml                                         |  14 +-
 redback-common/pom.xml                          |   1 +
 redback-common/redback-common-jpa/pom.xml       |  57 +++
 .../resources/META-INF/persistence-hsqldb.xml   |  41 ++
 .../src/main/resources/META-INF/persistence.xml |  36 ++
 redback-rbac/redback-rbac-providers/pom.xml     |   1 +
 .../redback-rbac-jpa/pom.xml                    |  70 +++
 .../redback/rbac/jpa/JpaRbacManager.java        | 452 +++++++++++++++++++
 .../redback/rbac/jpa/model/JpaOperation.java    |  91 ++++
 .../redback/rbac/jpa/model/JpaPermission.java   | 123 +++++
 .../redback/rbac/jpa/model/JpaResource.java     |  91 ++++
 .../archiva/redback/rbac/jpa/model/JpaRole.java | 172 +++++++
 .../rbac/jpa/model/JpaUserAssignment.java       |  97 ++++
 .../redback/rbac/jpa/JpaRbacManagerTest.java    | 132 ++++++
 .../src/test/resources/spring-context.xml       |  40 ++
 .../src/test/resources/test.properties          |  22 +
 redback-users/redback-users-providers/pom.xml   |   1 +
 .../redback-users-jpa/pom.xml                   |  68 +++
 .../redback/users/jpa/JpaUserManager.java       | 307 +++++++++++++
 .../archiva/redback/users/jpa/JpaUserQuery.java | 123 +++++
 .../redback/users/jpa/model/JpaUser.java        | 206 +++++++++
 .../redback/users/jpa/JpaUserManagerTest.java   |  86 ++++
 .../src/test/resources/spring-context.xml       |  50 ++
 .../src/test/resources/test.properties          |  22 +
 .../test/AbstractUserManagerTestCase.java       |   2 +
 25 files changed, 2304 insertions(+), 1 deletion(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/archiva-redback-core/blob/62efc70b/pom.xml
----------------------------------------------------------------------
diff --git a/pom.xml b/pom.xml
index fa30ae5..3e218ee 100644
--- a/pom.xml
+++ b/pom.xml
@@ -78,6 +78,8 @@
     <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
     <gpg.useagent>true</gpg.useagent>
     <cxf.version>3.0.3</cxf.version>
+    <openjpa.version>2.4.1</openjpa.version>
+
     <!--
     
<redbackTestJdbcUrl>jdbc:derby:memory:users-test;create=true</redbackTestJdbcUrl>
     
<redbackTestJdbcDriver>org.apache.derby.jdbc.EmbeddedDriver</redbackTestJdbcDriver>
@@ -300,6 +302,16 @@
         <version>${project.version}</version>
       </dependency>
       <dependency>
+        <groupId>org.apache.archiva.redback</groupId>
+        <artifactId>redback-common-jpa</artifactId>
+        <version>2.5-SNAPSHOT</version>
+      </dependency>
+      <dependency>
+        <groupId>org.apache.archiva.redback</groupId>
+        <artifactId>redback-users-jpa</artifactId>
+        <version>2.5-SNAPSHOT</version>
+      </dependency>
+      <dependency>
         <groupId>javax.servlet</groupId>
         <artifactId>servlet-api</artifactId>
         <version>2.5</version>
@@ -930,7 +942,7 @@
                   <descriptorRefs>
                     
<descriptorRef>${sourceReleaseAssemblyDescriptor}</descriptorRef>
                   </descriptorRefs>
-                  <tarLongFileFormat>gnu</tarLongFileFormat>
+                  <tarLongFileMode>gnu</tarLongFileMode>
                 </configuration>
               </execution>
             </executions>

http://git-wip-us.apache.org/repos/asf/archiva-redback-core/blob/62efc70b/redback-common/pom.xml
----------------------------------------------------------------------
diff --git a/redback-common/pom.xml b/redback-common/pom.xml
index 4a68200..9819bb0 100644
--- a/redback-common/pom.xml
+++ b/redback-common/pom.xml
@@ -31,5 +31,6 @@
     <module>redback-common-jdo</module>
     <module>redback-common-ldap</module>
     <module>redback-common-test-resources</module>
+      <module>redback-common-jpa</module>
   </modules>
 </project>
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/archiva-redback-core/blob/62efc70b/redback-common/redback-common-jpa/pom.xml
----------------------------------------------------------------------
diff --git a/redback-common/redback-common-jpa/pom.xml 
b/redback-common/redback-common-jpa/pom.xml
new file mode 100644
index 0000000..ca3b942
--- /dev/null
+++ b/redback-common/redback-common-jpa/pom.xml
@@ -0,0 +1,57 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  ~ 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.
+  -->
+<project xmlns="http://maven.apache.org/POM/4.0.0";
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance";
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 
http://maven.apache.org/xsd/maven-4.0.0.xsd";>
+    <parent>
+        <artifactId>redback-common</artifactId>
+        <groupId>org.apache.archiva.redback</groupId>
+        <version>2.5-SNAPSHOT</version>
+    </parent>
+    <modelVersion>4.0.0</modelVersion>
+
+    <artifactId>redback-common-jpa</artifactId>
+    <packaging>jar</packaging>
+    <name>Redback :: JPA Common Package</name>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.apache.archiva.redback</groupId>
+            <artifactId>redback-rbac-model</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.archiva.redback</groupId>
+            <artifactId>redback-users-api</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>org.apache.openjpa</groupId>
+            <artifactId>openjpa</artifactId>
+            <version>${openjpa.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>org.hsqldb</groupId>
+            <artifactId>hsqldb</artifactId>
+            <scope>test</scope>
+        </dependency>
+    </dependencies>
+
+
+</project>
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/archiva-redback-core/blob/62efc70b/redback-common/redback-common-jpa/src/main/resources/META-INF/persistence-hsqldb.xml
----------------------------------------------------------------------
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 0000000..a78444e
--- /dev/null
+++ 
b/redback-common/redback-common-jpa/src/main/resources/META-INF/persistence-hsqldb.xml
@@ -0,0 +1,41 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  ~ 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.
+  -->
+<persistence xmlns="http://java.sun.com/xml/ns/persistence"; version="2.0">
+  <persistence-unit name="redback-jpa">
+    <provider>org.apache.openjpa.persistence.PersistenceProviderImpl</provider>
+    <jta-data-source>java:comp/env/jdbc/redbackjpa</jta-data-source>
+    <class>org.apache.archiva.redback.users.jpa.model.JpaUser</class>
+    <class>org.apache.archiva.redback.rbac.jpa.model.JpaOperation</class>
+    <class>org.apache.archiva.redback.rbac.jpa.model.JpaResource</class>
+    <class>org.apache.archiva.redback.rbac.jpa.model.JpaPermission</class>
+    <class>org.apache.archiva.redback.rbac.jpa.model.JpaRole</class>
+    <class>org.apache.archiva.redback.rbac.jpa.model.JpaUserAssignment</class>
+    <properties>
+      <property name="openjpa.ConnectionURL" 
value="jdbc:hsqldb:mem:redback_database"/>
+      <property name="openjpa.ConnectionDriverName" 
value="org.hsqldb.jdbcDriver"/>
+      <property name="openjpa.ConnectionUserName" value="sa"/>
+      <property name="openjpa.ConnectionPassword" value=""/>
+      <property name="openjpa.Log" value="DefaultLevel=WARN, Tool=INFO"/>
+      <property name="openjpa.jdbc.SchemaFactory" 
value="native(ForeignKeys=true)" />
+      <property name="openjpa.jdbc.MappingDefaults"
+                
value="ForeignKeyDeleteAction=restrict,JoinForeignKeyDeleteAction=restrict"/>
+    </properties>
+  </persistence-unit>
+</persistence>

http://git-wip-us.apache.org/repos/asf/archiva-redback-core/blob/62efc70b/redback-common/redback-common-jpa/src/main/resources/META-INF/persistence.xml
----------------------------------------------------------------------
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 0000000..46878e8
--- /dev/null
+++ 
b/redback-common/redback-common-jpa/src/main/resources/META-INF/persistence.xml
@@ -0,0 +1,36 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  ~ 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.
+  -->
+<persistence xmlns="http://java.sun.com/xml/ns/persistence"; version="2.0">
+    <persistence-unit name="redback-jpa">
+        
<provider>org.apache.openjpa.persistence.PersistenceProviderImpl</provider>
+        <jta-data-source>java:comp/env/jdbc/redbackjpa</jta-data-source>
+        <class>org.apache.archiva.redback.users.jpa.model.JpaUser</class>
+        <class>org.apache.archiva.redback.rbac.jpa.model.JpaOperation</class>
+        <class>org.apache.archiva.redback.rbac.jpa.model.JpaResource</class>
+        <class>org.apache.archiva.redback.rbac.jpa.model.JpaPermission</class>
+        <class>org.apache.archiva.redback.rbac.jpa.model.JpaRole</class>
+        
<class>org.apache.archiva.redback.rbac.jpa.model.JpaUserAssignment</class>
+        <properties>
+            <property name="openjpa.jdbc.SchemaFactory" 
value="native(ForeignKeys=true)" />
+            <property name="openjpa.jdbc.MappingDefaults"
+                      
value="ForeignKeyDeleteAction=restrict,JoinForeignKeyDeleteAction=restrict"/>
+        </properties>
+    </persistence-unit>
+</persistence>

http://git-wip-us.apache.org/repos/asf/archiva-redback-core/blob/62efc70b/redback-rbac/redback-rbac-providers/pom.xml
----------------------------------------------------------------------
diff --git a/redback-rbac/redback-rbac-providers/pom.xml 
b/redback-rbac/redback-rbac-providers/pom.xml
index 0edb497..a94d451 100644
--- a/redback-rbac/redback-rbac-providers/pom.xml
+++ b/redback-rbac/redback-rbac-providers/pom.xml
@@ -32,5 +32,6 @@
     <module>redback-rbac-memory</module>
     <module>redback-rbac-cached</module>
     <module>redback-rbac-ldap</module>
+      <module>redback-rbac-jpa</module>
   </modules>
 </project>

http://git-wip-us.apache.org/repos/asf/archiva-redback-core/blob/62efc70b/redback-rbac/redback-rbac-providers/redback-rbac-jpa/pom.xml
----------------------------------------------------------------------
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 0000000..77045ab
--- /dev/null
+++ b/redback-rbac/redback-rbac-providers/redback-rbac-jpa/pom.xml
@@ -0,0 +1,70 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  ~ 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.
+  -->
+<project xmlns="http://maven.apache.org/POM/4.0.0";
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance";
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 
http://maven.apache.org/xsd/maven-4.0.0.xsd";>
+    <parent>
+        <artifactId>redback-rbac-providers</artifactId>
+        <groupId>org.apache.archiva.redback</groupId>
+        <version>2.5-SNAPSHOT</version>
+    </parent>
+    <modelVersion>4.0.0</modelVersion>
+
+    <artifactId>redback-rbac-jpa</artifactId>
+  <packaging>jar</packaging>
+  <name>Redback :: RBAC Provider :: JPA</name>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.apache.archiva.redback</groupId>
+            <artifactId>redback-rbac-model</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.springframework</groupId>
+            <artifactId>spring-context-support</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>javax.annotation</groupId>
+            <artifactId>jsr250-api</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.archiva.redback</groupId>
+            <artifactId>redback-rbac-tests</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.archiva.redback</groupId>
+            <artifactId>redback-common-jpa</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>org.apache.openjpa</groupId>
+            <artifactId>openjpa</artifactId>
+            <version>${openjpa.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>org.hsqldb</groupId>
+            <artifactId>hsqldb</artifactId>
+            <scope>test</scope>
+        </dependency>
+    </dependencies>
+
+
+</project>
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/archiva-redback-core/blob/62efc70b/redback-rbac/redback-rbac-providers/redback-rbac-jpa/src/main/java/org/apache/archiva/redback/rbac/jpa/JpaRbacManager.java
----------------------------------------------------------------------
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 0000000..ced9104
--- /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<Role> 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<JpaRole> q = em.createQuery("SELECT r FROM JpaRole  r WHERE 
r.name = :rolename", JpaRole.class);
+        q.setParameter("rolename",roleName);
+        return q.getSingleResult();
+    }
+
+    @Override
+    public List<Role> 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<Permission>());
+        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<Permission> 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<Permission> getAllPermissions() throws RbacManagerException {
+        final EntityManager em = getEm();
+        TypedQuery<JpaPermission> q = em.createQuery("SELECT p FROM 
JpaPermission p",JpaPermission.class);
+        return (List<Permission>)(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<Operation> 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<Resource> getAllResources() throws RbacManagerException {
+        final EntityManager em = getEm();
+        TypedQuery<JpaResource> q = em.createQuery("SELECT r FROM JpaResource 
r",JpaResource.class);
+        return (List<Resource>)(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<UserAssignment> getAllUserAssignments() throws 
RbacManagerException {
+        final EntityManager em = getEm();
+        Query q = em.createQuery("SELECT ua FROM JpaUserAssignment ua");
+        return q.getResultList();
+    }
+
+    @Override
+    public List<UserAssignment> getUserAssignmentsForRoles(Collection<String> 
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<JpaPermission> tqp = em.createQuery("SELECT r FROM 
JpaPermission r",JpaPermission.class);
+        for(JpaPermission p : tqp.getResultList()) {
+            p.setOperation(null);
+            p.setResource(null);
+        }
+        TypedQuery<JpaRole> tqr = em.createQuery("SELECT r FROM JpaRole 
r",JpaRole.class);
+        for (JpaRole r : tqr.getResultList()) {
+            r.getPermissions().clear();
+        }
+        em.flush();
+        TypedQuery<JpaOperation> tqo = em.createQuery("SELECT o FROM 
JpaOperation o",JpaOperation.class);
+        for(JpaOperation o : tqo.getResultList()) {
+            em.remove(o);
+        }
+        TypedQuery<JpaResource> 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<JpaUserAssignment> 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;
+    }
+
+
+}

http://git-wip-us.apache.org/repos/asf/archiva-redback-core/blob/62efc70b/redback-rbac/redback-rbac-providers/redback-rbac-jpa/src/main/java/org/apache/archiva/redback/rbac/jpa/model/JpaOperation.java
----------------------------------------------------------------------
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 0000000..66a41a1
--- /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();
+    }
+}

http://git-wip-us.apache.org/repos/asf/archiva-redback-core/blob/62efc70b/redback-rbac/redback-rbac-providers/redback-rbac-jpa/src/main/java/org/apache/archiva/redback/rbac/jpa/model/JpaPermission.java
----------------------------------------------------------------------
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 0000000..4ac6989
--- /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();
+    }
+}

http://git-wip-us.apache.org/repos/asf/archiva-redback-core/blob/62efc70b/redback-rbac/redback-rbac-providers/redback-rbac-jpa/src/main/java/org/apache/archiva/redback/rbac/jpa/model/JpaResource.java
----------------------------------------------------------------------
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 0000000..fc7ee77
--- /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();
+    }
+}

http://git-wip-us.apache.org/repos/asf/archiva-redback-core/blob/62efc70b/redback-rbac/redback-rbac-providers/redback-rbac-jpa/src/main/java/org/apache/archiva/redback/rbac/jpa/model/JpaRole.java
----------------------------------------------------------------------
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 0000000..1f8a62c
--- /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<JpaPermission> permissions = new ArrayList<JpaPermission>();
+
+    @ElementCollection
+    @CollectionTable(
+            name="SECURITY_ROLE_CHILDROLE_MAP",
+            joinColumns = {
+                    @JoinColumn(name="NAME_OID",referencedColumnName = "NAME")
+            }
+    )
+    List<String> childRoleNames = new ArrayList<String>();
+
+
+
+    @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<String> getChildRoleNames() {
+        return childRoleNames;
+    }
+
+    @Override
+    public String getDescription() {
+        return description;
+    }
+
+    @Override
+    public String getName() {
+        return name;
+    }
+
+    @Override
+    public List<Permission> getPermissions() {
+        // Maybe better to create a new list?
+        return (List<Permission>)(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<String> 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<Permission> 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();
+    }
+}

http://git-wip-us.apache.org/repos/asf/archiva-redback-core/blob/62efc70b/redback-rbac/redback-rbac-providers/redback-rbac-jpa/src/main/java/org/apache/archiva/redback/rbac/jpa/model/JpaUserAssignment.java
----------------------------------------------------------------------
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 0000000..39e0b2d
--- /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 <marti...@apache.org> 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<String> roleNames = new ArrayList<String>();
+    @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<String> getRoleNames() {
+        return roleNames;
+    }
+
+    @Override
+    public void setRoleNames(List<String> 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();
+    }
+}

http://git-wip-us.apache.org/repos/asf/archiva-redback-core/blob/62efc70b/redback-rbac/redback-rbac-providers/redback-rbac-jpa/src/test/java/org/apache/archiva/redback/rbac/jpa/JpaRbacManagerTest.java
----------------------------------------------------------------------
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 0000000..3c5bc9f
--- /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 <a href="mailto:joa...@erdfelt.com";>Joakim Erdfelt</a>
+ */
+@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 );
+    }
+
+
+}

http://git-wip-us.apache.org/repos/asf/archiva-redback-core/blob/62efc70b/redback-rbac/redback-rbac-providers/redback-rbac-jpa/src/test/resources/spring-context.xml
----------------------------------------------------------------------
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 0000000..abee0b5
--- /dev/null
+++ 
b/redback-rbac/redback-rbac-providers/redback-rbac-jpa/src/test/resources/spring-context.xml
@@ -0,0 +1,40 @@
+<?xml version="1.0"?>
+
+<!--
+  ~ 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.
+  -->
+<beans xmlns="http://www.springframework.org/schema/beans";
+       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance";
+       xmlns:context="http://www.springframework.org/schema/context";
+       xsi:schemaLocation="http://www.springframework.org/schema/beans
+           http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
+           http://www.springframework.org/schema/context
+           
http://www.springframework.org/schema/context/spring-context-3.0.xsd";>
+
+  <context:component-scan base-package="org.apache.archiva.redback.rbac.jpa" />
+
+  <bean name="userConfiguration#default" 
class="org.apache.archiva.redback.configuration.DefaultUserConfiguration">
+    <property name="registry" ref="test-conf"/>
+  </bean>
+
+  <bean name="commons-configuration" 
class="org.apache.archiva.redback.components.registry.commons.CommonsConfigurationRegistry">
+  </bean>
+
+  <alias name="commons-configuration" alias="test-conf"/>
+
+</beans>
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/archiva-redback-core/blob/62efc70b/redback-rbac/redback-rbac-providers/redback-rbac-jpa/src/test/resources/test.properties
----------------------------------------------------------------------
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 0000000..df848c4
--- /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

http://git-wip-us.apache.org/repos/asf/archiva-redback-core/blob/62efc70b/redback-users/redback-users-providers/pom.xml
----------------------------------------------------------------------
diff --git a/redback-users/redback-users-providers/pom.xml 
b/redback-users/redback-users-providers/pom.xml
index 9ddab02..4a90648 100644
--- a/redback-users/redback-users-providers/pom.xml
+++ b/redback-users/redback-users-providers/pom.xml
@@ -39,5 +39,6 @@
     <module>redback-users-jdo</module>
     <module>redback-users-ldap</module>
     <module>redback-users-configurable</module>
+      <module>redback-users-jpa</module>
   </modules>
 </project>

http://git-wip-us.apache.org/repos/asf/archiva-redback-core/blob/62efc70b/redback-users/redback-users-providers/redback-users-jpa/pom.xml
----------------------------------------------------------------------
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 0000000..2606b71
--- /dev/null
+++ b/redback-users/redback-users-providers/redback-users-jpa/pom.xml
@@ -0,0 +1,68 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  ~ 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.
+  -->
+<project xmlns="http://maven.apache.org/POM/4.0.0";
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance";
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 
http://maven.apache.org/xsd/maven-4.0.0.xsd";>
+    <parent>
+        <artifactId>redback-users-providers</artifactId>
+        <groupId>org.apache.archiva.redback</groupId>
+        <version>2.5-SNAPSHOT</version>
+    </parent>
+    <modelVersion>4.0.0</modelVersion>
+
+    <artifactId>redback-users-jpa</artifactId>
+    <packaging>jar</packaging>
+    <name>Redback :: Users Provider :: JPA</name>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.springframework</groupId>
+            <artifactId>spring-context-support</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>javax.annotation</groupId>
+            <artifactId>jsr250-api</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.archiva.redback</groupId>
+            <artifactId>redback-policy</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.archiva.redback</groupId>
+            <artifactId>redback-common-jpa</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.openjpa</groupId>
+            <artifactId>openjpa</artifactId>
+            <version>${openjpa.version}</version>
+        </dependency>
+
+        <dependency>
+            <groupId>org.hsqldb</groupId>
+            <artifactId>hsqldb</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.archiva.redback</groupId>
+            <artifactId>redback-users-tests</artifactId>
+            <scope>test</scope>
+        </dependency>
+    </dependencies>
+</project>
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/archiva-redback-core/blob/62efc70b/redback-users/redback-users-providers/redback-users-jpa/src/main/java/org/apache/archiva/redback/users/jpa/JpaUserManager.java
----------------------------------------------------------------------
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 0000000..c4c83f6
--- /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<User> getUsers() throws UserManagerException {
+        final EntityManager em = getEm();
+        Query q= em.createQuery("SELECT x from JpaUser x");
+        return q.getResultList();
+    }
+
+    @Override
+    public List<User> 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 <null>");
+        }
+        final EntityManager em = getEm();
+        TypedQuery<JpaUser> 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<User> findUsersByUsernameKey(String usernameKey, boolean 
orderAscending) throws UserManagerException {
+        return findUsers("username",usernameKey,"username",orderAscending);
+    }
+
+    @Override
+    public List<User> findUsersByFullNameKey(String fullNameKey, boolean 
orderAscending) throws UserManagerException {
+        return findUsers("fullName",fullNameKey,"username",orderAscending);
+    }
+
+    @Override
+    public List<User> findUsersByEmailKey(String emailKey, boolean 
orderAscending) throws UserManagerException {
+        return findUsers("email",emailKey,"username", orderAscending);
+    }
+
+    @Override
+    public List<User> 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<User> 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<User> 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<User> 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;
+    }
+
+
+
+
+}

http://git-wip-us.apache.org/repos/asf/archiva-redback-core/blob/62efc70b/redback-users/redback-users-providers/redback-users-jpa/src/main/java/org/apache/archiva/redback/users/jpa/JpaUserQuery.java
----------------------------------------------------------------------
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 0000000..d48a59c
--- /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;
+    }
+}

http://git-wip-us.apache.org/repos/asf/archiva-redback-core/blob/62efc70b/redback-users/redback-users-providers/redback-users-jpa/src/main/java/org/apache/archiva/redback/users/jpa/model/JpaUser.java
----------------------------------------------------------------------
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 0000000..04f441e
--- /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<String> previousEncodedPasswords = new ArrayList<String>();
+    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<String> getPreviousEncodedPasswords() {
+        return previousEncodedPasswords;
+    }
+
+    @Override
+    public void setPreviousEncodedPasswords(List<String> 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;
+    }
+}

Reply via email to