ch.entwine.weblounge.security.sql.endpoint.SQLDirectoryProviderEndpoint.java Source code

Java tutorial

Introduction

Here is the source code for ch.entwine.weblounge.security.sql.endpoint.SQLDirectoryProviderEndpoint.java

Source

/*
 *  Weblounge: Web Content Management System
 *  Copyright (c) 2003 - 2011 The Weblounge Team
 *  http://entwinemedia.com/weblounge
 *
 *  This program 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
 *  of the License, or (at your option) any later version.
 *
 *  This program 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 program; if not, write to the Free Software Foundation
 *  Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
 */

package ch.entwine.weblounge.security.sql.endpoint;

import ch.entwine.weblounge.common.impl.language.LanguageUtils;
import ch.entwine.weblounge.common.impl.security.PasswordEncoder;
import ch.entwine.weblounge.common.impl.security.RoleImpl;
import ch.entwine.weblounge.common.impl.security.SecurityUtils;
import ch.entwine.weblounge.common.impl.security.SystemRole;
import ch.entwine.weblounge.common.impl.security.WebloungeUserImpl;
import ch.entwine.weblounge.common.language.UnknownLanguageException;
import ch.entwine.weblounge.common.security.DigestType;
import ch.entwine.weblounge.common.security.SecurityService;
import ch.entwine.weblounge.common.security.User;
import ch.entwine.weblounge.common.security.UserExistsException;
import ch.entwine.weblounge.common.security.UserShadowedException;
import ch.entwine.weblounge.common.security.WebloungeUser;
import ch.entwine.weblounge.common.site.Site;
import ch.entwine.weblounge.common.url.UrlUtils;
import ch.entwine.weblounge.kernel.site.SiteManager;
import ch.entwine.weblounge.security.sql.SQLDirectoryProvider;
import ch.entwine.weblounge.security.sql.entities.JpaAccount;
import ch.entwine.weblounge.security.sql.entities.JpaRole;

import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.net.URI;
import java.net.URL;
import java.nio.charset.Charset;

import javax.servlet.http.HttpServletRequest;
import javax.ws.rs.DELETE;
import javax.ws.rs.FormParam;
import javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.PUT;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.QueryParam;
import javax.ws.rs.WebApplicationException;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.Response.Status;

/**
 * This class implements the <code>REST</code> endpoint to manage the SQL
 * directory provider.
 */
@Path("/")
@Produces(MediaType.APPLICATION_XML)
public class SQLDirectoryProviderEndpoint {

    /** The logging facility */
    private static final Logger logger = LoggerFactory.getLogger(SQLDirectoryProviderEndpoint.class);

    /** The endpoint documentation */
    private String docs = null;

    /** The cache configuration factory */
    private SQLDirectoryProvider directory = null;

    /** The security service */
    protected SecurityService securityService = null;

    /** The sites that are online */
    protected SiteManager sites = null;

    @GET
    @Path("/")
    public Response getStatistics(@Context HttpServletRequest request) {
        Site site = getSite(request);

        try {
            StringBuilder stats = new StringBuilder();
            stats.append("<directory id=\"").append(site.getIdentifier()).append("\">");
            stats.append("<enabled>").append(directory.isSiteEnabled(site)).append("</enabled>");
            stats.append("<users>").append(directory.getAccounts(site).size()).append("</users>");
            stats.append("</directory>");
            Response response = Response.ok(stats.toString()).build();
            return response;
        } catch (Throwable t) {
            logger.warn("Error creating directory statistics: {}", t.getMessage());
            return Response.serverError().build();
        }
    }

    @PUT
    @Path("/status")
    public Response enableSite(@Context HttpServletRequest request) {
        Site site = getSite(request);

        // Make sure that the user owns the roles required for this operation
        User user = securityService.getUser();
        if (!SecurityUtils.userHasRole(user, SystemRole.SITEADMIN))
            return Response.status(Status.FORBIDDEN).build();

        // Enable login
        try {
            if (directory.isSiteEnabled(site))
                return Response.notModified().build();
            directory.enableSite(site);
            return Response.ok().build();
        } catch (Throwable t) {
            logger.warn("Error enabling site logins: {}", t.getMessage());
            return Response.serverError().build();
        }
    }

