Author: dj
Date: Fri Mar 18 10:00:35 2016
New Revision: 1735564
URL: http://svn.apache.org/viewvc?rev=1735564&view=rev
Log:
OAK-4003 - Add support for Group-Membership actions
Added:
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/spi/security/user/action/AbstractGroupAction.java
- copied, changed from r1735040,
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/spi/security/user/action/AbstractAuthorizableAction.java
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/spi/security/user/action/GroupAction.java
(with props)
jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/spi/security/user/action/GroupActionBestEffortTest.java
(with props)
jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/spi/security/user/action/GroupActionTest.java
(with props)
jackrabbit/oak/trunk/oak-doc/src/site/markdown/security/user/groupaction.md
- copied, changed from r1735040,
jackrabbit/oak/trunk/oak-doc/src/site/markdown/security/user/authorizableaction.md
jackrabbit/oak/trunk/oak-jcr/src/test/java/org/apache/jackrabbit/oak/jcr/security/user/GroupImportWithActionsBestEffortTest.java
(with props)
jackrabbit/oak/trunk/oak-jcr/src/test/java/org/apache/jackrabbit/oak/jcr/security/user/GroupImportWithActionsTest.java
- copied, changed from r1735040,
jackrabbit/oak/trunk/oak-jcr/src/test/java/org/apache/jackrabbit/oak/jcr/security/user/GroupImportIgnoreTest.java
Modified:
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/security/user/GroupImpl.java
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/security/user/UserImporter.java
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/security/user/UserManagerImpl.java
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/spi/security/user/action/package-info.java
jackrabbit/oak/trunk/oak-doc/src/site/markdown/security/user.md
Modified:
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/security/user/GroupImpl.java
URL:
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/security/user/GroupImpl.java?rev=1735564&r1=1735563&r2=1735564&view=diff
==============================================================================
---
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/security/user/GroupImpl.java
(original)
+++
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/security/user/GroupImpl.java
Fri Mar 18 10:00:35 2016
@@ -19,6 +19,7 @@ package org.apache.jackrabbit.oak.securi
import java.security.Principal;
import java.util.Iterator;
import java.util.Set;
+
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import javax.jcr.RepositoryException;
@@ -116,7 +117,13 @@ class GroupImpl extends AuthorizableImpl
}
}
- return getMembershipProvider().addMember(getTree(),
authorizableImpl.getTree());
+ boolean success = getMembershipProvider().addMember(getTree(),
authorizableImpl.getTree());
+
+ if (success) {
+ getUserManager().onGroupUpdate(this, false, authorizable);
+ }
+
+ return success;
}
@Override
@@ -154,7 +161,14 @@ class GroupImpl extends AuthorizableImpl
return false;
} else {
Tree memberTree = ((AuthorizableImpl) authorizable).getTree();
- return getMembershipProvider().removeMember(getTree(), memberTree);
+
+ boolean success = getMembershipProvider().removeMember(getTree(),
memberTree);
+
+ if (success) {
+ getUserManager().onGroupUpdate(this, true, authorizable);
+ }
+
+ return success;
}
}
@@ -246,6 +260,7 @@ class GroupImpl extends AuthorizableImpl
*/
private final Set<String> updateMembers(boolean isRemove, @Nonnull
String... memberIds) throws RepositoryException {
Set<String> idSet =
Sets.newLinkedHashSet(Lists.newArrayList(memberIds));
+ Set<String> processedIds = Sets.newLinkedHashSet();
int importBehavior =
UserUtil.getImportBehavior(getUserManager().getConfig());
Iterator<String> idIterator = idSet.iterator();
@@ -282,8 +297,12 @@ class GroupImpl extends AuthorizableImpl
}
if (success) {
idIterator.remove();
+ processedIds.add(memberId);
}
}
+
+ getUserManager().onGroupUpdate(this, isRemove, false, processedIds,
idSet);
+
return idSet;
}
Modified:
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/security/user/UserImporter.java
URL:
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/security/user/UserImporter.java?rev=1735564&r1=1735563&r2=1735564&view=diff
==============================================================================
---
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/security/user/UserImporter.java
(original)
+++
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/security/user/UserImporter.java
Fri Mar 18 10:00:35 2016
@@ -35,6 +35,7 @@ import javax.jcr.Session;
import javax.jcr.nodetype.ConstraintViolationException;
import javax.jcr.nodetype.PropertyDefinition;
+import com.google.common.collect.Sets;
import org.apache.jackrabbit.api.JackrabbitSession;
import org.apache.jackrabbit.api.security.principal.PrincipalIterator;
import org.apache.jackrabbit.api.security.principal.PrincipalManager;
@@ -639,9 +640,17 @@ class UserImporter implements ProtectedP
Tree groupTree = root.getTree(gr.getPath());
MembershipProvider membershipProvider =
userManager.getMembershipProvider();
+ Set<String> memberContentIds = Sets.newLinkedHashSet();
+ Set<String> failedContentIds = Sets.newLinkedHashSet();
for (String member : nonExisting) {
- membershipProvider.addMember(groupTree, member);
+ boolean success = membershipProvider.addMember(groupTree,
member);
+ if (success) {
+ memberContentIds.add(member);
+ } else {
+ failedContentIds.add(member);
+ }
}
+ userManager.onGroupUpdate(gr, false, true, memberContentIds,
failedContentIds);
}
}
}
Modified:
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/security/user/UserManagerImpl.java
URL:
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/security/user/UserManagerImpl.java?rev=1735564&r1=1735563&r2=1735564&view=diff
==============================================================================
---
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/security/user/UserManagerImpl.java
(original)
+++
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/security/user/UserManagerImpl.java
Fri Mar 18 10:00:35 2016
@@ -20,6 +20,8 @@ import java.io.UnsupportedEncodingExcept
import java.security.NoSuchAlgorithmException;
import java.security.Principal;
import java.util.Iterator;
+import java.util.List;
+import java.util.Set;
import javax.annotation.CheckForNull;
import javax.annotation.Nonnull;
@@ -28,6 +30,7 @@ import javax.jcr.RepositoryException;
import javax.jcr.UnsupportedRepositoryOperationException;
import com.google.common.base.Strings;
+import com.google.common.collect.Lists;
import org.apache.jackrabbit.api.security.principal.PrincipalManager;
import org.apache.jackrabbit.api.security.user.Authorizable;
import org.apache.jackrabbit.api.security.user.AuthorizableExistsException;
@@ -52,6 +55,7 @@ import org.apache.jackrabbit.oak.spi.sec
import org.apache.jackrabbit.oak.spi.security.user.action.AuthorizableAction;
import
org.apache.jackrabbit.oak.spi.security.user.action.AuthorizableActionProvider;
import
org.apache.jackrabbit.oak.spi.security.user.action.DefaultAuthorizableActionProvider;
+import org.apache.jackrabbit.oak.spi.security.user.action.GroupAction;
import org.apache.jackrabbit.oak.spi.security.user.util.PasswordUtil;
import org.apache.jackrabbit.oak.spi.security.user.util.UserUtil;
import org.apache.jackrabbit.oak.util.NodeUtil;
@@ -309,6 +313,52 @@ public class UserManagerImpl implements
}
}
+ /**
+ * Upon a group being updated (single {@code Authorizable} successfully
added or removed),
+ * call available {@code GroupAction}s and execute the method specific to
removal or addition.
+ * {@code GroupAction}s may then validate or modify the changes.
+ *
+ * @param group The target group.
+ * @param isRemove Indicates whether the member is removed or added.
+ * @param member The member successfully removed or added.
+ * @throws RepositoryException If an error occurs.
+ */
+ void onGroupUpdate(@Nonnull Group group, boolean isRemove, @Nonnull
Authorizable member) throws RepositoryException {
+ for (GroupAction action : selectGroupActions()) {
+ if (isRemove) {
+ action.onMemberRemoved(group, member, root, namePathMapper);
+ } else {
+ action.onMemberAdded(group, member, root, namePathMapper);
+ }
+ }
+ }
+
+ /**
+ * Upon a group being updated (multiple {@code memberIds} added or
removed),
+ * call available {@code GroupAction}s and execute the method specific to
removal or addition.
+ * {@code GroupAction}s may then validate or modify the changes.
+ *
+ * @param group The target group.
+ * @param isRemove Indicates whether the member is removed or added.
+ * @param isContentId Indicates whether member ids are expressed as
content-ids (UUID) or member-ids.
+ * @param memberIds The IDs of all members successfully removed or added.
+ * @param failedIds The IDs of all members whose addition or removal
failed.
+ * @throws RepositoryException If an error occurs.
+ */
+ void onGroupUpdate(@Nonnull Group group, boolean isRemove, boolean
isContentId, @Nonnull Set<String> memberIds, @Nonnull Set<String> failedIds)
throws RepositoryException {
+ for (GroupAction action : selectGroupActions()) {
+ if (isRemove) {
+ action.onMembersRemoved(group, memberIds, failedIds, root,
namePathMapper);
+ } else {
+ if (isContentId) {
+ action.onMembersAddedContentId(group, memberIds,
failedIds, root, namePathMapper);
+ } else {
+ action.onMembersAdded(group, memberIds, failedIds, root,
namePathMapper);
+ }
+ }
+ }
+ }
+
//--------------------------------------------------------------------------
@CheckForNull
Authorizable getAuthorizable(@CheckForNull Tree tree) throws
RepositoryException {
@@ -439,4 +489,20 @@ public class UserManagerImpl implements
}
return queryManager;
}
+
+ /**
+ * Select only {@code GroupAction}s from the available {@code
AuthorizableAction}s.
+ *
+ * @return A {@code List} of {@code GroupAction}s. List may be empty.
+ */
+ @Nonnull
+ private List<GroupAction> selectGroupActions() {
+ List<GroupAction> actions = Lists.newArrayList();
+ for (AuthorizableAction action :
actionProvider.getAuthorizableActions(securityProvider)) {
+ if (action instanceof GroupAction) {
+ actions.add((GroupAction) action);
+ }
+ }
+ return actions;
+ }
}
\ No newline at end of file
Copied:
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/spi/security/user/action/AbstractGroupAction.java
(from r1735040,
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/spi/security/user/action/AbstractAuthorizableAction.java)
URL:
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/spi/security/user/action/AbstractGroupAction.java?p2=jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/spi/security/user/action/AbstractGroupAction.java&p1=jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/spi/security/user/action/AbstractAuthorizableAction.java&r1=1735040&r2=1735564&rev=1735564&view=diff
==============================================================================
---
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/spi/security/user/action/AbstractAuthorizableAction.java
(original)
+++
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/spi/security/user/action/AbstractGroupAction.java
Fri Mar 18 10:00:35 2016
@@ -16,31 +16,28 @@
*/
package org.apache.jackrabbit.oak.spi.security.user.action;
-import javax.annotation.Nonnull;
-import javax.annotation.Nullable;
-import javax.jcr.RepositoryException;
-
import org.apache.jackrabbit.api.security.user.Authorizable;
import org.apache.jackrabbit.api.security.user.Group;
-import org.apache.jackrabbit.api.security.user.User;
import org.apache.jackrabbit.oak.api.Root;
import org.apache.jackrabbit.oak.namepath.NamePathMapper;
-import org.apache.jackrabbit.oak.spi.security.ConfigurationParameters;
-import org.apache.jackrabbit.oak.spi.security.SecurityProvider;
+
+import javax.annotation.Nonnull;
+import javax.jcr.RepositoryException;
/**
- * Abstract implementation of the {@code AuthorizableAction} interface that
+ * Abstract implementation of the {@code GroupAction} interface that
* doesn't perform any action. This is a convenience implementation allowing
* subclasses to only implement methods that need extra attention.
*/
-public abstract class AbstractAuthorizableAction implements AuthorizableAction
{
+public abstract class AbstractGroupAction extends AbstractAuthorizableAction
implements GroupAction {
+
+ //-------------------------------------------------< GroupAction >---
- //-------------------------------------------------< AuthorizableAction
>---
/**
* Doesn't perform any action.
*/
@Override
- public void init(SecurityProvider securityProvider,
ConfigurationParameters config) {
+ public void onMemberAdded(@Nonnull Group group, @Nonnull Authorizable
member, @Nonnull Root root, @Nonnull NamePathMapper namePathMapper) throws
RepositoryException {
// nothing to do
}
@@ -48,7 +45,7 @@ public abstract class AbstractAuthorizab
* Doesn't perform any action.
*/
@Override
- public void onCreate(@Nonnull Group group, @Nonnull Root root, @Nonnull
NamePathMapper namePathMapper) throws RepositoryException {
+ public void onMembersAdded(@Nonnull Group group, @Nonnull Iterable<String>
memberIds, @Nonnull Iterable<String> failedIds, @Nonnull Root root, @Nonnull
NamePathMapper namePathMapper) throws RepositoryException {
// nothing to do
}
@@ -56,7 +53,7 @@ public abstract class AbstractAuthorizab
* Doesn't perform any action.
*/
@Override
- public void onCreate(@Nonnull User user, @Nullable String password,
@Nonnull Root root, @Nonnull NamePathMapper namePathMapper) throws
RepositoryException {
+ public void onMembersAddedContentId(Group group, Iterable<String>
memberContentIds, Iterable<String> failedIds, Root root, NamePathMapper
namePathMapper) throws RepositoryException {
// nothing to do
}
@@ -64,7 +61,7 @@ public abstract class AbstractAuthorizab
* Doesn't perform any action.
*/
@Override
- public void onRemove(@Nonnull Authorizable authorizable, @Nonnull Root
root, @Nonnull NamePathMapper namePathMapper) throws RepositoryException {
+ public void onMemberRemoved(@Nonnull Group group, @Nonnull Authorizable
member, @Nonnull Root root, @Nonnull NamePathMapper namePathMapper) throws
RepositoryException {
// nothing to do
}
@@ -72,7 +69,7 @@ public abstract class AbstractAuthorizab
* Doesn't perform any action.
*/
@Override
- public void onPasswordChange(@Nonnull User user, @Nullable String
newPassword, @Nonnull Root root, @Nonnull NamePathMapper namePathMapper) throws
RepositoryException {
+ public void onMembersRemoved(@Nonnull Group group, @Nonnull
Iterable<String> memberIds, @Nonnull Iterable<String> failedIds, @Nonnull Root
root, @Nonnull NamePathMapper namePathMapper) throws RepositoryException {
// nothing to do
}
}
Added:
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/spi/security/user/action/GroupAction.java
URL:
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/spi/security/user/action/GroupAction.java?rev=1735564&view=auto
==============================================================================
---
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/spi/security/user/action/GroupAction.java
(added)
+++
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/spi/security/user/action/GroupAction.java
Fri Mar 18 10:00:35 2016
@@ -0,0 +1,115 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.jackrabbit.oak.spi.security.user.action;
+
+import org.apache.jackrabbit.api.security.user.Authorizable;
+import org.apache.jackrabbit.api.security.user.Group;
+import org.apache.jackrabbit.oak.api.Root;
+import org.apache.jackrabbit.oak.namepath.NamePathMapper;
+
+import javax.jcr.RepositoryException;
+
+/**
+ * The {@code GroupAction} interface allows for implementations to be informed
about and react to the following
+ * changes to a {@link Group}'s members:
+ *
+ * <ul>
+ * <li>{@link #onMemberAdded(Group, Authorizable, Root, NamePathMapper)}</li>
+ * <li>{@link #onMembersAdded(Group, Iterable, Iterable, Root,
NamePathMapper)}</li>
+ * <li>{@link #onMembersAddedContentId(Group, Iterable, Iterable, Root,
NamePathMapper)} </li>
+ * <li>{@link #onMemberRemoved(Group, Authorizable, Root, NamePathMapper)}</li>
+ * <li>{@link #onMembersRemoved(Group, Iterable, Iterable, Root,
NamePathMapper)}</li>
+ * </ul>
+ *
+ * <p>
+ * Please consult the parent interface {@link AuthorizableAction} for details
on persisting changes,
+ * configuring actions and the API through which actions are invoked.
+ * </p>
+ *
+ * <p>
+ * For convenience, an {@link AbstractGroupAction} is provided.
+ * </p>
+ * @since OAK 1.6
+ */
+public interface GroupAction extends AuthorizableAction {
+
+ /**
+ * A specific {@link Authorizable} was added as a member of the {@link
Group}.
+ * Implementations may perform specific modifications or validations.
+ *
+ * @param group The {@link Group} to which the {@link
Authorizable} was added.
+ * @param member The {@link Authorizable} added.
+ * @param root The root associated with the user manager.
+ * @param namePathMapper
+ * @throws RepositoryException If an error occurs.
+ */
+ void onMemberAdded(Group group, Authorizable member, Root root,
NamePathMapper namePathMapper) throws RepositoryException;
+
+ /**
+ * Multiple members were added to the {@link Group}. The members are
provided as an iterable
+ * of their string-based IDs, as some members may no longer or not yet
exist.
+ * Implementations may perform specific modifications or validations.
+ *
+ * @param group The {@link Group} to which the members were added.
+ * @param memberIds An {@link Iterable} of the member IDs.
+ * @param root The root associated with the user manager.
+ * @param namePathMapper
+ * @throws RepositoryException If an error occurs.
+ */
+ void onMembersAdded(Group group, Iterable<String> memberIds,
Iterable<String> failedIds, Root root, NamePathMapper namePathMapper) throws
RepositoryException;
+
+ /**
+ * Multiple members were added to the {@link Group} during XML group
import.
+ * The members are provided as an iterable of their string-based content
IDs (UUIDs), as these
+ * members do not exist yet (group imported before users). Implementations
may track such content ids
+ * for later processing once the user identified by the content id is
added.
+ * <p/>
+ * Implementations may perform specific modifications or validations.
+ *
+ * @param group The {@link Group} to which the members were
added.
+ * @param memberContentIds An {@link Iterable} of the member content IDs
(UUIDs).
+ * @param root The root associated with the user manager.
+ * @param namePathMapper
+ * @throws RepositoryException If an error occurs.
+ */
+ void onMembersAddedContentId(Group group, Iterable<String>
memberContentIds, Iterable<String> failedIds, Root root, NamePathMapper
namePathMapper) throws RepositoryException;
+
+ /**
+ * A specific {@link Authorizable} was removed from the {@link Group}.
+ * Implementations may perform specific modifications or validations.
+ *
+ * @param group The {@link Group} from which the {@link
Authorizable} was removed.
+ * @param member The {@link Authorizable} removed.
+ * @param root The root associated with the user manager.
+ * @param namePathMapper
+ * @throws RepositoryException If an error occurs.
+ */
+ void onMemberRemoved(Group group, Authorizable member, Root root,
NamePathMapper namePathMapper) throws RepositoryException;
+
+ /**
+ * Multiple members were removed from the {@link Group}. The members are
provided as an iterable
+ * of their string-based IDs, as some members may no longer or not yet
exist.
+ * Implementations may perform specific modifications or validations.
+ *
+ * @param group The {@link Group} from which the members were
removed.
+ * @param memberIds An {@link Iterable} of the member IDs.
+ * @param root The root associated with the user manager.
+ * @param namePathMapper
+ * @throws RepositoryException If an error occurs.
+ */
+ void onMembersRemoved(Group group, Iterable<String> memberIds,
Iterable<String> failedIds, Root root, NamePathMapper namePathMapper) throws
RepositoryException;
+}
Propchange:
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/spi/security/user/action/GroupAction.java
------------------------------------------------------------------------------
svn:eol-style = native
Modified:
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/spi/security/user/action/package-info.java
URL:
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/spi/security/user/action/package-info.java?rev=1735564&r1=1735563&r2=1735564&view=diff
==============================================================================
---
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/spi/security/user/action/package-info.java
(original)
+++
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/spi/security/user/action/package-info.java
Fri Mar 18 10:00:35 2016
@@ -14,7 +14,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-@Version("1.0.2")
+@Version("1.1.0")
@Export(optional = "provide:=true")
package org.apache.jackrabbit.oak.spi.security.user.action;
Added:
jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/spi/security/user/action/GroupActionBestEffortTest.java
URL:
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/spi/security/user/action/GroupActionBestEffortTest.java?rev=1735564&view=auto
==============================================================================
---
jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/spi/security/user/action/GroupActionBestEffortTest.java
(added)
+++
jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/spi/security/user/action/GroupActionBestEffortTest.java
Fri Mar 18 10:00:35 2016
@@ -0,0 +1,53 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.jackrabbit.oak.spi.security.user.action;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Iterables;
+import org.apache.jackrabbit.oak.spi.xml.ImportBehavior;
+import org.junit.Test;
+
+import java.util.List;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+public class GroupActionBestEffortTest extends GroupActionTest {
+
+ @Test
+ public void testMembersAddedNonExisting() throws Exception {
+ List<String> nonExisting = ImmutableList.of("blinder", "passagier");
+
+ testGroup.addMembers(nonExisting.toArray(new
String[nonExisting.size()]));
+ assertTrue(Iterables.elementsEqual(nonExisting,
groupAction.memberIds));
+ assertFalse(groupAction.failedIds.iterator().hasNext());
+ }
+
+ @Test
+ public void testMembersRemovedNonExisting() throws Exception {
+ List<String> nonExisting = ImmutableList.of("blinder", "passagier");
+
+ testGroup.removeMembers(nonExisting.toArray(new
String[nonExisting.size()]));
+ assertFalse(groupAction.memberIds.iterator().hasNext());
+ assertTrue(Iterables.elementsEqual(nonExisting,
groupAction.failedIds));
+ }
+
+ @Override
+ String getImportBehavior() {
+ return ImportBehavior.NAME_BESTEFFORT;
+ }
+}
Propchange:
jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/spi/security/user/action/GroupActionBestEffortTest.java
------------------------------------------------------------------------------
svn:eol-style = native
Added:
jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/spi/security/user/action/GroupActionTest.java
URL:
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/spi/security/user/action/GroupActionTest.java?rev=1735564&view=auto
==============================================================================
---
jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/spi/security/user/action/GroupActionTest.java
(added)
+++
jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/spi/security/user/action/GroupActionTest.java
Fri Mar 18 10:00:35 2016
@@ -0,0 +1,255 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.jackrabbit.oak.spi.security.user.action;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Iterables;
+import org.apache.jackrabbit.api.security.user.Authorizable;
+import org.apache.jackrabbit.api.security.user.Group;
+import org.apache.jackrabbit.api.security.user.User;
+import org.apache.jackrabbit.oak.AbstractSecurityTest;
+import org.apache.jackrabbit.oak.api.Root;
+import org.apache.jackrabbit.oak.namepath.NamePathMapper;
+import org.apache.jackrabbit.oak.security.SecurityProviderImpl;
+import org.apache.jackrabbit.oak.security.user.UserConfigurationImpl;
+import org.apache.jackrabbit.oak.spi.security.ConfigurationParameters;
+import org.apache.jackrabbit.oak.spi.security.SecurityProvider;
+import org.apache.jackrabbit.oak.spi.security.principal.PrincipalProvider;
+import org.apache.jackrabbit.oak.spi.security.user.UserConfiguration;
+import org.apache.jackrabbit.oak.spi.security.user.UserConstants;
+import org.apache.jackrabbit.oak.spi.xml.ImportBehavior;
+import org.apache.jackrabbit.oak.spi.xml.ProtectedItemImporter;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+import javax.jcr.RepositoryException;
+import java.util.List;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+public class GroupActionTest extends AbstractSecurityTest {
+
+ private static final String TEST_GROUP_ID = "testGroup";
+ private static final String TEST_USER_PREFIX = "testUser";
+
+ TestGroupAction groupAction = new TestGroupAction();
+ Group testGroup;
+ private User testUser01;
+ private User testUser02;
+
+ @Before
+ public void before() throws Exception {
+ super.before();
+
+ testGroup = getUserManager(root).createGroup(TEST_GROUP_ID);
+ root.commit();
+ }
+
+ @After
+ public void after() throws Exception {
+ if (testGroup != null) {
+ testGroup.remove();
+ root.commit();
+ }
+
+ if (testUser01 != null) {
+ testUser01.remove();
+ root.commit();
+ }
+
+ if (testUser02 != null) {
+ testUser02.remove();
+ root.commit();
+ }
+
+ root = null;
+ super.after();
+ }
+
+ @Test
+ public void testMemberAdded() throws Exception {
+ testUser01 = getUserManager(root).createUser(TEST_USER_PREFIX + "01",
"");
+
+ testGroup.addMember(testUser01);
+ assertTrue(groupAction.onMemberAddedCalled);
+ assertEquals(testGroup, groupAction.group);
+ assertEquals(testUser01, groupAction.member);
+ }
+
+ @Test
+ public void testMemberRemoved() throws Exception {
+ testUser01 = getUserManager(root).createUser(TEST_USER_PREFIX + "01",
"");
+ testGroup.addMember(testUser01);
+ root.commit();
+
+ testGroup.removeMember(testUser01);
+ assertTrue(groupAction.onMemberRemovedCalled);
+ assertEquals(testGroup, groupAction.group);
+ assertEquals(testUser01, groupAction.member);
+ }
+
+ @Test
+ public void testMembersAdded() throws Exception {
+ testUser01 = getUserManager(root).createUser(TEST_USER_PREFIX + "01",
"");
+ testUser02 = getUserManager(root).createUser(TEST_USER_PREFIX + "02",
"");
+ testGroup.addMember(testUser02);
+
+ List<String> memberIds = ImmutableList.of(testUser01.getID());
+ List<String> failedIds = ImmutableList.of(testUser02.getID(),
testGroup.getID());
+ Iterable<String> ids = Iterables.concat(memberIds, failedIds);
+
+ testGroup.addMembers(Iterables.toArray(ids, String.class));
+ assertTrue(groupAction.onMembersAddedCalled);
+ assertEquals(testGroup, groupAction.group);
+ assertTrue(Iterables.elementsEqual(memberIds, groupAction.memberIds));
+ assertTrue(Iterables.elementsEqual(failedIds, groupAction.failedIds));
+ }
+
+ @Test
+ public void testMembersAddedNonExisting() throws Exception {
+ List<String> nonExisting = ImmutableList.of("blinder", "passagier");
+
+ testGroup.addMembers(nonExisting.toArray(new
String[nonExisting.size()]));
+ assertFalse(groupAction.memberIds.iterator().hasNext());
+ assertTrue(Iterables.elementsEqual(nonExisting,
groupAction.failedIds));
+ }
+
+ @Test
+ public void testMembersRemoved() throws Exception {
+ testUser01 = getUserManager(root).createUser(TEST_USER_PREFIX + "01",
"");
+ testUser02 = getUserManager(root).createUser(TEST_USER_PREFIX + "02",
"");
+ testGroup.addMember(testUser01);
+
+ List<String> memberIds = ImmutableList.of(testUser01.getID());
+ List<String> failedIds = ImmutableList.of(testUser02.getID(),
testGroup.getID());
+ Iterable<String> ids = Iterables.concat(memberIds, failedIds);
+
+ testGroup.removeMembers(Iterables.toArray(ids, String.class));
+ assertTrue(groupAction.onMembersRemovedCalled);
+ assertEquals(testGroup, groupAction.group);
+ assertTrue(Iterables.elementsEqual(memberIds, groupAction.memberIds));
+ assertTrue(Iterables.elementsEqual(failedIds, groupAction.failedIds));
+ }
+
+ @Test
+ public void testMembersRemovedNonExisting() throws Exception {
+ List<String> nonExisting = ImmutableList.of("blinder", "passagier");
+
+ testGroup.removeMembers(nonExisting.toArray(new
String[nonExisting.size()]));
+ assertFalse(groupAction.memberIds.iterator().hasNext());
+ assertTrue(Iterables.elementsEqual(nonExisting,
groupAction.failedIds));
+ }
+
+ @Override
+ protected SecurityProvider getSecurityProvider() {
+ if (securityProvider == null) {
+ securityProvider = new TestSecurityProvider();
+ }
+ return securityProvider;
+ }
+
+ class TestGroupAction extends AbstractGroupAction {
+
+ boolean onMemberAddedCalled = false;
+ boolean onMembersAddedCalled = false;
+ boolean onMemberRemovedCalled = false;
+ boolean onMembersRemovedCalled = false;
+
+ Group group;
+ Iterable<String> memberIds;
+ Iterable<String> failedIds;
+ Authorizable member;
+
+ @Override
+ public void onMemberAdded(@Nonnull Group group, @Nonnull Authorizable
member, @Nonnull Root root, @Nonnull NamePathMapper namePathMapper) throws
RepositoryException {
+ this.group = group;
+ this.member = member;
+ onMemberAddedCalled = true;
+ }
+
+ @Override
+ public void onMembersAdded(@Nonnull Group group, @Nonnull
Iterable<String> memberIds, @Nonnull Iterable<String> failedIds, @Nonnull Root
root, @Nonnull NamePathMapper namePathMapper) throws RepositoryException {
+ this.group = group;
+ this.memberIds = memberIds;
+ this.failedIds = failedIds;
+ onMembersAddedCalled = true;
+ }
+
+ @Override
+ public void onMemberRemoved(@Nonnull Group group, @Nonnull
Authorizable member, @Nonnull Root root, @Nonnull NamePathMapper
namePathMapper) throws RepositoryException {
+ this.group = group;
+ this.member = member;
+ onMemberRemovedCalled = true;
+ }
+
+ @Override
+ public void onMembersRemoved(@Nonnull Group group, @Nonnull
Iterable<String> memberIds, @Nonnull Iterable<String> failedIds, @Nonnull Root
root, @Nonnull NamePathMapper namePathMapper) throws RepositoryException {
+ this.group = group;
+ this.memberIds = memberIds;
+ this.failedIds = failedIds;
+ onMembersRemovedCalled = true;
+ }
+ }
+
+ private class TestSecurityProvider extends SecurityProviderImpl {
+
+ private final AuthorizableActionProvider actionProvider;
+
+ private TestSecurityProvider() {
+ actionProvider = new AuthorizableActionProvider() {
+ @Nonnull
+ @Override
+ public List<? extends AuthorizableAction>
getAuthorizableActions(@Nonnull SecurityProvider securityProvider) {
+ return ImmutableList.of(groupAction);
+ }
+ };
+ }
+
+ @Nonnull
+ public <T> T getConfiguration(@Nonnull Class<T> configClass) {
+ if (UserConfiguration.class == configClass) {
+ return (T) new UserConfigurationImpl(this) {
+ @Nonnull
+ @Override
+ public ConfigurationParameters getParameters() {
+ return
ConfigurationParameters.of(super.getParameters(),
+
ConfigurationParameters.of(UserConstants.PARAM_AUTHORIZABLE_ACTION_PROVIDER,
actionProvider),
+
ConfigurationParameters.of(ProtectedItemImporter.PARAM_IMPORT_BEHAVIOR,
getImportBehavior())
+ );
+ }
+
+ @Nullable
+ @Override
+ public PrincipalProvider getUserPrincipalProvider(@Nonnull
Root root, @Nonnull NamePathMapper namePathMapper) {
+ return null;
+ }
+ };
+ } else {
+ return super.getConfiguration(configClass);
+ }
+ }
+ }
+
+ String getImportBehavior() {
+ return ImportBehavior.NAME_IGNORE;
+ }
+}
Propchange:
jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/spi/security/user/action/GroupActionTest.java
------------------------------------------------------------------------------
svn:eol-style = native
Modified: jackrabbit/oak/trunk/oak-doc/src/site/markdown/security/user.md
URL:
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-doc/src/site/markdown/security/user.md?rev=1735564&r1=1735563&r2=1735564&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-doc/src/site/markdown/security/user.md (original)
+++ jackrabbit/oak/trunk/oak-doc/src/site/markdown/security/user.md Fri Mar 18
10:00:35 2016
@@ -50,6 +50,7 @@ interfaces and classes:
- `AuthorizableType`: ease handling with the different authorizable types.
- `AuthorizableAction` and `AuthorizableActionProvider`: see [Authorizable
Actions](user/authorizableaction.html) for details.
- `AuthorizableNodeName`: see section [Authorizable Node Name
Generation](user/authorizablenodename.html).
+- `GroupAction` (via `AuthorizableActionProvider`): see [Group
Actions](user/groupaction.html) for details.
- `UserAuthenticationFactory`: see sections
[pluggability](user/default.html#pluggability)
and [user authentication](authentication/default.html#user_authentication) for
additional details.
Copied:
jackrabbit/oak/trunk/oak-doc/src/site/markdown/security/user/groupaction.md
(from r1735040,
jackrabbit/oak/trunk/oak-doc/src/site/markdown/security/user/authorizableaction.md)
URL:
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-doc/src/site/markdown/security/user/groupaction.md?p2=jackrabbit/oak/trunk/oak-doc/src/site/markdown/security/user/groupaction.md&p1=jackrabbit/oak/trunk/oak-doc/src/site/markdown/security/user/authorizableaction.md&r1=1735040&r2=1735564&rev=1735564&view=diff
==============================================================================
---
jackrabbit/oak/trunk/oak-doc/src/site/markdown/security/user/authorizableaction.md
(original)
+++ jackrabbit/oak/trunk/oak-doc/src/site/markdown/security/user/groupaction.md
Fri Mar 18 10:00:35 2016
@@ -15,175 +15,123 @@
limitations under the License.
-->
-Authorizable Actions
---------------------------------------------------------------------------------
+Group Actions
+-------------
### Overview
-Oak 1.0 comes with a extension to the Jackrabbit user management API that
allows
-to perform additional actions or validations upon common user management tasks
+Oak 1.6 comes with an extension to the Jackrabbit user management API that
allows
+to perform additional actions or validations upon group member management tasks
such as
-- create authorizables
-- remove authorizables
-- change a user's password
+- add an authorizable to a group
+- remove an authorizable from a group
+- add a set of member ids as members of a group
+- remove a set of member ids from a group
-Similar functionality has been present in Jackrabbit 2.x as internal interface.
-Compared to the Jackrabbit interface the new `AuthorizableAction` has been
slightly
-adjusted to match Oak requirements operate directly on the Oak API, which eases
-the handling of implementation specific tasks such as writing protected items.
+### GroupAction API
-### AuthorizableAction API
+The following public interface is provided by Oak in the package
`org.apache.jackrabbit.oak.spi.security.user.action`:
-The following public interfaces are provided by Oak in the package
`org.apache.jackrabbit.oak.spi.security.user.action`:
+- [GroupAction]
-- [AuthorizableAction]
-- [AuthorizableActionProvider]
-
-The `AuthorizableAction` interface itself allows to perform validations or
write
-additional application specific content while executing user management related
+The `GroupAction` interface extends from `AuthorizableAction` and itself
allows to perform validations or write
+additional application specific content while executing group member
management related
write operations. Therefore these actions are executed as part of the
transient
user management modifications. This contrasts to
`org.apache.jackrabbit.oak.spi.commit.CommitHook`s
which in turn are only triggered once modifications are persisted.
-Consequently, implementations of the `AuthorizableAction` interface are
expected
-to adhere to this rule and perform transient repository operation or
validation.
+Consequently, implementations of the `GroupAction` interface are expected
+to adhere to this rule and perform transient repository operations or
validation.
They must not force changes to be persisted by calling
`org.apache.jackrabbit.oak.api.Root.commit()`.
-### Default Implementations
-
-Oak 1.0 provides the following base implementations:
-
-- `AbstractAuthorizableAction`: abstract base implementation that doesn't
perform any action.
-- `DefaultAuthorizableActionProvider`: default action provider service that
allows to enable the built-in actions provided with oak.
-- `CompositeActionProvider`: Allows to aggregate multiple provider
implementations.
-
-#### Changes wrt Jackrabbit 2.x
-
-- actions no longer operate on JCR API but rather on the Oak API direct.
-- provider interface simplifies pluggability
-
-#### Built-in AuthorizableAction Implementations
-
-The following implementations of the `AuthorizableAction` interface are
provided:
-
-* `AccessControlAction`: set up permission for new authorizables
-* `PasswordValidationAction`: simplistic password verification upon user
creation and password modification
-* `PasswordChangeAction`: verifies that the new password is different from the
old one
-* `ClearMembershipAction`: clear group membership upon removal of an
authorizable.
-
-As in Jackrabbit 2.x the actions are executed with the editing session and the
+Any group actions are executed with the editing session and the
target operation will fail if any of the configured actions fails (e.g. due to
insufficient permissions by the editing Oak ContentSession).
-### Pluggability
-
-The default security setup as present with Oak 1.0 is able to provide custom
-`AuthorizableActionProvider` implementations and will automatically combine the
-different implementations using the `CompositeActionProvider`.
-
-In an OSGi setup the following steps are required in order to add an action
provider
-implementation:
-
-- implement `AuthorizableActionProvider` interface exposing your custom
action(s).
-- make the provider implementation an OSGi service and make it available to
the Oak repository.
-
-##### Examples
+### Default Implementations
-###### Example Action Provider
+Oak 1.5 provides the following base implementation for `GroupAction`
implementations to build upon:
- @Component()
- @Service(AuthorizableActionProvider.class)
- public class MyAuthorizableActionProvider implements
AuthorizableActionProvider {
+- `AbstractGroupAction`: abstract base implementation that doesn't perform any
action.
- private static final String PUBLIC_PROFILE_NAME = "publicProfileName";
- private static final String PRIVATE_PROFILE_NAME =
"privateProfileName";
- private static final String FRIENDS_PROFILE_NAME =
"friendsProfileName";
+### Pluggability
- @Property(name = PUBLIC_PROFILE_NAME, value = "publicProfile")
- private String publicName;
+Refer to [Authorizable Actions | Pluggability
](user/authorizableaction.html#Pluggability) for details on how to plug
+a new group action into the system.
- @Property(name = PRIVATE_PROFILE_NAME, value = "privateProfile")
- private String privateName;
+### XML Import
- @Property(name = FRIENDS_PROFILE_NAME, value = "friendsProfile")
- private String friendsName;
+During import the group actions are called in the same fashion as for regular
groups as long as the member reference
+can be resolved to an existing authorizable. Member IDs of authorizables that
do not exist at group import time or
+failed member IDs are passed to the group actions if
`ImportBehavior.BESTEFFORT` is set for the import.
- private ConfigurationParameters config = ConfigurationParameters.EMPTY;
+##### Examples
- public MyAuthorizableActionProvider() {}
+###### Example Action
- public MyAuthorizableActionProvider(ConfigurationParameters config) {
- this.config = config;
- }
+This example action creates or removes asset home directories for members
+added to or removed from a specific group:
- //-----------------------------------------<
AuthorizableActionProvider >---
+ public class CreateHomeForMemberGroupAction extends AbstractGroupAction {
+
+ private static final String GROUP_ID = "asset-editors";
+ private static final String ASSET_ROOT = "/content/assets";
+ private SecurityProvider securityProvider;
+
@Override
- public List<? extends AuthorizableAction>
getAuthorizableActions(SecurityProvider securityProvider) {
- AuthorizableAction action = new ProfileAction(publicName,
privateName, friendsName);
- action.init(securityProvider, config);
- return Collections.singletonList(action);
+ public void init(SecurityProvider securityProvider,
ConfigurationParameters config) {
+ this.securityProvider = securityProvider;
}
-
- //----------------------------------------------------< SCR
Integration >---
- @Activate
- private void activate(Map<String, Object> properties) {
- config = ConfigurationParameters.of(properties);
+
+ @Override
+ public void onMemberAdded(@Nonnull Group group, @Nonnull Authorizable
member, @Nonnull Root root, @Nonnull NamePathMapper namePathMapper) throws
RepositoryException {
+ createHome(group, root, member.getID(), namePathMapper);
}
- }
-
-###### Example Action
-
-This example action generates additional child nodes upon user/group creation
-that will later be used to store various target-specific profile information:
-
- class ProfileAction extends AbstractAuthorizableAction {
-
- private final String publicName;
- private final String privateName;
- private final String friendsName;
-
- ProfileAction(@Nullable String publicName, @Nullable String
privateName, @Nullable String friendsName) {
- this.publicName = publicName;
- this.privateName = privateName;
- this.friendsName = friendsName;
+
+ @Override
+ public void onMembersAdded(@Nonnull Group group, @Nonnull
Iterable<String> memberIds, @Nonnull Iterable<String> failedIds, @Nonnull Root
root, @Nonnull NamePathMapper namePathMapper) throws RepositoryException {
+ createHome(group, root, memberIds, failedIds, namePathMapper);
}
-
+
@Override
- public void onCreate(Group group, Root root, NamePathMapper
namePathMapper) throws RepositoryException {
- createProfileNodes(group.getPath(), root);
+ public void onMemberRemoved(@Nonnull Group group, @Nonnull
Authorizable member, @Nonnull Root root, @Nonnull NamePathMapper
namePathMapper) throws RepositoryException {
+ removeHome(group, root, member.getID(), namePathMapper);
}
-
+
@Override
- public void onCreate(User user, String password, Root root,
NamePathMapper namePathMapper) throws RepositoryException {
- createProfileNodes(user.getPath(), root);
+ public void onMembersRemoved(@Nonnull Group group, @Nonnull
Iterable<String> memberIds, @Nonnull Iterable<String> failedIds, @Nonnull Root
root, @Nonnull NamePathMapper namePathMapper) throws RepositoryException {
+ removeHome(group, root, memberIds, failedIds, namePathMapper);
}
-
- private void createProfileNodes(@Nonnull String authorizablePath,
@Nonnull Root root) throws AccessDeniedException {
- Tree tree = root.getTree(authorizablePath);
- if (tree.exists()) {
- NodeUtil authorizableNode = new NodeUtil(tree);
- if (publicName != null) {
- authorizableNode.addChild(publicName,
NodeTypeConstants.NT_OAK_UNSTRUCTURED);
- }
- if (privateName != null) {
- authorizableNode.addChild(privateName,
NodeTypeConstants.NT_OAK_UNSTRUCTURED);
- }
- if (friendsName != null) {
- authorizableNode.addChild(friendsName,
NodeTypeConstants.NT_OAK_UNSTRUCTURED);
+
+ private void createHome(Group group, Root root, String memberId,
NamePathMapper namePathMapper) throws RepositoryException {
+ createHome(group, root, Lists.newArrayList(memberId),
Lists.<String>newArrayList(), namePathMapper);
+ }
+
+ private void createHome(Group group, Root root, Iterable<String>
memberIds, Iterable<String> failedIds, NamePathMapper namePathMapper) throws
RepositoryException {
+ if (GROUP_ID.equals(group.getID())) {
+ UserManager userManager =
securityProvider.getConfiguration(UserConfiguration.class).getUserManager(root,
namePathMapper);
+ for (String memberId : memberIds) {
+ Authorizable authorizable =
userManager.getAuthorizable(memberId);
+ if (authorizable != null && !authorizable.isGroup()) {
+ // Note: this is done with the editing session of the
group modification and may not
+ // be the desired session / privilege level with which
to perform these actions.
+ NodeUtil assetRoot = new
NodeUtil(root.getTree(ASSET_ROOT));
+ NodeUtil home = assetRoot.addChild(memberId,
NodeTypeConstants.NT_OAK_UNSTRUCTURED);
+ // ...
+ }
}
}
}
-
-###### Example Non-OSGI Setup
-
- Map<String, Object> userParams = new HashMap<String, Object>();
- userParams.put(UserConstants.PARAM_AUTHORIZABLE_ACTION_PROVIDER, new
MyAuthorizableActionProvider());
- ConfigurationParameters config =
ConfigurationParameters.of(ImmutableMap.of(UserConfiguration.NAME,
ConfigurationParameters.of(userParams)));
- SecurityProvider securityProvider = new SecurityProviderImpl(config));
- Repository repo = new Jcr(new
Oak()).with(securityProvider).createRepository();
-
+
+ private void removeHome(Group group, Root root, String memberId,
NamePathMapper namePathMapper) {
+ removeHome(group, root, Lists.newArrayList(memberId),
Lists.<String>newArrayList(), namePathMapper);
+ }
+
+ private void removeHome(Group group, Root root, Iterable<String>
memberIds, Iterable<String> failedIds, NamePathMapper namePathMapper) {
+
+ }
+ }
<!-- hidden references -->
-[AuthorizableAction]:
/oak/docs/apidocs/org/apache/jackrabbit/oak/spi/security/user/action/AuthorizableAction.html
-[AuthorizableActionProvider]:
/oak/docs/apidocs/org/apache/jackrabbit/oak/spi/security/user/action/AuthorizableActionProvider.html
+[GroupAction]:
/oak/docs/apidocs/org/apache/jackrabbit/oak/spi/security/user/action/GroupAction.html
Added:
jackrabbit/oak/trunk/oak-jcr/src/test/java/org/apache/jackrabbit/oak/jcr/security/user/GroupImportWithActionsBestEffortTest.java
URL:
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-jcr/src/test/java/org/apache/jackrabbit/oak/jcr/security/user/GroupImportWithActionsBestEffortTest.java?rev=1735564&view=auto
==============================================================================
---
jackrabbit/oak/trunk/oak-jcr/src/test/java/org/apache/jackrabbit/oak/jcr/security/user/GroupImportWithActionsBestEffortTest.java
(added)
+++
jackrabbit/oak/trunk/oak-jcr/src/test/java/org/apache/jackrabbit/oak/jcr/security/user/GroupImportWithActionsBestEffortTest.java
Fri Mar 18 10:00:35 2016
@@ -0,0 +1,161 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.jackrabbit.oak.jcr.security.user;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Iterables;
+import com.google.common.collect.Lists;
+import org.apache.jackrabbit.api.security.user.Authorizable;
+import org.apache.jackrabbit.api.security.user.Group;
+import org.apache.jackrabbit.api.security.user.User;
+import org.apache.jackrabbit.oak.api.Root;
+import org.apache.jackrabbit.oak.namepath.NamePathMapper;
+import org.apache.jackrabbit.oak.spi.security.ConfigurationParameters;
+import org.apache.jackrabbit.oak.spi.security.SecurityProvider;
+import org.apache.jackrabbit.oak.spi.security.user.UserConfiguration;
+import org.apache.jackrabbit.oak.spi.security.user.UserConstants;
+import org.apache.jackrabbit.oak.spi.security.user.action.AbstractGroupAction;
+import org.apache.jackrabbit.oak.spi.security.user.action.AuthorizableAction;
+import
org.apache.jackrabbit.oak.spi.security.user.action.AuthorizableActionProvider;
+import org.apache.jackrabbit.oak.spi.xml.ImportBehavior;
+import org.apache.jackrabbit.oak.spi.xml.ProtectedItemImporter;
+import org.junit.Test;
+
+import javax.annotation.Nonnull;
+import javax.jcr.RepositoryException;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.UUID;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+/**
+ * Testing {@link ImportBehavior#IGNORE} for group import
+ */
+public class GroupImportWithActionsBestEffortTest extends AbstractImportTest {
+
+ private final TestGroupAction groupAction = new TestGroupAction();
+ private final TestActionProvider actionProvider = new TestActionProvider();
+
+ @Override
+ public void before() throws Exception {
+ super.before();
+ actionProvider.addAction(groupAction);
+ }
+
+ @Test
+ public void testImportMembersBestEffort() throws Exception {
+
+
+ User user1 = getUserManager().createUser("user1", "");
+ String uuid1 = getImportSession().getNode(user1.getPath()).getUUID();
+ User user2 = getUserManager().createUser("user2", "");
+ String uuid2 = getImportSession().getNode(user2.getPath()).getUUID();
+ String nonExistingUUID = UUID.randomUUID().toString();
+ String failedUUID = uuid1;
+
+ String xml =
+ "<?xml version=\"1.0\" encoding=\"UTF-8\"?>" +
+ "<sv:node sv:name=\"gFolder\"
xmlns:mix=\"http://www.jcp.org/jcr/mix/1.0\"
xmlns:nt=\"http://www.jcp.org/jcr/nt/1.0\"
xmlns:fn_old=\"http://www.w3.org/2004/10/xpath-functions\"
xmlns:fn=\"http://www.w3.org/2005/xpath-functions\"
xmlns:xs=\"http://www.w3.org/2001/XMLSchema\"
xmlns:sv=\"http://www.jcp.org/jcr/sv/1.0\" xmlns:rep=\"internal\"
xmlns:jcr=\"http://www.jcp.org/jcr/1.0\">" +
+ " <sv:property sv:name=\"jcr:primaryType\"
sv:type=\"Name\">" +
+ " <sv:value>rep:AuthorizableFolder</sv:value>" +
+ " </sv:property>" +
+ " <sv:node sv:name=\"g1\">" +
+ " <sv:property sv:name=\"jcr:primaryType\"
sv:type=\"Name\"><sv:value>rep:Group</sv:value></sv:property>" +
+ " <sv:property sv:name=\"jcr:uuid\"
sv:type=\"String\"><sv:value>0120a4f9-196a-3f9e-b9f5-23f31f914da7</sv:value></sv:property>"
+
+ " <sv:property sv:name=\"rep:principalName\"
sv:type=\"String\"><sv:value>g1</sv:value></sv:property>" + " <sv:property
sv:name=\"rep:members\" sv:multiple=\"true\" sv:type=\"WeakReference\">" +
+ " <sv:value>" + uuid1 + "</sv:value>" +
+ " <sv:value>" + uuid2 + "</sv:value>" +
+ " <sv:value>" + nonExistingUUID +
"</sv:value>" +
+ " <sv:value>" + failedUUID + "</sv:value>" +
+ " </sv:property>" +
+ " </sv:node>" +
+ "</sv:node>";
+
+ doImport(getTargetPath(), xml);
+
+ Group g1 = (Group) getUserManager().getAuthorizable("g1");
+ assertTrue(groupAction.onMemberAddedCalled);
+ assertTrue(groupAction.onMembersAddedContentIdCalled);
+ assertEquals(g1.getID(), groupAction.group.getID());
+ assertTrue(Iterables.elementsEqual(ImmutableList.of(user1.getID(),
user2.getID()), groupAction.memberIds));
+ assertTrue(Iterables.elementsEqual(ImmutableList.of(nonExistingUUID),
groupAction.memberContentIds));
+ assertFalse(groupAction.failedIds.iterator().hasNext()); // duplicate
uuids are swallowed by the set in userImporter: nonExisting#add
+ }
+
+ @Override
+ protected String getImportBehavior() {
+ return ImportBehavior.NAME_BESTEFFORT;
+ }
+
+ @Override
+ protected String getTargetPath() {
+ return GROUPPATH;
+ }
+
+ @Override
+ protected ConfigurationParameters getConfigurationParameters() {
+ Map<String, Object> userParams = new HashMap<String, Object>();
+ userParams.put(ProtectedItemImporter.PARAM_IMPORT_BEHAVIOR,
getImportBehavior());
+ userParams.put(UserConstants.PARAM_AUTHORIZABLE_ACTION_PROVIDER,
actionProvider);
+ return ConfigurationParameters.of(UserConfiguration.NAME,
ConfigurationParameters.of(userParams));
+ }
+
+ private class TestGroupAction extends AbstractGroupAction {
+
+ boolean onMemberAddedCalled = false;
+ boolean onMembersAddedContentIdCalled = false;
+
+ Group group;
+ List<String> memberIds = Lists.newArrayList();
+ Iterable<String> memberContentIds = Lists.newArrayList();
+ Iterable<String> failedIds;
+
+ @Override
+ public void onMemberAdded(@Nonnull Group group, @Nonnull Authorizable
member, @Nonnull Root root, @Nonnull NamePathMapper namePathMapper) throws
RepositoryException {
+ this.group = group;
+ memberIds.add(member.getID());
+ onMemberAddedCalled = true;
+ }
+
+ @Override
+ public void onMembersAddedContentId(@Nonnull Group group, @Nonnull
Iterable<String> memberContentIds, @Nonnull Iterable<String> failedIds,
@Nonnull Root root, @Nonnull NamePathMapper namePathMapper) throws
RepositoryException {
+ this.group = group;
+ this.memberContentIds = memberContentIds;
+ this.failedIds = failedIds;
+ onMembersAddedContentIdCalled = true;
+ }
+ }
+
+ private final class TestActionProvider implements
AuthorizableActionProvider {
+
+ private final List<AuthorizableAction> actions = Lists.newArrayList();
+
+ private void addAction(AuthorizableAction action) {
+ actions.add(action);
+ }
+
+ @Nonnull
+ @Override
+ public List<? extends AuthorizableAction>
getAuthorizableActions(@Nonnull SecurityProvider securityProvider) {
+ return actions;
+ }
+ }
+}
Propchange:
jackrabbit/oak/trunk/oak-jcr/src/test/java/org/apache/jackrabbit/oak/jcr/security/user/GroupImportWithActionsBestEffortTest.java
------------------------------------------------------------------------------
svn:eol-style = native
Propchange:
jackrabbit/oak/trunk/oak-jcr/src/test/java/org/apache/jackrabbit/oak/jcr/security/user/GroupImportWithActionsBestEffortTest.java
------------------------------------------------------------------------------
svn:executable = *
Copied:
jackrabbit/oak/trunk/oak-jcr/src/test/java/org/apache/jackrabbit/oak/jcr/security/user/GroupImportWithActionsTest.java
(from r1735040,
jackrabbit/oak/trunk/oak-jcr/src/test/java/org/apache/jackrabbit/oak/jcr/security/user/GroupImportIgnoreTest.java)
URL:
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-jcr/src/test/java/org/apache/jackrabbit/oak/jcr/security/user/GroupImportWithActionsTest.java?p2=jackrabbit/oak/trunk/oak-jcr/src/test/java/org/apache/jackrabbit/oak/jcr/security/user/GroupImportWithActionsTest.java&p1=jackrabbit/oak/trunk/oak-jcr/src/test/java/org/apache/jackrabbit/oak/jcr/security/user/GroupImportIgnoreTest.java&r1=1735040&r2=1735564&rev=1735564&view=diff
==============================================================================
---
jackrabbit/oak/trunk/oak-jcr/src/test/java/org/apache/jackrabbit/oak/jcr/security/user/GroupImportIgnoreTest.java
(original)
+++
jackrabbit/oak/trunk/oak-jcr/src/test/java/org/apache/jackrabbit/oak/jcr/security/user/GroupImportWithActionsTest.java
Fri Mar 18 10:00:35 2016
@@ -16,21 +16,86 @@
*/
package org.apache.jackrabbit.oak.jcr.security.user;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.UUID;
-
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Iterables;
+import com.google.common.collect.Lists;
import org.apache.jackrabbit.api.security.user.Authorizable;
import org.apache.jackrabbit.api.security.user.Group;
+import org.apache.jackrabbit.api.security.user.User;
+import org.apache.jackrabbit.oak.api.Root;
+import org.apache.jackrabbit.oak.namepath.NamePathMapper;
+import org.apache.jackrabbit.oak.spi.security.ConfigurationParameters;
+import org.apache.jackrabbit.oak.spi.security.SecurityProvider;
+import org.apache.jackrabbit.oak.spi.security.user.UserConfiguration;
+import org.apache.jackrabbit.oak.spi.security.user.UserConstants;
+import org.apache.jackrabbit.oak.spi.security.user.action.AbstractGroupAction;
+import org.apache.jackrabbit.oak.spi.security.user.action.AuthorizableAction;
+import
org.apache.jackrabbit.oak.spi.security.user.action.AuthorizableActionProvider;
import org.apache.jackrabbit.oak.spi.xml.ImportBehavior;
+import org.apache.jackrabbit.oak.spi.xml.ProtectedItemImporter;
import org.junit.Test;
-import static org.junit.Assert.fail;
+import javax.annotation.Nonnull;
+import javax.jcr.RepositoryException;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.UUID;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
/**
- * Testing {@link org.apache.jackrabbit.oak.spi.xml.ImportBehavior#IGNORE} for
group import
+ * Testing {@link ImportBehavior#IGNORE} for group import
*/
-public class GroupImportIgnoreTest extends AbstractImportTest {
+public class GroupImportWithActionsTest extends AbstractImportTest {
+
+ private final TestGroupAction groupAction = new TestGroupAction();
+ private final TestActionProvider actionProvider = new TestActionProvider();
+
+ @Override
+ public void before() throws Exception {
+ super.before();
+ actionProvider.addAction(groupAction);
+ }
+
+ @Test
+ public void testImportMembersIgnore() throws Exception {
+
+ User user1 = getUserManager().createUser("user1", "");
+ String uuid1 = getImportSession().getNode(user1.getPath()).getUUID();
+ User user2 = getUserManager().createUser("user2", "");
+ String uuid2 = getImportSession().getNode(user2.getPath()).getUUID();
+ String nonExistingUUID = UUID.randomUUID().toString();
+ String failedUUID = uuid1;
+
+ String xml =
+ "<?xml version=\"1.0\" encoding=\"UTF-8\"?>" +
+ "<sv:node sv:name=\"gFolder\"
xmlns:mix=\"http://www.jcp.org/jcr/mix/1.0\"
xmlns:nt=\"http://www.jcp.org/jcr/nt/1.0\"
xmlns:fn_old=\"http://www.w3.org/2004/10/xpath-functions\"
xmlns:fn=\"http://www.w3.org/2005/xpath-functions\"
xmlns:xs=\"http://www.w3.org/2001/XMLSchema\"
xmlns:sv=\"http://www.jcp.org/jcr/sv/1.0\" xmlns:rep=\"internal\"
xmlns:jcr=\"http://www.jcp.org/jcr/1.0\">" +
+ " <sv:property sv:name=\"jcr:primaryType\"
sv:type=\"Name\">" +
+ " <sv:value>rep:AuthorizableFolder</sv:value>" +
+ " </sv:property>" +
+ " <sv:node sv:name=\"g1\">" +
+ " <sv:property sv:name=\"jcr:primaryType\"
sv:type=\"Name\"><sv:value>rep:Group</sv:value></sv:property>" +
+ " <sv:property sv:name=\"jcr:uuid\"
sv:type=\"String\"><sv:value>0120a4f9-196a-3f9e-b9f5-23f31f914da7</sv:value></sv:property>"
+
+ " <sv:property sv:name=\"rep:principalName\"
sv:type=\"String\"><sv:value>g1</sv:value></sv:property>" + " <sv:property
sv:name=\"rep:members\" sv:multiple=\"true\" sv:type=\"WeakReference\">" +
+ " <sv:value>" + uuid1 + "</sv:value>" +
+ " <sv:value>" + uuid2 + "</sv:value>" +
+ " <sv:value>" + nonExistingUUID +
"</sv:value>" +
+ " <sv:value>" + failedUUID + "</sv:value>" +
+ " </sv:property>" +
+ " </sv:node>" +
+ "</sv:node>";
+
+ doImport(getTargetPath(), xml);
+
+ Group g1 = (Group) getUserManager().getAuthorizable("g1");
+ assertTrue(groupAction.onMemberAddedCalled);
+ assertEquals(g1.getID(), groupAction.group.getID());
+ assertTrue(Iterables.elementsEqual(ImmutableList.of(user1.getID(),
user2.getID()), groupAction.memberIds));
+ assertFalse(groupAction.onMembersAddedContentIdCalled);
+ }
@Override
protected String getImportBehavior() {
@@ -42,57 +107,47 @@ public class GroupImportIgnoreTest exten
return GROUPPATH;
}
- @Test
- public void testImportSelfAsGroupIgnore() throws Exception {
- String invalidId = "0120a4f9-196a-3f9e-b9f5-23f31f914da7"; // uuid of
the group itself
- String xml = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>" +
- "<sv:node sv:name=\"gFolder\"
xmlns:mix=\"http://www.jcp.org/jcr/mix/1.0\"
xmlns:nt=\"http://www.jcp.org/jcr/nt/1.0\"
xmlns:fn_old=\"http://www.w3.org/2004/10/xpath-functions\"
xmlns:fn=\"http://www.w3.org/2005/xpath-functions\"
xmlns:xs=\"http://www.w3.org/2001/XMLSchema\"
xmlns:sv=\"http://www.jcp.org/jcr/sv/1.0\" xmlns:rep=\"internal\"
xmlns:jcr=\"http://www.jcp.org/jcr/1.0\">" +
- " <sv:property sv:name=\"jcr:primaryType\"
sv:type=\"Name\"><sv:value>rep:AuthorizableFolder</sv:value></sv:property>" +
- "<sv:node sv:name=\"g1\"><sv:property
sv:name=\"jcr:primaryType\"
sv:type=\"Name\"><sv:value>rep:Group</sv:value></sv:property>" +
- " <sv:property sv:name=\"jcr:uuid\"
sv:type=\"String\"><sv:value>"+invalidId+"</sv:value></sv:property>" +
- " <sv:property sv:name=\"rep:principalName\"
sv:type=\"String\"><sv:value>g1</sv:value></sv:property>" +
- " <sv:property sv:name=\"rep:members\"
sv:type=\"WeakReference\"><sv:value>" +invalidId+ "</sv:value></sv:property>" +
- "</sv:node>" +
- "</sv:node>";
- doImport(getTargetPath(), xml);
- // no exception during import -> member must have been ignored though.
- Authorizable a = getUserManager().getAuthorizable("g1");
- if (a.isGroup()) {
- assertNotDeclaredMember((Group) a, invalidId, getImportSession());
- } else {
- fail("'g1' was not imported as Group.");
+ @Override
+ protected ConfigurationParameters getConfigurationParameters() {
+ Map<String, Object> userParams = new HashMap<String, Object>();
+ userParams.put(ProtectedItemImporter.PARAM_IMPORT_BEHAVIOR,
getImportBehavior());
+ userParams.put(UserConstants.PARAM_AUTHORIZABLE_ACTION_PROVIDER,
actionProvider);
+ return ConfigurationParameters.of(UserConfiguration.NAME,
ConfigurationParameters.of(userParams));
+ }
+
+ private class TestGroupAction extends AbstractGroupAction {
+
+ private boolean onMemberAddedCalled = false;
+ private boolean onMembersAddedContentIdCalled = false;
+
+ Group group;
+ List<String> memberIds = Lists.newArrayList();
+
+ @Override
+ public void onMemberAdded(@Nonnull Group group, @Nonnull Authorizable
member, @Nonnull Root root, @Nonnull NamePathMapper namePathMapper) throws
RepositoryException {
+ this.group = group;
+ memberIds.add(member.getID());
+ onMemberAddedCalled = true;
+ }
+
+ @Override
+ public void onMembersAddedContentId(Group group, Iterable<String>
memberContentIds, Iterable<String> failedIds, Root root, NamePathMapper
namePathMapper) throws RepositoryException {
+ onMembersAddedContentIdCalled = true;
}
}
- @Test
- public void testImportNonExistingMemberIgnore() throws Exception {
- List<String> invalid = new ArrayList<String>();
- invalid.add(UUID.randomUUID().toString()); // random uuid
- invalid.add(getExistingUUID()); // uuid of non-authorizable node
-
- for (String id : invalid) {
- String xml = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>" +
- "<sv:node sv:name=\"gFolder\"
xmlns:mix=\"http://www.jcp.org/jcr/mix/1.0\"
xmlns:nt=\"http://www.jcp.org/jcr/nt/1.0\"
xmlns:fn_old=\"http://www.w3.org/2004/10/xpath-functions\"
xmlns:fn=\"http://www.w3.org/2005/xpath-functions\"
xmlns:xs=\"http://www.w3.org/2001/XMLSchema\"
xmlns:sv=\"http://www.jcp.org/jcr/sv/1.0\" xmlns:rep=\"internal\"
xmlns:jcr=\"http://www.jcp.org/jcr/1.0\">" +
- " <sv:property sv:name=\"jcr:primaryType\"
sv:type=\"Name\"><sv:value>rep:AuthorizableFolder</sv:value></sv:property>" +
- "<sv:node sv:name=\"g1\"><sv:property
sv:name=\"jcr:primaryType\"
sv:type=\"Name\"><sv:value>rep:Group</sv:value></sv:property>" +
- " <sv:property sv:name=\"jcr:uuid\"
sv:type=\"String\"><sv:value>0120a4f9-196a-3f9e-b9f5-23f31f914da7</sv:value></sv:property>"
+
- " <sv:property sv:name=\"rep:principalName\"
sv:type=\"String\"><sv:value>g1</sv:value></sv:property>" +
- " <sv:property sv:name=\"rep:members\"
sv:type=\"WeakReference\"><sv:value>" +id+ "</sv:value></sv:property>" +
- "</sv:node>" +
- "</sv:node>";
- try {
- // there should be no exception during import,
- // but invalid members must be ignored.
- doImport(getTargetPath(), xml);
- Authorizable a = getUserManager().getAuthorizable("g1");
- if (a.isGroup()) {
- assertNotDeclaredMember((Group) a, id, getImportSession());
- } else {
- fail("'g1' was not imported as Group.");
- }
- } finally {
- getImportSession().refresh(false);
- }
+ private final class TestActionProvider implements
AuthorizableActionProvider {
+
+ private final List<AuthorizableAction> actions = Lists.newArrayList();
+
+ private void addAction(AuthorizableAction action) {
+ actions.add(action);
+ }
+
+ @Nonnull
+ @Override
+ public List<? extends AuthorizableAction>
getAuthorizableActions(@Nonnull SecurityProvider securityProvider) {
+ return actions;
}
}
}