From 5f9b46ee430b4ef83264e77c446e714e893bb434 Mon Sep 17 00:00:00 2001 From: dewmini <5234594+dewmini@users.noreply.github.com> Date: Tue, 25 Jun 2024 11:34:25 +1000 Subject: [PATCH 1/2] Add biosecurity feature --- build.gradle | 2 +- .../ala/userdetails/AdminController.groovy | 13 +++++- .../ala/userdetails/ProfileController.groovy | 3 +- .../ala/userdetails/PropertyController.groovy | 4 +- .../userdetails/RoleBasedInterceptor.groovy | 6 +-- .../org/ala/userdetails/UserController.groovy | 16 ++++++- .../AuthorisedSystemService.groovy | 8 ++-- .../au/org/ala/userdetails/UserService.groovy | 11 +++++ grails-app/views/admin/index.gsp | 23 ++++++---- grails-app/views/profile/myprofile.gsp | 2 +- grails-app/views/user/_form.gsp | 4 +- grails-app/views/user/create.gsp | 2 +- .../au/org/ala/auth/PreAuthorise.groovy | 6 ++- .../RoleBasedInterceptorSpec.groovy | 45 +++++++++++++++++++ 14 files changed, 118 insertions(+), 27 deletions(-) diff --git a/build.gradle b/build.gradle index e9207bda..a1ea4a6e 100644 --- a/build.gradle +++ b/build.gradle @@ -17,7 +17,7 @@ plugins { id "com.gorylenko.gradle-git-properties" version "2.4.1" } -version "3.0.3" +version "3.1.0-SNAPSHOT" group "au.org.ala" apply plugin:"eclipse" diff --git a/grails-app/controllers/au/org/ala/userdetails/AdminController.groovy b/grails-app/controllers/au/org/ala/userdetails/AdminController.groovy index d24e1e9f..6b1adce8 100644 --- a/grails-app/controllers/au/org/ala/userdetails/AdminController.groovy +++ b/grails-app/controllers/au/org/ala/userdetails/AdminController.groovy @@ -32,7 +32,18 @@ class AdminController { def profileService def authorisedSystemService - def index() {} + @PreAuthorise(allowedRoles = ["ROLE_ADMIN", "ROLE_USER_CREATOR"]) + def index() { + def user = userService.currentUser + + if (user) { + def isBiosecurityAdmin = request.isUserInRole("ROLE_USER_CREATOR") + [isBiosecurityAdmin: isBiosecurityAdmin] + } else { + log.info('my-profile without a user?') + render(status: SC_UNAUTHORIZED) + } + } def resetPasswordForUser(){ } diff --git a/grails-app/controllers/au/org/ala/userdetails/ProfileController.groovy b/grails-app/controllers/au/org/ala/userdetails/ProfileController.groovy index 00503558..e7c9557c 100644 --- a/grails-app/controllers/au/org/ala/userdetails/ProfileController.groovy +++ b/grails-app/controllers/au/org/ala/userdetails/ProfileController.groovy @@ -50,7 +50,8 @@ class ProfileController { if (user) { def props = user.propsAsMap() def isAdmin = request.isUserInRole("ROLE_ADMIN") - render(view: "myprofile", model: [user: user, props: props, isAdmin: isAdmin]) + def isBiosecurityAdmin = request.isUserInRole("ROLE_USER_CREATOR") + render(view: "myprofile", model: [user: user, props: props, isAdmin: isAdmin, isBiosecurityAdmin: isBiosecurityAdmin]) } else { log.info('my-profile without a user?') render(status: SC_UNAUTHORIZED) diff --git a/grails-app/controllers/au/org/ala/userdetails/PropertyController.groovy b/grails-app/controllers/au/org/ala/userdetails/PropertyController.groovy index 6ffd14b1..7bf88c08 100644 --- a/grails-app/controllers/au/org/ala/userdetails/PropertyController.groovy +++ b/grails-app/controllers/au/org/ala/userdetails/PropertyController.groovy @@ -89,7 +89,7 @@ class PropertyController extends BaseController { ) @Path("getProperty") @Produces("application/json") - @PreAuthorise(requiredScope = 'users/read', requiredRole = '') + @PreAuthorise(requiredScope = 'users/read', allowedRoles = []) def getProperty() { String name = params.name Long alaId = params.long('alaId') @@ -166,7 +166,7 @@ class PropertyController extends BaseController { ) @Path("saveProperty") @Produces("application/json") - @PreAuthorise(requiredScope = 'users/write', requiredRole = '') + @PreAuthorise(requiredScope = 'users/write', allowedRoles = []) def saveProperty(){ String name = params.name; String value = params.value; diff --git a/grails-app/controllers/au/org/ala/userdetails/RoleBasedInterceptor.groovy b/grails-app/controllers/au/org/ala/userdetails/RoleBasedInterceptor.groovy index e7f621bb..ec3adca8 100644 --- a/grails-app/controllers/au/org/ala/userdetails/RoleBasedInterceptor.groovy +++ b/grails-app/controllers/au/org/ala/userdetails/RoleBasedInterceptor.groovy @@ -42,7 +42,7 @@ class RoleBasedInterceptor { PreAuthorise pa = method.getAnnotation(PreAuthorise) ?: controllerClass.getAnnotation(PreAuthorise) response.withFormat { json { - if (!authorisedSystemService.isAuthorisedRequest(request, response, pa.requiredRole(), pa.requiredScope())) { + if (!authorisedSystemService.isAuthorisedRequest(request, response, pa.allowedRoles(), pa.requiredScope())) { log.warn("Denying access to $actionName from remote addr: ${request.remoteAddr}, remote host: ${request.remoteHost}") response.status = HttpStatus.SC_UNAUTHORIZED render(['error': "Unauthorized"] as JSON) @@ -51,8 +51,8 @@ class RoleBasedInterceptor { } } '*' { - def requiredRole = pa.requiredRole() - def inRole = request?.isUserInRole(requiredRole) + def allowedRoles = pa.allowedRoles() + def inRole = allowedRoles.any { role -> request?.isUserInRole(role) } if (!inRole) { log.warn("Denying access to $controllerName, $actionName to ${request?.userPrincipal?.name}") diff --git a/grails-app/controllers/au/org/ala/userdetails/UserController.groovy b/grails-app/controllers/au/org/ala/userdetails/UserController.groovy index 893f4751..3f46b15b 100644 --- a/grails-app/controllers/au/org/ala/userdetails/UserController.groovy +++ b/grails-app/controllers/au/org/ala/userdetails/UserController.groovy @@ -40,10 +40,13 @@ class UserController { } } + @PreAuthorise(allowedRoles = ["ROLE_ADMIN", "ROLE_USER_CREATOR"]) def create() { - [userInstance: new User(params)] + def isBiosecurityAdmin = request.isUserInRole("ROLE_USER_CREATOR") + [userInstance: new User(params), isBiosecurityAdmin: isBiosecurityAdmin] } + @PreAuthorise(allowedRoles = ["ROLE_ADMIN", "ROLE_USER_CREATOR"]) @Transactional def save() { def userInstance = new User(params) @@ -58,9 +61,18 @@ class UserController { } userService.updateProperties(userInstance, params) + userService.addUserRole(userInstance, "ROLE_USER") + + def isBiosecurityAdmin = request.isUserInRole("ROLE_USER_CREATOR") flash.message = message(code: 'default.created.message', args: [message(code: 'user.label', default: 'User'), userInstance.id]) - redirect(action: "show", id: userInstance.id) + if(!isBiosecurityAdmin) { + redirect(action: "show", id: userInstance.id) + } + else{ + //ROLE_USER_CREATOR role does not have permission to show(id) action + redirect(controller: "user", action: 'create') + } } def show(Long id) { diff --git a/grails-app/services/au/org/ala/userdetails/AuthorisedSystemService.groovy b/grails-app/services/au/org/ala/userdetails/AuthorisedSystemService.groovy index f27d7af4..7e24868f 100644 --- a/grails-app/services/au/org/ala/userdetails/AuthorisedSystemService.groovy +++ b/grails-app/services/au/org/ala/userdetails/AuthorisedSystemService.groovy @@ -49,11 +49,11 @@ class AuthorisedSystemService { /** * Validate a JWT Bearer token instead of the API key. * @param fallbackToLegacy Whether to fall back to legacy authorised systems if the JWT is not present. - * @param role The user role required to continue + * @param roles The user roles required to continue. The user must have at least one role to be authorized. * @param scope The JWT scope required for the request to be authorized * @return true */ - def isAuthorisedRequest(HttpServletRequest request, HttpServletResponse response, String role, String scope) { + def isAuthorisedRequest(HttpServletRequest request, HttpServletResponse response, String[] roles, String scope) { def result = false if (jwtProperties.enabled) { @@ -73,8 +73,8 @@ class AuthorisedSystemService { ) result = true - if (role) { - result = userProfile.roles.contains(role) + if (roles) { + result = roles.any { role -> userProfile.roles.contains(role) } } if (result && scope) { diff --git a/grails-app/services/au/org/ala/userdetails/UserService.groovy b/grails-app/services/au/org/ala/userdetails/UserService.groovy index 0f3678da..e804265f 100644 --- a/grails-app/services/au/org/ala/userdetails/UserService.groovy +++ b/grails-app/services/au/org/ala/userdetails/UserService.groovy @@ -272,6 +272,17 @@ class UserService { } } + def addUserRole(User user, String roleString) { + + def role = Role.findByRole(roleString) + + def userRole = UserRole.findByUserAndRole(user, role) + + if(!userRole) { + new UserRole(user:user, role:role).save(flush:true, failOnError: true) + } + } + def deleteUser(User user) { if (user) { diff --git a/grails-app/views/admin/index.gsp b/grails-app/views/admin/index.gsp index 6b77dc5c..d4896643 100644 --- a/grails-app/views/admin/index.gsp +++ b/grails-app/views/admin/index.gsp @@ -25,16 +25,21 @@