You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@tomcat.apache.org by mi...@apache.org on 2023/06/29 09:31:43 UTC

[tomcat] branch main updated (d1f0c34b18 -> c8adc4c486)

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

michaelo pushed a change to branch main
in repository https://gitbox.apache.org/repos/asf/tomcat.git


    from d1f0c34b18 Pull up as default method since it avoids API compatibility issues
     new ecca27f193 Add BZ issue ID
     new c8adc4c486 Bug 66665: Provide option to supply role mapping from a properties file

The 2 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.


Summary of changes:
 .../apache/catalina/core/LocalStrings.properties   |   7 +
 .../core/PropertiesRoleMappingListener.java        | 168 +++++++++++++++++++++
 .../core/TestPropertiesRoleMappingListener.java    | 168 +++++++++++++++++++++
 .../com/example/prefixed-role-mapping.properties   |   2 +
 .../classes/com/example/role-mapping.properties    |   2 +
 .../WEB-INF/prefixed-role-mapping.properties       |   2 +
 .../WEB-INF/role-mapping.properties                |   2 +
 test/webapp-role-mapping/admin                     |   1 +
 test/webapp-role-mapping/unmapped                  |   1 +
 test/webapp-role-mapping/user                      |   1 +
 webapps/docs/changelog.xml                         |   9 +-
 webapps/docs/config/listeners.xml                  |  31 ++++
 12 files changed, 393 insertions(+), 1 deletion(-)
 create mode 100644 java/org/apache/catalina/core/PropertiesRoleMappingListener.java
 create mode 100644 test/org/apache/catalina/core/TestPropertiesRoleMappingListener.java
 create mode 100644 test/webapp-role-mapping/WEB-INF/classes/com/example/prefixed-role-mapping.properties
 create mode 100644 test/webapp-role-mapping/WEB-INF/classes/com/example/role-mapping.properties
 create mode 100644 test/webapp-role-mapping/WEB-INF/prefixed-role-mapping.properties
 create mode 100644 test/webapp-role-mapping/WEB-INF/role-mapping.properties
 create mode 100644 test/webapp-role-mapping/admin
 create mode 100644 test/webapp-role-mapping/unmapped
 create mode 100644 test/webapp-role-mapping/user


---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@tomcat.apache.org
For additional commands, e-mail: dev-help@tomcat.apache.org


[tomcat] 01/02: Add BZ issue ID

Posted by mi...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

michaelo pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/tomcat.git

commit ecca27f193585ff7b4bd17a1739fa0d59138b02c
Author: Michael Osipov <mi...@apache.org>
AuthorDate: Thu Jun 29 11:22:38 2023 +0200

    Add BZ issue ID
---
 webapps/docs/changelog.xml | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/webapps/docs/changelog.xml b/webapps/docs/changelog.xml
index 537749e57a..dd909b6b1a 100644
--- a/webapps/docs/changelog.xml
+++ b/webapps/docs/changelog.xml
@@ -108,7 +108,8 @@
   <subsection name="Catalina">
     <changelog>
       <add>
-        Add <code>org.apache.catalina.core.ContextNamingInfoListener</code>,
+        <bug>59232</bug>: Add
+        <code>org.apache.catalina.core.ContextNamingInfoListener</code>,
         a listener which creates context naming information environment entries.
         (michaelo)
       </add>


---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@tomcat.apache.org
For additional commands, e-mail: dev-help@tomcat.apache.org


[tomcat] 02/02: Bug 66665: Provide option to supply role mapping from a properties file

Posted by mi...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

michaelo pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/tomcat.git

commit c8adc4c4869f432b900606ae52a89e54c324f3dd
Author: Michael Osipov <mi...@apache.org>
AuthorDate: Fri Jun 23 15:07:25 2023 +0200

    Bug 66665: Provide option to supply role mapping from a properties file
