org.obiba.agate.web.rest.user.UsersPublicResource.java Source code

Java tutorial

Introduction

Here is the source code for org.obiba.agate.web.rest.user.UsersPublicResource.java

Source

/*
 * Copyright (c) 2015 OBiBa. All rights reserved.
 *
 * This program and the accompanying materials
 * are made available under the terms of the GNU Public License v3.0.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

package org.obiba.agate.web.rest.user;

import java.io.IOException;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;

import javax.inject.Inject;
import javax.servlet.http.HttpServletRequest;
import javax.ws.rs.BadRequestException;
import javax.ws.rs.FormParam;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.UriBuilder;

import org.bson.types.ObjectId;
import org.codehaus.jackson.map.ObjectMapper;
import org.codehaus.jackson.type.TypeReference;
import org.hibernate.validator.internal.constraintvalidators.EmailValidator;
import org.joda.time.DateTime;
import org.obiba.agate.domain.AttributeConfiguration;
import org.obiba.agate.domain.User;
import org.obiba.agate.domain.UserCredentials;
import org.obiba.agate.domain.UserStatus;
import org.obiba.agate.security.AgateUserRealm;
import org.obiba.agate.security.Roles;
import org.obiba.agate.service.ApplicationService;
import org.obiba.agate.service.ConfigurationService;
import org.obiba.agate.service.ReCaptchaService;
import org.obiba.agate.service.UserService;
import org.obiba.agate.web.rest.config.JerseyConfiguration;
import org.obiba.shiro.authc.HttpAuthorizationToken;
import org.obiba.shiro.realm.ObibaRealm;
import org.springframework.stereotype.Component;

import com.google.common.base.Strings;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;

/**
 * Public resource for user join requests. Default realm is {@link org.obiba.agate.security.AgateUserRealm}.
 */
@Component
@Path("/users")
public class UsersPublicResource {

    private static final String CURRENT_USER_NAME = "_current";

    private static final String[] BUILTIN_PARAMS = new String[] { "username", "firstname", "lastname",
            "application", //
            "email", "group", "reCaptchaResponse" };

    @Inject
    private UserService userService;

    @Inject
    private ApplicationService applicationService;

    @Inject
    private ConfigurationService configurationService;

    @Inject
    private ReCaptchaService reCaptchaService;

    @POST
    @Path("/_confirm")
    public Response confirm(@FormParam("key") String key, @FormParam("password") String password) {
        String username = configurationService.decrypt(key);
        User user = userService.findUser(username);

        if (user == null)
            throw new BadRequestException("User not found");

        if (user.getStatus() != UserStatus.APPROVED)
            throw new BadRequestException("Invalid user status.");

        userService.confirmUser(user, password);

        return Response.ok().build();
    }

    @POST
    @Path("/_forgot_password")
    public Response forgotPassword(@FormParam("username") String username) throws IOException {
        User user = userService.findUser(username);
        if (user == null)
            user = userService.findUserByEmail(username);
        UserCredentials userCredentials = user != null ? userService.findUserCredentials(user.getName()) : null;

        if (user != null && userCredentials != null)
            userService.resetPassword(user);

        return Response.ok().build();
    }

    @POST
    @Path("/_reset_password")
    public Response resetPassword(@FormParam("key") String key, @FormParam("password") String password)
            throws IOException {
        ObjectMapper mapper = new ObjectMapper();
        Map<String, String> data = mapper.readValue(configurationService.decrypt(key),
                new TypeReference<HashMap<String, String>>() {
                });

        if (DateTime.now().isAfter(DateTime.parse(data.get("expire")))) {
            throw new BadRequestException("Invalid key");
        }

        User user = userService.findUser(data.get("username"));

        if (user == null)
            throw new BadRequestException("User not found");

        UserCredentials userCredentials = userService.findUserCredentials(user.getName());

        if (userCredentials == null)
            throw new BadRequestException("user has no credentials defined");

        userCredentials.setPassword(userService.hashPassword(password));

        userService.save(userCredentials);

        return Response.noContent().build();
    }

