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