net.maritimecloud.identityregistry.controllers.ServiceController.java Source code

Java tutorial

Introduction

Here is the source code for net.maritimecloud.identityregistry.controllers.ServiceController.java

Source

/*
 * Copyright 2017 Danish Maritime Authority.
 *
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 */
package net.maritimecloud.identityregistry.controllers;

import lombok.extern.slf4j.Slf4j;
import net.maritimecloud.identityregistry.exception.DuplicatedKeycloakEntry;
import net.maritimecloud.identityregistry.exception.McBasicRestException;
import net.maritimecloud.identityregistry.model.data.CertificateRevocation;
import net.maritimecloud.identityregistry.model.data.PemCertificate;
import net.maritimecloud.identityregistry.model.database.Certificate;
import net.maritimecloud.identityregistry.model.database.CertificateModel;
import net.maritimecloud.identityregistry.model.database.Organization;
import net.maritimecloud.identityregistry.model.database.entities.Service;
import net.maritimecloud.identityregistry.services.EntityService;
import net.maritimecloud.identityregistry.utils.KeycloakAdminUtil;
import net.maritimecloud.identityregistry.utils.MCIdRegConstants;
import net.maritimecloud.identityregistry.utils.MrnUtil;
import net.maritimecloud.identityregistry.utils.ValidateUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.DataIntegrityViolationException;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;

import javax.servlet.http.HttpServletRequest;
import javax.validation.Valid;
import java.io.IOException;

@RestController
@Slf4j
public class ServiceController extends EntityController<Service> {
    @Autowired
    private KeycloakAdminUtil keycloakAU;

    @Autowired
    public void setEntityService(EntityService<Service> entityService) {
        this.entityService = entityService;
    }

    /**
     * Creates a new Service
     * 
     * @return a reply...
     * @throws McBasicRestException 
     */
    @RequestMapping(value = "/api/org/{orgMrn}/service", method = RequestMethod.POST, produces = "application/json;charset=UTF-8")
    @ResponseBody
    @PreAuthorize("hasRole('SERVICE_ADMIN') and @accessControlUtil.hasAccessToOrg(#orgMrn)")
    public ResponseEntity<Service> createService(HttpServletRequest request, @PathVariable String orgMrn,
            @Valid @RequestBody Service input, BindingResult bindingResult) throws McBasicRestException {
        ValidateUtil.hasErrors(bindingResult, request);
        Organization org = this.organizationService.getOrganizationByMrn(orgMrn);
        if (org != null) {
            // Check that the entity being created belongs to the organization
            if (!MrnUtil.getOrgShortNameFromOrgMrn(orgMrn)
                    .equals(MrnUtil.getOrgShortNameFromEntityMrn(input.getMrn()))) {
                throw new McBasicRestException(HttpStatus.BAD_REQUEST, MCIdRegConstants.MISSING_RIGHTS,
                        request.getServletPath());
            }
            input.setIdOrganization(org.getId());
            // Setup a keycloak client for the service if needed
            if (input.getOidcAccessType() != null && !input.getOidcAccessType().trim().isEmpty()) {
                // Check if the redirect uri is set if access type is "bearer-only"
                if (!"bearer-only".equals(input.getOidcAccessType())
                        && (input.getOidcRedirectUri() == null || input.getOidcRedirectUri().trim().isEmpty())) {
                    throw new McBasicRestException(HttpStatus.BAD_REQUEST,
                            MCIdRegConstants.OIDC_MISSING_REDIRECT_URL, request.getServletPath());
                }
                keycloakAU.init(KeycloakAdminUtil.BROKER_INSTANCE);
                input.setOidcClientId(input.getMrn());
                try {
                    String clientSecret = keycloakAU.createClient(input.getMrn(), input.getOidcAccessType(),
                            input.getOidcRedirectUri());
                    if ("confidential".equals(input.getOidcAccessType())) {
                        input.setOidcClientSecret(clientSecret);
                    } else {
                        input.setOidcClientSecret(null);
                    }
                } catch (IOException e) {
                    throw new McBasicRestException(HttpStatus.INTERNAL_SERVER_ERROR,
                            MCIdRegConstants.ERROR_CREATING_KC_CLIENT, request.getServletPath());
                } catch (DuplicatedKeycloakEntry dke) {
                    throw new McBasicRestException(HttpStatus.CONFLICT, dke.getErrorMessage(),
                            request.getServletPath());
                }
            } else {
                input.setOidcAccessType(null);
                input.setOidcClientId(null);
                input.setOidcClientSecret(null);
                input.setOidcRedirectUri(null);
            }
            try {
                Service newService = this.entityService.save(input);
                return new ResponseEntity<>(newService, HttpStatus.OK);
            } catch (DataIntegrityViolationException e) {
                // If save to DB failed, remove the client from keycloak if it was created.
                if (input.getOidcAccessType() != null && !input.getOidcAccessType().trim().isEmpty()) {
                    keycloakAU.deleteClient(input.getMrn());
                }
                throw new McBasicRestException(HttpStatus.CONFLICT, e.getRootCause().getMessage(),
                        request.getServletPath());
            }
        } else {
            throw new McBasicRestException(HttpStatus.NOT_FOUND, MCIdRegConstants.ORG_NOT_FOUND,
                    request.getServletPath());
        }
    }

