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??