Skip to content

Commit

Permalink
SLING-3227 - FormLoginModulePlugin does not work with Oak
Browse files Browse the repository at this point in the history
Add FormLoginModule based on Oak support for JAAS authentication
-- FormAuthenticationHandler would use FormLoginModule or FormLoginModulePlugin depending on support for Oak LoginModule. This would enable use of same bundle in both Oak and JR2 based Sling deployments
-- For JAAS auth the handler would construct a FormCrendentials instance which is supported by FormLoginModule only. Once validated it would make use of Oak pre auth support to let Oak complete the JAAS login

git-svn-id: https://svn.apache.org/repos/asf/sling/trunk@1649302 13f79535-47bb-0310-9956-ffa450edef68
  • Loading branch information
chetanmeh committed Jan 4, 2015
1 parent 95dd7f1 commit e3bdec0
Show file tree
Hide file tree
Showing 6 changed files with 334 additions and 10 deletions.
13 changes: 13 additions & 0 deletions bundles/auth/form/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,19 @@
<scope>provided</scope>
</dependency>

<dependency>
<groupId>org.apache.jackrabbit</groupId>
<artifactId>oak-core</artifactId>
<version>1.0.0</version>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.apache.felix</groupId>
<artifactId>org.apache.felix.jaas</artifactId>
<version>0.0.2</version>
<optional>true</optional>
</dependency>

<!-- Test Dependencies -->
<dependency>
<groupId>junit</groupId>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@
import javax.servlet.http.HttpSession;

import org.apache.commons.codec.binary.Base64;
import org.apache.felix.jaas.LoginModuleFactory;
import org.apache.felix.scr.annotations.Component;
import org.apache.felix.scr.annotations.Deactivate;
import org.apache.felix.scr.annotations.Properties;
Expand All @@ -56,6 +57,8 @@
import org.apache.sling.auth.core.spi.AuthenticationInfo;
import org.apache.sling.auth.core.spi.DefaultAuthenticationFeedbackHandler;
import org.apache.sling.auth.form.FormReason;
import org.apache.sling.auth.form.impl.jaas.FormCredentials;
import org.apache.sling.auth.form.impl.jaas.JaasHelper;
import org.apache.sling.commons.osgi.OsgiUtil;
import org.osgi.framework.BundleContext;
import org.osgi.framework.Constants;
Expand All @@ -73,7 +76,12 @@
@Property(name = Constants.SERVICE_DESCRIPTION, value = "Apache Sling Form Based Authentication Handler"),
@Property(name = AuthenticationHandler.PATH_PROPERTY, value = "/", cardinality = 100),
@Property(name = AuthenticationHandler.TYPE_PROPERTY, value = HttpServletRequest.FORM_AUTH, propertyPrivate = true),
@Property(name = Constants.SERVICE_RANKING, intValue = 0, propertyPrivate = false) })
@Property(name = Constants.SERVICE_RANKING, intValue = 0, propertyPrivate = false),