    @POST
    @Path("/_join")
    public Response create(@FormParam("username") String username, @FormParam("firstname") String firstName,
            @FormParam("lastname") String lastName, @FormParam("email") String email,
            @FormParam("application") List<String> applications, @FormParam("group") List<String> groups,
            @FormParam("password") String password, @FormParam("reCaptchaResponse") String reCaptchaResponse,
            @Context HttpServletRequest request) {
        if (Strings.isNullOrEmpty(email))
            throw new BadRequestException("Email cannot be empty");

        if (!new EmailValidator().isValid(email, null))
            throw new BadRequestException("Not a valid email address");

        String name = username;

        if (Strings.isNullOrEmpty(username)) {
            if (configurationService.getConfiguration().isJoinWithUsername())
                throw new BadRequestException("User name cannot be empty");

            try {
                name = email.split("@")[0];
            } catch (Exception e) {
                name = new ObjectId().toString();
            }
        }

        if (new EmailValidator().isValid(name, null))
            throw new BadRequestException("User name cannot be an email address");

        if (!reCaptchaService.verify(reCaptchaResponse))
            throw new BadRequestException("Invalid reCaptcha response");

        if (CURRENT_USER_NAME.equals(name))
            throw new BadRequestException("Reserved user name: " + CURRENT_USER_NAME);

        User user = userService.findUserByEmail(email);

        if (user != null)
            throw new BadRequestException("Email already in use: " + user.getEmail());

        user = userService.findUser(name);

        int i = 1;
        String originalName = name;
        while (user != null) {
            name = originalName + i;
            user = userService.findUser(name);
            i++;
        }

        user = User.newBuilder().name(name).realm(AgateUserRealm.AGATE_REALM).role(Roles.AGATE_USER).pending()
                .firstName(firstName).lastName(lastName).email(email).build();
        user.setGroups(Sets.newHashSet(groups));
        user.setApplications(Sets.newHashSet(applications));
        user.setAttributes(extractAttributes(request));

        if (isRequestedByApplication(request)) {
            user.setStatus(UserStatus.APPROVED);
        }

        userService.createUser(user, password);

        return Response.created(
                UriBuilder.fromPath(JerseyConfiguration.WS_ROOT).path(UserResource.class).build(user.getId()))
                .build();
    }

    private Map<String, String> extractAttributes(HttpServletRequest request) {
        final Map<String, AttributeConfiguration> attributes = configurationService.getConfiguration()
                .getUserAttributes().stream().collect(Collectors.toMap(a -> a.getName(), a -> a));
        final Map<String, String[]> params = request.getParameterMap();
        final Set<String> extraParams = Sets.difference(params.keySet(),
                Sets.newHashSet(Arrays.asList(BUILTIN_PARAMS)));

        Map<String, String> res = Maps.newHashMap();

        attributes.values().forEach(a -> {
            if (!params.containsKey(a.getName())) {
                if (a.isRequired())
                    throw new BadRequestException("Missing attribute " + a.getName());
            }
        });

        for (String param : extraParams) {
            String[] values = params.get(param);

            if (values.length > 1) {
                throw new BadRequestException("Invalid repeated parameter " + param);
            }

            if (attributes.containsKey(param)) {
                AttributeConfiguration attribute = attributes.get(param);
                res.put(attribute.getName(), getParsedAttribute(attribute, values[0]));
            } else {
                res.put(param, params.get(param)[0]);
            }
        }

        return res;
    }

    private String getParsedAttribute(AttributeConfiguration attribute, String value) {
        String parsedValue;

        try {
            switch (attribute.getType()) {
            case INTEGER:
                parsedValue = Integer.valueOf(value).toString();
                break;
            case BOOLEAN:
                parsedValue = Boolean.valueOf(value).toString();
                break;
            case NUMBER:
                parsedValue = Float.valueOf(value).toString();
                break;
            default:
                parsedValue = value;
            }
        } catch (NumberFormatException e) {
            throw new BadRequestException("Invalid value " + value);
        }

        if (attribute.hasValues() && !attribute.getValues().contains(parsedValue)) {
            throw new BadRequestException("Invalid value " + parsedValue);
        }

        return parsedValue;
    }

    protected boolean isRequestedByApplication(HttpServletRequest servletRequest) {
        String appAuthHeader = servletRequest.getHeader(ObibaRealm.APPLICATION_AUTH_HEADER);
        if (appAuthHeader == null)
            return false;

        HttpAuthorizationToken token = new HttpAuthorizationToken(ObibaRealm.APPLICATION_AUTH_SCHEMA,
                appAuthHeader);
        return applicationService.isValid(token.getUsername(), new String(token.getPassword()));
    }
}