All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.structr.web.resource.RegistrationResource Maven / Gradle / Ivy

Go to download

Structr is an open source framework based on the popular Neo4j graph database.

The newest version!
/**
 * Copyright (C) 2010-2016 Structr GmbH
 *
 * This file is part of Structr .
 *
 * Structr is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as
 * published by the Free Software Foundation, either version 3 of the
 * License, or (at your option) any later version.
 *
 * Structr 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 Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with Structr.  If not, see .
 */
package org.structr.web.resource;

import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.UUID;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.commons.lang3.StringUtils;
import org.structr.common.MailHelper;
import org.structr.common.SecurityContext;
import org.structr.common.error.FrameworkException;
import org.structr.core.Result;
import org.structr.core.Services;
import org.structr.core.app.App;
import org.structr.core.app.Query;
import org.structr.core.app.StructrApp;
import org.structr.core.auth.Authenticator;
import org.structr.core.entity.AbstractNode;
import org.structr.core.entity.MailTemplate;
import org.structr.core.entity.Person;
import org.structr.core.entity.Principal;
import org.structr.core.graph.NodeFactory;
import org.structr.core.property.PropertyKey;
import org.structr.core.property.PropertyMap;
import org.structr.rest.RestMethodResult;
import org.structr.rest.auth.AuthHelper;
import org.structr.rest.exception.NotAllowedException;
import org.structr.rest.resource.Resource;
import org.structr.rest.service.HttpService;
import org.structr.web.entity.User;
import org.structr.web.servlet.HtmlServlet;

//~--- classes ----------------------------------------------------------------

/**
 * A resource to register new users
 *
 *
 */
public class RegistrationResource extends Resource {

	private static final Logger logger = Logger.getLogger(RegistrationResource.class.getName());

	public static final String CUSTOM_ATTRIBUTES                      = "Registration.customUserAttributes";
	public static final String ALLOW_LOGIN_BEFORE_CONFIRMATION        = "Registration.allowLoginBeforeConfirmation";

	private enum TemplateKey {
		SENDER_NAME,
		SENDER_ADDRESS,
		SUBJECT,
		TEXT_BODY,
		HTML_BODY,
		BASE_URL,
		TARGET_PAGE,
		ERROR_PAGE,
		CONFIRM_REGISTRATION_PAGE,
		CONFIRM_KEY_KEY,
		TARGET_PAGE_KEY,
		ERROR_PAGE_KEY
	}

	private static String localeString;
	private static String confKey;

	//~--- methods --------------------------------------------------------

	@Override
	public boolean checkAndConfigure(String part, SecurityContext securityContext, HttpServletRequest request) {

		this.securityContext = securityContext;

		return (getUriPart().equals(part));

	}

	@Override
	public Result doGet(PropertyKey sortKey, boolean sortDescending, int pageSize, int page, String offsetId) throws FrameworkException {
		throw new NotAllowedException("GET not allowed on " + getResourceSignature());
	}

	@Override
	public RestMethodResult doPut(Map propertySet) throws FrameworkException {
		throw new NotAllowedException("PUT not allowed on " + getResourceSignature());
	}

	@Override
	public RestMethodResult doPost(Map propertySet) throws FrameworkException {

		boolean existingUser = false;

		if (propertySet.containsKey(User.eMail.jsonName())) {

			final Principal user;

			final String emailString  = (String) propertySet.get(User.eMail.jsonName());

			if (StringUtils.isEmpty(emailString)) {
				return new RestMethodResult(HttpServletResponse.SC_BAD_REQUEST);
			}

			localeString = (String) propertySet.get(MailTemplate.locale.jsonName());
			confKey = UUID.randomUUID().toString();

			final Result result = StructrApp.getInstance().nodeQuery(User.class).and(User.eMail, emailString).getResult();
			if (!result.isEmpty()) {

				user = (Principal) result.get(0);

				// For existing users, update confirmation key
				user.setProperty(User.confirmationKey, confKey);

				existingUser = true;


			} else {

				final Authenticator auth = securityContext.getAuthenticator();
				user = createUser(securityContext, User.eMail, emailString, propertySet, auth.getUserAutoCreate(), auth.getUserClass());
			}

			if (user != null) {

				if (!sendInvitationLink(user, propertySet)) {

					// return 400 Bad request
					return new RestMethodResult(HttpServletResponse.SC_BAD_REQUEST);

				}

				// If we have just updated the confirmation key for an existing user,
				// return 200 to distinguish from new users
				if (existingUser) {

					// return 200 OK
					return new RestMethodResult(HttpServletResponse.SC_OK);

				} else {

					// return 201 Created
					return new RestMethodResult(HttpServletResponse.SC_CREATED);

				}

			} else {

				// return 400 Bad request
				return new RestMethodResult(HttpServletResponse.SC_BAD_REQUEST);

			}


		} else {

			// return 400 Bad request
			return new RestMethodResult(HttpServletResponse.SC_BAD_REQUEST);

		}

	}

