You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@sling.apache.org by dk...@apache.org on 2018/01/12 22:48:15 UTC

[sling-org-apache-sling-serviceuser-webconsole] branch master updated: Fixing an issue where it failed to start without xss compat, wasn't serving the js file and didn't create the app folder correctly

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

dklco pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/sling-org-apache-sling-serviceuser-webconsole.git


The following commit(s) were added to refs/heads/master by this push:
     new 26b6663  Fixing an issue where it failed to start without xss compat, wasn't serving the js file and didn't create the app folder correctly
26b6663 is described below

commit 26b6663275b7f636f0eaaa397a82c3c51c602cea
Author: Dan Klco <da...@gmail.com>
AuthorDate: Fri Jan 12 17:44:11 2018 -0500

    Fixing an issue where it failed to start without xss compat, wasn't serving the js file and didn't create the app folder correctly
---
 pom.xml                                            | 290 +++++++-------
 .../impl/ServiceUserWebConsolePlugin.java          | 417 ++++++++++++---------
 2 files changed, 379 insertions(+), 328 deletions(-)

diff --git a/pom.xml b/pom.xml
index aa6e3d4..97e097e 100644
--- a/pom.xml
+++ b/pom.xml
@@ -1,154 +1,162 @@
 <?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>
diff --git a/src/main/java/org/apache/sling/serviceuser/webconsole/impl/ServiceUserWebConsolePlugin.java b/src/main/java/org/apache/sling/serviceuser/webconsole/impl/ServiceUserWebConsolePlugin.java
index fd9ab5f..5d952fa 100644
--- a/src/main/java/org/apache/sling/serviceuser/webconsole/impl/ServiceUserWebConsolePlugin.java
+++ b/src/main/java/org/apache/sling/serviceuser/webconsole/impl/ServiceUserWebConsolePlugin.java
@@ -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;
+	}
+
 }

-- 
To stop receiving notification emails like this one, please contact
['"commits@sling.apache.org" <co...@sling.apache.org>'].