org.surfnet.oaaas.resource.resourceserver.ResourceServerResource.java Source code

Java tutorial

Introduction

Here is the source code for org.surfnet.oaaas.resource.resourceserver.ResourceServerResource.java

Source

/*
 * Copyright 2012 SURFnet bv, The Netherlands
 *
 * 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.surfnet.oaaas.resource.resourceserver;

import org.apache.commons.collections.CollectionUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.transaction.annotation.Transactional;
import org.surfnet.oaaas.auth.AuthorizationServerFilter;
import org.surfnet.oaaas.model.Client;
import org.surfnet.oaaas.model.ResourceServer;
import org.surfnet.oaaas.model.StatisticsResponse;
import org.surfnet.oaaas.model.StatisticsResponse.ClientStat;
import org.surfnet.oaaas.model.StatisticsResponse.ResourceServerStat;
import org.surfnet.oaaas.model.VerifyTokenResponse;
import org.surfnet.oaaas.repository.AccessTokenRepository;
import org.surfnet.oaaas.repository.ClientRepository;
import org.surfnet.oaaas.repository.ResourceServerRepository;

import javax.inject.Inject;
import javax.inject.Named;
import javax.servlet.http.HttpServletRequest;
import javax.validation.Valid;
import javax.ws.rs.*;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.UriBuilder;
import java.net.URI;
import java.util.*;

import static org.apache.commons.collections.CollectionUtils.subtract;

/**
 * JAX-RS Resource for resource servers.
 */
@Named
@Path("/resourceServer")
@Produces(MediaType.APPLICATION_JSON)
@Transactional
public class ResourceServerResource extends AbstractResource {

    private static final Logger LOG = LoggerFactory.getLogger(ResourceServerResource.class);

    @Inject
    private ResourceServerRepository resourceServerRepository;

    @Inject
    private AccessTokenRepository accessTokenRepository;

    @Inject
    private ClientRepository clientRepository;

    /**
     * Get all existing resource servers for the provided credentials (== owner) or in case of an adminPrincipal we return all resource servers.
     */
    @GET
    public Response getAll(@Context HttpServletRequest request) {
        Response validateScopeResponse = validateScope(request,
                Collections.singletonList(AbstractResource.SCOPE_READ));
        if (validateScopeResponse != null) {
            return validateScopeResponse;
        }
        List<ResourceServer> resourceServers = getAllResourceServers(request);
        return Response.ok(resourceServers).build();
    }

    /**
     * Get one resource server.
     */
    @GET
    @Path("/{resourceServerId}")
    public Response getById(@Context HttpServletRequest request, @PathParam("resourceServerId") Long id) {
        Response validateScopeResponse = validateScope(request,
                Collections.singletonList(AbstractResource.SCOPE_READ));
        if (validateScopeResponse != null) {
            return validateScopeResponse;
        }
        return response(getResourceServer(request, id));
    }

    /**
     * Get statistics
     */
    @GET
    @Path("/stats")
    public Response stats(@Context HttpServletRequest request) {
        Iterable<ResourceServer> resourceServers = this.getAllResourceServers(request);
        List<ResourceServerStat> resourceServerStats = new ArrayList<StatisticsResponse.ResourceServerStat>();
        for (ResourceServer resourceServer : resourceServers) {
            List<ClientStat> clientStats = new ArrayList<StatisticsResponse.ClientStat>();
            Set<Client> clients = resourceServer.getClients();
            for (Client client : clients) {
                clientStats.add(new StatisticsResponse.ClientStat(client.getName(), client.getDescription(),
                        accessTokenRepository.countByUniqueResourceOwnerIdAndClientId(client.getId())));
            }
            resourceServerStats.add(new StatisticsResponse.ResourceServerStat(resourceServer.getName(),
                    resourceServer.getDescription(), clientStats));
        }
        return Response.ok(new StatisticsResponse(resourceServerStats)).build();
    }

    /**
     * Get the principal
     */
    @GET
    @Path("/principal")
    public Response principal(@Context HttpServletRequest request) {
        VerifyTokenResponse verifyTokenResponse = (VerifyTokenResponse) request
                .getAttribute(AuthorizationServerFilter.VERIFY_TOKEN_RESPONSE);
        return Response.ok(verifyTokenResponse.getPrincipal()).build();
    }

    /**
     * Save a new resource server.
     */
    @PUT
    public Response put(@Context HttpServletRequest request, @Valid ResourceServer newOne) {
        Response validateScopeResponse = validateScope(request,
                Collections.singletonList(AbstractResource.SCOPE_WRITE));
        if (validateScopeResponse != null) {
            return validateScopeResponse;
        }

        String owner = getUserId(request);

        // Read only fields
        newOne.setKey(generateKey());
        newOne.setSecret(generateSecret());
        newOne.setOwner(owner);

        ResourceServer resourceServerSaved;
        try {
            //we run transactional modus, so any constraint violations only occur after the commit of the transaction (to late...)
            validate(newOne);
            resourceServerSaved = resourceServerRepository.save(newOne);
        } catch (Exception e) {
            return buildErrorResponse(e);
        }

        LOG.debug("New resourceServer has been saved: {}. ", resourceServerSaved);

        final URI uri = UriBuilder.fromPath("{resourceServerId}.json").build(resourceServerSaved.getId());
        return Response.created(uri).entity(resourceServerSaved).build();
    }

