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