SLING-11128 : Escape tenant id
diff --git a/.gitignore b/.gitignore
index 5b783ed..38f5ca4 100644
--- a/.gitignore
+++ b/.gitignore
@@ -14,4 +14,5 @@
 .vlt
 .DS_Store
 jcr.log
+.vscode
 atlassian-ide-plugin.xml
diff --git a/pom.xml b/pom.xml
index 0181a30..b1779fe 100644
--- a/pom.xml
+++ b/pom.xml
@@ -21,14 +21,13 @@
     <modelVersion>4.0.0</modelVersion>
     <parent>
         <groupId>org.apache.sling</groupId>
-        <artifactId>sling</artifactId>
-        <version>34</version>
+        <artifactId>sling-bundle-parent</artifactId>
+        <version>46</version>
         <relativePath />
     </parent>
 
     <artifactId>org.apache.sling.tenant</artifactId>
     <version>1.1.5-SNAPSHOT</version>
-    <packaging>bundle</packaging>
 
     <name>Apache Sling Tenant</name>
     <description>
@@ -42,11 +41,15 @@
       <tag>HEAD</tag>
   </scm>
 
+    <properties>
+        <project.build.outputTimestamp>1</project.build.outputTimestamp>
+    </properties>
+
     <build>
         <plugins>
             <plugin>
                 <groupId>org.apache.sling</groupId>
-                <artifactId>maven-sling-plugin</artifactId>
+                <artifactId>sling-maven-plugin</artifactId>
                 <executions>
                     <execution>
                         <id>generate-adapter-metadata</id>
@@ -57,11 +60,6 @@
                     </execution>
                 </executions>
             </plugin>
-            <plugin>
-                <groupId>org.apache.felix</groupId>
-                <artifactId>maven-bundle-plugin</artifactId>
-                <extensions>true</extensions>
-            </plugin>
         </plugins>
     </build>
     
@@ -69,7 +67,13 @@
         <dependency>
             <groupId>org.apache.sling</groupId>
             <artifactId>org.apache.sling.api</artifactId>
-            <version>2.5.0</version>
+            <version>2.22.0</version>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.sling</groupId>
+            <artifactId>org.apache.sling.xss</artifactId>
+            <version>2.2.14</version>
             <scope>provided</scope>
         </dependency>
         <dependency>
@@ -81,11 +85,13 @@
         <dependency>
             <groupId>org.osgi</groupId>
             <artifactId>org.osgi.service.component.annotations</artifactId>
-            <scope>provided</scope>
+            <version>1.4.0</version>
+	    <scope>provided</scope>
         </dependency>
         <dependency>
             <groupId>org.osgi</groupId>
-            <artifactId>org.osgi.service.metatype.annotations</artifactId>
+	    <artifactId>org.osgi.service.metatype.annotations</artifactId>
+            <version>1.4.0</version>
             <scope>provided</scope>
         </dependency>
         <dependency>
@@ -101,7 +107,8 @@
         <dependency>
             <groupId>org.osgi</groupId>
             <artifactId>osgi.core</artifactId>
-            <scope>provided</scope>
+            <version>6.0.0</version>
+	    <scope>provided</scope>
         </dependency>
         <dependency>
             <groupId>org.slf4j</groupId>
@@ -109,7 +116,8 @@
         </dependency>
         <dependency>
             <groupId>org.osgi</groupId>
-            <artifactId>org.osgi.annotation.versioning</artifactId>
+	    <artifactId>org.osgi.annotation.versioning</artifactId>
+            <version>1.1.0</version>
             <scope>provided</scope>
         </dependency>
         <!-- Webconsole -->
diff --git a/src/main/java/org/apache/sling/tenant/internal/TenantProviderImpl.java b/src/main/java/org/apache/sling/tenant/internal/TenantProviderImpl.java
index ab4e12a..bcd021c 100644
--- a/src/main/java/org/apache/sling/tenant/internal/TenantProviderImpl.java
+++ b/src/main/java/org/apache/sling/tenant/internal/TenantProviderImpl.java
@@ -43,6 +43,7 @@
 import org.apache.sling.tenant.internal.console.WebConsolePlugin;

 import org.apache.sling.tenant.spi.TenantCustomizer;

 import org.apache.sling.tenant.spi.TenantManagerHook;

+import org.apache.sling.xss.XSSAPI;

 import org.osgi.framework.BundleContext;

 import org.osgi.framework.Constants;

 import org.osgi.framework.Filter;

@@ -54,6 +55,7 @@
 import org.osgi.service.component.annotations.Reference;

 import org.osgi.service.component.annotations.ReferenceCardinality;

 import org.osgi.service.component.annotations.ReferencePolicy;

+import org.osgi.service.component.annotations.ReferencePolicyOption;

 import org.osgi.service.metatype.annotations.AttributeDefinition;

 import org.osgi.service.metatype.annotations.Designate;

 import org.osgi.service.metatype.annotations.ObjectClassDefinition;

