From: Mathieu Baudier Date: Sat, 7 Feb 2015 21:41:24 +0000 (+0000) Subject: Use standard JAAS login context for RAP login X-Git-Tag: argeo-commons-2.1.30~413 X-Git-Url: http://git.argeo.org/?a=commitdiff_plain;h=ac0e87610688fb0634ab5528ab170459134e94a2;p=lgpl%2Fargeo-commons.git Use standard JAAS login context for RAP login git-svn-id: https://svn.argeo.org/commons/trunk@7763 4cfe0d0a-d680-48aa-b62c-e0a02a3f76cc --- diff --git a/org.argeo.cms/src/org/argeo/cms/internal/kernel/KernelConstants.java b/org.argeo.cms/src/org/argeo/cms/internal/kernel/KernelConstants.java index 4d53917b4..eb60f4596 100644 --- a/org.argeo.cms/src/org/argeo/cms/internal/kernel/KernelConstants.java +++ b/org.argeo.cms/src/org/argeo/cms/internal/kernel/KernelConstants.java @@ -17,6 +17,7 @@ interface KernelConstants { final static String DEFAULT_SECURITY_KEY = "argeo"; final static String ANONYMOUS_USER = "anonymous"; final static String ADMIN_USER = "root"; + final static String JAAS_CONFIG = "/org/argeo/cms/internal/kernel/jaas.cfg"; // Roles final static String ROLE_USER = "ROLE_USER"; diff --git a/org.argeo.cms/src/org/argeo/cms/internal/kernel/NodeSecurity.java b/org.argeo.cms/src/org/argeo/cms/internal/kernel/NodeSecurity.java index 6ad8fb15c..b5d2eb412 100644 --- a/org.argeo.cms/src/org/argeo/cms/internal/kernel/NodeSecurity.java +++ b/org.argeo.cms/src/org/argeo/cms/internal/kernel/NodeSecurity.java @@ -1,7 +1,8 @@ package org.argeo.cms.internal.kernel; +import java.net.URL; + import javax.jcr.RepositoryException; -import javax.security.auth.spi.LoginModule; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; @@ -9,11 +10,8 @@ import org.argeo.cms.CmsException; import org.argeo.security.UserAdminService; import org.argeo.security.core.InternalAuthentication; import org.argeo.security.core.InternalAuthenticationProvider; -import org.argeo.security.core.ThreadedLoginModule; import org.argeo.security.jcr.SimpleJcrSecurityModel; import org.argeo.security.jcr.jackrabbit.JackrabbitUserAdminService; -import org.eclipse.rap.rwt.RWT; -import org.eclipse.swt.widgets.Display; import org.osgi.framework.BundleContext; import org.osgi.framework.ServiceRegistration; import org.springframework.security.authentication.AnonymousAuthenticationProvider; @@ -33,15 +31,18 @@ class NodeSecurity implements AuthenticationManager { private final InternalAuthenticationProvider internalAuth; private final AnonymousAuthenticationProvider anonymousAuth; private final JackrabbitUserAdminService jackrabbitUserAdmin; - private Login loginModule; private ServiceRegistration authenticationManagerReg; private ServiceRegistration userAdminReg; private ServiceRegistration userDetailsManagerReg; - private ServiceRegistration loginModuleReg; public NodeSecurity(BundleContext bundleContext, JackrabbitNode node) throws RepositoryException { + URL url = getClass().getClassLoader().getResource( + KernelConstants.JAAS_CONFIG); + System.setProperty("java.security.auth.login.config", + url.toExternalForm()); + this.bundleContext = bundleContext; internalAuth = new InternalAuthenticationProvider( @@ -54,8 +55,6 @@ class NodeSecurity implements AuthenticationManager { jackrabbitUserAdmin.setRepository(node); jackrabbitUserAdmin.setSecurityModel(new SimpleJcrSecurityModel()); jackrabbitUserAdmin.init(); - - loginModule = new Login(); } public void publish() { @@ -68,9 +67,6 @@ class NodeSecurity implements AuthenticationManager { // userAdminReg = // bundleContext.registerService(UserDetailsService.class, // jackrabbitUserAdmin, null); - - loginModuleReg = bundleContext.registerService(LoginModule.class, - loginModule, null); } void destroy() { @@ -82,7 +78,6 @@ class NodeSecurity implements AuthenticationManager { userDetailsManagerReg.unregister(); userAdminReg.unregister(); authenticationManagerReg.unregister(); - loginModuleReg.unregister(); } @Override @@ -99,18 +94,4 @@ class NodeSecurity implements AuthenticationManager { throw new CmsException("Could not authenticate " + authentication); return auth; } - - private class Login extends ThreadedLoginModule { - - @Override - protected LoginModule createLoginModule() { - SpringLoginModule springLoginModule = new SpringLoginModule(); - springLoginModule.setAuthenticationManager(NodeSecurity.this); - if (Display.getCurrent() != null) { - - } - return springLoginModule; - } - - } } diff --git a/org.argeo.cms/src/org/argeo/cms/internal/kernel/SpringLoginModule.java b/org.argeo.cms/src/org/argeo/cms/internal/kernel/SpringLoginModule.java deleted file mode 100644 index f3e0b608c..000000000 --- a/org.argeo.cms/src/org/argeo/cms/internal/kernel/SpringLoginModule.java +++ /dev/null @@ -1,255 +0,0 @@ -/* - * Copyright (C) 2007-2012 Argeo GmbH - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.argeo.cms.internal.kernel; - -import java.util.Collections; -import java.util.List; -import java.util.Locale; -import java.util.Map; -import java.util.UUID; - -import javax.security.auth.Subject; -import javax.security.auth.callback.Callback; -import javax.security.auth.callback.CallbackHandler; -import javax.security.auth.callback.NameCallback; -import javax.security.auth.callback.PasswordCallback; -import javax.security.auth.login.LoginException; - -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; -import org.argeo.security.NodeAuthenticationToken; -import org.argeo.util.LocaleCallback; -import org.argeo.util.LocaleUtils; -import org.springframework.security.authentication.AnonymousAuthenticationToken; -import org.springframework.security.authentication.AuthenticationManager; -import org.springframework.security.authentication.BadCredentialsException; -import org.springframework.security.authentication.jaas.SecurityContextLoginModule; -import org.springframework.security.core.Authentication; -import org.springframework.security.core.authority.SimpleGrantedAuthority; -import org.springframework.security.core.context.SecurityContextHolder; - -/** Login module which caches one subject per thread. */ -class SpringLoginModule extends SecurityContextLoginModule { - final static String NODE_REPO_URI = "argeo.node.repo.uri"; - - private final static Log log = LogFactory.getLog(SpringLoginModule.class); - - private AuthenticationManager authenticationManager; - - private CallbackHandler callbackHandler; - - private Subject subject; - - private Long waitBetweenFailedLoginAttempts = 5 * 1000l; - - private Boolean remote = false; - private Boolean anonymous = false; - /** Comma separated list of locales */ - private String availableLocales = ""; - - private String key = null; - private String anonymousRole = "ROLE_ANONYMOUS"; - - public SpringLoginModule() { - - } - - @SuppressWarnings("rawtypes") - public void initialize(Subject subject, CallbackHandler callbackHandler, - Map sharedState, Map options) { - super.initialize(subject, callbackHandler, sharedState, options); - this.callbackHandler = callbackHandler; - this.subject = subject; - } - - public boolean login() throws LoginException { - try { - // thread already logged in - Authentication currentAuth = SecurityContextHolder.getContext() - .getAuthentication(); - if (currentAuth != null) { - if (subject.getPrincipals(Authentication.class).size() == 0) { - subject.getPrincipals().add(currentAuth); - } else { - Authentication principal = subject - .getPrincipals(Authentication.class).iterator() - .next(); - if (principal != currentAuth) - throw new LoginException( - "Already authenticated with a different auth"); - } - return super.login(); - } - - if (remote && anonymous) - throw new LoginException( - "Cannot have a Spring login module which is remote and anonymous"); - - // reset all principals and credentials - if (log.isTraceEnabled()) - log.trace("Resetting all principals and credentials of " - + subject); - subject.getPrincipals().clear(); - subject.getPrivateCredentials().clear(); - subject.getPublicCredentials().clear(); - - Locale selectedLocale = null; - // deals first with public access since it's simple - if (anonymous) { - // multi locale - if (callbackHandler != null && availableLocales != null - && !availableLocales.trim().equals("")) { - LocaleCallback localeCallback = new LocaleCallback( - availableLocales); - callbackHandler.handle(new Callback[] { localeCallback }); - selectedLocale = localeCallback.getSelectedLocale(); - } - - // TODO integrate with JCR? - Object principal = UUID.randomUUID().toString(); - List authorities = Collections - .singletonList(new SimpleGrantedAuthority(anonymousRole)); - AnonymousAuthenticationToken anonymousToken = new AnonymousAuthenticationToken( - key, principal, authorities); - Authentication auth = authenticationManager - .authenticate(anonymousToken); - registerAuthentication(auth); - } else { - if (callbackHandler == null) - throw new LoginException("No call back handler available"); - - // ask for username and password - NameCallback nameCallback = new NameCallback("User"); - PasswordCallback passwordCallback = new PasswordCallback( - "Password", false); - final String defaultNodeUrl = System - .getProperty(NODE_REPO_URI, - "http://localhost:7070/org.argeo.jcr.webapp/remoting/node"); - NameCallback urlCallback = new NameCallback("Site URL", - defaultNodeUrl); - LocaleCallback localeCallback = new LocaleCallback( - availableLocales); - - // handle callbacks - if (remote) - callbackHandler.handle(new Callback[] { nameCallback, - passwordCallback, urlCallback, localeCallback }); - else - callbackHandler.handle(new Callback[] { nameCallback, - passwordCallback, localeCallback }); - - selectedLocale = localeCallback.getSelectedLocale(); - - // create credentials - final String username = nameCallback.getName(); - if (username == null || username.trim().equals("")) - return false; - - char[] password = {}; - if (passwordCallback.getPassword() != null) - password = passwordCallback.getPassword(); - - NodeAuthenticationToken credentials; - if (remote) { - String url = urlCallback.getName(); - credentials = new NodeAuthenticationToken(username, - password, url); - } else { - credentials = new NodeAuthenticationToken(username, - password); - } - - Authentication authentication; - try { - authentication = authenticationManager - .authenticate(credentials); - } catch (BadCredentialsException e) { - // wait between failed login attempts - Thread.sleep(waitBetweenFailedLoginAttempts); - throw e; - } - registerAuthentication(authentication); - subject.getPrincipals().add(authentication); - } - - if (selectedLocale != null) - LocaleUtils.threadLocale.set(selectedLocale); - - return super.login(); - } catch (LoginException e) { - throw e; - } catch (ThreadDeath e) { - LoginException le = new LoginException( - "Spring Security login thread died"); - le.initCause(e); - throw le; - } catch (Exception e) { - LoginException le = new LoginException( - "Spring Security login failed"); - le.initCause(e); - throw le; - } - } - - @Override - public boolean logout() throws LoginException { - subject.getPrincipals().clear(); - return super.logout(); - } - - /** - * Register an {@link Authentication} in the security context. - * - * @param authentication - * has to implement {@link Authentication}. - */ - protected void registerAuthentication(Object authentication) { - SecurityContextHolder.getContext().setAuthentication( - (Authentication) authentication); - } - - public void setAuthenticationManager( - AuthenticationManager authenticationManager) { - this.authenticationManager = authenticationManager; - } - - /** Authenticates on a remote node */ - public void setRemote(Boolean remote) { - this.remote = remote; - } - - /** - * Request anonymous authentication (incompatible with remote) - */ - public void setAnonymous(Boolean anonymous) { - this.anonymous = anonymous; - } - - /** Role identifying an anonymous user */ - public void setAnonymousRole(String anonymousRole) { - this.anonymousRole = anonymousRole; - } - - /** System key */ - public void setKey(String key) { - this.key = key; - } - - public void setAvailableLocales(String locales) { - this.availableLocales = locales; - } - -} diff --git a/org.argeo.cms/src/org/argeo/cms/internal/kernel/jaas.cfg b/org.argeo.cms/src/org/argeo/cms/internal/kernel/jaas.cfg new file mode 100644 index 000000000..110d0e143 --- /dev/null +++ b/org.argeo.cms/src/org/argeo/cms/internal/kernel/jaas.cfg @@ -0,0 +1,8 @@ +SPRING_SECURITY_CONTEXT { + org.argeo.security.core.SpringLoginModule required; + org.springframework.security.authentication.jaas.SecurityContextLoginModule required; +}; + +KEYRING { + org.argeo.security.crypto.KeyringLoginModule required; +}; diff --git a/org.argeo.security.core/src/org/argeo/security/core/BundleContextCallback.java b/org.argeo.security.core/src/org/argeo/security/core/BundleContextCallback.java new file mode 100644 index 000000000..51831fdbb --- /dev/null +++ b/org.argeo.security.core/src/org/argeo/security/core/BundleContextCallback.java @@ -0,0 +1,19 @@ +package org.argeo.security.core; + +import javax.security.auth.callback.Callback; + +import org.osgi.framework.BundleContext; + +/** Gives access to the OSGi {@link BundleContext} */ +public class BundleContextCallback implements Callback { + private BundleContext bundleContext; + + public BundleContext getBundleContext() { + return bundleContext; + } + + public void setBundleContext(BundleContext bundleContext) { + this.bundleContext = bundleContext; + } + +} diff --git a/org.argeo.security.core/src/org/argeo/security/core/SpringLoginModule.java b/org.argeo.security.core/src/org/argeo/security/core/SpringLoginModule.java new file mode 100644 index 000000000..75051d27b --- /dev/null +++ b/org.argeo.security.core/src/org/argeo/security/core/SpringLoginModule.java @@ -0,0 +1,276 @@ +/* + * Copyright (C) 2007-2012 Argeo GmbH + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.argeo.security.core; + +import java.util.Collections; +import java.util.List; +import java.util.Locale; +import java.util.Map; +import java.util.UUID; + +import javax.security.auth.Subject; +import javax.security.auth.callback.Callback; +import javax.security.auth.callback.CallbackHandler; +import javax.security.auth.callback.NameCallback; +import javax.security.auth.callback.PasswordCallback; +import javax.security.auth.login.LoginException; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.argeo.security.NodeAuthenticationToken; +import org.argeo.util.LocaleCallback; +import org.argeo.util.LocaleUtils; +import org.osgi.framework.BundleContext; +import org.springframework.security.authentication.AnonymousAuthenticationToken; +import org.springframework.security.authentication.AuthenticationManager; +import org.springframework.security.authentication.BadCredentialsException; +import org.springframework.security.authentication.jaas.SecurityContextLoginModule; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.authority.SimpleGrantedAuthority; +import org.springframework.security.core.context.SecurityContextHolder; + +/** Login module which caches one subject per thread. */ +public class SpringLoginModule extends SecurityContextLoginModule { + final static String NODE_REPO_URI = "argeo.node.repo.uri"; + + private final static Log log = LogFactory.getLog(SpringLoginModule.class); + + // private AuthenticationManager authenticationManager; + + private CallbackHandler callbackHandler; + + private Subject subject; + + private Long waitBetweenFailedLoginAttempts = 5 * 1000l; + + private Boolean remote = false; + private Boolean anonymous = false; + /** Comma separated list of locales */ + private String availableLocales = ""; + + private String key = null; + private String anonymousRole = "ROLE_ANONYMOUS"; + + public SpringLoginModule() { + + } + + @SuppressWarnings("rawtypes") + public void initialize(Subject subject, CallbackHandler callbackHandler, + Map sharedState, Map options) { + super.initialize(subject, callbackHandler, sharedState, options); + this.callbackHandler = callbackHandler; + this.subject = subject; + } + + public boolean login() throws LoginException { + try { + // thread already logged in + Authentication currentAuth = SecurityContextHolder.getContext() + .getAuthentication(); + if (currentAuth != null) { + if (subject.getPrincipals(Authentication.class).size() == 0) { + subject.getPrincipals().add(currentAuth); + } else { + Authentication principal = subject + .getPrincipals(Authentication.class).iterator() + .next(); + if (principal != currentAuth) + throw new LoginException( + "Already authenticated with a different auth"); + } + return super.login(); + } + + if (remote && anonymous) + throw new LoginException( + "Cannot have a Spring login module which is remote and anonymous"); + + // reset all principals and credentials + if (log.isTraceEnabled()) + log.trace("Resetting all principals and credentials of " + + subject); + subject.getPrincipals().clear(); + subject.getPrivateCredentials().clear(); + subject.getPublicCredentials().clear(); + + Locale selectedLocale = null; + // deals first with public access since it's simple + if (anonymous) { + // FIXME Is this code still needed? + AuthenticationManager authenticationManager = null; + + // multi locale + if (callbackHandler != null && availableLocales != null + && !availableLocales.trim().equals("")) { + LocaleCallback localeCallback = new LocaleCallback( + availableLocales); + callbackHandler.handle(new Callback[] { localeCallback }); + selectedLocale = localeCallback.getSelectedLocale(); + } + + // TODO integrate with JCR? + Object principal = UUID.randomUUID().toString(); + List authorities = Collections + .singletonList(new SimpleGrantedAuthority(anonymousRole)); + AnonymousAuthenticationToken anonymousToken = new AnonymousAuthenticationToken( + key, principal, authorities); + Authentication auth = authenticationManager + .authenticate(anonymousToken); + registerAuthentication(auth); + } else { + if (callbackHandler == null) + throw new LoginException("No call back handler available"); + + // ask for username and password + NameCallback nameCallback = new NameCallback("User"); + PasswordCallback passwordCallback = new PasswordCallback( + "Password", false); + final String defaultNodeUrl = System + .getProperty(NODE_REPO_URI, + "http://localhost:7070/org.argeo.jcr.webapp/remoting/node"); + NameCallback urlCallback = new NameCallback("Site URL", + defaultNodeUrl); + LocaleCallback localeCallback = new LocaleCallback( + availableLocales); + BundleContextCallback bundleContextCallback = new BundleContextCallback(); + + // handle callbacks + if (remote) + callbackHandler.handle(new Callback[] { nameCallback, + passwordCallback, urlCallback, localeCallback, + bundleContextCallback }); + else + callbackHandler.handle(new Callback[] { nameCallback, + passwordCallback, localeCallback, + bundleContextCallback }); + + selectedLocale = localeCallback.getSelectedLocale(); + + // create credentials + final String username = nameCallback.getName(); + if (username == null || username.trim().equals("")) + return false; + + char[] password = {}; + if (passwordCallback.getPassword() != null) + password = passwordCallback.getPassword(); + + NodeAuthenticationToken credentials; + if (remote) { + String url = urlCallback.getName(); + credentials = new NodeAuthenticationToken(username, + password, url); + } else { + credentials = new NodeAuthenticationToken(username, + password); + } + + BundleContext bc = bundleContextCallback.getBundleContext(); + AuthenticationManager authenticationManager = bc.getService(bc + .getServiceReference(AuthenticationManager.class)); + + Authentication authentication; + try { + authentication = authenticationManager + .authenticate(credentials); + } catch (BadCredentialsException e) { + // wait between failed login attempts + Thread.sleep(waitBetweenFailedLoginAttempts); + throw e; + } + registerAuthentication(authentication); + subject.getPrincipals().add(authentication); + } + + if (selectedLocale != null) + LocaleUtils.threadLocale.set(selectedLocale); + + return super.login(); + } catch (LoginException e) { + throw e; + } catch (ThreadDeath e) { + LoginException le = new LoginException( + "Spring Security login thread died"); + le.initCause(e); + throw le; + } catch (Exception e) { + LoginException le = new LoginException( + "Spring Security login failed"); + le.initCause(e); + throw le; + } + } + + @Override + public boolean logout() throws LoginException { + subject.getPrincipals().clear(); + return super.logout(); + } + + @Override + public boolean commit() throws LoginException { + return super.commit(); + } + + @Override + public boolean abort() throws LoginException { + return super.abort(); + } + + /** + * Register an {@link Authentication} in the security context. + * + * @param authentication + * has to implement {@link Authentication}. + */ + protected void registerAuthentication(Object authentication) { + SecurityContextHolder.getContext().setAuthentication( + (Authentication) authentication); + } + + // public void setAuthenticationManager( + // AuthenticationManager authenticationManager) { + // this.authenticationManager = authenticationManager; + // } + + /** Authenticates on a remote node */ + public void setRemote(Boolean remote) { + this.remote = remote; + } + + /** + * Request anonymous authentication (incompatible with remote) + */ + public void setAnonymous(Boolean anonymous) { + this.anonymous = anonymous; + } + + /** Role identifying an anonymous user */ + public void setAnonymousRole(String anonymousRole) { + this.anonymousRole = anonymousRole; + } + + /** System key */ + public void setKey(String key) { + this.key = key; + } + + public void setAvailableLocales(String locales) { + this.availableLocales = locales; + } + +} diff --git a/org.argeo.security.core/src/org/argeo/security/core/ThreadedLoginModule.java b/org.argeo.security.core/src/org/argeo/security/core/ThreadedLoginModule.java deleted file mode 100644 index 8ddac8905..000000000 --- a/org.argeo.security.core/src/org/argeo/security/core/ThreadedLoginModule.java +++ /dev/null @@ -1,50 +0,0 @@ -package org.argeo.security.core; - -import java.util.Map; - -import javax.security.auth.Subject; -import javax.security.auth.callback.CallbackHandler; -import javax.security.auth.login.LoginException; -import javax.security.auth.spi.LoginModule; - -/** Attach login modules to threads. */ -public abstract class ThreadedLoginModule implements LoginModule { - private ThreadLocal loginModule = new ThreadLocal() { - - @Override - protected LoginModule initialValue() { - return createLoginModule(); - } - - }; - - protected abstract LoginModule createLoginModule(); - - @Override - public void initialize(Subject subject, CallbackHandler callbackHandler, - Map sharedState, Map options) { - loginModule.get().initialize(subject, callbackHandler, sharedState, - options); - } - - @Override - public boolean login() throws LoginException { - return loginModule.get().login(); - } - - @Override - public boolean commit() throws LoginException { - return loginModule.get().commit(); - } - - @Override - public boolean abort() throws LoginException { - return loginModule.get().abort(); - } - - @Override - public boolean logout() throws LoginException { - return loginModule.get().logout(); - } - -} diff --git a/org.argeo.security.ui.rap/bnd.bnd b/org.argeo.security.ui.rap/bnd.bnd index e69de29bb..4dfbbceee 100644 --- a/org.argeo.security.ui.rap/bnd.bnd +++ b/org.argeo.security.ui.rap/bnd.bnd @@ -0,0 +1,3 @@ +Import-Package: org.argeo.security.core,\ +org.springframework.security.authentication.jaas,\ +* \ No newline at end of file diff --git a/org.argeo.security.ui.rap/src/org/argeo/security/ui/rap/SecureEntryPoint.java b/org.argeo.security.ui.rap/src/org/argeo/security/ui/rap/SecureEntryPoint.java index 159152186..d78cdd15d 100644 --- a/org.argeo.security.ui.rap/src/org/argeo/security/ui/rap/SecureEntryPoint.java +++ b/org.argeo.security.ui.rap/src/org/argeo/security/ui/rap/SecureEntryPoint.java @@ -18,8 +18,8 @@ package org.argeo.security.ui.rap; import java.security.PrivilegedAction; import javax.security.auth.Subject; +import javax.security.auth.login.LoginContext; import javax.security.auth.login.LoginException; -import javax.security.auth.spi.LoginModule; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpSession; @@ -34,7 +34,6 @@ import org.eclipse.rap.rwt.RWT; import org.eclipse.rap.rwt.application.EntryPoint; import org.eclipse.swt.widgets.Display; import org.eclipse.ui.PlatformUI; -import org.osgi.framework.BundleContext; import org.springframework.security.authentication.BadCredentialsException; import org.springframework.security.core.Authentication; import org.springframework.security.core.context.SecurityContext; @@ -93,16 +92,27 @@ public class SecureEntryPoint implements EntryPoint { Subject subject = new Subject(); // log in - BundleContext bc = SecureRapActivator.getActivator().getBundleContext(); - final LoginModule loginModule = bc.getService(bc - .getServiceReference(LoginModule.class)); - loginModule.initialize(subject, - new DefaultLoginDialog(display.getActiveShell()), null, null); + // BundleContext bc = + // SecureRapActivator.getActivator().getBundleContext(); + Thread.currentThread().setContextClassLoader( + getClass().getClassLoader()); + final LoginContext loginContext; + try { + loginContext = new LoginContext(SPRING_SECURITY_CONTEXT_KEY, + subject, new DefaultLoginDialog(display.getActiveShell())); + } catch (LoginException e1) { + throw new ArgeoException("Cannot initialize login context", e1); + } + // final LoginModule loginModule = bc.getService(bc + // .getServiceReference(LoginModule.class)); + // loginModule.initialize(subject, + // new DefaultLoginDialog(display.getActiveShell()), null, null); tryLogin: while (subject.getPrincipals(Authentication.class).size() == 0) { try { - if (!loginModule.login()) { - throw new ArgeoException("Login failed"); - } + loginContext.login(); + // if () { + // throw new ArgeoException("Login failed"); + // } if (subject.getPrincipals(Authentication.class).size() == 0) throw new ArgeoException("Login succeeded but no auth");// fatal @@ -141,7 +151,7 @@ public class SecureEntryPoint implements EntryPoint { log.debug("Display disposed"); // logout(loginContext, username); try { - loginModule.logout(); + loginContext.logout(); } catch (LoginException e) { log.error("Error when logging out", e); } @@ -162,7 +172,7 @@ public class SecureEntryPoint implements EntryPoint { } }); // Explicit exit from workbench - logout(loginModule, username); + logout(loginContext, username); } finally { display.dispose(); } @@ -214,9 +224,9 @@ public class SecureEntryPoint implements EntryPoint { return null; } - private void logout(LoginModule loginModule, String username) { + private void logout(LoginContext loginContext, String username) { try { - loginModule.logout(); + loginContext.logout(); SecurityContextHolder.clearContext(); HttpServletRequest httpRequest = RWT.getRequest(); diff --git a/org.argeo.security.ui/src/org/argeo/security/ui/SecurityUiPlugin.java b/org.argeo.security.ui/src/org/argeo/security/ui/SecurityUiPlugin.java index 03584185b..a19fec17d 100644 --- a/org.argeo.security.ui/src/org/argeo/security/ui/SecurityUiPlugin.java +++ b/org.argeo.security.ui/src/org/argeo/security/ui/SecurityUiPlugin.java @@ -43,6 +43,7 @@ public class SecurityUiPlugin extends AbstractUIPlugin { private ServiceRegistration defaultCallbackHandlerReg; private static SecurityUiPlugin plugin; + private static BundleContext bundleContext; public static InheritableThreadLocal display = new InheritableThreadLocal() { @@ -55,6 +56,7 @@ public class SecurityUiPlugin extends AbstractUIPlugin { public void start(BundleContext context) throws Exception { super.start(context); plugin = this; + bundleContext = context; defaultCallbackHandler = new DefaultCallbackHandler(); defaultCallbackHandlerReg = context.registerService( @@ -63,6 +65,7 @@ public class SecurityUiPlugin extends AbstractUIPlugin { public void stop(BundleContext context) throws Exception { plugin = null; + bundleContext = null; defaultCallbackHandlerReg.unregister(); super.stop(context); } @@ -76,6 +79,10 @@ public class SecurityUiPlugin extends AbstractUIPlugin { return plugin; } + public static BundleContext getBundleContext() { + return bundleContext; + } + public static ImageDescriptor getImageDescriptor(String path) { return imageDescriptorFromPlugin(PLUGIN_ID, path); } diff --git a/org.argeo.security.ui/src/org/argeo/security/ui/dialogs/DefaultLoginDialog.java b/org.argeo.security.ui/src/org/argeo/security/ui/dialogs/DefaultLoginDialog.java index 57ba01b5b..c2c5c079d 100644 --- a/org.argeo.security.ui/src/org/argeo/security/ui/dialogs/DefaultLoginDialog.java +++ b/org.argeo.security.ui/src/org/argeo/security/ui/dialogs/DefaultLoginDialog.java @@ -21,6 +21,7 @@ import javax.security.auth.callback.NameCallback; import javax.security.auth.callback.PasswordCallback; import javax.security.auth.callback.TextOutputCallback; +import org.argeo.security.core.BundleContextCallback; import org.argeo.security.ui.SecurityUiPlugin; import org.argeo.util.LocaleCallback; import org.eclipse.swt.SWT; @@ -95,6 +96,9 @@ public class DefaultLoginDialog extends AbstractLoginDialog { createPasswordHandler(composite, (PasswordCallback) callback); } else if (callback instanceof LocaleCallback) { createLocaleHandler(composite, (LocaleCallback) callback); + } else if (callback instanceof BundleContextCallback) { + ((BundleContextCallback) callback) + .setBundleContext(SecurityUiPlugin.getBundleContext()); } } }