diff --git a/deploy/internal-auth/db-server/import/record-manager-app/role-groups.trig b/deploy/internal-auth/db-server/import/record-manager-app/role-groups.trig index 1470bff3..d0e77201 100644 --- a/deploy/internal-auth/db-server/import/record-manager-app/role-groups.trig +++ b/deploy/internal-auth/db-server/import/record-manager-app/role-groups.trig @@ -10,6 +10,7 @@ @prefix ufo: . { + rm:admin-role-group rdf:type owl:NamedIndividual, rm:role-group; rm:has-role rm:read-all-records-role, rm:write-all-records-role, @@ -29,8 +30,25 @@ rm:read-all-organizations-role, rm:write-all-organizations-role, rm:read-action-history-role, - rm:read-statistics-role; + rm:read-statistics-role, + rm:impersonate-role; rdfs:label "admin-role-group"@en . + + rm:data-collection-coordinator-impersonate-role-group rdf:type owl:NamedIndividual, rm:role-group; + rm:has-role rm:read-all-users-role, + rm:write-all-users-role, + rm:read-organization-users-role, + rm:write-organization-users-role, + rm:read-organization-role, + rm:write-organization-role, + rm:read-all-organizations-role, + rm:write-all-organizations-role, + rm:read-organization-records-role, + rm:write-organization-records-role, + rm:comment-record-questions-role, + rm:complete-records-role, + rm:impersonate-role; + rdfs:label "data-collection-coordinator-impersonate-role-group"@en . rm:data-collection-coordinator-role-group rdf:type owl:NamedIndividual, rm:role-group; rm:has-role rm:read-all-users-role, @@ -45,9 +63,19 @@ rm:write-organization-records-role, rm:comment-record-questions-role, rm:complete-records-role; - rdfs:label "data-collection-coordinator-role-group"@en . + rm:organization-manager-impersonate-role-group rdf:type owl:NamedIndividual, rm:role-group; + rm:has-role rm:read-organization-role, + rm:write-organization-role, + rm:read-organization-users-role, + rm:write-organization-users-role, + rm:read-organization-records-role, + rm:write-organization-records-role, + rm:comment-record-questions-role, + rm:impersonate-role; + rdfs:label "organization-manager-impersonate-role-group"@en . + rm:organization-manager-role-group rdf:type owl:NamedIndividual, rm:role-group; rm:has-role rm:read-organization-role, rm:write-organization-role, @@ -58,12 +86,25 @@ rm:comment-record-questions-role; rdfs:label "organization-manager-role-group"@en . + rm:entry-clerk-impersonate-role-group rdf:type owl:NamedIndividual, rm:role-group; + rm:has-role rm:read-organization-role, + rm:read-organization-records-role, + rm:comment-record-questions-role, + rm:impersonate-role; + rdfs:label "entry-clerk-impersonate-role-group"@en . + rm:entry-clerk-role-group rdf:type owl:NamedIndividual, rm:role-group; rm:has-role rm:read-organization-role, rm:read-organization-records-role, rm:comment-record-questions-role; rdfs:label "entry-clerk-role-group"@en . + rm:reviewer-impersonate-role-group rdf:type owl:NamedIndividual, rm:role-group; + rm:has-role rm:complete-records-role, + rm:comment-record-questions-role, + rm:impersonate-role; + rdfs:label "reviewer-role-group"@en . + rm:reviewer-role-group rdf:type owl:NamedIndividual, rm:role-group; rm:has-role rm:complete-records-role, rm:comment-record-questions-role; diff --git a/deploy/keycloak-auth/keycloak-config/roles.tf b/deploy/keycloak-auth/keycloak-config/roles.tf index 3bf01752..047b1f09 100644 --- a/deploy/keycloak-auth/keycloak-config/roles.tf +++ b/deploy/keycloak-auth/keycloak-config/roles.tf @@ -19,7 +19,7 @@ variable "roles" { read-all-organizations-role = "", write-all-organizations-role = "", read-action-history-role = "", - read-statistics-role = "" + read-statistics-role = "", } } @@ -30,3 +30,23 @@ resource "keycloak_role" "realm_roles" { name = each.key description = length(each.value) > 0 ? each.value : null } + +# --- Impersonation role composite --- +data "keycloak_openid_client" "realm_management" { + realm_id = var.kc_realm + client_id = "realm-management" +} + +data "keycloak_role" "realm_management_impersonation" { + realm_id = var.kc_realm + client_id = data.keycloak_openid_client.realm_management.id + name = "impersonation" +} + +resource "keycloak_role" "impersonate_role_composite" { + realm_id = var.kc_realm + name = "impersonate-role" + composite_roles = [ + data.keycloak_role.realm_management_impersonation.id + ] +} diff --git a/src/components/user/User.jsx b/src/components/user/User.jsx index 0573bb5f..bb4cb729 100644 --- a/src/components/user/User.jsx +++ b/src/components/user/User.jsx @@ -13,7 +13,7 @@ import { FaRandom } from "react-icons/fa"; import { isUsingOidcAuth } from "../../utils/OidcUtils"; import IfInternalAuth from "../misc/oidc/IfInternalAuth.jsx"; import RoleBadges from "../RoleBadges.jsx"; -import { canWriteUserInfo, getRoles, hasSupersetOfPrivileges, hasRole } from "../../utils/RoleUtils.js"; +import { canWriteUserInfo, getRoles, hasRole, canImpersonate } from "../../utils/RoleUtils.js"; import RoleGroupsSelector from "../RoleGroupsSelector.jsx"; import InstitutionSelector from "../institution/InstitutionSelector.jsx"; @@ -160,7 +160,7 @@ class User extends React.Component { _impersonateButton() { const { user, currentUser, handlers, impersonation } = this.props; - if (!user.isNew && hasSupersetOfPrivileges(currentUser, user) && currentUser.username !== user.username) { + if (!user.isNew && canImpersonate(currentUser, user)) { return ( {this._impersonateButton()} - {isUsingOidcAuth() ? this._redirectToKeycloakButton() : this._passwordChangeButton()} - {this._saveAndSendEmailButton()} + {canWriteUserInfo(currentUser, user) && + (isUsingOidcAuth() ? this._redirectToKeycloakButton() : this._passwordChangeButton())} + {canWriteUserInfo(currentUser, user) && this._saveAndSendEmailButton()} {canWriteUserInfo(currentUser, user) && ( , ); const buttons = TestUtils.scryRenderedDOMComponentsWithTag(tree, "Button"); - expect(buttons.length).toEqual(5); + expect(buttons.length).toEqual(3); - TestUtils.Simulate.click(buttons[4]); // cancel + TestUtils.Simulate.click(buttons[2]); // cancel expect(handlers.onCancel).toHaveBeenCalled(); }); @@ -442,9 +442,9 @@ describe("User", function () { ); const buttons = TestUtils.scryRenderedDOMComponentsWithTag(tree, "Button"); - expect(buttons.length).toEqual(5); + expect(buttons.length).toEqual(3); - TestUtils.Simulate.click(buttons[4]); // back to institution + TestUtils.Simulate.click(buttons[2]); // back to institution expect(handlers.onCancel).toHaveBeenCalled(); });