001/*
002 * (C) Copyright 2006-2012 Nuxeo SA (http://nuxeo.com/) and others.
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 *
016 * Contributors:
017 *     Antoine Taillefer
018 */
019package org.nuxeo.ecm.platform.ui.web.auth.token;
020
021import java.util.List;
022import java.util.Map;
023
024import javax.servlet.http.Cookie;
025import javax.servlet.http.HttpServletRequest;
026import javax.servlet.http.HttpServletResponse;
027
028import org.apache.commons.logging.Log;
029import org.apache.commons.logging.LogFactory;
030import org.nuxeo.ecm.platform.api.login.UserIdentificationInfo;
031import org.nuxeo.ecm.platform.ui.web.auth.interfaces.NuxeoAuthenticationPlugin;
032import org.nuxeo.ecm.platform.ui.web.auth.interfaces.NuxeoAuthenticationPluginLogoutExtension;
033import org.nuxeo.ecm.platform.usermanager.UserManager;
034import org.nuxeo.ecm.platform.web.common.CookieHelper;
035import org.nuxeo.ecm.tokenauth.service.TokenAuthenticationService;
036import org.nuxeo.runtime.api.Framework;
037
038/**
039 * Handles authentication with a token sent as a request header.
040 * <p>
041 * The user is retrieved with the {@link TokenAuthenticationService}.
042 *
043 * @author Antoine Taillefer (ataillefer@nuxeo.com)
044 * @since 5.7
045 */
046public class TokenAuthenticator implements NuxeoAuthenticationPlugin, NuxeoAuthenticationPluginLogoutExtension {
047
048    public static final String ALLOW_ANONYMOUS_KEY = "allowAnonymous";
049
050    private static final Log log = LogFactory.getLog(TokenAuthenticator.class);
051
052    protected static final String TOKEN_HEADER = "X-Authentication-Token";
053
054    protected static final String TOKEN_PARAM = "token";
055
056    protected boolean allowAnonymous = false;
057
058    @Override
059    public Boolean handleLoginPrompt(HttpServletRequest httpRequest, HttpServletResponse httpResponse, String baseURL) {
060        return false;
061    }
062
063    @Override
064    public UserIdentificationInfo handleRetrieveIdentity(HttpServletRequest httpRequest,
065            HttpServletResponse httpResponse) {
066
067        String token = getTokenFromRequest(httpRequest);
068
069        if (token == null) {
070            log.debug(String.format("Found no '%s' header in the request.", TOKEN_HEADER));
071            return null;
072        }
073
074        String userName = getUserByToken(token);
075        if (userName == null) {
076            log.debug(String.format("No user bound to the token '%s' (maybe it has been revoked), returning null.",
077                    token));
078            return null;
079        }
080        // Don't retrieve identity for anonymous user unless 'allowAnonymous' parameter is explicitly set to true in
081        // the authentication plugin configuration
082        UserManager userManager = Framework.getService(UserManager.class);
083        if (userManager != null && userName.equals(userManager.getAnonymousUserId()) && !allowAnonymous) {
084            log.debug("Anonymous user is not allowed to get authenticated by token, returning null.");
085            return null;
086        }
087
088        Cookie cookie = CookieHelper.createCookie(httpRequest, TOKEN_HEADER, token);
089        httpResponse.addCookie(cookie);
090
091        return new UserIdentificationInfo(userName);
092    }
093
094    /**
095     * Gets the token from the request if present else null.
096     *
097     * @since 5.9.2
098     */
099    private String getTokenFromRequest(HttpServletRequest httpRequest) {
100        String token = httpRequest.getHeader(TOKEN_HEADER);
101
102        if (token == null) {
103            token = httpRequest.getParameter(TOKEN_PARAM);
104        }
105
106        // If we don't find the token in request header, let's check in cookies
107        if (token == null && httpRequest.getCookies() != null) {
108            Cookie cookie = getTokenCookie(httpRequest);
109            if (cookie != null) {
110                return cookie.getValue();
111            }
112        }
113        return token;
114    }
115
116    /**
117     * Returns the token from the cookies if found else null.
118     *
119     * @since 5.9.2
120     */
121    private Cookie getTokenCookie(HttpServletRequest httpRequest) {
122        Cookie[] cookies = httpRequest.getCookies();
123        if (cookies != null) {
124            for (Cookie cookie : cookies) {
125                if (cookie.getName().equals(TOKEN_HEADER)) {
126                    return cookie;
127                }
128            }
129        }
130        return null;
131    }
132
133    @Override
134    public Boolean needLoginPrompt(HttpServletRequest httpRequest) {
135        return false;
136    }
137
138    @Override
139    public void initPlugin(Map<String, String> parameters) {
140        if (parameters.containsKey(ALLOW_ANONYMOUS_KEY)) {
141            allowAnonymous = Boolean.valueOf(parameters.get(ALLOW_ANONYMOUS_KEY));
142        }
143    }
144
145    @Override
146    public List<String> getUnAuthenticatedURLPrefix() {
147        return null;
148    }
149
150    protected String getUserByToken(String token) {
151
152        TokenAuthenticationService tokenAuthService = Framework.getService(TokenAuthenticationService.class);
153        return tokenAuthService.getUserName(token);
154    }
155
156    @Override
157    public Boolean handleLogout(HttpServletRequest httpRequest, HttpServletResponse httpResponse) {
158        Cookie cookie = getTokenCookie(httpRequest);
159        if (cookie != null) {
160            cookie.setValue("");
161            httpResponse.addCookie(cookie);
162        }
163        return false;
164    }
165}