You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@servicemix.apache.org by ge...@apache.org on 2009/11/16 09:37:39 UTC

svn commit: r880669 - in /servicemix/smx3/trunk: ./ core/servicemix-core/src/main/java/org/apache/servicemix/jbi/jmx/ core/servicemix-core/src/test/java/org/apache/servicemix/jbi/jmx/ samples/bridge-camel/ samples/bridge-camel/bridge-camel-sa/ samples/...

Author: gertv
Date: Mon Nov 16 08:37:37 2009
New Revision: 880669

URL: http://svn.apache.org/viewvc?rev=880669&view=rev
Log:
SM-1915: Support for more fine-grained authorization in JMX

Added:
    servicemix/smx3/trunk/core/servicemix-core/src/main/java/org/apache/servicemix/jbi/jmx/AdminReadWritePolicy.java
    servicemix/smx3/trunk/core/servicemix-core/src/main/java/org/apache/servicemix/jbi/jmx/Policy.java
    servicemix/smx3/trunk/core/servicemix-core/src/test/java/org/apache/servicemix/jbi/jmx/
    servicemix/smx3/trunk/core/servicemix-core/src/test/java/org/apache/servicemix/jbi/jmx/AdminReadWritePolicyTest.java
Modified:
    servicemix/smx3/trunk/   (props changed)
    servicemix/smx3/trunk/core/servicemix-core/src/main/java/org/apache/servicemix/jbi/jmx/ConnectorServerFactoryBean.java
    servicemix/smx3/trunk/samples/bridge-camel/   (props changed)
    servicemix/smx3/trunk/samples/bridge-camel/bridge-camel-sa/   (props changed)
    servicemix/smx3/trunk/samples/bridge-camel/bridge-camel-su/   (props changed)
    servicemix/smx3/trunk/samples/bridge/bridge-sa-itest/   (props changed)
    servicemix/smx3/trunk/samples/camel/   (props changed)
    servicemix/smx3/trunk/samples/camel/camel-sa/   (props changed)
    servicemix/smx3/trunk/samples/camel/camel-sa-itest/   (props changed)
    servicemix/smx3/trunk/samples/camel/camel-simple-su/   (props changed)
    servicemix/smx3/trunk/samples/cxf-wsdl-first/   (props changed)
    servicemix/smx3/trunk/samples/cxf-wsdl-first/wsdl-first-cxf-sa/   (props changed)
    servicemix/smx3/trunk/samples/cxf-wsdl-first/wsdl-first-cxfbc-su/   (props changed)
    servicemix/smx3/trunk/samples/cxf-wsdl-first/wsdl-first-cxfse-su/   (props changed)

Propchange: servicemix/smx3/trunk/
------------------------------------------------------------------------------
--- svn:ignore (original)
+++ svn:ignore Mon Nov 16 08:37:37 2009
@@ -10,3 +10,4 @@
 *.iml
 velocity.log
 .project
+.idea

