Refactor Shiro/HTTP integration 63/101763/7
authorRobert Varga <robert.varga@pantheon.tech>
Wed, 6 Jul 2022 17:08:03 +0000 (19:08 +0200)
committerRobert Varga <robert.varga@pantheon.tech>
Wed, 6 Jul 2022 21:17:42 +0000 (23:17 +0200)
ShiroFilter is designed to work with EnvironmentLoaderListener, both of
which are designed to work with web.xml.

We are in programmatic-land, hence the indirection through
ServletContext lifecycle is completely unnecessary and actually is
hurtful, as we cannot smuggle WebEnvironment through OSGi HTTP
Whiteboard -- it requires String properties when coupled with
ServletContextHelper.

Here we create a much more direct connection, where AAAWebEnvironment
is eagerly-initialized and it is passed directly to AAAShiroFilter. This
allows us to use AbstractShiroFilter only without having to dance around
init() methods -- and eliminate binding to to static SecurityManager
wiring as well.

JIRA: AAA-225
Change-Id: Id3990dd533b5e60f3781f9e4cdc3114163ecb931
Signed-off-by: Robert Varga <robert.varga@pantheon.tech>
aaa-shiro/impl/src/main/java/org/opendaylight/aaa/shiro/filters/AAAShiroFilter.java
aaa-shiro/impl/src/main/java/org/opendaylight/aaa/shiro/web/env/AAAIniWebEnvironment.java [deleted file]
aaa-shiro/impl/src/main/java/org/opendaylight/aaa/shiro/web/env/AAAWebEnvironment.java [new file with mode: 0644]
aaa-shiro/impl/src/main/java/org/opendaylight/aaa/shiro/web/env/ShiroWebContextSecurer.java
aaa-shiro/impl/src/main/java/org/opendaylight/aaa/shiro/web/env/ShiroWebEnvironmentLoaderListener.java [deleted file]
aaa-shiro/impl/src/main/resources/OSGI-INF/blueprint/impl-blueprint.xml

index cd2577bfcec02515d20bdb7c9977f02522112595..49514fc7b5005a0c89a80a157c7155eed87810ef 100644 (file)
@@ -7,37 +7,37 @@
  */
 package org.opendaylight.aaa.shiro.filters;
 
+import static java.util.Objects.requireNonNull;
+
 import javax.servlet.Filter;
-import org.apache.shiro.web.servlet.ShiroFilter;
+import org.apache.shiro.web.env.WebEnvironment;
+import org.apache.shiro.web.servlet.AbstractShiroFilter;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 /**
- * The default AAA JAX-RS 1.X Web Filter.  Unlike AAAFilter, which is aimed towards
- * supporting RESTCONF and its existing API mechanisms, AAAShiroFilter is a generic
- * <code>ShiroFilter</code> for use with any other ODL Servlets.  The main difference
- * is that <code>AAAFilter</code> was designed to support the existing noauth
- * mechanism, while this filter cannot be disabled.
- *
- * <p>
- * This class is also responsible for delivering debug information; to enable these
- * debug statements, please issue the following in the karaf shell:
- *
- * <code>log:set DEBUG AAAShiroFilter</code>
- *
- * @see Filter
- * @see ShiroFilter
+ * The default AAA JAX-RS Web {@link Filter} based on {@link AbstractShiroFilter}. It gets a constant reference
+ * to a Shiro {@link WebEnvironment} at instantiation time and it during {@link #init()}.
  */
