Issue #6406 - backport jetty-jaspi changes to 10.0.x
Co-authored-by: Frode Carlsen <frode.odde.carlsen@nav.no> Signed-off-by: Lachlan Roberts <lachlan@webtide.com>
This commit is contained in:
parent
c150ce6859
commit
4d579d682c
|
@ -511,3 +511,19 @@ Below is an example which, like the one above, sets up a server with a `HashLogi
|
|||
----
|
||||
include::{SRCDIR}/examples/embedded/src/main/java/org/eclipse/jetty/embedded/SecuredHelloHandler.java[]
|
||||
----
|
||||
|
||||
==== JSR 196: Java Authentication Service Provider Interface for Containers (JASPI)
|
||||
|
||||
Jetty can utilize portable authentication modules that implements the Jakarta Authentication specification. This requires the jetty-jaspi module.
|
||||
|
||||
Only modules conforming to the ServerAuthModule interface in the https://www.jcp.org/en/jsr/detail?id=196[JASPI Spec] are supported. These modules must be configured before start-up.
|
||||
|
||||
The following illustrates a jetty module setting up HTTP Basic Authentication using an Authentication module that comes packaged with the jetty-jaspi module: `org.eclipse.jetty.security.jaspi.modules.BasicAuthenticationAuthModule`
|
||||
|
||||
[source, xml, subs="{sub-order}"]
|
||||
----
|
||||
include::{SRCDIR}/jetty-jaspi/src/main/config/etc/jaspi/jaspi-demo.xml[tags=documentation]
|
||||
----
|
||||
|
||||
Given the portability goal of Jakarta Authentication, custom or 3rd party `ServerAuthModule` implementations may be configured instead here.
|
||||
|
||||
|
|
|
@ -34,6 +34,7 @@ include::annotations/chapter.adoc[]
|
|||
include::jsp/chapter.adoc[]
|
||||
include::jndi/chapter.adoc[]
|
||||
include::jaas/chapter.adoc[]
|
||||
include::jaspi/chapter.adoc[]
|
||||
include::jmx/chapter.adoc[]
|
||||
include::logging/chapter.adoc[]
|
||||
include::troubleshooting/chapter.adoc[]
|
||||
|
|
|
@ -0,0 +1,68 @@
|
|||
//
|
||||
// ========================================================================
|
||||
// Copyright (c) 1995-2021 Mort Bay Consulting Pty Ltd and others.
|
||||
//
|
||||
// This program and the accompanying materials are made available under the
|
||||
// terms of the Eclipse Public License v. 2.0 which is available at
|
||||
// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
|
||||
// which is available at https://www.apache.org/licenses/LICENSE-2.0.
|
||||
//
|
||||
// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
|
||||
// ========================================================================
|
||||
//
|
||||
|
||||
[[og-jaspi]]
|
||||
=== JASPI
|
||||
|
||||
Enabling this module allows Jetty to utilize authentication modules that implement the JSR 196 (JASPI) specification. JASPI provides an SPI (Service Provider Interface) for pluggable, portable, and standardized authentication modules. Compatible modules are portable between servers that support the JASPI specification. This module provides a bridge from Java Authentication to the Jetty Security framework.
|
||||
|
||||
Only modules conforming to the "Servlet Container Profile" with the ServerAuthModule interface within the https://www.jcp.org/en/jsr/detail?id=196[JASPI Spec] are supported. These modules must be configured before start-up. Operations for runtime registering or de-registering authentication modules are not supported.
|
||||
|
||||
[[og-jaspi-configuration]]
|
||||
==== Configuration
|
||||
|
||||
[[og-jaspi-module]]
|
||||
===== The `jaspi` module
|
||||
|
||||
Enable the `jaspi` module:
|
||||
|
||||
----
|
||||
include::{JETTY_HOME}/modules/jaspi.mod[]
|
||||
----
|
||||
|
||||
[[og-jaspi-xml]]
|
||||
===== Configure JASPI
|
||||
|
||||
To enable the `jaspi` module you can use the following command (issued from within the `$JETTY_BASE` directory):
|
||||
|
||||
----
|
||||
$ java -jar $JETTY_HOME/start.jar --add-modules=jaspi
|
||||
----
|
||||
|
||||
You can then register a `AuthConfigProvider` onto the static `AuthConfigFactory` obtained with `AuthConfigFactory.getFactory()`. This registration can be done in the XML configuration file which will be copied to `$JETTY_BASE/etc/jaspi/jaspi-authmoduleconfig.xml` when the module is enabled.
|
||||
|
||||
====== JASPI Demo
|
||||
The `jaspi-demo` module illustrates setting up HTTP Basic Authentication using a Java Authentication module that comes packaged with jetty: `org.eclipse.jetty.security.jaspi.modules.BasicAuthenticationAuthModule`, and applies it for a context named `/test`.
|
||||
|
||||
[source, xml]
|
||||
----
|
||||
include::{JETTY_HOME}/etc/jaspi/jaspi-demo.xml[]
|
||||
----
|
||||
|
||||
This example uses the `AuthConfigProvider` implementation provided by Jetty to register a `ServerAuthModule` directly. Other custom or 3rd party modules that are compatible with the `ServerAuthModule` interface in JASPI can be registered in the same way.
|
||||
|
||||
===== Integration with Jetty Authentication Mechanisms
|
||||
|
||||
To integrate with Jetty authentication mechanisms you must add a `LoginService` to your context. The `LoginService` provides a way for you to obtain a `UserIdentity` from a username and credentials. JASPI can interact with this Jetty `LoginService` by using the `PasswordValidationCallback`.
|
||||
|
||||
The `CallerPrincipalCallback` and `GroupPrincipalCallback` do not require use of a Jetty `LoginService`. The principal from the `CallerPrincipalCallback` will be used directly with the `IdentityService` to produce a `UserIdentity`.
|
||||
|
||||
===== Replacing the Jetty DefaultAuthConfigFactory
|
||||
|
||||
Jetty provides an implementation of the `AuthConfigFactory` interface which is used to register `AuthConfigProviders`. This can be replaced by a custom implementation by adding a custom module which provides `auth-config-factory`.
|
||||
This custom module must reference an XML file which sets a new instance of the `AuthConfigFactory` with the static method `AuthConfigFactory.setFactory()`.
|
||||
For an example of this see the `jaspi-default-auth-config-factory` module, which provides the default implementation used by Jetty.
|
||||
|
||||
----
|
||||
include::{JETTY_HOME}/modules/jaspi-default-auth-config-factory.mod[]
|
||||
----
|
|
@ -80,17 +80,5 @@
|
|||
<version>1.2.0</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.geronimo.components</groupId>
|
||||
<artifactId>geronimo-jaspi</artifactId>
|
||||
<version>2.0.0</version>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<groupId>org.apache.geronimo.specs</groupId>
|
||||
<artifactId>geronimo-jaspic_1.0_spec</artifactId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
||||
|
|
|
@ -0,0 +1,8 @@
|
|||
<?xml version="1.0"?>
|
||||
<!DOCTYPE Configure PUBLIC "-//Jetty//Configure//EN" "https://www.eclipse.org/jetty/configure_10_0.dtd">
|
||||
|
||||
<Configure>
|
||||
<Call class="javax.security.auth.message.config.AuthConfigFactory" name="getFactory">
|
||||
<!-- Configure the AuthConfigFactory here. -->
|
||||
</Call>
|
||||
</Configure>
|
|
@ -0,0 +1,20 @@
|
|||
<?xml version="1.0"?>
|
||||
<!DOCTYPE Configure PUBLIC "-//Jetty//Configure//EN" "https://www.eclipse.org/jetty/configure_10_0.dtd">
|
||||
|
||||
<Configure id="Server" class="org.eclipse.jetty.server.Server">
|
||||
|
||||
<!-- ===================================================================== -->
|
||||
<!-- Configure a factory for Jaspi -->
|
||||
<!-- ===================================================================== -->
|
||||
<Call class="javax.security.auth.message.config.AuthConfigFactory" name="setFactory">
|
||||
<Arg>
|
||||
<New id="jaspiAuthConfigFactory" class="org.eclipse.jetty.security.jaspi.DefaultAuthConfigFactory" />
|
||||
</Arg>
|
||||
</Call>
|
||||
<Call name="addBean">
|
||||
<Arg>
|
||||
<Ref refid="jaspiAuthConfigFactory" />
|
||||
</Arg>
|
||||
<Arg type="boolean">false</Arg>
|
||||
</Call>
|
||||
</Configure>
|
|
@ -0,0 +1,48 @@
|
|||
<?xml version="1.0"?>
|
||||
<!DOCTYPE Configure PUBLIC "-//Jetty//Configure//EN" "https://www.eclipse.org/jetty/configure_10_0.dtd">
|
||||
|
||||
<Configure>
|
||||
<Call class="javax.security.auth.message.config.AuthConfigFactory" name="getFactory">
|
||||
<Call name="registerConfigProvider">
|
||||
|
||||
<!-- The Jetty provided implementation of AuthConfigProvider which will wrap a ServerAuthModule. -->
|
||||
<Arg type="String">org.eclipse.jetty.security.jaspi.provider.JaspiAuthConfigProvider</Arg>
|
||||
|
||||
<!-- A Map of initialization properties. -->
|
||||
<Arg>
|
||||
<Map>
|
||||
<Entry>
|
||||
<!-- Provide the fully qualified classname of the ServerAuthModule to be used. -->
|
||||
<Item>ServerAuthModule</Item>
|
||||
<Item>org.eclipse.jetty.security.jaspi.modules.BasicAuthenticationAuthModule</Item>
|
||||
</Entry>
|
||||
<Entry>
|
||||
<!-- Realm as utilised by Jetty Security -->
|
||||
<Item>org.eclipse.jetty.security.jaspi.modules.RealmName</Item>
|
||||
<Item>Test Realm</Item>
|
||||
</Entry>
|
||||
</Map>
|
||||
</Arg>
|
||||
|
||||
<!-- Message Layer Identifier as per spec chapter 3.1 -->
|
||||
<Arg type="String">HttpServlet</Arg>
|
||||
|
||||
<!-- Application Context Identifier as per spec chapter 3.2
|
||||
|
||||
AppContextID ::= hostname blank context-path
|
||||
The algorithm applied here will use the
|
||||
_serverName on the configured JaspiAuthenticatorFactory (if set) and try to match it
|
||||
against the "server" part (in the "server /test" example below).
|
||||
Next it will try to match the ServletContext#getVirtualServerName to the "server" part.
|
||||
If neither are set, it will then try to match the first Subject's principal name, and finally fall back to
|
||||
the default value "server" if none are available.
|
||||
|
||||
The context-path should match the context path where this applies.
|
||||
-->
|
||||
<Arg type="String">server /test</Arg>
|
||||
|
||||
<!-- A friendly description of the provided auth-module. -->
|
||||
<Arg type="String">A simple provider using HTTP BASIC authentication.</Arg>
|
||||
</Call>
|
||||
</Call>
|
||||
</Configure>
|
|
@ -0,0 +1,16 @@
|
|||
# DO NOT EDIT - See: https://www.eclipse.org/jetty/documentation/current/startup-modules.html
|
||||
|
||||
[description]
|
||||
Provides a DefaultAuthConfigFactory for jaspi
|
||||
|
||||
[tags]
|
||||
security
|
||||
|
||||
[depend]
|
||||
security
|
||||
|
||||
[provide]
|
||||
auth-config-factory
|
||||
|
||||
[xml]
|
||||
etc/jaspi/jaspi-default.xml
|
|
@ -0,0 +1,16 @@
|
|||
# DO NOT EDIT - See: https://www.eclipse.org/jetty/documentation/current/startup-modules.html
|
||||
|
||||
[description]
|
||||
Enables JASPI basic authentication the /test context path.
|
||||
|
||||
[tags]
|
||||
security
|
||||
|
||||
[depend]
|
||||
jaspi
|
||||
|
||||
[xml]
|
||||
etc/jaspi/jaspi-demo.xml
|
||||
|
||||
[files]
|
||||
basehome:etc/jaspi/jaspi-demo.xml|etc/jaspi/jaspi-demo.xml
|
|
@ -3,9 +3,20 @@
|
|||
[description]
|
||||
Enables JASPI authentication for deployed web applications.
|
||||
|
||||
[tags]
|
||||
security
|
||||
|
||||
[depend]
|
||||
security
|
||||
auth-config-factory
|
||||
|
||||
[lib]
|
||||
lib/jetty-jaspi-${jetty.version}.jar
|
||||
lib/jaspi/*.jar
|
||||
|
||||
[xml]
|
||||
etc/jaspi/jaspi-authmoduleconfig.xml
|
||||
|
||||
[files]
|
||||
basehome:etc/jaspi/jaspi-authmoduleconfig.xml|etc/jaspi/jaspi-authmoduleconfig.xml
|
||||
|
||||
|
|
|
@ -19,8 +19,9 @@ module org.eclipse.jetty.security.jaspi
|
|||
exports org.eclipse.jetty.security.jaspi;
|
||||
exports org.eclipse.jetty.security.jaspi.callback;
|
||||
exports org.eclipse.jetty.security.jaspi.modules;
|
||||
exports org.eclipse.jetty.security.jaspi.provider;
|
||||
|
||||
requires javax.security.auth.message;
|
||||
requires transitive javax.security.auth.message;
|
||||
requires jetty.servlet.api;
|
||||
requires transitive org.eclipse.jetty.security;
|
||||
requires org.slf4j;
|
||||
|
|
|
@ -0,0 +1,249 @@
|
|||
//
|
||||
// ========================================================================
|
||||
// Copyright (c) 1995-2021 Mort Bay Consulting Pty Ltd and others.
|
||||
//
|
||||
// This program and the accompanying materials are made available under the
|
||||
// terms of the Eclipse Public License v. 2.0 which is available at
|
||||
// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
|
||||
// which is available at https://www.apache.org/licenses/LICENSE-2.0.
|
||||
//
|
||||
// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
|
||||
// ========================================================================
|
||||
//
|
||||
|
||||
package org.eclipse.jetty.security.jaspi;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.CopyOnWriteArrayList;
|
||||
import javax.security.auth.AuthPermission;
|
||||
import javax.security.auth.message.config.AuthConfigFactory;
|
||||
import javax.security.auth.message.config.AuthConfigProvider;
|
||||
import javax.security.auth.message.config.RegistrationListener;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* A very basic {@link AuthConfigFactory} that allows for registering providers programmatically.
|
||||
*/
|
||||
public class DefaultAuthConfigFactory extends AuthConfigFactory
|
||||
{
|
||||
private static final Logger LOG = LoggerFactory.getLogger(DefaultAuthConfigFactory.class);
|
||||
private final Map<String, DefaultRegistrationContext> _registrations = new ConcurrentHashMap<>();
|
||||
|
||||
public DefaultAuthConfigFactory()
|
||||
{
|
||||
}
|
||||
|
||||
@Override
|
||||
public AuthConfigProvider getConfigProvider(String layer, String appContext, RegistrationListener listener)
|
||||
{
|
||||
DefaultRegistrationContext registrationContext = _registrations.get(getKey(layer, appContext));
|
||||
if (registrationContext == null)
|
||||
registrationContext = _registrations.get(getKey(null, appContext));
|
||||
if (registrationContext == null)
|
||||
registrationContext = _registrations.get(getKey(layer, null));
|
||||
if (registrationContext == null)
|
||||
registrationContext = _registrations.get(getKey(null, null));
|
||||
if (registrationContext == null)
|
||||
return null;
|
||||
|
||||
// TODO: according to the javadoc you're supposed to register listener even if there is no context available.
|
||||
if (listener != null)
|
||||
registrationContext.addListener(listener);
|
||||
return registrationContext.getProvider();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String registerConfigProvider(String className, Map properties, String layer, String appContext, String description)
|
||||
{
|
||||
SecurityManager sm = System.getSecurityManager();
|
||||
if (sm != null)
|
||||
sm.checkPermission(new AuthPermission("registerAuthConfigProvider"));
|
||||
|
||||
String key = getKey(layer, appContext);
|
||||
AuthConfigProvider configProvider = createConfigProvider(className, properties);
|
||||
DefaultRegistrationContext context = new DefaultRegistrationContext(configProvider, layer, appContext, description, true);
|
||||
DefaultRegistrationContext oldContext = _registrations.put(key, context);
|
||||
if (oldContext != null)
|
||||
oldContext.notifyListeners();
|
||||
return key;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String registerConfigProvider(AuthConfigProvider provider, String layer, String appContext, String description)
|
||||
{
|
||||
SecurityManager sm = System.getSecurityManager();
|
||||
if (sm != null)
|
||||
sm.checkPermission(new AuthPermission("registerAuthConfigProvider"));
|
||||
|
||||
String key = getKey(layer, appContext);
|
||||
DefaultRegistrationContext context = new DefaultRegistrationContext(provider, layer, appContext, description, false);
|
||||
DefaultRegistrationContext oldContext = _registrations.put(key, context);
|
||||
if (oldContext != null)
|
||||
oldContext.notifyListeners();
|
||||
return key;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean removeRegistration(String registrationID)
|
||||
{
|
||||
SecurityManager sm = System.getSecurityManager();
|
||||
if (sm != null)
|
||||
sm.checkPermission(new AuthPermission("removeAuthRegistration"));
|
||||
|
||||
DefaultRegistrationContext registrationContext = _registrations.remove(registrationID);
|
||||
if (registrationContext == null)
|
||||
return false;
|
||||
|
||||
registrationContext.notifyListeners();
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String[] detachListener(RegistrationListener listener, String layer, String appContext)
|
||||
{
|
||||
SecurityManager sm = System.getSecurityManager();
|
||||
if (sm != null)
|
||||
sm.checkPermission(new AuthPermission("detachAuthListener"));
|
||||
|
||||
List<String> registrationIds = new ArrayList<>();
|
||||
for (DefaultRegistrationContext registration : _registrations.values())
|
||||
{
|
||||
if ((layer == null || layer.equals(registration.getMessageLayer())) && (appContext == null || appContext.equals(registration.getAppContext())))
|
||||
{
|
||||
if (registration.removeListener(listener))
|
||||
registrationIds.add(getKey(registration.getMessageLayer(), registration.getAppContext()));
|
||||
}
|
||||
}
|
||||
|
||||
return registrationIds.toArray(new String[0]);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String[] getRegistrationIDs(AuthConfigProvider provider)
|
||||
{
|
||||
List<String> registrationIds = new ArrayList<>();
|
||||
for (DefaultRegistrationContext registration : _registrations.values())
|
||||
{
|
||||
if (provider == registration.getProvider())
|
||||
registrationIds.add(getKey(registration.getMessageLayer(), registration.getAppContext()));
|
||||
}
|
||||
|
||||
return registrationIds.toArray(new String[0]);
|
||||
}
|
||||
|
||||
@Override
|
||||
public RegistrationContext getRegistrationContext(String registrationID)
|
||||
{
|
||||
return _registrations.get(registrationID);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void refresh()
|
||||
{
|
||||
SecurityManager sm = System.getSecurityManager();
|
||||
if (sm != null)
|
||||
sm.checkPermission(new AuthPermission("refreshAuth"));
|
||||
|
||||
// TODO: maybe we should re-construct providers created from classname.
|
||||
}
|
||||
|
||||
private static String getKey(String layer, String appContext)
|
||||
{
|
||||
return layer + "/" + appContext;
|
||||
}
|
||||
|
||||
@SuppressWarnings("rawtypes")
|
||||
private AuthConfigProvider createConfigProvider(String className, Map properties)
|
||||
{
|
||||
try
|
||||
{
|
||||
// Javadoc specifies all AuthConfigProvider implementations must have this constructor, and that
|
||||
// to construct this we must pass a null value for the factory argument of the constructor.
|
||||
return (AuthConfigProvider)Class.forName(className)
|
||||
.getConstructor(Map.class, AuthConfigFactory.class)
|
||||
.newInstance(properties, null);
|
||||
}
|
||||
catch (ReflectiveOperationException e)
|
||||
{
|
||||
throw new SecurityException(e);
|
||||
}
|
||||
}
|
||||
|
||||
private static class DefaultRegistrationContext implements RegistrationContext
|
||||
{
|
||||
private final String _layer;
|
||||
private final String _appContext;
|
||||
private final boolean _persistent;
|
||||
private final AuthConfigProvider _provider;
|
||||
private final String _description;
|
||||
private final List<RegistrationListener> _listeners = new CopyOnWriteArrayList<>();
|
||||
|
||||
public DefaultRegistrationContext(AuthConfigProvider provider, String layer, String appContext, String description, boolean persistent)
|
||||
{
|
||||
_provider = provider;
|
||||
_layer = layer;
|
||||
_appContext = appContext;
|
||||
_description = description;
|
||||
_persistent = persistent;
|
||||
}
|
||||
|
||||
public AuthConfigProvider getProvider()
|
||||
{
|
||||
return _provider;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getMessageLayer()
|
||||
{
|
||||
return _layer;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getAppContext()
|
||||
{
|
||||
return _appContext;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDescription()
|
||||
{
|
||||
return _description;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isPersistent()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
public void addListener(RegistrationListener listener)
|
||||
{
|
||||
_listeners.add(listener);
|
||||
}
|
||||
|
||||
public void notifyListeners()
|
||||
{
|
||||
for (RegistrationListener listener : _listeners)
|
||||
{
|
||||
try
|
||||
{
|
||||
listener.notify(_layer, _appContext);
|
||||
}
|
||||
catch (Throwable t)
|
||||
{
|
||||
LOG.warn("Error from RegistrationListener", t);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public boolean removeListener(RegistrationListener listener)
|
||||
{
|
||||
return _listeners.remove(listener);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -15,6 +15,7 @@ package org.eclipse.jetty.security.jaspi;
|
|||
|
||||
import java.io.IOException;
|
||||
import java.security.Principal;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import javax.security.auth.Subject;
|
||||
|
@ -22,6 +23,9 @@ import javax.security.auth.message.AuthException;
|
|||
import javax.security.auth.message.AuthStatus;
|
||||
import javax.security.auth.message.callback.CallerPrincipalCallback;
|
||||
import javax.security.auth.message.callback.GroupPrincipalCallback;
|
||||
import javax.security.auth.message.config.AuthConfigFactory;
|
||||
import javax.security.auth.message.config.AuthConfigProvider;
|
||||
import javax.security.auth.message.config.RegistrationListener;
|
||||
import javax.security.auth.message.config.ServerAuthConfig;
|
||||
import javax.security.auth.message.config.ServerAuthContext;
|
||||
import javax.servlet.ServletRequest;
|
||||
|
@ -30,57 +34,100 @@ import javax.servlet.http.HttpServletRequest;
|
|||
import javax.servlet.http.HttpServletResponse;
|
||||
import javax.servlet.http.HttpSession;
|
||||
|
||||
import org.eclipse.jetty.security.EmptyLoginService;
|
||||
import org.eclipse.jetty.security.IdentityService;
|
||||
import org.eclipse.jetty.security.LoginService;
|
||||
import org.eclipse.jetty.security.ServerAuthException;
|
||||
import org.eclipse.jetty.security.UserAuthentication;
|
||||
import org.eclipse.jetty.security.WrappedAuthConfiguration;
|
||||
import org.eclipse.jetty.security.authentication.DeferredAuthentication;
|
||||
import org.eclipse.jetty.security.authentication.LoginAuthenticator;
|
||||
import org.eclipse.jetty.security.authentication.SessionAuthentication;
|
||||
import org.eclipse.jetty.server.Authentication;
|
||||
import org.eclipse.jetty.server.Authentication.User;
|
||||
import org.eclipse.jetty.server.UserIdentity;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import static org.eclipse.jetty.security.jaspi.JaspiAuthenticatorFactory.MESSAGE_LAYER;
|
||||
|
||||
/**
|
||||
* @version $Rev: 4793 $ $Date: 2009-03-19 00:00:01 +0100 (Thu, 19 Mar 2009) $
|
||||
* Implementation of Jetty {@link LoginAuthenticator} that is a bridge from Javax Authentication to Jetty Security.
|
||||
*/
|
||||
@SuppressWarnings({"rawtypes", "unchecked"})
|
||||
public class JaspiAuthenticator extends LoginAuthenticator
|
||||
{
|
||||
private static final Logger LOG = LoggerFactory.getLogger(JaspiAuthenticator.class.getName());
|
||||
|
||||
private final ServerAuthConfig _authConfig;
|
||||
|
||||
private final Map _authProperties;
|
||||
|
||||
private final ServletCallbackHandler _callbackHandler;
|
||||
|
||||
private final Subject _serviceSubject;
|
||||
|
||||
private final String _appContext;
|
||||
private final boolean _allowLazyAuthentication;
|
||||
private final AuthConfigFactory _authConfigFactory = AuthConfigFactory.getFactory();
|
||||
private Map _authProperties;
|
||||
private IdentityService _identityService;
|
||||
private ServletCallbackHandler _callbackHandler;
|
||||
private ServerAuthConfig _authConfig;
|
||||
|
||||
private final IdentityService _identityService;
|
||||
|
||||
public JaspiAuthenticator(ServerAuthConfig authConfig, Map authProperties, ServletCallbackHandler callbackHandler, Subject serviceSubject,
|
||||
boolean allowLazyAuthentication, IdentityService identityService)
|
||||
public JaspiAuthenticator(Subject serviceSubject, String appContext, boolean allowLazyAuthentication)
|
||||
{
|
||||
_serviceSubject = serviceSubject;
|
||||
_appContext = appContext;
|
||||
_allowLazyAuthentication = allowLazyAuthentication;
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
public JaspiAuthenticator(ServerAuthConfig authConfig, Map authProperties, ServletCallbackHandler callbackHandler, Subject serviceSubject, boolean allowLazyAuthentication, IdentityService identityService)
|
||||
{
|
||||
// TODO maybe pass this in via setConfiguration ?
|
||||
if (callbackHandler == null)
|
||||
throw new NullPointerException("No CallbackHandler");
|
||||
if (authConfig == null)
|
||||
throw new NullPointerException("No AuthConfig");
|
||||
this._authConfig = authConfig;
|
||||
this._authProperties = authProperties;
|
||||
this._callbackHandler = callbackHandler;
|
||||
this._serviceSubject = serviceSubject;
|
||||
this._allowLazyAuthentication = allowLazyAuthentication;
|
||||
this._identityService = identityService;
|
||||
this._appContext = null;
|
||||
this._authConfig = authConfig;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setConfiguration(AuthConfiguration configuration)
|
||||
{
|
||||
LoginService loginService = configuration.getLoginService();
|
||||
if (loginService == null)
|
||||
{
|
||||
// Add an empty login service so we can use JASPI without tying into Jetty auth mechanisms.
|
||||
configuration = new JaspiAuthConfiguration(configuration);
|
||||
loginService = configuration.getLoginService();
|
||||
}
|
||||
|
||||
super.setConfiguration(configuration);
|
||||
|
||||
// Only do this if the new constructor was used.
|
||||
if (_authConfig == null)
|
||||
{
|
||||
_identityService = configuration.getIdentityService();
|
||||
_callbackHandler = new ServletCallbackHandler(loginService);
|
||||
_authProperties = new HashMap();
|
||||
for (String key : configuration.getInitParameterNames())
|
||||
{
|
||||
_authProperties.put(key, configuration.getInitParameter(key));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private ServerAuthConfig getAuthConfig() throws AuthException
|
||||
{
|
||||
if (_authConfig != null)
|
||||
return _authConfig;
|
||||
|
||||
RegistrationListener listener = (layer, appContext) -> _authConfig = null;
|
||||
AuthConfigProvider authConfigProvider = _authConfigFactory.getConfigProvider(MESSAGE_LAYER, _appContext, listener);
|
||||
if (authConfigProvider == null)
|
||||
{
|
||||
_authConfigFactory.detachListener(listener, MESSAGE_LAYER, _appContext);
|
||||
return null;
|
||||
}
|
||||
|
||||
_authConfig = authConfigProvider.getServerAuthConfig(MESSAGE_LAYER, _appContext, _callbackHandler);
|
||||
return _authConfig;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -124,8 +171,12 @@ public class JaspiAuthenticator extends LoginAuthenticator
|
|||
{
|
||||
try
|
||||
{
|
||||
String authContextId = _authConfig.getAuthContextID(messageInfo);
|
||||
ServerAuthContext authContext = _authConfig.getAuthContext(authContextId, _serviceSubject, _authProperties);
|
||||
ServerAuthConfig authConfig = getAuthConfig();
|
||||
if (authConfig == null)
|
||||
throw new ServerAuthException("No ServerAuthConfig");
|
||||
|
||||
String authContextId = authConfig.getAuthContextID(messageInfo);
|
||||
ServerAuthContext authContext = authConfig.getAuthContext(authContextId, _serviceSubject, _authProperties);
|
||||
Subject clientSubject = new Subject();
|
||||
|
||||
AuthStatus authStatus = authContext.validateRequest(messageInfo, clientSubject, _serviceSubject);
|
||||
|
@ -154,6 +205,8 @@ public class JaspiAuthenticator extends LoginAuthenticator
|
|||
if (principal == null)
|
||||
{
|
||||
String principalName = principalCallback.getName();
|
||||
|
||||
// TODO: if the Principal class is provided it doesn't need to be in subject, why do we enforce this here?
|
||||
Set<Principal> principals = principalCallback.getSubject().getPrincipals();
|
||||
for (Principal p : principals)
|
||||
{
|
||||
|
@ -214,8 +267,12 @@ public class JaspiAuthenticator extends LoginAuthenticator
|
|||
{
|
||||
try
|
||||
{
|
||||
String authContextId = _authConfig.getAuthContextID(messageInfo);
|
||||
ServerAuthContext authContext = _authConfig.getAuthContext(authContextId, _serviceSubject, _authProperties);
|
||||
ServerAuthConfig authConfig = getAuthConfig();
|
||||
if (authConfig == null)
|
||||
throw new NullPointerException("no ServerAuthConfig found for context");
|
||||
|
||||
String authContextId = authConfig.getAuthContextID(messageInfo);
|
||||
ServerAuthContext authContext = authConfig.getAuthContext(authContextId, _serviceSubject, _authProperties);
|
||||
// TODO
|
||||
// authContext.cleanSubject(messageInfo,validatedUser.getUserIdentity().getSubject());
|
||||
AuthStatus status = authContext.secureResponse(messageInfo, _serviceSubject);
|
||||
|
@ -226,4 +283,20 @@ public class JaspiAuthenticator extends LoginAuthenticator
|
|||
throw new ServerAuthException(e);
|
||||
}
|
||||
}
|
||||
|
||||
private static class JaspiAuthConfiguration extends WrappedAuthConfiguration
|
||||
{
|
||||
private final LoginService loginService = new EmptyLoginService();
|
||||
|
||||
public JaspiAuthConfiguration(AuthConfiguration configuration)
|
||||
{
|
||||
super(configuration);
|
||||
}
|
||||
|
||||
@Override
|
||||
public LoginService getLoginService()
|
||||
{
|
||||
return loginService;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -14,16 +14,10 @@
|
|||
package org.eclipse.jetty.security.jaspi;
|
||||
|
||||
import java.security.Principal;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import javax.security.auth.Subject;
|
||||
import javax.security.auth.message.AuthException;
|
||||
import javax.security.auth.message.config.AuthConfigFactory;
|
||||
import javax.security.auth.message.config.AuthConfigProvider;
|
||||
import javax.security.auth.message.config.RegistrationListener;
|
||||
import javax.security.auth.message.config.ServerAuthConfig;
|
||||
import javax.servlet.ServletContext;
|
||||
|
||||
import org.eclipse.jetty.security.Authenticator;
|
||||
|
@ -32,14 +26,29 @@ import org.eclipse.jetty.security.DefaultAuthenticatorFactory;
|
|||
import org.eclipse.jetty.security.IdentityService;
|
||||
import org.eclipse.jetty.security.LoginService;
|
||||
import org.eclipse.jetty.server.Server;
|
||||
import org.eclipse.jetty.util.StringUtil;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* Javax Authentication (JASPI) Authenticator Factory.
|
||||
*
|
||||
* This is used to link a jetty-security {@link Authenticator.Factory} to a Javax Authentication {@link AuthConfigFactory}.
|
||||
* <p>
|
||||
* This should be initialized with the provided {@link DefaultAuthConfigFactory} to set up Javax Authentication {@link AuthConfigFactory} before use.
|
||||
* (A different {@link AuthConfigFactory} may also be provided using the same steps below)
|
||||
* <p>
|
||||
* To initialize either:
|
||||
* <ul>
|
||||
* <li>invoke {@link AuthConfigFactory#setFactory(AuthConfigFactory)}</li>
|
||||
* <li>Alternatively: set {@link AuthConfigFactory#DEFAULT_FACTORY_SECURITY_PROPERTY}</li>
|
||||
* </ul>
|
||||
*
|
||||
*/
|
||||
public class JaspiAuthenticatorFactory extends DefaultAuthenticatorFactory
|
||||
{
|
||||
private static final Logger LOG = LoggerFactory.getLogger(JaspiAuthenticatorFactory.class);
|
||||
|
||||
private static String MESSAGE_LAYER = "HTTP";
|
||||
public static final String MESSAGE_LAYER = "HttpServlet";
|
||||
|
||||
private Subject _serviceSubject;
|
||||
private String _serverName;
|
||||
|
@ -79,52 +88,26 @@ public class JaspiAuthenticatorFactory extends DefaultAuthenticatorFactory
|
|||
@Override
|
||||
public Authenticator getAuthenticator(Server server, ServletContext context, AuthConfiguration configuration, IdentityService identityService, LoginService loginService)
|
||||
{
|
||||
Authenticator authenticator = null;
|
||||
try
|
||||
{
|
||||
AuthConfigFactory authConfigFactory = AuthConfigFactory.getFactory();
|
||||
RegistrationListener listener = (layer, appContext) ->
|
||||
{
|
||||
};
|
||||
AuthConfigFactory factory = AuthConfigFactory.getFactory();
|
||||
if (factory == null)
|
||||
return null;
|
||||
|
||||
Subject serviceSubject = findServiceSubject(server);
|
||||
String serverName = findServerName(server);
|
||||
String serverName = findServerName(context, server);
|
||||
Subject serviceSubject = findServiceSubject(server);
|
||||
String contextPath = StringUtil.isEmpty(context.getContextPath()) ? "/" : context.getContextPath();
|
||||
String appContext = serverName + " " + contextPath;
|
||||
|
||||
String contextPath = context.getContextPath();
|
||||
if (contextPath == null || contextPath.length() == 0)
|
||||
contextPath = "/";
|
||||
String appContext = serverName + " " + contextPath;
|
||||
// We will only create the Authenticator if an AuthConfigProvider matches this context.
|
||||
if (factory.getConfigProvider(MESSAGE_LAYER, appContext, null) == null)
|
||||
return null;
|
||||
|
||||
AuthConfigProvider authConfigProvider = authConfigFactory.getConfigProvider(MESSAGE_LAYER, appContext, listener);
|
||||
|
||||
if (authConfigProvider != null)
|
||||
{
|
||||
ServletCallbackHandler servletCallbackHandler = new ServletCallbackHandler(loginService);
|
||||
ServerAuthConfig serverAuthConfig = authConfigProvider.getServerAuthConfig(MESSAGE_LAYER, appContext, servletCallbackHandler);
|
||||
if (serverAuthConfig != null)
|
||||
{
|
||||
Map map = new HashMap();
|
||||
for (String key : configuration.getInitParameterNames())
|
||||
{
|
||||
map.put(key, configuration.getInitParameter(key));
|
||||
}
|
||||
authenticator = new JaspiAuthenticator(serverAuthConfig, map, servletCallbackHandler,
|
||||
serviceSubject, true, identityService);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (AuthException e)
|
||||
{
|
||||
LOG.warn("Failed to get ServerAuthConfig", e);
|
||||
}
|
||||
return authenticator;
|
||||
return new JaspiAuthenticator(serviceSubject, appContext, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Find a service Subject.
|
||||
* If {@link #setServiceSubject(Subject)} has not been used to
|
||||
* set a subject, then the {@link Server#getBeans(Class)} method is
|
||||
* used to look for a Subject.
|
||||
* Find a service Subject. If {@link #setServiceSubject(Subject)} has not been
|
||||
* used to set a subject, then the {@link Server#getBeans(Class)} method is used
|
||||
* to look for a Subject.
|
||||
*
|
||||
* @param server the server to pull the Subject from
|
||||
* @return the subject
|
||||
|
@ -140,18 +123,24 @@ public class JaspiAuthenticatorFactory extends DefaultAuthenticatorFactory
|
|||
}
|
||||
|
||||
/**
|
||||
* Find a servername.
|
||||
* If {@link #setServerName(String)} has not been called, then
|
||||
* use the name of the a principal in the service subject.
|
||||
* If not found, return "server".
|
||||
* Find a servername. If {@link #setServerName(String)} has not been called,
|
||||
* then use the virtualServerName of the context.
|
||||
* If this is also null, then use the name of the a principal in the service subject.
|
||||
* If none are found, return "server".
|
||||
* @param context
|
||||
*
|
||||
* @param server the server to find the name of
|
||||
* @return the server name from the service Subject (or default value if not found in subject or principals)
|
||||
* @return the server name from the service Subject (or default value if not
|
||||
* found in subject or principals)
|
||||
*/
|
||||
protected String findServerName(Server server)
|
||||
{
|
||||
protected String findServerName(ServletContext context, Server server)
|
||||
{
|
||||
if (_serverName != null)
|
||||
return _serverName;
|
||||
|
||||
String virtualServerName = context.getVirtualServerName();
|
||||
if (virtualServerName != null)
|
||||
return virtualServerName;
|
||||
|
||||
Subject subject = findServiceSubject(server);
|
||||
if (subject != null)
|
||||
|
|
|
@ -33,14 +33,13 @@ import org.eclipse.jetty.security.jaspi.callback.CredentialValidationCallback;
|
|||
import org.eclipse.jetty.server.UserIdentity;
|
||||
|
||||
/**
|
||||
* Idiot class required by jaspi stupidity
|
||||
* This {@link CallbackHandler} will bridge {@link Callback}s to handle to the given to the Jetty {@link LoginService}.
|
||||
*/
|
||||
public class ServletCallbackHandler implements CallbackHandler
|
||||
{
|
||||
private final LoginService _loginService;
|
||||
|
||||
private final ThreadLocal<CallerPrincipalCallback> _callerPrincipals = new ThreadLocal<CallerPrincipalCallback>();
|
||||
private final ThreadLocal<GroupPrincipalCallback> _groupPrincipals = new ThreadLocal<GroupPrincipalCallback>();
|
||||
private final ThreadLocal<CallerPrincipalCallback> _callerPrincipals = new ThreadLocal<>();
|
||||
private final ThreadLocal<GroupPrincipalCallback> _groupPrincipals = new ThreadLocal<>();
|
||||
|
||||
public ServletCallbackHandler(LoginService loginService)
|
||||
{
|
||||
|
@ -64,6 +63,7 @@ public class ServletCallbackHandler implements CallbackHandler
|
|||
else if (callback instanceof PasswordValidationCallback)
|
||||
{
|
||||
PasswordValidationCallback passwordValidationCallback = (PasswordValidationCallback)callback;
|
||||
@SuppressWarnings("unused")
|
||||
Subject subject = passwordValidationCallback.getSubject();
|
||||
|
||||
UserIdentity user = _loginService.login(passwordValidationCallback.getUsername(), passwordValidationCallback.getPassword(), null);
|
||||
|
|
|
@ -15,11 +15,14 @@ package org.eclipse.jetty.security.jaspi;
|
|||
|
||||
import java.util.Map;
|
||||
import javax.security.auth.Subject;
|
||||
import javax.security.auth.message.AuthException;
|
||||
import javax.security.auth.message.MessageInfo;
|
||||
import javax.security.auth.message.config.ServerAuthConfig;
|
||||
import javax.security.auth.message.config.ServerAuthContext;
|
||||
|
||||
/**
|
||||
* @deprecated use {@link org.eclipse.jetty.security.jaspi.provider.JaspiAuthConfigProvider}.
|
||||
*/
|
||||
@Deprecated
|
||||
public class SimpleAuthConfig implements ServerAuthConfig
|
||||
{
|
||||
public static final String HTTP_SERVLET = "HttpServlet";
|
||||
|
@ -35,7 +38,7 @@ public class SimpleAuthConfig implements ServerAuthConfig
|
|||
}
|
||||
|
||||
@Override
|
||||
public ServerAuthContext getAuthContext(String authContextID, Subject serviceSubject, Map properties) throws AuthException
|
||||
public ServerAuthContext getAuthContext(String authContextID, Subject serviceSubject, Map properties)
|
||||
{
|
||||
return _serverAuthContext;
|
||||
}
|
||||
|
|
|
@ -39,7 +39,11 @@ import org.eclipse.jetty.security.jaspi.callback.CredentialValidationCallback;
|
|||
import org.eclipse.jetty.util.security.Credential;
|
||||
import org.eclipse.jetty.util.security.Password;
|
||||
|
||||
public class BaseAuthModule implements ServerAuthModule, ServerAuthContext
|
||||
/**
|
||||
* Simple abstract module implementing a Javax Authentication {@link ServerAuthModule} and {@link ServerAuthContext}.
|
||||
* To be used as a building block for building more sophisticated auth modules.
|
||||
*/
|
||||
public abstract class BaseAuthModule implements ServerAuthModule, ServerAuthContext
|
||||
{
|
||||
private static final Class[] SUPPORTED_MESSAGE_TYPES = new Class[]{HttpServletRequest.class, HttpServletResponse.class};
|
||||
|
||||
|
@ -92,12 +96,6 @@ public class BaseAuthModule implements ServerAuthModule, ServerAuthContext
|
|||
return AuthStatus.SEND_SUCCESS;
|
||||
}
|
||||
|
||||
@Override
|
||||
public AuthStatus validateRequest(MessageInfo messageInfo, Subject clientSubject, Subject serviceSubject) throws AuthException
|
||||
{
|
||||
return AuthStatus.SEND_FAILURE;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param messageInfo message info to examine for mandatory flag
|
||||
* @return whether authentication is mandatory or optional
|
||||
|
@ -110,9 +108,7 @@ public class BaseAuthModule implements ServerAuthModule, ServerAuthContext
|
|||
return Boolean.parseBoolean(mandatory);
|
||||
}
|
||||
|
||||
protected boolean login(Subject clientSubject, String credentials,
|
||||
String authMethod, MessageInfo messageInfo)
|
||||
throws IOException, UnsupportedCallbackException
|
||||
protected boolean login(Subject clientSubject, String credentials, String authMethod, MessageInfo messageInfo) throws IOException, UnsupportedCallbackException
|
||||
{
|
||||
credentials = credentials.substring(credentials.indexOf(' ') + 1);
|
||||
credentials = new String(Base64.getDecoder().decode(credentials), StandardCharsets.ISO_8859_1);
|
||||
|
@ -122,10 +118,7 @@ public class BaseAuthModule implements ServerAuthModule, ServerAuthContext
|
|||
return login(clientSubject, userName, new Password(password), authMethod, messageInfo);
|
||||
}
|
||||
|
||||
protected boolean login(Subject clientSubject, String username,
|
||||
Credential credential, String authMethod,
|
||||
MessageInfo messageInfo)
|
||||
throws IOException, UnsupportedCallbackException
|
||||
protected boolean login(Subject clientSubject, String username, Credential credential, String authMethod, MessageInfo messageInfo) throws IOException, UnsupportedCallbackException
|
||||
{
|
||||
CredentialValidationCallback credValidationCallback = new CredentialValidationCallback(clientSubject, username, credential);
|
||||
callbackHandler.handle(new Callback[]{credValidationCallback});
|
||||
|
|
|
@ -11,7 +11,7 @@
|
|||
// ========================================================================
|
||||
//
|
||||
|
||||
package org.eclipse.jetty.security.jaspi;
|
||||
package org.eclipse.jetty.security.jaspi.modules;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Map;
|
||||
|
@ -22,46 +22,45 @@ import javax.security.auth.message.AuthException;
|
|||
import javax.security.auth.message.AuthStatus;
|
||||
import javax.security.auth.message.MessageInfo;
|
||||
import javax.security.auth.message.MessagePolicy;
|
||||
import javax.security.auth.message.module.ServerAuthModule;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.eclipse.jetty.http.HttpHeader;
|
||||
import org.eclipse.jetty.security.jaspi.modules.BaseAuthModule;
|
||||
import org.eclipse.jetty.util.security.Constraint;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
public class BasicAuthModule extends BaseAuthModule
|
||||
/**
|
||||
* A {@link ServerAuthModule} implementation of HTTP Basic Authentication.
|
||||
*/
|
||||
public class BasicAuthenticationAuthModule extends BaseAuthModule
|
||||
{
|
||||
private static final Logger LOG = LoggerFactory.getLogger(BasicAuthModule.class);
|
||||
private static final Logger LOG = LoggerFactory.getLogger(BasicAuthenticationAuthModule.class);
|
||||
|
||||
private String realmName;
|
||||
|
||||
private static final String REALM_KEY = "org.eclipse.jetty.security.jaspi.modules.RealmName";
|
||||
|
||||
public BasicAuthModule()
|
||||
public BasicAuthenticationAuthModule()
|
||||
{
|
||||
}
|
||||
|
||||
public BasicAuthModule(CallbackHandler callbackHandler, String realmName)
|
||||
public BasicAuthenticationAuthModule(CallbackHandler callbackHandler, String realmName)
|
||||
{
|
||||
super(callbackHandler);
|
||||
this.realmName = realmName;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initialize(MessagePolicy requestPolicy, MessagePolicy responsePolicy,
|
||||
CallbackHandler handler, Map options)
|
||||
throws AuthException
|
||||
public void initialize(MessagePolicy requestPolicy, MessagePolicy responsePolicy, CallbackHandler callbackHandler, Map options) throws AuthException
|
||||
{
|
||||
super.initialize(requestPolicy, responsePolicy, handler, options);
|
||||
super.initialize(requestPolicy, responsePolicy, callbackHandler, options);
|
||||
realmName = (String)options.get(REALM_KEY);
|
||||
}
|
||||
|
||||
@Override
|
||||
public AuthStatus validateRequest(MessageInfo messageInfo, Subject clientSubject,
|
||||
Subject serviceSubject)
|
||||
throws AuthException
|
||||
public AuthStatus validateRequest(MessageInfo messageInfo, Subject clientSubject, Subject serviceSubject) throws AuthException
|
||||
{
|
||||
HttpServletRequest request = (HttpServletRequest)messageInfo.getRequestMessage();
|
||||
HttpServletResponse response = (HttpServletResponse)messageInfo.getResponseMessage();
|
||||
|
@ -87,11 +86,7 @@ public class BasicAuthModule extends BaseAuthModule
|
|||
response.sendError(HttpServletResponse.SC_UNAUTHORIZED);
|
||||
return AuthStatus.SEND_CONTINUE;
|
||||
}
|
||||
catch (IOException e)
|
||||
{
|
||||
throw new AuthException(e.getMessage());
|
||||
}
|
||||
catch (UnsupportedCallbackException e)
|
||||
catch (IOException | UnsupportedCallbackException e)
|
||||
{
|
||||
throw new AuthException(e.getMessage());
|
||||
}
|
|
@ -1,45 +0,0 @@
|
|||
//
|
||||
// ========================================================================
|
||||
// Copyright (c) 1995-2021 Mort Bay Consulting Pty Ltd and others.
|
||||
//
|
||||
// This program and the accompanying materials are made available under the
|
||||
// terms of the Eclipse Public License v. 2.0 which is available at
|
||||
// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
|
||||
// which is available at https://www.apache.org/licenses/LICENSE-2.0.
|
||||
//
|
||||
// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
|
||||
// ========================================================================
|
||||
//
|
||||
|
||||
package org.eclipse.jetty.security.jaspi.modules;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
public class UserInfo
|
||||
{
|
||||
private final String userName;
|
||||
|
||||
private char[] password;
|
||||
|
||||
public UserInfo(String userName, char[] password)
|
||||
{
|
||||
this.userName = userName;
|
||||
this.password = password;
|
||||
}
|
||||
|
||||
public String getUserName()
|
||||
{
|
||||
return userName;
|
||||
}
|
||||
|
||||
public char[] getPassword()
|
||||
{
|
||||
return password;
|
||||
}
|
||||
|
||||
public void clearPassword()
|
||||
{
|
||||
Arrays.fill(password, (char)0);
|
||||
password = null;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,132 @@
|
|||
//
|
||||
// ========================================================================
|
||||
// Copyright (c) 1995-2021 Mort Bay Consulting Pty Ltd and others.
|
||||
//
|
||||
// This program and the accompanying materials are made available under the
|
||||
// terms of the Eclipse Public License v. 2.0 which is available at
|
||||
// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
|
||||
// which is available at https://www.apache.org/licenses/LICENSE-2.0.
|
||||
//
|
||||
// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
|
||||
// ========================================================================
|
||||
//
|
||||
|
||||
package org.eclipse.jetty.security.jaspi.provider;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import javax.security.auth.callback.CallbackHandler;
|
||||
import javax.security.auth.message.config.AuthConfigFactory;
|
||||
import javax.security.auth.message.config.AuthConfigProvider;
|
||||
import javax.security.auth.message.config.ClientAuthConfig;
|
||||
import javax.security.auth.message.config.ServerAuthConfig;
|
||||
import javax.security.auth.message.module.ServerAuthModule;
|
||||
|
||||
import org.eclipse.jetty.security.jaspi.JaspiAuthenticatorFactory;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* <p>A Jetty implementation of the {@link AuthConfigProvider} to allow registration of a {@link ServerAuthModule}
|
||||
* directly without having to write a custom {@link AuthConfigProvider}.</p>
|
||||
* <p>If this is being constructed by an {@link AuthConfigFactory} after being passed in as a className, then
|
||||
* you will need to provide the property {@code ServerAuthModule} containing the fully qualified name of
|
||||
* the {@link ServerAuthModule} class you wish to use.</p>
|
||||
*/
|
||||
@SuppressWarnings("rawtypes")
|
||||
public class JaspiAuthConfigProvider implements AuthConfigProvider
|
||||
{
|
||||
private static final Logger LOG = LoggerFactory.getLogger(JaspiAuthConfigProvider.class);
|
||||
private final Map providerProperties;
|
||||
private final ServerAuthModule serverAuthModule;
|
||||
|
||||
/**
|
||||
* <p>Constructor with signature and implementation that's required by API.</p>
|
||||
* <p>The property map must contain the {@code ServerAuthModule} property containing the fully qualified name of
|
||||
* the {@link ServerAuthModule} class you wish to use. If this constructor is being used for self-registration an
|
||||
* optional property of {@code appContext} can be used specify the appContext value to register the provider.</p>
|
||||
*
|
||||
* @param properties A Map of initialization properties.
|
||||
* @param factory The {@link AuthConfigFactory} to register on.
|
||||
*/
|
||||
public JaspiAuthConfigProvider(Map properties, AuthConfigFactory factory)
|
||||
{
|
||||
if (properties == null || !properties.containsKey("ServerAuthModule"))
|
||||
throw new IllegalArgumentException("Missing property 'ServerAuthModule', cannot create JaspiAuthConfigProvider");
|
||||
|
||||
this.providerProperties = Map.copyOf(properties);
|
||||
this.serverAuthModule = createServerAuthModule((String)properties.get("ServerAuthModule"));
|
||||
|
||||
// API requires self registration if factory is provided.
|
||||
if (factory != null)
|
||||
factory.registerConfigProvider(this, JaspiAuthenticatorFactory.MESSAGE_LAYER, (String)properties.get("appContext"), "Self Registration");
|
||||
}
|
||||
|
||||
/**
|
||||
* @param className The fully qualified name of a {@link ServerAuthModule} class.
|
||||
*/
|
||||
public JaspiAuthConfigProvider(String className)
|
||||
{
|
||||
this(className, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param className The fully qualified name of a {@link ServerAuthModule} class.
|
||||
* @param properties A Map of initialization properties.
|
||||
*/
|
||||
public JaspiAuthConfigProvider(String className, Map properties)
|
||||
{
|
||||
this(createServerAuthModule(className), properties);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param serverAuthModule The instance of {@link ServerAuthModule} to use.
|
||||
*/
|
||||
public JaspiAuthConfigProvider(ServerAuthModule serverAuthModule)
|
||||
{
|
||||
this.serverAuthModule = Objects.requireNonNull(serverAuthModule);
|
||||
this.providerProperties = Collections.emptyMap();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param serverAuthModule The instance of {@link ServerAuthModule} to use.
|
||||
* @param properties A Map of initialization properties.
|
||||
*/
|
||||
public JaspiAuthConfigProvider(ServerAuthModule serverAuthModule, Map properties)
|
||||
{
|
||||
this.serverAuthModule = Objects.requireNonNull(serverAuthModule);
|
||||
this.providerProperties = properties == null ? Collections.emptyMap() : Map.copyOf(properties);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ClientAuthConfig getClientAuthConfig(String layer, String appContext, CallbackHandler handler)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ServerAuthConfig getServerAuthConfig(String layer, String appContext, CallbackHandler handler)
|
||||
{
|
||||
if (LOG.isDebugEnabled())
|
||||
LOG.debug("getServerAuthConfig");
|
||||
return new SimpleAuthConfig(layer, appContext, handler, providerProperties, serverAuthModule);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void refresh()
|
||||
{
|
||||
}
|
||||
|
||||
private static ServerAuthModule createServerAuthModule(String serverAuthModuleClassName)
|
||||
{
|
||||
try
|
||||
{
|
||||
return (ServerAuthModule)Class.forName(serverAuthModuleClassName).getDeclaredConstructor().newInstance();
|
||||
}
|
||||
catch (ReflectiveOperationException e)
|
||||
{
|
||||
throw new IllegalStateException(e);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,83 @@
|
|||
//
|
||||
// ========================================================================
|
||||
// Copyright (c) 1995-2021 Mort Bay Consulting Pty Ltd and others.
|
||||
//
|
||||
// This program and the accompanying materials are made available under the
|
||||
// terms of the Eclipse Public License v. 2.0 which is available at
|
||||
// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
|
||||
// which is available at https://www.apache.org/licenses/LICENSE-2.0.
|
||||
//
|
||||
// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
|
||||
// ========================================================================
|
||||
//
|
||||
|
||||
package org.eclipse.jetty.security.jaspi.provider;
|
||||
|
||||
import java.util.Map;
|
||||
import javax.security.auth.Subject;
|
||||
import javax.security.auth.callback.CallbackHandler;
|
||||
import javax.security.auth.message.AuthException;
|
||||
import javax.security.auth.message.MessageInfo;
|
||||
import javax.security.auth.message.config.ServerAuthConfig;
|
||||
import javax.security.auth.message.config.ServerAuthContext;
|
||||
import javax.security.auth.message.module.ServerAuthModule;
|
||||
|
||||
/**
|
||||
* Simple implementation of the {@link ServerAuthConfig} interface.
|
||||
*
|
||||
* This implementation wires up the given {@link ServerAuthModule} to the appropriate Javax Authentication {@link ServerAuthContext} responsible
|
||||
* for providing it.
|
||||
*/
|
||||
@SuppressWarnings("rawtypes")
|
||||
class SimpleAuthConfig implements ServerAuthConfig
|
||||
{
|
||||
private final String _messageLayer;
|
||||
private final String _appContext;
|
||||
private final CallbackHandler _callbackHandler;
|
||||
private final Map _properties;
|
||||
private final ServerAuthModule _serverAuthModule;
|
||||
|
||||
public SimpleAuthConfig(String messageLayer, String appContext, CallbackHandler callbackHandler, Map properties, ServerAuthModule serverAuthModule)
|
||||
{
|
||||
_messageLayer = messageLayer;
|
||||
_appContext = appContext;
|
||||
_callbackHandler = callbackHandler;
|
||||
_properties = properties;
|
||||
_serverAuthModule = serverAuthModule;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ServerAuthContext getAuthContext(String authContextID, Subject serviceSubject, Map properties) throws AuthException
|
||||
{
|
||||
return new SimpleServerAuthContext(_callbackHandler, _serverAuthModule, _properties);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getAppContext()
|
||||
{
|
||||
return _appContext;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getAuthContextID(MessageInfo messageInfo)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getMessageLayer()
|
||||
{
|
||||
return _messageLayer;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isProtected()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void refresh()
|
||||
{
|
||||
}
|
||||
}
|
|
@ -0,0 +1,58 @@
|
|||
//
|
||||
// ========================================================================
|
||||
// Copyright (c) 1995-2021 Mort Bay Consulting Pty Ltd and others.
|
||||
//
|
||||
// This program and the accompanying materials are made available under the
|
||||
// terms of the Eclipse Public License v. 2.0 which is available at
|
||||
// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
|
||||
// which is available at https://www.apache.org/licenses/LICENSE-2.0.
|
||||
//
|
||||
// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
|
||||
// ========================================================================
|
||||
//
|
||||
|
||||
package org.eclipse.jetty.security.jaspi.provider;
|
||||
|
||||
import java.util.Map;
|
||||
import javax.security.auth.Subject;
|
||||
import javax.security.auth.callback.CallbackHandler;
|
||||
import javax.security.auth.message.AuthException;
|
||||
import javax.security.auth.message.AuthStatus;
|
||||
import javax.security.auth.message.MessageInfo;
|
||||
import javax.security.auth.message.config.ServerAuthContext;
|
||||
import javax.security.auth.message.module.ServerAuthModule;
|
||||
|
||||
/**
|
||||
* Simple bridge implementation of the Javax Authentication {@link ServerAuthContext} interface.
|
||||
*
|
||||
* This implementation will only delegate to the provided {@link ServerAuthModule} implementation.
|
||||
*/
|
||||
class SimpleServerAuthContext implements ServerAuthContext
|
||||
{
|
||||
private final ServerAuthModule serverAuthModule;
|
||||
|
||||
@SuppressWarnings("rawtypes")
|
||||
public SimpleServerAuthContext(CallbackHandler callbackHandler, ServerAuthModule serverAuthModule, Map properties) throws AuthException
|
||||
{
|
||||
this.serverAuthModule = serverAuthModule;
|
||||
serverAuthModule.initialize(null, null, callbackHandler, properties);
|
||||
}
|
||||
|
||||
@Override
|
||||
public AuthStatus validateRequest(MessageInfo messageInfo, Subject clientSubject, Subject serviceSubject) throws AuthException
|
||||
{
|
||||
return serverAuthModule.validateRequest(messageInfo, clientSubject, serviceSubject);
|
||||
}
|
||||
|
||||
@Override
|
||||
public AuthStatus secureResponse(MessageInfo messageInfo, Subject serviceSubject) throws AuthException
|
||||
{
|
||||
return serverAuthModule.secureResponse(messageInfo, serviceSubject);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void cleanSubject(MessageInfo messageInfo, Subject subject) throws AuthException
|
||||
{
|
||||
serverAuthModule.cleanSubject(messageInfo, subject);
|
||||
}
|
||||
}
|
|
@ -1 +1 @@
|
|||
org.eclipse.jetty.security.jaspi.JaspiAuthenticatorFactory
|
||||
org.eclipse.jetty.security.jaspi.JaspiAuthenticatorFactory
|
||||
|
|
|
@ -0,0 +1,115 @@
|
|||
//
|
||||
// ========================================================================
|
||||
// Copyright (c) 1995-2021 Mort Bay Consulting Pty Ltd and others.
|
||||
//
|
||||
// This program and the accompanying materials are made available under the
|
||||
// terms of the Eclipse Public License v. 2.0 which is available at
|
||||
// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
|
||||
// which is available at https://www.apache.org/licenses/LICENSE-2.0.
|
||||
//
|
||||
// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
|
||||
// ========================================================================
|
||||
//
|
||||
|
||||
package org.eclipse.jetty.security.jaspi;
|
||||
|
||||
import java.util.Map;
|
||||
import javax.security.auth.message.config.AuthConfigFactory;
|
||||
import javax.security.auth.message.config.AuthConfigProvider;
|
||||
import javax.security.auth.message.config.RegistrationListener;
|
||||
|
||||
import org.eclipse.jetty.security.jaspi.provider.JaspiAuthConfigProvider;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import static org.hamcrest.MatcherAssert.assertThat;
|
||||
import static org.hamcrest.Matchers.arrayContaining;
|
||||
import static org.hamcrest.Matchers.equalTo;
|
||||
import static org.hamcrest.Matchers.instanceOf;
|
||||
import static org.hamcrest.Matchers.notNullValue;
|
||||
import static org.hamcrest.Matchers.nullValue;
|
||||
|
||||
public class DefaultAuthConfigFactoryTest
|
||||
{
|
||||
|
||||
private static final String MESSAGE_LAYER = "HttpServlet";
|
||||
|
||||
private final String jettyAuthConfigProvider = "org.eclipse.jetty.security.jaspi.provider.JaspiAuthConfigProvider";
|
||||
private final String appContext = "server /test";
|
||||
|
||||
private final Map<String, String> serverAuthModuleProperties = Map.of("ServerAuthModule",
|
||||
"org.eclipse.jetty.security.jaspi.modules.BasicAuthenticationAuthModule", "AppContextID", appContext,
|
||||
"org.eclipse.jetty.security.jaspi.modules.RealmName", "TestRealm");
|
||||
|
||||
private final String serverAuthModuleClassName = "org.eclipse.jetty.security.jaspi.modules.BasicAuthenticationAuthModule";
|
||||
|
||||
@Test
|
||||
public void testRegisterConfigProviderByClassName() throws Exception
|
||||
{
|
||||
AuthConfigFactory factory = new DefaultAuthConfigFactory();
|
||||
String registrationId = factory.registerConfigProvider(jettyAuthConfigProvider,
|
||||
serverAuthModuleProperties, MESSAGE_LAYER, appContext, "a test provider");
|
||||
AuthConfigProvider registeredProvider = factory.getConfigProvider(MESSAGE_LAYER, appContext, null);
|
||||
assertThat(registeredProvider, instanceOf(JaspiAuthConfigProvider.class));
|
||||
assertThat(registeredProvider.getServerAuthConfig(MESSAGE_LAYER, appContext, null), notNullValue());
|
||||
|
||||
assertThat(factory.getRegistrationContext(registrationId), notNullValue());
|
||||
assertThat(factory.getRegistrationIDs(registeredProvider), arrayContaining(registrationId));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRegisterAuthConfigProviderDirect() throws Exception
|
||||
{
|
||||
AuthConfigProvider provider = new JaspiAuthConfigProvider(
|
||||
serverAuthModuleClassName,
|
||||
serverAuthModuleProperties);
|
||||
|
||||
AuthConfigFactory factory = new DefaultAuthConfigFactory();
|
||||
String registrationId = factory.registerConfigProvider(provider, MESSAGE_LAYER, appContext, "a test provider");
|
||||
|
||||
AuthConfigProvider registeredProvider = factory.getConfigProvider(MESSAGE_LAYER, appContext, null);
|
||||
assertThat(registeredProvider, instanceOf(JaspiAuthConfigProvider.class));
|
||||
assertThat(registeredProvider.getServerAuthConfig(MESSAGE_LAYER, appContext, null), notNullValue());
|
||||
|
||||
assertThat(factory.getRegistrationContext(registrationId), notNullValue());
|
||||
assertThat(factory.getRegistrationIDs(registeredProvider), arrayContaining(registrationId));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRemoveRegistration() throws Exception
|
||||
{
|
||||
// Arrange
|
||||
AuthConfigProvider provider = new JaspiAuthConfigProvider(
|
||||
serverAuthModuleClassName,
|
||||
serverAuthModuleProperties);
|
||||
|
||||
AuthConfigFactory factory = new DefaultAuthConfigFactory();
|
||||
String registrationId = factory.registerConfigProvider(provider, MESSAGE_LAYER, appContext, "a test provider");
|
||||
|
||||
DummyRegistrationListener dummyListener = new DummyRegistrationListener();
|
||||
assertThat(factory.getConfigProvider(MESSAGE_LAYER, appContext, dummyListener), notNullValue());
|
||||
|
||||
// Act
|
||||
factory.removeRegistration(registrationId);
|
||||
|
||||
// Assert config provider removed
|
||||
assertThat(factory.getConfigProvider(MESSAGE_LAYER, appContext, null), nullValue());
|
||||
|
||||
// Assert listeners invoked
|
||||
assertThat(dummyListener.appContext, equalTo(appContext));
|
||||
assertThat(dummyListener.layer, equalTo(MESSAGE_LAYER));
|
||||
|
||||
}
|
||||
|
||||
static class DummyRegistrationListener implements RegistrationListener
|
||||
{
|
||||
String layer;
|
||||
String appContext;
|
||||
|
||||
@Override
|
||||
public void notify(String layer, String appContext)
|
||||
{
|
||||
this.layer = layer;
|
||||
this.appContext = appContext;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -20,6 +20,7 @@ import java.util.HashMap;
|
|||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.stream.Collectors;
|
||||
import javax.security.auth.message.config.AuthConfigFactory;
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
@ -39,7 +40,9 @@ import org.eclipse.jetty.util.security.Constraint;
|
|||
import org.eclipse.jetty.util.security.Credential;
|
||||
import org.eclipse.jetty.util.security.Password;
|
||||
import org.hamcrest.Matchers;
|
||||
import org.junit.jupiter.api.AfterAll;
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.BeforeAll;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
|
@ -52,7 +55,7 @@ public class JaspiTest
|
|||
Server _server;
|
||||
LocalConnector _connector;
|
||||
|
||||
public class TestLoginService extends AbstractLoginService
|
||||
public static class TestLoginService extends AbstractLoginService
|
||||
{
|
||||
protected Map<String, UserPrincipal> _users = new HashMap<>();
|
||||
protected Map<String, List<RolePrincipal>> _roles = new HashMap<>();
|
||||
|
@ -86,10 +89,34 @@ public class JaspiTest
|
|||
}
|
||||
}
|
||||
|
||||
@BeforeAll
|
||||
public static void beforeAll() throws Exception
|
||||
{
|
||||
AuthConfigFactory factory = new DefaultAuthConfigFactory();
|
||||
|
||||
factory.registerConfigProvider("org.eclipse.jetty.security.jaspi.provider.JaspiAuthConfigProvider",
|
||||
Map.of("ServerAuthModule", "org.eclipse.jetty.security.jaspi.modules.BasicAuthenticationAuthModule",
|
||||
"AppContextID", "server /ctx",
|
||||
"org.eclipse.jetty.security.jaspi.modules.RealmName", "TestRealm"),
|
||||
"HttpServlet", "server /ctx", "a test provider");
|
||||
|
||||
factory.registerConfigProvider("org.eclipse.jetty.security.jaspi.provider.JaspiAuthConfigProvider",
|
||||
Map.of("ServerAuthModule", "org.eclipse.jetty.security.jaspi.HttpHeaderAuthModule",
|
||||
"AppContextID", "server /other"),
|
||||
"HttpServlet", "server /other", "another test provider");
|
||||
|
||||
AuthConfigFactory.setFactory(factory);
|
||||
}
|
||||
|
||||
@AfterAll
|
||||
public static void afterAll() throws Exception
|
||||
{
|
||||
AuthConfigFactory.setFactory(null);
|
||||
}
|
||||
|
||||
@BeforeEach
|
||||
public void before() throws Exception
|
||||
{
|
||||
System.setProperty("org.apache.geronimo.jaspic.configurationFile", "src/test/resources/jaspi.xml");
|
||||
_server = new Server();
|
||||
_connector = new LocalConnector(_server);
|
||||
_server.addConnector(_connector);
|
||||
|
@ -98,8 +125,8 @@ public class JaspiTest
|
|||
_server.setHandler(contexts);
|
||||
|
||||
TestLoginService loginService = new TestLoginService("TestRealm");
|
||||
loginService.putUser("user", new Password("password"), new String[]{"users"});
|
||||
loginService.putUser("admin", new Password("secret"), new String[]{"users", "admins"});
|
||||
loginService.putUser("user", new Password("password"), new String[] {"users"});
|
||||
loginService.putUser("admin", new Password("secret"), new String[] {"users", "admins"});
|
||||
_server.addBean(loginService);
|
||||
|
||||
ContextHandler context = new ContextHandler();
|
||||
|
@ -159,9 +186,8 @@ public class JaspiTest
|
|||
@Test
|
||||
public void testConstraintWrongAuth() throws Exception
|
||||
{
|
||||
String response = _connector.getResponse("GET /ctx/jaspi/test HTTP/1.0\n" +
|
||||
"Authorization: Basic " + Base64.getEncoder().encodeToString("user:wrong".getBytes(ISO_8859_1)) +
|
||||
"\n\n");
|
||||
String response = _connector.getResponse("GET /ctx/jaspi/test HTTP/1.0\n" + "Authorization: Basic " +
|
||||
Base64.getEncoder().encodeToString("user:wrong".getBytes(ISO_8859_1)) + "\n\n");
|
||||
assertThat(response, startsWith("HTTP/1.1 401 Unauthorized"));
|
||||
assertThat(response, Matchers.containsString("WWW-Authenticate: basic realm=\"TestRealm\""));
|
||||
}
|
||||
|
@ -169,9 +195,8 @@ public class JaspiTest
|
|||
@Test
|
||||
public void testConstraintAuth() throws Exception
|
||||
{
|
||||
String response = _connector.getResponse("GET /ctx/jaspi/test HTTP/1.0\n" +
|
||||
"Authorization: Basic " + Base64.getEncoder().encodeToString("user:password".getBytes(ISO_8859_1)) +
|
||||
"\n\n");
|
||||
String response = _connector.getResponse("GET /ctx/jaspi/test HTTP/1.0\n" + "Authorization: Basic " +
|
||||
Base64.getEncoder().encodeToString("user:password".getBytes(ISO_8859_1)) + "\n\n");
|
||||
assertThat(response, startsWith("HTTP/1.1 200 OK"));
|
||||
}
|
||||
|
||||
|
@ -185,8 +210,7 @@ public class JaspiTest
|
|||
@Test
|
||||
public void testOtherAuth() throws Exception
|
||||
{
|
||||
String response = _connector.getResponse("GET /other/test HTTP/1.0\n" +
|
||||
"X-Forwarded-User: user\n\n");
|
||||
String response = _connector.getResponse("GET /other/test HTTP/1.0\n" + "X-Forwarded-User: user\n\n");
|
||||
assertThat(response, startsWith("HTTP/1.1 200 OK"));
|
||||
}
|
||||
|
||||
|
@ -194,7 +218,8 @@ public class JaspiTest
|
|||
{
|
||||
|
||||
@Override
|
||||
public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
|
||||
public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response)
|
||||
throws IOException, ServletException
|
||||
{
|
||||
baseRequest.setHandled(true);
|
||||
response.setStatus(200);
|
||||
|
|
|
@ -1,39 +0,0 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
<jaspi xmlns="http://geronimo.apache.org/xml/ns/geronimo-jaspi">
|
||||
<configProvider>
|
||||
<messageLayer>HTTP</messageLayer>
|
||||
<appContext>server /ctx</appContext>
|
||||
<description>description</description>
|
||||
<serverAuthConfig>
|
||||
<authenticationContextID>authenticationContextID1</authenticationContextID>
|
||||
<protected>true</protected>
|
||||
<serverAuthContext>
|
||||
<serverAuthModule>
|
||||
<className>org.eclipse.jetty.security.jaspi.BasicAuthModule</className>
|
||||
<options>
|
||||
org.eclipse.jetty.security.jaspi.modules.RealmName=TestRealm
|
||||
</options>
|
||||
</serverAuthModule>
|
||||
</serverAuthContext>
|
||||
</serverAuthConfig>
|
||||
<persistent>true</persistent>
|
||||
</configProvider>
|
||||
<configProvider>
|
||||
<messageLayer>HTTP</messageLayer>
|
||||
<appContext>server /other</appContext>
|
||||
<description>description</description>
|
||||
<serverAuthConfig>
|
||||
<authenticationContextID>authenticationContextID2</authenticationContextID>
|
||||
<protected>true</protected>
|
||||
<serverAuthContext>
|
||||
<serverAuthModule>
|
||||
<className>org.eclipse.jetty.security.jaspi.HttpHeaderAuthModule</className>
|
||||
<options>
|
||||
</options>
|
||||
</serverAuthModule>
|
||||
</serverAuthContext>
|
||||
</serverAuthConfig>
|
||||
<persistent>true</persistent>
|
||||
</configProvider>
|
||||
</jaspi>
|
|
@ -0,0 +1,58 @@
|
|||
//
|
||||
// ========================================================================
|
||||
// Copyright (c) 1995-2021 Mort Bay Consulting Pty Ltd and others.
|
||||
//
|
||||
// This program and the accompanying materials are made available under the
|
||||
// terms of the Eclipse Public License v. 2.0 which is available at
|
||||
// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
|
||||
// which is available at https://www.apache.org/licenses/LICENSE-2.0.
|
||||
//
|
||||
// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
|
||||
// ========================================================================
|
||||
//
|
||||
|
||||
package org.eclipse.jetty.security;
|
||||
|
||||
import javax.servlet.ServletRequest;
|
||||
|
||||
import org.eclipse.jetty.server.UserIdentity;
|
||||
|
||||
/**
|
||||
* LoginService implementation which always denies any attempt to login.
|
||||
*/
|
||||
public class EmptyLoginService implements LoginService
|
||||
{
|
||||
@Override
|
||||
public String getName()
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public UserIdentity login(String username, Object credentials, ServletRequest request)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean validate(UserIdentity user)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public IdentityService getIdentityService()
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setIdentityService(IdentityService service)
|
||||
{
|
||||
}
|
||||
|
||||
@Override
|
||||
public void logout(UserIdentity user)
|
||||
{
|
||||
}
|
||||
}
|
|
@ -0,0 +1,74 @@
|
|||
//
|
||||
// ========================================================================
|
||||
// Copyright (c) 1995-2021 Mort Bay Consulting Pty Ltd and others.
|
||||
//
|
||||
// This program and the accompanying materials are made available under the
|
||||
// terms of the Eclipse Public License v. 2.0 which is available at
|
||||
// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
|
||||
// which is available at https://www.apache.org/licenses/LICENSE-2.0.
|
||||
//
|
||||
// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
|
||||
// ========================================================================
|
||||
//
|
||||
|
||||
package org.eclipse.jetty.security;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
import org.eclipse.jetty.security.Authenticator.AuthConfiguration;
|
||||
|
||||
/**
|
||||
* A wrapper for {@link AuthConfiguration}. This allows you create a new AuthConfiguration which can
|
||||
* override a method to change a value from an another instance of AuthConfiguration.
|
||||
*/
|
||||
public class WrappedAuthConfiguration implements AuthConfiguration
|
||||
{
|
||||
private final AuthConfiguration _configuration;
|
||||
|
||||
public WrappedAuthConfiguration(AuthConfiguration configuration)
|
||||
{
|
||||
_configuration = configuration;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getAuthMethod()
|
||||
{
|
||||
return _configuration.getAuthMethod();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getRealmName()
|
||||
{
|
||||
return _configuration.getRealmName();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getInitParameter(String param)
|
||||
{
|
||||
return _configuration.getInitParameter(param);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<String> getInitParameterNames()
|
||||
{
|
||||
return _configuration.getInitParameterNames();
|
||||
}
|
||||
|
||||
@Override
|
||||
public LoginService getLoginService()
|
||||
{
|
||||
return _configuration.getLoginService();
|
||||
}
|
||||
|
||||
@Override
|
||||
public IdentityService getIdentityService()
|
||||
{
|
||||
return _configuration.getIdentityService();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSessionRenewedOnAuthentication()
|
||||
{
|
||||
return _configuration.isSessionRenewedOnAuthentication();
|
||||
}
|
||||
}
|
|
@ -27,6 +27,7 @@ module org.eclipse.jetty.webapp
|
|||
provides Configuration with
|
||||
org.eclipse.jetty.webapp.FragmentConfiguration,
|
||||
org.eclipse.jetty.webapp.JaasConfiguration,
|
||||
org.eclipse.jetty.webapp.JaspiConfiguration,
|
||||
org.eclipse.jetty.webapp.JettyWebXmlConfiguration,
|
||||
org.eclipse.jetty.webapp.JmxConfiguration,
|
||||
org.eclipse.jetty.webapp.JndiConfiguration,
|
||||
|
|
|
@ -0,0 +1,50 @@
|
|||
//
|
||||
// ========================================================================
|
||||
// Copyright (c) 1995-2021 Mort Bay Consulting Pty Ltd and others.
|
||||
//
|
||||
// This program and the accompanying materials are made available under the
|
||||
// terms of the Eclipse Public License v. 2.0 which is available at
|
||||
// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
|
||||
// which is available at https://www.apache.org/licenses/LICENSE-2.0.
|
||||
//
|
||||
// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
|
||||
// ========================================================================
|
||||
//
|
||||
|
||||
package org.eclipse.jetty.webapp;
|
||||
|
||||
import org.eclipse.jetty.util.Loader;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* <p>JASPI Configuration</p>
|
||||
* <p>This configuration configures the WebAppContext server/system classes to
|
||||
* not be able to see the {@code javax.security.auth.message} package.</p>
|
||||
*/
|
||||
public class JaspiConfiguration extends AbstractConfiguration
|
||||
{
|
||||
private static final Logger LOG = LoggerFactory.getLogger(JaspiConfiguration.class);
|
||||
|
||||
public JaspiConfiguration()
|
||||
{
|
||||
addDependencies(WebXmlConfiguration.class, MetaInfConfiguration.class, WebInfConfiguration.class, FragmentConfiguration.class);
|
||||
addDependents(WebAppConfiguration.class);
|
||||
|
||||
hide("javax.security.auth.message.");
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isAvailable()
|
||||
{
|
||||
try
|
||||
{
|
||||
return Loader.loadClass("org.eclipse.jetty.security.jaspi.JaspiAuthenticator") != null;
|
||||
}
|
||||
catch (Throwable e)
|
||||
{
|
||||
LOG.trace("IGNORED", e);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,6 +1,7 @@
|
|||
org.eclipse.jetty.webapp.FragmentConfiguration
|
||||
org.eclipse.jetty.webapp.JettyWebXmlConfiguration
|
||||
org.eclipse.jetty.webapp.JaasConfiguration
|
||||
org.eclipse.jetty.webapp.JaspiConfiguration
|
||||
org.eclipse.jetty.webapp.JmxConfiguration
|
||||
org.eclipse.jetty.webapp.JndiConfiguration
|
||||
org.eclipse.jetty.webapp.JspConfiguration
|
||||
|
|
Loading…
Reference in New Issue