    /**
     * Delete an existing resource server.
     */
    @DELETE
    @Path("/{resourceServerId}")
    public Response delete(@Context HttpServletRequest request, @PathParam("resourceServerId") Long id) {
        Response validateScopeResponse = validateScope(request,
                Collections.singletonList(AbstractResource.SCOPE_WRITE));
        if (validateScopeResponse != null) {
            return validateScopeResponse;
        }
        ResourceServer resourceServer = getResourceServer(request, id);

        if (resourceServer == null) {
            return Response.status(Response.Status.NOT_FOUND).build();
        }
        LOG.debug("About to delete resourceServer {}", id);
        resourceServerRepository.delete(id);
        return Response.noContent().build();
    }

    /**
     * Update an existing resource server.
     */
    @POST
    @Path("/{resourceServerId}")
    public Response post(@Valid final ResourceServer resourceServer, @Context HttpServletRequest request,
            @PathParam("resourceServerId") Long id) {
        Response validateScopeResponse = validateScope(request,
                Collections.singletonList(AbstractResource.SCOPE_WRITE));
        if (validateScopeResponse != null) {
            return validateScopeResponse;
        }

        ResourceServer persistedResourceServer = getResourceServer(request, id);
        if (persistedResourceServer == null) {
            return Response.status(Response.Status.NOT_FOUND).build();
        }

        // Copy over read-only fields
        resourceServer.setSecret(persistedResourceServer.getSecret());
        resourceServer.setKey(persistedResourceServer.getKey());
        resourceServer.setOwner(getUserId(request));

        pruneClientScopes(resourceServer.getScopes(), persistedResourceServer.getScopes(),
                persistedResourceServer.getClients());
        LOG.debug("About to update existing resourceServer {} with new properties: {}", persistedResourceServer,
                resourceServer);

        ResourceServer savedInstance;
        try {
            //we run transactional modus, so any constraint violations only occur after the commit of the transaction (to late...)
            validate(resourceServer);
            savedInstance = resourceServerRepository.save(resourceServer);
        } catch (Exception e) {
            return buildErrorResponse(e);
        }

        return Response.ok(savedInstance).build();
    }

    /**
     * Delete all scopes from clients that are not valid anymore with the new
     * resource server
     *
     * @param newScopes the newly saved scopes
     * @param oldScopes the scopes from the existing resource server
     * @param clients   the clients of the resource server
     */
    @SuppressWarnings("unchecked")
    protected void pruneClientScopes(final List<String> newScopes, List<String> oldScopes, Set<Client> clients) {
        if (!newScopes.containsAll(oldScopes)) {
            subtract(oldScopes, newScopes);
            Collection<String> outdatedScopes = subtract(oldScopes, newScopes);
            LOG.info("Resource server has updated scopes. Will remove all outdated scopes from clients: {}",
                    outdatedScopes);

            for (Client c : clients) {
                final List<String> clientScopes = c.getScopes();
                if (CollectionUtils.containsAny(clientScopes, outdatedScopes)) {
                    ArrayList<String> prunedScopes = new ArrayList<String>(subtract(clientScopes, outdatedScopes));
                    LOG.info(
                            "Client scopes of client {} were: {}. After pruning (because resourceServer has new scopes): {}",
                            new Object[] { c.getClientId(), c.getScopes(), prunedScopes });
                    c.setScopes(prunedScopes);
                }
            }
        }
    }

    protected String generateKey() {
        return super.generateRandom();
    }

    protected String generateSecret() {
        return super.generateRandom();
    }

    private ResourceServer getResourceServer(HttpServletRequest request, Long id) {
        ResourceServer resourceServer;
        if (isAdminPrincipal(request)) {
            resourceServer = resourceServerRepository.findOne(id);
        } else {
            String owner = getUserId(request);
            resourceServer = resourceServerRepository.findByIdAndOwner(id, owner);
        }
        LOG.debug("About to return one resourceServer with id {}: {}", id, resourceServer);
        return resourceServer;
    }

    private List<ResourceServer> getAllResourceServers(HttpServletRequest request) {
        List<ResourceServer> resourceServers;
        if (isAdminPrincipal(request)) {
            resourceServers = addAll(resourceServerRepository.findAll().iterator());
            LOG.debug("About to return all resource servers ({}) for adminPrincipal", resourceServers.size());
        } else {
            String owner = getUserId(request);
            resourceServers = resourceServerRepository.findByOwner(owner);
            LOG.debug("About to return all resource servers ({}) for owner {}", resourceServers.size(), owner);
        }
        return resourceServers;
    }

}