org.trustedanalytics.servicecatalog.service.rest.ServicesController.java Source code

Java tutorial

Introduction

Here is the source code for org.trustedanalytics.servicecatalog.service.rest.ServicesController.java

Source

/**
 * Copyright (c) 2015 Intel Corporation
 *
 * 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 org.trustedanalytics.servicecatalog.service.rest;

import static org.springframework.http.MediaType.APPLICATION_JSON_VALUE;
import static org.springframework.web.bind.annotation.RequestMethod.DELETE;
import static org.springframework.web.bind.annotation.RequestMethod.GET;
import static org.springframework.web.bind.annotation.RequestMethod.POST;

import com.google.common.base.Preconditions;

import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiResponse;
import io.swagger.annotations.ApiResponses;

import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.context.SecurityContextHolder;
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.RequestParam;
import org.springframework.web.bind.annotation.RestController;

import org.trustedanalytics.cloud.auth.OAuth2TokenRetriever;
import org.trustedanalytics.cloud.cc.api.CcExtendedService;
import org.trustedanalytics.cloud.cc.api.CcExtendedServicePlan;
import org.trustedanalytics.cloud.cc.api.CcOperations;
import org.trustedanalytics.cloud.cc.api.CcOrg;
import org.trustedanalytics.cloud.cc.api.queries.Filter;
import org.trustedanalytics.cloud.cc.api.queries.FilterOperator;
import org.trustedanalytics.cloud.cc.api.queries.FilterQuery;
import org.trustedanalytics.servicecatalog.service.CatalogOperations;
import org.trustedanalytics.servicecatalog.service.model.ServiceBroker;
import org.trustedanalytics.servicecatalog.service.model.ServiceDetails;
import org.trustedanalytics.servicecatalog.service.model.ServicePlanResponse;

import org.springframework.beans.factory.annotation.Autowired;

import java.util.ArrayList;
import java.util.Collection;
import java.util.LinkedList;
import java.util.Optional;
import java.util.UUID;

import org.trustedanalytics.servicecatalog.service.model.ServiceRegistrationRequest;
import rx.Observable;

@RestController
public class ServicesController {

    public static final String GET_ALL_SERVICES_URL = "/rest/services";
    public static final String GET_SERVICE_PLAN_URL = "/rest/service_plan";
    public static final String GET_SERVICE_PLANS_URL = "/rest/services/{label}/service_plans";
    public static final String GET_FILTERED_SERVICES_URL = "/rest/services?space={space}";
    public static final String GET_SERVICE_DETAILS_URL = "/rest/services/{service}";
    public static final String REGISTER_APPLICATION = "/rest/marketplace/application";
    public static final String CLONED_APPLICATION = "/rest/marketplace/application/{service}";

    private final CcOperations ccClient;
    private final CcOperations privilegedClient;
    private final CatalogOperations catalogClient;

    private static final String ADMIN_ROLE = "console.admin";

    @Autowired
    private OAuth2TokenRetriever tokenRetriever;

    @Autowired
    public ServicesController(CcOperations ccClient, CcOperations ccPrivilegedClient,
            CatalogOperations catalogClient) {
        this.ccClient = ccClient;
        this.privilegedClient = ccPrivilegedClient;
        this.catalogClient = catalogClient;
    }

    @ApiOperation(value = "Get plan guid for service using label", notes = "Privilege level: Consumer of this endpoint must have access to space that service belongs to"
            + " Verification is performed by Cloud Controller using user token")
    @RequestMapping(value = GET_SERVICE_PLAN_URL, method = GET, produces = APPLICATION_JSON_VALUE)
    public ServicePlanResponse getPlanGuid(@RequestParam(required = true) String label,
            @RequestParam(required = true) String plan) {

        ServicePlanResponse servicePlanResponse = new ServicePlanResponse();
        CcExtendedService service = ccClient.getExtendedServices()
                .firstOrDefault(null, ccExtendedService -> ccExtendedService.getEntity().getLabel().equals(label))
                .toBlocking().single();
        if (service == null)
            return servicePlanResponse;

        CcExtendedServicePlan servicePlan = ccClient.getExtendedServicePlans(service.getMetadata().getGuid())
                .firstOrDefault(null,
                        extendedServicePlans -> extendedServicePlans.getEntity().getName().equals(plan))
                .toBlocking().single();
        if (servicePlan == null)
            return servicePlanResponse;

        servicePlanResponse.setGuid(servicePlan.getMetadata().getGuid());
        return servicePlanResponse;
    }

    @ApiOperation(value = "Get available plans for service using label", notes = "Privilege level: Consumer of this endpoint must have access to space that service belongs to"
            + " Verification is performed by Cloud Controller using user token")
    @RequestMapping(value = GET_SERVICE_PLANS_URL, method = GET, produces = APPLICATION_JSON_VALUE)
    public Collection<CcExtendedServicePlan> getServicePlans(@PathVariable String label) {
        return ccClient.getExtendedServices().filter(service -> label.equals(service.getEntity().getLabel()))
                .firstOrDefault(null).flatMap(service -> {
                    if (service != null) {
                        return ccClient.getExtendedServicePlans(service.getMetadata().getGuid());
                    } else {
                        return Observable.empty();
                    }
                }).toList().toBlocking().single();
    }

    @ApiOperation(value = "Get summary for services in space", notes = "Privilege level: Consumer of this endpoint must be a member of specified space")
    @RequestMapping(value = GET_ALL_SERVICES_URL, method = GET, produces = APPLICATION_JSON_VALUE)
    public Collection<CcExtendedService> getServices(@RequestParam(required = false) UUID space) {
        if (space == null) {
            throw new UnsupportedOperationException("Handling not filtered request, not implemented yet");
        }
        return ccClient.getServices(space).toList().toBlocking().single();
    }

    @ApiOperation(value = "Get service summary", notes = "Privilege level: Consumer of this endpoint must have access to space that service belongs to"
            + " Verification is performed by Cloud Controller using user token")
    @RequestMapping(value = GET_SERVICE_DETAILS_URL, method = GET, produces = APPLICATION_JSON_VALUE)
    public ServiceDetails getService(@PathVariable UUID service) {
        CcExtendedService ccService = ccClient.getService(service).toBlocking().single();

        return catalogClient.getCatalog().getServices().stream()
                .filter(item -> item.getId().toString().equals(ccService.getEntity().getUniqueId())).findFirst()
                .map(item -> new ServiceDetails(ccService, canDeleteOffering(ccService.getMetadata().getGuid())))
                .orElse(new ServiceDetails(ccService, false));
    }

    @ApiOperation(value = "Register application in catalog", notes = "Privilege level: Consumer of this endpoint must be a member of organization")
    @ApiResponses(value = { @ApiResponse(code = 200, message = "OK"),
            @ApiResponse(code = 403, message = "Request was malformed while registering without organization access") })
    @RequestMapping(value = REGISTER_APPLICATION, method = POST, produces = APPLICATION_JSON_VALUE)
    public CcExtendedService registerApplication(@RequestBody ServiceRegistrationRequest data) {
        Preconditions.checkNotNull(data.getOrganizationGuid());

        CcOrg org = ccClient.getOrgs().filter(o -> data.getOrganizationGuid().equals(o.getGuid())).toBlocking()
                .firstOrDefault(null);
        if (org == null) {
            throw new AccessDeniedException(
                    "Permission denied to access organization: " + data.getOrganizationGuid());
        }

        catalogClient.register(data);

        Observable<CcExtendedService> extendedService = privilegedClient.getExtendedServices().firstOrDefault(null,
                service -> data.getName().equals(service.getEntity().getLabel()));

        extendedService.flatMap(service -> {
            if (service != null) {
                return privilegedClient.getExtendedServicePlans(service.getMetadata().getGuid());
            } else {
                return Observable.empty();
            }
        }).flatMap(plan -> privilegedClient.setExtendedServicePlanVisibility(plan.getMetadata().getGuid(),
                org.getGuid())).toList().toBlocking().single();

        return extendedService.toBlocking().single();
    }

    @ApiResponses(value = { @ApiResponse(code = 200, message = "OK"),
            @ApiResponse(code = 400, message = "No services for delete"),
            @ApiResponse(code = 403, message = "Your are not authorize to delete this service") })
    @RequestMapping(value = CLONED_APPLICATION, method = DELETE)
    public void deregisterApplication(@PathVariable UUID service) {

        ServiceBroker catalogResult = catalogClient.getCatalog();
        if (catalogResult.getServices().isEmpty() || catalogResult.getServices().size() < 2) {
            throw new NoItemInCatalogException("Catalog is empty or have only 1 item left.");
        }

        CcExtendedService ccService = ccClient.getService(service).toBlocking().single();

        if (!canDeleteOffering(ccService.getMetadata().getGuid())) {
            throw new AccessDeniedException("User not authorize to delete this service");
        }

        for (ServiceRegistrationRequest item : catalogResult.getServices()) {
            if (item.getId().toString().equals(ccService.getEntity().getUniqueId())) {
                catalogClient.deregister(ccService.getEntity().getUniqueId());
            }
        }

    }

    @RequestMapping(value = CLONED_APPLICATION, method = GET)
    public Collection<CcExtendedService> getClonedApplications(@PathVariable UUID service) {
        ServiceBroker catalogResult = catalogClient.getCatalog();
        Collection<CcExtendedService> result = new LinkedList<>();

        for (ServiceRegistrationRequest item : catalogResult.getServices()) {
            if (item.getApp().getMetadata().getGuid().equals(service)) {
                final FilterQuery filter = FilterQuery.from(Filter.LABEL, FilterOperator.EQ, item.getName());
                result.add(ccClient.getExtendedServices(filter).toBlocking().single());
            }
        }

        return result;
    }

    public boolean canDeleteOffering(UUID serviceGuid) {
        final FilterQuery filter = FilterQuery.from(Filter.SERVICE_PLAN_GUID, FilterOperator.EQ, serviceGuid);
        boolean isPublic = privilegedClient.getExtendedServicePlans(serviceGuid)
                .exists(plan -> plan.getEntity().getPublicStatus()).toBlocking().single();

        boolean inAnotherOrg = privilegedClient.getExtendedServicePlanVisibility(filter)
                .distinct(visibility -> visibility.getEntity().getOrgGuid()).count().toBlocking().single() > 1;

        if (!isPublic && !inAnotherOrg) {
            return true;
        }

        Authentication auth = SecurityContextHolder.getContext().getAuthentication();
        Collection<? extends GrantedAuthority> authorities = Optional.ofNullable(auth.getAuthorities())
                .orElse(new ArrayList<>());
        return authorities.stream().map(GrantedAuthority::getAuthority).anyMatch(ADMIN_ROLE::equalsIgnoreCase);
    }

}