Written by massi
As you can imagine this post is about Single Sign On and HippoCMS. In particular, at the end of this post, we will be able to deploy Hippo CMS in Single Sign On in our environment.
First of all we need an authentication manager representing the entry point in our environment. Its purpose, besides the obvious management of the authentication mechanism, is to set an http header attribute with authenticated username. This task is achievable in many different ways because the mechanism is always the same; personally I tried this scenario with:
<location "/blog/cms"> AuthType CAS Require valid-user CASScope / ProxyPass http://xxx.tirasa.net:8080/cms ProxyPassReverse http://xxx.tirasa.net:8080/cms ProxyHTMLExtended On ProxyHTMLURLMap /cms /cms ProxyPassReverseCookiePath / / RewriteEngine On RewriteCond %{LA-U:REMOTE_USER} (.+) RewriteRule . - [E=RU:%1,NS] RequestHeader add X-Forwarded-User %{RU}e </location>
In this case the authentication on a Windows PC was the entry point so, to obtain the SSO in HippoCMS, we have to add a CAS filter to it. Trying to use the CMS, CAS filter puts the username of the authenticated user into the http-request RemoteUser attribute.
In order to et confidence with the authentication and the authorization in HippoCMS I suggest you to read this post on hippo site.
Unfortunately following the Hippo documentation I could not achieve the expected result. So, from my point of view, this is the official practice guide to get SSO in HippoCMS:
Jackrabbit { net.tirasa.hippo.cms.sso.SSOModule optional; org.hippoecm.repository.security.JAASLoginModule required preAuthorized=true; };
package net.tirasa.hippo.cms.sso; import java.io.IOException; import java.util.Arrays; import java.util.List; import java.util.Locale; 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.servlet.http.Cookie; import org.apache.jackrabbit.core.security.authentication.CredentialsCallback; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.apache.wicket.RequestCycle; import org.apache.wicket.ajax.AjaxRequestTarget; import org.apache.wicket.ajax.form.AjaxFormComponentUpdatingBehavior; import org.apache.wicket.markup.html.basic.Label; import org.apache.wicket.markup.html.form.DropDownChoice; import org.apache.wicket.markup.html.panel.FeedbackPanel; import org.apache.wicket.model.PropertyModel; import org.apache.wicket.protocol.http.WebRequest; import org.apache.wicket.protocol.http.WebResponse; import org.apache.wicket.PageParameters; import org.hippoecm.frontend.PluginPage; import org.hippoecm.frontend.model.UserCredentials; import org.hippoecm.frontend.plugin.IPluginContext; import org.hippoecm.frontend.plugin.config.IPluginConfig; import org.hippoecm.frontend.plugins.login.LoginPlugin; import org.hippoecm.frontend.service.render.RenderPlugin; import org.hippoecm.frontend.session.LoginException; import org.hippoecm.frontend.session.PluginUserSession; import org.hippoecm.frontend.util.WebApplicationHelper; import org.hippoecm.repository.WebCredentials; public class SSOPlugin extends RenderPlugin implements CallbackHandler { private static final long serialVersionUID = 6971843172794119352L; private static final Logger log = LoggerFactory.getLogger(SSOPlugin.class); @SuppressWarnings("unused") private final static String SVN_ID = "$Id$"; private static final String LOCALE_COOKIE = "loc"; private DropDownChoice locale; public String selectedLocale; public SSOPlugin(final IPluginContext context, final IPluginConfig config) throws LoginException { super(context, config); fromOfficialDocs(); login(); } private void login() throws LoginException { final PluginUserSession userSession = (PluginUserSession) getSession(); userSession.login(new UserCredentials(this)); userSession.setLocale(new Locale(selectedLocale)); userSession.getJcrSession(); setResponsePage(PluginPage.class, new PageParameters(RequestCycle.get().getRequest().getParameterMap())); } private void fromOfficialDocs() { String[] localeArray = getPluginConfig().getStringArray("locales"); if (localeArray == null) { localeArray = LoginPlugin.LOCALES; } final List locales = Arrays.asList(localeArray); // by default, use the user's browser settings for the locale selectedLocale = "en"; if (locales.contains(getSession().getLocale().getLanguage())) { selectedLocale = getSession().getLocale().getLanguage(); } // check if user has previously selected a locale Cookie[] cookies = ((WebRequest) RequestCycle.get().getRequest()).getHttpServletRequest().getCookies(); if (cookies != null) { for (Cookie cookie : cookies) { if (LOCALE_COOKIE.equals(cookie.getName())) { if (locales.contains(cookie.getValue())) { selectedLocale = cookie.getValue(); getSession().setLocale(new Locale(selectedLocale)); } } } } add(locale = new DropDownChoice("locale", new PropertyModel(this, "selectedLocale"), locales)); locale.add(new AjaxFormComponentUpdatingBehavior("onchange") { private static final long serialVersionUID = -1107858522700306810L; @Override protected void onUpdate(AjaxRequestTarget target) { //immediately set the locale when the user changes it Cookie localeCookie = new Cookie(LOCALE_COOKIE, selectedLocale); localeCookie.setMaxAge(365 * 24 * 3600); // expire one year from now ((WebResponse) RequestCycle.get().getResponse()).addCookie(localeCookie); getSession().setLocale(new Locale(selectedLocale)); setResponsePage(this.getFormComponent().getPage()); } }); add(new FeedbackPanel("feedback").setEscapeModelStrings(false)); add(new Label("pinger")); } @Override public void handle(Callback[] callbacks) throws IOException, UnsupportedCallbackException { for (Callback callback : callbacks) { if (callback instanceof NameCallback) { NameCallback nameCallback = (NameCallback) callback; String username = (String) WebApplicationHelper.retrieveWebRequest().getHttpServletRequest().getRemoteUser(); if (username != null) { nameCallback.setName(username); } } else if (callback instanceof PasswordCallback) { } else if (callback instanceof CredentialsCallback) { CredentialsCallback credentialsCallback = (CredentialsCallback) callback; credentialsCallback.setCredentials( new WebCredentials(((WebRequest) getRequest()).getHttpServletRequest())); } } } }
package net.tirasa.hippo.cms.sso; 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.apache.wicket.RequestCycle; import org.hippoecm.frontend.util.WebApplicationHelper; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class SSOModule implements LoginModule { private static final Logger log = LoggerFactory.getLogger(SSOModule.class); private final boolean validLogin = true; @Override public void initialize(final Subject subject, final CallbackHandler callbackHandler, final Map sharedState, final Map options) { if (RequestCycle.get() != null) { final String session = WebApplicationHelper.retrieveWebRequest().getHttpServletRequest().getHeader("X-Forwarded-User"); ((Map) sharedState).put("javax.security.auth.login.name", session != null ? session : "anonymous"); log.debug("Set username with {}", session != null ? session : "anonymous"); } } @Override public boolean login() throws LoginException { log.debug("LOGIN"); return validLogin; } protected String validate(final String key) { log.debug("VALIDATE"); return key; } @Override public boolean commit() throws LoginException { log.debug("COMMIT"); return validLogin; } @Override public boolean abort() throws LoginException { log.debug("ABORT"); return validLogin; } @Override public boolean logout() throws LoginException { log.debug("LOGOUT"); return validLogin; } }