Return-Path: X-Original-To: apmail-karaf-commits-archive@minotaur.apache.org Delivered-To: apmail-karaf-commits-archive@minotaur.apache.org Received: from mail.apache.org (hermes.apache.org [140.211.11.3]) by minotaur.apache.org (Postfix) with SMTP id 9355518677 for ; Mon, 29 Feb 2016 10:07:16 +0000 (UTC) Received: (qmail 14015 invoked by uid 500); 29 Feb 2016 10:06:54 -0000 Delivered-To: apmail-karaf-commits-archive@karaf.apache.org Received: (qmail 13980 invoked by uid 500); 29 Feb 2016 10:06:54 -0000 Mailing-List: contact commits-help@karaf.apache.org; run by ezmlm Precedence: bulk List-Help: List-Unsubscribe: List-Post: List-Id: Reply-To: dev@karaf.apache.org Delivered-To: mailing list commits@karaf.apache.org Received: (qmail 13967 invoked by uid 99); 29 Feb 2016 10:06:54 -0000 Received: from git1-us-west.apache.org (HELO git1-us-west.apache.org) (140.211.11.23) by apache.org (qpsmtpd/0.29) with ESMTP; Mon, 29 Feb 2016 10:06:54 +0000 Received: by git1-us-west.apache.org (ASF Mail Server at git1-us-west.apache.org, from userid 33) id 58CA8E0446; Mon, 29 Feb 2016 10:06:54 +0000 (UTC) Content-Type: text/plain; charset="us-ascii" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit From: gnodet@apache.org To: commits@karaf.apache.org Date: Mon, 29 Feb 2016 10:06:54 -0000 Message-Id: X-Mailer: ASF-Git Admin Mailer Subject: [1/2] karaf git commit: [KARAF-4351] Optimize ConfigAdmin access in bulk RBAC calls Repository: karaf Updated Branches: refs/heads/karaf-4.0.x e41bf62f7 -> 3877c0013 [KARAF-4351] Optimize ConfigAdmin access in bulk RBAC calls Project: http://git-wip-us.apache.org/repos/asf/karaf/repo Commit: http://git-wip-us.apache.org/repos/asf/karaf/commit/78ee25b4 Tree: http://git-wip-us.apache.org/repos/asf/karaf/tree/78ee25b4 Diff: http://git-wip-us.apache.org/repos/asf/karaf/diff/78ee25b4 Branch: refs/heads/karaf-4.0.x Commit: 78ee25b467bdd26a9451f1f293a50fe321e5175e Parents: e41bf62 Author: Grzegorz Grzybek Authored: Fri Feb 19 18:47:15 2016 +0100 Committer: Guillaume Nodet Committed: Mon Feb 29 11:00:16 2016 +0100 ---------------------------------------------------------------------- .../karaf/management/KarafMBeanServerGuard.java | 139 +++++++++++++------ .../management/internal/BulkRequestContext.java | 100 +++++++++++++ .../internal/JMXSecurityMBeanImpl.java | 30 ++-- .../internal/JMXSecurityMBeanImplTestCase.java | 87 ++++++++++-- 4 files changed, 290 insertions(+), 66 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/karaf/blob/78ee25b4/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 645a0df..b112c2f 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 @@ -16,10 +16,9 @@ */ package org.apache.karaf.management; +import org.apache.karaf.management.internal.BulkRequestContext; import org.apache.karaf.service.guard.tools.ACLConfigurationParser; import org.apache.karaf.util.jaas.JaasHelper; -import org.osgi.framework.InvalidSyntaxException; -import org.osgi.service.cm.Configuration; import org.osgi.service.cm.ConfigurationAdmin; import javax.management.*; @@ -27,9 +26,9 @@ import java.io.IOException; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; 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; @@ -95,6 +94,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 null + * @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()) { @@ -102,18 +115,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; } } @@ -132,6 +145,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 null + * @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); @@ -144,7 +172,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; } } @@ -152,10 +180,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() }); } } @@ -176,15 +204,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 null + * @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 (JaasHelper.currentUserHasRole(role)) return true; } @@ -203,7 +252,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[]{}); } } @@ -226,7 +275,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 { @@ -235,24 +284,17 @@ public class KarafMBeanServerGuard implements InvocationHandler { } } - private boolean canBypassRBAC(ObjectName objectName, String operationName) { + private boolean canBypassRBAC(BulkRequestContext context, ObjectName objectName, String operationName) { List allBypassObjectName = new ArrayList(); - try { - Configuration[] configs = configAdmin.listConfigurations("(service.pid=" + JMX_ACL_WHITELIST + ")"); - if (configs != null) { - for (Configuration config : configs) { - Enumeration keys = config.getProperties().keys(); - while (keys.hasMoreElements()) { - String element = keys.nextElement(); - allBypassObjectName.add(element); - } - } + + List> configs = context.getWhitelistProperties(); + for (Dictionary config : configs) { + Enumeration 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")) { @@ -276,10 +318,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 (JaasHelper.currentUserHasRole(role)) return; } @@ -287,26 +336,24 @@ public class KarafMBeanServerGuard implements InvocationHandler { } List 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 getRequiredRoles(ObjectName objectName, String methodName, Object[] params, String[] signature) throws IOException { + List getRequiredRoles(BulkRequestContext context, ObjectName objectName, String methodName, String[] signature) throws IOException { + return getRequiredRoles(context, objectName, methodName, null, signature); + } - List allPids = new ArrayList(); - try { - for (Configuration config : configAdmin.listConfigurations("(service.pid=jmx.acl*)")) { - allPids.add(config.getPid()); - } - } catch (InvalidSyntaxException ise) { - throw new RuntimeException(ise); - } + List getRequiredRoles(ObjectName objectName, String methodName, Object[] params, String[] signature) throws IOException { + return getRequiredRoles(BulkRequestContext.newContext(configAdmin), objectName, methodName, params, signature); + } + List 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, null); + Dictionary config = context.getConfiguration(generalPid); List roles = new ArrayList(); - 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/78ee25b4/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; + +/** + *

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.

+ *

Access to this object doesn't have to be synchronized, as it is passed down the canInvoke chain.

+ */ +public class BulkRequestContext { + + private List allPids = new ArrayList(); + private List> whiteListProperties = new ArrayList>(); + + private ConfigurationAdmin configAdmin; + + // cache with lifecycle bound to BulkRequestContext instance + private Map> cachedConfigurations = new HashMap>(); + + 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 getAllPids() { + return allPids; + } + + /** + * Returns list of configurations from + * @return + */ + public List> getWhitelistProperties() { + return whiteListProperties; + } + + /** + * Returns {@link Configuration ConfigAdmin configuration} - may be cached in this instance of + * {@link BulkRequestContext context} + * @param generalPid + * @return + */ + public Dictionary 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/78ee25b4/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 031fbdf..683be51 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 @@ -41,36 +41,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> bulkQuery) throws Exception { TabularData table = new TabularDataSupport(CAN_INVOKE_TABULAR_TYPE); + BulkRequestContext context = BulkRequestContext.newContext(guard.getConfigAdmin()); + for (Map.Entry> entry : bulkQuery.entrySet()) { String objectName = entry.getKey(); List 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 { @@ -80,9 +94,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/78ee25b4/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 cea0092..c77c484 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,6 +19,8 @@ package org.apache.karaf.management.internal; 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; @@ -44,7 +46,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(); @@ -59,7 +61,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(); @@ -75,7 +77,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(); @@ -102,9 +104,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(); @@ -123,7 +125,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(); @@ -145,17 +147,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(); @@ -190,4 +200,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 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> query = new HashMap>(); + 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); + } + }