---
 .../apache/catalina/core/LocalStrings.properties   |   7 +
 .../core/PropertiesRoleMappingListener.java        | 168 +++++++++++++++++++++
 .../core/TestPropertiesRoleMappingListener.java    | 168 +++++++++++++++++++++
 .../com/example/prefixed-role-mapping.properties   |   2 +
 .../classes/com/example/role-mapping.properties    |   2 +
 .../WEB-INF/prefixed-role-mapping.properties       |   2 +
 .../WEB-INF/role-mapping.properties                |   2 +
 test/webapp-role-mapping/admin                     |   1 +
 test/webapp-role-mapping/unmapped                  |   1 +
 test/webapp-role-mapping/user                      |   1 +
 webapps/docs/changelog.xml                         |   6 +
 webapps/docs/config/listeners.xml                  |  31 ++++
 12 files changed, 391 insertions(+)

diff --git a/java/org/apache/catalina/core/LocalStrings.properties b/java/org/apache/catalina/core/LocalStrings.properties
index 66e5067aac..aa6e810ba7 100644
--- a/java/org/apache/catalina/core/LocalStrings.properties
+++ b/java/org/apache/catalina/core/LocalStrings.properties
@@ -164,6 +164,13 @@ noPluggabilityServletContext.notAllowed=Section 4.4 of the Servlet 3.0 specifica
 
 pushBuilder.noPath=It is illegal to call push() before setting a path
 
+propertiesRoleMappingListener.roleMappingFileNull=Role mapping file cannot be null
+propertiesRoleMappingListener.roleMappingFileEmpty=Role mapping file cannot be empty
+propertiesRoleMappingListener.roleMappingFileNotFound=Role mapping file [{0}] not found
+propertiesRoleMappingListener.roleMappingFileFail=Failed to load role mapping file [{0}]
+propertiesRoleMappingListener.linkedRole=Successfully linked application role [{0}] to technical role [{1}]
+propertiesRoleMappingListener.linkedRoleCount=Linked [{0}] application roles to technical roles
+
 standardContext.applicationListener=Error configuring application listener of class [{0}]
 standardContext.applicationSkipped=Skipped installing application listeners due to previous error(s)
 standardContext.backgroundProcess.instanceManager=Exception processing instance manager [{0}] background process
