From ac0e87610688fb0634ab5528ab170459134e94a2 Mon Sep 17 00:00:00 2001 From: Mathieu Baudier Date: Sat, 7 Feb 2015 21:41:24 +0000 Subject: [PATCH] Use standard JAAS login context for RAP login git-svn-id: https://svn.argeo.org/commons/trunk@7763 4cfe0d0a-d680-48aa-b62c-e0a02a3f76cc --- .../cms/internal/kernel/KernelConstants.java | 1 + .../cms/internal/kernel/NodeSecurity.java | 33 +++--------- .../org/argeo/cms/internal/kernel/jaas.cfg | 8 +++ .../security/core/BundleContextCallback.java | 19 +++++++ .../security/core}/SpringLoginModule.java | 39 +++++++++++---- .../security/core/ThreadedLoginModule.java | 50 ------------------- org.argeo.security.ui.rap/bnd.bnd | 3 ++ .../security/ui/rap/SecureEntryPoint.java | 38 ++++++++------ .../argeo/security/ui/SecurityUiPlugin.java | 7 +++ .../ui/dialogs/DefaultLoginDialog.java | 4 ++ 10 files changed, 103 insertions(+), 99 deletions(-) create mode 100644 org.argeo.cms/src/org/argeo/cms/internal/kernel/jaas.cfg create mode 100644 org.argeo.security.core/src/org/argeo/security/core/BundleContextCallback.java rename {org.argeo.cms/src/org/argeo/cms/internal/kernel => org.argeo.security.core/src/org/argeo/security/core}/SpringLoginModule.java (88%) delete mode 100644 org.argeo.security.core/src/org/argeo/security/core/ThreadedLoginModule.java 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/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.cms/src/org/argeo/cms/internal/kernel/SpringLoginModule.java b/org.argeo.security.core/src/org/argeo/security/core/SpringLoginModule.java similarity index 88% rename from org.argeo.cms/src/org/argeo/cms/internal/kernel/SpringLoginModule.java rename to org.argeo.security.core/src/org/argeo/security/core/SpringLoginModule.java index f3e0b608c..75051d27b 100644 --- a/org.argeo.cms/src/org/argeo/cms/internal/kernel/SpringLoginModule.java +++ b/org.argeo.security.core/src/org/argeo/security/core/SpringLoginModule.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.argeo.cms.internal.kernel; +package org.argeo.security.core; import java.util.Collections; import java.util.List; @@ -33,6 +33,7 @@ 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; @@ -42,12 +43,12 @@ 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 { +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 AuthenticationManager authenticationManager; private CallbackHandler callbackHandler; @@ -109,6 +110,9 @@ class SpringLoginModule extends SecurityContextLoginModule { 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("")) { @@ -142,14 +146,17 @@ class SpringLoginModule extends SecurityContextLoginModule { defaultNodeUrl); LocaleCallback localeCallback = new LocaleCallback( availableLocales); + BundleContextCallback bundleContextCallback = new BundleContextCallback(); // handle callbacks if (remote) callbackHandler.handle(new Callback[] { nameCallback, - passwordCallback, urlCallback, localeCallback }); + passwordCallback, urlCallback, localeCallback, + bundleContextCallback }); else callbackHandler.handle(new Callback[] { nameCallback, - passwordCallback, localeCallback }); + passwordCallback, localeCallback, + bundleContextCallback }); selectedLocale = localeCallback.getSelectedLocale(); @@ -172,6 +179,10 @@ class SpringLoginModule extends SecurityContextLoginModule { password); } + BundleContext bc = bundleContextCallback.getBundleContext(); + AuthenticationManager authenticationManager = bc.getService(bc + .getServiceReference(AuthenticationManager.class)); + Authentication authentication; try { authentication = authenticationManager @@ -210,6 +221,16 @@ class SpringLoginModule extends SecurityContextLoginModule { 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. * @@ -221,10 +242,10 @@ class SpringLoginModule extends SecurityContextLoginModule { (Authentication) authentication); } - public void setAuthenticationManager( - AuthenticationManager authenticationManager) { - this.authenticationManager = authenticationManager; - } + // public void setAuthenticationManager( + // AuthenticationManager authenticationManager) { + // this.authenticationManager = authenticationManager; + // } /** Authenticates on a remote node */ public void setRemote(Boolean remote) { 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()); } } } -- 2.30.2