Package org.jtalks.jcommune.plugin.auth.poulpe.service

Source Code of org.jtalks.jcommune.plugin.auth.poulpe.service.PoulpeAuthService

/**
* Copyright (C) 2011  JTalks.org Team
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
* This library 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
* Lesser General Public License for more details.
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
*/

package org.jtalks.jcommune.plugin.auth.poulpe.service;

import org.jtalks.jcommune.model.dto.UserDto;
import org.jtalks.jcommune.plugin.api.exceptions.NoConnectionException;
import org.jtalks.jcommune.plugin.api.exceptions.UnexpectedErrorException;
import org.jtalks.jcommune.plugin.auth.poulpe.dto.Authentication;
import org.jtalks.jcommune.plugin.auth.poulpe.dto.Errors;
import org.jtalks.jcommune.plugin.auth.poulpe.dto.User;
import org.restlet.Context;
import org.restlet.data.ChallengeScheme;
import org.restlet.data.Status;
import org.restlet.engine.header.Header;
import org.restlet.engine.header.HeaderConstants;
import org.restlet.representation.Representation;
import org.restlet.resource.ClientResource;
import org.restlet.resource.ResourceException;
import org.restlet.util.Series;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Unmarshaller;
import java.io.IOException;
import java.util.*;
import java.util.concurrent.ConcurrentMap;

/**
* This class contains method needed for communicate with Poulpe rest service.
*
* @author Andrey Pogorelov
*/
public class PoulpeAuthService {

    private static final int CONNECTION_TIMEOUT = 5000;
    public static final String DRY_RUN_PARAM = "dryRun";
    public static final String TRUE = "true";

    private final Logger logger = LoggerFactory.getLogger(getClass());
    private String regUrl;
    private String authUrl;
    private String login;
    private String password;

    public PoulpeAuthService(String url, String login, String password) {
        this.regUrl = url + "/rest/private/user";
        this.authUrl = url + "/rest/authenticate";
        this.login = login;
        this.password = password;
    }

    /**
     * Register user with specified data via Poulpe.
     * Returns errors if request failed, otherwise return null.
     *
     * @param userDto user
     * @param dryRun  do not register the user, just check if it is possible
     * @return errors
     */
    public Map<String, String> registerUser(UserDto userDto, Boolean dryRun)
            throws IOException, NoConnectionException, JAXBException, UnexpectedErrorException {
        User user = createUser(userDto.getUsername(), userDto.getPassword(), userDto.getEmail());
        ClientResource clientResource = sendRegistrationRequest(user, dryRun);
        Map<String, String> result = getRegistrationResult(clientResource, userDto.getLanguage().getLocale());
        closeRestletConnection(clientResource);
        return result;
    }

    /**
     * Authenticate user with specified data via Poulpe.
     * Returns true if auth success, otherwise return false.
     *
     * @param username     username
     * @param passwordHash password hash
     * @return map with user details
     */
    public Map<String, String> authenticate(String username, String passwordHash)
            throws JAXBException, IOException, NoConnectionException {
        ClientResource clientResource = sendAuthRequest(username, passwordHash);
        Map<String, String> result = getAuthResult(clientResource);
        closeRestletConnection(clientResource);
        return result;
    }



    private void closeRestletConnection(ClientResource clientResource) {
        try {
            clientResource.getResponseEntity().exhaust();
        } catch (IOException e) {
            logger.warn("Error closing connection: {}", e.getMessage() );
        }
    }

    /**
     * Gets authentication result from response entity.
     *
     * @param clientResource response container
     * @return map with user details
     * @throws org.jtalks.jcommune.plugin.api.exceptions.NoConnectionException
     *
     */
    private Map<String, String> getAuthResult(ClientResource clientResource)
            throws NoConnectionException, JAXBException, IOException {
        if (clientResource.getStatus().getCode() == Status.SUCCESS_OK.getCode()
                && clientResource.getResponseEntity() != null) {
            return parseUserDetails(clientResource.getResponseEntity());
        } else if (clientResource.getStatus().getCode() == Status.CLIENT_ERROR_NOT_FOUND.getCode()) {
            return Collections.emptyMap();
        } else {
            throw new NoConnectionException(clientResource.getStatus().toString());
        }
    }

