Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
@prefix ufo: <http://onto.fel.cvut.cz/ontologies/ufo/> .

<http://onto.fel.cvut.cz/ontologies/record-manager/role-groups> {

rm:admin-role-group rdf:type owl:NamedIndividual, rm:role-group;
rm:has-role rm:read-all-records-role,
rm:write-all-records-role,
Expand All @@ -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,
Expand All @@ -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,
Expand All @@ -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;
Expand Down
22 changes: 21 additions & 1 deletion deploy/keycloak-auth/keycloak-config/roles.tf
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ variable "roles" {
read-all-organizations-role = "",
write-all-organizations-role = "",
read-action-history-role = "",
read-statistics-role = ""
read-statistics-role = "",
}
}

Expand All @@ -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
]
}
9 changes: 5 additions & 4 deletions src/components/user/User.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -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";

Expand Down Expand Up @@ -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 (
<Button
style={{ margin: "0 0.3em 0 0" }}
Expand Down Expand Up @@ -329,8 +329,9 @@ class User extends React.Component {
</IfInternalAuth>
<div className="buttons-line-height mt-3 text-center">
{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) && (
<Button
variant="success"
Expand Down
3 changes: 3 additions & 0 deletions src/constants/DefaultConstants.js
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,9 @@ export const ROLE = {
COMMENT_RECORD_QUESTIONS: "commentRecordQuestions",
READ_ACTION_HISTORY: "readActionHistory",
READ_STATISTICS: "readStatistics",

// Impersonate
IMPERSONATE: "impersonate",
};

// Default number of table elements per page.
Expand Down
8 changes: 8 additions & 0 deletions src/utils/RoleUtils.js
Original file line number Diff line number Diff line change
Expand Up @@ -95,3 +95,11 @@ export function canSelectInstitution(currentUser, user) {
canWriteUserInfo(currentUser, user)
);
}

export function canImpersonate(currentUser, user) {
return (
hasRole(currentUser, ROLE.IMPERSONATE) &&
hasSupersetOfPrivileges(currentUser, user) &&
currentUser.username !== user.username
);
}
16 changes: 8 additions & 8 deletions tests/__tests__/components/User.spec.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -126,8 +126,8 @@
const inputElements = TestUtils.scryRenderedDOMComponentsWithTag(tree, "input");
const selectElements = TestUtils.scryRenderedDOMComponentsWithTag(tree, "select");

expect(inputElements.length).toEqual(5);
expect(selectElements.length).toEqual(2);
expect(inputElements.length).toEqual(4);

Check failure on line 129 in tests/__tests__/components/User.spec.jsx

View workflow job for this annotation

GitHub Actions / build

tests/__tests__/components/User.spec.jsx > User > renders admin's form empty with random button

AssertionError: expected 5 to deeply equal 4 - Expected + Received - 4 + 5 ❯ tests/__tests__/components/User.spec.jsx:129:34
expect(selectElements.length).toEqual(1);

for (let input of inputElements) {
switch (input.name) {
Expand Down Expand Up @@ -298,7 +298,7 @@
}
}
const selects = TestUtils.scryRenderedDOMComponentsWithTag(tree, "select");
expect(selects.length).toEqual(2);
expect(selects.length).toEqual(1);

Check failure on line 301 in tests/__tests__/components/User.spec.jsx

View workflow job for this annotation

GitHub Actions / build

tests/__tests__/components/User.spec.jsx > User > renders filled user's form without random button

AssertionError: expected 2 to deeply equal 1 - Expected + Received - 1 + 2 ❯ tests/__tests__/components/User.spec.jsx:301:28
const randomButton = TestUtils.scryRenderedDOMComponentsWithClass(tree, "glyphicon");
expect(randomButton.length).toEqual(0);
});
Expand Down Expand Up @@ -344,7 +344,7 @@
}
}
const selects = TestUtils.scryRenderedDOMComponentsWithTag(tree, "select");
expect(selects.length).toEqual(2);
expect(selects.length).toEqual(1);

Check failure on line 347 in tests/__tests__/components/User.spec.jsx

View workflow job for this annotation

GitHub Actions / build

tests/__tests__/components/User.spec.jsx > User > renders filled admin's form

AssertionError: expected 2 to deeply equal 1 - Expected + Received - 1 + 2 ❯ tests/__tests__/components/User.spec.jsx:347:28
});

it("renders filled user's form", function () {
Expand Down Expand Up @@ -416,9 +416,9 @@
</IntlProvider>,
);
const buttons = TestUtils.scryRenderedDOMComponentsWithTag(tree, "Button");
expect(buttons.length).toEqual(5);
expect(buttons.length).toEqual(3);

Check failure on line 419 in tests/__tests__/components/User.spec.jsx

View workflow job for this annotation

GitHub Actions / build

tests/__tests__/components/User.spec.jsx > User > renders "Cancel" button and click on it

AssertionError: expected 4 to deeply equal 3 - Expected + Received - 3 + 4 ❯ tests/__tests__/components/User.spec.jsx:419:28

TestUtils.Simulate.click(buttons[4]); // cancel
TestUtils.Simulate.click(buttons[2]); // cancel
expect(handlers.onCancel).toHaveBeenCalled();
});

Expand All @@ -442,9 +442,9 @@
);
const buttons = TestUtils.scryRenderedDOMComponentsWithTag(tree, "Button");

expect(buttons.length).toEqual(5);
expect(buttons.length).toEqual(3);

Check failure on line 445 in tests/__tests__/components/User.spec.jsx

View workflow job for this annotation

GitHub Actions / build

tests/__tests__/components/User.spec.jsx > User > renders "Back to institution" button and click on it

AssertionError: expected 4 to deeply equal 3 - Expected + Received - 3 + 4 ❯ tests/__tests__/components/User.spec.jsx:445:28

TestUtils.Simulate.click(buttons[4]); // back to institution
TestUtils.Simulate.click(buttons[2]); // back to institution
expect(handlers.onCancel).toHaveBeenCalled();
});

Expand Down
Loading