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); } }