    private Map<String, String> parseUserDetails(Representation repr) throws JAXBException, IOException {
        JAXBContext context = JAXBContext.newInstance(Authentication.class);
        Unmarshaller unmarshaller = context.createUnmarshaller();
        Authentication auth = (Authentication) unmarshaller.unmarshal(repr.getStream());

        Map<String, String> authInfo = new HashMap<>();
        authInfo.put("username", auth.getCredintals().getUsername());
        authInfo.put("email", auth.getProfile().getEmail());
        authInfo.put("firstName", auth.getProfile().getFirstName());
        authInfo.put("lastName", auth.getProfile().getLastName());
        authInfo.put("enabled", String.valueOf(auth.getProfile().isEnabled()));
        return authInfo;
    }

    /**
     * Gets errors from response if request wasn't successful, otherwise return null.
     *
     * @param clientResource response container
     * @param locale         locale
     * @return errors
     * @throws org.jtalks.jcommune.plugin.api.exceptions.NoConnectionException
     *
     * @throws java.io.IOException
     */
    private Map<String, String> getRegistrationResult(ClientResource clientResource, Locale locale)
            throws NoConnectionException, IOException, JAXBException, UnexpectedErrorException {
        if (clientResource.getStatus().getCode() == Status.SUCCESS_OK.getCode()
                && clientResource.getResponseEntity() != null) {
            return Collections.emptyMap();
        } else if (clientResource.getStatus().getCode() == Status.CLIENT_ERROR_BAD_REQUEST.getCode()) {
            return parseErrors(clientResource.getResponseEntity(), locale);
        } else if (clientResource.getStatus().getCode() == Status.SERVER_ERROR_INTERNAL.getCode()) {
            throw new UnexpectedErrorException();
        } else {
            throw new NoConnectionException(clientResource.getStatus().toString());
        }
    }

    /**
     * Parse bad response representation for errors.
     *
     * @param repr   response representation
     * @param locale locale
     * @return errors
     * @throws java.io.IOException
     */
    private Map<String, String> parseErrors(Representation repr, Locale locale) throws IOException, JAXBException {
        JAXBContext context = JAXBContext.newInstance(Errors.class);
        Unmarshaller unmarshaller = context.createUnmarshaller();
        Errors errorsRepr = (Errors) unmarshaller.unmarshal(repr.getStream());

        Map<String, String> errors = new HashMap<>();
        ResourceBundle resourceBundle = ResourceBundle.getBundle("ValidationMessages", locale);
        for (org.jtalks.jcommune.plugin.auth.poulpe.dto.Error error : errorsRepr.getErrorList()) {
            if (error.getCode() != null && !error.getCode().isEmpty()) {
                Map.Entry<String, String> errorEntry = parseErrorCode(error.getCode(), resourceBundle);
                if (errorEntry != null) {
                    errors.put(errorEntry.getKey(), errorEntry.getValue());
                }
            }
        }
        return errors;
    }

    /**
     * Parse error code with specified {@link ResourceBundle}.
     *
     * @param errorCode      error code
     * @param resourceBundle used {@link ResourceBundle}
     * @return parsed error as pair field - error message
     */
    private Map.Entry<String, String> parseErrorCode(String errorCode, ResourceBundle resourceBundle) {
        Map.Entry<String, String> error = null;
        if (resourceBundle.containsKey(errorCode)) {
            String errorMessage = resourceBundle.getString(errorCode);
            if (errorCode.contains("email")) {
                errorMessage = errorMessage
                        .replace("{max}", String.valueOf(org.jtalks.common.model.entity.User.EMAIL_MAX_LENGTH));
                error = new HashMap.SimpleEntry<>("userDto.email", errorMessage);
            } else if (errorCode.contains("username")) {
                errorMessage = errorMessage
                        .replace("{min}", String.valueOf(org.jtalks.common.model.entity.User.USERNAME_MIN_LENGTH))
                        .replace("{max}", String.valueOf(org.jtalks.common.model.entity.User.USERNAME_MAX_LENGTH));
                error = new HashMap.SimpleEntry<>("userDto.username", errorMessage);
            } else if (errorCode.contains("password")) {
                errorMessage = errorMessage
                        .replace("{min}", String.valueOf(org.jtalks.common.model.entity.User.PASSWORD_MIN_LENGTH))
                        .replace("{max}", String.valueOf(org.jtalks.common.model.entity.User.PASSWORD_MAX_LENGTH));
                error = new HashMap.SimpleEntry<>("userDto.password", errorMessage);
            }
        }
        return error;
    }

