/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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.apache.jackrabbit.core.security.authentication;
import org.apache.jackrabbit.api.security.authentication.token.TokenCredentials;
import org.apache.jackrabbit.core.RepositoryImpl;
import org.apache.jackrabbit.core.config.ConfigurationEntityResolver;
import org.apache.jackrabbit.core.config.ConfigurationErrorHandler;
import org.apache.jackrabbit.core.config.ConfigurationException;
import org.apache.jackrabbit.core.config.LoginModuleConfig;
import org.apache.jackrabbit.core.config.RepositoryConfig;
import org.apache.jackrabbit.core.config.RepositoryConfigurationParser;
import org.apache.jackrabbit.core.security.authentication.token.TokenBasedAuthentication;
import org.apache.jackrabbit.core.security.principal.FallbackPrincipalProvider;
import org.apache.jackrabbit.core.security.principal.ProviderRegistryImpl;
import org.apache.jackrabbit.test.AbstractJCRTest;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
import org.xml.sax.SAXParseException;
import javax.jcr.Credentials;
import javax.jcr.RepositoryException;
import javax.jcr.Session;
import javax.jcr.SimpleCredentials;
import javax.security.auth.Subject;
import javax.security.auth.callback.CallbackHandler;
import javax.security.auth.login.LoginException;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import java.io.IOException;
import java.io.StringReader;
import java.util.Properties;
/**
* <code>DefaultLoginModuleTest</code>...
*/
public class DefaultLoginModuleTest extends AbstractJCRTest {
private static final String DEFAULT_CONFIG =
"<Security appName=\"Jackrabbit\">" +
"<LoginModule class=\"org.apache.jackrabbit.core.security.authentication.DefaultLoginModule\">\n" +
" <param name=\"anonymousId\" value=\"anonymous\"/>\n" +
" <param name=\"adminId\" value=\"admin\"/>\n" +
"</LoginModule>" +
"</Security>";
private static final String DISABLE_TOKEN_CONFIG =
"<Security appName=\"Jackrabbit\">" +
"<LoginModule class=\"org.apache.jackrabbit.core.security.authentication.DefaultLoginModule\">\n" +
" <param name=\"anonymousId\" value=\"anonymous\"/>\n" +
" <param name=\"adminId\" value=\"admin\"/>\n" +
" <param name=\"disableTokenAuth\" value=\"true\"/>\n" +
"</LoginModule>" +
"</Security>";
private SimpleCredentials simpleCredentials = new SimpleCredentials("admin", "admin".toCharArray());
private Session securitySession;
@Override
protected void setUp() throws Exception {
super.setUp();
RepositoryConfig rc = ((RepositoryImpl) superuser.getRepository()).getConfig();
String workspaceName = rc.getSecurityConfig().getSecurityManagerConfig().getWorkspaceName();
if (workspaceName == null) {
workspaceName = rc.getDefaultWorkspaceName();
}
securitySession = getHelper().getSuperuserSession(workspaceName);
}
@Override
protected void cleanUp() throws Exception {
if (securitySession != null && securitySession.isLive()) {
securitySession.logout();
}
super.cleanUp();
}
public void testSimpleCredentialsLogin() throws Exception {
AuthContext ac = getAuthContext(simpleCredentials, DEFAULT_CONFIG);
ac.login();
ac.logout();
}
public void testSimpleCredentialsLoginLogout() throws Exception {
AuthContext ac = getAuthContext(simpleCredentials, DEFAULT_CONFIG);
ac.login();
Subject subject = ac.getSubject();
assertFalse(subject.getPrincipals().isEmpty());
assertFalse(subject.getPublicCredentials().isEmpty());
assertFalse(subject.getPublicCredentials(SimpleCredentials.class).isEmpty());
ac.logout();
assertTrue(subject.getPrincipals().isEmpty());
assertTrue(subject.getPublicCredentials().isEmpty());
assertTrue(subject.getPublicCredentials(SimpleCredentials.class).isEmpty());
}
public void testTokenCredentialsLoginLogout() throws Exception {
simpleCredentials.setAttribute(TokenBasedAuthentication.TOKEN_ATTRIBUTE, "");
try {
// login with simple credentials forcing token creation.
AuthContext ac = getAuthContext(simpleCredentials, DEFAULT_CONFIG);
ac.login();
Subject subject = ac.getSubject();
assertFalse(subject.getPrincipals().isEmpty());
assertFalse(subject.getPublicCredentials().isEmpty());
assertFalse(subject.getPublicCredentials(SimpleCredentials.class).isEmpty());
assertFalse(subject.getPublicCredentials(TokenCredentials.class).isEmpty());
assertEquals(2, subject.getPublicCredentials(Credentials.class).size());
TokenCredentials tokenCredentials = subject.getPublicCredentials(TokenCredentials.class).iterator().next();
ac.logout();
// second login with token credentials
ac = getAuthContext(tokenCredentials, DEFAULT_CONFIG);
ac.login();
subject = ac.getSubject();
assertFalse(subject.getPrincipals().isEmpty());
assertFalse(subject.getPublicCredentials().isEmpty());
assertFalse(subject.getPublicCredentials(SimpleCredentials.class).isEmpty());
assertFalse(subject.getPublicCredentials(TokenCredentials.class).isEmpty());
assertEquals(2, subject.getPublicCredentials(Credentials.class).size());
ac.logout();
assertTrue(subject.getPrincipals().isEmpty());
assertTrue(subject.getPublicCredentials().isEmpty());
assertTrue(subject.getPublicCredentials(SimpleCredentials.class).isEmpty());
assertTrue(subject.getPublicCredentials(TokenCredentials.class).isEmpty());
} finally {
simpleCredentials.removeAttribute(TokenBasedAuthentication.TOKEN_ATTRIBUTE);
}
}
public void testDisabledTokenCredentials() throws Exception {
simpleCredentials.setAttribute(TokenBasedAuthentication.TOKEN_ATTRIBUTE, "");
try {
AuthContext ac = getAuthContext(simpleCredentials, DISABLE_TOKEN_CONFIG);
ac.login();
Subject subject = ac.getSubject();
assertFalse(subject.getPrincipals().isEmpty());
assertFalse(subject.getPublicCredentials().isEmpty());
assertFalse(subject.getPublicCredentials(SimpleCredentials.class).isEmpty());
assertTrue(subject.getPublicCredentials(TokenCredentials.class).isEmpty());
assertEquals(1, subject.getPublicCredentials(Credentials.class).size());
ac.logout();
} finally {
simpleCredentials.removeAttribute(TokenBasedAuthentication.TOKEN_ATTRIBUTE);
}
}
public void testDisabledTokenCredentials2() throws Exception {
simpleCredentials.setAttribute(TokenBasedAuthentication.TOKEN_ATTRIBUTE, "");
try {
AuthContext ac = getAuthContext(simpleCredentials, DEFAULT_CONFIG);
ac.login();
Subject subj = ac.getSubject();
assertFalse(subj.getPublicCredentials(SimpleCredentials.class).isEmpty());
assertFalse(subj.getPublicCredentials(TokenCredentials.class).isEmpty());
TokenCredentials tokenCredentials = subj.getPublicCredentials(TokenCredentials.class).iterator().next();
ac.logout();
// test login with token credentials
ac = getAuthContext(tokenCredentials, DEFAULT_CONFIG);
ac.login();
ac.logout();
// test login with token credentials if token-auth is disabled.
try {
ac = getAuthContext(tokenCredentials, DISABLE_TOKEN_CONFIG);
ac.login();
ac.logout();
fail();
} catch (LoginException e) {
// success
}
} finally {
simpleCredentials.removeAttribute(TokenBasedAuthentication.TOKEN_ATTRIBUTE);
}
}
public void testTokenConfigurationWithJaas() throws Exception {
// define the location of the JAAS configuration
System.setProperty(
"java.security.auth.login.config",
"target/test-classes/jaas.config");
simpleCredentials.setAttribute(TokenBasedAuthentication.TOKEN_ATTRIBUTE, "");
try {
AuthContext ac = getJAASAuthContext(simpleCredentials, "defaultLoginModuleTest");
ac.login();
Subject subject = ac.getSubject();
assertFalse(subject.getPrincipals().isEmpty());
assertFalse(subject.getPublicCredentials().isEmpty());
assertFalse(subject.getPublicCredentials(SimpleCredentials.class).isEmpty());
assertTrue(subject.getPublicCredentials(TokenCredentials.class).isEmpty());
assertEquals(1, subject.getPublicCredentials(Credentials.class).size());
ac.logout();
} finally {
simpleCredentials.removeAttribute(TokenBasedAuthentication.TOKEN_ATTRIBUTE);
}
}
private AuthContext getAuthContext(Credentials creds, String config) throws RepositoryException {
CallbackHandler ch = new CallbackHandlerImpl(creds,
securitySession, new ProviderRegistryImpl(new FallbackPrincipalProvider()),
"admin", "anonymous");
return new LocalAuthContext(getLoginModuleConfig(config), ch, null);
}
private AuthContext getJAASAuthContext(Credentials creds, String appName) {
CallbackHandler ch = new CallbackHandlerImpl(creds,
securitySession, new ProviderRegistryImpl(new FallbackPrincipalProvider()),
"admin", "anonymous");
return new JAASAuthContext(appName, ch, null);
}
private static LoginModuleConfig getLoginModuleConfig(String config) throws ConfigurationException {
return new RepositoryConfigurationParser(new Properties()).parseLoginModuleConfig(parseXML(new InputSource(new StringReader(config)), false));
}
private static Element parseXML(InputSource xml, boolean validate) throws ConfigurationException {
try {
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
factory.setValidating(validate);
DocumentBuilder builder = factory.newDocumentBuilder();
if (validate) {
builder.setErrorHandler(new ConfigurationErrorHandler());
}
builder.setEntityResolver(ConfigurationEntityResolver.INSTANCE);
Document document = builder.parse(xml);
return document.getDocumentElement();
} catch (ParserConfigurationException e) {
throw new ConfigurationException("Unable to create configuration XML parser", e);
} catch (SAXParseException e) {
throw new ConfigurationException("Configuration file syntax error. (Line: " + e.getLineNumber() + " Column: " + e.getColumnNumber() + ")", e);
} catch (SAXException e) {
throw new ConfigurationException("Configuration file syntax error. ", e);
} catch (IOException e) {
throw new ConfigurationException("Configuration file could not be read.", e);
}
}
}