diff --git a/ccm-core/src/main/java/org/libreccm/api/IdentifierParser.java b/ccm-core/src/main/java/org/libreccm/api/IdentifierParser.java index 3bf741b4a..5919eea24 100644 --- a/ccm-core/src/main/java/org/libreccm/api/IdentifierParser.java +++ b/ccm-core/src/main/java/org/libreccm/api/IdentifierParser.java @@ -29,7 +29,7 @@ import javax.enterprise.context.Dependent; @Dependent public class IdentifierParser { - public Identifier extractIdentifier(final String identifierParam) { + public Identifier parseIdentifier(final String identifierParam) { Objects.requireNonNull(identifierParam, "identifier param is null."); if (identifierParam.startsWith(ApiConstants.IDENTIFIER_PREFIX_ID)) { diff --git a/ccm-core/src/main/java/org/libreccm/api/admin/categorization/CategoriesApi.java b/ccm-core/src/main/java/org/libreccm/api/admin/categorization/CategoriesApi.java index a0b88cb8b..15a0429fa 100644 --- a/ccm-core/src/main/java/org/libreccm/api/admin/categorization/CategoriesApi.java +++ b/ccm-core/src/main/java/org/libreccm/api/admin/categorization/CategoriesApi.java @@ -18,6 +18,7 @@ */ package org.libreccm.api.admin.categorization; +import org.libreccm.api.admin.categorization.dto.CategorizationData; import org.libreccm.api.admin.categorization.dto.CategoryData; import org.libreccm.api.dto.ListView; import org.libreccm.core.CoreConstants; @@ -28,12 +29,14 @@ import javax.enterprise.context.RequestScoped; import javax.transaction.Transactional; import javax.ws.rs.Consumes; import javax.ws.rs.DELETE; +import javax.ws.rs.DefaultValue; import javax.ws.rs.GET; import javax.ws.rs.POST; import javax.ws.rs.PUT; import javax.ws.rs.Path; import javax.ws.rs.PathParam; import javax.ws.rs.Produces; +import javax.ws.rs.QueryParam; import javax.ws.rs.core.MediaType; /** @@ -190,13 +193,56 @@ public class CategoriesApi { throw new UnsupportedOperationException(); } + @GET + @Path("/{domainIdentifier}/{path:^[\\w\\-/]+$}/subcategories") + @Produces(MediaType.APPLICATION_JSON) + @AuthorizationRequired + @RequiresPrivilege(CoreConstants.PRIVILEGE_ADMIN) + @Transactional(Transactional.TxType.REQUIRED) + public ListView getSubCategories( + @PathParam("domainIdentifier") final String domainIdentifierParam, + @PathParam("path") final String categoryPathTokens, + @QueryParam("limit") @DefaultValue("20") final int limit, + @QueryParam("offset") @DefaultValue("20") final int offset + ) { + throw new UnsupportedOperationException(); + } + + @GET + @Path("/ID-{categoryId}/subcategories") + @Produces(MediaType.APPLICATION_JSON) + @AuthorizationRequired + @RequiresPrivilege(CoreConstants.PRIVILEGE_ADMIN) + @Transactional(Transactional.TxType.REQUIRED) + public ListView getSubCategories( + @PathParam("categoryId") final long categoryId, + @QueryParam("limit") @DefaultValue("20") final int limit, + @QueryParam("offset") @DefaultValue("20") final int offset + ) { + throw new UnsupportedOperationException(); + } + + @GET + @Path("/UUID-{categoryId}/subcategories") + @Produces(MediaType.APPLICATION_JSON) + @AuthorizationRequired + @RequiresPrivilege(CoreConstants.PRIVILEGE_ADMIN) + @Transactional(Transactional.TxType.REQUIRED) + public ListView getSubCategories( + @PathParam("categoryUuid") final String uuid, + @QueryParam("limit") @DefaultValue("20") final int limit, + @QueryParam("offset") @DefaultValue("20") final int offset + ) { + throw new UnsupportedOperationException(); + } + @GET @Path("/{domainIdentifier}/{path:^[\\w\\-/]+$}/objects") @Produces(MediaType.APPLICATION_JSON) @AuthorizationRequired @RequiresPrivilege(CoreConstants.PRIVILEGE_ADMIN) @Transactional(Transactional.TxType.REQUIRED) - public CategoryData getObjectsInCategory( + public ListView getObjectsInCategory( @PathParam("domainIdentifier") final String domainIdentifierParam, @PathParam("path") final String categoryPathTokens ) { @@ -209,7 +255,7 @@ public class CategoriesApi { @AuthorizationRequired @RequiresPrivilege(CoreConstants.PRIVILEGE_ADMIN) @Transactional(Transactional.TxType.REQUIRED) - public CategoryData getCategoryObjectsInCategory( + public ListView getCategoryObjectsInCategory( @PathParam("categoryId") final long categoryId ) { throw new UnsupportedOperationException(); @@ -221,7 +267,7 @@ public class CategoriesApi { @AuthorizationRequired @RequiresPrivilege(CoreConstants.PRIVILEGE_ADMIN) @Transactional(Transactional.TxType.REQUIRED) - public CategoryData getObjectsInCategory( + public ListView getObjectsInCategory( @PathParam("categoryId") final String categoryUuid ) { throw new UnsupportedOperationException(); diff --git a/ccm-core/src/main/java/org/libreccm/api/admin/categorization/CategorizationApiRepository.java b/ccm-core/src/main/java/org/libreccm/api/admin/categorization/CategorizationApiRepository.java new file mode 100644 index 000000000..bdfdabc74 --- /dev/null +++ b/ccm-core/src/main/java/org/libreccm/api/admin/categorization/CategorizationApiRepository.java @@ -0,0 +1,92 @@ +/* + * Copyright (C) 2020 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.api.admin.categorization; + +import org.libreccm.api.Identifier; +import org.libreccm.api.IdentifierParser; +import org.libreccm.categorization.Domain; +import org.libreccm.categorization.DomainRepository; + +import javax.enterprise.context.Dependent; +import javax.inject.Inject; +import javax.ws.rs.WebApplicationException; +import javax.ws.rs.core.Response; + +/** + * Shared repository for the classes of the categorization RESTful api. + * + * All methods in this class will throw a {@link WebApplicationException} with a + * 404 status code if the requested entity is not found. + * + * @author Jens Pelzetter + */ +@Dependent +public class CategorizationApiRepository { + + @Inject + private DomainRepository domainRepository; + + @Inject + private IdentifierParser identifierParser; + + public Domain findDomain(final String domainIdentifier) { + final Identifier identifier = identifierParser.parseIdentifier( + domainIdentifier + ); + switch (identifier.getType()) { + case ID: + return domainRepository + .findById(Long.parseLong(identifier.getIdentifier())) + .orElseThrow( + () -> new WebApplicationException( + String.format( + "No Domain with ID %s found.", + identifier.getIdentifier() + ), + Response.Status.NOT_FOUND + ) + ); + case UUID: + return domainRepository + .findByUuid(identifier.getIdentifier()) + .orElseThrow( + () -> new WebApplicationException( + String.format( + "No Domain with UUID %s found.", + identifier.getIdentifier() + ), + Response.Status.NOT_FOUND + ) + ); + default: + return domainRepository + .findByDomainKey(identifier.getIdentifier()) + .orElseThrow( + () -> new WebApplicationException( + String.format( + "No Domain with domain key %s found.", + identifier.getIdentifier() + ), + Response.Status.NOT_FOUND + ) + ); + } + } + +} diff --git a/ccm-core/src/main/java/org/libreccm/api/admin/categorization/dto/CategoryData.java b/ccm-core/src/main/java/org/libreccm/api/admin/categorization/dto/CategoryData.java index a943a9217..00bbf64f2 100644 --- a/ccm-core/src/main/java/org/libreccm/api/admin/categorization/dto/CategoryData.java +++ b/ccm-core/src/main/java/org/libreccm/api/admin/categorization/dto/CategoryData.java @@ -21,10 +21,7 @@ package org.libreccm.api.admin.categorization.dto; import org.libreccm.categorization.Category; import org.libreccm.l10n.LocalizedString; -import java.util.ArrayList; -import java.util.List; import java.util.Objects; -import java.util.stream.Collectors; /** * @@ -50,17 +47,12 @@ public class CategoryData { private boolean abstractCategory; - private List objects; - - private List subCategories; - private AssociatedCategoryData parentCategory; private long categoryOrder; public CategoryData() { - objects = new ArrayList<>(); - subCategories = new ArrayList<>(); + // Nothing } public CategoryData(final Category fromCategory) { @@ -77,16 +69,6 @@ public class CategoryData { enabled = fromCategory.isEnabled(); visible = fromCategory.isVisible(); abstractCategory = fromCategory.isAbstractCategory(); - objects = fromCategory - .getObjects() - .stream() - .map(CategorizationData::new) - .collect(Collectors.toList()); - subCategories = fromCategory - .getSubCategories() - .stream() - .map(AssociatedCategoryData::new) - .collect(Collectors.toList()); parentCategory = new AssociatedCategoryData( fromCategory.getParentCategory() ); @@ -164,23 +146,6 @@ public class CategoryData { this.abstractCategory = abstractCategory; } - public List getObjects() { - return new ArrayList<>(objects); - } - - public void setObjects(final List objects) { - this.objects = new ArrayList<>(objects); - } - - public List getSubCategories() { - return new ArrayList<>(subCategories); - } - - public void setSubCategories( - final List subCategories) { - this.subCategories = new ArrayList<>(subCategories); - } - public AssociatedCategoryData getParentCategory() { return parentCategory; } diff --git a/ccm-core/src/main/java/org/libreccm/api/admin/security/GroupsApi.java b/ccm-core/src/main/java/org/libreccm/api/admin/security/GroupsApi.java index bb1e34ef5..e15fef66b 100644 --- a/ccm-core/src/main/java/org/libreccm/api/admin/security/GroupsApi.java +++ b/ccm-core/src/main/java/org/libreccm/api/admin/security/GroupsApi.java @@ -203,14 +203,23 @@ public class GroupsApi { @AuthorizationRequired @RequiresPrivilege(CoreConstants.PRIVILEGE_ADMIN) @Transactional(Transactional.TxType.REQUIRED) - public List getMembers( - @PathParam("groupIdentifier") final String groupIdentifier + public ListView getMembers( + @PathParam("groupIdentifier") final String groupIdentifier, + @QueryParam("limit") @DefaultValue("20") final int limit, + @QueryParam("offset") @DefaultValue("0") final int offset ) { - return repository.findGroup(groupIdentifier) - .getMemberships() + final Group group = repository.findGroup(groupIdentifier); + + return new ListView<>( + groupRepository + .findGroupMemberships(group, limit, offset) .stream() .map(GroupUserMembership::new) - .collect(Collectors.toList()); + .collect(Collectors.toList()), + groupRepository.countGroupMemberships(group), + limit, + offset + ); } @PUT diff --git a/ccm-core/src/main/java/org/libreccm/api/admin/security/RolesApi.java b/ccm-core/src/main/java/org/libreccm/api/admin/security/RolesApi.java index 394ae3801..9fbf00677 100644 --- a/ccm-core/src/main/java/org/libreccm/api/admin/security/RolesApi.java +++ b/ccm-core/src/main/java/org/libreccm/api/admin/security/RolesApi.java @@ -48,6 +48,8 @@ import java.net.URI; import java.util.stream.Collectors; import javax.ws.rs.WebApplicationException; +import javax.ws.rs.core.Context; +import javax.ws.rs.core.UriInfo; /** * @@ -56,31 +58,34 @@ import javax.ws.rs.WebApplicationException; @RequestScoped @Path("/roles") public class RolesApi { - + + @Context + private UriInfo uriInfo; + @Inject private CcmObjectRepository ccmObjectRepository; - + @Inject private IdentifierParser identifierExtractor; - + @Inject private PartyRepository partyRepository; - + @Inject private PermissionManager permissionManager; - + @Inject private PermissionRepository permissionRepository; - + @Inject private SecurityApiRepository repository; - + @Inject private RoleManager roleManager; - + @Inject private RoleRepository roleRepository; - + @GET @Path("/") @Produces(MediaType.APPLICATION_JSON) @@ -93,7 +98,7 @@ public class RolesApi { ) { final long count = roleRepository.countAll(); final List roles = roleRepository.findAll(limit, offset); - + return new ListView<>( roles.stream().map(RoleData::new).collect(Collectors.toList()), count, @@ -101,7 +106,7 @@ public class RolesApi { offset ); } - + @GET @Path("/{roleIdentifier}") @Produces(MediaType.APPLICATION_JSON) @@ -113,7 +118,7 @@ public class RolesApi { ) { return new RoleData(repository.findRole(roleIdentifier)); } - + @POST @Path("/") @Consumes(MediaType.APPLICATION_JSON) @@ -124,14 +129,14 @@ public class RolesApi { final Role role = new Role(); role.setName(roleData.getName()); role.setDescription(roleData.getDescription()); - + roleRepository.save(role); - + return Response.created( URI.create(String.format("/api/admin/roles/%s", role.getName())) ).build(); } - + @PUT @Path("/{roleIdentifier}") @Consumes(MediaType.APPLICATION_JSON) @@ -143,20 +148,20 @@ public class RolesApi { final RoleData roleData ) { final Role role = repository.findRole(roleIdentifier); - + if (roleData != null && roleData.getName() != null && !roleData.getName().equals(role.getName())) { role.setName(roleData.getName()); } - + roleRepository.save(role); - + return Response .ok(String.format("Role %s updated succesfully.", roleIdentifier)) .build(); } - + @DELETE @Path("/{roleIdentifier}") @Consumes(MediaType.APPLICATION_JSON) @@ -172,23 +177,32 @@ public class RolesApi { .ok(String.format("Role %s deleted successfully.", roleIdentifier)) .build(); } - + @GET @Path("/{roleIdentifier}/members") @Produces(MediaType.APPLICATION_JSON) @AuthorizationRequired @RequiresPrivilege(CoreConstants.PRIVILEGE_ADMIN) @Transactional(Transactional.TxType.REQUIRED) - public List getMembers( - @PathParam("roleIdentifier") final String roleIdentifier + public ListView getMembers( + @PathParam("roleIdentifier") final String roleIdentifier, + @QueryParam("limit") @DefaultValue("20") final int limit, + @QueryParam("offset") @DefaultValue("0") final int offset ) { - return repository.findRole(roleIdentifier) - .getMemberships() - .stream() - .map(RolePartyMembership::new) - .collect(Collectors.toList()); + final Role role = repository.findRole(roleIdentifier); + final long count = roleRepository.countMembershipsByRole(role); + return new ListView<>( + roleRepository + .findMembershipsByRole(role, limit, offset) + .stream() + .map(RolePartyMembership::new) + .collect(Collectors.toList()), + count, + limit, + offset + ); } - + @PUT @Path("/{roleIdentifier}/members/{partyIdentifier}") @AuthorizationRequired @@ -200,9 +214,9 @@ public class RolesApi { ) { final Role role = repository.findRole(groupIdentifier); final Party party = repository.findParty(partyIdentifier); - + roleManager.assignRoleToParty(role, party); - + return Response .ok( String.format( @@ -212,7 +226,7 @@ public class RolesApi { ) ).build(); } - + @DELETE @Path("/{roleIdentifier}/members/{partyIdentifier}") @AuthorizationRequired @@ -224,9 +238,9 @@ public class RolesApi { ) { final Role role = repository.findRole(groupIdentifier); final Party party = repository.findParty(partyIdentifier); - + roleManager.removeRoleFromParty(role, party); - + return Response .ok( String.format( @@ -237,23 +251,31 @@ public class RolesApi { ) .build(); } - + @GET @Path("/{roleIdentifier}/permissions") @Produces(MediaType.APPLICATION_JSON) @AuthorizationRequired @RequiresPrivilege(CoreConstants.PRIVILEGE_ADMIN) @Transactional(Transactional.TxType.REQUIRED) - public List getPermissions( - @PathParam("roleIdentifier") final String roleIdentifier + public ListView getPermissions( + @PathParam("roleIdentifier") final String roleIdentifier, + @QueryParam("limit") @DefaultValue("20") final int limit, + @QueryParam("offset") @DefaultValue("0") final int offset ) { - return repository.findRole(roleIdentifier) - .getPermissions() - .stream() - .map(RolePermission::new) - .collect(Collectors.toList()); + final Role role = repository.findRole(roleIdentifier); + return new ListView<>( + permissionRepository + .findPermissionsForRole(role, limit, offset) + .stream() + .map(RolePermission::new) + .collect(Collectors.toList()), + permissionRepository.countPermissionsForRole(role), + limit, + offset + ); } - + @POST @Path("/{roleIdentifier}/permissions") @Consumes(MediaType.APPLICATION_JSON) @@ -266,7 +288,7 @@ public class RolesApi { ) { final Role role = repository.findRole(roleIdentifier); final String privilege = permissionData.getGrantedPrivilege(); - + final Permission permission; if (permissionData.getObject() != null) { final CcmObject object = ccmObjectRepository @@ -317,7 +339,7 @@ public class RolesApi { ).build(); } else { permission = permissionManager.grantPrivilege(privilege, role); - + return Response.created( URI.create( String.format( @@ -330,7 +352,7 @@ public class RolesApi { } } } - + @DELETE @Path("/{roleIdentifier}/permissions/{permissionIdentifier}") @AuthorizationRequired @@ -343,10 +365,10 @@ public class RolesApi { final String permissionIdentifierParam ) { final Role role = repository.findRole(roleIdentifier); - + final Identifier permissionIdentifier = identifierExtractor - .extractIdentifier(roleIdentifier); - + .parseIdentifier(roleIdentifier); + final Permission permission; switch (permissionIdentifier.getType()) { case ID: @@ -375,18 +397,18 @@ public class RolesApi { Response.Status.NOT_FOUND ) ); - break; - + break; + default: return Response .status(Response.Status.BAD_REQUEST) .entity("Permissions can only be identified by ID or UUID.") .build(); } - + permissionRepository.delete(permission); return Response.ok().build(); } - + } diff --git a/ccm-core/src/main/java/org/libreccm/api/admin/security/SecurityApiRepository.java b/ccm-core/src/main/java/org/libreccm/api/admin/security/SecurityApiRepository.java index 210fbca62..bad101d54 100644 --- a/ccm-core/src/main/java/org/libreccm/api/admin/security/SecurityApiRepository.java +++ b/ccm-core/src/main/java/org/libreccm/api/admin/security/SecurityApiRepository.java @@ -63,7 +63,7 @@ class SecurityApiRepository { protected Group findGroup(final String groupIdentifier) { final Identifier identifier = identifierExtractor - .extractIdentifier(groupIdentifier); + .parseIdentifier(groupIdentifier); switch (identifier.getType()) { case ID: @@ -107,7 +107,7 @@ class SecurityApiRepository { protected Party findParty(final String partyIdentifier) { final Identifier identifier = identifierExtractor - .extractIdentifier(partyIdentifier); + .parseIdentifier(partyIdentifier); switch (identifier.getType()) { case ID: @@ -152,7 +152,7 @@ class SecurityApiRepository { protected Role findRole(final String roleIdentifier) { final Identifier identifier = identifierExtractor - .extractIdentifier(roleIdentifier); + .parseIdentifier(roleIdentifier); switch (identifier.getType()) { case ID: @@ -196,7 +196,7 @@ class SecurityApiRepository { protected User findUser(final String identifierParam) { final Identifier identifier = identifierExtractor - .extractIdentifier(identifierParam); + .parseIdentifier(identifierParam); switch (identifier.getType()) { case ID: diff --git a/ccm-core/src/main/java/org/libreccm/api/admin/security/dto/RoleData.java b/ccm-core/src/main/java/org/libreccm/api/admin/security/dto/RoleData.java index ed3132865..204001714 100644 --- a/ccm-core/src/main/java/org/libreccm/api/admin/security/dto/RoleData.java +++ b/ccm-core/src/main/java/org/libreccm/api/admin/security/dto/RoleData.java @@ -41,19 +41,19 @@ public class RoleData { private LocalizedString description; - private List memberships; +// private List memberships; private List permissions; - private List assignedTasks; +// private List assignedTasks; /** * Parameterless constructor for creating empty instances. */ public RoleData() { - memberships = new ArrayList<>(); +// membership = new ArrayList<>(); permissions = new ArrayList<>(); - assignedTasks = new ArrayList<>(); +// assignedTasks = new ArrayList<>(); } public RoleData(final Role role) { @@ -64,23 +64,23 @@ public class RoleData { name = role.getName(); description = role.getDescription(); - memberships = role - .getMemberships() - .stream() - .map(RolePartyMembership::new) - .collect(Collectors.toList()); - - permissions = role - .getPermissions() - .stream() - .map(RolePermission::new) - .collect(Collectors.toList()); - - assignedTasks = role - .getAssignedTasks() - .stream() - .map(RoleAssignedTask::new) - .collect(Collectors.toList()); +// memberships = role +// .getMemberships() +// .stream() +// .map(RolePartyMembership::new) +// .collect(Collectors.toList()); +// +// permissions = role +// .getPermissions() +// .stream() +// .map(RolePermission::new) +// .collect(Collectors.toList()); +// +// assignedTasks = role +// .getAssignedTasks() +// .stream() +// .map(RoleAssignedTask::new) +// .collect(Collectors.toList()); } public long getRoleId() { @@ -107,21 +107,21 @@ public class RoleData { this.name = name; } - public List getMemberships() { - return new ArrayList<>(memberships); - } - - public void setMemberships(final List memberships) { - this.memberships = new ArrayList<>(memberships); - } - - public List getAssignedTasks() { - return new ArrayList<>(assignedTasks); - } - - public void setAssignedTasks(final List assignedTasks) { - this.assignedTasks = new ArrayList<>(assignedTasks); - } +// public List getMemberships() { +// return new ArrayList<>(memberships); +// } +// +// public void setMemberships(final List memberships) { +// this.memberships = new ArrayList<>(memberships); +// } +// +// public List getAssignedTasks() { +// return new ArrayList<>(assignedTasks); +// } +// +// public void setAssignedTasks(final List assignedTasks) { +// this.assignedTasks = new ArrayList<>(assignedTasks); +// } public LocalizedString getDescription() { return description; diff --git a/ccm-core/src/main/java/org/libreccm/security/Group.java b/ccm-core/src/main/java/org/libreccm/security/Group.java index 242da61c7..38b853073 100644 --- a/ccm-core/src/main/java/org/libreccm/security/Group.java +++ b/ccm-core/src/main/java/org/libreccm/security/Group.java @@ -66,31 +66,38 @@ import javax.persistence.Table; @NamedQuery( name = "Group.findByName", query = "SELECT g FROM Group g WHERE g.name = :name " - + "ORDER BY g.name") - , + + "ORDER BY g.name"), @NamedQuery( name = "Group.searchByName", query = "SELECT g FROM Group g " + "WHERE LOWER(g.name) LIKE CONCAT(LOWER(:name), '%') " - + "ORDER BY g.name") - , + + "ORDER BY g.name"), @NamedQuery( name = "Group.findAllOrderedByGroupName", - query = "SELECT g FROM Group g ORDER BY g.name") - , + query = "SELECT g FROM Group g ORDER BY g.name"), @NamedQuery( name = "Group.findByMember", query = "SELECT g FROM Group g " + "JOIN g.memberships m " + "WHERE m.member = :member" + ), + @NamedQuery( + name = "Group.findMemberships", + query = "SELECT m FROM GroupMembership m " + + "JOIN m.member u " + + "WHERE m.group = :group " + + "ORDER BY u.name" + ), + @NamedQuery( + name = "Group.countMemberships", + query = "SELECT COUNT(m) FROM GroupMembership m WHERE m.group = :group" ) }) @NamedEntityGraphs({ @NamedEntityGraph( name = "Group.withMembersAndRoleMemberships", attributeNodes = { - @NamedAttributeNode(value = "memberships") - , + @NamedAttributeNode(value = "memberships"), @NamedAttributeNode(value = "roleMemberships", subgraph = "role")}, subgraphs = { @@ -99,8 +106,7 @@ import javax.persistence.Table; attributeNodes = { @NamedAttributeNode(value = "role", subgraph = "permissions") - }) - , + }), @NamedSubgraph( name = "permissions", attributeNodes = { @@ -177,4 +183,4 @@ public class Group extends Party implements Serializable, Exportable { return super.hashCode(); } -} \ No newline at end of file +} diff --git a/ccm-core/src/main/java/org/libreccm/security/GroupRepository.java b/ccm-core/src/main/java/org/libreccm/security/GroupRepository.java index 46b641649..003c5d887 100644 --- a/ccm-core/src/main/java/org/libreccm/security/GroupRepository.java +++ b/ccm-core/src/main/java/org/libreccm/security/GroupRepository.java @@ -26,6 +26,7 @@ import javax.persistence.TypedQuery; import javax.transaction.Transactional; import java.util.List; +import java.util.Objects; import java.util.Optional; import java.util.UUID; @@ -62,11 +63,11 @@ public class GroupRepository extends AbstractEntityRepository { return entity.getPartyId() == 0; } - + public Optional findByUuid(final String uuid) { - + final TypedQuery query = getEntityManager() - .createNamedQuery("Group.findByUuid", Group.class); + .createNamedQuery("Group.findByUuid", Group.class); query.setParameter("uuid", uuid); final List result = query.getResultList(); if (result.isEmpty()) { @@ -107,6 +108,38 @@ public class GroupRepository extends AbstractEntityRepository { return query.getResultList(); } + public List findGroupMemberships( + final Group group, + final int limit, + final int offset + ) { + return getEntityManager() + .createNamedQuery("Group.findMemberships", GroupMembership.class) + .setParameter( + "group", + Objects.requireNonNull( + group, + "Can't retrieve menberships for group null." + ) + ) + .setMaxResults(limit) + .setFirstResult(offset) + .getResultList(); + } + + public long countGroupMemberships(final Group group) { + return getEntityManager() + .createNamedQuery("Group.countMemberships", Long.class) + .setParameter( + "group", + Objects.requireNonNull( + group, + "Can't count menberships for group null." + ) + ) + .getSingleResult(); + } + /** * Tries to find a group which name contains a provided token. * @@ -134,10 +167,10 @@ public class GroupRepository extends AbstractEntityRepository { public void save(final Group group) { super.save(group); } - + @Override public void initNewEntity(final Group group) { - + group.setUuid(UUID.randomUUID().toString()); } diff --git a/ccm-core/src/main/java/org/libreccm/security/Permission.java b/ccm-core/src/main/java/org/libreccm/security/Permission.java index 4fc40dabd..b8022c1f2 100644 --- a/ccm-core/src/main/java/org/libreccm/security/Permission.java +++ b/ccm-core/src/main/java/org/libreccm/security/Permission.java @@ -70,44 +70,65 @@ import javax.persistence.TemporalType; @Entity @Table(name = "PERMISSIONS", schema = DB_SCHEMA) @NamedQueries({ - @NamedQuery(name = "Permission.findByUuid", - query = "SELECT p FROM Permission p WHERE p.uuid = :uuid"), - @NamedQuery(name = "Permission.findByCustomPermId", - query = "SELECT p FROM Permission p " - + "WHERE p.grantedPrivilege = :privilege " - + "AND p.grantee = :grantee " - + "AND p.object = :object"), - @NamedQuery(name = "Permission.existsForPrivilegeRoleObject", - query = "SELECT COUNT(p) FROM Permission p " - + "WHERE p.grantedPrivilege = :privilege " - + "AND p.grantee = :grantee " - + "AND p.object = :object"), - @NamedQuery(name = "Permission.existsDirectForPrivilegeRoleObject", - query = "SELECT COUNT(p) FROM Permission p " - + "WHERE p.grantedPrivilege = :privilege " - + "AND p.grantee = :grantee " - + "AND p.object = :object " - + "AND p.inherited = false"), - @NamedQuery(name = "Permission.existsInheritedForPrivilegeRoleObject", - query = "SELECT COUNT(p) FROM Permission p " - + "WHERE p.grantedPrivilege = :privilege " - + "AND p.grantee = :grantee " - + "AND p.object = :object " - + "AND p.inherited = true"), - @NamedQuery(name = "Permission.existsForPrivilegeAndRole", - query = "SELECT COUNT(p) FROM Permission p " - + "WHERE p.grantedPrivilege = :privilege " - + "AND p.grantee = :grantee " - + "AND p.object IS NULL"), - @NamedQuery(name = "Permission.findPermissionsForRole", - query = "SELECT p FROM Permission p " - + "WHERE p.grantee = :grantee"), - @NamedQuery(name = "Permission.findPermissionsForCcmObject", - query = "SELECT p FROM Permission p " - + "WHERE p.object = :object"), - @NamedQuery(name = "Permission.findPermissionsForRoleAndObject", - query = "SELECT p FROM Permission p " - + "WHERE p.object = :object and p.grantee = :grantee") + @NamedQuery( + name = "Permission.findByUuid", + query = "SELECT p FROM Permission p WHERE p.uuid = :uuid" + ), + @NamedQuery( + name = "Permission.findByCustomPermId", + query = "SELECT p FROM Permission p " + + "WHERE p.grantedPrivilege = :privilege " + + "AND p.grantee = :grantee " + + "AND p.object = :object" + ), + @NamedQuery( + name = "Permission.existsForPrivilegeRoleObject", + query = "SELECT COUNT(p) FROM Permission p " + + "WHERE p.grantedPrivilege = :privilege " + + "AND p.grantee = :grantee " + + "AND p.object = :object" + ), + @NamedQuery( + name = "Permission.existsDirectForPrivilegeRoleObject", + query = "SELECT COUNT(p) FROM Permission p " + + "WHERE p.grantedPrivilege = :privilege " + + "AND p.grantee = :grantee " + + "AND p.object = :object " + + "AND p.inherited = false" + ), + @NamedQuery( + name = "Permission.existsInheritedForPrivilegeRoleObject", + query = "SELECT COUNT(p) FROM Permission p " + + "WHERE p.grantedPrivilege = :privilege " + + "AND p.grantee = :grantee " + + "AND p.object = :object " + + "AND p.inherited = true" + ), + @NamedQuery( + name = "Permission.existsForPrivilegeAndRole", + query = "SELECT COUNT(p) FROM Permission p " + + "WHERE p.grantedPrivilege = :privilege " + + "AND p.grantee = :grantee " + + "AND p.object IS NULL" + ), + @NamedQuery( + name = "Permission.findPermissionsForRole", + query = "SELECT p FROM Permission p WHERE p.grantee = :grantee" + ), + @NamedQuery( + name = "Permission.countPermissionsForRole", + query = "SELECT COUNT(p) FROM Permission p WHERE p.grantee = :grantee" + ), + @NamedQuery( + name = "Permission.findPermissionsForCcmObject", + query = "SELECT p FROM Permission p " + + "WHERE p.object = :object" + ), + @NamedQuery( + name = "Permission.findPermissionsForRoleAndObject", + query = "SELECT p FROM Permission p " + + "WHERE p.object = :object and p.grantee = :grantee" + ) }) @XmlRootElement(name = "permission", namespace = CORE_XML_NS) @@ -408,7 +429,7 @@ public class Permission implements Serializable, Exportable { public JsonObject toJson() { return buildJson().build(); } - + @Override public String toString() { return String.format("%s{ " diff --git a/ccm-core/src/main/java/org/libreccm/security/PermissionRepository.java b/ccm-core/src/main/java/org/libreccm/security/PermissionRepository.java index 929c4471b..8bf6f8ce7 100644 --- a/ccm-core/src/main/java/org/libreccm/security/PermissionRepository.java +++ b/ccm-core/src/main/java/org/libreccm/security/PermissionRepository.java @@ -21,6 +21,9 @@ package org.libreccm.security; import org.libreccm.core.AbstractEntityRepository; import org.libreccm.core.CcmObject; +import java.util.List; +import java.util.Objects; + import javax.enterprise.context.RequestScoped; import javax.persistence.NoResultException; import javax.persistence.TypedQuery; @@ -28,6 +31,8 @@ import javax.persistence.TypedQuery; import java.util.Optional; import java.util.UUID; +import javax.transaction.Transactional; + /** * A repository class for {@link Permission}. * @@ -55,11 +60,13 @@ public class PermissionRepository } @Override + @Transactional(Transactional.TxType.REQUIRED) public String getIdAttributeName() { return "permissionId"; } @Override + @Transactional(Transactional.TxType.REQUIRED) public Long getIdOfEntity(final Permission entity) { return entity.getPermissionId(); } @@ -73,6 +80,7 @@ public class PermissionRepository } @Override + @Transactional(Transactional.TxType.REQUIRED) public void initNewEntity(final Permission permission) { permission.setUuid(UUID.randomUUID().toString()); @@ -97,6 +105,7 @@ public class PermissionRepository * * @return An optional either with the found item or empty */ + @Transactional(Transactional.TxType.REQUIRED) public Optional findByCustomPermId(final String privilege, final Role grantee, final Object object) { @@ -116,6 +125,38 @@ public class PermissionRepository } } + @Transactional(Transactional.TxType.REQUIRED) + public List findPermissionsForRole( + final Role role, final int limit, final int offset + ) { + return getEntityManager() + .createNamedQuery( + "Permission.findPermissionsForRole", Permission.class) + .setParameter( + "grantee", + Objects.requireNonNull( + role, + "Can't retrieve permissions for role null" + ) + ) + .setMaxResults(limit) + .setFirstResult(offset) + .getResultList(); + } + + @Transactional(Transactional.TxType.REQUIRED) + public long countPermissionsForRole(final Role role) { + return getEntityManager() + .createNamedQuery("Permission.countPermissionsForRole", Long.class) + .setParameter( + "grantee", + Objects.requireNonNull( + role, + "Can't count permissions for role null." + ) + ) + .getSingleResult(); + } /** * Checks if a not inherited permission granting the provided * {@code privilege} on the provided {@code object} to the provided @@ -128,6 +169,8 @@ public class PermissionRepository * @return {@code true} if there is a matching permission, {@code false} if * not. */ + @Transactional(Transactional.TxType.REQUIRED) + public boolean existsPermission(final String privilege, final Role grantee, final CcmObject object) { @@ -141,10 +184,11 @@ public class PermissionRepository return query.getSingleResult() > 0; } - - public boolean existsInheritedPermission(final String privilege, - final Role grantee, - final CcmObject object) { + + @Transactional(Transactional.TxType.REQUIRED) + public boolean existsInheritedPermission(final String privilege, + final Role grantee, + final CcmObject object) { final TypedQuery query = getEntityManager().createNamedQuery( "Permission.existsInheritedForPrivilegeRoleObject", Long.class); query.setParameter(QUERY_PARAM_PRIVILEGE, privilege); @@ -164,8 +208,9 @@ public class PermissionRepository * @return {@code true} if there is a matching permission, {@code false} if * not. */ + @Transactional(Transactional.TxType.REQUIRED) public boolean existsPermission(final String privilege, - final Role grantee) { + final Role grantee) { final TypedQuery query = getEntityManager().createNamedQuery( "Permission.existsForPrivilegeAndRole", Long.class); query.setParameter(QUERY_PARAM_PRIVILEGE, privilege); diff --git a/ccm-core/src/main/java/org/libreccm/security/RoleMembership.java b/ccm-core/src/main/java/org/libreccm/security/RoleMembership.java index 3ef431a44..8f6272021 100644 --- a/ccm-core/src/main/java/org/libreccm/security/RoleMembership.java +++ b/ccm-core/src/main/java/org/libreccm/security/RoleMembership.java @@ -57,11 +57,31 @@ import javax.persistence.Table; @Entity @Table(name = "ROLE_MEMBERSHIPS", schema = DB_SCHEMA) @NamedQueries({ - @NamedQuery(name = "RoleMembership.findByUuid", - query = "SELECT m FROM RoleMembership m WHERE m.uuid = :uuid"), - @NamedQuery(name = "RoleMembership.findByRoleAndMember", - query = "SELECT m FROM RoleMembership m " - + "WHERE m.member = :member AND m.role = :role") + @NamedQuery( + name = "RoleMembership.findByUuid", + query = "SELECT m FROM RoleMembership m WHERE m.uuid = :uuid" + ), + @NamedQuery( + name = "RoleMembership.findByRoleAndMember", + query = "SELECT m FROM RoleMembership m " + + "WHERE m.member = :member AND m.role = :role" + ), + @NamedQuery( + name = "RoleMembership.countByParty", + query = "SELECT COUNT(m) FROM RoleMembership m WHERE m.member = :party" + ), + @NamedQuery( + name = "RoleMembership.countByRole", + query = "SELECT COUNT(m) FROM RoleMembership m WHERE m.role = :role" + ), + @NamedQuery( + name = "RoleMembership.findByParty", + query = "SELECT m FROM RoleMembership m WHERE m.member = :party" + ), + @NamedQuery( + name = "RoleMembership.findByRole", + query = "SELECT m FROM RoleMembership m WHERE m.role = :role" + ) }) @XmlRootElement(name = "role-membership", namespace = CORE_XML_NS) @JsonIdentityInfo(generator = ObjectIdGenerators.PropertyGenerator.class, @@ -161,7 +181,7 @@ public class RoleMembership implements Serializable, Exportable { public boolean canEqual(final Object obj) { return obj instanceof RoleMembership; } - + public JsonObject toJson() { return buildJson().build(); } diff --git a/ccm-core/src/main/java/org/libreccm/security/RoleRepository.java b/ccm-core/src/main/java/org/libreccm/security/RoleRepository.java index b694dabb0..5fecb565c 100644 --- a/ccm-core/src/main/java/org/libreccm/security/RoleRepository.java +++ b/ccm-core/src/main/java/org/libreccm/security/RoleRepository.java @@ -55,7 +55,7 @@ public class RoleRepository extends AbstractEntityRepository { public Long getIdOfEntity(final Role entity) { return entity.getRoleId(); } - + @Override public boolean isNew(final Role entity) { if (entity == null) { @@ -63,12 +63,12 @@ public class RoleRepository extends AbstractEntityRepository { } return entity.getRoleId() == 0; } - + @Override public void initNewEntity(final Role role) { - + role.setUuid(UUID.randomUUID().toString()); - + } public long count() { @@ -76,13 +76,13 @@ public class RoleRepository extends AbstractEntityRepository { "Role.count", Long.class); return query.getSingleResult(); } - + public Optional findByUuid(final String uuid) { - + final TypedQuery query = getEntityManager() - .createNamedQuery("Role.findByUuid", Role.class); + .createNamedQuery("Role.findByUuid", Role.class); query.setParameter("uuid", uuid); - + return getSingleResult(query); } @@ -150,6 +150,24 @@ public class RoleRepository extends AbstractEntityRepository { return query.getResultList(); } + public long countMembershipsByRole(final Role role) { + return getEntityManager() + .createNamedQuery("RoleMembership.countByRole", Long.class) + .setParameter("role", role) + .getSingleResult(); + } + + public List findMembershipsByRole( + final Role role, final int limit, final int offset + ) { + return getEntityManager() + .createNamedQuery("RoleMembership.findByRole", RoleMembership.class) + .setParameter("role", role) + .setMaxResults(limit) + .setFirstResult(offset) + .getResultList(); + } + public List searchByName(final String name) { final TypedQuery query = getEntityManager().createNamedQuery( "Role.searchByName", Role.class); diff --git a/ccm-core/src/main/java/org/libreccm/security/User.java b/ccm-core/src/main/java/org/libreccm/security/User.java index 30a67f5fc..f73da8229 100644 --- a/ccm-core/src/main/java/org/libreccm/security/User.java +++ b/ccm-core/src/main/java/org/libreccm/security/User.java @@ -116,7 +116,7 @@ import javax.xml.bind.annotation.XmlTransient; @NamedQuery(name = "User.findByGroup", query = "SELECT u FROM User u " + "JOIN u.groupMemberships m " - + "WHERE m.group = :group") + + "WHERE m.group = :group"), }) @NamedEntityGraphs({ @NamedEntityGraph(