Added: servicemix/smx3/trunk/core/servicemix-core/src/main/java/org/apache/servicemix/jbi/jmx/AdminReadWritePolicy.java
URL: http://svn.apache.org/viewvc/servicemix/smx3/trunk/core/servicemix-core/src/main/java/org/apache/servicemix/jbi/jmx/AdminReadWritePolicy.java?rev=880669&view=auto
==============================================================================
--- servicemix/smx3/trunk/core/servicemix-core/src/main/java/org/apache/servicemix/jbi/jmx/AdminReadWritePolicy.java (added)
+++ servicemix/smx3/trunk/core/servicemix-core/src/main/java/org/apache/servicemix/jbi/jmx/AdminReadWritePolicy.java Mon Nov 16 08:37:37 2009
@@ -0,0 +1,95 @@
+/*
+ * 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.servicemix.jbi.jmx;
+
+import java.lang.reflect.Method;
+import java.security.Principal;
+import javax.security.auth.Subject;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.servicemix.jbi.security.GroupPrincipal;
+
+/**
+ * Policy implementation that grants read-write access to members of the 'admin' group
+ * and read-only access to all other users.
+ *
+ * @org.apache.xbean.XBean element="adminReadWritePolicy"
+ */
+public class AdminReadWritePolicy extends Policy {
+
+    private static final Log LOG = LogFactory.getLog(AdminReadWritePolicy.class);
+    private static final String INVOKE = "invoke";
+
+    /**
+     * {@inheritDoc} 
+     */
+    public void checkAuthorization(Subject subject, Object target, Method method, Object[] args)
+        throws SecurityException {
+        if (isReadOnly(method) || isAdmin(subject) || isInvokeReadOnly(method, args)) {
+            // allow the method invocation
+        } else {
+            LOG.warn(String.format("Denied access to MBeanServer.%s(%s) to %s",
+                                   method.getName(), explode(args), subject));
+            throw new SecurityException("Not authorized to run MBeanServer." + method.getName()
+                                        + "\n(" + explode(args) + ")");
+        }
+    }
+
+    /*
+     * Check if the call to invoke target a read-only method
+     */
+    private boolean isInvokeReadOnly(Method method, Object[] args) {
+        return INVOKE.equals(method.getName()) && args != null && args.length >= 2 && isReadOnly((String) args[1]);
+    }
+
+    /*
+     * Explode the array of arguments into a ,-separated String
+     */
+    private String explode(Object[] args) {
+        if (args == null || args.length == 0) {
+            return "";
+        } else {
+            StringBuffer buffer = new StringBuffer();
+            Object last = args[args.length - 1];
+            for (Object arg : args) {
+                buffer.append(arg);
+                if (arg != last) {
+                    buffer.append(", ");
+                }
+            }
+            return buffer.toString();
+        }
+    }
+
+    /*
+     * Check if the subject is a member of the 'admin' group
+     */
+    private boolean isAdmin(Subject subject) {
+        for (Principal principal : subject.getPrincipals()) {
+            if (principal instanceof GroupPrincipal && "admin".equals(principal.getName())) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    @Override
+    public String toString() {
+        return "admin group read-write access";
+    }
+}

Modified: servicemix/smx3/trunk/core/servicemix-core/src/main/java/org/apache/servicemix/jbi/jmx/ConnectorServerFactoryBean.java
URL: http://svn.apache.org/viewvc/servicemix/smx3/trunk/core/servicemix-core/src/main/java/org/apache/servicemix/jbi/jmx/ConnectorServerFactoryBean.java?rev=880669&r1=880668&r2=880669&view=diff
==============================================================================
--- servicemix/smx3/trunk/core/servicemix-core/src/main/java/org/apache/servicemix/jbi/jmx/ConnectorServerFactoryBean.java (original)
+++ servicemix/smx3/trunk/core/servicemix-core/src/main/java/org/apache/servicemix/jbi/jmx/ConnectorServerFactoryBean.java Mon Nov 16 08:37:37 2009
@@ -16,10 +16,17 @@
  */
 package org.apache.servicemix.jbi.jmx;
 
+import java.lang.reflect.InvocationHandler;
+import java.lang.reflect.Method;
+import java.lang.reflect.Proxy;
+import java.security.AccessController;
 import java.util.Map;
 
 import javax.management.MBeanServer;
 import javax.management.MalformedObjectNameException;
+import javax.management.remote.JMXConnectorServer;
+import javax.management.remote.MBeanServerForwarder;
+import javax.security.auth.Subject;
 
 import org.apache.commons.logging.Log;
 import org.apache.commons.logging.LogFactory;
@@ -75,7 +82,8 @@
     private Object objectName;
     private int registrationBehavior = REGISTRATION_FAIL_ON_EXISTING;
     private MBeanServer server;
-    
+    private Policy policy;
+
 
     /**
      * Set whether any threads started for the <code>JMXConnectorServer</code> should be
@@ -178,6 +186,14 @@
         return csfb.isSingleton();
     }
 
+    /**
+     * 
+     * @param policy
+     */
+    public void setPolicy(Policy policy) {
+        this.policy = policy;
+    }
+
     public void afterPropertiesSet() throws Exception {
         csfb = new org.springframework.jmx.support.ConnectorServerFactoryBean();
         csfb.setDaemon(daemon);
@@ -189,9 +205,25 @@
         csfb.setServiceUrl(serviceUrl);
         csfb.setServer(server);
         csfb.afterPropertiesSet();
+        if (policy != null) {
+            log.info("Configuring JMX authorization policy: " + policy);
+            JMXConnectorServer jcs = (JMXConnectorServer) csfb.getObject();
+            jcs.setMBeanServerForwarder(createForwarder());
+        }
         log.info("JMX connector available at: " + serviceUrl);
     }
 
+    private MBeanServerForwarder createForwarder() {
+        final InvocationHandler handler = new MBSFInvocationHandler(policy);
+
+        Object proxy = Proxy.newProxyInstance(
+                MBeanServerForwarder.class.getClassLoader(),
+                new Class[] {MBeanServerForwarder.class},
+                handler);
+
+        return MBeanServerForwarder.class.cast(proxy);
+    }
+
     public void destroy() throws Exception {
         if (csfb != null) {
             try {
@@ -202,4 +234,48 @@
         }
     }
 
+    public static class MBSFInvocationHandler implements InvocationHandler {
+
+        private static final String GET_MBEAN_SERVER = "getMBeanServer";
+        private static final String SET_MBEAN_SERVER = "setMBeanServer";
+
+        private Policy policy;
+        private MBeanServer mbs;
+
+        public MBSFInvocationHandler(Policy policy) {
+            this.policy = policy;
+        }
+
+        public Object invoke(Object proxy, Method method, Object[] args)
+            throws Throwable {
+
+            final String methodName = method.getName();
+
+            if (GET_MBEAN_SERVER.equals(methodName)) {
+                return mbs;
+            }
+
+            if (SET_MBEAN_SERVER.equals(methodName)) {
+                if (args[0] == null) {
+                    throw new IllegalArgumentException("Null MBeanServer");
+                }
+                if (mbs != null) {
+                    throw new IllegalArgumentException("MBeanServer object already initialized");
+                }
+                mbs = (MBeanServer) args[0];
+                return null;
+            }
+
+            // Retrieve Subject from current AccessControlContext
+            Subject subject = Subject.getSubject(AccessController.getContext());
+
+            if (subject == null)  {
+                // running operation locally
+                return method.invoke(mbs, args);
+            } else {
+                policy.checkAuthorization(subject, mbs, method, args);
+                return method.invoke(mbs, args);
+            }
+        }
+    }
 }

Added: servicemix/smx3/trunk/core/servicemix-core/src/main/java/org/apache/servicemix/jbi/jmx/Policy.java
URL: http://svn.apache.org/viewvc/servicemix/smx3/trunk/core/servicemix-core/src/main/java/org/apache/servicemix/jbi/jmx/Policy.java?rev=880669&view=auto
==============================================================================
--- servicemix/smx3/trunk/core/servicemix-core/src/main/java/org/apache/servicemix/jbi/jmx/Policy.java (added)
+++ servicemix/smx3/trunk/core/servicemix-core/src/main/java/org/apache/servicemix/jbi/jmx/Policy.java Mon Nov 16 08:37:37 2009
@@ -0,0 +1,66 @@
+/*
+ * 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.servicemix.jbi.jmx;
+
+import java.lang.reflect.Method;
+import javax.security.auth.Subject;
+
+/**
+ * Remote JMX access policy.
+ *
+ * When configured on a {@link org.apache.servicemix.jbi.jmx.ConnectorServerFactoryBean}, all calls to the
+ * MBean server will first be authorized by calling the
+ * {@link #checkAuthorization(javax.security.auth.Subject, Object, java.lang.reflect.Method, Object[])} method
+ */
+public abstract class Policy {
+
+    /**
+     * Check if the subject is authorized to call the method.
+     *
+     * @param subject the subject that want to invoke the method
+     * @param target the object on which the method is to be invoked
+     * @param method the method that is to be invoked
+     * @param args the method parameters
+     *
+     * @throws SecurityException when the subject is not authorized to call this method
+     */
+    public abstract void checkAuthorization(Subject subject, Object target, Method method, Object[] args) 
+        throws SecurityException;
+
+    /**
+     * Does the method represent a read-only operation?
+     *
+     * @param method the method
+     * @return
+     */
+    protected boolean isReadOnly(Method method) {
+        return isReadOnly(method.getName());
+    }
+
+    /**
+     * Does the method name represent a read-only operation?
+     *
+     * @param method the method name
+     * @return
+     */
+    protected boolean isReadOnly(String method) {
+        return method.startsWith("get")
+               || method.startsWith("is")
+               || method.startsWith("query")
+               || method.startsWith("hashCode");
+    }
+}

Added: servicemix/smx3/trunk/core/servicemix-core/src/test/java/org/apache/servicemix/jbi/jmx/AdminReadWritePolicyTest.java
URL: http://svn.apache.org/viewvc/servicemix/smx3/trunk/core/servicemix-core/src/test/java/org/apache/servicemix/jbi/jmx/AdminReadWritePolicyTest.java?rev=880669&view=auto
==============================================================================
--- servicemix/smx3/trunk/core/servicemix-core/src/test/java/org/apache/servicemix/jbi/jmx/AdminReadWritePolicyTest.java (added)
+++ servicemix/smx3/trunk/core/servicemix-core/src/test/java/org/apache/servicemix/jbi/jmx/AdminReadWritePolicyTest.java Mon Nov 16 08:37:37 2009
@@ -0,0 +1,75 @@
+/*
+ * 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.servicemix.jbi.jmx;
+
+import java.lang.reflect.Method;
+import javax.management.MBeanServer;
+import javax.management.MalformedObjectNameException;
+import javax.management.ObjectName;
+import javax.management.QueryExp;
+import javax.security.auth.Subject;
+
+import junit.framework.TestCase;
+import org.apache.servicemix.jbi.security.GroupPrincipal;
+
+/**
+ * Test cases for {@link org.apache.servicemix.jbi.jmx.AdminReadWritePolicy}
+ */
+public class AdminReadWritePolicyTest extends TestCase {
+
+    private AdminReadWritePolicy policy = new AdminReadWritePolicy();
+
+    public void testCheckAuthorization() throws NoSuchMethodException, MalformedObjectNameException {
+        Subject user = new Subject();
+        Subject admin = new Subject();
+        admin.getPrincipals().add(new GroupPrincipal("admin"));
+
+        Method hashCode = Object.class.getMethod("hashCode");
+        Method isRegistered = MBeanServer.class.getMethod("isRegistered", ObjectName.class);
+        Method getAttribute = MBeanServer.class.getMethod("getAttribute", ObjectName.class, String.class);
+        Method queryNames = MBeanServer.class.getMethod("queryNames", ObjectName.class, QueryExp.class);
+        Method invoke =
+            MBeanServer.class.getMethod("invoke", ObjectName.class, String.class, Object[].class, String[].class);
+
+        ObjectName threading = ObjectName.getInstance("java.lang:type=Threading");
+
+        // admin user is allowed to use all methods
+        policy.checkAuthorization(admin, null, hashCode, null);
+        policy.checkAuthorization(admin, null, isRegistered, null);
+        policy.checkAuthorization(admin, null, getAttribute, null);
+        policy.checkAuthorization(admin, null, queryNames, null);
+        policy.checkAuthorization(admin, null, invoke, null);
+        policy.checkAuthorization(admin, null, invoke, new Object[] {threading, "getThreadInfo"});
+
+        // users are allowed to use these four methods...
+        policy.checkAuthorization(user, null, hashCode, null);
+        policy.checkAuthorization(user, null, isRegistered, null);
+        policy.checkAuthorization(user, null, getAttribute, null);
+        policy.checkAuthorization(user, null, queryNames, null);
+        policy.checkAuthorization(user, null, invoke, new Object[] {threading, "getThreadInfo"});
+
+        // ...but aren't allow to invoke any operations remotely
+        try {
+            policy.checkAuthorization(user, null, invoke, null);
+            fail("Policy check should have thrown a SecurityException");
+        } catch (SecurityException e) {
+            // this is OK
+        }
+
+    }
+
+}

Propchange: servicemix/smx3/trunk/samples/bridge-camel/
------------------------------------------------------------------------------
--- svn:ignore (original)
+++ svn:ignore Mon Nov 16 08:37:37 2009
@@ -1 +1,2 @@
 target
+*.i??

Propchange: servicemix/smx3/trunk/samples/bridge-camel/bridge-camel-sa/
------------------------------------------------------------------------------
--- svn:ignore (original)
+++ svn:ignore Mon Nov 16 08:37:37 2009
@@ -2,3 +2,4 @@
 .classpath
 .settings
 .project
+*.i??

Propchange: servicemix/smx3/trunk/samples/bridge-camel/bridge-camel-su/
------------------------------------------------------------------------------
--- svn:ignore (original)
+++ svn:ignore Mon Nov 16 08:37:37 2009
@@ -2,3 +2,4 @@
 .classpath
 .settings
 .project
+*.i??

Propchange: servicemix/smx3/trunk/samples/bridge/bridge-sa-itest/
------------------------------------------------------------------------------
--- svn:ignore (original)
+++ svn:ignore Mon Nov 16 08:37:37 2009
@@ -4,3 +4,4 @@
 .project
 .settings
 target
+*.i??

Propchange: servicemix/smx3/trunk/samples/camel/
------------------------------------------------------------------------------
--- svn:ignore (original)
+++ svn:ignore Mon Nov 16 08:37:37 2009
@@ -1 +1,2 @@
 target
+*.i??

Propchange: servicemix/smx3/trunk/samples/camel/camel-sa/
------------------------------------------------------------------------------
--- svn:ignore (original)
+++ svn:ignore Mon Nov 16 08:37:37 2009
@@ -2,3 +2,4 @@
 .project
 .settings
 target
+*.i??

Propchange: servicemix/smx3/trunk/samples/camel/camel-sa-itest/
------------------------------------------------------------------------------
--- svn:ignore (original)
+++ svn:ignore Mon Nov 16 08:37:37 2009
@@ -3,3 +3,4 @@
 .project
 .settings
 target
+*.i??

Propchange: servicemix/smx3/trunk/samples/camel/camel-simple-su/
------------------------------------------------------------------------------
--- svn:ignore (original)
+++ svn:ignore Mon Nov 16 08:37:37 2009
@@ -2,3 +2,4 @@
 .project
 .settings
 target
+*.i??

Propchange: servicemix/smx3/trunk/samples/cxf-wsdl-first/
------------------------------------------------------------------------------
--- svn:ignore (original)
+++ svn:ignore Mon Nov 16 08:37:37 2009
@@ -1 +1,2 @@
 target
+*.i??

Propchange: servicemix/smx3/trunk/samples/cxf-wsdl-first/wsdl-first-cxf-sa/
------------------------------------------------------------------------------
--- svn:ignore (original)
+++ svn:ignore Mon Nov 16 08:37:37 2009
@@ -2,3 +2,4 @@
 .project
 .settings
 target
+*.i??

Propchange: servicemix/smx3/trunk/samples/cxf-wsdl-first/wsdl-first-cxfbc-su/
------------------------------------------------------------------------------
--- svn:ignore (original)
+++ svn:ignore Mon Nov 16 08:37:37 2009
@@ -2,3 +2,4 @@
 .project
 .settings
 target
+*.i??

Propchange: servicemix/smx3/trunk/samples/cxf-wsdl-first/wsdl-first-cxfse-su/
------------------------------------------------------------------------------
--- svn:ignore (original)
+++ svn:ignore Mon Nov 16 08:37:37 2009
@@ -2,3 +2,4 @@
 .project
 .settings
 target
+*.i??