diff --git a/ccm-core/src/main/java/org/libreccm/core/Permission.java b/ccm-core/src/main/java/org/libreccm/core/Permission.java index c0cd4171d..feeeecb10 100644 --- a/ccm-core/src/main/java/org/libreccm/core/Permission.java +++ b/ccm-core/src/main/java/org/libreccm/core/Permission.java @@ -31,6 +31,8 @@ import javax.persistence.GenerationType; import javax.persistence.Id; import javax.persistence.JoinColumn; import javax.persistence.ManyToOne; +import javax.persistence.NamedQueries; +import javax.persistence.NamedQuery; import javax.persistence.OneToOne; import javax.persistence.Table; import javax.persistence.Temporal; @@ -44,6 +46,18 @@ import javax.xml.bind.annotation.XmlRootElement; */ @Entity @Table(name = "permissions") +@NamedQueries({ + @NamedQuery(name = "findPermissionsForSubject", + query = "SELECT p FROM Permission p WHERE p.grantee = :subject"), + @NamedQuery(name = "findPermissionsForUser", + query = "SELECT p FROM Permission p " + + "WHERE p.grantee = :user " + + " OR p.grantee IN (SELECT g " + + " FROM Group g JOIN g.members m" + + " WHERE m.user = :user)"), + @NamedQuery(name = "findPermissionsForCcmObject", + query = "SELECT p FROM Permission p WHERE p.object = :object") +}) //Can't reduce complexity yet @SuppressWarnings({"PMD.CyclomaticComplexity", "PMD.StdCyclomaticComplexity", @@ -150,7 +164,7 @@ public class Permission implements Serializable { public int hashCode() { int hash = 3; hash - = 31 * hash + (int) (permissionId ^ (permissionId >>> 32)); + = 31 * hash + (int) (permissionId ^ (permissionId >>> 32)); hash = 31 * hash + Objects.hashCode(grantee); hash = 31 * hash + Objects.hashCode(grantedPrivilege); hash = 31 * hash + Objects.hashCode(object); @@ -207,14 +221,14 @@ public class Permission implements Serializable { @Override public String toString() { return String.format("%s{ " - + "permissionId = %d, " - + "grantee = %s, " - + "grantedPrivilege = %s, " - + "object = %s, " - + "creationUser = %s," - + "creationDate = %tF %Jens Pelzetter + */ +@RequestScoped +public class PermissionManager { + + /** + * Creates a new permission granting the provided {@code privilege} on the + * provided {@code object} to the provided {@code subject}. If the + * permission is already granted to the provided {@code subject} this method + * does nothing. + * + * @param privilege The privilege to grant. + * @param object The object on which the privilege is granted. + * @param subject The subject to grant the privilege to. + */ + public void grantPermission(final Privilege privilege, + final CcmObject object, + final Subject subject) { + throw new UnsupportedOperationException(); + } + + /** + * Removes the permission granting the provided {@code privilege} on the + * provided {@code object} to the provided {@code subject}. If there is not + * permission granting the provided privilege on the provided {@code object} + * to the provided {@code subject} this method does nothing. + * + * @param privilege The privilege to revoke + * @param object The object on which the privilege is revoked. + * @param subject The subject to revoke the privilege from. + */ + public void revokePermission(final Privilege privilege, + final CcmObject object, + final Subject subject) { + throw new UnsupportedOperationException(); + } + + /** + * Checks if the the provided {@code subject} has a permission granting the + * provided {@code privilege} on the provided {@code object}. + * + * @param privilege The privilege to check. + * @param object The object on which the privilege is granted. + * @param subject The subject to which the privilege is granted. + * + * @return {@code true} of the subject has a permission granting + * {@code privilege} on {@code object}, either explicit or implicit. + */ + public boolean isPermitted(final Privilege privilege, + final CcmObject object, + final Subject subject) { + throw new UnsupportedOperationException(); + } + + /** + * Checks if the the provided {@code subject} has a permission granting the + * provided {@code privilege} on the provided {@code object}. + * + * @param privilege The privilege to check. + * @param object The object on which the privilege is granted. + * @param subject The subject to which the privilege is granted. + * + * @throws UnauthorizedAcccessException If there is no permission granting + * {@code privilege} on {@code object} + * to {@code subject} + * + */ + public void checkPermission(final Privilege privilege, + final CcmObject object, + final Subject subject) + throws UnauthorizedAcccessException { + throw new UnsupportedOperationException(); + } + +} diff --git a/ccm-core/src/main/java/org/libreccm/core/PermissionRepository.java b/ccm-core/src/main/java/org/libreccm/core/PermissionRepository.java new file mode 100644 index 000000000..a67d72313 --- /dev/null +++ b/ccm-core/src/main/java/org/libreccm/core/PermissionRepository.java @@ -0,0 +1,92 @@ +/* + * Copyright (C) 2015 LibreCCM Foundation. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301 USA + */ +package org.libreccm.core; + +import java.util.List; + +import javax.enterprise.context.RequestScoped; +import javax.inject.Inject; +import javax.persistence.EntityManager; +import javax.persistence.TypedQuery; + +/** + * + * @author Jens Pelzetter + */ +@RequestScoped +public class PermissionRepository + extends AbstractEntityRepository { + + @Inject + private EntityManager entityManager; + + @Override + public Class getEntityClass() { + return Permission.class; + } + + @Override + public boolean isNew(final Permission entity) { + if (entity == null) { + throw new UnsupportedOperationException( + "Entity to save can't be null"); + } + return entity.getPermissionId() == 0; + } + + public List findPermissionsForSubject(final Subject subject) { + if (subject == null) { + throw new IllegalArgumentException( + "Illegal value 'null' provided for parameter subject."); + } + + final TypedQuery query = entityManager.createNamedQuery( + "findPermissionsForSubject", Permission.class); + query.setParameter("subject", subject); + + return query.getResultList(); + } + + public List findPermissionsForUser(final User user) { + if (user == null) { + throw new IllegalArgumentException( + "Illegal value 'null' provided for parameter user"); + } + + final TypedQuery query = entityManager.createNamedQuery( + "findPermissionsForUser", Permission.class); + query.setParameter("user", user); + + return query.getResultList(); + } + + public List findPermissionsForCcmObject(final CcmObject object) { + if (object == null) { + throw new IllegalArgumentException( + "Illegal value 'null' provided for parameter object."); + } + + final TypedQuery query = entityManager.createNamedQuery( + "findPermissionsForCcmObject", Permission.class); + query.setParameter("object", object); + + return query.getResultList(); + } + +} diff --git a/ccm-core/src/main/java/org/libreccm/core/Subject.java b/ccm-core/src/main/java/org/libreccm/core/Subject.java index 172a60571..6caac9951 100644 --- a/ccm-core/src/main/java/org/libreccm/core/Subject.java +++ b/ccm-core/src/main/java/org/libreccm/core/Subject.java @@ -25,22 +25,16 @@ import java.security.acl.Group; import java.util.ArrayList; import java.util.Collections; import java.util.List; -import java.util.Objects; -import javax.persistence.CollectionTable; import javax.persistence.Column; -import javax.persistence.ElementCollection; import javax.persistence.Entity; -import javax.persistence.FetchType; import javax.persistence.GeneratedValue; import javax.persistence.GenerationType; import javax.persistence.Id; import javax.persistence.Inheritance; import javax.persistence.InheritanceType; -import javax.persistence.JoinColumn; import javax.persistence.OneToMany; import javax.persistence.Table; -import javax.validation.constraints.Size; import javax.xml.bind.annotation.XmlElement; import javax.xml.bind.annotation.XmlElementWrapper; import javax.xml.bind.annotation.XmlRootElement; diff --git a/ccm-core/src/main/java/org/libreccm/core/UnauthorizedAcccessException.java b/ccm-core/src/main/java/org/libreccm/core/UnauthorizedAcccessException.java new file mode 100644 index 000000000..0d15f6409 --- /dev/null +++ b/ccm-core/src/main/java/org/libreccm/core/UnauthorizedAcccessException.java @@ -0,0 +1,69 @@ +/* + * Copyright (C) 2015 LibreCCM Foundation. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301 USA + */ + +package org.libreccm.core; + +/** + * Thrown by the {@link PermissionManager} if a provided subject does not have + * a specific permission. + * + * @author Jens Pelzetter + */ +public class UnauthorizedAcccessException extends Exception { + + private static final long serialVersionUID = 1L; + + /** + * Creates a new instance of UnauthorizedAcccessException without detail message. + */ + public UnauthorizedAcccessException() { + super(); + } + + + /** + * Constructs an instance of UnauthorizedAcccessException with the specified detail message. + * + * @param msg The detail message. + */ + public UnauthorizedAcccessException(final String msg) { + super(msg); + } + + /** + * Constructs an instance of UnauthorizedAcccessException which wraps the + * specified exception. + * + * @param exception The exception to wrap. + */ + public UnauthorizedAcccessException(final Exception exception) { + super(exception); + } + + /** + * Constructs an instance of UnauthorizedAcccessException with the specified message which also wraps the + * specified exception. + * + * @param msg The detail message. + * @param exception The exception to wrap. + */ + public UnauthorizedAcccessException(final String msg, final Exception exception) { + super(msg, exception); + } +} diff --git a/ccm-core/src/test/java/org/libreccm/core/DatasetsTest.java b/ccm-core/src/test/java/org/libreccm/core/DatasetsTest.java index b990bc0d4..b93488033 100644 --- a/ccm-core/src/test/java/org/libreccm/core/DatasetsTest.java +++ b/ccm-core/src/test/java/org/libreccm/core/DatasetsTest.java @@ -54,6 +54,7 @@ public class DatasetsTest extends DatasetsVerifier { "/datasets/org/libreccm/core/GroupRepositoryTest/after-delete.json", "/datasets/org/libreccm/core/GroupRepositoryTest/after-save-changed.json", "/datasets/org/libreccm/core/GroupRepositoryTest/after-save-new.json", + "/datasets/org/libreccm/core/PermissionRepositoryTest/data.json", "/datasets/org/libreccm/core/RoleRepositoryTest/data.json", "/datasets/org/libreccm/core/RoleRepositoryTest/after-delete.json", "/datasets/org/libreccm/core/RoleRepositoryTest/after-save-changed.json", diff --git a/ccm-core/src/test/java/org/libreccm/core/UserRepositoryTest.java b/ccm-core/src/test/java/org/libreccm/core/UserRepositoryTest.java index 63e2185b1..71178b48d 100644 --- a/ccm-core/src/test/java/org/libreccm/core/UserRepositoryTest.java +++ b/ccm-core/src/test/java/org/libreccm/core/UserRepositoryTest.java @@ -131,7 +131,6 @@ public class UserRepositoryTest { "META-INF/persistence.xml") .addAsWebInfResource("test-web.xml", "WEB-INF/web.xml") .addAsWebInfResource(EmptyAsset.INSTANCE, "WEB-INF/beans.xml"); - } @Test diff --git a/ccm-core/src/test/resources/datasets/org/libreccm/core/PermissionRepositoryTest/data.json b/ccm-core/src/test/resources/datasets/org/libreccm/core/PermissionRepositoryTest/data.json new file mode 100644 index 000000000..7a1c0348a --- /dev/null +++ b/ccm-core/src/test/resources/datasets/org/libreccm/core/PermissionRepositoryTest/data.json @@ -0,0 +1,165 @@ +{ + "subjects": + [ + { + "subject_id": -10 + }, + { + "subject_id": -20 + }, + { + "subject_id": -30 + }, + { + "subject_id": -40 + }, + { + "subject_id": -50 + } + ], + "ccm_users": + [ + { + "banned": false, + "hash_algorithm": "SHA-512", + "family_name": "Doe", + "given_name": "John", + "password": "C+o2w6mp+eLrbluMEgKMVSdP50A9BMethXN8R3yihtkbzt7WfWsde2nmq/t5gq6im3J8i3jw4Y3YrKHou8JQ2A==", + "password_reset_required": false, + "salt": "Fu8FPgqAal4GZp1hDjkOB+t6ITRCcO7HBoN5Xqf29UnVj5NUdUFZRTyKYMBEx6JmZGmHcMDG9OGVCKcEM9oyScSRreJs4B51wM44NM6KeRwbCf+VhBn14DkBrl40ygraNf+AJacKpMyCpFI0O/Am7mMDWL4flskBsylkxaQn3vKfzgN5MVG2szW//I6Q6YEH9AuL8LauS6fKaVynMzzu3xzD8Hjqvvlnzym898eom2lqScPfg5g4e8Ww13HCHAYe6twupAW/BjUNax5HSioEisZN/P1UGrde8uFEj+hbbavrWYZuilPuEu25+/98jyXx6542agqrWN8j0SFYcIyOgA==", + "screen_name": "jdoe", + "subject_id": -10 + }, + { + "banned": false, + "hash_algorithm": "SHA-512", + "family_name": "Mustermann", + "given_name": "Max", + "password": "1c9626af429a6291766d15cbfb38689bd8d49450520765973de70aecaf644b7d4fda711266ba9ec8fb6df30c8ab391d40330829aa85adf371bcde6b4c9bc01e6", + "password_reset_required": false, + "salt": "fjiajhigafgapoa", + "screen_name": "mmuster", + "subject_id": -50 + } + ], + "ccm_groups": + [ + { + "name": "admins", + "subject_id": -20 + }, + { + "name": "users", + "subject_id": -30 + }, + { + "name": "authors", + "subject_id": -40 + } + ], + "user_email_addresses": + [ + { + "user_id": -10, + "email_address": "john.doe@example.com", + "bouncing": false, + "verified": true + }, + { + "user_id": -50, + "email_address": "max.mustermann@example.com", + "bouncing": false, + "verified": true + } + ], + "group_memberships": + [ + { + "membership_id": -10, + "group_subject_id": -30, + "user_subject_id": -10 + }, + { + "membership_id": -20, + "group_subject_id": -40, + "user_subject_id": -10 + }, + { + "membership_id": -30, + "group_subject_id": -20, + "user_subject_id": -50 + } + ], + "ccm_privileges": + [ + { + "privilege_id": -10, + "privilege": "admin" + }, + { + "privilege_id": -20, + "privilege": "read" + }, + { + "privilege_id": -30, + "privilege": "write" + } + ], + "ccm_objects": + [ + { + "object_id": -10, + "display_name": "Test Object 1" + }, + { + "object_id": -20, + "display_name": "Test Object 2" + }, + { + "object_id": -30, + "display_name": "Test Object 3" + }, + { + "object_id": -40, + "display_name": "Test Object 4" + } + ], + "permissions": + [ + { + "permission_id": -10, + "creation_date": "2015-07-29 14:32:46", + "granted_privilege_id": -10, + "grantee_id": -20 + }, + { + "permission_id": -20, + "creation_date": "2015-07-29 14:34:30", + "granted_privilege_id": -20, + "grantee_id": -30, + "object_id": -10 + }, + { + "permission_id": -30, + "creation_date": "2015-07-29 14:36:05", + "granted_privilege_id": -30, + "grantee_id": -40, + "object_id": -10 + }, + { + "permission_id": -35, + "creation_date": "2015-07-29 14:57:55", + "granted_privilege_id": -20, + "grantee_id": -40, + "object_id": -10 + }, + { + "permission_id": -40, + "creation_date": "2015-07-29 14:37:22", + "granted_privilege_id": -20, + "grantee_id": -10, + "object_id": -20 + } + ] + +} \ No newline at end of file diff --git a/ccm-core/src/test/resources/scripts/h2-cleanup.sql b/ccm-core/src/test/resources/scripts/h2-cleanup.sql index adbf243eb..96bdacf02 100644 --- a/ccm-core/src/test/resources/scripts/h2-cleanup.sql +++ b/ccm-core/src/test/resources/scripts/h2-cleanup.sql @@ -1,3 +1,7 @@ +DELETE FROM permissions; + +DELETE FROM ccm_privileges; + DELETE FROM ccm_objects; DELETE FROM ccm_roles; diff --git a/ccm-core/src/test/resources/scripts/mysql-cleanup.sql b/ccm-core/src/test/resources/scripts/mysql-cleanup.sql index adbf243eb..96bdacf02 100644 --- a/ccm-core/src/test/resources/scripts/mysql-cleanup.sql +++ b/ccm-core/src/test/resources/scripts/mysql-cleanup.sql @@ -1,3 +1,7 @@ +DELETE FROM permissions; + +DELETE FROM ccm_privileges; + DELETE FROM ccm_objects; DELETE FROM ccm_roles; diff --git a/ccm-core/src/test/resources/scripts/pgsql-cleanup.sql b/ccm-core/src/test/resources/scripts/pgsql-cleanup.sql index adbf243eb..96bdacf02 100644 --- a/ccm-core/src/test/resources/scripts/pgsql-cleanup.sql +++ b/ccm-core/src/test/resources/scripts/pgsql-cleanup.sql @@ -1,3 +1,7 @@ +DELETE FROM permissions; + +DELETE FROM ccm_privileges; + DELETE FROM ccm_objects; DELETE FROM ccm_roles;