content: /**
   * Licensed to the Apache Software Foundation (ASF) under one or more
   * contributor license agreements.  See the NOTICE file distributed with
   * this work for additional information regarding copyright ownership.
   * The ASF licenses this file to You under the Apache License, Version 2.0
   * (the "License"); you may not use this file except in compliance with
   * the License.  You may obtain a copy of the License at
   *
   *      http://www.apache.org/licenses/LICENSE-2.0
   *
   * Unless required by applicable law or agreed to in writing, software
   * distributed under the License is distributed on an "AS IS" BASIS,
   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   * See the License for the specific language governing permissions and
   * limitations under the License.
   */
  package org.apache.activemq.shiro;
  
  import org.apache.activemq.broker.BrokerPlugin;
  import org.apache.activemq.broker.BrokerService;
  import org.apache.activemq.broker.MutableBrokerFilter;
  import org.apache.activemq.shiro.authc.AuthenticationFilter;
  import org.apache.activemq.shiro.authc.DefaultAuthenticationPolicy;
  import org.apache.activemq.shiro.authz.AuthorizationFilter;
  import org.apache.activemq.shiro.env.IniEnvironment;
  import org.apache.activemq.shiro.subject.DefaultConnectionSubjectFactory;
  import org.apache.activemq.shiro.subject.SubjectFilter;
  import org.apache.activemq.test.JmsResourceProvider;
  import org.apache.activemq.test.TestSupport;
  import org.apache.shiro.authc.AuthenticationException;
  import org.apache.shiro.config.Ini;
  import org.apache.shiro.env.DefaultEnvironment;
  import org.apache.shiro.env.Environment;
  import org.apache.shiro.mgt.DefaultSecurityManager;
  import org.apache.shiro.realm.text.IniRealm;
  import org.slf4j.Logger;
  import org.slf4j.LoggerFactory;
  
  import jakarta.jms.Connection;
  import jakarta.jms.ConnectionFactory;
  import jakarta.jms.Destination;
  import jakarta.jms.JMSException;
  import jakarta.jms.MessageConsumer;
  import jakarta.jms.MessageProducer;
  import jakarta.jms.Session;
  
  /**
   * @since 5.10.0
   */
  public class ShiroPluginTest extends TestSupport {
  
      private static final Logger LOG = LoggerFactory.getLogger(ShiroPluginTest.class);
  
      protected BrokerService broker;
      protected SecureJmsResourceProvider resourceProvider;
      protected ConnectionFactory connectionFactory;
      protected Connection connection;
      protected Session session;
      protected Destination destination;
      protected MessageConsumer consumer;
      protected MessageProducer producer;
  
      @Override
      protected void setUp() throws Exception {
          resourceProvider = new SecureJmsResourceProvider();
      }
  
      @Override
      protected void tearDown() throws Exception {
          LOG.info("Shutting down broker...");
  
          if (session != null) {
              session.close();
          }
          session = null;
  
          if (connection != null) {
              connection.close();
          }
          connection = null;
  
          if (broker != null) {
              broker.stop();
              broker.waitUntilStopped();
          }
          broker = null;
  
          LOG.info("Broker shut down.");
      }
  
      protected void reconnect() throws Exception {
          reconnect(null, null);
      }
  
      protected void reconnect(String username, String password) throws Exception {
          if (connection != null) {
              // Close the prev connection.
              connection.close();
          }
          session = null;
          if (username == null && password == null) {
              connection = resourceProvider.createConnection(connectionFactory);
          } else {
              connection = resourceProvider.createConnection(connectionFactory, username, password);
          }
          reconnectSession();
          connection.start();
      }
  
      protected void reconnectSession() throws JMSException {
          if (session != null) {
              session.close();
          }
          session = resourceProvider.createSession(connection);
          destination = resourceProvider.createDestination(session, getSubject());
          producer = resourceProvider.createProducer(session, destination);
          consumer = resourceProvider.createConsumer(session, destination);
      }
  
      protected ConnectionFactory newConnectionFactory() throws Exception {
          return resourceProvider.createConnectionFactory();
      }
  
      protected void start() throws Exception {
          startBroker();
          topic = resourceProvider.isTopic();
          connectionFactory = newConnectionFactory();
      }
  
      protected void startBroker() throws Exception {
          broker.start();
          broker.waitUntilStarted();
      }
  
      protected BrokerService createBroker(BrokerPlugin... plugins) throws Exception {
          return createBroker(plugins, resourceProvider.getServerUri());
      }
  
      protected BrokerService createBroker(BrokerPlugin[] plugins, String... connectorUris) throws Exception {
          BrokerService brokerService = new BrokerService();
          if (plugins != null && plugins.length > 0) {
              brokerService.setPlugins(plugins);
          }
          if (connectorUris != null) {
              for (String uri : connectorUris) {
                  brokerService.addConnector(uri);
              }
          }
          return brokerService;
      }
  
      protected ShiroPlugin createPlugin(String iniPath) {
          Ini ini = Ini.fromResourcePath(iniPath);
          Environment env = new IniEnvironment(ini);
          ShiroPlugin plugin = new ShiroPlugin();
          plugin.setEnvironment(env);
          return plugin;
      }
  
      public void testNoEnvironmentOrSecurityManager() throws Exception {
          //should build IniEnvironment from shiro.ini in the classpath at the least:
          ShiroPlugin plugin = new ShiroPlugin();
          plugin.installPlugin(new MutableBrokerFilter(null));
  
          Ini ini = Ini.fromResourcePath("classpath:shiro.ini");
          IniRealm realm = (IniRealm) ((DefaultSecurityManager) plugin.getEnvironment().getSecurityManager()).getRealms().iterator().next();
          assertEquals(ini, realm.getIni());
      }
  
      public void testSetIni() throws Exception {
          ShiroPlugin plugin = new ShiroPlugin();
          Ini ini = Ini.fromResourcePath("classpath:minimal.shiro.ini");
          plugin.setIni(ini);
          plugin.installPlugin(new MutableBrokerFilter(null));
  
          IniRealm realm = (IniRealm) ((DefaultSecurityManager) plugin.getEnvironment().getSecurityManager()).getRealms().iterator().next();
          assertSame(ini, realm.getIni());
      }
  
      public void testSetIniString() throws Exception {
          ShiroPlugin plugin = new ShiroPlugin();
          plugin.setIniConfig(
                  "[users]\n" +
                  "system = manager, system\n" +
                  "[roles]\n" +
                  "system = *");
          plugin.installPlugin(new MutableBrokerFilter(null));
  
          IniRealm realm = (IniRealm) ((DefaultSecurityManager) plugin.getEnvironment().getSecurityManager()).getRealms().iterator().next();
          Ini ini = realm.getIni();
          assertEquals(1, ini.getSection("users").size());
          assertEquals("manager, system", ini.getSection("users").get("system"));
          assertEquals(1, ini.getSection("roles").size());
          assertEquals("*", ini.getSection("roles").get("system"));
      }
  
      public void testSetIniResourcePath() throws Exception {
          ShiroPlugin plugin = new ShiroPlugin();
  
          String path = "classpath:minimal.shiro.ini";
  
          plugin.setIniResourcePath(path);
          plugin.installPlugin(new MutableBrokerFilter(null));
  
          Ini ini = Ini.fromResourcePath(path);
  
          IniRealm realm = (IniRealm) ((DefaultSecurityManager) plugin.getEnvironment().getSecurityManager()).getRealms().iterator().next();
          assertEquals(ini, realm.getIni());
      }
  
      public void testSetSubjectFilter() {
          ShiroPlugin plugin = new ShiroPlugin();
          SubjectFilter filter = new SubjectFilter();
          plugin.setSubjectFilter(filter);
          assertSame(filter, plugin.getSubjectFilter());
          //assert that the AuthenticationFilter is always the next filter in the chain after the SubjectFilter:
          assertSame(plugin.getAuthenticationFilter(), filter.getNext());
      }
  
      public void testSetAuthenticationFilter() {
          ShiroPlugin plugin = new ShiroPlugin();
          AuthenticationFilter filter = new AuthenticationFilter();
          plugin.setAuthenticationFilter(filter);
          assertSame(filter, plugin.getAuthenticationFilter());
          //assert that the AuthenticationFilter is always the next filter in the chain after the SubjectFilter:
          assertSame(plugin.getSubjectFilter().getNext(), filter);
      }
  
      public void testSetAuthorizationFilter() {
          ShiroPlugin plugin = new ShiroPlugin();
          AuthorizationFilter filter = new AuthorizationFilter();
          plugin.setAuthorizationFilter(filter);
          assertSame(filter, plugin.getAuthorizationFilter());
          //assert that the AuthenticationFilter is always the next filter in the chain after the AuthenticationFilter:
          assertSame(plugin.getAuthenticationFilter().getNext(), filter);
      }
  
      public void testSetEnvironment() {
          ShiroPlugin plugin = new ShiroPlugin();
          Environment env = new DefaultEnvironment();
          plugin.setEnvironment(env);
          assertSame(env, plugin.getEnvironment());
      }
  
      public void testSetSecurityManager() {
          ShiroPlugin plugin = new ShiroPlugin();
          org.apache.shiro.mgt.SecurityManager securityManager = new DefaultSecurityManager();
          plugin.setSecurityManager(securityManager);
          assertSame(securityManager, plugin.getSecurityManager());
      }
  
      public void testSecurityManagerWhenInstalled() throws Exception {
          ShiroPlugin plugin = new ShiroPlugin();
          org.apache.shiro.mgt.SecurityManager securityManager = new DefaultSecurityManager();
          plugin.setSecurityManager(securityManager);
  
          assertNull(plugin.getEnvironment()); //we will auto-create one when only a sm is provided
  
          plugin.installPlugin(new MutableBrokerFilter(null));
  
          assertSame(securityManager, plugin.getSecurityManager());
          assertNotNull(plugin.getEnvironment());
          assertSame(securityManager, plugin.getEnvironment().getSecurityManager());
      }
  
      public void testEnabledWhenNotInstalled() {
          ShiroPlugin plugin = new ShiroPlugin();
          assertTrue(plugin.isEnabled()); //enabled by default
  
          plugin.setEnabled(false);
          assertFalse(plugin.isEnabled());
  
          plugin.setEnabled(true);
          assertTrue(plugin.isEnabled());
      }
  
      public void testEnabledWhenInstalled() throws Exception {
          ShiroPlugin plugin = createPlugin("classpath:minimal.shiro.ini");
          this.broker = createBroker(plugin);
          start();
          assertTrue(plugin.isEnabled());
  
          plugin.setEnabled(false);
          assertFalse(plugin.isEnabled());
  
          plugin.setEnabled(true);
          assertTrue(plugin.isEnabled());
      }
  
      public void testAuthenticationEnabledWhenNotInstalled() {
          ShiroPlugin plugin = new ShiroPlugin();
          assertTrue(plugin.isAuthenticationEnabled());
  
          plugin.setAuthenticationEnabled(false);
          assertFalse(plugin.isAuthenticationEnabled());
  
          plugin.setAuthenticationEnabled(true);
          assertTrue(plugin.isAuthenticationEnabled());
      }
  
      public void testAuthenticationEnabledWhenInstalled() throws Exception {
          ShiroPlugin plugin = new ShiroPlugin();
          plugin.setEnvironment(new DefaultEnvironment());
          plugin.installPlugin(new MutableBrokerFilter(null));
  
          assertTrue(plugin.isAuthenticationEnabled());
  
          plugin.setAuthenticationEnabled(false);
          assertFalse(plugin.isAuthenticationEnabled());
  
          plugin.setAuthenticationEnabled(true);
          assertTrue(plugin.isAuthenticationEnabled());
      }
  
      public void testSetAuthenticationPolicy() {
          ShiroPlugin plugin = new ShiroPlugin();
          DefaultAuthenticationPolicy policy = new DefaultAuthenticationPolicy();
          plugin.setAuthenticationPolicy(policy);
          assertSame(policy, plugin.getAuthenticationPolicy());
          assertSame(policy, plugin.getAuthenticationFilter().getAuthenticationPolicy());
          assertSame(policy, ((DefaultConnectionSubjectFactory) plugin.getSubjectFilter().getConnectionSubjectFactory()).getAuthenticationPolicy());
      }
  
      public void testAuthorizationEnabledWhenNotInstalled() {
          ShiroPlugin plugin = new ShiroPlugin();
          assertTrue(plugin.isAuthorizationEnabled());
  
          plugin.setAuthorizationEnabled(false);
          assertFalse(plugin.isAuthorizationEnabled());
  
          plugin.setAuthorizationEnabled(true);
          assertTrue(plugin.isAuthorizationEnabled());
      }
  
      public void testAuthorizationEnabledWhenInstalled() throws Exception {
          ShiroPlugin plugin = new ShiroPlugin();
          plugin.setEnvironment(new DefaultEnvironment());
          plugin.installPlugin(new MutableBrokerFilter(null));
  
          assertTrue(plugin.isAuthorizationEnabled());
  
          plugin.setAuthorizationEnabled(false);
          assertFalse(plugin.isAuthorizationEnabled());
  
          plugin.setAuthorizationEnabled(true);
          assertTrue(plugin.isAuthorizationEnabled());
      }
  
  
      public void testSimple() throws Exception {
          ShiroPlugin plugin = createPlugin("classpath:minimal.shiro.ini");
          this.broker = createBroker(plugin);
          start();
          reconnect();
      }
  
      public void testDisabled() throws Exception {
          ShiroPlugin plugin = createPlugin("classpath:nosystem.shiro.ini");
          plugin.setEnabled(false);
          this.broker = createBroker(plugin);
          start();
      }
  
      public void testRuntimeDisableEnableChanges() throws Exception {
          ShiroPlugin plugin = createPlugin("classpath:nosystem.shiro.ini");
          ((DefaultAuthenticationPolicy) plugin.getAuthenticationPolicy()).setVmConnectionAuthenticationRequired(true);
          plugin.setEnabled(false);
          this.broker = createBroker(plugin);
          start();
  
          //connection has no credentials.  When disabled, this should succeed:
          reconnect();
  
          //now enable the plugin and assert that credentials are required:
          plugin.setEnabled(true);
  
          try {
              reconnect();
              fail("Connections without passwords in this configuration should fail.");
          } catch (JMSException expected) {
              assertTrue(expected.getCause() instanceof AuthenticationException);
          }
  
          //this should work now that we're authenticating:
          reconnect("foo", "bar");
      }
  
      static class SecureJmsResourceProvider extends JmsResourceProvider {
  
          /**
           * Creates a connection, authenticating with the specified username and password.
           *
           * @see org.apache.activemq.test.JmsResourceProvider#createConnection(jakarta.jms.ConnectionFactory)
           */
          public Connection createConnection(ConnectionFactory cf, String username, String password) throws JMSException {
              Connection connection = cf.createConnection(username, password);
              if (getClientID() != null) {
                  connection.setClientID(getClientID());
              }
              return connection;
          }
      }
  }
  