    @DELETE
    @Path("/status")
    public Response disableSite(@Context HttpServletRequest request) {
        Site site = getSite(request);

        // Make sure that the user owns the roles required for this operation
        User user = securityService.getUser();
        if (!SecurityUtils.userHasRole(user, SystemRole.SITEADMIN))
            return Response.status(Status.FORBIDDEN).build();

        // Disable login
        try {
            if (!directory.isSiteEnabled(site))
                return Response.notModified().build();
            directory.disableSite(site);
            return Response.ok().build();
        } catch (Throwable t) {
            logger.warn("Error disabling site logins: {}", t.getMessage());
            return Response.serverError().build();
        }
    }

    @POST
    @Path("/account")
    public Response createAccount(@FormParam("login") String login, @FormParam("password") String password,
            @FormParam("email") String eMail, @Context HttpServletRequest request) {

        // TODO: If not, return a one time pad that needs to be used when verifying
        // the e-mail

        // Check the arguments
        if (StringUtils.isBlank(login))
            return Response.status(Status.BAD_REQUEST).build();

        Response response = null;
        Site site = getSite(request);

        // Hash the password
        if (StringUtils.isNotBlank(password)) {
            logger.debug("Hashing password for user '{}@{}' using md5", login, site.getIdentifier());
            password = PasswordEncoder.encode(StringUtils.trim(password));
        }

        // Create the user
        try {
            JpaAccount account = directory.addAccount(site, login, password);
            account.setEmail(StringUtils.trimToNull(eMail));
            directory.updateAccount(account);
            response = Response
                    .created(new URI(UrlUtils.concat(request.getRequestURL().toString(), account.getLogin())))
                    .build();
        } catch (UserExistsException e) {
            logger.warn("Error creating account: {}", e.getMessage());
            return Response.status(Status.CONFLICT).build();
        } catch (UserShadowedException e) {
            logger.warn("Error creating account: {}", e.getMessage());
            return Response.status(Status.CONFLICT).build();
        } catch (Throwable t) {
            logger.warn("Error creating account: {}", t.getMessage());
            response = Response.serverError().build();
        }
        return response;
    }