    /**
     * Returns info about the service identified by the given ID
     * 
     * @return a reply...
     * @throws McBasicRestException 
     */
    @RequestMapping(value = "/api/org/{orgMrn}/service/{serviceMrn}", method = RequestMethod.GET, produces = "application/json;charset=UTF-8")
    @ResponseBody
    @PreAuthorize("@accessControlUtil.hasAccessToOrg(#orgMrn)")
    public ResponseEntity<Service> getService(HttpServletRequest request, @PathVariable String orgMrn,
            @PathVariable String serviceMrn) throws McBasicRestException {
        return this.getEntity(request, orgMrn, serviceMrn);
    }

    /**
     * Updates a Service
     * 
     * @return a reply...
     * @throws McBasicRestException 
     */
    @RequestMapping(value = "/api/org/{orgMrn}/service/{serviceMrn}", method = RequestMethod.PUT)
    @ResponseBody
    @PreAuthorize("hasRole('SERVICE_ADMIN') and @accessControlUtil.hasAccessToOrg(#orgMrn)")
    public ResponseEntity<?> updateService(HttpServletRequest request, @PathVariable String orgMrn,
            @PathVariable String serviceMrn, @Valid @RequestBody Service input, BindingResult bindingResult)
            throws McBasicRestException {
        ValidateUtil.hasErrors(bindingResult, request);
        if (!serviceMrn.equals(input.getMrn())) {
            throw new McBasicRestException(HttpStatus.BAD_REQUEST, MCIdRegConstants.URL_DATA_MISMATCH,
                    request.getServletPath());
        }
        Organization org = this.organizationService.getOrganizationByMrn(orgMrn);
        if (org != null) {
            // Check that the entity being updated belongs to the organization
            if (!MrnUtil.getOrgShortNameFromOrgMrn(orgMrn)
                    .equals(MrnUtil.getOrgShortNameFromEntityMrn(input.getMrn()))) {
                throw new McBasicRestException(HttpStatus.BAD_REQUEST, MCIdRegConstants.MISSING_RIGHTS,
                        request.getServletPath());
            }
            Service service = this.entityService.getByMrn(serviceMrn);
            if (service == null) {
                throw new McBasicRestException(HttpStatus.NOT_FOUND, MCIdRegConstants.ENTITY_NOT_FOUND,
                        request.getServletPath());
            }
            if (service.getIdOrganization().compareTo(org.getId()) == 0) {
                // Update the keycloak client for the service if needed
                if (input.getOidcAccessType() != null && !input.getOidcAccessType().trim().isEmpty()) {
                    // Check if the redirect uri is set if access type is "bearer-only"
                    if (!"bearer-only".equals(input.getOidcAccessType()) && (input.getOidcRedirectUri() == null
                            || input.getOidcRedirectUri().trim().isEmpty())) {
                        throw new McBasicRestException(HttpStatus.BAD_REQUEST,
                                MCIdRegConstants.OIDC_MISSING_REDIRECT_URL, request.getServletPath());
                    }
                    keycloakAU.init(KeycloakAdminUtil.BROKER_INSTANCE);
                    String clientSecret;
                    try {
                        if (service.getOidcClientId() != null && !service.getOidcClientId().isEmpty()) {
                            clientSecret = keycloakAU.updateClient(service.getMrn(), service.getOidcAccessType(),
                                    service.getOidcRedirectUri());
                        } else {
                            service.setOidcClientId(service.getMrn());
                            clientSecret = keycloakAU.createClient(service.getMrn(), service.getOidcAccessType(),
                                    service.getOidcRedirectUri());
                        }
                    } catch (IOException e) {
                        log.error("Error while updating/creation client in keycloak.", e);
                        throw new McBasicRestException(HttpStatus.INTERNAL_SERVER_ERROR,
                                MCIdRegConstants.ERROR_CREATING_KC_CLIENT, request.getServletPath());
                    } catch (DuplicatedKeycloakEntry dke) {
                        throw new McBasicRestException(HttpStatus.CONFLICT, dke.getErrorMessage(),
                                request.getServletPath());
                    }
                    if ("confidential".equals(service.getOidcAccessType())) {
                        service.setOidcClientSecret(clientSecret);
                    } else {
                        service.setOidcClientSecret(null);
                    }
                }
                input.selectiveCopyTo(service);
                try {
                    this.entityService.save(service);
                    return new ResponseEntity<>(HttpStatus.OK);
                } catch (DataIntegrityViolationException e) {
                    throw new McBasicRestException(HttpStatus.CONFLICT, e.getRootCause().getMessage(),
                            request.getServletPath());
                }
            }
            throw new McBasicRestException(HttpStatus.FORBIDDEN, MCIdRegConstants.MISSING_RIGHTS,
                    request.getServletPath());
        } else {
            throw new McBasicRestException(HttpStatus.NOT_FOUND, MCIdRegConstants.ORG_NOT_FOUND,
                    request.getServletPath());
        }
    }

