From: Mathieu Baudier Date: Tue, 13 Oct 2015 19:01:10 +0000 (+0000) Subject: Simplify authentication X-Git-Tag: argeo-commons-2.1.30~101 X-Git-Url: http://git.argeo.org/?a=commitdiff_plain;h=86db10fcb2299ebf71d5599a80dc54444b26f893;p=lgpl%2Fargeo-commons.git Simplify authentication git-svn-id: https://svn.argeo.org/commons/trunk@8476 4cfe0d0a-d680-48aa-b62c-e0a02a3f76cc --- diff --git a/org.argeo.cms/src/org/argeo/cms/AbstractCmsEntryPoint.java b/org.argeo.cms/src/org/argeo/cms/AbstractCmsEntryPoint.java index b5078696d..d27ed006e 100644 --- a/org.argeo.cms/src/org/argeo/cms/AbstractCmsEntryPoint.java +++ b/org.argeo.cms/src/org/argeo/cms/AbstractCmsEntryPoint.java @@ -1,6 +1,5 @@ package org.argeo.cms; -import java.security.AccessControlContext; import java.security.PrivilegedAction; import java.util.HashMap; import java.util.Map; @@ -12,16 +11,16 @@ import javax.jcr.RepositoryException; import javax.jcr.Session; import javax.jcr.nodetype.NodeType; import javax.security.auth.Subject; +import javax.security.auth.login.CredentialNotFoundException; import javax.security.auth.login.LoginContext; import javax.security.auth.login.LoginException; -import javax.security.auth.x500.X500Principal; -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.auth.AuthConstants; +import org.argeo.cms.auth.HttpRequestCallbackHandler; +import org.argeo.eclipse.ui.specific.UiContext; import org.argeo.jcr.JcrUtils; import org.eclipse.rap.rwt.RWT; import org.eclipse.rap.rwt.application.AbstractEntryPoint; @@ -40,6 +39,7 @@ public abstract class AbstractCmsEntryPoint extends AbstractEntryPoint private final Log log = LogFactory.getLog(AbstractCmsEntryPoint.class); private final Subject subject; + private LoginContext loginContext; private final Repository repository; private final String workspace; @@ -63,36 +63,26 @@ public abstract class AbstractCmsEntryPoint extends AbstractEntryPoint this.workspace = workspace; this.defaultPath = defaultPath; this.factoryProperties = new HashMap(factoryProperties); + subject = new Subject(); - // load context from session - HttpServletRequest httpRequest = RWT.getRequest(); - final HttpSession httpSession = httpRequest.getSession(); - AccessControlContext acc = (AccessControlContext) httpSession - .getAttribute(AuthConstants.ACCESS_CONTROL_CONTEXT); - if (acc != null - && Subject.getSubject(acc).getPrincipals(X500Principal.class) - .size() == 1) { - subject = Subject.getSubject(acc); - } else { - subject = new Subject(); - - // Initial login + // Initial login + try { + loginContext = new LoginContext(AuthConstants.LOGIN_CONTEXT_USER, + subject, new HttpRequestCallbackHandler( + UiContext.getHttpRequest())); + loginContext.login(); + } catch (CredentialNotFoundException e) { try { - new LoginContext(AuthConstants.LOGIN_CONTEXT_USER, subject) - .login(); - } catch (LoginException e) { - // if (log.isTraceEnabled()) - // log.trace("Cannot authenticate user", e); - try { - new LoginContext(AuthConstants.LOGIN_CONTEXT_ANONYMOUS, - subject).login(); - } catch (LoginException eAnonymous) { - throw new ArgeoException("Cannot initialize subject", - eAnonymous); - } + loginContext = new LoginContext( + AuthConstants.LOGIN_CONTEXT_ANONYMOUS, subject); + loginContext.login(); + } catch (LoginException e1) { + throw new ArgeoException("Cannot log as anonymous", e); } + } catch (LoginException e) { + throw new ArgeoException("Cannot initialize subject", e); } - authChange(); + authChange(loginContext); jsExecutor = RWT.getClient().getService(JavaScriptExecutor.class); browserNavigation = RWT.getClient().getService(BrowserNavigation.class); @@ -119,7 +109,7 @@ public abstract class AbstractCmsEntryPoint extends AbstractEntryPoint @Override protected final void createContents(final Composite parent) { - getShell().getDisplay().setData(CmsView.KEY, this); + UiContext.setData(CmsView.KEY, this); Subject.doAs(subject, new PrivilegedAction() { @Override public Void run() { @@ -173,7 +163,25 @@ public abstract class AbstractCmsEntryPoint extends AbstractEntryPoint } @Override - public void authChange() { + public void logout() { + if (loginContext == null) + throw new CmsException("Login context should not be null"); + try { + loginContext.logout(); + LoginContext anonymousLc = new LoginContext( + AuthConstants.LOGIN_CONTEXT_ANONYMOUS, subject); + anonymousLc.login(); + authChange(anonymousLc); + } catch (LoginException e) { + throw new CmsException("Cannot logout", e); + } + } + + @Override + public void authChange(LoginContext loginContext) { + if (loginContext == null) + throw new CmsException("Login context cannot be null"); + this.loginContext = loginContext; Subject.doAs(subject, new PrivilegedAction() { @Override @@ -189,19 +197,7 @@ public abstract class AbstractCmsEntryPoint extends AbstractEntryPoint try { node = session.getNode(currentPath); } catch (Exception e) { - try { - // TODO find a less hacky way to log out - new LoginContext( - AuthConstants.LOGIN_CONTEXT_ANONYMOUS, - subject).logout(); - new LoginContext( - AuthConstants.LOGIN_CONTEXT_ANONYMOUS, - subject).login(); - } catch (LoginException eAnonymous) { - throw new ArgeoException( - "Cannot reset to anonymous", eAnonymous); - } - JcrUtils.logoutQuietly(session); + logout(); session = repository.login(workspace); navigateTo("~"); throw e; diff --git a/org.argeo.cms/src/org/argeo/cms/CmsApplication.java b/org.argeo.cms/src/org/argeo/cms/CmsApplication.java deleted file mode 100644 index c428a0bf9..000000000 --- a/org.argeo.cms/src/org/argeo/cms/CmsApplication.java +++ /dev/null @@ -1,405 +0,0 @@ -package org.argeo.cms; - -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; -import org.argeo.cms.util.SimpleApp; - -/** - * Configures an Argeo CMS RWT application. - * - * @deprecated Use {@link SimpleApp} instead (same method signatures) - */ -@Deprecated -public class CmsApplication extends SimpleApp { - private final static Log log = LogFactory.getLog(CmsApplication.class); - - public CmsApplication() { - super(); - log.warn("org.argeo.cms.CmsApplication will be removed soon, use org.argeo.cms.util.SimpleApp"); - } - - // - // private Map> branding = new HashMap>(); - // private Map> styleSheets = new HashMap>(); - // - // private List resources = new ArrayList(); - // - // private BundleContext bundleContext; - // - // private Repository repository; - // private String workspace = null; - // private String basePath = "/"; - // private List roPrincipals = Arrays.asList("anonymous", - // "everyone"); - // private List rwPrincipals = Arrays.asList("everyone"); - // - // private CmsUiProvider header; - // private Map pages = new LinkedHashMap(); - // - // private Integer headerHeight = 40; - // - // // Managers - // private CmsImageManager imageManager = new ImageManagerImpl(); - // - // public void configure(Application application) { - // try { - // application.setOperationMode(OperationMode.SWT_COMPATIBILITY); - // application.setExceptionHandler(new CmsExceptionHandler()); - // - // // TODO load all pics under icons - // // loading animated gif - // application.addResource(LOADING_IMAGE, - // createResourceLoader(LOADING_IMAGE)); - // // empty image - // application.addResource(NO_IMAGE, createResourceLoader(NO_IMAGE)); - // - // for (String resource : resources) { - // application.addResource(resource, new BundleResourceLoader( - // bundleContext)); - // if (log.isDebugEnabled()) - // log.debug("Registered resource " + resource); - // } - // - // Map defaultBranding = null; - // if (branding.containsKey("*")) - // defaultBranding = branding.get("*"); - // - // // entry points - // for (String page : pages.keySet()) { - // Map properties = defaultBranding != null ? new - // HashMap( - // defaultBranding) : new HashMap(); - // if (branding.containsKey(page)) { - // properties.putAll(branding.get(page)); - // } - // // favicon - // if (properties.containsKey(WebClient.FAVICON)) { - // String faviconRelPath = properties.get(WebClient.FAVICON); - // application.addResource(faviconRelPath, - // new BundleResourceLoader(bundleContext)); - // if (log.isTraceEnabled()) - // log.trace("Registered favicon " + faviconRelPath); - // - // } - // - // // page title - // if (!properties.containsKey(WebClient.PAGE_TITLE)) - // properties.put( - // WebClient.PAGE_TITLE, - // Character.toUpperCase(page.charAt(0)) - // + page.substring(1)); - // - // // default body HTML - // if (!properties.containsKey(WebClient.BODY_HTML)) - // properties.put(WebClient.BODY_HTML, DEFAULT_LOADING_BODY); - // - // // - // // ADD ENTRY POINT - // // - // application.addEntryPoint("/" + page, new CmsEntryPointFactory( - // pages.get(page), repository, workspace, properties), - // properties); - // log.info("Registered entry point /" + page); - // } - // - // // stylesheets - // for (String themeId : styleSheets.keySet()) { - // List cssLst = styleSheets.get(themeId); - // for (String css : cssLst) { - // application.addStyleSheet(themeId, css, - // new BundleResourceLoader(bundleContext)); - // } - // - // } - // } catch (RuntimeException e) { - // // Easier access to initialisation errors - // log.error("Unexpected exception when configuring RWT application.", - // e); - // throw e; - // } - // } - // - // public void init() throws RepositoryException { - // Session session = null; - // try { - // session = JcrUtils.loginOrCreateWorkspace(repository, workspace); - // VersionManager vm = session.getWorkspace().getVersionManager(); - // if (!vm.isCheckedOut("/")) - // vm.checkout("/"); - // JcrUtils.mkdirs(session, basePath); - // for (String principal : rwPrincipals) - // JcrUtils.addPrivilege(session, basePath, principal, - // Privilege.JCR_WRITE); - // for (String principal : roPrincipals) - // JcrUtils.addPrivilege(session, basePath, principal, - // Privilege.JCR_READ); - // - // for (String pageName : pages.keySet()) { - // try { - // initPage(session, pages.get(pageName)); - // session.save(); - // } catch (Exception e) { - // throw new CmsException( - // "Cannot initialize page " + pageName, e); - // } - // } - // - // } finally { - // JcrUtils.logoutQuietly(session); - // } - // } - // - // protected void initPage(Session adminSession, CmsUiProvider page) - // throws RepositoryException { - // if (page instanceof LifeCycleUiProvider) - // ((LifeCycleUiProvider) page).init(adminSession); - // } - // - // public void destroy() { - // for (String pageName : pages.keySet()) { - // try { - // CmsUiProvider page = pages.get(pageName); - // if (page instanceof LifeCycleUiProvider) - // ((LifeCycleUiProvider) page).destroy(); - // } catch (Exception e) { - // log.error("Cannot destroy page " + pageName, e); - // } - // } - // } - // - // public void setRepository(Repository repository) { - // this.repository = repository; - // } - // - // public void setWorkspace(String workspace) { - // this.workspace = workspace; - // } - // - // public void setCmsLogin(@SuppressWarnings("deprecation") CmsLogin - // cmsLogin) { - // // this.cmsLogin = cmsLogin; - // log.warn("cmsLogin" - // + - // " is deprecated and will be removed soon. Adapt your configuration ASAP."); - // } - // - // public void setHeader(CmsUiProvider header) { - // this.header = header; - // } - // - // public void setPages(Map pages) { - // this.pages = pages; - // } - // - // public void setBasePath(String basePath) { - // this.basePath = basePath; - // } - // - // public void setRoPrincipals(List roPrincipals) { - // this.roPrincipals = roPrincipals; - // } - // - // public void setRwPrincipals(List rwPrincipals) { - // this.rwPrincipals = rwPrincipals; - // } - // - // public void setHeaderHeight(Integer headerHeight) { - // this.headerHeight = headerHeight; - // } - // - // public void setBranding(Map> branding) { - // this.branding = branding; - // } - // - // public void setStyleSheets(Map> styleSheets) { - // this.styleSheets = styleSheets; - // } - // - // public void setBundleContext(BundleContext bundleContext) { - // this.bundleContext = bundleContext; - // } - // - // public void setResources(List resources) { - // this.resources = resources; - // } - // - // class CmsExceptionHandler implements ExceptionHandler { - // - // @Override - // public void handleException(Throwable throwable) { - // CmsSession.current.get().exception(throwable); - // } - // - // } - // - // private class CmsEntryPointFactory implements EntryPointFactory { - // private final CmsUiProvider page; - // private final Repository repository; - // private final String workspace; - // private final Map properties; - // - // public CmsEntryPointFactory(CmsUiProvider page, Repository repository, - // String workspace, Map properties) { - // this.page = page; - // this.repository = repository; - // this.workspace = workspace; - // this.properties = properties; - // } - // - // @Override - // public EntryPoint create() { - // CmsEntryPoint entryPoint = new CmsEntryPoint(repository, workspace, - // page, properties); - // entryPoint.setState(""); - // CmsSession.current.set(entryPoint); - // return entryPoint; - // } - // - // } - // - // private class CmsEntryPoint extends AbstractCmsEntryPoint { - // private Composite headerArea; - // private Composite bodyArea; - // private final CmsUiProvider uiProvider; - // - // public CmsEntryPoint(Repository repository, String workspace, - // CmsUiProvider uiProvider, Map factoryProperties) { - // super(repository, workspace, factoryProperties); - // this.uiProvider = uiProvider; - // } - // - // @Override - // protected void createContents(Composite parent) { - // try { - // getShell().getDisplay().setData(CmsSession.KEY, this); - // - // parent.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, - // true)); - // parent.setLayout(CmsUtils.noSpaceGridLayout()); - // - // headerArea = new Composite(parent, SWT.NONE); - // headerArea.setLayout(new FillLayout()); - // GridData headerData = new GridData(SWT.FILL, SWT.FILL, false, - // false); - // headerData.heightHint = headerHeight; - // headerArea.setLayoutData(headerData); - // refreshHeader(); - // - // bodyArea = new Composite(parent, SWT.NONE); - // bodyArea.setData(RWT.CUSTOM_VARIANT, CmsStyles.CMS_BODY); - // bodyArea.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, - // true)); - // // Should not be set here: it then prevent all children - // // composite to define a background color via CSS - // // bodyArea.setBackgroundMode(SWT.INHERIT_DEFAULT); - // bodyArea.setLayout(CmsUtils.noSpaceGridLayout()); - // refreshBody(); - // } catch (Exception e) { - // throw new CmsException("Cannot create entrypoint contents", e); - // } - // } - // - // @Override - // protected void refreshHeader() { - // if (headerArea == null) - // return; - // for (Control child : headerArea.getChildren()) - // child.dispose(); - // try { - // header.createUi(headerArea, getNode()); - // } catch (RepositoryException e) { - // throw new CmsException("Cannot refresh header", e); - // } - // headerArea.layout(true, true); - // } - // - // @Override - // protected void refreshBody() { - // if (bodyArea == null) - // return; - // // Exception - // Throwable exception = getException(); - // if (exception != null) { - // // new Label(bodyArea, SWT.NONE).setText("Unreachable state : " - // // + getState()); - // // if (getNode() != null) - // // new Label(bodyArea, SWT.NONE).setText("Context : " - // // + getNode()); - // // - // // Text errorText = new Text(bodyArea, SWT.MULTI | SWT.H_SCROLL - // // | SWT.V_SCROLL); - // // errorText.setLayoutData(new GridData(SWT.FILL, SWT.FILL, - // // true, - // // true)); - // // StringWriter sw = new StringWriter(); - // // exception.printStackTrace(new PrintWriter(sw)); - // // errorText.setText(sw.toString()); - // // IOUtils.closeQuietly(sw); - // SystemNotifications systemNotifications = new SystemNotifications( - // bodyArea); - // systemNotifications.notifyException(exception); - // resetException(); - // return; - // // TODO report - // } - // - // // clear - // for (Control child : bodyArea.getChildren()) - // child.dispose(); - // bodyArea.setLayout(CmsUtils.noSpaceGridLayout()); - // - // String state = getState(); - // try { - // if (state == null) - // throw new CmsException("State cannot be null"); - // uiProvider.createUi(bodyArea, getNode()); - // } catch (RepositoryException e) { - // throw new CmsException("Cannot refresh body", e); - // } - // - // bodyArea.layout(true, true); - // } - // - // @Override - // protected Node getDefaultNode(Session session) - // throws RepositoryException { - // if (!session.hasPermission(basePath, "read")) { - // if (session.getUserID().equals("anonymous")) - // throw new LoginRequiredException(); - // else - // throw new CmsException("Unauthorized"); - // } - // return session.getNode(basePath); - // } - // - // @Override - // public CmsImageManager getImageManager() { - // return imageManager; - // } - // - // } - // - // private static ResourceLoader createResourceLoader(final String - // resourceName) { - // return new ResourceLoader() { - // public InputStream getResourceAsStream(String resourceName) - // throws IOException { - // return getClass().getClassLoader().getResourceAsStream( - // resourceName); - // } - // }; - // } - // - // /* - // * TEXTS - // */ - // private static String DEFAULT_LOADING_BODY = "" - // + - // "" - // + ""; -} diff --git a/org.argeo.cms/src/org/argeo/cms/CmsView.java b/org.argeo.cms/src/org/argeo/cms/CmsView.java index ba960de33..0ff1211b7 100644 --- a/org.argeo.cms/src/org/argeo/cms/CmsView.java +++ b/org.argeo.cms/src/org/argeo/cms/CmsView.java @@ -1,6 +1,7 @@ package org.argeo.cms; import javax.security.auth.Subject; +import javax.security.auth.login.LoginContext; /** Provides interaction with the CMS system. UNSTABLE API at this stage. */ public interface CmsView { @@ -10,10 +11,12 @@ public interface CmsView { public void navigateTo(String state); // SECURITY - public void authChange(); + public void authChange(LoginContext loginContext); public Subject getSubject(); + public void logout(); + // SERVICES public void exception(Throwable e); diff --git a/org.argeo.cms/src/org/argeo/cms/auth/AuthConstants.java b/org.argeo.cms/src/org/argeo/cms/auth/AuthConstants.java index b5b3c776c..932b847e3 100644 --- a/org.argeo.cms/src/org/argeo/cms/auth/AuthConstants.java +++ b/org.argeo.cms/src/org/argeo/cms/auth/AuthConstants.java @@ -9,6 +9,7 @@ public interface AuthConstants { final static String LOGIN_CONTEXT_SINGLE_USER = "SINGLE_USER"; // HTTP + @Deprecated final static String ACCESS_CONTROL_CONTEXT = "org.argeo.node.accessControlContext"; // RESERVED ROLES @@ -24,6 +25,6 @@ public interface AuthConstants { public final static String ROLE_ANONYMOUS = "cn=anonymous," + ROLES_BASEDN; // SHARED STATE KEYS - public final static String BUNDLE_CONTEXT_KEY = "org.argeo.security.bundleContext"; - public final static String AUTHORIZATION_KEY = "org.argeo.security.authorization"; + //public final static String BUNDLE_CONTEXT_KEY = "org.argeo.security.bundleContext"; + //public final static String AUTHORIZATION_KEY = "org.argeo.security.authorization"; } diff --git a/org.argeo.cms/src/org/argeo/cms/auth/CurrentUser.java b/org.argeo.cms/src/org/argeo/cms/auth/CurrentUser.java index 5a65fcb11..11126014f 100644 --- a/org.argeo.cms/src/org/argeo/cms/auth/CurrentUser.java +++ b/org.argeo.cms/src/org/argeo/cms/auth/CurrentUser.java @@ -43,6 +43,15 @@ public final class CurrentUser { return getDisplayName(currentSubject()); } + public static boolean isAnonymous() { + return isAnonymous(currentSubject()); + } + + public static boolean isAnonymous(Subject subject) { + return getUsername(subject).equalsIgnoreCase( + AuthConstants.ROLE_ANONYMOUS); + } + private static Subject currentSubject() { Subject subject = Subject.getSubject(AccessController.getContext()); if (subject != null) diff --git a/org.argeo.cms/src/org/argeo/cms/auth/HttpRequestCallback.java b/org.argeo.cms/src/org/argeo/cms/auth/HttpRequestCallback.java new file mode 100644 index 000000000..eb3368d05 --- /dev/null +++ b/org.argeo.cms/src/org/argeo/cms/auth/HttpRequestCallback.java @@ -0,0 +1,17 @@ +package org.argeo.cms.auth; + +import javax.security.auth.callback.Callback; +import javax.servlet.http.HttpServletRequest; + +public class HttpRequestCallback implements Callback { + private HttpServletRequest request; + + public HttpServletRequest getRequest() { + return request; + } + + public void setRequest(HttpServletRequest request) { + this.request = request; + } + +} diff --git a/org.argeo.cms/src/org/argeo/cms/auth/HttpRequestCallbackHandler.java b/org.argeo.cms/src/org/argeo/cms/auth/HttpRequestCallbackHandler.java new file mode 100644 index 000000000..0486d7479 --- /dev/null +++ b/org.argeo.cms/src/org/argeo/cms/auth/HttpRequestCallbackHandler.java @@ -0,0 +1,29 @@ +package org.argeo.cms.auth; + +import java.io.IOException; + +import javax.security.auth.callback.Callback; +import javax.security.auth.callback.CallbackHandler; +import javax.security.auth.callback.UnsupportedCallbackException; +import javax.servlet.http.HttpServletRequest; + +/** + * Callback handler populating {@link HttpRequestCallback}s with the provided + * {@link HttpServletRequest}, and ignoring any other callback. + */ +public class HttpRequestCallbackHandler implements CallbackHandler { + final private HttpServletRequest request; + + public HttpRequestCallbackHandler(HttpServletRequest request) { + this.request = request; + } + + @Override + public void handle(Callback[] callbacks) throws IOException, + UnsupportedCallbackException { + for (Callback callback : callbacks) + if (callback instanceof HttpRequestCallback) + ((HttpRequestCallback) callback).setRequest(request); + } + +} diff --git a/org.argeo.cms/src/org/argeo/cms/auth/NodeContextLoginModule.java b/org.argeo.cms/src/org/argeo/cms/auth/NodeContextLoginModule.java deleted file mode 100644 index d898e6c48..000000000 --- a/org.argeo.cms/src/org/argeo/cms/auth/NodeContextLoginModule.java +++ /dev/null @@ -1,72 +0,0 @@ -package org.argeo.cms.auth; - -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; - -import org.argeo.cms.internal.kernel.Activator; -import org.eclipse.swt.widgets.Display; -import org.osgi.service.useradmin.Authorization; - -/** Populates the shared state with this node context. */ -public class NodeContextLoginModule implements LoginModule, AuthConstants { - private Subject subject; - private Map sharedState; - - @SuppressWarnings("unchecked") - @Override - public void initialize(Subject subject, CallbackHandler callbackHandler, - Map sharedState, Map options) { - this.subject = subject; - this.sharedState = (Map) sharedState; - } - - @Override - public boolean login() throws LoginException { - sharedState.put(AuthConstants.BUNDLE_CONTEXT_KEY, Activator.getBundleContext()); - Display display = Display.getCurrent(); - if (display != null) { - Authorization authorization = (Authorization) display - .getData(AuthConstants.AUTHORIZATION_KEY); - if (authorization != null) - sharedState.put(AuthConstants.AUTHORIZATION_KEY, authorization); - } - return true; - } - - @Override - public boolean commit() throws LoginException { - Display display = Display.getCurrent(); - if (display != null) { - Authorization authorization = subject - .getPrivateCredentials(Authorization.class).iterator() - .next(); - display.setData(AuthConstants.AUTHORIZATION_KEY, authorization); - } - return true; - } - - @Override - public boolean abort() throws LoginException { - sharedState.remove(AuthConstants.BUNDLE_CONTEXT_KEY); - sharedState.remove(AuthConstants.AUTHORIZATION_KEY); - Display display = Display.getCurrent(); - if (display != null) - display.setData(AuthConstants.AUTHORIZATION_KEY, null); - return true; - } - - @Override - public boolean logout() throws LoginException { - sharedState.remove(AuthConstants.BUNDLE_CONTEXT_KEY); - sharedState.remove(AuthConstants.AUTHORIZATION_KEY); - Display display = Display.getCurrent(); - if (display != null) - display.setData(AuthConstants.AUTHORIZATION_KEY, null); - return true; - } - -} diff --git a/org.argeo.cms/src/org/argeo/cms/auth/NodeUserLoginModule.java b/org.argeo.cms/src/org/argeo/cms/auth/NodeUserLoginModule.java index 74fe4e421..a2f5863cb 100644 --- a/org.argeo.cms/src/org/argeo/cms/auth/NodeUserLoginModule.java +++ b/org.argeo.cms/src/org/argeo/cms/auth/NodeUserLoginModule.java @@ -68,49 +68,48 @@ public class NodeUserLoginModule implements LoginModule { @Override public boolean commit() throws LoginException { - if (authorization != null) { - Set principals = subject.getPrincipals(); - try { - String authName = authorization.getName(); - - // determine user's principal - final LdapName name; - final Principal userPrincipal; - if (authName == null) { - name = ROLE_ANONYMOUS_NAME; - userPrincipal = ROLE_ANONYMOUS_PRINCIPAL; - principals.add(userPrincipal); - principals.add(new AnonymousPrincipal()); + if (authorization == null) + throw new LoginException("Authorization should not be null"); + Set principals = subject.getPrincipals(); + try { + String authName = authorization.getName(); + + // determine user's principal + final LdapName name; + final Principal userPrincipal; + if (authName == null) { + name = ROLE_ANONYMOUS_NAME; + userPrincipal = ROLE_ANONYMOUS_PRINCIPAL; + principals.add(userPrincipal); + principals.add(new AnonymousPrincipal()); + } else { + name = new LdapName(authName); + checkUserName(name); + userPrincipal = new X500Principal(name.toString()); + principals.add(userPrincipal); + principals.add(new ImpliedByPrincipal(ROLE_USER_NAME, + userPrincipal)); + } + + // Add roles provided by authorization + for (String role : authorization.getRoles()) { + LdapName roleName = new LdapName(role); + if (roleName.equals(name)) { + // skip } else { - name = new LdapName(authName); - checkUserName(name); - userPrincipal = new X500Principal(name.toString()); - principals.add(userPrincipal); - principals.add(new ImpliedByPrincipal(ROLE_USER_NAME, + checkImpliedPrincipalName(roleName); + principals.add(new ImpliedByPrincipal(roleName.toString(), userPrincipal)); + if (roleName.equals(ROLE_ADMIN_NAME)) + principals.add(new AdminPrincipal( + SecurityConstants.ADMIN_ID)); } - - // Add roles provided by authorization - for (String role : authorization.getRoles()) { - LdapName roleName = new LdapName(role); - if (roleName.equals(name)) { - // skip - } else { - checkImpliedPrincipalName(roleName); - principals.add(new ImpliedByPrincipal(roleName - .toString(), userPrincipal)); - if (roleName.equals(ROLE_ADMIN_NAME)) - principals.add(new AdminPrincipal( - SecurityConstants.ADMIN_ID)); - } - } - - return true; - } catch (InvalidNameException e) { - throw new CmsException("Cannot commit", e); } - } else - return false; + + return true; + } catch (InvalidNameException e) { + throw new CmsException("Cannot commit", e); + } } @Override @@ -121,14 +120,14 @@ public class NodeUserLoginModule implements LoginModule { @Override public boolean logout() throws LoginException { - // TODO better deal with successive logout if (subject == null) - return true; - // TODO make it less brutal + throw new LoginException("Subject should not be null"); + // Argeo subject.getPrincipals().removeAll( subject.getPrincipals(X500Principal.class)); subject.getPrincipals().removeAll( subject.getPrincipals(ImpliedByPrincipal.class)); + // Jackrabbit subject.getPrincipals().removeAll( subject.getPrincipals(AdminPrincipal.class)); subject.getPrincipals().removeAll( diff --git a/org.argeo.cms/src/org/argeo/cms/auth/UserAdminLoginModule.java b/org.argeo.cms/src/org/argeo/cms/auth/UserAdminLoginModule.java index 3e44e6533..ea8821185 100644 --- a/org.argeo.cms/src/org/argeo/cms/auth/UserAdminLoginModule.java +++ b/org.argeo.cms/src/org/argeo/cms/auth/UserAdminLoginModule.java @@ -1,35 +1,42 @@ package org.argeo.cms.auth; +import java.io.IOException; +import java.util.Iterator; import java.util.Map; +import java.util.Set; 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.CredentialNotFoundException; import javax.security.auth.login.LoginException; import javax.security.auth.spi.LoginModule; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpSession; import org.argeo.ArgeoException; +import org.argeo.cms.internal.kernel.Activator; import org.osgi.framework.BundleContext; +import org.osgi.service.http.HttpContext; import org.osgi.service.useradmin.Authorization; import org.osgi.service.useradmin.User; import org.osgi.service.useradmin.UserAdmin; public class UserAdminLoginModule implements LoginModule, AuthConstants { private Subject subject; - private Map sharedState; private CallbackHandler callbackHandler; private boolean isAnonymous = false; - @SuppressWarnings("unchecked") + private HttpServletRequest request = null; + @Override public void initialize(Subject subject, CallbackHandler callbackHandler, Map sharedState, Map options) { try { this.subject = subject; - this.sharedState = (Map) sharedState; this.callbackHandler = callbackHandler; if (options.containsKey("anonymous")) isAnonymous = Boolean.parseBoolean(options.get("anonymous") @@ -41,55 +48,93 @@ public class UserAdminLoginModule implements LoginModule, AuthConstants { @Override public boolean login() throws LoginException { - BundleContext bc = (BundleContext) sharedState - .get(AuthConstants.BUNDLE_CONTEXT_KEY); + BundleContext bc = Activator.getBundleContext(); UserAdmin userAdmin = bc.getService(bc .getServiceReference(UserAdmin.class)); - Authorization authorization = (Authorization) sharedState - .get(AuthConstants.AUTHORIZATION_KEY); - if (authorization == null) - if (!isAnonymous) { - // ask for username and password - NameCallback nameCallback = new NameCallback("User"); - PasswordCallback passwordCallback = new PasswordCallback( - "Password", false); - - // handle callbacks - try { - callbackHandler.handle(new Callback[] { nameCallback, - passwordCallback }); - } catch (Exception e) { - throw new ArgeoException("Cannot handle callbacks", e); - } + Authorization authorization = null; + if (isAnonymous) { + authorization = userAdmin.getAuthorization(null); + } else { + HttpRequestCallback httpCallback = new HttpRequestCallback(); + // ask for username and password + NameCallback nameCallback = new NameCallback("User"); + PasswordCallback passwordCallback = new PasswordCallback( + "Password", false); + try { + callbackHandler.handle(new Callback[] { httpCallback, + nameCallback, passwordCallback }); + } catch (IOException e) { + throw new LoginException("Cannot handle http callback: " + + e.getMessage()); + } catch (UnsupportedCallbackException e) { + return false; + } + request = httpCallback.getRequest(); + if (request != null) { + authorization = (Authorization) request + .getAttribute(HttpContext.AUTHORIZATION); + if (authorization == null) + authorization = (Authorization) request.getSession() + .getAttribute(HttpContext.AUTHORIZATION); + } + // if (authorization == null || authorization.getName() == null) + // if (!isAnonymous) { + // // ask for username and password + // NameCallback nameCallback = new NameCallback("User"); + // PasswordCallback passwordCallback = new PasswordCallback( + // "Password", false); + // + // // handle callbacks + // try { + // callbackHandler.handle(new Callback[] { nameCallback, + // passwordCallback }); + // } catch (Exception e) { + // throw new ArgeoException("Cannot handle callbacks", e); + // } + if (authorization == null) { // create credentials final String username = nameCallback.getName(); - if (username == null || username.trim().equals("")) - throw new CredentialNotFoundException( - "No credentials provided"); - - char[] password = {}; - if (passwordCallback.getPassword() != null) - password = passwordCallback.getPassword(); - else + if (username == null || username.trim().equals("")) { + // authorization = userAdmin.getAuthorization(null); throw new CredentialNotFoundException( "No credentials provided"); + } else { + char[] password = {}; + if (passwordCallback.getPassword() != null) + password = passwordCallback.getPassword(); + else + throw new CredentialNotFoundException( + "No credentials provided"); - User user = userAdmin.getUser(null, username); - if (user == null) - return false; - if (!user.hasCredential(null, password)) - return false; - authorization = userAdmin.getAuthorization(user); - } else { - authorization = userAdmin.getAuthorization(null); + User user = userAdmin.getUser(null, username); + if (user == null) + return false; + if (!user.hasCredential(null, password)) + return false; + authorization = userAdmin.getAuthorization(user); + } } + // } else { + // authorization = userAdmin.getAuthorization(null); + // } + } subject.getPrivateCredentials().add(authorization); return true; } @Override public boolean commit() throws LoginException { + Authorization authorization = subject + .getPrivateCredentials(Authorization.class).iterator().next(); + if (request != null) { + request.setAttribute(HttpContext.REMOTE_USER, + authorization.getName()); + request.setAttribute(HttpContext.AUTHORIZATION, authorization); + request.getSession().setAttribute(HttpContext.AUTHORIZATION, + authorization); + subject.getPrivateCredentials().add(request.getSession()); + } return true; } @@ -101,6 +146,15 @@ public class UserAdminLoginModule implements LoginModule, AuthConstants { @Override public boolean logout() throws LoginException { + Set httpSession = subject + .getPrivateCredentials(HttpSession.class); + Iterator it = httpSession.iterator(); + while (it.hasNext()) { + HttpSession sess = it.next(); + sess.setAttribute(HttpContext.AUTHORIZATION, null); + // sess.setMaxInactiveInterval(1);// invalidate session + } + subject.getPrivateCredentials().removeAll(httpSession); cleanUp(); return true; } diff --git a/org.argeo.cms/src/org/argeo/cms/internal/kernel/DataHttp.java b/org.argeo.cms/src/org/argeo/cms/internal/kernel/DataHttp.java index 8f95a3ef9..ab9211a54 100644 --- a/org.argeo.cms/src/org/argeo/cms/internal/kernel/DataHttp.java +++ b/org.argeo.cms/src/org/argeo/cms/internal/kernel/DataHttp.java @@ -1,16 +1,12 @@ package org.argeo.cms.internal.kernel; -import static org.argeo.cms.auth.AuthConstants.ACCESS_CONTROL_CONTEXT; +import static org.argeo.cms.auth.AuthConstants.LOGIN_CONTEXT_USER; import java.io.IOException; import java.io.Serializable; import java.net.URL; -import java.security.AccessControlContext; -import java.security.AccessController; -import java.security.PrivilegedAction; import java.security.PrivilegedActionException; import java.security.PrivilegedExceptionAction; -import java.security.cert.X509Certificate; import java.util.Properties; import java.util.StringTokenizer; @@ -22,13 +18,13 @@ 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.CredentialNotFoundException; import javax.security.auth.login.LoginContext; import javax.security.auth.login.LoginException; import javax.servlet.Servlet; import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; -import javax.servlet.http.HttpSession; import org.apache.commons.codec.binary.Base64; import org.apache.commons.logging.Log; @@ -38,6 +34,8 @@ import org.apache.jackrabbit.server.remoting.davex.JcrRemotingServlet; import org.apache.jackrabbit.webdav.simple.SimpleWebdavServlet; import org.argeo.cms.CmsException; import org.argeo.cms.auth.AuthConstants; +import org.argeo.cms.auth.HttpRequestCallback; +import org.argeo.cms.auth.HttpRequestCallbackHandler; import org.argeo.jcr.ArgeoJcrConstants; import org.argeo.jcr.JcrUtils; import org.osgi.service.http.HttpContext; @@ -52,11 +50,10 @@ import org.osgi.service.useradmin.Authorization; class DataHttp implements KernelConstants, ArgeoJcrConstants { private final static Log log = LogFactory.getLog(DataHttp.class); - private final static String ATTR_AUTH = "auth"; + // private final static String ATTR_AUTH = "auth"; private final static String HEADER_AUTHORIZATION = "Authorization"; private final static String HEADER_WWW_AUTHENTICATE = "WWW-Authenticate"; - // private final AuthenticationManager authenticationManager; private final HttpService httpService; // FIXME Make it more unique @@ -129,82 +126,29 @@ class DataHttp implements KernelConstants, ArgeoJcrConstants { new DataHttpContext(anonymous)); } - // private Boolean isSessionAuthenticated(HttpSession httpSession) { - // SecurityContext contextFromSession = (SecurityContext) httpSession - // .getAttribute(SPRING_SECURITY_CONTEXT_KEY); - // return contextFromSession != null; - // } - - private void requestBasicAuth(HttpSession httpSession, - HttpServletResponse response) { - response.setStatus(401); - response.setHeader(HEADER_WWW_AUTHENTICATE, "basic realm=\"" - + httpAuthRealm + "\""); - httpSession.setAttribute(ATTR_AUTH, Boolean.TRUE); - } - - private CallbackHandler basicAuth(String authHeader) { - if (authHeader != null) { - StringTokenizer st = new StringTokenizer(authHeader); - if (st.hasMoreTokens()) { - String basic = st.nextToken(); - if (basic.equalsIgnoreCase("Basic")) { - try { - // TODO manipulate char[] - String credentials = new String(Base64.decodeBase64(st - .nextToken()), "UTF-8"); - // log.debug("Credentials: " + credentials); - int p = credentials.indexOf(":"); - if (p != -1) { - final String login = credentials.substring(0, p) - .trim(); - final char[] password = credentials - .substring(p + 1).trim().toCharArray(); - - return new CallbackHandler() { - public void handle(Callback[] callbacks) { - for (Callback cb : callbacks) { - if (cb instanceof NameCallback) - ((NameCallback) cb).setName(login); - else if (cb instanceof PasswordCallback) - ((PasswordCallback) cb) - .setPassword(password); - } - } - }; - } else { - throw new CmsException( - "Invalid authentication token"); - } - } catch (Exception e) { - throw new CmsException( - "Couldn't retrieve authentication", e); - } - } - } - } - throw new CmsException("Couldn't retrieve authentication"); - } - - private X509Certificate extractCertificate(HttpServletRequest req) { - X509Certificate[] certs = (X509Certificate[]) req - .getAttribute("javax.servlet.request.X509Certificate"); - if (null != certs && certs.length > 0) { - return certs[0]; - } - return null; - } +// private X509Certificate extractCertificate(HttpServletRequest req) { +// X509Certificate[] certs = (X509Certificate[]) req +// .getAttribute("javax.servlet.request.X509Certificate"); +// if (null != certs && certs.length > 0) { +// return certs[0]; +// } +// return null; +// } private Subject subjectFromRequest(HttpServletRequest request) { - HttpSession httpSession = request.getSession(); Authorization authorization = (Authorization) request .getAttribute(HttpContext.AUTHORIZATION); if (authorization == null) throw new CmsException("Not authenticated"); - AccessControlContext acc = (AccessControlContext) httpSession - .getAttribute(AuthConstants.ACCESS_CONTROL_CONTEXT); - Subject subject = Subject.getSubject(acc); - return subject; + try { + LoginContext lc = new LoginContext( + AuthConstants.LOGIN_CONTEXT_USER, + new HttpRequestCallbackHandler(request)); + lc.login(); + return lc.getSubject(); + } catch (LoginException e) { + throw new CmsException("Cannot login", e); + } } private class DataHttpContext implements HttpContext { @@ -215,56 +159,44 @@ class DataHttp implements KernelConstants, ArgeoJcrConstants { } @Override - public boolean handleSecurity(HttpServletRequest request, + public boolean handleSecurity(final HttpServletRequest request, HttpServletResponse response) throws IOException { - final Subject subject; if (anonymous) { - subject = KernelUtils.anonymousLogin(); + Subject subject = KernelUtils.anonymousLogin(); Authorization authorization = subject .getPrivateCredentials(Authorization.class).iterator() .next(); + request.setAttribute(REMOTE_USER, AuthConstants.ROLE_ANONYMOUS); request.setAttribute(AUTHORIZATION, authorization); return true; } - final HttpSession httpSession = request.getSession(); - AccessControlContext acc = (AccessControlContext) httpSession - .getAttribute(AuthConstants.ACCESS_CONTROL_CONTEXT); - if (acc != null) { - subject = Subject.getSubject(acc); - } else { - // Process basic auth - String basicAuth = request.getHeader(HEADER_AUTHORIZATION); - if (basicAuth != null) { - CallbackHandler token = basicAuth(basicAuth); + KernelUtils.logRequestHeaders(log, request); + try { + new LoginContext(LOGIN_CONTEXT_USER, + new HttpRequestCallbackHandler(request)).login(); + return true; + } catch (CredentialNotFoundException e) { + CallbackHandler token = basicAuth(request); + if (token != null) { try { - LoginContext lc = new LoginContext( - AuthConstants.LOGIN_CONTEXT_USER, token); + LoginContext lc = new LoginContext(LOGIN_CONTEXT_USER, + token); lc.login(); - subject = lc.getSubject(); - } catch (LoginException e) { - throw new CmsException("Could not login", e); + // Note: this is impossible to reliably clear the + // authorization header when access from a browser. + return true; + } catch (LoginException e1) { + throw new CmsException("Could not login", e1); } - Subject.doAs(subject, new PrivilegedAction() { - public Void run() { - // add security context to session - httpSession.setAttribute(ACCESS_CONTROL_CONTEXT, - AccessController.getContext()); - return null; - } - }); } else { - requestBasicAuth(httpSession, response); + requestBasicAuth(request, response); return false; } + } catch (LoginException e) { + throw new CmsException("Could not login", e); } - // authenticate request - Authorization authorization = subject - .getPrivateCredentials(Authorization.class).iterator() - .next(); - request.setAttribute(AUTHORIZATION, authorization); - return true; } @Override @@ -277,6 +209,62 @@ class DataHttp implements KernelConstants, ArgeoJcrConstants { return null; } + private void requestBasicAuth(HttpServletRequest request, + HttpServletResponse response) { + response.setStatus(401); + response.setHeader(HEADER_WWW_AUTHENTICATE, "basic realm=\"" + + httpAuthRealm + "\""); + // request.getSession().setAttribute(ATTR_AUTH, Boolean.TRUE); + } + + private CallbackHandler basicAuth(final HttpServletRequest httpRequest) { + String authHeader = httpRequest.getHeader(HEADER_AUTHORIZATION); + if (authHeader != null) { + StringTokenizer st = new StringTokenizer(authHeader); + if (st.hasMoreTokens()) { + String basic = st.nextToken(); + if (basic.equalsIgnoreCase("Basic")) { + try { + // TODO manipulate char[] + String credentials = new String( + Base64.decodeBase64(st.nextToken()), + "UTF-8"); + // log.debug("Credentials: " + credentials); + int p = credentials.indexOf(":"); + if (p != -1) { + final String login = credentials + .substring(0, p).trim(); + final char[] password = credentials + .substring(p + 1).trim().toCharArray(); + return new CallbackHandler() { + public void handle(Callback[] callbacks) { + for (Callback cb : callbacks) { + if (cb instanceof NameCallback) + ((NameCallback) cb) + .setName(login); + else if (cb instanceof PasswordCallback) + ((PasswordCallback) cb) + .setPassword(password); + else if (cb instanceof HttpRequestCallback) + ((HttpRequestCallback) cb) + .setRequest(httpRequest); + } + } + }; + } else { + throw new CmsException( + "Invalid authentication token"); + } + } catch (Exception e) { + throw new CmsException( + "Couldn't retrieve authentication", e); + } + } + } + } + return null; + } + } /** diff --git a/org.argeo.cms/src/org/argeo/cms/internal/kernel/Kernel.java b/org.argeo.cms/src/org/argeo/cms/internal/kernel/Kernel.java index 32672bef5..68b3fa289 100644 --- a/org.argeo.cms/src/org/argeo/cms/internal/kernel/Kernel.java +++ b/org.argeo.cms/src/org/argeo/cms/internal/kernel/Kernel.java @@ -106,6 +106,8 @@ final class Kernel implements KernelConstants, ServiceListener { Kernel.class.getClassLoader()); long begin = System.currentTimeMillis(); + // KernelUtils.logFrameworkProperties(log); + try { // Initialise services logger = new NodeLogger(); diff --git a/org.argeo.cms/src/org/argeo/cms/internal/kernel/KernelUtils.java b/org.argeo.cms/src/org/argeo/cms/internal/kernel/KernelUtils.java index c1e208924..28eb52695 100644 --- a/org.argeo.cms/src/org/argeo/cms/internal/kernel/KernelUtils.java +++ b/org.argeo.cms/src/org/argeo/cms/internal/kernel/KernelUtils.java @@ -16,6 +16,8 @@ import javax.servlet.http.HttpServletRequest; import org.apache.commons.logging.Log; import org.argeo.cms.CmsException; import org.argeo.cms.auth.AuthConstants; +import org.osgi.framework.BundleContext; +import org.osgi.framework.Constants; /** Package utilities */ class KernelUtils implements KernelConstants { @@ -128,6 +130,19 @@ class KernelUtils implements KernelConstants { } } + static void logFrameworkProperties(Log log) { + BundleContext bc = Activator.getBundleContext(); + String[] keys = { Constants.FRAMEWORK_STORAGE, + Constants.FRAMEWORK_OS_NAME, Constants.FRAMEWORK_OS_VERSION, + Constants.FRAMEWORK_PROCESSOR, Constants.FRAMEWORK_SECURITY, + Constants.FRAMEWORK_TRUST_REPOSITORIES, + Constants.FRAMEWORK_WINDOWSYSTEM, Constants.FRAMEWORK_VENDOR, + Constants.FRAMEWORK_VERSION, Constants.FRAMEWORK_STORAGE_CLEAN, + Constants.FRAMEWORK_LANGUAGE, Constants.FRAMEWORK_UUID }; + for (String key : keys) + log.debug(key + "=" + bc.getProperty(key)); + } + private KernelUtils() { } 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 bc74cf638..5e1f39acb 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 @@ -1,43 +1,20 @@ package org.argeo.cms.internal.kernel; -import static org.argeo.cms.auth.AuthConstants.ACCESS_CONTROL_CONTEXT; - import java.io.IOException; -import java.security.AccessControlContext; -import java.security.AccessController; -import java.security.PrivilegedActionException; -import java.security.PrivilegedExceptionAction; import java.security.cert.X509Certificate; import java.util.Enumeration; -import java.util.Properties; -import java.util.StringTokenizer; -import javax.jcr.Repository; -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.LoginContext; -import javax.security.auth.login.LoginException; import javax.servlet.FilterChain; -import javax.servlet.Servlet; import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpSession; -import org.apache.commons.codec.binary.Base64; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.argeo.cms.CmsException; -import org.argeo.cms.auth.AuthConstants; -import org.argeo.jackrabbit.servlet.OpenInViewSessionProvider; -import org.argeo.jackrabbit.servlet.RemotingServlet; -import org.argeo.jackrabbit.servlet.WebdavServlet; import org.argeo.jcr.ArgeoJcrConstants; import org.eclipse.equinox.http.servlet.ExtendedHttpService; -import org.osgi.service.http.NamespaceException; /** * Intercepts and enriches http access, mainly focusing on security and @@ -47,159 +24,25 @@ import org.osgi.service.http.NamespaceException; class NodeHttp implements KernelConstants, ArgeoJcrConstants { private final static Log log = LogFactory.getLog(NodeHttp.class); - private final static String ATTR_AUTH = "auth"; - private final static String HEADER_AUTHORIZATION = "Authorization"; - private final static String HEADER_WWW_AUTHENTICATE = "WWW-Authenticate"; - - // private final AuthenticationManager authenticationManager; - private final ExtendedHttpService httpService; - - // FIXME Make it more unique - private String httpAuthRealm = "Argeo"; - // Filters private final RootFilter rootFilter; + // private final DoSFilter dosFilter; // private final QoSFilter qosFilter; - // WebDav / JCR remoting - private OpenInViewSessionProvider sessionProvider; - - NodeHttp(ExtendedHttpService httpService, NodeRepository node) { - // this.bundleContext = bundleContext; - // this.authenticationManager = authenticationManager; - - this.httpService = httpService; - - // Filters + NodeHttp(ExtendedHttpService httpService) { rootFilter = new RootFilter(); // dosFilter = new CustomDosFilter(); // qosFilter = new QoSFilter(); - // DAV - sessionProvider = new OpenInViewSessionProvider(); - - registerRepositoryServlets(ALIAS_NODE, node); try { httpService.registerFilter("/", rootFilter, null, null); } catch (Exception e) { - throw new CmsException("Could not register root filter", e); + throw new CmsException("Cannot register filters", e); } } public void destroy() { - sessionProvider.destroy(); - unregisterRepositoryServlets(ALIAS_NODE); - } - - void registerRepositoryServlets(String alias, Repository repository) { - try { - registerWebdavServlet(alias, repository, true); - registerWebdavServlet(alias, repository, false); - registerRemotingServlet(alias, repository, true); - registerRemotingServlet(alias, repository, false); - } catch (Exception e) { - throw new CmsException( - "Could not register servlets for repository " + alias, e); - } - } - - void unregisterRepositoryServlets(String alias) { - // FIXME unregister servlets - } - - void registerWebdavServlet(String alias, Repository repository, - boolean anonymous) throws NamespaceException, ServletException { - WebdavServlet webdavServlet = new WebdavServlet(repository, - sessionProvider); - String pathPrefix = anonymous ? WEBDAV_PUBLIC : WEBDAV_PRIVATE; - String path = pathPrefix + "/" + alias; - Properties ip = new Properties(); - ip.setProperty(WebdavServlet.INIT_PARAM_RESOURCE_CONFIG, WEBDAV_CONFIG); - ip.setProperty(WebdavServlet.INIT_PARAM_RESOURCE_PATH_PREFIX, path); - httpService.registerFilter(path, anonymous ? new AnonymousFilter() - : new DavFilter(), null, null); - // Cast to servlet because of a weird behaviour in Eclipse - httpService.registerServlet(path, (Servlet) webdavServlet, ip, null); - } - - void registerRemotingServlet(String alias, Repository repository, - boolean anonymous) throws NamespaceException, ServletException { - String pathPrefix = anonymous ? REMOTING_PUBLIC : REMOTING_PRIVATE; - RemotingServlet remotingServlet = new RemotingServlet(repository, - sessionProvider); - String path = pathPrefix + "/" + alias; - Properties ip = new Properties(); - ip.setProperty(RemotingServlet.INIT_PARAM_RESOURCE_PATH_PREFIX, path); - - // Looks like a bug in Jackrabbit remoting init - ip.setProperty(RemotingServlet.INIT_PARAM_HOME, - KernelUtils.getOsgiInstanceDir() + "/tmp/jackrabbit"); - ip.setProperty(RemotingServlet.INIT_PARAM_TMP_DIRECTORY, "remoting"); - // in order to avoid annoying warning. - ip.setProperty(RemotingServlet.INIT_PARAM_PROTECTED_HANDLERS_CONFIG, - ""); - // Cast to servlet because of a weird behaviour in Eclipse - httpService.registerFilter(path, anonymous ? new AnonymousFilter() - : new DavFilter(), null, null); - 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 void requestBasicAuth(HttpSession httpSession, - HttpServletResponse response) { - response.setStatus(401); - response.setHeader(HEADER_WWW_AUTHENTICATE, "basic realm=\"" - + httpAuthRealm + "\""); - httpSession.setAttribute(ATTR_AUTH, Boolean.TRUE); - } - - private CallbackHandler basicAuth(String authHeader) { - if (authHeader != null) { - StringTokenizer st = new StringTokenizer(authHeader); - if (st.hasMoreTokens()) { - String basic = st.nextToken(); - if (basic.equalsIgnoreCase("Basic")) { - try { - // TODO manipulate char[] - String credentials = new String(Base64.decodeBase64(st - .nextToken()), "UTF-8"); - // log.debug("Credentials: " + credentials); - int p = credentials.indexOf(":"); - if (p != -1) { - final String login = credentials.substring(0, p) - .trim(); - final char[] password = credentials - .substring(p + 1).trim().toCharArray(); - - return new CallbackHandler() { - public void handle(Callback[] callbacks) { - for (Callback cb : callbacks) { - if (cb instanceof NameCallback) - ((NameCallback) cb).setName(login); - else if (cb instanceof PasswordCallback) - ((PasswordCallback) cb) - .setPassword(password); - } - } - }; - } else { - throw new CmsException( - "Invalid authentication token"); - } - } catch (Exception e) { - throw new CmsException( - "Couldn't retrieve authentication", e); - } - } - } - } - throw new CmsException("Couldn't retrieve authentication"); } /** Intercepts all requests. Authenticates. */ @@ -293,96 +136,6 @@ class NodeHttp implements KernelConstants, ArgeoJcrConstants { return null; } - /** Intercepts all requests. Authenticates. */ - private class AnonymousFilter extends HttpFilter { - @Override - public void doFilter(HttpSession httpSession, - final HttpServletRequest request, - final HttpServletResponse response, - final FilterChain filterChain) throws IOException, - ServletException { - - // Authenticate from session - // if (isSessionAuthenticated(httpSession)) { - // filterChain.doFilter(request, response); - // return; - // } - - Subject subject = KernelUtils.anonymousLogin(); - try { - Subject.doAs(subject, new PrivilegedExceptionAction() { - public Void run() throws IOException, ServletException { - filterChain.doFilter(request, response); - return null; - } - }); - } catch (PrivilegedActionException e) { - if (e.getCause() instanceof ServletException) - throw (ServletException) e.getCause(); - else if (e.getCause() instanceof IOException) - throw (IOException) e.getCause(); - else - throw new CmsException("Unexpected exception", e.getCause()); - } - } - } - - /** Intercepts all requests. Authenticates. */ - private class DavFilter extends HttpFilter { - - @Override - public void doFilter(final HttpSession httpSession, - final HttpServletRequest request, - final HttpServletResponse response, - final FilterChain filterChain) throws IOException, - ServletException { - - AccessControlContext acc = (AccessControlContext) httpSession - .getAttribute(AuthConstants.ACCESS_CONTROL_CONTEXT); - final Subject subject; - if (acc != null) { - subject = Subject.getSubject(acc); - } else { - // Process basic auth - String basicAuth = request.getHeader(HEADER_AUTHORIZATION); - if (basicAuth != null) { - CallbackHandler token = basicAuth(basicAuth); - try { - LoginContext lc = new LoginContext( - AuthConstants.LOGIN_CONTEXT_USER, token); - lc.login(); - subject = lc.getSubject(); - } catch (LoginException e) { - throw new CmsException("Could not login", e); - } - } else { - requestBasicAuth(httpSession, response); - return; - } - } - // do filter as subject - try { - Subject.doAs(subject, new PrivilegedExceptionAction() { - public Void run() throws IOException, ServletException { - // add security context to session - httpSession.setAttribute(ACCESS_CONTROL_CONTEXT, - AccessController.getContext()); - filterChain.doFilter(request, response); - return null; - } - }); - } catch (PrivilegedActionException e) { - if (e.getCause() instanceof ServletException) - throw (ServletException) e.getCause(); - else if (e.getCause() instanceof IOException) - throw (IOException) e.getCause(); - else - throw new CmsException("Unexpected exception", e.getCause()); - } - - } - } - // class CustomDosFilter extends DoSFilter { // @Override // protected String extractUserId(ServletRequest request) { 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 index 90c68bd21..e5e3be5cd 100644 --- a/org.argeo.cms/src/org/argeo/cms/internal/kernel/jaas.cfg +++ b/org.argeo.cms/src/org/argeo/cms/internal/kernel/jaas.cfg @@ -1,11 +1,9 @@ USER { - org.argeo.cms.auth.NodeContextLoginModule requisite; org.argeo.cms.auth.UserAdminLoginModule requisite; org.argeo.cms.auth.NodeUserLoginModule requisite; }; ANONYMOUS { - org.argeo.cms.auth.NodeContextLoginModule requisite; org.argeo.cms.auth.UserAdminLoginModule requisite anonymous=true; org.argeo.cms.auth.NodeUserLoginModule requisite; }; diff --git a/org.argeo.cms/src/org/argeo/cms/util/CmsUtils.java b/org.argeo.cms/src/org/argeo/cms/util/CmsUtils.java index 439f48af1..6e895818a 100644 --- a/org.argeo.cms/src/org/argeo/cms/util/CmsUtils.java +++ b/org.argeo.cms/src/org/argeo/cms/util/CmsUtils.java @@ -11,6 +11,7 @@ import org.apache.commons.io.IOUtils; import org.argeo.cms.CmsConstants; import org.argeo.cms.CmsException; import org.argeo.cms.CmsView; +import org.argeo.eclipse.ui.specific.UiContext; import org.argeo.jcr.JcrUtils; import org.eclipse.rap.rwt.RWT; import org.eclipse.rap.rwt.service.ResourceManager; @@ -34,13 +35,7 @@ public class CmsUtils implements CmsConstants { * this call. */ public static CmsView getCmsView() { - Display display = Display.getCurrent(); - if (display == null) - return null; - CmsView cmsView = (CmsView) display.getData(CmsView.KEY); - if (cmsView == null) - return null; - return cmsView; + return UiContext.getData(CmsView.KEY); } /** @deprecated Use rowData16px() instead. GridData should not be reused. */ diff --git a/org.argeo.cms/src/org/argeo/cms/util/CurrentUserUtils.java b/org.argeo.cms/src/org/argeo/cms/util/CurrentUserUtils.java deleted file mode 100644 index 0ebac9dcc..000000000 --- a/org.argeo.cms/src/org/argeo/cms/util/CurrentUserUtils.java +++ /dev/null @@ -1,67 +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.util; - -import java.security.AccessController; -import java.security.Principal; -import java.security.acl.Group; -import java.util.Collections; -import java.util.HashSet; -import java.util.Set; - -import javax.security.auth.Subject; -import javax.security.auth.x500.X500Principal; - -import org.argeo.ArgeoException; - -/** - * Retrieves information about the current user. Not an API, can change without - * notice. - */ -class CurrentUserUtils { - public final static String getUsername() { - Subject subject = getSubject(); - if (subject == null) - return null; - Principal principal = subject.getPrincipals(X500Principal.class) - .iterator().next(); - return principal.getName(); - - } - - public final static Set roles() { - Set roles = Collections.synchronizedSet(new HashSet()); - // roles.add("ROLE_USER"); - Subject subject = getSubject(); - X500Principal userPrincipal = subject - .getPrincipals(X500Principal.class).iterator().next(); - roles.add(userPrincipal.getName()); - for (Principal group : subject.getPrincipals(Group.class)) { - roles.add(group.getName()); - } - return roles; - } - - public final static Subject getSubject() { - Subject subject = Subject.getSubject(AccessController.getContext()); - if (subject == null) { - subject = CmsUtils.getCmsView().getSubject(); - if (subject == null) - throw new ArgeoException("Not authenticated."); - } - return subject; - } -} 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 72289cb3e..ed8f20c0b 100644 --- a/org.argeo.cms/src/org/argeo/cms/util/UserMenu.java +++ b/org.argeo.cms/src/org/argeo/cms/util/UserMenu.java @@ -1,12 +1,9 @@ package org.argeo.cms.util; -import static org.argeo.cms.auth.AuthConstants.ACCESS_CONTROL_CONTEXT; import static org.argeo.cms.auth.AuthConstants.LOGIN_CONTEXT_ANONYMOUS; import static org.argeo.cms.auth.AuthConstants.LOGIN_CONTEXT_USER; import java.io.IOException; -import java.security.AccessController; -import java.security.PrivilegedAction; import javax.security.auth.Subject; import javax.security.auth.callback.Callback; @@ -16,8 +13,6 @@ 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 javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpSession; import org.argeo.cms.CmsException; import org.argeo.cms.CmsMsg; @@ -25,6 +20,7 @@ import org.argeo.cms.CmsStyles; import org.argeo.cms.CmsView; import org.argeo.cms.auth.AuthConstants; import org.argeo.cms.auth.CurrentUser; +import org.argeo.cms.auth.HttpRequestCallback; import org.eclipse.rap.rwt.RWT; import org.eclipse.swt.SWT; import org.eclipse.swt.events.MouseAdapter; @@ -59,8 +55,7 @@ public class UserMenu implements CmsStyles, CallbackHandler { cmsView = CmsUtils.getCmsView(); if (cmsView != null) { - String username = CurrentUser.getUsername(CmsUtils.getCmsView() - .getSubject()); + String username = CurrentUser.getUsername(cmsView.getSubject()); if (username == null || username.equalsIgnoreCase(AuthConstants.ROLE_ANONYMOUS)) { username = null; @@ -178,67 +173,42 @@ public class UserMenu implements CmsStyles, CallbackHandler { protected void login() { Subject subject = cmsView.getSubject(); + LoginContext loginContext; try { // // LOGIN // new LoginContext(LOGIN_CONTEXT_ANONYMOUS, subject).logout(); - LoginContext loginContext = new LoginContext(LOGIN_CONTEXT_USER, - subject, this); + loginContext = new LoginContext(LOGIN_CONTEXT_USER, subject, this); loginContext.login(); - - // save context in session - final HttpSession httpSession = RWT.getRequest().getSession(); - Subject.doAs(subject, new PrivilegedAction() { - - @Override - public Void run() { - httpSession.setAttribute(ACCESS_CONTROL_CONTEXT, - AccessController.getContext()); - return null; - } - }); } catch (LoginException e1) { - try { - new LoginContext(LOGIN_CONTEXT_ANONYMOUS, subject).login(); - } catch (LoginException e) { - throw new CmsException("Cannot authenticate anonymous", e1); - } throw new CmsException("Cannot authenticate", e1); } closeShell(); - cmsView.authChange(); + cmsView.authChange(loginContext); } protected void logout() { - Subject subject = cmsView.getSubject(); - try { - // - // LOGOUT - // - new LoginContext(LOGIN_CONTEXT_USER, subject).logout(); - new LoginContext(LOGIN_CONTEXT_ANONYMOUS, subject).login(); - - HttpServletRequest httpRequest = RWT.getRequest(); - HttpSession httpSession = httpRequest.getSession(); - httpSession.setAttribute(ACCESS_CONTROL_CONTEXT, null); - } catch (LoginException e1) { - throw new CmsException("Cannot authenticate anonymous", e1); - } closeShell(); + cmsView.logout(); cmsView.navigateTo("~"); - cmsView.authChange(); } @Override public void handle(Callback[] callbacks) throws IOException, UnsupportedCallbackException { - ((NameCallback) callbacks[0]).setName(username.getText()); - ((PasswordCallback) callbacks[1]).setPassword(password.getTextChars()); + for (Callback callback : callbacks) { + if (callback instanceof NameCallback) + ((NameCallback) callback).setName(username.getText()); + else if (callback instanceof PasswordCallback) + ((PasswordCallback) callback).setPassword(password + .getTextChars()); + else if (callback instanceof HttpRequestCallback) + ((HttpRequestCallback) callback).setRequest(RWT.getRequest()); + } } public Shell getShell() { return shell; } - } diff --git a/org.argeo.cms/src/org/argeo/cms/util/UserMenuLink.java b/org.argeo.cms/src/org/argeo/cms/util/UserMenuLink.java index afab928aa..e8fd556bf 100644 --- a/org.argeo.cms/src/org/argeo/cms/util/UserMenuLink.java +++ b/org.argeo.cms/src/org/argeo/cms/util/UserMenuLink.java @@ -1,6 +1,7 @@ package org.argeo.cms.util; import javax.jcr.Node; +import javax.security.auth.Subject; import org.argeo.cms.CmsMsg; import org.argeo.cms.CmsStyles; @@ -23,13 +24,12 @@ public class UserMenuLink extends MenuLink { @Override public Control createUi(Composite parent, Node context) { - String username = CurrentUser.getUsername(CmsUtils.getCmsView() - .getSubject()); + Subject subject = CmsUtils.getCmsView().getSubject(); + String username = CurrentUser.getUsername(subject); if (username.equalsIgnoreCase(AuthConstants.ROLE_ANONYMOUS)) setLabel(CmsMsg.login.lead()); else { - setLabel(CurrentUser.getDisplayName(CmsUtils.getCmsView() - .getSubject())); + setLabel(CurrentUser.getDisplayName(subject)); } Label link = (Label) ((Composite) super.createUi(parent, context)) .getChildren()[0]; diff --git a/org.argeo.eclipse.ui.rap/src/org/argeo/eclipse/ui/specific/SingleSourcingException.java b/org.argeo.eclipse.ui.rap/src/org/argeo/eclipse/ui/specific/SingleSourcingException.java new file mode 100644 index 000000000..9b7569066 --- /dev/null +++ b/org.argeo.eclipse.ui.rap/src/org/argeo/eclipse/ui/specific/SingleSourcingException.java @@ -0,0 +1,15 @@ +package org.argeo.eclipse.ui.specific; + +/** Exception related to SWT/RWT single sourcing. */ +public class SingleSourcingException extends RuntimeException { + private static final long serialVersionUID = -727700418055348468L; + + public SingleSourcingException(String message, Throwable cause) { + super(message, cause); + } + + public SingleSourcingException(String message) { + super(message); + } + +} diff --git a/org.argeo.eclipse.ui.rap/src/org/argeo/eclipse/ui/specific/UiContext.java b/org.argeo.eclipse.ui.rap/src/org/argeo/eclipse/ui/specific/UiContext.java new file mode 100644 index 000000000..ce50873f8 --- /dev/null +++ b/org.argeo.eclipse.ui.rap/src/org/argeo/eclipse/ui/specific/UiContext.java @@ -0,0 +1,39 @@ +package org.argeo.eclipse.ui.specific; + +import javax.servlet.http.HttpServletRequest; + +import org.eclipse.rap.rwt.RWT; +import org.eclipse.swt.widgets.Display; + +/** Singleton class providing single sources infos about the UI context. */ +public class UiContext { + + public static HttpServletRequest getHttpRequest() { + return RWT.getRequest(); + } + + /** Can always be null */ + @SuppressWarnings("unchecked") + public static T getData(String key) { + Display display = getDisplay(); + if (display == null) + return null; + return (T) display.getData(key); + } + + public static void setData(String key, Object value) { + Display display = getDisplay(); + if (display == null) + throw new SingleSourcingException( + "Not display available in RAP context"); + display.setData(key, value); + } + + private static Display getDisplay() { + return Display.getCurrent(); + } + + private UiContext() { + } + +} diff --git a/org.argeo.security.ui.rap/src/org/argeo/security/ui/rap/RapLoginEntryPoint.java b/org.argeo.security.ui.rap/src/org/argeo/security/ui/rap/RapLoginEntryPoint.java index 3fac3eba7..bb97b6549 100644 --- a/org.argeo.security.ui.rap/src/org/argeo/security/ui/rap/RapLoginEntryPoint.java +++ b/org.argeo.security.ui.rap/src/org/argeo/security/ui/rap/RapLoginEntryPoint.java @@ -1,6 +1,9 @@ package org.argeo.security.ui.rap; +import javax.servlet.http.HttpServletRequest; + import org.argeo.security.ui.login.WorkbenchLogin; +import org.eclipse.rap.rwt.RWT; import org.eclipse.swt.widgets.Display; import org.eclipse.ui.PlatformUI; @@ -17,4 +20,7 @@ public class RapLoginEntryPoint extends WorkbenchLogin { return new RapWorkbenchAdvisor(username); } + protected HttpServletRequest getRequest() { + return RWT.getRequest(); + } } diff --git a/org.argeo.security.ui/src/org/argeo/security/ui/login/WorkbenchLogin.java b/org.argeo.security.ui/src/org/argeo/security/ui/login/WorkbenchLogin.java index 6a4e879e2..0eee6745c 100644 --- a/org.argeo.security.ui/src/org/argeo/security/ui/login/WorkbenchLogin.java +++ b/org.argeo.security.ui/src/org/argeo/security/ui/login/WorkbenchLogin.java @@ -3,11 +3,20 @@ package org.argeo.security.ui.login; import java.security.PrivilegedAction; import javax.security.auth.Subject; +import javax.security.auth.login.CredentialNotFoundException; +import javax.security.auth.login.LoginContext; +import javax.security.auth.login.LoginException; +import javax.servlet.http.HttpServletRequest; +import org.argeo.ArgeoException; +import org.argeo.cms.CmsException; import org.argeo.cms.CmsImageManager; import org.argeo.cms.CmsView; +import org.argeo.cms.auth.AuthConstants; import org.argeo.cms.auth.CurrentUser; +import org.argeo.cms.auth.HttpRequestCallbackHandler; import org.argeo.cms.util.UserMenu; +import org.argeo.eclipse.ui.specific.UiContext; import org.eclipse.rap.rwt.application.EntryPoint; import org.eclipse.swt.SWT; import org.eclipse.swt.widgets.Display; @@ -15,20 +24,31 @@ import org.eclipse.swt.widgets.Shell; import org.eclipse.ui.PlatformUI; public abstract class WorkbenchLogin implements EntryPoint, CmsView { + // private final static Log log = LogFactory.getLog(WorkbenchLogin.class); private final Subject subject = new Subject(); + private LoginContext loginContext; @Override public int createUI() { final Display display = PlatformUI.createDisplay(); - display.setData(CmsView.KEY, this); - Shell shell = new Shell(display, SWT.NO_TRIM); - shell.setMaximized(true); - UserMenu userMenu = new UserMenu(shell, false); - shell.open(); - while (!userMenu.getShell().isDisposed()) { - if (!display.readAndDispatch()) { - display.sleep(); + UiContext.setData(CmsView.KEY, this); + try { + loginContext = new LoginContext(AuthConstants.LOGIN_CONTEXT_USER, + subject, new HttpRequestCallbackHandler(getRequest())); + loginContext.login(); + } catch (CredentialNotFoundException e) { + Shell shell = new Shell(display, SWT.NO_TRIM); + shell.setMaximized(true); + //shell.setBackground(display.getSystemColor(SWT.COLOR_CYAN)); + UserMenu userMenu = new UserMenu(shell, false); + shell.open(); + while (!userMenu.getShell().isDisposed()) { + if (!display.readAndDispatch()) { + display.sleep(); + } } + } catch (LoginException e) { + throw new ArgeoException("Cannot log in", e); } // // RUN THE WORKBENCH @@ -45,14 +65,36 @@ public abstract class WorkbenchLogin implements EntryPoint, CmsView { } finally { display.dispose(); } + // explicit workbench closing + logout(); return returnCode; - // display.dispose(); - // return 0; } protected abstract int createAndRunWorkbench(Display display, String username); + // private void fullLogout() { + // String username = CurrentUser.getUsername(subject); + // try { + // LoginContext loginContext = new LoginContext( + // AuthConstants.LOGIN_CONTEXT_USER, subject); + // loginContext.logout(); + // HttpServletRequest httpRequest = getRequest(); + // httpRequest.setAttribute(HttpContext.AUTHORIZATION, null); + // HttpSession httpSession = httpRequest.getSession(); + // httpSession.setAttribute(HttpContext.AUTHORIZATION, null); + // httpSession.setMaxInactiveInterval(1); + // log.info("Logged out " + (username != null ? username : "") + // + " (THREAD=" + Thread.currentThread().getId() + ")"); + // } catch (LoginException e) { + // log.error("Error when logging out", e); + // } + // } + + protected HttpServletRequest getRequest() { + return UiContext.getHttpRequest(); + } + @Override public void navigateTo(String state) { // TODO Auto-generated method stub @@ -60,7 +102,19 @@ public abstract class WorkbenchLogin implements EntryPoint, CmsView { } @Override - public void authChange() { + public void authChange(LoginContext loginContext) { + this.loginContext = loginContext; + } + + @Override + public void logout() { + if (loginContext == null) + throw new CmsException("Login context should not bet null"); + try { + loginContext.logout(); + } catch (LoginException e) { + throw new CmsException("Cannot log out", e); + } } @Override