You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@karaf.apache.org by jb...@apache.org on 2013/10/22 05:13:23 UTC

svn commit: r1534467 [1/5] - in /karaf/trunk: ./ assemblies/features/framework/src/main/feature/ assemblies/features/framework/src/main/resources/resources/etc/ itests/src/test/java/org/apache/karaf/itests/ jaas/command/src/main/java/org/apache/karaf/j...

Author: jbonofre
Date: Tue Oct 22 03:13:20 2013
New Revision: 1534467

URL: http://svn.apache.org/r1534467
Log:
[KARAF-2455] Add role-based security for OSGi services
[KARAF-2442] Add role-based security for shell/console commands

Added:
    karaf/trunk/assemblies/features/framework/src/main/resources/resources/etc/org.apache.karaf.command.acl.config.cfg
    karaf/trunk/service/guard/
    karaf/trunk/service/guard/NOTICE
    karaf/trunk/service/guard/pom.xml
      - copied, changed from r1533110, karaf/trunk/management/server/pom.xml
    karaf/trunk/service/guard/src/
    karaf/trunk/service/guard/src/main/
    karaf/trunk/service/guard/src/main/java/
    karaf/trunk/service/guard/src/main/java/org/
    karaf/trunk/service/guard/src/main/java/org/apache/
    karaf/trunk/service/guard/src/main/java/org/apache/karaf/
    karaf/trunk/service/guard/src/main/java/org/apache/karaf/service/
    karaf/trunk/service/guard/src/main/java/org/apache/karaf/service/guard/
    karaf/trunk/service/guard/src/main/java/org/apache/karaf/service/guard/impl/
    karaf/trunk/service/guard/src/main/java/org/apache/karaf/service/guard/impl/Activator.java
    karaf/trunk/service/guard/src/main/java/org/apache/karaf/service/guard/impl/GuardProxyCatalog.java
    karaf/trunk/service/guard/src/main/java/org/apache/karaf/service/guard/impl/GuardingEventHook.java
    karaf/trunk/service/guard/src/main/java/org/apache/karaf/service/guard/impl/GuardingFindHook.java
    karaf/trunk/service/guard/src/main/java/org/apache/karaf/service/guard/tools/
    karaf/trunk/service/guard/src/main/java/org/apache/karaf/service/guard/tools/ACLConfigurationParser.java
    karaf/trunk/service/guard/src/main/resources/
    karaf/trunk/service/guard/src/main/resources/OSGI-INF/
    karaf/trunk/service/guard/src/main/resources/OSGI-INF/bundle.info
    karaf/trunk/service/guard/src/test/
    karaf/trunk/service/guard/src/test/java/
    karaf/trunk/service/guard/src/test/java/org/
    karaf/trunk/service/guard/src/test/java/org/apache/
    karaf/trunk/service/guard/src/test/java/org/apache/karaf/
    karaf/trunk/service/guard/src/test/java/org/apache/karaf/service/
    karaf/trunk/service/guard/src/test/java/org/apache/karaf/service/guard/
    karaf/trunk/service/guard/src/test/java/org/apache/karaf/service/guard/impl/
    karaf/trunk/service/guard/src/test/java/org/apache/karaf/service/guard/impl/ACLConfigurationParserTest.java
    karaf/trunk/service/guard/src/test/java/org/apache/karaf/service/guard/impl/ActivatorTest.java
    karaf/trunk/service/guard/src/test/java/org/apache/karaf/service/guard/impl/GuardProxyCatalogTest.java
    karaf/trunk/service/guard/src/test/java/org/apache/karaf/service/guard/impl/GuardingEventHookTest.java
    karaf/trunk/service/guard/src/test/java/org/apache/karaf/service/guard/impl/GuardingFindHookTest.java
    karaf/trunk/shell/console/src/main/java/org/apache/karaf/shell/security/
    karaf/trunk/shell/console/src/main/java/org/apache/karaf/shell/security/impl/
    karaf/trunk/shell/console/src/main/java/org/apache/karaf/shell/security/impl/SecuredCommandConfigTransformer.java
    karaf/trunk/shell/console/src/main/java/org/apache/karaf/shell/security/impl/SecuredCommandProcessorImpl.java
    karaf/trunk/shell/console/src/test/java/org/apache/karaf/shell/console/impl/
    karaf/trunk/shell/console/src/test/java/org/apache/karaf/shell/console/impl/jline/
    karaf/trunk/shell/console/src/test/java/org/apache/karaf/shell/console/impl/jline/ConsoleImplTest.java
    karaf/trunk/shell/console/src/test/java/org/apache/karaf/shell/security/
    karaf/trunk/shell/console/src/test/java/org/apache/karaf/shell/security/impl/
    karaf/trunk/shell/console/src/test/java/org/apache/karaf/shell/security/impl/SecuredCommandConfigTransformerTest.java
    karaf/trunk/shell/console/src/test/java/org/apache/karaf/shell/security/impl/SecuredCommandProcessorImplTest.java
Modified:
    karaf/trunk/assemblies/features/framework/src/main/feature/feature.xml
    karaf/trunk/assemblies/features/framework/src/main/resources/resources/etc/jmx.acl.org.apache.karaf.config.cfg
    karaf/trunk/assemblies/features/framework/src/main/resources/resources/etc/jmx.acl.osgi.compendium.cm.cfg
    karaf/trunk/assemblies/features/framework/src/main/resources/resources/etc/system.properties
    karaf/trunk/itests/src/test/java/org/apache/karaf/itests/KarafTestSupport.java
    karaf/trunk/jaas/command/src/main/java/org/apache/karaf/jaas/command/GroupAddCommand.java
    karaf/trunk/jaas/command/src/main/java/org/apache/karaf/jaas/command/GroupDeleteCommand.java
    karaf/trunk/jaas/modules/src/main/java/org/apache/karaf/jaas/modules/BackingEngine.java
    karaf/trunk/management/boot/src/test/java/org/apache/karaf/management/boot/KarafMBeanServerBuilderTest.java
    karaf/trunk/management/server/pom.xml
    karaf/trunk/management/server/src/main/java/org/apache/karaf/management/KarafMBeanServerGuard.java
    karaf/trunk/management/server/src/main/resources/OSGI-INF/blueprint/karaf-management.xml
    karaf/trunk/pom.xml
    karaf/trunk/service/pom.xml
    karaf/trunk/shell/console/pom.xml
    karaf/trunk/shell/console/src/main/java/org/apache/karaf/shell/console/impl/Main.java
    karaf/trunk/shell/console/src/main/java/org/apache/karaf/shell/console/impl/jline/ConsoleFactoryService.java
    karaf/trunk/shell/console/src/main/java/org/apache/karaf/shell/console/impl/jline/ConsoleImpl.java
    karaf/trunk/shell/console/src/main/java/org/apache/karaf/shell/console/impl/jline/LocalConsoleManager.java
    karaf/trunk/shell/console/src/main/resources/OSGI-INF/blueprint/karaf-console.xml
    karaf/trunk/shell/console/src/test/java/org/apache/karaf/shell/console/ExampleSubclassMain.java
    karaf/trunk/shell/ssh/src/main/java/org/apache/karaf/shell/ssh/KarafJaasAuthenticator.java
    karaf/trunk/shell/ssh/src/main/resources/OSGI-INF/blueprint/shell-ssh.xml

Modified: karaf/trunk/assemblies/features/framework/src/main/feature/feature.xml
URL: http://svn.apache.org/viewvc/karaf/trunk/assemblies/features/framework/src/main/feature/feature.xml?rev=1534467&r1=1534466&r2=1534467&view=diff
==============================================================================
--- karaf/trunk/assemblies/features/framework/src/main/feature/feature.xml (original)
+++ karaf/trunk/assemblies/features/framework/src/main/feature/feature.xml Tue Oct 22 03:13:20 2013
@@ -31,6 +31,7 @@
         <bundle start="true" start-level="5">mvn:org.ops4j.pax.url/pax-url-wrap/${pax.url.version}</bundle>
         <bundle start="true" start-level="8">mvn:org.ops4j.pax.logging/pax-logging-api/${pax.logging.version}</bundle>
         <bundle start="true" start-level="8">mvn:org.ops4j.pax.logging/pax-logging-service/${pax.logging.version}</bundle>
+        <bundle start="true" start-level="10">mvn:org.apache.karaf.service/org.apache.karaf.service.guard/${project.version}</bundle>
         <bundle start="true" start-level="10">mvn:org.apache.felix/org.apache.felix.configadmin/${felix.configadmin.version}</bundle>
         <bundle start="true" start-level="11">mvn:org.apache.felix/org.apache.felix.fileinstall/${felix.fileinstall.version}</bundle>
         <bundle start="true" start-level="12">mvn:org.ow2.asm/asm-all/${asm.version}</bundle>

Modified: karaf/trunk/assemblies/features/framework/src/main/resources/resources/etc/jmx.acl.org.apache.karaf.config.cfg
URL: http://svn.apache.org/viewvc/karaf/trunk/assemblies/features/framework/src/main/resources/resources/etc/jmx.acl.org.apache.karaf.config.cfg?rev=1534467&r1=1534466&r2=1534467&view=diff
==============================================================================
--- karaf/trunk/assemblies/features/framework/src/main/resources/resources/etc/jmx.acl.org.apache.karaf.config.cfg (original)
+++ karaf/trunk/assemblies/features/framework/src/main/resources/resources/etc/jmx.acl.org.apache.karaf.config.cfg Tue Oct 22 03:13:20 2013
@@ -4,15 +4,27 @@
 # By default, only an admin can make changes to the JMX ACL rules, but managers can make
 # changes to other PIDs.
 #
-appendProperty(java.lang.String,java.lang.String,java.lang.String)[/^jmx[.]acl.*/,/.*/,/.*/] = admin
+appendProperty(java.lang.String,java.lang.String,java.lang.String)[/jmx[.]acl.*/,/.*/,/.*/] = admin
+appendProperty(java.lang.String,java.lang.String,java.lang.String)[/org[.]apache[.]karaf[.]command[.]acl[.].+/,/.*/,/.*/] = admin
+appendProperty(java.lang.String,java.lang.String,java.lang.String)[/org[.]apache[.]karaf[.]service[.]acl[.].+/,/.*/,/.*/] = admin
 appendProperty(java.lang.String,java.lang.String,java.lang.String) = manager
