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 * 012 * $Id$ 013 */ 014 015package org.nuxeo.ecm.core.api.local; 016 017import java.security.Principal; 018import java.util.Map; 019import java.util.Set; 020 021import javax.security.auth.Subject; 022import javax.security.auth.callback.CallbackHandler; 023import javax.security.auth.login.LoginException; 024import javax.security.auth.spi.LoginModule; 025 026import org.nuxeo.ecm.core.api.NuxeoPrincipal; 027import org.nuxeo.ecm.core.api.SystemPrincipal; 028import org.nuxeo.runtime.api.login.LoginComponent; 029 030/** 031 * A login module that is propagating the login information into the core login stack. 032 * <p> 033 * This login module doesn't make any authentication - it is called only after the authentication is successfully done 034 * by a previous login module. 035 * <p> 036 * The static method of this class can also be used to manage the current login stack. 037 * 038 * @author eionica@nuxeo.com 039 */ 040public class ClientLoginModule implements LoginModule { 041 042 /** 043 * The global login stack 044 */ 045 protected static final LoginStack globalInstance = LoginStack.synchronizedStack(); 046 047 /** 048 * The thread local login stack - for per thread logins 049 */ 050 protected static final ThreadLocal<LoginStack> threadInstance = new ThreadLocal<LoginStack>() { 051 @Override 052 protected LoginStack initialValue() { 053 return new LoginStack(); 054 } 055 }; 056 057 /** 058 * @since 5.7 059 */ 060 public static void clearThreadLocalLogin() { 061 threadInstance.remove(); 062 } 063 064 public static LoginStack getThreadLocalLogin() { 065 return threadInstance.get(); 066 } 067 068 public static LoginStack.Entry getCurrentLogin() { 069 LoginStack.Entry entry = threadInstance.get().peek(); 070 if (entry == null) { 071 entry = globalInstance.peek(); 072 } 073 return entry; 074 } 075 076 /** 077 * Returns the current logged {@link NuxeoPrincipal} from the login stack 078 * 079 * @since 5.6 080 */ 081 public static NuxeoPrincipal getCurrentPrincipal() { 082 LoginStack.Entry entry = getCurrentLogin(); 083 if (entry != null) { 084 Principal p = entry.getPrincipal(); 085 if (p instanceof NuxeoPrincipal) { 086 return (NuxeoPrincipal) p; 087 } else if (LoginComponent.isSystemLogin(p)) { 088 return new SystemPrincipal(p.getName()); 089 } 090 } 091 return null; 092 } 093 094 private Subject subject; 095 096 private Map sharedState; 097 098 // active login stack 099 private LoginStack stack; 100 101 // whether or not the login was propagated 102 private boolean commited = false; 103 104 /** 105 * Initialize this LoginModule. 106 */ 107 @Override 108 public void initialize(Subject subject, CallbackHandler callbackHandler, Map sharedState, Map options) { 109 this.subject = subject; 110 this.sharedState = sharedState; 111 // Check if login must be propagated to entire JVM or only to the 112 // current thread. 113 // the default is per-thread login 114 boolean globalLogin = true; 115 String global = (String) options.get("global"); 116 if (global != null) { 117 globalLogin = Boolean.parseBoolean(global); 118 } 119 if (globalLogin) { 120 // propagate the login only for the current thread 121 stack = threadInstance.get(); 122 } else { 123 // propagate the login for all threads in JVM 124 stack = globalInstance; 125 } 126 } 127 128 @Override 129 public boolean login() throws LoginException { 130 // this login module doesn't make any user authentication 131 // it simply propagate the login to the login stack. 132 // So it must be put after the authenticating module. 133 // The authenticating module should update the sharedState map 134 // with the login info. 135 return true; 136 } 137 138 @Override 139 public boolean commit() throws LoginException { 140 Principal p = null; 141 Object user = sharedState.get("javax.security.auth.login.name"); 142 if (user instanceof Principal) { 143 p = (Principal) user; 144 } else { 145 Set<Principal> principals = subject.getPrincipals(); 146 if (!principals.isEmpty()) { 147 p = principals.iterator().next(); 148 } 149 } 150 if (p != null) { 151 Object credential = sharedState.get("javax.security.auth.login.password"); 152 stack.push(p, credential, subject); 153 commited = true; 154 } 155 return true; 156 } 157 158 @Override 159 public boolean abort() throws LoginException { 160 commited = false; 161 stack.clear(); 162 return true; 163 } 164 165 @Override 166 public boolean logout() throws LoginException { 167 if (commited) { 168 stack.pop(); 169 commited = false; 170 } 171 return true; 172 } 173 174}