    /**
     * Deletes a Service
     * 
     * @return a reply...
     * @throws McBasicRestException 
     */
    @RequestMapping(value = "/api/org/{orgMrn}/service/{serviceMrn}", method = RequestMethod.DELETE)
    @ResponseBody
    @PreAuthorize("hasRole('SERVICE_ADMIN') and @accessControlUtil.hasAccessToOrg(#orgMrn)")
    public ResponseEntity<?> deleteService(HttpServletRequest request, @PathVariable String orgMrn,
            @PathVariable String serviceMrn) throws McBasicRestException {
        Organization org = this.organizationService.getOrganizationByMrn(orgMrn);
        if (org != null) {
            // Check that the entity being deleted belongs to the organization
            if (!MrnUtil.getOrgShortNameFromOrgMrn(orgMrn)
                    .equals(MrnUtil.getOrgShortNameFromEntityMrn(serviceMrn))) {
                throw new McBasicRestException(HttpStatus.BAD_REQUEST, MCIdRegConstants.MISSING_RIGHTS,
                        request.getServletPath());
            }
            Service service = this.entityService.getByMrn(serviceMrn);
            if (service == null) {
                throw new McBasicRestException(HttpStatus.NOT_FOUND, MCIdRegConstants.ENTITY_NOT_FOUND,
                        request.getServletPath());
            }
            if (service.getIdOrganization().compareTo(org.getId()) == 0) {
                // Delete the keycloak client for the service if needed
                if (service.getOidcAccessType() != null && !service.getOidcAccessType().trim().isEmpty()
                        && service.getOidcRedirectUri() != null && !service.getOidcRedirectUri().trim().isEmpty()) {
                    keycloakAU.init(KeycloakAdminUtil.BROKER_INSTANCE);
                    keycloakAU.deleteClient(service.getMrn());
                }
                this.entityService.delete(service.getId());
                return new ResponseEntity<>(HttpStatus.OK);
            }
            throw new McBasicRestException(HttpStatus.FORBIDDEN, MCIdRegConstants.MISSING_RIGHTS,
                    request.getServletPath());
        } else {
            throw new McBasicRestException(HttpStatus.NOT_FOUND, MCIdRegConstants.ORG_NOT_FOUND,
                    request.getServletPath());
        }
    }

