net.e2.bw.servicereg.core.rest.OrganizationRestService.java Source code

Java tutorial

Introduction

Here is the source code for net.e2.bw.servicereg.core.rest.OrganizationRestService.java

Source

/* Copyright (c) 2011 Danish Maritime Authority
 *
 * 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 3 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 General Public License
 * along with this library.  If not, see <http://www.gnu.org/licenses/>.
 */
package net.e2.bw.servicereg.core.rest;

import net.e2.bw.servicereg.core.Roles.OrganizationRole;
import net.e2.bw.servicereg.core.service.OrganizationService;
import net.e2.bw.servicereg.model.AuthorizedUser;
import net.e2.bw.servicereg.model.JsonSerializable;
import net.e2.bw.servicereg.model.Organization;
import net.e2.bw.servicereg.model.UserOrganization;
import org.apache.commons.io.IOUtils;
import org.jboss.resteasy.annotations.GZIP;
import org.jboss.resteasy.annotations.cache.NoCache;
import org.jboss.resteasy.plugins.providers.multipart.InputPart;
import org.jboss.resteasy.plugins.providers.multipart.MultipartFormDataInput;
import org.slf4j.Logger;

import javax.imageio.ImageIO;
import javax.inject.Inject;
import javax.ws.rs.*;
import javax.ws.rs.core.CacheControl;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.MultivaluedMap;
import javax.ws.rs.core.Response;
import java.awt.image.BufferedImage;
import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import java.net.URI;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Map;

import static net.e2.bw.servicereg.core.rest.OrganizationRestService.OrganizationRoleUpdateParam.Update.*;

/**
 * Implements the organization REST api
 */
@Path("/api")
public class OrganizationRestService extends AbstractRestService {

    @Inject
    Logger log;

    @Inject
    @SuppressWarnings("CdiInjectionPointsInspection")
    OrganizationService organizationService;

    /**
     * Searches the organizations based on an organization name pattern
     * @param organizationNamePattern the name pattern to match
     * @return the search result
     */
    @GET
    @Produces(MediaType.APPLICATION_JSON)
    @Path("org")
    @GZIP
    @NoCache
    public Iterable<Organization> getOrganizations(
            @QueryParam("namePattern") @DefaultValue("") String organizationNamePattern) {
        requireAuthenticated();
        return organizationService.searchOrganizations(organizationNamePattern);
    }

    /**
     * Returns a specific organization
     * @param organizationId the organization id
     * @return the organization
     */
    @GET
    @Produces(MediaType.APPLICATION_JSON)
    @Path("org/{organizationId}")
    @GZIP
    @NoCache
    public UserOrganization getOrganization(@PathParam("organizationId") String organizationId) {
        requireAuthenticated();
        return organizationService.getUserOrganization(organizationId, getSubject().getUserId());
    }

    /**
     * Returns if a specific organization exists
     * @param organizationId the organization id
     * @return if the organization exists
     */
    @GET
    @Produces(MediaType.TEXT_PLAIN)
    @Path("org/{organizationId}/exists")
    @NoCache
    public boolean organizationExists(@PathParam("organizationId") String organizationId) {
        requireAuthenticated();
        return organizationService.getOrganization(organizationId) != null;
    }

    /**
     * Returns the logo of an organization
     * @param organizationId the organization id
     * @return the logo of an organization
     */
    @GET
    @Path("org/{organizationId}/logo")
    @Produces("image/png")
    public Response getOrgPhoto(@PathParam("organizationId") String organizationId) {

        CacheControl cc = new CacheControl();
        cc.setMaxAge(86400);

        byte[] image = organizationService.getOrganizationLogo(organizationId);
        if (image == null) {
            // Appallingly, Response.temporaryRedirect() is relative to the "/rest" root :-(
            URI profileImage = URI.create("../app/img/organization.png");
            return Response.temporaryRedirect(profileImage).cacheControl(cc).build();
        }

        return Response.ok(image).cacheControl(cc).build();
    }

