/**
* This file Copyright (c) 2003-2012 Magnolia International
* Ltd. (http://www.magnolia-cms.com). All rights reserved.
*
*
* This file is dual-licensed under both the Magnolia
* Network Agreement and the GNU General Public License.
* You may elect to use one or the other of these licenses.
*
* This file is distributed in the hope that it will be
* useful, but AS-IS and WITHOUT ANY WARRANTY; without even the
* implied warranty of MERCHANTABILITY or FITNESS FOR A
* PARTICULAR PURPOSE, TITLE, or NONINFRINGEMENT.
* Redistribution, except as permitted by whichever of the GPL
* or MNA you select, is prohibited.
*
* 1. For the GPL license (GPL), you can redistribute and/or
* modify this file under the terms of the GNU General
* Public License, Version 3, as published by the Free Software
* Foundation. You should have received a copy of the GNU
* General Public License, Version 3 along with this program;
* if not, write to the Free Software Foundation, Inc., 51
* Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* 2. For the Magnolia Network Agreement (MNA), this file
* and the accompanying materials are made available under the
* terms of the MNA which accompanies this distribution, and
* is available at http://www.magnolia-cms.com/mna.html
*
* Any modifications to this file must keep this entire header
* intact.
*
*/
package info.magnolia.jaas.sp.jcr;
import info.magnolia.cms.beans.config.ContentRepository;
import info.magnolia.cms.security.PrincipalUtil;
import info.magnolia.cms.security.User;
import info.magnolia.cms.security.UserManager;
import info.magnolia.context.Context;
import info.magnolia.context.MgnlContext;
import info.magnolia.init.MagnoliaConfigurationProperties;
import info.magnolia.objectfactory.Components;
import java.io.IOException;
import java.io.Serializable;
import java.util.Arrays;
import java.util.Map;
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.FailedLoginException;
import javax.security.auth.login.LoginException;
import javax.security.auth.spi.LoginModule;
import org.apache.jackrabbit.core.security.UserPrincipal;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Login module for internal Jackrabbit authentication, validates the JackRabbit 'admin' user and uses the Subject
* provided by the magnolia context.
*
* Note that Jackrabbit requires the login module to be serializable.
*
* @version $Id$
*/
public class JackrabbitAuthenticationModule implements LoginModule, Serializable {
private static final Logger log = LoggerFactory.getLogger(JackrabbitAuthenticationModule.class);
private Subject subject;
private CallbackHandler callbackHandler;
private String name;
@Override
public void initialize(Subject subject, CallbackHandler callbackHandler, Map<String, ?> sharedState, Map<String, ?> options) {
this.subject = subject;
this.callbackHandler = callbackHandler;
}
@Override
public boolean login() throws LoginException {
if (this.callbackHandler == null) {
throw new LoginException("Error: no CallbackHandler available");
}
Callback[] callbacks = new Callback[2];
callbacks[0] = new NameCallback("name");
callbacks[1] = new PasswordCallback("pswd", false);
char[] password;
try {
this.callbackHandler.handle(callbacks);
this.name = ((NameCallback) callbacks[0]).getName();
password = ((PasswordCallback) callbacks[1]).getPassword();
} catch (IOException ioe) {
throw new LoginException(ioe.toString());
} catch (UnsupportedCallbackException ce) {
throw new LoginException(ce.getCallback().toString() + " not available");
}
// When we log in to register workspaces and node types we do it as 'admin', we do this in SystemContext but we
// can't use the context here because it's bound to the system user which is configured in the repository and
// attempting to access it would fail. More specifically calling MgnlContext.getSubject() fails as a result of
// trying to use SecuritySupport.
if (getAdminUser().equals(this.name)) {
if (!Arrays.equals(password, getAdminPassword().toCharArray())) {
throw new FailedLoginException();
}
compileAdminPrincipals();
return true;
}
Context context = MgnlContext.hasInstance() ? MgnlContext.getInstance() : null;
if (context == null) {
throw new FailedLoginException("Cannot login, magnolia context is not set");
}
Subject magnoliaSubject = context.getSubject();
if (magnoliaSubject == null) {
throw new FailedLoginException("Cannot login, invalid setup or deserialization error");
}
if (isSuperuser(magnoliaSubject)) {
compileAdminPrincipals();
return true;
}
compileUserPrincipals(magnoliaSubject);
return true;
}
@Override
public boolean commit() throws LoginException {
return true;
}
@Override
public boolean abort() throws LoginException {
return false;
}
@Override
public boolean logout() throws LoginException {
callbackHandler = null;
name = null;
return true;
}
private void compileUserPrincipals(Subject magnoliaSubject) {
subject.getPrincipals().addAll(magnoliaSubject.getPrincipals());
subject.getPrincipals().add(new UserPrincipal(name));
}
private void compileAdminPrincipals() {
this.subject.getPrincipals().add(new MagnoliaJRAdminPrincipal(getAdminUser()));
}
protected String getAdminUser() {
String user = ContentRepository.REPOSITORY_USER;
if (user == null) {
MagnoliaConfigurationProperties mcp = Components.getSingleton(MagnoliaConfigurationProperties.class);
user = mcp.getProperty("magnolia.connection.jcr.userId");
}
if (user == null) {
user = System.getProperty("magnolia.connection.jcr.userId");
}
return user;
}
protected String getAdminPassword() {
String user = ContentRepository.REPOSITORY_PSWD;
if (user == null) {
MagnoliaConfigurationProperties mcp = Components.getSingleton(MagnoliaConfigurationProperties.class);
user = mcp.getProperty("magnolia.connection.jcr.password");
}
if (user == null) {
user = System.getProperty("magnolia.connection.jcr.password");
}
return user;
}
/**
* Returns true if the subject has a principal that represents the magnolia superuser.
*/
private boolean isSuperuser(Subject magnoliaSubject) {
User user = PrincipalUtil.findPrincipal(magnoliaSubject, User.class);
return user != null && UserManager.SYSTEM_USER.equals(user.getName());
}
}