001/*
002 * Licensed to DuraSpace under one or more contributor license agreements.
003 * See the NOTICE file distributed with this work for additional information
004 * regarding copyright ownership.
005 *
006 * DuraSpace licenses this file to you under the Apache License,
007 * Version 2.0 (the "License"); you may not use this file except in
008 * compliance with the License.  You may obtain a copy of the License at
009 *
010 *     http://www.apache.org/licenses/LICENSE-2.0
011 *
012 * Unless required by applicable law or agreed to in writing, software
013 * distributed under the License is distributed on an "AS IS" BASIS,
014 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
015 * See the License for the specific language governing permissions and
016 * limitations under the License.
017 */
018package org.fcrepo.http.commons.test.util;
019
020import static java.lang.reflect.Proxy.newProxyInstance;
021import static java.util.Collections.emptySet;
022import static java.util.Collections.singleton;
023import static javax.ws.rs.core.HttpHeaders.AUTHORIZATION;
024import static org.slf4j.LoggerFactory.getLogger;
025
026import java.io.IOException;
027import java.lang.reflect.InvocationHandler;
028import java.lang.reflect.Method;
029import java.security.Principal;
030import java.util.Set;
031
032import javax.servlet.Filter;
033import javax.servlet.FilterChain;
034import javax.servlet.FilterConfig;
035import javax.servlet.ServletException;
036import javax.servlet.ServletRequest;
037import javax.servlet.ServletResponse;
038import javax.servlet.http.HttpServletRequest;
039import javax.ws.rs.WebApplicationException;
040
041import org.glassfish.grizzly.http.server.GrizzlyPrincipal;
042import org.glassfish.jersey.internal.util.Base64;
043import org.slf4j.Logger;
044
045/**
046 * @author Gregory Jansen
047 */
048public class TestAuthenticationRequestFilter implements Filter {
049
050    private static final Logger log = getLogger(TestAuthenticationRequestFilter.class);
051
052    private static final String FEDORA_ADMIN_USER = "fedoraAdmin";
053
054    /*
055     * (non-Javadoc)
056     * @see
057     * com.sun.jersey.spi.container.ContainerRequestFilter#filter(com.sun.jersey
058     * .spi.container.ContainerRequest)
059     */
060    @Override
061    public void doFilter(final ServletRequest request,
062            final ServletResponse response, final FilterChain chain)
063            throws IOException, ServletException {
064        final HttpServletRequest req = (HttpServletRequest) request;
065        final String username = getUsername(req);
066        // Validate the extracted credentials
067        Set<String> containerRoles = emptySet();
068        if (username == null) {
069            log.debug("ANONYMOUS");
070        } else if (FEDORA_ADMIN_USER.equals(username)) {
071            containerRoles = singleton("fedoraAdmin");
072            log.debug("ADMIN AUTHENTICATED");
073        } else if ("noroles".equals(username)) {
074            log.debug("USER (without roles); AUTHENTICATED");
075        } else {
076            containerRoles = singleton("fedoraUser");
077            log.debug("USER AUTHENTICATED");
078        }
079        final ServletRequest proxy = proxy(req, username, containerRoles);
080        chain.doFilter(proxy, response);
081    }
082
083    private static ServletRequest proxy(final HttpServletRequest request,
084            final String username, final Set<String> containerRoles) {
085        final Principal user = username != null ? new GrizzlyPrincipal(username) : null;
086        final HttpServletRequest result =
087                (HttpServletRequest) newProxyInstance(request.getClass()
088                        .getClassLoader(),
089                        new Class[] {HttpServletRequest.class},
090                        new InvocationHandler() {
091
092                            @Override
093                            public Object invoke(final Object proxy,
094                                    final Method method, final Object[] args)
095                                    throws Throwable {
096                                if (method.getName().equals("isUserInRole")) {
097                                    final String role = (String) args[0];
098                                    return containerRoles.contains(role);
099                                } else if (method.getName().equals(
100                                        "getUserPrincipal")) {
101                                    return user;
102                                } else if (method.getName().equals(
103                                        "getRemoteUser")) {
104                                    return username;
105                                }
106                                return method.invoke(request, args);
107                            }
108                        });
109        return result;
110    }
111
112    private static String getUsername(final HttpServletRequest request) {
113        // Extract authentication credentials
114        String authentication = request.getHeader(AUTHORIZATION);
115        if (authentication == null) {
116            return null;
117        }
118        if (!authentication.startsWith("Basic ")) {
119            return null;
120        }
121        authentication = authentication.substring("Basic ".length());
122        final String[] values = Base64.decodeAsString(authentication).split(":");
123        if (values.length < 2) {
124            throw new WebApplicationException(400);
125            // "Invalid syntax for username and password"
126        }
127        final String username = values[0];
128        final String password = values[1];
129        if ((username == null) || (password == null)) {
130            return null;
131        }
132        return username;
133    }
134
135    /*
136     * (non-Javadoc)
137     * @see javax.servlet.Filter#init(javax.servlet.FilterConfig)
138     */
139    @Override
140    public void init(final FilterConfig filterConfig) {
141    }
142
143    /*
144     * (non-Javadoc)
145     * @see javax.servlet.Filter#destroy()
146     */
147    @Override
148    public void destroy() {
149    }
150}