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 * Nuxeo - initial API and implementation 018 * 019 * $Id$ 020 */ 021 022package org.nuxeo.runtime.api.login; 023 024import java.io.Serializable; 025import java.security.AccessController; 026import java.security.Principal; 027import java.security.PrivilegedActionException; 028import java.security.PrivilegedExceptionAction; 029import java.util.HashSet; 030import java.util.Hashtable; 031import java.util.Map; 032import java.util.Set; 033 034import javax.security.auth.Subject; 035import javax.security.auth.callback.CallbackHandler; 036import javax.security.auth.login.AppConfigurationEntry; 037import javax.security.auth.login.LoginContext; 038import javax.security.auth.login.LoginException; 039 040import org.apache.commons.logging.Log; 041import org.apache.commons.logging.LogFactory; 042import org.nuxeo.runtime.api.RuntimeInstanceIdentifier; 043import org.nuxeo.runtime.model.ComponentContext; 044import org.nuxeo.runtime.model.ComponentInstance; 045import org.nuxeo.runtime.model.ComponentName; 046import org.nuxeo.runtime.model.DefaultComponent; 047 048/** 049 * @author <a href="mailto:bs@nuxeo.com">Bogdan Stefanescu</a> 050 * @author <a href="mailto:td@nuxeo.com">Thierry Delprat</a> 051 */ 052public class LoginComponent extends DefaultComponent implements LoginService { 053 054 public static final ComponentName NAME = new ComponentName("org.nuxeo.runtime.LoginComponent"); 055 056 public static final String SYSTEM_LOGIN = "nuxeo-system-login"; 057 058 public static final String CLIENT_LOGIN = "nuxeo-client-login"; 059 060 public static final String SYSTEM_USERNAME = "system"; 061 062 protected static final String instanceId = RuntimeInstanceIdentifier.getId(); 063 064 protected static final SystemLoginRestrictionManager systemLoginManager = new SystemLoginRestrictionManager(); 065 066 protected static final Log log = LogFactory.getLog(LoginComponent.class); 067 068 private final Map<String, SecurityDomain> domains = new Hashtable<String, SecurityDomain>(); 069 070 private SecurityDomain systemLogin; 071 072 private SecurityDomain clientLogin; 073 074 @Override 075 public void activate(ComponentContext context) { 076 LoginConfiguration.INSTANCE.install(new LoginConfiguration.Provider() { 077 078 @Override 079 public AppConfigurationEntry[] getAppConfigurationEntry(String name) { 080 return LoginComponent.this.getAppConfigurationEntry(name); 081 } 082 083 }); 084 } 085 086 @Override 087 public void deactivate(ComponentContext context) { 088 LoginConfiguration.INSTANCE.uninstall(); 089 } 090 091 @Override 092 public void registerContribution(Object contribution, String extensionPoint, ComponentInstance contributor) { 093 if (extensionPoint.equals("domains")) { 094 SecurityDomain domain = (SecurityDomain) contribution; 095 addSecurityDomain(domain); 096 } 097 } 098 099 @Override 100 public void unregisterContribution(Object contribution, String extensionPoint, ComponentInstance contributor) { 101 if (extensionPoint.equals("domains")) { 102 SecurityDomain domain = (SecurityDomain) contribution; 103 removeSecurityDomain(domain.getName()); 104 } 105 } 106 107 public AppConfigurationEntry[] getAppConfigurationEntry(String name) { 108 SecurityDomain domain = domains.get(name); 109 if (domain != null) { 110 return domain.getAppConfigurationEntries(); 111 } 112 return null; 113 } 114 115 @Override 116 @SuppressWarnings("unchecked") 117 public <T> T getAdapter(Class<T> adapter) { 118 if (LoginService.class.isAssignableFrom(adapter)) { 119 return (T) this; 120 } 121 return null; 122 } 123 124 @Override 125 public SecurityDomain getSecurityDomain(String name) { 126 return domains.get(name); 127 } 128 129 @Override 130 public void addSecurityDomain(SecurityDomain domain) { 131 domains.put(domain.getName(), domain); 132 if (SYSTEM_LOGIN.equals(domain.getName())) { 133 systemLogin = domain; 134 } else if (CLIENT_LOGIN.equals(domain.getName())) { 135 clientLogin = domain; 136 } 137 } 138 139 @Override 140 public void removeSecurityDomain(String name) { 141 domains.remove(name); 142 if (SYSTEM_LOGIN.equals(name)) { 143 systemLogin = null; 144 } else if (CLIENT_LOGIN.equals(name)) { 145 clientLogin = null; 146 } 147 } 148 149 @Override 150 public SecurityDomain[] getSecurityDomains() { 151 return domains.values().toArray(new SecurityDomain[domains.size()]); 152 } 153 154 @Override 155 public void removeSecurityDomains() { 156 domains.clear(); 157 systemLogin = null; 158 clientLogin = null; 159 } 160 161 private LoginContext systemLogin(String username) throws LoginException { 162 if (systemLogin != null) { 163 Set<Principal> principals = new HashSet<Principal>(); 164 SystemID sysId = new SystemID(username); 165 principals.add(sysId); 166 Subject subject = new Subject(false, principals, new HashSet<String>(), new HashSet<String>()); 167 return systemLogin.login(subject, new CredentialsCallbackHandler(sysId.getName(), sysId)); 168 } 169 return null; 170 } 171 172 @Override 173 public LoginContext login() throws LoginException { 174 return loginAs(null); 175 } 176 177 @Override 178 public LoginContext loginAs(final String username) throws LoginException { 179 // login as system user is a privileged action 180 try { 181 return AccessController.doPrivileged(new PrivilegedExceptionAction<LoginContext>() { 182 @Override 183 public LoginContext run() throws LoginException { 184 SecurityManager sm = System.getSecurityManager(); 185 if (sm != null) { 186 sm.checkPermission(new SystemLoginPermission()); 187 } 188 return systemLogin(username); 189 } 190 }); 191 } catch (PrivilegedActionException e) { 192 throw (LoginException) e.getException(); 193 } 194 } 195 196 @Override 197 public LoginContext login(String username, Object credentials) throws LoginException { 198 if (clientLogin != null) { 199 return clientLogin.login(username, credentials); 200 } 201 return null; 202 } 203 204 @Override 205 public LoginContext login(CallbackHandler cbHandler) throws LoginException { 206 if (clientLogin != null) { 207 return clientLogin.login(cbHandler); 208 } 209 return null; 210 } 211 212 @Override 213 public boolean isSystemId(Principal principal) { 214 return isSystemLogin(principal); 215 } 216 217 public static boolean isSystemLogin(Object principal) { 218 if (principal != null && principal.getClass() == SystemID.class) { 219 if (!systemLoginManager.isRemoteSystemLoginRestricted()) { 220 return true; 221 } else { 222 SystemID sys = (SystemID) principal; 223 String sourceInstanceId = sys.getSourceInstanceId(); 224 if (sourceInstanceId == null) { 225 log.warn("Can not accept a system login without InstanceID of the source : System login is rejected"); 226 return false; 227 } else { 228 if (sourceInstanceId.equals(instanceId)) { 229 return true; 230 } else { 231 if (systemLoginManager.isRemoveSystemLoginAllowedForInstance(sourceInstanceId)) { 232 if (log.isTraceEnabled()) { 233 log.trace("Remote SystemLogin from instance " + sourceInstanceId + " accepted"); 234 } 235 return true; 236 } else { 237 log.warn("Remote SystemLogin attempt from instance " + sourceInstanceId + " was denied"); 238 return false; 239 } 240 } 241 } 242 } 243 } 244 return false; 245 } 246 247 public static class SystemID implements Principal, Serializable { 248 249 private static final long serialVersionUID = 2758247997191809993L; 250 251 private final String userName; 252 253 protected final String sourceInstanceId = instanceId; 254 255 public SystemID() { 256 userName = null; 257 } 258 259 public SystemID(String origUser) { 260 userName = origUser == null ? SYSTEM_USERNAME : origUser; 261 } 262 263 @Override 264 public String getName() { 265 return userName; 266 } 267 268 public String getSourceInstanceId() { 269 return sourceInstanceId; 270 } 271 272 @Override 273 public boolean equals(Object other) { 274 if (other instanceof Principal) { 275 Principal oPal = (Principal) other; 276 String oName = oPal.getName(); 277 if (userName == null && oName != null) { 278 return false; 279 } else if (!userName.equals(oName)) { 280 return false; 281 } 282 if (systemLoginManager.isRemoteSystemLoginRestricted() && (other instanceof LoginComponent.SystemID)) { 283 // compare sourceInstanceId 284 String oSysId = ((LoginComponent.SystemID) other).sourceInstanceId; 285 if (sourceInstanceId == null) { 286 return oSysId == null; 287 } else { 288 return sourceInstanceId.equals(oSysId); 289 } 290 } else { 291 return true; 292 } 293 } 294 return false; 295 } 296 297 @Override 298 public int hashCode() { 299 if (!systemLoginManager.isRemoteSystemLoginRestricted()) { 300 return userName == null ? 0 : userName.hashCode(); 301 } else { 302 return userName == null ? 0 : userName.hashCode() + sourceInstanceId.hashCode(); 303 } 304 } 305 306 } 307 308}