Started implementation of RESTful API for administrative tasks

Former-commit-id: 0a03224761
pull/2/head
Jens Pelzetter 2020-05-11 20:33:18 +02:00
parent efe1d9554e
commit b747f42ffe
18 changed files with 745 additions and 36 deletions

1
.gitignore vendored
View File

@ -1,3 +1,4 @@
nb-configuration.xml
node
node_modules
runtime

View File

@ -0,0 +1,52 @@
/*
* 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;
import java.io.IOException;
import javax.ws.rs.container.ContainerRequestContext;
import javax.ws.rs.container.ContainerResponseContext;
import javax.ws.rs.container.ContainerResponseFilter;
/**
* A JAX-RS filter adding HTTP headers required for CORS.
*
* @author <a href="mailto:jens.pelzetter@googlemail.com">Jens Pelzetter</a>
*/
public class CorsFilter implements ContainerResponseFilter {
@Override
public void filter(
final ContainerRequestContext requestContext,
final ContainerResponseContext responseContext)
throws IOException {
if (!requestContext.getMethod().equals("OPTIONS")) {
responseContext.getHeaders().add("Access-Control-Allow-Origin", "*");
responseContext.getHeaders().add(
"Access-Control-Allow-Headers", "Authorization, Content-Type"
);
responseContext.getHeaders().add(
"Access-Control-Allow-Methods",
"DELETE, HEAD, GET, OPTIONS, POST, PUT"
);
responseContext.getHeaders().add("Access-Control-Max-Age", "86400");
}
}
}

View File

@ -0,0 +1,43 @@
/*
* 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;
import java.io.IOException;
import javax.ws.rs.WebApplicationException;
import javax.ws.rs.ext.WriterInterceptor;
import javax.ws.rs.ext.WriterInterceptorContext;
/**
* Add some headers to every response.
*
* @author <a href="mailto:jens.pelzetter@googlemail.com">Jens Pelzetter</a>
*/
public class DefaultResponseHeaders implements WriterInterceptor {
@Override
public void aroundWriteTo(WriterInterceptorContext context)
throws IOException, WebApplicationException {
context
.getHeaders()
.add("Access-Control-Allow-Origin", "*");
context.proceed();
}
}

View File

@ -0,0 +1,58 @@
/*
* 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;
import javax.ws.rs.container.ContainerRequestContext;
import javax.ws.rs.container.ContainerRequestFilter;
import javax.ws.rs.core.Response;
/**
* A filter which intercepts {@code OPTION} requests to the API and responds
* with the required headers.
*
* A {@code OPTIONS} request is send by browsers to send to endpoints to prevent
* cross site scripting.
*
* @author <a href="mailto:jens.pelzetter@googlemail.com">Jens Pelzetter</a>
*/
public class PreflightRequestFilter implements ContainerRequestFilter {
@Override
public void filter(final ContainerRequestContext requestContext) {
if (requestContext.getMethod().equals("OPTIONS")) {
requestContext.abortWith(
Response
.status(Response.Status.NO_CONTENT)
.header("Connection", "keep-alive")
.header("Access-Control-Allow-Origin", "*")
.header(
"Access-Control-Allow-Headers",
"Authorization, Content-Type"
)
.header(
"Access-Control-Allow-Methods",
"DELETE, HEAD, GET, OPTIONS, POST, PUT"
)
.header("Access-Control-Max-Age", "86400")
.build()
);
}
}
}

View File

@ -0,0 +1,23 @@
/*
* 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
*/
/**
* Utilility classes for RESTful apis of the modules.
*/
package org.libreccm.api;

View File