    /**
     * Returns a list of services owned by the organization identified by the given ID
     * 
     * @return a reply...
     * @throws McBasicRestException 
     */
    @RequestMapping(value = "/api/org/{orgMrn}/services", method = RequestMethod.GET, produces = "application/json;charset=UTF-8")
    @PreAuthorize("@accessControlUtil.hasAccessToOrg(#orgMrn)")
    public Page<Service> getOrganizationServices(HttpServletRequest request, @PathVariable String orgMrn,
            Pageable pageable) throws McBasicRestException {
        return this.getOrganizationEntities(request, orgMrn, pageable);
    }

    /**
     * Returns new certificate for the service identified by the given ID
     * 
     * @return a reply...
     * @throws McBasicRestException 
     */
    @RequestMapping(value = "/api/org/{orgMrn}/service/{serviceMrn}/certificate/issue-new", method = RequestMethod.GET, produces = "application/json;charset=UTF-8")
    @PreAuthorize("hasRole('SERVICE_ADMIN') and @accessControlUtil.hasAccessToOrg(#orgMrn)")
    public ResponseEntity<PemCertificate> newServiceCert(HttpServletRequest request, @PathVariable String orgMrn,
            @PathVariable String serviceMrn) throws McBasicRestException {
        return this.newEntityCert(request, orgMrn, serviceMrn, "service");
    }

    /**
     * Revokes certificate for the service identified by the given ID
     * 
     * @return a reply...
     * @throws McBasicRestException 
     */
    @RequestMapping(value = "/api/org/{orgMrn}/service/{serviceMrn}/certificate/{certId}/revoke", method = RequestMethod.POST, produces = "application/json;charset=UTF-8")
    @PreAuthorize("hasRole('SERVICE_ADMIN') and @accessControlUtil.hasAccessToOrg(#orgMrn)")
    public ResponseEntity<?> revokeServiceCert(HttpServletRequest request, @PathVariable String orgMrn,
            @PathVariable String serviceMrn, @PathVariable Long certId,
            @Valid @RequestBody CertificateRevocation input) throws McBasicRestException {
        return this.revokeEntityCert(request, orgMrn, serviceMrn, certId, input);
    }

    /**
     * Returns keycloak.json the service identified by the given ID
     *
     * @return a reply...
     * @throws McBasicRestException
     */
    @RequestMapping(value = "/api/org/{orgMrn}/service/{serviceMrn}/keycloakjson", method = RequestMethod.GET, produces = "application/json;charset=UTF-8")
    @ResponseBody
    @PreAuthorize("hasRole('SERVICE_ADMIN') and @accessControlUtil.hasAccessToOrg(#orgMrn)")
    public ResponseEntity<String> getServiceKeycloakJson(HttpServletRequest request, @PathVariable String orgMrn,
            @PathVariable String serviceMrn) throws McBasicRestException {
        Organization org = this.organizationService.getOrganizationByMrn(orgMrn);
        if (org != null) {
            // Check that the entity being queried belongs to the organization
            if (!MrnUtil.getOrgShortNameFromOrgMrn(orgMrn)
                    .equals(MrnUtil.getOrgShortNameFromEntityMrn(serviceMrn))) {
                throw new McBasicRestException(HttpStatus.BAD_REQUEST, MCIdRegConstants.MISSING_RIGHTS,
                        request.getServletPath());
            }
            Service service = this.entityService.getByMrn(serviceMrn);
            if (service == null) {
                throw new McBasicRestException(HttpStatus.NOT_FOUND, MCIdRegConstants.ENTITY_NOT_FOUND,
                        request.getServletPath());
            }
            if (service.getIdOrganization().compareTo(org.getId()) == 0) {
                // Get the keycloak json for the client the service represents if it exists
                if (service.getOidcAccessType() != null && !service.getOidcAccessType().trim().isEmpty()) {
                    keycloakAU.init(KeycloakAdminUtil.BROKER_INSTANCE);
                    String keycloakJson = keycloakAU.getClientKeycloakJson(service.getMrn());
                    return new ResponseEntity<>(keycloakJson, HttpStatus.OK);
                }
                throw new McBasicRestException(HttpStatus.NOT_FOUND, MCIdRegConstants.OIDC_CONF_FILE_NOT_AVAILABLE,
                        request.getServletPath());
            }
            throw new McBasicRestException(HttpStatus.FORBIDDEN, MCIdRegConstants.MISSING_RIGHTS,
                    request.getServletPath());
        } else {
            throw new McBasicRestException(HttpStatus.NOT_FOUND, MCIdRegConstants.ORG_NOT_FOUND,
                    request.getServletPath());
        }
    }

