diff --git a/client/src/components/Entities.jsx b/client/src/components/Entities.jsx index 152e97df..2ff6e8fd 100644 --- a/client/src/components/Entities.jsx +++ b/client/src/components/Entities.jsx @@ -38,6 +38,7 @@ export const Entities = ({ newEntityPath, newEntityFunc, defaultSort, + rowClassNameResolver, searchAutoFocus = false, busy = false }) => { @@ -145,8 +146,9 @@ export const Entities = ({ } const entityRow = (entity, index) => { + const additionalClassName = isEmpty(rowClassNameResolver) ? "" : rowClassNameResolver(entity); return + className={`${typeof rowLinkMapper === "function" ? "clickable" : ""} ${onHover ? "hoverable" : ""} ${additionalClassName}`}> {columns.map((column, i) => (column.key !== "check" && !column.hasLink) ? diff --git a/client/src/components/Entities.scss b/client/src/components/Entities.scss index 1fe23ada..63c2a980 100644 --- a/client/src/components/Entities.scss +++ b/client/src/components/Entities.scss @@ -184,6 +184,10 @@ cursor: pointer; } + &.multi-role { + color: var(--sds--color--green--500) + } + .action-icons-container { display: flex; align-items: center; @@ -252,6 +256,12 @@ height: auto; margin-right: 5px; } + + svg.multi-role { + height: 36px; + width: auto; + margin-left: 15px; + } } .sds--chips span { diff --git a/client/src/icons/multi-role.svg b/client/src/icons/multi-role.svg index b8322143..feafbf55 100644 --- a/client/src/icons/multi-role.svg +++ b/client/src/icons/multi-role.svg @@ -1,4 +1,4 @@ - + diff --git a/client/src/pages/RoleForm.js b/client/src/pages/RoleForm.js index 0db3d61f..225fd384 100644 --- a/client/src/pages/RoleForm.js +++ b/client/src/pages/RoleForm.js @@ -74,7 +74,7 @@ export const RoleForm = () => { setRole({...role, applications: [providerOption]}) } else { breadcrumbPath.push({path: `/roles/${res[0].id}`, value: name}); - setManagementOption([singleProviderToOption(res[0].application)]); + setManagementOption(providersToOptions(res[0].applicationMaps)); } breadcrumbPath.push({value: I18n.t(`roles.${newRole ? "new" : "edit"}`, {name: name})}); useAppStore.setState({breadcrumbPath: breadcrumbPath}); diff --git a/client/src/tabs/Applications.js b/client/src/tabs/Applications.js index 4f56ab82..9c3f12bc 100644 --- a/client/src/tabs/Applications.js +++ b/client/src/tabs/Applications.js @@ -3,6 +3,7 @@ import React, {useEffect, useState} from "react"; import {Entities} from "../components/Entities"; import I18n from "../locale/I18n"; import {Loader} from "@surfnet/sds"; +import {ReactComponent as MultipleIcon} from "../icons/multi-role.svg"; import {applications, rolesByApplication} from "../api"; import {isEmpty, splitListSemantically, stopEvent} from "../utils/Utils"; import {AUTHORITIES, isUserAllowed} from "../utils/UserRole"; @@ -26,9 +27,9 @@ const Applications = () => { const providers = res[0].providers; const provisionings = res[0].provisionings; res[1].forEach(role => { - role.logo = providerLogoById(role.manageId, providers); - role.provider = providerById(role.manageId, providers); - role.provisioning = provisioningsByProviderId(role.manageId, provisionings); + role.logo = providerLogoById(role.applicationMaps, providers); + role.provider = providerById(role.applicationMaps, providers); + role.provisioning = provisioningsByProviderId(role.applicationMaps, provisionings); }) setRoles(res[1]); setLoading(false); @@ -51,21 +52,30 @@ const Applications = () => { } }; - const providerById = (manageId, allProviders) => { + const providerById = (manageMaps, allProviders) => { + if (manageMaps.length > 1) { + return I18n.t("roles.multiple"); + } + const manageId = manageMaps[0].id const provider = allProviders.find(provider => provider.id === manageId) || providerInfo(null); const organisation = provider["OrganizationName:en"]; const organisationValue = isEmpty(organisation) ? "" : ` (${organisation})`; return `${provider["name:en"]}${organisationValue}`; } - const providerLogoById = (manageId, allProviders) => { + const providerLogoById = (manageMaps, allProviders) => { + if (manageMaps.length > 1) { + return + } + const manageId = manageMaps[0].id const provider = allProviders.find(provider => provider.id === manageId) || providerInfo(null); return provider.logo; } - const provisioningsByProviderId = (manageId, allProvisionings) => { + const provisioningsByProviderId = (manageMaps, allProvisionings) => { + const manageIdentifiers = manageMaps.map(m => m.id); const providers = allProvisionings - .filter(provisioning => provisioning.applications.some(app => app.id === manageId)) + .filter(provisioning => provisioning.applications.some(app => manageIdentifiers.includes( app.id))) .map(provider => `${provider["name:en"]} (${provider.provisioning_type})`); return splitListSemantically(providers, I18n.t("forms.and")); } @@ -75,7 +85,9 @@ const Applications = () => { key: "logo", header: "", nonSortable: true, - mapper: role => logo + mapper: role =>
+ {typeof role.logo === "string" ? logo : role.logo} +
}, { key: "name", @@ -105,7 +117,8 @@ const Applications = () => { customNoEntities={I18n.t(`roles.noResults`)} searchAttributes={["name", "provider", "provisioning"]} rowLinkMapper={isUserAllowed(AUTHORITIES.INVITER, user) ? openRole : null} - inputFocus={true}> + inputFocus={true} + rowClassNameResolver={entity => entity.applications.length > 1 ? "multi-role" : ""}> diff --git a/client/src/tabs/Roles.js b/client/src/tabs/Roles.js index 30db3d3b..833d2b85 100644 --- a/client/src/tabs/Roles.js +++ b/client/src/tabs/Roles.js @@ -148,12 +148,11 @@ export const Roles = () => { { nonSortable: true, key: "logo", + header: "", - mapper: role => { - return
- {typeof role.logo === "string" ? logo :role.logo} + mapper: role =>
+ {typeof role.logo === "string" ? logo : role.logo}
- } }, { key: "applicationName", @@ -221,6 +220,7 @@ export const Roles = () => { filters={filter(filterOptions, filterValue)} customSearch={roleSearchRequired && isSuperUser ? search : null} rowLinkMapper={isUserAllowed(AUTHORITIES.INVITER, user) ? openRole : null} + rowClassNameResolver={entity => entity.applications.length > 1 ? "multi-role" : ""} busy={searching}/>
); diff --git a/client/src/tabs/Roles.scss b/client/src/tabs/Roles.scss index ce6af6d6..4e46036f 100644 --- a/client/src/tabs/Roles.scss +++ b/client/src/tabs/Roles.scss @@ -66,7 +66,6 @@ div.mod-roles { td.authority, td.defaultExpiryDays, td.userRoleCount { text-align: center; } - } } diff --git a/client/src/utils/Manage.js b/client/src/utils/Manage.js index 311e94d7..509e8728 100644 --- a/client/src/utils/Manage.js +++ b/client/src/utils/Manage.js @@ -4,12 +4,14 @@ import {ReactComponent as MultipleIcon} from "../icons/multi-role.svg"; export const singleProviderToOption = provider => { const organisation = provider["OrganizationName:en"]; const organisationValue = isEmpty(organisation) ? "" : ` (${organisation})`; + const manageType = provider.type ? provider.type.toUpperCase() : provider.manageType; + const manageId = provider.id || provider.manageId; return { - value: provider.id, + value: manageId, label: `${provider["name:en"]}${organisationValue}`, - type: provider.type.toUpperCase(), - manageType: provider.type.toUpperCase(), - manageId: provider.id + type: manageType, + manageType: manageType, + manageId: manageId }; } diff --git a/client/src/utils/Utils.js b/client/src/utils/Utils.js index 0917c0bd..cfcfbad0 100644 --- a/client/src/utils/Utils.js +++ b/client/src/utils/Utils.js @@ -17,6 +17,10 @@ export function isEmpty(obj) { if (typeof obj === "string") { return obj.trim().length === 0; } + if (obj && obj.getTime) { + // eslint-disable-next-line + return obj.getTime() !== obj.getTime(); + } if (typeof obj === "object") { return Object.keys(obj).length === 0; } diff --git a/server/src/main/java/access/voot/VootController.java b/server/src/main/java/access/voot/VootController.java index e7a51fac..1056ea6f 100644 --- a/server/src/main/java/access/voot/VootController.java +++ b/server/src/main/java/access/voot/VootController.java @@ -55,7 +55,7 @@ public ResponseEntity>> getGroupMemberships(@PathVariab private Map parseUserRole(UserRole userRole) { Map res = new HashMap<>(); Role role = userRole.getRole(); - String urn = role.isTeamsOrigin() ? GroupURN.teamsUrnFromRole(teamsNameContext, role) : GroupURN.urnFromRole(groupUrnPrefix, userRole.getRole()); + String urn = role.isTeamsOrigin() ? GroupURN.teamsUrnFromRole(teamsNameContext, role) : GroupURN.urnFromRole(groupUrnPrefix, role); res.put("urn", urn); res.put("name", role.getName()); return res;