001/**
002 * Copyright 2015 DuraSpace, Inc.
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 *     http://www.apache.org/licenses/LICENSE-2.0
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 */
016package org.fcrepo.auth.common;
017
018import java.security.Principal;
019import java.util.Collections;
020import java.util.HashSet;
021import java.util.Map;
022import java.util.Set;
023
024import javax.jcr.Credentials;
025import javax.servlet.http.HttpServletRequest;
026
027import org.modeshape.jcr.ExecutionContext;
028import org.modeshape.jcr.api.ServletCredentials;
029import org.modeshape.jcr.security.AuthenticationProvider;
030import org.slf4j.Logger;
031import org.slf4j.LoggerFactory;
032
033/**
034 * Authenticates ModeShape logins where JAX-RS credentials are supplied. Capable
035 * of authenticating whether or not container has performed user authentication.
036 * This is a singleton with an injected policy enforcement point. The singleton
037 * pattern allows ModeShape to obtain this instance via classname configuration.
038 *
039 * @author Gregory Jansen
040 */
041public final class ServletContainerAuthenticationProvider implements
042        AuthenticationProvider {
043
044    private static ServletContainerAuthenticationProvider instance = null;
045
046    private ServletContainerAuthenticationProvider() {
047        instance = this;
048    }
049
050    /**
051     * User role for Fedora's admin users
052     */
053    public static final String FEDORA_ADMIN_ROLE = "fedoraAdmin";
054
055    /**
056     * User role for Fedora's ordinary users
057     */
058    public static final String FEDORA_USER_ROLE = "fedoraUser";
059
060    private static final Logger LOGGER = LoggerFactory
061            .getLogger(ServletContainerAuthenticationProvider.class);
062
063    private Set<PrincipalProvider> principalProviders = Collections.emptySet();
064
065    private FedoraAuthorizationDelegate fad;
066
067    /**
068     * Provides the singleton bean to ModeShape via reflection based on class
069     * name.
070     *
071     * @return a AuthenticationProvider
072     */
073    public static synchronized AuthenticationProvider getInstance() {
074        if (instance != null) {
075            return instance;
076        }
077        instance = new ServletContainerAuthenticationProvider();
078        LOGGER.warn("Security is MINIMAL, no Policy Enforcement Point configured.");
079        return instance;
080    }
081
082    /**
083     * @return the principalProviders
084     */
085    public Set<PrincipalProvider> getPrincipalProviders() {
086        return principalProviders;
087    }
088
089    /**
090     * @param principalProviders the principalProviders to set
091     */
092    public void setPrincipalProviders(
093            final Set<PrincipalProvider> principalProviders) {
094        this.principalProviders = principalProviders;
095    }
096
097    /**
098     * Authenticate the user that is using the supplied credentials.
099     * <p>
100     * If the credentials given establish that the authenticated user has the fedoraAdmin role, construct an
101     * ExecutionContext with FedoraAdminSecurityContext as the SecurityContext. Otherwise, construct an
102     * ExecutionContext with FedoraUserSecurityContext as the SecurityContext.
103     * </p>
104     * <p>
105     * If the authenticated user does not have the fedoraAdmin role, session attributes will be assigned in the
106     * sessionAttributes map:
107     * </p>
108     * <ul>
109     * <li>FEDORA_SERVLET_REQUEST will be assigned the ServletRequest instance associated with credentials.</li>
110     * <li>FEDORA_ALL_PRINCIPALS will be assigned the union of all principals obtained from configured
111     * PrincipalProvider instances plus the authenticated user's principal; FEDORA_ALL_PRINCIPALS will be assigned the
112     * singleton set containing the fad.getEveryonePrincipal() principal otherwise.</li>
113     * </ul>
114     */
115    @Override
116    public ExecutionContext authenticate(final Credentials credentials,
117            final String repositoryName, final String workspaceName,
118            final ExecutionContext repositoryContext,
119            final Map<String, Object> sessionAttributes) {
120        LOGGER.debug("Trying to authenticate: {}; FAD: {}", credentials, fad);
121
122        if (!(credentials instanceof ServletCredentials)) {
123            return null;
124        }
125
126        final HttpServletRequest servletRequest =
127                ((ServletCredentials) credentials).getRequest();
128        final Principal userPrincipal = servletRequest.getUserPrincipal();
129
130        if (userPrincipal != null &&
131                servletRequest.isUserInRole(FEDORA_ADMIN_ROLE)) {
132            LOGGER.debug("Returning admin user");
133
134            return repositoryContext.with(new FedoraAdminSecurityContext(
135                    userPrincipal.getName()));
136        }
137
138        if (userPrincipal != null) {
139            LOGGER.debug("Found user-principal: {}.", userPrincipal.getName());
140
141            sessionAttributes.put(
142                    FedoraAuthorizationDelegate.FEDORA_SERVLET_REQUEST,
143                    servletRequest);
144
145            sessionAttributes.put(
146                    FedoraAuthorizationDelegate.FEDORA_USER_PRINCIPAL,
147                    userPrincipal);
148
149            final Set<Principal> principals = collectPrincipals(credentials);
150            principals.add(userPrincipal);
151            principals.add(fad.getEveryonePrincipal());
152
153            sessionAttributes.put(
154                    FedoraAuthorizationDelegate.FEDORA_ALL_PRINCIPALS,
155                    principals);
156
157        } else {
158            LOGGER.debug("No user-principal found.");
159
160            sessionAttributes.put(FedoraAuthorizationDelegate.FEDORA_USER_PRINCIPAL,
161                    fad.getEveryonePrincipal());
162
163            sessionAttributes.put(
164                    FedoraAuthorizationDelegate.FEDORA_ALL_PRINCIPALS,
165                    Collections.singleton(fad.getEveryonePrincipal()));
166
167        }
168
169        return repositoryContext.with(new FedoraUserSecurityContext(
170                userPrincipal, fad));
171    }
172
173    /**
174     * @return the authorization delegate
175     */
176    public FedoraAuthorizationDelegate getFad() {
177        return fad;
178    }
179
180    /**
181     * @param fad the authorization delegate to set
182     */
183    public void setFad(final FedoraAuthorizationDelegate fad) {
184        this.fad = fad;
185    }
186
187    private Set<Principal> collectPrincipals(final Credentials credentials) {
188        final Set<Principal> principals = new HashSet<>();
189
190        // TODO add exception handling for principal providers
191        for (final PrincipalProvider p : this.getPrincipalProviders()) {
192            final Set<Principal> ps = p.getPrincipals(credentials);
193
194            if (ps != null) {
195                principals.addAll(p.getPrincipals(credentials));
196            }
197        }
198
199        return principals;
200    }
201}