Improvements for RESTful API

Jens Pelzetter 2020-06-05 20:32:27 +02:00
parent 8b3f843531
commit fe6e680733
15 changed files with 489 additions and 212 deletions

View File

@ -29,7 +29,7 @@ import javax.enterprise.context.Dependent;
@Dependent @Dependent
public class IdentifierParser { public class IdentifierParser {
public Identifier extractIdentifier(final String identifierParam) { public Identifier parseIdentifier(final String identifierParam) {
Objects.requireNonNull(identifierParam, "identifier param is null."); Objects.requireNonNull(identifierParam, "identifier param is null.");
if (identifierParam.startsWith(ApiConstants.IDENTIFIER_PREFIX_ID)) { if (identifierParam.startsWith(ApiConstants.IDENTIFIER_PREFIX_ID)) {

View File

@ -18,6 +18,7 @@
*/ */
package org.libreccm.api.admin.categorization; 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.admin.categorization.dto.CategoryData;
import org.libreccm.api.dto.ListView; import org.libreccm.api.dto.ListView;
import org.libreccm.core.CoreConstants; import org.libreccm.core.CoreConstants;
@ -28,12 +29,14 @@ import javax.enterprise.context.RequestScoped;
import javax.transaction.Transactional; import javax.transaction.Transactional;
import javax.ws.rs.Consumes; import javax.ws.rs.Consumes;
import javax.ws.rs.DELETE; import javax.ws.rs.DELETE;
import javax.ws.rs.DefaultValue;
import javax.ws.rs.GET; import javax.ws.rs.GET;
import javax.ws.rs.POST; import javax.ws.rs.POST;
import javax.ws.rs.PUT; import javax.ws.rs.PUT;
import javax.ws.rs.Path; import javax.ws.rs.Path;
import javax.ws.rs.PathParam; import javax.ws.rs.PathParam;
import javax.ws.rs.Produces; import javax.ws.rs.Produces;
import javax.ws.rs.QueryParam;
import javax.ws.rs.core.MediaType; import javax.ws.rs.core.MediaType;
/** /**
@ -190,13 +193,56 @@ public class CategoriesApi {
throw new UnsupportedOperationException(); throw new UnsupportedOperationException();
} }
@GET
@Path("/{domainIdentifier}/{path:^[\\w\\-/]+$}/subcategories")
@Produces(MediaType.APPLICATION_JSON)
@AuthorizationRequired
@RequiresPrivilege(CoreConstants.PRIVILEGE_ADMIN)
@Transactional(Transactional.TxType.REQUIRED)
public ListView<CategoryData> 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<CategoryData> 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<CategoryData> getSubCategories(
@PathParam("categoryUuid") final String uuid,
@QueryParam("limit") @DefaultValue("20") final int limit,
@QueryParam("offset") @DefaultValue("20") final int offset
) {
throw new UnsupportedOperationException();
}
@GET @GET
@Path("/{domainIdentifier}/{path:^[\\w\\-/]+$}/objects") @Path("/{domainIdentifier}/{path:^[\\w\\-/]+$}/objects")
@Produces(MediaType.APPLICATION_JSON) @Produces(MediaType.APPLICATION_JSON)
@AuthorizationRequired @AuthorizationRequired
@RequiresPrivilege(CoreConstants.PRIVILEGE_ADMIN) @RequiresPrivilege(CoreConstants.PRIVILEGE_ADMIN)
@Transactional(Transactional.TxType.REQUIRED) @Transactional(Transactional.TxType.REQUIRED)
public CategoryData getObjectsInCategory( public ListView<CategorizationData> getObjectsInCategory(
@PathParam("domainIdentifier") final String domainIdentifierParam, @PathParam("domainIdentifier") final String domainIdentifierParam,
@PathParam("path") final String categoryPathTokens @PathParam("path") final String categoryPathTokens
) { ) {
@ -209,7 +255,7 @@ public class CategoriesApi {
@AuthorizationRequired @AuthorizationRequired
@RequiresPrivilege(CoreConstants.PRIVILEGE_ADMIN) @RequiresPrivilege(CoreConstants.PRIVILEGE_ADMIN)
@Transactional(Transactional.TxType.REQUIRED) @Transactional(Transactional.TxType.REQUIRED)
public CategoryData getCategoryObjectsInCategory( public ListView<CategorizationData> getCategoryObjectsInCategory(
@PathParam("categoryId") final long categoryId @PathParam("categoryId") final long categoryId
) { ) {
throw new UnsupportedOperationException(); throw new UnsupportedOperationException();
@ -221,7 +267,7 @@ public class CategoriesApi {
@AuthorizationRequired @AuthorizationRequired
@RequiresPrivilege(CoreConstants.PRIVILEGE_ADMIN) @RequiresPrivilege(CoreConstants.PRIVILEGE_ADMIN)
@Transactional(Transactional.TxType.REQUIRED) @Transactional(Transactional.TxType.REQUIRED)
public CategoryData getObjectsInCategory( public ListView<CategorizationData> getObjectsInCategory(
@PathParam("categoryId") final String categoryUuid @PathParam("categoryId") final String categoryUuid
) { ) {
throw new UnsupportedOperationException(); throw new UnsupportedOperationException();

View File

@ -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 <a href="mailto:jens.pelzetter@googlemail.com">Jens Pelzetter</a>
*/
@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
)
);
}
}
}

View File

@ -21,10 +21,7 @@ package org.libreccm.api.admin.categorization.dto;
import org.libreccm.categorization.Category; import org.libreccm.categorization.Category;
import org.libreccm.l10n.LocalizedString; import org.libreccm.l10n.LocalizedString;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects; import java.util.Objects;
import java.util.stream.Collectors;
/** /**
* *
@ -50,17 +47,12 @@ public class CategoryData {
private boolean abstractCategory; private boolean abstractCategory;
private List<CategorizationData> objects;
private List<AssociatedCategoryData> subCategories;
private AssociatedCategoryData parentCategory; private AssociatedCategoryData parentCategory;
private long categoryOrder; private long categoryOrder;
public CategoryData() { public CategoryData() {
objects = new ArrayList<>(); // Nothing
subCategories = new ArrayList<>();
} }
public CategoryData(final Category fromCategory) { public CategoryData(final Category fromCategory) {
@ -77,16 +69,6 @@ public class CategoryData {
enabled = fromCategory.isEnabled(); enabled = fromCategory.isEnabled();
visible = fromCategory.isVisible(); visible = fromCategory.isVisible();
abstractCategory = fromCategory.isAbstractCategory(); 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( parentCategory = new AssociatedCategoryData(
fromCategory.getParentCategory() fromCategory.getParentCategory()
); );
@ -164,23 +146,6 @@ public class CategoryData {
this.abstractCategory = abstractCategory; this.abstractCategory = abstractCategory;
} }
public List<CategorizationData> getObjects() {
return new ArrayList<>(objects);
}
public void setObjects(final List<CategorizationData> objects) {
this.objects = new ArrayList<>(objects);
}
public List<AssociatedCategoryData> getSubCategories() {
return new ArrayList<>(subCategories);
}
public void setSubCategories(
final List<AssociatedCategoryData> subCategories) {
this.subCategories = new ArrayList<>(subCategories);
}
public AssociatedCategoryData getParentCategory() { public AssociatedCategoryData getParentCategory() {
return parentCategory; return parentCategory;
} }

View File

@ -203,14 +203,23 @@ public class GroupsApi {
@AuthorizationRequired @AuthorizationRequired
@RequiresPrivilege(CoreConstants.PRIVILEGE_ADMIN) @RequiresPrivilege(CoreConstants.PRIVILEGE_ADMIN)
@Transactional(Transactional.TxType.REQUIRED) @Transactional(Transactional.TxType.REQUIRED)
public List<GroupUserMembership> getMembers( public ListView<GroupUserMembership> getMembers(
@PathParam("groupIdentifier") final String groupIdentifier @PathParam("groupIdentifier") final String groupIdentifier,
@QueryParam("limit") @DefaultValue("20") final int limit,
@QueryParam("offset") @DefaultValue("0") final int offset
) { ) {
return repository.findGroup(groupIdentifier) final Group group = repository.findGroup(groupIdentifier);
.getMemberships()
return new ListView<>(
groupRepository
.findGroupMemberships(group, limit, offset)
.stream() .stream()
.map(GroupUserMembership::new) .map(GroupUserMembership::new)
.collect(Collectors.toList()); .collect(Collectors.toList()),
groupRepository.countGroupMemberships(group),
limit,
offset
);
} }
@PUT @PUT

View File

@ -48,6 +48,8 @@ import java.net.URI;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import javax.ws.rs.WebApplicationException; import javax.ws.rs.WebApplicationException;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.UriInfo;
/** /**
* *
@ -57,6 +59,9 @@ import javax.ws.rs.WebApplicationException;
@Path("/roles") @Path("/roles")
public class RolesApi { public class RolesApi {
@Context
private UriInfo uriInfo;
@Inject @Inject
private CcmObjectRepository ccmObjectRepository; private CcmObjectRepository ccmObjectRepository;
@ -179,14 +184,23 @@ public class RolesApi {
@AuthorizationRequired @AuthorizationRequired
@RequiresPrivilege(CoreConstants.PRIVILEGE_ADMIN) @RequiresPrivilege(CoreConstants.PRIVILEGE_ADMIN)
@Transactional(Transactional.TxType.REQUIRED) @Transactional(Transactional.TxType.REQUIRED)
public List<RolePartyMembership> getMembers( public ListView<RolePartyMembership> getMembers(
@PathParam("roleIdentifier") final String roleIdentifier @PathParam("roleIdentifier") final String roleIdentifier,
@QueryParam("limit") @DefaultValue("20") final int limit,
@QueryParam("offset") @DefaultValue("0") final int offset
) { ) {
return repository.findRole(roleIdentifier) final Role role = repository.findRole(roleIdentifier);
.getMemberships() final long count = roleRepository.countMembershipsByRole(role);
.stream() return new ListView<>(
.map(RolePartyMembership::new) roleRepository
.collect(Collectors.toList()); .findMembershipsByRole(role, limit, offset)
.stream()
.map(RolePartyMembership::new)
.collect(Collectors.toList()),
count,
limit,
offset
);
} }
@PUT @PUT
@ -244,14 +258,22 @@ public class RolesApi {
@AuthorizationRequired @AuthorizationRequired
@RequiresPrivilege(CoreConstants.PRIVILEGE_ADMIN) @RequiresPrivilege(CoreConstants.PRIVILEGE_ADMIN)
@Transactional(Transactional.TxType.REQUIRED) @Transactional(Transactional.TxType.REQUIRED)
public List<RolePermission> getPermissions( public ListView<RolePermission> getPermissions(
@PathParam("roleIdentifier") final String roleIdentifier @PathParam("roleIdentifier") final String roleIdentifier,
@QueryParam("limit") @DefaultValue("20") final int limit,
@QueryParam("offset") @DefaultValue("0") final int offset
) { ) {
return repository.findRole(roleIdentifier) final Role role = repository.findRole(roleIdentifier);
.getPermissions() return new ListView<>(
.stream() permissionRepository
.map(RolePermission::new) .findPermissionsForRole(role, limit, offset)
.collect(Collectors.toList()); .stream()
.map(RolePermission::new)
.collect(Collectors.toList()),
permissionRepository.countPermissionsForRole(role),
limit,
offset
);
} }
@POST @POST
@ -345,7 +367,7 @@ public class RolesApi {
final Role role = repository.findRole(roleIdentifier); final Role role = repository.findRole(roleIdentifier);
final Identifier permissionIdentifier = identifierExtractor final Identifier permissionIdentifier = identifierExtractor
.extractIdentifier(roleIdentifier); .parseIdentifier(roleIdentifier);
final Permission permission; final Permission permission;
switch (permissionIdentifier.getType()) { switch (permissionIdentifier.getType()) {
@ -375,7 +397,7 @@ public class RolesApi {
Response.Status.NOT_FOUND Response.Status.NOT_FOUND
) )
); );
break; break;
default: default:
return Response return Response

View File

@ -63,7 +63,7 @@ class SecurityApiRepository {
protected Group findGroup(final String groupIdentifier) { protected Group findGroup(final String groupIdentifier) {
final Identifier identifier = identifierExtractor final Identifier identifier = identifierExtractor
.extractIdentifier(groupIdentifier); .parseIdentifier(groupIdentifier);
switch (identifier.getType()) { switch (identifier.getType()) {
case ID: case ID:
@ -107,7 +107,7 @@ class SecurityApiRepository {
protected Party findParty(final String partyIdentifier) { protected Party findParty(final String partyIdentifier) {
final Identifier identifier = identifierExtractor final Identifier identifier = identifierExtractor
.extractIdentifier(partyIdentifier); .parseIdentifier(partyIdentifier);
switch (identifier.getType()) { switch (identifier.getType()) {
case ID: case ID:
@ -152,7 +152,7 @@ class SecurityApiRepository {
protected Role findRole(final String roleIdentifier) { protected Role findRole(final String roleIdentifier) {
final Identifier identifier = identifierExtractor final Identifier identifier = identifierExtractor
.extractIdentifier(roleIdentifier); .parseIdentifier(roleIdentifier);
switch (identifier.getType()) { switch (identifier.getType()) {
case ID: case ID:
@ -196,7 +196,7 @@ class SecurityApiRepository {
protected User findUser(final String identifierParam) { protected User findUser(final String identifierParam) {
final Identifier identifier = identifierExtractor final Identifier identifier = identifierExtractor
.extractIdentifier(identifierParam); .parseIdentifier(identifierParam);
switch (identifier.getType()) { switch (identifier.getType()) {
case ID: case ID:

View File

@ -41,19 +41,19 @@ public class RoleData {
private LocalizedString description; private LocalizedString description;
private List<RolePartyMembership> memberships; // private List<RolePartyMembership> memberships;
private List<RolePermission> permissions; private List<RolePermission> permissions;
private List<RoleAssignedTask> assignedTasks; // private List<RoleAssignedTask> assignedTasks;
/** /**
* Parameterless constructor for creating empty instances. * Parameterless constructor for creating empty instances.
*/ */
public RoleData() { public RoleData() {
memberships = new ArrayList<>(); // membership = new ArrayList<>();
permissions = new ArrayList<>(); permissions = new ArrayList<>();
assignedTasks = new ArrayList<>(); // assignedTasks = new ArrayList<>();
} }
public RoleData(final Role role) { public RoleData(final Role role) {
@ -64,23 +64,23 @@ public class RoleData {
name = role.getName(); name = role.getName();
description = role.getDescription(); description = role.getDescription();
memberships = role // memberships = role
.getMemberships() // .getMemberships()
.stream() // .stream()
.map(RolePartyMembership::new) // .map(RolePartyMembership::new)
.collect(Collectors.toList()); // .collect(Collectors.toList());
//
permissions = role // permissions = role
.getPermissions() // .getPermissions()
.stream() // .stream()
.map(RolePermission::new) // .map(RolePermission::new)
.collect(Collectors.toList()); // .collect(Collectors.toList());
//
assignedTasks = role // assignedTasks = role
.getAssignedTasks() // .getAssignedTasks()
.stream() // .stream()
.map(RoleAssignedTask::new) // .map(RoleAssignedTask::new)
.collect(Collectors.toList()); // .collect(Collectors.toList());
} }
public long getRoleId() { public long getRoleId() {
@ -107,21 +107,21 @@ public class RoleData {
this.name = name; this.name = name;
} }
public List<RolePartyMembership> getMemberships() { // public List<RolePartyMembership> getMemberships() {
return new ArrayList<>(memberships); // return new ArrayList<>(memberships);
} // }
//
public void setMemberships(final List<RolePartyMembership> memberships) { // public void setMemberships(final List<RolePartyMembership> memberships) {
this.memberships = new ArrayList<>(memberships); // this.memberships = new ArrayList<>(memberships);
} // }
//
public List<RoleAssignedTask> getAssignedTasks() { // public List<RoleAssignedTask> getAssignedTasks() {
return new ArrayList<>(assignedTasks); // return new ArrayList<>(assignedTasks);
} // }
//
public void setAssignedTasks(final List<RoleAssignedTask> assignedTasks) { // public void setAssignedTasks(final List<RoleAssignedTask> assignedTasks) {
this.assignedTasks = new ArrayList<>(assignedTasks); // this.assignedTasks = new ArrayList<>(assignedTasks);
} // }
public LocalizedString getDescription() { public LocalizedString getDescription() {
return description; return description;

View File

@ -66,31 +66,38 @@ import javax.persistence.Table;
@NamedQuery( @NamedQuery(
name = "Group.findByName", name = "Group.findByName",
query = "SELECT g FROM Group g WHERE g.name = :name " query = "SELECT g FROM Group g WHERE g.name = :name "
+ "ORDER BY g.name") + "ORDER BY g.name"),
,
@NamedQuery( @NamedQuery(
name = "Group.searchByName", name = "Group.searchByName",
query = "SELECT g FROM Group g " query = "SELECT g FROM Group g "
+ "WHERE LOWER(g.name) LIKE CONCAT(LOWER(:name), '%') " + "WHERE LOWER(g.name) LIKE CONCAT(LOWER(:name), '%') "
+ "ORDER BY g.name") + "ORDER BY g.name"),
,
@NamedQuery( @NamedQuery(
name = "Group.findAllOrderedByGroupName", name = "Group.findAllOrderedByGroupName",
query = "SELECT g FROM Group g ORDER BY g.name") query = "SELECT g FROM Group g ORDER BY g.name"),
,
@NamedQuery( @NamedQuery(
name = "Group.findByMember", name = "Group.findByMember",
query = "SELECT g FROM Group g " query = "SELECT g FROM Group g "
+ "JOIN g.memberships m " + "JOIN g.memberships m "
+ "WHERE m.member = :member" + "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({ @NamedEntityGraphs({
@NamedEntityGraph( @NamedEntityGraph(
name = "Group.withMembersAndRoleMemberships", name = "Group.withMembersAndRoleMemberships",
attributeNodes = { attributeNodes = {
@NamedAttributeNode(value = "memberships") @NamedAttributeNode(value = "memberships"),
,
@NamedAttributeNode(value = "roleMemberships", @NamedAttributeNode(value = "roleMemberships",
subgraph = "role")}, subgraph = "role")},
subgraphs = { subgraphs = {
@ -99,8 +106,7 @@ import javax.persistence.Table;
attributeNodes = { attributeNodes = {
@NamedAttributeNode(value = "role", @NamedAttributeNode(value = "role",
subgraph = "permissions") subgraph = "permissions")
}) }),
,
@NamedSubgraph( @NamedSubgraph(
name = "permissions", name = "permissions",
attributeNodes = { attributeNodes = {

View File

@ -26,6 +26,7 @@ import javax.persistence.TypedQuery;
import javax.transaction.Transactional; import javax.transaction.Transactional;
import java.util.List; import java.util.List;
import java.util.Objects;
import java.util.Optional; import java.util.Optional;
import java.util.UUID; import java.util.UUID;
@ -66,7 +67,7 @@ public class GroupRepository extends AbstractEntityRepository<Long, Group> {
public Optional<Group> findByUuid(final String uuid) { public Optional<Group> findByUuid(final String uuid) {
final TypedQuery<Group> query = getEntityManager() final TypedQuery<Group> query = getEntityManager()
.createNamedQuery("Group.findByUuid", Group.class); .createNamedQuery("Group.findByUuid", Group.class);
query.setParameter("uuid", uuid); query.setParameter("uuid", uuid);
final List<Group> result = query.getResultList(); final List<Group> result = query.getResultList();
if (result.isEmpty()) { if (result.isEmpty()) {
@ -107,6 +108,38 @@ public class GroupRepository extends AbstractEntityRepository<Long, Group> {
return query.getResultList(); return query.getResultList();
} }
public List<GroupMembership> 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. * Tries to find a group which name contains a provided token.
* *

View File

@ -70,44 +70,65 @@ import javax.persistence.TemporalType;
@Entity @Entity
@Table(name = "PERMISSIONS", schema = DB_SCHEMA) @Table(name = "PERMISSIONS", schema = DB_SCHEMA)
@NamedQueries({ @NamedQueries({
@NamedQuery(name = "Permission.findByUuid", @NamedQuery(
query = "SELECT p FROM Permission p WHERE p.uuid = :uuid"), name = "Permission.findByUuid",
@NamedQuery(name = "Permission.findByCustomPermId", query = "SELECT p FROM Permission p WHERE p.uuid = :uuid"
query = "SELECT p FROM Permission p " ),
+ "WHERE p.grantedPrivilege = :privilege " @NamedQuery(
+ "AND p.grantee = :grantee " name = "Permission.findByCustomPermId",
+ "AND p.object = :object"), query = "SELECT p FROM Permission p "
@NamedQuery(name = "Permission.existsForPrivilegeRoleObject", + "WHERE p.grantedPrivilege = :privilege "
query = "SELECT COUNT(p) FROM Permission p " + "AND p.grantee = :grantee "
+ "WHERE p.grantedPrivilege = :privilege " + "AND p.object = :object"
+ "AND p.grantee = :grantee " ),
+ "AND p.object = :object"), @NamedQuery(
@NamedQuery(name = "Permission.existsDirectForPrivilegeRoleObject", name = "Permission.existsForPrivilegeRoleObject",
query = "SELECT COUNT(p) FROM Permission p " query = "SELECT COUNT(p) FROM Permission p "
+ "WHERE p.grantedPrivilege = :privilege " + "WHERE p.grantedPrivilege = :privilege "
+ "AND p.grantee = :grantee " + "AND p.grantee = :grantee "
+ "AND p.object = :object " + "AND p.object = :object"
+ "AND p.inherited = false"), ),
@NamedQuery(name = "Permission.existsInheritedForPrivilegeRoleObject", @NamedQuery(
query = "SELECT COUNT(p) FROM Permission p " name = "Permission.existsDirectForPrivilegeRoleObject",
+ "WHERE p.grantedPrivilege = :privilege " query = "SELECT COUNT(p) FROM Permission p "
+ "AND p.grantee = :grantee " + "WHERE p.grantedPrivilege = :privilege "
+ "AND p.object = :object " + "AND p.grantee = :grantee "
+ "AND p.inherited = true"), + "AND p.object = :object "
@NamedQuery(name = "Permission.existsForPrivilegeAndRole", + "AND p.inherited = false"
query = "SELECT COUNT(p) FROM Permission p " ),
+ "WHERE p.grantedPrivilege = :privilege " @NamedQuery(
+ "AND p.grantee = :grantee " name = "Permission.existsInheritedForPrivilegeRoleObject",
+ "AND p.object IS NULL"), query = "SELECT COUNT(p) FROM Permission p "
@NamedQuery(name = "Permission.findPermissionsForRole", + "WHERE p.grantedPrivilege = :privilege "
query = "SELECT p FROM Permission p " + "AND p.grantee = :grantee "
+ "WHERE p.grantee = :grantee"), + "AND p.object = :object "
@NamedQuery(name = "Permission.findPermissionsForCcmObject", + "AND p.inherited = true"
query = "SELECT p FROM Permission p " ),
+ "WHERE p.object = :object"), @NamedQuery(
@NamedQuery(name = "Permission.findPermissionsForRoleAndObject", name = "Permission.existsForPrivilegeAndRole",
query = "SELECT p FROM Permission p " query = "SELECT COUNT(p) FROM Permission p "
+ "WHERE p.object = :object and p.grantee = :grantee") + "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) @XmlRootElement(name = "permission", namespace = CORE_XML_NS)

View File

@ -21,6 +21,9 @@ package org.libreccm.security;
import org.libreccm.core.AbstractEntityRepository; import org.libreccm.core.AbstractEntityRepository;
import org.libreccm.core.CcmObject; import org.libreccm.core.CcmObject;
import java.util.List;
import java.util.Objects;
import javax.enterprise.context.RequestScoped; import javax.enterprise.context.RequestScoped;
import javax.persistence.NoResultException; import javax.persistence.NoResultException;
import javax.persistence.TypedQuery; import javax.persistence.TypedQuery;
@ -28,6 +31,8 @@ import javax.persistence.TypedQuery;
import java.util.Optional; import java.util.Optional;
import java.util.UUID; import java.util.UUID;
import javax.transaction.Transactional;
/** /**
* A repository class for {@link Permission}. * A repository class for {@link Permission}.
* *
@ -55,11 +60,13 @@ public class PermissionRepository
} }
@Override @Override
@Transactional(Transactional.TxType.REQUIRED)
public String getIdAttributeName() { public String getIdAttributeName() {
return "permissionId"; return "permissionId";
} }
@Override @Override
@Transactional(Transactional.TxType.REQUIRED)
public Long getIdOfEntity(final Permission entity) { public Long getIdOfEntity(final Permission entity) {
return entity.getPermissionId(); return entity.getPermissionId();
} }
@ -73,6 +80,7 @@ public class PermissionRepository
} }
@Override @Override
@Transactional(Transactional.TxType.REQUIRED)
public void initNewEntity(final Permission permission) { public void initNewEntity(final Permission permission) {
permission.setUuid(UUID.randomUUID().toString()); permission.setUuid(UUID.randomUUID().toString());
@ -97,6 +105,7 @@ public class PermissionRepository
* *
* @return An optional either with the found item or empty * @return An optional either with the found item or empty
*/ */
@Transactional(Transactional.TxType.REQUIRED)
public Optional<Permission> findByCustomPermId(final String privilege, public Optional<Permission> findByCustomPermId(final String privilege,
final Role grantee, final Role grantee,
final Object object) { final Object object) {
@ -116,6 +125,38 @@ public class PermissionRepository
} }
} }
@Transactional(Transactional.TxType.REQUIRED)
public List<Permission> 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 * Checks if a not inherited permission granting the provided
* {@code privilege} on the provided {@code object} to 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 * @return {@code true} if there is a matching permission, {@code false} if
* not. * not.
*/ */
@Transactional(Transactional.TxType.REQUIRED)
public boolean existsPermission(final String privilege, public boolean existsPermission(final String privilege,
final Role grantee, final Role grantee,
final CcmObject object) { final CcmObject object) {
@ -142,9 +185,10 @@ public class PermissionRepository
return query.getSingleResult() > 0; return query.getSingleResult() > 0;
} }
public boolean existsInheritedPermission(final String privilege, @Transactional(Transactional.TxType.REQUIRED)
final Role grantee, public boolean existsInheritedPermission(final String privilege,
final CcmObject object) { final Role grantee,
final CcmObject object) {
final TypedQuery<Long> query = getEntityManager().createNamedQuery( final TypedQuery<Long> query = getEntityManager().createNamedQuery(
"Permission.existsInheritedForPrivilegeRoleObject", Long.class); "Permission.existsInheritedForPrivilegeRoleObject", Long.class);
query.setParameter(QUERY_PARAM_PRIVILEGE, privilege); 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 * @return {@code true} if there is a matching permission, {@code false} if
* not. * not.
*/ */
@Transactional(Transactional.TxType.REQUIRED)
public boolean existsPermission(final String privilege, public boolean existsPermission(final String privilege,
final Role grantee) { final Role grantee) {
final TypedQuery<Long> query = getEntityManager().createNamedQuery( final TypedQuery<Long> query = getEntityManager().createNamedQuery(
"Permission.existsForPrivilegeAndRole", Long.class); "Permission.existsForPrivilegeAndRole", Long.class);
query.setParameter(QUERY_PARAM_PRIVILEGE, privilege); query.setParameter(QUERY_PARAM_PRIVILEGE, privilege);

View File

@ -57,11 +57,31 @@ import javax.persistence.Table;
@Entity @Entity
@Table(name = "ROLE_MEMBERSHIPS", schema = DB_SCHEMA) @Table(name = "ROLE_MEMBERSHIPS", schema = DB_SCHEMA)
@NamedQueries({ @NamedQueries({
@NamedQuery(name = "RoleMembership.findByUuid", @NamedQuery(
query = "SELECT m FROM RoleMembership m WHERE m.uuid = :uuid"), name = "RoleMembership.findByUuid",
@NamedQuery(name = "RoleMembership.findByRoleAndMember", query = "SELECT m FROM RoleMembership m WHERE m.uuid = :uuid"
query = "SELECT m FROM RoleMembership m " ),
+ "WHERE m.member = :member AND m.role = :role") @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) @XmlRootElement(name = "role-membership", namespace = CORE_XML_NS)
@JsonIdentityInfo(generator = ObjectIdGenerators.PropertyGenerator.class, @JsonIdentityInfo(generator = ObjectIdGenerators.PropertyGenerator.class,

View File

@ -80,7 +80,7 @@ public class RoleRepository extends AbstractEntityRepository<Long, Role> {
public Optional<Role> findByUuid(final String uuid) { public Optional<Role> findByUuid(final String uuid) {
final TypedQuery<Role> query = getEntityManager() final TypedQuery<Role> query = getEntityManager()
.createNamedQuery("Role.findByUuid", Role.class); .createNamedQuery("Role.findByUuid", Role.class);
query.setParameter("uuid", uuid); query.setParameter("uuid", uuid);
return getSingleResult(query); return getSingleResult(query);
@ -150,6 +150,24 @@ public class RoleRepository extends AbstractEntityRepository<Long, Role> {
return query.getResultList(); return query.getResultList();
} }
public long countMembershipsByRole(final Role role) {
return getEntityManager()
.createNamedQuery("RoleMembership.countByRole", Long.class)
.setParameter("role", role)
.getSingleResult();
}
public List<RoleMembership> 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<Role> searchByName(final String name) { public List<Role> searchByName(final String name) {
final TypedQuery<Role> query = getEntityManager().createNamedQuery( final TypedQuery<Role> query = getEntityManager().createNamedQuery(
"Role.searchByName", Role.class); "Role.searchByName", Role.class);

View File

@ -116,7 +116,7 @@ import javax.xml.bind.annotation.XmlTransient;
@NamedQuery(name = "User.findByGroup", @NamedQuery(name = "User.findByGroup",
query = "SELECT u FROM User u " query = "SELECT u FROM User u "
+ "JOIN u.groupMemberships m " + "JOIN u.groupMemberships m "
+ "WHERE m.group = :group") + "WHERE m.group = :group"),
}) })
@NamedEntityGraphs({ @NamedEntityGraphs({
@NamedEntityGraph( @NamedEntityGraph(