eu.dasish.annotation.backend.rest.PrincipalResource.java Source code

Java tutorial

Introduction

Here is the source code for eu.dasish.annotation.backend.rest.PrincipalResource.java

Source

/*
 * Copyright (C) 2013 DASISH
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU 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 General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 */
package eu.dasish.annotation.backend.rest;

import eu.dasish.annotation.backend.ForbiddenException;
import eu.dasish.annotation.backend.NotInDataBaseException;
import eu.dasish.annotation.backend.PrincipalCannotBeDeleted;
import eu.dasish.annotation.backend.PrincipalExists;
import eu.dasish.annotation.backend.Resource;
import eu.dasish.annotation.backend.dao.ILambda;
import eu.dasish.annotation.backend.dao.ILambdaPrincipal;
import eu.dasish.annotation.schema.CurrentPrincipalInfo;
import eu.dasish.annotation.schema.ObjectFactory;
import eu.dasish.annotation.schema.Principal;
import java.io.IOException;
import java.sql.SQLException;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.ws.rs.Consumes;
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.core.MediaType;
import javax.xml.bind.JAXBElement;
import javax.xml.parsers.ParserConfigurationException;
import org.springframework.dao.DuplicateKeyException;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;

/**
 * A REST class for GETting, POSTing, PUTting and DELETing principals or their substructures (child elements).
 * Every REST method in the case of successful completion produces an object of the declared output type
 * (a JAXB-element or a message string) or sends a HTTP-error with the corresponding diagnostics otherwise.
* @author olhsha
 */
@Component
@Path("/principals")
@Transactional(rollbackFor = { Exception.class, SQLException.class, IOException.class,
        ParserConfigurationException.class })
public class PrincipalResource extends ResourceResource {

    int shaStrength = 512;

    public PrincipalResource() {
    }

    /**
     * 
     * @param externalIdentifier the external UUID of a principal.
     * @return the {@link Principal} element representing the principal object with the "externalIdentifier".
     * @throws IOException is sending an error fails.
     */
    @GET
    @Produces(MediaType.TEXT_XML)
    @Path("{principalid}")
    @Transactional(readOnly = true)
    public JAXBElement<Principal> getPrincipal(@PathParam("principalid") String externalIdentifier)
            throws IOException {
        Map params = new HashMap<String, String>();
        params.put("externalId", externalIdentifier);
        try {
            Principal result = (Principal) (new RequestWrappers(this)).wrapRequestResource(params,
                    new GetPrincipal());
            return (result != null) ? (new ObjectFactory().createPrincipal(result))
                    : (new ObjectFactory().createPrincipal(new Principal()));
        } catch (NotInDataBaseException e) {
            httpServletResponse.sendError(HttpServletResponse.SC_NOT_FOUND, e.getMessage());
            return new ObjectFactory().createPrincipal(new Principal());
        } catch (ForbiddenException e2) {
            httpServletResponse.sendError(HttpServletResponse.SC_FORBIDDEN, e2.getMessage());
            return new ObjectFactory().createPrincipal(new Principal());
        }
    }

    private class GetPrincipal implements ILambda<Map, Principal> {

        @Override
        public Principal apply(Map params) throws NotInDataBaseException {
            final Number principalID = dbDispatcher.getResourceInternalIdentifier(
                    UUID.fromString((String) params.get("externalId")), Resource.PRINCIPAL);
            return dbDispatcher.getPrincipal(principalID);
        }
    }

    /**
     * 
     * @return a message containing the full name and the e-mail of the admin.
     * @throws IOException if sending an error in the call of "this.getPrincipalID()" fails.
     */
    @GET
    @Produces(MediaType.TEXT_PLAIN)
    @Path("admin")
    @Transactional(readOnly = true)
    public String getAdmin() throws IOException {
        Number remotePrincipalID = this.getPrincipalID();
        if (remotePrincipalID == null) {
            return " ";
        }
        return "The admin of the server database " + dbDispatcher.getDataBaseAdmin().getDisplayName()
                + " is availiable via e-mail " + dbDispatcher.getDataBaseAdmin().getEMail();
    }

