Fixing an issue where it failed to start without xss compat, wasn't serving the js...
authorDan Klco <daniel.klco@gmail.com>
Fri, 12 Jan 2018 22:44:11 +0000 (17:44 -0500)
committerDan Klco <daniel.klco@gmail.com>
Fri, 12 Jan 2018 22:48:09 +0000 (17:48 -0500)
pom.xml
src/main/java/org/apache/sling/serviceuser/webconsole/impl/ServiceUserWebConsolePlugin.java

diff --git a/pom.xml b/pom.xml
index aa6e3d4..97e097e 100644 (file)
--- a/pom.xml
+++ b/pom.xml
 <?xml version="1.0" encoding="ISO-8859-1"?>
-<!--
-    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.
--->
-<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+<!-- 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. -->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+       xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
 
-    <modelVersion>4.0.0</modelVersion>
-    <parent>
-        <groupId>org.apache.sling</groupId>
-        <artifactId>sling</artifactId>
-        <version>30</version>
-        <relativePath />
-    </parent>
+       <modelVersion>4.0.0</modelVersion>
+       <parent>
+               <groupId>org.apache.sling</groupId>
+               <artifactId>sling</artifactId>
+               <version>30</version>
+               <relativePath />
+       </parent>
 
-    <artifactId>org.apache.sling.serviceuser.webconsole</artifactId>
-    <packaging>bundle</packaging>
-    <version>1.0.1-SNAPSHOT</version>
+       <artifactId>org.apache.sling.serviceuser.webconsole</artifactId>
+       <packaging>bundle</packaging>
+       <version>1.0.0-SNAPSHOT</version>
 
-    <name>Apache Sling Service User Web Console</name>
-    <description>
+       <name>Apache Sling Service User Web Console</name>
+       <description>
         Provides an OSGi Web Console for creating, updating and viewing Service Users.
     </description>
 
-    <scm>
-        <connection>scm:git:https://gitbox.apache.org/repos/asf/sling-org-apache-sling-serviceuser-webconsole.git</connection>
-        <developerConnection>scm:git:https://gitbox.apache.org/repos/asf/sling-org-apache-sling-serviceuser-webconsole.git</developerConnection>
-        <url>https://gitbox.apache.org/repos/asf?p=sling-org-apache-sling-serviceuser-webconsole.git</url>
-      <tag>HEAD</tag>
-  </scm>
+       <scm>
+               <connection>scm:git:https://gitbox.apache.org/repos/asf/sling-org-apache-sling-serviceuser-webconsole.git</connection>
+               <developerConnection>scm:git:https://gitbox.apache.org/repos/asf/sling-org-apache-sling-serviceuser-webconsole.git</developerConnection>
+               <url>https://gitbox.apache.org/repos/asf?p=sling-org-apache-sling-serviceuser-webconsole.git</url>
+               <tag>HEAD</tag>
+       </scm>
 
-    <build>
-        <plugins>
-            <plugin>
-                <groupId>org.apache.felix</groupId>
-                <artifactId>maven-bundle-plugin</artifactId>
-                <extensions>true</extensions>
-                <configuration>
-                </configuration>
-            </plugin>
-            <plugin>
-                <groupId>org.apache.maven.plugins</groupId>
-                <artifactId>maven-javadoc-plugin</artifactId>
-                <configuration>
-                    <excludePackageNames>
-                        org.apache.sling.serviceuser.console.impl
-                    </excludePackageNames>
-                </configuration>
-            </plugin>
-        </plugins>
-    </build>
-    <dependencies>
-        <dependency>
-            <groupId>org.apache.sling</groupId>
-            <artifactId>org.apache.sling.api</artifactId>
-            <version>2.5.0</version>
-            <scope>provided</scope>
-        </dependency>
-        <dependency>
-            <groupId>org.apache.sling</groupId>
+       <build>
+               <plugins>
+                       <plugin>
+                               <groupId>org.apache.felix</groupId>
+                               <artifactId>maven-bundle-plugin</artifactId>
+                               <extensions>true</extensions>
+                               <configuration>
+                                       <instructions>
+                                               <Import-Package>
+                                                       <!-- Support XSS API 1.x and 2.x - we use only classes from the API 
+                                                               with same signature in both versions -->
+                                                       org.apache.sling.xss;version="[1.0.0,3)",
+                                                       *
+                                               </Import-Package>
+                                       </instructions>
+                               </configuration>
+                       </plugin>
+                       <plugin>
+                               <groupId>org.apache.maven.plugins</groupId>
+                               <artifactId>maven-javadoc-plugin</artifactId>
+                               <configuration>
+                                       <excludePackageNames>
+                                               org.apache.sling.serviceuser.console.impl
+                                       </excludePackageNames>
+                               </configuration>
+                       </plugin>
+               </plugins>
+       </build>
+       <dependencies>
+               <dependency>
+                       <groupId>org.apache.sling</groupId>
+                       <artifactId>org.apache.sling.api</artifactId>
+                       <version>2.5.0</version>
+                       <scope>provided</scope>
+               </dependency>
+               <dependency>
+                       <groupId>org.apache.sling</groupId>
                        <artifactId>org.apache.sling.serviceusermapper</artifactId>
                        <version>1.4.0</version>
