This is an automated email from the ASF dual-hosted git repository. rombert pushed a commit to annotated tag org.apache.sling.auth.form-1.0.8 in repository https://gitbox.apache.org/repos/asf/sling-org-apache-sling-auth-form.git
commit 12e637b34b950372f15103e5f6c3ad686122b076 Author: Chetan Mehrotra <[email protected]> AuthorDate: Sun Jan 4 09:08:39 2015 +0000 SLING-3227 - FormLoginModulePlugin does not work with Oak 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/bundles/auth/form@1649302 13f79535-47bb-0310-9956-ffa450edef68 --- pom.xml | 13 +++ .../auth/form/impl/FormAuthenticationHandler.java | 44 +++++-- .../sling/auth/form/impl/jaas/FormCredentials.java | 40 +++++++ .../sling/auth/form/impl/jaas/FormLoginModule.java | 102 ++++++++++++++++ .../sling/auth/form/impl/jaas/JaasHelper.java | 128 +++++++++++++++++++++ .../OSGI-INF/metatype/metatype.properties | 17 ++- 6 files changed, 334 insertions(+), 10 deletions(-) diff --git a/pom.xml b/pom.xml index e129707..51d34ce 100644 --- a/pom.xml +++ b/pom.xml @@ -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> diff --git a/src/main/java/org/apache/sling/auth/form/impl/FormAuthenticationHandler.java b/src/main/java/org/apache/sling/auth/form/impl/FormAuthenticationHandler.java index 6343b04..d14c8c6 100644 --- a/src/main/java/org/apache/sling/auth/form/impl/FormAuthenticationHandler.java +++ b/src/main/java/org/apache/sling/auth/form/impl/FormAuthenticationHandler.java @@ -36,6 +36,7 @@ import javax.servlet.http.HttpServletResponse; 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; @@ -56,6 +57,8 @@ import org.apache.sling.auth.core.spi.AuthenticationHandler; 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; @@ -73,7 +76,12 @@ import org.slf4j.LoggerFactory; @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 { @@ -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 @@ -622,7 +632,13 @@ public class FormAuthenticationHandler extends DefaultAuthenticationFeedbackHand 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; } @@ -643,6 +659,8 @@ public class FormAuthenticationHandler extends DefaultAuthenticationFeedbackHand if (data instanceof String) { return (String) data; } + } else if (credentials instanceof FormCredentials){ + return ((FormCredentials) credentials).getAuthData(); } // no SimpleCredentials or no valid attribute @@ -653,7 +671,7 @@ public class FormAuthenticationHandler extends DefaultAuthenticationFeedbackHand 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); @@ -679,6 +697,7 @@ public class FormAuthenticationHandler extends DefaultAuthenticationFeedbackHand 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); @@ -730,12 +749,14 @@ public class FormAuthenticationHandler extends DefaultAuthenticationFeedbackHand 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); @@ -745,6 +766,11 @@ public class FormAuthenticationHandler extends DefaultAuthenticationFeedbackHand @Deactivate protected void deactivate() { + if (jaasHelper != null){ + jaasHelper.close(); + jaasHelper = null; + } + if (loginModule != null) { loginModule.unregister(); loginModule = null; diff --git a/src/main/java/org/apache/sling/auth/form/impl/jaas/FormCredentials.java b/src/main/java/org/apache/sling/auth/form/impl/jaas/FormCredentials.java new file mode 100644 index 0000000..6f127fc --- /dev/null +++ b/src/main/java/org/apache/sling/auth/form/impl/jaas/FormCredentials.java @@ -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; + } +} diff --git a/src/main/java/org/apache/sling/auth/form/impl/jaas/FormLoginModule.java b/src/main/java/org/apache/sling/auth/form/impl/jaas/FormLoginModule.java new file mode 100644 index 0000000..e98ddf1 --- /dev/null +++ b/src/main/java/org/apache/sling/auth/form/impl/jaas/FormLoginModule.java @@ -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(); + } +} diff --git a/src/main/java/org/apache/sling/auth/form/impl/jaas/JaasHelper.java b/src/main/java/org/apache/sling/auth/form/impl/jaas/JaasHelper.java new file mode 100644 index 0000000..bb5a1b5 --- /dev/null +++ b/src/main/java/org/apache/sling/auth/form/impl/jaas/JaasHelper.java @@ -0,0 +1,128 @@ +/* + * 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.Dictionary; + +import javax.security.auth.spi.LoginModule; + +import org.apache.felix.jaas.LoginModuleFactory; +import org.apache.sling.auth.form.impl.FormAuthenticationHandler; +import org.osgi.framework.BundleContext; +import org.osgi.framework.Constants; +import org.osgi.framework.ServiceRegistration; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class JaasHelper { + + private static final Logger log = LoggerFactory.getLogger(JaasHelper.class); + + private final FormAuthenticationHandler authHandler; + + /** + * login module service registration + */ + private final ServiceRegistration factoryRegistration; + + /** + * Opens/Initializes the helper and registers the login module factory (LMF) service if possible. + * + * @param ctx the bundle context + * @param properties properties that contain the jaas related LMF service properties. + */ + public JaasHelper(FormAuthenticationHandler authHandler, BundleContext ctx, Dictionary properties) { + this.authHandler = authHandler; + // we dynamically register the LoginModuleFactory for the case we detect a login module. + if (hasSSOLoginModule(ctx)) { + factoryRegistration = registerLoginModuleFactory(ctx, properties); + } else { + factoryRegistration = null; + } + } + + /** + * Checks if JAAS support is enabled and the SSO login module is present. + * + * @return {@code true} if JAAS support is enabled. + */ + public boolean enabled() { + return factoryRegistration != null; + } + + + /** + * Closes this helper and unregisters the login module factory if needed. + */ + public void close() { + if (factoryRegistration != null) { + factoryRegistration.unregister(); + } + } + + private ServiceRegistration registerLoginModuleFactory(BundleContext ctx, Dictionary properties) { + ServiceRegistration reg = null; + try { + java.util.Properties props = new java.util.Properties(); + final String desc = "LoginModule Support for FormAuthenticationHandler"; + props.put(Constants.SERVICE_DESCRIPTION, desc); + props.put(Constants.SERVICE_VENDOR, ctx.getBundle().getHeaders().get(Constants.BUNDLE_VENDOR)); + + props.put(LoginModuleFactory.JAAS_RANKING, properties.get(LoginModuleFactory.JAAS_RANKING)); + props.put(LoginModuleFactory.JAAS_CONTROL_FLAG, properties.get(LoginModuleFactory.JAAS_CONTROL_FLAG)); + props.put(LoginModuleFactory.JAAS_REALM_NAME, properties.get(LoginModuleFactory.JAAS_REALM_NAME)); + reg = ctx.registerService(LoginModuleFactory.class.getName(), + new LoginModuleFactory() { + public LoginModule createLoginModule() { + return new FormLoginModule(authHandler); + } + + @Override + public String toString() { + return desc + " (" +FormLoginModule.class.getName()+")"; + } + }, + props + ); + log.info("Registered FormLoginModuleFactory"); + } catch (Throwable e) { + log.error("unable to create an register the SSO login module factory", e); + } + return reg; + } + + /** + * Checks if the {@link org.apache.sling.auth.form.impl.jaas.FormLoginModule} is available. This would not be the case + * in an non-oak setup. Note this only checks if the login module can be loaded, not if it is actually enabled + * in the jaas config. + * + * @return {@code true} if the SSOLoginModule is available. + */ + private static boolean hasSSOLoginModule(BundleContext ctx) { + try { + ctx.getBundle().loadClass("org.apache.sling.auth.form.impl.jaas.FormLoginModule"); + log.debug("FormLoginModule available."); + return true; + } catch (Throwable e) { + log.debug("no FormLoginModule available.", e); + } + return false; + } +} diff --git a/src/main/resources/OSGI-INF/metatype/metatype.properties b/src/main/resources/OSGI-INF/metatype/metatype.properties index 2d92666..7070cdb 100644 --- a/src/main/resources/OSGI-INF/metatype/metatype.properties +++ b/src/main/resources/OSGI-INF/metatype/metatype.properties @@ -91,4 +91,19 @@ form.default.cookie.domain.name = Default Cookie Domain form.default.cookie.domain.description = The domain on which authentication cookies will \ be set, unless overridden in the AuthenticationInfo object. The default is null \ which means to set the cookie on the request domain. - \ No newline at end of file + +jaas.controlFlag.name = JAAS Control Flag +jaas.controlFlag.description = Property name specifying whether or not a LoginModule is REQUIRED, REQUISITE, SUFFICIENT \ + or OPTIONAL. Refer to the JAAS configuration documentation for more details around the meaning of these flags. \ + Jackrabbit Oak only. + +jaas.realmName.name = JAAS Realm +jaas.realmName.description = Property name specifying the realm name (or application name) against which the LoginModule \ + is be registered. If no realm name is provided then LoginModule is registered with a default realm as configured in \ + the Felix JAAS configuration. \ + Jackrabbit Oak only. + +jaas.ranking.name = JAAS Ranking +jaas.ranking.description = Property name specifying the ranking (i.e. sort order) of the configured login module \ + entries. The entries are sorted in a descending order (i.e. higher value ranked configurations come first). \ + Jackrabbit Oak only. -- To stop receiving notification emails like this one, please contact "[email protected]" <[email protected]>.