    /**
     * 
     * @param email the e-mail of a principal.
     * @return a {@link Principal} representing the principal with the "email", 
     * if such a principal is found; otherwise "SC_NOT_FOUND" error is sent.
     * @throws IOException if sending an error fails.
     */
    @GET
    @Produces(MediaType.TEXT_XML)
    @Path("info")
    @Transactional(readOnly = true)
    public JAXBElement<Principal> getPrincipalByInfo(@QueryParam("email") String email) throws IOException {
        Map params = new HashMap<String, String>();
        params.put("email", email);
        try {
            Principal result = (Principal) (new RequestWrappers(this)).wrapRequestResource(params,
                    new GetPrincipalByInfo());
            return (result != null) ? (new ObjectFactory().createPrincipal(result))
                    : (new ObjectFactory().createPrincipal(new Principal()));
        } catch (NotInDataBaseException e) {
            httpServletResponse.sendError(HttpServletResponse.SC_NOT_FOUND, e.getMessage());
            return new ObjectFactory().createPrincipal(new Principal());
        } catch (ForbiddenException e2) {
            httpServletResponse.sendError(HttpServletResponse.SC_FORBIDDEN, e2.getMessage());
            return new ObjectFactory().createPrincipal(new Principal());
        }
    }

    private class GetPrincipalByInfo implements ILambda<Map, Principal> {

        @Override
        public Principal apply(Map params) throws NotInDataBaseException {
            return dbDispatcher.getPrincipalByInfo((String) params.get("email"));
        }
    }

    /**
     * 
     * @param externalIdentifier the external UUID of a principal.
     * @return a {@link CurrentPrincipalInfo} element containing "true" if the principal 
     * with externalIdentifier is logged-in in this session; 
     * with the current implementation does not make that much sense for me,
     * because the logged-in user knows his/her externalIDdentifier and knows that the other UUIDs
     * will be "not logged in", and therefore "false" is expected.
     * @throws IOException if sending an error fails.
     */
    @GET
    @Produces(MediaType.TEXT_XML)
    @Path("{principalid}/current")
    @Transactional(readOnly = true)
    public JAXBElement<CurrentPrincipalInfo> getCurrentPrincipalInfo(
            @PathParam("principalid") String externalIdentifier) throws IOException {
        Map params = new HashMap<String, String>();
        params.put("externalId", externalIdentifier);
        try {
            CurrentPrincipalInfo result = (CurrentPrincipalInfo) (new RequestWrappers(this))
                    .wrapRequestResource(params, new GetCurrentPrincipalInfo());
            return (result != null) ? (new ObjectFactory().createCurrentPrincipalInfo(result))
                    : (new ObjectFactory().createCurrentPrincipalInfo(new CurrentPrincipalInfo()));
        } catch (NotInDataBaseException e) {
            httpServletResponse.sendError(HttpServletResponse.SC_NOT_FOUND, e.getMessage());
            return new ObjectFactory().createCurrentPrincipalInfo(new CurrentPrincipalInfo());
        } catch (ForbiddenException e2) {
            httpServletResponse.sendError(HttpServletResponse.SC_FORBIDDEN, e2.getMessage());
            return new ObjectFactory().createCurrentPrincipalInfo(new CurrentPrincipalInfo());
        }
    }

    private class GetCurrentPrincipalInfo implements ILambda<Map, CurrentPrincipalInfo> {

        @Override
        public CurrentPrincipalInfo apply(Map params) throws NotInDataBaseException {
            String externalId = (String) params.get("externalId");
            String loggedInExternalId = (dbDispatcher
                    .getPrincipalExternalIDFromRemoteID(httpServletRequest.getRemoteUser())).toString();
            final CurrentPrincipalInfo principalInfo = new CurrentPrincipalInfo();
            principalInfo.setHref(getRelativeServiceURI() + "/principals/" + externalId);
            principalInfo.setCurrentPrincipal(externalId.equals(loggedInExternalId));
            return principalInfo;
        }
    }
    ////////////////////////////// 

