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();
+ }
+ }
+ }
+
+}