-            <scope>provided</scope>
-        </dependency>
-        
-        <!-- JCR Specific items -->
-        <dependency>
-            <groupId>org.apache.jackrabbit</groupId>
-            <artifactId>jackrabbit-api</artifactId>
-            <version>2.10.6</version>
-            <scope>provided</scope>
-        </dependency>
-        <dependency>
-            <groupId>javax.jcr</groupId>
-            <artifactId>jcr</artifactId>
-        </dependency>
-        <dependency>
-            <groupId>org.apache.sling</groupId>
-            <artifactId>org.apache.sling.jcr.base</artifactId>
-            <version>2.1.0</version>
-            <scope>provided</scope>
-        </dependency>
-  
-        <dependency>
-            <groupId>org.slf4j</groupId>
-            <artifactId>slf4j-api</artifactId>
-        </dependency>
+                       <scope>provided</scope>
+               </dependency>
 
-        <dependency>
-            <groupId>org.osgi</groupId>
-            <artifactId>osgi.core</artifactId>
-        </dependency>
-        <dependency>
-               <groupId>org.osgi</groupId>
-               <artifactId>org.osgi.compendium</artifactId>
-               <version>4.2.0</version>
-               <scope>provided</scope>
-        </dependency>
-        <dependency>
-            <groupId>javax.servlet</groupId>
-            <artifactId>javax.servlet-api</artifactId>
-        </dependency>
-        <dependency>
-            <groupId>org.apache.commons</groupId>
-            <artifactId>commons-lang3</artifactId>
-            <version>3.3.2</version>
-            <scope>provided</scope>
-        </dependency>
-        
-        <!-- Webconsole Dependencies -->
-        <dependency>
-            <groupId>org.apache.felix</groupId>
-            <artifactId>org.apache.felix.webconsole</artifactId>
-            <version>4.2.0</version>
-            <scope>provided</scope>
-        </dependency>
-        <dependency>
-            <groupId>org.apache.sling</groupId>
-            <artifactId>org.apache.sling.xss</artifactId>
-            <version>1.0.0</version>
-            <scope>provided</scope>
-        </dependency>
+               <!-- JCR Specific items -->
+               <dependency>
+                       <groupId>org.apache.jackrabbit</groupId>
+                       <artifactId>jackrabbit-api</artifactId>
+                       <version>2.10.6</version>
+                       <scope>provided</scope>
+               </dependency>
+               <dependency>
+                       <groupId>javax.jcr</groupId>
+                       <artifactId>jcr</artifactId>
+               </dependency>
+               <dependency>
+                       <groupId>org.apache.sling</groupId>
+                       <artifactId>org.apache.sling.jcr.base</artifactId>
+                       <version>2.1.0</version>
+                       <scope>provided</scope>
+               </dependency>
+               <dependency>
+                       <groupId>org.apache.jackrabbit</groupId>
+                       <artifactId>jackrabbit-jcr-commons</artifactId>
+                       <version>2.0.0</version>
+                       <scope>provided</scope>
+               </dependency>
 
