diff --git a/app/aem/actions.checks/build.gradle.kts b/app/aem/actions.checks/build.gradle.kts index fd2c33a0e..b04e28f1d 100644 --- a/app/aem/actions.checks/build.gradle.kts +++ b/app/aem/actions.checks/build.gradle.kts @@ -22,7 +22,6 @@ aem { val currentVersion = rootProject.version as String version.set(currentVersion) description.set(project.description) - property("dependencies", "com.cognifide.apm:apm-ui.apps:" + currentVersion.substringBefore("-SNAPSHOT")) } } jar { diff --git a/app/aem/actions.checks/src/main/java/com/cognifide/apm/checks/actions/exclude/CheckExcludes.java b/app/aem/actions.checks/src/main/java/com/cognifide/apm/checks/actions/exclude/CheckExcludes.java index c1aa259e6..264364483 100644 --- a/app/aem/actions.checks/src/main/java/com/cognifide/apm/checks/actions/exclude/CheckExcludes.java +++ b/app/aem/actions.checks/src/main/java/com/cognifide/apm/checks/actions/exclude/CheckExcludes.java @@ -23,6 +23,7 @@ import com.cognifide.apm.api.actions.ActionResult; import com.cognifide.apm.api.actions.Context; import com.cognifide.apm.api.exceptions.ActionExecutionException; +import com.cognifide.apm.api.exceptions.AuthorizableNotFoundException; import com.cognifide.apm.checks.utils.ActionUtils; import com.cognifide.apm.checks.utils.MessagingUtils; import java.util.ArrayList; @@ -37,7 +38,7 @@ public class CheckExcludes implements Action { private final String groupId; - public CheckExcludes(final String groupId, final List authorizableIds) { + public CheckExcludes(String groupId, List authorizableIds) { this.groupId = groupId; this.authorizableIds = authorizableIds; } @@ -48,12 +49,11 @@ public ActionResult simulate(Context context) { } @Override - public ActionResult execute(final Context context) { + public ActionResult execute(Context context) { return process(context, true); } - private ActionResult process(final Context context, boolean execute) { - + private ActionResult process(Context context, boolean execute) { ActionResult actionResult = context.createActionResult(); Group group = tryGetGroup(context, actionResult); if (group == null) { @@ -74,8 +74,7 @@ private ActionResult process(final Context context, boolean execute) { return actionResult; } - private boolean checkMembers(final Context context, final ActionResult actionResult, final Group group, - final List errors) { + private boolean checkMembers(Context context, ActionResult actionResult, Group group, List errors) { boolean checkFailed = false; for (String authorizableId : authorizableIds) { try { @@ -95,19 +94,12 @@ private boolean checkMembers(final Context context, final ActionResult actionRes return checkFailed; } - private Group tryGetGroup(final Context context, final ActionResult actionResult) { - Group group; - + private Group tryGetGroup(Context context, ActionResult actionResult) { try { - group = context.getAuthorizableManager().getGroup(groupId); - } catch (RepositoryException e) { + return context.getAuthorizableManager().getGroup(groupId); + } catch (RepositoryException | ActionExecutionException | AuthorizableNotFoundException e) { actionResult.logError(MessagingUtils.createMessage(e)); - return null; - } catch (ActionExecutionException e) { - actionResult.logError(MessagingUtils.createMessage(e)); - return null; } - return group; + return null; } - } diff --git a/app/aem/actions.checks/src/main/java/com/cognifide/apm/checks/actions/exclude/CheckExcludesMapper.java b/app/aem/actions.checks/src/main/java/com/cognifide/apm/checks/actions/exclude/CheckExcludesMapper.java index 46de025a2..0d07bc561 100644 --- a/app/aem/actions.checks/src/main/java/com/cognifide/apm/checks/actions/exclude/CheckExcludesMapper.java +++ b/app/aem/actions.checks/src/main/java/com/cognifide/apm/checks/actions/exclude/CheckExcludesMapper.java @@ -22,6 +22,7 @@ import com.cognifide.apm.api.actions.Action; import com.cognifide.apm.api.actions.annotations.Mapper; import com.cognifide.apm.api.actions.annotations.Mapping; +import com.cognifide.apm.api.actions.annotations.Required; import com.cognifide.apm.checks.actions.ActionGroup; import java.util.Collections; import java.util.List; @@ -32,16 +33,22 @@ public final class CheckExcludesMapper { public static final String REFERENCE = "Verify that provided group DOES NOT contain any of listed authorizables."; @Mapping( + examples = "CHECK-EXCLUDES 'authors' 'author'", reference = REFERENCE ) - public Action mapAction(String group, String id) { + public Action mapAction( + @Required(value = "group", description = "group's id e.g.: 'authors'") String group, + @Required(value = "id", description = "users' or groups' id e.g.: 'author'") String id) { return mapAction(group, Collections.singletonList(id)); } @Mapping( + examples = "CHECK-EXCLUDES 'authors' ['author']", reference = REFERENCE ) - public Action mapAction(String group, List ids) { + public Action mapAction( + @Required(value = "group", description = "group's id e.g.: 'authors'") String group, + @Required(value = "ids", description = "users' or groups' ids e.g.: ['author']") List ids) { return new CheckExcludes(group, ids); } } diff --git a/app/aem/actions.checks/src/main/java/com/cognifide/apm/checks/actions/exists/CheckGroupExistsMapper.java b/app/aem/actions.checks/src/main/java/com/cognifide/apm/checks/actions/exists/CheckGroupExistsMapper.java index 348bfca64..8d86db8c4 100644 --- a/app/aem/actions.checks/src/main/java/com/cognifide/apm/checks/actions/exists/CheckGroupExistsMapper.java +++ b/app/aem/actions.checks/src/main/java/com/cognifide/apm/checks/actions/exists/CheckGroupExistsMapper.java @@ -22,7 +22,7 @@ import com.cognifide.apm.api.actions.Action; import com.cognifide.apm.api.actions.annotations.Mapper; import com.cognifide.apm.api.actions.annotations.Mapping; -import com.cognifide.apm.api.actions.annotations.Named; +import com.cognifide.apm.api.actions.annotations.Required; import com.cognifide.apm.checks.actions.ActionGroup; @Mapper(value = "CHECK-GROUP-EXISTS", group = ActionGroup.CHECKS) @@ -32,9 +32,21 @@ public final class CheckGroupExistsMapper { + " Optionally it can be used to verify that given group resides in specific path."; @Mapping( + examples = "CHECK-GROUP-EXISTS 'authors'", reference = REFERENCE ) - public Action mapAction(String id, @Named("path") String path) { + public Action mapAction( + @Required(value = "id", description = "group's id e.g.: 'authors'") String id) { + return mapAction(id, null); + } + + @Mapping( + examples = "CHECK-GROUP-EXISTS 'authors' '/home/groups/client/domain'", + reference = REFERENCE + ) + public Action mapAction( + @Required(value = "id", description = "group's id e.g.: 'authors'") String id, + @Required(value = "path", description = "group's home e.g.: '/home/groups/client/domain'") String path) { return new CheckAuthorizableExists(id, path, true); } diff --git a/app/aem/actions.checks/src/main/java/com/cognifide/apm/checks/actions/exists/CheckUserExistsMapper.java b/app/aem/actions.checks/src/main/java/com/cognifide/apm/checks/actions/exists/CheckUserExistsMapper.java index 938e9f16d..5078ee8cd 100644 --- a/app/aem/actions.checks/src/main/java/com/cognifide/apm/checks/actions/exists/CheckUserExistsMapper.java +++ b/app/aem/actions.checks/src/main/java/com/cognifide/apm/checks/actions/exists/CheckUserExistsMapper.java @@ -22,7 +22,7 @@ import com.cognifide.apm.api.actions.Action; import com.cognifide.apm.api.actions.annotations.Mapper; import com.cognifide.apm.api.actions.annotations.Mapping; -import com.cognifide.apm.api.actions.annotations.Named; +import com.cognifide.apm.api.actions.annotations.Required; import com.cognifide.apm.checks.actions.ActionGroup; @Mapper(value = "CHECK-USER-EXISTS", group = ActionGroup.CHECKS) @@ -32,9 +32,21 @@ public final class CheckUserExistsMapper { + " Optionally it can be used to verify that given user resides in specific path."; @Mapping( + examples = "CHECK-USER-EXISTS 'author'", reference = REFERENCE ) - public Action mapAction(String id, @Named("path") String path) { + public Action mapAction( + @Required(value = "id", description = "user's login e.g.: 'author'") String id) { + return mapAction(id, null); + } + + @Mapping( + examples = "CHECK-USER-EXISTS 'author' '/home/users/client/domain'", + reference = REFERENCE + ) + public Action mapAction( + @Required(value = "id", description = "user's login e.g.: 'author'") String id, + @Required(value = "path", description = "user's home e.g.: '/home/users/client/domain'") String path) { return new CheckAuthorizableExists(id, path, false); } } diff --git a/app/aem/actions.checks/src/main/java/com/cognifide/apm/checks/actions/include/CheckIncludes.java b/app/aem/actions.checks/src/main/java/com/cognifide/apm/checks/actions/include/CheckIncludes.java index 58b3eca04..c5c3565d5 100644 --- a/app/aem/actions.checks/src/main/java/com/cognifide/apm/checks/actions/include/CheckIncludes.java +++ b/app/aem/actions.checks/src/main/java/com/cognifide/apm/checks/actions/include/CheckIncludes.java @@ -23,6 +23,7 @@ import com.cognifide.apm.api.actions.ActionResult; import com.cognifide.apm.api.actions.Context; import com.cognifide.apm.api.exceptions.ActionExecutionException; +import com.cognifide.apm.api.exceptions.AuthorizableNotFoundException; import com.cognifide.apm.checks.utils.ActionUtils; import com.cognifide.apm.checks.utils.MessagingUtils; import java.util.ArrayList; @@ -37,7 +38,7 @@ public class CheckIncludes implements Action { private final String authorizableId; - public CheckIncludes(final String id, final List groupIds) { + public CheckIncludes(String id, List groupIds) { this.authorizableId = id; this.groupIds = groupIds; } @@ -48,11 +49,11 @@ public ActionResult simulate(Context context) { } @Override - public ActionResult execute(final Context context) { + public ActionResult execute(Context context) { return process(context, true); } - private ActionResult process(final Context context, boolean execute) { + private ActionResult process(Context context, boolean execute) { ActionResult actionResult = context.createActionResult(); Group authorizable = tryGetGroup(context, actionResult); if (authorizable == null) { @@ -71,11 +72,9 @@ private ActionResult process(final Context context, boolean execute) { ActionUtils.logErrors(errors, actionResult); return actionResult; - } - private boolean checkMembers(final Context context, final ActionResult actionResult, - final Group authorizable, final List errors) { + private boolean checkMembers(Context context, ActionResult actionResult, Group authorizable, List errors) { boolean checkFailed = false; for (String id : groupIds) { try { @@ -86,17 +85,17 @@ private boolean checkMembers(final Context context, final ActionResult actionRes checkFailed = true; } actionResult.logMessage(id + " is a member of group " + authorizableId); - } catch (RepositoryException | ActionExecutionException e) { + } catch (RepositoryException | ActionExecutionException | AuthorizableNotFoundException e) { errors.add(MessagingUtils.createMessage(e)); } } return checkFailed; } - private Group tryGetGroup(final Context context, final ActionResult actionResult) { + private Group tryGetGroup(Context context, ActionResult actionResult) { try { return context.getAuthorizableManager().getGroup(authorizableId); - } catch (RepositoryException | ActionExecutionException e) { + } catch (RepositoryException | ActionExecutionException | AuthorizableNotFoundException e) { actionResult.logError(MessagingUtils.createMessage(e)); } return null; diff --git a/app/aem/actions.checks/src/main/java/com/cognifide/apm/checks/actions/include/CheckIncludesMapper.java b/app/aem/actions.checks/src/main/java/com/cognifide/apm/checks/actions/include/CheckIncludesMapper.java index 56de9ebac..66791e8b3 100644 --- a/app/aem/actions.checks/src/main/java/com/cognifide/apm/checks/actions/include/CheckIncludesMapper.java +++ b/app/aem/actions.checks/src/main/java/com/cognifide/apm/checks/actions/include/CheckIncludesMapper.java @@ -22,6 +22,7 @@ import com.cognifide.apm.api.actions.Action; import com.cognifide.apm.api.actions.annotations.Mapper; import com.cognifide.apm.api.actions.annotations.Mapping; +import com.cognifide.apm.api.actions.annotations.Required; import com.cognifide.apm.checks.actions.ActionGroup; import java.util.Collections; import java.util.List; @@ -32,16 +33,22 @@ public final class CheckIncludesMapper { public static final String REFERENCE = "Verify that provided group contains all listed authorizables."; @Mapping( + examples = "CHECK-INCLUDES 'authors' 'author'", reference = REFERENCE ) - public Action mapAction(String id, String group) { - return mapAction(id, Collections.singletonList(group)); + public Action mapAction( + @Required(value = "group", description = "group's id e.g.: 'authors'") String group, + @Required(value = "id", description = "users' or groups' id e.g.: 'author'") String id) { + return mapAction(group, Collections.singletonList(id)); } @Mapping( + examples = "CHECK-INCLUDES 'authors' ['author']", reference = REFERENCE ) - public Action mapAction(String id, List groups) { - return new CheckIncludes(id, groups); + public Action mapAction( + @Required(value = "group", description = "group's id e.g.: 'authors'") String group, + @Required(value = "ids", description = "users' or groups' ids e.g.: ['author']") List ids) { + return new CheckIncludes(group, ids); } } diff --git a/app/aem/actions.checks/src/main/java/com/cognifide/apm/checks/actions/notexists/CheckNotExistsMapper.java b/app/aem/actions.checks/src/main/java/com/cognifide/apm/checks/actions/notexists/CheckNotExistsMapper.java index fc061552f..afabbbab2 100644 --- a/app/aem/actions.checks/src/main/java/com/cognifide/apm/checks/actions/notexists/CheckNotExistsMapper.java +++ b/app/aem/actions.checks/src/main/java/com/cognifide/apm/checks/actions/notexists/CheckNotExistsMapper.java @@ -22,6 +22,7 @@ import com.cognifide.apm.api.actions.Action; import com.cognifide.apm.api.actions.annotations.Mapper; import com.cognifide.apm.api.actions.annotations.Mapping; +import com.cognifide.apm.api.actions.annotations.Required; import com.cognifide.apm.checks.actions.ActionGroup; import java.util.Collections; import java.util.List; @@ -32,16 +33,20 @@ public final class CheckNotExistsMapper { public static final String REFERENCE = "Verify that specific authorizables do not exist."; @Mapping( + examples = "CHECK-NOT-EXISTS 'author'", reference = REFERENCE ) - public Action mapAction(String id) { + public Action mapAction( + @Required(value = "id", description = "users' or groups' id e.g.: 'author'") String id) { return mapAction(Collections.singletonList(id)); } @Mapping( + examples = "CHECK-NOT-EXISTS ['author']", reference = REFERENCE ) - public Action mapAction(List ids) { + public Action mapAction( + @Required(value = "ids", description = "users' or groups' ids e.g.: ['author']") List ids) { return new CheckNotExists(ids); } } diff --git a/app/aem/actions.checks/src/main/java/com/cognifide/apm/checks/actions/password/CheckPasswordMapper.java b/app/aem/actions.checks/src/main/java/com/cognifide/apm/checks/actions/password/CheckPasswordMapper.java index 1b478456c..4a3b5a6f4 100644 --- a/app/aem/actions.checks/src/main/java/com/cognifide/apm/checks/actions/password/CheckPasswordMapper.java +++ b/app/aem/actions.checks/src/main/java/com/cognifide/apm/checks/actions/password/CheckPasswordMapper.java @@ -22,6 +22,7 @@ import com.cognifide.apm.api.actions.Action; import com.cognifide.apm.api.actions.annotations.Mapper; import com.cognifide.apm.api.actions.annotations.Mapping; +import com.cognifide.apm.api.actions.annotations.Required; import com.cognifide.apm.checks.actions.ActionGroup; @Mapper(value = "CHECK-PASSWORD", group = ActionGroup.CHECKS) @@ -30,9 +31,12 @@ public final class CheckPasswordMapper { public static final String REFERENCE = "Verify that specific password is set for given authorizable."; @Mapping( + examples = "CHECK-PASSWORD 'author' 'p@$$w0rd'", reference = REFERENCE ) - public Action mapAction(String userId, String password) { - return new CheckPassword(userId, password); + public Action mapAction( + @Required(value = "id", description = "user's login e.g.: 'author'") String id, + @Required(value = "password", description = "user's password e.g.: 'p@$$w0rd'") String password) { + return new CheckPassword(id, password); } } diff --git a/app/aem/actions.checks/src/main/java/com/cognifide/apm/checks/actions/permissions/CheckAllowMapper.java b/app/aem/actions.checks/src/main/java/com/cognifide/apm/checks/actions/permissions/CheckAllowMapper.java index 9320b5060..d4ef3c8b8 100644 --- a/app/aem/actions.checks/src/main/java/com/cognifide/apm/checks/actions/permissions/CheckAllowMapper.java +++ b/app/aem/actions.checks/src/main/java/com/cognifide/apm/checks/actions/permissions/CheckAllowMapper.java @@ -23,7 +23,9 @@ import com.cognifide.apm.api.actions.annotations.Mapper; import com.cognifide.apm.api.actions.annotations.Mapping; import com.cognifide.apm.api.actions.annotations.Named; +import com.cognifide.apm.api.actions.annotations.Required; import com.cognifide.apm.checks.actions.ActionGroup; +import java.util.Collections; import java.util.List; @Mapper(value = "CHECK-ALLOW", group = ActionGroup.CHECKS) @@ -33,9 +35,14 @@ public final class CheckAllowMapper { + " on specified path."; @Mapping( + examples = "CHECK-ALLOW author '/content/dam' [READ, 'jcr:all']", reference = REFERENCE ) - public Action mapAction(String id, String path, List permissions, @Named("glob") String glob) { + public Action mapAction( + @Required(value = "id", description = "users' or groups' id e.g.: 'author'") String id, + @Required(value = "path", description = "e.g.: '/content/dam'") String path, + @Required(value = "permissions", description = "e.g.: [READ, 'jcr:all']") List permissions, + @Named(value = "glob", description = "regular expression to narrow set of paths") String glob) { return new CheckPermissions(id, path, glob, permissions, true); } } diff --git a/app/aem/actions.checks/src/main/java/com/cognifide/apm/checks/actions/permissions/CheckDenyMapper.java b/app/aem/actions.checks/src/main/java/com/cognifide/apm/checks/actions/permissions/CheckDenyMapper.java index 5f51e4bed..2f068078b 100644 --- a/app/aem/actions.checks/src/main/java/com/cognifide/apm/checks/actions/permissions/CheckDenyMapper.java +++ b/app/aem/actions.checks/src/main/java/com/cognifide/apm/checks/actions/permissions/CheckDenyMapper.java @@ -22,7 +22,10 @@ import com.cognifide.apm.api.actions.Action; import com.cognifide.apm.api.actions.annotations.Mapper; import com.cognifide.apm.api.actions.annotations.Mapping; +import com.cognifide.apm.api.actions.annotations.Named; +import com.cognifide.apm.api.actions.annotations.Required; import com.cognifide.apm.checks.actions.ActionGroup; +import java.util.Collections; import java.util.List; @Mapper(value = "CHECK-DENY", group = ActionGroup.CHECKS) @@ -32,9 +35,14 @@ public final class CheckDenyMapper { + " on specified path."; @Mapping( + examples = "CHECK-DENY author '/content/dam' [READ, 'jcr:all']", reference = REFERENCE ) - public Action mapAction(String id, String path, String glob, List permissions) { + public Action mapAction( + @Required(value = "id", description = "users' or groups' id e.g.: 'author'") String id, + @Required(value = "path", description = "e.g.: '/content/dam'") String path, + @Required(value = "permissions", description = "e.g.: [READ, 'jcr:all']") List permissions, + @Named(value = "glob", description = "regular expression to narrow set of paths") String glob) { return new CheckPermissions(id, path, glob, permissions, false); } } diff --git a/app/aem/actions.checks/src/main/java/com/cognifide/apm/checks/actions/permissions/CheckPermissions.java b/app/aem/actions.checks/src/main/java/com/cognifide/apm/checks/actions/permissions/CheckPermissions.java index 0c956c67f..9a27fc673 100644 --- a/app/aem/actions.checks/src/main/java/com/cognifide/apm/checks/actions/permissions/CheckPermissions.java +++ b/app/aem/actions.checks/src/main/java/com/cognifide/apm/checks/actions/permissions/CheckPermissions.java @@ -23,10 +23,10 @@ import com.cognifide.apm.api.actions.ActionResult; import com.cognifide.apm.api.actions.Context; import com.cognifide.apm.api.exceptions.ActionExecutionException; +import com.cognifide.apm.api.exceptions.AuthorizableNotFoundException; import com.cognifide.apm.checks.utils.ActionUtils; import com.cognifide.apm.checks.utils.MessagingUtils; import com.day.cq.security.util.CqActions; -import com.google.common.base.Function; import com.google.common.collect.Lists; import java.security.Principal; import java.util.ArrayList; @@ -37,13 +37,11 @@ import java.util.regex.Pattern; import javax.jcr.Node; import javax.jcr.NodeIterator; -import javax.jcr.PathNotFoundException; import javax.jcr.RepositoryException; import javax.jcr.Session; import org.apache.commons.lang3.StringUtils; import org.apache.jackrabbit.api.security.principal.PrincipalIterator; import org.apache.jackrabbit.api.security.user.Authorizable; -import org.apache.sling.api.resource.LoginException; public class CheckPermissions implements Action { @@ -57,8 +55,7 @@ public class CheckPermissions implements Action { private final String authorizableId; - public CheckPermissions(final String authorizableId, final String path, final String glob, - final List permissions, boolean isAllow) { + public CheckPermissions(String authorizableId, String path, String glob, List permissions, boolean isAllow) { this.authorizableId = authorizableId; this.path = path; this.glob = glob; @@ -72,47 +69,42 @@ public ActionResult simulate(Context context) { } @Override - public ActionResult execute(final Context context) { + public ActionResult execute(Context context) { return process(context, true); } - private ActionResult process(final Context context, boolean execute) { + private ActionResult process(Context context, boolean execute) { ActionResult actionResult = context.createActionResult(); try { - final Authorizable authorizable = context.getAuthorizableManager().getAuthorizable(authorizableId); + Authorizable authorizable = context.getAuthorizableManager().getAuthorizable(authorizableId); - final Set authorizablesToCheck = getAuthorizablesToCheck(authorizable, context); + Set authorizablesToCheck = getAuthorizablesToCheck(authorizable, context); - final CqActions actions = new CqActions(context.getSession()); + CqActions actions = new CqActions(context.getSession()); - final List privilegesToCheck = preparePrivilegesToCheck(); + List privilegesToCheck = preparePrivilegesToCheck(); if (StringUtils.isBlank(glob)) { if (checkPermissionsForPath(authorizablesToCheck, actions, privilegesToCheck, path)) { logFailure(execute, actionResult, authorizable, path); } else { - actionResult.logMessage( - "All required privileges are set for " + authorizable.getID() + " on " + path); + actionResult.logMessage("All required privileges are set for " + authorizable.getID() + " on " + path); } } else { checkPermissionsForGlob(context.getSession(), execute, actionResult, authorizable, authorizablesToCheck, - actions, - privilegesToCheck); + actions, privilegesToCheck); } - } catch (final PathNotFoundException e) { - actionResult.logError("Path " + path + " not found"); - } catch (RepositoryException | ActionExecutionException | LoginException e) { + } catch (RepositoryException | ActionExecutionException | AuthorizableNotFoundException e) { actionResult.logError(MessagingUtils.createMessage(e)); } return actionResult; } - private void checkPermissionsForGlob(Session session, final boolean execute, final ActionResult actionResult, - final Authorizable authorizable, final Set authorizablesToCheck, - final CqActions actions, final List privilegesToCheck) - throws RepositoryException, LoginException { - final List subpaths = getAllSubpaths(session, path); + private void checkPermissionsForGlob(Session session, boolean execute, ActionResult actionResult, + Authorizable authorizable, Set authorizablesToCheck, CqActions actions, List privilegesToCheck) + throws RepositoryException { + List subpaths = getAllSubpaths(session, path); Pattern pattern = Pattern.compile(path + StringUtils.replace(glob, "*", ".*")); boolean foundMatch = false; boolean failed = false; @@ -127,43 +119,36 @@ private void checkPermissionsForGlob(Session session, final boolean execute, fin } } if (!foundMatch) { - actionResult - .logError("No match was found for " + authorizable.getID() + " for given glob " + glob); + actionResult.logError("No match was found for " + authorizable.getID() + " for given glob " + glob); if (execute) { actionResult.logError(ActionUtils.ASSERTION_FAILED_MSG); } } else if (!failed) { - actionResult.logMessage( - "All required privileges are set for " + authorizable.getID() + " on " + path); + actionResult.logMessage("All required privileges are set for " + authorizable.getID() + " on " + path); } } - private boolean checkPermissionsForPath(final Set authorizablesToCheck, - final CqActions actions, final List privilegesToCheck, String subpath) - throws RepositoryException { + private boolean checkPermissionsForPath(Set authorizablesToCheck, CqActions actions, + List privilegesToCheck, String subpath) throws RepositoryException { Collection allowedActions = actions.getAllowedActions(subpath, authorizablesToCheck); - final boolean containsAll = allowedActions.containsAll(privilegesToCheck); + boolean containsAll = allowedActions.containsAll(privilegesToCheck); return (!containsAll && isAllow) || (containsAll && !isAllow); } - private void logFailure(boolean execute, ActionResult actionResult, final Authorizable authorizable, - String subpath) throws RepositoryException { - actionResult.logError( - "Not all required privileges are set for " + authorizable.getID() + " on " + subpath); + private static void logFailure(boolean execute, ActionResult actionResult, Authorizable authorizable, String subpath) + throws RepositoryException { + actionResult.logError("Not all required privileges are set for " + authorizable.getID() + " on " + subpath); if (execute) { actionResult.logError(ActionUtils.ASSERTION_FAILED_MSG); } } - private List getAllSubpaths(Session session, final String path) throws RepositoryException, LoginException { - List subPaths = new ArrayList<>(); + private static List getAllSubpaths(Session session, String path) throws RepositoryException { Node node = session.getNode(path); - subPaths.addAll(crawl(node)); - - return subPaths; + return new ArrayList<>(crawl(node)); } - private List crawl(final Node node) throws RepositoryException { + private static List crawl(Node node) throws RepositoryException { List paths = new ArrayList<>(); paths.add(node.getPath()); for (NodeIterator iter = node.getNodes(); iter.hasNext(); ) { @@ -172,7 +157,7 @@ private List crawl(final Node node) throws RepositoryException { return paths; } - private Set getAuthorizablesToCheck(Authorizable authorizable, Context context) + private static Set getAuthorizablesToCheck(Authorizable authorizable, Context context) throws RepositoryException { Set principals = new HashSet<>(); Principal principal = authorizable.getPrincipal(); @@ -186,15 +171,7 @@ private Set getAuthorizablesToCheck(Authorizable authorizable, Contex return principals; } - private List preparePrivilegesToCheck() throws RepositoryException { - return Lists.transform(permissions, new toLowerCase()); - } - - private static class toLowerCase implements Function { - - @Override - public String apply(String input) { - return input.toLowerCase(); - } + private List preparePrivilegesToCheck() { + return Lists.transform(permissions, String::toLowerCase); } } diff --git a/app/aem/actions.checks/src/main/java/com/cognifide/apm/checks/actions/property/CheckProperty.java b/app/aem/actions.checks/src/main/java/com/cognifide/apm/checks/actions/property/CheckProperty.java index 805f9e003..8fa6378cc 100644 --- a/app/aem/actions.checks/src/main/java/com/cognifide/apm/checks/actions/property/CheckProperty.java +++ b/app/aem/actions.checks/src/main/java/com/cognifide/apm/checks/actions/property/CheckProperty.java @@ -23,6 +23,7 @@ import com.cognifide.apm.api.actions.ActionResult; import com.cognifide.apm.api.actions.Context; import com.cognifide.apm.api.exceptions.ActionExecutionException; +import com.cognifide.apm.api.exceptions.AuthorizableNotFoundException; import com.cognifide.apm.checks.utils.ActionUtils; import com.cognifide.apm.checks.utils.MessagingUtils; import javax.jcr.PropertyType; @@ -39,7 +40,7 @@ public class CheckProperty implements Action { private final String authorizableId; - public CheckProperty(final String authorizableId, final String name, final String value) { + public CheckProperty(String authorizableId, String name, String value) { this.authorizableId = authorizableId; this.propertyName = name; this.propertyValue = value; @@ -51,11 +52,11 @@ public ActionResult simulate(Context context) { } @Override - public ActionResult execute(final Context context) { + public ActionResult execute(Context context) { return process(context, true); } - private ActionResult process(final Context context, boolean execute) { + private ActionResult process(Context context, boolean execute) { ActionResult actionResult = context.createActionResult(); try { Authorizable authorizable = context.getAuthorizableManager().getAuthorizable(authorizableId); @@ -68,30 +69,28 @@ private ActionResult process(final Context context, boolean execute) { return actionResult; } - actionResult.logError( - "Authorizable " + authorizableId + ": unexpected value of property: " + propertyName); + actionResult.logError("Authorizable " + authorizableId + ": unexpected value of property: " + propertyName); if (execute) { actionResult.logError(ActionUtils.ASSERTION_FAILED_MSG); } - } catch (final RepositoryException | ActionExecutionException e) { + } catch (RepositoryException | ActionExecutionException | AuthorizableNotFoundException e) { actionResult.logError(MessagingUtils.createMessage(e)); } return actionResult; } - private boolean checkPropertyExists(final Authorizable authorizable) throws RepositoryException { + private boolean checkPropertyExists(Authorizable authorizable) throws RepositoryException { Value[] values = authorizable.getProperty(propertyName); for (Value val : values) { - if ((val.getType() == PropertyType.STRING) && StringUtils - .equals(val.getString(), propertyValue)) { + if ((val.getType() == PropertyType.STRING) && StringUtils.equals(val.getString(), propertyValue)) { return true; } } return false; } - private boolean checkIfAuthHasProperty(boolean execute, ActionResult actionResult, - Authorizable authorizable) throws RepositoryException { + private boolean checkIfAuthHasProperty(boolean execute, ActionResult actionResult, Authorizable authorizable) + throws RepositoryException { if (!authorizable.hasProperty(propertyName)) { actionResult.logError("Authorizable " + authorizableId + ": no such property: " + propertyName); if (execute) { diff --git a/app/aem/actions.checks/src/main/java/com/cognifide/apm/checks/actions/property/CheckPropertyMapper.java b/app/aem/actions.checks/src/main/java/com/cognifide/apm/checks/actions/property/CheckPropertyMapper.java index c4cf1c69d..e7be20401 100644 --- a/app/aem/actions.checks/src/main/java/com/cognifide/apm/checks/actions/property/CheckPropertyMapper.java +++ b/app/aem/actions.checks/src/main/java/com/cognifide/apm/checks/actions/property/CheckPropertyMapper.java @@ -22,6 +22,7 @@ import com.cognifide.apm.api.actions.Action; import com.cognifide.apm.api.actions.annotations.Mapper; import com.cognifide.apm.api.actions.annotations.Mapping; +import com.cognifide.apm.api.actions.annotations.Required; import com.cognifide.apm.checks.actions.ActionGroup; @Mapper(value = "CHECK-PROPERTY", group = ActionGroup.CHECKS) @@ -30,9 +31,13 @@ public final class CheckPropertyMapper { public static final String REFERENCE = "Verify that a property is set to specific value for given authorizable."; @Mapping( + examples = "CHECK-PROPERTY 'author' 'title' 'John Doe'", reference = REFERENCE ) - public Action mapAction(String id, String name, String value) { + public Action mapAction( + @Required(value = "id", description = "users' or groups' id e.g.: 'author'") String id, + @Required(value = "name", description = "property name") String name, + @Required(value = "value", description = "property value") String value) { return new CheckProperty(id, name, value); } } diff --git a/app/aem/actions.main/src/main/java/com/cognifide/apm/main/actions/addchildren/AddChildren.java b/app/aem/actions.main/src/main/java/com/cognifide/apm/main/actions/addchildren/AddChildren.java index 23e37d1a8..e700b592f 100644 --- a/app/aem/actions.main/src/main/java/com/cognifide/apm/main/actions/addchildren/AddChildren.java +++ b/app/aem/actions.main/src/main/java/com/cognifide/apm/main/actions/addchildren/AddChildren.java @@ -23,9 +23,10 @@ import com.cognifide.apm.api.actions.ActionResult; import com.cognifide.apm.api.actions.Context; import com.cognifide.apm.api.exceptions.ActionExecutionException; +import com.cognifide.apm.api.exceptions.AuthorizableNotFoundException; +import com.cognifide.apm.api.status.Status; import com.cognifide.apm.main.utils.ActionUtils; import com.cognifide.apm.main.utils.MessagingUtils; -import java.util.ArrayList; import java.util.List; import javax.jcr.RepositoryException; import org.apache.commons.lang3.StringUtils; @@ -40,8 +41,11 @@ public class AddChildren implements Action { private final List authorizableIds; - public AddChildren(final List authorizableIds) { + private final boolean ignoreNonExistingAuthorizables; + + public AddChildren(List authorizableIds, boolean ignoreNonExistingAuthorizables) { this.authorizableIds = authorizableIds; + this.ignoreNonExistingAuthorizables = ignoreNonExistingAuthorizables; } @Override @@ -50,31 +54,25 @@ public ActionResult simulate(Context context) { } @Override - public ActionResult execute(final Context context) { + public ActionResult execute(Context context) { return process(context, true); } private ActionResult process(Context context, boolean execute) { ActionResult actionResult = context.createActionResult(); - Group group = null; + Group group; try { group = context.getCurrentGroup(); actionResult.setAuthorizable(group.getID()); LOGGER.info(String.format("Adding authorizables %s to group with id = %s", StringUtils.join(authorizableIds, ", "), group.getID())); - } catch (ActionExecutionException e) { - actionResult.logError(MessagingUtils.createMessage(e)); - return actionResult; - } catch (RepositoryException e) { + } catch (RepositoryException | ActionExecutionException e) { actionResult.logError(MessagingUtils.createMessage(e)); return actionResult; } - List errors = new ArrayList<>(); - for (String authorizableId : authorizableIds) { try { - Authorizable authorizable = context.getAuthorizableManager().getAuthorizable(authorizableId); if (authorizable.isGroup()) { @@ -87,14 +85,17 @@ private ActionResult process(Context context, boolean execute) { actionResult.logMessage(MessagingUtils.addedToGroup(authorizableId, group.getID())); } catch (RepositoryException | ActionExecutionException e) { - errors.add(MessagingUtils.createMessage(e)); + actionResult.logError(MessagingUtils.createMessage(e)); + } catch (AuthorizableNotFoundException e) { + if (ignoreNonExistingAuthorizables) { + actionResult.logWarning(MessagingUtils.createMessage(e)); + } else { + actionResult.logError(MessagingUtils.createMessage(e)); + } } } - if (!errors.isEmpty()) { - for (String error : errors) { - actionResult.logError(error); - } + if (actionResult.getStatus() == Status.ERROR) { actionResult.logError("Execution interrupted"); } return actionResult; diff --git a/app/aem/actions.main/src/main/java/com/cognifide/apm/main/actions/addchildren/AddChildrenMapper.java b/app/aem/actions.main/src/main/java/com/cognifide/apm/main/actions/addchildren/AddChildrenMapper.java index 3475fc255..6429898ab 100644 --- a/app/aem/actions.main/src/main/java/com/cognifide/apm/main/actions/addchildren/AddChildrenMapper.java +++ b/app/aem/actions.main/src/main/java/com/cognifide/apm/main/actions/addchildren/AddChildrenMapper.java @@ -19,7 +19,10 @@ */ package com.cognifide.apm.main.actions.addchildren; +import static com.cognifide.apm.main.actions.CommonFlags.IF_EXISTS; + import com.cognifide.apm.api.actions.Action; +import com.cognifide.apm.api.actions.annotations.Flag; import com.cognifide.apm.api.actions.annotations.Mapper; import com.cognifide.apm.api.actions.annotations.Mapping; import com.cognifide.apm.api.actions.annotations.Required; @@ -30,22 +33,30 @@ @Mapper(value = "ADD-CHILDREN", group = ActionGroup.CORE) public final class AddChildrenMapper { - public static final String REFERENCE = "Add specified users and groups to current group."; + private static final String REFERENCE = "Add specified users and groups to current group."; @Mapping( - examples = "ADD-CHILDREN 'authors'", + examples = { + "ADD-CHILDREN 'authors'", + "ADD-CHILDREN 'authors' --IF-EXISTS" + }, reference = REFERENCE ) - public Action mapAction(@Required(value = "id", description = "user's or group's id e.g.: 'author'") String id) { - return mapAction(Collections.singletonList(id)); + public Action mapAction(@Required(value = "id", description = "user's or group's id e.g.: 'author'") String id, + @Flag(value = IF_EXISTS, description = "script doesn't fail if authorizable doesn't exist") boolean ifExists) { + return mapAction(Collections.singletonList(id), ifExists); } @Mapping( - examples = "ADD-CHILDREN ['authors']", + examples = { + "ADD-CHILDREN ['authors']", + "ADD-CHILDREN ['authors'] --IF-EXISTS" + }, reference = REFERENCE ) public Action mapAction( - @Required(value = "ids", description = "users' or groups' ids e.g.: ['author']") List ids) { - return new AddChildren(ids); + @Required(value = "ids", description = "users' or groups' ids e.g.: ['author']") List ids, + @Flag(value = IF_EXISTS, description = "script doesn't fail if authorizable doesn't exist") boolean ifExists) { + return new AddChildren(ids, ifExists); } } diff --git a/app/aem/actions.main/src/main/java/com/cognifide/apm/main/actions/addparents/AddParents.java b/app/aem/actions.main/src/main/java/com/cognifide/apm/main/actions/addparents/AddParents.java index acd500331..e4201e0a7 100644 --- a/app/aem/actions.main/src/main/java/com/cognifide/apm/main/actions/addparents/AddParents.java +++ b/app/aem/actions.main/src/main/java/com/cognifide/apm/main/actions/addparents/AddParents.java @@ -23,9 +23,10 @@ import com.cognifide.apm.api.actions.ActionResult; import com.cognifide.apm.api.actions.Context; import com.cognifide.apm.api.exceptions.ActionExecutionException; +import com.cognifide.apm.api.exceptions.AuthorizableNotFoundException; +import com.cognifide.apm.api.status.Status; import com.cognifide.apm.main.utils.ActionUtils; import com.cognifide.apm.main.utils.MessagingUtils; -import java.util.ArrayList; import java.util.List; import javax.jcr.RepositoryException; import org.apache.jackrabbit.api.security.user.Authorizable; @@ -39,8 +40,11 @@ public class AddParents implements Action { private final List groupIds; - public AddParents(final List groupIds) { + private final boolean ignoreNonExistingGroups; + + public AddParents(List groupIds, boolean ignoreNonExistingGroups) { this.groupIds = groupIds; + this.ignoreNonExistingGroups = ignoreNonExistingGroups; } @Override @@ -49,14 +53,13 @@ public ActionResult simulate(Context context) { } @Override - public ActionResult execute(final Context context) { + public ActionResult execute(Context context) { return process(context, true); } - private ActionResult process(final Context context, boolean execute) { + private ActionResult process(Context context, boolean execute) { ActionResult actionResult = context.createActionResult(); - List errors = new ArrayList<>(); - Authorizable authorizable = null; + Authorizable authorizable; try { authorizable = context.getCurrentAuthorizable(); actionResult.setAuthorizable(authorizable.getID()); @@ -80,12 +83,17 @@ private ActionResult process(final Context context, boolean execute) { } actionResult.logMessage(MessagingUtils.addedToGroup(authorizable.getID(), id)); } catch (RepositoryException | ActionExecutionException e) { - errors.add(MessagingUtils.createMessage(e)); + actionResult.logError(MessagingUtils.createMessage(e)); + } catch (AuthorizableNotFoundException e) { + if (ignoreNonExistingGroups) { + actionResult.logWarning(MessagingUtils.createMessage(e)); + } else { + actionResult.logError(MessagingUtils.createMessage(e)); + } } } - if (!errors.isEmpty()) { - ActionUtils.logErrors(errors, actionResult); + if (actionResult.getStatus() == Status.ERROR) { actionResult.logError("Execution interrupted"); } return actionResult; diff --git a/app/aem/actions.main/src/main/java/com/cognifide/apm/main/actions/addparents/AddParentsMapper.java b/app/aem/actions.main/src/main/java/com/cognifide/apm/main/actions/addparents/AddParentsMapper.java index 35cc3e86e..7b0f2f854 100644 --- a/app/aem/actions.main/src/main/java/com/cognifide/apm/main/actions/addparents/AddParentsMapper.java +++ b/app/aem/actions.main/src/main/java/com/cognifide/apm/main/actions/addparents/AddParentsMapper.java @@ -19,7 +19,10 @@ */ package com.cognifide.apm.main.actions.addparents; +import static com.cognifide.apm.main.actions.CommonFlags.IF_EXISTS; + import com.cognifide.apm.api.actions.Action; +import com.cognifide.apm.api.actions.annotations.Flag; import com.cognifide.apm.api.actions.annotations.Mapper; import com.cognifide.apm.api.actions.annotations.Mapping; import com.cognifide.apm.api.actions.annotations.Required; @@ -30,23 +33,31 @@ @Mapper(value = "ADD-PARENTS", group = ActionGroup.CORE) public final class AddParentsMapper { - public static final String REFERENCE = "Add current authorizable to specified groups."; + private static final String REFERENCE = "Add current authorizable to specified groups."; @Mapping( - examples = "ADD-PARENTS 'authors'", + examples = { + "ADD-PARENTS 'authors'", + "ADD-PARENTS 'authors' --IF-EXISTS" + }, reference = REFERENCE ) public Action mapAction( - @Required(value = "group", description = "group") String group) { - return mapAction(Collections.singletonList(group)); + @Required(value = "group", description = "group") String group, + @Flag(value = IF_EXISTS, description = "script doesn't fail if group doesn't exist") boolean ifExists) { + return mapAction(Collections.singletonList(group), ifExists); } @Mapping( - examples = "ADD-PARENTS ['authors', 'publishers']", + examples = { + "ADD-PARENTS ['authors', 'publishers']", + "ADD-PARENTS ['authors', 'publishers'] --IF-EXISTS" + }, reference = REFERENCE ) public Action mapAction( - @Required(value = "groups", description = "list of groups") List groups) { - return new AddParents(groups); + @Required(value = "groups", description = "list of groups") List groups, + @Flag(value = IF_EXISTS, description = "script doesn't fail if group doesn't exist") boolean ifExists) { + return new AddParents(groups, ifExists); } } diff --git a/app/aem/actions.main/src/main/java/com/cognifide/apm/main/actions/addparents/AddToGroupMapper.java b/app/aem/actions.main/src/main/java/com/cognifide/apm/main/actions/addparents/AddToGroupMapper.java index 563839aa6..34376c86a 100644 --- a/app/aem/actions.main/src/main/java/com/cognifide/apm/main/actions/addparents/AddToGroupMapper.java +++ b/app/aem/actions.main/src/main/java/com/cognifide/apm/main/actions/addparents/AddToGroupMapper.java @@ -19,7 +19,10 @@ */ package com.cognifide.apm.main.actions.addparents; +import static com.cognifide.apm.main.actions.CommonFlags.IF_EXISTS; + import com.cognifide.apm.api.actions.Action; +import com.cognifide.apm.api.actions.annotations.Flag; import com.cognifide.apm.api.actions.annotations.Mapper; import com.cognifide.apm.api.actions.annotations.Mapping; import com.cognifide.apm.api.actions.annotations.Required; @@ -30,24 +33,32 @@ @Mapper(value = "ADD-TO-GROUP", group = ActionGroup.CORE) public final class AddToGroupMapper { - public static final String REFERENCE = "Add current authorizable to specified groups. " + private static final String REFERENCE = "Add current authorizable to specified groups. " + "Alias for ADD-PARENTS command."; @Mapping( - examples = "ADD-TO-GROUP 'authors'", + examples = { + "ADD-TO-GROUP 'authors'", + "ADD-TO-GROUP 'authors' --IF-EXISTS" + }, reference = REFERENCE ) public Action mapAction( - @Required(value = "group", description = "group") String group) { - return mapAction(Collections.singletonList(group)); + @Required(value = "group", description = "group") String group, + @Flag(value = IF_EXISTS, description = "script doesn't fail if group doesn't exist") boolean ifExists) { + return mapAction(Collections.singletonList(group), ifExists); } @Mapping( - examples = "ADD-TO-GROUP ['authors', 'publishers']", + examples = { + "ADD-TO-GROUP ['authors', 'publishers']", + "ADD-TO-GROUP ['authors', 'publishers'] --IF-EXISTS" + }, reference = REFERENCE ) public Action mapAction( - @Required(value = "groups", description = "list of groups") List groups) { - return new AddParents(groups); + @Required(value = "groups", description = "list of groups") List groups, + @Flag(value = IF_EXISTS, description = "script doesn't fail if group doesn't exist") boolean ifExists) { + return new AddParents(groups, ifExists); } } diff --git a/app/aem/actions.main/src/main/java/com/cognifide/apm/main/actions/allow/Allow.java b/app/aem/actions.main/src/main/java/com/cognifide/apm/main/actions/allow/Allow.java index 5bb5c849c..e6fd77e3e 100644 --- a/app/aem/actions.main/src/main/java/com/cognifide/apm/main/actions/allow/Allow.java +++ b/app/aem/actions.main/src/main/java/com/cognifide/apm/main/actions/allow/Allow.java @@ -31,6 +31,7 @@ import com.cognifide.apm.main.utils.PathUtils; import java.util.Collections; import java.util.List; +import java.util.Map; import javax.jcr.PathNotFoundException; import javax.jcr.RepositoryException; import org.apache.commons.lang3.StringUtils; @@ -51,25 +52,25 @@ public class Allow implements Action { private final boolean ignoreNonExistingPaths; public Allow(String path, List permissions, - String glob, List ntNames, - List itemNames, boolean ignoreNonExistingPaths) { + String glob, List ntNames, List itemNames, Map customRestrictions, + boolean ignoreNonExistingPaths) { this.path = path; this.permissions = permissions; - this.restrictions = new Restrictions(glob, ntNames, itemNames); + this.restrictions = new Restrictions(glob, ntNames, itemNames, customRestrictions); this.ignoreNonExistingPaths = ignoreNonExistingPaths; } @Override - public ActionResult simulate(final Context context) { + public ActionResult simulate(Context context) { return process(context, true); } @Override - public ActionResult execute(final Context context) { + public ActionResult execute(Context context) { return process(context, false); } - private ActionResult process(final Context context, boolean simulate) { + private ActionResult process(Context context, boolean simulate) { ActionResult actionResult = context.createActionResult(); try { Authorizable authorizable = context.getCurrentAuthorizable(); @@ -78,7 +79,7 @@ private ActionResult process(final Context context, boolean simulate) { actionResult.changeStatus(Status.SKIPPED, "Skipped adding allow privilege for " + authorizable.getID() + " on " + path); } else { context.getSession().getNode(path); - final PermissionActionHelper permissionActionHelper = new PermissionActionHelper( + PermissionActionHelper permissionActionHelper = new PermissionActionHelper( context.getValueFactory(), path, permissions, restrictions); LOGGER.info(String.format("Adding permissions %s for authorizable with id = %s for path = %s %s", permissions.toString(), context.getCurrentAuthorizable().getID(), path, restrictions)); @@ -92,11 +93,12 @@ private ActionResult process(final Context context, boolean simulate) { String preparedGlob = recalculateGlob(restrictions.getGlob()); new Allow(path, Collections.singletonList("MODIFY_PAGE"), preparedGlob + "*/jcr:content*", restrictions.getNtNames(), restrictions.getItemNames(), + restrictions.getCustomRestrictions(), ignoreNonExistingPaths ).process(context, simulate); } } - } catch (final PathNotFoundException e) { + } catch (PathNotFoundException e) { if (ignoreNonExistingPaths) { actionResult.logWarning("Path " + path + " not found"); } else { diff --git a/app/aem/actions.main/src/main/java/com/cognifide/apm/main/actions/allow/AllowMapper.java b/app/aem/actions.main/src/main/java/com/cognifide/apm/main/actions/allow/AllowMapper.java index 7589de79d..aac7f78f4 100644 --- a/app/aem/actions.main/src/main/java/com/cognifide/apm/main/actions/allow/AllowMapper.java +++ b/app/aem/actions.main/src/main/java/com/cognifide/apm/main/actions/allow/AllowMapper.java @@ -29,6 +29,7 @@ import com.cognifide.apm.api.actions.annotations.Required; import com.cognifide.apm.main.actions.ActionGroup; import java.util.List; +import java.util.Map; @Mapper(value = "ALLOW", group = ActionGroup.CORE) public class AllowMapper { @@ -51,9 +52,10 @@ public Action create( @Required(value = "permissions", description = "e.g.: [READ, 'jcr:all']") List permissions, @Named(value = "glob", description = "regular expression to narrow set of paths") String glob, @Named(value = "types", description = "list of jcr types which will be affected") List types, - @Named(value = "properties", description = "list of properties which will be affected ") List items, + @Named(value = "properties", description = "list of properties which will be affected") List items, + @Named(value = "restrictions", description = "map of custom restrictions") Map restrictions, @Flag(value = IF_EXISTS, description = "script doesn't fail if path doesn't exist") boolean ifExists) { - return new Allow(path, permissions, glob, types, items, ifExists); + return new Allow(path, permissions, glob, types, items, restrictions, ifExists); } @Mapping( @@ -71,8 +73,9 @@ public Action create( @Required(value = "path", description = "e.g.: '/content/dam'") String path, @Named(value = "glob", description = "regular expression to narrow set of paths") String glob, @Named(value = "types", description = "list of jcr types which will be affected") List types, - @Named(value = "properties", description = "list of properties which will be affected ") List items, + @Named(value = "properties", description = "list of properties which will be affected") List items, + @Named(value = "restrictions", description = "map of custom restrictions") Map restrictions, @Flag(value = IF_EXISTS, description = "script doesn't fail if path doesn't exist") boolean ifExists) { - return new Allow(path, permissions, glob, types, items, ifExists); + return new Allow(path, permissions, glob, types, items, restrictions, ifExists); } } diff --git a/app/aem/actions.main/src/main/java/com/cognifide/apm/main/actions/clearpermissions/Purge.java b/app/aem/actions.main/src/main/java/com/cognifide/apm/main/actions/clearpermissions/Purge.java index 4977f50df..326c55fc7 100644 --- a/app/aem/actions.main/src/main/java/com/cognifide/apm/main/actions/clearpermissions/Purge.java +++ b/app/aem/actions.main/src/main/java/com/cognifide/apm/main/actions/clearpermissions/Purge.java @@ -27,13 +27,23 @@ import com.cognifide.apm.api.status.Status; import com.cognifide.apm.main.utils.MessagingUtils; import com.cognifide.apm.main.utils.PathUtils; +import java.util.HashSet; +import java.util.List; +import java.util.Set; import javax.jcr.Node; import javax.jcr.NodeIterator; import javax.jcr.RepositoryException; +import javax.jcr.security.AccessControlPolicy; import org.apache.commons.lang3.StringUtils; import org.apache.jackrabbit.api.JackrabbitSession; +import org.apache.jackrabbit.api.security.JackrabbitAccessControlEntry; +import org.apache.jackrabbit.api.security.JackrabbitAccessControlManager; import org.apache.jackrabbit.api.security.user.Authorizable; +import org.apache.jackrabbit.oak.api.Type; +import org.apache.jackrabbit.oak.spi.security.authorization.accesscontrol.ACE; +import org.apache.jackrabbit.oak.spi.security.authorization.accesscontrol.AbstractAccessControlList; import org.apache.jackrabbit.oak.spi.security.authorization.permission.PermissionConstants; +import org.apache.jackrabbit.oak.spi.security.authorization.restriction.Restriction; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -45,7 +55,7 @@ public class Purge implements Action { private final String path; - public Purge(final String path) { + public Purge(String path) { this.path = path; } @@ -55,11 +65,11 @@ public ActionResult simulate(Context context) { } @Override - public ActionResult execute(final Context context) { + public ActionResult execute(Context context) { return process(context, true); } - private ActionResult process(final Context context, boolean execute) { + private ActionResult process(Context context, boolean execute) { ActionResult actionResult = context.createActionResult(); try { Authorizable authorizable = context.getCurrentAuthorizable(); @@ -67,7 +77,7 @@ private ActionResult process(final Context context, boolean execute) { if (context.isCompositeNodeStore() && PathUtils.isAppsOrLibsPath(path)) { actionResult.changeStatus(Status.SKIPPED, "Skipped purging privileges for " + authorizable.getID() + " on " + path); } else { - LOGGER.info(String.format("Purging privileges for authorizable with id = %s under path = %s", + LOGGER.info(String.format("Purging privileges for authorizable with id=%s under path=%s", authorizable.getID(), path)); if (execute) { purge(context, actionResult); @@ -81,23 +91,18 @@ private ActionResult process(final Context context, boolean execute) { return actionResult; } - private void purge(final Context context, final ActionResult actionResult) + private void purge(Context context, ActionResult actionResult) throws RepositoryException, ActionExecutionException { - NodeIterator iterator = getPermissions(context); + Set accessControlledPaths = getAccessControlledPaths(context); String normalizedPath = normalizePath(path); - while (iterator != null && iterator.hasNext()) { - Node node = iterator.nextNode(); - if (node.hasProperty(PermissionConstants.REP_ACCESS_CONTROLLED_PATH)) { - String parentPath = node.getProperty(PermissionConstants.REP_ACCESS_CONTROLLED_PATH) - .getString(); - String normalizedParentPath = normalizePath(parentPath); - boolean isUsersPermission = parentPath.startsWith(context.getCurrentAuthorizable().getPath()); - if (StringUtils.startsWith(normalizedParentPath, normalizedPath) && !isUsersPermission) { - RemoveAll removeAll = new RemoveAll(parentPath); - ActionResult removeAllResult = removeAll.execute(context); - if (Status.ERROR.equals(removeAllResult.getStatus())) { - copyErrorMessages(removeAllResult, actionResult); - } + for (String parentPath : accessControlledPaths) { + String normalizedParentPath = normalizePath(parentPath); + boolean isUsersPermission = parentPath.startsWith(context.getCurrentAuthorizable().getPath()); + if (StringUtils.startsWith(normalizedParentPath, normalizedPath) && !isUsersPermission) { + RemoveAll removeAll = new RemoveAll(parentPath); + ActionResult removeAllResult = removeAll.execute(context); + if (Status.ERROR.equals(removeAllResult.getStatus())) { + copyErrorMessages(removeAllResult, actionResult); } } } @@ -111,14 +116,33 @@ private void copyErrorMessages(ActionResult from, ActionResult to) { } } - private NodeIterator getPermissions(Context context) + private Set getAccessControlledPaths(Context context) throws ActionExecutionException, RepositoryException { + Set result = new HashSet<>(); JackrabbitSession session = context.getSession(); String path = PERMISSION_STORE_PATH + context.getCurrentAuthorizable().getID(); - NodeIterator result = null; if (session.nodeExists(path)) { Node node = session.getNode(path); - result = node.getNodes(); + NodeIterator nodes = node.getNodes(); + while (nodes.hasNext()) { + node = nodes.nextNode(); + if (node.hasProperty(PermissionConstants.REP_ACCESS_CONTROLLED_PATH)) { + result.add(node.getProperty(PermissionConstants.REP_ACCESS_CONTROLLED_PATH).getString()); + } + } + } else { + JackrabbitAccessControlManager accessControlManager = (JackrabbitAccessControlManager) session.getAccessControlManager(); + AccessControlPolicy[] accessControlPolicies = accessControlManager.getPolicies(context.getCurrentAuthorizable().getPrincipal()); + for (AccessControlPolicy accessControlPolicy : accessControlPolicies) { + AbstractAccessControlList abstractAccessControlList = (AbstractAccessControlList) accessControlPolicy; + List jackrabbitAccessControlEntries = abstractAccessControlList.getEntries(); + for (JackrabbitAccessControlEntry jackrabbitAccessControlEntry : jackrabbitAccessControlEntries) { + Set restrictions = ((ACE) jackrabbitAccessControlEntry).getRestrictions(); + for (Restriction restriction : restrictions) { + result.add(restriction.getProperty().getValue(Type.STRING)); + } + } + } } return result; } diff --git a/app/aem/actions.main/src/main/java/com/cognifide/apm/main/actions/createauthorizable/CreateAuthorizable.java b/app/aem/actions.main/src/main/java/com/cognifide/apm/main/actions/createauthorizable/CreateAuthorizable.java index 0658b8274..d8d2fa63b 100644 --- a/app/aem/actions.main/src/main/java/com/cognifide/apm/main/actions/createauthorizable/CreateAuthorizable.java +++ b/app/aem/actions.main/src/main/java/com/cognifide/apm/main/actions/createauthorizable/CreateAuthorizable.java @@ -23,6 +23,7 @@ import com.cognifide.apm.api.actions.ActionResult; import com.cognifide.apm.api.actions.Context; import com.cognifide.apm.api.status.Status; +import com.cognifide.apm.main.actions.forauthorizable.ForAuthorizable; import com.cognifide.apm.main.utils.MessagingUtils; import javax.jcr.RepositoryException; import org.apache.jackrabbit.api.security.user.Authorizable; @@ -42,12 +43,12 @@ public class CreateAuthorizable implements Action { private final String externalId; - private final Boolean ignoreIfExists; + private final boolean ignoreIfExists; private final CreateAuthorizableStrategy createStrategy; public CreateAuthorizable(String id, String password, String path, String externalId, - Boolean ignoreIfExists, CreateAuthorizableStrategy createStrategy) { + boolean ignoreIfExists, CreateAuthorizableStrategy createStrategy) { this.id = id; this.password = password; this.path = path; @@ -70,16 +71,19 @@ public ActionResult process(Context context, boolean simulate) { ActionResult actionResult = context.createActionResult(); try { Authorizable authorizable = context.getAuthorizableManager().getAuthorizableIfExists(id); - LOGGER.info("Creating authorizable with id = " + id); + LOGGER.info("Creating authorizable with id = {}", id); if (authorizable != null) { logMessage(actionResult, authorizable); } else { - authorizable = createStrategy.create(id, password, path, externalId, context, actionResult, simulate); + createStrategy.create(id, password, path, externalId, context, actionResult, simulate); } - context.setCurrentAuthorizable(authorizable); } catch (RepositoryException e) { actionResult.logError(MessagingUtils.createMessage(e)); } + if (actionResult.getStatus() != Status.ERROR) { + ActionResult forAuthorizableActionResult = new ForAuthorizable(id, false, createStrategy == CreateAuthorizableStrategy.GROUP).process(context); + actionResult.changeStatus(forAuthorizableActionResult.getStatus(), forAuthorizableActionResult.getMessages().get(0).getText()); + } return actionResult; } diff --git a/app/aem/actions.main/src/main/java/com/cognifide/apm/main/actions/deletegroup/DeleteGroup.java b/app/aem/actions.main/src/main/java/com/cognifide/apm/main/actions/deletegroup/DeleteGroup.java index 13215fb85..99caa6a35 100644 --- a/app/aem/actions.main/src/main/java/com/cognifide/apm/main/actions/deletegroup/DeleteGroup.java +++ b/app/aem/actions.main/src/main/java/com/cognifide/apm/main/actions/deletegroup/DeleteGroup.java @@ -23,8 +23,9 @@ import com.cognifide.apm.api.actions.ActionResult; import com.cognifide.apm.api.actions.Context; import com.cognifide.apm.api.exceptions.ActionExecutionException; +import com.cognifide.apm.api.exceptions.AuthorizableNotFoundException; +import com.cognifide.apm.api.status.Status; import com.cognifide.apm.main.utils.MessagingUtils; -import java.util.ArrayList; import java.util.List; import javax.jcr.RepositoryException; import org.apache.commons.lang3.StringUtils; @@ -38,7 +39,7 @@ public class DeleteGroup implements Action { private final List ids; - public DeleteGroup(final List ids) { + public DeleteGroup(List ids) { this.ids = ids; } @@ -48,34 +49,30 @@ public ActionResult simulate(Context context) { } @Override - public ActionResult execute(final Context context) { + public ActionResult execute(Context context) { return process(context, true); } - private ActionResult process(final Context context, boolean execute) { + private ActionResult process(Context context, boolean execute) { ActionResult actionResult = context.createActionResult(); - List errors = new ArrayList<>(); LOGGER.info(String.format("Removing groups with ids = %s", StringUtils.join(ids, ", "))); for (String id : ids) { try { - Group group = context.getAuthorizableManager().getGroupIfExists(id); - if (group != null) { - context.getAuthorizableManager().markAuthorizableAsRemoved(group); - if (execute) { - context.getAuthorizableManager().removeGroup(group); - } - actionResult.logMessage("Group with id: " + id + " removed"); + Group group = context.getAuthorizableManager().getGroup(id); + context.getAuthorizableManager().markAuthorizableAsRemoved(group); + if (execute) { + context.getAuthorizableManager().removeGroup(group); } - + actionResult.logMessage("Group with id: " + id + " removed"); } catch (RepositoryException | ActionExecutionException e) { - errors.add(MessagingUtils.createMessage(e)); + actionResult.logError(MessagingUtils.createMessage(e)); + } catch (AuthorizableNotFoundException e) { + actionResult.logWarning(MessagingUtils.createMessage(e)); } } - if (!errors.isEmpty()) { - for (String error : errors) { - actionResult.logError(error); - } + + if (actionResult.getStatus() == Status.ERROR) { actionResult.logError("Execution interrupted"); } return actionResult; diff --git a/app/aem/actions.main/src/main/java/com/cognifide/apm/main/actions/deletegroup/DeleteGroupMapper.java b/app/aem/actions.main/src/main/java/com/cognifide/apm/main/actions/deletegroup/DeleteGroupMapper.java index 061671c62..79ecb7858 100644 --- a/app/aem/actions.main/src/main/java/com/cognifide/apm/main/actions/deletegroup/DeleteGroupMapper.java +++ b/app/aem/actions.main/src/main/java/com/cognifide/apm/main/actions/deletegroup/DeleteGroupMapper.java @@ -30,7 +30,7 @@ @Mapper(value = "DELETE-GROUP", group = ActionGroup.CORE) public final class DeleteGroupMapper { - public static final String REFERENCE = "Remove specified groups.\n" + private static final String REFERENCE = "Remove specified groups.\n" + "No group's members are removed, but they no longer belong to the removed group (reference is removed).\n" + "Note that no permissions for removed group are cleaned, so after creating a new group with the same id" + " - it will automatically gain those permissions."; @@ -51,5 +51,4 @@ public Action mapAction( @Required(value = "groupIds", description = "groups' ids e.g.: ['authors']") List ids) { return new DeleteGroup(ids); } - } diff --git a/app/aem/actions.main/src/main/java/com/cognifide/apm/main/actions/deleteuser/DeleteUserMapper.java b/app/aem/actions.main/src/main/java/com/cognifide/apm/main/actions/deleteuser/DeleteUserMapper.java index 1c0c109fc..b99413996 100644 --- a/app/aem/actions.main/src/main/java/com/cognifide/apm/main/actions/deleteuser/DeleteUserMapper.java +++ b/app/aem/actions.main/src/main/java/com/cognifide/apm/main/actions/deleteuser/DeleteUserMapper.java @@ -33,10 +33,11 @@ @Mapper(value = "DELETE-USER", group = ActionGroup.CORE) public final class DeleteUserMapper { - public static final String CLEAR_PERMISSIONS = "CLEAR-PERMISSIONS"; - public static final String CLEAR_PERMISSIONS_DESC = "additionally clears permissions related to given user"; + private static final String CLEAR_PERMISSIONS = "CLEAR-PERMISSIONS"; - public static final String REFERENCE = "Remove specified users.\n" + private static final String CLEAR_PERMISSIONS_DESC = "additionally clears permissions related to given user"; + + private static final String REFERENCE = "Remove specified users.\n" + "Removed user are no longer listed as any group members.\n" + "Note that no permissions for removed users are cleaned, so after creating a new user with the same id" + " - it will automatically gain those permissions."; @@ -69,7 +70,7 @@ public Action create( @Required(value = "userIds", description = "users' ids e.g.: ['author']") List ids, @Flag(value = CLEAR_PERMISSIONS, description = CLEAR_PERMISSIONS_DESC) boolean clearPermissions) { if (clearPermissions) { - List actions = ids.stream().map(id -> new DestroyUser(id)).collect(Collectors.toList()); + List actions = ids.stream().map(DestroyUser::new).collect(Collectors.toList()); return new CompositeAction(actions); } else { return new RemoveUser(ids); diff --git a/app/aem/actions.main/src/main/java/com/cognifide/apm/main/actions/deleteuser/DestroyUser.java b/app/aem/actions.main/src/main/java/com/cognifide/apm/main/actions/deleteuser/DestroyUser.java index a1ce5d6d7..902230842 100644 --- a/app/aem/actions.main/src/main/java/com/cognifide/apm/main/actions/deleteuser/DestroyUser.java +++ b/app/aem/actions.main/src/main/java/com/cognifide/apm/main/actions/deleteuser/DestroyUser.java @@ -23,6 +23,7 @@ import com.cognifide.apm.api.actions.ActionResult; import com.cognifide.apm.api.actions.Context; import com.cognifide.apm.api.exceptions.ActionExecutionException; +import com.cognifide.apm.api.exceptions.AuthorizableNotFoundException; import com.cognifide.apm.main.actions.clearpermissions.Purge; import com.cognifide.apm.main.actions.removeparents.RemoveParents; import com.cognifide.apm.main.utils.MessagingUtils; @@ -49,12 +50,12 @@ public DestroyUser(String userId) { } @Override - public ActionResult simulate(Context context) throws ActionExecutionException { + public ActionResult simulate(Context context) { ActionResult actionResult; try { User user = context.getAuthorizableManager().getUser(userId); context.setCurrentAuthorizable(user); - Action removeFromGroups = new RemoveParents(getGroups(user)); + Action removeFromGroups = new RemoveParents(getGroups(user), true); ActionResult purgeResult = purge.simulate(context); ActionResult removeFromGroupsResult = removeFromGroups.execute(context); ActionResult removeResult = remove.simulate(context); @@ -62,19 +63,22 @@ public ActionResult simulate(Context context) throws ActionExecutionException { } catch (RepositoryException | ActionExecutionException e) { actionResult = context.createActionResult(); actionResult.logError(MessagingUtils.createMessage(e)); + } catch (AuthorizableNotFoundException e) { + actionResult = context.createActionResult(); + actionResult.logWarning(MessagingUtils.createMessage(e)); } return actionResult; } @Override - public ActionResult execute(Context context) throws ActionExecutionException { + public ActionResult execute(Context context) { ActionResult actionResult; try { User user = context.getAuthorizableManager().getUser(userId); // local context is used here to not override current authorizable in given context Context localContext = context.newContext(); localContext.setCurrentAuthorizable(user); - Action removeFromGroups = new RemoveParents(getGroups(user)); + Action removeFromGroups = new RemoveParents(getGroups(user), true); ActionResult purgeResult = purge.execute(localContext); ActionResult removeFromGroupsResult = removeFromGroups.execute(localContext); ActionResult removeResult = remove.execute(localContext); @@ -83,11 +87,14 @@ public ActionResult execute(Context context) throws ActionExecutionException { } catch (RepositoryException | ActionExecutionException e) { actionResult = context.createActionResult(); actionResult.logError(MessagingUtils.createMessage(e)); + } catch (AuthorizableNotFoundException e) { + actionResult = context.createActionResult(); + actionResult.logWarning(MessagingUtils.createMessage(e)); } return actionResult; } - private List getGroups(User user) throws RepositoryException { + private static List getGroups(User user) throws RepositoryException { List groups = new ArrayList<>(); Iterator groupIterator = user.declaredMemberOf(); while (groupIterator.hasNext()) { diff --git a/app/aem/actions.main/src/main/java/com/cognifide/apm/main/actions/deleteuser/RemoveUser.java b/app/aem/actions.main/src/main/java/com/cognifide/apm/main/actions/deleteuser/RemoveUser.java index 16c3d3c72..1c8c74e3a 100644 --- a/app/aem/actions.main/src/main/java/com/cognifide/apm/main/actions/deleteuser/RemoveUser.java +++ b/app/aem/actions.main/src/main/java/com/cognifide/apm/main/actions/deleteuser/RemoveUser.java @@ -23,8 +23,9 @@ import com.cognifide.apm.api.actions.ActionResult; import com.cognifide.apm.api.actions.Context; import com.cognifide.apm.api.exceptions.ActionExecutionException; +import com.cognifide.apm.api.exceptions.AuthorizableNotFoundException; +import com.cognifide.apm.api.status.Status; import com.cognifide.apm.main.utils.MessagingUtils; -import java.util.ArrayList; import java.util.List; import javax.jcr.RepositoryException; import org.apache.commons.lang3.StringUtils; @@ -38,7 +39,7 @@ public class RemoveUser implements Action { private final List ids; - public RemoveUser(final List ids) { + public RemoveUser(List ids) { this.ids = ids; } @@ -48,33 +49,29 @@ public ActionResult simulate(Context context) { } @Override - public ActionResult execute(final Context context) { + public ActionResult execute(Context context) { return process(context, true); } - private ActionResult process(final Context context, boolean execute) { + private ActionResult process(Context context, boolean execute) { ActionResult actionResult = context.createActionResult(); - List errors = new ArrayList<>(); LOGGER.info(String.format("Removing users with ids = %s", StringUtils.join(ids, ", "))); for (String id : ids) { try { - User user = context.getAuthorizableManager().getUserIfExists(id); - if (user != null) { - context.getAuthorizableManager().markAuthorizableAsRemoved(user); - if (execute) { - context.getAuthorizableManager().removeUser(user); - } - actionResult.logMessage("User with id: " + id + " removed"); + User user = context.getAuthorizableManager().getUser(id); + context.getAuthorizableManager().markAuthorizableAsRemoved(user); + if (execute) { + context.getAuthorizableManager().removeUser(user); } + actionResult.logMessage("User with id: " + id + " removed"); } catch (RepositoryException | ActionExecutionException e) { - errors.add(MessagingUtils.createMessage(e)); + actionResult.logError(MessagingUtils.createMessage(e)); + } catch (AuthorizableNotFoundException e) { + actionResult.logWarning(MessagingUtils.createMessage(e)); } } - if (!errors.isEmpty()) { - for (String error : errors) { - actionResult.logError(error); - } + if (actionResult.getStatus() == Status.ERROR) { actionResult.logError("Execution interrupted"); } return actionResult; diff --git a/app/aem/actions.main/src/main/java/com/cognifide/apm/main/actions/deny/Deny.java b/app/aem/actions.main/src/main/java/com/cognifide/apm/main/actions/deny/Deny.java index a7be3e53a..5e37dbabf 100644 --- a/app/aem/actions.main/src/main/java/com/cognifide/apm/main/actions/deny/Deny.java +++ b/app/aem/actions.main/src/main/java/com/cognifide/apm/main/actions/deny/Deny.java @@ -31,6 +31,7 @@ import com.cognifide.apm.main.utils.PathUtils; import java.util.ArrayList; import java.util.List; +import java.util.Map; import javax.jcr.PathNotFoundException; import javax.jcr.RepositoryException; import org.apache.commons.lang3.StringUtils; @@ -50,26 +51,26 @@ public class Deny implements Action { private final boolean ignoreNonExistingPaths; - public Deny(final String path, final List permissions, - final String glob, List ntNames, final List itemNames, - final boolean ignoreNonExistingPaths) { + public Deny(String path, List permissions, + String glob, List ntNames, List itemNames, Map customRestrictions, + boolean ignoreNonExistingPaths) { this.path = path; this.permissions = permissions; - this.restrictions = new Restrictions(glob, ntNames, itemNames); + this.restrictions = new Restrictions(glob, ntNames, itemNames, customRestrictions); this.ignoreNonExistingPaths = ignoreNonExistingPaths; } @Override - public ActionResult simulate(final Context context) { + public ActionResult simulate(Context context) { return process(context, true); } @Override - public ActionResult execute(final Context context) { + public ActionResult execute(Context context) { return process(context, false); } - private ActionResult process(final Context context, boolean simulate) { + private ActionResult process(Context context, boolean simulate) { ActionResult actionResult = context.createActionResult(); try { Authorizable authorizable = context.getCurrentAuthorizable(); @@ -78,7 +79,7 @@ private ActionResult process(final Context context, boolean simulate) { actionResult.changeStatus(Status.SKIPPED, "Skipped adding deny privilege for " + authorizable.getID() + " on " + path); } else { context.getSession().getNode(path); - final PermissionActionHelper permissionActionHelper = new PermissionActionHelper( + PermissionActionHelper permissionActionHelper = new PermissionActionHelper( context.getValueFactory(), path, permissions, restrictions); LOGGER.info(String.format("Denying permissions %s for authorizable with id = %s for path = %s %s", permissions.toString(), context.getCurrentAuthorizable().getID(), path, restrictions)); @@ -93,18 +94,18 @@ private ActionResult process(final Context context, boolean simulate) { globModifyPermission.add("MODIFY_PAGE"); String preparedGlob = recalculateGlob(restrictions.getGlob()); new Deny(path, globModifyPermission, - preparedGlob + "*/jcr:content*", restrictions.getNtNames(), restrictions.getItemNames(), + preparedGlob + "*/jcr:content*", restrictions.getNtNames(), restrictions.getItemNames(), restrictions.getCustomRestrictions(), ignoreNonExistingPaths) .process(context, simulate); } } - } catch (final PathNotFoundException e) { + } catch (PathNotFoundException e) { if (ignoreNonExistingPaths) { actionResult.logWarning("Path " + path + " not found"); } else { actionResult.logError("Path " + path + " not found"); } - } catch (final RepositoryException | PermissionException | ActionExecutionException e) { + } catch (RepositoryException | PermissionException | ActionExecutionException e) { actionResult.logError(MessagingUtils.createMessage(e)); } return actionResult; diff --git a/app/aem/actions.main/src/main/java/com/cognifide/apm/main/actions/deny/DenyMapper.java b/app/aem/actions.main/src/main/java/com/cognifide/apm/main/actions/deny/DenyMapper.java index 27c7fee89..1f48c14bb 100644 --- a/app/aem/actions.main/src/main/java/com/cognifide/apm/main/actions/deny/DenyMapper.java +++ b/app/aem/actions.main/src/main/java/com/cognifide/apm/main/actions/deny/DenyMapper.java @@ -29,6 +29,7 @@ import com.cognifide.apm.api.actions.annotations.Required; import com.cognifide.apm.main.actions.ActionGroup; import java.util.List; +import java.util.Map; @Mapper(value = "DENY", group = ActionGroup.CORE) public class DenyMapper { @@ -51,9 +52,10 @@ public Action create( @Required(value = "permissions", description = "e.g.: [READ, 'jcr:all']") List permissions, @Named(value = "glob", description = "regular expression to narrow set of paths") String glob, @Named(value = "types", description = "list of jcr types which will be affected") List types, - @Named(value = "properties", description = "list of properties which will be affected ") List items, + @Named(value = "properties", description = "list of properties which will be affected") List items, + @Named(value = "restrictions", description = "map of custom restrictions") Map restrictions, @Flag(value = IF_EXISTS, description = "script doesn't fail if path doesn't exist") boolean ifExists) { - return new Deny(path, permissions, glob, types, items, ifExists); + return new Deny(path, permissions, glob, types, items, restrictions, ifExists); } @Mapping( @@ -71,8 +73,9 @@ public Action create( @Required(value = "path", description = "e.g.: '/content/dam'") String path, @Named(value = "glob", description = "regular expression to narrow set of paths") String glob, @Named(value = "types", description = "list of jcr types which will be affected") List types, - @Named(value = "properties", description = "list of properties which will be affected ") List items, + @Named(value = "properties", description = "list of properties which will be affected") List items, + @Named(value = "restrictions", description = "map of custom restrictions") Map restrictions, @Flag(value = IF_EXISTS, description = "script doesn't fail if path doesn't exist") boolean ifExists) { - return new Deny(path, permissions, glob, types, items, ifExists); + return new Deny(path, permissions, glob, types, items, restrictions, ifExists); } } diff --git a/app/aem/actions.main/src/main/java/com/cognifide/apm/main/actions/forauthorizable/ForAuthorizable.java b/app/aem/actions.main/src/main/java/com/cognifide/apm/main/actions/forauthorizable/ForAuthorizable.java index 4a27c9ca8..6d89da22b 100644 --- a/app/aem/actions.main/src/main/java/com/cognifide/apm/main/actions/forauthorizable/ForAuthorizable.java +++ b/app/aem/actions.main/src/main/java/com/cognifide/apm/main/actions/forauthorizable/ForAuthorizable.java @@ -23,6 +23,7 @@ import com.cognifide.apm.api.actions.ActionResult; import com.cognifide.apm.api.actions.Context; import com.cognifide.apm.api.exceptions.ActionExecutionException; +import com.cognifide.apm.api.exceptions.AuthorizableNotFoundException; import com.cognifide.apm.main.utils.MessagingUtils; import javax.jcr.RepositoryException; import org.apache.jackrabbit.api.security.user.Group; @@ -32,24 +33,27 @@ public class ForAuthorizable implements Action { private final String id; - private final Boolean shouldBeGroup; + private final boolean ignoreNonExistingAuthorizable; - public ForAuthorizable(final String id, final Boolean shouldBeGroup) { + private final boolean shouldBeGroup; + + public ForAuthorizable(String id, boolean ignoreNonExistingAuthorizable, boolean shouldBeGroup) { this.id = id; + this.ignoreNonExistingAuthorizable = ignoreNonExistingAuthorizable; this.shouldBeGroup = shouldBeGroup; } @Override - public ActionResult simulate(final Context context) { + public ActionResult simulate(Context context) { return process(context); } @Override - public ActionResult execute(final Context context) { + public ActionResult execute(Context context) { return process(context); } - public ActionResult process(final Context context) { + public ActionResult process(Context context) { ActionResult actionResult = context.createActionResult(); try { @@ -65,6 +69,12 @@ public ActionResult process(final Context context) { } catch (RepositoryException | ActionExecutionException e) { actionResult.logError(MessagingUtils.createMessage(e)); + } catch (AuthorizableNotFoundException e) { + if (ignoreNonExistingAuthorizable) { + actionResult.logWarning(MessagingUtils.createMessage(e)); + } else { + actionResult.logError(MessagingUtils.createMessage(e)); + } } return actionResult; } diff --git a/app/aem/actions.main/src/main/java/com/cognifide/apm/main/actions/forauthorizable/ForGroupMapper.java b/app/aem/actions.main/src/main/java/com/cognifide/apm/main/actions/forauthorizable/ForGroupMapper.java index 8c6d067e2..bb9624d25 100644 --- a/app/aem/actions.main/src/main/java/com/cognifide/apm/main/actions/forauthorizable/ForGroupMapper.java +++ b/app/aem/actions.main/src/main/java/com/cognifide/apm/main/actions/forauthorizable/ForGroupMapper.java @@ -19,7 +19,10 @@ */ package com.cognifide.apm.main.actions.forauthorizable; +import static com.cognifide.apm.main.actions.CommonFlags.IF_EXISTS; + import com.cognifide.apm.api.actions.Action; +import com.cognifide.apm.api.actions.annotations.Flag; import com.cognifide.apm.api.actions.annotations.Mapper; import com.cognifide.apm.api.actions.annotations.Mapping; import com.cognifide.apm.api.actions.annotations.Required; @@ -29,10 +32,14 @@ public final class ForGroupMapper { @Mapping( - examples = {"FOR-GROUP 'authors' BEGIN ... END"}, + examples = { + "FOR-GROUP 'authors' BEGIN ... END", + "FOR-GROUP 'authors' --IF-EXISTS BEGIN ... END" + }, reference = "Set specified group as a current authorizable for execution context." ) - public Action mapAction(@Required(value = "groupId", description = "group's id e.g.: 'authors'") String groupId) { - return new ForAuthorizable(groupId, true); + public Action mapAction(@Required(value = "groupId", description = "group's id e.g.: 'authors'") String groupId, + @Flag(value = IF_EXISTS, description = "script doesn't fail if group doesn't exist") boolean ifExists) { + return new ForAuthorizable(groupId, ifExists, true); } } diff --git a/app/aem/actions.main/src/main/java/com/cognifide/apm/main/actions/forauthorizable/ForUserMapper.java b/app/aem/actions.main/src/main/java/com/cognifide/apm/main/actions/forauthorizable/ForUserMapper.java index 03f8aa073..5fa0affb8 100644 --- a/app/aem/actions.main/src/main/java/com/cognifide/apm/main/actions/forauthorizable/ForUserMapper.java +++ b/app/aem/actions.main/src/main/java/com/cognifide/apm/main/actions/forauthorizable/ForUserMapper.java @@ -19,7 +19,10 @@ */ package com.cognifide.apm.main.actions.forauthorizable; +import static com.cognifide.apm.main.actions.CommonFlags.IF_EXISTS; + import com.cognifide.apm.api.actions.Action; +import com.cognifide.apm.api.actions.annotations.Flag; import com.cognifide.apm.api.actions.annotations.Mapper; import com.cognifide.apm.api.actions.annotations.Mapping; import com.cognifide.apm.api.actions.annotations.Required; @@ -29,10 +32,14 @@ public final class ForUserMapper { @Mapping( - examples = {"FOR-USER 'author' BEGIN ... END"}, + examples = { + "FOR-USER 'author' BEGIN ... END", + "FOR-USER 'author' --IF-EXISTS BEGIN ... END" + }, reference = "Set specified user as a current authorizable for execution context." ) - public Action mapAction(@Required(value = "userId", description = "user's id e.g.: 'author'") String userId) { - return new ForAuthorizable(userId, false); + public Action mapAction(@Required(value = "userId", description = "user's id e.g.: 'author'") String userId, + @Flag(value = IF_EXISTS, description = "script doesn't fail if user doesn't exist") boolean ifExists) { + return new ForAuthorizable(userId, ifExists, false); } } diff --git a/app/aem/actions.main/src/main/java/com/cognifide/apm/main/actions/internal/ForEachMapper.java b/app/aem/actions.main/src/main/java/com/cognifide/apm/main/actions/internal/ForEachMapper.java index fb1700232..cb41e8df3 100644 --- a/app/aem/actions.main/src/main/java/com/cognifide/apm/main/actions/internal/ForEachMapper.java +++ b/app/aem/actions.main/src/main/java/com/cognifide/apm/main/actions/internal/ForEachMapper.java @@ -31,7 +31,7 @@ public final class ForEachMapper { @Mapping( examples = { "FOR-EACH i IN ['a', 'b'] BEGIN ... END", - "FOR-EACH [i, j] IN [['a', 'b'], ['c', 'd']] BEGIN ... END" + "FOR-EACH i IN [{x: 'a', y: 'b'}, {x: 'c', y: 'd'}] BEGIN ... END" }, reference = "Executes commands in block of code for each item in given array." ) diff --git a/app/aem/actions.main/src/main/java/com/cognifide/apm/main/actions/removechildren/RemoveChildren.java b/app/aem/actions.main/src/main/java/com/cognifide/apm/main/actions/removechildren/RemoveChildren.java index aad0662d5..14e784861 100644 --- a/app/aem/actions.main/src/main/java/com/cognifide/apm/main/actions/removechildren/RemoveChildren.java +++ b/app/aem/actions.main/src/main/java/com/cognifide/apm/main/actions/removechildren/RemoveChildren.java @@ -23,8 +23,9 @@ import com.cognifide.apm.api.actions.ActionResult; import com.cognifide.apm.api.actions.Context; import com.cognifide.apm.api.exceptions.ActionExecutionException; +import com.cognifide.apm.api.exceptions.AuthorizableNotFoundException; +import com.cognifide.apm.api.status.Status; import com.cognifide.apm.main.utils.MessagingUtils; -import java.util.ArrayList; import java.util.List; import javax.jcr.RepositoryException; import org.apache.commons.lang3.StringUtils; @@ -39,7 +40,7 @@ public class RemoveChildren implements Action { private final List authorizableIds; - public RemoveChildren(final List authorizableIds) { + public RemoveChildren(List authorizableIds) { this.authorizableIds = authorizableIds; } @@ -49,51 +50,40 @@ public ActionResult simulate(Context context) { } @Override - public ActionResult execute(final Context context) { + public ActionResult execute(Context context) { return process(context, true); } - private ActionResult process(final Context context, boolean execute) { + private ActionResult process(Context context, boolean execute) { ActionResult actionResult = context.createActionResult(); - Group group = null; + Group group; try { group = context.getCurrentGroup(); actionResult.setAuthorizable(group.getID()); LOGGER.info(String.format("Removing authorizables %s from group with id = %s", StringUtils.join(authorizableIds, ", "), group.getID())); - } catch (ActionExecutionException e) { - actionResult.logError(MessagingUtils.createMessage(e)); - return actionResult; - } catch (RepositoryException e) { + } catch (RepositoryException | ActionExecutionException e) { actionResult.logError(MessagingUtils.createMessage(e)); return actionResult; } - List errors = new ArrayList<>(); for (String authorizableId : authorizableIds) { try { - - Authorizable authorizable = context.getAuthorizableManager().getAuthorizableIfExists(authorizableId); - - if (authorizable == null) { - actionResult.logWarning(MessagingUtils.authorizableNotExists(authorizableId)); - continue; - } + Authorizable authorizable = context.getAuthorizableManager().getAuthorizable(authorizableId); if (execute) { group.removeMember(authorizable); } actionResult.logMessage(MessagingUtils.removedFromGroup(authorizableId, group.getID())); - } catch (RepositoryException e) { - errors.add(MessagingUtils.createMessage(e)); + } catch (RepositoryException | ActionExecutionException e) { + actionResult.logError(MessagingUtils.createMessage(e)); + } catch (AuthorizableNotFoundException e) { + actionResult.logWarning(MessagingUtils.createMessage(e)); } } - if (!errors.isEmpty()) { - for (String error : errors) { - actionResult.logError(error); - } + if (actionResult.getStatus() == Status.ERROR) { actionResult.logError("Execution interrupted"); } return actionResult; diff --git a/app/aem/actions.main/src/main/java/com/cognifide/apm/main/actions/removeparents/RemoveFromGroupMapper.java b/app/aem/actions.main/src/main/java/com/cognifide/apm/main/actions/removeparents/RemoveFromGroupMapper.java index a834deb37..01e907116 100644 --- a/app/aem/actions.main/src/main/java/com/cognifide/apm/main/actions/removeparents/RemoveFromGroupMapper.java +++ b/app/aem/actions.main/src/main/java/com/cognifide/apm/main/actions/removeparents/RemoveFromGroupMapper.java @@ -19,7 +19,10 @@ */ package com.cognifide.apm.main.actions.removeparents; +import static com.cognifide.apm.main.actions.CommonFlags.IF_EXISTS; + import com.cognifide.apm.api.actions.Action; +import com.cognifide.apm.api.actions.annotations.Flag; import com.cognifide.apm.api.actions.annotations.Mapper; import com.cognifide.apm.api.actions.annotations.Mapping; import com.cognifide.apm.api.actions.annotations.Required; @@ -30,23 +33,31 @@ @Mapper(value = "REMOVE-FROM-GROUP", group = ActionGroup.CORE) public final class RemoveFromGroupMapper { - public static final String REFERENCE = "Remove current authorizable from specified groups. " + private static final String REFERENCE = "Remove current authorizable from specified groups. " + "Alias for REMOVE-PARENTS command."; @Mapping( - examples = "REMOVE-FROM-GROUP 'authors'", + examples = { + "REMOVE-FROM-GROUP 'authors'", + "REMOVE-FROM-GROUP 'authors' --IF-EXISTS" + }, reference = REFERENCE ) - public Action mapAction(@Required(value = "groupId", description = "group's id e.g.: 'authors'") String id) { - return mapAction(Collections.singletonList(id)); + public Action mapAction(@Required(value = "groupId", description = "group's id e.g.: 'authors'") String id, + @Flag(value = IF_EXISTS, description = "script doesn't fail if group doesn't exist") boolean ifExists) { + return mapAction(Collections.singletonList(id), ifExists); } @Mapping( - examples = "REMOVE-FROM-GROUP ['authors']", + examples = { + "REMOVE-FROM-GROUP ['authors']", + "REMOVE-FROM-GROUP ['authors'] --IF-EXISTS" + }, reference = REFERENCE ) public Action mapAction( - @Required(value = "groupIds", description = "groups' ids e.g.: ['authors']") List ids) { - return new RemoveParents(ids); + @Required(value = "groupIds", description = "groups' ids e.g.: ['authors']") List ids, + @Flag(value = IF_EXISTS, description = "script doesn't fail if group doesn't exist") boolean ifExists) { + return new RemoveParents(ids, ifExists); } } diff --git a/app/aem/actions.main/src/main/java/com/cognifide/apm/main/actions/removeparents/RemoveParents.java b/app/aem/actions.main/src/main/java/com/cognifide/apm/main/actions/removeparents/RemoveParents.java index 698a159ad..c3c596f3d 100644 --- a/app/aem/actions.main/src/main/java/com/cognifide/apm/main/actions/removeparents/RemoveParents.java +++ b/app/aem/actions.main/src/main/java/com/cognifide/apm/main/actions/removeparents/RemoveParents.java @@ -23,8 +23,9 @@ import com.cognifide.apm.api.actions.ActionResult; import com.cognifide.apm.api.actions.Context; import com.cognifide.apm.api.exceptions.ActionExecutionException; +import com.cognifide.apm.api.exceptions.AuthorizableNotFoundException; +import com.cognifide.apm.api.status.Status; import com.cognifide.apm.main.utils.MessagingUtils; -import java.util.ArrayList; import java.util.List; import javax.jcr.RepositoryException; import org.apache.jackrabbit.api.security.user.Authorizable; @@ -38,8 +39,11 @@ public class RemoveParents implements Action { private final List groupIds; - public RemoveParents(final List groupIds) { + private final boolean ignoreNonExistingGroups; + + public RemoveParents(List groupIds, boolean ignoreNonExistingGroups) { this.groupIds = groupIds; + this.ignoreNonExistingGroups = ignoreNonExistingGroups; } @Override @@ -48,24 +52,20 @@ public ActionResult simulate(Context context) { } @Override - public ActionResult execute(final Context context) { + public ActionResult execute(Context context) { return process(context, true); } - public ActionResult process(final Context context, boolean execute) { + public ActionResult process(Context context, boolean execute) { ActionResult actionResult = context.createActionResult(); - List errors = new ArrayList<>(); - Authorizable authorizable = null; + Authorizable authorizable; try { authorizable = context.getCurrentAuthorizable(); actionResult.setAuthorizable(authorizable.getID()); LOGGER.info( String.format("Removing authorizable with id = %s from groups %s", authorizable.getID(), groupIds)); - } catch (ActionExecutionException e) { - actionResult.logError(MessagingUtils.createMessage(e)); - return actionResult; - } catch (RepositoryException e) { + } catch (RepositoryException | ActionExecutionException e) { actionResult.logError(MessagingUtils.createMessage(e)); return actionResult; } @@ -80,14 +80,17 @@ public ActionResult process(final Context context, boolean execute) { actionResult.logMessage(MessagingUtils.removedFromGroup(authorizable.getID(), id)); } catch (RepositoryException | ActionExecutionException e) { - errors.add(MessagingUtils.createMessage(e)); + actionResult.logError(MessagingUtils.createMessage(e)); + } catch (AuthorizableNotFoundException e) { + if (ignoreNonExistingGroups) { + actionResult.logWarning(MessagingUtils.createMessage(e)); + } else { + actionResult.logError(MessagingUtils.createMessage(e)); + } } } - if (!errors.isEmpty()) { - for (String error : errors) { - actionResult.logError(error); - } + if (actionResult.getStatus() == Status.ERROR) { actionResult.logError("Execution interrupted"); } return actionResult; diff --git a/app/aem/actions.main/src/main/java/com/cognifide/apm/main/actions/removeparents/RemoveParentsMapper.java b/app/aem/actions.main/src/main/java/com/cognifide/apm/main/actions/removeparents/RemoveParentsMapper.java index aeddc9bec..89c33efaf 100644 --- a/app/aem/actions.main/src/main/java/com/cognifide/apm/main/actions/removeparents/RemoveParentsMapper.java +++ b/app/aem/actions.main/src/main/java/com/cognifide/apm/main/actions/removeparents/RemoveParentsMapper.java @@ -19,7 +19,10 @@ */ package com.cognifide.apm.main.actions.removeparents; +import static com.cognifide.apm.main.actions.CommonFlags.IF_EXISTS; + import com.cognifide.apm.api.actions.Action; +import com.cognifide.apm.api.actions.annotations.Flag; import com.cognifide.apm.api.actions.annotations.Mapper; import com.cognifide.apm.api.actions.annotations.Mapping; import com.cognifide.apm.api.actions.annotations.Required; @@ -30,22 +33,30 @@ @Mapper(value = "REMOVE-PARENTS", group = ActionGroup.CORE) public final class RemoveParentsMapper { - public static final String REFERENCE = "Remove current authorizable from specified groups."; + private static final String REFERENCE = "Remove current authorizable from specified groups."; @Mapping( - examples = "REMOVE-PARENTS 'authors'", + examples = { + "REMOVE-PARENTS 'authors'", + "REMOVE-PARENTS 'authors' --IF-EXISTS" + }, reference = REFERENCE ) - public Action mapAction(@Required(value = "groupId", description = "group's id e.g.: 'authors'") String id) { - return mapAction(Collections.singletonList(id)); + public Action mapAction(@Required(value = "groupId", description = "group's id e.g.: 'authors'") String id, + @Flag(value = IF_EXISTS, description = "script doesn't fail if group doesn't exist") boolean ifExists) { + return mapAction(Collections.singletonList(id), ifExists); } @Mapping( - examples = "REMOVE-PARENTS ['authors']", + examples = { + "REMOVE-PARENTS ['authors']", + "REMOVE-PARENTS ['authors'] --IF-EXISTS" + }, reference = REFERENCE ) public Action mapAction( - @Required(value = "groupIds", description = "groups' ids e.g.: ['authors']") List ids) { - return new RemoveParents(ids); + @Required(value = "groupIds", description = "groups' ids e.g.: ['authors']") List ids, + @Flag(value = IF_EXISTS, description = "script doesn't fail if group doesn't exist") boolean ifExists) { + return new RemoveParents(ids, ifExists); } } diff --git a/app/aem/actions.main/src/main/java/com/cognifide/apm/main/permissions/Restrictions.java b/app/aem/actions.main/src/main/java/com/cognifide/apm/main/permissions/Restrictions.java index 4d70e075a..e219e32c7 100644 --- a/app/aem/actions.main/src/main/java/com/cognifide/apm/main/permissions/Restrictions.java +++ b/app/aem/actions.main/src/main/java/com/cognifide/apm/main/permissions/Restrictions.java @@ -21,6 +21,7 @@ package com.cognifide.apm.main.permissions; import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableMap; import java.util.Collections; import java.util.HashMap; import java.util.List; @@ -40,27 +41,49 @@ public class Restrictions { private static final String STRICT = "STRICT"; private final String glob; + private final List ntNames; + private final List itemNames; - public Restrictions(String glob, List ntNames, List itemNames) { + private final Map customRestrictions; + + public Restrictions(String glob, List ntNames, List itemNames, Map customRestrictions) { this.glob = glob; this.ntNames = notNullCopy(ntNames); this.itemNames = notNullCopy(itemNames); + this.customRestrictions = notNullCopy(customRestrictions); } private List notNullCopy(List strings) { return strings != null ? ImmutableList.copyOf(strings) : Collections.emptyList(); } - public Map getSingleValueRestrictions(ValueFactory valueFactory) { + private Map notNullCopy(Map items) { + return items != null ? ImmutableMap.copyOf(items) : Collections.emptyMap(); + } + + public Map getSingleValueRestrictions(ValueFactory valueFactory) throws ValueFormatException { Map result = new HashMap<>(); - if (StringUtils.isNotBlank(glob)) { - result.put("rep:glob", normalizeGlob(valueFactory)); + addRestriction(valueFactory, result, "rep:glob", glob); + for (Map.Entry entry : customRestrictions.entrySet()) { + if (entry.getValue() instanceof String) { + addRestriction(valueFactory, result, entry.getKey(), (String) entry.getValue()); + } } return result; } + private void addRestriction(ValueFactory valueFactory, Map result, String key, String value) throws ValueFormatException { + if (StringUtils.isNotBlank(value)) { + if (key.equals("rep:glob")) { + result.put(key, normalizeGlob(valueFactory)); + } else { + result.put(key, valueFactory.createValue(value, PropertyType.NAME)); + } + } + } + private Value normalizeGlob(ValueFactory valueFactory) { if (STRICT.equalsIgnoreCase(glob)) { return valueFactory.createValue(StringUtils.EMPTY); @@ -68,26 +91,25 @@ private Value normalizeGlob(ValueFactory valueFactory) { return valueFactory.createValue(glob); } - public Map getMultiValueRestrictions(ValueFactory valueFactory) - throws ValueFormatException { - + public Map getMultiValueRestrictions(ValueFactory valueFactory) throws ValueFormatException { Map result = new HashMap<>(); addRestrictions(valueFactory, result, "rep:ntNames", ntNames); addRestrictions(valueFactory, result, "rep:itemNames", itemNames); + for (Map.Entry entry : customRestrictions.entrySet()) { + if (entry.getValue() instanceof List) { + addRestrictions(valueFactory, result, entry.getKey(), (List) entry.getValue()); + } + } return result; } - private void addRestrictions(ValueFactory valueFactory, Map result, String key, List names) - throws ValueFormatException { - + private void addRestrictions(ValueFactory valueFactory, Map result, String key, List names) throws ValueFormatException { if (names != null && !names.isEmpty()) { result.put(key, createRestrictions(valueFactory, names)); } } - private Value[] createRestrictions(ValueFactory valueFactory, List names) - throws ValueFormatException { - + private Value[] createRestrictions(ValueFactory valueFactory, List names) throws ValueFormatException { Value[] values = new Value[names.size()]; for (int index = 0; index < names.size(); index++) { values[index] = valueFactory.createValue(names.get(index), PropertyType.NAME); diff --git a/app/aem/actions.main/src/main/java/com/cognifide/apm/main/permissions/exceptions/PermissionException.java b/app/aem/actions.main/src/main/java/com/cognifide/apm/main/permissions/exceptions/PermissionException.java index fd881ff43..01b0dc4a4 100644 --- a/app/aem/actions.main/src/main/java/com/cognifide/apm/main/permissions/exceptions/PermissionException.java +++ b/app/aem/actions.main/src/main/java/com/cognifide/apm/main/permissions/exceptions/PermissionException.java @@ -21,8 +21,6 @@ public class PermissionException extends Exception { - private static final long serialVersionUID = -764235266128215763L; - public PermissionException(String message) { super(message); } diff --git a/app/aem/api/src/main/java/com/cognifide/apm/api/actions/AuthorizableManager.java b/app/aem/api/src/main/java/com/cognifide/apm/api/actions/AuthorizableManager.java index ba381d9be..bd2786ed0 100644 --- a/app/aem/api/src/main/java/com/cognifide/apm/api/actions/AuthorizableManager.java +++ b/app/aem/api/src/main/java/com/cognifide/apm/api/actions/AuthorizableManager.java @@ -21,6 +21,7 @@ package com.cognifide.apm.api.actions; import com.cognifide.apm.api.exceptions.ActionExecutionException; +import com.cognifide.apm.api.exceptions.AuthorizableNotFoundException; import java.security.Principal; import javax.jcr.RepositoryException; import org.apache.jackrabbit.api.security.user.Authorizable; @@ -31,13 +32,13 @@ public interface AuthorizableManager { Authorizable getAuthorizableIfExists(String id) throws RepositoryException; - Authorizable getAuthorizable(String id) throws ActionExecutionException, RepositoryException; + Authorizable getAuthorizable(String id) throws ActionExecutionException, RepositoryException, AuthorizableNotFoundException; void markAuthorizableAsRemoved(Authorizable authorizable) throws RepositoryException; Group getGroupIfExists(String id) throws RepositoryException, ActionExecutionException; - Group getGroup(String id) throws ActionExecutionException, RepositoryException; + Group getGroup(String id) throws ActionExecutionException, RepositoryException, AuthorizableNotFoundException; Group createGroup(String id, Principal namePrincipal, String path) throws RepositoryException; @@ -47,7 +48,7 @@ public interface AuthorizableManager { User getUserIfExists(String id) throws ActionExecutionException, RepositoryException; - User getUser(String id) throws ActionExecutionException, RepositoryException; + User getUser(String id) throws ActionExecutionException, RepositoryException, AuthorizableNotFoundException; User createUser(String id, String password, Principal namePrincipal, String path) throws RepositoryException; diff --git a/app/aem/api/src/main/java/com/cognifide/apm/api/exceptions/ActionCreationException.java b/app/aem/api/src/main/java/com/cognifide/apm/api/exceptions/ActionCreationException.java index 390a8d61f..d83e37527 100644 --- a/app/aem/api/src/main/java/com/cognifide/apm/api/exceptions/ActionCreationException.java +++ b/app/aem/api/src/main/java/com/cognifide/apm/api/exceptions/ActionCreationException.java @@ -21,8 +21,6 @@ public class ActionCreationException extends ActionException { - private static final long serialVersionUID = -6238146921748809870L; - public ActionCreationException(final String message) { super(message); } diff --git a/app/aem/api/src/main/java/com/cognifide/apm/api/exceptions/ActionException.java b/app/aem/api/src/main/java/com/cognifide/apm/api/exceptions/ActionException.java index a214d776d..0f50d3494 100644 --- a/app/aem/api/src/main/java/com/cognifide/apm/api/exceptions/ActionException.java +++ b/app/aem/api/src/main/java/com/cognifide/apm/api/exceptions/ActionException.java @@ -21,8 +21,6 @@ public class ActionException extends ExecutionException { - private static final long serialVersionUID = -6238146921748809870L; - public ActionException(final String message) { super(message); } diff --git a/app/aem/api/src/main/java/com/cognifide/apm/api/exceptions/ActionExecutionException.java b/app/aem/api/src/main/java/com/cognifide/apm/api/exceptions/ActionExecutionException.java index 2567f5035..f32156fb9 100644 --- a/app/aem/api/src/main/java/com/cognifide/apm/api/exceptions/ActionExecutionException.java +++ b/app/aem/api/src/main/java/com/cognifide/apm/api/exceptions/ActionExecutionException.java @@ -21,8 +21,6 @@ public class ActionExecutionException extends ActionException { - private static final long serialVersionUID = -6238146921748809870L; - public ActionExecutionException(final String message) { super(message); } diff --git a/app/aem/api/src/main/java/com/cognifide/apm/api/exceptions/AuthorizableNotFoundException.java b/app/aem/api/src/main/java/com/cognifide/apm/api/exceptions/AuthorizableNotFoundException.java new file mode 100644 index 000000000..0fab607ac --- /dev/null +++ b/app/aem/api/src/main/java/com/cognifide/apm/api/exceptions/AuthorizableNotFoundException.java @@ -0,0 +1,31 @@ +/*- + * ========================LICENSE_START================================= + * AEM Permission Management + * %% + * Copyright (C) 2013 Wunderman Thompson Technology + * %% + * Licensed 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. + * =========================LICENSE_END================================== + */ +package com.cognifide.apm.api.exceptions; + +public class AuthorizableNotFoundException extends ActionException { + + public AuthorizableNotFoundException(String message) { + super(message); + } + + public AuthorizableNotFoundException(String message, Throwable throwable) { + super(message, throwable); + } +} diff --git a/app/aem/api/src/main/java/com/cognifide/apm/api/exceptions/ExecutionException.java b/app/aem/api/src/main/java/com/cognifide/apm/api/exceptions/ExecutionException.java index 514b1a2ea..55a783cba 100644 --- a/app/aem/api/src/main/java/com/cognifide/apm/api/exceptions/ExecutionException.java +++ b/app/aem/api/src/main/java/com/cognifide/apm/api/exceptions/ExecutionException.java @@ -21,8 +21,6 @@ public class ExecutionException extends Exception { - private static final long serialVersionUID = -6238146921748809870L; - public ExecutionException(final String message) { super(message); } diff --git a/app/aem/api/src/main/java/com/cognifide/apm/api/exceptions/InitializationException.java b/app/aem/api/src/main/java/com/cognifide/apm/api/exceptions/InitializationException.java index 5dbbbc2c6..2c3035317 100644 --- a/app/aem/api/src/main/java/com/cognifide/apm/api/exceptions/InitializationException.java +++ b/app/aem/api/src/main/java/com/cognifide/apm/api/exceptions/InitializationException.java @@ -21,8 +21,6 @@ public class InitializationException extends ExecutionException { - private static final long serialVersionUID = -6238146921748809870L; - public InitializationException(final String message) { super(message); } diff --git a/app/aem/api/src/main/java/com/cognifide/apm/api/exceptions/InvalidActionMapperException.java b/app/aem/api/src/main/java/com/cognifide/apm/api/exceptions/InvalidActionMapperException.java index e90b907e4..8edbbd042 100644 --- a/app/aem/api/src/main/java/com/cognifide/apm/api/exceptions/InvalidActionMapperException.java +++ b/app/aem/api/src/main/java/com/cognifide/apm/api/exceptions/InvalidActionMapperException.java @@ -21,8 +21,6 @@ public class InvalidActionMapperException extends RuntimeException { - private static final long serialVersionUID = -6238146921748809870L; - public InvalidActionMapperException(final String message) { super(message); } diff --git a/app/aem/api/src/main/java/com/cognifide/apm/api/services/ScriptManager.java b/app/aem/api/src/main/java/com/cognifide/apm/api/services/ScriptManager.java index dcfda978b..dc6886def 100644 --- a/app/aem/api/src/main/java/com/cognifide/apm/api/services/ScriptManager.java +++ b/app/aem/api/src/main/java/com/cognifide/apm/api/services/ScriptManager.java @@ -20,6 +20,7 @@ package com.cognifide.apm.api.services; import com.cognifide.apm.api.scripts.Script; +import java.util.Collections; import java.util.Map; import javax.jcr.RepositoryException; import org.apache.sling.api.resource.PersistenceException; @@ -30,13 +31,31 @@ public interface ScriptManager { /** * Fail-safe execution of script in concrete mode (dry run, automatic execution, validation) */ - ExecutionResult process(Script script, ExecutionMode mode, ResourceResolver resolver) - throws RepositoryException, PersistenceException; + default ExecutionResult process(Script script, ExecutionMode mode, ResourceResolver resolver) + throws RepositoryException, PersistenceException { + return process(script, mode, resolver, resolver.getUserID()); + } + + /** + * Fail-safe execution of script in concrete mode (dry run, automatic execution, validation) + */ + default ExecutionResult process(Script script, ExecutionMode mode, Map customDefinitions, ResourceResolver resolver) + throws RepositoryException, PersistenceException { + return process(script, mode, customDefinitions, resolver, resolver.getUserID()); + } + + /** + * Fail-safe execution of script in concrete mode (dry run, automatic execution, validation) + */ + default ExecutionResult process(Script script, ExecutionMode mode, ResourceResolver resolver, String executor) + throws RepositoryException, PersistenceException { + return process(script, mode, Collections.emptyMap(), resolver, executor); + } /** * Fail-safe execution of script in concrete mode (dry run, automatic execution, validation) */ - ExecutionResult process(Script script, ExecutionMode mode, Map customDefinitions, ResourceResolver resolver) + ExecutionResult process(Script script, ExecutionMode mode, Map customDefinitions, ResourceResolver resolver, String executor) throws RepositoryException, PersistenceException; /** diff --git a/app/aem/api/src/main/java/com/cognifide/apm/api/status/Status.java b/app/aem/api/src/main/java/com/cognifide/apm/api/status/Status.java index a265e0d3a..dd8c1def1 100644 --- a/app/aem/api/src/main/java/com/cognifide/apm/api/status/Status.java +++ b/app/aem/api/src/main/java/com/cognifide/apm/api/status/Status.java @@ -21,10 +21,10 @@ public enum Status { - ERROR("entry-error", "skipped", "chevronRight"), + ERROR("entry-error", "error", "close"), WARNING("entry-warning", "warning", "alert"), SUCCESS("entry-success", "success", "check"), - SKIPPED("entry-warning", "error", "close"); + SKIPPED("entry-warning", "skipped", "chevronRight"); private final String className; diff --git a/app/aem/core/src/main/antlr/ApmLang.g4 b/app/aem/core/src/main/antlr/ApmLang.g4 index 9428dc380..320302369 100644 --- a/app/aem/core/src/main/antlr/ApmLang.g4 +++ b/app/aem/core/src/main/antlr/ApmLang.g4 @@ -25,7 +25,7 @@ grammar ApmLang; */ apm - : command+ + : command+ EOF ; name diff --git a/app/aem/core/src/main/java/com/cognifide/apm/core/actions/MapperDescriptorFactory.java b/app/aem/core/src/main/java/com/cognifide/apm/core/actions/MapperDescriptorFactory.java index 1c336c03d..d0822fa0e 100644 --- a/app/aem/core/src/main/java/com/cognifide/apm/core/actions/MapperDescriptorFactory.java +++ b/app/aem/core/src/main/java/com/cognifide/apm/core/actions/MapperDescriptorFactory.java @@ -34,6 +34,7 @@ import com.cognifide.apm.core.actions.ParameterDescriptor.RequiredParameterDescriptor; import com.cognifide.apm.core.grammar.ApmInteger; import com.cognifide.apm.core.grammar.ApmList; +import com.cognifide.apm.core.grammar.ApmMap; import com.cognifide.apm.core.grammar.ApmString; import com.cognifide.apm.core.grammar.ApmType; import com.google.common.collect.ImmutableList; @@ -43,6 +44,7 @@ import java.lang.reflect.ParameterizedType; import java.lang.reflect.Type; import java.util.List; +import java.util.Map; import java.util.Optional; public class MapperDescriptorFactory { @@ -53,10 +55,10 @@ public MapperDescriptor create(Class mapperClass) { throw new InvalidActionMapperException("Mapper must be annotated with " + Mapper.class.getName()); } - final Object mapper = createInstance(mapperClass); - final String name = mapperAnnotation.value(); - final String group = mapperAnnotation.group(); - final List mappingDescriptors = Lists.newArrayList(); + Object mapper = createInstance(mapperClass); + String name = mapperAnnotation.value(); + String group = mapperAnnotation.group(); + List mappingDescriptors = Lists.newArrayList(); for (Method method : mapperClass.getDeclaredMethods()) { create(mapperAnnotation, method).ifPresent(mappingDescriptors::add); } @@ -138,6 +140,8 @@ private Class getApmType(Type type) { Class rawType = (Class) parameterizedType.getRawType(); if (List.class.equals(rawType)) { return ApmList.class; + } else if (Map.class.equals(rawType)) { + return ApmMap.class; } } return null; diff --git a/app/aem/core/src/main/java/com/cognifide/apm/core/jobs/ScriptRunnerJobConsumer.java b/app/aem/core/src/main/java/com/cognifide/apm/core/jobs/ScriptRunnerJobConsumer.java index 111379501..9b9ecac6a 100644 --- a/app/aem/core/src/main/java/com/cognifide/apm/core/jobs/ScriptRunnerJobConsumer.java +++ b/app/aem/core/src/main/java/com/cognifide/apm/core/jobs/ScriptRunnerJobConsumer.java @@ -65,14 +65,14 @@ public class ScriptRunnerJobConsumer { public void process(Map properties) { LOG.info("Script runner properties consumer started"); - final String id = (String) properties.get(AsyncScriptExecutorImpl.ID); - final ExecutionMode mode = getMode(properties); - final String userId = getUserId(properties); - SlingHelper.operateTraced(resolverProvider, userId, resolver -> { - final Script script = getScript(properties, resolver); + String id = (String) properties.get(AsyncScriptExecutorImpl.ID); + ExecutionMode mode = getMode(properties); + String userId = getUserId(properties); + SlingHelper.operateTraced(resolverProvider, resolver -> { + Script script = getScript(properties, resolver); if (script != null && mode != null) { try { - ExecutionResult executionResult = scriptManager.process(script, mode, getDefinitions(properties), resolver); + ExecutionResult executionResult = scriptManager.process(script, mode, getDefinitions(properties), resolver, userId); String summaryPath = getSummaryPath(resolver, script, mode); jobResultsCache.put(id, ExecutionSummary.finished(executionResult, summaryPath)); } catch (RepositoryException | PersistenceException e) { @@ -113,9 +113,9 @@ private Map getDefinitions(Map properties) { private Script getScript(Map properties, ResourceResolver resolver) { String scriptSearchPath = (String) properties.get(AsyncScriptExecutorImpl.SCRIPT_PATH); if (StringUtils.isNotBlank(scriptSearchPath)) { - final Script script = scriptFinder.find(scriptSearchPath, resolver); + Script script = scriptFinder.find(scriptSearchPath, resolver); if (script == null) { - LOG.error("Script not found: %s", scriptSearchPath); + LOG.error("Script not found: {}", scriptSearchPath); return null; } return script; diff --git a/app/aem/core/src/main/java/com/cognifide/apm/core/progress/ProgressImpl.java b/app/aem/core/src/main/java/com/cognifide/apm/core/progress/ProgressImpl.java index cabfd74ea..4a32759dd 100644 --- a/app/aem/core/src/main/java/com/cognifide/apm/core/progress/ProgressImpl.java +++ b/app/aem/core/src/main/java/com/cognifide/apm/core/progress/ProgressImpl.java @@ -26,7 +26,6 @@ import com.cognifide.apm.api.actions.Message; import com.cognifide.apm.api.status.Status; import com.cognifide.apm.core.actions.ActionDescriptor; -import com.cognifide.apm.core.actions.ActionResultImpl; import com.cognifide.apm.core.grammar.argument.Arguments; import com.cognifide.apm.core.logger.Position; import com.cognifide.apm.core.logger.Progress; @@ -70,14 +69,16 @@ public void addEntry(Status status, List messages, String command, Strin @Override public void addEntry(ActionDescriptor descriptor, ActionResult result) { this.entries.add( - new ProgressEntry(result.getStatus(), toMessages(((ActionResultImpl) result).getMessages()), descriptor.getCommand(), + new ProgressEntry(result.getStatus(), toMessages(result.getMessages()), descriptor.getCommand(), result.getAuthorizable(), toParameters(descriptor.getArguments()), null ) ); } - private List toMessages(List messages) { - return messages.stream().map(it -> it.getText()).collect(Collectors.toList()); + private static List toMessages(List messages) { + return messages.stream() + .map(Message::getText) + .collect(Collectors.toList()); } private List toParameters(Arguments arguments) { @@ -111,7 +112,7 @@ public void addEntry(Status status, List messages, String command) { this.entries.add(shortEntry(command, messages, status)); } - private ProgressEntry shortEntry(String command, List messages, Status status) { + private static ProgressEntry shortEntry(String command, List messages, Status status) { return new ProgressEntry(status, messages, command, "", Collections.emptyList(), null); } @@ -122,15 +123,10 @@ public boolean isSuccess() { @Override public ProgressEntry getLastError() { - for (int i = entries.size() - 1; i >= 0; i--) { - final ProgressEntry entry = entries.get(i); - - if (entry.getStatus().equals(Status.ERROR)) { - return entry; - } - } - - return null; + return entries.stream() + .filter(entry -> entry.getStatus() == Status.ERROR) + .reduce((first, second) -> second) + .orElse(null); } @Override diff --git a/app/aem/core/src/main/java/com/cognifide/apm/core/scripts/FileDescriptor.java b/app/aem/core/src/main/java/com/cognifide/apm/core/scripts/FileDescriptor.java index 2be8d6fa9..dc61ecc13 100644 --- a/app/aem/core/src/main/java/com/cognifide/apm/core/scripts/FileDescriptor.java +++ b/app/aem/core/src/main/java/com/cognifide/apm/core/scripts/FileDescriptor.java @@ -54,7 +54,7 @@ private static String getPathFromOriginalFileName(String savePath, String origin path = savePath + (subPath.startsWith("/") ? "" : "/") + subPath; } } - if (!path.startsWith(SCRIPT_PATH)) { + if (!path.startsWith("/")) { path = SCRIPT_PATH + (path.startsWith("/") ? "" : "/") + path; } return path; diff --git a/app/aem/core/src/main/java/com/cognifide/apm/core/scripts/ScriptManagerImpl.java b/app/aem/core/src/main/java/com/cognifide/apm/core/scripts/ScriptManagerImpl.java index dda43a1d8..e369e521d 100644 --- a/app/aem/core/src/main/java/com/cognifide/apm/core/scripts/ScriptManagerImpl.java +++ b/app/aem/core/src/main/java/com/cognifide/apm/core/scripts/ScriptManagerImpl.java @@ -46,7 +46,6 @@ import com.cognifide.apm.core.services.event.EventManager; import com.cognifide.apm.core.services.version.VersionService; import com.cognifide.apm.core.utils.RuntimeUtils; -import com.google.common.collect.Maps; import java.util.Arrays; import java.util.HashMap; import java.util.Map; @@ -96,8 +95,8 @@ public class ScriptManagerImpl implements ScriptManager { ) private final Set definitionsProviders = new CopyOnWriteArraySet<>(); - private Progress execute(Script script, final ExecutionMode mode, Map customDefinitions, - ResourceResolver resolver) throws ExecutionException, RepositoryException { + private Progress execute(Script script, ExecutionMode mode, Map customDefinitions, + ResourceResolver resolver, String executor) throws ExecutionException, RepositoryException { if (script == null) { throw new ExecutionException("Script is not specified"); } @@ -106,13 +105,13 @@ private Progress execute(Script script, final ExecutionMode mode, Map customDefinitions, - ResourceResolver resolver) throws RepositoryException, PersistenceException { + public Progress process(Script script, ExecutionMode mode, Map customDefinitions, + ResourceResolver resolver, String executor) throws RepositoryException, PersistenceException { Progress progress; try { - progress = execute(script, mode, customDefinitions, resolver); + progress = execute(script, mode, customDefinitions, resolver, executor); } catch (ExecutionException e) { - progress = new ProgressImpl(resolver.getUserID()); + progress = new ProgressImpl(executor); progress.addEntry(Status.ERROR, e.getMessage()); } @@ -182,10 +175,10 @@ private void saveHistory(Script script, ExecutionMode mode, Progress progress) { } } - private void updateScriptProperties(final Script script, final ExecutionMode mode, final boolean success) + private void updateScriptProperties(Script script, ExecutionMode mode, boolean success) throws PersistenceException { - final MutableScriptWrapper mutableScriptWrapper = new MutableScriptWrapper(script); + MutableScriptWrapper mutableScriptWrapper = new MutableScriptWrapper(script); if (Arrays.asList(ExecutionMode.RUN, ExecutionMode.AUTOMATIC_RUN).contains(mode)) { mutableScriptWrapper.setExecuted(true); @@ -205,7 +198,7 @@ public Map getPredefinedDefinitions() { private ActionExecutor createExecutor(ExecutionMode mode, ResourceResolver resolver) throws RepositoryException { boolean compositeNodeStore = RuntimeUtils.determineCompositeNodeStore(resolver); - final Context context = new ContextImpl((JackrabbitSession) resolver.adaptTo(Session.class), compositeNodeStore); + Context context = new ContextImpl((JackrabbitSession) resolver.adaptTo(Session.class), compositeNodeStore); return ActionExecutorFactory.create(mode, context, actionFactory); } } diff --git a/app/aem/core/src/main/java/com/cognifide/apm/core/scripts/ScriptModel.java b/app/aem/core/src/main/java/com/cognifide/apm/core/scripts/ScriptModel.java index 29a1365a5..0ab8d263c 100644 --- a/app/aem/core/src/main/java/com/cognifide/apm/core/scripts/ScriptModel.java +++ b/app/aem/core/src/main/java/com/cognifide/apm/core/scripts/ScriptModel.java @@ -22,9 +22,12 @@ import com.cognifide.apm.api.scripts.LaunchEnvironment; import com.cognifide.apm.api.scripts.LaunchMode; import com.cognifide.apm.api.scripts.MutableScript; +import com.cognifide.apm.api.services.ExecutionMode; +import com.cognifide.apm.api.services.ScriptManager; import com.cognifide.apm.core.Apm; import com.cognifide.apm.core.utils.PathUtils; import com.cognifide.apm.core.utils.ResourceMixinUtil; +import com.cognifide.apm.core.utils.RuntimeUtils; import com.day.cq.commons.jcr.JcrConstants; import com.google.common.collect.Lists; import com.google.common.collect.Sets; @@ -34,14 +37,18 @@ import java.util.List; import java.util.Optional; import java.util.Set; +import javax.annotation.PostConstruct; import javax.inject.Inject; import javax.inject.Named; +import javax.jcr.RepositoryException; import org.apache.commons.lang3.BooleanUtils; 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.models.annotations.DefaultInjectionStrategy; import org.apache.sling.models.annotations.Model; +import org.apache.sling.models.annotations.injectorspecific.OSGiService; import org.apache.sling.models.annotations.injectorspecific.Self; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -56,6 +63,10 @@ public class ScriptModel implements MutableScript { @Self private Resource resource; + @Inject + @OSGiService + private ScriptManager scriptManager; + @Inject @Named(ScriptNode.APM_LAUNCH_ENABLED) private Boolean launchEnabled; @@ -106,6 +117,17 @@ public ScriptModel(Resource resource) { this.path = resource.getPath(); } + @PostConstruct + private void afterCreated() { + if (verified == null) { + try { + scriptManager.process(this, ExecutionMode.VALIDATION, resource.getResourceResolver()); + } catch (RepositoryException | PersistenceException e) { + LOGGER.error("", e); + } + } + } + @Override public boolean isValid() { return BooleanUtils.toBoolean(verified); @@ -206,12 +228,14 @@ public void setLastExecuted(Date date) throws PersistenceException { } private void setProperty(String name, Object value) throws PersistenceException { - if (!PathUtils.isAppsOrLibsPath(path)) { + ResourceResolver resolver = resource.getResourceResolver(); + boolean compositeNodeStore = RuntimeUtils.determineCompositeNodeStore(resolver); + if (!compositeNodeStore || !PathUtils.isAppsOrLibsPath(path)) { ModifiableValueMap vm = resource.adaptTo(ModifiableValueMap.class); ResourceMixinUtil.addMixin(vm, ScriptNode.APM_SCRIPT); vm.put(name, convertValue(value)); - resource.getResourceResolver().commit(); + resolver.commit(); } } diff --git a/app/aem/core/src/main/java/com/cognifide/apm/core/services/ResourceResolverProvider.java b/app/aem/core/src/main/java/com/cognifide/apm/core/services/ResourceResolverProvider.java index fe3588216..0ca74be06 100644 --- a/app/aem/core/src/main/java/com/cognifide/apm/core/services/ResourceResolverProvider.java +++ b/app/aem/core/src/main/java/com/cognifide/apm/core/services/ResourceResolverProvider.java @@ -20,8 +20,6 @@ package com.cognifide.apm.core.services; import com.cognifide.apm.core.Property; -import com.google.common.collect.Maps; -import java.util.Map; import org.apache.sling.adapter.Adaption; import org.apache.sling.api.adapter.AdapterFactory; import org.apache.sling.api.resource.LoginException; @@ -70,16 +68,8 @@ public class ResourceResolverProvider { @Reference(target = "(adapters=com.cognifide.apm.core.history.HistoryEntryImpl)") private Adaption historyEntryImplAdaption; - public ResourceResolver getResourceResolver(String userId) throws LoginException { - ResourceResolver resolver; - if (userId != null) { - Map authenticationInfo = Maps.newHashMap(); - authenticationInfo.put(ResourceResolverFactory.USER_IMPERSONATION, userId); - resolver = resolverFactory.getAdministrativeResourceResolver(authenticationInfo); - } else { - resolver = resolverFactory.getServiceResourceResolver(null); - } - return resolver; + public ResourceResolver getResourceResolver() throws LoginException { + return resolverFactory.getServiceResourceResolver(null); } } diff --git a/app/aem/core/src/main/java/com/cognifide/apm/core/services/ScriptRootPathsProvider.java b/app/aem/core/src/main/java/com/cognifide/apm/core/services/ScriptRootPathsProvider.java new file mode 100644 index 000000000..251f6b8b8 --- /dev/null +++ b/app/aem/core/src/main/java/com/cognifide/apm/core/services/ScriptRootPathsProvider.java @@ -0,0 +1,98 @@ +/*- + * ========================LICENSE_START================================= + * AEM Permission Management + * %% + * Copyright (C) 2013 Wunderman Thompson Technology + * %% + * Licensed 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. + * =========================LICENSE_END================================== + */ +package com.cognifide.apm.core.services; + +import java.util.Arrays; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Set; +import javax.servlet.http.HttpServletRequest; +import org.apache.commons.lang3.StringUtils; +import org.apache.sling.api.resource.Resource; +import org.apache.sling.api.resource.ResourceDecorator; +import org.apache.sling.api.resource.ResourceWrapper; +import org.apache.sling.api.resource.ValueMap; +import org.apache.sling.api.wrappers.ValueMapDecorator; +import org.osgi.service.component.annotations.Activate; +import org.osgi.service.component.annotations.Component; +import org.osgi.service.metatype.annotations.AttributeDefinition; +import org.osgi.service.metatype.annotations.Designate; +import org.osgi.service.metatype.annotations.ObjectClassDefinition; + + +@Component(service = {ScriptRootPathsProvider.class, ResourceDecorator.class}, immediate = true) +@Designate(ocd = ScriptRootPathsProvider.Configuration.class) +public class ScriptRootPathsProvider implements ResourceDecorator { + + private static final String RESOURCE_TYPE = "wcm/commons/ui/shell/datasources/breadcrumbs"; + + private static final String RESOURCE_PATH = "/apps/apm/views/scripts/jcr:content/breadcrumbs"; + + private static final String DEFAULT_SCRIPT_PATH = "/conf/apm/scripts"; + + private Set rootPaths; + + @Activate + public void activate(Configuration config) { + this.rootPaths = new HashSet<>(); + this.rootPaths.add(DEFAULT_SCRIPT_PATH); + this.rootPaths.addAll(Arrays.asList(config.rootPaths())); + } + + @Override + public Resource decorate(Resource resource) { + Resource result = resource; + if (isAllowed(resource)) { + ValueMap valueMap = new ValueMapDecorator(new HashMap<>()); + valueMap.putAll(resource.getValueMap()); + valueMap.put("rootPath", "/"); + result = new ResourceWrapper(resource) { + @Override + public ValueMap getValueMap() { + return valueMap; + } + }; + } + return result; + } + + @Override + public Resource decorate(Resource resource, HttpServletRequest request) { + return decorate(resource); + } + + public boolean isValidPath(String path) { + return rootPaths.stream() + .anyMatch(rootPath -> path.startsWith(rootPath) || rootPath.startsWith(path)); + } + + private boolean isAllowed(Resource resource) { + return StringUtils.equals(resource.getResourceType(), RESOURCE_TYPE) + && StringUtils.equals(resource.getPath(), RESOURCE_PATH) + && rootPaths.size() > 1; + } + + @ObjectClassDefinition(name = "AEM Permission Management - Script Root Paths Provider") + public @interface Configuration { + + @AttributeDefinition(name = "Additional Script Root Paths") + String[] rootPaths(); + } +} diff --git a/app/aem/core/src/main/java/com/cognifide/apm/core/ui/datasources/ScriptsDatasourceServlet.java b/app/aem/core/src/main/java/com/cognifide/apm/core/ui/datasources/ScriptsDatasourceServlet.java index 07c540861..7abf19a64 100644 --- a/app/aem/core/src/main/java/com/cognifide/apm/core/ui/datasources/ScriptsDatasourceServlet.java +++ b/app/aem/core/src/main/java/com/cognifide/apm/core/ui/datasources/ScriptsDatasourceServlet.java @@ -26,6 +26,7 @@ import com.adobe.granite.ui.components.ds.SimpleDataSource; import com.cognifide.apm.core.Property; import com.cognifide.apm.core.scripts.ScriptModel; +import com.cognifide.apm.core.services.ScriptRootPathsProvider; import com.cognifide.apm.core.ui.models.ScriptsRowModel; import java.util.ArrayList; import java.util.List; @@ -36,6 +37,7 @@ import org.apache.sling.api.resource.ResourceWrapper; import org.apache.sling.api.servlets.SlingSafeMethodsServlet; import org.osgi.service.component.annotations.Component; +import org.osgi.service.component.annotations.Reference; @Component( service = Servlet.class, @@ -48,13 +50,17 @@ ) public class ScriptsDatasourceServlet extends SlingSafeMethodsServlet { + @Reference + private ScriptRootPathsProvider scriptRootPathsProvider; + @Override protected void doGet(SlingHttpServletRequest request, SlingHttpServletResponse response) { String path = request.getRequestPathInfo().getSuffix(); List scripts = new ArrayList<>(); Resource resource = request.getResourceResolver().getResource(path); for (Resource child : resource.getChildren()) { - if (ScriptsRowModel.isFolder(child) || ScriptModel.isScript(child)) { + if ((ScriptsRowModel.isFolder(child) || ScriptModel.isScript(child)) + && scriptRootPathsProvider.isValidPath(child.getPath())) { scripts.add(new ResourceTypeWrapper(child)); } } diff --git a/app/aem/core/src/main/java/com/cognifide/apm/core/utils/AuthorizableManagerImpl.java b/app/aem/core/src/main/java/com/cognifide/apm/core/utils/AuthorizableManagerImpl.java index c57c2a7f6..41e4178d7 100644 --- a/app/aem/core/src/main/java/com/cognifide/apm/core/utils/AuthorizableManagerImpl.java +++ b/app/aem/core/src/main/java/com/cognifide/apm/core/utils/AuthorizableManagerImpl.java @@ -20,19 +20,18 @@ package com.cognifide.apm.core.utils; -import static java.lang.String.format; - import com.cognifide.apm.api.actions.AuthorizableManager; import com.cognifide.apm.api.exceptions.ActionExecutionException; +import com.cognifide.apm.api.exceptions.AuthorizableNotFoundException; import com.cognifide.apm.core.utils.mocks.MockGroup; import com.cognifide.apm.core.utils.mocks.MockPrincipal; import com.cognifide.apm.core.utils.mocks.MockUser; import java.security.Principal; -import java.util.ArrayList; import java.util.HashMap; +import java.util.HashSet; import java.util.Iterator; -import java.util.List; import java.util.Map; +import java.util.Set; import javax.jcr.RepositoryException; import org.apache.jackrabbit.api.security.user.Authorizable; import org.apache.jackrabbit.api.security.user.Group; @@ -44,7 +43,8 @@ public class AuthorizableManagerImpl implements AuthorizableManager { private final UserManager userManager; private final Map existingAuthorizables = new HashMap<>(); - private final List removedAuthorizables = new ArrayList<>(); + + private final Set removedAuthorizables = new HashSet<>(); public AuthorizableManagerImpl(UserManager userManager) { this.userManager = userManager; @@ -65,7 +65,7 @@ public Authorizable getAuthorizableIfExists(String id) throws RepositoryExceptio } @Override - public Authorizable getAuthorizable(String id) throws ActionExecutionException, RepositoryException { + public Authorizable getAuthorizable(String id) throws ActionExecutionException, RepositoryException, AuthorizableNotFoundException { return getAuthorizable(Authorizable.class, id); } @@ -81,7 +81,7 @@ public Group getGroupIfExists(String id) throws RepositoryException, ActionExecu } @Override - public Group getGroup(String id) throws ActionExecutionException, RepositoryException { + public Group getGroup(String id) throws ActionExecutionException, RepositoryException, AuthorizableNotFoundException { return getAuthorizable(Group.class, id); } @@ -113,7 +113,7 @@ public User getUserIfExists(String id) throws ActionExecutionException, Reposito } @Override - public User getUser(String id) throws ActionExecutionException, RepositoryException { + public User getUser(String id) throws ActionExecutionException, RepositoryException, AuthorizableNotFoundException { return getAuthorizable(User.class, id); } @@ -174,7 +174,7 @@ private T getAuthorizableIfExists(Class authorizable if (!authorizableClass.isInstance(authorizable)) { throw new ActionExecutionException( - format("Authorizable with id %s exists but is a ", authorizableClass.getSimpleName())); + String.format("Authorizable with id %s exists but is a %s", id, authorizableClass.getSimpleName())); } existingAuthorizables.put(id, authorizable); @@ -182,10 +182,10 @@ private T getAuthorizableIfExists(Class authorizable } private T getAuthorizable(Class authorizableClass, String id) - throws ActionExecutionException, RepositoryException { + throws ActionExecutionException, RepositoryException, AuthorizableNotFoundException { if (checkIfRemoved(id)) { - throw new ActionExecutionException( - format("%s with id %s not found", authorizableClass.getSimpleName(), id)); + throw new AuthorizableNotFoundException( + String.format("%s with id %s not found", authorizableClass.getSimpleName(), id)); } Authorizable authorizable = existingAuthorizables.get(id); @@ -195,13 +195,13 @@ private T getAuthorizable(Class authorizableClass, S } if (authorizable == null) { - throw new ActionExecutionException( - format("%s with id %s not found", authorizableClass.getSimpleName(), id)); + throw new AuthorizableNotFoundException( + String.format("%s with id %s not found", authorizableClass.getSimpleName(), id)); } if (!authorizableClass.isInstance(authorizable)) { throw new ActionExecutionException( - format("Authorizable with id %s exists but is a ", authorizableClass.getSimpleName())); + String.format("Authorizable with id %s exists but is a %s", id, authorizableClass.getSimpleName())); } existingAuthorizables.put(id, authorizable); diff --git a/app/aem/core/src/main/java/com/cognifide/apm/core/utils/sling/SlingHelper.java b/app/aem/core/src/main/java/com/cognifide/apm/core/utils/sling/SlingHelper.java index 90934dbff..d55086780 100644 --- a/app/aem/core/src/main/java/com/cognifide/apm/core/utils/sling/SlingHelper.java +++ b/app/aem/core/src/main/java/com/cognifide/apm/core/utils/sling/SlingHelper.java @@ -37,13 +37,12 @@ private SlingHelper() { } /** - * Retrieve values from repository with wrapped impersonated session (automatically opened and closed). + * Retrieve values from repository with wrapped session (automatically opened and closed). */ - @SuppressWarnings("unchecked") - public static T resolve(ResourceResolverProvider provider, String userId, ResolveCallback callback) + public static T resolve(ResourceResolverProvider provider, ResolveCallback callback) throws ResolveException { - try (ResourceResolver resolver = provider.getResourceResolver(userId)) { - return (T) callback.resolve(resolver); + try (ResourceResolver resolver = provider.getResourceResolver()) { + return callback.resolve(resolver); } catch (Exception e) { throw new ResolveException(RESOLVE_ERROR_MESSAGE, e); } @@ -52,18 +51,9 @@ public static T resolve(ResourceResolverProvider provider, String userId, Re /** * Retrieve values from repository with wrapped session (automatically opened and closed). */ - public static T resolveDefault(ResourceResolverProvider provider, ResolveCallback callback, - T defaultValue) { - return resolveDefault(provider, null, callback, defaultValue); - } - - /** - * Retrieve values from repository with wrapped session (automatically opened and closed). - */ - public static T resolveDefault(ResourceResolverProvider provider, String userId, ResolveCallback callback, - T defaultValue) { + public static T resolveDefault(ResourceResolverProvider provider, ResolveCallback callback, T defaultValue) { try { - return resolve(provider, userId, callback); + return resolve(provider, callback); } catch (ResolveException e) { LOG.error(RESOLVE_ERROR_MESSAGE, e); } @@ -71,12 +61,12 @@ public static T resolveDefault(ResourceResolverProvider provider, String use } /** - * Do some operation on repository (delete or update resource etc) with wrapped impersonated session - * (automatically opened and closed). + * Do some operation on repository (delete or update resource etc) with wrapped session (automatically + * opened and closed). */ - public static void operate(ResourceResolverProvider provider, String userId, OperateCallback callback) + public static void operate(ResourceResolverProvider provider, OperateCallback callback) throws OperateException { - try (ResourceResolver resolver = provider.getResourceResolver(userId)) { + try (ResourceResolver resolver = provider.getResourceResolver()) { callback.operate(resolver); resolver.commit(); } catch (Exception e) { @@ -89,16 +79,8 @@ public static void operate(ResourceResolverProvider provider, String userId, Ope * opened and closed). */ public static void operateTraced(ResourceResolverProvider provider, OperateCallback callback) { - operateTraced(provider, null, callback); - } - - /** - * Do some operation on repository (delete or update resource etc) with wrapped session (automatically - * opened and closed). - */ - public static void operateTraced(ResourceResolverProvider provider, String userId, OperateCallback callback) { try { - operate(provider, userId, callback); + operate(provider, callback); } catch (OperateException e) { LOG.error(OPERATE_ERROR_MESSAGE, e); } diff --git a/app/aem/core/src/main/kotlin/com/cognifide/apm/core/endpoints/ScriptExecutionServlet.kt b/app/aem/core/src/main/kotlin/com/cognifide/apm/core/endpoints/ScriptExecutionServlet.kt index ddf841fb7..d7fa913be 100644 --- a/app/aem/core/src/main/kotlin/com/cognifide/apm/core/endpoints/ScriptExecutionServlet.kt +++ b/app/aem/core/src/main/kotlin/com/cognifide/apm/core/endpoints/ScriptExecutionServlet.kt @@ -28,9 +28,11 @@ import com.cognifide.apm.core.endpoints.response.internalServerError import com.cognifide.apm.core.endpoints.response.notFound import com.cognifide.apm.core.endpoints.response.ok import com.cognifide.apm.core.endpoints.utils.RequestProcessor +import com.cognifide.apm.core.services.ResourceResolverProvider import com.cognifide.apm.core.services.async.AsyncScriptExecutor import com.cognifide.apm.core.services.async.FinishedFailedExecution import com.cognifide.apm.core.services.async.FinishedSuccessfulExecution +import com.cognifide.apm.core.utils.sling.SlingHelper import org.apache.sling.api.SlingHttpServletRequest import org.apache.sling.api.SlingHttpServletResponse import org.apache.sling.api.resource.ResourceResolver @@ -67,6 +69,10 @@ class ScriptExecutionServlet : SlingAllMethodsServlet() { @Transient private lateinit var modelFactory: ModelFactory + @Reference + @Transient + private lateinit var resolverProvider: ResourceResolverProvider + override fun doGet(request: SlingHttpServletRequest, response: SlingHttpServletResponse) { RequestProcessor(modelFactory, ScriptExecutionStatusForm::class.java).process(request, response) { form, _ -> when (val status = asyncScriptExecutor.checkStatus(form.id)) { @@ -92,12 +98,13 @@ class ScriptExecutionServlet : SlingAllMethodsServlet() { } override fun doPost(request: SlingHttpServletRequest, response: SlingHttpServletResponse) { - RequestProcessor(modelFactory, ScriptExecutionForm::class.java).process(request, response) { form, resourceResolver -> - executeScript(form, resourceResolver) + val executor = request.resourceResolver.userID!! + RequestProcessor(modelFactory, ScriptExecutionForm::class.java).process(request, response) { form, _ -> + SlingHelper.resolve(resolverProvider) { executeScript(form, it, executor) } } } - private fun executeScript(form: ScriptExecutionForm, resourceResolver: ResourceResolver): ResponseEntity { + private fun executeScript(form: ScriptExecutionForm, resourceResolver: ResourceResolver, executor: String): ResponseEntity { try { val script: Script = scriptFinder.find(form.script, resourceResolver) ?: return notFound { message = "Script not found: ${form.script}" } @@ -105,25 +112,25 @@ class ScriptExecutionServlet : SlingAllMethodsServlet() { if (!script.isValid) return internalServerError { message = "Script cannot be executed because it is invalid" } return if (form.async) { - asyncExecute(script, form, resourceResolver) + asyncExecute(script, form, executor) } else { - syncExecute(script, form, resourceResolver) + syncExecute(script, form, resourceResolver, executor) } } catch (e: RepositoryException) { return internalServerError { message = "Script cannot be executed because of repository error: ${e.message}" } } } - private fun asyncExecute(script: Script, form: ScriptExecutionForm, resourceResolver: ResourceResolver): ResponseEntity { - val id = asyncScriptExecutor.process(script, form.executionMode, form.customDefinitions, resourceResolver) + private fun asyncExecute(script: Script, form: ScriptExecutionForm, executor: String): ResponseEntity { + val id = asyncScriptExecutor.process(script, form.executionMode, form.customDefinitions, executor) return ok { message = "Script successfully queued for async execution" "id" set id } } - private fun syncExecute(script: Script, form: ScriptExecutionForm, resourceResolver: ResourceResolver): ResponseEntity { - val result = scriptManager.process(script, form.executionMode, form.customDefinitions, resourceResolver) + private fun syncExecute(script: Script, form: ScriptExecutionForm, resourceResolver: ResourceResolver, executor: String): ResponseEntity { + val result = scriptManager.process(script, form.executionMode, form.customDefinitions, resourceResolver, executor) return if (result.isSuccess) { ok { message = "Script successfully executed" diff --git a/app/aem/core/src/main/kotlin/com/cognifide/apm/core/grammar/ScriptRunner.kt b/app/aem/core/src/main/kotlin/com/cognifide/apm/core/grammar/ScriptRunner.kt index a8569beca..15a466e87 100644 --- a/app/aem/core/src/main/kotlin/com/cognifide/apm/core/grammar/ScriptRunner.kt +++ b/app/aem/core/src/main/kotlin/com/cognifide/apm/core/grammar/ScriptRunner.kt @@ -36,6 +36,7 @@ import com.cognifide.apm.core.grammar.utils.RequiredVariablesChecker import com.cognifide.apm.core.logger.Position import com.cognifide.apm.core.logger.Progress import org.antlr.v4.runtime.ParserRuleContext +import org.antlr.v4.runtime.tree.RuleNode import org.apache.sling.api.resource.ResourceResolver class ScriptRunner( @@ -63,42 +64,62 @@ class ScriptRunner( return progress } - private inner class Executor(private val executionContext: ExecutionContext) : - com.cognifide.apm.core.grammar.antlr.ApmLangBaseVisitor() { + private inner class Executor( + private val executionContext: ExecutionContext, + private var globalResult: Status = Status.SUCCESS + ) : com.cognifide.apm.core.grammar.antlr.ApmLangBaseVisitor() { - override fun visitDefineVariable(ctx: DefineVariableContext) { + private fun shouldVisitNextChild(): Boolean { + return globalResult != Status.ERROR + } + + override fun shouldVisitNextChild(node: RuleNode, currentResult: Status?): Boolean { + return shouldVisitNextChild() + } + + override fun aggregateResult(aggregate: Status?, nextResult: Status?): Status { + globalResult = if (nextResult == Status.ERROR) Status.ERROR else globalResult + return globalResult + } + + override fun visitDefineVariable(ctx: DefineVariableContext): Status { val variableName = ctx.IDENTIFIER().toString() val variableValue = executionContext.resolveArgument(ctx.argument()) executionContext.setVariable(variableName, variableValue) progress(ctx, Status.SUCCESS, "define", "Defined variable: $variableName= $variableValue") + return Status.SUCCESS } - override fun visitRequireVariable(ctx: RequireVariableContext) { + override fun visitRequireVariable(ctx: RequireVariableContext): Status { val variableName = ctx.IDENTIFIER().toString() if (executionContext.getVariable(variableName) == null) { val status = if (validateOnly) Status.WARNING else Status.ERROR progress(ctx, status, "require", "Variable \"$variableName\" is required") } + return Status.SUCCESS } - override fun visitForEach(ctx: ForEachContext) { + override fun visitForEach(ctx: ForEachContext): Status { val values: List> = readValues(ctx) for ((index, value) in values.withIndex()) { - try { - executionContext.createLocalContext() - val valueStr = value.map { it.key + "=" + it.value } - .joinToString() - progress(ctx, Status.SUCCESS, "for-each", "$index. Begin: $valueStr") - value.forEach { (k, v) -> executionContext.setVariable(k, v) } - visit(ctx.body()) - progress(ctx, Status.SUCCESS, "for-each", "$index. End") - } finally { - executionContext.removeLocalContext() + if (shouldVisitNextChild()) { + try { + executionContext.createLocalContext() + val valueStr = value.map { it.key + "=" + it.value } + .joinToString() + progress(ctx, Status.SUCCESS, "for-each", "$index. Begin: $valueStr") + value.forEach { (k, v) -> executionContext.setVariable(k, v) } + visit(ctx.body()) + progress(ctx, Status.SUCCESS, "for-each", "$index. End") + } finally { + executionContext.removeLocalContext() + } } } + return Status.SUCCESS } - override fun visitRunScript(ctx: RunScriptContext) { + override fun visitRunScript(ctx: RunScriptContext): Status { val path = getPath(ctx.path()) val arguments = executionContext.resolveArguments(ctx.namedArguments()) val loadScript = executionContext.loadScript(path) @@ -118,15 +139,16 @@ class ScriptRunner( } else { progress(ctx, Status.ERROR, "run", result.toMessages(), arguments) } + return Status.SUCCESS } - override fun visitGenericCommand(ctx: GenericCommandContext) { - val commandName = getIdentifier(ctx.commandName().identifier()).toUpperCase() + override fun visitGenericCommand(ctx: GenericCommandContext): Status { + val commandName = getIdentifier(ctx.commandName().identifier()).uppercase() val arguments = executionContext.resolveArguments(ctx.complexArguments()) - visitGenericCommand(ctx, commandName, arguments, ctx.body()) + return visitGenericCommand(ctx, commandName, arguments, ctx.body()) } - override fun visitAllowDenyCommand(ctx: AllowDenyCommandContext) { + override fun visitAllowDenyCommand(ctx: AllowDenyCommandContext): Status { val commandName = if (ctx.ALLOW() != null) "ALLOW" else "DENY" val argument = executionContext.resolveArgument(ctx.argument()) val arguments = executionContext.resolveArguments(ctx.complexArguments()) @@ -136,13 +158,13 @@ class ScriptRunner( arguments.required + argument } val newArguments = Arguments(required, arguments.named, arguments.flags) - visitGenericCommand(ctx, commandName, newArguments) + return visitGenericCommand(ctx, commandName, newArguments) } private fun visitGenericCommand( ctx: ParserRuleContext, commandName: String, arguments: Arguments, body: BodyContext? = null - ) { - if (validateOnly) { + ): Status { + return if (validateOnly) { visitGenericCommandValidateMode(ctx, commandName, arguments, body) } else { visitGenericCommandRunMode(ctx, commandName, arguments, body) @@ -151,15 +173,16 @@ class ScriptRunner( private fun visitGenericCommandRunMode( ctx: ParserRuleContext, commandName: String, arguments: Arguments, body: BodyContext? - ) { + ): Status { try { if (body != null) { executionContext.createLocalContext() } - actionInvoker.runAction(executionContext, commandName, arguments) - if (body != null) { + val status = actionInvoker.runAction(executionContext, commandName, arguments) + if (status == Status.SUCCESS && body != null) { visit(body) } + return status } catch (e: ArgumentResolverException) { progress(ctx, Status.ERROR, commandName, "Action failed: ${e.message}") } finally { @@ -167,11 +190,12 @@ class ScriptRunner( executionContext.removeLocalContext() } } + return Status.ERROR } private fun visitGenericCommandValidateMode( ctx: ParserRuleContext, commandName: String, arguments: Arguments, body: BodyContext? - ) { + ): Status { try { if (body != null) { executionContext.createLocalContext() @@ -189,12 +213,14 @@ class ScriptRunner( executionContext.removeLocalContext() } } + return Status.SUCCESS } - override fun visitImportScript(ctx: ImportScriptContext) { + override fun visitImportScript(ctx: ImportScriptContext): Status { val result = ImportScript(executionContext).import(ctx) executionContext.variableHolder.setAll(result.variableHolder) progress(ctx, Status.SUCCESS, "import", result.toMessages()) + return Status.SUCCESS } private fun readValues(ctx: ForEachContext): List> { diff --git a/app/aem/core/src/main/kotlin/com/cognifide/apm/core/grammar/parsedscript/ApmLangParserFactory.kt b/app/aem/core/src/main/kotlin/com/cognifide/apm/core/grammar/parsedscript/ApmLangParserFactory.kt index 556763428..da919fca4 100644 --- a/app/aem/core/src/main/kotlin/com/cognifide/apm/core/grammar/parsedscript/ApmLangParserFactory.kt +++ b/app/aem/core/src/main/kotlin/com/cognifide/apm/core/grammar/parsedscript/ApmLangParserFactory.kt @@ -48,6 +48,10 @@ object ApmLangParserFactory { throw InvalidSyntaxException(InputMismatchException(recognizer)) } + override fun reportError(recognizer: Parser?, e: RecognitionException?) { + throw InvalidSyntaxException(e!!) + } + override fun sync(recognizer: Parser) {} } diff --git a/app/aem/core/src/main/kotlin/com/cognifide/apm/core/services/async/AsyncScriptExecutor.kt b/app/aem/core/src/main/kotlin/com/cognifide/apm/core/services/async/AsyncScriptExecutor.kt index 4096b9f56..6bbe80224 100644 --- a/app/aem/core/src/main/kotlin/com/cognifide/apm/core/services/async/AsyncScriptExecutor.kt +++ b/app/aem/core/src/main/kotlin/com/cognifide/apm/core/services/async/AsyncScriptExecutor.kt @@ -21,11 +21,10 @@ package com.cognifide.apm.core.services.async import com.cognifide.apm.api.scripts.Script import com.cognifide.apm.api.services.ExecutionMode -import org.apache.sling.api.resource.ResourceResolver interface AsyncScriptExecutor { - fun process(script: Script, executionMode: ExecutionMode, customDefinitions: Map, resourceResolver: ResourceResolver): String + fun process(script: Script, executionMode: ExecutionMode, customDefinitions: Map, executor: String): String fun checkStatus(id: String): ExecutionStatus } \ No newline at end of file diff --git a/app/aem/core/src/main/kotlin/com/cognifide/apm/core/services/async/AsyncScriptExecutorImpl.kt b/app/aem/core/src/main/kotlin/com/cognifide/apm/core/services/async/AsyncScriptExecutorImpl.kt index 5251626d1..9b8790575 100644 --- a/app/aem/core/src/main/kotlin/com/cognifide/apm/core/services/async/AsyncScriptExecutorImpl.kt +++ b/app/aem/core/src/main/kotlin/com/cognifide/apm/core/services/async/AsyncScriptExecutorImpl.kt @@ -21,7 +21,6 @@ package com.cognifide.apm.core.services.async import com.cognifide.apm.api.scripts.Script import com.cognifide.apm.api.services.ExecutionMode -import com.cognifide.apm.api.status.Status import com.cognifide.apm.core.Property import com.cognifide.apm.core.jobs.JobResultsCache import com.cognifide.apm.core.jobs.JobResultsCache.ExecutionSummary @@ -48,13 +47,13 @@ class AsyncScriptExecutorImpl : AsyncScriptExecutor { @Transient private lateinit var jobResultsCache: JobResultsCache - override fun process(script: Script, executionMode: ExecutionMode, customDefinitions: Map, resourceResolver: ResourceResolver): String { + override fun process(script: Script, executionMode: ExecutionMode, customDefinitions: Map, executor: String): String { val id = UUID.randomUUID().toString() val properties = mutableMapOf() properties[ID] = id properties[SCRIPT_PATH] = script.path properties[EXECUTION_MODE] = executionMode.toString() - properties[USER_ID] = resourceResolver.userID!! + properties[USER_ID] = executor properties[DEFINITIONS] = customDefinitions jobResultsCache.put(id, ExecutionSummary.running()) thread(start = true) { @@ -74,7 +73,7 @@ class AsyncScriptExecutorImpl : AsyncScriptExecutor { private fun finishedExecution(executionSummary: ExecutionSummary): ExecutionStatus { val entries = executionSummary.result.entries - val errorEntry = entries.findLast { it.status == Status.ERROR } + val errorEntry = executionSummary.result.lastError return if (errorEntry != null) { FinishedFailedExecution(executionSummary.path, entries, errorEntry) } else { diff --git a/app/aem/core/src/test/groovy/com/cognifide/apm/core/grammar/ScriptRunnerTest.groovy b/app/aem/core/src/test/groovy/com/cognifide/apm/core/grammar/ScriptRunnerTest.groovy index b54f7ab9a..36b10cb0b 100644 --- a/app/aem/core/src/test/groovy/com/cognifide/apm/core/grammar/ScriptRunnerTest.groovy +++ b/app/aem/core/src/test/groovy/com/cognifide/apm/core/grammar/ScriptRunnerTest.groovy @@ -23,7 +23,6 @@ package com.cognifide.apm.core.grammar import com.cognifide.apm.api.scripts.Script import com.cognifide.apm.api.services.ScriptFinder import com.cognifide.apm.api.status.Status -import com.cognifide.apm.core.grammar.argument.Arguments import com.cognifide.apm.core.progress.ProgressImpl import org.apache.commons.io.IOUtils import org.apache.sling.api.resource.ResourceResolver @@ -104,7 +103,7 @@ class ScriptRunnerTest extends Specification { def result = scriptExecutor.execute(script, new ProgressImpl("")) then: - result.entries.size() == 3 + result.entries.size() == 7 result.entries[0].messages == ["Import from script /import-define.apm. Notice, only DEFINE actions were processed!", "Imported variable: var=\"imported val\""] @@ -116,6 +115,11 @@ class ScriptRunnerTest extends Specification { result.entries[2].messages == ["Import from script /import-deep-define.apm. Notice, only DEFINE actions were processed!", "Imported variable: deepNamespace={deeperNamespace: {var: \"imported val\"}, deepVar: \"imported val + imported val\"}"] + + result.entries[3].command == "Executing command SHOW \"imported val\"" + result.entries[4].command == "Executing command SHOW \"imported val\"" + result.entries[5].command == "Executing command SHOW \"imported val\"" + result.entries[6].command == "Executing command SHOW \"imported val + imported val\"" } def "run script filename.apm"() { diff --git a/app/aem/core/src/test/groovy/com/cognifide/apm/core/grammar/parsedscript/ParsedScriptTest.groovy b/app/aem/core/src/test/groovy/com/cognifide/apm/core/grammar/parsedscript/ParsedScriptTest.groovy index f44bc970a..333783f49 100644 --- a/app/aem/core/src/test/groovy/com/cognifide/apm/core/grammar/parsedscript/ParsedScriptTest.groovy +++ b/app/aem/core/src/test/groovy/com/cognifide/apm/core/grammar/parsedscript/ParsedScriptTest.groovy @@ -44,8 +44,20 @@ class ParsedScriptTest extends Specification { error == output where: - file | output - "/invalid1.apm" | ["Invalid line [20:7]: DEFINE \$ nana", "Invalid sequence: \$"] - "/invalid2.apm" | ["Invalid line [20:7]: DEFINE / nana"] + file | output + "/invalid/invalid1.apm" | ["Invalid line [20:7]: DEFINE \$ nana", "Invalid sequence: \$"] + "/invalid/invalid2.apm" | ["Invalid line [20:7]: DEFINE / nana"] + "/invalid/invalid3.apm" | ["Invalid line [20:0]: // define variable", "Invalid sequence: //"] + "/invalid/invalid4.apm" | ["Invalid line [20:0]: / define variable"] + "/invalid/invalid5.apm" | ["Invalid line [20:0]: /x define variable", "Invalid sequence: /x"] + "/invalid/invalid6.apm" | ["Invalid line [20:0]: /* define variable", "Invalid sequence: /*"] + "/invalid/invalid7.apm" | ["Invalid line [20:0]: diff --git a/app/aem/core/src/test/resources/invalid/invalid13.apm b/app/aem/core/src/test/resources/invalid/invalid13.apm new file mode 100644 index 000000000..6ac88d297 --- /dev/null +++ b/app/aem/core/src/test/resources/invalid/invalid13.apm @@ -0,0 +1,21 @@ + + # ========================LICENSE_START================================= + # AEM Permission Management + # %% + # Copyright (C) 2013 Wunderman Thompson Technology + # %% + # Licensed 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. + # =========================LICENSE_END================================== + +"define variable" +DEFINE nana "nana" diff --git a/app/aem/core/src/test/resources/invalid/invalid14.apm b/app/aem/core/src/test/resources/invalid/invalid14.apm new file mode 100644 index 000000000..90ff2e9b6 --- /dev/null +++ b/app/aem/core/src/test/resources/invalid/invalid14.apm @@ -0,0 +1,21 @@ + + # ========================LICENSE_START================================= + # AEM Permission Management + # %% + # Copyright (C) 2013 Wunderman Thompson Technology + # %% + # Licensed 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. + # =========================LICENSE_END================================== + +DEFINE nana "nana" +"define variable" diff --git a/app/aem/core/src/test/resources/invalid2.apm b/app/aem/core/src/test/resources/invalid/invalid2.apm similarity index 100% rename from app/aem/core/src/test/resources/invalid2.apm rename to app/aem/core/src/test/resources/invalid/invalid2.apm diff --git a/app/aem/core/src/test/resources/invalid/invalid3.apm b/app/aem/core/src/test/resources/invalid/invalid3.apm new file mode 100644 index 000000000..853619fac --- /dev/null +++ b/app/aem/core/src/test/resources/invalid/invalid3.apm @@ -0,0 +1,21 @@ + + # ========================LICENSE_START================================= + # AEM Permission Management + # %% + # Copyright (C) 2013 Wunderman Thompson Technology + # %% + # Licensed 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. + # =========================LICENSE_END================================== + +// define variable +DEFINE / nana diff --git a/app/aem/core/src/test/resources/invalid/invalid4.apm b/app/aem/core/src/test/resources/invalid/invalid4.apm new file mode 100644 index 000000000..b0a87e6dd --- /dev/null +++ b/app/aem/core/src/test/resources/invalid/invalid4.apm @@ -0,0 +1,21 @@ + + # ========================LICENSE_START================================= + # AEM Permission Management + # %% + # Copyright (C) 2013 Wunderman Thompson Technology + # %% + # Licensed 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. + # =========================LICENSE_END================================== + +/ define variable +DEFINE / nana diff --git a/app/aem/core/src/test/resources/invalid/invalid5.apm b/app/aem/core/src/test/resources/invalid/invalid5.apm new file mode 100644 index 000000000..ebe4a4cc8 --- /dev/null +++ b/app/aem/core/src/test/resources/invalid/invalid5.apm @@ -0,0 +1,21 @@ + + # ========================LICENSE_START================================= + # AEM Permission Management + # %% + # Copyright (C) 2013 Wunderman Thompson Technology + # %% + # Licensed 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. + # =========================LICENSE_END================================== + +/x define variable +DEFINE / nana diff --git a/app/aem/core/src/test/resources/invalid/invalid6.apm b/app/aem/core/src/test/resources/invalid/invalid6.apm new file mode 100644 index 000000000..013ed0b1e --- /dev/null +++ b/app/aem/core/src/test/resources/invalid/invalid6.apm @@ -0,0 +1,22 @@ + + # ========================LICENSE_START================================= + # AEM Permission Management + # %% + # Copyright (C) 2013 Wunderman Thompson Technology + # %% + # Licensed 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. + # =========================LICENSE_END================================== + +/* define variable +*/ +DEFINE / nana diff --git a/app/aem/core/src/test/resources/invalid/invalid7.apm b/app/aem/core/src/test/resources/invalid/invalid7.apm new file mode 100644 index 000000000..0c092ad4a --- /dev/null +++ b/app/aem/core/src/test/resources/invalid/invalid7.apm @@ -0,0 +1,22 @@ + + # ========================LICENSE_START================================= + # AEM Permission Management + # %% + # Copyright (C) 2013 Wunderman Thompson Technology + # %% + # Licensed 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. + # =========================LICENSE_END================================== + + +DEFINE / nana diff --git a/app/aem/core/src/test/resources/invalid/invalid8.apm b/app/aem/core/src/test/resources/invalid/invalid8.apm new file mode 100644 index 000000000..3179844bb --- /dev/null +++ b/app/aem/core/src/test/resources/invalid/invalid8.apm @@ -0,0 +1,21 @@ + + # ========================LICENSE_START================================= + # AEM Permission Management + # %% + # Copyright (C) 2013 Wunderman Thompson Technology + # %% + # Licensed 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. + # =========================LICENSE_END================================== + +DEFINE nana "nana" +// define variable diff --git a/app/aem/core/src/test/resources/invalid/invalid9.apm b/app/aem/core/src/test/resources/invalid/invalid9.apm new file mode 100644 index 000000000..84e8a1f2b --- /dev/null +++ b/app/aem/core/src/test/resources/invalid/invalid9.apm @@ -0,0 +1,21 @@ + + # ========================LICENSE_START================================= + # AEM Permission Management + # %% + # Copyright (C) 2013 Wunderman Thompson Technology + # %% + # Licensed 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. + # =========================LICENSE_END================================== + +DEFINE nana "nana" +/ define variable diff --git a/app/aem/install/src/main/java/com/cognifide/apm/install/launchers/ApmInstallService.java b/app/aem/install/src/main/java/com/cognifide/apm/install/launchers/ApmInstallService.java index 15b3a2de1..3c582c915 100644 --- a/app/aem/install/src/main/java/com/cognifide/apm/install/launchers/ApmInstallService.java +++ b/app/aem/install/src/main/java/com/cognifide/apm/install/launchers/ApmInstallService.java @@ -30,11 +30,14 @@ import com.cognifide.apm.core.services.ResourceResolverProvider; import com.cognifide.apm.core.services.version.ScriptVersion; import com.cognifide.apm.core.services.version.VersionService; +import com.cognifide.apm.core.utils.RuntimeUtils; import com.cognifide.apm.core.utils.sling.SlingHelper; +import java.lang.management.ManagementFactory; import java.util.Arrays; import java.util.List; import java.util.Objects; import java.util.stream.Collectors; +import org.apache.commons.lang3.StringUtils; import org.apache.sling.api.resource.PersistenceException; import org.apache.sling.api.resource.ResourceResolver; import org.osgi.service.component.annotations.Activate; @@ -55,6 +58,8 @@ @Designate(ocd = ApmInstallService.Configuration.class, factory = true) public class ApmInstallService extends AbstractLauncher { + private static final String AEM_MUTABLE_CONTENT_INSTANCE = "aem-install-mutable-content"; + @Reference private ResourceResolverProvider resolverProvider; @@ -72,7 +77,13 @@ public class ApmInstallService extends AbstractLauncher { @Activate public void activate(Configuration config) { - SlingHelper.operateTraced(resolverProvider, resolver -> processScripts(config, resolver)); + SlingHelper.operateTraced(resolverProvider, resolver -> { + boolean compositeNodeStore = RuntimeUtils.determineCompositeNodeStore(resolver); + String instanceName = ManagementFactory.getRuntimeMXBean().getName(); + if (!compositeNodeStore || StringUtils.contains(instanceName, AEM_MUTABLE_CONTENT_INSTANCE)) { + processScripts(config, resolver); + } + }); } private void processScripts(Configuration config, ResourceResolver resolver) throws PersistenceException { diff --git a/app/aem/ui.apps.base/src/main/content/jcr_root/apps/apm/components/scriptsRow/scriptsRow.html b/app/aem/ui.apps.base/src/main/content/jcr_root/apps/apm/components/scriptsRow/scriptsRow.html index da82b8d64..239b31faf 100644 --- a/app/aem/ui.apps.base/src/main/content/jcr_root/apps/apm/components/scriptsRow/scriptsRow.html +++ b/app/aem/ui.apps.base/src/main/content/jcr_root/apps/apm/components/scriptsRow/scriptsRow.html @@ -60,6 +60,5 @@ - diff --git a/app/aem/ui.apps.base/src/main/content/jcr_root/apps/apm/views/scripts/.content.xml b/app/aem/ui.apps.base/src/main/content/jcr_root/apps/apm/views/scripts/.content.xml index 1531a9861..fda2271cd 100644 --- a/app/aem/ui.apps.base/src/main/content/jcr_root/apps/apm/views/scripts/.content.xml +++ b/app/aem/ui.apps.base/src/main/content/jcr_root/apps/apm/views/scripts/.content.xml @@ -108,6 +108,20 @@ variant="quiet"/> + + + + + +