Repository: karaf
Updated Branches:
  refs/heads/karaf-2.x fcc3e44c5 -> 5b29a4466


[KARAF-4351] Optimize ConfigAdmin access in bulk RBAC calls

(cherry picked from commit 8c8dffdc2e0439e099b865bc4a2b8cd4614cd2a2)


Project: http://git-wip-us.apache.org/repos/asf/karaf/repo
Commit: http://git-wip-us.apache.org/repos/asf/karaf/commit/e96633f0
Tree: http://git-wip-us.apache.org/repos/asf/karaf/tree/e96633f0
Diff: http://git-wip-us.apache.org/repos/asf/karaf/diff/e96633f0

Branch: refs/heads/karaf-2.x
Commit: e96633f09bda58f24db980e21ddba149d9e58e74
Parents: fcc3e44
Author: Grzegorz Grzybek <[email protected]>
Authored: Fri Feb 19 18:47:15 2016 +0100
Committer: Grzegorz Grzybek <[email protected]>
Committed: Mon Feb 22 14:22:04 2016 +0100

----------------------------------------------------------------------
 .../karaf/management/KarafMBeanServerGuard.java | 139 +++++++++++++------
 .../management/internal/BulkRequestContext.java | 100 +++++++++++++
 .../internal/JMXSecurityMBeanImpl.java          |  30 ++--
 .../internal/JMXSecurityMBeanImplTestCase.java  |  97 ++++++++++---
 4 files changed, 295 insertions(+), 71 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/karaf/blob/e96633f0/management/server/src/main/java/org/apache/karaf/management/KarafMBeanServerGuard.java
----------------------------------------------------------------------
diff --git 
a/management/server/src/main/java/org/apache/karaf/management/KarafMBeanServerGuard.java
 
b/management/server/src/main/java/org/apache/karaf/management/KarafMBeanServerGuard.java
index 884f8f6..e4e0377 100644
--- 
a/management/server/src/main/java/org/apache/karaf/management/KarafMBeanServerGuard.java
+++ 
b/management/server/src/main/java/org/apache/karaf/management/KarafMBeanServerGuard.java
@@ -23,9 +23,9 @@ import java.security.AccessControlContext;
 import java.security.AccessController;
 import java.security.Principal;
 import java.util.ArrayList;
-import java.util.Arrays;
 import java.util.Collections;
 import java.util.Comparator;
+import java.util.Dictionary;
 import java.util.Enumeration;
 import java.util.Iterator;
 import java.util.List;
@@ -45,9 +45,8 @@ import javax.management.ObjectName;
 import javax.security.auth.Subject;
 
 import org.apache.karaf.jaas.boot.principal.RolePrincipal;
+import org.apache.karaf.management.internal.BulkRequestContext;
 import org.apache.karaf.management.tools.ACLConfigurationParser;
-import org.osgi.framework.InvalidSyntaxException;
-import org.osgi.service.cm.Configuration;
 import org.osgi.service.cm.ConfigurationAdmin;
 
 import org.slf4j.Logger;