@Property(name = LoginModuleFactory.JAAS_CONTROL_FLAG, value = "sufficient"),
@Property(name = LoginModuleFactory.JAAS_REALM_NAME, value = "jackrabbit.oak"),
@Property(name = LoginModuleFactory.JAAS_RANKING, intValue = 1000)
})
@Service
public class FormAuthenticationHandler extends DefaultAuthenticationFeedbackHandler implements AuthenticationHandler {

Expand Down Expand Up @@ -295,6 +303,8 @@ public class FormAuthenticationHandler extends DefaultAuthenticationFeedbackHand
*/
private boolean loginAfterExpire;

private JaasHelper jaasHelper;

/**
* Extracts cookie/session based credentials from the request. Returns
* <code>null</code> if the handler assumes HTTP Basic authentication would
Expand Down Expand Up @@ -622,7 +632,13 @@ private AuthenticationInfo createAuthInfo(final String authData) {

final AuthenticationInfo info = new AuthenticationInfo(
HttpServletRequest.FORM_AUTH, userId);
info.put(attrCookieAuthData, authData);

if (jaasHelper.enabled()) {
//JcrResourceConstants.AUTHENTICATION_INFO_CREDENTIALS
info.put("user.jcr.credentials", new FormCredentials(userId, authData));
} else {
info.put(attrCookieAuthData, authData);
}

return info;
}
Expand All @@ -643,6 +659,8 @@ private String getCookieAuthData(final Credentials credentials) {
if (data instanceof String) {
return (String) data;
}
} else if (credentials instanceof FormCredentials){
return ((FormCredentials) credentials).getAuthData();
}

// no SimpleCredentials or no valid attribute
Expand All @@ -653,7 +671,7 @@ boolean hasAuthData(final Credentials credentials) {
return getCookieAuthData(credentials) != null;
}

boolean isValid(final Credentials credentials) {
public boolean isValid(final Credentials credentials) {
String authData = getCookieAuthData(credentials);
if (authData != null) {
return tokenStore.isValid(authData);
Expand All @@ -679,6 +697,7 @@ protected void activate(ComponentContext componentContext)

Dictionary<?, ?> properties = componentContext.getProperties();

this.jaasHelper = new JaasHelper(this, componentContext.getBundleContext(), properties);
this.loginForm = OsgiUtil.toString(properties.get(PAR_LOGIN_FORM),
AuthenticationFormServlet.SERVLET_PATH);
log.info("Login Form URL {}", loginForm);
Expand Down Expand Up @@ -730,12 +749,14 @@ protected void activate(ComponentContext componentContext)
this.tokenStore = new TokenStore(tokenFile, sessionTimeout, fastSeed);

this.loginModule = null;
try {
this.loginModule = FormLoginModulePlugin.register(this,
componentContext.getBundleContext());
} catch (Throwable t) {
log.info("Cannot register FormLoginModulePlugin. This is expected if Sling LoginModulePlugin services are not supported");
log.debug("dump", t);
if (!jaasHelper.enabled()) {
try {
this.loginModule = FormLoginModulePlugin.register(this,
componentContext.getBundleContext());
} catch (Throwable t) {
log.info("Cannot register FormLoginModulePlugin. This is expected if Sling LoginModulePlugin services are not supported");
log.debug("dump", t);
}
}

this.includeLoginForm = OsgiUtil.toBoolean(properties.get(PAR_INCLUDE_FORM), DEFAULT_INCLUDE_FORM);
Expand All @@ -745,6 +766,11 @@ protected void activate(ComponentContext componentContext)

@Deactivate
protected void deactivate() {
if (jaasHelper != null){
jaasHelper.close();
jaasHelper = null;
}

if (loginModule != null) {
loginModule.unregister();
loginModule = null;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
/*
* 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.sling.auth.form.impl.jaas;

import javax.jcr.Credentials;

public class FormCredentials implements Credentials {
private final String userId;
private final String authData;

public FormCredentials(String userId, String authData) {
this.userId = userId;
this.authData = authData;
}

public String getUserId() {
return userId;
}

public String getAuthData() {
return authData;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
/*
* 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.sling.auth.form.impl.jaas;

import java.util.Collections;
import java.util.Set;

import javax.jcr.Credentials;
import javax.jcr.SimpleCredentials;
import javax.security.auth.login.LoginException;

import org.apache.jackrabbit.oak.spi.security.authentication.AbstractLoginModule;
import org.apache.jackrabbit.oak.spi.security.authentication.PreAuthenticatedLogin;
import org.apache.sling.auth.form.impl.FormAuthenticationHandler;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

final class FormLoginModule extends AbstractLoginModule {
private static final Logger log = LoggerFactory.getLogger(FormLoginModule.class);

/**
* The set of supported credentials. required by the {@link org.apache.jackrabbit.oak.spi.security.authentication.AbstractLoginModule}
*/
private static final Set<Class> SUPPORTED_CREDENTIALS = Collections.<Class>singleton(FormCredentials.class);
private static final char[] EMPTY_PWD = new char[0];

/**
* Extracted userId during login call.
*/
private String userId;

@Override
protected Set<Class> getSupportedCredentials() {
return SUPPORTED_CREDENTIALS;
}

/**
* The {@link org.apache.sling.auth.form.impl.FormAuthenticationHandler} used to validate the credentials
* and its contents.
*/
private final FormAuthenticationHandler authHandler;

FormLoginModule(FormAuthenticationHandler authHandler) {
this.authHandler = authHandler;
}

@SuppressWarnings("unchecked")
public boolean login() throws LoginException {
Credentials credentials = getCredentials();
if (credentials instanceof FormCredentials) {
FormCredentials cred = (FormCredentials) credentials;
userId = cred.getUserId();

if (!authHandler.isValid(cred)){
log.debug("Invalid credentials");
return false;
}

if (userId == null) {
log.debug("Could not extract userId/credentials");
} else {
// we just set the login name and rely on the following login modules to populate the subject
sharedState.put(SHARED_KEY_PRE_AUTH_LOGIN, new PreAuthenticatedLogin(userId));
sharedState.put(SHARED_KEY_CREDENTIALS, new SimpleCredentials(userId, EMPTY_PWD));
sharedState.put(SHARED_KEY_LOGIN_NAME, userId);
log.debug("login succeeded with trusted user: {}", userId);
}
}
return false;
}

public boolean commit() throws LoginException {
if (userId == null) {
// login attempt in this login module was not successful
clearState();
}
return false;
}

@Override
protected void clearState() {
userId = null;
super.clearState();
}
}
Loading

0 comments on commit e3bdec0

Please sign in to comment.