	@Override
	public RestMethodResult doOptions() throws FrameworkException {
		throw new NotAllowedException("OPTIONS not allowed on " + getResourceSignature());
	}

	@Override
	public Resource tryCombineWith(Resource next) throws FrameworkException {

		return null;

	}

	private boolean sendInvitationLink(final Principal user, final Map propertySetFromUserPOST) {

		Map replacementMap = new HashMap();

		// Populate the replacement map with all POSTed values
		// WARNING! This is unchecked user input!!
		populateReplacementMap(replacementMap, propertySetFromUserPOST);

		final String userEmail = user.getProperty(User.eMail);
		final String appHost   = Services.getInstance().getConfigurationValue(HttpService.APPLICATION_HOST);
		final String httpPort  = Services.getInstance().getConfigurationValue(HttpService.APPLICATION_HTTP_PORT);

		replacementMap.put(toPlaceholder(User.eMail.jsonName()), userEmail);
		replacementMap.put(toPlaceholder("link"),
			getTemplateText(TemplateKey.BASE_URL, "http://" + appHost + ":" + httpPort)
			      + getTemplateText(TemplateKey.CONFIRM_REGISTRATION_PAGE, HtmlServlet.CONFIRM_REGISTRATION_PAGE)
			+ "?" + getTemplateText(TemplateKey.CONFIRM_KEY_KEY, HtmlServlet.CONFIRM_KEY_KEY) + "=" + confKey
			+ "&" + getTemplateText(TemplateKey.TARGET_PAGE_KEY, HtmlServlet.TARGET_PAGE_KEY) + "=" + getTemplateText(TemplateKey.TARGET_PAGE, "register_thanks")
			+ "&" + getTemplateText(TemplateKey.ERROR_PAGE_KEY, HtmlServlet.ERROR_PAGE_KEY)   + "=" + getTemplateText(TemplateKey.ERROR_PAGE, "register_error"));

		String textMailTemplate = getTemplateText(TemplateKey.TEXT_BODY, "Go to ${link} to finalize registration.");
		String htmlMailTemplate = getTemplateText(TemplateKey.HTML_BODY, "
Click here to finalize registration.
"); String textMailContent = MailHelper.replacePlaceHoldersInTemplate(textMailTemplate, replacementMap); String htmlMailContent = MailHelper.replacePlaceHoldersInTemplate(htmlMailTemplate, replacementMap); try { MailHelper.sendHtmlMail( getTemplateText(TemplateKey.SENDER_ADDRESS, "structr-mail-daemon@localhost"), getTemplateText(TemplateKey.SENDER_NAME, "Structr Mail Daemon"), userEmail, "", null, null, null, getTemplateText(TemplateKey.SUBJECT, "Welcome to Structr, please finalize registration"), htmlMailContent, textMailContent); } catch (Exception e) { logger.log(Level.SEVERE, "Unable to send registration e-mail", e); return false; } return true; } private String getTemplateText(final TemplateKey key, final String defaultValue) { try { final Query query = StructrApp.getInstance().nodeQuery(MailTemplate.class).andName(key.name()); if (localeString != null) { query.and(MailTemplate.locale, localeString); } MailTemplate template = query.getFirst(); if (template != null) { final String text = template.getProperty(MailTemplate.text); return text != null ? text : defaultValue; } else { return defaultValue; } } catch (FrameworkException ex) { Logger.getLogger(RegistrationResource.class.getName()).log(Level.WARNING, "Could not get mail template for key " + key, ex); } return null; } private static void populateReplacementMap(final Map replacementMap, final Map props) { for (Entry entry : props.entrySet()) { replacementMap.put(toPlaceholder(entry.getKey()), entry.getValue().toString()); } } private static String toPlaceholder(final String key) { return "${".concat(key).concat("}"); } /** * Create a new user. * * If a {@link Person} is found, convert that object to a {@link User} object. * Do not auto-create a new user. * * @param securityContext * @param credentialKey * @param credentialValue * @return user */ public static Principal createUser(final SecurityContext securityContext, final PropertyKey credentialKey, final String credentialValue) { return createUser(securityContext, credentialKey, credentialValue, Collections.EMPTY_MAP); } /** * Create a new user. * * If a {@link Person} is found, convert that object to a {@link User} object. * Do not auto-create a new user. * * @param securityContext * @param credentialKey * @param credentialValue * @param propertySet * @return user */ public static Principal createUser(final SecurityContext securityContext, final PropertyKey credentialKey, final String credentialValue, final Map propertySet) { return createUser(securityContext, credentialKey, credentialValue, propertySet, false); } /** * Create a new user. * * If a {@link Person} is found, convert that object to a {@link User} object. * Do not auto-create a new user. * * @param securityContext * @param credentialKey * @param credentialValue * @param autoCreate * @return user */ public static Principal createUser(final SecurityContext securityContext, final PropertyKey credentialKey, final String credentialValue, final boolean autoCreate) { return createUser(securityContext, credentialKey, credentialValue, Collections.EMPTY_MAP, autoCreate); } /** * Create a new user. * * If a {@link Person} is found, convert that object to a {@link User} object. * Do not auto-create a new user. * * @param securityContext * @param credentialKey * @param credentialValue * @param autoCreate * @param userClass * @return user */ public static Principal createUser(final SecurityContext securityContext, final PropertyKey credentialKey, final String credentialValue, final boolean autoCreate, final Class userClass) { return createUser(securityContext, credentialKey, credentialValue, Collections.EMPTY_MAP, autoCreate, userClass); } /** * Create a new user. * * If a {@link Person} is found, convert that object to a {@link User} object. * If autoCreate is true, auto-create a new user, even if no matching person is found. * * @param securityContext * @param credentialKey * @param credentialValue * @param propertySet * @param autoCreate * @return user */ public static Principal createUser(final SecurityContext securityContext, final PropertyKey credentialKey, final String credentialValue, final Map propertySet, final boolean autoCreate) { return createUser(securityContext, credentialKey, credentialValue, propertySet, autoCreate, User.class); } /** * Create a new user. * * If a {@link Principal} is found, convert that object to a {@link Principal} object. * If autoCreate is true, auto-create a new user, even if no matching person is found. * * @param securityContext * @param credentialKey * @param credentialValue * @param propertySet * @param autoCreate * @param userClass * @return user */ public static Principal createUser(final SecurityContext securityContext, final PropertyKey credentialKey, final String credentialValue, final Map propertySet, final boolean autoCreate, final Class userClass) { Principal user = null; try { // First, search for a person with that e-mail address user = AuthHelper.getPrincipalForCredential(credentialKey, credentialValue); if (user != null) { user = new NodeFactory(securityContext).instantiate(user.getNode()); // convert to user user.unlockSystemPropertiesOnce(); user.setProperty(AbstractNode.type, User.class.getSimpleName()); user.setProperty(User.confirmationKey, confKey); } else if (autoCreate) { final App app = StructrApp.getInstance(securityContext); // Clear properties set by us from the user-defined props propertySet.remove(credentialKey.jsonName()); propertySet.remove(User.confirmationKey.jsonName()); PropertyMap props = PropertyMap.inputTypeToJavaType(securityContext, Principal.class, propertySet); // Remove any property which is not included in configuration // eMail is mandatory and necessary final String customAttributesString = User.eMail.jsonName() + "," + Services.getInstance().getConfigurationValue(CUSTOM_ATTRIBUTES); final List customAttributes = Arrays.asList(customAttributesString.split("[ ,]+")); final Set propsToRemove = new HashSet<>(); for (final PropertyKey key : props.keySet()) { if (!customAttributes.contains(key.jsonName())) { propsToRemove.add(key); } } for (final PropertyKey propToRemove : propsToRemove) { props.remove(propToRemove); } props.put(credentialKey, credentialValue); props.put(User.confirmationKey, confKey); // // Remove security-relevant properties // props.remove(Principal.isAdmin); // props.remove(Principal.ownedNodes); // props.remove(Principal.salt); // props.remove(Principal.sessionIds); user = (Principal) app.create(userClass, props); } } catch (FrameworkException ex) { logger.log(Level.SEVERE, null, ex); } return user; } //~--- get methods ---------------------------------------------------- @Override public Class getEntityClass() { return null; } @Override public String getUriPart() { return "registration"; } @Override public String getResourceSignature() { return "_registration"; } @Override public boolean isCollectionResource() { return false; } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy