package org.molgenis.security.permission;
import com.google.common.base.Function;
import com.google.common.collect.Lists;
import org.molgenis.auth.*;
import org.molgenis.data.DataService;
import org.molgenis.data.support.QueryImpl;
import org.molgenis.framework.ui.MolgenisPlugin;
import org.molgenis.framework.ui.MolgenisPluginRegistry;
import org.molgenis.security.core.utils.SecurityUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.authority.mapping.GrantedAuthoritiesMapper;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.*;
import java.util.stream.Stream;
import static java.util.stream.Collectors.toList;
import static org.molgenis.auth.GroupAuthorityMetaData.GROUP_AUTHORITY;
import static org.molgenis.auth.GroupMemberMetaData.GROUP_MEMBER;
import static org.molgenis.auth.GroupMetaData.GROUP;
import static org.molgenis.auth.UserMetaData.USER;
import static org.molgenis.auth.UserAuthorityMetaData.USER_AUTHORITY;
@Service
public class PermissionManagerServiceImpl implements PermissionManagerService
{
private final DataService dataService;
private final MolgenisPluginRegistry molgenisPluginRegistry;
private final GrantedAuthoritiesMapper grantedAuthoritiesMapper;
@Autowired
public PermissionManagerServiceImpl(DataService dataService, MolgenisPluginRegistry molgenisPluginRegistry,
GrantedAuthoritiesMapper grantedAuthoritiesMapper)
{
if (dataService == null) throw new IllegalArgumentException("DataService is null");
if (molgenisPluginRegistry == null) throw new IllegalArgumentException("Molgenis plugin registry is null");
if (grantedAuthoritiesMapper == null) throw new IllegalArgumentException("Granted authorities mapper is null");
this.dataService = dataService;
this.molgenisPluginRegistry = molgenisPluginRegistry;
this.grantedAuthoritiesMapper = grantedAuthoritiesMapper;
}
@Override
@PreAuthorize("hasAnyRole('ROLE_SU')")
@Transactional(readOnly = true)
public List<User> getUsers()
{
return dataService.findAll(USER, User.class).collect(toList());
}
@Override
@PreAuthorize("hasAnyRole('ROLE_SU')")
@Transactional(readOnly = true)
public List<Group> getGroups()
{
return dataService.findAll(GROUP, Group.class).collect(toList());
}
@Override
@PreAuthorize("hasAnyRole('ROLE_SU')")
public List<MolgenisPlugin> getPlugins()
{
return Lists.newArrayList(molgenisPluginRegistry);
}
@Override
@PreAuthorize("hasAnyRole('ROLE_SU')")
public List<String> getEntityClassIds()
{
return dataService.getEntityNames().collect(toList());
}
@Override
@PreAuthorize("hasAnyRole('ROLE_SU')")
@Transactional(readOnly = true)
public Permissions getGroupPluginPermissions(String groupId)
{
Group group = dataService.findOneById(GROUP, groupId, Group.class);
if (group == null) throw new RuntimeException("unknown group id [" + groupId + "]");
List<Authority> groupPermissions = getGroupPermissions(group);
Permissions permissions = createPermissions(groupPermissions, SecurityUtils.AUTHORITY_PLUGIN_PREFIX);
permissions.setGroupId(groupId);
return permissions;
}
@Override
@PreAuthorize("hasAnyRole('ROLE_SU')")
@Transactional(readOnly = true)
public Permissions getGroupEntityClassPermissions(String groupId)
{
Group group = dataService.findOneById(GROUP, groupId, Group.class);
if (group == null) throw new RuntimeException("unknown group id [" + groupId + "]");
List<Authority> groupPermissions = getGroupPermissions(group);
Permissions permissions = createPermissions(groupPermissions, SecurityUtils.AUTHORITY_ENTITY_PREFIX);
permissions.setGroupId(groupId);
return permissions;
}
@Override
@PreAuthorize("hasAnyRole('ROLE_SU')")
@Transactional(readOnly = true)
public Permissions getUserPluginPermissions(String userId)
{
List<? extends Authority> userPermissions = getUserPermissions(userId);
Permissions permissions = createPermissions(userPermissions, SecurityUtils.AUTHORITY_PLUGIN_PREFIX);
permissions.setUserId(userId);
return permissions;
}
@Override
@PreAuthorize("hasAnyRole('ROLE_SU')")
@Transactional(readOnly = true)
public Permissions getUserEntityClassPermissions(String userId)
{
List<? extends Authority> userPermissions = getUserPermissions(userId);
Permissions permissions = createPermissions(userPermissions, SecurityUtils.AUTHORITY_ENTITY_PREFIX);
permissions.setUserId(userId);
return permissions;
}
private List<? extends Authority> getUserPermissions(String userId)
{
return getUserPermissions(userId, null);
}
private List<? extends Authority> getUserPermissions(String userId, String authorityPrefix)
{
User user = dataService.findOneById(USER, userId, User.class);
if (user == null) throw new RuntimeException("unknown user id [" + userId + "]");
List<Authority> userPermissions = getUserPermissions(user, authorityPrefix);
List<GroupMember> groupMembers = dataService.findAll(GROUP_MEMBER,
new QueryImpl<GroupMember>().eq(GroupMemberMetaData.USER, user),
GroupMember.class).collect(toList());
if (!groupMembers.isEmpty())
{
List<Group> groups = Lists
.transform(groupMembers, new Function<GroupMember, Group>()
{
@Override
public Group apply(GroupMember molgenisGroupMember)
{
return molgenisGroupMember.getGroup();
}
});
List<Authority> groupAuthorities = getGroupPermissions(groups, authorityPrefix);
if (groupAuthorities != null && !groupAuthorities.isEmpty()) userPermissions.addAll(groupAuthorities);
}
return userPermissions;
}
@Override
@PreAuthorize("hasAnyRole('ROLE_SU')")
@Transactional
public void replaceGroupPluginPermissions(List<GroupAuthority> pluginAuthorities, String groupId)
{
replaceGroupPermissions(pluginAuthorities, groupId, SecurityUtils.AUTHORITY_PLUGIN_PREFIX);
}
@Override
@PreAuthorize("hasAnyRole('ROLE_SU')")
@Transactional
public void replaceGroupEntityClassPermissions(List<GroupAuthority> entityAuthorities, String groupId)
{
replaceGroupPermissions(entityAuthorities, groupId, SecurityUtils.AUTHORITY_ENTITY_PREFIX);
}
private void replaceGroupPermissions(List<GroupAuthority> entityAuthorities, String groupId, String authorityPrefix)
{
Group group = dataService.findOneById(GROUP, groupId, Group.class);
if (group == null) throw new RuntimeException("unknown group id [" + groupId + "]");
// inject user
for (GroupAuthority entityAuthority : entityAuthorities)
entityAuthority.setGroup(group);
// delete old plugin authorities
Stream<Authority> oldEntityAuthorities = getGroupPermissions(group, authorityPrefix).stream();
if (oldEntityAuthorities != null) dataService.delete(GROUP_AUTHORITY, oldEntityAuthorities);
// insert new plugin authorities
if (!entityAuthorities.isEmpty()) dataService.add(GROUP_AUTHORITY, entityAuthorities.stream());
}
@Override
@PreAuthorize("hasAnyRole('ROLE_SU')")
@Transactional
public void replaceUserPluginPermissions(List<UserAuthority> pluginAuthorities, String userId)
{
replaceUserPermissions(pluginAuthorities, userId, SecurityUtils.AUTHORITY_PLUGIN_PREFIX);
}
@Override
@PreAuthorize("hasAnyRole('ROLE_SU')")
@Transactional
public void replaceUserEntityClassPermissions(List<UserAuthority> pluginAuthorities, String userId)
{
replaceUserPermissions(pluginAuthorities, userId, SecurityUtils.AUTHORITY_ENTITY_PREFIX);
}
private void replaceUserPermissions(List<UserAuthority> entityAuthorities, String userId, String authorityType)
{
User user = dataService.findOneById(USER, userId, User.class);
if (user == null) throw new RuntimeException("unknown user id [" + userId + "]");
// inject user
for (UserAuthority entityAuthority : entityAuthorities)
entityAuthority.setUser(user);
// delete old plugin authorities
List<? extends Authority> oldEntityAuthorities = getUserPermissions(user, authorityType);
if (oldEntityAuthorities != null && !oldEntityAuthorities.isEmpty())
dataService.delete(USER_AUTHORITY, oldEntityAuthorities.stream());
// insert new plugin authorities
if (!entityAuthorities.isEmpty()) dataService.add(USER_AUTHORITY, entityAuthorities.stream());
}
private List<Authority> getUserPermissions(User user, final String authorityPrefix)
{
Stream<UserAuthority> authorities = dataService.findAll(USER_AUTHORITY,
new QueryImpl<UserAuthority>().eq(UserAuthorityMetaData.USER, user),
UserAuthority.class);
return authorities.filter(authority ->
{
return authorityPrefix != null ? authority.getRole().startsWith(authorityPrefix) : true;
}).collect(toList());
}
private List<Authority> getGroupPermissions(Group group)
{
return getGroupPermissions(Arrays.asList(group));
}
private List<Authority> getGroupPermissions(Group group, String authorityPrefix)
{
return getGroupPermissions(Arrays.asList(group), authorityPrefix);
}
private List<Authority> getGroupPermissions(List<Group> groups)
{
return getGroupPermissions(groups, null);
}
private List<Authority> getGroupPermissions(List<Group> groups, final String authorityPrefix)
{
Stream<GroupAuthority> authorities = dataService.findAll(GROUP_AUTHORITY,
new QueryImpl<GroupAuthority>().in(GroupAuthorityMetaData.GROUP, groups),
GroupAuthority.class);
return authorities.filter(authority ->
{
return authorityPrefix != null ? authority.getRole().startsWith(authorityPrefix) : true;
}).collect(toList());
}
private Permissions createPermissions(List<? extends Authority> entityAuthorities, String authorityPrefix)
{
Permissions permissions = new Permissions();
if (authorityPrefix.equals(SecurityUtils.AUTHORITY_PLUGIN_PREFIX))
{
List<MolgenisPlugin> plugins = this.getPlugins();
if (plugins != null)
{
Collections.sort(plugins, new Comparator<MolgenisPlugin>()
{
@Override
public int compare(MolgenisPlugin o1, MolgenisPlugin o2)
{
return o1.getName().compareTo(o2.getName());
}
});
Map<String, String> pluginMap = new LinkedHashMap<String, String>();
for (MolgenisPlugin plugin : plugins)
pluginMap.put(plugin.getId(), plugin.getName());
permissions.setEntityIds(pluginMap);
}
}
else if (authorityPrefix.equals(SecurityUtils.AUTHORITY_ENTITY_PREFIX))
{
List<String> entityClassIds = this.getEntityClassIds();
if (entityClassIds != null)
{
Map<String, String> entityClassMap = new TreeMap<String, String>();
for (String entityClassId : entityClassIds)
entityClassMap.put(entityClassId, entityClassId);
permissions.setEntityIds(entityClassMap);
}
}
else throw new RuntimeException("Invalid authority prefix [" + authorityPrefix + "]");
for (Authority authority : entityAuthorities)
{
// add permissions for authorities that match prefix
if (authority.getRole().startsWith(authorityPrefix))
{
Permission permission = new Permission();
String authorityType = getAuthorityType(authority.getRole(), authorityPrefix);
String authorityPluginId = getAuthorityEntityId(authority.getRole(), authorityPrefix);
permission.setType(authorityType);
if (authority instanceof GroupAuthority)
{
permission.setGroup(((GroupAuthority) authority).getGroup().getName());
permissions.addGroupPermission(authorityPluginId, permission);
}
else
{
permissions.addUserPermission(authorityPluginId, permission);
}
}
// add permissions for inherited authorities from authority that match prefix
SimpleGrantedAuthority grantedAuthority = new SimpleGrantedAuthority(authority.getRole());
Collection<? extends GrantedAuthority> hierarchyAuthorities = grantedAuthoritiesMapper
.mapAuthorities(Collections.singletonList(grantedAuthority));
hierarchyAuthorities.remove(grantedAuthority);
for (GrantedAuthority hierarchyAuthority : hierarchyAuthorities)
{
if (hierarchyAuthority.getAuthority().startsWith(authorityPrefix))
{
String authorityPluginId = getAuthorityEntityId(hierarchyAuthority.getAuthority(), authorityPrefix);
Permission hierarchyPermission = new Permission();
hierarchyPermission.setType(getAuthorityType(hierarchyAuthority.getAuthority(), authorityPrefix));
permissions.addHierarchyPermission(authorityPluginId, hierarchyPermission);
}
}
}
permissions.sort();
return permissions;
}
private String getAuthorityEntityId(String role, String authorityPrefix)
{
role = role.substring(authorityPrefix.length());
return role.substring(role.indexOf('_') + 1).toLowerCase();
}
private String getAuthorityType(String role, String authorityPrefix)
{
role = role.substring(authorityPrefix.length());
return role.substring(0, role.indexOf('_')).toLowerCase();
}
}