@ -137,6 +137,7 @@ public abstract class AbstractEntityRepository<K, E> implements Serializable {
* Get the primary key/id of a entity.
*
* @param entity The entity
*
* @return The ID of the provided {@code entity}.
*/
public abstract K getIdOfEntity(E entity);
@ -229,9 +230,9 @@ public abstract class AbstractEntityRepository<K, E> implements Serializable {
return findById(getIdOfEntity(entity), fetchJoins)
.orElseThrow(() -> new IllegalArgumentException(String
.format("No Entity of type \"%s\" with ID %s in the database.",
getEntityClass().getName(),
Objects.toString(getIdOfEntity(entity)))));
.format("No Entity of type \"%s\" with ID %s in the database.",
getEntityClass().getName(),
Objects.toString(getIdOfEntity(entity)))));
}
/**
@ -248,6 +249,10 @@ public abstract class AbstractEntityRepository<K, E> implements Serializable {
return executeCriteriaQuery(createCriteriaQuery());
}
public List<E> findAll(final int limit, final int offset) {
return executeCriteriaQuery(createCriteriaQuery(), limit, offset);
}
@Transactional(Transactional.TxType.REQUIRED)
public List<E> findAll(final String entityGraphName) {
@SuppressWarnings("unchecked")
@ -282,6 +287,18 @@ public abstract class AbstractEntityRepository<K, E> implements Serializable {
return query.getResultList();
}
public List<E> executeCriteriaQuery(
final CriteriaQuery<E> criteriaQuery,
final int limit,
final int offset
) {
return entityManager
.createQuery(criteriaQuery)
.setFirstResult(offset)
.setMaxResults(limit)
.getResultList();
}
public List<E> executeCriteriaQuery(final CriteriaQuery<E> criteriaQuery,
final String graphName) {
@SuppressWarnings("unchecked")

View File

@ -25,11 +25,15 @@ import javax.persistence.Column;
import javax.persistence.Embeddable;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
import java.io.Serializable;
import java.util.Objects;
import static org.libreccm.core.CoreConstants.CORE_XML_NS;
import javax.json.Json;
import javax.json.JsonObjectBuilder;
/**
* An embeddable entity for storing email addresses.
*
@ -122,6 +126,14 @@ public class EmailAddress implements Serializable {
return obj instanceof EmailAddress;
}
public JsonObjectBuilder buildJson() {
return Json
.createObjectBuilder()
.add("address", address)
.add("bouncing", bouncing)
.add("verified", verified);
}
@Override
public String toString() {
return String.format("%s{ "

View File

@ -0,0 +1,34 @@
/*
* 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.core.api;
/**
*
* @author <a href="mailto:jens.pelzetter@googlemail.com">Jens Pelzetter</a>
*/
public final class ApiConstants {
private ApiConstants() {
// Nothing
}
public static final String IDENTIFIER_PREFIX_ID = "ID-";
public static final String IDENTIFIER_PREFIX_UUID = "UUID-";
}

View File

@ -0,0 +1,39 @@
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package org.libreccm.core.api;
import org.libreccm.api.CorsFilter;
import org.libreccm.api.DefaultResponseHeaders;
import org.libreccm.api.PreflightRequestFilter;
import org.libreccm.security.UsersApi;
import java.util.HashSet;
import java.util.Set;
import javax.ws.rs.ApplicationPath;
import javax.ws.rs.core.Application;
/**
*
* @author <a href="mailto:jens.pelzetter@googlemail.com">Jens Pelzetter</a>
*/
@ApplicationPath("/api/ccm-core")
public class CcmCoreApi extends Application {
@Override
public Set<Class<?>> getClasses() {
final Set<Class<?>> classes = new HashSet<>();
classes.add(CorsFilter.class);
classes.add(DefaultResponseHeaders.class);
classes.add(PreflightRequestFilter.class);
classes.add(UsersApi.class);
return classes;
}
}

View File

@ -0,0 +1,46 @@
/*
* 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.core.api;
/**
*
* @author <a href="mailto:jens.pelzetter@googlemail.com">Jens Pelzetter</a>
*/
public class ExtractedIdentifier {
private final IdentifierType type;
private final String identifier;
protected ExtractedIdentifier(
final IdentifierType type, final String identifier
) {
this.type = type;
this.identifier = identifier;
}
public IdentifierType getType() {
return type;
}
public String getIdentifier() {
return identifier;
}
}

