Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Make group roles include each other #7619

Merged
merged 6 commits into from Aug 1, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
Binary file added docs/images/security/role_model.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
34 changes: 34 additions & 0 deletions docs/user_documentation/admin-features/guide-ref-security.md
Expand Up @@ -108,3 +108,37 @@ You can now view the members of the group and see that the "test" member has a M
You can also remove a member from a group. Click on a member ("test" in this example) and click on "Remove from group".

![Remove member from group](../../images/security/remove_member_from_group.png?raw=true, "Remove member from group")

## Roles and inclusion
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nice piece of documentation!

MOLGENIS comes with three default roles out of the box: Manager, Editor and Viewer. When you add a Group, the three group roles
that are added actually include (or inherit) these default roles. That's how you get all those permissions on plugins
listed above automatically.

The default roles include each other as well. The Editor can see and do everything a Viewer can, and some more. The Manager
can see and do everything the Editor can, and some more. System admins can change the permissions of these roles, so that
certain plugins and functionality can be turned on or off across the system for all groups. Some examples:
1. As the admin, you don't want any user to use the Search All plugin. You achieve this by removing the Search All plugin permission
from the `VIEWER` role.
2. As the admin, you only want Managers and Editors to use the Navigator. You achieve this by removing the Navigator plugin permission
from the `VIEWER` role, and giving the `EDITOR` role `VIEW` permission to the Navigator plugin.

*For more information on how to give permissions to roles, read up on the [Permission Manager](guide-ref-permission-manager.md)*

The group roles include each other as well, in the same manner as the default roles. Your group's Manager can see and do
everything your group's Editor can, etc. See the following schematic for an overview of how all the roles connect for a group
that has been aptly named 'test':

![Role inclusions](../../images/security/role_model.png?raw=true, "Overview of the roles and how they work together")

The group roles do not contain any permissions when you first create them; they only include the default roles. However,
just like with any other role, you can still add permissions to them. This setup gives you fine grained control, especially
when your MOLGENIS hosts multiple groups. Some examples:

1. You want all the users in your group to see a special plugin that's not relevant for other groups. You achieve this by
giving the `TEST_VIEWER` role `VIEW` permission on that plugin.
2. You don't want all Editors in the system to see the Job Overview plugin. You achieve this by removing that plugin permission
from the default Editor role and adding it to your group's Editor role. Now both the Editors and Managers in your group can use it,
but from now on any new group that's added won't have these permission by default.

Even though you'll find that the default settings are sufficient most of the time, you have complete control over all the roles and
permissions in the system if you ever wish to change something.
Expand Up @@ -15,20 +15,19 @@
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Set;
import java.util.*;
import java.util.stream.Collectors;

import static com.google.common.collect.Lists.newArrayList;
import static java.util.Collections.singletonList;
import static com.google.common.collect.Streams.stream;
import static java.util.Objects.requireNonNull;
import static java.util.stream.Collectors.toList;
import static java.util.function.Function.identity;
import static java.util.stream.Collectors.toMap;
import static org.molgenis.data.meta.model.PackageMetadata.PACKAGE;
import static org.molgenis.data.security.auth.GroupMetadata.GROUP;
import static org.molgenis.data.security.auth.RoleMetadata.NAME;
import static org.molgenis.data.security.auth.RoleMetadata.ROLE;
import static org.molgenis.security.core.GroupValueFactory.createRoleName;
import static org.molgenis.security.core.SidUtils.createRoleAuthority;

@Service
Expand Down Expand Up @@ -78,20 +77,22 @@ public void persist(GroupValue groupValue)
{
Package rootPackage = packageFactory.create(groupValue.getRootPackage());

List<Role> roles = groupValue.getRoles()
.stream()
.map(roleFactory::create)
.map(this::addIncludedRole).collect(toList());
Map<String, Role> roles = groupValue.getRoles()
.stream()
.map(roleFactory::create)
.collect(toMap(Role::getName, identity()));

roles.values().forEach(role -> addIncludedRoles(role, roles, groupValue.getName()));

Group group = groupFactory.create(groupValue);
group.setRootPackage(rootPackage.getId());
group.setRoles(roles);
group.setRoles(roles.values());

dataService.add(PACKAGE, rootPackage);
dataService.add(GROUP, group);
roles.forEach(role -> role.setGroup(group));
roles.values().forEach(role -> role.setGroup(group));

dataService.add(ROLE, roles.stream());
dataService.add(ROLE, roles.values().stream());
}

@RunAsSystem
Expand Down Expand Up @@ -136,8 +137,8 @@ public Group getGroup(String groupName)
* The user can only have a single role within the group
*
* @param group group to add the user to in the given role
* @param user user to be added in the given role to the given group
* @param role role in which the given user is to be added to given group
* @param user user to be added in the given role to the given group
* @param role role in which the given user is to be added to given group
*/
@RunAsSystem
public void addMember(final Group group, final User user, final Role role)
Expand All @@ -146,14 +147,14 @@ public void addMember(final Group group, final User user, final Role role)
Collection<RoleMembership> memberships = roleMembershipService.getMemberships(groupRoles);
boolean isGroupRole = groupRoles.stream().anyMatch(gr -> gr.getName().equals(role.getName()));

if(!isGroupRole)
if (!isGroupRole)
{
throw new NotAValidGroupRoleException(role, group);
}

boolean isMember = memberships.stream().parallel().anyMatch(m -> m.getUser().equals(user));

if(isMember)
if (isMember)
{
throw new IsAlreadyMemberException(user, group);
}
Expand Down Expand Up @@ -205,13 +206,21 @@ private RoleMembership findRoleMembership(User member, List<Role> groupRoles)
Collection<RoleMembership> memberships = roleMembershipService.getMemberships(groupRoles);
return memberships.stream()
.filter(m -> m.getUser().getId().equals(member.getId()))
.findFirst().orElseThrow(() -> unknownMembershipForUser(member));
.findFirst()
.orElseThrow(() -> unknownMembershipForUser(member));
}

private Role addIncludedRole(Role role)
private void addIncludedRoles(Role role, Map<String, Role> groupRoles, String groupName)
{
role.setIncludes(singletonList(findRoleNamed(role.getLabel().toUpperCase())));
return role;
List<Role> toInclude = newArrayList();
Role defaultRole = findRoleNamed(role.getLabel().toUpperCase());
toInclude.add(defaultRole);

stream(defaultRole.getIncludes()).map(includedRole -> createRoleName(groupName, includedRole.getLabel()))
.map(groupRoles::get)
.forEach(toInclude::add);

role.setIncludes(toInclude);
}

private Role findRoleNamed(String rolename)
Expand Down