001/* 002 * (C) Copyright 2006-20012 Nuxeo SA (http://nuxeo.com/) and contributors. 003 * 004 * All rights reserved. This program and the accompanying materials 005 * are made available under the terms of the GNU Lesser General Public License 006 * (LGPL) version 2.1 which accompanies this distribution, and is available at 007 * http://www.gnu.org/licenses/lgpl.html 008 * 009 * This library 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 GNU 012 * Lesser General Public License for more details. 013 * 014 * Contributors: 015 * Antoine Taillefer 016 */ 017package org.nuxeo.ecm.platform.ui.web.auth.token; 018 019import java.util.List; 020import java.util.Map; 021 022import javax.security.auth.spi.LoginModule; 023import javax.servlet.http.Cookie; 024import javax.servlet.http.HttpServletRequest; 025import javax.servlet.http.HttpServletResponse; 026 027import org.apache.commons.logging.Log; 028import org.apache.commons.logging.LogFactory; 029import org.nuxeo.ecm.platform.api.login.UserIdentificationInfo; 030import org.nuxeo.ecm.platform.ui.web.auth.interfaces.NuxeoAuthenticationPlugin; 031import org.nuxeo.ecm.platform.usermanager.UserManager; 032import org.nuxeo.ecm.tokenauth.service.TokenAuthenticationService; 033import org.nuxeo.runtime.api.Framework; 034 035/** 036 * Handles authentication with a token sent as a request header. 037 * <p> 038 * The user is retrieved with the {@link TokenAuthenticationService}. 039 * <p> 040 * This Authentication Plugin is configured to be used with the Trusting_LM {@link LoginModule} plugin => no password 041 * check will be done, a principal will be created from the userName if the user exists in the user directory. 042 * 043 * @author Antoine Taillefer (ataillefer@nuxeo.com) 044 * @since 5.7 045 */ 046public class TokenAuthenticator implements NuxeoAuthenticationPlugin { 047 048 public static final String ALLOW_ANONYMOUS_KEY = "allowAnonymous"; 049 050 private static final String HTTPS = "https"; 051 052 private static final String LOCALHOST = "localhost"; 053 054 private static final Log log = LogFactory.getLog(TokenAuthenticator.class); 055 056 protected static final String TOKEN_HEADER = "X-Authentication-Token"; 057 058 protected boolean allowAnonymous = false; 059 060 @Override 061 public Boolean handleLoginPrompt(HttpServletRequest httpRequest, HttpServletResponse httpResponse, String baseURL) { 062 return false; 063 } 064 065 @Override 066 public UserIdentificationInfo handleRetrieveIdentity(HttpServletRequest httpRequest, 067 HttpServletResponse httpResponse) { 068 069 String token = getTokenFromRequest(httpRequest); 070 071 if (token == null) { 072 log.debug(String.format("Found no '%s' header in the request.", TOKEN_HEADER)); 073 return null; 074 } 075 076 String userName = getUserByToken(token); 077 if (userName == null) { 078 log.debug(String.format("No user bound to the token '%s' (maybe it has been revoked), returning null.", 079 token)); 080 return null; 081 } 082 // Don't retrieve identity for anonymous user unless 'allowAnonymous' parameter is explicitly set to true in 083 // the authentication plugin configuration 084 UserManager userManager = Framework.getService(UserManager.class); 085 if (userManager != null && userName.equals(userManager.getAnonymousUserId()) && !allowAnonymous) { 086 log.debug("Anonymous user is not allowed to get authenticated by token, returning null."); 087 return null; 088 } 089 return new UserIdentificationInfo(userName, userName); 090 } 091 092 /** 093 * Gets the token from the request if present else null. 094 * 095 * @param httpRequest 096 * @return 097 * @since 5.9.2 098 */ 099 private String getTokenFromRequest(HttpServletRequest httpRequest) { 100 String token = httpRequest.getHeader(TOKEN_HEADER); 101 102 // If we don't find the token in request header, let's check in cookies 103 if (token == null && httpRequest.getCookies() != null) { 104 Cookie cookie = getTokenCookie(httpRequest); 105 if (cookie != null && isAllowedToUseCookieToken(httpRequest)) { 106 return cookie.getValue(); 107 } 108 } 109 return token; 110 } 111 112 /** 113 * Returns the token from the cookies if found else null. 114 * 115 * @param httpRequest 116 * @return 117 * @since 5.9.2 118 */ 119 private Cookie getTokenCookie(HttpServletRequest httpRequest) { 120 Cookie[] cookies = httpRequest.getCookies(); 121 if (cookies != null) { 122 for (Cookie cookie : cookies) { 123 if (cookie.getName().equals(TOKEN_HEADER) && isAllowedToUseCookieToken(httpRequest)) { 124 return cookie; 125 } 126 } 127 } 128 return null; 129 } 130 131 /** 132 * Guard the use of cookie token to htpps only or localhost. 133 * 134 * @param httpRequest 135 * @return 136 * @since 5.9.2 137 */ 138 private boolean isAllowedToUseCookieToken(HttpServletRequest req) { 139 if (LOCALHOST.equals(req.getServerName())) { 140 return true; 141 } 142 return HTTPS.equals(req.getScheme()); 143 } 144 145 @Override 146 public Boolean needLoginPrompt(HttpServletRequest httpRequest) { 147 return false; 148 } 149 150 @Override 151 public void initPlugin(Map<String, String> parameters) { 152 if (parameters.containsKey(ALLOW_ANONYMOUS_KEY)) { 153 allowAnonymous = Boolean.valueOf(parameters.get(ALLOW_ANONYMOUS_KEY)); 154 } 155 } 156 157 @Override 158 public List<String> getUnAuthenticatedURLPrefix() { 159 return null; 160 } 161 162 protected String getUserByToken(String token) { 163 164 TokenAuthenticationService tokenAuthService = Framework.getLocalService(TokenAuthenticationService.class); 165 return tokenAuthService.getUserName(token); 166 } 167 168}