    @GET
    @Path("/account/{login}")
    public Response getAccount(@PathParam("login") String login, @Context HttpServletRequest request) {
        Site site = getSite(request);

        JpaAccount account = null;
        try {
            account = directory.getAccount(site, login);
        } catch (Throwable t) {
            logger.warn("Error accessing account '{}': {}", login, t.getMessage());
            throw new WebApplicationException();
        }

        if (account == null)
            throw new WebApplicationException(Status.NOT_FOUND);

        WebloungeUser wu = new WebloungeUserImpl(login, directory.getIdentifier());
        wu.setFirstName(account.getFirstname());
        wu.setLastName(account.getLastname());
        wu.setEmail(account.getEmail());
        wu.setInitials(account.getInitials());
        wu.setLastLogin(account.getLastLoginDate(), account.getLastLoginFrom());
        wu.setChallenge(account.getChallenge());
        if (account.getLanguage() != null)
            wu.setLanguage(LanguageUtils.getLanguage(account.getLanguage()));
        if (account.getResponse() != null)
            wu.setResponse(account.getResponse().getBytes(Charset.forName("utf-8")), DigestType.md5);
        for (JpaRole r : account.getRoles()) {
            wu.addPublicCredentials(new RoleImpl(r.getContext(), r.getRolename()));
        }

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

    @GET
    @Path("/account/{login}/activate")
    public Response activateAccount(@PathParam("login") String login, @QueryParam("activation") String activation,
            @Context HttpServletRequest request) {
        Site site = getSite(request);
        try {
            boolean success = directory.activateAccount(site, login, activation);
            return (success) ? Response.ok().build() : Response.status(Status.UNAUTHORIZED).build();
        } catch (Throwable t) {
            logger.warn("Error activating account '{}': {}", login, t.getMessage());
            return Response.serverError().build();
        }
    }

    @PUT
    @Path("/account/{id}")
    public Response updateAccount(@PathParam("id") String login, @FormParam("password") String password,
            @FormParam("firstname") String firstname, @FormParam("lastname") String lastname,
            @FormParam("initials") String initials, @FormParam("email") String email,
            @FormParam("language") String language, @FormParam("challenge") String challenge,
            @FormParam("response") String response, @Context HttpServletRequest request) {

        // Make sure that the user owns the roles required for this operation
        User user = securityService.getUser();
        if (!SecurityUtils.userHasRole(user, SystemRole.SITEADMIN) && !user.getLogin().equals(login))
            return Response.status(Status.FORBIDDEN).build();

        JpaAccount account = null;
        Site site = getSite(request);
        try {
            account = directory.getAccount(site, login);
            if (account == null)
                return Response.status(Status.NOT_FOUND).build();

            // Hash the password
            if (StringUtils.isNotBlank(password)) {
                logger.debug("Hashing password for user '{}@{}' using md5", login, site.getIdentifier());
                String digestPassword = PasswordEncoder.encode(StringUtils.trim(password));
                account.setPassword(digestPassword);
            }

            account.setFirstname(StringUtils.trimToNull(firstname));
            account.setLastname(StringUtils.trimToNull(lastname));
            account.setInitials(StringUtils.trimToNull(initials));
            account.setEmail(StringUtils.trimToNull(email));

            // The language
            if (StringUtils.isNotBlank(language)) {
                try {
                    account.setLanguage(LanguageUtils.getLanguage(language));
                } catch (UnknownLanguageException e) {
                    return Response.status(Status.BAD_REQUEST).build();
                }
            } else {
                account.setLanguage(null);
            }

            // Hash the response
            if (StringUtils.isNotBlank(response)) {
                logger.debug("Hashing response for user '{}@{}' using md5", login, site.getIdentifier());
                String digestResponse = PasswordEncoder.encode(StringUtils.trim(response));
                account.setResponse(digestResponse);
            }

            directory.updateAccount(account);
            return Response.ok().build();
        } catch (Throwable t) {
            return Response.serverError().build();
        }
    }

    @PUT
    @Path("/account/{id}/password")
    public Response updateAccountPassword(@PathParam("id") String login, @FormParam("password") String password,
            @Context HttpServletRequest request) {

        // Make sure that the user owns the roles required for this operation
        User user = securityService.getUser();
        if (!SecurityUtils.userHasRole(user, SystemRole.SITEADMIN) && !user.getLogin().equals(login))
            return Response.status(Status.FORBIDDEN).build();

        JpaAccount account = null;
        Site site = getSite(request);
        try {
            account = directory.getAccount(site, login);
            if (account == null)
                return Response.status(Status.NOT_FOUND).build();

            // Hash the password
            if (StringUtils.isNotBlank(password)) {
                logger.debug("Hashing password for user '{}@{}' using md5", login, site.getIdentifier());
                String digestPassword = PasswordEncoder.encode(StringUtils.trim(password));
                account.setPassword(digestPassword);
            } else {
                account.setPassword(null);
            }

            directory.updateAccount(account);
            return Response.ok().build();
        } catch (Throwable t) {
            return Response.serverError().build();
        }
    }

    @PUT
    @Path("/account/{id}/challenge")
    public Response updateAccountChallenge(@PathParam("id") String login, @FormParam("challenge") String challenge,
            @FormParam("response") String response, @Context HttpServletRequest request) {

        // Make sure that the user owns the roles required for this operation
        User user = securityService.getUser();
        if (!SecurityUtils.userHasRole(user, SystemRole.SITEADMIN) && !user.getLogin().equals(login))
            return Response.status(Status.FORBIDDEN).build();

        JpaAccount account = null;
        Site site = getSite(request);
        try {
            account = directory.getAccount(site, login);
            if (account == null)
                return Response.status(Status.NOT_FOUND).build();

            // Set the challenge
            account.setChallenge(StringUtils.trimToNull(challenge));

            // Hash the response
            if (StringUtils.isNotBlank(response)) {
                logger.debug("Hashing response for user '{}@{}' using md5", login, site.getIdentifier());
                String digestResponse = PasswordEncoder.encode(StringUtils.trim(response));
                account.setResponse(digestResponse);
            } else {
                account.setResponse(response);
            }

            directory.updateAccount(account);
            return Response.ok().build();
        } catch (Throwable t) {
            return Response.serverError().build();
        }
    }

    @DELETE
    @Path("/account/{id}")
    public Response deleteAccount(@PathParam("id") String login, @Context HttpServletRequest request) {

        // Make sure that the user owns the roles required for this operation
        User user = securityService.getUser();
        if (!SecurityUtils.userHasRole(user, SystemRole.SITEADMIN) && !user.getLogin().equals(login))
            return Response.status(Status.FORBIDDEN).build();

        Site site = getSite(request);
        try {
            JpaAccount account = directory.getAccount(site, login);
            if (account == null)
                return Response.status(Status.NOT_FOUND).build();

            directory.removeAccount(site, login);
            return Response.ok().build();
        } catch (Throwable t) {
            logger.warn("Error removing account '{}': {}", login, t.getMessage());
            return Response.serverError().build();
        }
    }

    @PUT
    @Path("/account/{id}/status")
    public Response enableAccount(@PathParam("id") String login, @Context HttpServletRequest request) {

        // Make sure that the user owns the roles required for this operation
        User user = securityService.getUser();
        if (!SecurityUtils.userHasRole(user, SystemRole.SITEADMIN))
            return Response.status(Status.FORBIDDEN).build();

        JpaAccount account = null;
        Site site = getSite(request);
        try {
            account = directory.getAccount(site, login);
            if (account == null)
                return Response.status(Status.NOT_FOUND).build();
            if (account.isEnabled())
                return Response.status(Status.NOT_MODIFIED).build();

            directory.enableAccount(site, login);
            return Response.ok().build();
        } catch (Throwable t) {
            logger.warn("Error enabling account '{}': {}", login, t.getMessage());
            return Response.serverError().build();
        }
    }

    @DELETE
    @Path("/account/{id}/status")
    public Response disableAccount(@PathParam("id") String login, @Context HttpServletRequest request) {

        // Make sure that the user owns the roles required for this operation
        User user = securityService.getUser();
        if (!SecurityUtils.userHasRole(user, SystemRole.SITEADMIN))
            return Response.status(Status.FORBIDDEN).build();

        JpaAccount account = null;
        Site site = getSite(request);
        try {
            account = directory.getAccount(site, login);
            if (account == null)
                return Response.status(Status.NOT_FOUND).build();
            if (!account.isEnabled())
                return Response.status(Status.NOT_MODIFIED).build();

            directory.disableAccount(site, login);
            return Response.ok().build();
        } catch (Throwable t) {
            logger.warn("Error disabling account '{}': {}", login, t.getMessage());
            return Response.serverError().build();
        }
    }

    @POST
    @Path("/account/{id}/roles/{context}")
    public Response addRole(@PathParam("id") String login, @PathParam("context") String context,
            @FormParam("role") String role, @Context HttpServletRequest request) {

        // Make sure that the user owns the roles required for this operation
        User user = securityService.getUser();
        if (!SecurityUtils.userHasRole(user, SystemRole.SITEADMIN))
            return Response.status(Status.FORBIDDEN).build();

        // Make sure a role has been provided as part of the request
        if (StringUtils.isBlank(role))
            return Response.status(Status.BAD_REQUEST).build();

        JpaAccount account = null;
        Site site = getSite(request);
        try {
            account = directory.getAccount(site, login);
            if (account == null)
                return Response.status(Status.NOT_FOUND).build();

            if (account.hasRole(context, role))
                return Response.status(Status.NOT_MODIFIED).build();

            account.addRole(context, role);
            directory.updateAccount(account);
            return Response.ok().build();
        } catch (Throwable t) {
            logger.warn("Error adding role '{}:{}' to account {}: {}",
                    new String[] { context, role, login, t.getMessage() });
            return Response.serverError().build();
        }
    }

    @DELETE
    @Path("/account/{id}/roles/{context}")
    public Response removeRole(@PathParam("id") String login, @PathParam("context") String context,
            @FormParam("role") String role, @Context HttpServletRequest request) {

        // Make sure that the user owns the roles required for this operation
        User user = securityService.getUser();
        if (!SecurityUtils.userHasRole(user, SystemRole.SITEADMIN))
            return Response.status(Status.FORBIDDEN).build();

        // Make sure a role has been provided as part of the request
        if (StringUtils.isBlank(role))
            return Response.status(Status.BAD_REQUEST).build();

        JpaAccount account = null;
        Site site = getSite(request);
        try {
            account = directory.getAccount(site, login);
            if (account == null)
                return Response.status(Status.NOT_FOUND).build();

            if (!account.hasRole(context, role))
                return Response.status(Status.NOT_MODIFIED).build();

            account.removeRole(context, role);
            directory.updateAccount(account);
            return Response.ok().build();
        } catch (Throwable t) {
            logger.warn("Error adding role '{}:{}' to account: {}",
                    new String[] { context, role, login, t.getMessage() });
            return Response.serverError().build();
        }
    }

    /**
     * Returns the endpoint documentation.
     * 
     * @return the endpoint documentation
     */
    @GET
    @Path("/docs")
    @Produces(MediaType.TEXT_HTML)
    public String getDocumentation(@Context HttpServletRequest request) {
        if (docs == null) {
            String docsPath = request.getRequestURI();
            String docsPathExtension = request.getPathInfo();
            String servicePath = request.getRequestURI().substring(0,
                    docsPath.length() - docsPathExtension.length());
            docs = SQLDirectoryProviderEndpointDocs.createDocumentation(servicePath);
        }
        return docs;
    }

    /**
     * Extracts the site from the request and returns it. If the site is not found
     * or it's not running, a corresponding <code>WebApplicationException</code>
     * is thrown.
     * 
     * @param request
     *          the http request
     * @return the site
     * @throws WebApplicationException
     *           if the site is not found or is not running
     */
    protected Site getSite(HttpServletRequest request) throws WebApplicationException {
        URL url = UrlUtils.toURL(request, false, false);
        Site site = sites.findSiteByURL(url);
        if (site == null) {
            throw new WebApplicationException(Status.NOT_FOUND);
        } else if (!site.isOnline()) {
            throw new WebApplicationException(Status.SERVICE_UNAVAILABLE);
        }
        return site;
    }

    /**
     * Callback for OSGi to set the site manager.
     * 
     * @param siteManager
     *          the site manager
     */
    void setSiteManager(SiteManager siteManager) {
        this.sites = siteManager;
    }

    /**
     * Callback from the OSGi declarative services environment that will pass in a
     * reference to the directory provider.
     * 
     * @param directoryProvider
     *          the sql directory provider
     */
    void setDiretoryProvider(SQLDirectoryProvider directoryProvider) {
        this.directory = directoryProvider;
    }

    /**
     * Callback from OSGi to set the security service.
     * 
     * @param securityService
     *          the security service
     */
    void setSecurityService(SecurityService securityService) {
        this.securityService = securityService;
    }

    /**
     * {@inheritDoc}
     * 
     * @see java.lang.Object#toString()
     */
    @Override
    public String toString() {
        return "SQL directory rest endpoint";
    }

}