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