Merge pull request #23841 from arjantijms/implement_authentication_300

diff --git a/appserver/security/jaspic-provider-framework/src/main/java/com/sun/jaspic/config/factory/BaseAuthConfigFactory.java b/appserver/security/jaspic-provider-framework/src/main/java/com/sun/jaspic/config/factory/BaseAuthConfigFactory.java
index aaadb88..8215649 100644
--- a/appserver/security/jaspic-provider-framework/src/main/java/com/sun/jaspic/config/factory/BaseAuthConfigFactory.java
+++ b/appserver/security/jaspic-provider-framework/src/main/java/com/sun/jaspic/config/factory/BaseAuthConfigFactory.java
@@ -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
@@ -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;
@@ -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;
 
 
 /**
@@ -46,6 +51,8 @@
 
     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();
@@ -370,18 +377,70 @@
         }
     }
 
+    /**
+     * 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) {
diff --git a/appserver/security/jaspic-provider-framework/src/main/java/com/sun/jaspic/config/factory/singlemodule/DefaultAuthConfigProvider.java b/appserver/security/jaspic-provider-framework/src/main/java/com/sun/jaspic/config/factory/singlemodule/DefaultAuthConfigProvider.java
new file mode 100644
index 0000000..5f19a29
--- /dev/null
+++ b/appserver/security/jaspic-provider-framework/src/main/java/com/sun/jaspic/config/factory/singlemodule/DefaultAuthConfigProvider.java
@@ -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());
+        }
+    }
+
+}
diff --git a/appserver/security/jaspic-provider-framework/src/main/java/com/sun/jaspic/config/factory/singlemodule/DefaultServerAuthConfig.java b/appserver/security/jaspic-provider-framework/src/main/java/com/sun/jaspic/config/factory/singlemodule/DefaultServerAuthConfig.java
new file mode 100644
index 0000000..b1d950e
--- /dev/null
+++ b/appserver/security/jaspic-provider-framework/src/main/java/com/sun/jaspic/config/factory/singlemodule/DefaultServerAuthConfig.java
@@ -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;
+    }
+
+}
diff --git a/appserver/security/jaspic-provider-framework/src/main/java/com/sun/jaspic/config/factory/singlemodule/DefaultServerAuthContext.java b/appserver/security/jaspic-provider-framework/src/main/java/com/sun/jaspic/config/factory/singlemodule/DefaultServerAuthContext.java
new file mode 100644
index 0000000..86ef098
--- /dev/null
+++ b/appserver/security/jaspic-provider-framework/src/main/java/com/sun/jaspic/config/factory/singlemodule/DefaultServerAuthContext.java
@@ -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);
+    }
+
+}
diff --git a/appserver/security/jaspic-provider-framework/src/test/java/test/com/sun/jaspic/config/FactoryTest.java b/appserver/security/jaspic-provider-framework/src/test/java/test/com/sun/jaspic/config/FactoryTest.java
index 4556ca1..2177809 100644
--- a/appserver/security/jaspic-provider-framework/src/test/java/test/com/sun/jaspic/config/FactoryTest.java
+++ b/appserver/security/jaspic-provider-framework/src/test/java/test/com/sun/jaspic/config/FactoryTest.java
@@ -63,7 +63,6 @@
 import jakarta.security.auth.message.config.RegistrationListener;
 import jakarta.security.auth.message.config.ServerAuthConfig;
 import jakarta.security.auth.message.config.ServerAuthContext;
-import jakarta.security.auth.message.module.ServerAuthModule;
 
 /**
  * @author Ron Monzillo
@@ -282,17 +281,6 @@
             }
         }
 
-        @Override
-        public String registerServerAuthModule(ServerAuthModule serverAuthModule, Object context) {
-            // TODO Auto-generated method stub
-            return null;
-        }
-
-        @Override
-        public void removeServerAuthModule(Object context) {
-            // TODO Auto-generated method stub
-
-        }
     }
 
     static class _Extends_ExtendsAuthConfigFactory extends _ExtendsBaseAuthConfigFactory {