-create(java.lang.String)[/^jmx[.]acl.*/] = admin
+create(java.lang.String)[/jmx[.]acl.*/] = admin
+create(java.lang.String)[/org[.]apache[.]karaf[.]command[.]acl[.].+/ = admin
+create(java.lang.String)[/org[.]apache[.]karaf[.]service[.]acl[.].+/ = admin
 create(java.lang.String) = manager
-delete(java.lang.String)[/^jmx[.]acl.*/] = admin
+delete(java.lang.String)[/jmx[.]acl.*/] = admin
+delete(java.lang.String)[/org[.]apache[.]karaf[.]command[.]acl[.].+/ = admin
+delete(java.lang.String)[/org[.]apache[.]karaf[.]service[.]acl[.].+/ = admin
 delete(java.lang.String) = manager
-deleteProperty(java.lang.String,java.lang.String)[/^jmx[.]acl.*/,/.*/] = admin
+deleteProperty(java.lang.String,java.lang.String)[/jmx[.]acl.*/,/.*/] = admin
+deleteProperty(java.lang.String,java.lang.String)[/org[.]apache[.]karaf[.]command[.]acl[.].+/,/.*/] = admin
+deleteProperty(java.lang.String,java.lang.String)[/org[.]apache[.]karaf[.]service[.]acl[.].+/,/.*/] = admin
 deleteProperty(java.lang.String,java.lang.String) = manager
-setProperty(java.lang.String,java.lang.String,java.lang.String)[/^jmx[.]acl.*/,/.*/,/.*/] = admin
+setProperty(java.lang.String,java.lang.String,java.lang.String)[/jmx[.]acl.*/,/.*/,/.*/] = admin
+setProperty(java.lang.String,java.lang.String,java.lang.String)[/org[.]apache[.]karaf[.]command[.]acl[.].+/,/.*/,/.*/] = admin
+setProperty(java.lang.String,java.lang.String,java.lang.String)[/org[.]apache[.]karaf[.]service[.]acl[.].+/,/.*/,/.*/] = admin
 setProperty(java.lang.String,java.lang.String,java.lang.String) = manager
-update(java.lang.String,java.util.Map)[/^jmx[.]acl.*/,/.*/] = admin
+update(java.lang.String,java.util.Map)[/jmx[.]acl.*/,/.*/] = admin
+update(java.lang.String,java.util.Map)[/org[.]apache[.]karaf[.]command[.]acl[.].+/,/.*/] = admin
+update(java.lang.String,java.util.Map)[/org[.]apache[.]karaf[.]service[.]acl[.].+/,/.*/] = admin
 update(java.lang.String,java.util.Map) = manager
\ No newline at end of file

Modified: karaf/trunk/assemblies/features/framework/src/main/resources/resources/etc/jmx.acl.osgi.compendium.cm.cfg
URL: http://svn.apache.org/viewvc/karaf/trunk/assemblies/features/framework/src/main/resources/resources/etc/jmx.acl.osgi.compendium.cm.cfg?rev=1534467&r1=1534466&r2=1534467&view=diff
==============================================================================
--- karaf/trunk/assemblies/features/framework/src/main/resources/resources/etc/jmx.acl.osgi.compendium.cm.cfg (original)
+++ karaf/trunk/assemblies/features/framework/src/main/resources/resources/etc/jmx.acl.osgi.compendium.cm.cfg Tue Oct 22 03:13:20 2013
@@ -5,16 +5,28 @@
 # Such that only an admin can make changes to the JMX ACL rules, but managers can make
 # changes to other PIDs.
 #
-createFactoryConfiguration(java.lang.String)[/^jmx[.]acl.*/] = admin
+createFactoryConfiguration(java.lang.String)[/jmx[.]acl.*/] = admin
+createFactoryConfiguration(java.lang.String)[/org[.]apache[.]karaf[.]command[.]acl[.].+/] = admin
+createFactoryConfiguration(java.lang.String)[/org[.]apache[.]karaf[.]service[.]acl[.].+/] = admin
 createFactoryConfiguration(java.lang.String) = manager
-createFactoryConfigurationForLocation(java.lang.String,java.lang.String)[/^jmx[.]acl.*/,/.*/] = admin
+createFactoryConfigurationForLocation(java.lang.String,java.lang.String)[/jmx[.]acl.*/,/.*/] = admin
+createFactoryConfigurationForLocation(java.lang.String,java.lang.String)[/org[.]apache[.]karaf[.]command[.]acl[.].+/,/.*/] = admin
+createFactoryConfigurationForLocation(java.lang.String,java.lang.String)[/org[.]apache[.]karaf[.]service[.]acl[.].+/,/.*/] = admin
 createFactoryConfigurationForLocation(java.lang.String,java.lang.String) = manager
-delete(java.lang.String)[/^jmx[.]acl.*/] = admin
+delete(java.lang.String)[/jmx[.]acl.*/] = admin
+delete(java.lang.String)[/org[.]apache[.]karaf[.]command[.]acl[.].+/] = admin
+delete(java.lang.String)[/org[.]apache[.]karaf[.]service[.]acl[.].+/] = admin
 delete(java.lang.String) = manager
 deleteConfigurations = admin
-deleteForLocation(java.lang.String,java.lang.String)[/^jmx[.]acl.*/,/.*/] = admin
+deleteForLocation(java.lang.String,java.lang.String)[/jmx[.]acl.*/,/.*/] = admin
+deleteForLocation(java.lang.String,java.lang.String)[/org[.]apache[.]karaf[.]command[.]acl[.].+/,/.*/] = admin
+deleteForLocation(java.lang.String,java.lang.String)[/org[.]apache[.]karaf[.]service[.]acl[.].+/,/.*/] = admin
 deleteForLocation(java.lang.String,java.lang.String) = manager
-update(java.lang.String,javax.management.openmbean.TabularData)[/^jmx[.]acl.*/,/.*/] = admin
+update(java.lang.String,javax.management.openmbean.TabularData)[/jmx[.]acl.*/,/.*/] = admin
+update(java.lang.String,javax.management.openmbean.TabularData)[/org[.]apache[.]karaf[.]command[.]acl[.].+/,/.*/] = admin
+update(java.lang.String,javax.management.openmbean.TabularData)[/org[.]apache[.]karaf[.]service[.]acl[.].+/,/.*/] = admin
 update(java.lang.String,javax.management.openmbean.TabularData) = manager
-updateForLocation(java.lang.String,java.lang.String,javax.management.openmbean.TabularData)[/^jmx[.]acl.*/,/.*/,/.*/] = admin
+updateForLocation(java.lang.String,java.lang.String,javax.management.openmbean.TabularData)[/jmx[.]acl.*/,/.*/,/.*/] = admin
+updateForLocation(java.lang.String,java.lang.String,javax.management.openmbean.TabularData)[/org[.]apache[.]karaf[.]command[.]acl[.].+/,/.*/,/.*/] = admin
+updateForLocation(java.lang.String,java.lang.String,javax.management.openmbean.TabularData)[/org[.]apache[.]karaf[.]service[.]acl[.].+/,/.*/,/.*/] = admin
 updateForLocation(java.lang.String,java.lang.String,javax.management.openmbean.TabularData) = manager
\ No newline at end of file

Added: karaf/trunk/assemblies/features/framework/src/main/resources/resources/etc/org.apache.karaf.command.acl.config.cfg
URL: http://svn.apache.org/viewvc/karaf/trunk/assemblies/features/framework/src/main/resources/resources/etc/org.apache.karaf.command.acl.config.cfg?rev=1534467&view=auto
==============================================================================
--- karaf/trunk/assemblies/features/framework/src/main/resources/resources/etc/org.apache.karaf.command.acl.config.cfg (added)
+++ karaf/trunk/assemblies/features/framework/src/main/resources/resources/etc/org.apache.karaf.command.acl.config.cfg Tue Oct 22 03:13:20 2013
@@ -0,0 +1,19 @@
+cancel = manager
+delete = admin
+edit = manager
+edit[/.*jmx[.]acl.*/] = admin
+edit[/.*org[.]apache[.]karaf[.]command[.]acl[.].+/] = admin
+edit[/.*org[.]apache[.]karaf[.]service[.]acl[.].+/] = admin
+property-append = manager
+property-append[/.*jmx[.]acl.*/] = admin
+property-append[/.*org[.]apache[.]karaf[.]command[.]acl|.].+/] = admin
+property-append[/.*org[.]apache[.]karaf[.]service[.]acl[.].+/] = admin
+property-delete = manager
+property-delete[/.*jmx[.]acl.*/] = admin
+property-delete[/.*org[.]apache[.]karaf[.]command[.]acl[.].+/] = admin
+property-delete[/.*org[.]apache[.]karaf[.]service[.]acl[.].+/] = admin
+property-set = manager
+property-set[/.*jmx[.]acl.*/] = admin
+property-set[/.*org[.]apache[.]karaf[.]command[.]acl[.].+/] = admin
+property-set[/.*org[.]apache[.]karaf[.]service[.]acl[.].+/] = admin
+update = manager
\ No newline at end of file

Modified: karaf/trunk/assemblies/features/framework/src/main/resources/resources/etc/system.properties
URL: http://svn.apache.org/viewvc/karaf/trunk/assemblies/features/framework/src/main/resources/resources/etc/system.properties?rev=1534467&r1=1534466&r2=1534467&view=diff
==============================================================================
--- karaf/trunk/assemblies/features/framework/src/main/resources/resources/etc/system.properties (original)
+++ karaf/trunk/assemblies/features/framework/src/main/resources/resources/etc/system.properties Tue Oct 22 03:13:20 2013
@@ -60,7 +60,8 @@ karaf.clean.all=false
 karaf.clean.cache=false
 
 #
-# Default role name used for console authorization (JMX, SSH and WEB)
+# Roles to use when logging into a local Karaf console.
+#
 # The syntax is the following:
 #   [classname:]principal
 # where classname is the class name of the principal object
@@ -68,10 +69,7 @@ karaf.clean.cache=false
 # and principal is the name of the principal of that class
 # (defaults to instance).
 #
-# Note that this value can be overriden using the various ConfigAdmin
-# configurations for JMX, SSH or the WebConsole.
-#
-karaf.admin.role=admin
+karaf.local.roles=admin,manager,viewer
 
 #
 # Set this empty property to avoid errors when validating xml documents.
@@ -98,3 +96,9 @@ org.apache.servicemix.specs.timeout=100
 org.apache.aries.proxy.weaving.enabled=none
 # Classes not to weave - Aries default + Xerces which is known to have issues.
 org.apache.aries.proxy.weaving.disabled=org.objectweb.asm.*,org.slf4j.*,org.apache.log4j.*,javax.*,org.apache.xerces.*
+
+#
+# By default, only Karaf shell commands are secured, but additional services can be
+# secured by expanding this filter
+#
+karaf.secured.services=(&(osgi.command.scope=*)(osgi.command.function=*))

Modified: karaf/trunk/itests/src/test/java/org/apache/karaf/itests/KarafTestSupport.java
URL: http://svn.apache.org/viewvc/karaf/trunk/itests/src/test/java/org/apache/karaf/itests/KarafTestSupport.java?rev=1534467&r1=1534466&r2=1534467&view=diff
==============================================================================
--- karaf/trunk/itests/src/test/java/org/apache/karaf/itests/KarafTestSupport.java (original)
+++ karaf/trunk/itests/src/test/java/org/apache/karaf/itests/KarafTestSupport.java Tue Oct 22 03:13:20 2013
@@ -13,12 +13,12 @@
  */
 package org.apache.karaf.itests;
 
+import static org.junit.Assert.assertTrue;
+import static org.ops4j.pax.exam.CoreOptions.maven;
 import static org.ops4j.pax.exam.karaf.options.KarafDistributionOption.editConfigurationFilePut;
 import static org.ops4j.pax.exam.karaf.options.KarafDistributionOption.karafDistributionConfiguration;
 import static org.ops4j.pax.exam.karaf.options.KarafDistributionOption.keepRuntimeFolder;
 import static org.ops4j.pax.exam.karaf.options.KarafDistributionOption.logLevel;
-import static org.junit.Assert.assertTrue;
-import static org.ops4j.pax.exam.CoreOptions.maven;
 
 import java.io.ByteArrayOutputStream;
 import java.io.File;
@@ -96,6 +96,7 @@ public class KarafTestSupport {
     public Option[] config() {
         MavenArtifactUrlReference karafUrl = maven().groupId("org.apache.karaf").artifactId("apache-karaf").versionAsInProject().type("tar.gz");
         return new Option[]{
+            // KarafDistributionOption.debugConfiguration("8889", true),
             karafDistributionConfiguration().frameworkUrl(karafUrl).name("Apache Karaf").unpackDirectory(new File("target/exam")),
             keepRuntimeFolder(),
             logLevel(LogLevelOption.LogLevel.INFO),
@@ -127,6 +128,8 @@ public class KarafTestSupport {
      * @return
      */
     protected String executeCommand(final String command, final Long timeout, final Boolean silent) {
+        waitForCommandService(command);
+
         String response;
         final ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
         final PrintStream printStream = new PrintStream(byteArrayOutputStream);
@@ -210,6 +213,44 @@ public class KarafTestSupport {
         }
     }
 
+    private void waitForCommandService(String command) {
+        // the commands are represented by services. Due to the asynchronous nature of services they may not be
+        // immediately available. This code waits the services to be available, in their secured form. It
+        // means that the code waits for the command service to appear with the roles defined.
+
+        if (command == null || command.length() == 0) {
+            return;
+        }
+
+        int spaceIdx = command.indexOf(' ');
+        if (spaceIdx > 0) {
+            command = command.substring(0, spaceIdx);
+        }
+        int colonIndx = command.indexOf(':');
+
+        try {
+            if (colonIndx > 0) {
+                String scope = command.substring(0, colonIndx);
+                String function = command.substring(colonIndx + 1);
+                waitForService("(&(osgi.command.scope=" + scope + ")(osgi.command.function=" + function + ")(org.apache.karaf.service.guard.roles=*))", SERVICE_TIMEOUT);
+            } else {
+                waitForService("(&(osgi.command.function=" + command + ")(org.apache.karaf.service.guard.roles=*))", SERVICE_TIMEOUT);
+            }
+        } catch (Exception e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    private void waitForService(String filter, long timeout) throws InvalidSyntaxException, InterruptedException {
+        ServiceTracker<Object, Object> st = new ServiceTracker<Object, Object>(bundleContext, bundleContext.createFilter(filter), null);
+        try {
+            st.open();
+            st.waitForService(timeout);
+        } finally {
+            st.close();
+        }
+    }
+
     /*
     * Explode the dictionary into a ,-delimited list of key=value pairs
     */
@@ -289,4 +330,5 @@ public class KarafTestSupport {
 	    }
 	    return false;
 	}
+
 }

Modified: karaf/trunk/jaas/command/src/main/java/org/apache/karaf/jaas/command/GroupAddCommand.java
URL: http://svn.apache.org/viewvc/karaf/trunk/jaas/command/src/main/java/org/apache/karaf/jaas/command/GroupAddCommand.java?rev=1534467&r1=1534466&r2=1534467&view=diff
==============================================================================
--- karaf/trunk/jaas/command/src/main/java/org/apache/karaf/jaas/command/GroupAddCommand.java (original)
+++ karaf/trunk/jaas/command/src/main/java/org/apache/karaf/jaas/command/GroupAddCommand.java Tue Oct 22 03:13:20 2013
@@ -19,7 +19,7 @@ import org.apache.karaf.jaas.modules.Bac
 import org.apache.karaf.shell.commands.Argument;
 import org.apache.karaf.shell.commands.Command;
 
-@Command(scope = "jaas", name = "group-add", description = "Make an user part of a group")
+@Command(scope = "jaas", name = "group-add", description = "Make a user part of a group")
 public class GroupAddCommand extends JaasCommandSupport {
 
     @Argument(index = 0, name = "username", description = "Username", required = true, multiValued = false)

Modified: karaf/trunk/jaas/command/src/main/java/org/apache/karaf/jaas/command/GroupDeleteCommand.java
URL: http://svn.apache.org/viewvc/karaf/trunk/jaas/command/src/main/java/org/apache/karaf/jaas/command/GroupDeleteCommand.java?rev=1534467&r1=1534466&r2=1534467&view=diff
==============================================================================
--- karaf/trunk/jaas/command/src/main/java/org/apache/karaf/jaas/command/GroupDeleteCommand.java (original)
+++ karaf/trunk/jaas/command/src/main/java/org/apache/karaf/jaas/command/GroupDeleteCommand.java Tue Oct 22 03:13:20 2013
@@ -19,7 +19,7 @@ import org.apache.karaf.jaas.modules.Bac
 import org.apache.karaf.shell.commands.Argument;
 import org.apache.karaf.shell.commands.Command;
 
-@Command(scope = "jaas", name = "group-delete", description = "Remove an user from a group")
+@Command(scope = "jaas", name = "group-delete", description = "Remove a user from a group")
 public class GroupDeleteCommand extends JaasCommandSupport {
 
     @Argument(index = 0, name = "username", description = "Username", required = true, multiValued = false)

Modified: karaf/trunk/jaas/modules/src/main/java/org/apache/karaf/jaas/modules/BackingEngine.java
URL: http://svn.apache.org/viewvc/karaf/trunk/jaas/modules/src/main/java/org/apache/karaf/jaas/modules/BackingEngine.java?rev=1534467&r1=1534466&r2=1534467&view=diff
==============================================================================
--- karaf/trunk/jaas/modules/src/main/java/org/apache/karaf/jaas/modules/BackingEngine.java (original)
+++ karaf/trunk/jaas/modules/src/main/java/org/apache/karaf/jaas/modules/BackingEngine.java Tue Oct 22 03:13:20 2013
@@ -44,7 +44,7 @@ public interface BackingEngine {
     List<UserPrincipal> listUsers();
 
     /**
-     * List groups that an user is in.
+     * List groups that a user is in.
      *
      * @param user
      * @return
@@ -52,7 +52,7 @@ public interface BackingEngine {
     List<GroupPrincipal> listGroups(UserPrincipal user);
 
     /**
-     * Add an user to a group.
+     * Add a user to a group.
      *
      * @param username
      * @param group
@@ -60,7 +60,7 @@ public interface BackingEngine {
     void addGroup(String username, String group);
 
     /**
-     * Remove an user from a group.
+     * Remove a user from a group.
      *
      * @param username
      * @param group

Modified: karaf/trunk/management/boot/src/test/java/org/apache/karaf/management/boot/KarafMBeanServerBuilderTest.java
URL: http://svn.apache.org/viewvc/karaf/trunk/management/boot/src/test/java/org/apache/karaf/management/boot/KarafMBeanServerBuilderTest.java?rev=1534467&r1=1534466&r2=1534467&view=diff
==============================================================================
--- karaf/trunk/management/boot/src/test/java/org/apache/karaf/management/boot/KarafMBeanServerBuilderTest.java (original)
+++ karaf/trunk/management/boot/src/test/java/org/apache/karaf/management/boot/KarafMBeanServerBuilderTest.java Tue Oct 22 03:13:20 2013
@@ -56,6 +56,7 @@ public class KarafMBeanServerBuilderTest
         ObjectName on = ObjectName.getInstance("foo.bar:type=TestObject");
 
         try {
+            // obtain a JMX attribute
             kmbs.getAttribute(on, "myAttr");
             fail("Should have access denied");
         } catch (SecurityException se) {
@@ -70,6 +71,7 @@ public class KarafMBeanServerBuilderTest
         }
 
         try {
+            // obtain a number of JMX attributes
             kmbs.getAttributes(on, new String[]{"foo", "bar"});
             fail("Should have access denied");
         } catch (SecurityException se) {
@@ -77,6 +79,7 @@ public class KarafMBeanServerBuilderTest
         }
 
         try {
+            // set a JMX attribute
             kmbs.getAttributes(on, new String[]{ "goo", "far" });
             fail("Should have access denied");
         } catch (SecurityException se) {
@@ -84,6 +87,7 @@ public class KarafMBeanServerBuilderTest
         }
 
         try {
+            // set a number of JMX attributes
             kmbs.setAttributes(on, new AttributeList());
             fail("Should have access denied");
         } catch (SecurityException se) {
@@ -91,6 +95,7 @@ public class KarafMBeanServerBuilderTest
         }
 
         try {
+            // mimic a JMX method invocation
             kmbs.setAttributes(on, new AttributeList());
             fail("Should have access denied");
         } catch (SecurityException se) {

Modified: karaf/trunk/management/server/pom.xml
URL: http://svn.apache.org/viewvc/karaf/trunk/management/server/pom.xml?rev=1534467&r1=1534466&r2=1534467&view=diff
==============================================================================
--- karaf/trunk/management/server/pom.xml (original)
+++ karaf/trunk/management/server/pom.xml Tue Oct 22 03:13:20 2013
@@ -65,6 +65,11 @@
             <scope>provided</scope>
         </dependency>
         <dependency>
+            <groupId>org.apache.karaf.service</groupId>
+            <artifactId>org.apache.karaf.service.guard</artifactId>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
             <groupId>org.apache.servicemix.bundles</groupId>
             <artifactId>org.apache.servicemix.bundles.junit</artifactId>
             <scope>test</scope>
@@ -106,7 +111,6 @@
                     <instructions>
                         <Export-Package>org.apache.karaf.management;version=${project.version};-split-package:=merge-first</Export-Package>
                         <Private-Package>
-                            org.apache.karaf.management,
                             org.apache.karaf.management.internal
                         </Private-Package>
                     </instructions>

Modified: karaf/trunk/management/server/src/main/java/org/apache/karaf/management/KarafMBeanServerGuard.java
URL: http://svn.apache.org/viewvc/karaf/trunk/management/server/src/main/java/org/apache/karaf/management/KarafMBeanServerGuard.java?rev=1534467&r1=1534466&r2=1534467&view=diff
==============================================================================
--- karaf/trunk/management/server/src/main/java/org/apache/karaf/management/KarafMBeanServerGuard.java (original)
+++ karaf/trunk/management/server/src/main/java/org/apache/karaf/management/KarafMBeanServerGuard.java Tue Oct 22 03:13:20 2013
@@ -18,6 +18,7 @@ package org.apache.karaf.management;
 
 import org.apache.karaf.jaas.boot.principal.RolePrincipal;
 import org.apache.karaf.management.boot.KarafMBeanServerBuilder;
+import org.apache.karaf.service.guard.tools.ACLConfigurationParser;
 import org.osgi.framework.InvalidSyntaxException;
 import org.osgi.service.cm.Configuration;
 import org.osgi.service.cm.ConfigurationAdmin;
@@ -232,7 +233,6 @@ public class KarafMBeanServerGuard imple
     }
 
     List<String> getRequiredRoles(ObjectName objectName, String methodName, Object[] params, String[] signature) throws IOException {
-        List<String> roles = new ArrayList<String>();
 
         List<String> allPids = new ArrayList<String>();
         try {
@@ -246,255 +246,14 @@ public class KarafMBeanServerGuard imple
         for (String pid : iterateDownPids(getNameSegments(objectName))) {
             if (allPids.contains(pid)) {
                 Configuration config = configAdmin.getConfiguration(pid);
-                Dictionary<String, Object> properties = trimKeys(config.getProperties());
-
-                /*
-                1. get all direct string matches
-                2. get regex matches
-                3. get signature matches
-                4. without signature
-                5. method name wildcard
-
-                We return immediately when a definition is found, so if a specific definition is found, we do not
-                search for a more generic specification.
-                Regular expressions and exact matches are considered equally specific, so they are combined...
-                 */
-
-                boolean foundExactOrRegex = false;
-                if (params != null) {
-                    Object exactArgMatchRoles = properties.get(getExactArgSignature(methodName, signature, params));
-                    if (exactArgMatchRoles instanceof String) {
-                        roles.addAll(parseRoles((String) exactArgMatchRoles));
-                        foundExactOrRegex = true;
-                    }
-
-                    foundExactOrRegex |= getRegexRoles(properties, methodName, signature, params, roles);
-
-                    if (foundExactOrRegex) {
-                        // since we have the actual parameters we can match them and if they do, we won't look for any
-                        // more generic rules...
-                        return roles;
-                    }
-                } else {
-                    foundExactOrRegex = getExactArgOrRegexRoles(properties, methodName, signature, roles);
-                }
-
-                Object signatureRoles = properties.get(getSignature(methodName, signature));
-                if (signatureRoles instanceof String) {
-                    roles.addAll(parseRoles((String) signatureRoles));
-                    return roles;
-                }
-
-                if (foundExactOrRegex) {
-                    // we can get here if params == null and there were exact and/or regex rules but no signature rules
-                    return roles;
-                }
-
-                Object methodRoles = properties.get(methodName);
-                if (methodRoles instanceof String) {
-                    roles.addAll(parseRoles((String) methodRoles));
-                    return roles;
-                }
-
-                if (getMethodNameWildcardRoles(properties, methodName, roles))
+                List<String> roles = new ArrayList<String>();
+                ACLConfigurationParser.Specificity s = ACLConfigurationParser.getRolesForInvocation(methodName, params, signature, config.getProperties(), roles);
+                if (s != ACLConfigurationParser.Specificity.NO_MATCH) {
                     return roles;
-            }
-        }
-        return roles;
-    }
-
-    private Dictionary<String, Object> trimKeys(Dictionary<String, Object> properties) {
-        Dictionary<String, Object> d = new Hashtable<String, Object>();
-        for (Enumeration<String> e = properties.keys(); e.hasMoreElements(); ) {
-            String key = e.nextElement();
-            Object value = properties.get(key);
-
-            d.put(removeSpaces(key), value);
-        }
-        return d;
-    }
-
-    private String removeSpaces(String key) {
-        StringBuilder sb = new StringBuilder();
-        char quoteChar = 0;
-        for (int i = 0; i < key.length(); i++) {
-            char c = key.charAt(i);
-
-            if (quoteChar == 0 && c == ' ')
-                continue;
-
-            if (quoteChar == 0 && (c == '\"' || c == '/') && sb.length() > 0 &&
-                    (sb.charAt(sb.length() - 1) == '[' || sb.charAt(sb.length() - 1) == ',')) {
-                // we are in a quoted string
-                quoteChar = c;
-            } else if (quoteChar != 0 && c == quoteChar) {
-                // look ahead to see if the next non-space is the closing bracket or a comma, which ends the quoted string
-                for (int j = i + 1; j < key.length(); j++) {
-                    if (key.charAt(j) == ' ') {
-                        continue;
-                    }
-                    if (key.charAt(j) == ']' || key.charAt(j) == ',') {
-                        quoteChar = 0;
-                    }
-                    break;
-                }
-            }
-            sb.append(c);
-        }
-
-        return sb.toString();
-    }
-
-    private List<String> parseRoles(String roleString) {
-        int hashIndex = roleString.indexOf('#');
-        if (hashIndex >= 0) {
-            // you can put a comment at the end
-            roleString = roleString.substring(0, hashIndex);
-        }
-
-        List<String> roles = new ArrayList<String>();
-        for (String role : roleString.split("[,]")) {
-            String trimmed = role.trim();
-            if (trimmed.length() > 0)
-                roles.add(trimmed);
-        }
-
-        return roles;
-    }
-
-    private Object getExactArgSignature(String methodName, String[] signature, Object[] params) {
-        StringBuilder sb = new StringBuilder(getSignature(methodName, signature));
-        sb.append('[');
-        boolean first = true;
-        for (Object param : params) {
-            if (first)
-                first = false;
-            else
-                sb.append(',');
-
-            sb.append('"');
-            sb.append(param.toString().trim());
-            sb.append('"');
-        }
-        sb.append(']');
-        return sb.toString();
-    }
-
-    private String getSignature(String methodName, String[] signature) {
-        StringBuilder sb = new StringBuilder(methodName);
-        sb.append('(');
-        boolean first = true;
-        for (String s : signature) {
-            if (first)
-                first = false;
-            else
-                sb.append(',');
-
-            sb.append(s);
-        }
-        sb.append(')');
-        return sb.toString();
-    }
-
-    private boolean getRegexRoles(Dictionary<String, Object> properties, String methodName, String[] signature, Object[] params, List<String> roles) {
-        boolean matchFound = false;
-        String methodSig = getSignature(methodName, signature);
-        String prefix = methodSig + "[/";
-        for (Enumeration<String> e = properties.keys(); e.hasMoreElements(); ) {
-            String key = e.nextElement().trim();
-            if (key.startsWith(prefix) && key.endsWith("/]")) {
-                List<String> regexArgs = getRegexDecl(key.substring(methodName.length()));
-                if (allParamsMatch(regexArgs, params)) {
-                    matchFound = true;
-                    Object roleString = properties.get(key);
-                    if (roleString instanceof String)
-                        roles.addAll(parseRoles((String) roleString));
-                }
-            }
-        }
-        return matchFound;
-    }
-
-    private boolean getExactArgOrRegexRoles(Dictionary<String, Object> properties, String methodName, String[] signature, List<String> roles) {
-        boolean matchFound = false;
-        String methodSig = getSignature(methodName, signature);
-        String prefix = methodSig + "[";
-        for (Enumeration<String> e = properties.keys(); e.hasMoreElements(); ) {
-            String key = e.nextElement().trim();
-            if (key.startsWith(prefix) && key.endsWith("]")) {
-                matchFound = true;
-                Object roleString = properties.get(key);
-                if (roleString instanceof String)
-                    roles.addAll(parseRoles((String) roleString));
-            }
-        }
-        return matchFound;
-    }
-
-    private boolean getMethodNameWildcardRoles(Dictionary<String, Object> properties, String methodName, List<String> roles) {
-        SortedMap<String, String> wildcardRules = new TreeMap<String, String>(new Comparator<String>() {
-            public int compare(String s1, String s2) {
-                // returns longer entries before shorter ones...
-                return s2.length() - s1.length();
-            }
-        });
-        for (Enumeration<String> e = properties.keys(); e.hasMoreElements(); ) {
-            String key = e.nextElement();
-            if (key.endsWith("*")) {
-                String prefix = key.substring(0, key.length() - 1);
-                if (methodName.startsWith(prefix)) {
-                    wildcardRules.put(prefix, properties.get(key).toString());
-                }
-            }
-        }
-
-        if (wildcardRules.size() != 0) {
-            roles.addAll(parseRoles(wildcardRules.values().iterator().next()));
-            return true;
-        } else
-            return false;
-    }
-
-    private boolean allParamsMatch(List<String> regexArgs, Object[] params) {
-        if (regexArgs.size() != params.length)
-            return false;
-
-        for (int i = 0; i < regexArgs.size(); i++) {
-            if (!params[i].toString().trim().matches(regexArgs.get(i)))
-                return false;
-        }
-
-        return true;
-    }
-
-    private List<String> getRegexDecl(String key) {
-        List<String> l = new ArrayList<String>();
-
-        boolean inRegex = false;
-        StringBuilder currentRegex = new StringBuilder();
-        for (int i = 0; i < key.length(); i++) {
-            if (!inRegex) {
-                if (key.length() > i+1) {
-                    String s = key.substring(i, i+2);
-
-                    if ("[/".equals(s) || ",/".equals(s)) {
-                        inRegex = true;
-                        i++;
-                        continue;
-                    }
-                }
-            } else {
-                String s = key.substring(i, i+2);
-                if ("/]".equals(s) || "/,".equals(s)) {
-                    l.add(currentRegex.toString());
-                    currentRegex = new StringBuilder();
-                    inRegex = false;
-                    continue;
                 }
-                currentRegex.append(key.charAt(i));
             }
         }
-        return l;
+        return Collections.emptyList();
     }
 
     private List<String> getNameSegments(ObjectName objectName) {

Modified: karaf/trunk/management/server/src/main/resources/OSGI-INF/blueprint/karaf-management.xml
URL: http://svn.apache.org/viewvc/karaf/trunk/management/server/src/main/resources/OSGI-INF/blueprint/karaf-management.xml?rev=1534467&r1=1534466&r2=1534467&view=diff
==============================================================================
--- karaf/trunk/management/server/src/main/resources/OSGI-INF/blueprint/karaf-management.xml (original)
+++ karaf/trunk/management/server/src/main/resources/OSGI-INF/blueprint/karaf-management.xml Tue Oct 22 03:13:20 2013
@@ -23,11 +23,7 @@
            xmlns:jaas="http://karaf.apache.org/xmlns/jaas/v1.0.0">
 
     <!-- Allow the use of system properties -->
-    <ext:property-placeholder placeholder-prefix="$[" placeholder-suffix="]">
-        <ext:default-properties>
-            <ext:property name="karaf.admin.role" value="admin" />
-        </ext:default-properties>
-    </ext:property-placeholder>
+    <ext:property-placeholder placeholder-prefix="$[" placeholder-suffix="]" />
 
     <!-- Property place holder -->
     <cm:property-placeholder persistent-id="org.apache.karaf.management" update-strategy="reload">

Modified: karaf/trunk/pom.xml
URL: http://svn.apache.org/viewvc/karaf/trunk/pom.xml?rev=1534467&r1=1534466&r2=1534467&view=diff
==============================================================================
--- karaf/trunk/pom.xml (original)
+++ karaf/trunk/pom.xml Tue Oct 22 03:13:20 2013
@@ -397,6 +397,11 @@
                 <artifactId>org.apache.karaf.service.command</artifactId>
                 <version>${project.version}</version>
             </dependency>
+            <dependency>
+                <groupId>org.apache.karaf.service</groupId>
+                <artifactId>org.apache.karaf.service.guard</artifactId>
+                <version>${project.version}</version>
+            </dependency>
 
             <dependency>
                 <groupId>org.apache.karaf.log</groupId>

Added: karaf/trunk/service/guard/NOTICE
URL: http://svn.apache.org/viewvc/karaf/trunk/service/guard/NOTICE?rev=1534467&view=auto
==============================================================================
--- karaf/trunk/service/guard/NOTICE (added)
+++ karaf/trunk/service/guard/NOTICE Tue Oct 22 03:13:20 2013
@@ -0,0 +1,71 @@
+Apache Karaf
+Copyright 2010-2013 The Apache Software Foundation
+
+
+I. Included Software
+
+This product includes software developed at
+The Apache Software Foundation (http://www.apache.org/).
+Licensed under the Apache License 2.0.
+
+This product uses software developed at
+The OSGi Alliance (http://www.osgi.org/).
+Copyright (c) OSGi Alliance (2000, 2010).
+Licensed under the Apache License 2.0.
+
+This product includes software developed at
+OW2 (http://www.ow2.org/).
+Licensed under the BSD License.
+
+This product includes software developed at
+OPS4J (http://www.ops4j.org/).
+Licensed under the Apache License 2.0.
+
+This product includes software developed at
+Eclipse Foundation (http://www.eclipse.org/).
+Licensed under the EPL.
+
+This product includes software written by
+Antony Lesuisse.
+Licensed under Public Domain.
+
+
+II. Used Software
+
+This product uses software developed at
+FUSE Source (http://www.fusesource.org/).
+Licensed under the Apache License 2.0.
+
+This product uses software developed at
+AOP Alliance (http://aopalliance.sourceforge.net/).
+Licensed under the Public Domain.
+
+This product uses software developed at
+Tanuki Software (http://www.tanukisoftware.com/).
+Licensed under the Apache License 2.0.
+
+This product uses software developed at
+Jasypt (http://jasypt.sourceforge.net/).
+Licensed under the Apache License 2.0.
+
+This product uses software developed at
+JLine (http://jline.sourceforge.net).
+Licensed under the BSD License.
+
+This product uses software developed at
+SLF4J (http://www.slf4j.org/).
+Licensed under the MIT License.
+
+This product uses software developed at
+SpringSource (http://www.springsource.org/).
+Licensed under the Apache License 2.0.
+
+This product includes software from http://www.json.org.
+Copyright (c) 2002 JSON.org
+
+
+III. License Summary
+- Apache License 2.0
+- BSD License
+- EPL License
+- MIT License

Copied: karaf/trunk/service/guard/pom.xml (from r1533110, karaf/trunk/management/server/pom.xml)
URL: http://svn.apache.org/viewvc/karaf/trunk/service/guard/pom.xml?p2=karaf/trunk/service/guard/pom.xml&p1=karaf/trunk/management/server/pom.xml&r1=1533110&r2=1534467&rev=1534467&view=diff
==============================================================================
--- karaf/trunk/management/server/pom.xml (original)
+++ karaf/trunk/service/guard/pom.xml Tue Oct 22 03:13:20 2013
@@ -22,62 +22,61 @@
     <modelVersion>4.0.0</modelVersion>
 
     <parent>
-        <groupId>org.apache.karaf.management</groupId>
-        <artifactId>management</artifactId>
+        <groupId>org.apache.karaf.service</groupId>
+        <artifactId>service</artifactId>
         <version>3.0.0-SNAPSHOT</version>
         <relativePath>../pom.xml</relativePath>
     </parent>
 
-    <artifactId>org.apache.karaf.management.server</artifactId>
+    <artifactId>org.apache.karaf.service.guard</artifactId>
     <packaging>bundle</packaging>
-    <name>Apache Karaf :: Management</name>
-    <description>This bundle starts the Karaf embedded MBean server and RMI registry allowing users
-    to use JMX to manage Karaf.</description>
+    <name>Apache Karaf :: Service :: Guard</name>
+    <description>Adds role-based access control to OSGi services</description>
 
     <properties>
-        <appendedResourcesDirectory>${basedir}/../etc/appended-resources</appendedResourcesDirectory>
+        <appendedResourcesDirectory>${basedir}/../../etc/appended-resources</appendedResourcesDirectory>
     </properties>
 
     <dependencies>
         <dependency>
-            <groupId>org.osgi</groupId>
-            <artifactId>org.osgi.core</artifactId>
+            <groupId>org.apache.aries.proxy</groupId>
+            <artifactId>org.apache.aries.proxy.api</artifactId>
             <scope>provided</scope>
         </dependency>
         <dependency>
-            <groupId>org.osgi</groupId>
-            <artifactId>org.osgi.compendium</artifactId>
+            <groupId>org.apache.karaf.jaas</groupId>
+            <artifactId>org.apache.karaf.jaas.boot</artifactId>
             <scope>provided</scope>
         </dependency>
         <dependency>
-            <groupId>org.apache.karaf.jaas</groupId>
-            <artifactId>org.apache.karaf.jaas.boot</artifactId>
+            <groupId>org.osgi</groupId>
+            <artifactId>org.osgi.core</artifactId>
             <scope>provided</scope>
         </dependency>
         <dependency>
-            <groupId>org.apache.karaf.jaas</groupId>
-            <artifactId>org.apache.karaf.jaas.config</artifactId>
+            <groupId>org.osgi</groupId>
+            <artifactId>org.osgi.compendium</artifactId>
             <scope>provided</scope>
         </dependency>
         <dependency>
-            <groupId>org.apache.karaf.management</groupId>
-            <artifactId>org.apache.karaf.management.boot</artifactId>
+            <groupId>org.slf4j</groupId>
+            <artifactId>slf4j-api</artifactId>
             <scope>provided</scope>
         </dependency>
+
         <dependency>
-            <groupId>org.apache.servicemix.bundles</groupId>
-            <artifactId>org.apache.servicemix.bundles.junit</artifactId>
+            <groupId>org.apache.aries.proxy</groupId>
+            <artifactId>org.apache.aries.proxy.impl</artifactId>
             <scope>test</scope>
         </dependency>
         <dependency>
-            <groupId>org.easymock</groupId>
-            <artifactId>easymock</artifactId>
+            <groupId>org.ow2.asm</groupId>
+            <artifactId>asm-all</artifactId>
             <scope>test</scope>
         </dependency>
         <dependency>
-            <groupId>org.easymock</groupId>
-            <artifactId>easymockclassextension</artifactId>
-            <version>${easymock.version}</version>
+            <groupId>org.slf4j</groupId>
+            <artifactId>slf4j-nop</artifactId>
             <scope>test</scope>
         </dependency>
     </dependencies>
@@ -85,13 +84,13 @@
     <build>
         <resources>
             <resource>
-                <directory>${project.basedir}/src/main/resources</directory>
+                <directory>src/main/resources</directory>
                 <includes>
                     <include>**/*</include>
                 </includes>
             </resource>
             <resource>
-                <directory>${project.basedir}/src/main/resources</directory>
+                <directory>src/main/resources</directory>
                 <filtering>true</filtering>
                 <includes>
                     <include>**/*.info</include>
@@ -104,16 +103,13 @@
                 <artifactId>maven-bundle-plugin</artifactId>
                 <configuration>
                     <instructions>
-                        <Export-Package>org.apache.karaf.management;version=${project.version};-split-package:=merge-first</Export-Package>
-                        <Private-Package>
-                            org.apache.karaf.management,
-                            org.apache.karaf.management.internal
-                        </Private-Package>
+                        <Bundle-Activator>org.apache.karaf.service.guard.impl.Activator</Bundle-Activator>
+                        <Export-Package>org.apache.karaf.service.guard.tools</Export-Package>
+                        <Private-Package>org.apache.karaf.service.guard.impl</Private-Package>
                     </instructions>
                 </configuration>
             </plugin>
-       </plugins>
+        </plugins>
     </build>
 
-
-</project>
+</project>
\ No newline at end of file

Added: karaf/trunk/service/guard/src/main/java/org/apache/karaf/service/guard/impl/Activator.java
URL: http://svn.apache.org/viewvc/karaf/trunk/service/guard/src/main/java/org/apache/karaf/service/guard/impl/Activator.java?rev=1534467&view=auto
==============================================================================
--- karaf/trunk/service/guard/src/main/java/org/apache/karaf/service/guard/impl/Activator.java (added)
+++ karaf/trunk/service/guard/src/main/java/org/apache/karaf/service/guard/impl/Activator.java Tue Oct 22 03:13:20 2013
@@ -0,0 +1,67 @@
+/*
+ * 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.service.guard.impl;
+
+import org.osgi.framework.BundleActivator;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.Filter;
+import org.osgi.framework.hooks.service.EventListenerHook;
+import org.osgi.framework.hooks.service.FindHook;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+// This bundle is quite low-level and benefits from starting early in the process. Therefore it does not depend
+// on Blueprint but rather uses direct OSGi framework APIs and Service Trackers ...
+public class Activator implements BundleActivator {
+
+    private final Logger logger = LoggerFactory.getLogger(getClass());
+
+    GuardingEventHook guardingEventHook;
+    GuardingFindHook guardingFindHook;
+    GuardProxyCatalog guardProxyCatalog;
+
+    @Override
+    public void start(BundleContext bundleContext) throws Exception {
+        String f = System.getProperty(GuardProxyCatalog.KARAF_SECURED_SERVICES_SYSPROP);
+        Filter securedServicesFilter;
+        if (f == null) {
+            // no services need to be secured
+            logger.info("No role-based security for services as its system property is not set: {}", GuardProxyCatalog.KARAF_SECURED_SERVICES_SYSPROP);
+            return;
+        } else {
+            securedServicesFilter = bundleContext.createFilter(f);
+            logger.info("Adding role-based security to services with filter: {}", f);
+        }
+
+        guardProxyCatalog = new GuardProxyCatalog(bundleContext);
+
+        guardingEventHook = new GuardingEventHook(bundleContext, guardProxyCatalog, securedServicesFilter);
+        bundleContext.registerService(EventListenerHook.class, guardingEventHook, null);
+
+        guardingFindHook = new GuardingFindHook(bundleContext, guardProxyCatalog, securedServicesFilter);
+        bundleContext.registerService(FindHook.class, guardingFindHook, null);
+
+    }
+
+    @Override
+    public void stop(BundleContext bundleContext) throws Exception {
+        if (guardProxyCatalog != null) {
+            guardProxyCatalog.close();
+        }
+    }
+
+}

Added: karaf/trunk/service/guard/src/main/java/org/apache/karaf/service/guard/impl/GuardProxyCatalog.java
URL: http://svn.apache.org/viewvc/karaf/trunk/service/guard/src/main/java/org/apache/karaf/service/guard/impl/GuardProxyCatalog.java?rev=1534467&view=auto
==============================================================================
--- karaf/trunk/service/guard/src/main/java/org/apache/karaf/service/guard/impl/GuardProxyCatalog.java (added)
+++ karaf/trunk/service/guard/src/main/java/org/apache/karaf/service/guard/impl/GuardProxyCatalog.java Tue Oct 22 03:13:20 2013
@@ -0,0 +1,594 @@
+/*
+ * 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.service.guard.impl;
+
+import java.io.IOException;
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+import java.security.AccessControlContext;
+import java.security.AccessController;
+import java.security.Principal;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Dictionary;
+import java.util.Enumeration;
+import java.util.HashSet;
+import java.util.Hashtable;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Set;
+import java.util.TreeMap;
+import java.util.concurrent.BlockingQueue;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+import java.util.concurrent.LinkedBlockingQueue;
+import java.util.regex.Pattern;
+
+import javax.security.auth.Subject;
+
+import org.apache.aries.proxy.InvocationListener;
+import org.apache.aries.proxy.ProxyManager;
+import org.apache.aries.proxy.UnableToProxyException;
+import org.apache.karaf.jaas.boot.principal.RolePrincipal;
+import org.apache.karaf.service.guard.tools.ACLConfigurationParser;
+import org.apache.karaf.service.guard.tools.ACLConfigurationParser.Specificity;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.Constants;
+import org.osgi.framework.Filter;
+import org.osgi.framework.InvalidSyntaxException;
+import org.osgi.framework.ServiceEvent;
+import org.osgi.framework.ServiceFactory;
+import org.osgi.framework.ServiceListener;
+import org.osgi.framework.ServiceReference;
+import org.osgi.framework.ServiceRegistration;
+import org.osgi.service.cm.Configuration;
+import org.osgi.service.cm.ConfigurationAdmin;
+import org.osgi.util.tracker.ServiceTracker;
+import org.osgi.util.tracker.ServiceTrackerCustomizer;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class GuardProxyCatalog implements ServiceListener {
+    public static final String KARAF_SECURED_SERVICES_SYSPROP = "karaf.secured.services";
+    public static final String SERVICE_GUARD_ROLES_PROPERTY = "org.apache.karaf.service.guard.roles";
+
+    static final String PROXY_CREATOR_THREAD_NAME = "Secure OSGi Service Proxy Creator";
+    static final String PROXY_SERVICE_KEY = "." + GuardProxyCatalog.class.getName(); // The only currently used value is Boolean.TRUE
+    static final String SERVICE_ACL_PREFIX = "org.apache.karaf.service.acl.";
+    static final String SERVICE_GUARD_KEY = "service.guard";
+    static final Logger LOG = LoggerFactory.getLogger(GuardProxyCatalog.class);
+
+    private static final Pattern JAVA_METHOD_NAME_PATTERN = Pattern.compile("[a-zA-Z_$][a-zA-Z0-9_$]*");
+    private static final String ROLE_WILDCARD = "*";
+
+    private final BundleContext myBundleContext;
+
+    final ServiceTracker<ConfigurationAdmin, ConfigurationAdmin> configAdminTracker;
+    final ServiceTracker<ProxyManager, ProxyManager> proxyManagerTracker;
+    final ConcurrentMap<Long, ServiceRegistrationHolder> proxyMap = new ConcurrentHashMap<Long, ServiceRegistrationHolder>();
+    final BlockingQueue<CreateProxyRunnable> createProxyQueue = new LinkedBlockingQueue<CreateProxyRunnable>();
+
+    // These two variables control the proxy creator thread, which is started as soon as a ProxyManager Service
+    // becomes available.
+    volatile boolean runProxyCreator = true;
+    volatile Thread proxyCreatorThread = null;
+
+    GuardProxyCatalog(BundleContext bc) throws Exception {
+        LOG.trace("Starting GuardProxyCatalog");
+        myBundleContext = bc;
+
+        // The service listener is used to update/unregister proxies if the backing service changes/goes away
+        bc.addServiceListener(this);
+
+        Filter caFilter = getNonProxyFilter(bc, ConfigurationAdmin.class);
+        LOG.trace("Creating Config Admin Tracker using filter {}", caFilter);
+        configAdminTracker = new ServiceTracker<ConfigurationAdmin, ConfigurationAdmin>(bc, caFilter, null);
+        configAdminTracker.open();
+
+        Filter pmFilter = getNonProxyFilter(bc, ProxyManager.class);
+        LOG.trace("Creating Proxy Manager Tracker using filter {}", pmFilter);
+        proxyManagerTracker = new ServiceTracker<ProxyManager, ProxyManager>(bc, pmFilter, new ServiceProxyCreatorCustomizer());
+        proxyManagerTracker.open();
+    }
+
+    static Filter getNonProxyFilter(BundleContext bc, Class<?> clazz) throws InvalidSyntaxException {
+        Filter caFilter = bc.createFilter(
+                "(&(" + Constants.OBJECTCLASS + "=" + clazz.getName() +
+                        ")(!(" + PROXY_SERVICE_KEY + "=*)))");
+        return caFilter;
+    }
+
+    void close() {
+        LOG.trace("Stopping GuardProxyCatalog");
+        stopProxyCreator();
+        proxyManagerTracker.close();
+        configAdminTracker.close();
+
+        myBundleContext.removeServiceListener(this);
+
+        // Remove all proxy registrations
+        for (ServiceRegistrationHolder holder : proxyMap.values()) {
+            ServiceRegistration<?> reg = holder.registration;
+            if (reg != null) {
+                LOG.info("Unregistering proxy service of {} with properties {}",
+                        reg.getReference().getProperty(Constants.OBJECTCLASS), copyProperties(reg.getReference()));
+                reg.unregister();
+            }
+        }
+        proxyMap.clear();
+    }
+
+    @Override
+    public void serviceChanged(ServiceEvent event) {
+        // This method is to ensure that proxied services follow the original service. I.e. if the original service
+        // goes away the proxies should go away too. If the original service is modified, the proxies should be
+        // modified accordingly
+
+        ServiceReference<?> sr = event.getServiceReference();
+        if (event.getType() == ServiceEvent.REGISTERED) {
+            // Nothing to do for new services
+            return;
+        }
+
+        if (isProxy(sr)) {
+            // Ignore proxies, we only react to real service changes
+            return;
+        }
+
+        Long orgServiceID = (Long) sr.getProperty(Constants.SERVICE_ID);
+        if (event.getType() == ServiceEvent.UNREGISTERING) {
+            handleOriginalServiceUnregistering(orgServiceID);
+        }
+
+        if ((event.getType() & (ServiceEvent.MODIFIED | ServiceEvent.MODIFIED_ENDMATCH)) > 0) {
+            handleOriginalServiceModifed(orgServiceID, sr);
+        }
+    }
+
+    private void handleOriginalServiceUnregistering(Long orgServiceID) {
+        // If the service queued up to be proxied, remove it.
+        for (Iterator<CreateProxyRunnable> i = createProxyQueue.iterator(); i.hasNext(); ) {
+            CreateProxyRunnable cpr = i.next();
+            if (orgServiceID.equals(cpr.getOriginalServiceID())) {
+                i.remove();
+            }
+        }
+
+        ServiceRegistrationHolder holder = proxyMap.remove(orgServiceID);
+        if (holder != null) {
+            if (holder.registration != null) {
+                holder.registration.unregister();
+            }
+        }
+    }
+
+    private void handleOriginalServiceModifed(Long orgServiceID, ServiceReference<?> orgServiceRef) {
+        // We don't need to do anything for services that are queued up to be proxied, as the
+        // properties are only taken at the point of proxyfication...
+
+        ServiceRegistrationHolder holder = proxyMap.get(orgServiceID);
+        if (holder != null) {
+            ServiceRegistration<?> reg = holder.registration;
+            if (reg != null) {
+                // Preserve the roles as they are expensive to compute
+                Object roles = reg.getReference().getProperty(SERVICE_GUARD_ROLES_PROPERTY);
+                Dictionary<String, Object> newProxyProps = proxyProperties(orgServiceRef);
+                if (roles != null) {
+                    newProxyProps.put(SERVICE_GUARD_ROLES_PROPERTY, roles);
+                } else {
+                    newProxyProps.remove(SERVICE_GUARD_ROLES_PROPERTY);
+                }
+                reg.setProperties(newProxyProps);
+            }
+        }
+    }
+
+    boolean isProxy(ServiceReference<?> sr) {
+        return sr.getProperty(PROXY_SERVICE_KEY) != null;
+    }
+
+    // Called by hooks to find out whether the service should be hidden.
+    // Also handles the proxy creation of services if applicable.
+    // Return true if the hook should hide the service for the bundle
+    boolean handleProxificationForHook(ServiceReference<?> sr) {
+        // Note that when running under an OSGi R6 framework the number of proxies created
+        // can be limited by looking at the new 'service.scope' property. If the value is
+        // 'singleton' then the same proxy can be shared across all clients.
+        // Pre OSGi R6 it is not possible to find out whether a service is backed by a
+        // Service Factory, so we assume that every service is.
+
+        if (isProxy(sr)) {
+            return false;
+        }
+
+        proxyIfNotAlreadyProxied(sr); // Note does most of the work async
+        return true;
+    }
+
+    void proxyIfNotAlreadyProxied(final ServiceReference<?> originalRef)  {
+        final long orgServiceID = (Long) originalRef.getProperty(Constants.SERVICE_ID);
+
+        // make sure it's on the map before the proxy is registered, as that can trigger
+        // another call into this method, and we need to make sure that it doesn't proxy
+        // the service again.
+        final ServiceRegistrationHolder registrationHolder = new ServiceRegistrationHolder();
+        ServiceRegistrationHolder previousHolder = proxyMap.putIfAbsent(orgServiceID, registrationHolder);
+        if (previousHolder != null) {
+            // There is already a proxy for this service
+            return;
+        }
+
+        LOG.trace("Will create proxy of service {}({})", originalRef.getProperty(Constants.OBJECTCLASS), orgServiceID);
+
+        // Instead of immediately creating the proxy, we add the code that creates the proxy to the proxyQueue.
+        // This means that we can better react to the fact that the ProxyManager service might arrive
+        // later. As soon as the Proxy Manager is available, the queue is emptied and the proxies created.
+        CreateProxyRunnable cpr = new CreateProxyRunnable() {
+            @Override
+            public long getOriginalServiceID() {
+                return orgServiceID;
+            }
+
+            @Override
+            public void run(final ProxyManager pm) throws Exception {
+                String[] objectClassProperty = (String[]) originalRef.getProperty(Constants.OBJECTCLASS);
+                ServiceFactory<Object> sf = new ProxyServiceFactory(pm, originalRef);
+                registrationHolder.registration = originalRef.getBundle().getBundleContext().registerService(
+                        objectClassProperty, sf, proxyPropertiesRoles());
+
+                Dictionary<String, Object> actualProxyProps = copyProperties(registrationHolder.registration.getReference());
+                LOG.info("Created proxy of service {} under {} with properties {}",
+                        orgServiceID, actualProxyProps.get(Constants.OBJECTCLASS), actualProxyProps);
+            }
+
+            private Dictionary<String, Object> proxyPropertiesRoles() throws Exception {
+                Dictionary<String, Object> p = proxyProperties(originalRef);
+
+                Set<String> roles = getServiceInvocationRoles(originalRef);
+                if (roles != null) {
+                    roles.remove(ROLE_WILDCARD); // we don't expose that on the service property
+                    p.put(SERVICE_GUARD_ROLES_PROPERTY, roles);
+                } else {
+                    // In this case there are no roles defined for the service so anyone can invoke it
+                    p.remove(SERVICE_GUARD_ROLES_PROPERTY);
+                }
+                return p;
+            }
+        };
+
+        try {
+            createProxyQueue.put(cpr);
+        } catch (InterruptedException e) {
+            LOG.warn("Problem scheduling a proxy creator for service {}({})",
+                    originalRef.getProperty(Constants.OBJECTCLASS), orgServiceID, e);
+            e.printStackTrace();
+        }
+    }
+
+    private static Dictionary<String, Object> proxyProperties(ServiceReference<?> sr) {
+        Dictionary<String, Object> p = copyProperties(sr);
+        p.put(PROXY_SERVICE_KEY, Boolean.TRUE);
+        return p;
+    }
+
+    private static Dictionary<String, Object> copyProperties(ServiceReference<?> sr) {
+        Dictionary<String, Object> p = new Hashtable<String, Object>();
+
+        for (String key : sr.getPropertyKeys()) {
+            p.put(key, sr.getProperty(key));
+        }
+        return p;
+    }
+
+    // Returns what roles can possibly ever invoke this service. Note that not every invocation may be successful
+    // as there can be different roles for different methods and also roles based on arguments passed in.
+    Set<String> getServiceInvocationRoles(ServiceReference<?> serviceReference) throws Exception {
+        boolean definitionFound = false;
+        Set<String> allRoles = new HashSet<String>();
+
+        // This can probably be optimized. Maybe we can cache the config object relevant instead of
+        // walking through all of the ones that have 'service.guard'.
+        for (Configuration config : getServiceGuardConfigs()) {
+            Object guardFilter = config.getProperties().get(SERVICE_GUARD_KEY);
+            if (guardFilter instanceof String) {
+                Filter filter = myBundleContext.createFilter((String) guardFilter);
+                if (filter.match(serviceReference)) {
+                    definitionFound = true;
+                    for (Enumeration<String> e = config.getProperties().keys(); e.hasMoreElements(); ) {
+                        String key = e.nextElement();
+                        String bareKey = key;
+                        int idx = bareKey.indexOf('(');
+                        if (idx >= 0) {
+                            bareKey = bareKey.substring(0, idx);
+                        }
+                        int idx2 = bareKey.indexOf('*');
+                        if (idx2 >= 0) {
+                            bareKey = bareKey.substring(0, idx2);
+                        }
+                        if (!isValidMethodName(bareKey)) {
+                            continue;
+                        }
+                        Object value = config.getProperties().get(key);
+                        if (value instanceof String) {
+                            allRoles.addAll(ACLConfigurationParser.parseRoles((String) value));
+                        }
+                    }
+                }
+            }
+        }
+        return definitionFound ? allRoles : null;
+    }
+
+    // Ensures that it never returns null
+    private Configuration[] getServiceGuardConfigs() throws IOException, InvalidSyntaxException {
+        ConfigurationAdmin ca = null;
+        try {
+            ca = configAdminTracker.waitForService(5000);
+        } catch (InterruptedException e) {
+        }
+        if (ca == null) {
+            throw new IllegalStateException("Role based access for services requires the OSGi Configuration Admin Service to be present");
+        }
+
+        Configuration[] configs = ca.listConfigurations(
+                "(&(" + Constants.SERVICE_PID  + "=" + SERVICE_ACL_PREFIX + "*)(" + SERVICE_GUARD_KEY + "=*))");
+        if (configs == null) {
+            return new Configuration [] {};
+        }
+        return configs;
+    }
+
+    private boolean isValidMethodName(String name) {
+        return JAVA_METHOD_NAME_PATTERN.matcher(name).matches();
+    }
+
+    void stopProxyCreator() {
+        runProxyCreator = false; // Will end the proxy creation thread
+        if (proxyCreatorThread != null) {
+            proxyCreatorThread.interrupt();
+        }
+    }
+
+    static boolean currentUserHasRole(String reqRole) {
+        if (ROLE_WILDCARD.equals(reqRole)) {
+            return true;
+        }
+
+        String clazz;
+        String role;
+        int idx = reqRole.indexOf(':');
+        if (idx > 0) {
+            clazz = reqRole.substring(0, idx);
+            role = reqRole.substring(idx + 1);
+        } else {
+            clazz = RolePrincipal.class.getName();
+            role = reqRole;
+        }
+
+        AccessControlContext acc = AccessController.getContext();
+        if (acc == null) {
+            return false;
+        }
+
+        Subject subject = Subject.getSubject(acc);
+        if (subject == null) {
+            return false;
+        }
+
+        for (Principal p : subject.getPrincipals()) {
+            if (clazz.equals(p.getClass().getName()) && role.equals(p.getName())) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    static class ServiceRegistrationHolder {
+        volatile ServiceRegistration<?> registration;
+    }
+
+    class ProxyServiceFactory implements ServiceFactory<Object> {
+        private final ProxyManager pm;
+        private final ServiceReference<?> originalRef;
+
+        ProxyServiceFactory(ProxyManager pm, ServiceReference<?> originalRef) {
+            this.pm = pm;
+            this.originalRef = originalRef;
+        }
+
+        @Override
+        public Object getService(Bundle bundle, ServiceRegistration<Object> registration) {
+            Set<Class<?>> allClasses = new HashSet<Class<?>>();
+
+            // This needs to be done on the Client BundleContext since the bundle might be backed by a Service Factory
+            // in which case it needs to be given a chance to produce the right service for this client.
+            Object svc = bundle.getBundleContext().getService(originalRef);
+            Class<?> curClass = svc.getClass();
+            while (!Object.class.equals(curClass)) {
+                allClasses.add(curClass);
+                allClasses.addAll(Arrays.asList(curClass.getInterfaces()));
+                curClass = curClass.getSuperclass(); // Collect super types too
+            }
+
+            for (Iterator<Class<?>> i = allClasses.iterator(); i.hasNext(); ) {
+                Class<?> cls = i.next();
+                if (((cls.getModifiers() & (Modifier.PUBLIC | Modifier.PROTECTED)) == 0) ||
+                        ((cls.getModifiers() & Modifier.FINAL) > 0) ||
+                        cls.isAnonymousClass()  || cls.isLocalClass()) {
+                    // Do not attempt to proxy private, package-default, final,  anonymous or local classes
+                    i.remove();
+                } else {
+                    for (Method m : cls.getDeclaredMethods()) {
+                        if ((m.getModifiers() & Modifier.FINAL) > 0) {
+                            // If the class contains final methods, don't attempt to proxy it
+                            i.remove();
+                            break;
+                        }
+                    }
+                }
+            }
+
+            InvocationListener il = new ProxyInvocationListener(originalRef);
+            try {
+                return pm.createInterceptingProxy(originalRef.getBundle(), allClasses, svc, il);
+            } catch (UnableToProxyException e) {
+                throw new RuntimeException(e);
+            }
+        }
+
+        @Override
+        public void ungetService(Bundle bundle, ServiceRegistration<Object> registration, Object service) {
+            bundle.getBundleContext().ungetService(originalRef);
+        }
+    }
+
+    class ProxyInvocationListener implements InvocationListener {
+        private final ServiceReference<?> serviceReference;
+
+        ProxyInvocationListener(ServiceReference<?> sr) {
+            this.serviceReference = sr;
+        }
+
+        @Override
+        public Object preInvoke(Object proxy, Method m, Object[] args) throws Throwable {
+            String[] sig = new String[m.getParameterTypes().length];
+            for (int i = 0; i < m.getParameterTypes().length; i++) {
+                sig[i] = m.getParameterTypes()[i].getName();
+            }
+
+            // The ordering of the keys is important because the first value when iterating has the highest specificity
+            TreeMap<Specificity, List<String>> roleMappings = new TreeMap<ACLConfigurationParser.Specificity, List<String>>();
+            boolean foundMatchingConfig = false;
+
+            // This can probably be optimized. Maybe we can cache the config object relevant instead of
+            // walking through all of the ones that have 'service.guard'.
+            for (Configuration config : getServiceGuardConfigs()) {
+                Object guardFilter = config.getProperties().get(SERVICE_GUARD_KEY);
+                if (guardFilter instanceof String) {
+                    Filter filter = myBundleContext.createFilter((String) guardFilter);
+                    if (filter.match(serviceReference)) {
+                        foundMatchingConfig = true;
+                        List<String> roles = new ArrayList<String>();
+                        Specificity s = ACLConfigurationParser.
+                                getRolesForInvocation(m.getName(), args, sig, config.getProperties(), roles);
+                        if (s != Specificity.NO_MATCH) {
+                            roleMappings.put(s, roles);
+                            if (s == Specificity.ARGUMENT_MATCH) {
+                                // No more specific mapping can be found
+                                break;
+                            }
+                        }
+                    }
+                }
+            }
+
+            if (!foundMatchingConfig) {
+                // No mappings for this service, anyone can invoke
+                return null;
+            }
+
+            if (roleMappings.size() == 0) {
+                LOG.info("Service {} has role mapping, but assigned no roles to method {}", serviceReference, m);
+                throw new SecurityException("Insufficient credentials.");
+            }
+
+            // The first entry on the map has the highest significance because the keys are sorted in the order of
+            // the Specificity enum.
+            List<String> allowedRoles = roleMappings.values().iterator().next();
+            for (String role : allowedRoles) {
+                if (currentUserHasRole(role)) {
+                    LOG.trace("Allow user with role {} to invoke service {} method {}", role, serviceReference, m);
+                    return null;
+                }
+            }
+
+            // The current user does not have the required roles to invoke the service.
+            LOG.info("Current user does not have required roles ({}) for service {} method {} and/or arguments",
+                    allowedRoles, serviceReference, m);
+            throw new SecurityException("Insufficient credentials.");
+        }
+
+
+        @Override
+        public void postInvokeExceptionalReturn(Object token, Object proxy, Method m, Throwable exception) throws Throwable {
+        }
+
+        @Override
+        public void postInvoke(Object token, Object proxy, Method m, Object returnValue) throws Throwable {
+        }
+    }
+
+    // This customizer comes into action as the ProxyManager service arrives.
+    class ServiceProxyCreatorCustomizer implements ServiceTrackerCustomizer<ProxyManager, ProxyManager> {
+        @Override
+        public ProxyManager addingService(ServiceReference<ProxyManager> reference) {
+            runProxyCreator = true;
+            final ProxyManager svc = myBundleContext.getService(reference);
+            if (proxyCreatorThread == null && svc != null) {
+                proxyCreatorThread = newProxyProducingThread(svc);
+            }
+            return svc;
+        }
+
+        private Thread newProxyProducingThread(final ProxyManager proxyManager) {
+            Thread t = new Thread(new Runnable() {
+                @Override
+                public void run() {
+                    while (runProxyCreator) {
+                        CreateProxyRunnable proxyCreator = null;
+                        try {
+                            proxyCreator = createProxyQueue.take(); // take waits until there is something on the queue
+                        } catch (InterruptedException ie) {
+                            // part of normal behaviour
+                        }
+
+                        if (proxyCreator != null) {
+                            try {
+                                proxyCreator.run(proxyManager);
+                            } catch (Exception e) {
+                                LOG.warn("Problem creating secured service proxy", e);
+                            }
+                        }
+                    }
+                    // finished running
+                    proxyCreatorThread = null;
+                }
+            });
+            t.setName(PROXY_CREATOR_THREAD_NAME);
+            t.setDaemon(true);
+            t.start();
+
+            return t;
+        }
+
+        @Override
+        public void modifiedService(ServiceReference<ProxyManager> reference, ProxyManager service) {
+            // no need to react
+        }
+
+        @Override
+        public void removedService(ServiceReference<ProxyManager> reference, ProxyManager service) {
+            stopProxyCreator();
+        }
+    }
+
+    interface CreateProxyRunnable {
+        long getOriginalServiceID();
+        void run(ProxyManager pm) throws Exception;
+    }
+}

Added: karaf/trunk/service/guard/src/main/java/org/apache/karaf/service/guard/impl/GuardingEventHook.java
URL: http://svn.apache.org/viewvc/karaf/trunk/service/guard/src/main/java/org/apache/karaf/service/guard/impl/GuardingEventHook.java?rev=1534467&view=auto
==============================================================================
--- karaf/trunk/service/guard/src/main/java/org/apache/karaf/service/guard/impl/GuardingEventHook.java (added)
+++ karaf/trunk/service/guard/src/main/java/org/apache/karaf/service/guard/impl/GuardingEventHook.java Tue Oct 22 03:13:20 2013
@@ -0,0 +1,63 @@
+/*
+ * 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.service.guard.impl;
+
+import org.osgi.framework.*;
+import org.osgi.framework.hooks.service.EventListenerHook;
+import org.osgi.framework.hooks.service.ListenerHook;
+
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.Map;
+
+public class GuardingEventHook implements EventListenerHook {
+
+    private final BundleContext bundleContext;
+    private final GuardProxyCatalog guardProxyCatalog;
+    private final Filter servicesFilter;
+
+    GuardingEventHook(BundleContext bundleContext, GuardProxyCatalog guardProxyCatalog, Filter securedServicesFilter) throws InvalidSyntaxException {
+        this.bundleContext = bundleContext;
+        this.guardProxyCatalog = guardProxyCatalog;
+        this.servicesFilter = securedServicesFilter;
+    }
+
+    @Override
+    public void event(ServiceEvent event, Map<BundleContext, Collection<ListenerHook.ListenerInfo>> listeners) {
+        if (servicesFilter == null) {
+            return;
+        }
+
+        ServiceReference<?> sr = event.getServiceReference();
+        if (!servicesFilter.match(sr)) {
+            return;
+        }
+
+        for (Iterator<BundleContext> i = listeners.keySet().iterator(); i.hasNext(); ) {
+            BundleContext bc = i.next();
+            if (bundleContext.equals(bc) || bc.getBundle().getBundleId() == 0L) {
+                // don't hide anything from this bundle or the system bundle
+                continue;
+            }
+
+            if (guardProxyCatalog.handleProxificationForHook(sr)) {
+                i.remove();
+            }
+        }
+    }
+
+}

Added: karaf/trunk/service/guard/src/main/java/org/apache/karaf/service/guard/impl/GuardingFindHook.java
URL: http://svn.apache.org/viewvc/karaf/trunk/service/guard/src/main/java/org/apache/karaf/service/guard/impl/GuardingFindHook.java?rev=1534467&view=auto
==============================================================================
--- karaf/trunk/service/guard/src/main/java/org/apache/karaf/service/guard/impl/GuardingFindHook.java (added)
+++ karaf/trunk/service/guard/src/main/java/org/apache/karaf/service/guard/impl/GuardingFindHook.java Tue Oct 22 03:13:20 2013
@@ -0,0 +1,62 @@
+/*
+ * 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.service.guard.impl;
+
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.Filter;
+import org.osgi.framework.ServiceReference;
+import org.osgi.framework.hooks.service.FindHook;
+
+import java.util.Collection;
+import java.util.Iterator;
+
+public class GuardingFindHook implements FindHook {
+
+    private final BundleContext bundleContext;
+    private final GuardProxyCatalog guardProxyCatalog;
+    private final Filter servicesFilter;
+
+    GuardingFindHook(BundleContext bundleContext, GuardProxyCatalog guardProxyCatalog, Filter securedServicesFilter) {
+        this.bundleContext = bundleContext;
+        this.guardProxyCatalog = guardProxyCatalog;
+        this.servicesFilter = securedServicesFilter;
+    }
+
+    public void find(BundleContext bundleContext, String name, String filter, boolean allServices, Collection<ServiceReference<?>> references) {
+        if (servicesFilter == null) {
+            return;
+        }
+
+        if (this.bundleContext.equals(bundleContext) || bundleContext.getBundle().getBundleId() == 0) {
+            // don't hide anything from this bundle or the system bundle
+            return;
+        }
+
+        for (Iterator<ServiceReference<?>> i = references.iterator(); i.hasNext(); ) {
+            ServiceReference<?> sr = i.next();
+
+            if (!servicesFilter.match(sr)) {
+                continue;
+            }
+
+            if (guardProxyCatalog.handleProxificationForHook(sr)) {
+                i.remove();
+            }
+        }
+    }
+
+}