001    /*
002     * Copyright 2011-2012 UnboundID Corp.
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    
018    package com.unboundid.scim.sdk;
019    
020    import com.unboundid.scim.data.GroupResource;
021    import com.unboundid.scim.data.BaseResource;
022    import com.unboundid.scim.data.ResourceFactory;
023    import com.unboundid.scim.data.ServiceProviderConfig;
024    import com.unboundid.scim.data.UserResource;
025    import com.unboundid.scim.schema.CoreSchema;
026    import com.unboundid.scim.schema.ResourceDescriptor;
027    import org.apache.wink.client.ClientConfig;
028    import org.apache.wink.client.RestClient;
029    
030    import javax.ws.rs.core.MediaType;
031    import java.net.URI;
032    import java.util.List;
033    
034    
035    
036    /**
037     * The SCIMService class represents a client connection to a SCIM service
038     * provider. It handles setting up and configuring the connection which will
039     * be used by the SCIMEndpoints that are obtained form this SCIMService.
040     */
041    public class SCIMService
042    {
043      private final RestClient client;
044      private final URI baseURL;
045    
046      private MediaType acceptType = MediaType.APPLICATION_JSON_TYPE;
047      private MediaType contentType = MediaType.APPLICATION_JSON_TYPE;
048      private final boolean[] overrides = new boolean[3];
049      private String userAgent;
050    
051      /**
052       * Constructs a new SCIMService that is configured from the provided
053       * <code>org.apache.wink.client.ClientConfig</code> instance.
054       *
055       * @param baseUrl The SCIM Service Provider URL.
056       * @param clientConfig The configuration to use.
057       */
058      public SCIMService(final URI baseUrl, final ClientConfig clientConfig)
059      {
060        this.baseURL = baseUrl;
061        this.client = new RestClient(clientConfig);
062      }
063    
064      /**
065       * Constructs a new SCIMService that uses
066       * <code>java.net.HttpURLConnection</code> for the HTTP layer.
067       *
068       * @param baseUrl The SCIM Service Provider URL.
069       */
070      public SCIMService(final URI baseUrl)
071      {
072        this(baseUrl, new ClientConfig());
073      }
074    
075      /**
076       * Constructs a new SCIMService with basic authentication support
077       * using the provided credentials. This SCIMService will use
078       * <code>java.net.HttpURLConnection</code> for the HTTP layer.
079       *
080       * @param baseUrl The SCIM Service Provider URL.
081       * @param username The username.
082       * @param password The password.
083       */
084      public SCIMService(final URI baseUrl, final String username,
085                         final String password)
086      {
087        this(baseUrl, new ClientConfig().handlers(new HttpBasicAuthSecurityHandler
088          (username,password)));
089      }
090    
091      /**
092       * Returns a SCIMEndpoint with the current settings that can be used to
093       * invoke CRUD operations. Any changes to the SCIMService configuration will
094       * not be reflected in the returned SCIMEndpoint.
095       *
096       * @param resourceDescriptor The ResourceDescriptor of the endpoint.
097       * @param resourceFactory The ResourceFactory that should be used to
098       *                        create SCIM resource instances.
099       * @param <R> The type of SCIM resource instances.
100       * @return The SCIMEndpoint that can be used to invoke CRUD operations.
101       */
102      public <R extends BaseResource> SCIMEndpoint<R> getEndpoint(
103          final ResourceDescriptor resourceDescriptor,
104          final ResourceFactory<R> resourceFactory)
105      {
106        return new SCIMEndpoint<R>(this, client, resourceDescriptor,
107            resourceFactory);
108      }
109    
110      /**
111       * Returns a SCIMEndpoint for the Users endpoint defined in the core schema.
112       *
113       * @return The SCIMEndpoint for the Users endpoint defined in the core schema.
114       */
115      public SCIMEndpoint<UserResource> getUserEndpoint()
116      {
117        return new SCIMEndpoint<UserResource>(this, client,
118            CoreSchema.USER_DESCRIPTOR, UserResource.USER_RESOURCE_FACTORY);
119      }
120    
121      /**
122       * Returns a SCIMEndpoint for the Groups endpoint defined in the core schema.
123       *
124       * @return The SCIMEndpoint for the Groups endpoint defined in the
125       *         core schema.
126       */
127      public SCIMEndpoint<GroupResource> getGroupEndpoint()
128      {
129        return new SCIMEndpoint<GroupResource>(this, client,
130            CoreSchema.GROUP_DESCRIPTOR, GroupResource.GROUP_RESOURCE_FACTORY);
131      }
132    
133      /**
134       * Returns a SCIMEndpoint for the Schemas endpoint. This endpoint allows for
135       * the retrieval of schema for all service provider supported resources.
136       *
137       * @return The SCIMEndpoint for the Schemas endpoint.
138       */
139      public SCIMEndpoint<ResourceDescriptor> getResourceSchemaEndpoint()
140      {
141        return new SCIMEndpoint<ResourceDescriptor>(this, client,
142            CoreSchema.RESOURCE_SCHEMA_DESCRIPTOR,
143            ResourceDescriptor.RESOURCE_DESCRIPTOR_FACTORY);
144      }
145    
146      /**
147       * Retrieves the ResourceDescriptor for the specified resource from the
148       * SCIM service provider.
149       *
150       * @param resourceName The name of the resource.
151       * @param schema The schema URN of the resource or <code>null</code>
152       *        to match only based on the name of the resource.
153       * @return The ResourceDescriptor for the specified resource or
154       *         <code>null</code> if none are found.
155       * @throws SCIMException If the ResourceDescriptor could not be read.
156       */
157      public ResourceDescriptor getResourceDescriptor(final String resourceName,
158                                                      final String schema)
159          throws SCIMException
160      {
161        final SCIMEndpoint<ResourceDescriptor> endpoint =
162            getResourceSchemaEndpoint();
163        String filter = "name eq \"" + resourceName + "\"";
164        if(schema != null)
165        {
166          filter += " and schema eq \"" + schema + "\"";
167        }
168        final Resources<ResourceDescriptor> resources = endpoint.query(filter);
169        if(resources.getTotalResults() == 0)
170        {
171          return null;
172        }
173        if(resources.getTotalResults() > 1)
174        {
175          throw new InvalidResourceException(
176              "The service provider returned multiple resource descriptors " +
177                  "with resource name '" + resourceName);
178        }
179        return resources.iterator().next();
180      }
181    
182    
183    
184      /**
185       * Retrieves the Service Provider Config from the SCIM service provider.
186       *
187       * @return  The Service Provider Config.
188       *
189       * @throws SCIMException  If the Service Provider Config could not be read.
190       */
191      public ServiceProviderConfig getServiceProviderConfig()
192          throws SCIMException
193      {
194        final SCIMEndpoint<ServiceProviderConfig> endpoint =
195            getEndpoint(CoreSchema.SERVICE_PROVIDER_CONFIG_SCHEMA_DESCRIPTOR,
196                    ServiceProviderConfig.SERVICE_PROVIDER_CONFIG_RESOURCE_FACTORY);
197    
198        // The ServiceProviderConfig is a special case where there is only a
199        // single resource at the endpoint, so the id is not specified.
200        return endpoint.get(null);
201      }
202    
203    
204    
205      /**
206       * Invoke a bulk request. The service provider will perform as
207       * many operations as possible without regard to the number of failures.
208       *
209       * @param operations  The operations to be performed.
210       *
211       * @return  The bulk response.
212       *
213       * @throws SCIMException  If the request fails.
214       */
215      public BulkResponse processBulkRequest(
216          final List<BulkOperation> operations)
217          throws SCIMException
218      {
219        return processBulkRequest(operations, -1);
220      }
221    
222    
223    
224      /**
225       * Invoke a bulk request.
226       *
227       * @param operations    The operations to be performed.
228       * @param failOnErrors  The number of errors that the service provider will
229       *                      accept before the operation is terminated and an
230       *                      error response is returned. A value of -1 indicates
231       *                      the the service provider will continue to perform
232       *                      as many operations as possible without regard to
233       *                      failures.
234       *
235       * @return  The bulk response.
236       *
237       * @throws SCIMException  If the request fails.
238       */
239      public BulkResponse processBulkRequest(
240          final List<BulkOperation> operations,
241          final int failOnErrors)
242          throws SCIMException
243      {
244        final BulkEndpoint request = new BulkEndpoint(this, client);
245    
246        return request.processRequest(operations, failOnErrors);
247      }
248    
249    
250    
251      /**
252       * Retrieves the SCIM Service Provider URL.
253       *
254       * @return The SCIM Service Provider URL.
255       */
256      public URI getBaseURL() {
257        return baseURL;
258      }
259    
260      /**
261       * Retrieves the content media type that should be used when writing data to
262       * the SCIM service provider.
263       *
264       * @return The content media type that should be used when writing data to
265       * the SCIM service provider.
266       */
267      public MediaType getContentType() {
268        return contentType;
269      }
270    
271      /**
272       * Sets the content media type that should be used when writing data to
273       * the SCIM service provider.
274       *
275       * @param contentType he content media type that should be used when writing
276       * data to the SCIM service provider.
277       */
278      public void setContentType(final MediaType contentType) {
279        this.contentType = contentType;
280      }
281    
282      /**
283       * Retrieves the accept media type that should be used when reading data from
284       * the SCIM service provider.
285       *
286       * @return The accept media type that should be used when reading data from
287       * the SCIM service provider.
288       */
289      public MediaType getAcceptType() {
290        return acceptType;
291      }
292    
293      /**
294       * Sets the accept media type that should be used when reading data from
295       * the SCIM service provider.
296       *
297       * @param acceptType The accept media type that should be used when reading
298       * data from the SCIM service provider.
299       */
300      public void setAcceptType(final MediaType acceptType) {
301        this.acceptType = acceptType;
302      }
303    
304      /**
305       * Retrieves the user-agent string that will be used in the HTTP request
306       * headers.
307       *
308       * @return The user-agent string. This may be null, in which case a default
309       * user-agent will be used.
310       */
311      public String getUserAgent() {
312        return userAgent;
313      }
314    
315      /**
316       * Sets the user-agent string to use in the request headers.
317       *
318       * @param userAgent The user-agent string that should be used.
319       */
320      public void setUserAgent(final String userAgent) {
321        this.userAgent = userAgent;
322      }
323    
324      /**
325       * Whether to override DELETE operations with POST.
326       *
327       * @return <code>true</code> to override DELETE operations with POST or
328       * <code>false</code> to use the DELETE method.
329       */
330      public boolean isOverrideDelete() {
331        return overrides[2];
332      }
333    
334      /**
335       * Sets whether to override DELETE operations with POST.
336       *
337       * @param overrideDelete <code>true</code> to override DELETE operations with
338       * POST or <code>false</code> to use the DELETE method.
339       */
340      public void setOverrideDelete(final boolean overrideDelete) {
341        this.overrides[2] = overrideDelete;
342      }
343    
344      /**
345       * Whether to override PATCH operations with POST.
346       *
347       * @return <code>true</code> to override PATCH operations with POST or
348       * <code>false</code> to use the PATCH method.
349       */
350      public boolean isOverridePatch() {
351        return overrides[1];
352      }
353    
354      /**
355       * Sets whether to override PATCH operations with POST.
356       *
357       * @param overridePatch <code>true</code> to override PATCH operations with
358       * POST or <code>false</code> to use the PATCH method.
359       */
360      public void setOverridePatch(final boolean overridePatch) {
361        this.overrides[1] = overridePatch;
362      }
363    
364      /**
365       * Whether to override PUT operations with POST.
366       *
367       * @return <code>true</code> to override PUT operations with POST or
368       * <code>false</code> to use the PUT method.
369       */
370      public boolean isOverridePut() {
371        return overrides[0];
372      }
373    
374      /**
375       * Sets whether to override PUT operations with POST.
376       *
377       * @param overridePut <code>true</code> to override PUT operations with
378       * POST or <code>false</code> to use the PUT method.
379       */
380      public void setOverridePut(final boolean overridePut) {
381        this.overrides[0] = overridePut;
382      }
383    }