Skip to content

Commit e9db6a1

Browse files
committed
Fixes #398
1 parent 9d3ab10 commit e9db6a1

File tree

14 files changed

+165
-22
lines changed

14 files changed

+165
-22
lines changed

client/src/api/index.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -168,6 +168,10 @@ export function organizationGUIDValidation(organizationGUID) {
168168
return fetchJson(`/api/v1/manage/organization-guid-validation/${organizationGUID}`, {}, {}, false);
169169
}
170170

171+
export function hasProvisionings(manageId) {
172+
return fetchJson(`/api/v1/manage/provisionings/${manageId}`);
173+
}
174+
171175
//Roles
172176
export function rolesByApplication(force = true, pagination = {}) {
173177
const queryPart = paginationQueryParams(pagination, {force: !!force})
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
import React from "react";
2+
import "./WarningIndicator.scss";
3+
import {ReactComponent as WarningIco} from "../icons/warning.svg";
4+
import DOMPurify from "dompurify";
5+
6+
export default function WarningIndicator({msg, standalone = false, decode = true, adjustMargin = false}) {
7+
const className = `warning-indication ${standalone ? "standalone" : ""} ${adjustMargin ? "adjust-margin" : ""}`;
8+
msg = msg.replaceAll("?", "");
9+
return decode ? <span className={className}><WarningIco/>{msg}</span> :
10+
<span className={className}>
11+
<WarningIco/>
12+
<span className={"warning-message"}
13+
dangerouslySetInnerHTML={{__html: DOMPurify.sanitize(msg, {ADD_ATTR: ['target']})}}/>
14+
</span>
15+
}
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
span.warning-indication {
2+
color: #C15500;
3+
4+
display: flex;
5+
align-items: center;
6+
margin-top: 5px;
7+
8+
&.standalone {
9+
margin-bottom: 15px;
10+
}
11+
12+
&.adjust-margin {
13+
margin-top: -15px;
14+
}
15+
16+
span.warning-message {
17+
word-break: keep-all;
18+
}
19+
20+
svg {
21+
margin-right: 10px;
22+
width: 18px;
23+
height: auto;
24+
}
25+
}

client/src/icons/warning.svg

Lines changed: 32 additions & 0 deletions
Loading

client/src/locale/en.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -186,7 +186,8 @@
186186
info: "The following users will lose their access:",
187187
userInfo: "{{name}} ({{authority}}), last activity {{lastActivity}}",
188188
andMore: "And {{nbr}} more.. Check the list of current users for more details."
189-
}
189+
},
190+
noProvisioning: "Users added to this role, won't have immediate access to this application. See the <a href='http://wiki.surfnet.nl/' target='_blank'>wiki</a> for more information"
190191
},
191192
applications: {
192193
title: "Access Roles for this application ({{nbr}})",

client/src/locale/nl.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -186,7 +186,8 @@ const nl = {
186186
info: "De volgende gebruikers verliezen toegang tot deze rol:",
187187
userInfo: "{{name}} ({{authority}}), laatste activiteit {{lastActivity}}",
188188
andMore: "En nog {{nbr}} meer.. Bekijk de lijst van huidige gebruikers voor meer details."
189-
}
189+
},
190+
noProvisioning: "Gebruikers die toegevoegd worden aan deze rol, hebben niet gelijk toegang tot deze applicatie. Zie de <a href='http://wiki.surfnet.nl/' target='_blank'>wiki</a> voor meer information"
190191
},
191192
applications: {
192193
title: "Toegangsrollen voor deze applicatie ({{nbr}})",

client/src/pages/RoleForm.js

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import {
77
allProviders,
88
consequencesRoleDeletion,
99
createRole,
10-
deleteRole,
10+
deleteRole, hasProvisionings,
1111
me,
1212
organizationGUIDValidation,
1313
roleByID,
@@ -28,6 +28,7 @@ import ConfirmationDialog from "../components/ConfirmationDialog";
2828
import SwitchField from "../components/SwitchField";
2929
import {dateFromEpoch, displayExpiryDate, futureDate} from "../utils/Date";
3030
import DOMPurify from "dompurify";
31+
import WarningIndicator from "../components/WarningIndicator";
3132

3233
const DEFAULT_EXPIRY_DAYS = 365;
3334
const CUT_OFF_DELETED_USER = 5;
@@ -58,6 +59,7 @@ export const RoleForm = () => {
5859
const [customRoleExpiryDate, setCustomRoleExpiryDate] = useState(false);
5960
const [customInviterDisplayName, setCustomInviterDisplayName] = useState(false);
6061
const [applications, setApplications] = useState([]);
62+
const [provisionings, setProvisionings] = useState({});
6163
const [allowedToEditApplication, setAllowedToEditApplication] = useState(true);
6264
const [deletedUserRoles, setDeletedUserRoles] = useState(null);
6365

@@ -247,6 +249,11 @@ export const RoleForm = () => {
247249
const changeApplication = (index, application) => {
248250
applications.splice(index, 1, application);
249251
setApplications([...applications]);
252+
hasProvisionings(application.manageId)
253+
.then(res => {
254+
const newProvisionings = {...provisionings, [application.manageId]: !res && !application.receivesMemberships};
255+
setProvisionings(newProvisionings);
256+
})
250257
}
251258

252259
const changeApplicationLandingPage = (index, e) => {
@@ -367,6 +374,9 @@ export const RoleForm = () => {
367374
<ErrorIndicator msg={I18n.t("forms.required", {
368375
attribute: I18n.t("roles.manage").toLowerCase()
369376
})}/>}
377+
{provisionings[application?.manageId] &&
378+
<WarningIndicator msg={I18n.t("roles.noProvisioning")} decode={false}/>
379+
}
370380
</div>
371381
<div className="input-field-container">
372382
<InputField name={I18n.t("roles.landingPage")}

client/src/utils/Manage.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ export const singleProviderToOption = (provider, locale) => {
1010
type: manageType,
1111
manageType: manageType,
1212
manageId: manageId,
13+
receivesMemberships: provider.receivesMemberships,
1314
url: provider.url || provider.landingPage,
1415
landingPage: provider.landingPage || provider.url
1516
};

server/src/main/java/invite/api/ManageController.java

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,6 @@ public ResponseEntity<Map<String, Object>> organizationGUIDValidation(@Parameter
9191

9292
@GetMapping("applications")
9393
public ResponseEntity<Map<String, List<Map<String, Object>>>> applications(@Parameter(hidden = true) User user) {
94-
LOG.debug("/applications");
9594
LOG.debug(String.format("GET /manage/applications for user %s", user.getEduPersonPrincipalName()));
9695

9796
UserPermissions.assertInstitutionAdmin(user);
@@ -130,4 +129,12 @@ public ResponseEntity<Map<String, List<Map<String, Object>>>> applications(@Para
130129
));
131130
}
132131

132+
@GetMapping("provisionings/{id}")
133+
public ResponseEntity<Boolean> provisionings(@PathVariable("id") String id,
134+
@Parameter(hidden = true) User user) {
135+
LOG.debug(String.format("GET /manage/provisionings for user %s", user.getEduPersonPrincipalName()));
136+
UserPermissions.assertInstitutionAdmin(user);
137+
List<Map<String, Object>> provisionings = manage.provisioning(List.of(id));
138+
return ResponseEntity.ok(!provisionings.isEmpty());
139+
}
133140
}

server/src/main/java/invite/manage/Manage.java

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,23 @@ default Map<String, Object> transformProvider(Map<String, Object> provider) {
9191
application.put("name:en", metaDataFields.get("name:en"));
9292
application.put("name:nl", metaDataFields.get("name:nl"));
9393
application.put("institutionGuid", metaDataFields.get("coin:institution_guid"));
94+
Map<String, Object> arp = (Map<String, Object>) data.get("arp");
95+
if (!CollectionUtils.isEmpty(arp)) {
96+
boolean enabled = (boolean) arp.getOrDefault("enabled", false);
97+
if (!enabled) {
98+
//Will receive all attributes, but not from any other source than idp
99+
application.put("receivesMemberships", false);
100+
} else {
101+
Map<String, Object> attributes = (Map<String, Object>) arp.getOrDefault("attributes", Map.of());
102+
List<Map<String, Object>> isMemberOf = (List<Map<String, Object>>) attributes.get("urn:mace:dir:attribute-def:isMemberOf");
103+
if (CollectionUtils.isEmpty(isMemberOf)) {
104+
application.put("receivesMemberships", false);
105+
} else {
106+
boolean receivesVootMemberships = isMemberOf.stream().anyMatch(m -> m.get("source") == "voot");
107+
application.put("receivesMemberships", receivesVootMemberships);
108+
}
109+
}
110+
}
94111
return application;
95112
}
96113

0 commit comments

Comments
 (0)