diff --git a/java/org/apache/catalina/core/PropertiesRoleMappingListener.java b/java/org/apache/catalina/core/PropertiesRoleMappingListener.java
new file mode 100644
index 0000000000..60b135d27f
--- /dev/null
+++ b/java/org/apache/catalina/core/PropertiesRoleMappingListener.java
@@ -0,0 +1,168 @@
+/*
+ * 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.catalina.core;
+
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.Map.Entry;
+import java.util.Objects;
+import java.util.Properties;
+
+import org.apache.catalina.Context;
+import org.apache.catalina.Lifecycle;
+import org.apache.catalina.LifecycleEvent;
+import org.apache.catalina.LifecycleListener;
+import org.apache.juli.logging.Log;
+import org.apache.juli.logging.LogFactory;
+import org.apache.tomcat.util.file.ConfigFileLoader;
+import org.apache.tomcat.util.res.StringManager;
+
+/**
+ * Implementation of {@code LifecycleListener} that will populate the context's role mapping from a properties file.
+ * <p>
+ * This listener must only be nested within {@link Context} elements.
+ * <p>
+ * The keys represent application roles (e.g., admin, user, uservisor, etc.) while the values represent technical roles
+ * (e.g., DNs, SIDs, UUIDs, etc.). A key can also be prefixed if, e.g., the properties file contains generic
+ * application configuration as well: {@code app-roles.}.
+ * <p>
+ * Note: The default value for the {@code roleMappingFile} is {@code webapp:/WEB-INF/role-mapping.properties}.
+ */
+public class PropertiesRoleMappingListener implements LifecycleListener {
+
+    private static final String WEBAPP_PROTOCOL = "webapp:";
+
+    private static final Log log = LogFactory.getLog(PropertiesRoleMappingListener.class);
+    /**
+     * The string manager for this package.
+     */
+    private static final StringManager sm = StringManager.getManager(ContextNamingInfoListener.class);
+
+    private String roleMappingFile = WEBAPP_PROTOCOL + "/WEB-INF/role-mapping.properties";
+    private String keyPrefix;
+
+    /**
+     * Sets the path to the role mapping properties file. You can use protocol {@code webapp:} and whatever
+     * {@link ConfigFileLoader} supports.
+     *
+     * @param roleMappingFile the role mapping properties file to load from
+     * @throws NullPointerException if roleMappingFile is null
+     * @throws IllegalArgumentException if roleMappingFile is empty
+     */
+    public void setRoleMappingFile(String roleMappingFile) {
+        Objects.requireNonNull(roleMappingFile, sm.getString("propertiesRoleMappingListener.roleMappingFileNull"));
+        if (roleMappingFile.isEmpty()) {
+            throw new IllegalArgumentException(sm.getString("propertiesRoleMappingListener.roleMappingFileEmpty"));
+        }
+
+        this.roleMappingFile = roleMappingFile;
+    }
+
+    /**
+     * Gets the path to the role mapping properties file.
+     *
+     * @return the path to the role mapping properties file
+     */
+    public String getRoleMappingFile() {
+        return roleMappingFile;
+    }
+
+    /**
+     * Sets the prefix to filter from property keys. All other keys will be ignored which do not have the prefix.
+     *
+     * @param keyPrefix the properties key prefix
+     */
+    public void setKeyPrefix(String keyPrefix) {
+        this.keyPrefix = keyPrefix;
+    }
+
+    /**
+     * Gets the prefix to filter from property keys.
+     *
+     * @return the properties key prefix
+     */
+    public String getKeyPrefix() {
+        return keyPrefix;
+    }
+
+    @Override
+    public void lifecycleEvent(LifecycleEvent event) {
+        if (event.getType().equals(Lifecycle.CONFIGURE_START_EVENT)) {
+            if (!(event.getLifecycle() instanceof Context)) {
+                log.warn(sm.getString("listener.notContext", event.getLifecycle().getClass().getSimpleName()));
+                return;
+            }
+            Context context = (Context) event.getLifecycle();
+
+            InputStream is;
+            if (roleMappingFile.startsWith(WEBAPP_PROTOCOL)) {
+                String path = roleMappingFile.substring(WEBAPP_PROTOCOL.length());
+                is = context.getServletContext().getResourceAsStream(path);
+            } else {
+                try {
+                    is = ConfigFileLoader.getSource().getResource(roleMappingFile).getInputStream();
+                } catch (FileNotFoundException e1) {
+                    is = null;
+                } catch (IOException e2) {
+                    throw new IllegalStateException(
+                            sm.getString("propertiesRoleMappingListener.roleMappingFileFail", roleMappingFile), e2);
+                }
+            }
+
+            if (is == null) {
+                throw new IllegalStateException(
+                        sm.getString("propertiesRoleMappingListener.roleMappingFileNotFound", roleMappingFile));
+            }
+
+            Properties props = new Properties();
+
+            try (InputStream _is = is) {
+                props.load(_is);
+            } catch (IOException e) {
+                throw new IllegalStateException(
+                        sm.getString("propertiesRoleMappingListener.roleMappingFileFail", roleMappingFile), e);
+            }
+
+            int linkCount = 0;
+            for (Entry<Object, Object> prop : props.entrySet()) {
+                String role = (String) prop.getKey();
+
+                if (keyPrefix != null) {
+                    if (role.startsWith(keyPrefix)) {
+                        role = role.substring(keyPrefix.length());
+                    } else {
+                        continue;
+                    }
+                }
+
+                String link = (String) prop.getValue();
+
+                if (log.isTraceEnabled()) {
+                    log.trace(sm.getString("propertiesRoleMappingListener.linkedRole", role, link));
+                }
+                context.addRoleMapping(role, link);
+                linkCount++;
+            }
+
+            if (log.isDebugEnabled()) {
+                log.debug(sm.getString("propertiesRoleMappingListener.linkedRoleCount", linkCount));
+            }
+        }
+    }
+
+}
diff --git a/test/org/apache/catalina/core/TestPropertiesRoleMappingListener.java b/test/org/apache/catalina/core/TestPropertiesRoleMappingListener.java
new file mode 100644
index 0000000000..9ba4fa38ce
--- /dev/null
+++ b/test/org/apache/catalina/core/TestPropertiesRoleMappingListener.java
@@ -0,0 +1,168 @@
+/*
+ *  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.catalina.core;
+
+import java.io.File;
+import java.io.IOException;
+import java.nio.charset.StandardCharsets;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.catalina.Context;
+import org.apache.catalina.LifecycleException;
+import org.apache.catalina.authenticator.BasicAuthenticator;
+import org.apache.catalina.servlets.DefaultServlet;
+import org.apache.catalina.startup.TesterMapRealm;
+import org.apache.catalina.startup.Tomcat;
+import org.apache.catalina.startup.TomcatBaseTest;
+import org.apache.tomcat.util.buf.ByteChunk;
+import org.apache.tomcat.util.codec.binary.Base64;
+import org.apache.tomcat.util.descriptor.web.LoginConfig;
+import org.apache.tomcat.util.descriptor.web.SecurityCollection;
+import org.apache.tomcat.util.descriptor.web.SecurityConstraint;
+import org.junit.Assert;
+import org.junit.Test;
+
+import jakarta.servlet.http.HttpServletRequest;
+
+public class TestPropertiesRoleMappingListener extends TomcatBaseTest {
+
+    @Test(expected = NullPointerException.class)
+    public void testNullRoleMappingFile() throws Exception {
+        PropertiesRoleMappingListener listener = new PropertiesRoleMappingListener();
+        listener.setRoleMappingFile(null);
+    }
+
+    @Test(expected = IllegalArgumentException.class)
+    public void testEmptyRoleMappingFile() throws Exception {
+        PropertiesRoleMappingListener listener = new PropertiesRoleMappingListener();
+        listener.setRoleMappingFile("");
+    }
+
+    @Test(expected = LifecycleException.class)
+    public void testNotFoundRoleMappingFile() throws Exception {
+        Tomcat tomcat = getTomcatInstance();
+
+        Context ctx = tomcat.addContext("", null);
+
+        PropertiesRoleMappingListener listener = new PropertiesRoleMappingListener();
+        ctx.addLifecycleListener(listener);
+
+        try {
+            tomcat.start();
+        } finally {
+            tomcat.stop();
+        }
+    }
+
+    @Test
+    public void testFileFromServletContext() throws Exception {
+        doTest("webapp:/WEB-INF/role-mapping.properties", null);
+    }
+
+    @Test
+    public void testFileFromServletContextWithKeyPrefix() throws Exception {
+        doTest("webapp:/WEB-INF/prefixed-role-mapping.properties", "app-roles.");
+    }
+
+    @Test
+    public void testFileFromClasspath() throws Exception {
+        doTest("classpath:/com/example/role-mapping.properties", null);
+    }
+
+    @Test
+    public void testFileFromClasspathWithKeyPrefix() throws Exception {
+        doTest("classpath:/com/example/prefixed-role-mapping.properties", "app-roles.");
+    }
+
+    @Test
+    public void testFileFromFile() throws Exception {
+        File appDir = new File("test/webapp-role-mapping");
+        File file = new File(appDir, "WEB-INF/role-mapping.properties");
+        doTest(file.getAbsoluteFile().toURI().toASCIIString(), null);
+    }
+
+    @Test
+    public void testFileFromFileWithKeyPrefix() throws Exception {
+        File appDir = new File("test/webapp-role-mapping");
+        File file = new File(appDir, "WEB-INF/prefixed-role-mapping.properties");
+        doTest(file.getAbsoluteFile().toURI().toASCIIString(), "app-roles.");
+    }
+
+    private void doTest(String roleMappingFile, String keyPrefix) throws Exception {
+        Tomcat tomcat = getTomcatInstance();
+
+        File appDir = new File("test/webapp-role-mapping");
+        Context ctx = tomcat.addContext("", appDir.getAbsolutePath());
+
+        PropertiesRoleMappingListener listener = new PropertiesRoleMappingListener();
+        listener.setRoleMappingFile(roleMappingFile);
+        listener.setKeyPrefix(keyPrefix);
+        ctx.addLifecycleListener(listener);
+
+        Tomcat.addServlet(ctx, "default", new DefaultServlet());
+        ctx.addServletMappingDecoded("/", "default");
+
+        LoginConfig loginConfig  = new LoginConfig();
+        loginConfig.setAuthMethod(HttpServletRequest.BASIC_AUTH);
+        ctx.setLoginConfig(loginConfig);
+        ctx.getPipeline().addValve(new BasicAuthenticator());
+
+        TesterMapRealm realm = new TesterMapRealm();
+        realm.addUser("foo", "bar");
+        // role 'admin'
+        realm.addUserRole("foo", "de25f8f5-e534-4980-9351-e316384b1127");
+        realm.addUser("waldo", "fred");
+        // role 'user'
+        realm.addUserRole("waldo", "13f6b886-cba8-4b5b-9a1b-06a6fe533356");
+        // role 'supervisor'
+        realm.addUserRole("waldo", "45071e9a-13ef-11ee-89dc-20677cd45840");
+        ctx.setRealm(realm);
+
+        for (String role : Arrays.asList("admin", "user", "unmapped")) {
+            SecurityCollection securityCollection = new SecurityCollection();
+            securityCollection.addPattern("/" + role);
+            SecurityConstraint constraint = new SecurityConstraint();
+            constraint.addAuthRole(role);
+            constraint.addCollection(securityCollection);
+            ctx.addConstraint(constraint);
+            ctx.addSecurityRole(role);
+        }
+
+        tomcat.start();
+
+        testRequest("foo:bar", "/admin", 200);
+        testRequest("waldo:fred", "/user", 200);
+        testRequest("waldo:fred", "/unmapped", 403);
+        testRequest("bar:baz", "/user", 401);
+    }
+
+    private void testRequest(String credentials, String path, int statusCode) throws IOException {
+        ByteChunk out = new ByteChunk();
+        Map<String, List<String>> reqHead = new HashMap<>();
+        List<String> head = new ArrayList<>();
+        head.add(HttpServletRequest.BASIC_AUTH + " " +
+                Base64.encodeBase64String(credentials.getBytes(StandardCharsets.ISO_8859_1)));
+        reqHead.put("Authorization", head);
+        int rc = getUrl("http://localhost:" + getPort() + path, out, reqHead, null);
+        Assert.assertEquals(statusCode, rc);
+    }
+
+}
diff --git a/test/webapp-role-mapping/WEB-INF/classes/com/example/prefixed-role-mapping.properties b/test/webapp-role-mapping/WEB-INF/classes/com/example/prefixed-role-mapping.properties
new file mode 100644
index 0000000000..f2510d7841
--- /dev/null
+++ b/test/webapp-role-mapping/WEB-INF/classes/com/example/prefixed-role-mapping.properties
@@ -0,0 +1,2 @@
+app-roles.admin=de25f8f5-e534-4980-9351-e316384b1127
+app-roles.user=13f6b886-cba8-4b5b-9a1b-06a6fe533356
diff --git a/test/webapp-role-mapping/WEB-INF/classes/com/example/role-mapping.properties b/test/webapp-role-mapping/WEB-INF/classes/com/example/role-mapping.properties
new file mode 100644
index 0000000000..b186493cf5
--- /dev/null
+++ b/test/webapp-role-mapping/WEB-INF/classes/com/example/role-mapping.properties
@@ -0,0 +1,2 @@
+admin=de25f8f5-e534-4980-9351-e316384b1127
+user=13f6b886-cba8-4b5b-9a1b-06a6fe533356
diff --git a/test/webapp-role-mapping/WEB-INF/prefixed-role-mapping.properties b/test/webapp-role-mapping/WEB-INF/prefixed-role-mapping.properties
new file mode 100644
index 0000000000..f2510d7841
--- /dev/null
+++ b/test/webapp-role-mapping/WEB-INF/prefixed-role-mapping.properties
@@ -0,0 +1,2 @@
+app-roles.admin=de25f8f5-e534-4980-9351-e316384b1127
+app-roles.user=13f6b886-cba8-4b5b-9a1b-06a6fe533356
diff --git a/test/webapp-role-mapping/WEB-INF/role-mapping.properties b/test/webapp-role-mapping/WEB-INF/role-mapping.properties
new file mode 100644
index 0000000000..b186493cf5
--- /dev/null
+++ b/test/webapp-role-mapping/WEB-INF/role-mapping.properties
@@ -0,0 +1,2 @@
+admin=de25f8f5-e534-4980-9351-e316384b1127
+user=13f6b886-cba8-4b5b-9a1b-06a6fe533356
diff --git a/test/webapp-role-mapping/admin b/test/webapp-role-mapping/admin
new file mode 100644
index 0000000000..7fbe952b76
--- /dev/null
+++ b/test/webapp-role-mapping/admin
@@ -0,0 +1 @@
+admin
diff --git a/test/webapp-role-mapping/unmapped b/test/webapp-role-mapping/unmapped
new file mode 100644
index 0000000000..80a617a00e
--- /dev/null
+++ b/test/webapp-role-mapping/unmapped
@@ -0,0 +1 @@
+unmapped
diff --git a/test/webapp-role-mapping/user b/test/webapp-role-mapping/user
new file mode 100644
index 0000000000..4eb8387fed
--- /dev/null
+++ b/test/webapp-role-mapping/user
@@ -0,0 +1 @@
+user
diff --git a/webapps/docs/changelog.xml b/webapps/docs/changelog.xml
index dd909b6b1a..dbe8c5bc73 100644
--- a/webapps/docs/changelog.xml
+++ b/webapps/docs/changelog.xml
@@ -113,6 +113,12 @@
         a listener which creates context naming information environment entries.
         (michaelo)
       </add>
+      <add>
+        <bug>66665</bug>: Add
+        <code>org.apache.catalina.core.PropertiesRoleMappingListener</code>,
+        a listener which populates the context's role mapping from a properties
+        file. (michaelo)
+      </add>
       <fix>
         Fix an edge case where intra-web application symlinks would be followed
         if the web applications were deliberately crafted to allow it even when
diff --git a/webapps/docs/config/listeners.xml b/webapps/docs/config/listeners.xml
index 9f0dd02eae..3e84cea656 100644
--- a/webapps/docs/config/listeners.xml
+++ b/webapps/docs/config/listeners.xml
@@ -295,6 +295,37 @@
 
   </subsection>
 
+  <subsection name="Properties Role Mapping Listener - org.apache.catalina.core.PropertiesRoleMappingListener">
+
+    <p>The <strong>Properties Role Mapping Listener</strong> populates the context's role mapping
+    from a properties file. The keys represent application roles (e.g., admin, user, uservisor,
+    etc.) while the values represent technical roles (e.g., DNs, SIDs, UUIDs, etc.). A key can
+    also be prefixed if, e.g., the properties file contains generic application configuration
+    as well: <code>app-roles.</code>.</p>
+
+    <p>This listener must only be nested within
+    <a href="context.html">Context</a> elements.</p>
+
+    <p>The following additional attributes are supported by the
+    <strong>Properties Role Mapping Listener</strong>:</p>
+
+    <attributes>
+
+      <attribute name="roleMappingFile" required="false">
+        <p>The path to the role mapping properties file. You can use protocol <code>webapp:</code>
+        and whatever <code>ConfigFileLoader</code> supports.</p>
+        <p>The default value is <code>webapp:/WEB-INF/role-mapping.properties</code>.</p>
+      </attribute>
+
+      <attribute name="keyPrefix" required="false">
+        <p>The prefix to filter from property keys. All other keys will be ignored which do
+        not have the prefix.</p>
+      </attribute>
+
+    </attributes>
+
+  </subsection>
+
   <subsection name="Security Lifecycle Listener - org.apache.catalina.security.SecurityListener">
 
     <p>The <strong>Security Lifecycle Listener</strong> performs a number of


---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@tomcat.apache.org
For additional commands, e-mail: dev-help@tomcat.apache.org