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
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)) {

View File

@ -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<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
@Path("/{domainIdentifier}/{path:^[\\w\\-/]+$}/objects")
@Produces(MediaType.APPLICATION_JSON)
@AuthorizationRequired
@RequiresPrivilege(CoreConstants.PRIVILEGE_ADMIN)
@Transactional(Transactional.TxType.REQUIRED)
public CategoryData getObjectsInCategory(
public ListView<CategorizationData> 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<CategorizationData> 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<CategorizationData> getObjectsInCategory(
@PathParam("categoryId") final String categoryUuid
) {
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.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<CategorizationData> objects;
private List<AssociatedCategoryData> 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<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() {
return parentCategory;
}

View File

@ -203,14 +203,23 @@ public class GroupsApi {
@AuthorizationRequired
@RequiresPrivilege(CoreConstants.PRIVILEGE_ADMIN)
@Transactional(Transactional.TxType.REQUIRED)
public List<GroupUserMembership> getMembers(
@PathParam("groupIdentifier") final String groupIdentifier
public ListView<GroupUserMembership> 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

View File

@ -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<Role> 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<RolePartyMembership> getMembers(
@PathParam("roleIdentifier") final String roleIdentifier
public ListView<RolePartyMembership> 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<RolePermission> getPermissions(
@PathParam("roleIdentifier") final String roleIdentifier
public ListView<RolePermission> 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();
}
}

View File

@ -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:

View File

@ -41,19 +41,19 @@ public class RoleData {
private LocalizedString description;
private List<RolePartyMembership> memberships;
// private List<RolePartyMembership> memberships;
private List<RolePermission> permissions;
private List<RoleAssignedTask> assignedTasks;
// private List<RoleAssignedTask> 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<RolePartyMembership> getMemberships() {
return new ArrayList<>(memberships);
}
public void setMemberships(final List<RolePartyMembership> memberships) {
this.memberships = new ArrayList<>(memberships);
}
public List<RoleAssignedTask> getAssignedTasks() {
return new ArrayList<>(assignedTasks);
}
public void setAssignedTasks(final List<RoleAssignedTask> assignedTasks) {
this.assignedTasks = new ArrayList<>(assignedTasks);
}
// public List<RolePartyMembership> getMemberships() {
// return new ArrayList<>(memberships);
// }
//
// public void setMemberships(final List<RolePartyMembership> memberships) {
// this.memberships = new ArrayList<>(memberships);
// }
//
// public List<RoleAssignedTask> getAssignedTasks() {
// return new ArrayList<>(assignedTasks);
// }
//
// public void setAssignedTasks(final List<RoleAssignedTask> assignedTasks) {
// this.assignedTasks = new ArrayList<>(assignedTasks);
// }
public LocalizedString getDescription() {
return description;

View File

@ -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();
}
}
}

View File

@ -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<Long, Group> {
return entity.getPartyId() == 0;
}
public Optional<Group> findByUuid(final String uuid) {
final TypedQuery<Group> query = getEntityManager()
.createNamedQuery("Group.findByUuid", Group.class);
.createNamedQuery("Group.findByUuid", Group.class);
query.setParameter("uuid", uuid);
final List<Group> result = query.getResultList();
if (result.isEmpty()) {
@ -107,6 +108,38 @@ public class GroupRepository extends AbstractEntityRepository<Long, Group> {
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.
*
@ -134,10 +167,10 @@ public class GroupRepository extends AbstractEntityRepository<Long, Group> {
public void save(final Group group) {
super.save(group);
}
@Override
public void initNewEntity(final Group group) {
group.setUuid(UUID.randomUUID().toString());
}

View File

@ -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{ "

View File

@ -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<Permission> findByCustomPermId(final String privilege,
final Role grantee,
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
* {@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<Long> 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<Long> query = getEntityManager().createNamedQuery(
"Permission.existsForPrivilegeAndRole", Long.class);
query.setParameter(QUERY_PARAM_PRIVILEGE, privilege);

View File

@ -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();
}

View File

@ -55,7 +55,7 @@ public class RoleRepository extends AbstractEntityRepository<Long, Role> {
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<Long, Role> {
}
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<Long, Role> {
"Role.count", Long.class);
return query.getSingleResult();
}
public Optional<Role> findByUuid(final String uuid) {
final TypedQuery<Role> 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<Long, Role> {
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) {
final TypedQuery<Role> query = getEntityManager().createNamedQuery(
"Role.searchByName", Role.class);

View File

@ -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(