Skip to content

Commit 3d26bfe

Browse files
[SCIM-44] added support for propagation of custom roles and entitlements (#146)
1 parent f55f847 commit 3d26bfe

File tree

9 files changed

+88
-44
lines changed

9 files changed

+88
-44
lines changed

src/main/java/net/tirasa/connid/bundles/scim/common/AbstractSCIMConnector.java

Lines changed: 17 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -997,15 +997,6 @@ protected Object buildPatchValue(
997997
}).collect(Collectors.toList());
998998
break;
999999

1000-
case "roles.default.value":
1001-
patchValue = values.stream().filter(Objects::nonNull).map(v -> {
1002-
SCIMGenericComplex<String> value = new SCIMGenericComplex<>();
1003-
value.setValue(v.toString());
1004-
value.setType("default");
1005-
return value;
1006-
}).collect(Collectors.toList());
1007-
break;
1008-
10091000
case "entitlements.default.value":
10101001
patchValue = values.stream().filter(Objects::nonNull).map(v -> {
10111002
SCIMGenericComplex<String> value = new SCIMGenericComplex<>();
@@ -1031,10 +1022,23 @@ protected Object buildPatchValue(
10311022
break;
10321023

10331024
default:
1034-
// this is mainly useful to manage custom attributes
1035-
patchValue = CollectionUtil.isEmpty(values) ? null
1036-
: attributeDefinition != null && attributeDefinition.getMultiValued() ? values
1037-
: values.get(0).toString();
1025+
// manage and entitlements roles
1026+
if (name.startsWith(SCIMAttributeUtils.SCIM_USER_ROLES + ".")
1027+
|| name.startsWith(SCIMAttributeUtils.SCIM_USER_ENTITLEMENTS + ".")) {
1028+
patchValue = values.stream().filter(Objects::nonNull).map(v -> {
1029+
SCIMGenericComplex<String> value = new SCIMGenericComplex<>();
1030+
value.setValue(v.toString());
1031+
value.setType(SCIMUtils.getTypeFromAttributeName(name));
1032+
return value;
1033+
}).collect(Collectors.toList());
1034+
} else {
1035+
// this is mainly useful to manage custom attributes
1036+
patchValue = CollectionUtil.isEmpty(values)
1037+
? null
1038+
: attributeDefinition != null && attributeDefinition.getMultiValued()
1039+
? values
1040+
: values.get(0).toString();
1041+
}
10381042
break;
10391043
}
10401044

src/main/java/net/tirasa/connid/bundles/scim/common/dto/AbstractSCIMUser.java

Lines changed: 8 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -815,14 +815,6 @@ private void doSetAttribute(final String name, final List<Object> values) {
815815
handleSCIMUserAddressObject(AddressCanonicalType.other, s -> s.setOperation(String.class.cast(value)));
816816
break;
817817

818-
case "roles.default.value":
819-
handleRoles(value);
820-
break;
821-
822-
case "entitlements.default.value":
823-
handleDefaultEntitlement(value);
824-
break;
825-
826818
case "x509Certificates.default.value":
827819
handlex509Certificates(value);
828820
break;
@@ -832,15 +824,21 @@ private void doSetAttribute(final String name, final List<Object> values) {
832824
break;
833825

834826
default:
827+
// manage all other custom roles abd entitlements
828+
if (name.startsWith(SCIMAttributeUtils.SCIM_USER_ENTITLEMENTS + ".")) {
829+
handleEntitlement(SCIMUtils.getTypeFromAttributeName(name), value);
830+
} else if (name.startsWith(SCIMAttributeUtils.SCIM_USER_ROLES + ".")) {
831+
handleRole(SCIMUtils.getTypeFromAttributeName(name), value);
832+
}
835833
break;
836834
}
837835
}
838836

839-
protected abstract void handleRoles(Object value);
837+
protected abstract void handleRole(String type, Object value);
840838

841839
protected abstract void handlex509Certificates(Object value);
842840

843-
protected abstract void handleDefaultEntitlement(Object value);
841+
protected abstract void handleEntitlement(String type, Object value);
844842

845843
@JsonIgnore
846844
protected <T extends Serializable> void handleSCIMComplexObject(final T type,

src/main/java/net/tirasa/connid/bundles/scim/common/service/AbstractSCIMService.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -364,8 +364,8 @@ protected String checkServiceErrors(final Response response) {
364364

365365
if (response.getStatusInfo().getFamily() != Status.Family.SUCCESSFUL) {
366366
SCIMUtils.handleGeneralError(
367-
"While executing SCIM request: status is " + response.getStatus() + " and reponse "
368-
+ responseAsString);
367+
"While executing SCIM request: status is " + response.getStatus() + " and response "
368+
+ responseAsString);
369369
}
370370
return responseAsString;
371371
}

src/main/java/net/tirasa/connid/bundles/scim/common/utils/SCIMUtils.java

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,8 @@
2727
import java.util.List;
2828
import java.util.Optional;
2929
import java.util.Set;
30+
import java.util.regex.Matcher;
31+
import java.util.regex.Pattern;
3032
import net.tirasa.connid.bundles.scim.common.SCIMConnectorConfiguration;
3133
import net.tirasa.connid.bundles.scim.common.SCIMProvider;
3234
import net.tirasa.connid.bundles.scim.common.dto.BaseResourceReference;
@@ -229,6 +231,14 @@ public static String getPath(final String id, final SCIMConnectorConfiguration c
229231
return config.getEnableURLPathEncoding() ? URLEncoder.encode(id, StandardCharsets.UTF_8) : id;
230232
}
231233

234+
public static String getTypeFromAttributeName(final String attributeName) {
235+
if (StringUtil.isBlank(attributeName)) {
236+
return null;
237+
}
238+
Matcher matcher = Pattern.compile("^[^.]+\\.([^.]+)\\.[^.]+$").matcher(attributeName);
239+
return matcher.find() ? matcher.group(1) : null;
240+
}
241+
232242
private SCIMUtils() {
233243
// private constructor for static utility class
234244
}

src/main/java/net/tirasa/connid/bundles/scim/v11/dto/SCIMv11User.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ public SCIMv11User() {
4343
}
4444

4545
@Override
46-
protected void handleRoles(final Object value) {
46+
protected void handleRole(final String type, final Object value) {
4747
handleSCIMDefaultObject(
4848
String.class.cast(value),
4949
this.roles,
@@ -59,7 +59,7 @@ protected void handlex509Certificates(final Object value) {
5959
}
6060

6161
@Override
62-
protected void handleDefaultEntitlement(final Object value) {
62+
protected void handleEntitlement(final String type, final Object value) {
6363
handleBaseResourceReference(
6464
String.class.cast(value),
6565
this.entitlements,

src/main/java/net/tirasa/connid/bundles/scim/v2/SCIMv2Connector.java

Lines changed: 34 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -161,6 +161,7 @@ protected SCIMv2Patch buildPatchFromGroup(final SCIMv2Group group) {
161161
}
162162

163163
@Override
164+
@SuppressWarnings("unchecked")
164165
protected SCIMv2Patch buildUserPatch(
165166
final Set<AttributeDelta> modifications,
166167
final SCIMv2User currentUser,
@@ -191,7 +192,24 @@ protected SCIMv2Patch buildUserPatch(
191192
&& !attrDelta.is(SCIMAttributeUtils.SCIM_USER_GROUPS)
192193
// custom attributes are going to be managed further
193194
&& !isCustomAttribute(attrDelta.getName(), configuration.getUseColonOnExtensionAttributes())) {
194-
patch.addOperations(buildPatchOperations(attrDelta, null));
195+
196+
if (CollectionUtil.isEmpty(attrDelta.getValuesToReplace())) {
197+
patch.addOperations(buildPatchOperations(attrDelta, null));
198+
} else {
199+
// build the patch operation
200+
SCIMv2PatchOperation replacePatchOperation =
201+
buildReplacePatchOperation(attrDelta.getName(), attrDelta.getValuesToReplace(), null);
202+
// check if the attribute has already been added as a patch operation or not, if so aggregate
203+
// values in a single patch operation
204+
patch.getOperations()
205+
.stream()
206+
.filter(op -> op.getValue() instanceof List && replacePatchOperation.getPath()
207+
.equalsIgnoreCase(op.getPath()))
208+
.findFirst()
209+
.ifPresentOrElse(op -> ((List<Object>) op.getValue()).addAll(
210+
(List<Object>) replacePatchOperation.getValue()),
211+
() -> patch.addOperation(replacePatchOperation));
212+
}
195213
}
196214
}
197215
// manage addresses patches
@@ -340,12 +358,8 @@ protected List<SCIMv2PatchOperation> buildPatchOperations(
340358
operations.add(removePatchOperation);
341359
}
342360
} else {
343-
SCIMv2PatchOperation replacePatchOperation = new SCIMv2PatchOperation();
344-
replacePatchOperation.setPath(SCIMAttributeUtils.getBaseAttributeName(currentDelta.getName()));
345-
replacePatchOperation.setOperation(SCIMAttributeUtils.SCIM_REPLACE);
346-
replacePatchOperation.setValue(
347-
buildPatchValue(currentDelta.getName(), currentDelta.getValuesToReplace(), attributeDefinition));
348-
operations.add(replacePatchOperation);
361+
operations.add(buildReplacePatchOperation(currentDelta.getName(), currentDelta.getValuesToReplace(),
362+
attributeDefinition));
349363
}
350364

351365
return operations;
@@ -571,7 +585,7 @@ private static void setAddressAttribute(
571585
protected void manageEntitlements(final SCIMv2User user, final List<String> values) {
572586
List<SCIMv2EntitlementResource> scimEntitlementRefs = values == null
573587
? Collections.emptyList()
574-
: values.stream().map(client::getEntitlement).filter(g -> g != null).collect(Collectors.toList());
588+
: values.stream().map(client::getEntitlement).filter(Objects::nonNull).collect(Collectors.toList());
575589
scimEntitlementRefs.forEach(e -> user.getEntitlements().add(new SCIMv2Entitlement.Builder().value(e.getId())
576590
.ref(configuration.getBaseAddress() + "Entitlements/" + e.getId()).display(e.getDisplayName())
577591
.primary(true).type(e.getType()).build()));
@@ -610,4 +624,16 @@ protected BaseResourceReference buildPatchValue(final SCIMv2User user) {
610624
}
611625
return builder.build();
612626
}
627+
628+
protected SCIMv2PatchOperation buildReplacePatchOperation(
629+
final String attrName,
630+
final List<Object> valuesToReplace,
631+
final SCIMBaseAttribute<?> attributeDefinition) {
632+
SCIMv2PatchOperation replacePatchOperation = new SCIMv2PatchOperation();
633+
replacePatchOperation.setPath(SCIMAttributeUtils.getBaseAttributeName(attrName));
634+
replacePatchOperation.setOperation(SCIMAttributeUtils.SCIM_REPLACE);
635+
replacePatchOperation.setValue(
636+
buildPatchValue(attrName, valuesToReplace, attributeDefinition));
637+
return replacePatchOperation;
638+
}
613639
}

src/main/java/net/tirasa/connid/bundles/scim/v2/dto/SCIMv2User.java

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -49,11 +49,11 @@ public SCIMv2User() {
4949
}
5050

5151
@Override
52-
protected void handleRoles(final Object value) {
52+
protected void handleRole(final String type, final Object value) {
5353
handleSCIMComplexObject(
54-
SCIMAttributeUtils.SCIM_SCHEMA_TYPE_DEFAULT,
54+
type,
5555
this.roles,
56-
s -> s.setValue(String.class.cast(value)));
56+
s -> s.setValue((String) value));
5757
}
5858

5959
@Override
@@ -65,10 +65,10 @@ protected void handlex509Certificates(final Object value) {
6565
}
6666

6767
@Override
68-
protected void handleDefaultEntitlement(final Object value) {
68+
protected void handleEntitlement(final String type, final Object value) {
6969
handleSCIMv2Entitlement(this.entitlements, s -> {
7070
s.setValue(String.class.cast(value));
71-
s.setType(SCIMAttributeUtils.SCIM_SCHEMA_TYPE_DEFAULT);
71+
s.setType(type);
7272
});
7373
}
7474

src/test/java/net/tirasa/connid/bundles/scim/v2/SCIMv2ConnectorTests.java

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -223,6 +223,7 @@ private static Uid createUser(final UUID uid, final String... groups) {
223223
SCIMv2ConnectorTestsUtils.VALUE_PHONE_NUMBER));
224224
userAttrs.add(AttributeBuilder.build(SCIMv2ConnectorTestsUtils.USER_ATTRIBUTE_PHONE_OTHER_PRIMARY, false));
225225
userAttrs.add(AttributeBuilder.build(SCIMAttributeUtils.USER_ATTRIBUTE_ACTIVE, true));
226+
userAttrs.add(AttributeBuilder.build(SCIMAttributeUtils.SCIM_USER_ROLES + ".default.value", "mytestrole"));
226227
userAttrs.add(password);
227228

228229
if (PROPS.containsKey("auth.defaultEntitlement") && StringUtil.isNotBlank(
@@ -331,6 +332,7 @@ private static Set<Attribute> updateUserAttributes(final Uid created, final Stri
331332
userAttrs.add(AttributeBuilder.build(SCIMv2ConnectorTestsUtils.USER_ATTRIBUTE_ENTITLEMENTS_DEFAULT_VALUE,
332333
PROPS.getProperty("auth.defaultEntitlement")));
333334
}
335+
userAttrs.add(AttributeBuilder.build(SCIMAttributeUtils.SCIM_USER_ROLES + ".default.value", "mytestrole"));
334336

335337
// custom attributes
336338
addCustomAttributes(userAttrs);
@@ -481,6 +483,8 @@ private static SCIMv2User readUser(final String id, final SCIMv2Client client)
481483
SCIMv2ConnectorTestsUtils.USER_ATTRIBUTE_EMAIL_WORK_VALUE));
482484
assertTrue(SCIMv2ConnectorTestsUtils.hasAttribute(toAttributes, SCIMAttributeUtils.SCIM_USER_SCHEMAS));
483485
assertTrue(SCIMv2ConnectorTestsUtils.hasAttribute(toAttributes, SCIMAttributeUtils.USER_ATTRIBUTE_ACTIVE));
486+
assertTrue(SCIMv2ConnectorTestsUtils.hasAttribute(toAttributes,
487+
SCIMAttributeUtils.SCIM_USER_ROLES + ".default.value", "mytestrole"));
484488
if (PROPS.containsKey("auth.defaultEntitlement") && StringUtil.isNotBlank(
485489
PROPS.getProperty("auth.defaultEntitlement"))) {
486490
assertTrue(SCIMv2ConnectorTestsUtils.containsAttribute(toAttributes,
@@ -1044,8 +1048,8 @@ public void search() {
10441048

10451049
SearchResult result = FACADE.search(ObjectClass.ACCOUNT, null, handler,
10461050
new OperationOptionsBuilder().setAttributesToGet("name", "emails.work.value", "name.familyName",
1047-
"displayName", "active", "entitlements.default.value", "entitlements",
1048-
"urn:mem:params:scim:schemas:extension:LuckyNumberExtension:luckyNumber",
1051+
"displayName", "active", "entitlements.default.value", "roles.default.value", "roles",
1052+
"entitlements", "urn:mem:params:scim:schemas:extension:LuckyNumberExtension:luckyNumber",
10491053
SCIMv2EnterpriseUser.SCHEMA_URI + ":employeeNumber",
10501054
SCIMv2EnterpriseUser.SCHEMA_URI + ":manager.value").build());
10511055
assertNotNull(result);

src/test/java/net/tirasa/connid/bundles/scim/v2/SCIMv2ConnectorTestsUtils.java

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
*/
1616
package net.tirasa.connid.bundles.scim.v2;
1717

18+
import java.util.List;
1819
import java.util.Map;
1920
import java.util.Set;
2021
import net.tirasa.connid.bundles.scim.common.SCIMConnectorConfiguration;
@@ -156,10 +157,11 @@ public static boolean isConfigurationValid(final SCIMConnectorConfiguration conn
156157
return true;
157158
}
158159

159-
public static boolean hasAttribute(final Set<Attribute> attrs, final String name) {
160+
public static boolean hasAttribute(final Set<Attribute> attrs, final String name, final Object... values) {
160161
for (Attribute attr : attrs) {
161162
if (attr.getName().equals(name)) {
162-
return true;
163+
return values.length == 0 || (attr.getValue() != null && !attr.getValue().isEmpty() && attr.getValue()
164+
.containsAll(List.of(values)));
163165
}
164166
}
165167
return false;

0 commit comments

Comments
 (0)