001/* 002 * Copyright (c) 2006-2011 Nuxeo SA (http://nuxeo.com/) and others. 003 * 004 * All rights reserved. This program and the accompanying materials 005 * are made available under the terms of the Eclipse Public License v1.0 006 * which accompanies this distribution, and is available at 007 * http://www.eclipse.org/legal/epl-v10.html 008 * 009 * Contributors: 010 * bstefanescu 011 */ 012package org.nuxeo.ecm.webengine.jaxrs.login; 013 014import java.io.IOException; 015import java.security.Principal; 016import java.util.Set; 017 018import javax.security.auth.login.LoginContext; 019import javax.security.auth.login.LoginException; 020import javax.servlet.FilterChain; 021import javax.servlet.FilterConfig; 022import javax.servlet.ServletException; 023import javax.servlet.http.HttpServletRequest; 024import javax.servlet.http.HttpServletRequestWrapper; 025import javax.servlet.http.HttpServletResponse; 026 027import org.nuxeo.common.utils.Base64; 028import org.nuxeo.common.utils.StringUtils; 029import org.nuxeo.ecm.core.api.local.ClientLoginModule; 030import org.nuxeo.ecm.webengine.jaxrs.HttpFilter; 031import org.nuxeo.runtime.api.Framework; 032 033/** 034 * Filter using the {@link SimpleLoginModule} to authenticate a request. 035 * 036 * @author <a href="mailto:bs@nuxeo.com">Bogdan Stefanescu</a> 037 */ 038public class AuthenticationFilter extends HttpFilter { 039 040 public static final String DEFAULT_SECURITY_DOMAIN = "nuxeo-client-login"; 041 042 protected String domain = DEFAULT_SECURITY_DOMAIN; 043 044 protected boolean autoPrompt = true; 045 046 protected String realmName = "Nuxeo"; 047 048 @Override 049 public void init(FilterConfig filterConfig) throws ServletException { 050 String v = filterConfig.getInitParameter("securityDomain"); 051 if (v != null) { 052 domain = v; 053 } 054 v = filterConfig.getInitParameter("realmName"); 055 if (v != null) { 056 realmName = v; 057 } 058 } 059 060 @Override 061 public void run(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws IOException, 062 ServletException { 063 064 LoginContext lc = null; 065 if (request.getUserPrincipal() == null) { 066 try { 067 lc = doLogin(request, response); 068 request = wrapRequest(request, lc); 069 } catch (LoginException e) { 070 // login failed 071 handleLoginFailure(request, response, e); 072 return; 073 } 074 } 075 076 try { 077 chain.doFilter(request, response); 078 } finally { 079 ClientLoginModule.getThreadLocalLogin().clear(); 080 if (lc != null) { 081 // a null lc may indicate an anonymous login 082 try { 083 lc.logout(); 084 } catch (LoginException e) { 085 throw new RuntimeException(e); 086 } 087 } 088 } 089 } 090 091 @Override 092 public void destroy() { 093 } 094 095 protected String[] retrieveBasicLogin(HttpServletRequest httpRequest) { 096 String auth = httpRequest.getHeader("authorization"); 097 if (auth != null && auth.toLowerCase().startsWith("basic")) { 098 int idx = auth.indexOf(' '); 099 String b64userpassword = auth.substring(idx + 1); 100 byte[] clearUp = Base64.decode(b64userpassword); 101 String userpassword = new String(clearUp); 102 String[] up = StringUtils.split(userpassword, ':', false); 103 if (up.length != 2) { 104 return null; 105 } 106 return up; 107 } 108 return null; 109 } 110 111 protected LoginContext doLogin(HttpServletRequest request, HttpServletResponse response) throws LoginException { 112 String[] login = retrieveBasicLogin(request); 113 if (login != null) { 114 return Framework.login(login[0], login[1]); 115 } 116 // TODO no login provided - use anonymous ? 117 // for now no anonymous user supported - we require a login 118 throw new LoginException("User must login"); 119 // return null; 120 } 121 122 protected void handleLoginFailure(HttpServletRequest request, HttpServletResponse response, LoginException e) { 123 String s = "Basic realm=\"" + realmName + "\""; 124 response.setHeader("WWW-Authenticate", s); 125 response.setStatus(401); 126 } 127 128 protected HttpServletRequest wrapRequest(HttpServletRequest request, LoginContext lc) { 129 Set<Principal> set = lc.getSubject().getPrincipals(); 130 if (!set.isEmpty()) { 131 final Principal principal = set.iterator().next(); 132 return new HttpServletRequestWrapper(request) { 133 @Override 134 public Principal getUserPrincipal() { 135 return principal; 136 } 137 }; 138 } 139 return request; 140 } 141}