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}