This is an automated email from the ASF dual-hosted git repository.

jbonofre pushed a commit to branch karaf-4.4.x
in repository https://gitbox.apache.org/repos/asf/karaf.git


The following commit(s) were added to refs/heads/karaf-4.4.x by this push:
     new 8ee4009b49 fix(jdbc,jms): add command ACLs for the jdbc and jms scopes 
(karaf-4.4.x) (#2863)
8ee4009b49 is described below

commit 8ee4009b49eb1e59c9565551b3004e8c5880762f
Author: JB Onofré <[email protected]>
AuthorDate: Sat Sep 12 07:55:47 2026 +0200

    fix(jdbc,jms): add command ACLs for the jdbc and jms scopes (karaf-4.4.x) 
(#2863)
    
    * fix(jdbc,jms): add command ACLs for the jdbc and jms scopes
    
    The jdbc and jms command scopes had no org.apache.karaf.command.acl.*
    configuration. The command guard treats an unmatched command as allowed,
    so any authenticated shell user (viewer role included) could run every
    jdbc:* and jms:* command.
    
    jdbc:ds-create and jms:create store a user-supplied URL into a
    pax-jdbc-config / pax-jms-config factory configuration that is turned
    into a live DataSource / ConnectionFactory. Several JDBC drivers run
    code at connection time based on URL parameters (for instance H2
    INIT=RUNSCRIPT), so creating a datasource is equivalent to arbitrary
    code execution. jdbc:execute / jdbc:query run arbitrary SQL, and
    jms:send / jms:consume / jms:move write to or destructively drain
    broker destinations.
    
    Add an ACL for each scope, provided by the jdbc / jms feature via a
    <config> element under a shell conditional (same mechanism as the kar
    and maven scopes):
    
      jdbc: ds-create, ds-delete, execute, query = admin;
            ds-list, ds-info, ds-factories, tables = viewer
      jms:  create, delete, send, consume, move = admin;
            connectionfactories, info, queues, topics, count, browse = viewer
    
    This is consistent with shell:exec and config:install, which are
    already restricted to admin. Add SSH command security itests for both
    scopes.
    
    * fix(jdbc,jms): grant admin/viewer roles in itests using jdbc/jms shell 
commands
    
    KarafTestSupport.executeCommand() runs without a Subject when no Principal
    is passed, so XATest, JmsTest and JmsExampleTest were being denied by the
    new jdbc/jms command ACLs with a misleading CommandNotFoundException.
---
 .../enterprise/src/main/feature/feature.xml        | 94 ++++++++++++++++++++++
 .../test/java/org/apache/karaf/itests/JmsTest.java | 28 ++++---
 .../test/java/org/apache/karaf/itests/XATest.java  | 24 +++---
 .../karaf/itests/examples/JmsExampleTest.java      |  6 +-
 .../itests/ssh/JdbcSshCommandSecurityTest.java     | 79 ++++++++++++++++++
 .../itests/ssh/JmsSshCommandSecurityTest.java      | 80 ++++++++++++++++++
 manual/src/main/asciidoc/user-guide/security.adoc  |  4 +
 7 files changed, 292 insertions(+), 23 deletions(-)

diff --git a/assemblies/features/enterprise/src/main/feature/feature.xml 
b/assemblies/features/enterprise/src/main/feature/feature.xml
index 3a90627234..e58a51c180 100644
--- a/assemblies/features/enterprise/src/main/feature/feature.xml
+++ b/assemblies/features/enterprise/src/main/feature/feature.xml
@@ -300,6 +300,52 @@ com.atomikos.icatch.log_base_dir=${karaf.data}/atomikos
         <feature prerequisite="true">pax-jdbc-config</feature>
         <bundle 
dependency="true">mvn:org.osgi/org.osgi.service.jdbc/${org.osgi.service.jdbc.version}</bundle>
         
<bundle>mvn:org.apache.karaf.jdbc/org.apache.karaf.jdbc.core/${project.version}</bundle>
+        <conditional>
+            <condition>shell</condition>
+            <config name="org.apache.karaf.command.acl.jdbc">
+################################################################################
+#
+#    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.
+#
+################################################################################
+
+#
+# This configuration file defines the ACLs for commands in the jdbc subshell
+#
+# For an explanation of the syntax of this file, see the file:
+#   org.apache.karaf.command.acl.system.cfg
+#
+# 'ds-create' stores a user-supplied JDBC URL into a pax-jdbc-config factory
+# configuration that is turned into a live DataSource. Several JDBC drivers run
+# code at connection time based on URL parameters (for instance H2 
INIT=RUNSCRIPT),
+# so creating a datasource is equivalent to arbitrary code execution and is
+# restricted to 'admin', consistent with shell:exec and config:install.
+# 'execute' and 'query' run arbitrary SQL and are restricted to 'admin' as 
well.
+# 'ds-delete' removes a datasource and is an admin operation.
+#
+ds-create = admin
+ds-delete = admin
+execute = admin
+query = admin
+ds-list = viewer
+ds-info = viewer
+ds-factories = viewer
+tables = viewer
+            </config>
+        </conditional>
     </feature>
 
     <feature name="jms" description="JMS service and commands" 
version="${project.version}">
@@ -313,6 +359,54 @@ com.atomikos.icatch.log_base_dir=${karaf.data}/atomikos
         <bundle 
dependency="true">mvn:org.apache.geronimo.specs/geronimo-jms_1.1_spec/${geronimo.jms-spec.version}</bundle>
         <bundle 
dependency="true">mvn:org.apache.commons/commons-pool2/${commons-pool2.version}</bundle>
         
<bundle>mvn:org.apache.karaf.jms/org.apache.karaf.jms.core/${project.version}</bundle>
+        <conditional>
+            <condition>shell</condition>
+            <config name="org.apache.karaf.command.acl.jms">
+################################################################################
+#
+#    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.
+#
+################################################################################
+
+#
+# This configuration file defines the ACLs for commands in the jms subshell
+#
+# For an explanation of the syntax of this file, see the file:
+#   org.apache.karaf.command.acl.system.cfg
+#
+# 'create' stores a user-supplied broker URL into a pax-jms-config factory
+# configuration that is turned into a live ConnectionFactory, so it is 
restricted
+# to 'admin', consistent with jdbc:ds-create and config:install. 'delete' 
removes a
+# connection factory. 'send', 'consume' and 'move' write to / destructively 
read
+# from broker destinations. All of these are restricted to 'admin'. The 
read-only
+# commands require the 'viewer' role.
+#
+create = admin
+delete = admin
+send = admin
+consume = admin
+move = admin
+connectionfactories = viewer
+info = viewer
+queues = viewer
+topics = viewer
+count = viewer
+browse = viewer
+            </config>
+        </conditional>
     </feature>
 
     <feature name="application-without-isolation" description="Provide EBA 
archive support" version="${aries.application.version}">
diff --git a/itests/test/src/test/java/org/apache/karaf/itests/JmsTest.java 
b/itests/test/src/test/java/org/apache/karaf/itests/JmsTest.java
index ec8865487a..14d6ee0ea0 100644
--- a/itests/test/src/test/java/org/apache/karaf/itests/JmsTest.java
+++ b/itests/test/src/test/java/org/apache/karaf/itests/JmsTest.java
@@ -14,10 +14,12 @@
 package org.apache.karaf.itests;
 
 import java.lang.management.ManagementFactory;
+import java.security.Principal;
 import java.util.Arrays;
 import java.util.LinkedList;
 import java.util.List;
 
+import org.apache.karaf.jaas.boot.principal.RolePrincipal;
 import org.junit.Assert;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -52,6 +54,8 @@ public class JmsTest extends BaseTest {
 
     @Test(timeout = 60000)
     public void testCommands() throws Exception {
+        Principal[] roles = { new RolePrincipal("admin"), new 
RolePrincipal("viewer") };
+
         System.out.println("== Installing ActiveMQ");
         featureService.installFeature("aries-blueprint");
         featureService.installFeature("activemq-broker-noweb");
@@ -61,42 +65,42 @@ public class JmsTest extends BaseTest {
         featureService.installFeature("pax-jms-activemq");
 
         System.out.println("== Creating JMS ConnectionFactory");
-        executeCommand("jms:create test");
+        executeCommand("jms:create test", roles);
         Thread.sleep(2000);
-        String output = executeCommand("jms:connectionfactories");
+        String output = executeCommand("jms:connectionfactories", roles);
         System.out.println(output);
         assertContains("jms/test", output);
 
-        output = executeCommand("jms:info jms/test");
+        output = executeCommand("jms:info jms/test", roles);
         System.out.println(output);
         assertContains("ActiveMQ", output);
 
-        executeCommand("jms:send jms/test queue message");
-        output = executeCommand("jms:count jms/test queue");
+        executeCommand("jms:send jms/test queue message", roles);
+        output = executeCommand("jms:count jms/test queue", roles);
         System.out.println(output);
         assertContains("1", output);
 
-        output = executeCommand("jms:consume jms/test queue");
+        output = executeCommand("jms:consume jms/test queue", roles);
         System.out.println(output);
         assertContains("1 message", output);
 
-        executeCommand("jms:send test queue message");
-        output = executeCommand("jms:move test queue other");
+        executeCommand("jms:send test queue message", roles);
+        output = executeCommand("jms:move test queue other", roles);
         System.out.println(output);
         assertContains("1 message", output);
 
-        output = executeCommand("jms:queues test");
+        output = executeCommand("jms:queues test", roles);
         System.out.println(output);
         assertContains("queue", output);
         assertContains("other", output);
 
-        output = executeCommand("jms:browse test other");
+        output = executeCommand("jms:browse test other", roles);
         System.out.println(output);
         assertContains("queue", output);
         assertContains("queue://other", output);
 
-        System.out.println(executeCommand("jms:consume test other"));
-        System.out.println(executeCommand("jms:delete test"));
+        System.out.println(executeCommand("jms:consume test other", roles));
+        System.out.println(executeCommand("jms:delete test", roles));
     }
 
     @Test
diff --git a/itests/test/src/test/java/org/apache/karaf/itests/XATest.java 
b/itests/test/src/test/java/org/apache/karaf/itests/XATest.java
index bf9d37c5e4..4ebdb22476 100644
--- a/itests/test/src/test/java/org/apache/karaf/itests/XATest.java
+++ b/itests/test/src/test/java/org/apache/karaf/itests/XATest.java
@@ -18,6 +18,7 @@ package org.apache.karaf.itests;
 
 import org.apache.karaf.features.FeaturesService;
 import org.apache.karaf.itests.util.RunIfRule;
+import org.apache.karaf.jaas.boot.principal.RolePrincipal;
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -30,6 +31,7 @@ import org.ops4j.pax.exam.spi.reactors.ExamReactorStrategy;
 import org.ops4j.pax.exam.spi.reactors.PerClass;
 import org.osgi.framework.Bundle;
 
+import java.security.Principal;
 import java.util.Arrays;
 import java.util.EnumSet;
 import java.util.LinkedList;
@@ -70,6 +72,8 @@ public class XATest extends BaseTest {
 
     @Test
     public void test() throws Exception {
+        Principal[] roles = { new RolePrincipal("admin"), new 
RolePrincipal("viewer") };
+
         System.out.println("== Starting Artemis broker == ");
         String logDisplay = executeCommand("log:display");
         while (!logDisplay.contains("AMQ221007: Server is now live")) {
@@ -77,7 +81,7 @@ public class XATest extends BaseTest {
             logDisplay = executeCommand("log:display");
         }
         System.out.println("AMQ221007: Server is now live");
-        System.out.println(executeCommand("jms:info artemis"));
+        System.out.println(executeCommand("jms:info artemis", roles));
 
         System.out.println("== Installing Derby database == ");
         featureService.installFeature("jdbc", NO_AUTO_REFRESH);
@@ -85,20 +89,20 @@ public class XATest extends BaseTest {
         featureService.installFeature("pax-jdbc-pool-transx", NO_AUTO_REFRESH);
 
         System.out.println(" ");
-        String dsList = executeCommand("jdbc:ds-list");
+        String dsList = executeCommand("jdbc:ds-list", roles);
         while (!dsList.contains("OK")) {
             Thread.sleep(500);
-            dsList = executeCommand("jdbc:ds-list");
+            dsList = executeCommand("jdbc:ds-list", roles);
         }
         System.out.println(dsList);
-        
+
         System.out.println("== Creating table in Derby ==");
-        System.out.println(executeCommand("jdbc:execute derby CREATE TABLE 
messages (id INTEGER NOT NULL GENERATED ALWAYS AS IDENTITY, message 
VARCHAR(1024) NOT NULL, CONSTRAINT primary_key PRIMARY KEY (id))"));
+        System.out.println(executeCommand("jdbc:execute derby CREATE TABLE 
messages (id INTEGER NOT NULL GENERATED ALWAYS AS IDENTITY, message 
VARCHAR(1024) NOT NULL, CONSTRAINT primary_key PRIMARY KEY (id))", roles));
 
-        String tableOutput = executeCommand("jdbc:query derby select * from 
messages");
+        String tableOutput = executeCommand("jdbc:query derby select * from 
messages", roles);
         while (!tableOutput.contains("MESSAGE")) {
             Thread.sleep(500);
-            tableOutput = executeCommand("jdbc:query derby select * from 
messages");;
+            tableOutput = executeCommand("jdbc:query derby select * from 
messages", roles);;
         }
         System.out.println("== Table created ==");
 
@@ -122,13 +126,13 @@ public class XATest extends BaseTest {
         System.out.println(routeList);
 
         System.out.println("== Sending a message in Artemis broker that should 
be consumed by Camel route and inserted into the Derby database");
-        System.out.println(executeCommand("jms:send artemis MyQueue 
'the-message'"));
+        System.out.println(executeCommand("jms:send artemis MyQueue 
'the-message'", roles));
 
-        String output = executeCommand("jdbc:query derby select * from 
messages");
+        String output = executeCommand("jdbc:query derby select * from 
messages", roles);
 
         while (!output.contains("the-message")) {
             Thread.sleep(500);
-            output = executeCommand("jdbc:query derby select * from messages");
+            output = executeCommand("jdbc:query derby select * from messages", 
roles);
         }
 
         System.out.println(output);
diff --git 
a/itests/test/src/test/java/org/apache/karaf/itests/examples/JmsExampleTest.java
 
b/itests/test/src/test/java/org/apache/karaf/itests/examples/JmsExampleTest.java
index 350ed89acd..d1c5fa2c36 100644
--- 
a/itests/test/src/test/java/org/apache/karaf/itests/examples/JmsExampleTest.java
+++ 
b/itests/test/src/test/java/org/apache/karaf/itests/examples/JmsExampleTest.java
@@ -15,6 +15,7 @@ package org.apache.karaf.itests.examples;
 
 import org.apache.karaf.features.FeaturesService;
 import org.apache.karaf.itests.BaseTest;
+import org.apache.karaf.jaas.boot.principal.RolePrincipal;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.ops4j.pax.exam.Configuration;
@@ -24,6 +25,7 @@ import org.ops4j.pax.exam.junit.PaxExam;
 import org.ops4j.pax.exam.spi.reactors.ExamReactorStrategy;
 import org.ops4j.pax.exam.spi.reactors.PerClass;
 
+import java.security.Principal;
 import java.util.Arrays;
 import java.util.EnumSet;
 import java.util.LinkedList;
@@ -57,8 +59,10 @@ public class JmsExampleTest extends BaseTest {
 
     @Test
     public void test() throws Exception {
+        Principal[] roles = { new RolePrincipal("admin"), new 
RolePrincipal("viewer") };
+
         Thread.sleep(10000);//wait until artemis up
-        String output = executeCommand("jms:info artemis");
+        String output = executeCommand("jms:info artemis", roles);
         System.out.println(output);
         assertContains("ActiveMQ", output);
 
diff --git 
a/itests/test/src/test/java/org/apache/karaf/itests/ssh/JdbcSshCommandSecurityTest.java
 
b/itests/test/src/test/java/org/apache/karaf/itests/ssh/JdbcSshCommandSecurityTest.java
new file mode 100644
index 0000000000..b9cd23a58f
--- /dev/null
+++ 
b/itests/test/src/test/java/org/apache/karaf/itests/ssh/JdbcSshCommandSecurityTest.java
@@ -0,0 +1,79 @@
+/*
+ * Licensed 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.itests.ssh;
+
+import java.util.EnumSet;
+
+import org.apache.karaf.features.FeaturesService;
+import org.junit.Assume;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.ops4j.pax.exam.junit.PaxExam;
+import org.ops4j.pax.exam.spi.reactors.ExamReactorStrategy;
+import org.ops4j.pax.exam.spi.reactors.PerClass;
+
+/**
+ * This test exercises the Shell Command ACL for the jdbc scope commands as 
defined in the
+ * {@code org.apache.karaf.command.acl.jdbc} configuration provided by the 
{@code jdbc} feature.
+ *
+ * <p>{@code jdbc:ds-create} stores an arbitrary JDBC URL that pax-jdbc-config 
turns into a live
+ * datasource - several JDBC drivers run code at connection time based on URL 
parameters (for
+ * instance H2 {@code INIT=RUNSCRIPT}) - so it, together with {@code 
jdbc:ds-delete},
+ * {@code jdbc:execute} and {@code jdbc:query} (arbitrary SQL), is restricted 
to the
+ * {@code admin} role. The read-only commands require the {@code viewer} 
role.</p>
+ */
+@RunWith(PaxExam.class)
+@ExamReactorStrategy(PerClass.class)
+public class JdbcSshCommandSecurityTest extends SshCommandTestBase {
+
+    private static int counter = 0;
+
+    @Test
+    public void testJdbcCommandSecurityViaSsh() throws Exception {
+        // Skip on Windows where PTY output can be garbled,
+        // when upgrading to Junit5, this can be replaced with 
@DisabledOnOs(OS.WINDOWS)
+        Assume.assumeFalse(System.getProperty("os.name", 
"").toLowerCase().contains("win"));
+
+        featureService.installFeature("jdbc", 
EnumSet.of(FeaturesService.Option.NoAutoRefreshBundles));
+
+        String manageruser = "man" + System.nanoTime() + "_" + counter++;
+        String vieweruser = "view" + System.nanoTime() + "_" + counter++;
+
+        addUsers(manageruser, vieweruser);
+
+        // read-only commands are available to a viewer
+        assertCommand(vieweruser, "jdbc:ds-list", Result.OK);
+        assertCommand(vieweruser, "jdbc:ds-factories", Result.OK);
+
+        // ds-create / ds-delete / execute / query are admin-only: a viewer 
and a manager
+        // must not even see the commands
+        assertCommand(vieweruser, "jdbc:ds-create", Result.NOT_FOUND);
+        assertCommand(vieweruser, "jdbc:ds-delete", Result.NOT_FOUND);
+        assertCommand(vieweruser, "jdbc:execute", Result.NOT_FOUND);
+        assertCommand(vieweruser, "jdbc:query", Result.NOT_FOUND);
+
+        assertCommand(manageruser, "jdbc:ds-create", Result.NOT_FOUND);
+        assertCommand(manageruser, "jdbc:execute", Result.NOT_FOUND);
+        assertCommand(manageruser, "jdbc:query", Result.NOT_FOUND);
+
+        // the admin user can see and run the jdbc commands (a bare invocation 
reports a
+        // missing-argument / missing-driver error, not "Command not found")
+        assertCommand("karaf", "jdbc:ds-list", Result.OK);
+        assertCommand("karaf", "jdbc:ds-factories", Result.OK);
+        assertCommand("karaf", "jdbc:ds-create", Result.OK);
+        assertCommand("karaf", "jdbc:execute", Result.OK);
+        assertCommand("karaf", "jdbc:query", Result.OK);
+        assertCommand("karaf", "jdbc:ds-delete", Result.OK);
+    }
+}
diff --git 
a/itests/test/src/test/java/org/apache/karaf/itests/ssh/JmsSshCommandSecurityTest.java
 
b/itests/test/src/test/java/org/apache/karaf/itests/ssh/JmsSshCommandSecurityTest.java
new file mode 100644
index 0000000000..cb6fabf96a
--- /dev/null
+++ 
b/itests/test/src/test/java/org/apache/karaf/itests/ssh/JmsSshCommandSecurityTest.java
@@ -0,0 +1,80 @@
+/*
+ * Licensed 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.itests.ssh;
+
+import java.util.EnumSet;
+
+import org.apache.karaf.features.FeaturesService;
+import org.junit.Assume;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.ops4j.pax.exam.junit.PaxExam;
+import org.ops4j.pax.exam.spi.reactors.ExamReactorStrategy;
+import org.ops4j.pax.exam.spi.reactors.PerClass;
+
+/**
+ * This test exercises the Shell Command ACL for the jms scope commands as 
defined in the
+ * {@code org.apache.karaf.command.acl.jms} configuration provided by the 
{@code jms} feature.
+ *
+ * <p>{@code jms:create} stores an arbitrary broker URL that pax-jms-config 
turns into a live
+ * connection factory, and {@code jms:send}, {@code jms:consume} and {@code 
jms:move} write to
+ * or destructively read from broker destinations, so these commands together 
with
+ * {@code jms:delete} are restricted to the {@code admin} role. The read-only 
commands require
+ * the {@code viewer} role.</p>
+ */
+@RunWith(PaxExam.class)
+@ExamReactorStrategy(PerClass.class)
+public class JmsSshCommandSecurityTest extends SshCommandTestBase {
+
+    private static int counter = 0;
+
+    @Test
+    public void testJmsCommandSecurityViaSsh() throws Exception {
+        // Skip on Windows where PTY output can be garbled,
+        // when upgrading to Junit5, this can be replaced with 
@DisabledOnOs(OS.WINDOWS)
+        Assume.assumeFalse(System.getProperty("os.name", 
"").toLowerCase().contains("win"));
+
+        featureService.installFeature("jms", 
EnumSet.of(FeaturesService.Option.NoAutoRefreshBundles));
+
+        String manageruser = "man" + System.nanoTime() + "_" + counter++;
+        String vieweruser = "view" + System.nanoTime() + "_" + counter++;
+
+        addUsers(manageruser, vieweruser);
+
+        // read-only commands are available to a viewer
+        assertCommand(vieweruser, "jms:connectionfactories", Result.OK);
+
+        // create / delete / send / consume / move are admin-only: a viewer 
and a manager
+        // must not even see the commands
+        assertCommand(vieweruser, "jms:create", Result.NOT_FOUND);
+        assertCommand(vieweruser, "jms:delete", Result.NOT_FOUND);
+        assertCommand(vieweruser, "jms:send", Result.NOT_FOUND);
+        assertCommand(vieweruser, "jms:consume", Result.NOT_FOUND);
+        assertCommand(vieweruser, "jms:move", Result.NOT_FOUND);
+
+        assertCommand(manageruser, "jms:create", Result.NOT_FOUND);
+        assertCommand(manageruser, "jms:send", Result.NOT_FOUND);
+        assertCommand(manageruser, "jms:consume", Result.NOT_FOUND);
+        assertCommand(manageruser, "jms:move", Result.NOT_FOUND);
+
+        // the admin user can see and run the jms commands (a bare invocation 
reports a
+        // missing-argument error, not "Command not found")
+        assertCommand("karaf", "jms:connectionfactories", Result.OK);
+        assertCommand("karaf", "jms:create", Result.OK);
+        assertCommand("karaf", "jms:delete", Result.OK);
+        assertCommand("karaf", "jms:send", Result.OK);
+        assertCommand("karaf", "jms:consume", Result.OK);
+        assertCommand("karaf", "jms:move", Result.OK);
+    }
+}
diff --git a/manual/src/main/asciidoc/user-guide/security.adoc 
b/manual/src/main/asciidoc/user-guide/security.adoc
index a542b88994..7a736b02a1 100644
--- a/manual/src/main/asciidoc/user-guide/security.adoc
+++ b/manual/src/main/asciidoc/user-guide/security.adoc
@@ -506,6 +506,10 @@ By default, Apache Karaf defines the following commands 
ACLs:
 * `etc/org.apache.karaf.command.acl.system.cfg` configuration file defines the 
ACL for `system:*` commands.
  Only the users with `admin` role can execute `system:property` and 
`system:shutdown` commands. Users with `manager` role can call 
`system:start-level` above 100, otherwise `admin` role is required. Also users 
with `viewer` role can obtain the current start-level.
  The other `system:*` commands can be executed by any user.
+* `etc/org.apache.karaf.command.acl.jdbc.cfg` configuration file (provided by 
the `jdbc` feature) defines the ACL for `jdbc:*` commands.
+ As `jdbc:ds-create` stores an arbitrary JDBC URL that pax-jdbc-config turns 
into a live datasource (several drivers run code at connection time, for 
instance H2 `INIT=RUNSCRIPT`), and `jdbc:execute` and `jdbc:query` run 
arbitrary SQL, these commands together with `jdbc:ds-delete` are restricted to 
the users with the `admin` role. The read-only `jdbc:*` commands require the 
`viewer` role.
+* `etc/org.apache.karaf.command.acl.jms.cfg` configuration file (provided by 
the `jms` feature) defines the ACL for `jms:*` commands.
+ As `jms:create` stores an arbitrary broker URL that pax-jms-config turns into 
a live connection factory, and `jms:send`, `jms:consume` and `jms:move` write 
to or destructively read from broker destinations, these commands together with 
`jms:delete` are restricted to the users with the `admin` role. The read-only 
`jms:*` commands require the `viewer` role.
 
 You can change these default ACLs, and add your own ACLs for additional 
command scopes (for instance `etc/org.apache.karaf.command.acl.cluster.cfg` for
 Apache Karaf Cellar, `etc/org.apache.karaf.command.acl.camel.cfg` from Apache 
Camel, ...).

Reply via email to