org.alfresco.repo.webdav.auth.AuthenticationFilter.java Source code

Java tutorial

Introduction

Here is the source code for org.alfresco.repo.webdav.auth.AuthenticationFilter.java

Source

/*
 * #%L
 * Alfresco Remote API
 * %%
 * Copyright (C) 2005 - 2016 Alfresco Software Limited
 * %%
 * This file is part of the Alfresco software. 
 * If the software was purchased under a paid Alfresco license, the terms of 
 * the paid license agreement will prevail.  Otherwise, the software is 
 * provided under the following open source license terms:
 * 
 * Alfresco is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 * 
 * Alfresco is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 * 
 * You should have received a copy of the GNU Lesser General Public License
 * along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
 * #L%
 */

package org.alfresco.repo.webdav.auth;

import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.charset.CharacterCodingException;
import java.nio.charset.Charset;
import java.nio.charset.CharsetDecoder;
import java.nio.charset.CodingErrorAction;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.Map;
import java.util.Set;

import javax.servlet.FilterChain;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.alfresco.repo.SessionUser;
import org.alfresco.repo.security.authentication.AuthenticationException;
import org.alfresco.repo.security.authentication.AuthenticationUtil;
import org.alfresco.repo.security.authentication.Authorization;
import org.alfresco.repo.web.auth.BasicAuthCredentials;
import org.alfresco.repo.web.auth.TicketCredentials;
import org.alfresco.repo.web.filter.beans.DependencyInjectedFilter;
import org.alfresco.repo.webdav.WebDAV;
import org.alfresco.service.cmr.security.NoSuchPersonException;
import org.apache.commons.codec.binary.Base64;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

/**
 * WebDAV Authentication Filter Class
 * 
 * @author GKSpencer
 */
public class AuthenticationFilter extends BaseAuthenticationFilter implements DependencyInjectedFilter {
    // Debug logging

    private static Log logger = LogFactory.getLog(AuthenticationFilter.class);

    // Authenticated user session object name

    private static final String PPT_EXTN = ".ppt";

    /** The password encodings to try in priority order **/
    private static final String[] ENCODINGS;

    static {
        String[] encodings = new String[] { "UTF-8", System.getProperty("file.encoding"), "ISO-8859-1" };

        Set<String> encodingsSet = new LinkedHashSet<String>();
        for (String encoding : encodings) {
            encodingsSet.add(encoding);
        }
        ENCODINGS = new String[encodingsSet.size()];
        encodingsSet.toArray(ENCODINGS);
    }

    // Various services required by NTLM authenticator

    /**
     * Run the authentication filter
     * 
     * @param context ServletContext
     * @param req ServletRequest
     * @param resp ServletResponse
     * @param chain FilterChain
     * @exception ServletException
     * @exception IOException
     */
    public void doFilter(ServletContext context, ServletRequest req, ServletResponse resp, FilterChain chain)
            throws IOException, ServletException {
        if (logger.isDebugEnabled())
            logger.debug("Entering AuthenticationFilter.");

        // Assume it's an HTTP request

        HttpServletRequest httpReq = (HttpServletRequest) req;
        HttpServletResponse httpResp = (HttpServletResponse) resp;

        // Get the user details object from the session
        SessionUser user = getSessionUser(context, httpReq, httpResp, false);

        if (user == null) {
            if (logger.isDebugEnabled())
                logger.debug("There is no user in the session.");
            // Get the authorization header

            String authHdr = httpReq.getHeader("Authorization");

            if (authHdr != null && authHdr.length() > 5 && authHdr.substring(0, 5).equalsIgnoreCase("BASIC")) {
                if (logger.isDebugEnabled())
                    logger.debug("Basic authentication details present in the header.");
                byte[] encodedString = Base64.decodeBase64(authHdr.substring(5).getBytes());

                // ALF-13621: Due to browser inconsistencies we have to try a fallback path of encodings
                Set<String> attemptedAuths = new HashSet<String>(ENCODINGS.length * 2);
                for (String encoding : ENCODINGS) {
                    CharsetDecoder decoder = Charset.forName(encoding).newDecoder()
                            .onMalformedInput(CodingErrorAction.REPORT);
                    try {
                        // Attempt to decode using this charset 
                        String basicAuth = decoder.decode(ByteBuffer.wrap(encodedString)).toString();

                        // It decoded OK but we may already have tried this string.
                        if (!attemptedAuths.add(basicAuth)) {
                            // Already tried - no need to try again
                            continue;
                        }

                        String username = null;
                        String password = null;

                        // Split the username and password
                        int pos = basicAuth.indexOf(":");
                        if (pos != -1) {
                            username = basicAuth.substring(0, pos);
                            password = basicAuth.substring(pos + 1);
                        } else {
                            username = basicAuth;
                            password = "";
                        }

                        // Go to the repo and authenticate
                        Authorization auth = new Authorization(username, password);
                        if (auth.isTicket()) {
                            authenticationService.validate(auth.getTicket());
                        } else {
                            authenticationService.authenticate(username, password.toCharArray());
                            authenticationListener.userAuthenticated(new BasicAuthCredentials(username, password));
                        }

                        user = createUserEnvironment(httpReq.getSession(),
                                authenticationService.getCurrentUserName(),
                                authenticationService.getCurrentTicket(), false);

                        // Success so break out
                        break;
                    } catch (CharacterCodingException e) {
                        if (logger.isDebugEnabled())
                            logger.debug("Didn't decode using " + decoder.getClass().getName(), e);
                    } catch (AuthenticationException ex) {
                        if (logger.isDebugEnabled())
                            logger.debug("Authentication error ", ex);
                    } catch (NoSuchPersonException e) {
                        if (logger.isDebugEnabled())
                            logger.debug("There is no such person error ", e);
                    }
                }
            } else {
                // Check if the request includes an authentication ticket

                String ticket = req.getParameter(ARG_TICKET);

                if (ticket != null && ticket.length() > 0) {
                    // PowerPoint bug fix
                    if (ticket.endsWith(PPT_EXTN)) {
                        ticket = ticket.substring(0, ticket.length() - PPT_EXTN.length());
                    }

                    // Debug

                    if (logger.isDebugEnabled())
                        logger.debug("Logon via ticket from " + req.getRemoteHost() + " (" + req.getRemoteAddr()
                                + ":" + req.getRemotePort() + ")" + " ticket=" + ticket);

                    // Validate the ticket

                    authenticationService.validate(ticket);
                    authenticationListener.userAuthenticated(new TicketCredentials(ticket));

                    // Need to create the User instance if not already available

                    String currentUsername = authenticationService.getCurrentUserName();

                    user = createUserEnvironment(httpReq.getSession(), currentUsername, ticket, false);
                }
            }

            // Check if the user is authenticated, if not then prompt again

            if (user == null) {
                if (logger.isDebugEnabled())
                    logger.debug("No user/ticket, force the client to prompt for logon details.");

                httpResp.setHeader("WWW-Authenticate", "BASIC realm=\"Alfresco DAV Server\"");
                httpResp.setStatus(HttpServletResponse.SC_UNAUTHORIZED);

                httpResp.flushBuffer();
                return;
            }
        } else {
            authenticationListener.userAuthenticated(new TicketCredentials(user.getTicket()));
        }

        // Chain other filters

        chain.doFilter(req, resp);
    }

    /**
     * Cleanup filter resources
     */
    public void destroy() {
        // Nothing to do
    }

    /* (non-Javadoc)
     * @see org.alfresco.repo.webdav.auth.BaseAuthenticationFilter#getLogger()
     */
    protected Log getLogger() {
        return logger;
    }
}