View File

@ -0,0 +1,51 @@
/*
* 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.core.api;
import java.util.Objects;
import javax.enterprise.context.Dependent;
/**
*
* @author <a href="mailto:jens.pelzetter@googlemail.com">Jens Pelzetter</a>
*/
@Dependent
public class IdentifierExtractor {
public ExtractedIdentifier extractIdentifier(final String identifierParam) {
Objects.requireNonNull(identifierParam, "identifier param is null.");
if (identifierParam.startsWith(ApiConstants.IDENTIFIER_PREFIX_ID)) {
final String identifier = identifierParam
.substring(ApiConstants.IDENTIFIER_PREFIX_ID.length());
return new ExtractedIdentifier(IdentifierType.ID, identifier);
} else if (identifierParam.startsWith(
ApiConstants.IDENTIFIER_PREFIX_UUID)) {
final String identifier = identifierParam
.substring(ApiConstants.IDENTIFIER_PREFIX_UUID.length());
return new ExtractedIdentifier(IdentifierType.ID, identifier);
} else {
return new ExtractedIdentifier(
IdentifierType.PROPERTY, identifierParam
);
}
}
}

View File

@ -0,0 +1,31 @@
/*
* 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.core.api;
/**
*
* @author <a href="mailto:jens.pelzetter@googlemail.com">Jens Pelzetter</a>
*/
public enum IdentifierType {
ID,
UUID,
PROPERTY
}

View File

@ -0,0 +1,67 @@
/*
* 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.core.api;
import com.google.common.collect.Sets;
import java.util.Collections;
import java.util.Set;
import java.util.function.BiConsumer;
import java.util.function.BinaryOperator;
import java.util.function.Function;
import java.util.function.Supplier;
import java.util.stream.Collector;
import javax.json.Json;
import javax.json.JsonArray;
import javax.json.JsonArrayBuilder;
import javax.json.JsonValue;
/**
*
* @author <a href="mailto:jens.pelzetter@googlemail.com">Jens Pelzetter</a>
*/
public class JsonArrayCollector implements Collector<JsonValue, JsonArrayBuilder, JsonArray>{
@Override
public Supplier<JsonArrayBuilder> supplier() {
return Json::createArrayBuilder;
}
@Override
public BiConsumer<JsonArrayBuilder, JsonValue> accumulator() {
return JsonArrayBuilder::add;
}
@Override
public BinaryOperator<JsonArrayBuilder> combiner() {
return (left, right) -> left.add(right);
}
@Override
public Function<JsonArrayBuilder, JsonArray> finisher() {
return JsonArrayBuilder::build;
}
@Override
public Set<Characteristics> characteristics() {
return Collections.emptySet();
}
}

View File

