001/*
002 * Copyright 2015-2019 Ping Identity Corporation
003 *
004 * This program is free software; you can redistribute it and/or modify
005 * it under the terms of the GNU General Public License (GPLv2 only)
006 * or the terms of the GNU Lesser General Public License (LGPLv2.1 only)
007 * as published by the Free Software Foundation.
008 *
009 * This program is distributed in the hope that it will be useful,
010 * but WITHOUT ANY WARRANTY; without even the implied warranty of
011 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
012 * GNU General Public License for more details.
013 *
014 * You should have received a copy of the GNU General Public License
015 * along with this program; if not, see <http://www.gnu.org/licenses>.
016 */
017
018package com.unboundid.scim2.server.resources;
019
020import com.unboundid.scim2.common.GenericScimResource;
021import com.unboundid.scim2.common.ScimResource;
022import com.unboundid.scim2.common.filters.Filter;
023import com.unboundid.scim2.common.messages.ListResponse;
024import com.unboundid.scim2.common.types.ResourceTypeResource;
025import com.unboundid.scim2.common.exceptions.ForbiddenException;
026import com.unboundid.scim2.common.exceptions.ResourceNotFoundException;
027import com.unboundid.scim2.common.exceptions.ScimException;
028import com.unboundid.scim2.server.annotations.ResourceType;
029import com.unboundid.scim2.server.utils.ResourcePreparer;
030import com.unboundid.scim2.server.utils.ResourceTypeDefinition;
031import com.unboundid.scim2.server.utils.SchemaAwareFilterEvaluator;
032
033import javax.ws.rs.GET;
034import javax.ws.rs.Path;
035import javax.ws.rs.PathParam;
036import javax.ws.rs.Produces;
037import javax.ws.rs.QueryParam;
038import javax.ws.rs.core.Application;
039import javax.ws.rs.core.Context;
040import javax.ws.rs.core.MediaType;
041import javax.ws.rs.core.UriInfo;
042import java.util.ArrayList;
043import java.util.Collection;
044import java.util.HashSet;
045import java.util.Set;
046
047import static com.unboundid.scim2.common.utils.ApiConstants.*;
048
049/**
050 * An abstract JAX-RS resource class for servicing the Resource Types
051 * endpoint.
052 */
053@ResourceType(
054    description = "SCIM 2.0 Resource Type",
055    name = "ResourceType",
056    schema = ResourceTypeResource.class,
057    discoverable = false)
058@Path("ResourceTypes")
059public class ResourceTypesEndpoint
060{
061  private static final ResourceTypeDefinition RESOURCE_TYPE_DEFINITION =
062      ResourceTypeDefinition.fromJaxRsResource(
063          ResourceTypesEndpoint.class);
064
065  @Context
066  private Application application;
067
068  /**
069   * Service SCIM request to retrieve all resource types defined at the
070   * service provider using GET.
071   *
072   * @param filterString The filter string used to request a subset of
073   *                     resources. Will throw 403 Forbidden if specified.
074   * @param uriInfo UriInfo of the request.
075   * @return All resource types in a ListResponse container.
076   * @throws ScimException If an error occurs.
077   */
078  @GET
079  @Produces({MEDIA_TYPE_SCIM, MediaType.APPLICATION_JSON})
080  public ListResponse<GenericScimResource> search(
081      @QueryParam(QUERY_PARAMETER_FILTER) final String filterString,
082      @Context final UriInfo uriInfo)
083      throws ScimException
084  {
085    if(filterString != null)
086    {
087      throw new ForbiddenException("Filtering not allowed");
088    }
089
090    // https://tools.ietf.org/html/draft-ietf-scim-api-19#section-4 says
091    // query params should be ignored for discovery endpoints so we can't use
092    // SimpleSearchResults.
093    ResourcePreparer<GenericScimResource> preparer =
094        new ResourcePreparer<GenericScimResource>(
095            RESOURCE_TYPE_DEFINITION, uriInfo);
096    Collection<ResourceTypeResource> resourceTypes = getResourceTypes();
097    Collection<GenericScimResource> preparedResources =
098        new ArrayList<GenericScimResource>(resourceTypes.size());
099    for(ResourceTypeResource resourceType : resourceTypes)
100    {
101      GenericScimResource preparedResource =
102          resourceType.asGenericScimResource();
103      preparer.setResourceTypeAndLocation(preparedResource);
104      preparedResources.add(preparedResource);
105    }
106    return new ListResponse<GenericScimResource>(preparedResources);
107  }
108
109  /**
110   * Service SCIM request to retrieve a resource type by ID.
111   *
112   * @param id The ID of the resource type to retrieve.
113   * @param uriInfo UriInfo of the request.
114   * @return The retrieved resource type.
115   * @throws ScimException If an error occurs.
116   */
117  @Path("{id}")
118  @GET
119  @Produces({MEDIA_TYPE_SCIM, MediaType.APPLICATION_JSON})
120  public ScimResource get(@PathParam("id") final String id,
121                          @Context final UriInfo uriInfo)
122      throws ScimException
123  {
124    Filter filter = Filter.or(Filter.eq("id", id), Filter.eq("name", id));
125    SchemaAwareFilterEvaluator filterEvaluator =
126        new SchemaAwareFilterEvaluator(RESOURCE_TYPE_DEFINITION);
127    ResourcePreparer<GenericScimResource> resourcePreparer =
128        new ResourcePreparer<GenericScimResource>(
129            RESOURCE_TYPE_DEFINITION, uriInfo);
130    for(ResourceTypeResource resourceType : getResourceTypes())
131    {
132      GenericScimResource resource = resourceType.asGenericScimResource();
133      if(filter.visit(filterEvaluator, resource.getObjectNode()))
134      {
135        resourcePreparer.setResourceTypeAndLocation(resource);
136        return resource;
137      }
138    }
139
140    throw new ResourceNotFoundException(
141        "No resource type defined with ID or name " + id);
142  }
143
144  /**
145   * Retrieve all resource types defined at the service provider. The default
146   * implementation will generate ResourceType definitions from all JAX-RS
147   * resource classes with the ResourceType annotation.
148   *
149   * @return All resource types defined at the service provider.
150   * @throws ScimException If an error occurs.
151   */
152  public Collection<ResourceTypeResource> getResourceTypes()
153      throws ScimException
154  {
155    Set<ResourceTypeResource> resourceTypes =
156        new HashSet<ResourceTypeResource>();
157    for(Class<?> resourceClass : application.getClasses())
158    {
159      ResourceTypeDefinition resourceTypeDefinition =
160          ResourceTypeDefinition.fromJaxRsResource(resourceClass);
161      if(resourceTypeDefinition != null &&
162          resourceTypeDefinition.isDiscoverable())
163      {
164        resourceTypes.add(resourceTypeDefinition.toScimResource());
165      }
166    }
167
168    for(Object resourceInstance : application.getSingletons())
169    {
170      ResourceTypeDefinition resourceTypeDefinition =
171          ResourceTypeDefinition.fromJaxRsResource(resourceInstance.getClass());
172      if(resourceTypeDefinition != null &&
173          resourceTypeDefinition.isDiscoverable())
174      {
175        resourceTypes.add(resourceTypeDefinition.toScimResource());
176      }
177    }
178
179    return resourceTypes;
180  }
181}