-        <!-- Testing -->
-        <dependency>
-            <groupId>junit</groupId>
-            <artifactId>junit</artifactId>
-        </dependency>
-        <dependency>
-            <groupId>org.slf4j</groupId>
-            <artifactId>slf4j-simple</artifactId>
-        </dependency>
-        <dependency>
-            <groupId>org.mockito</groupId>
-            <artifactId>mockito-all</artifactId>
-            <version>1.9.5</version>
-            <scope>test</scope>
-        </dependency>
-    </dependencies>
+
+               <dependency>
+                       <groupId>org.slf4j</groupId>
+                       <artifactId>slf4j-api</artifactId>
+               </dependency>
+
+               <dependency>
+                       <groupId>org.osgi</groupId>
+                       <artifactId>osgi.core</artifactId>
+               </dependency>
+               <dependency>
+                       <groupId>org.osgi</groupId>
+                       <artifactId>org.osgi.compendium</artifactId>
+                       <version>4.2.0</version>
+                       <scope>provided</scope>
+               </dependency>
+               <dependency>
+                       <groupId>javax.servlet</groupId>
+                       <artifactId>javax.servlet-api</artifactId>
+               </dependency>
+               <dependency>
+                       <groupId>org.apache.commons</groupId>
+                       <artifactId>commons-lang3</artifactId>
+                       <version>3.3.2</version>
+                       <scope>provided</scope>
+               </dependency>
+
+               <!-- Webconsole Dependencies -->
+               <dependency>
+                       <groupId>org.apache.felix</groupId>
+                       <artifactId>org.apache.felix.webconsole</artifactId>
+                       <version>4.2.0</version>
+                       <scope>provided</scope>
+               </dependency>
+               <dependency>
+                       <groupId>org.apache.sling</groupId>
+                       <artifactId>org.apache.sling.xss</artifactId>
+                       <version>1.0.0</version>
+                       <scope>provided</scope>
+               </dependency>
+
+               <!-- Testing -->
+               <dependency>
+                       <groupId>junit</groupId>
+                       <artifactId>junit</artifactId>
+               </dependency>
+               <dependency>
+                       <groupId>org.slf4j</groupId>
+                       <artifactId>slf4j-simple</artifactId>
+               </dependency>
+               <dependency>
+                       <groupId>org.mockito</groupId>
+                       <artifactId>mockito-all</artifactId>
+                       <version>1.9.5</version>
+                       <scope>test</scope>
+               </dependency>
+       </dependencies>
 </project>
index fd9ab5f..5d952fa 100644 (file)
@@ -21,6 +21,7 @@ package org.apache.sling.serviceuser.webconsole.impl;
 import java.io.IOException;
 import java.io.PrintWriter;
 import java.lang.reflect.Array;
+import java.net.URL;
 import java.net.URLEncoder;
 import java.security.Principal;
 import java.util.ArrayList;
@@ -35,7 +36,6 @@ import java.util.Map;
 import java.util.Map.Entry;
 
 import javax.jcr.AccessDeniedException;
-import javax.jcr.Property;
 import javax.jcr.RepositoryException;
 import javax.jcr.Session;
 import javax.jcr.UnsupportedRepositoryOperationException;
@@ -56,17 +56,20 @@ import org.apache.commons.lang3.ObjectUtils;
 import org.apache.commons.lang3.StringUtils;
 import org.apache.commons.lang3.tuple.ImmutablePair;
 import org.apache.commons.lang3.tuple.Pair;
-import org.apache.felix.webconsole.SimpleWebConsolePlugin;
+import org.apache.felix.webconsole.AbstractWebConsolePlugin;
 import org.apache.felix.webconsole.WebConsoleConstants;
 import org.apache.felix.webconsole.WebConsoleUtil;
+import org.apache.jackrabbit.JcrConstants;
 import org.apache.jackrabbit.api.security.principal.PrincipalManager;
 import org.apache.jackrabbit.api.security.user.Authorizable;
 import org.apache.jackrabbit.api.security.user.User;
 import org.apache.jackrabbit.api.security.user.UserManager;
+import org.apache.sling.api.resource.LoginException;
 import org.apache.sling.api.resource.ModifiableValueMap;
 import org.apache.sling.api.resource.PersistenceException;
 import org.apache.sling.api.resource.Resource;
 import org.apache.sling.api.resource.ResourceResolver;
+import org.apache.sling.api.resource.ResourceResolverFactory;
 import org.apache.sling.api.resource.ResourceUtil;
 import org.apache.sling.api.resource.ValueMap;
 import org.apache.sling.jcr.base.util.AccessControlUtil;
@@ -93,11 +96,7 @@ import org.slf4j.LoggerFactory;
                WebConsoleConstants.PLUGIN_TITLE + "=" + ServiceUserWebConsolePlugin.TITLE,
                WebConsoleConstants.PLUGIN_CATEGORY + "=Sling" })
 @SuppressWarnings("serial")
