From 0aace113e5cd3c265f2a1c7aeec5bac565fe581a Mon Sep 17 00:00:00 2001 From: Mathieu Baudier Date: Thu, 26 Feb 2015 00:56:27 +0000 Subject: [PATCH] Start improving CMS login git-svn-id: https://svn.argeo.org/commons/trunk@7969 4cfe0d0a-d680-48aa-b62c-e0a02a3f76cc --- .../org/argeo/cms/AbstractCmsEntryPoint.java | 25 +++--- org.argeo.cms/src/org/argeo/cms/CmsLogin.java | 10 +-- .../internal/auth/AbstractLoginModule.java | 60 +++++++++++--- .../cms/internal/kernel/KernelConstants.java | 3 - .../argeo/cms/internal/kernel/NodeHttp.java | 25 +++--- .../src/org/argeo/cms/util/UserMenu.java | 81 +++++++++++++++---- .../security/ui/rap/SecureEntryPoint.java | 26 +++--- 7 files changed, 153 insertions(+), 77 deletions(-) diff --git a/org.argeo.cms/src/org/argeo/cms/AbstractCmsEntryPoint.java b/org.argeo.cms/src/org/argeo/cms/AbstractCmsEntryPoint.java index 60758da88..cd7eae423 100644 --- a/org.argeo.cms/src/org/argeo/cms/AbstractCmsEntryPoint.java +++ b/org.argeo.cms/src/org/argeo/cms/AbstractCmsEntryPoint.java @@ -1,7 +1,5 @@ package org.argeo.cms; -import static org.argeo.cms.internal.kernel.KernelConstants.SPRING_SECURITY_CONTEXT_KEY; - import java.util.Locale; import java.util.ResourceBundle; @@ -10,7 +8,6 @@ import javax.jcr.Repository; import javax.jcr.RepositoryException; import javax.jcr.Session; import javax.jcr.nodetype.NodeType; -import javax.servlet.http.HttpSession; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; @@ -23,7 +20,6 @@ import org.eclipse.rap.rwt.client.service.BrowserNavigationEvent; import org.eclipse.rap.rwt.client.service.BrowserNavigationListener; import org.eclipse.swt.widgets.Display; import org.eclipse.swt.widgets.Shell; -import org.springframework.security.core.context.SecurityContext; import org.springframework.security.core.context.SecurityContextHolder; /** Manages history and navigation */ @@ -44,16 +40,17 @@ abstract class AbstractCmsEntryPoint extends AbstractEntryPoint implements private BrowserNavigation history; public AbstractCmsEntryPoint(Repository repository, String workspace) { - if (SecurityContextHolder.getContext().getAuthentication() == null) { - HttpSession httpSession = RWT.getRequest().getSession(); - // log.debug("Session: " + httpSession.getId()); - SecurityContext contextFromSessionObject = (SecurityContext) httpSession - .getAttribute(SPRING_SECURITY_CONTEXT_KEY); - if (contextFromSessionObject != null) - SecurityContextHolder.setContext(contextFromSessionObject); - else - logAsAnonymous(); - } + // if (SecurityContextHolder.getContext().getAuthentication() == null) { + // HttpSession httpSession = RWT.getRequest().getSession(); + // // log.debug("Session: " + httpSession.getId()); + // SecurityContext contextFromSessionObject = (SecurityContext) + // httpSession + // .getAttribute(SPRING_SECURITY_CONTEXT_KEY); + // if (contextFromSessionObject != null) + // SecurityContextHolder.setContext(contextFromSessionObject); + // else + // logAsAnonymous(); + // } this.repository = repository; this.workspace = workspace; diff --git a/org.argeo.cms/src/org/argeo/cms/CmsLogin.java b/org.argeo.cms/src/org/argeo/cms/CmsLogin.java index 0919ee9c9..eab83ee31 100644 --- a/org.argeo.cms/src/org/argeo/cms/CmsLogin.java +++ b/org.argeo.cms/src/org/argeo/cms/CmsLogin.java @@ -1,18 +1,14 @@ package org.argeo.cms; -import static org.argeo.cms.internal.kernel.KernelConstants.SPRING_SECURITY_CONTEXT_KEY; - import javax.security.auth.Subject; import javax.security.auth.login.LoginContext; import javax.security.auth.login.LoginException; -import javax.servlet.http.HttpSession; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.argeo.ArgeoException; import org.argeo.cms.auth.ArgeoLoginContext; import org.argeo.security.NodeAuthenticationToken; -import org.eclipse.rap.rwt.RWT; import org.springframework.security.authentication.AuthenticationManager; import org.springframework.security.core.Authentication; import org.springframework.security.core.context.SecurityContextHolder; @@ -42,9 +38,9 @@ public class CmsLogin { Authentication authentication = authenticationManager .authenticate(token); SecurityContextHolder.getContext().setAuthentication(authentication); - HttpSession httpSession = RWT.getRequest().getSession(); - httpSession.setAttribute(SPRING_SECURITY_CONTEXT_KEY, - SecurityContextHolder.getContext()); + // HttpSession httpSession = RWT.getRequest().getSession(); + // httpSession.setAttribute(SPRING_SECURITY_CONTEXT_KEY, + // SecurityContextHolder.getContext()); if (log.isDebugEnabled()) log.debug("Authenticated as " + authentication); } diff --git a/org.argeo.cms/src/org/argeo/cms/internal/auth/AbstractLoginModule.java b/org.argeo.cms/src/org/argeo/cms/internal/auth/AbstractLoginModule.java index 427ec8318..f464ebad9 100644 --- a/org.argeo.cms/src/org/argeo/cms/internal/auth/AbstractLoginModule.java +++ b/org.argeo.cms/src/org/argeo/cms/internal/auth/AbstractLoginModule.java @@ -23,19 +23,31 @@ import javax.security.auth.callback.CallbackHandler; import javax.security.auth.callback.UnsupportedCallbackException; import javax.security.auth.login.LoginException; import javax.security.auth.spi.LoginModule; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpSession; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; import org.argeo.ArgeoException; import org.argeo.cms.internal.kernel.Activator; +import org.eclipse.rap.rwt.RWT; +import org.eclipse.swt.widgets.Display; import org.osgi.framework.BundleContext; import org.osgi.framework.ServiceReference; import org.springframework.security.authentication.AuthenticationManager; import org.springframework.security.core.Authentication; +import org.springframework.security.core.context.SecurityContext; import org.springframework.security.core.context.SecurityContextHolder; /** Login module which caches one subject per thread. */ public abstract class AbstractLoginModule implements LoginModule { - // private final static Log log = LogFactory - // .getLog(AbstractSpringLoginModule.class); + /** + * From org.springframework.security.context. + * HttpSessionContextIntegrationFilter + */ + private final static String SPRING_SECURITY_CONTEXT_KEY = "SPRING_SECURITY_CONTEXT"; + + private final static Log log = LogFactory.getLog(AbstractLoginModule.class); private CallbackHandler callbackHandler; private Subject subject; @@ -62,9 +74,24 @@ public abstract class AbstractLoginModule implements LoginModule { @Override public boolean login() throws LoginException { try { - // thread already logged in Authentication currentAuth = SecurityContextHolder.getContext() .getAuthentication(); + + if (currentAuth == null && Display.getCurrent() != null) { + // try to load authentication from session + HttpServletRequest httpRequest = RWT.getRequest(); + HttpSession httpSession = httpRequest.getSession(); + // log.debug(httpSession.getId()); + Object contextFromSessionObject = httpSession + .getAttribute(SPRING_SECURITY_CONTEXT_KEY); + if (contextFromSessionObject != null) { + currentAuth = (Authentication) contextFromSessionObject; + SecurityContextHolder.getContext().setAuthentication( + currentAuth); + } + } + + // thread already logged in if (currentAuth != null) { if (subject.getPrincipals(Authentication.class).size() == 0) { // throw new LoginException( @@ -85,8 +112,19 @@ public abstract class AbstractLoginModule implements LoginModule { authentication = processLogin(callbackHandler); if (authentication != null) { - SecurityContextHolder.getContext().setAuthentication( - authentication); + // + // SET THE AUTHENTICATION + // + SecurityContext securityContext = SecurityContextHolder + .getContext(); + securityContext.setAuthentication(authentication); + if (Display.getCurrent() != null) { + HttpServletRequest httpRequest = RWT.getRequest(); + HttpSession httpSession = httpRequest.getSession(); + if (httpSession.getAttribute(SPRING_SECURITY_CONTEXT_KEY) == null) + httpSession.setAttribute(SPRING_SECURITY_CONTEXT_KEY, + authentication); + } return true; } else { throw new LoginException("No authentication returned"); @@ -109,6 +147,12 @@ public abstract class AbstractLoginModule implements LoginModule { @Override public boolean logout() throws LoginException { SecurityContextHolder.getContext().setAuthentication(null); + if (Display.getCurrent() != null) { + HttpServletRequest httpRequest = RWT.getRequest(); + HttpSession httpSession = httpRequest.getSession(); + if (httpSession.getAttribute(SPRING_SECURITY_CONTEXT_KEY) != null) + httpSession.setAttribute(SPRING_SECURITY_CONTEXT_KEY, null); + } return true; } @@ -139,12 +183,6 @@ public abstract class AbstractLoginModule implements LoginModule { return bc.getService(authenticationManager); } - // protected UserAdmin getUserAdmin(BundleContextCallback - // bundleContextCallback) { - // BundleContext bc = bundleContextCallback.getBundleContext(); - // return bc.getService(bc.getServiceReference(UserAdmin.class)); - // } - protected Subject getSubject() { return subject; } 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 b3c0c5196..6d73e1188 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 @@ -1,9 +1,6 @@ package org.argeo.cms.internal.kernel; public interface KernelConstants { - // (internal) API - public final static String SPRING_SECURITY_CONTEXT_KEY = "SPRING_SECURITY_CONTEXT"; - // Node final static String REPO_HOME = "argeo.node.repo.home"; final static String REPO_TYPE = "argeo.node.repo.type"; diff --git a/org.argeo.cms/src/org/argeo/cms/internal/kernel/NodeHttp.java b/org.argeo.cms/src/org/argeo/cms/internal/kernel/NodeHttp.java index 28c569bfd..7afab675f 100644 --- a/org.argeo.cms/src/org/argeo/cms/internal/kernel/NodeHttp.java +++ b/org.argeo.cms/src/org/argeo/cms/internal/kernel/NodeHttp.java @@ -30,7 +30,6 @@ import org.osgi.service.http.NamespaceException; import org.springframework.security.authentication.AuthenticationManager; import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; import org.springframework.security.core.Authentication; -import org.springframework.security.core.context.SecurityContext; import org.springframework.security.core.context.SecurityContextHolder; /** @@ -137,11 +136,11 @@ class NodeHttp implements KernelConstants, ArgeoJcrConstants { httpService.registerServlet(path, (Servlet) remotingServlet, ip, null); } - private Boolean isSessionAuthenticated(HttpSession httpSession) { - SecurityContext contextFromSession = (SecurityContext) httpSession - .getAttribute(SPRING_SECURITY_CONTEXT_KEY); - return contextFromSession != null; - } +// private Boolean isSessionAuthenticated(HttpSession httpSession) { +// SecurityContext contextFromSession = (SecurityContext) httpSession +// .getAttribute(SPRING_SECURITY_CONTEXT_KEY); +// return contextFromSession != null; +// } private void requestBasicAuth(HttpSession httpSession, HttpServletResponse response) { @@ -277,10 +276,10 @@ class NodeHttp implements KernelConstants, ArgeoJcrConstants { FilterChain filterChain) throws IOException, ServletException { // Authenticate from session - if (isSessionAuthenticated(httpSession)) { - filterChain.doFilter(request, response); - return; - } +// if (isSessionAuthenticated(httpSession)) { +// filterChain.doFilter(request, response); +// return; +// } KernelUtils.anonymousLogin(authenticationManager); filterChain.doFilter(request, response); @@ -307,9 +306,9 @@ class NodeHttp implements KernelConstants, ArgeoJcrConstants { UsernamePasswordAuthenticationToken token = basicAuth(basicAuth); Authentication auth = authenticationManager.authenticate(token); SecurityContextHolder.getContext().setAuthentication(auth); - httpSession.setAttribute(SPRING_SECURITY_CONTEXT_KEY, - SecurityContextHolder.getContext()); - httpSession.setAttribute(ATTR_AUTH, Boolean.FALSE); +// httpSession.setAttribute(SPRING_SECURITY_CONTEXT_KEY, +// SecurityContextHolder.getContext()); +// httpSession.setAttribute(ATTR_AUTH, Boolean.FALSE); filterChain.doFilter(request, response); return; } diff --git a/org.argeo.cms/src/org/argeo/cms/util/UserMenu.java b/org.argeo.cms/src/org/argeo/cms/util/UserMenu.java index 98311560a..622ce1c31 100644 --- a/org.argeo.cms/src/org/argeo/cms/util/UserMenu.java +++ b/org.argeo.cms/src/org/argeo/cms/util/UserMenu.java @@ -1,13 +1,23 @@ package org.argeo.cms.util; -import static org.argeo.cms.internal.kernel.KernelConstants.SPRING_SECURITY_CONTEXT_KEY; - -import javax.servlet.http.HttpSession; - +import java.io.IOException; + +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.callback.UnsupportedCallbackException; +import javax.security.auth.login.LoginContext; +import javax.security.auth.login.LoginException; + +import org.argeo.ArgeoException; import org.argeo.cms.CmsLogin; import org.argeo.cms.CmsMsg; import org.argeo.cms.CmsSession; import org.argeo.cms.CmsStyles; +import org.argeo.cms.KernelHeader; +import org.argeo.cms.auth.ArgeoLoginContext; import org.eclipse.rap.rwt.RWT; import org.eclipse.swt.SWT; import org.eclipse.swt.events.MouseAdapter; @@ -25,11 +35,12 @@ import org.eclipse.swt.widgets.Text; import org.springframework.security.core.context.SecurityContextHolder; /** The site-related user menu */ -public class UserMenu extends Shell implements CmsStyles { +public class UserMenu extends Shell implements CmsStyles, CallbackHandler { private static final long serialVersionUID = -5788157651532106301L; private CmsLogin cmsLogin; - private String username = null; + // private String username = null; + private Text username, password; public UserMenu(CmsLogin cmsLogin, Control source) { super(source.getDisplay(), SWT.NO_TRIM | SWT.BORDER | SWT.ON_TOP); @@ -37,8 +48,8 @@ public class UserMenu extends Shell implements CmsStyles { setData(RWT.CUSTOM_VARIANT, CMS_USER_MENU); - username = SecurityContextHolder.getContext().getAuthentication() - .getName(); + String username = SecurityContextHolder.getContext() + .getAuthentication().getName(); if (username.equals("anonymous")) { username = null; anonymousUi(); @@ -69,6 +80,9 @@ public class UserMenu extends Shell implements CmsStyles { protected void userUi() { setLayout(new GridLayout()); + String username = SecurityContextHolder.getContext() + .getAuthentication().getName(); + Label l = new Label(this, SWT.NONE); l.setData(RWT.CUSTOM_VARIANT, CMS_USER_MENU_ITEM); l.setData(RWT.MARKUP_ENABLED, true); @@ -88,9 +102,19 @@ public class UserMenu extends Shell implements CmsStyles { private static final long serialVersionUID = 6444395812777413116L; public void mouseDown(MouseEvent e) { - SecurityContextHolder.getContext().setAuthentication(null); - HttpSession httpSession = RWT.getRequest().getSession(); - httpSession.removeAttribute(SPRING_SECURITY_CONTEXT_KEY); + Subject subject = new Subject(); + try { + new ArgeoLoginContext(KernelHeader.LOGIN_CONTEXT_USER, + subject).logout(); + new ArgeoLoginContext(KernelHeader.LOGIN_CONTEXT_ANONYMOUS, + subject).login(); + } catch (LoginException e1) { + throw new ArgeoException("Cannot authenticate anonymous", + e1); + } + // SecurityContextHolder.getContext().setAuthentication(null); + // HttpSession httpSession = RWT.getRequest().getSession(); + // httpSession.removeAttribute(SPRING_SECURITY_CONTEXT_KEY); close(); dispose(); cmsSession.authChange(); @@ -104,14 +128,14 @@ public class UserMenu extends Shell implements CmsStyles { setLayout(new GridLayout(2, false)); new Label(this, SWT.NONE).setText(CmsMsg.username.lead()); - final Text username = new Text(this, SWT.BORDER); + username = new Text(this, SWT.BORDER); username.setData(RWT.CUSTOM_VARIANT, CMS_LOGIN_DIALOG_USERNAME); GridData gd = CmsUtils.fillWidth(); gd.widthHint = textWidth; username.setLayoutData(gd); new Label(this, SWT.NONE).setText(CmsMsg.password.lead()); - final Text password = new Text(this, SWT.BORDER | SWT.PASSWORD); + password = new Text(this, SWT.BORDER | SWT.PASSWORD); password.setData(RWT.CUSTOM_VARIANT, CMS_LOGIN_DIALOG_PASSWORD); gd = CmsUtils.fillWidth(); gd.widthHint = textWidth; @@ -123,21 +147,46 @@ public class UserMenu extends Shell implements CmsStyles { public void keyTraversed(TraverseEvent e) { if (e.detail == SWT.TRAVERSE_RETURN) - login(username.getText(), password.getTextChars()); + login(); } }; username.addTraverseListener(tl); password.addTraverseListener(tl); } - protected void login(String username, char[] password) { + protected void login() { CmsSession cmsSession = (CmsSession) getDisplay().getData( CmsSession.KEY); - cmsLogin.logInWithPassword(username, password); + + Subject subject = new Subject(); + try { + new ArgeoLoginContext(KernelHeader.LOGIN_CONTEXT_ANONYMOUS, subject) + .logout(); + LoginContext loginContext = new ArgeoLoginContext( + KernelHeader.LOGIN_CONTEXT_USER, subject, this); + loginContext.login(); + } catch (LoginException e1) { + throw new ArgeoException("Cannot authenticate anonymous", e1); + } + + // cmsLogin.logInWithPassword(username, password); close(); dispose(); // refreshUi(source.getParent()); cmsSession.authChange(); } + @Override + public void handle(Callback[] callbacks) throws IOException, + UnsupportedCallbackException { + ((NameCallback) callbacks[0]).setName(username.getText()); + ((PasswordCallback) callbacks[1]).setPassword(password.getTextChars()); + // while (!isDisposed()) + // try { + // Thread.sleep(500); + // } catch (InterruptedException e) { + // // silent + // } + } + } 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 67e76ceae..b79d60d26 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 @@ -78,13 +78,13 @@ public class SecureEntryPoint implements EntryPoint { RWT.getRequest().getSession().setMaxInactiveInterval(loginTimeout); // Try to load security context thanks to the session processing filter - HttpServletRequest httpRequest = RWT.getRequest(); - HttpSession httpSession = httpRequest.getSession(); - Object contextFromSessionObject = httpSession - .getAttribute(SPRING_SECURITY_CONTEXT_KEY); - if (contextFromSessionObject != null) - SecurityContextHolder - .setContext((SecurityContext) contextFromSessionObject); +// HttpServletRequest httpRequest = RWT.getRequest(); +// HttpSession httpSession = httpRequest.getSession(); +// Object contextFromSessionObject = httpSession +// .getAttribute(SPRING_SECURITY_CONTEXT_KEY); +// if (contextFromSessionObject != null) +// SecurityContextHolder +// .setContext((SecurityContext) contextFromSessionObject); final Display display = PlatformUI.createDisplay(); Subject subject = new Subject(); @@ -106,9 +106,9 @@ public class SecureEntryPoint implements EntryPoint { throw new ArgeoException("Login succeeded but no auth");// fatal // add security context to session - if (httpSession.getAttribute(SPRING_SECURITY_CONTEXT_KEY) == null) - httpSession.setAttribute(SPRING_SECURITY_CONTEXT_KEY, - SecurityContextHolder.getContext()); +// if (httpSession.getAttribute(SPRING_SECURITY_CONTEXT_KEY) == null) +// httpSession.setAttribute(SPRING_SECURITY_CONTEXT_KEY, +// SecurityContextHolder.getContext()); // add thread locale to RWT session if (log.isTraceEnabled()) @@ -222,9 +222,9 @@ public class SecureEntryPoint implements EntryPoint { loginContext.logout(); SecurityContextHolder.clearContext(); - HttpServletRequest httpRequest = RWT.getRequest(); - HttpSession httpSession = httpRequest.getSession(); - httpSession.setAttribute(SPRING_SECURITY_CONTEXT_KEY, null); + // HttpServletRequest httpRequest = RWT.getRequest(); + // HttpSession httpSession = httpRequest.getSession(); + // httpSession.setAttribute(SPRING_SECURITY_CONTEXT_KEY, null); RWT.getRequest().getSession().setMaxInactiveInterval(1); log.info("Logged out " + (username != null ? username : "") + " (THREAD=" + Thread.currentThread().getId() + ")"); -- 2.30.2