    //    @POST
    //    @Produces(MediaType.TEXT_PLAIN)
    //    @Path("create/{remoteId}/{password}")
    //    public String createSpringAuthenticationRecord(@PathParam("remoteId") String remoteId, @PathParam("password") String password) throws IOException {
    //        Number remotePrincipalID = this.getPrincipalID();
    //        if (remotePrincipalID == null) {
    //            return "Logged in principal is null or anonymous.";
    //        }
    //
    //        if (dbDispatcher.getTypeOfPrincipalAccount(remotePrincipalID).equals(admin)) {
    //            try {
    //                int result = dbDispatcher.addSpringUser(remoteId, password, shaStrength, remoteId);
    //                return result + " record(s) has been added. Must be 2: 1 record for the principal, another for the authorities table.";
    //            } catch (DuplicateKeyException e) {
    //                loggerServer.error(e.toString());
    //                httpServletResponse.sendError(HttpServletResponse.SC_BAD_REQUEST);
    //                return e.toString();
    //            }
    //        } else {
    //            this.ADMIN_RIGHTS_EXPECTED();
    //            httpServletResponse.sendError(HttpServletResponse.SC_FORBIDDEN);
    //            return "Nothing is added.";
    //        }
    //
    //    }
    ///////////////////////////////////////
    //    @POST
    //    @Consumes(MediaType.APPLICATION_XML)
    //    @Produces(MediaType.APPLICATION_XML)
    //    @Path("{remoteId}")
    //    public JAXBElement<Principal> addPrincipal(@PathParam("remoteId") String remoteId, Principal principal) throws IOException {
    //
    //        Number remotePrincipalID = this.getPrincipalID();
    //        if (remotePrincipalID == null) {
    //            return new ObjectFactory().createPrincipal(new Principal());
    //        }
    //
    //        Map params = new HashMap<String, Object>();
    //        params.put("remoteId", remoteId);
    //        params.put("newPrincipal", principal);
    //
    //        if (dbDispatcher.getTypeOfPrincipalAccount(remotePrincipalID).equals(admin)) {
    //            return (new RequestWrappers(this)).wrapAddPrincipalRequest(params, new AddPrincipal());
    //        } else {
    //            this.ADMIN_RIGHTS_EXPECTED();
    //            httpServletResponse.sendError(HttpServletResponse.SC_FORBIDDEN);
    //            return new ObjectFactory().createPrincipal(new Principal());
    //        }
    //
    //    }
    /**
     * 
     * @param name the name of the principal to be registered as a basic-authentication user.
     * @param remoteId the  remote id of the principal to be registered.
     * @param password the password selected by the principal to be registered.
     * @param email the email of the principal to be registered.
     * @return a {@link Principal} element representing the just registered principal.
     * @throws IOException if sending an error fails.
     */
    @POST
    @Consumes(MediaType.APPLICATION_FORM_URLENCODED)
    @Produces(MediaType.APPLICATION_XML)
    @Path("register/nonshibboleth")
    public JAXBElement<Principal> registerNonShibbolizedPrincipal(@FormParam("name") String name,
            @FormParam("remoteId") String remoteId, @FormParam("password") String password,
            @FormParam("email") String email) throws IOException {
        Principal newPrincipal = new Principal();
        newPrincipal.setDisplayName(name);
        newPrincipal.setEMail(email);

        Map params = new HashMap<String, Object>();
        params.put("remoteId", remoteId);
        params.put("password", password);
        params.put("shaStrength", shaStrength);
        params.put("newPrincipal", newPrincipal);

        dbDispatcher.setResourcesPaths(this.getRelativeServiceURI());
        try {
            return (new RequestWrappers(this)).wrapAddPrincipalRequest(params,
                    new RegisterNonShibbolizedPrincipal());
        } catch (NotInDataBaseException e1) {
            httpServletResponse.sendError(HttpServletResponse.SC_NOT_FOUND, e1.getMessage());
            return new ObjectFactory().createPrincipal(new Principal());
        } catch (PrincipalExists e2) {
            httpServletResponse.sendError(HttpServletResponse.SC_CONFLICT, e2.getMessage());
            return new ObjectFactory().createPrincipal(new Principal());
        }
    }

