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