diff --git a/.gitignore b/.gitignore
index 3e32cc4d3..6c5749265 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,3 +1,4 @@
+nb-configuration.xml
node
node_modules
runtime
diff --git a/ccm-core/src/main/java/org/libreccm/api/CorsFilter.java b/ccm-core/src/main/java/org/libreccm/api/CorsFilter.java
new file mode 100644
index 000000000..769941604
--- /dev/null
+++ b/ccm-core/src/main/java/org/libreccm/api/CorsFilter.java
@@ -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 Jens Pelzetter
+ */
+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");
+ }
+ }
+
+}
diff --git a/ccm-core/src/main/java/org/libreccm/api/DefaultResponseHeaders.java b/ccm-core/src/main/java/org/libreccm/api/DefaultResponseHeaders.java
new file mode 100644
index 000000000..e10a66fac
--- /dev/null
+++ b/ccm-core/src/main/java/org/libreccm/api/DefaultResponseHeaders.java
@@ -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 Jens Pelzetter
+ */
+public class DefaultResponseHeaders implements WriterInterceptor {
+
+ @Override
+ public void aroundWriteTo(WriterInterceptorContext context)
+ throws IOException, WebApplicationException {
+ context
+ .getHeaders()
+ .add("Access-Control-Allow-Origin", "*");
+ context.proceed();
+ }
+
+}
diff --git a/ccm-core/src/main/java/org/libreccm/api/PreflightRequestFilter.java b/ccm-core/src/main/java/org/libreccm/api/PreflightRequestFilter.java
new file mode 100644
index 000000000..b201617a5
--- /dev/null
+++ b/ccm-core/src/main/java/org/libreccm/api/PreflightRequestFilter.java
@@ -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 Jens Pelzetter
+ */
+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()
+ );
+ }
+ }
+
+}
diff --git a/ccm-core/src/main/java/org/libreccm/api/package-info.java b/ccm-core/src/main/java/org/libreccm/api/package-info.java
new file mode 100644
index 000000000..206797864
--- /dev/null
+++ b/ccm-core/src/main/java/org/libreccm/api/package-info.java
@@ -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;
diff --git a/ccm-core/src/main/java/org/libreccm/core/AbstractEntityRepository.java b/ccm-core/src/main/java/org/libreccm/core/AbstractEntityRepository.java
index e33c8b1ea..1f303a273 100644
--- a/ccm-core/src/main/java/org/libreccm/core/AbstractEntityRepository.java
+++ b/ccm-core/src/main/java/org/libreccm/core/AbstractEntityRepository.java
@@ -135,12 +135,13 @@ public abstract class AbstractEntityRepository 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);
-
+
/**
* Finds an entity by it ID.
*
@@ -223,15 +224,15 @@ public abstract class AbstractEntityRepository implements Serializable {
return Optional.empty();
}
}
-
+
@Transactional(Transactional.TxType.REQUIRED)
public E reload(final E entity, final String... fetchJoins) {
-
+
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 implements Serializable {
return executeCriteriaQuery(createCriteriaQuery());
}
+ public List findAll(final int limit, final int offset) {
+ return executeCriteriaQuery(createCriteriaQuery(), limit, offset);
+ }
+
@Transactional(Transactional.TxType.REQUIRED)
public List findAll(final String entityGraphName) {
@SuppressWarnings("unchecked")
@@ -282,6 +287,18 @@ public abstract class AbstractEntityRepository implements Serializable {
return query.getResultList();
}
+ public List executeCriteriaQuery(
+ final CriteriaQuery criteriaQuery,
+ final int limit,
+ final int offset
+ ) {
+ return entityManager
+ .createQuery(criteriaQuery)
+ .setFirstResult(offset)
+ .setMaxResults(limit)
+ .getResultList();
+ }
+
public List executeCriteriaQuery(final CriteriaQuery criteriaQuery,
final String graphName) {
@SuppressWarnings("unchecked")
diff --git a/ccm-core/src/main/java/org/libreccm/core/EmailAddress.java b/ccm-core/src/main/java/org/libreccm/core/EmailAddress.java
index 6ac47fbe4..3d012d596 100644
--- a/ccm-core/src/main/java/org/libreccm/core/EmailAddress.java
+++ b/ccm-core/src/main/java/org/libreccm/core/EmailAddress.java
@@ -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{ "
diff --git a/ccm-core/src/main/java/org/libreccm/core/api/ApiConstants.java b/ccm-core/src/main/java/org/libreccm/core/api/ApiConstants.java
new file mode 100644
index 000000000..31d6489e1
--- /dev/null
+++ b/ccm-core/src/main/java/org/libreccm/core/api/ApiConstants.java
@@ -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 Jens Pelzetter
+ */
+public final class ApiConstants {
+
+ private ApiConstants() {
+ // Nothing
+ }
+
+ public static final String IDENTIFIER_PREFIX_ID = "ID-";
+ public static final String IDENTIFIER_PREFIX_UUID = "UUID-";
+
+}
diff --git a/ccm-core/src/main/java/org/libreccm/core/api/CcmCoreApi.java b/ccm-core/src/main/java/org/libreccm/core/api/CcmCoreApi.java
new file mode 100644
index 000000000..4c4802bb7
--- /dev/null
+++ b/ccm-core/src/main/java/org/libreccm/core/api/CcmCoreApi.java
@@ -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 Jens Pelzetter
+ */
+@ApplicationPath("/api/ccm-core")
+public class CcmCoreApi extends Application {
+
+ @Override
+ public Set> getClasses() {
+
+ final Set> classes = new HashSet<>();
+ classes.add(CorsFilter.class);
+ classes.add(DefaultResponseHeaders.class);
+ classes.add(PreflightRequestFilter.class);
+ classes.add(UsersApi.class);
+ return classes;
+ }
+
+
+
+}
diff --git a/ccm-core/src/main/java/org/libreccm/core/api/ExtractedIdentifier.java b/ccm-core/src/main/java/org/libreccm/core/api/ExtractedIdentifier.java
new file mode 100644
index 000000000..faf20ca54
--- /dev/null
+++ b/ccm-core/src/main/java/org/libreccm/core/api/ExtractedIdentifier.java
@@ -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 Jens Pelzetter
+ */
+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;
+ }
+
+}
diff --git a/ccm-core/src/main/java/org/libreccm/core/api/IdentifierExtractor.java b/ccm-core/src/main/java/org/libreccm/core/api/IdentifierExtractor.java
new file mode 100644
index 000000000..d0894e1d2
--- /dev/null
+++ b/ccm-core/src/main/java/org/libreccm/core/api/IdentifierExtractor.java
@@ -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 Jens Pelzetter
+ */
+@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
+ );
+ }
+ }
+
+}
diff --git a/ccm-core/src/main/java/org/libreccm/core/api/IdentifierType.java b/ccm-core/src/main/java/org/libreccm/core/api/IdentifierType.java
new file mode 100644
index 000000000..a2a2b1551
--- /dev/null
+++ b/ccm-core/src/main/java/org/libreccm/core/api/IdentifierType.java
@@ -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 Jens Pelzetter
+ */
+public enum IdentifierType {
+
+ ID,
+ UUID,
+ PROPERTY
+
+}
diff --git a/ccm-core/src/main/java/org/libreccm/core/api/JsonArrayCollector.java b/ccm-core/src/main/java/org/libreccm/core/api/JsonArrayCollector.java
new file mode 100644
index 000000000..94a9730a6
--- /dev/null
+++ b/ccm-core/src/main/java/org/libreccm/core/api/JsonArrayCollector.java
@@ -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 Jens Pelzetter
+ */
+public class JsonArrayCollector implements Collector{
+
+ @Override
+ public Supplier supplier() {
+ return Json::createArrayBuilder;
+ }
+
+ @Override
+ public BiConsumer accumulator() {
+ return JsonArrayBuilder::add;
+ }
+
+ @Override
+ public BinaryOperator combiner() {
+ return (left, right) -> left.add(right);
+ }
+
+ @Override
+ public Function finisher() {
+ return JsonArrayBuilder::build;
+ }
+
+ @Override
+ public Set characteristics() {
+ return Collections.emptySet();
+ }
+
+}
diff --git a/ccm-core/src/main/java/org/libreccm/security/GroupMembership.java b/ccm-core/src/main/java/org/libreccm/security/GroupMembership.java
index fcb19541f..fe18e4c81 100644
--- a/ccm-core/src/main/java/org/libreccm/security/GroupMembership.java
+++ b/ccm-core/src/main/java/org/libreccm/security/GroupMembership.java
@@ -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{ "
diff --git a/ccm-core/src/main/java/org/libreccm/security/Party.java b/ccm-core/src/main/java/org/libreccm/security/Party.java
index e0be03b9c..0cad09b9c 100644
--- a/ccm-core/src/main/java/org/libreccm/security/Party.java
+++ b/ccm-core/src/main/java/org/libreccm/security/Party.java
@@ -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 "
@@ -99,7 +101,7 @@ public class Party implements Serializable {
@GeneratedValue(strategy = GenerationType.AUTO)
@XmlElement(name = "party-id", namespace = CORE_XML_NS)
private long partyId;
-
+
@Column(name = "UUID", unique = true, nullable = false)
@XmlElement(name = "uuid", namespace = CORE_XML_NS)
private String uuid;
@@ -134,11 +136,11 @@ public class Party implements Serializable {
protected void setPartyId(final long partyId) {
this.partyId = partyId;
}
-
+
public String getUuid() {
return uuid;
}
-
+
protected void setUuid(final String uuid) {
this.uuid = uuid;
}
@@ -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("");
diff --git a/ccm-core/src/main/java/org/libreccm/security/RoleMembership.java b/ccm-core/src/main/java/org/libreccm/security/RoleMembership.java
index 449169f80..acea95f50 100644
--- a/ccm-core/src/main/java/org/libreccm/security/RoleMembership.java
+++ b/ccm-core/src/main/java/org/libreccm/security/RoleMembership.java
@@ -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{ "
diff --git a/ccm-core/src/main/java/org/libreccm/security/User.java b/ccm-core/src/main/java/org/libreccm/security/User.java
index e34c1a932..ce71aa92c 100644
--- a/ccm-core/src/main/java/org/libreccm/security/User.java
+++ b/ccm-core/src/main/java/org/libreccm/security/User.java
@@ -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(
diff --git a/ccm-core/src/main/java/org/libreccm/security/UsersApi.java b/ccm-core/src/main/java/org/libreccm/security/UsersApi.java
new file mode 100644
index 000000000..b14e840a0
--- /dev/null
+++ b/ccm-core/src/main/java/org/libreccm/security/UsersApi.java
@@ -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 Jens Pelzetter
+ */
+@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();
+ }
+ }
+
+}