001/*
002 * Copyright 2015-2016 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
018package com.unboundid.scim2.server.providers;
019
020import com.unboundid.scim2.common.exceptions.NotImplementedException;
021import com.unboundid.scim2.common.exceptions.ScimException;
022import com.unboundid.scim2.common.utils.ApiConstants;
023import com.unboundid.scim2.server.utils.ServerUtils;
024
025import javax.ws.rs.container.ContainerRequestContext;
026import javax.ws.rs.container.ContainerRequestFilter;
027import javax.ws.rs.container.PreMatching;
028import javax.ws.rs.core.MultivaluedMap;
029import javax.ws.rs.core.Response;
030import javax.ws.rs.core.SecurityContext;
031import javax.ws.rs.core.UriBuilder;
032import javax.ws.rs.ext.Provider;
033import java.io.IOException;
034import java.util.Collection;
035import java.util.Collections;
036import java.util.List;
037
038/**
039 * A ContainerRequestFilter implementation to resolve the /Me alias to the
040 * path of the resource that represents the authenticated subject. This
041 * implementation will use the user principal within the SecurityContext
042 * as the resource ID and assumes the resource is part of the /Users resource
043 * type.
044 */
045@Provider
046@PreMatching
047public class AuthenticatedSubjectAliasFilter implements ContainerRequestFilter
048{
049  /**
050   * {@inheritDoc}
051   */
052  public void filter(final ContainerRequestContext requestContext)
053      throws IOException
054  {
055    String requestPath = requestContext.getUriInfo().getPath();
056    for(String alias : getAliases())
057    {
058      if(requestPath.startsWith(alias + "/") || requestPath.equals(alias))
059      {
060        String authSubjectPath;
061        try
062        {
063          authSubjectPath = getAuthenticatedSubjectPath(
064              requestContext.getSecurityContext());
065          UriBuilder newRequestUri =
066              requestContext.getUriInfo().getBaseUriBuilder();
067          newRequestUri.path(authSubjectPath +
068              requestPath.substring(alias.length()));
069          MultivaluedMap<String, String> queryParams =
070              requestContext.getUriInfo().getQueryParameters();
071          for (String key : queryParams.keySet())
072          {
073            List<String> values = queryParams.get(key);
074            newRequestUri.queryParam(key, values.toArray());
075          }
076
077          requestContext.setRequestUri(newRequestUri.build());
078        }
079        catch (ScimException e)
080        {
081          requestContext.abortWith(
082              ServerUtils.setAcceptableType(Response.
083                  status(e.getScimError().getStatus()).
084                  entity(e.getScimError()),
085                  requestContext.getAcceptableMediaTypes()).build());
086        }
087        break;
088      }
089    }
090  }
091
092  /**
093   * Get the path of the resource the represents the authenticated subject.
094   *
095   * @param securityContext The request's security context.
096   * @return The path relative to the base URI.
097   * @throws ScimException if an error occurs while resolving the path.
098   */
099  protected String getAuthenticatedSubjectPath(
100      final SecurityContext securityContext)
101      throws ScimException
102  {
103    if(securityContext == null || securityContext.getUserPrincipal() == null)
104    {
105      throw new NotImplementedException("/Me not supported");
106    }
107
108    return "Users/"+ securityContext.getUserPrincipal().toString();
109  }
110
111  /**
112   * Get the aliases for the authenticated subject.
113   *
114   * @return The aliases for the authenticated subject.
115   */
116  protected Collection<String> getAliases()
117  {
118    return Collections.singleton(ApiConstants.ME_ENDPOINT);
119  }
120}