UserRegistrationController.java

/*
 * Copyright 2019 Global Crop Diversity Trust
 *
 * 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.genesys.server.api.v1;

import javax.servlet.http.HttpServletRequest;

import org.apache.commons.validator.routines.EmailValidator;
import org.genesys.blocks.model.JsonViews;
import org.genesys.blocks.security.NotUniqueUserException;
import org.genesys.blocks.security.UserException;
import org.genesys.blocks.security.model.BasicUser;
import org.genesys.blocks.security.service.PasswordPolicy.PasswordPolicyException;
import org.genesys.server.api.ApiBaseController;
import org.genesys.server.exception.InvalidApiUsageException;
import org.genesys.server.model.impl.User;
import org.genesys.server.service.EMailVerificationService;
import org.genesys.server.service.TokenVerificationService.NoSuchVerificationTokenException;
import org.genesys.server.service.TokenVerificationService.TokenExpiredException;
import org.genesys.server.service.UserService;
import org.genesys.spring.CaptchaChecker;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.DataIntegrityViolationException;
import org.springframework.http.MediaType;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

import com.fasterxml.jackson.annotation.JsonView;

import io.swagger.annotations.Api;

/**
 * @author Maxym Borodenko
 */
@RestController("userRegistrationApi1")
@PreAuthorize("isAuthenticated()")
@RequestMapping(value = { UserRegistrationController.CONTROLLER_URL })
@Api(tags = { "user-registration" })
public class UserRegistrationController {

	/** The Constant LOG. */
	private static final Logger LOG = LoggerFactory.getLogger(UserRegistrationController.class);

	/** The Constant CONTROLLER_URL. */
	public static final String CONTROLLER_URL = ApiBaseController.APIv1_BASE + "/user";

	@Autowired
	private UserService userService;

	@Autowired
	private EMailVerificationService emailVerificationService;

	@Autowired
	private CaptchaChecker captchaChecker;

	private final EmailValidator emailValidator = EmailValidator.getInstance();

	@JsonView(JsonViews.Public.class)
	@RequestMapping(value = "/register", method = RequestMethod.POST, produces = { MediaType.APPLICATION_JSON_VALUE })
	public User registerUser(@RequestParam(value = "g-recaptcha-response", required = true) final String captchaResponse,
			@RequestParam(name = "email", required = true) final String email, @RequestParam(name = "pass", required = true) final String password,
			@RequestParam(name = "fullName", required = true) final String fullName, final HttpServletRequest request) throws Exception {

		// Validate the reCAPTCHA
		captchaChecker.assureValidResponseForClient(captchaResponse, request.getRemoteAddr());

		try {
			if (!emailValidator.isValid(email)) {
				LOG.warn("Invalid email provided: {}", email);
				throw new UserException("Invalid email provided: " + email);
			}

			final User newUser = userService.createUser(email, fullName, password, BasicUser.AccountType.LOCAL);
			emailVerificationService.sendVerificationEmail(newUser);
			return newUser;
		} catch (final PasswordPolicyException e) {
			LOG.error(e.getMessage());
			throw new PasswordPolicyException(e.getMessage());
		} catch (final DataIntegrityViolationException e) {
			LOG.error(e.getMessage());
			throw new NotUniqueUserException("E-mail already taken: " + email);
		} catch (final Exception e) {
			LOG.error(e.getMessage());
			throw e;
		}
	}

	@RequestMapping(value = "/{tokenUuid:.+}/validate", method = RequestMethod.POST, produces = { MediaType.APPLICATION_JSON_VALUE })
	public boolean validateEMail(@PathVariable("tokenUuid") String tokenUuid, @RequestParam(value = "key", required = true) String key) throws Exception {
		try {
			emailVerificationService.validateEMail(tokenUuid, key);
			return true;
		} catch (final NoSuchVerificationTokenException e) {
			throw new InvalidApiUsageException("Verification token was already used or is not valid!");
		} catch (final TokenExpiredException e) {
			throw new InvalidApiUsageException("Verification token has expired!");
		}
	}

	@PostMapping(value = "/{tokenUuid:.+}/cancel")
	public boolean cancelValidation(@PathVariable("tokenUuid") String tokenUuid, HttpServletRequest req, @RequestParam(value = "g-recaptcha-response", required = false) String response) throws Exception {

		// Validate the reCAPTCHA
		captchaChecker.assureValidResponseForClient(response, req.getRemoteAddr());
		// Cancel validation
		emailVerificationService.cancelValidation(tokenUuid);
		return true;
	}
}