@@ -119,18 +121,21 @@
 

     private String tenantRootPath = RESOURCE_TENANT_ROOT;

 

-    @Reference

-    private ResourceResolverFactory factory;

+    private final ResourceResolverFactory factory;

 

     private TenantAdapterFactory adapterFactory;

 

     private WebConsolePlugin plugin;

 

     @Activate

-    protected void activate(final BundleContext bundleContext, final Configuration configuration) {

+    public TenantProviderImpl(final BundleContext bundleContext, 

+        final @Reference(policyOption = ReferencePolicyOption.GREEDY) XSSAPI xss,

+        final @Reference ResourceResolverFactory resourceResolverFactory,

+        final Configuration configuration) {

+        this.factory = resourceResolverFactory;

         this.tenantRootPath = configuration.tenant_root();

         this.adapterFactory = new TenantAdapterFactory(bundleContext, this, configuration.tenant_path_matcher());

-        this.plugin = new WebConsolePlugin(bundleContext, this);

+        this.plugin = new WebConsolePlugin(bundleContext, this, xss);

     }

 

     @Deactivate

@@ -347,7 +352,6 @@
         });

     }

 

-    @SuppressWarnings("serial")

     private Resource createTenantResource(final ResourceResolver resolver, final String tenantId,

             final Map<String, Object> properties) throws PersistenceException {

 

@@ -384,7 +388,17 @@
     }

 

     private Resource getTenantResource(final ResourceResolver resolver, final String tenantId) {

-        return resolver.getResource(tenantRootPath + "/" + tenantId);

+        Resource rsrc = resolver.getResource(tenantRootPath + "/" + tenantId);

+        if ( rsrc == null ) {

+            // this is a hack for special characters that otherwise would need escaping before getResource() is called

+            for(final Resource r : resolver.getResource(tenantRootPath).getChildren()) {

+                if ( tenantId.equals(r.getName())) {

+                    rsrc = r;

+                    break;

+                }

+            }

+        }

+        return rsrc;

     }

 

     private void customizeTenant(final Resource tenantRes, final Tenant tenant, boolean isCreate) {

diff --git a/src/main/java/org/apache/sling/tenant/internal/console/WebConsolePlugin.java b/src/main/java/org/apache/sling/tenant/internal/console/WebConsolePlugin.java
index fb7871e..d028b15 100644
--- a/src/main/java/org/apache/sling/tenant/internal/console/WebConsolePlugin.java
+++ b/src/main/java/org/apache/sling/tenant/internal/console/WebConsolePlugin.java
@@ -20,6 +20,7 @@
 
 import java.io.IOException;
 import java.io.PrintWriter;
+import java.net.URLEncoder;
 import java.util.Dictionary;
 import java.util.HashMap;
 import java.util.Hashtable;
@@ -32,9 +33,11 @@
 
 import org.apache.sling.tenant.Tenant;
 import org.apache.sling.tenant.internal.TenantProviderImpl;
+import org.apache.sling.xss.XSSAPI;
 import org.osgi.framework.BundleContext;
 import org.osgi.framework.Constants;
 import org.osgi.framework.ServiceRegistration;
+import org.slf4j.LoggerFactory;
 
 /**
  * This is a webconsole plugin displaying the active queues, some statistics and
@@ -61,17 +64,11 @@
 
     private TenantProviderImpl tenantProvider;
 
-    private final ServiceRegistration<?> service;
+    private final ServiceRegistration<Servlet> service;
 
-    /** Escape the output for html. */
-    private String escape(final String text) {
-        if (text == null) {
-            return "";
-        }
-        return text.replace("&", "&amp;").replace("<", "&lt;").replace(">", "&gt;");
-    }
+    private final XSSAPI xss;
 
-    public WebConsolePlugin(final BundleContext bundleContext, final TenantProviderImpl tenantProvider) {
+    public WebConsolePlugin(final BundleContext bundleContext, final TenantProviderImpl tenantProvider, final XSSAPI xss) {
         this.tenantProvider = tenantProvider;
 
         Dictionary<String, Object> props = new Hashtable<String, Object>();
@@ -79,16 +76,13 @@
         props.put("felix.webconsole.label", LABEL);
         props.put("felix.webconsole.title", TITLE);
         props.put("felix.webconsole.category", CATEGORY);
-        // props.put("felix.webconsole.configprinter.modes", new String[]{"zip",
-        // "txt"});
 
-        this.service = bundleContext.registerService(Servlet.class.getCanonicalName(), this, props);
+        this.xss = xss;
+        this.service = bundleContext.registerService(Servlet.class, this, props);
     }
 
     public void dispose() {
-        if (this.service != null) {
-            this.service.unregister();
-        }
+        this.service.unregister();
     }
 
     @Override
@@ -113,16 +107,15 @@
         if (msg == null) {
             redirectTo = path;
         } else {
-            redirectTo = path + "?message=" + msg;
+            redirectTo = path.concat("?message=").concat(URLEncoder.encode(msg, "UTF-8"));
         }
 
-        resp.sendRedirect(redirectTo);
+        resp.sendRedirect(resp.encodeRedirectURL(redirectTo));
     }
 
-    private void removeTenant(HttpServletRequest request) {
+    private void removeTenant(final HttpServletRequest request) {
         final String tenantId = request.getParameter(REQ_PRM_TENANT_ID);
         final Tenant tenant = this.tenantProvider.getTenant(tenantId);
-
         if (tenant != null) {
             this.tenantProvider.remove(tenant);
         }
@@ -130,10 +123,9 @@
 
     private void printForm(final PrintWriter pw, final Tenant t, final String buttonLabel, final String cmd) {
         pw.printf("<button class='ui-state-default ui-corner-all' onclick='javascript:cmdsubmit(\"%s\", \"%s\");'>"
-            + "%s</button>", cmd, (t != null ? t.getId() : ""), buttonLabel);
+            + "%s</button>", cmd, (t != null ? xss.encodeForJSString(t.getId()) : ""), xss.encodeForHTML(buttonLabel));
     }
 
-    @SuppressWarnings("serial")
     private Tenant createTenant(HttpServletRequest request) {
         final String tenantName = request.getParameter(REQ_PRM_TENANT_NAME);
         final String tenantId = request.getParameter(REQ_PRM_TENANT_ID);
@@ -189,19 +181,19 @@
                 pw.printf("<p class='statline ui-state-highlight'>Registered Tenants</p>");
             }
             pw.println("<div class='ui-widget-header ui-corner-top buttonGroup'>");
-            pw.printf("<span style='float: left; margin-left: 1em'>Tenant : %s </span>", escape(tenant.getName()));
+            pw.printf("<span style='float: left; margin-left: 1em'>Tenant : %s </span>", xss.encodeForHTML(tenant.getName()));
             this.printForm(pw, tenant, "Remove", "remove");
             pw.println("</div>");
             pw.println("<table class='nicetable'><tbody>");
 
-            pw.printf("<tr><td style='width: 30%%;'>Identifier</td><td>%s</td></tr>", escape(tenant.getId()));
-            pw.printf("<tr><td style='width: 30%%;'>Name</td><td>%s</td></tr>", escape(tenant.getName()));
-            pw.printf("<tr><td style='width: 30%%;'>Description</td><td>%s</td></tr>", escape(tenant.getDescription()));
+            pw.printf("<tr><td style='width: 30%%;'>Identifier</td><td>%s</td></tr>", xss.encodeForHTML(tenant.getId()));
+            pw.printf("<tr><td style='width: 30%%;'>Name</td><td>%s</td></tr>", xss.encodeForHTML(tenant.getName()));
+            pw.printf("<tr><td style='width: 30%%;'>Description</td><td>%s</td></tr>", xss.encodeForHTML(tenant.getDescription()));
             pw.println("</tbody></table>");
         }
         // no existing tenants
         if (count == 0) {
-            pw.printf("<p class='statline ui-state-highlight'>There are not registered tenants</p>");
+            pw.printf("<p class='statline ui-state-highlight'>There are no registered tenants</p>");
         }
     }
 }
