Skip to content

Commit

Permalink
Merge pull request #23841 from arjantijms/implement_authentication_300
Browse files Browse the repository at this point in the history
  • Loading branch information
arjantijms authored Mar 3, 2022
2 parents 237a4ac + b17a103 commit 8429fa4
Show file tree
Hide file tree
Showing 5 changed files with 343 additions and 15 deletions.
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
/*
* Copyright (c) 2022, 2022 Contributors to the Eclipse Foundation.
* Copyright (c) 1997, 2020 Oracle and/or its affiliates. All rights reserved.
*
* This program and the accompanying materials are made available under the
Expand All @@ -17,6 +18,8 @@
package com.sun.jaspic.config.factory;

import java.lang.reflect.Constructor;
import java.security.AccessController;
import java.security.PrivilegedAction;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
Expand All @@ -30,12 +33,14 @@
import java.util.logging.Level;
import java.util.logging.Logger;

import com.sun.jaspic.config.factory.singlemodule.DefaultAuthConfigProvider;
import com.sun.jaspic.config.helper.JASPICLogManager;

import jakarta.security.auth.message.config.AuthConfigFactory;
import jakarta.security.auth.message.config.AuthConfigProvider;
import jakarta.security.auth.message.config.RegistrationListener;
import jakarta.security.auth.message.module.ServerAuthModule;
import jakarta.servlet.ServletContext;


/**
Expand All @@ -46,6 +51,8 @@ public abstract class BaseAuthConfigFactory extends AuthConfigFactory {

private static final Logger logger = Logger.getLogger(JASPICLogManager.JASPIC_LOGGER, JASPICLogManager.RES_BUNDLE);

private static final String CONTEXT_REGISTRATION_ID = "org.glassfish.security.message.registrationId";

private static final ReadWriteLock rwLock = new ReentrantReadWriteLock(true);
public static final Lock rLock = rwLock.readLock();
public static final Lock wLock = rwLock.writeLock();
Expand Down Expand Up @@ -370,18 +377,70 @@ public void refresh() {
}
}

/**
* Gets the app context ID from the servlet context.
*
* <p>
* The app context ID is the ID that Jakarta Authentication associates with the given application.
* In this case that given application is the web application corresponding to the
* ServletContext.
*
* @param context the servlet context for which to obtain the Jakarta Authentication app context ID
* @return the app context ID for the web application corresponding to the given context
*/
public static String getAppContextID(ServletContext context) {
return context.getVirtualServerName() + " " + context.getContextPath();
}

@Override
public String registerServerAuthModule(ServerAuthModule serverAuthModule, Object context) {
// TODO Auto-generated method stub
return null;
if (!(context instanceof ServletContext)) {
return null;
}

ServletContext servletContext = (ServletContext) context;

// Register the factory-factory-factory for the SAM
String registrationId = AccessController.doPrivileged(new PrivilegedAction<String>() {
@Override
public String run() {
return registerConfigProvider(
new DefaultAuthConfigProvider(serverAuthModule),
"HttpServlet",
getAppContextID(servletContext),
"Default single SAM authentication config provider");
}
});

// Remember the registration ID returned by the factory, so we can unregister the JASPIC module when the web module
// is undeployed. JASPIC being the low level API that it is won't do this automatically.
servletContext.setAttribute(CONTEXT_REGISTRATION_ID, registrationId);

return registrationId;
}

@Override
public void removeServerAuthModule(Object context) {
// TODO Auto-generated method stub
if (!(context instanceof ServletContext)) {
return;
}

ServletContext servletContext = (ServletContext) context;

String registrationId = (String) servletContext.getAttribute(CONTEXT_REGISTRATION_ID);
if (!isEmpty(registrationId)) {
AccessController.doPrivileged(new PrivilegedAction<Boolean>() {
@Override
public Boolean run() {
return removeRegistration(registrationId);
}
});
}
}

private static boolean isEmpty(String string) {
return string == null || string.isEmpty();
}