    /**
     * Creates user entity by specified username, password and email.
     *
     * @param username     username
     * @param passwordHash password hash
     * @param email        user email
     * @return user entity
     */
    private User createUser(String username, String passwordHash, String email) {
        User user = new User();
        user.setUsername(username == null ? "" : username);
        user.setEmail(email == null ? "" : email);
        user.setPasswordHash(passwordHash);
        return user;
    }

    private void addHeaderAttribute(ClientResource clientResource, String attrName, String attrValue) {
        ConcurrentMap<String, Object> attrs = clientResource.getRequest().getAttributes();
        Series<Header> headers = (Series<Header>) attrs.get(HeaderConstants.ATTRIBUTE_HEADERS);
        if (headers == null) {
            headers = new Series<>(Header.class);
            Series<Header> prev = (Series<Header>) attrs.putIfAbsent(HeaderConstants.ATTRIBUTE_HEADERS, headers);
            if (prev != null) {
                headers = prev;
            }
        }
        headers.add(attrName, attrValue);
    }

    /**
     * Sends registration post request.
     *
     * @param user   user entity for registration
     * @param dryRun do not register the user, just check if it is possible
     * @return ClientResource result
     */
    protected ClientResource sendRegistrationRequest(User user, Boolean dryRun) {
        ClientResource clientResource = createClientResource(regUrl, true);
        if (login != null && !login.isEmpty() && password != null && !password.isEmpty()) {
            clientResource.setChallengeResponse(ChallengeScheme.HTTP_BASIC, login, password);
        }
        if (dryRun) {
            addHeaderAttribute(clientResource, DRY_RUN_PARAM, TRUE);
        }
        writeRequestInfoToLog(clientResource);
        try {
            clientResource.post(user);
        } catch (ResourceException e) {
            logger.debug("Poulpe registration request error: {}", e.getStatus());
        }
        return clientResource;
    }

    /**
     * Sends registration post request.
     *
     * @param username     user name
     * @param passwordHash password hash
     * @return ClientResource result
     */
    protected ClientResource sendAuthRequest(String username, String passwordHash) {
        String url = authUrl + "?username=" + username + "&passwordHash=" + passwordHash;
        ClientResource clientResource = createClientResource(url, false);
        if (login != null && !login.isEmpty() && password != null && !password.isEmpty()) {
            clientResource.setChallengeResponse(ChallengeScheme.HTTP_BASIC, login, password);
        }
        writeRequestInfoToLog(clientResource);
        try {
            clientResource.get();
        } catch (ResourceException e) {
            logger.debug("Poulpe authentication request error: {}", e.getStatus());
        }
        return clientResource;
    }

    private void writeRequestInfoToLog(ClientResource clientResource) {
        ConcurrentMap<String, Object> attrs = clientResource.getRequest().getAttributes();
        Series<Header> headers = (Series<Header>) attrs.get(HeaderConstants.ATTRIBUTE_HEADERS);
        if (headers != null) {
            String h = headers.toString();
        }
        logger.debug("Request to Poulpe: requested URI - {}, request headers - {}, request body - {}",
                new Object[]{clientResource.getRequest().getResourceRef(), headers, clientResource.getRequest()});
    }

    private ClientResource createClientResource(String url, boolean buffering) {
        ClientResource clientResource = new ClientResource(new Context(), url);
        clientResource.getContext().getParameters().add("socketConnectTimeoutMs", String.valueOf(CONNECTION_TIMEOUT));
        clientResource.getContext().getParameters().add("maxIoIdleTimeMs", String.valueOf(CONNECTION_TIMEOUT));
        clientResource.setEntityBuffering(buffering);
        return clientResource;
    }
}
TOP

Related Classes of org.jtalks.jcommune.plugin.auth.poulpe.service.PoulpeAuthService

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.