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.
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 "/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;
}
}