    /**
     * Updates the logo of an organization
     * @param organizationId the organization id
     * @param input the image
     * @return the updated organization
     */
    @POST
    @Path("org/{organizationId}/logo")
    @Consumes("multipart/form-data")
    @NoCache
    public Response setOrgPhoto(@PathParam("organizationId") String organizationId, MultipartFormDataInput input) {

        // Look up organization with incl. the roles of the current subject in relation to the organization
        UserOrganization organization = organizationService.getUserOrganization(organizationId,
                getSubject().getUserId());

        // Operation only allowed for site or organization admins
        if (!hasSiteRole("admin") && !hasOrganizationRole(organization, "admin")) {
            throw new WebApplicationException("User is not authorized to updated organization photo ",
                    Response.Status.UNAUTHORIZED);
        }

        //Get API input data
        Map<String, List<InputPart>> uploadForm = input.getFormDataMap();

        //Get file data to save
        List<InputPart> inputParts = uploadForm.get("attachment");

        try {
            if (inputParts != null && inputParts.size() > 0) {
                InputPart inputPart = inputParts.get(0);

                // convert the uploaded file to input stream
                InputStream inputStream = inputPart.getBody(InputStream.class, null);

                // If not PNG, convert it
                byte[] bytes;
                String fileName = getFileName(inputPart.getHeaders());
                if (fileName == null || !fileName.toLowerCase().endsWith(".png")) {
                    BufferedImage image = ImageIO.read(inputStream);
                    ByteArrayOutputStream pngImage = new ByteArrayOutputStream();
                    ImageIO.write(image, "png", pngImage);
                    bytes = pngImage.toByteArray();

                } else {
                    // Already a PNG image
                    bytes = IOUtils.toByteArray(inputStream);
                }

                organization.setLogo(bytes);
                organizationService.updateOrganization(organization);
            }
        } catch (Exception e) {
            log.error("Error uploading logo", e);
            return Response.serverError().build();
        }
        return Response.status(201).entity(getOrganization(organizationId)).build();
    }

    /** Extracts the file name from the header */
    private String getFileName(MultivaluedMap<String, String> header) {

        return Arrays.asList(header.getFirst("Content-Disposition").split(";")).stream()
                .filter(fn -> fn.trim().startsWith("filename"))
                .map(fn -> fn.split("=")[1].trim().replaceAll("\"", "")).findAny().orElse(null);
    }

    /**
     * Creates a new organization
     * @param organization the organization to create
     * @return the newly created organization
     */
    @POST
    @Produces(MediaType.APPLICATION_JSON)
    @Consumes(MediaType.APPLICATION_JSON)
    @Path("org")
    @NoCache
    public Organization createOrganization(Organization organization) {

        // All registered users can create a new organization
        requiresSiteRole("user");

        return organizationService.createOrganization(organization, getSubject().getUserId());
    }

    /**
     * Updates an organization
     * @param organization the organization to update
     * @return the updated organization
     */
    @PUT
    @Produces(MediaType.APPLICATION_JSON)
    @Consumes(MediaType.APPLICATION_JSON)
    @Path("org")
    @NoCache
    public Organization updateOrganization(Organization organization) {

        // Look up organization with incl. the roles of the current subject in relation to the organization
        UserOrganization existingOrg = organizationService.getUserOrganization(organization.getOrganizationId(),
                getSubject().getUserId());

        // Operation only allowed for site or organization admins
        if (!hasSiteRole("admin") && !hasOrganizationRole(existingOrg, "admin")) {
            throw new WebApplicationException("User is not authorized to updated organization",
                    Response.Status.UNAUTHORIZED);
        }

        // The logo is not sent along
        if (organization.getLogo() == null) {
            organization.setLogo(existingOrg.getLogo());
        }

        return organizationService.updateOrganization(organization);
    }

    // ------------------------------------------------------------------------
    // MEMBERSHIP
    // ------------------------------------------------------------------------