-public class ServiceUserWebConsolePlugin extends SimpleWebConsolePlugin {
-
-       public ServiceUserWebConsolePlugin() {
-               super(LABEL, TITLE, "Sling", new String[0]);
-       }
+public class ServiceUserWebConsolePlugin extends AbstractWebConsolePlugin {
 
        public static final String COMPONENT_NAME = "org.apache.sling.serviceusermapping.impl.ServiceUserMapperImpl.amended";
        public static final String LABEL = "serviceusers";
@@ -119,6 +118,9 @@ public class ServiceUserWebConsolePlugin extends SimpleWebConsolePlugin {
        @Reference(policyOption = ReferencePolicyOption.GREEDY)
        private XSSAPI xss;
 
+       @Reference(policyOption = ReferencePolicyOption.GREEDY)
+       private ResourceResolverFactory resolverFactory;
+
        @Reference
        private ServiceUserMapper mapper;
 
@@ -137,11 +139,11 @@ public class ServiceUserWebConsolePlugin extends SimpleWebConsolePlugin {
                                config = configs.next();
                                log.debug("Using existing configuration {}", config);
                        } else {
-                               String path = appPath + "/config/" + COMPONENT_NAME + "-" + appPath.substring(appPath.lastIndexOf('/'));
+                               String path = appPath + "/config/" + COMPONENT_NAME + "-" + appPath.substring(appPath.lastIndexOf('/') + 1);
                                log.debug("Creating new configuration {}", path);
                                config = ResourceUtil.getOrCreateResource(resolver, path, new HashMap<String, Object>() {
                                        {
-                                               put(Property.JCR_PRIMARY_TYPE, "sling:OsgiConfig");
+                                               put(JcrConstants.JCR_PRIMARYTYPE, "sling:OsgiConfig");
                                        }
                                }, NodeType.NT_FOLDER, false);
                                dirty = true;
@@ -223,102 +225,6 @@ public class ServiceUserWebConsolePlugin extends SimpleWebConsolePlugin {
 
        }
 
-       private boolean updatePrivileges(HttpServletRequest request, ResourceResolver resolver) {
-
-               List<Pair<String, String>> privileges = this.getPrivileges(request);
-               String name = getParameter(request, PN_NAME, "");
-
-               List<String> currentPolicies = new ArrayList<String>();
-               findACLs(resolver, name, currentPolicies);
-               for (int i = 0; i < currentPolicies.size(); i++) {
-                       String path = StringUtils.substringBefore(currentPolicies.get(i), "/rep:policy");
-                       currentPolicies.set(i, StringUtils.isNotBlank(path) ? path : "/");
-               }
-               log.debug("Loaded current policy paths: {}", currentPolicies);
-
-               Map<String, List<String>> toSet = new HashMap<String, List<String>>();
-               for (Pair<String, String> privilege : privileges) {
-                       if (!toSet.containsKey(privilege.getKey())) {
-                               toSet.put(privilege.getKey(), new ArrayList<String>());
-                       }
-                       toSet.get(privilege.getKey()).add(privilege.getValue());
-               }
-               log.debug("Loaded updated policy paths: {}", currentPolicies);
-
-               String lastEntry = null;
-
-               try {
-
-                       Session session = resolver.adaptTo(Session.class);
-                       AccessControlManager accessManager = session.getAccessControlManager();
-                       PrincipalManager principalManager = AccessControlUtil.getPrincipalManager(session);
-
-                       for (Entry<String, List<String>> pol : toSet.entrySet()) {
-                               lastEntry = pol.getKey();
-                               currentPolicies.remove(pol.getKey());
-                               log.debug("Updating policies for {}", pol.getKey());
-
-                               AccessControlPolicy[] policies = accessManager.getPolicies(pol.getKey());
-                               List<String> toRemove = new ArrayList<String>();
-                               for (AccessControlPolicy p : policies) {
-                                       if (p instanceof AccessControlList) {
-                                               AccessControlList policy = (AccessControlList) p;
-                                               for (AccessControlEntry entry : policy.getAccessControlEntries()) {
-                                                       Principal prin = entry.getPrincipal();
-                                                       if (prin.getName().equals(name)) {
-                                                               for (Privilege privilege : entry.getPrivileges()) {
-                                                                       if (!pol.getValue().contains(privilege.getName())) {
-                                                                               log.debug("Removing privilege {}", privilege);
-                                                                               toRemove.add(privilege.getName());
-                                                                       }
-                                                               }
-                                                       }
-                                               }
-                                       }
-                               }
-                               Principal principal = principalManager.getPrincipal(name);
-                               AccessControlUtil.replaceAccessControlEntry(session, pol.getKey(), principal,
-                                               pol.getValue().toArray(new String[pol.getValue().size()]), new String[0],
-                                               toRemove.toArray(new String[toRemove.size()]), null);
-                       }
-                       session.save();
-
-                       for (String oldPolicy : currentPolicies) {
-                               boolean removed = false;
-                               log.debug("Removing policy for {}", oldPolicy);
-                               AccessControlPolicy[] policies = accessManager.getPolicies(oldPolicy);
-                               AccessControlEntry toRemove = null;
-                               for (AccessControlPolicy p : policies) {
-                                       if (p instanceof AccessControlList) {
-                                               AccessControlList policy = (AccessControlList) p;
-                                               for (AccessControlEntry entry : policy.getAccessControlEntries()) {
-                                                       Principal prin = entry.getPrincipal();
-                                                       if (prin.getName().equals(name)) {
-                                                               toRemove = entry;
-                                                               break;
-                                                       }
-                                               }
-                                               if (toRemove != null) {
-                                                       removed = true;
-                                                       policy.removeAccessControlEntry(toRemove);
-                                                       accessManager.setPolicy(oldPolicy, policy);
-                                                       session.save();
-                                                       log.debug("Removed access control entry {}", toRemove);
-                                               }
-                                       }
-                               }
-                               if (!removed) {
-                                       log.warn("No policy found for {}", oldPolicy);
-                               }
-                       }
-               } catch (RepositoryException e) {
-                       log.error("Exception updating principals with {}, failed on {}", toSet, lastEntry, e);
-                       return false;
-               }
-
-               return true;
-       }
-
        private List<String> extractPrincipals(Mapping mapping) {
                List<String> principals = new ArrayList<String>();
                String userName = mapping.map(mapping.getServiceName(), mapping.getSubServiceName());
@@ -404,6 +310,11 @@ public class ServiceUserWebConsolePlugin extends SimpleWebConsolePlugin {
                return bundles;
        }
 
+       @Override
+       public String getLabel() {
+               return LABEL;
+       }
+
        private Resource getOrCreateServiceUser(HttpServletRequest request, ResourceResolver resolver) {
 
                final String name = getParameter(request, PN_NAME, "");
@@ -450,12 +361,86 @@ public class ServiceUserWebConsolePlugin extends SimpleWebConsolePlugin {
                return defaultValue;
        }
 
+       private List<Pair<String, String>> getPrivileges(HttpServletRequest request) {
+               List<Pair<String, String>> privileges = new ArrayList<Pair<String, String>>();
+               List<String> params = Collections.list(request.getParameterNames());
+
+               for (String param : params) {
+                       if (param.startsWith("acl-path-")) {
+                               String path = request.getParameter(param);
+                               String privilege = request.getParameter(param.replace("-path-", "-privilege-"));
+                               if (StringUtils.isNotBlank(path) && StringUtils.isNotBlank(privilege)) {
+                                       privileges.add(new ImmutablePair<String, String>(path, privilege));
+                               } else {
+                                       log.warn("Unable to load ACL due to missing value {}={}", path, privilege);
+                               }
+                       }
+               }
+
+               return privileges;
+       }
+
+       @SuppressWarnings("deprecation")
        private ResourceResolver getResourceResolver(HttpServletRequest request) {
-               ResourceResolver resolver = (ResourceResolver) request
-                               .getAttribute("org.apache.sling.auth.core.ResourceResolver");
+               ResourceResolver resolver = null;
+               try {
+                       resolver = (ResourceResolver) request.getAttribute("org.apache.sling.auth.core.ResourceResolver");
+                       if (resolver == null) {
+                               log.warn("Resource resolver not available in request, falling back to adminstrative resource resolver");
+                               resolver = resolverFactory.getAdministrativeResourceResolver(null);
+                       }
+               } catch (LoginException le) {
+                       throw new RuntimeException(
+                                       "Unable to get Administrative Resource Resolver, add the bundle org.apache.sling.serviceuser.webconsole in the Apache Sling Login Admin Whitelist",
+                                       le);
+               }
                return resolver;
        }
 
+       /**
+        * Called internally by {@link AbstractWebConsolePlugin} to load resources.
+        *
+        * This particular implementation depends on the label. As example, if the
+        * plugin is accessed as <code>/system/console/abc</code>, and the plugin
+        * resources are accessed like
+        * <code>/system/console/abc/res/logo.gif</code>, the code here will try
+        * load resource <code>/res/logo.gif</code> from the bundle, providing the
+        * plugin.
+        *
+        *
+        * @param path
+        *            the path to read.
+        * @return the URL of the resource or <code>null</code> if not found.
+        */
+       protected URL getResource(String path) {
+               String base = "/" + LABEL + "/";
+               return (path != null && path.startsWith(base)) ? getClass().getResource(path.substring(base.length() - 1))
+                               : null;
+       }
+
+       private String[] getSupportedPrivileges(HttpServletRequest request) {
+               String[] names = null;
+               try {
+                       ResourceResolver resolver = getResourceResolver(request);
+                       Session session = resolver.adaptTo(Session.class);
+                       AccessControlManager accessControl = session.getAccessControlManager();
+                       Privilege[] privileges = accessControl.getSupportedPrivileges("/");
+                       names = new String[privileges.length];
+                       for (int i = 0; i < privileges.length; i++) {
+                               names[i] = privileges[i].getName();
+                       }
+                       Arrays.sort(names);
+               } catch (RepositoryException re) {
+                       log.error("Exception loading Supported Privileges", re);
+               }
+               return names;
+       }
+
+       @Override
+       public String getTitle() {
+               return TITLE;
+       }
+
        private boolean hasPrincipal(Mapping map, String name) {
                Iterable<String> principals = map.mapPrincipals(map.getServiceName(), map.getSubServiceName());
                if (principals != null) {
@@ -533,6 +518,48 @@ public class ServiceUserWebConsolePlugin extends SimpleWebConsolePlugin {
 
        }
 
+       private void printPrivilegeSelect(PrintWriter pw, String label, List<Pair<String, String>> privileges,
+                       String[] supportedPrivileges, String alertMessage) {
+               pw.print("<td style='width:20%'>");
+               pw.print(xss.encodeForHTMLAttr(label));
+               pw.println("</td>");
+               pw.print("<td><table class=\"repeating-container\" style=\"width: 100%\" data-length=\"" + privileges.size()
+                               + "\"><tr><td>Path</td><td>Privilege</td><td></td>");
+
+               int idx = 0;
+               for (Pair<String, String> privilege : privileges) {
+                       pw.print("</tr><tr class=\"repeating-item\"><td>");
+
+                       pw.print("<input type=\"text\"  name=\"acl-path-" + idx + "\" value='");
+                       pw.print(xss.encodeForHTMLAttr(StringUtils.defaultString(privilege.getKey())));
+                       pw.print("' style='width:100%' />");
+
+                       pw.print("</td><td>");
+
+                       pw.print("<input type=\"text\" list=\"data-privileges\" name=\"acl-privilege-" + idx + "\" value='");
+                       pw.print(xss.encodeForHTMLAttr(StringUtils.defaultString(privilege.getValue())));
+                       pw.print("' style='width:100%' />");
+
+                       pw.print("</td><td>");
+
+                       pw.print("<input type=\"button\" value=\"&nbsp;-&nbsp;\" class=\"repeating-remove\" /></td>");
+               }
+               pw.print("</tr></table>");
+
+               pw.print("<input type=\"button\" value=\"&nbsp;+&nbsp;\" class=\"repeating-add\" />");
+
+               pw.print("<datalist id=\"data-privileges\">");
+               for (String option : supportedPrivileges) {
+                       pw.print("<option");
+                       pw.print(">");
+                       pw.print(xss.encodeForHTMLAttr(option));
+                       pw.print("</option>");
+               }
+               pw.print("</datalist><script src=\"/system/console/serviceusers/res/ui/serviceusermanager.js\"></script>");
+               infoDiv(pw, alertMessage);
+               pw.println("</td>");
+       }
+
        private void printServiceUserDetails(HttpServletRequest request, PrintWriter pw)
                        throws AccessDeniedException, UnsupportedRepositoryOperationException, RepositoryException {
                String name = getParameter(request, PN_USER, "");
@@ -676,44 +703,6 @@ public class ServiceUserWebConsolePlugin extends SimpleWebConsolePlugin {
                }
        }
 
-       private List<Pair<String, String>> getPrivileges(HttpServletRequest request) {
-               List<Pair<String, String>> privileges = new ArrayList<Pair<String, String>>();
-               List<String> params = Collections.list(request.getParameterNames());
-
-               for (String param : params) {
-                       if (param.startsWith("acl-path-")) {
-                               String path = request.getParameter(param);
-                               String privilege = request.getParameter(param.replace("-path-", "-privilege-"));
-                               if (StringUtils.isNotBlank(path) && StringUtils.isNotBlank(privilege)) {
-                                       privileges.add(new ImmutablePair<String, String>(path, privilege));
-                               } else {
-                                       log.warn("Unable to load ACL due to missing value {}={}", path, privilege);
-                               }
-                       }
-               }
-
-               return privileges;
-       }
-
-       private String[] getSupportedPrivileges(HttpServletRequest request) {
-               String[] names = null;
-               try {
-                       ResourceResolver resolver = getResourceResolver(request);
-                       Session session = resolver.adaptTo(Session.class);
-                       AccessControlManager accessControl = session.getAccessControlManager();
-                       Privilege[] privileges = accessControl.getSupportedPrivileges("/");
-                       names = new String[privileges.length];
-                       for (int i = 0; i < privileges.length; i++) {
-                               names[i] = privileges[i].getName();
-                       }
-                       Arrays.sort(names);
-               } catch (RepositoryException re) {
-                       log.error("Exception loading Supported Privileges", re);
-               }
-               return names;
-
-       }
-
        @Override
        protected void renderContent(HttpServletRequest request, HttpServletResponse response)
                        throws ServletException, IOException {
@@ -745,48 +734,6 @@ public class ServiceUserWebConsolePlugin extends SimpleWebConsolePlugin {
                }
        }
 
-       private void printPrivilegeSelect(PrintWriter pw, String label, List<Pair<String, String>> privileges,
-                       String[] supportedPrivileges, String alertMessage) {
-               pw.print("<td style='width:20%'>");
-               pw.print(xss.encodeForHTMLAttr(label));
-               pw.println("</td>");
-               pw.print("<td><table class=\"repeating-container\" style=\"width: 100%\" data-length=\"" + privileges.size()
-                               + "\"><tr><td>Path</td><td>Privilege</td><td></td>");
-
-               int idx = 0;
-               for (Pair<String, String> privilege : privileges) {
-                       pw.print("</tr><tr class=\"repeating-item\"><td>");
-
-                       pw.print("<input type=\"text\"  name=\"acl-path-" + idx + "\" value='");
-                       pw.print(xss.encodeForHTMLAttr(StringUtils.defaultString(privilege.getKey())));
-                       pw.print("' style='width:100%' />");
-
-                       pw.print("</td><td>");
-
-                       pw.print("<input type=\"text\" list=\"data-privileges\" name=\"acl-privilege-" + idx + "\" value='");
-                       pw.print(xss.encodeForHTMLAttr(StringUtils.defaultString(privilege.getValue())));
-                       pw.print("' style='width:100%' />");
-
-                       pw.print("</td><td>");
-
-                       pw.print("<input type=\"button\" value=\"&nbsp;-&nbsp;\" class=\"repeating-remove\" /></td>");
-               }
-               pw.print("</tr></table>");
-
-               pw.print("<input type=\"button\" value=\"&nbsp;+&nbsp;\" class=\"repeating-add\" />");
-
-               pw.print("<datalist id=\"data-privileges\">");
-               for (String option : supportedPrivileges) {
-                       pw.print("<option");
-                       pw.print(">");
-                       pw.print(xss.encodeForHTMLAttr(option));
-                       pw.print("</option>");
-               }
-               pw.print("</datalist><script src=\"/system/console/serviceusers/res/ui/serviceusermanager.js\"></script>");
-               infoDiv(pw, alertMessage);
-               pw.println("</td>");
-       }
-
        private void selectField(PrintWriter pw, String label, String fieldName, String value, Collection<String> options,
                        String... alertMessages) {
                pw.print("<td style='width:20%'>");
@@ -902,4 +849,100 @@ public class ServiceUserWebConsolePlugin extends SimpleWebConsolePlugin {
                pw.println("</td>");
        }
 
+       private boolean updatePrivileges(HttpServletRequest request, ResourceResolver resolver) {
+
+               List<Pair<String, String>> privileges = this.getPrivileges(request);
+               String name = getParameter(request, PN_NAME, "");
+
+               List<String> currentPolicies = new ArrayList<String>();
+               findACLs(resolver, name, currentPolicies);
+               for (int i = 0; i < currentPolicies.size(); i++) {
+                       String path = StringUtils.substringBefore(currentPolicies.get(i), "/rep:policy");
+                       currentPolicies.set(i, StringUtils.isNotBlank(path) ? path : "/");
+               }
+               log.debug("Loaded current policy paths: {}", currentPolicies);
+
+               Map<String, List<String>> toSet = new HashMap<String, List<String>>();
+               for (Pair<String, String> privilege : privileges) {
+                       if (!toSet.containsKey(privilege.getKey())) {
+                               toSet.put(privilege.getKey(), new ArrayList<String>());
+                       }
+                       toSet.get(privilege.getKey()).add(privilege.getValue());
+               }
+               log.debug("Loaded updated policy paths: {}", currentPolicies);
+
+               String lastEntry = null;
+
+               try {
+
+                       Session session = resolver.adaptTo(Session.class);
+                       AccessControlManager accessManager = session.getAccessControlManager();
+                       PrincipalManager principalManager = AccessControlUtil.getPrincipalManager(session);
+
+                       for (Entry<String, List<String>> pol : toSet.entrySet()) {
+                               lastEntry = pol.getKey();
+                               currentPolicies.remove(pol.getKey());
+                               log.debug("Updating policies for {}", pol.getKey());
+
+                               AccessControlPolicy[] policies = accessManager.getPolicies(pol.getKey());
+                               List<String> toRemove = new ArrayList<String>();
+                               for (AccessControlPolicy p : policies) {
+                                       if (p instanceof AccessControlList) {
+                                               AccessControlList policy = (AccessControlList) p;
+                                               for (AccessControlEntry entry : policy.getAccessControlEntries()) {
+                                                       Principal prin = entry.getPrincipal();
+                                                       if (prin.getName().equals(name)) {
+                                                               for (Privilege privilege : entry.getPrivileges()) {
+                                                                       if (!pol.getValue().contains(privilege.getName())) {
+                                                                               log.debug("Removing privilege {}", privilege);
+                                                                               toRemove.add(privilege.getName());
+                                                                       }
+                                                               }
+                                                       }
+                                               }
+                                       }
+                               }
+                               Principal principal = principalManager.getPrincipal(name);
+                               AccessControlUtil.replaceAccessControlEntry(session, pol.getKey(), principal,
+                                               pol.getValue().toArray(new String[pol.getValue().size()]), new String[0],
+                                               toRemove.toArray(new String[toRemove.size()]), null);
+                       }
+                       session.save();
+
+                       for (String oldPolicy : currentPolicies) {
+                               boolean removed = false;
+                               log.debug("Removing policy for {}", oldPolicy);
+                               AccessControlPolicy[] policies = accessManager.getPolicies(oldPolicy);
+                               AccessControlEntry toRemove = null;
+                               for (AccessControlPolicy p : policies) {
+                                       if (p instanceof AccessControlList) {
+                                               AccessControlList policy = (AccessControlList) p;
+                                               for (AccessControlEntry entry : policy.getAccessControlEntries()) {
+                                                       Principal prin = entry.getPrincipal();
+                                                       if (prin.getName().equals(name)) {
+                                                               toRemove = entry;
+                                                               break;
+                                                       }
+                                               }
+                                               if (toRemove != null) {
+                                                       removed = true;
+                                                       policy.removeAccessControlEntry(toRemove);
+                                                       accessManager.setPolicy(oldPolicy, policy);
+                                                       session.save();
+                                                       log.debug("Removed access control entry {}", toRemove);
+                                               }
+                                       }
+                               }
+                               if (!removed) {
+                                       log.warn("No policy found for {}", oldPolicy);
+                               }
+                       }
+               } catch (RepositoryException e) {
+                       log.error("Exception updating principals with {}, failed on {}", toSet, lastEntry, e);
+                       return false;
+               }
+
+               return true;
+       }
+
 }