GroupResource.java

/*
 * Copyright (c) 2007-2017 MetaSolutions AB
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.entrystore.rest.resources;

import com.google.common.collect.Sets;
import org.entrystore.AuthorizationException;
import org.entrystore.Context;
import org.entrystore.Entry;
import org.entrystore.GraphType;
import org.entrystore.Group;
import org.entrystore.PrincipalManager.AccessProperty;
import org.entrystore.repository.config.Settings;
import org.restlet.data.Status;
import org.restlet.representation.Representation;
import org.restlet.resource.Post;
import org.restlet.resource.ResourceException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.net.URI;
import java.net.URLDecoder;
import java.nio.charset.StandardCharsets;


/**
 * A resource that allows normal users to create a group with a linked context.
 * This resource is an exception as it does not require admin-rights. This feature
 * needs to be explicitly activated in the configuration (the approach may change
 * in the future).
 *
 * @author Hannes Ebner
 */
public class GroupResource extends BaseResource {

	static Logger log = LoggerFactory.getLogger(GroupResource.class);

	/**
	 * Creates a group with linked context.
	 *
	 * @param r Request body
	 */
	@Post
	public void acceptRepresentation(Representation r) throws ResourceException {
		URI requestingUser = getPM().getAuthenticatedUserURI();

		try {
			// guests are prohibited from using this resource
			if (requestingUser == null || getPM().getGuestUser().getURI().equals(requestingUser)) {
				getResponse().setStatus(Status.CLIENT_ERROR_FORBIDDEN);
				return;
			}

			if (!getRM().getConfiguration().getBoolean(Settings.NONADMIN_GROUPCONTEXT_CREATION, false)) {
				try {
					// only admin users are allowed to create a group in this case
					if (!getPM().getAdminUser().getURI().equals(requestingUser) &&
							!getPM().getAdminGroup().isMember(getPM().getUser(requestingUser))) {
						getResponse().setStatus(Status.CLIENT_ERROR_FORBIDDEN);
						return;
					}
				} catch (AuthorizationException ae) {
					getResponse().setStatus(Status.CLIENT_ERROR_FORBIDDEN);
					return;
				}
			}

			String name = null;
			String contextId = null;
			boolean setName = false;
			// read name, to be used for group and context
			if (parameters.containsKey("name")) {
				name = URLDecoder.decode(parameters.get("name"), StandardCharsets.UTF_8).trim();
				setName = !name.isEmpty();
			}

			// we allow to manually set the context entry ID
			if (parameters.containsKey("contextId")) {
				contextId = URLDecoder.decode(parameters.get("contextId"), StandardCharsets.UTF_8).trim();
				if (contextId.isEmpty()) {
					contextId = null;
				}
			}

			// we need admin-rights to create groups and contexts
			getPM().setAuthenticatedUserURI(getPM().getAdminUser().getURI());

			// check whether context or group with desired name already exists
			// and abort execution of request if necessary
			if (setName && getPM().getPrincipalEntry(name) != null || getCM().getContextURI(name) != null) {
				getResponse().setStatus(Status.CLIENT_ERROR_CONFLICT);
				return;
			}

			if (contextId != null && getCM().getContext(contextId) != null) {
				getResponse().setStatus(Status.CLIENT_ERROR_CONFLICT);
				return;
			}

			// create entry for new group
			Entry newGroupEntry = getCM().getContext("_principals").createResource(null, GraphType.Group, null, null);
			// make the requesting user admin for group
			newGroupEntry.setAllowedPrincipalsFor(AccessProperty.Administer, Sets.newHashSet(requestingUser));
			// change creator from admin to requesting user
			newGroupEntry.setCreator(requestingUser);

			Group newGroup = (Group) newGroupEntry.getResource();
			// make requesting user a group member
			newGroup.addMember(getPM().getUser(requestingUser));

			if (setName) {
				// set name of the group
				newGroup.setName(name);
			}

			// create entry for new context
			Entry newContextEntry = getCM().getContext("_contexts").createResource(contextId, GraphType.Context, null, null);
			// make the requesting user admin for context
			newContextEntry.setAllowedPrincipalsFor(AccessProperty.Administer, Sets.newHashSet(requestingUser));
			// new group gets write access for context
			newContextEntry.setAllowedPrincipalsFor(AccessProperty.WriteResource, Sets.newHashSet(newGroupEntry.getResourceURI()));
			// change creator from admin to requesting user
			newContextEntry.setCreator(requestingUser);

			Context newContext = (Context) newContextEntry.getResource();

			if (setName) {
				// set name of the new context
				getCM().setName(newContextEntry.getEntryURI(), name);
			}

			// set the group's home context to the newly created context
			newGroup.setHomeContext(newContext);

			// return HTTP 201 with the newly created group as Location-header
			getResponse().setStatus(Status.SUCCESS_CREATED);
			getResponse().setLocationRef(newGroupEntry.getEntryURI().toString());
			getResponse().setEntity(createEmptyRepresentationWithLastModified(newGroupEntry.getModifiedDate()));
		} finally {
			getPM().setAuthenticatedUserURI(requestingUser);
		}
	}

}