/* * (C) Copyright 2006-2012 Nuxeo SA (http://nuxeo.com/) and others. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * * Contributors: * Antoine Taillefer */ package org.nuxeo.ecm.tokenauth.servlet; import java.io.IOException; import java.io.OutputStream; import java.security.Principal; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.apache.commons.httpclient.HttpStatus; import org.apache.commons.httpclient.util.URIUtil; import org.apache.commons.lang.StringUtils; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.nuxeo.ecm.core.api.NuxeoPrincipal; import org.nuxeo.ecm.platform.ui.web.auth.service.AuthenticationPluginDescriptor; import org.nuxeo.ecm.platform.ui.web.auth.service.PluggableAuthenticationService; import org.nuxeo.ecm.platform.ui.web.auth.token.TokenAuthenticator; import org.nuxeo.ecm.tokenauth.TokenAuthenticationException; import org.nuxeo.ecm.tokenauth.service.TokenAuthenticationService; import org.nuxeo.runtime.api.Framework; /** * Servlet that allows to get a unique authentication token given the request Principal and some device information * passed as request parameters: application name, device id, device description, permission. An error response will be * sent with a 400 status code if one of the required parameters is null or empty. All parameters are required except * for the device description. * <p> * The token is provided by the {@link TokenAuthenticationService}. * * @author Antoine Taillefer (ataillefer@nuxeo.com) * @since 5.7 */ public class TokenAuthenticationServlet extends HttpServlet { private static final long serialVersionUID = 7792388601558509103L; private static final Log log = LogFactory.getLog(TokenAuthenticationServlet.class); protected static final String TOKEN_AUTH_PLUGIN_NAME = "TOKEN_AUTH"; protected static final String APPLICATION_NAME_PARAM = "applicationName"; protected static final String DEVICE_ID_PARAM = "deviceId"; protected static final String DEVICE_DESCRIPTION_PARAM = "deviceDescription"; protected static final String PERMISSION_PARAM = "permission"; protected static final String REVOKE_PARAM = "revoke"; @Override public void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { // Don't provide token for anonymous user unless 'allowAnonymous' parameter is explicitly set to true in // the authentication plugin configuration Principal principal = req.getUserPrincipal(); if (principal instanceof NuxeoPrincipal && ((NuxeoPrincipal) principal).isAnonymous()) { PluggableAuthenticationService authenticationService = (PluggableAuthenticationService) Framework.getRuntime().getComponent( PluggableAuthenticationService.NAME); AuthenticationPluginDescriptor tokenAuthPluginDesc = authenticationService.getDescriptor(TOKEN_AUTH_PLUGIN_NAME); if (tokenAuthPluginDesc == null || !(Boolean.valueOf(tokenAuthPluginDesc.getParameters().get(TokenAuthenticator.ALLOW_ANONYMOUS_KEY)))) { log.debug("Anonymous user is not allowed to acquire an authentication token."); resp.sendError(HttpStatus.SC_UNAUTHORIZED); return; } } // Get request parameters String applicationName = req.getParameter(APPLICATION_NAME_PARAM); String deviceId = req.getParameter(DEVICE_ID_PARAM); String deviceDescription = req.getParameter(DEVICE_DESCRIPTION_PARAM); String permission = req.getParameter(PERMISSION_PARAM); String revokeParam = req.getParameter(REVOKE_PARAM); boolean revoke = Boolean.valueOf(revokeParam); // If one of the required parameters is null or empty, send an // error with the 400 status if (!revoke && (StringUtils.isEmpty(applicationName) || StringUtils.isEmpty(deviceId) || StringUtils.isEmpty(permission))) { log.error("The following request parameters are mandatory to acquire an authentication token: applicationName, deviceId, permission."); resp.sendError(HttpStatus.SC_BAD_REQUEST); return; } if (revoke && (StringUtils.isEmpty(applicationName) || StringUtils.isEmpty(deviceId))) { log.error("The following request parameters are mandatory to revoke an authentication token: applicationName, deviceId."); resp.sendError(HttpStatus.SC_BAD_REQUEST); return; } // Decode parameters applicationName = URIUtil.decode(applicationName); deviceId = URIUtil.decode(deviceId); if (!StringUtils.isEmpty(deviceDescription)) { deviceDescription = URIUtil.decode(deviceDescription); } if (!StringUtils.isEmpty(permission)) { permission = URIUtil.decode(permission); } // Get user name from request Principal if (principal == null) { resp.sendError(HttpStatus.SC_UNAUTHORIZED); return; } String userName = principal.getName(); // Write response String response = null; int statusCode; TokenAuthenticationService tokenAuthService = Framework.getLocalService(TokenAuthenticationService.class); try { // Token acquisition: acquire token and write it to the response // body if (!revoke) { response = tokenAuthService.acquireToken(userName, applicationName, deviceId, deviceDescription, permission); statusCode = 201; } // Token revocation else { String token = tokenAuthService.getToken(userName, applicationName, deviceId); if (token == null) { response = String.format( "No token found for userName %s, applicationName %s and deviceId %s; nothing to do.", userName, applicationName, deviceId); statusCode = 400; } else { tokenAuthService.revokeToken(token); response = String.format("Token revoked for userName %s, applicationName %s and deviceId %s.", userName, applicationName, deviceId); statusCode = 202; } } sendTextResponse(resp, response, statusCode); } catch (TokenAuthenticationException e) { // Should never happen as parameters have already been checked resp.sendError(HttpStatus.SC_NOT_FOUND); } } protected void sendTextResponse(HttpServletResponse resp, String textResponse, int statusCode) throws IOException { resp.setContentType("text/plain"); resp.setStatus(statusCode); resp.setContentLength(textResponse.getBytes().length); OutputStream out = resp.getOutputStream(); out.write(textResponse.getBytes()); out.close(); } }