    private class RegisterNonShibbolizedPrincipal implements ILambdaPrincipal<Map, Principal> {

        @Override
        public Principal apply(Map params) throws NotInDataBaseException, PrincipalExists {
            try {
                final int updatedSpringTables = dbDispatcher.addSpringUser((String) params.get("remoteId"),
                        (String) params.get("password"), (Integer) params.get("shaStrength"),
                        (String) params.get("remoteId"));
                final Number principalID = dbDispatcher.addPrincipal((Principal) params.get("newPrincipal"),
                        (String) params.get("remoteId"));
                return dbDispatcher.getPrincipal(principalID);
            } catch (DuplicateKeyException e) {
                throw new PrincipalExists(e);
            }
        }
    }

    /**
     * 
     * @param name the name of the principal to be registered.
     * @param remoteId the remote shibboleth id.
     * @param email the email.
     * @return a {@link Principal} element representing the just registered principal.
     * @throws IOException if sending an error fails.
     */
    @POST
    @Consumes(MediaType.APPLICATION_FORM_URLENCODED)
    @Produces(MediaType.APPLICATION_XML)
    @Path("register/shibboleth")
    public JAXBElement<Principal> registerShibbolizedPrincipal(@FormParam("name") String name,
            @FormParam("remoteId") String remoteId, @FormParam("email") String email) throws IOException {
        dbDispatcher.setResourcesPaths(this.getRelativeServiceURI());
        Principal newPrincipal = new Principal();
        newPrincipal.setDisplayName(name);
        newPrincipal.setEMail(email);
        Map params = new HashMap<String, Object>();
        params.put("remoteId", remoteId);
        params.put("newPrincipal", newPrincipal);

        dbDispatcher.setResourcesPaths(this.getRelativeServiceURI());
        try {
            return (new RequestWrappers(this)).wrapAddPrincipalRequest(params, new RegisterShibbolizedPrincipal());
        } catch (NotInDataBaseException e1) {
            httpServletResponse.sendError(HttpServletResponse.SC_NOT_FOUND, e1.getMessage());
            return new ObjectFactory().createPrincipal(new Principal());
        } catch (PrincipalExists e2) {
            httpServletResponse.sendError(HttpServletResponse.SC_CONFLICT, e2.getMessage());
            return new ObjectFactory().createPrincipal(new Principal());
        }
    }

    private class RegisterShibbolizedPrincipal implements ILambdaPrincipal<Map, Principal> {

        @Override
        public Principal apply(Map params) throws NotInDataBaseException, PrincipalExists {
            final Number principalID = dbDispatcher.addPrincipal((Principal) params.get("newPrincipal"),
                    (String) params.get("remoteId"));
            return dbDispatcher.getPrincipal(principalID);
        }
    }

