QPID-8356: [Broker-J] Fix loading of ACL rules containing firewall properties
diff --git a/broker-plugins/access-control/src/main/java/org/apache/qpid/server/security/access/config/AclAction.java b/broker-plugins/access-control/src/main/java/org/apache/qpid/server/security/access/config/AclAction.java
index 20adbd2..78866f5 100644
--- a/broker-plugins/access-control/src/main/java/org/apache/qpid/server/security/access/config/AclAction.java
+++ b/broker-plugins/access-control/src/main/java/org/apache/qpid/server/security/access/config/AclAction.java
@@ -18,6 +18,10 @@
  */
 package org.apache.qpid.server.security.access.config;
 
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Set;
+
 import org.apache.qpid.server.security.access.firewall.FirewallRule;
 
 public class AclAction
@@ -96,4 +100,21 @@
                ", firewallRule=" + _firewallRule +
                ']';
     }
+
+    public Map<ObjectProperties.Property, String> getAttributes()
+    {
+        final ObjectProperties properties = _action.getProperties();
+        final Map<ObjectProperties.Property, String> attributes = new HashMap<>(properties.asPropertyMap());
+        final Set<String> attributeNames = properties.getAttributeNames();
+        if (attributeNames != null && !attributeNames.isEmpty())
+        {
+            attributes.put(ObjectProperties.Property.ATTRIBUTES, String.join(",", attributeNames));
+        }
+        final FirewallRule firewallRule = getFirewallRule();
+        if (firewallRule != null)
+        {
+            attributes.put(firewallRule.getPropertyName(), firewallRule.getPropertyValue());
+        }
+        return attributes;
+    }
 }
diff --git a/broker-plugins/access-control/src/main/java/org/apache/qpid/server/security/access/config/AclRulePredicates.java b/broker-plugins/access-control/src/main/java/org/apache/qpid/server/security/access/config/AclRulePredicates.java
index 4d2423b..5b5b73d 100644
--- a/broker-plugins/access-control/src/main/java/org/apache/qpid/server/security/access/config/AclRulePredicates.java
+++ b/broker-plugins/access-control/src/main/java/org/apache/qpid/server/security/access/config/AclRulePredicates.java
@@ -119,6 +119,11 @@
         return _properties;
     }
 