diff --git a/src/test/java/org/apache/sling/tenant/internal/TenantProviderImplTest.java b/src/test/java/org/apache/sling/tenant/internal/TenantProviderImplTest.java
index 1628fe5..0f47ec8 100644
--- a/src/test/java/org/apache/sling/tenant/internal/TenantProviderImplTest.java
+++ b/src/test/java/org/apache/sling/tenant/internal/TenantProviderImplTest.java
@@ -35,13 +35,11 @@
 
     @Test
     public void testListTenantsWithoutTenantRoot() throws Exception {
-        TenantProviderImpl provider = new TenantProviderImpl();
         final ResourceResolverFactory rrf = Mockito.mock(ResourceResolverFactory.class);
         final BundleContext context = Mockito.mock(BundleContext.class);
         final ResourceResolver rr = Mockito.mock(ResourceResolver.class);
         Mockito.when(rrf.getServiceResourceResolver(
                 Mockito.anyMapOf(String.class, Object.class))).thenReturn(rr);
-        set(provider, "factory", rrf);
         TenantProviderImpl.Configuration configuration = new TenantProviderImpl.Configuration() {
             @Override
             public Class<? extends Annotation> annotationType() {
@@ -56,16 +54,9 @@
                 return new String[] {};
             }
         };
-        provider.activate(context, configuration);
+        TenantProviderImpl provider = new TenantProviderImpl(context, null, rrf, configuration);
         Iterator<Tenant> tenants = provider.getTenants();
         TestCase.assertNotNull(tenants);
         TestCase.assertFalse(tenants.hasNext());
     }
-
-    private static void set(Object o, String name, Object value) throws Exception {
-        final Field f = o.getClass().getDeclaredField(name);
-        f.setAccessible(true);
-        f.set(o, value);
-    }
-
 }