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