@ -34,6 +34,9 @@ import static org.libreccm.core.CoreConstants.DB_SCHEMA;
import com.fasterxml.jackson.annotation.ObjectIdGenerators;
import org.libreccm.imexport.Exportable;
import javax.json.Json;
import javax.json.JsonObject;
import javax.json.JsonObjectBuilder;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
@ -55,8 +58,7 @@ import javax.persistence.Table;
@Table(name = "GROUP_MEMBERSHIPS", schema = DB_SCHEMA)
@NamedQueries({
@NamedQuery(name = "GroupMembership.findByUuid",
query = "SELECT m FROM GroupMembership m WHERE m.uuid = :uuid")
,
query = "SELECT m FROM GroupMembership m WHERE m.uuid = :uuid"),
@NamedQuery(name = "GroupMembership.findByGroupAndUser",
query = "SELECT m FROM GroupMembership m "
+ "WHERE m.member = :member AND m.group = :group")})
@ -158,6 +160,30 @@ public class GroupMembership implements Serializable, Exportable {
return obj instanceof GroupMembership;
}
public JsonObjectBuilder buildJson() {
return Json
.createObjectBuilder()
.add("membershipId", membershipId)
.add("uuid", uuid)
.add(
"group",
Json
.createObjectBuilder()
.add("partyId", group.getPartyId())
.add("uuid", group.getUuid())
.add("name", group.getName())
)
.add(
"member",
Json
.createObjectBuilder()
.add("partyId", member.getPartyId())
.add("uuid", member.getUuid())
.add("name", member.getName())
);
}
@Override
public String toString() {
return String.format("%s{ "

View File

@ -23,6 +23,7 @@ import static org.libreccm.core.CoreConstants.*;
import com.fasterxml.jackson.annotation.JsonIdentityInfo;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.ObjectIdGenerators;
import org.libreccm.core.api.JsonArrayCollector;
import javax.persistence.FetchType;
import javax.validation.constraints.NotNull;
@ -35,6 +36,9 @@ import java.util.HashSet;
import java.util.Objects;
import java.util.Set;
import javax.json.Json;
import javax.json.JsonArray;
import javax.json.JsonObjectBuilder;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
@ -67,14 +71,12 @@ import javax.persistence.Table;
),
@NamedQuery(
name = "Party.findByName",
query = "SELECT p FROM Party p WHERE p.name = :name")
,
query = "SELECT p FROM Party p WHERE p.name = :name"),
@NamedQuery(
name = "Party.searchByName",
query = "SELECT p FROM Party p "
+ "WHERE LOWER(p.name) LIKE CONCAT(LOWER(:term), '%') "
+ "ORDER BY p.name")
,
+ "ORDER BY p.name"),
@NamedQuery(
name = "Party.findByRole",
query = "SELECT p FROM Party p "
@ -203,6 +205,29 @@ public class Party implements Serializable {
return obj instanceof Party;
}
public JsonObjectBuilder buildJson() {
final JsonArray array = roleMemberships
.stream()
.map(RoleMembership::buildJson)
.map(JsonObjectBuilder::build)
.collect(new JsonArrayCollector());
return Json
.createObjectBuilder()
.add("partyId", partyId)
.add("uuid", uuid)
.add("name", name)
.add(
"roleMemberships",
roleMemberships
.stream()
.map(RoleMembership::buildJson)
.map(JsonObjectBuilder::build)
.collect(new JsonArrayCollector())
);
}
@Override
public final String toString() {
return toString("");

View File

@ -34,6 +34,8 @@ import static org.libreccm.core.CoreConstants.DB_SCHEMA;
import com.fasterxml.jackson.annotation.ObjectIdGenerators;
import org.libreccm.imexport.Exportable;
import javax.json.Json;
import javax.json.JsonObjectBuilder;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
@ -55,8 +57,7 @@ import javax.persistence.Table;
@Table(name = "ROLE_MEMBERSHIPS", schema = DB_SCHEMA)
@NamedQueries({
@NamedQuery(name = "RoleMembership.findByUuid",
query = "SELECT m FROM RoleMembership m WHERE m.uuid = :uuid")
,
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")
@ -160,6 +161,29 @@ public class RoleMembership implements Serializable, Exportable {
return obj instanceof RoleMembership;
}
public JsonObjectBuilder buildJson() {
return Json
.createObjectBuilder()
.add("membershipId", membershipId)
.add("uuid", uuid)
.add(
"role",
Json
.createObjectBuilder()
.add("roleId", role.getRoleId())
.add("uuid", role.getUuid())
.add("name", role.getName())
)
.add(
"member",
Json
.createObjectBuilder()
.add("partyId", member.getPartyId())
.add("uuid", member.getUuid())
.add("name", member.getName())
);
}
@Override
public String toString() {
return String.format("%s{ "

View File

@ -30,6 +30,7 @@ import java.io.Serializable;
import static org.libreccm.core.CoreConstants.CORE_XML_NS;
import static org.libreccm.core.CoreConstants.DB_SCHEMA;
import org.libreccm.core.api.JsonArrayCollector;
import org.libreccm.imexport.Exportable;
import java.util.ArrayList;
@ -39,6 +40,9 @@ import java.util.List;
import java.util.Objects;
import java.util.Set;
import javax.json.Json;
import javax.json.JsonArrayBuilder;
import javax.json.JsonObjectBuilder;
import javax.persistence.AssociationOverride;
import javax.persistence.CollectionTable;
import javax.persistence.Column;
@ -73,30 +77,25 @@ import javax.xml.bind.annotation.XmlTransient;
@Table(name = "USERS", schema = DB_SCHEMA)
@NamedQueries({
@NamedQuery(name = "User.findByUuid",
query = "SELECT u FROM User u WHERE u.uuid = :uuid")
,
query = "SELECT u FROM User u WHERE u.uuid = :uuid"),
@NamedQuery(name = "User.findByName",
query = "SELECT u FROM User u WHERE u.name = :name "
+ "ORDER BY u.name, "
+ " u.familyName, "
+ " u.givenName, "
+ " u.primaryEmailAddress.address")
,
+ " u.primaryEmailAddress.address"),
@NamedQuery(name = "User.countByName",
query = "SELECT COUNT(u) FROM User u WHERE u.name = :name")
,
query = "SELECT COUNT(u) FROM User u WHERE u.name = :name"),
@NamedQuery(name = "User.findByEmailAddress",
query = "SELECT u FROM User u WHERE "
+ "u.primaryEmailAddress.address = :emailAddress "
+ "ORDER BY u.name, "
+ " u.familyName, "
+ " u.givenName, "
+ " u.primaryEmailAddress.address")
,
+ " u.primaryEmailAddress.address"),
@NamedQuery(name = "User.countByPrimaryEmailAddress",
query = "SELECT COUNT(u) FROM User u "
+ "WHERE u.primaryEmailAddress.address = :emailAddress")
,
+ "WHERE u.primaryEmailAddress.address = :emailAddress"),
@NamedQuery(
name = "User.filterByNameAndEmail",
query = "SELECT u FROM User u WHERE "
@ -107,15 +106,13 @@ import javax.xml.bind.annotation.XmlTransient;
+ "ORDER BY u.name,"
+ "u.familyName, "
+ "u.givenName, "
+ "u.primaryEmailAddress.address")
,
+ "u.primaryEmailAddress.address"),
@NamedQuery(
name = "User.findAllOrderedByUsername",
query = "SELECT u FROM User u ORDER BY u.name, "
+ " u.familyName, "
+ " u.givenName, "
+ " u.primaryEmailAddress.address")
,
+ " u.primaryEmailAddress.address"),
@NamedQuery(name = "User.findByGroup",
query = "SELECT u FROM User u "
+ "JOIN u.groupMemberships m "
@ -126,8 +123,7 @@ import javax.xml.bind.annotation.XmlTransient;
name = "User.withGroupAndRoleMemberships",
attributeNodes = {
@NamedAttributeNode(
value = "groupMemberships")
,
value = "groupMemberships"),
@NamedAttributeNode(
value = "roleMemberships",
subgraph = "role")},
@ -137,8 +133,7 @@ import javax.xml.bind.annotation.XmlTransient;
attributeNodes = {
@NamedAttributeNode(value = "role",
subgraph = "permissions")
})
,
}),
@NamedSubgraph(
name = "permissions",
attributeNodes = {
@ -366,6 +361,40 @@ public class User extends Party implements Serializable, Exportable {
return obj instanceof User;
}
@Override
public JsonObjectBuilder buildJson() {
final JsonArrayBuilder emailAddressesArrayBuilder = Json.createArrayBuilder();
emailAddresses
.stream()
.map(EmailAddress::buildJson)
.forEach(emailAddressesArrayBuilder::add);
return super
.buildJson()
.add("givenName", givenName)
.add("familyName", familyName)
.add("primaryEmailAddress", primaryEmailAddress.buildJson())
.add(
"emailAddresses",
emailAddresses
.stream()
.map(EmailAddress::buildJson)
.map(JsonObjectBuilder::build)
.collect(new JsonArrayCollector())
)
.add("banned", banned)
.add("passwordResetRequired", passwordResetRequired)
.add(
"groupMemberships",
groupMemberships
.stream()
.map(GroupMembership::buildJson)
.map(JsonObjectBuilder::build)
.collect(new JsonArrayCollector())
);
}
@Override
public String toString(final String data) {
return super.toString(String.format(

View File

@ -0,0 +1,131 @@
/*
* 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.security;
import org.apache.shiro.authz.annotation.RequiresAuthentication;
import org.libreccm.core.CoreConstants;
import org.libreccm.core.api.ApiConstants;
import org.libreccm.core.api.ExtractedIdentifier;
import org.libreccm.core.api.IdentifierExtractor;
import org.libreccm.core.api.JsonArrayCollector;
import javax.enterprise.context.RequestScoped;
import javax.inject.Inject;
import javax.json.JsonArray;
import javax.json.JsonObject;
import javax.json.JsonObjectBuilder;
import javax.transaction.Transactional;
import javax.ws.rs.DefaultValue;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.QueryParam;
import javax.ws.rs.WebApplicationException;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
/**
*
* @author <a href="mailto:jens.pelzetter@googlemail.com">Jens Pelzetter</a>
*/
@RequestScoped
@Path("/users")
public class UsersApi {
@Inject
private IdentifierExtractor identifierExtractor;
@Inject
private UserRepository userRepository;
@GET
@Path("/")
@Produces(MediaType.APPLICATION_JSON)
@AuthorizationRequired
@RequiresPrivilege(CoreConstants.PRIVILEGE_ADMIN)
@Transactional(Transactional.TxType.REQUIRED)
public JsonArray getUsers(
@QueryParam("limit") @DefaultValue("20") final int limit,
@QueryParam("offset") @DefaultValue("0") final int offset
) {
return userRepository
.findAll(limit, offset)
.stream()
.map(User::buildJson)
.map(JsonObjectBuilder::build)
.collect(new JsonArrayCollector());
}
@GET
@Path("/{userIdentifier}")
@Produces(MediaType.APPLICATION_JSON)
@AuthorizationRequired
@RequiresPrivilege(CoreConstants.PRIVILEGE_ADMIN)
@Transactional(Transactional.TxType.REQUIRED)
public JsonObject getUser(
final @PathParam("userIdentifier") String identifierParam
) {
final ExtractedIdentifier identifier = identifierExtractor
.extractIdentifier(identifierParam);
switch (identifier.getType()) {
case ID:
return userRepository
.findById(Long.parseLong(identifier.getIdentifier()))
.orElseThrow(
() -> new WebApplicationException(
String.format(
"No user with ID %s found.",
identifier.getIdentifier()
),
Response.Status.NOT_FOUND)
)
.buildJson()
.build();
case UUID:
return userRepository
.findByUuid(identifier.getIdentifier())
.orElseThrow(
() -> new WebApplicationException(
String.format(
"No user with ID %s found.",
identifier.getIdentifier()
),
Response.Status.NOT_FOUND)
)
.buildJson()
.build();
default:
return userRepository
.findByName(identifier.getIdentifier())
.orElseThrow(
() -> new WebApplicationException(
String.format(
"No user with ID %s found.",
identifier.getIdentifier()
),
Response.Status.NOT_FOUND)
)
.buildJson()
.build();
}
}
}