package org.ohdsi.webapi.shiro;
import java.security.Principal;
import java.util.Arrays;
import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.UnknownAccountException;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.subject.Subject;
import org.ohdsi.webapi.helper.Guard;
import org.ohdsi.webapi.shiro.Entities.PermissionEntity;
import org.ohdsi.webapi.shiro.Entities.PermissionRepository;
import org.ohdsi.webapi.shiro.Entities.PermissionRequest;
import org.ohdsi.webapi.shiro.Entities.RequestStatus;
import org.ohdsi.webapi.shiro.Entities.RoleEntity;
import org.ohdsi.webapi.shiro.Entities.RolePermissionEntity;
import org.ohdsi.webapi.shiro.Entities.RolePermissionRepository;
import org.ohdsi.webapi.shiro.Entities.RoleRepository;
import org.ohdsi.webapi.shiro.Entities.RoleRequest;
import org.ohdsi.webapi.shiro.Entities.UserEntity;
import org.ohdsi.webapi.shiro.Entities.UserRepository;
import org.ohdsi.webapi.shiro.Entities.UserRoleEntity;
import org.ohdsi.webapi.shiro.Entities.UserRoleRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;
/**
*
* @author gennadiy.anisimov
*/
@Component
@Transactional
public class PermissionManager {
@Autowired
private UserRepository userRepository;
@Autowired
private RoleRepository roleRepository;
@Autowired
private PermissionRepository permissionRepository;
@Autowired
private RolePermissionRepository rolePermissionRepository;
@Autowired
private UserRoleRepository userRoleRepository;
public RoleEntity addRole(String roleName) throws Exception {
Guard.checkNotEmpty(roleName);
RoleEntity role = this.roleRepository.findByName(roleName);
if (role != null) {
throw new Exception("Can't create role - it already exists");
}
role = new RoleEntity();
role.setName(roleName);
role = this.roleRepository.save(role);
return role;
}
public String addUserToRole(String roleName, String login) throws Exception {
Guard.checkNotEmpty(roleName);
Guard.checkNotEmpty(login);
RoleEntity role = this.getRoleByName(roleName);
UserEntity user = this.getUserByLogin(login);
UserRoleEntity userRole = this.addUser(user, role, null);
return userRole.getStatus();
}
public void removeUserFromRole(String roleName, String login) throws Exception {
Guard.checkNotEmpty(roleName);
Guard.checkNotEmpty(login);
if (roleName.equalsIgnoreCase(login))
throw new Exception("Can't remove user from personal role");
RoleEntity role = this.getRoleByName(roleName);
UserEntity user = this.getUserByLogin(login);
UserRoleEntity userRole = this.userRoleRepository.findByUserAndRole(user, role);
if (userRole != null)
this.userRoleRepository.delete(userRole);
}
public Iterable<RoleEntity> getRoles(boolean includePersonalRoles) {
Iterable<RoleEntity> roles = this.roleRepository.findAll();
if (includePersonalRoles) {
return roles;
}
Set<String> logins = this.userRepository.getUserLogins();
HashSet<RoleEntity> filteredRoles = new HashSet<>();
for (RoleEntity role : roles) {
if (!logins.contains(role.getName())) {
filteredRoles.add(role);
}
}
return filteredRoles;
}
public AuthorizationInfo getAuthorizationInfo(final String login) {
final SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
final UserEntity userEntity = userRepository.findByLogin(login);
if(userEntity == null) {
throw new UnknownAccountException("Account does not exist");
}
final Set<String> permissionNames = new LinkedHashSet<>();
final Set<PermissionEntity> permissions = this.getUserPermissions(userEntity);
for (PermissionEntity permission : permissions) {
permissionNames.add(permission.getValue());
}
info.setStringPermissions(permissionNames);
return info;
}
@Transactional
public UserEntity registerUser(final String login, final Set<String> defaultRoles) throws Exception {
Guard.checkNotEmpty(login);
UserEntity user = userRepository.findByLogin(login);
if (user != null) {
return user;
}
user = new UserEntity();
user.setLogin(login);
user = userRepository.save(user);
RoleEntity personalRole = this.addRole(login);
this.addUser(user, personalRole, null);
if (defaultRoles != null) {
for (String roleName: defaultRoles) {
RoleEntity defaultRole = this.roleRepository.findByName(roleName);
if (defaultRole != null) {
this.addUser(user, defaultRole, null);
}
}
}
user = userRepository.findOne(user.getId());
return user;
}
@Transactional
public void removeUser(final String login) {
UserEntity user = userRepository.findByLogin(login);
if (user != null) {
this.deleteRole(login); // delete individual role
userRepository.delete(user);
}
}
@Transactional
public String requestPermission(final String role, final String permission, final String description) throws Exception {
final RoleEntity roleEntity = this.getRoleByName(role);
final PermissionEntity permissionEntity = this.addPermission(permission, description);
final RolePermissionEntity request = this.addPermission(roleEntity, permissionEntity, RequestStatus.REQUESTED);
final String status = request.getStatus();
return status;
}
public void removePermission(final String role, final String permission) {
Guard.checkNotEmpty(role);
Guard.checkNotEmpty(permission);
RoleEntity roleEntity = this.roleRepository.findByName(role);
if (roleEntity == null)
return;
PermissionEntity permissionEntity = this.permissionRepository.findByValueIgnoreCase(permission);
if (permissionEntity == null)
return;
RolePermissionEntity rolePermissionEntity = this.rolePermissionRepository.findByRoleAndPermission(roleEntity, permissionEntity);
if (rolePermissionEntity == null)
return;
this.rolePermissionRepository.delete(rolePermissionEntity);
}
public Set<PermissionRequest> getRequestedPermissions() {
List<RolePermissionEntity> requestedRolePermissions = this.rolePermissionRepository.findByStatusIgnoreCase(RequestStatus.REQUESTED);
Set<PermissionRequest> requests = new LinkedHashSet<>();
for (RolePermissionEntity rp: requestedRolePermissions) {
PermissionRequest request = new PermissionRequest();
request.setId(rp.getId());
request.setRole(rp.getRole().getName());
request.setPermission(rp.getPermission().getValue());
request.setDescription(rp.getPermission().getDescription());
requests.add(request);
}
return requests;
}
public String approvePermissionRequest(final Long requestId) throws Exception {
return this.changePermissionRequestStatus(requestId, RequestStatus.APPROVED);
}
public String refusePermissionRequest(final Long requestId) throws Exception {
return this.changePermissionRequestStatus(requestId, RequestStatus.REFUSED);
}
public String requestRole(final String role) throws Exception {
final RoleEntity roleEntity = this.getRoleByName(role);
final UserEntity userEntity = this.getCurrentUser();
final UserRoleEntity request = this.addUser(userEntity, roleEntity, RequestStatus.REQUESTED);
final String status = request.getStatus();
return status;
}
public Set<RoleRequest> getRequestedRoles() {
List<UserRoleEntity> requestedUserRoles = this.userRoleRepository.findByStatusIgnoreCase(RequestStatus.REQUESTED);
Set<RoleRequest> requests = new LinkedHashSet<>();
for (UserRoleEntity userRole: requestedUserRoles) {
RoleRequest request = new RoleRequest();
request.setId(userRole.getId());
request.setRole(userRole.getRole().getName());
request.setUser(userRole.getUser().getLogin());
requests.add(request);
}
return requests;
}
public String approveRoleRequest(final Long requestId) throws Exception {
return this.changeRoleRequestStatus(requestId, RequestStatus.APPROVED);
}
public String refuseRoleRequest(final Long requestId) throws Exception {
return this.changeRoleRequestStatus(requestId, RequestStatus.REFUSED);
}
public Iterable<UserEntity> getUsers() {
return this.userRepository.findAll();
}
public PermissionEntity addPermission(final String permissionName, final String permissionDescription) throws Exception {
Guard.checkNotEmpty(permissionName);
PermissionEntity permission = this.permissionRepository.findByValueIgnoreCase(permissionName);
if (permission != null) {
throw new Exception("Can't create permission - it already exists");
}
permission = new PermissionEntity();
permission.setValue(permissionName);
permission.setDescription(permissionDescription);
permission = this.permissionRepository.save(permission);
return permission;
}
public Set<RoleEntity> getUserRoles(Long userId) throws Exception {
UserEntity user = this.getUserById(userId);
Set<RoleEntity> roles = this.getUserRoles(user);
return roles;
}
public Iterable<PermissionEntity> getPermissions() {
return this.permissionRepository.findAll();
}
public Set<PermissionEntity> getUserPermissions(Long userId) throws Exception {
UserEntity user = this.getUserById(userId);
Set<PermissionEntity> permissions = this.getUserPermissions(user);
return permissions;
}
public void removeRole(Long roleId) {
this.roleRepository.delete(roleId);
}
public Set<PermissionEntity> getRolePermissions(Long roleId) throws Exception {
RoleEntity role = this.getRoleById(roleId);
Set<PermissionEntity> permissions = this.getRolePermissions(role);
return permissions;
}
public void addPermission(Long roleId, Long permissionId) throws Exception {
PermissionEntity permission = this.getPermissionById(permissionId);
RoleEntity role = this.getRoleById(roleId);
this.addPermission(role, permission, null);
}
public void addPermission(RoleEntity role, PermissionEntity permission) {
this.addPermission(role, permission, null);
}
public void removePermission(Long permissionId, Long roleId) {
RolePermissionEntity rolePermission = this.rolePermissionRepository.findByRoleIdAndPermissionId(roleId, permissionId);
if (rolePermission != null)
this.rolePermissionRepository.delete(rolePermission);
}
public Set<UserEntity> getRoleUsers(Long roleId) throws Exception {
RoleEntity role = this.getRoleById(roleId);
Set<UserEntity> users = this.getRoleUsers(role);
return users;
}
public void addUser(Long userId, Long roleId) throws Exception {
UserEntity user = this.getUserById(userId);
RoleEntity role = this.getRoleById(roleId);
this.addUser(user, role, null);
}
public void removeUser(Long userId, Long roleId) {
UserRoleEntity userRole = this.userRoleRepository.findByUserIdAndRoleId(userId, roleId);
if (userRole != null)
this.userRoleRepository.delete(userRole);
}
public void removePermission(Long permissionId) {
this.permissionRepository.delete(permissionId);
}
public void removePermission(String value) {
PermissionEntity permission = this.permissionRepository.findByValueIgnoreCase(value);
if (permission != null)
this.permissionRepository.delete(permission);
}
public RoleEntity getCurrentUserPersonalRole() throws Exception {
String roleName = this.getSubjectName();
RoleEntity role = this.roleRepository.findByName(roleName);
if (role == null)
throw new Exception(String.format("There is no personal role for user %s", roleName));
return role;
}
private Set<PermissionEntity> getUserPermissions(UserEntity user) {
Set<RoleEntity> roles = this.getUserRoles(user);
Set<PermissionEntity> permissions = new LinkedHashSet<>();
for (RoleEntity role : roles) {
permissions.addAll(this.getRolePermissions(role));
}
return permissions;
}
private Set<PermissionEntity> getRolePermissions(RoleEntity role) {
Set<PermissionEntity> permissions = new LinkedHashSet<>();
Set<RolePermissionEntity> rolePermissions = role.getRolePermissions();
for (RolePermissionEntity rolePermission : rolePermissions) {
if (isRelationAllowed(rolePermission.getStatus())) {
PermissionEntity permission = rolePermission.getPermission();
permissions.add(permission);
}
}
return permissions;
}
private Set<RoleEntity> getUserRoles(UserEntity user) {
Set<UserRoleEntity> userRoles = user.getUserRoles();
Set<RoleEntity> roles = new LinkedHashSet<>();
for (UserRoleEntity userRole : userRoles) {
if (isRelationAllowed(userRole.getStatus())) {
RoleEntity role = userRole.getRole();
roles.add(role);
}
}
return roles;
}
private Set<UserEntity> getRoleUsers(RoleEntity role) {
Set<UserEntity> users = new LinkedHashSet<>();
for (UserRoleEntity userRole : role.getUserRoles()) {
if (isRelationAllowed(userRole.getStatus())) {
users.add(userRole.getUser());
}
}
return users;
}
private UserEntity getCurrentUser() throws Exception {
final String login = this.getSubjectName();
final UserEntity currentUser = this.getUserByLogin(login);
return currentUser;
}
private UserEntity getUserById(Long userId) throws Exception {
UserEntity user = this.userRepository.findOne(userId);
if (user == null)
throw new Exception("User doesn't exist");
return user;
}
private UserEntity getUserByLogin(final String login) throws Exception {
final UserEntity user = this.userRepository.findByLogin(login);
if (user == null)
throw new Exception("User doesn't exist");
return user;
}
private RoleEntity getRoleByName(String roleName) throws Exception {
final RoleEntity roleEntity = this.roleRepository.findByName(roleName);
if (roleEntity == null)
throw new Exception("Role doesn't exist");
return roleEntity;
}
private RoleEntity getRoleById(Long roleId) throws Exception {
final RoleEntity roleEntity = this.roleRepository.findById(roleId);
if (roleEntity == null)
throw new Exception("Role doesn't exist");
return roleEntity;
}
private PermissionEntity getPermissionById(Long permissionId) throws Exception {
final PermissionEntity permission = this.permissionRepository.findById(permissionId);
if (permission == null )
throw new Exception("Permission doesn't exist");
return permission;
}
private String changePermissionRequestStatus(Long requestId, String status) throws Exception {
RolePermissionEntity rolePermission = this.rolePermissionRepository.findById(requestId);
if (rolePermission == null)
throw new Exception("Request doesn't exist");
if (RequestStatus.REQUESTED.equals(rolePermission.getStatus())) {
rolePermission.setStatus(status);
rolePermission = this.rolePermissionRepository.save(rolePermission);
}
return rolePermission.getStatus();
}
private RolePermissionEntity addPermission(final RoleEntity role, final PermissionEntity permission, final String status) {
RolePermissionEntity relation = this.rolePermissionRepository.findByRoleAndPermission(role, permission);
if (relation == null) {
relation = new RolePermissionEntity();
relation.setRole(role);
relation.setPermission(permission);
relation.setStatus(status);
relation = this.rolePermissionRepository.save(relation);
}
return relation;
}
private boolean isRelationAllowed(final String relationStatus) {
return relationStatus == null || relationStatus == RequestStatus.APPROVED;
}
private UserRoleEntity addUser(final UserEntity user, final RoleEntity role, final String status) {
UserRoleEntity relation = this.userRoleRepository.findByUserAndRole(user, role);
if (relation == null) {
relation = new UserRoleEntity();
relation.setUser(user);
relation.setRole(role);
relation.setStatus(status);
relation = this.userRoleRepository.save(relation);
}
return relation;
}
public String getSubjectName() {
Subject subject = SecurityUtils.getSubject();
Object principalObject = subject.getPrincipals().getPrimaryPrincipal();
if (principalObject instanceof String)
return (String)principalObject;
if (principalObject instanceof Principal) {
Principal principal = (Principal)principalObject;
return principal.getName();
}
throw new UnsupportedOperationException();
}
private void deleteRole(final String role) {
RoleEntity roleEntity = this.roleRepository.findByName(role);
if (roleEntity != null) {
this.roleRepository.delete(roleEntity);
}
}
private String changeRoleRequestStatus(Long requestId, String status) throws Exception {
UserRoleEntity userRole = this.userRoleRepository.findOne(requestId);
if (userRole == null)
throw new Exception("Request doesn't exist");
if (RequestStatus.REQUESTED.equals(userRole.getStatus())) {
userRole.setStatus(status);
userRole = this.userRoleRepository.save(userRole);
}
return userRole.getStatus();
}
public RoleEntity getRole(Long id) {
return this.roleRepository.findById(id);
}
public RoleEntity updateRole(RoleEntity roleEntity) {
return this.roleRepository.save(roleEntity);
}
public void addPermissionsFromTemplate(RoleEntity roleEntity, Map<String, String> template, String value) throws Exception {
for (Map.Entry<String, String> entry : template.entrySet()) {
String permission = String.format(entry.getKey(), value);
String description = String.format(entry.getValue(), value);
PermissionEntity permissionEntity = this.addPermission(permission, description);
this.addPermission(roleEntity, permissionEntity);
}
}
public void removePermissionsFromTemplate(Map<String, String> template, String value) {
for (Map.Entry<String, String> entry : template.entrySet()) {
String permission = String.format(entry.getKey(), value);
this.removePermission(permission);
}
}
public boolean roleExists(String roleName) {
return this.roleRepository.existsByName(roleName);
}
}