    /**
     * Returns the members of an organization
     * @param organizationId the organization id
     * @return the members of the organization
     */
    @GET
    @Produces(MediaType.APPLICATION_JSON)
    @Path("org/{organizationId}/member")
    @GZIP
    @NoCache
    public Iterable<AuthorizedUser> getOrganizationMembers(@PathParam("organizationId") String organizationId) {

        // Look up organization with incl. the roles of the current subject in relation to the organization
        UserOrganization organization = organizationService.getUserOrganization(organizationId,
                getSubject().getUserId());

        // Operation only allowed for site admins or organization members
        if (!hasSiteRole("admin") && !hasOrganizationRole(organization, "user")) {
            return Collections.emptyList();
        }

        return organizationService.getOrganizationUsers(organizationId);
    }

    /**
     * Returns the number of organization members
     * @param organizationId the organization id
     * @return the number of organization members
     */
    @GET
    @Produces(MediaType.APPLICATION_JSON)
    @Path("org/{organizationId}/member/count")
    @NoCache
    public String getOrganizationMemberCount(@PathParam("organizationId") String organizationId) {
        requireAuthenticated();

        int cnt = organizationService.getOrganizationUsers(organizationId).size();
        return "{\"membersCount\":" + cnt + "}";
    }

    // ------------------------------------------------------------------------
    // ORGANIZATION ROLES
    // ------------------------------------------------------------------------

    /**
     * Updates the organization role of user
     * @param organizationId the organization
     * @param roleUpdate the role update
     */
    @PUT
    @Consumes(MediaType.APPLICATION_JSON)
    @Produces(MediaType.APPLICATION_JSON)
    @Path("org/{organizationId}/role")
    @NoCache
    public void assignOrganizationRole(@PathParam("organizationId") String organizationId,
            OrganizationRoleUpdateParam roleUpdate) {

        // TODO: Check roles. There are many combinations depending on
        // the site roles of the user, organization roles of the user
        // and whether the user is the current subject.

        String userId = roleUpdate.getUserId();
        OrganizationRole role = OrganizationRole.valueOf(roleUpdate.getRole());

        if (roleUpdate.getUpdate() == reject) {
            if (role != OrganizationRole.applicant && role != OrganizationRole.invited) {
                throw new WebApplicationException("Rejected role must be 'applicant' or 'invited'",
                        Response.Status.BAD_REQUEST);
            }
            // Delete role and membership
            organizationService.removeOrganizationRole(organizationId, userId, role.toString(), true);

        } else if (roleUpdate.getUpdate() == accept) {
            if (role != OrganizationRole.applicant && role != OrganizationRole.invited) {
                throw new WebApplicationException("Accepted role must be 'applicant' or 'invited'",
                        Response.Status.BAD_REQUEST);
            }
            // Delete role, but not membership
            organizationService.removeOrganizationRole(organizationId, userId, role.toString(), false);

        } else if (roleUpdate.getUpdate() == remove && role == OrganizationRole.user) {
            // Leave the organization
            organizationService.leaveOrganization(organizationId, userId);

        } else if (roleUpdate.getUpdate() == remove) {
            // Delete role, but not membership
            organizationService.removeOrganizationRole(organizationId, userId, role.toString(), false);

        } else if (roleUpdate.getUpdate() == assign) {
            organizationService.assignOrganizationRole(organizationId, userId, role.toString());
        }
    }

    /** Used for managing organization roles */
    public static class OrganizationRoleUpdateParam implements JsonSerializable {
        enum Update {
            assign, remove, accept, reject
        }

        String userId;
        String role;
        Update update;
        String message;

        public String getUserId() {
            return userId;
        }

        public void setUserId(String userId) {
            this.userId = userId;
        }

        public String getRole() {
            return role;
        }

        public void setRole(String role) {
            this.role = role;
        }

        public Update getUpdate() {
            return update;
        }

        public void setUpdate(Update update) {
            this.update = update;
        }

        public String getMessage() {
            return message;
        }

        public void setMessage(String message) {
            this.message = message;
        }
    }

}