@@ -110,6 +109,20 @@ public class KarafMBeanServerGuard implements 
InvocationHandler {
      * @throws IOException
      */
     public boolean canInvoke(MBeanServer mbeanServer, ObjectName objectName) 
throws JMException, IOException {
+        return canInvoke(null, mbeanServer, objectName);
+    }
+
+    /**
+     * Returns whether there is any method that the current user can invoke.
+     *
+     * @param context {@link BulkRequestContext} for optimized ConfigAdmin 
access, may be <code>null</code>
+     * @param mbeanServer the MBeanServer where the object is registered.
+     * @param objectName the ObjectName to check.
+     * @return {@code true} if there is a method on the object that can be 
invoked, {@code false} else.
+     * @throws JMException
+     * @throws IOException
+     */
+    public boolean canInvoke(BulkRequestContext context, MBeanServer 
mbeanServer, ObjectName objectName) throws JMException, IOException {
         MBeanInfo info = mbeanServer.getMBeanInfo(objectName);
 
         for (MBeanOperationInfo operation : info.getOperations()) {
@@ -117,18 +130,18 @@ public class KarafMBeanServerGuard implements 
InvocationHandler {
             for (MBeanParameterInfo param : operation.getSignature()) {
                 sig.add(param.getType());
             }
-            if (canInvoke(objectName, operation.getName(), sig.toArray(new 
String[] {}))) {
+            if (canInvoke(context, objectName, operation.getName(), 
sig.toArray(new String[] {}))) {
                 return true;
             }
         }
 
         for (MBeanAttributeInfo attr : info.getAttributes()) {
             if (attr.isReadable()) {
-                if (canInvoke(objectName, attr.isIs() ? "is" : "get" + 
attr.getName(), new String[] {}))
+                if (canInvoke(context, objectName, attr.isIs() ? "is" : "get" 
+ attr.getName(), new String[] {}))
                     return true;
             }
             if (attr.isWritable()) {
-                if (canInvoke(objectName, "set" + attr.getName(), new 
String[]{attr.getType()}))
+                if (canInvoke(context, objectName, "set" + attr.getName(), new 
String[]{attr.getType()}))
                     return true;
             }
         }
@@ -147,6 +160,21 @@ public class KarafMBeanServerGuard implements 
InvocationHandler {
      * @throws IOException
      */
     public boolean canInvoke(MBeanServer mbeanServer, ObjectName objectName, 
String methodName) throws JMException, IOException {
+        return canInvoke(null, mbeanServer, objectName, methodName);
+    }
+
+    /**
+     * Returns whether there is any overload of the specified method that can 
be invoked by the current user.
+     *
+     * @param context {@link BulkRequestContext} for optimized ConfigAdmin 
access, may be <code>null</code>
+     * @param mbeanServer the MBeanServer where the object is registered.
+     * @param objectName the MBean ObjectName.
+     * @param methodName the name of the method.
+     * @return {@code true} if there is an overload of the method that can be 
invoked by the current user.
+     * @throws JMException
+     * @throws IOException
+     */
+    public boolean canInvoke(BulkRequestContext context, MBeanServer 
mbeanServer, ObjectName objectName, String methodName) throws JMException, 
IOException {
         methodName = methodName.trim();
         MBeanInfo info = mbeanServer.getMBeanInfo(objectName);
 
@@ -159,7 +187,7 @@ public class KarafMBeanServerGuard implements 
InvocationHandler {
             for (MBeanParameterInfo param : op.getSignature()) {
                 sig.add(param.getType());
             }
-            if (canInvoke(objectName, op.getName(), sig.toArray(new String[] 
{}))) {
+            if (canInvoke(context, objectName, op.getName(), sig.toArray(new 
String[] {}))) {
                 return true;
             }
         }
@@ -167,10 +195,10 @@ public class KarafMBeanServerGuard implements 
InvocationHandler {
         for (MBeanAttributeInfo attr : info.getAttributes()) {
             String attrName = attr.getName();
             if (methodName.equals("is" + attrName) || methodName.equals("get" 
+ attrName)) {
-                return canInvoke(objectName, methodName, new String[] {});
+                return canInvoke(context, objectName, methodName, new String[] 
{});
             }
             if (methodName.equals("set" + attrName)) {
-                return canInvoke(objectName, methodName, new String[] { 
attr.getType() });
+                return canInvoke(context, objectName, methodName, new String[] 
{ attr.getType() });
             }
         }
 
@@ -191,15 +219,36 @@ public class KarafMBeanServerGuard implements 
InvocationHandler {
      * @throws IOException
      */
     public boolean canInvoke(MBeanServer mbeanServer, ObjectName objectName, 
String methodName, String[] signature) throws IOException {
+        return canInvoke(null, mbeanServer, objectName, methodName, signature);
+    }
+
+    /**
+     * Returns true if the method on the MBean with the specified signature 
can be invoked.
+     *
+     * @param context {@link BulkRequestContext} for optimized ConfigAdmin 
access, may be <code>null</code>
+     * @param mbeanServer the MBeanServer where the object is registered.
+     * @param objectName the MBean ObjectName.
+     * @param methodName the name of the method.
+     * @param signature the signature of the method.
+     * @return {@code true} if the method can be invoked, {@code false} else. 
Note that if a method name or signature
+     *      is provided that does not exist on the MBean, the behaviour of 
this method is undefined. In other words,
+     *      if you ask whether a method that does not exist can be invoked, 
the method may return {@code true} but
+     *      actually invoking that method will obviously not work.
+     * @throws IOException
+     */
+    public boolean canInvoke(BulkRequestContext context, MBeanServer 
mbeanServer, ObjectName objectName, String methodName, String[] signature) 
throws IOException {
         // no checking done on the MBeanServer of whether the method actually 
exists...
-        return canInvoke(objectName, methodName, signature);
+        return canInvoke(context, objectName, methodName, signature);
     }
 
-    private boolean canInvoke(ObjectName objectName, String methodName, 
String[] signature) throws IOException {
-        if (canBypassRBAC(objectName, methodName)) {
+    private boolean canInvoke(BulkRequestContext context, ObjectName 
objectName, String methodName, String[] signature) throws IOException {
+        if (context == null) {
+            context = BulkRequestContext.newContext(configAdmin);
+        }
+        if (canBypassRBAC(context, objectName, methodName)) {
             return true;
         }
-        for (String role : getRequiredRoles(objectName, methodName, 
signature)) {
+        for (String role : getRequiredRoles(context, objectName, methodName, 
signature)) {
             if (currentUserHasRole(role))
                 return true;
         }
@@ -218,7 +267,7 @@ public class KarafMBeanServerGuard implements 
InvocationHandler {
         if (prefix == null) {
             LOG.debug("Attribute " + attributeName + " can not be found for 
MBean " + objectName.toString());
         } else {
-            handleInvoke(objectName, prefix + attributeName, new Object[]{}, 
new String[]{});
+            handleInvoke(null, objectName, prefix + attributeName, new 
Object[]{}, new String[]{});
         }
     }
 
@@ -241,7 +290,7 @@ public class KarafMBeanServerGuard implements 
InvocationHandler {
         if (dataType == null)
             throw new IllegalStateException("Attribute data type can not be 
found");
 
-        handleInvoke(objectName, "set" + attribute.getName(), new Object[]{ 
attribute.getValue() }, new String[]{ dataType });
+        handleInvoke(null, objectName, "set" + attribute.getName(), new 
Object[]{ attribute.getValue() }, new String[]{ dataType });
     }
 
     private void handleSetAttributes(MBeanServer proxy, ObjectName objectName, 
AttributeList attributes) throws JMException, IOException {
@@ -250,24 +299,17 @@ public class KarafMBeanServerGuard implements 
InvocationHandler {
         }
     }
     
-    private boolean canBypassRBAC(ObjectName objectName, String operationName) 
{
+    private boolean canBypassRBAC(BulkRequestContext context, ObjectName 
objectName, String operationName) {
         List<String> allBypassObjectName = new ArrayList<String>();
-        try {
-            Configuration[] configs = 
configAdmin.listConfigurations("(service.pid=" + JMX_ACL_WHITELIST + ")");
-            if (configs != null) {
-                for (Configuration config : configs) {
-                    Enumeration<String> keys = config.getProperties().keys();
-                    while (keys.hasMoreElements()) {
-                        String element = keys.nextElement();
-                        allBypassObjectName.add(element);
-                    }
-                }
+
+        List<Dictionary<String, Object>> configs = 
context.getWhitelistProperties();
+        for (Dictionary<String, Object> config : configs) {
+            Enumeration<String> keys = config.keys();
+            while (keys.hasMoreElements()) {
+                String element = keys.nextElement();
+                allBypassObjectName.add(element);
             }
-        } catch (InvalidSyntaxException ise) {
-            throw new RuntimeException(ise);
-        } catch (IOException e) {
-            throw new RuntimeException(e);
-        } 
+        }
 
         for (String pid : iterateDownPids(getNameSegments(objectName))) {
             if (!pid.equals("jmx.acl"))  {
@@ -291,10 +333,17 @@ public class KarafMBeanServerGuard implements 
InvocationHandler {
     }
 
     void handleInvoke(ObjectName objectName, String operationName, Object[] 
params, String[] signature) throws IOException {
-        if (canBypassRBAC(objectName, operationName)) {
+        handleInvoke(null, objectName, operationName, params, signature);
+    }
+
+    void handleInvoke(BulkRequestContext context, ObjectName objectName, 
String operationName, Object[] params, String[] signature) throws IOException {
+        if (context == null) {
+            context = BulkRequestContext.newContext(configAdmin);
+        }
+        if (canBypassRBAC(context, objectName, operationName)) {
             return;
         }
-        for (String role : getRequiredRoles(objectName, operationName, params, 
signature)) {
+        for (String role : getRequiredRoles(context, objectName, 
operationName, params, signature)) {
             if (currentUserHasRole(role))
                 return;
         }
@@ -302,26 +351,24 @@ public class KarafMBeanServerGuard implements 
InvocationHandler {
     }
 
     List<String> getRequiredRoles(ObjectName objectName, String methodName, 
String[] signature) throws IOException {
-        return getRequiredRoles(objectName, methodName, null, signature);
+        return getRequiredRoles(BulkRequestContext.newContext(configAdmin), 
objectName, methodName, null, signature);
     }
 
-    List<String> getRequiredRoles(ObjectName objectName, String methodName, 
Object[] params, String[] signature) throws IOException {
+    List<String> getRequiredRoles(BulkRequestContext context, ObjectName 
objectName, String methodName, String[] signature) throws IOException {
+        return getRequiredRoles(context, objectName, methodName, null, 
signature);
+    }
 
-        List<String> allPids = new ArrayList<String>();
-        try {
-            for (Configuration config : 
configAdmin.listConfigurations("(service.pid=jmx.acl*)")) {
-                allPids.add(config.getPid());
-            }
-        } catch (InvalidSyntaxException ise) {
-            throw new RuntimeException(ise);
-        }
+    List<String> getRequiredRoles(ObjectName objectName, String methodName, 
Object[] params, String[] signature) throws IOException {
+        return getRequiredRoles(BulkRequestContext.newContext(configAdmin), 
objectName, methodName, params, signature);
+    }
 
+    List<String> getRequiredRoles(BulkRequestContext context, ObjectName 
objectName, String methodName, Object[] params, String[] signature) throws 
IOException {
         for (String pid : iterateDownPids(getNameSegments(objectName))) {
-            String generalPid = getGeneralPid(allPids, pid);
+            String generalPid = getGeneralPid(context.getAllPids(), pid);
             if (generalPid.length() > 0) {
-                Configuration config = 
configAdmin.getConfiguration(generalPid);
+                Dictionary<String, Object> config = 
context.getConfiguration(generalPid);
                 List<String> roles = new ArrayList<String>();
-                ACLConfigurationParser.Specificity s = 
ACLConfigurationParser.getRolesForInvocation(methodName, params, signature, 
config.getProperties(), roles);
+                ACLConfigurationParser.Specificity s = 
ACLConfigurationParser.getRolesForInvocation(methodName, params, signature, 
config, roles);
                 if (s != ACLConfigurationParser.Specificity.NO_MATCH) {
                     return roles;
                 }

http://git-wip-us.apache.org/repos/asf/karaf/blob/e96633f0/management/server/src/main/java/org/apache/karaf/management/internal/BulkRequestContext.java
----------------------------------------------------------------------
diff --git 
a/management/server/src/main/java/org/apache/karaf/management/internal/BulkRequestContext.java
 
b/management/server/src/main/java/org/apache/karaf/management/internal/BulkRequestContext.java
new file mode 100644
index 0000000..1c72a60
--- /dev/null
+++ 
b/management/server/src/main/java/org/apache/karaf/management/internal/BulkRequestContext.java
@@ -0,0 +1,100 @@
+/*
+ * 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.karaf.management.internal;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Dictionary;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.osgi.framework.InvalidSyntaxException;
+import org.osgi.service.cm.Configuration;
+import org.osgi.service.cm.ConfigurationAdmin;
+
+/**
+ * <p>Class to optimize ConfigAdmin access with the lifecycle of single
+ * {@link org.apache.karaf.management.JMXSecurityMBean#canInvoke(Map) bulk 
query invocation}. This prevents countless
+ * {@link org.osgi.service.cm.ConfigurationAdmin#listConfigurations(String) 
listings of ConfigAdmin configurations}
+ * for each checked MBean/method.</p>
+ * <p>Access to this object doesn't have to be synchronized, as it is passed 
down the <code>canInvoke</code> chain.</p>
+ */
+public class BulkRequestContext {
+
+    private List<String> allPids = new ArrayList<String>();
+    private List<Dictionary<String, Object>> whiteListProperties = new 
ArrayList<Dictionary<String, Object>>();
+
+    private ConfigurationAdmin configAdmin;
+
+    // cache with lifecycle bound to BulkRequestContext instance
+    private Map<String, Dictionary<String, Object>> cachedConfigurations = new 
HashMap<String, Dictionary<String, Object>>();
+
+    private BulkRequestContext() {}
+
+    public static BulkRequestContext newContext(ConfigurationAdmin 
configAdmin) throws IOException {
+        BulkRequestContext context = new BulkRequestContext();
+        context.configAdmin = configAdmin;
+        try {
+            // list available ACL configs - valid for this instance only
+            for (Configuration config : 
configAdmin.listConfigurations("(service.pid=jmx.acl*)")) {
+                context.allPids.add(config.getPid());
+            }
+            // list available ACT whitelist configs
+            Configuration[] configs = 
configAdmin.listConfigurations("(service.pid=jmx.acl.whitelist)");
+            if (configs != null) {
+                for (Configuration config : configs) {
+                    context.whiteListProperties.add(config.getProperties());
+                }
+            }
+        } catch (InvalidSyntaxException ise) {
+            throw new RuntimeException(ise);
+        }
+
+        return context;
+    }
+
+    /**
+     * Returns list of PIDs related to RBAC/ACL
+     * @return
+     */
+    public List<String> getAllPids() {
+        return allPids;
+    }
+
+    /**
+     * Returns list of configurations from
+     * @return
+     */
+    public List<Dictionary<String,Object>> getWhitelistProperties() {
+        return whiteListProperties;
+    }
+
+    /**
+     * Returns {@link Configuration ConfigAdmin configuration} - may be cached 
in this instance of
+     * {@link BulkRequestContext context}
+     * @param generalPid
+     * @return
+     */
+    public Dictionary<String, Object> getConfiguration(String generalPid) 
throws IOException {
+        if (!cachedConfigurations.containsKey(generalPid)) {
+            cachedConfigurations.put(generalPid, 
configAdmin.getConfiguration(generalPid, null).getProperties());
+        }
+        return cachedConfigurations.get(generalPid);
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/karaf/blob/e96633f0/management/server/src/main/java/org/apache/karaf/management/internal/JMXSecurityMBeanImpl.java
----------------------------------------------------------------------
diff --git 
a/management/server/src/main/java/org/apache/karaf/management/internal/JMXSecurityMBeanImpl.java
 
b/management/server/src/main/java/org/apache/karaf/management/internal/JMXSecurityMBeanImpl.java
index d572e70..3e158fb 100644
--- 
a/management/server/src/main/java/org/apache/karaf/management/internal/JMXSecurityMBeanImpl.java
+++ 
b/management/server/src/main/java/org/apache/karaf/management/internal/JMXSecurityMBeanImpl.java
@@ -42,36 +42,50 @@ public class JMXSecurityMBeanImpl extends StandardMBean 
implements JMXSecurityMB
     }
 
     public boolean canInvoke(String objectName) throws Exception {
+        return canInvoke((BulkRequestContext) null, objectName);
+    }
+
+    public boolean canInvoke(String objectName, String methodName) throws 
Exception {
+        return canInvoke(null, objectName, methodName);
+    }
+
+    public boolean canInvoke(String objectName, String methodName, String[] 
argumentTypes) throws Exception {
+        return canInvoke(null, objectName, methodName, argumentTypes);
+    }
+
+    private boolean canInvoke(BulkRequestContext context, String objectName) 
throws Exception {
         if (guard == null)
             return true;
 
-        return guard.canInvoke(mbeanServer, new ObjectName(objectName));
+        return guard.canInvoke(context, mbeanServer, new 
ObjectName(objectName));
     }
 
-    public boolean canInvoke(String objectName, String methodName) throws 
Exception {
+    private boolean canInvoke(BulkRequestContext context, String objectName, 
String methodName) throws Exception {
         if (guard == null)
             return true;
 
-        return guard.canInvoke(mbeanServer, new ObjectName(objectName), 
methodName);
+        return guard.canInvoke(context, mbeanServer, new 
ObjectName(objectName), methodName);
     }
 
-    public boolean canInvoke(String objectName, String methodName, String[] 
argumentTypes) throws Exception {
+    private boolean canInvoke(BulkRequestContext context, String objectName, 
String methodName, String[] argumentTypes) throws Exception {
         ObjectName on = new ObjectName(objectName);
 
         if (guard == null)
             return true;
 
-        return guard.canInvoke(mbeanServer, on, methodName, argumentTypes);
+        return guard.canInvoke(context, mbeanServer, on, methodName, 
argumentTypes);
     }
 
     public TabularData canInvoke(Map<String, List<String>> bulkQuery) throws 
Exception {
         TabularData table = new TabularDataSupport(CAN_INVOKE_TABULAR_TYPE);
 
+        BulkRequestContext context = 
BulkRequestContext.newContext(guard.getConfigAdmin());
+
         for (Map.Entry<String, List<String>> entry : bulkQuery.entrySet()) {
             String objectName = entry.getKey();
             List<String> methods = entry.getValue();
             if (methods.size() == 0) {
-                boolean res = canInvoke(objectName);
+                boolean res = canInvoke(context, objectName);
                 CompositeData data = new 
CompositeDataSupport(CAN_INVOKE_RESULT_ROW_TYPE, CAN_INVOKE_RESULT_COLUMNS, new 
Object[]{ objectName, "", res });
                 table.put(data);
             } else {
@@ -81,9 +95,9 @@ public class JMXSecurityMBeanImpl extends StandardMBean 
implements JMXSecurityMB
 
                     boolean res;
                     if (name.equals(method)) {
-                        res = canInvoke(objectName, name);
+                        res = canInvoke(context, objectName, name);
                     } else {
-                        res = canInvoke(objectName, name, argTypes.toArray(new 
String[]{}));
+                        res = canInvoke(context, objectName, name, 
argTypes.toArray(new String[]{}));
                     }
                     CompositeData data = new 
CompositeDataSupport(CAN_INVOKE_RESULT_ROW_TYPE, CAN_INVOKE_RESULT_COLUMNS, new 
Object[]{ objectName, method, res });
                     table.put(data);

http://git-wip-us.apache.org/repos/asf/karaf/blob/e96633f0/management/server/src/test/java/org/apache/karaf/management/internal/JMXSecurityMBeanImplTestCase.java
----------------------------------------------------------------------
diff --git 
a/management/server/src/test/java/org/apache/karaf/management/internal/JMXSecurityMBeanImplTestCase.java
 
b/management/server/src/test/java/org/apache/karaf/management/internal/JMXSecurityMBeanImplTestCase.java
index 28ce695..d1bd053 100644
--- 
a/management/server/src/test/java/org/apache/karaf/management/internal/JMXSecurityMBeanImplTestCase.java
+++ 
b/management/server/src/test/java/org/apache/karaf/management/internal/JMXSecurityMBeanImplTestCase.java
@@ -19,20 +19,22 @@ package org.apache.karaf.management.internal;
 import java.io.IOException;
 import java.util.Arrays;
 import java.util.Collections;
+import java.util.Dictionary;
 import java.util.HashMap;
+import java.util.Hashtable;
 import java.util.List;
 import java.util.Map;
+import junit.framework.TestCase;
+import org.apache.karaf.management.KarafMBeanServerGuard;
+import org.easymock.EasyMock;
+import org.osgi.service.cm.Configuration;
+import org.osgi.service.cm.ConfigurationAdmin;
 
 import javax.management.MBeanServer;
 import javax.management.ObjectName;
 import javax.management.openmbean.CompositeData;
 import javax.management.openmbean.TabularData;
 
-import junit.framework.TestCase;
-
-import org.apache.karaf.management.KarafMBeanServerGuard;
-import org.easymock.EasyMock;
-
 public class JMXSecurityMBeanImplTestCase extends TestCase {
 
     public void testMBeanServerAccessors() throws Exception {
@@ -50,7 +52,7 @@ public class JMXSecurityMBeanImplTestCase extends TestCase {
 
         String objectName = "foo.bar.testing:type=SomeMBean";
         KarafMBeanServerGuard testGuard = 
EasyMock.createMock(KarafMBeanServerGuard.class);
-        EasyMock.expect(testGuard.canInvoke(mbs, new 
ObjectName(objectName))).andReturn(true);
+        EasyMock.expect(testGuard.canInvoke(null, mbs, new 
ObjectName(objectName))).andReturn(true);
         EasyMock.replay(testGuard);
 
         JMXSecurityMBeanImpl mb = new JMXSecurityMBeanImpl();
@@ -65,7 +67,7 @@ public class JMXSecurityMBeanImplTestCase extends TestCase {
 
         String objectName = "foo.bar.testing:type=SomeMBean";
         KarafMBeanServerGuard testGuard = 
EasyMock.createMock(KarafMBeanServerGuard.class);
-        EasyMock.expect(testGuard.canInvoke(mbs, new 
ObjectName(objectName))).andReturn(false);
+        EasyMock.expect(testGuard.canInvoke(null, mbs, new 
ObjectName(objectName))).andReturn(false);
         EasyMock.replay(testGuard);
 
         JMXSecurityMBeanImpl mb = new JMXSecurityMBeanImpl();
@@ -81,7 +83,7 @@ public class JMXSecurityMBeanImplTestCase extends TestCase {
 
             String objectName = "foo.bar.testing:type=SomeMBean";
             KarafMBeanServerGuard testGuard = 
EasyMock.createMock(KarafMBeanServerGuard.class);
-            EasyMock.expect(testGuard.canInvoke(mbs, new 
ObjectName(objectName))).andThrow(new IOException());
+            EasyMock.expect(testGuard.canInvoke(null, mbs, new 
ObjectName(objectName))).andThrow(new IOException());
             EasyMock.replay(testGuard);
 
             JMXSecurityMBeanImpl mb = new JMXSecurityMBeanImpl();
@@ -108,9 +110,9 @@ public class JMXSecurityMBeanImplTestCase extends TestCase {
         String[] la = new String[]{"long"};
         String[] sa = new String[]{"java.lang.String"};
         String[] sa2 = new String[]{"java.lang.String", "java.lang.String"};
-        EasyMock.expect(testGuard.canInvoke(mbs, new ObjectName(objectName), 
"testMethod", la)).andReturn(true);
-        EasyMock.expect(testGuard.canInvoke(mbs, new ObjectName(objectName), 
"testMethod", sa)).andReturn(true);
-        EasyMock.expect(testGuard.canInvoke(mbs, new ObjectName(objectName), 
"otherMethod", sa2)).andReturn(false);
+        EasyMock.expect(testGuard.canInvoke(null, mbs, new 
ObjectName(objectName), "testMethod", la)).andReturn(true);
+        EasyMock.expect(testGuard.canInvoke(null, mbs, new 
ObjectName(objectName), "testMethod", sa)).andReturn(true);
+        EasyMock.expect(testGuard.canInvoke(null, mbs, new 
ObjectName(objectName), "otherMethod", sa2)).andReturn(false);
         EasyMock.replay(testGuard);
 
         JMXSecurityMBeanImpl mb = new JMXSecurityMBeanImpl();
@@ -129,7 +131,7 @@ public class JMXSecurityMBeanImplTestCase extends TestCase {
             String objectName = "foo.bar.testing:type=SomeMBean";
             KarafMBeanServerGuard testGuard = 
EasyMock.createMock(KarafMBeanServerGuard.class);
             String[] ea = new String[]{};
-            EasyMock.expect(testGuard.canInvoke(mbs, new 
ObjectName(objectName), "testMethod", ea)).andThrow(new IOException());
+            EasyMock.expect(testGuard.canInvoke(null, mbs, new 
ObjectName(objectName), "testMethod", ea)).andThrow(new IOException());
             EasyMock.replay(testGuard);
 
             JMXSecurityMBeanImpl mb = new JMXSecurityMBeanImpl();
@@ -151,17 +153,25 @@ public class JMXSecurityMBeanImplTestCase extends 
TestCase {
         MBeanServer mbs = EasyMock.createMock(MBeanServer.class);
         EasyMock.replay(mbs);
 
+        ConfigurationAdmin testConfigAdmin = 
EasyMock.createMock(ConfigurationAdmin.class);
+        
EasyMock.expect(testConfigAdmin.listConfigurations(EasyMock.eq("(service.pid=jmx.acl*)")))
+                .andReturn(new Configuration[0]).anyTimes();
+        
EasyMock.expect(testConfigAdmin.listConfigurations(EasyMock.eq("(service.pid=jmx.acl.whitelist)")))
+                .andReturn(new Configuration[0]).once();
+        EasyMock.replay(testConfigAdmin);
+
         KarafMBeanServerGuard testGuard = 
EasyMock.createMock(KarafMBeanServerGuard.class);
         String objectName = "foo.bar.testing:type=SomeMBean";
         final String[] la = new String[]{"long"};
         final String[] sa = new String[]{"java.lang.String"};
-        EasyMock.expect(testGuard.canInvoke(EasyMock.eq(mbs), EasyMock.eq(new 
ObjectName(objectName)), EasyMock.eq("testMethod"), 
EasyMock.aryEq(la))).andReturn(true).anyTimes();
-        EasyMock.expect(testGuard.canInvoke(EasyMock.eq(mbs), EasyMock.eq(new 
ObjectName(objectName)), EasyMock.eq("testMethod"), 
EasyMock.aryEq(sa))).andReturn(false).anyTimes();
-        EasyMock.expect(testGuard.canInvoke(EasyMock.eq(mbs), EasyMock.eq(new 
ObjectName(objectName)), 
EasyMock.eq("otherMethod"))).andReturn(true).anyTimes();
+        
EasyMock.expect(testGuard.getConfigAdmin()).andReturn(testConfigAdmin).anyTimes();
+        
EasyMock.expect(testGuard.canInvoke(EasyMock.anyObject(BulkRequestContext.class),
 EasyMock.eq(mbs), EasyMock.eq(new ObjectName(objectName)), 
EasyMock.eq("testMethod"), EasyMock.aryEq(la))).andReturn(true).anyTimes();
+        
EasyMock.expect(testGuard.canInvoke(EasyMock.anyObject(BulkRequestContext.class),
 EasyMock.eq(mbs), EasyMock.eq(new ObjectName(objectName)), 
EasyMock.eq("testMethod"), EasyMock.aryEq(sa))).andReturn(false).anyTimes();
+        
EasyMock.expect(testGuard.canInvoke(EasyMock.anyObject(BulkRequestContext.class),
 EasyMock.eq(mbs), EasyMock.eq(new ObjectName(objectName)), 
EasyMock.eq("otherMethod"))).andReturn(true).anyTimes();
         String objectName2 = "foo.bar.testing:type=SomeOtherMBean";
-        EasyMock.expect(testGuard.canInvoke(EasyMock.eq(mbs), EasyMock.eq(new 
ObjectName(objectName2)))).andReturn(true).anyTimes();
+        
EasyMock.expect(testGuard.canInvoke(EasyMock.anyObject(BulkRequestContext.class),
 EasyMock.eq(mbs), EasyMock.eq(new 
ObjectName(objectName2)))).andReturn(true).anyTimes();
         String objectName3 = "foo.bar.foo.testing:type=SomeOtherMBean";
-        EasyMock.expect(testGuard.canInvoke(EasyMock.eq(mbs), EasyMock.eq(new 
ObjectName(objectName3)))).andReturn(false).anyTimes();
+        
EasyMock.expect(testGuard.canInvoke(EasyMock.anyObject(BulkRequestContext.class),
 EasyMock.eq(mbs), EasyMock.eq(new 
ObjectName(objectName3)))).andReturn(false).anyTimes();
         EasyMock.replay(testGuard);
 
         JMXSecurityMBeanImpl mb = new JMXSecurityMBeanImpl();
@@ -196,4 +206,57 @@ public class JMXSecurityMBeanImplTestCase extends TestCase 
{
         assertEquals(false, cd5.get("CanInvoke"));
     }
 
+    public void testCanInvokeBulkCacheConfigAdmin() throws Exception {
+        MBeanServer mbs = EasyMock.createMock(MBeanServer.class);
+        EasyMock.replay(mbs);
+
+        Configuration fooWildcardTesting = 
EasyMock.createMock(Configuration.class);
+        
EasyMock.expect(fooWildcardTesting.getPid()).andReturn("jmx.acl.foo._.testing").once();
+        EasyMock.replay(fooWildcardTesting);
+
+        Dictionary<String, Object> fooBarProperties = new Hashtable<>();
+        // using '*' frees us from mocking JAAS
+        fooBarProperties.put("testMethod(java.lang.String)", "*");
+        fooBarProperties.put("testMethod(long)", "*");
+        Configuration fooBarTesting = EasyMock.createMock(Configuration.class);
+        
EasyMock.expect(fooBarTesting.getPid()).andReturn("jmx.acl.foo.bar.testing").once();
+        
EasyMock.expect(fooBarTesting.getProperties()).andReturn(fooBarProperties).once();
+        EasyMock.replay(fooBarTesting);
+
+        ConfigurationAdmin testConfigAdmin = 
EasyMock.createMock(ConfigurationAdmin.class);
+        
EasyMock.expect(testConfigAdmin.listConfigurations(EasyMock.eq("(service.pid=jmx.acl*)")))
+                .andReturn(new Configuration[] { fooWildcardTesting, 
fooBarTesting }).once();
+        
EasyMock.expect(testConfigAdmin.listConfigurations(EasyMock.eq("(service.pid=jmx.acl.whitelist)")))
+                .andReturn(new Configuration[0]).once();
+        
EasyMock.expect(testConfigAdmin.getConfiguration(EasyMock.eq("jmx.acl.foo.bar.testing"),
 EasyMock.isNull(String.class)))
+                .andReturn(fooBarTesting).once();
+        EasyMock.replay(testConfigAdmin);
+
+        KarafMBeanServerGuard guard = new KarafMBeanServerGuard();
+        guard.setConfigAdmin(testConfigAdmin);
+
+        String objectName = "foo.bar.testing:type=SomeMBean";
+        String objectName2 = "foo.bar.testing:type=SomeOtherMBean";
+
+        JMXSecurityMBeanImpl mb = new JMXSecurityMBeanImpl();
+        mb.setMBeanServer(mbs);
+        mb.setGuard(guard);
+        Map<String, List<String>> query = new HashMap<String, List<String>>();
+        query.put(objectName, 
Collections.singletonList("testMethod(java.lang.String)"));
+        query.put(objectName2, Collections.singletonList("testMethod(long)"));
+        TabularData result = mb.canInvoke(query);
+        assertEquals(2, result.size());
+
+        CompositeData cd2 = result.get(new Object[]{objectName, 
"testMethod(java.lang.String)"});
+        assertEquals(objectName, cd2.get("ObjectName"));
+        assertEquals("testMethod(java.lang.String)", cd2.get("Method"));
+        assertEquals(true, cd2.get("CanInvoke"));
+        CompositeData cd4 = result.get(new Object[]{objectName2, 
"testMethod(long)"});
+        assertEquals(objectName2, cd4.get("ObjectName"));
+        assertEquals("testMethod(long)", cd4.get("Method"));
+        assertEquals(true, cd4.get("CanInvoke"));
+
+        EasyMock.verify(testConfigAdmin, fooWildcardTesting, fooBarTesting);
+    }
+
 }

Reply via email to