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