private AuthConfigProvider getConfigProviderUnderLock(String layer, String appContext,
RegistrationListener listener) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
/*
* Copyright (c) 2022, 2022 Contributors to the Eclipse Foundation.
* Copyright (c) 2015, 2020 Oracle and/or its affiliates. All rights reserved.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v. 2.0, which is available at
* http://www.eclipse.org/legal/epl-2.0.
*
* This Source Code may also be made available under the following Secondary
* Licenses when the conditions for such availability set forth in the
* Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
* version 2 with the GNU Classpath Exception, which is available at
* https://www.gnu.org/software/classpath/license.html.
*
* SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
*/

package com.sun.jaspic.config.factory.singlemodule;

import java.util.Map;

import javax.security.auth.callback.CallbackHandler;

import jakarta.security.auth.message.AuthException;
import jakarta.security.auth.message.config.AuthConfigFactory;
import jakarta.security.auth.message.config.AuthConfigProvider;
import jakarta.security.auth.message.config.ClientAuthConfig;
import jakarta.security.auth.message.config.ServerAuthConfig;
import jakarta.security.auth.message.config.ServerAuthContext;
import jakarta.security.auth.message.module.ServerAuthModule;

/**
* This class functions as a kind of factory-factory for {@link ServerAuthConfig} instances, which are by themselves factories
* for {@link ServerAuthContext} instances, which are delegates for the actual {@link ServerAuthModule} (SAM) that we're after.
*
* @author Arjan Tijms
*/
public class DefaultAuthConfigProvider implements AuthConfigProvider {

private static final String CALLBACK_HANDLER_PROPERTY_NAME = "authconfigprovider.client.callbackhandler";

private Map<String, String> providerProperties;
private ServerAuthModule serverAuthModule;

public DefaultAuthConfigProvider(ServerAuthModule serverAuthModule) {
this.serverAuthModule = serverAuthModule;
}

/**
* Constructor with signature and implementation that's required by API.
*
* @param properties provider properties
* @param factory the auth config factory
*/
public DefaultAuthConfigProvider(Map<String, String> properties, AuthConfigFactory factory) {
this.providerProperties = properties;

// API requires self registration if factory is provided. Not clear
// where the "layer" (2nd parameter)
// and especially "appContext" (3rd parameter) values have to come from
// at this place.
if (factory != null) {
// If this method ever gets called, it may throw a SecurityException.
// Don't bother with a PrivilegedAction as we don't expect to ever be
// constructed this way.
factory.registerConfigProvider(this, null, null, "Auto registration");
}
}

/**
* The actual factory method that creates the factory used to eventually obtain the delegate for a SAM.
*/
@Override
public ServerAuthConfig getServerAuthConfig(String layer, String appContext, CallbackHandler handler) throws AuthException,
SecurityException {
return new DefaultServerAuthConfig(layer, appContext, handler == null ? createDefaultCallbackHandler() : handler,
providerProperties, serverAuthModule);
}

@Override
public ClientAuthConfig getClientAuthConfig(String layer, String appContext, CallbackHandler handler) throws AuthException,
SecurityException {
return null;
}

@Override
public void refresh() {
}

/**
* Creates a default callback handler via the system property "authconfigprovider.client.callbackhandler", as seemingly
* required by the API (API uses wording "may" create default handler). TODO: Isn't
* "authconfigprovider.client.callbackhandler" JBoss specific?
*
* @return
* @throws AuthException
*/
private CallbackHandler createDefaultCallbackHandler() throws AuthException {
String callBackClassName = System.getProperty(CALLBACK_HANDLER_PROPERTY_NAME);

if (callBackClassName == null) {
throw new AuthException("No default handler set via system property: " + CALLBACK_HANDLER_PROPERTY_NAME);
}

try {
return (CallbackHandler)
Thread.currentThread()
.getContextClassLoader()
.loadClass(callBackClassName)
.getDeclaredConstructor()
.newInstance();
} catch (Exception e) {
throw new AuthException(e.getMessage());
}
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
/*
* Copyright (c) 2022, 2022 Contributors to the Eclipse Foundation.
* Copyright (c) 2015, 2020 Oracle and/or its affiliates. All rights reserved.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v. 2.0, which is available at
* http://www.eclipse.org/legal/epl-2.0.
*
* This Source Code may also be made available under the following Secondary
* Licenses when the conditions for such availability set forth in the
* Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
* version 2 with the GNU Classpath Exception, which is available at
* https://www.gnu.org/software/classpath/license.html.
*
* SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
*/

package com.sun.jaspic.config.factory.singlemodule;

import java.util.Map;

import javax.security.auth.Subject;
import javax.security.auth.callback.CallbackHandler;

import jakarta.security.auth.message.AuthException;
import jakarta.security.auth.message.MessageInfo;
import jakarta.security.auth.message.config.ServerAuthConfig;
import jakarta.security.auth.message.config.ServerAuthContext;
import jakarta.security.auth.message.module.ServerAuthModule;

/**
* This class functions as a kind of factory for {@link ServerAuthContext} instances, which are delegates for the actual
* {@link ServerAuthModule} (SAM) that we're after.
*
* @author Arjan Tijms
*/
public class DefaultServerAuthConfig implements ServerAuthConfig {

private String layer;
private String appContext;
private CallbackHandler handler;
private Map<String, String> providerProperties;
private ServerAuthModule serverAuthModule;

public DefaultServerAuthConfig(String layer, String appContext, CallbackHandler handler,
Map<String, String> providerProperties, ServerAuthModule serverAuthModule) {
this.layer = layer;
this.appContext = appContext;
this.handler = handler;
this.providerProperties = providerProperties;
this.serverAuthModule = serverAuthModule;
}

@Override
public ServerAuthContext getAuthContext(String authContextID, Subject serviceSubject,
@SuppressWarnings("rawtypes") Map properties) throws AuthException {
return new DefaultServerAuthContext(handler, serverAuthModule);
}

// ### The methods below mostly just return what has been passed into the
// constructor.
// ### In practice they don't seem to be called

@Override
public String getMessageLayer() {
return layer;
}

/**
* It's not entirely clear what the difference is between the "application context identifier" (appContext) and the
* "authentication context identifier" (authContext). In early iterations of the specification, authContext was called
* "operation" and instead of the MessageInfo it was obtained by something called an "authParam".
*/
@Override
public String getAuthContextID(MessageInfo messageInfo) {
return appContext;
}

@Override
public String getAppContext() {
return appContext;
}

@Override
public void refresh() {
}

@Override
public boolean isProtected() {
return false;
}

public Map<String, String> getProviderProperties() {
return providerProperties;
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
/*
* Copyright (c) 2022, 2022 Contributors to the Eclipse Foundation.
* Copyright (c) 2015, 2020 Oracle and/or its affiliates. All rights reserved.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v. 2.0, which is available at
* http://www.eclipse.org/legal/epl-2.0.
*
* This Source Code may also be made available under the following Secondary
* Licenses when the conditions for such availability set forth in the
* Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
* version 2 with the GNU Classpath Exception, which is available at
* https://www.gnu.org/software/classpath/license.html.
*
* SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
*/

package com.sun.jaspic.config.factory.singlemodule;

import java.util.Collections;

import javax.security.auth.Subject;
import javax.security.auth.callback.CallbackHandler;

import jakarta.security.auth.message.AuthException;
import jakarta.security.auth.message.AuthStatus;
import jakarta.security.auth.message.MessageInfo;
import jakarta.security.auth.message.ServerAuth;
import jakarta.security.auth.message.config.ServerAuthContext;
import jakarta.security.auth.message.module.ServerAuthModule;

/**
* The Server Authentication Context is an extra (required) indirection between the Application Server and the actual Server
* Authentication Module (SAM). This can be used to encapsulate any number of SAMs and either select one at run-time, invoke
* them all in order, etc.
* <p>
* Since this simple example only has a single SAM, we delegate directly to that one. Note that this {@link ServerAuthContext}
* and the {@link ServerAuthModule} (SAM) share a common base interface: {@link ServerAuth}.
*
* @author Arjan Tijms
*/
public class DefaultServerAuthContext implements ServerAuthContext {

private final ServerAuthModule serverAuthModule;

public DefaultServerAuthContext(CallbackHandler handler, ServerAuthModule serverAuthModule) throws AuthException {
this.serverAuthModule = serverAuthModule;
serverAuthModule.initialize(null, null, handler, Collections.<String, Object> emptyMap());
}

@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);
}

}
Loading

0 comments on commit 8429fa4

Please sign in to comment.