ResourceJsonSerializer.java
package org.entrystore.rest.serializer;
import org.eclipse.rdf4j.model.Model;
import org.eclipse.rdf4j.model.impl.LinkedHashModel;
import org.entrystore.AuthorizationException;
import org.entrystore.Context;
import org.entrystore.ContextManager;
import org.entrystore.Entry;
import org.entrystore.EntryType;
import org.entrystore.GraphType;
import org.entrystore.Group;
import org.entrystore.Metadata;
import org.entrystore.PrincipalManager;
import org.entrystore.Resource;
import org.entrystore.User;
import org.entrystore.impl.DataImpl;
import org.entrystore.impl.RDFResource;
import org.entrystore.impl.RepositoryProperties;
import org.entrystore.impl.StringResource;
import org.entrystore.repository.util.EntryUtil;
import org.entrystore.rest.auth.UserTempLockoutCache;
import org.entrystore.rest.auth.UserTempLockoutCache.UserTemporaryLockout;
import org.entrystore.rest.util.GraphUtil;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import org.restlet.data.MediaType;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.net.URI;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import static org.entrystore.EntryType.*;
import static org.entrystore.GraphType.SystemContext;
import static org.entrystore.PrincipalManager.AccessProperty.*;
public class ResourceJsonSerializer {
private final static Logger log = LoggerFactory.getLogger(ResourceJsonSerializer.class);
public final static JSONObject IMMUTABLE_EMPTY_JSONOBJECT = new JSONObject(Collections.EMPTY_MAP);
private final PrincipalManager pm;
private final ContextManager cm;
private final UserTempLockoutCache userTempLockoutCache;
public ResourceJsonSerializer(PrincipalManager pm, ContextManager cm, UserTempLockoutCache userTempLockoutCache) {
this.pm = pm;
this.cm = cm;
this.userTempLockoutCache = userTempLockoutCache;
}
public JSONObject serializeResourceGroup(Resource resource, MediaType rdfFormat) {
JSONObject resourceObj = new JSONObject();
if (resource instanceof Group group) {
try {
resourceObj.put("name", group.getName());
JSONArray userArray = new JSONArray();
for (User u : group.members()) {
JSONObject childJSON = new JSONObject();
childJSON.put("entryId", u.getEntry().getId());
childJSON.put("name", u.getName());
try {
if (u.isDisabled()) {
childJSON.put("disabled", true);
}
} catch (AuthorizationException ae) {
log.debug("Not allowed to read disabled status of [{}]", u.getEntry().getEntryURI());
}
JSONObject childInfo = GraphUtil.serializeGraphToJson(u.getEntry().getGraph(), rdfFormat);
childJSON.accumulate("info", childInfo);
JSONArray rights = this.serializeRights(u.getEntry());
childJSON.put("rights", rights);
try {
JSONObject childMd = GraphUtil.serializeGraphToJson(u.getEntry().getLocalMetadata().getGraph(), rdfFormat);
childJSON.accumulate(RepositoryProperties.MD_PATH, childMd);
} catch (AuthorizationException ae) {
//childJSON.accumulate("noAccessToMetadata", true);
//TODO: Replaced by using "rights" in json, do something else in this catch-clause
}
//Relations for every user in this group.
if (u.getEntry().getRelations() != null) {
Model childRelationsGraph = new LinkedHashModel(u.getEntry().getRelations());
JSONObject childRelationObj = GraphUtil.serializeGraphToJson(childRelationsGraph, rdfFormat);
childJSON.accumulate(RepositoryProperties.RELATION, childRelationObj);
}
userArray.put(childJSON);
}
resourceObj.put("children", userArray);
} catch (AuthorizationException ae) {
//jdilObj.accumulate("noAccessToResource", true);
//TODO: Replaced by using "rights" in json, do something else in this catch-clause
}
} else {
throw new IllegalArgumentException("Resource not instance of Group");
}
return resourceObj;
}
public JSONObject serializeResourceUser(Resource resource) {
JSONObject resourceObj = new JSONObject();
if (resource instanceof User user) {
try {
resourceObj.put("name", user.getName());
Context homeContext = user.getHomeContext();
if (homeContext != null) {
resourceObj.put("homecontext", homeContext.getEntry().getId());
}
String prefLang = user.getLanguage();
if (prefLang != null) {
resourceObj.put("language", prefLang);
}
if (user.isDisabled()) {
resourceObj.put("disabled", true);
}
UserTemporaryLockout lockedOutUser = userTempLockoutCache.getLockedOutUser(user.getName());
if (lockedOutUser != null) {
resourceObj.put("disabledUntil", lockedOutUser.disableUntil());
}
JSONObject customProperties = new JSONObject();
for (java.util.Map.Entry<String, String> propEntry : user.getCustomProperties().entrySet()) {
customProperties.put(propEntry.getKey(), propEntry.getValue());
}
resourceObj.put("customProperties", customProperties);
} catch (AuthorizationException ae) {
//jdilObj.accumulate("noAccessToResource", true);
// TODO: Replaced by using "rights" in json, do something else in this catch-clause
}
} else {
throw new IllegalArgumentException("Resource not instance of User");
}
return resourceObj;
}
public JSONObject serializeResourceList(Resource resource, ResourceJsonSerializer.ListParams params, MediaType rdfFormat) {
JSONObject resourceObj = new JSONObject();
if (resource instanceof org.entrystore.List list) {
int limit = Math.min(params.limit(), 100);
int offset = Math.max(params.offset(), 0);
try {
JSONArray childrenArray = new JSONArray();
int maxPos = offset + limit;
if (limit == 0) {
maxPos = Integer.MAX_VALUE;
}
List<URI> childrenURIs = list.getChildren();
Set<String> childrenIDs = new HashSet<>();
List<Entry> childrenEntries = new ArrayList<>();
for (URI uri : childrenURIs) {
String u = uri.toString();
String id = u.substring(u.lastIndexOf('/') + 1);
childrenIDs.add(id);
Entry childEntry = list.getEntry().getContext().get(id);
if (childEntry != null) {
childrenEntries.add(childEntry);
} else {
log.warn("Child resource [{}] in context [{}] does not exist, but is referenced by a list.", id, cm.getURI());
}
}
if (params.sort() != null && (childrenEntries.size() < 501)) {
Date before = new Date();
GraphType prioritizedGraphType = null;
if (params.prio() != null) {
prioritizedGraphType = GraphType.valueOf(params.prio());
}
String sortType = params.sort();
if ("title".equalsIgnoreCase(sortType)) {
EntryUtil.sortAfterTitle(childrenEntries, params.lang(), params.ascendingOrder(), prioritizedGraphType);
} else if ("modified".equalsIgnoreCase(sortType)) {
EntryUtil.sortAfterModificationDate(childrenEntries, params.ascendingOrder(), prioritizedGraphType);
} else if ("created".equalsIgnoreCase(sortType)) {
EntryUtil.sortAfterCreationDate(childrenEntries, params.ascendingOrder(), prioritizedGraphType);
} else if ("size".equalsIgnoreCase(sortType)) {
EntryUtil.sortAfterFileSize(childrenEntries, params.ascendingOrder(), prioritizedGraphType);
}
long sortDuration = new Date().getTime() - before.getTime();
log.debug("List resource sorting took " + sortDuration + " ms");
} else if (params.sort() != null) {
log.warn("Ignoring sort parameter for performance reasons because list has more than 500 children");
}
//for (int i = 0; i < childrenURIs.size(); i++) {
for (int i = offset; i < maxPos && i < childrenEntries.size(); i++) {
JSONObject childJSON = new JSONObject();
Entry childEntry = childrenEntries.get(i);
/*
* Children-rights
*/
// this.accumulateRights(childEntry, childJSON);
JSONArray rights = this.serializeRights(childEntry);
childJSON.put("rights", rights);
String uri = childEntry.getEntryURI().toString();
String entryId = uri.substring(uri.lastIndexOf('/') + 1);
childJSON.put("entryId", entryId);
GraphType childGraphType = childEntry.getGraphType();
EntryType childEntryType = childEntry.getEntryType();
if ((childGraphType == GraphType.Context || childGraphType == SystemContext) && childEntryType == Local) {
childJSON.put("name", cm.getName(childEntry.getResourceURI()));
} else if (childGraphType == GraphType.List) {
Resource childResource = childEntry.getResource();
if (childResource instanceof org.entrystore.List childList) {
try {
childJSON.put("size", childList.getChildren().size());
} catch (AuthorizationException ae) {
//TODO: Should we do something here?
}
} else {
log.warn("Entry has ResourceType.List but the resource is either null or not an instance of List");
}
} else if (childGraphType == GraphType.User && childEntryType == Local) {
childJSON.put("name", ((User) childEntry.getResource()).getName());
} else if (childGraphType == GraphType.Group && childEntryType == Local) {
childJSON.put("name", ((Group) childEntry.getResource()).getName());
}
try {
EntryType entryType = childEntry.getEntryType();
if (entryType == Reference || entryType == LinkReference) {
Metadata cachedExternalMetaData = childEntry.getCachedExternalMetadata();
if (cachedExternalMetaData != null) {
Model cachedExternalMetaDataGraph = cachedExternalMetaData.getGraph();
if (cachedExternalMetaDataGraph != null) {
JSONObject childCachedExternalMetaDataJSON = GraphUtil.serializeGraphToJson(cachedExternalMetaDataGraph, rdfFormat);
childJSON.accumulate(RepositoryProperties.EXTERNAL_MD_PATH, childCachedExternalMetaDataJSON);
}
}
}
if (entryType == Local || entryType == Link || entryType == LinkReference) {
Metadata localMetadata = childEntry.getLocalMetadata();
if (localMetadata != null) {
Model localMetadataGraph = localMetadata.getGraph();
if (localMetadataGraph != null) {
JSONObject localMDJSON = GraphUtil.serializeGraphToJson(localMetadataGraph, rdfFormat);
childJSON.accumulate(RepositoryProperties.MD_PATH, localMDJSON);
}
}
}
} catch (AuthorizationException e) {
//childJSON.accumulate("noAccessToMetadata", true);
//ODO: Replaced by using "rights" in json, do something else in this catch-clause
// childJSON.accumulate(RepositoryProperties.MD_PATH_STUB, new JSONObject());
}
Model childEntryGraph = childEntry.getGraph();
JSONObject childInfo = GraphUtil.serializeGraphToJson(childEntryGraph, rdfFormat);
childJSON.accumulate("info", childInfo);
if (childEntry.getRelations() != null) {
Model childRelationsGraph = new LinkedHashModel(childEntry.getRelations());
JSONObject childRelationObj = GraphUtil.serializeGraphToJson(childRelationsGraph, rdfFormat);
childJSON.accumulate(RepositoryProperties.RELATION, childRelationObj);
}
childrenArray.put(childJSON);
}
resourceObj.put("children", childrenArray);
resourceObj.put("size", childrenURIs.size());
resourceObj.put("limit", limit);
resourceObj.put("offset", offset);
JSONArray childrenIDArray = new JSONArray();
for (String id : childrenIDs) {
childrenIDArray.put(id);
}
resourceObj.put("allUnsorted", childrenIDArray);
} catch (AuthorizationException ae) {
//jdilObj.accumulate("noAccessToResource", true);
//TODO: Replaced by using "rights" in json, do something else in this catch-clause
}
} else {
throw new IllegalArgumentException("Resource not instance of List");
}
return resourceObj;
}
public JSONObject serializeResourceGraph(Resource resource, MediaType rdfFormat) {
if (resource instanceof RDFResource rdf) {
if (rdf.getGraph() != null) {
return GraphUtil.serializeGraphToJson(rdf.getGraph(), rdfFormat);
} else {
throw new IllegalArgumentException("Graph is empty in RDFResource");
}
} else {
throw new IllegalArgumentException("Resource not instance of RDFResource");
}
}
public JSONObject serializeResourcePipeline(Resource resource, MediaType rdfFormat) {
return serializeResourceGraph(resource, rdfFormat);
}
public String serializeResourceString(Resource resource) {
if (resource instanceof StringResource string) {
return string.getString();
} else {
throw new IllegalArgumentException("Resource not instance of StringResource");
}
}
public JSONObject serializeResourceNone(Resource resource) {
JSONObject resourceObj = new JSONObject();
DataImpl data = new DataImpl(resource.getEntry());
String digest = data.readDigest();
if (digest != null) {
resourceObj.put("sha256", digest);
} else {
log.debug("Digest does not exist for [{}]", resource.getURI());
}
return resourceObj;
}
public JSONArray serializeResourceContext(Resource resource) {
JSONArray array = new JSONArray();
if (resource instanceof Context context) {
Set<URI> uris = context.getEntries();
for(URI u: uris) {
String entryId = (u.toASCIIString()).substring((u.toASCIIString()).lastIndexOf('/')+1);
array.put(entryId);
}
} else {
throw new IllegalArgumentException("Resource not instance of Context");
}
return array;
}
public JSONArray serializeResourceSystemContext(Resource resource) {
return serializeResourceContext(resource);
}
public void accumulateRights(Entry entry, JSONObject jdilObj) throws JSONException {
Set<PrincipalManager.AccessProperty> rights = pm.getRights(entry);
if(rights.size() >0){
for(PrincipalManager.AccessProperty ap : rights){
if(ap == Administer)
jdilObj.append("rights", "administer");
else if (ap == WriteMetadata)
jdilObj.append("rights", "writemetadata");
else if (ap == WriteResource)
jdilObj.append("rights","writeresource");
else if (ap == ReadMetadata)
jdilObj.append("rights","readmetadata");
else if (ap == ReadResource)
jdilObj.append("rights","readresource");
}
}
}
public JSONArray serializeRights(Entry entry) throws JSONException {
JSONArray resourceObj = new JSONArray();
Set<PrincipalManager.AccessProperty> rights = pm.getRights(entry);
rights.forEach(ap -> {
switch (ap) {
case Administer -> resourceObj.put("administer");
case WriteMetadata -> resourceObj.put("writemetadata");
case WriteResource -> resourceObj.put("writeresource");
case ReadMetadata -> resourceObj.put("readmetadata");
case ReadResource -> resourceObj.put("readresource");
}
});
return resourceObj;
}
public record ListParams(
String sort,
String lang,
String prio,
String desc,
boolean ascendingOrder,
int offset,
int limit) {
public ListParams(Map<String, String> params) {
this(
params.get("sort"),
params.get("lang"),
params.get("prio"),
params.get("desc"),
!"desc".equalsIgnoreCase(params.get("order")),
Integer.parseInt(params.getOrDefault("offset", "0")),
Integer.parseInt(params.getOrDefault("limit", "0"))
);
}
}
}