    /**
     * Returns keycloak.json the service identified by the given ID
     *
     * @return a reply...
     * @throws McBasicRestException
     */
    @RequestMapping(value = "/api/org/{orgMrn}/service/{serviceMrn}/jbossxml", method = RequestMethod.GET)
    @ResponseBody
    @PreAuthorize("hasRole('SERVICE_ADMIN') and @accessControlUtil.hasAccessToOrg(#orgMrn)")
    public ResponseEntity<String> getServiceJbossXml(HttpServletRequest request, @PathVariable String orgMrn,
            @PathVariable String serviceMrn) throws McBasicRestException {
        Organization org = this.organizationService.getOrganizationByMrn(orgMrn);
        if (org != null) {
            // Check that the entity being queried belongs to the organization
            if (!MrnUtil.getOrgShortNameFromOrgMrn(orgMrn)
                    .equals(MrnUtil.getOrgShortNameFromEntityMrn(serviceMrn))) {
                throw new McBasicRestException(HttpStatus.BAD_REQUEST, MCIdRegConstants.MISSING_RIGHTS,
                        request.getServletPath());
            }
            Service service = this.entityService.getByMrn(serviceMrn);
            if (service == null) {
                throw new McBasicRestException(HttpStatus.NOT_FOUND, MCIdRegConstants.ENTITY_NOT_FOUND,
                        request.getServletPath());
            }
            if (service.getIdOrganization().compareTo(org.getId()) == 0) {
                // Get the jboss xml for the client the service represents if it exists
                if (service.getOidcAccessType() != null && !service.getOidcAccessType().trim().isEmpty()) {
                    keycloakAU.init(KeycloakAdminUtil.BROKER_INSTANCE);
                    String jbossXml = keycloakAU.getClientJbossXml(service.getMrn());
                    HttpHeaders responseHeaders = new HttpHeaders();
                    responseHeaders.setContentLength(jbossXml.length());
                    responseHeaders.setContentType(MediaType.APPLICATION_XML);
                    return new ResponseEntity<>(jbossXml, responseHeaders, HttpStatus.OK);
                }
                throw new McBasicRestException(HttpStatus.NOT_FOUND, MCIdRegConstants.OIDC_CONF_FILE_NOT_AVAILABLE,
                        request.getServletPath());
            }
            throw new McBasicRestException(HttpStatus.FORBIDDEN, MCIdRegConstants.MISSING_RIGHTS,
                    request.getServletPath());
        } else {
            throw new McBasicRestException(HttpStatus.NOT_FOUND, MCIdRegConstants.ORG_NOT_FOUND,
                    request.getServletPath());
        }
    }

    @Override
    protected Service getCertEntity(Certificate cert) {
        return cert.getService();
    }

    @Override
    protected String getName(CertificateModel certOwner) {
        String name = ((Service) certOwner).getCertDomainName();
        if (name == null || name.trim().isEmpty()) {
            name = ((Service) certOwner).getName();
        }
        return name;
    }

}