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.tokenauth.servlet; 018 019import java.io.IOException; 020import java.io.OutputStream; 021import java.security.Principal; 022 023import javax.servlet.ServletException; 024import javax.servlet.http.HttpServlet; 025import javax.servlet.http.HttpServletRequest; 026import javax.servlet.http.HttpServletResponse; 027 028import org.apache.commons.httpclient.HttpStatus; 029import org.apache.commons.httpclient.util.URIUtil; 030import org.apache.commons.lang.StringUtils; 031import org.apache.commons.logging.Log; 032import org.apache.commons.logging.LogFactory; 033import org.nuxeo.ecm.core.api.NuxeoPrincipal; 034import org.nuxeo.ecm.platform.ui.web.auth.service.AuthenticationPluginDescriptor; 035import org.nuxeo.ecm.platform.ui.web.auth.service.PluggableAuthenticationService; 036import org.nuxeo.ecm.platform.ui.web.auth.token.TokenAuthenticator; 037import org.nuxeo.ecm.tokenauth.TokenAuthenticationException; 038import org.nuxeo.ecm.tokenauth.service.TokenAuthenticationService; 039import org.nuxeo.runtime.api.Framework; 040 041/** 042 * Servlet that allows to get a unique authentication token given the request Principal and some device information 043 * passed as request parameters: application name, device id, device description, permission. An error response will be 044 * sent with a 400 status code if one of the required parameters is null or empty. All parameters are required except 045 * for the device description. 046 * <p> 047 * The token is provided by the {@link TokenAuthenticationService}. 048 * 049 * @author Antoine Taillefer (ataillefer@nuxeo.com) 050 * @since 5.7 051 */ 052public class TokenAuthenticationServlet extends HttpServlet { 053 054 private static final long serialVersionUID = 7792388601558509103L; 055 056 private static final Log log = LogFactory.getLog(TokenAuthenticationServlet.class); 057 058 protected static final String TOKEN_AUTH_PLUGIN_NAME = "TOKEN_AUTH"; 059 060 protected static final String APPLICATION_NAME_PARAM = "applicationName"; 061 062 protected static final String DEVICE_ID_PARAM = "deviceId"; 063 064 protected static final String DEVICE_DESCRIPTION_PARAM = "deviceDescription"; 065 066 protected static final String PERMISSION_PARAM = "permission"; 067 068 protected static final String REVOKE_PARAM = "revoke"; 069 070 @Override 071 public void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { 072 073 // Don't provide token for anonymous user unless 'allowAnonymous' parameter is explicitly set to true in 074 // the authentication plugin configuration 075 Principal principal = req.getUserPrincipal(); 076 if (principal instanceof NuxeoPrincipal && ((NuxeoPrincipal) principal).isAnonymous()) { 077 PluggableAuthenticationService authenticationService = (PluggableAuthenticationService) Framework.getRuntime().getComponent( 078 PluggableAuthenticationService.NAME); 079 AuthenticationPluginDescriptor tokenAuthPluginDesc = authenticationService.getDescriptor(TOKEN_AUTH_PLUGIN_NAME); 080 if (tokenAuthPluginDesc == null 081 || !(Boolean.valueOf(tokenAuthPluginDesc.getParameters().get(TokenAuthenticator.ALLOW_ANONYMOUS_KEY)))) { 082 log.debug("Anonymous user is not allowed to acquire an authentication token."); 083 resp.sendError(HttpStatus.SC_UNAUTHORIZED); 084 return; 085 } 086 087 } 088 089 // Get request parameters 090 String applicationName = req.getParameter(APPLICATION_NAME_PARAM); 091 String deviceId = req.getParameter(DEVICE_ID_PARAM); 092 String deviceDescription = req.getParameter(DEVICE_DESCRIPTION_PARAM); 093 String permission = req.getParameter(PERMISSION_PARAM); 094 String revokeParam = req.getParameter(REVOKE_PARAM); 095 boolean revoke = Boolean.valueOf(revokeParam); 096 097 // If one of the required parameters is null or empty, send an 098 // error with the 400 status 099 if (!revoke 100 && (StringUtils.isEmpty(applicationName) || StringUtils.isEmpty(deviceId) || StringUtils.isEmpty(permission))) { 101 log.error("The following request parameters are mandatory to acquire an authentication token: applicationName, deviceId, permission."); 102 resp.sendError(HttpStatus.SC_BAD_REQUEST); 103 return; 104 } 105 if (revoke && (StringUtils.isEmpty(applicationName) || StringUtils.isEmpty(deviceId))) { 106 log.error("The following request parameters are mandatory to revoke an authentication token: applicationName, deviceId."); 107 resp.sendError(HttpStatus.SC_BAD_REQUEST); 108 return; 109 } 110 111 // Decode parameters 112 applicationName = URIUtil.decode(applicationName); 113 deviceId = URIUtil.decode(deviceId); 114 if (!StringUtils.isEmpty(deviceDescription)) { 115 deviceDescription = URIUtil.decode(deviceDescription); 116 } 117 if (!StringUtils.isEmpty(permission)) { 118 permission = URIUtil.decode(permission); 119 } 120 121 // Get user name from request Principal 122 if (principal == null) { 123 resp.sendError(HttpStatus.SC_UNAUTHORIZED); 124 return; 125 } 126 String userName = principal.getName(); 127 128 // Write response 129 String response = null; 130 int statusCode; 131 TokenAuthenticationService tokenAuthService = Framework.getLocalService(TokenAuthenticationService.class); 132 try { 133 // Token acquisition: acquire token and write it to the response 134 // body 135 if (!revoke) { 136 response = tokenAuthService.acquireToken(userName, applicationName, deviceId, deviceDescription, 137 permission); 138 statusCode = 201; 139 } 140 // Token revocation 141 else { 142 String token = tokenAuthService.getToken(userName, applicationName, deviceId); 143 if (token == null) { 144 response = String.format( 145 "No token found for userName %s, applicationName %s and deviceId %s; nothing to do.", 146 userName, applicationName, deviceId); 147 statusCode = 400; 148 } else { 149 tokenAuthService.revokeToken(token); 150 response = String.format("Token revoked for userName %s, applicationName %s and deviceId %s.", 151 userName, applicationName, deviceId); 152 statusCode = 202; 153 } 154 } 155 sendTextResponse(resp, response, statusCode); 156 } catch (TokenAuthenticationException e) { 157 // Should never happen as parameters have already been checked 158 resp.sendError(HttpStatus.SC_NOT_FOUND); 159 } 160 } 161 162 protected void sendTextResponse(HttpServletResponse resp, String textResponse, int statusCode) throws IOException { 163 164 resp.setContentType("text/plain"); 165 resp.setStatus(statusCode); 166 resp.setContentLength(textResponse.getBytes().length); 167 OutputStream out = resp.getOutputStream(); 168 out.write(textResponse.getBytes()); 169 out.close(); 170 } 171 172}