+    public ObjectProperties getFirewallProperties()
+    {
+        return _properties;
+    }
+
     void setFirewallRuleFactory(FirewallRuleFactory firewallRuleFactory)
     {
         _firewallRuleFactory = firewallRuleFactory;
diff --git a/broker-plugins/access-control/src/main/java/org/apache/qpid/server/security/access/config/Rule.java b/broker-plugins/access-control/src/main/java/org/apache/qpid/server/security/access/config/Rule.java
index bf82b6c..37e5a31 100644
--- a/broker-plugins/access-control/src/main/java/org/apache/qpid/server/security/access/config/Rule.java
+++ b/broker-plugins/access-control/src/main/java/org/apache/qpid/server/security/access/config/Rule.java
@@ -20,6 +20,8 @@
  */
 package org.apache.qpid.server.security.access.config;
 
+import java.util.Map;
+
 import org.apache.qpid.server.security.access.plugins.RuleOutcome;
 
 /**
@@ -112,5 +114,8 @@
                ']';
     }
 
-
+    public Map<ObjectProperties.Property, String> getAttributes()
+    {
+        return _action.getAttributes();
+    }
 }
diff --git a/broker-plugins/access-control/src/main/java/org/apache/qpid/server/security/access/firewall/FirewallRule.java b/broker-plugins/access-control/src/main/java/org/apache/qpid/server/security/access/firewall/FirewallRule.java
index 482a795..72d8d69 100644
--- a/broker-plugins/access-control/src/main/java/org/apache/qpid/server/security/access/firewall/FirewallRule.java
+++ b/broker-plugins/access-control/src/main/java/org/apache/qpid/server/security/access/firewall/FirewallRule.java
@@ -20,7 +20,13 @@
 
 import java.net.InetAddress;
 
+import org.apache.qpid.server.security.access.config.ObjectProperties;
+
 public interface FirewallRule
 {
     boolean matches(InetAddress addressOfClient);
+
+    ObjectProperties.Property getPropertyName();
+
+    String getPropertyValue();
 }
diff --git a/broker-plugins/access-control/src/main/java/org/apache/qpid/server/security/access/firewall/HostnameFirewallRule.java b/broker-plugins/access-control/src/main/java/org/apache/qpid/server/security/access/firewall/HostnameFirewallRule.java
index 6990634..dfc0e22 100644
--- a/broker-plugins/access-control/src/main/java/org/apache/qpid/server/security/access/firewall/HostnameFirewallRule.java
+++ b/broker-plugins/access-control/src/main/java/org/apache/qpid/server/security/access/firewall/HostnameFirewallRule.java
@@ -30,6 +30,8 @@
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+import org.apache.qpid.server.security.access.config.ObjectProperties;
+
 public class HostnameFirewallRule implements FirewallRule
 {
     private static final Logger LOGGER = LoggerFactory.getLogger(HostnameFirewallRule.class);
@@ -82,6 +84,17 @@
         return false;
     }
 
+    @Override
+    public ObjectProperties.Property getPropertyName()
+    {
+        return ObjectProperties.Property.FROM_HOSTNAME;
+    }
+
+    @Override
+    public String getPropertyValue()
+    {
+        return String.join(",", _hostnames);
+    }
 
     /**
      * @param remote
diff --git a/broker-plugins/access-control/src/main/java/org/apache/qpid/server/security/access/firewall/NetworkFirewallRule.java b/broker-plugins/access-control/src/main/java/org/apache/qpid/server/security/access/firewall/NetworkFirewallRule.java
index 5e37cf9..de7511f 100644
--- a/broker-plugins/access-control/src/main/java/org/apache/qpid/server/security/access/firewall/NetworkFirewallRule.java
+++ b/broker-plugins/access-control/src/main/java/org/apache/qpid/server/security/access/firewall/NetworkFirewallRule.java
@@ -25,14 +25,17 @@
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+import org.apache.qpid.server.security.access.config.ObjectProperties;
+
 public class NetworkFirewallRule implements FirewallRule
 {
     private static final Logger LOGGER = LoggerFactory.getLogger(NetworkFirewallRule.class);
-
+    private final String _originalNetworks;
     private List<InetNetwork> _networks;
 
     public NetworkFirewallRule(String... networks)
     {
+        _originalNetworks = String.join(",", networks);
         _networks = new ArrayList<InetNetwork>();
         for (int i = 0; i < networks.length; i++)
         {
@@ -104,4 +107,16 @@
                "networks=" + _networks +
                ']';
     }
+
+    @Override
+    public ObjectProperties.Property getPropertyName()
+    {
+        return ObjectProperties.Property.FROM_NETWORK;
+    }
+
+    @Override
+    public String getPropertyValue()
+    {
+        return _originalNetworks;
+    }
 }
diff --git a/broker-plugins/access-control/src/main/java/org/apache/qpid/server/security/access/plugins/AbstractCommonRuleBasedAccessControlProvider.java b/broker-plugins/access-control/src/main/java/org/apache/qpid/server/security/access/plugins/AbstractCommonRuleBasedAccessControlProvider.java
index 0719e67..ed581bc 100644
--- a/broker-plugins/access-control/src/main/java/org/apache/qpid/server/security/access/plugins/AbstractCommonRuleBasedAccessControlProvider.java
+++ b/broker-plugins/access-control/src/main/java/org/apache/qpid/server/security/access/plugins/AbstractCommonRuleBasedAccessControlProvider.java
@@ -149,7 +149,7 @@
         @Override
         public Map<ObjectProperties.Property, String> getAttributes()
         {
-            return _rule.getAction().getProperties().asPropertyMap();
+            return _rule.getAttributes();
         }
 
         @Override
diff --git a/broker-plugins/access-control/src/test/java/org/apache/qpid/server/security/access/config/AclActionTest.java b/broker-plugins/access-control/src/test/java/org/apache/qpid/server/security/access/config/AclActionTest.java
index 6fb8974..2edc47b 100644
--- a/broker-plugins/access-control/src/test/java/org/apache/qpid/server/security/access/config/AclActionTest.java
+++ b/broker-plugins/access-control/src/test/java/org/apache/qpid/server/security/access/config/AclActionTest.java
@@ -18,11 +18,17 @@
  */
 package org.apache.qpid.server.security.access.config;
 
+import static org.hamcrest.CoreMatchers.allOf;
+import static org.hamcrest.Matchers.aMapWithSize;
+import static org.hamcrest.Matchers.hasEntry;
 import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertThat;
 import static org.junit.Assert.assertTrue;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.when;
 
+import java.util.Map;
+
 import org.junit.Test;
 
 import org.apache.qpid.server.security.access.firewall.FirewallRule;
@@ -30,6 +36,9 @@
 
 public class AclActionTest extends UnitTestBase
 {
+    private static final String TEST_HOSTNAME = "localhost";
+    private static final String TEST_ATTRIBUTES = "test";
+
     @Test
     public void testEqualsAndHashCode()
     {
@@ -57,6 +66,26 @@
                            aclAction.equals(new AclAction(operation, objectType, createAclRulePredicates())));
     }
 
+    @Test
+    public void testGetAttributes()
+    {
+        final ObjectType objectType = ObjectType.VIRTUALHOST;
+        final LegacyOperation operation = LegacyOperation.ACCESS;
+        final AclRulePredicates predicates = new AclRulePredicates();
+        predicates.parse(ObjectProperties.Property.FROM_HOSTNAME.name(), TEST_HOSTNAME);
+        predicates.parse(ObjectProperties.Property.ATTRIBUTES.name(), TEST_ATTRIBUTES);
+        predicates.parse(ObjectProperties.Property.NAME.name(), getTestName());
+
+        final AclAction aclAction = new AclAction(operation, objectType, predicates);
+        final Map<ObjectProperties.Property, String> attributes = aclAction.getAttributes();
+
+        assertThat(attributes,
+                   allOf(aMapWithSize(3),
+                         hasEntry(ObjectProperties.Property.FROM_HOSTNAME, TEST_HOSTNAME),
+                         hasEntry(ObjectProperties.Property.ATTRIBUTES, TEST_ATTRIBUTES),
+                         hasEntry(ObjectProperties.Property.NAME, getTestName())));
+    }
+
     private AclRulePredicates createAclRulePredicates()
     {
         AclRulePredicates predicates = mock(AclRulePredicates.class);
diff --git a/broker-plugins/access-control/src/test/java/org/apache/qpid/server/security/access/plugins/AclRuleImplTest.java b/broker-plugins/access-control/src/test/java/org/apache/qpid/server/security/access/plugins/AclRuleImplTest.java
new file mode 100644
index 0000000..5466255
--- /dev/null
+++ b/broker-plugins/access-control/src/test/java/org/apache/qpid/server/security/access/plugins/AclRuleImplTest.java
@@ -0,0 +1,56 @@
+/*
+ *
+ * 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.qpid.server.security.access.plugins;
+
+import static org.hamcrest.Matchers.is;
+import static org.hamcrest.core.IsEqual.equalTo;
+import static org.junit.Assert.assertThat;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import java.util.Collections;
+import java.util.Map;
+
+import org.junit.Test;
+
+import org.apache.qpid.server.security.access.config.ObjectProperties;
+import org.apache.qpid.server.security.access.config.Rule;
+import org.apache.qpid.test.utils.UnitTestBase;
+
+public class AclRuleImplTest extends UnitTestBase
+{
+    @Test
+    public void testGetAttributes()
+    {
+        final Rule rule = mock(Rule.class);
+        final Map<ObjectProperties.Property, String> expected =
+                Collections.singletonMap(ObjectProperties.Property.NAME, getTestName());
+        when(rule.getAttributes()).thenReturn(expected);
+
+        final AbstractCommonRuleBasedAccessControlProvider.AclRuleImpl aclRule =
+                new AbstractCommonRuleBasedAccessControlProvider.AclRuleImpl(rule);
+        assertThat(aclRule.getAttributes(), is(equalTo(expected)));
+        verify(rule).getAttributes();
+    }
+
+}
diff --git a/broker-plugins/access-control/src/test/java/org/apache/qpid/server/security/access/plugins/RuleBasedAccessControlProviderImplTest.java b/broker-plugins/access-control/src/test/java/org/apache/qpid/server/security/access/plugins/RuleBasedAccessControlProviderImplTest.java
new file mode 100644
index 0000000..b17e25f
--- /dev/null
+++ b/broker-plugins/access-control/src/test/java/org/apache/qpid/server/security/access/plugins/RuleBasedAccessControlProviderImplTest.java
@@ -0,0 +1,119 @@
+/*
+ *
+ * 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.qpid.server.security.access.plugins;
+
+import static java.nio.charset.StandardCharsets.UTF_8;
+import static org.hamcrest.CoreMatchers.allOf;
+import static org.hamcrest.CoreMatchers.equalTo;
+import static org.hamcrest.CoreMatchers.is;
+import static org.hamcrest.CoreMatchers.notNullValue;
+import static org.hamcrest.Matchers.aMapWithSize;
+import static org.hamcrest.Matchers.hasEntry;
+import static org.junit.Assert.assertThat;
+
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+
+import org.junit.Before;
+import org.junit.Test;
+
+import org.apache.qpid.server.model.Broker;
+import org.apache.qpid.server.model.BrokerTestHelper;
+import org.apache.qpid.server.model.ConfiguredObject;
+import org.apache.qpid.server.security.access.config.LegacyOperation;
+import org.apache.qpid.server.security.access.config.ObjectProperties;
+import org.apache.qpid.server.security.access.config.ObjectType;
+import org.apache.qpid.server.util.DataUrlUtils;
+import org.apache.qpid.test.utils.UnitTestBase;
+
+public class RuleBasedAccessControlProviderImplTest extends UnitTestBase
+{
+    private static final String ACL_RULE_PRINCIPAL = "guest";
+    private RuleBasedAccessControlProviderImpl _aclProvider;
+    private String _nameAttributeValue;
+
+    @Before
+    public void setUp()
+    {
+        final Map<String, Object>
+                attributes = Collections.singletonMap(RuleBasedAccessControlProvider.NAME, getTestName());
+        final Broker<?> broker = BrokerTestHelper.createBrokerMock();
+        _aclProvider = new RuleBasedAccessControlProviderImpl(attributes, broker);
+        _aclProvider.create();
+
+        _nameAttributeValue = getTestName();
+    }
+
+    @Test
+    public void testLoadACLWithFromHostnameFirewallRule()
+    {
+        loadAclAndAssertRule(ObjectProperties.Property.FROM_HOSTNAME, "localhost");
+    }
+
+    @Test
+    public void testLoadACLWithFromNetworkFirewallRule()
+    {
+        loadAclAndAssertRule(ObjectProperties.Property.FROM_NETWORK, "192.168.1.0/24");
+    }
+
+    @Test
+    public void testLoadACLWithFromNetworkFirewallRuleContainingWildcard()
+    {
+        loadAclAndAssertRule(ObjectProperties.Property.FROM_NETWORK, "192.168.1.*");
+    }
+
+    @Test
+    public void testLoadACLWithAttributes()
+    {
+        loadAclAndAssertRule(ObjectProperties.Property.ATTRIBUTES,
+                             String.join(",", ConfiguredObject.NAME, ConfiguredObject.LIFETIME_POLICY));
+    }
+
+    private void loadAclAndAssertRule(final ObjectProperties.Property attributeName,
+                                      final String attributeValue)
+    {
+        final String acl = String.format("ACL ALLOW-LOG %s ACCESS VIRTUALHOST %s=\"%s\" name=\"%s\"",
+                                         ACL_RULE_PRINCIPAL,
+                                         attributeName,
+                                         attributeValue,
+                                         _nameAttributeValue);
+
+        _aclProvider.loadFromFile(DataUrlUtils.getDataUrlForBytes(acl.getBytes(UTF_8)));
+
+        final List<AclRule> rules = _aclProvider.getRules();
+        assertThat(rules, is(notNullValue()));
+        assertThat(rules.size(), is(equalTo(1)));
+
+        final AclRule rule = rules.get(0);
+        assertThat(rule, is(notNullValue()));
+        assertThat(rule.getObjectType(), is(equalTo(ObjectType.VIRTUALHOST)));
+        assertThat(rule.getIdentity(), is(equalTo(ACL_RULE_PRINCIPAL)));
+        assertThat(rule.getOperation(), is(equalTo(LegacyOperation.ACCESS)));
+        assertThat(rule.getOutcome(), is(equalTo(RuleOutcome.ALLOW_LOG)));
+        assertThat(rule.getAttributes(),
+                   allOf(aMapWithSize(2),
+                         hasEntry(attributeName, attributeValue),
+                         hasEntry(ObjectProperties.Property.NAME, _nameAttributeValue)));
+    }
+
+}