-public final class AAAShiroFilter extends ShiroFilter {
+public final class AAAShiroFilter extends AbstractShiroFilter {
     private static final Logger LOG = LoggerFactory.getLogger(AAAShiroFilter.class);
 
-    public AAAShiroFilter() {
-        LOG.debug("Creating the AAAShiroFilter");
+    private final WebEnvironment env;
+
+    public AAAShiroFilter(final WebEnvironment env) {
+        this.env = requireNonNull(env);
+        LOG.debug("Instantiated AAAShiroFilter for {}", env);
     }
 
     @Override
-    public void init() throws Exception {
-        super.init();
-        LOG.debug("Initializing the AAAShiroFilter");
+    public void init() {
+        LOG.debug("Initializing AAAShiroFilter");
+        setSecurityManager(env.getWebSecurityManager());
+
+        final var resolver = env.getFilterChainResolver();
+        if (resolver != null) {
+            setFilterChainResolver(resolver);
+        }
+        LOG.debug("AAAShiroFilter initialized");
     }
 }
diff --git a/aaa-shiro/impl/src/main/java/org/opendaylight/aaa/shiro/web/env/AAAIniWebEnvironment.java b/aaa-shiro/impl/src/main/java/org/opendaylight/aaa/shiro/web/env/AAAIniWebEnvironment.java
deleted file mode 100644 (file)
index 82ce245..0000000
+++ /dev/null
@@ -1,110 +0,0 @@
-/*
- * Copyright (c) 2018 Inocybe Technologies and others.  All rights reserved.
- *
- * This program and the accompanying materials are made available under the
- * terms of the Eclipse Public License v1.0 which accompanies this distribution,
- * and is available at http://www.eclipse.org/legal/epl-v10.html
- */
-package org.opendaylight.aaa.shiro.web.env;
-
-import org.apache.shiro.SecurityUtils;
-import org.apache.shiro.config.Ini;
-import org.apache.shiro.config.IniSecurityManagerFactory;
-import org.apache.shiro.mgt.SecurityManager;
-import org.apache.shiro.util.Factory;
-import org.apache.shiro.web.env.IniWebEnvironment;
-import org.opendaylight.aaa.api.AuthenticationService;
-import org.opendaylight.aaa.api.TokenStore;
-import org.opendaylight.aaa.api.password.service.PasswordHashService;
-import org.opendaylight.aaa.cert.api.ICertificateManager;
-import org.opendaylight.aaa.shiro.realm.KeystoneAuthRealm;
-import org.opendaylight.aaa.shiro.realm.MDSALDynamicAuthorizationFilter;
-import org.opendaylight.aaa.shiro.realm.MdsalRealm;
-import org.opendaylight.aaa.shiro.realm.MoonRealm;
-import org.opendaylight.aaa.shiro.realm.TokenAuthRealm;
-import org.opendaylight.aaa.tokenauthrealm.auth.TokenAuthenticators;
-import org.opendaylight.aaa.web.servlet.ServletSupport;
-import org.opendaylight.mdsal.binding.api.DataBroker;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.aaa.app.config.rev170619.ShiroConfiguration;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.aaa.app.config.rev170619.shiro.configuration.Main;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.aaa.app.config.rev170619.shiro.configuration.Urls;
-import org.opendaylight.yangtools.util.ClassLoaderUtils;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-/**
- * Extends <code>IniWebEnvironment</code> to provide the Ini configuration via a clustered app config,
- * and sets the TCCL (x2) so that loading of classes by name (from aaa-app-config.xml) works even with
- * ShiroWebContextSecurer.
- *
- * @author Ryan Goulding
- * @author Thomas Pantelis
- * @author Michael Vorburger - use of TCCL for ShiroWebContextSecurer
- */
-class AAAIniWebEnvironment extends IniWebEnvironment {
-    private static final Logger LOG = LoggerFactory.getLogger(AAAIniWebEnvironment.class);
-
-    private static final String MAIN_SECTION_HEADER = "main";
-    private static final String URLS_SECTION_HEADER = "urls";
-
-    private final ShiroConfiguration shiroConfiguration;
-    private final DataBroker dataBroker;
-    private final ICertificateManager certificateManager;
-    private final AuthenticationService authenticationService;
-    private final TokenAuthenticators tokenAuthenticators;
-    private final TokenStore tokenStore;
-    private final PasswordHashService passwordHashService;
-    private final ServletSupport servletSupport;
-
-    AAAIniWebEnvironment(final ShiroConfiguration shiroConfiguration, final DataBroker dataBroker,
-                         final ICertificateManager certificateManager,
-                         final AuthenticationService authenticationService,
-                         final TokenAuthenticators tokenAuthenticators, final TokenStore tokenStore,
-                         final PasswordHashService passwordHashService, final ServletSupport servletSupport) {
-        this.shiroConfiguration = shiroConfiguration;
-        this.dataBroker = dataBroker;
-        this.certificateManager = certificateManager;
-        this.authenticationService = authenticationService;
-        this.tokenAuthenticators = tokenAuthenticators;
-        this.tokenStore = tokenStore;
-        this.passwordHashService = passwordHashService;
-        this.servletSupport = servletSupport;
-        LOG.debug("AAAIniWebEnvironment created");
-    }
-
-    static Ini createIniFromClusteredAppConfig(final ShiroConfiguration shiroConfiguration) {
-        final Ini ini = new Ini();
-
-        final Ini.Section mainSection = ini.addSection(MAIN_SECTION_HEADER);
-        for (final Main main : shiroConfiguration.nonnullMain()) {
-            mainSection.put(main.getPairKey(), main.getPairValue());
-        }
-
-        final Ini.Section urlsSection = ini.addSection(URLS_SECTION_HEADER);
-        for (final Urls url : shiroConfiguration.nonnullUrls()) {
-            urlsSection.put(url.getPairKey(), url.getPairValue());
-        }
-
-        final Factory<SecurityManager> factory = new IniSecurityManagerFactory(ini);
-        final SecurityManager securityManager = ClassLoaderUtils.getWithClassLoader(
-                AAAIniWebEnvironment.class.getClassLoader(), factory::getInstance);
-        SecurityUtils.setSecurityManager(securityManager);
-
-        return ini;
-    }
-
-    @Override
-    public void init() {
-        try (
-            var filterLoad = MDSALDynamicAuthorizationFilter.prepareForLoad(dataBroker);
-            var keyStoneLoad = KeystoneAuthRealm.prepareForLoad(certificateManager, servletSupport);
-            var mdsalLoad = MdsalRealm.prepareForLoad(passwordHashService, dataBroker);
-            var moonLoad = MoonRealm.prepareForLoad(servletSupport);
-            var tokenAuthLoad = TokenAuthRealm.prepareForLoad(authenticationService, tokenAuthenticators, tokenStore)) {
-            // Initialize the Shiro environment from clustered-app-config
-            final Ini ini = createIniFromClusteredAppConfig(shiroConfiguration);
-            setIni(ini);
-            ClassLoaderUtils.runWithClassLoader(AAAIniWebEnvironment.class.getClassLoader(), super::init);
-        }
-    }
-}
diff --git a/aaa-shiro/impl/src/main/java/org/opendaylight/aaa/shiro/web/env/AAAWebEnvironment.java b/aaa-shiro/impl/src/main/java/org/opendaylight/aaa/shiro/web/env/AAAWebEnvironment.java
new file mode 100644 (file)
index 0000000..94d45aa
--- /dev/null
@@ -0,0 +1,79 @@
+/*
+ * Copyright (c) 2018 Inocybe Technologies and others.  All rights reserved.
+ * Copyright (c) 2022 PANTHEON.tech, s.r.o.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.aaa.shiro.web.env;
+
+import org.apache.shiro.config.Ini;
+import org.apache.shiro.web.env.IniWebEnvironment;
+import org.opendaylight.aaa.api.AuthenticationService;
+import org.opendaylight.aaa.api.TokenStore;
+import org.opendaylight.aaa.api.password.service.PasswordHashService;
+import org.opendaylight.aaa.cert.api.ICertificateManager;
+import org.opendaylight.aaa.shiro.realm.KeystoneAuthRealm;
+import org.opendaylight.aaa.shiro.realm.MDSALDynamicAuthorizationFilter;
+import org.opendaylight.aaa.shiro.realm.MdsalRealm;
+import org.opendaylight.aaa.shiro.realm.MoonRealm;
+import org.opendaylight.aaa.shiro.realm.TokenAuthRealm;
+import org.opendaylight.aaa.tokenauthrealm.auth.TokenAuthenticators;
+import org.opendaylight.aaa.web.servlet.ServletSupport;
+import org.opendaylight.mdsal.binding.api.DataBroker;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.aaa.app.config.rev170619.ShiroConfiguration;
+import org.opendaylight.yangtools.util.ClassLoaderUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Extends {@code BasicInitEnvironment} to provide the {@link Ini} configuration via a clustered app config,
+ * Initialization happens in the context of this class's ClassLoader, with dependencies being injected into their
+ * thread-local variables.
+ */
+public final class AAAWebEnvironment extends IniWebEnvironment {
+    private static final Logger LOG = LoggerFactory.getLogger(AAAWebEnvironment.class);
+
+    private AAAWebEnvironment(final Ini ini) {
+        setIni(ini);
+    }
+
+    public static AAAWebEnvironment create(final ShiroConfiguration shiroConfiguration, final DataBroker dataBroker,
+            final ICertificateManager certificateManager, final AuthenticationService authenticationService,
+            final TokenAuthenticators tokenAuthenticators, final TokenStore tokenStore,
+            final PasswordHashService passwordHashService, final ServletSupport servletSupport) {
+        // Turn ShiroConfiguration into an Ini
+        final var ini = new Ini();
+
+        final var mainSection = ini.addSection("main");
+        for (var main : shiroConfiguration.nonnullMain()) {
+            mainSection.put(main.getPairKey(), main.getPairValue());
+        }
+
+        final var urlsSection = ini.addSection("urls");
+        for (var url : shiroConfiguration.nonnullUrls()) {
+            urlsSection.put(url.getPairKey(), url.getPairValue());
+        }
+
+        // Create an instance
+        final var ret = new AAAWebEnvironment(ini);
+
+        // Configure the instance with all known custom components prepared for loading via their thread locals and
+        // clean up afterwards. This needs to happen on our class loader so Shiro's ReflectionBuilder use of
+        // Class.forName() is happy.
+        ClassLoaderUtils.runWithClassLoader(AAAWebEnvironment.class.getClassLoader(), () -> {
+            try (var filterLoad = MDSALDynamicAuthorizationFilter.prepareForLoad(dataBroker);
+                 var keyStoneLoad = KeystoneAuthRealm.prepareForLoad(certificateManager, servletSupport);
+                 var mdsalLoad = MdsalRealm.prepareForLoad(passwordHashService, dataBroker);
+                 var moonLoad = MoonRealm.prepareForLoad(servletSupport);
+                 var tokenAuthLoad = TokenAuthRealm.prepareForLoad(authenticationService, tokenAuthenticators,
+                     tokenStore)) {
+                ret.configure();
+            }
+        });
+
+        LOG.debug("AAAWebEnvironment created");
+        return ret;
+    }
+}
index 53a7068ba53bcadbb3c1985157e6c90f7e6d617e..3e0cf68b215e7a262ab56a021b1bea6a2b5a9085 100644 (file)
@@ -9,7 +9,7 @@ package org.opendaylight.aaa.shiro.web.env;
 
 import static java.util.Objects.requireNonNull;
 
-import org.apache.shiro.web.env.EnvironmentLoaderListener;
+import org.apache.shiro.web.env.WebEnvironment;
 import org.opendaylight.aaa.shiro.filters.AAAShiroFilter;
 import org.opendaylight.aaa.web.FilterDetails;
 import org.opendaylight.aaa.web.WebContext;
@@ -22,20 +22,19 @@ import org.opendaylight.aaa.web.WebContextSecurer;
  * @author Michael Vorburger.ch
  */
 public class ShiroWebContextSecurer implements WebContextSecurer {
-    private final EnvironmentLoaderListener environmentLoaderListener;
+    private final WebEnvironment webEnvironment;
 
-    public ShiroWebContextSecurer(final EnvironmentLoaderListener environmentLoaderListener) {
-        this.environmentLoaderListener = requireNonNull(environmentLoaderListener);
+    public ShiroWebContextSecurer(final WebEnvironment webEnvironment) {
+        this.webEnvironment = requireNonNull(webEnvironment);
     }
 
     @Override
     public void requireAuthentication(final WebContextBuilder webContextBuilder, final boolean asyncSupported,
             final String... urlPatterns) {
         webContextBuilder
-            .addListener(environmentLoaderListener)
             // AAA filter in front of these REST web services as well as for moon endpoints
             .addFilter(FilterDetails.builder()
-                .filter(new AAAShiroFilter())
+                .filter(new AAAShiroFilter(webEnvironment))
                 .addUrlPatterns(urlPatterns)
                 .asyncSupported(asyncSupported)
                 .build());
diff --git a/aaa-shiro/impl/src/main/java/org/opendaylight/aaa/shiro/web/env/ShiroWebEnvironmentLoaderListener.java b/aaa-shiro/impl/src/main/java/org/opendaylight/aaa/shiro/web/env/ShiroWebEnvironmentLoaderListener.java
deleted file mode 100644 (file)
index a966bb6..0000000
+++ /dev/null
@@ -1,61 +0,0 @@
-/*
- * Copyright (c) 2018 Inocybe Technologies and others.  All rights reserved.
- *
- * This program and the accompanying materials are made available under the
- * terms of the Eclipse Public License v1.0 which accompanies this distribution,
- * and is available at http://www.eclipse.org/legal/epl-v10.html
- */
-package org.opendaylight.aaa.shiro.web.env;
-
-import javax.servlet.ServletContext;
-import org.apache.shiro.web.env.EnvironmentLoaderListener;
-import org.apache.shiro.web.env.WebEnvironment;
-import org.opendaylight.aaa.api.AuthenticationService;
-import org.opendaylight.aaa.api.TokenStore;
-import org.opendaylight.aaa.api.password.service.PasswordHashService;
-import org.opendaylight.aaa.cert.api.ICertificateManager;
-import org.opendaylight.aaa.tokenauthrealm.auth.TokenAuthenticators;
-import org.opendaylight.aaa.web.servlet.ServletSupport;
-import org.opendaylight.mdsal.binding.api.DataBroker;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.aaa.app.config.rev170619.ShiroConfiguration;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-/**
- * Initializes shiro components for a web environment.
- *
- * @author Thomas Pantelis
- */
-public class ShiroWebEnvironmentLoaderListener extends EnvironmentLoaderListener {
-    private static final Logger LOG = LoggerFactory.getLogger(ShiroWebEnvironmentLoaderListener.class);
-
-    private final ShiroConfiguration shiroConfiguration;
-    private final DataBroker dataBroker;
-    private final ICertificateManager certificateManager;
-    private final AuthenticationService authenticationService;
-    private final TokenAuthenticators tokenAuthenticators;
-    private final TokenStore tokenStore;
-    private final PasswordHashService passwordHashService;
-    private final ServletSupport servletSupport;
-
-    public ShiroWebEnvironmentLoaderListener(final ShiroConfiguration shiroConfiguration, final DataBroker dataBroker,
-            final ICertificateManager certificateManager, final AuthenticationService authenticationService,
-            final TokenAuthenticators tokenAuthenticators, final TokenStore tokenStore,
-            final PasswordHashService passwordHashService, final ServletSupport servletSupport) {
-        this.shiroConfiguration = shiroConfiguration;
-        this.dataBroker = dataBroker;
-        this.certificateManager = certificateManager;
-        this.authenticationService = authenticationService;
-        this.tokenAuthenticators = tokenAuthenticators;
-        this.tokenStore = tokenStore;
-        this.passwordHashService = passwordHashService;
-        this.servletSupport = servletSupport;
-        LOG.debug("ShiroWebEnvironmentLoaderListenerImpl created");
-    }
-
-    @Override
-    protected WebEnvironment determineWebEnvironment(final ServletContext servletContext) {
-        return new AAAIniWebEnvironment(shiroConfiguration, dataBroker, certificateManager, authenticationService,
-            tokenAuthenticators, tokenStore, passwordHashService, servletSupport);
-    }
-}
index a05ec0723821bab860d8ea7697d2a40f716c57d1..95102a5dad344b45f317bd085fada755aa94dbab 100644 (file)
@@ -38,7 +38,7 @@ and is available at http://www.eclipse.org/legal/epl-v10.html
 
   <reference id="servletSupport" interface="org.opendaylight.aaa.web.servlet.ServletSupport"/>
 
-  <bean id="shiroWebEnvLoader" class="org.opendaylight.aaa.shiro.web.env.ShiroWebEnvironmentLoaderListener">
+  <bean id="webEnvironment" class="org.opendaylight.aaa.shiro.web.env.AAAWebEnvironment" factory-method="create">
     <argument ref="shiroConfiguration"/>
     <argument ref="dataBroker"/>
     <argument ref="certManager"/>
@@ -53,14 +53,13 @@ and is available at http://www.eclipse.org/legal/epl-v10.html
     <argument ref="servletSupport"/>
   </bean>
 
-  <reference id="customFilterAdapterConfig"
-      interface="org.opendaylight.aaa.filterchain.configuration.CustomFilterAdapterConfiguration"/>
-
   <bean id="webContextSecurer" class="org.opendaylight.aaa.shiro.web.env.ShiroWebContextSecurer">
-    <argument ref="shiroWebEnvLoader"/>
+    <argument ref="webEnvironment"/>
   </bean>
   <service ref="webContextSecurer" interface="org.opendaylight.aaa.web.WebContextSecurer" />
 
+  <reference id="customFilterAdapterConfig"
+      interface="org.opendaylight.aaa.filterchain.configuration.CustomFilterAdapterConfiguration"/>
   <reference id="webServer" interface="org.opendaylight.aaa.web.WebServer" />
 
   <bean id="webInitializer" class="org.opendaylight.aaa.shiro.web.env.WebInitializer" destroy-method="close">