    /**
     * It is a convenience method limiting proliferation of user profiles, however
     * the method is currently not used till security issues will be discussed with the LAT 
     * system administrators.
     * @param remoteId a shibboleth remote id, which will be set as a basic-authentication remote id as well.
     * @param password a shibboleth password which will be set as a basic-authentication password.
     * @return a message about amount of records; which must be 2 if everything goes well: 
     * 1 record for the "principal" table, 1is for the "authorities" tabl.
     * @throws IOException if sending an error fails.
     */
    @POST
    @Consumes(MediaType.APPLICATION_FORM_URLENCODED)
    @Produces(MediaType.TEXT_PLAIN)
    @Path("register/shibbolethasnonshibboleth")
    public String registerShibbolizedPrincipalAsNonShibb(@FormParam("remoteId") String remoteId,
            @FormParam("password") String password) throws IOException {
        try {
            int result = dbDispatcher.addSpringUser(remoteId, password, shaStrength, remoteId);
            return result
                    + " record(s) has been added. Must be 2: 1 record for the principal, another for the authorities table.";
        } catch (DuplicateKeyException e) {
            loggerServer.error(e.toString());
            httpServletResponse.sendError(HttpServletResponse.SC_BAD_REQUEST);
            return e.toString();
        }
    }

    /**
     * 
     * @param principal a {@link Principal} object representing new information (name, e-mail) about the principal 
     * with the eternal UUID given in the object.
     * @return a {@link Principal} element representing the just updated principal.
     * @throws IOException if sending an error message fails.
     */
    @PUT
    @Consumes(MediaType.APPLICATION_XML)
    @Produces(MediaType.APPLICATION_XML)
    @Path("")
    public JAXBElement<Principal> updatePrincipal(Principal principal) throws IOException {
        Number remotePrincipalID = this.getPrincipalID();
        if (remotePrincipalID == null) {
            return new ObjectFactory().createPrincipal(new Principal());
        }
        if (dbDispatcher.getTypeOfPrincipalAccount(remotePrincipalID).equals(admin)) {
            Map params = new HashMap<String, Object>();
            params.put("newPrincipal", principal);
            try {
                Principal result = (Principal) (new RequestWrappers(this)).wrapRequestResource(params,
                        new UpdatePrincipal());
                return (result != null) ? (new ObjectFactory().createPrincipal(result))
                        : (new ObjectFactory().createPrincipal(new Principal()));
            } catch (NotInDataBaseException e) {
                httpServletResponse.sendError(HttpServletResponse.SC_NOT_FOUND, e.getMessage());
                return new ObjectFactory().createPrincipal(new Principal());
            } catch (ForbiddenException e2) {
                httpServletResponse.sendError(HttpServletResponse.SC_FORBIDDEN, e2.getMessage());
                return new ObjectFactory().createPrincipal(new Principal());
            }
        } else {
            this.ADMIN_RIGHTS_EXPECTED();
            httpServletResponse.sendError(HttpServletResponse.SC_FORBIDDEN);
            return new ObjectFactory().createPrincipal(new Principal());
        }
    }
    /////////////////////////////////

    /**
     * 
     * @param name a new full name.
     * @param email a new email.
     * @return a {@link Principal} element representing the just updated principal.
     * @throws IOException if sending an error fails.
     * @throws NotInDataBaseException if the in-logged principal is not found in the database
     * or update fails.
     */
    @POST
    @Consumes(MediaType.APPLICATION_FORM_URLENCODED)
    @Produces(MediaType.APPLICATION_XML)
    @Path("updateme")
    public JAXBElement<Principal> updatePrincipalFromForm(@FormParam("name") String name,
            @FormParam("email") String email) throws IOException, NotInDataBaseException {

        Principal newPrincipal = new Principal();
        newPrincipal.setDisplayName(name);
        newPrincipal.setEMail(email);
        String remoteId = httpServletRequest.getRemoteUser();
        String externalId = (dbDispatcher.getPrincipalExternalIDFromRemoteID(remoteId)).toString();
        String href = this.getRelativeServiceURI() + "/principals/" + externalId;
        newPrincipal.setId(externalId);
        newPrincipal.setHref(href);
        Map params = new HashMap<String, Object>();
        params.put("newPrincipal", newPrincipal);
        try {
            Principal result = (Principal) (new RequestWrappers(this)).wrapRequestResource(params,
                    new UpdatePrincipal());
            return (result != null) ? (new ObjectFactory().createPrincipal(result))
                    : (new ObjectFactory().createPrincipal(new Principal()));
        } catch (ForbiddenException e2) {
            httpServletResponse.sendError(HttpServletResponse.SC_FORBIDDEN, e2.getMessage());
            return new ObjectFactory().createPrincipal(new Principal());
        }
    }

