diff --git a/README.md b/README.md index db87fe826..3d5947173 100644 --- a/README.md +++ b/README.md @@ -50,7 +50,7 @@ There are also some [advanced configuration options](docs/AdvancedFeatures.md) s # User Interface -There is a Felix Web Console plugin (at `/system/console/actool`) as well as a Touch UI console (at `/mnt/overlay/netcentric/actool/content/overview.html`) to apply configurations and to inspect previous executions of the tool. Additionally there is a [JMX interface](docs/Jmx.md) for some advanced use cases. +There is a [Felix Web Console plugin (at `/system/console/actool`)](docs/ApplyConfig.md#web-console) as well as a [Touch UI console (at `/mnt/overlay/netcentric/actool/content/overview.html`)](docs/ApplyConfig.md#touch-ui) to apply configurations and to inspect previous executions of the tool. Additionally there is a [JMX interface](docs/Jmx.md) for some advanced use cases. # Applying AC Tool Configurations diff --git a/accesscontroltool-bundle/src/main/java/biz/netcentric/cq/tools/actool/history/impl/HistoryUtils.java b/accesscontroltool-bundle/src/main/java/biz/netcentric/cq/tools/actool/history/impl/HistoryUtils.java index 2febe078e..f137e5c55 100644 --- a/accesscontroltool-bundle/src/main/java/biz/netcentric/cq/tools/actool/history/impl/HistoryUtils.java +++ b/accesscontroltool-bundle/src/main/java/biz/netcentric/cq/tools/actool/history/impl/HistoryUtils.java @@ -58,12 +58,12 @@ public class HistoryUtils { public static final String HISTORY_NODE_NAME_PREFIX = "history_"; public static final String NODETYPE_NT_UNSTRUCTURED = "nt:unstructured"; - public static final String ACHISTORY_ROOT_NODE = "achistory"; - public static final String STATISTICS_ROOT_NODE = "var/statistics"; - public static final String ACHISTORY_PATH = "/"+ HistoryUtils.STATISTICS_ROOT_NODE + "/" + HistoryUtils.ACHISTORY_ROOT_NODE; + public static final String ACHISTORY_ROOT_NODE_NAME = "achistory"; + public static final String STATISTICS_ROOT_NODE_PATH = "/var/statistics"; + public static final String ACHISTORY_PATH = HistoryUtils.STATISTICS_ROOT_NODE_PATH + "/" + HistoryUtils.ACHISTORY_ROOT_NODE_NAME; private static final String AC_ROOT_PATH_IN_APPS = "/apps/netcentric"; - public static final String AC_HISTORY_PATH_IN_APPS = AC_ROOT_PATH_IN_APPS + "/" + ACHISTORY_ROOT_NODE; + public static final String AC_HISTORY_PATH_IN_APPS = AC_ROOT_PATH_IN_APPS + "/" + ACHISTORY_ROOT_NODE_NAME; public static final String PROPERTY_TIMESTAMP = "timestamp"; private static final String PROPERTY_MESSAGES = "messages"; @@ -85,10 +85,8 @@ public class HistoryUtils { public static Node getAcHistoryRootNode(final Session session) throws RepositoryException { - final Node rootNode = session.getRootNode(); - Node statisticsRootNode = safeGetNode(rootNode, STATISTICS_ROOT_NODE, NODETYPE_NT_UNSTRUCTURED); - Node acHistoryRootNode = safeGetNode(statisticsRootNode, ACHISTORY_ROOT_NODE, "sling:OrderedFolder"); - return acHistoryRootNode; + Node statisticsRootNode = JcrUtils.getOrCreateByPath(STATISTICS_ROOT_NODE_PATH, NODETYPE_NT_UNSTRUCTURED, session); + return JcrUtils.getOrAddNode(statisticsRootNode, ACHISTORY_ROOT_NODE_NAME, "sling:OrderedFolder"); } /** @@ -139,7 +137,7 @@ public static Node persistHistory(final Session session, } name += AcToolExecutionImpl.TRIGGER_SEPARATOR_IN_NODE_NAME + trigger; - Node newHistoryNode = safeGetNode(acHistoryRootNode, name, NODETYPE_NT_UNSTRUCTURED); + Node newHistoryNode = JcrUtils.getOrAddNode(acHistoryRootNode, name, NODETYPE_NT_UNSTRUCTURED); String path = newHistoryNode.getPath(); setHistoryNodeProperties(newHistoryNode, installLog, trigger); saveLogs(newHistoryNode, installLog); @@ -177,17 +175,6 @@ private static boolean isInStrackTracke(StackTraceElement[] stackTrace, String c return false; } - private static Node safeGetNode(final Node baseNode, final String name, - final String typeToCreate) throws RepositoryException { - if (!baseNode.hasNode(name)) { - LOG.debug("create node: {}", name); - return baseNode.addNode(name, typeToCreate); - - } else { - return baseNode.getNode(name); - } - } - public static void setHistoryNodeProperties(final Node historyNode, PersistableInstallationLogger installLog, String trigger) throws ValueFormatException, VersionException, LockException, ConstraintViolationException, diff --git a/accesscontroltool-bundle/src/main/java/biz/netcentric/cq/tools/actool/ui/AcToolUiService.java b/accesscontroltool-bundle/src/main/java/biz/netcentric/cq/tools/actool/ui/AcToolUiService.java index 19c20427b..69312195d 100644 --- a/accesscontroltool-bundle/src/main/java/biz/netcentric/cq/tools/actool/ui/AcToolUiService.java +++ b/accesscontroltool-bundle/src/main/java/biz/netcentric/cq/tools/actool/ui/AcToolUiService.java @@ -23,7 +23,6 @@ import java.text.SimpleDateFormat; import java.util.Arrays; import java.util.HashMap; -import java.util.Iterator; import java.util.LinkedHashMap; import java.util.LinkedList; import java.util.List; @@ -34,21 +33,25 @@ import java.util.stream.Collectors; import javax.jcr.RepositoryException; +import javax.jcr.Session; import javax.jcr.Value; import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; -import org.apache.commons.lang3.ArrayUtils; import org.apache.commons.lang3.StringUtils; import org.apache.felix.webconsole.WebConsoleConstants; -import org.apache.jackrabbit.api.security.user.Group; +import org.apache.jackrabbit.api.JackrabbitSession; import org.apache.jackrabbit.api.security.user.User; import org.apache.jackrabbit.oak.spi.security.principal.EveryonePrincipal; import org.apache.sling.api.SlingHttpServletRequest; +import org.osgi.service.component.annotations.Activate; import org.osgi.service.component.annotations.Component; import org.osgi.service.component.annotations.Reference; 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; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -64,6 +67,7 @@ import biz.netcentric.cq.tools.actool.user.UserProcessor; @Component(service = { AcToolUiService.class }) +@Designate(ocd=biz.netcentric.cq.tools.actool.ui.AcToolUiService.Configuration.class) public class AcToolUiService { private static final Logger LOG = LoggerFactory.getLogger(AcToolUiService.class); @@ -88,15 +92,27 @@ public class AcToolUiService { @Reference(policyOption = ReferencePolicyOption.GREEDY) AcInstallationServiceInternal acInstallationService; - @Reference(policyOption = ReferencePolicyOption.GREEDY) - private WebConsoleConfigTracker webConsoleConfig; - @Reference(policyOption = ReferencePolicyOption.GREEDY) private AcHistoryService acHistoryService; + @ObjectClassDefinition(name = "AC Tool UI Service", + description="Service that allows to apply AC Tool configuration and gather status of users/groups and permissions from a Web UI (either Touch UI or Web Console Plugin).") + protected static @interface Configuration { + + @AttributeDefinition(name="Read access", description="Principal names allowed to export all users/groups and permissions in the system. Only leveraged for Touch UI but not for Web Console Plugin.") + String[] readAccessPrincipalNames() default { "administrators", "admin" }; + + @AttributeDefinition(name="Write access", description="Principal names allowed to modify users/groups and permissions in the system via ACTool configuration files. Only leveraged for Touch UI but not for Web Console Plugin.") + String[] writeAccessPrincipalNames() default { "administrators", "admin" }; + } + private final Map countryCodePerName; - public AcToolUiService() { + private final Configuration config; + + @Activate + public AcToolUiService(Configuration config) { + this.config = config; countryCodePerName = new HashMap<>(); for (String iso : Locale.getISOCountries()) { Locale l = new Locale(Locale.ENGLISH.getLanguage(), iso); @@ -108,16 +124,17 @@ protected void doGet(HttpServletRequest req, HttpServletResponse resp, String pa throws ServletException, IOException { if (req.getRequestURI().endsWith(SUFFIX_DUMP_YAML)) { - callWhenAuthorized(req, resp, this::streamDumpToResponse); + callWhenReadAccessGranted(req, resp, this::streamDumpToResponse); } else if (req.getRequestURI().endsWith(SUFFIX_USERS_CSV)) { - callWhenAuthorized(req, resp, this::streamUsersCsvToResponse); + callWhenReadAccessGranted(req, resp, this::streamUsersCsvToResponse); } else { + // everyone is allows to see the UI in general renderUi(req, resp, path, isTouchUi); } } - private void callWhenAuthorized(HttpServletRequest req, HttpServletResponse resp, Consumer responseConsumer) throws IOException { - if (!hasAccessToFelixWebConsole(req)) { + private void callWhenReadAccessGranted(HttpServletRequest req, HttpServletResponse resp, Consumer responseConsumer) throws IOException, ServletException { + if (!isOneOfPrincipalNamesBound(req, config.readAccessPrincipalNames())) { resp.sendError(HttpServletResponse.SC_FORBIDDEN, "You do not have sufficent permissions to export users/groups/permissions"); return; } @@ -127,12 +144,13 @@ private void callWhenAuthorized(HttpServletRequest req, HttpServletResponse resp throw e.getCause(); } } + @SuppressWarnings(/* SonarCloud false positive */ { "javasecurity:S5131" /* response is sent as text/plain, it's not interpreted */, "javasecurity:S5145" /* logging the path is fine */ }) protected void doPost(final HttpServletRequest req, final HttpServletResponse resp) throws IOException, ServletException { - if (!hasAccessToFelixWebConsole(req)) { + if (!isOneOfPrincipalNamesBound(req, config.writeAccessPrincipalNames())) { resp.sendError(HttpServletResponse.SC_FORBIDDEN, "You do not have sufficent permissions to apply the configuration"); return; } @@ -157,45 +175,31 @@ protected void doPost(final HttpServletRequest req, final HttpServletResponse re } /** - * Replicates the logic of the Sling Web Console Security Provider. + * Similar to the logic of the Sling Web Console Security Provider but acting on principal names * @param req the request - * @return {@code true} if the user bound to the given request may also access the Felix Web Console or if we are outside of Sling, {@code false} otherwise + * @param principalNames the principal names to check against + * @return {@code true} if the session bound to the given request is bound to any of the given principal names + * @throws ServletException + * @throws RepositoryException */ - private boolean hasAccessToFelixWebConsole(HttpServletRequest req) { - + private boolean isOneOfPrincipalNamesBound(HttpServletRequest req, String[] principalNames) throws ServletException { if (!(req instanceof SlingHttpServletRequest)) { // outside Sling this is only called by the Felix Web Console, which has its own security layer LOG.debug("Outside Sling no additional security checks are performed!"); return true; } - try { - User requestUser = SlingHttpServletRequest.class.cast(req).getResourceResolver().adaptTo(User.class); - if (requestUser != null) { - if (StringUtils.equals(requestUser.getID(), "admin")) { - LOG.debug("Admin user is allowed to apply AC Tool"); - return true; - } - - if (ArrayUtils.contains(webConsoleConfig.getAllowedUsers(), requestUser.getID())) { - LOG.debug("User {} is allowed to apply AC Tool (allowed users: {})", requestUser.getID(), ArrayUtils.toString(webConsoleConfig.getAllowedUsers())); - return true; - } - - Iterator memberOfIt = requestUser.memberOf(); + Session session = SlingHttpServletRequest.class.cast(req).getResourceResolver().adaptTo(Session.class); + return isOneOfPrincipalNamesBound(JackrabbitSession.class.cast(session), principalNames); + } - while (memberOfIt.hasNext()) { - Group memberOfGroup = memberOfIt.next(); - if (ArrayUtils.contains(webConsoleConfig.getAllowedGroups(), memberOfGroup.getID())) { - LOG.debug("Group {} is allowed to apply AC Tool (allowed groups: {})", memberOfGroup.getID(), ArrayUtils.toString(webConsoleConfig.getAllowedGroups())); - return true; - } - } - } - LOG.debug("Could not get associated user for Sling request"); - return false; - } catch (Exception e) { - throw new IllegalStateException("Could not check if user may apply AC Tool configuration: " + e, e); + private boolean isOneOfPrincipalNamesBound(JackrabbitSession session, String[] principalNames) throws ServletException { + BoundPrincipals boundPrincipals; + try { + boundPrincipals = new BoundPrincipals(JackrabbitSession.class.cast(session)); + } catch (RepositoryException e) { + throw new ServletException("Could not determine bound principals", e); } + return boundPrincipals.containsOneOf(Arrays.asList(principalNames)); } public String getWebConsoleRoot(HttpServletRequest req) { @@ -210,8 +214,8 @@ private void renderUi(HttpServletRequest req, HttpServletResponse resp, String p printCss(isTouchUi, writer); printVersion(writer); - printImportSection(writer, reqParams, path, isTouchUi, getWebConsoleRoot(req)); - printExportSection(writer, reqParams, path, isTouchUi, getWebConsoleRoot(req)); + printImportSection(writer, reqParams, path, isTouchUi, getWebConsoleRoot(req), isOneOfPrincipalNamesBound(req, config.writeAccessPrincipalNames())); + printExportSection(writer, reqParams, path, isTouchUi, getWebConsoleRoot(req), isOneOfPrincipalNamesBound(req, config.readAccessPrincipalNames())); try { printInstallationLogsSection(writer, reqParams, isTouchUi); @@ -425,7 +429,7 @@ private String getExecutionStatusHtml(AcToolExecution acToolExecution) { return acToolExecution.isSuccess() ? "SUCCESS" : "FAILED"; } - private void printImportSection(final HtmlWriter writer, RequestParameters reqParams, String path, boolean isTouchUI, String webConsoleRoot) throws IOException { + private void printImportSection(final HtmlWriter writer, RequestParameters reqParams, String path, boolean isTouchUI, String webConsoleRoot, boolean hasWritePermission) throws IOException { writer.print("
"); writer.openTable("acFormTable"); @@ -473,7 +477,7 @@ private void printImportSection(final HtmlWriter writer, RequestParameters reqPa writer.openTd(); String onClick = "var as=$('#applySpinner');as.show(); var b=$('#applyButton');b.prop('disabled', true); oldL = b.text();b.text(' Applying AC Tool Configuration... ');var f=$('#acForm');var fd=f.serialize();$.post(f.attr('action'), fd).done(function(text){alert(text)}).fail(function(xhr){alert(xhr.status===403?'Permission Denied':'Config could not be applied - check log for errors')}).always(function(text) { " + "as.hide();b.text(oldL);b.prop('disabled', false);location.href='" + PAGE_NAME + "?'+fd; });return false"; - writer.println(""); + writer.println(""); writer.closeTd(); writer.openTd(); writer.println(""); @@ -487,7 +491,7 @@ private void printImportSection(final HtmlWriter writer, RequestParameters reqPa } - private void printExportSection(final HtmlWriter writer, RequestParameters reqParams, String path, boolean isTouchUI, String webConsoleRoot) throws IOException { + private void printExportSection(final HtmlWriter writer, RequestParameters reqParams, String path, boolean isTouchUI, String webConsoleRoot, boolean hasReadPermission) throws IOException { writer.openTable("acExportTable"); writer.tableHeader("Export", 2); writer.tr(); @@ -495,7 +499,7 @@ private void printExportSection(final HtmlWriter writer, RequestParameters reqPa writer.print("Export in AC Tool YAML format. This includes groups and permissions (in form of ACEs)."); writer.closeTd(); writer.openTd(); - writer.println(""); writer.closeTd(); writer.closeTr(); @@ -504,7 +508,7 @@ private void printExportSection(final HtmlWriter writer, RequestParameters reqPa writer.print("Export Users in Admin Console CSV format. This includes non-system users, their profiles and their direct group memberships."); writer.closeTd(); writer.openTd(); - writer.println(""); writer.closeTd(); writer.closeTr(); diff --git a/accesscontroltool-bundle/src/main/java/biz/netcentric/cq/tools/actool/ui/PrincipalUtil.java b/accesscontroltool-bundle/src/main/java/biz/netcentric/cq/tools/actool/ui/PrincipalUtil.java new file mode 100644 index 000000000..2bcb4c25d --- /dev/null +++ b/accesscontroltool-bundle/src/main/java/biz/netcentric/cq/tools/actool/ui/PrincipalUtil.java @@ -0,0 +1,82 @@ +package biz.netcentric.cq.tools.actool.ui; + +/*- + * #%L + * Access Control Tool Bundle + * %% + * Copyright (C) 2015 - 2024 Cognizant Netcentric + * %% + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * #L% + */ + +import java.security.Principal; +import java.util.Collection; +import java.util.HashSet; +import java.util.Iterator; +import java.util.Set; +import java.util.stream.Collectors; + +import javax.jcr.RepositoryException; + +import org.apache.jackrabbit.api.JackrabbitSession; +import org.apache.jackrabbit.api.security.user.Authorizable; +import org.apache.jackrabbit.api.security.user.AuthorizableTypeException; +import org.apache.jackrabbit.api.security.user.Group; +import org.jetbrains.annotations.NotNull; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + + +/** + * Encapsulates all principals bound to a given session. + * Natively exposed from Oak 1.40 onwards (see OAK-8611). + */ +class BoundPrincipals { + + private static final Logger log = LoggerFactory.getLogger(BoundPrincipals.class); + + private Set boundPrincipals; + + BoundPrincipals(@NotNull JackrabbitSession session) throws RepositoryException { + final String userId = session.getUserID(); + // newer Oak versions expose bound principals via session attribute (https://issues.apache.org/jira/browse/OAK-9415) + boundPrincipals = (Set)session.getAttribute("oak.bound-principals"); + if (boundPrincipals == null) { + boundPrincipals = new HashSet<>(); + Authorizable authorizable = session.getUserManager().getAuthorizable(userId); + if (authorizable == null) { + throw new AuthorizableTypeException("Could not find authorizable for session's user ID " + userId); + } + boundPrincipals.add(authorizable.getPrincipal()); + + Iterator groupIterator = authorizable.memberOf(); + while (groupIterator.hasNext()) { + boundPrincipals.add(groupIterator.next().getPrincipal()); + } + log.debug("Bound principals calculated from user manager"); + } else { + log.debug("Bound principals found in session attribute"); + } + if (log.isDebugEnabled()) { + log.debug("Bound principals for session associated with user id {}: {}", userId, boundPrincipals.stream().map(Principal::getName).collect(Collectors.joining(", "))); + } + } + + public boolean containsOneOf(@NotNull Collection principalNames) { + for (Principal principal : boundPrincipals) { + if (principalNames.contains(principal.getName())) { + return true; + } + } + if (log.isDebugEnabled()) { + log.debug("None of the bound principals {} match any of the provided names: {}", + boundPrincipals.stream().map(Principal::getName).collect(Collectors.joining(", ")), principalNames); + } + return false; + } + +} diff --git a/accesscontroltool-bundle/src/main/java/biz/netcentric/cq/tools/actool/ui/WebConsoleConfigTracker.java b/accesscontroltool-bundle/src/main/java/biz/netcentric/cq/tools/actool/ui/WebConsoleConfigTracker.java deleted file mode 100644 index 09bf17bdb..000000000 --- a/accesscontroltool-bundle/src/main/java/biz/netcentric/cq/tools/actool/ui/WebConsoleConfigTracker.java +++ /dev/null @@ -1,84 +0,0 @@ -package biz.netcentric.cq.tools.actool.ui; - -/*- - * #%L - * Access Control Tool Bundle - * %% - * Copyright (C) 2015 - 2024 Cognizant Netcentric - * %% - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the Eclipse Public License v1.0 - * which accompanies this distribution, and is available at - * http://www.eclipse.org/legal/epl-v10.html - * #L% - */ - -import java.io.IOException; -import java.util.Dictionary; - -import org.apache.commons.lang3.ArrayUtils; -import org.apache.sling.commons.osgi.PropertiesUtil; -import org.osgi.service.cm.ConfigurationAdmin; -import org.osgi.service.cm.ConfigurationEvent; -import org.osgi.service.cm.ConfigurationListener; -import org.osgi.service.component.annotations.Activate; -import org.osgi.service.component.annotations.Component; -import org.osgi.service.component.annotations.Reference; -import org.osgi.service.component.annotations.ReferencePolicyOption; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -@Component(service = { WebConsoleConfigTracker.class, ConfigurationListener.class }) -public class WebConsoleConfigTracker implements ConfigurationListener { - - private static final Logger LOG = LoggerFactory.getLogger(WebConsoleConfigTracker.class); - - private static final String CONSOLE_SEC_PROVIDER_PID = "org.apache.sling.extensions.webconsolesecurityprovider.internal.SlingWebConsoleSecurityProvider"; - private static final String CONSOLE_SEC_PROVIDER_USERS_PROP = "users"; - private static final String CONSOLE_SEC_PROVIDER_GROUPS_PROP = "groups"; - - private static final String[] RELEVANT_PIDS = new String[] {CONSOLE_SEC_PROVIDER_PID}; - - @Reference(policyOption = ReferencePolicyOption.GREEDY) - private ConfigurationAdmin configAdmin; - - private String[] allowedUsers = new String[] {}; - private String[] allowedGroups = new String[] {}; - - @Activate - private void updateConfig() { - try { - // make sure that you don't overwrite the binding! - Dictionary webconsoleSecProviderConfig = configAdmin.getConfiguration(CONSOLE_SEC_PROVIDER_PID, null).getProperties(); - if(webconsoleSecProviderConfig != null) { - allowedUsers = PropertiesUtil.toStringArray(webconsoleSecProviderConfig.get(CONSOLE_SEC_PROVIDER_USERS_PROP)); - allowedGroups = PropertiesUtil.toStringArray(webconsoleSecProviderConfig.get(CONSOLE_SEC_PROVIDER_GROUPS_PROP)); - } - if(LOG.isDebugEnabled()) { - LOG.debug("allowedUsers: {} allowedGroups: {}", - ArrayUtils.toString(allowedUsers), - ArrayUtils.toString(allowedGroups)); - } - - } catch (IOException e) { - LOG.warn("Could not update config: "+e, e); - } - } - - @Override - public void configurationEvent(ConfigurationEvent event) { - String pid = event.getPid(); - if(ArrayUtils.contains(RELEVANT_PIDS, pid)) { - updateConfig(); - } - } - - public String[] getAllowedUsers() { - return allowedUsers; - } - - public String[] getAllowedGroups() { - return allowedGroups; - } - -} diff --git a/accesscontroltool-content-package/pom.xml b/accesscontroltool-content-package/pom.xml index 81986ee06..2306ff990 100644 --- a/accesscontroltool-content-package/pom.xml +++ b/accesscontroltool-content-package/pom.xml @@ -25,7 +25,7 @@ accesscontroltool-content-package content-package Access Control Tool Package - Content - Content package of the AC Tool containing the system user and ACLs for it. + Content package of the AC Tool containing the system user and ACLs for it (only relevant for AEM onPrem) diff --git a/accesscontroltool-package/src/main/jcr_root-cloud/apps/netcentric/actool/config/biz.netcentric.cq.tools.actool.ui.AcToolUiService.cfg.json b/accesscontroltool-package/src/main/jcr_root-cloud/apps/netcentric/actool/config/biz.netcentric.cq.tools.actool.ui.AcToolUiService.cfg.json new file mode 100644 index 000000000..0b03f014d --- /dev/null +++ b/accesscontroltool-package/src/main/jcr_root-cloud/apps/netcentric/actool/config/biz.netcentric.cq.tools.actool.ui.AcToolUiService.cfg.json @@ -0,0 +1,12 @@ +{ + "writeAccessPrincipalNames":[ + "administrators", + "admin", + "$[env:aemCloudAdministrators;default=administrators]" + ], + "readAccessPrincipalNames":[ + "administrators", + "admin", + "$[env:aemCloudAdministrators;default=administrators]" + ] +} \ No newline at end of file diff --git a/docs/ApplyConfig.md b/docs/ApplyConfig.md index c30c4c734..6c11c3e8e 100644 --- a/docs/ApplyConfig.md +++ b/docs/ApplyConfig.md @@ -88,7 +88,10 @@ A Felix Web Console UI is available at "Main" -> "AC Tool". The web console prov ### Touch UI -The same interface as available via Web Console is also available via Touch UI at `Tools -> Security -> Netcentric AC Tool` if you are admin on the instance. When using [AEM as a Cloud Service](https://www.adobe.com/marketing/experience-manager/cloud-service.html), the console will be available if you are in the admin group for the AEM env as set up in [adminconsole](https://adminconsole.adobe.com/). +The same interface as available via Web Console is also available via Touch UI at `Tools -> Security -> Netcentric AC Tool`. +It allows viewing the logs of ACTool installations and both applying of ACLs as well as reading of users/ACLs of the system. The latter two require permissions which by default are only granted to the `admin` user and members of the `administrators` group. When using [AEM as a Cloud Service](https://www.adobe.com/marketing/experience-manager/cloud-service.html) in addition also to members of the IMS admin group for the AEM env as set up in [adminconsole](https://adminconsole.adobe.com/). In case you are not having the according permissions the buttons are disabled. + +Adjusting the permissions requires configuring the OSGi config with PID `biz.netcentric.cq.tools.actool.ui.AcToolUiService`. ### JMX diff --git a/pom.xml b/pom.xml index 8ee5cfb34..054f0897f 100644 --- a/pom.xml +++ b/pom.xml @@ -442,7 +442,7 @@ 2.4.0 epl_only_v1 - test/resources/**,it/** + test/resources/**,it/**,**/*.cfg.json