    private class UpdatePrincipal implements ILambda<Map, Principal> {

        @Override
        public Principal apply(Map params) throws NotInDataBaseException {
            Principal principal = (Principal) params.get("newPrincipal");
            Number principalIDupd = dbDispatcher.updatePrincipal(principal);
            return dbDispatcher.getPrincipal(principalIDupd);
        }
    }

    /**
     * 
     * @param externalId the external UUID of a principal.
     * @param accountType an account type: "user", "developer", "admin".
     * @return a message telling if and how the account is updated; this action can be performed only by admin.
     * @throws IOException 
     */
    @PUT
    @Produces(MediaType.TEXT_PLAIN)
    @Path("{externalId}/account/{accountType}")
    public String updatePrincipalAccount(@PathParam("externalId") String externalId,
            @PathParam("accountType") String accountType) throws IOException {
        Number remotePrincipalID = this.getPrincipalID();
        if (remotePrincipalID == null) {
            return "Nothing is updated.";
        }
        if (dbDispatcher.getTypeOfPrincipalAccount(remotePrincipalID).equals(admin)) {
            try {
                final boolean updated = dbDispatcher.updateAccount(UUID.fromString(externalId), accountType);
                if (updated) {
                    return "The account was updated to " + dbDispatcher.getTypeOfPrincipalAccount(dbDispatcher
                            .getResourceInternalIdentifier(UUID.fromString(externalId), Resource.PRINCIPAL));
                } else {
                    loggerServer.debug("The account is not updated.");
                    httpServletResponse.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR,
                            "Account is not updated.");
                    return "Account is not updated.";
                }
            } catch (NotInDataBaseException e) {
                loggerServer.debug(e.toString());
                httpServletResponse.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, e.toString());
                return "Account is updated.";
            }
        } else {
            this.ADMIN_RIGHTS_EXPECTED();
            httpServletResponse.sendError(HttpServletResponse.SC_FORBIDDEN);
            return "Account is not updated.";
        }

    }

    /**
     * 
     * @param externalIdentifier the external UUID of a principal to be deleted.
     * @return a message informing if deletion has been successful; see "error" messages if deletion 
     * has not taken place.
     * @throws IOException if sending error messages fails.
     */
    @DELETE
    @Path("{principalId}")
    public String deletePrincipal(@PathParam("principalId") String externalIdentifier) throws IOException {
        Number remotePrincipalID = this.getPrincipalID();
        if (remotePrincipalID == null) {
            return "Nothings is deleted.";
        }
        if (dbDispatcher.getTypeOfPrincipalAccount(remotePrincipalID).equals(admin)) {
            try {
                final Number principalID = dbDispatcher
                        .getResourceInternalIdentifier(UUID.fromString(externalIdentifier), Resource.PRINCIPAL);
                try {
                    final int result = dbDispatcher.deletePrincipal(principalID);
                    return "There is " + result + " row deleted";
                } catch (PrincipalCannotBeDeleted e2) {
                    loggerServer.debug(e2.toString());
                    ;
                    httpServletResponse.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, e2.toString());
                    return "Nothing is deleted.";
                }
            } catch (NotInDataBaseException e) {
                loggerServer.debug(e.toString());
                httpServletResponse.sendError(HttpServletResponse.SC_NOT_FOUND, e.toString());
                return "Nothing is deleted.";
            }
        } else {
            this.ADMIN_RIGHTS_EXPECTED();
            httpServletResponse.sendError(HttpServletResponse.SC_FORBIDDEN);
            return "Principal is not deleted.";
        }

    }
}