You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@syncope.apache.org by il...@apache.org on 2023/04/07 13:09:56 UTC
[syncope] branch 3_0_X updated: [SYNCOPE-1748] Implementing RFC 7644 section 3.5.2. Modifying with PATCH (#436)
This is an automated email from the ASF dual-hosted git repository.
ilgrosso pushed a commit to branch 3_0_X
in repository https://gitbox.apache.org/repos/asf/syncope.git
The following commit(s) were added to refs/heads/3_0_X by this push:
new b50f4a25b1 [SYNCOPE-1748] Implementing RFC 7644 section 3.5.2. Modifying with PATCH (#436)
b50f4a25b1 is described below
commit b50f4a25b1860967fa360717e008defb12429a31
Author: Francesco Chicchiriccò <il...@users.noreply.github.com>
AuthorDate: Fri Apr 7 15:09:50 2023 +0200
[SYNCOPE-1748] Implementing RFC 7644 section 3.5.2. Modifying with PATCH (#436)
---
.../java/data/AbstractAnyDataBinder.java | 3 +-
.../panels/SCIMConfEnterpriseUserPanel.java | 191 ++---
.../console/panels/SCIMConfGeneralPanel.java | 77 +-
.../client/console/panels/SCIMConfGroupPanel.java | 69 ++
.../client/console/panels/SCIMConfPanel.java | 16 +
.../client/console/panels/SCIMConfTabPanel.java | 33 +-
.../client/console/panels/SCIMConfUserPanel.java | 630 ++++++++-------
.../client/console/panels/SCIMConfGroupPanel.html | 23 +
.../client/console/panels/SCIMConfPanel.properties | 1 +
.../console/panels/SCIMConfPanel_it.properties | 1 +
.../console/panels/SCIMConfPanel_pt_BR.properties | 1 +
.../console/panels/SCIMConfPanel_ru.properties | 1 +
.../client/console/panels/SCIMConfUserPanel.html | 1 +
.../syncope/common/lib/scim/SCIMComplexConf.java | 1 -
.../apache/syncope/common/lib/scim/SCIMConf.java | 9 +
.../common/lib/scim/SCIMEnterpriseUserConf.java | 1 -
.../syncope/common/lib/scim/SCIMGeneralConf.java | 1 -
.../syncope/common/lib/scim/SCIMGroupConf.java} | 45 +-
.../syncope/common/lib/scim/SCIMUserConf.java | 15 +-
.../apache/syncope/core/logic/SCIMDataBinder.java | 874 +++++++++++++++------
.../org/apache/syncope/core/logic/SCIMLogic.java | 5 +-
.../syncope/core/logic/scim/SearchCondVisitor.java | 65 +-
ext/scimv2/scim-rest-api/pom.xml | 10 +-
.../ext/scimv2/api/BadRequestException.java | 1 -
.../ext/scimv2/api/data/AuthenticationScheme.java | 1 -
.../scimv2/api/data/BulkConfigurationOption.java | 1 -
.../ext/scimv2/api/data/ConfigurationOption.java | 1 -
.../scimv2/api/data/FilterConfigurationOption.java | 1 -
.../apache/syncope/ext/scimv2/api/data/Group.java | 1 -
.../syncope/ext/scimv2/api/data/ListResponse.java | 1 -
.../apache/syncope/ext/scimv2/api/data/Member.java | 1 -
.../syncope/ext/scimv2/api/data/Reference.java | 1 -
.../syncope/ext/scimv2/api/data/ResourceType.java | 1 -
.../ext/scimv2/api/data/SCIMComplexValue.java | 2 +-
.../syncope/ext/scimv2/api/data/SCIMError.java | 1 -
.../syncope/ext/scimv2/api/data/SCIMGroup.java | 13 +-
.../api/data/{Reference.java => SCIMPatchOp.java} | 27 +-
.../ext/scimv2/api/data/SCIMPatchOperation.java} | 51 +-
.../api/data/SCIMPatchOperationDeserializer.java | 227 ++++++
.../{SchemaExtension.java => SCIMPatchPath.java} | 47 +-
.../syncope/ext/scimv2/api/data/SCIMResource.java | 11 +-
.../ext/scimv2/api/data/SCIMSearchRequest.java | 1 -
.../syncope/ext/scimv2/api/data/SCIMUser.java | 24 +-
.../ext/scimv2/api/data/SchemaExtension.java | 1 -
.../ext/scimv2/api/data/ServiceProviderConfig.java | 1 -
.../apache/syncope/ext/scimv2/api/data/Value.java | 1 -
.../ext/scimv2/api/service/GroupService.java | 58 --
.../Value.java => service/SCIMGroupService.java} | 23 +-
.../{ReadService.java => SCIMResourceService.java} | 29 +-
.../Value.java => service/SCIMUserService.java} | 23 +-
.../ext/scimv2/api/service/UserService.java | 59 --
.../api/{data/Value.java => type/PatchOp.java} | 23 +-
.../syncope/ext/scimv2/api/type/Resource.java | 1 +
.../data/SCIMPatchOperationDeserializerTest.java | 349 ++++++++
.../ext/scimv2/cxf/SCIMExceptionMapper.java | 28 +-
.../ext/scimv2/cxf/SCIMv2RESTCXFContext.java | 16 +-
...stractService.java => AbstractSCIMService.java} | 10 +-
...pServiceImpl.java => SCIMGroupServiceImpl.java} | 144 ++--
.../ext/scimv2/cxf/service/SCIMServiceImpl.java | 2 +-
...erServiceImpl.java => SCIMUserServiceImpl.java} | 45 +-
.../org/apache/syncope/fit/core/SCIMITCase.java | 275 ++++++-
pom.xml | 4 +-
62 files changed, 2392 insertions(+), 1187 deletions(-)
diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/AbstractAnyDataBinder.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/AbstractAnyDataBinder.java
index b237bf43a9..98ac929871 100644
--- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/AbstractAnyDataBinder.java
+++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/AbstractAnyDataBinder.java
@@ -463,8 +463,7 @@ abstract class AbstractAnyDataBinder {
SyncopeClientException invalidValues = SyncopeClientException.build(ClientExceptionType.InvalidValues);
// 3. plain attributes
- anyUR.getPlainAttrs().stream().
- filter(patch -> patch.getAttr() != null).forEach(patch -> {
+ anyUR.getPlainAttrs().stream().filter(patch -> patch.getAttr() != null).forEach(patch -> {
PlainSchema schema = getPlainSchema(patch.getAttr().getSchema());
if (schema == null) {
LOG.debug("Invalid " + PlainSchema.class.getSimpleName() + " {}, ignoring...",
diff --git a/ext/scimv2/client-console/src/main/java/org/apache/syncope/client/console/panels/SCIMConfEnterpriseUserPanel.java b/ext/scimv2/client-console/src/main/java/org/apache/syncope/client/console/panels/SCIMConfEnterpriseUserPanel.java
index 0df39fabf8..2430c8cab5 100644
--- a/ext/scimv2/client-console/src/main/java/org/apache/syncope/client/console/panels/SCIMConfEnterpriseUserPanel.java
+++ b/ext/scimv2/client-console/src/main/java/org/apache/syncope/client/console/panels/SCIMConfEnterpriseUserPanel.java
@@ -19,16 +19,13 @@
package org.apache.syncope.client.console.panels;
import java.util.ArrayList;
-import java.util.Collections;
import java.util.List;
import org.apache.syncope.client.ui.commons.markup.html.form.AjaxTextFieldPanel;
import org.apache.syncope.client.ui.commons.wicket.markup.html.bootstrap.tabs.Accordion;
import org.apache.syncope.common.lib.scim.SCIMConf;
import org.apache.syncope.common.lib.scim.SCIMEnterpriseUserConf;
import org.apache.syncope.common.lib.scim.SCIMManagerConf;
-import org.apache.wicket.event.IEvent;
import org.apache.wicket.extensions.markup.html.tabs.AbstractTab;
-import org.apache.wicket.extensions.markup.html.tabs.ITab;
import org.apache.wicket.markup.html.WebMarkupContainer;
import org.apache.wicket.markup.html.basic.Label;
import org.apache.wicket.model.Model;
@@ -57,98 +54,93 @@ public class SCIMConfEnterpriseUserPanel extends SCIMConfTabPanel {
AjaxTextFieldPanel costCenterPanel =
new AjaxTextFieldPanel("costCenter", "costCenter",
- new PropertyModel<>("costCenter", "costCenter") {
+ new PropertyModel<>("costCenter", "costCenter") {
- private static final long serialVersionUID = -6427731218492117883L;
+ private static final long serialVersionUID = -6427731218492117883L;
- @Override
- public String getObject() {
- return scimEnterpriseUserConf.getCostCenter();
- }
-
- @Override
- public void setObject(final String object) {
- scimEnterpriseUserConf.setCostCenter(object);
- }
+ @Override
+ public String getObject() {
+ return scimEnterpriseUserConf.getCostCenter();
+ }
- });
- costCenterPanel.setChoices(plainSchemaNames);
+ @Override
+ public void setObject(final String object) {
+ scimEnterpriseUserConf.setCostCenter(object);
+ }
+ });
+ costCenterPanel.setChoices(userPlainSchemas.getObject());
AjaxTextFieldPanel departmentPanel =
new AjaxTextFieldPanel("department", "department",
- new PropertyModel<>("department", "department") {
+ new PropertyModel<>("department", "department") {
- private static final long serialVersionUID = -6427731218492117883L;
+ private static final long serialVersionUID = -6427731218492117883L;
- @Override
- public String getObject() {
- return scimEnterpriseUserConf.getDepartment();
- }
-
- @Override
- public void setObject(final String object) {
- scimEnterpriseUserConf.setDepartment(object);
- }
+ @Override
+ public String getObject() {
+ return scimEnterpriseUserConf.getDepartment();
+ }
- });
- departmentPanel.setChoices(plainSchemaNames);
+ @Override
+ public void setObject(final String object) {
+ scimEnterpriseUserConf.setDepartment(object);
+ }
+ });
+ departmentPanel.setChoices(userPlainSchemas.getObject());
AjaxTextFieldPanel divisionPanel =
new AjaxTextFieldPanel("division", "division",
- new PropertyModel<>("division", "division") {
+ new PropertyModel<>("division", "division") {
- private static final long serialVersionUID = -6427731218492117883L;
+ private static final long serialVersionUID = -6427731218492117883L;
- @Override
- public String getObject() {
- return scimEnterpriseUserConf.getDivision();
- }
-
- @Override
- public void setObject(final String object) {
- scimEnterpriseUserConf.setDivision(object);
- }
+ @Override
+ public String getObject() {
+ return scimEnterpriseUserConf.getDivision();
+ }
- });
- divisionPanel.setChoices(plainSchemaNames);
+ @Override
+ public void setObject(final String object) {
+ scimEnterpriseUserConf.setDivision(object);
+ }
+ });
+ divisionPanel.setChoices(userPlainSchemas.getObject());
AjaxTextFieldPanel employeeNumberPanel =
new AjaxTextFieldPanel("employeeNumber", "employeeNumber",
- new PropertyModel<>("employeeNumber", "employeeNumber") {
+ new PropertyModel<>("employeeNumber", "employeeNumber") {
- private static final long serialVersionUID = -6427731218492117883L;
+ private static final long serialVersionUID = -6427731218492117883L;
- @Override
- public String getObject() {
- return scimEnterpriseUserConf.getEmployeeNumber();
- }
-
- @Override
- public void setObject(final String object) {
- scimEnterpriseUserConf.setEmployeeNumber(object);
- }
+ @Override
+ public String getObject() {
+ return scimEnterpriseUserConf.getEmployeeNumber();
+ }
- });
- employeeNumberPanel.setChoices(plainSchemaNames);
+ @Override
+ public void setObject(final String object) {
+ scimEnterpriseUserConf.setEmployeeNumber(object);
+ }
+ });
+ employeeNumberPanel.setChoices(userPlainSchemas.getObject());
AjaxTextFieldPanel organizationPanel =
new AjaxTextFieldPanel("organization", "organization",
- new PropertyModel<>("organization", "organization") {
+ new PropertyModel<>("organization", "organization") {
- private static final long serialVersionUID = -6427731218492117883L;
+ private static final long serialVersionUID = -6427731218492117883L;
- @Override
- public String getObject() {
- return scimEnterpriseUserConf.getOrganization();
- }
-
- @Override
- public void setObject(final String object) {
- scimEnterpriseUserConf.setOrganization(object);
- }
+ @Override
+ public String getObject() {
+ return scimEnterpriseUserConf.getOrganization();
+ }
- });
- organizationPanel.setChoices(plainSchemaNames);
+ @Override
+ public void setObject(final String object) {
+ scimEnterpriseUserConf.setOrganization(object);
+ }
+ });
+ organizationPanel.setChoices(userPlainSchemas.getObject());
// manager
buildManagerAccordion();
@@ -160,14 +152,9 @@ public class SCIMConfEnterpriseUserPanel extends SCIMConfTabPanel {
add(organizationPanel);
}
- @Override
- public void onEvent(final IEvent<?> event) {
- super.onEvent(event);
- }
-
private void buildManagerAccordion() {
- final Accordion accordion = new Accordion("managerAccordion",
- Collections.<ITab>singletonList(new AbstractTab(Model.of("manager")) {
+ Accordion accordion = new Accordion("managerAccordion",
+ List.of(new AbstractTab(Model.of("manager")) {
private static final long serialVersionUID = -5861786415855103549L;
@@ -178,49 +165,46 @@ public class SCIMConfEnterpriseUserPanel extends SCIMConfTabPanel {
}), Model.of(-1)); // accordion closed at beginning
add(accordion.setOutputMarkupId(true));
-
}
private SCIMConfAccordionContainer buildNameAccordionContent(final String panelId) {
- final List<AjaxTextFieldPanel> panelList = new ArrayList<>();
+ List<AjaxTextFieldPanel> panelList = new ArrayList<>();
AjaxTextFieldPanel managerKeyPanel =
new AjaxTextFieldPanel("accordionContent", "manager.key",
- new PropertyModel<>(scimEnterpriseUserConf.getManager(), "accordionContent") {
+ new PropertyModel<>(scimEnterpriseUserConf.getManager(), "accordionContent") {
- private static final long serialVersionUID = -6427731218492117883L;
+ private static final long serialVersionUID = -6427731218492117883L;
- @Override
- public String getObject() {
- return scimEnterpriseUserConf.getManager().getKey();
- }
-
- @Override
- public void setObject(final String object) {
- scimEnterpriseUserConf.getManager().setKey(object);
- }
+ @Override
+ public String getObject() {
+ return scimEnterpriseUserConf.getManager().getKey();
+ }
- });
- managerKeyPanel.setChoices(plainSchemaNames);
+ @Override
+ public void setObject(final String object) {
+ scimEnterpriseUserConf.getManager().setKey(object);
+ }
+ });
+ managerKeyPanel.setChoices(userPlainSchemas.getObject());
AjaxTextFieldPanel managerDisplaNamePanel =
new AjaxTextFieldPanel("accordionContent", "manager.displayName",
- new PropertyModel<>(scimEnterpriseUserConf.getManager(), "accordionContent") {
+ new PropertyModel<>(scimEnterpriseUserConf.getManager(), "accordionContent") {
- private static final long serialVersionUID = -6427731218492117883L;
+ private static final long serialVersionUID = -6427731218492117883L;
- @Override
- public String getObject() {
- return scimEnterpriseUserConf.getManager().getDisplayName();
- }
-
- @Override
- public void setObject(final String object) {
- scimEnterpriseUserConf.getManager().setDisplayName(object);
- }
+ @Override
+ public String getObject() {
+ return scimEnterpriseUserConf.getManager().getDisplayName();
+ }
- });
- managerDisplaNamePanel.setChoices(plainSchemaNames);
+ @Override
+ public void setObject(final String object) {
+ scimEnterpriseUserConf.getManager().setDisplayName(object);
+ }
+ });
+ managerDisplaNamePanel.setChoices(userPlainSchemas.getObject());
panelList.add(managerKeyPanel);
panelList.add(managerDisplaNamePanel);
@@ -229,5 +213,4 @@ public class SCIMConfEnterpriseUserPanel extends SCIMConfTabPanel {
return new SCIMConfAccordionContainer(panelId, panelList);
}
-
}
diff --git a/ext/scimv2/client-console/src/main/java/org/apache/syncope/client/console/panels/SCIMConfGeneralPanel.java b/ext/scimv2/client-console/src/main/java/org/apache/syncope/client/console/panels/SCIMConfGeneralPanel.java
index 34e9f1bf9c..d368e69d5b 100644
--- a/ext/scimv2/client-console/src/main/java/org/apache/syncope/client/console/panels/SCIMConfGeneralPanel.java
+++ b/ext/scimv2/client-console/src/main/java/org/apache/syncope/client/console/panels/SCIMConfGeneralPanel.java
@@ -22,20 +22,17 @@ import java.util.Date;
import org.apache.commons.lang3.time.DateFormatUtils;
import org.apache.syncope.client.ui.commons.DateOps;
import org.apache.syncope.client.ui.commons.markup.html.form.AjaxDateTimeFieldPanel;
+import org.apache.syncope.client.ui.commons.markup.html.form.AjaxSpinnerFieldPanel;
import org.apache.syncope.client.ui.commons.markup.html.form.AjaxTextFieldPanel;
import org.apache.syncope.common.lib.scim.SCIMConf;
import org.apache.syncope.common.lib.scim.SCIMGeneralConf;
import org.apache.wicket.model.Model;
import org.apache.wicket.model.PropertyModel;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
public class SCIMConfGeneralPanel extends SCIMConfTabPanel {
private static final long serialVersionUID = 2765863608539154422L;
- private static final Logger LOG = LoggerFactory.getLogger(SCIMConfGeneralPanel.class);
-
public SCIMConfGeneralPanel(final String id, final SCIMConf scimConf) {
super(id);
@@ -75,71 +72,17 @@ public class SCIMConfGeneralPanel extends SCIMConfTabPanel {
}, DateFormatUtils.ISO_8601_EXTENDED_DATETIME_TIME_ZONE_FORMAT);
lastChangeDatePanel.setEnabled(false);
- AjaxTextFieldPanel bulkMaxOperationsPanel =
- new AjaxTextFieldPanel("bulkMaxOperations", "bulkMaxOperations",
- new PropertyModel<>("bulkMaxOperations", "bulkMaxOperations") {
-
- private static final long serialVersionUID = -6427731218492117883L;
-
- @Override
- public String getObject() {
- return String.valueOf(scimGeneralConf.getBulkMaxOperations());
- }
-
- @Override
- public void setObject(final String object) {
- try {
- scimGeneralConf.setBulkMaxOperations(Integer.parseInt(object));
- } catch (NumberFormatException e) {
- LOG.error("Invalid value provided for 'bulkMaxOperations': {}", object, e);
- }
- }
- });
- bulkMaxOperationsPanel.setChoices(plainSchemaNames);
-
- AjaxTextFieldPanel bulkMaxMaxPayloadSizePanel =
- new AjaxTextFieldPanel("bulkMaxMaxPayloadSize", "bulkMaxMaxPayloadSize",
- new PropertyModel<>("bulkMaxMaxPayloadSize", "bulkMaxMaxPayloadSize") {
-
- private static final long serialVersionUID = -6427731218492117883L;
-
- @Override
- public String getObject() {
- return String.valueOf(scimGeneralConf.getBulkMaxPayloadSize());
- }
-
- @Override
- public void setObject(final String object) {
- try {
- scimGeneralConf.setBulkMaxPayloadSize(Integer.parseInt(object));
- } catch (NumberFormatException e) {
- LOG.error("Invalid value provided for 'bulkMaxPayloadSize': {}", object, e);
- }
- }
- });
- bulkMaxMaxPayloadSizePanel.setChoices(plainSchemaNames);
-
- AjaxTextFieldPanel filterMaxResultsPanel =
- new AjaxTextFieldPanel("filterMaxResults", "filterMaxResults",
- new PropertyModel<>("filterMaxResults", "filterMaxResults") {
+ AjaxSpinnerFieldPanel<Integer> bulkMaxOperationsPanel = new AjaxSpinnerFieldPanel.Builder<Integer>().
+ build("bulkMaxOperations", "bulkMaxOperations", Integer.class,
+ new PropertyModel<>(scimGeneralConf, "bulkMaxOperations"));
- private static final long serialVersionUID = -6427731218492117883L;
+ AjaxSpinnerFieldPanel<Integer> bulkMaxMaxPayloadSizePanel = new AjaxSpinnerFieldPanel.Builder<Integer>().
+ build("bulkMaxMaxPayloadSize", "bulkMaxMaxPayloadSize", Integer.class,
+ new PropertyModel<>(scimGeneralConf, "bulkMaxMaxPayloadSize"));
- @Override
- public String getObject() {
- return String.valueOf(scimGeneralConf.getFilterMaxResults());
- }
-
- @Override
- public void setObject(final String object) {
- try {
- scimGeneralConf.setFilterMaxResults(Integer.parseInt(object));
- } catch (NumberFormatException e) {
- LOG.error("Invalid value provided for 'filterMaxResults': {}", object, e);
- }
- }
- });
- filterMaxResultsPanel.setChoices(plainSchemaNames);
+ AjaxSpinnerFieldPanel<Integer> filterMaxResultsPanel = new AjaxSpinnerFieldPanel.Builder<Integer>().
+ build("filterMaxResults", "filterMaxResults", Integer.class,
+ new PropertyModel<>(scimGeneralConf, "filterMaxResults"));
AjaxTextFieldPanel eTagValuePanel = new AjaxTextFieldPanel("eTagValue", "eTagValue",
new PropertyModel<>("eTagValue", "eTagValue") {
diff --git a/ext/scimv2/client-console/src/main/java/org/apache/syncope/client/console/panels/SCIMConfGroupPanel.java b/ext/scimv2/client-console/src/main/java/org/apache/syncope/client/console/panels/SCIMConfGroupPanel.java
new file mode 100644
index 0000000000..95b4d97ee6
--- /dev/null
+++ b/ext/scimv2/client-console/src/main/java/org/apache/syncope/client/console/panels/SCIMConfGroupPanel.java
@@ -0,0 +1,69 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.syncope.client.console.panels;
+
+import org.apache.syncope.client.ui.commons.markup.html.form.AjaxTextFieldPanel;
+import org.apache.syncope.common.lib.scim.SCIMConf;
+import org.apache.syncope.common.lib.scim.SCIMGroupConf;
+import org.apache.wicket.extensions.ajax.markup.html.autocomplete.AutoCompleteSettings;
+import org.apache.wicket.model.PropertyModel;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class SCIMConfGroupPanel extends SCIMConfTabPanel {
+
+ protected static final Logger LOG = LoggerFactory.getLogger(SCIMConfGroupPanel.class);
+
+ private static final long serialVersionUID = 8747864142447220523L;
+
+ private final SCIMGroupConf scimGroupConf;
+
+ public SCIMConfGroupPanel(final String id, final SCIMConf scimConf) {
+ super(id);
+
+ if (scimConf.getGroupConf() == null) {
+ scimConf.setGroupConf(new SCIMGroupConf());
+ }
+ scimGroupConf = scimConf.getGroupConf();
+
+ final AutoCompleteSettings settings = new AutoCompleteSettings();
+ settings.setShowCompleteListOnFocusGain(true);
+ settings.setShowListOnEmptyInput(true);
+ settings.setCssClassName("custom-autocomplete-box");
+
+ AjaxTextFieldPanel externalIdPanel = new AjaxTextFieldPanel("externalId", "externalId",
+ new PropertyModel<>("externalId", "externalId") {
+
+ private static final long serialVersionUID = -6427731218492117883L;
+
+ @Override
+ public String getObject() {
+ return scimGroupConf.getExternalId();
+ }
+
+ @Override
+ public void setObject(final String object) {
+ scimGroupConf.setExternalId(object);
+ }
+ });
+ externalIdPanel.setChoices(groupPlainSchemas.getObject());
+
+ add(externalIdPanel);
+ }
+}
diff --git a/ext/scimv2/client-console/src/main/java/org/apache/syncope/client/console/panels/SCIMConfPanel.java b/ext/scimv2/client-console/src/main/java/org/apache/syncope/client/console/panels/SCIMConfPanel.java
index ddc86bb26d..1ca2796d72 100644
--- a/ext/scimv2/client-console/src/main/java/org/apache/syncope/client/console/panels/SCIMConfPanel.java
+++ b/ext/scimv2/client-console/src/main/java/org/apache/syncope/client/console/panels/SCIMConfPanel.java
@@ -128,6 +128,22 @@ public abstract class SCIMConfPanel extends WizardMgtPanel<SCIMConf> {
}
});
+ tabs.add(new ITabComponent(
+ new Model<>(getString("tab4")), getString("tab4")) {
+
+ private static final long serialVersionUID = 1998052474181916792L;
+
+ @Override
+ public WebMarkupContainer getPanel(final String panelId) {
+ return new SCIMConfGroupPanel(panelId, scimConfTO);
+ }
+
+ @Override
+ public boolean isVisible() {
+ return true;
+ }
+ });
+
return tabs;
}
diff --git a/ext/scimv2/client-console/src/main/java/org/apache/syncope/client/console/panels/SCIMConfTabPanel.java b/ext/scimv2/client-console/src/main/java/org/apache/syncope/client/console/panels/SCIMConfTabPanel.java
index 6ac28e1cff..0c3be86032 100644
--- a/ext/scimv2/client-console/src/main/java/org/apache/syncope/client/console/panels/SCIMConfTabPanel.java
+++ b/ext/scimv2/client-console/src/main/java/org/apache/syncope/client/console/panels/SCIMConfTabPanel.java
@@ -26,21 +26,38 @@ import org.apache.syncope.common.lib.to.SchemaTO;
import org.apache.syncope.common.lib.types.AnyTypeKind;
import org.apache.syncope.common.lib.types.SchemaType;
import org.apache.wicket.markup.html.panel.Panel;
+import org.apache.wicket.model.LoadableDetachableModel;
public class SCIMConfTabPanel extends Panel implements ModalPanel {
private static final long serialVersionUID = -4482885585790492795L;
- protected final List<String> plainSchemaNames = getPlainSchemas();
+ protected final LoadableDetachableModel<List<String>> userPlainSchemas = new LoadableDetachableModel<>() {
+
+ private static final long serialVersionUID = 4659376149825914247L;
+
+ @Override
+ protected List<String> load() {
+ return SchemaRestClient.getSchemas(SchemaType.PLAIN, AnyTypeKind.USER).stream().
+ map(SchemaTO::getKey).
+ filter(name -> !"password".equals(name)).
+ sorted().collect(Collectors.toList());
+ }
+ };
+
+ protected final LoadableDetachableModel<List<String>> groupPlainSchemas = new LoadableDetachableModel<>() {
+
+ private static final long serialVersionUID = 4659376149825914247L;
+
+ @Override
+ protected List<String> load() {
+ return SchemaRestClient.getSchemas(SchemaType.PLAIN, AnyTypeKind.GROUP).stream().
+ map(SchemaTO::getKey).
+ sorted().collect(Collectors.toList());
+ }
+ };
public SCIMConfTabPanel(final String id) {
super(id);
}
-
- private static List<String> getPlainSchemas() {
- return SchemaRestClient.getSchemas(SchemaType.PLAIN, AnyTypeKind.USER).stream().
- map(SchemaTO::getKey).
- filter(name -> !"password".equals(name)).
- sorted().collect(Collectors.toList());
- }
}
diff --git a/ext/scimv2/client-console/src/main/java/org/apache/syncope/client/console/panels/SCIMConfUserPanel.java b/ext/scimv2/client-console/src/main/java/org/apache/syncope/client/console/panels/SCIMConfUserPanel.java
index 44060b8699..938bec2684 100644
--- a/ext/scimv2/client-console/src/main/java/org/apache/syncope/client/console/panels/SCIMConfUserPanel.java
+++ b/ext/scimv2/client-console/src/main/java/org/apache/syncope/client/console/panels/SCIMConfUserPanel.java
@@ -19,7 +19,6 @@
package org.apache.syncope.client.console.panels;
import java.util.ArrayList;
-import java.util.Collections;
import java.util.List;
import org.apache.syncope.client.console.wicket.markup.html.form.MultiFieldPanel;
import org.apache.syncope.client.ui.commons.markup.html.form.AjaxTextFieldPanel;
@@ -34,10 +33,8 @@ import org.apache.syncope.common.lib.scim.types.EmailCanonicalType;
import org.apache.syncope.common.lib.scim.types.IMCanonicalType;
import org.apache.syncope.common.lib.scim.types.PhoneNumberCanonicalType;
import org.apache.syncope.common.lib.scim.types.PhotoCanonicalType;
-import org.apache.wicket.event.IEvent;
import org.apache.wicket.extensions.ajax.markup.html.autocomplete.AutoCompleteSettings;
import org.apache.wicket.extensions.markup.html.tabs.AbstractTab;
-import org.apache.wicket.extensions.markup.html.tabs.ITab;
import org.apache.wicket.markup.html.WebMarkupContainer;
import org.apache.wicket.markup.html.basic.Label;
import org.apache.wicket.model.Model;
@@ -70,149 +67,158 @@ public class SCIMConfUserPanel extends SCIMConfTabPanel {
settings.setShowListOnEmptyInput(true);
settings.setCssClassName("custom-autocomplete-box");
- AjaxTextFieldPanel displayNamePanel = new AjaxTextFieldPanel("displayName", "displayName",
- new PropertyModel<>("displayName", "displayName") {
+ AjaxTextFieldPanel externalIdPanel = new AjaxTextFieldPanel("externalId", "externalId",
+ new PropertyModel<>("externalId", "externalId") {
- private static final long serialVersionUID = -6427731218492117883L;
+ private static final long serialVersionUID = -6427731218492117883L;
- @Override
- public String getObject() {
- return scimUserConf.getDisplayName();
- }
+ @Override
+ public String getObject() {
+ return scimUserConf.getExternalId();
+ }
- @Override
- public void setObject(final String object) {
- scimUserConf.setDisplayName(object);
- }
+ @Override
+ public void setObject(final String object) {
+ scimUserConf.setExternalId(object);
+ }
+ });
+ externalIdPanel.setChoices(userPlainSchemas.getObject());
- });
- displayNamePanel.setChoices(plainSchemaNames);
+ AjaxTextFieldPanel displayNamePanel = new AjaxTextFieldPanel("displayName", "displayName",
+ new PropertyModel<>("displayName", "displayName") {
- AjaxTextFieldPanel localePanel = new AjaxTextFieldPanel("locale", "locale",
- new PropertyModel<>("locale", "locale") {
+ private static final long serialVersionUID = -6427731218492117883L;
- private static final long serialVersionUID = -6427731218492117883L;
+ @Override
+ public String getObject() {
+ return scimUserConf.getDisplayName();
+ }
- @Override
- public String getObject() {
- return scimUserConf.getLocale();
- }
+ @Override
+ public void setObject(final String object) {
+ scimUserConf.setDisplayName(object);
+ }
+ });
+ displayNamePanel.setChoices(userPlainSchemas.getObject());
- @Override
- public void setObject(final String object) {
- scimUserConf.setLocale(object);
- }
+ AjaxTextFieldPanel localePanel = new AjaxTextFieldPanel("locale", "locale",
+ new PropertyModel<>("locale", "locale") {
- });
- localePanel.setChoices(plainSchemaNames);
+ private static final long serialVersionUID = -6427731218492117883L;
- AjaxTextFieldPanel nickNamePanel = new AjaxTextFieldPanel("nickName", "nickName",
- new PropertyModel<>("nickName", "nickName") {
+ @Override
+ public String getObject() {
+ return scimUserConf.getLocale();
+ }
- private static final long serialVersionUID = -6427731218492117883L;
+ @Override
+ public void setObject(final String object) {
+ scimUserConf.setLocale(object);
+ }
+ });
+ localePanel.setChoices(userPlainSchemas.getObject());
- @Override
- public String getObject() {
- return scimUserConf.getNickName();
- }
+ AjaxTextFieldPanel nickNamePanel = new AjaxTextFieldPanel("nickName", "nickName",
+ new PropertyModel<>("nickName", "nickName") {
- @Override
- public void setObject(final String object) {
- scimUserConf.setNickName(object);
- }
+ private static final long serialVersionUID = -6427731218492117883L;
- });
- nickNamePanel.setChoices(plainSchemaNames);
+ @Override
+ public String getObject() {
+ return scimUserConf.getNickName();
+ }
- AjaxTextFieldPanel preferredLanguagePanel = new AjaxTextFieldPanel("preferredLanguage", "preferredLanguage",
- new PropertyModel<>("preferredLanguage", "preferredLanguage") {
+ @Override
+ public void setObject(final String object) {
+ scimUserConf.setNickName(object);
+ }
+ });
+ nickNamePanel.setChoices(userPlainSchemas.getObject());
- private static final long serialVersionUID = -6427731218492117883L;
+ AjaxTextFieldPanel preferredLanguagePanel = new AjaxTextFieldPanel("preferredLanguage", "preferredLanguage",
+ new PropertyModel<>("preferredLanguage", "preferredLanguage") {
- @Override
- public String getObject() {
- return scimUserConf.getPreferredLanguage();
- }
+ private static final long serialVersionUID = -6427731218492117883L;
- @Override
- public void setObject(final String object) {
- scimUserConf.setPreferredLanguage(object);
- }
+ @Override
+ public String getObject() {
+ return scimUserConf.getPreferredLanguage();
+ }
- });
- preferredLanguagePanel.setChoices(plainSchemaNames);
+ @Override
+ public void setObject(final String object) {
+ scimUserConf.setPreferredLanguage(object);
+ }
+ });
+ preferredLanguagePanel.setChoices(userPlainSchemas.getObject());
AjaxTextFieldPanel profileUrlPanel = new AjaxTextFieldPanel("profileUrl", "profileUrl",
- new PropertyModel<>("profileUrl", "profileUrl") {
-
- private static final long serialVersionUID = -6427731218492117883L;
+ new PropertyModel<>("profileUrl", "profileUrl") {
- @Override
- public String getObject() {
- return scimUserConf.getProfileUrl();
- }
+ private static final long serialVersionUID = -6427731218492117883L;
- @Override
- public void setObject(final String object) {
- scimUserConf.setProfileUrl(object);
- }
+ @Override
+ public String getObject() {
+ return scimUserConf.getProfileUrl();
+ }
- });
- profileUrlPanel.setChoices(plainSchemaNames);
+ @Override
+ public void setObject(final String object) {
+ scimUserConf.setProfileUrl(object);
+ }
+ });
+ profileUrlPanel.setChoices(userPlainSchemas.getObject());
AjaxTextFieldPanel timezonePanel = new AjaxTextFieldPanel("timezone", "timezone",
- new PropertyModel<>("timezone", "timezone") {
+ new PropertyModel<>("timezone", "timezone") {
- private static final long serialVersionUID = -6427731218492117883L;
+ private static final long serialVersionUID = -6427731218492117883L;
- @Override
- public String getObject() {
- return scimUserConf.getTimezone();
- }
+ @Override
+ public String getObject() {
+ return scimUserConf.getTimezone();
+ }
- @Override
- public void setObject(final String object) {
- scimUserConf.setTimezone(object);
- }
-
- });
- timezonePanel.setChoices(plainSchemaNames);
+ @Override
+ public void setObject(final String object) {
+ scimUserConf.setTimezone(object);
+ }
+ });
+ timezonePanel.setChoices(userPlainSchemas.getObject());
AjaxTextFieldPanel titlePanel = new AjaxTextFieldPanel("title", "title",
- new PropertyModel<>("title", "title") {
-
- private static final long serialVersionUID = -6427731218492117883L;
+ new PropertyModel<>("title", "title") {
- @Override
- public String getObject() {
- return scimUserConf.getTitle();
- }
+ private static final long serialVersionUID = -6427731218492117883L;
- @Override
- public void setObject(final String object) {
- scimUserConf.setTitle(object);
- }
+ @Override
+ public String getObject() {
+ return scimUserConf.getTitle();
+ }
- });
- titlePanel.setChoices(plainSchemaNames);
+ @Override
+ public void setObject(final String object) {
+ scimUserConf.setTitle(object);
+ }
+ });
+ titlePanel.setChoices(userPlainSchemas.getObject());
AjaxTextFieldPanel userTypePanel = new AjaxTextFieldPanel("userType", "userType",
- new PropertyModel<>("userType", "userType") {
+ new PropertyModel<>("userType", "userType") {
- private static final long serialVersionUID = -6427731218492117883L;
+ private static final long serialVersionUID = -6427731218492117883L;
- @Override
- public String getObject() {
- return scimUserConf.getUserType();
- }
+ @Override
+ public String getObject() {
+ return scimUserConf.getUserType();
+ }
- @Override
- public void setObject(final String object) {
- scimUserConf.setUserType(object);
- }
-
- });
- userTypePanel.setChoices(plainSchemaNames);
+ @Override
+ public void setObject(final String object) {
+ scimUserConf.setUserType(object);
+ }
+ });
+ userTypePanel.setChoices(userPlainSchemas.getObject());
// name
buildNameAccordion();
@@ -220,7 +226,7 @@ public class SCIMConfUserPanel extends SCIMConfTabPanel {
// x509certificates
final AjaxTextFieldPanel x509CertificatesPanel = new AjaxTextFieldPanel("panel", "x509CertificatesPanel",
new Model<>(null));
- x509CertificatesPanel.setChoices(plainSchemaNames);
+ x509CertificatesPanel.setChoices(userPlainSchemas.getObject());
MultiFieldPanel<String> x509CertificatesMultiPanel = new MultiFieldPanel.Builder<>(
new ListModel<>(scimUserConf.getX509Certificates())).build(
"x509Certificates",
@@ -250,6 +256,7 @@ public class SCIMConfUserPanel extends SCIMConfTabPanel {
buildComplexPanels(scimUserConf.getIms(), "imsAccordion", "ims", IMCanonicalType.values());
buildComplexPanels(scimUserConf.getPhotos(), "photosAccordion", "photos", PhotoCanonicalType.values());
+ add(externalIdPanel);
add(displayNamePanel);
add(localePanel);
add(nickNamePanel);
@@ -267,7 +274,6 @@ public class SCIMConfUserPanel extends SCIMConfTabPanel {
add(new Label("phoneNumbersLabel", Model.of("phoneNumbers")));
add(new Label("imsLabel", Model.of("ims")));
add(new Label("photosLabel", Model.of("photos")));
-
}
private <T extends Enum<?>> void buildComplexPanels(
@@ -293,23 +299,22 @@ public class SCIMConfUserPanel extends SCIMConfTabPanel {
}
private void buildNameAccordion() {
- final Accordion accordion = new Accordion("nameAccordion",
- Collections.<ITab>singletonList(new AbstractTab(Model.of("name")) {
+ Accordion accordion = new Accordion("nameAccordion", List.of(new AbstractTab(Model.of("name")) {
- private static final long serialVersionUID = -5861786415855103549L;
+ private static final long serialVersionUID = -5861786415855103549L;
- @Override
- public WebMarkupContainer getPanel(final String panelId) {
- return buildNameAccordionContent(panelId);
- }
+ @Override
+ public WebMarkupContainer getPanel(final String panelId) {
+ return buildNameAccordionContent(panelId);
+ }
- }), Model.of(-1)); // accordion closed at beginning
+ }), Model.of(-1)); // accordion closed at beginning
add(accordion.setOutputMarkupId(true));
}
private void buildAddressAccordion(final SCIMUserAddressConf address, final AddressCanonicalType canonicalType) {
Accordion accordion = new Accordion("addressesAccordion_" + address.getType().name(),
- Collections.<ITab>singletonList(new AbstractTab(Model.of("address." + address.getType().name())) {
+ List.of(new AbstractTab(Model.of("address." + address.getType().name())) {
private static final long serialVersionUID = -5861786415855103549L;
@@ -317,7 +322,6 @@ public class SCIMConfUserPanel extends SCIMConfTabPanel {
public WebMarkupContainer getPanel(final String panelId) {
return buildAddressAccordionContent(address, canonicalType, panelId);
}
-
}), Model.of(-1)); // accordion closed at beginning
add(accordion.setOutputMarkupId(true));
}
@@ -329,7 +333,7 @@ public class SCIMConfUserPanel extends SCIMConfTabPanel {
final T canonicalType) {
Accordion accordion = new Accordion(basePanelId + '_' + complex.getType().name(),
- Collections.<ITab>singletonList(new AbstractTab(Model.of(baseTabId + '.' + complex.getType().name())) {
+ List.of(new AbstractTab(Model.of(baseTabId + '.' + complex.getType().name())) {
private static final long serialVersionUID = -5861786415855103549L;
@@ -343,115 +347,109 @@ public class SCIMConfUserPanel extends SCIMConfTabPanel {
}
private SCIMConfAccordionContainer buildNameAccordionContent(final String panelId) {
- final List<AjaxTextFieldPanel> panelList = new ArrayList<>();
+ List<AjaxTextFieldPanel> panelList = new ArrayList<>();
AjaxTextFieldPanel nameFamilyNamePanel = new AjaxTextFieldPanel("accordionContent", "name.familyName",
- new PropertyModel<>(scimUserConf.getName(), "accordionContent") {
+ new PropertyModel<>(scimUserConf.getName(), "accordionContent") {
- private static final long serialVersionUID = -6427731218492117883L;
+ private static final long serialVersionUID = -6427731218492117883L;
- @Override
- public String getObject() {
- return scimUserConf.getName().getFamilyName();
- }
+ @Override
+ public String getObject() {
+ return scimUserConf.getName().getFamilyName();
+ }
- @Override
- public void setObject(final String object) {
- scimUserConf.getName().setFamilyName(object);
- }
-
- });
- nameFamilyNamePanel.setChoices(plainSchemaNames);
+ @Override
+ public void setObject(final String object) {
+ scimUserConf.getName().setFamilyName(object);
+ }
+ });
+ nameFamilyNamePanel.setChoices(userPlainSchemas.getObject());
AjaxTextFieldPanel nameFormattedPanel = new AjaxTextFieldPanel("accordionContent", "name.formatted",
- new PropertyModel<>(scimUserConf.getName(), "accordionContent") {
-
- private static final long serialVersionUID = -6427731218492117883L;
+ new PropertyModel<>(scimUserConf.getName(), "accordionContent") {
- @Override
- public String getObject() {
- return scimUserConf.getName().getFormatted();
- }
+ private static final long serialVersionUID = -6427731218492117883L;
- @Override
- public void setObject(final String object) {
- scimUserConf.getName().setFormatted(object);
- }
+ @Override
+ public String getObject() {
+ return scimUserConf.getName().getFormatted();
+ }
- });
- nameFormattedPanel.setChoices(plainSchemaNames);
+ @Override
+ public void setObject(final String object) {
+ scimUserConf.getName().setFormatted(object);
+ }
+ });
+ nameFormattedPanel.setChoices(userPlainSchemas.getObject());
AjaxTextFieldPanel nameGivenNamePanel = new AjaxTextFieldPanel("accordionContent", "name.givenName",
- new PropertyModel<>(scimUserConf.getName(), "accordionContent") {
+ new PropertyModel<>(scimUserConf.getName(), "accordionContent") {
- private static final long serialVersionUID = -6427731218492117883L;
+ private static final long serialVersionUID = -6427731218492117883L;
- @Override
- public String getObject() {
- return scimUserConf.getName().getGivenName();
- }
+ @Override
+ public String getObject() {
+ return scimUserConf.getName().getGivenName();
+ }
- @Override
- public void setObject(final String object) {
- scimUserConf.getName().setGivenName(object);
- }
-
- });
- nameGivenNamePanel.setChoices(plainSchemaNames);
+ @Override
+ public void setObject(final String object) {
+ scimUserConf.getName().setGivenName(object);
+ }
+ });
+ nameGivenNamePanel.setChoices(userPlainSchemas.getObject());
AjaxTextFieldPanel nameHonorificPrefixPanel = new AjaxTextFieldPanel("accordionContent", "name.honorificPrefix",
- new PropertyModel<>(scimUserConf.getName(), "accordionContent") {
-
- private static final long serialVersionUID = -6427731218492117883L;
+ new PropertyModel<>(scimUserConf.getName(), "accordionContent") {
- @Override
- public String getObject() {
- return scimUserConf.getName().getHonorificPrefix();
- }
+ private static final long serialVersionUID = -6427731218492117883L;
- @Override
- public void setObject(final String object) {
- scimUserConf.getName().setHonorificPrefix(object);
- }
+ @Override
+ public String getObject() {
+ return scimUserConf.getName().getHonorificPrefix();
+ }
- });
- nameHonorificPrefixPanel.setChoices(plainSchemaNames);
+ @Override
+ public void setObject(final String object) {
+ scimUserConf.getName().setHonorificPrefix(object);
+ }
+ });
+ nameHonorificPrefixPanel.setChoices(userPlainSchemas.getObject());
AjaxTextFieldPanel nameHonorificSuffixPanel = new AjaxTextFieldPanel("accordionContent", "name.honorificSuffix",
- new PropertyModel<>(scimUserConf.getName(), "accordionContent") {
-
- private static final long serialVersionUID = -6427731218492117883L;
+ new PropertyModel<>(scimUserConf.getName(), "accordionContent") {
- @Override
- public String getObject() {
- return scimUserConf.getName().getHonorificSuffix();
- }
+ private static final long serialVersionUID = -6427731218492117883L;
- @Override
- public void setObject(final String object) {
- scimUserConf.getName().setHonorificSuffix(object);
- }
+ @Override
+ public String getObject() {
+ return scimUserConf.getName().getHonorificSuffix();
+ }
- });
- nameHonorificSuffixPanel.setChoices(plainSchemaNames);
+ @Override
+ public void setObject(final String object) {
+ scimUserConf.getName().setHonorificSuffix(object);
+ }
+ });
+ nameHonorificSuffixPanel.setChoices(userPlainSchemas.getObject());
AjaxTextFieldPanel nameMiddleNamePanel = new AjaxTextFieldPanel("accordionContent", "name.middleName",
- new PropertyModel<>(scimUserConf.getName(), "accordionContent") {
+ new PropertyModel<>(scimUserConf.getName(), "accordionContent") {
- private static final long serialVersionUID = -6427731218492117883L;
+ private static final long serialVersionUID = -6427731218492117883L;
- @Override
- public String getObject() {
- return scimUserConf.getName().getMiddleName();
- }
+ @Override
+ public String getObject() {
+ return scimUserConf.getName().getMiddleName();
+ }
- @Override
- public void setObject(final String object) {
- scimUserConf.getName().setMiddleName(object);
- }
-
- });
- nameMiddleNamePanel.setChoices(plainSchemaNames);
+ @Override
+ public void setObject(final String object) {
+ scimUserConf.getName().setMiddleName(object);
+ }
+ });
+ nameMiddleNamePanel.setChoices(userPlainSchemas.getObject());
panelList.add(nameFamilyNamePanel);
panelList.add(nameFormattedPanel);
@@ -468,61 +466,58 @@ public class SCIMConfUserPanel extends SCIMConfTabPanel {
final T canonicalType,
final String panelId) {
- final List<AjaxTextFieldPanel> panelList = new ArrayList<>();
- final String fieldName = panelId + '.' + canonicalType.name();
+ List<AjaxTextFieldPanel> panelList = new ArrayList<>();
+ String fieldName = panelId + '.' + canonicalType.name();
AjaxTextFieldPanel displayPanel = new AjaxTextFieldPanel("accordionContent", fieldName + ".display",
- new PropertyModel<>(complex, "accordionContent") {
-
- private static final long serialVersionUID = -6427731218492117883L;
+ new PropertyModel<>(complex, "accordionContent") {
- @Override
- public String getObject() {
- return complex.getDisplay();
- }
+ private static final long serialVersionUID = -6427731218492117883L;
- @Override
- public void setObject(final String object) {
- complex.setDisplay(object);
- }
+ @Override
+ public String getObject() {
+ return complex.getDisplay();
+ }
- });
- displayPanel.setChoices(plainSchemaNames);
+ @Override
+ public void setObject(final String object) {
+ complex.setDisplay(object);
+ }
+ });
+ displayPanel.setChoices(userPlainSchemas.getObject());
AjaxTextFieldPanel valuePanel = new AjaxTextFieldPanel("accordionContent", fieldName + ".value",
- new PropertyModel<>(complex, "accordionContent") {
+ new PropertyModel<>(complex, "accordionContent") {
- private static final long serialVersionUID = -6427731218492117883L;
+ private static final long serialVersionUID = -6427731218492117883L;
- @Override
- public String getObject() {
- return complex.getValue();
- }
+ @Override
+ public String getObject() {
+ return complex.getValue();
+ }
- @Override
- public void setObject(final String object) {
- complex.setValue(object);
- }
-
- });
- valuePanel.setChoices(plainSchemaNames);
+ @Override
+ public void setObject(final String object) {
+ complex.setValue(object);
+ }
+ });
+ valuePanel.setChoices(userPlainSchemas.getObject());
AjaxTextFieldPanel primaryPanel = new AjaxTextFieldPanel("accordionContent", fieldName + ".primary",
- new PropertyModel<>(complex, "accordionContent") {
-
- private static final long serialVersionUID = -6427731218492117883L;
+ new PropertyModel<>(complex, "accordionContent") {
- @Override
- public String getObject() {
- return String.valueOf(complex.isPrimary());
- }
+ private static final long serialVersionUID = -6427731218492117883L;
- @Override
- public void setObject(final String object) {
- complex.setPrimary(Boolean.valueOf(object));
- }
+ @Override
+ public String getObject() {
+ return String.valueOf(complex.isPrimary());
+ }
- });
+ @Override
+ public void setObject(final String object) {
+ complex.setPrimary(Boolean.parseBoolean(object));
+ }
+ });
primaryPanel.setChoices(List.of("true", "false"));
panelList.add(displayPanel);
@@ -537,116 +532,110 @@ public class SCIMConfUserPanel extends SCIMConfTabPanel {
final AddressCanonicalType canonicalType,
final String panelId) {
- final List<AjaxTextFieldPanel> panelList = new ArrayList<>();
- final String fieldName = "addresses." + canonicalType.name();
+ List<AjaxTextFieldPanel> panelList = new ArrayList<>();
+ String fieldName = "addresses." + canonicalType.name();
AjaxTextFieldPanel addressCountryPanel = new AjaxTextFieldPanel("accordionContent", fieldName + ".country",
- new PropertyModel<>(address, "accordionContent") {
-
- private static final long serialVersionUID = -6427731218492117883L;
+ new PropertyModel<>(address, "accordionContent") {
- @Override
- public String getObject() {
- return address.getCountry();
- }
+ private static final long serialVersionUID = -6427731218492117883L;
- @Override
- public void setObject(final String object) {
- address.setCountry(object);
- }
+ @Override
+ public String getObject() {
+ return address.getCountry();
+ }
- });
- addressCountryPanel.setChoices(plainSchemaNames);
+ @Override
+ public void setObject(final String object) {
+ address.setCountry(object);
+ }
+ });
+ addressCountryPanel.setChoices(userPlainSchemas.getObject());
AjaxTextFieldPanel addressFormattedPanel = new AjaxTextFieldPanel("accordionContent", fieldName + ".formatted",
- new PropertyModel<>(address, "accordionContent") {
+ new PropertyModel<>(address, "accordionContent") {
- private static final long serialVersionUID = -6427731218492117883L;
+ private static final long serialVersionUID = -6427731218492117883L;
- @Override
- public String getObject() {
- return address.getFormatted();
- }
+ @Override
+ public String getObject() {
+ return address.getFormatted();
+ }
- @Override
- public void setObject(final String object) {
- address.setFormatted(object);
- }
-
- });
- addressFormattedPanel.setChoices(plainSchemaNames);
+ @Override
+ public void setObject(final String object) {
+ address.setFormatted(object);
+ }
+ });
+ addressFormattedPanel.setChoices(userPlainSchemas.getObject());
AjaxTextFieldPanel addressLocalityPanel = new AjaxTextFieldPanel("accordionContent", fieldName + ".locality",
- new PropertyModel<>(address, "accordionContent") {
-
- private static final long serialVersionUID = -6427731218492117883L;
+ new PropertyModel<>(address, "accordionContent") {
- @Override
- public String getObject() {
- return address.getLocality();
- }
+ private static final long serialVersionUID = -6427731218492117883L;
- @Override
- public void setObject(final String object) {
- address.setLocality(object);
- }
+ @Override
+ public String getObject() {
+ return address.getLocality();
+ }
- });
- addressLocalityPanel.setChoices(plainSchemaNames);
+ @Override
+ public void setObject(final String object) {
+ address.setLocality(object);
+ }
+ });
+ addressLocalityPanel.setChoices(userPlainSchemas.getObject());
AjaxTextFieldPanel addressRegionPanel = new AjaxTextFieldPanel("accordionContent", fieldName + ".region",
- new PropertyModel<>(address, "accordionContent") {
+ new PropertyModel<>(address, "accordionContent") {
- private static final long serialVersionUID = -6427731218492117883L;
+ private static final long serialVersionUID = -6427731218492117883L;
- @Override
- public String getObject() {
- return address.getRegion();
- }
+ @Override
+ public String getObject() {
+ return address.getRegion();
+ }
- @Override
- public void setObject(final String object) {
- address.setRegion(object);
- }
-
- });
- addressRegionPanel.setChoices(plainSchemaNames);
+ @Override
+ public void setObject(final String object) {
+ address.setRegion(object);
+ }
+ });
+ addressRegionPanel.setChoices(userPlainSchemas.getObject());
AjaxTextFieldPanel addressPostalCodePanel =
new AjaxTextFieldPanel("accordionContent", fieldName + ".postalCode",
- new PropertyModel<>(address, "accordionContent") {
-
- private static final long serialVersionUID = -6427731218492117883L;
+ new PropertyModel<>(address, "accordionContent") {
- @Override
- public String getObject() {
- return address.getPostalCode();
- }
+ private static final long serialVersionUID = -6427731218492117883L;
- @Override
- public void setObject(final String object) {
- address.setPostalCode(object);
- }
+ @Override
+ public String getObject() {
+ return address.getPostalCode();
+ }
- });
- addressPostalCodePanel.setChoices(plainSchemaNames);
+ @Override
+ public void setObject(final String object) {
+ address.setPostalCode(object);
+ }
+ });
+ addressPostalCodePanel.setChoices(userPlainSchemas.getObject());
AjaxTextFieldPanel addressPrimaryPanel = new AjaxTextFieldPanel("accordionContent", fieldName + ".primary",
- new PropertyModel<>(address, "accordionContent") {
-
- private static final long serialVersionUID = -6427731218492117883L;
+ new PropertyModel<>(address, "accordionContent") {
- @Override
- public String getObject() {
- return String.valueOf(address.isPrimary());
- }
+ private static final long serialVersionUID = -6427731218492117883L;
- @Override
- public void setObject(final String object) {
- address.setPrimary(Boolean.valueOf(object));
- }
+ @Override
+ public String getObject() {
+ return String.valueOf(address.isPrimary());
+ }
- });
+ @Override
+ public void setObject(final String object) {
+ address.setPrimary(Boolean.parseBoolean(object));
+ }
+ });
addressPrimaryPanel.setChoices(List.of("true", "false"));
panelList.add(addressCountryPanel);
@@ -658,9 +647,4 @@ public class SCIMConfUserPanel extends SCIMConfTabPanel {
return new SCIMConfAccordionContainer(panelId, panelList);
}
-
- @Override
- public void onEvent(final IEvent<?> event) {
- super.onEvent(event);
- }
}
diff --git a/ext/scimv2/client-console/src/main/resources/org/apache/syncope/client/console/panels/SCIMConfGroupPanel.html b/ext/scimv2/client-console/src/main/resources/org/apache/syncope/client/console/panels/SCIMConfGroupPanel.html
new file mode 100644
index 0000000000..dd38ad806b
--- /dev/null
+++ b/ext/scimv2/client-console/src/main/resources/org/apache/syncope/client/console/panels/SCIMConfGroupPanel.html
@@ -0,0 +1,23 @@
+<!--
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements. See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership. The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied. See the License for the
+specific language governing permissions and limitations
+under the License.
+-->
+<html xmlns="http://www.w3.org/1999/xhtml" xmlns:wicket="http://wicket.apache.org">
+ <wicket:panel>
+ <div wicket:id="externalId"/>
+ </wicket:panel>
+</html>
diff --git a/ext/scimv2/client-console/src/main/resources/org/apache/syncope/client/console/panels/SCIMConfPanel.properties b/ext/scimv2/client-console/src/main/resources/org/apache/syncope/client/console/panels/SCIMConfPanel.properties
index 40063bde60..10d4781a8f 100644
--- a/ext/scimv2/client-console/src/main/resources/org/apache/syncope/client/console/panels/SCIMConfPanel.properties
+++ b/ext/scimv2/client-console/src/main/resources/org/apache/syncope/client/console/panels/SCIMConfPanel.properties
@@ -19,3 +19,4 @@ tab2=User
tab3=EnterpriseUser
saveButton=Save
+tab4=Group
diff --git a/ext/scimv2/client-console/src/main/resources/org/apache/syncope/client/console/panels/SCIMConfPanel_it.properties b/ext/scimv2/client-console/src/main/resources/org/apache/syncope/client/console/panels/SCIMConfPanel_it.properties
index 40063bde60..10d4781a8f 100644
--- a/ext/scimv2/client-console/src/main/resources/org/apache/syncope/client/console/panels/SCIMConfPanel_it.properties
+++ b/ext/scimv2/client-console/src/main/resources/org/apache/syncope/client/console/panels/SCIMConfPanel_it.properties
@@ -19,3 +19,4 @@ tab2=User
tab3=EnterpriseUser
saveButton=Save
+tab4=Group
diff --git a/ext/scimv2/client-console/src/main/resources/org/apache/syncope/client/console/panels/SCIMConfPanel_pt_BR.properties b/ext/scimv2/client-console/src/main/resources/org/apache/syncope/client/console/panels/SCIMConfPanel_pt_BR.properties
index 40063bde60..10d4781a8f 100644
--- a/ext/scimv2/client-console/src/main/resources/org/apache/syncope/client/console/panels/SCIMConfPanel_pt_BR.properties
+++ b/ext/scimv2/client-console/src/main/resources/org/apache/syncope/client/console/panels/SCIMConfPanel_pt_BR.properties
@@ -19,3 +19,4 @@ tab2=User
tab3=EnterpriseUser
saveButton=Save
+tab4=Group
diff --git a/ext/scimv2/client-console/src/main/resources/org/apache/syncope/client/console/panels/SCIMConfPanel_ru.properties b/ext/scimv2/client-console/src/main/resources/org/apache/syncope/client/console/panels/SCIMConfPanel_ru.properties
index 40063bde60..10d4781a8f 100644
--- a/ext/scimv2/client-console/src/main/resources/org/apache/syncope/client/console/panels/SCIMConfPanel_ru.properties
+++ b/ext/scimv2/client-console/src/main/resources/org/apache/syncope/client/console/panels/SCIMConfPanel_ru.properties
@@ -19,3 +19,4 @@ tab2=User
tab3=EnterpriseUser
saveButton=Save
+tab4=Group
diff --git a/ext/scimv2/client-console/src/main/resources/org/apache/syncope/client/console/panels/SCIMConfUserPanel.html b/ext/scimv2/client-console/src/main/resources/org/apache/syncope/client/console/panels/SCIMConfUserPanel.html
index 5208aca0fa..9f8d97059f 100644
--- a/ext/scimv2/client-console/src/main/resources/org/apache/syncope/client/console/panels/SCIMConfUserPanel.html
+++ b/ext/scimv2/client-console/src/main/resources/org/apache/syncope/client/console/panels/SCIMConfUserPanel.html
@@ -18,6 +18,7 @@ under the License.
-->
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:wicket="http://wicket.apache.org">
<wicket:panel>
+ <div wicket:id="externalId"/>
<div wicket:id="displayName"/>
<div wicket:id="locale"/>
<div wicket:id="nickName"/>
diff --git a/ext/scimv2/common-lib/src/main/java/org/apache/syncope/common/lib/scim/SCIMComplexConf.java b/ext/scimv2/common-lib/src/main/java/org/apache/syncope/common/lib/scim/SCIMComplexConf.java
index 84cb3faaca..bbdf0a4770 100644
--- a/ext/scimv2/common-lib/src/main/java/org/apache/syncope/common/lib/scim/SCIMComplexConf.java
+++ b/ext/scimv2/common-lib/src/main/java/org/apache/syncope/common/lib/scim/SCIMComplexConf.java
@@ -63,5 +63,4 @@ public class SCIMComplexConf<E extends Enum<?>> implements Serializable {
public void setPrimary(final boolean primary) {
this.primary = primary;
}
-
}
diff --git a/ext/scimv2/common-lib/src/main/java/org/apache/syncope/common/lib/scim/SCIMConf.java b/ext/scimv2/common-lib/src/main/java/org/apache/syncope/common/lib/scim/SCIMConf.java
index 1875ebaf6e..98fe8f2b4b 100644
--- a/ext/scimv2/common-lib/src/main/java/org/apache/syncope/common/lib/scim/SCIMConf.java
+++ b/ext/scimv2/common-lib/src/main/java/org/apache/syncope/common/lib/scim/SCIMConf.java
@@ -32,6 +32,8 @@ public class SCIMConf implements Serializable {
private SCIMEnterpriseUserConf enterpriseUserConf;
+ private SCIMGroupConf groupConf;
+
public SCIMGeneralConf getGeneralConf() {
return generalConf;
}
@@ -56,4 +58,11 @@ public class SCIMConf implements Serializable {
this.enterpriseUserConf = enterpriseUserConf;
}
+ public SCIMGroupConf getGroupConf() {
+ return groupConf;
+ }
+
+ public void setGroupConf(final SCIMGroupConf groupConf) {
+ this.groupConf = groupConf;
+ }
}
diff --git a/ext/scimv2/common-lib/src/main/java/org/apache/syncope/common/lib/scim/SCIMEnterpriseUserConf.java b/ext/scimv2/common-lib/src/main/java/org/apache/syncope/common/lib/scim/SCIMEnterpriseUserConf.java
index e3c1441cff..d3c9affe6c 100644
--- a/ext/scimv2/common-lib/src/main/java/org/apache/syncope/common/lib/scim/SCIMEnterpriseUserConf.java
+++ b/ext/scimv2/common-lib/src/main/java/org/apache/syncope/common/lib/scim/SCIMEnterpriseUserConf.java
@@ -110,5 +110,4 @@ public class SCIMEnterpriseUserConf implements Serializable {
public void setManager(final SCIMManagerConf manager) {
this.manager = manager;
}
-
}
diff --git a/ext/scimv2/common-lib/src/main/java/org/apache/syncope/common/lib/scim/SCIMGeneralConf.java b/ext/scimv2/common-lib/src/main/java/org/apache/syncope/common/lib/scim/SCIMGeneralConf.java
index 20da7cacb8..e7352b52ba 100644
--- a/ext/scimv2/common-lib/src/main/java/org/apache/syncope/common/lib/scim/SCIMGeneralConf.java
+++ b/ext/scimv2/common-lib/src/main/java/org/apache/syncope/common/lib/scim/SCIMGeneralConf.java
@@ -87,5 +87,4 @@ public class SCIMGeneralConf implements Serializable {
public void setFilterMaxResults(final int filterMaxResults) {
this.filterMaxResults = filterMaxResults;
}
-
}
diff --git a/ext/scimv2/scim-rest-api/src/main/java/org/apache/syncope/ext/scimv2/api/data/SCIMResource.java b/ext/scimv2/common-lib/src/main/java/org/apache/syncope/common/lib/scim/SCIMGroupConf.java
similarity index 59%
copy from ext/scimv2/scim-rest-api/src/main/java/org/apache/syncope/ext/scimv2/api/data/SCIMResource.java
copy to ext/scimv2/common-lib/src/main/java/org/apache/syncope/common/lib/scim/SCIMGroupConf.java
index d9bdccc17d..7aa56c86c2 100644
--- a/ext/scimv2/scim-rest-api/src/main/java/org/apache/syncope/ext/scimv2/api/data/SCIMResource.java
+++ b/ext/scimv2/common-lib/src/main/java/org/apache/syncope/common/lib/scim/SCIMGroupConf.java
@@ -16,47 +16,36 @@
* specific language governing permissions and limitations
* under the License.
*/
-package org.apache.syncope.ext.scimv2.api.data;
+package org.apache.syncope.common.lib.scim;
-import java.util.List;
-import java.util.Optional;
+import com.fasterxml.jackson.annotation.JsonIgnore;
+import java.io.Serializable;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
-public abstract class SCIMResource extends SCIMBean {
+public class SCIMGroupConf implements Serializable {
- private static final long serialVersionUID = -8465880682458920021L;
-
- private final String id;
+ private static final long serialVersionUID = -2700011089067219156L;
private String externalId;
- private final List<String> schemas;
-
- private final Meta meta;
-
- public SCIMResource(final String id, final List<String> schemas, final Meta meta) {
- this.id = id;
- this.schemas = Optional.ofNullable(schemas).orElse(List.of());
- this.meta = meta;
- }
+ @JsonIgnore
+ public Map<String, String> asMap() {
+ Map<String, String> map = new HashMap<>();
- public String getId() {
- return id;
- }
+ if (externalId != null) {
+ map.put("externalId", externalId);
+ }
- public List<String> getSchemas() {
- return schemas;
+ return Collections.unmodifiableMap(map);
}
- public Meta getMeta() {
- return meta;
+ public String getExternalId() {
+ return externalId;
}
public void setExternalId(final String externalId) {
this.externalId = externalId;
}
-
- public String getExternalId() {
- return externalId;
- }
-
}
diff --git a/ext/scimv2/common-lib/src/main/java/org/apache/syncope/common/lib/scim/SCIMUserConf.java b/ext/scimv2/common-lib/src/main/java/org/apache/syncope/common/lib/scim/SCIMUserConf.java
index c2c0d15b92..f89fc8b3da 100644
--- a/ext/scimv2/common-lib/src/main/java/org/apache/syncope/common/lib/scim/SCIMUserConf.java
+++ b/ext/scimv2/common-lib/src/main/java/org/apache/syncope/common/lib/scim/SCIMUserConf.java
@@ -34,6 +34,8 @@ public class SCIMUserConf implements Serializable {
private static final long serialVersionUID = -2700011089067219156L;
+ private String externalId;
+
private SCIMUserNameConf name;
private String displayName;
@@ -68,6 +70,10 @@ public class SCIMUserConf implements Serializable {
public Map<String, String> asMap() {
Map<String, String> map = new HashMap<>();
+ if (externalId != null) {
+ map.put("externalId", externalId);
+ }
+
if (displayName != null) {
map.put("displayName", displayName);
}
@@ -96,6 +102,14 @@ public class SCIMUserConf implements Serializable {
return Collections.unmodifiableMap(map);
}
+ public String getExternalId() {
+ return externalId;
+ }
+
+ public void setExternalId(final String externalId) {
+ this.externalId = externalId;
+ }
+
public SCIMUserNameConf getName() {
return name;
}
@@ -191,5 +205,4 @@ public class SCIMUserConf implements Serializable {
public List<String> getX509Certificates() {
return x509Certificates;
}
-
}
diff --git a/ext/scimv2/logic/src/main/java/org/apache/syncope/core/logic/SCIMDataBinder.java b/ext/scimv2/logic/src/main/java/org/apache/syncope/core/logic/SCIMDataBinder.java
index 65943b14c1..f42e820f40 100644
--- a/ext/scimv2/logic/src/main/java/org/apache/syncope/core/logic/SCIMDataBinder.java
+++ b/ext/scimv2/logic/src/main/java/org/apache/syncope/core/logic/SCIMDataBinder.java
@@ -22,23 +22,40 @@ import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
+import java.util.Optional;
import java.util.Set;
import java.util.stream.Collectors;
+import org.apache.commons.jexl3.MapContext;
+import org.apache.commons.lang3.BooleanUtils;
import org.apache.commons.lang3.StringUtils;
+import org.apache.commons.lang3.tuple.Pair;
+import org.apache.syncope.common.lib.AnyOperations;
import org.apache.syncope.common.lib.Attr;
import org.apache.syncope.common.lib.EntityTOUtils;
import org.apache.syncope.common.lib.SyncopeConstants;
+import org.apache.syncope.common.lib.request.AttrPatch;
import org.apache.syncope.common.lib.request.GroupCR;
+import org.apache.syncope.common.lib.request.GroupUR;
+import org.apache.syncope.common.lib.request.PasswordPatch;
+import org.apache.syncope.common.lib.request.StatusR;
+import org.apache.syncope.common.lib.request.StringReplacePatchItem;
import org.apache.syncope.common.lib.request.UserCR;
+import org.apache.syncope.common.lib.request.UserUR;
import org.apache.syncope.common.lib.scim.SCIMComplexConf;
import org.apache.syncope.common.lib.scim.SCIMConf;
+import org.apache.syncope.common.lib.scim.SCIMEnterpriseUserConf;
+import org.apache.syncope.common.lib.scim.SCIMManagerConf;
+import org.apache.syncope.common.lib.scim.SCIMUserAddressConf;
import org.apache.syncope.common.lib.to.GroupTO;
import org.apache.syncope.common.lib.to.MembershipTO;
import org.apache.syncope.common.lib.to.UserTO;
+import org.apache.syncope.common.lib.types.PatchOperation;
+import org.apache.syncope.common.lib.types.StatusRType;
import org.apache.syncope.core.logic.scim.SCIMConfManager;
import org.apache.syncope.core.persistence.api.dao.AnyDAO;
import org.apache.syncope.core.persistence.api.dao.search.MembershipCond;
import org.apache.syncope.core.persistence.api.dao.search.SearchCond;
+import org.apache.syncope.core.provisioning.api.jexl.JexlUtils;
import org.apache.syncope.core.spring.security.AuthDataAccessor;
import org.apache.syncope.ext.scimv2.api.BadRequestException;
import org.apache.syncope.ext.scimv2.api.data.Group;
@@ -47,6 +64,7 @@ import org.apache.syncope.ext.scimv2.api.data.Meta;
import org.apache.syncope.ext.scimv2.api.data.SCIMComplexValue;
import org.apache.syncope.ext.scimv2.api.data.SCIMEnterpriseInfo;
import org.apache.syncope.ext.scimv2.api.data.SCIMGroup;
+import org.apache.syncope.ext.scimv2.api.data.SCIMPatchOperation;
import org.apache.syncope.ext.scimv2.api.data.SCIMUser;
import org.apache.syncope.ext.scimv2.api.data.SCIMUserAddress;
import org.apache.syncope.ext.scimv2.api.data.SCIMUserManager;
@@ -54,9 +72,11 @@ import org.apache.syncope.ext.scimv2.api.data.SCIMUserName;
import org.apache.syncope.ext.scimv2.api.data.Value;
import org.apache.syncope.ext.scimv2.api.type.ErrorType;
import org.apache.syncope.ext.scimv2.api.type.Function;
+import org.apache.syncope.ext.scimv2.api.type.PatchOp;
import org.apache.syncope.ext.scimv2.api.type.Resource;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+import org.springframework.util.CollectionUtils;
public class SCIMDataBinder {
@@ -69,6 +89,30 @@ public class SCIMDataBinder {
protected static final List<String> GROUP_SCHEMAS = List.of(Resource.Group.schema());
+ public static String filter2JexlExpression(final String filter) {
+ String jexlExpression = filter.
+ replace(" co ", " =~ ").
+ replace(" sw ", " =^ ").
+ replace(" ew ", " =$ ");
+
+ boolean endsWithPR = jexlExpression.endsWith(" pr");
+ int pr = endsWithPR ? jexlExpression.indexOf(" pr") : jexlExpression.indexOf(" pr ");
+ while (pr != -1) {
+ String before = jexlExpression.substring(0, pr);
+ int start = before.indexOf(' ') == -1 ? 0 : jexlExpression.substring(0, pr).lastIndexOf(' ', pr) + 1;
+ String literal = jexlExpression.substring(start, pr);
+
+ endsWithPR = jexlExpression.endsWith(" pr");
+ jexlExpression = jexlExpression.replace(
+ literal + " pr" + (endsWithPR ? "" : " "),
+ "not(empty(" + literal + "))" + (endsWithPR ? "" : " "));
+
+ pr = endsWithPR ? jexlExpression.indexOf(" pr") : jexlExpression.indexOf(" pr ");
+ }
+
+ return jexlExpression;
+ }
+
protected final SCIMConfManager confManager;
protected final UserLogic userLogic;
@@ -102,9 +146,8 @@ public class SCIMDataBinder {
if (conf.getType() != null) {
value.setType(conf.getType().name());
}
- if (conf.isPrimary()) {
- value.setPrimary(true);
- }
+
+ value.setPrimary(conf.isPrimary());
if (!value.isEmpty()) {
values.add(value);
@@ -152,8 +195,7 @@ public class SCIMDataBinder {
new Meta(
Resource.User,
userTO.getCreationDate(),
- userTO.getLastChangeDate() == null
- ? userTO.getCreationDate() : userTO.getLastChangeDate(),
+ Optional.ofNullable(userTO.getLastChangeDate()).orElse(userTO.getCreationDate()),
userTO.getETagValue(),
location),
output(attributes, excludedAttributes, "userName", userTO.getUsername()),
@@ -166,6 +208,13 @@ public class SCIMDataBinder {
attrs.put("username", new Attr.Builder("username").value(userTO.getUsername()).build());
if (conf.getUserConf() != null) {
+ if (output(attributes, excludedAttributes, "externalId")
+ && conf.getUserConf().getExternalId() != null
+ && attrs.containsKey(conf.getUserConf().getExternalId())) {
+
+ user.setExternalId(attrs.get(conf.getUserConf().getExternalId()).getValues().get(0));
+ }
+
if (output(attributes, excludedAttributes, "name") && conf.getUserConf().getName() != null) {
SCIMUserName name = new SCIMUserName();
@@ -300,122 +349,139 @@ public class SCIMDataBinder {
});
}
if (output(attributes, excludedAttributes, "x509Certificates")) {
- conf.getUserConf().getX509Certificates().stream()
- .filter(attrs::containsKey)
- .forEachOrdered(certificate -> user.getX509Certificates().add(
- new Value(attrs.get(certificate).getValues().get(0))));
+ conf.getUserConf().getX509Certificates().stream().filter(attrs::containsKey).
+ forEach(cert -> user.getX509Certificates().add(new Value(attrs.get(cert).getValues().get(0))));
}
+ }
- if (conf.getEnterpriseUserConf() != null) {
- SCIMEnterpriseInfo enterpriseInfo = new SCIMEnterpriseInfo();
-
- if (output(attributes, excludedAttributes, "employeeNumber")
- && conf.getEnterpriseUserConf().getEmployeeNumber() != null
- && attrs.containsKey(conf.getEnterpriseUserConf().getEmployeeNumber())) {
-
- enterpriseInfo.setEmployeeNumber(
- attrs.get(conf.getEnterpriseUserConf().getEmployeeNumber()).getValues().get(0));
- }
- if (output(attributes, excludedAttributes, "costCenter")
- && conf.getEnterpriseUserConf().getCostCenter() != null
- && attrs.containsKey(conf.getEnterpriseUserConf().getCostCenter())) {
-
- enterpriseInfo.setCostCenter(
- attrs.get(conf.getEnterpriseUserConf().getCostCenter()).getValues().get(0));
- }
- if (output(attributes, excludedAttributes, "organization")
- && conf.getEnterpriseUserConf().getOrganization() != null
- && attrs.containsKey(conf.getEnterpriseUserConf().getOrganization())) {
-
- enterpriseInfo.setOrganization(
- attrs.get(conf.getEnterpriseUserConf().getOrganization()).getValues().get(0));
- }
- if (output(attributes, excludedAttributes, "division")
- && conf.getEnterpriseUserConf().getDivision() != null
- && attrs.containsKey(conf.getEnterpriseUserConf().getDivision())) {
+ if (conf.getEnterpriseUserConf() != null) {
+ SCIMEnterpriseInfo enterpriseInfo = new SCIMEnterpriseInfo();
- enterpriseInfo.setDivision(
- attrs.get(conf.getEnterpriseUserConf().getDivision()).getValues().get(0));
- }
- if (output(attributes, excludedAttributes, "department")
- && conf.getEnterpriseUserConf().getDepartment() != null
- && attrs.containsKey(conf.getEnterpriseUserConf().getDepartment())) {
+ if (output(attributes, excludedAttributes, "employeeNumber")
+ && conf.getEnterpriseUserConf().getEmployeeNumber() != null
+ && attrs.containsKey(conf.getEnterpriseUserConf().getEmployeeNumber())) {
- enterpriseInfo.setDepartment(
- attrs.get(conf.getEnterpriseUserConf().getDepartment()).getValues().get(0));
- }
- if (output(attributes, excludedAttributes, "manager")
- && conf.getEnterpriseUserConf().getManager() != null) {
+ enterpriseInfo.setEmployeeNumber(
+ attrs.get(conf.getEnterpriseUserConf().getEmployeeNumber()).getValues().get(0));
+ }
+ if (output(attributes, excludedAttributes, "costCenter")
+ && conf.getEnterpriseUserConf().getCostCenter() != null
+ && attrs.containsKey(conf.getEnterpriseUserConf().getCostCenter())) {
- SCIMUserManager manager = new SCIMUserManager();
+ enterpriseInfo.setCostCenter(
+ attrs.get(conf.getEnterpriseUserConf().getCostCenter()).getValues().get(0));
+ }
+ if (output(attributes, excludedAttributes, "organization")
+ && conf.getEnterpriseUserConf().getOrganization() != null
+ && attrs.containsKey(conf.getEnterpriseUserConf().getOrganization())) {
- if (conf.getEnterpriseUserConf().getManager().getKey() != null
- && attrs.containsKey(conf.getEnterpriseUserConf().getManager().getKey())) {
+ enterpriseInfo.setOrganization(
+ attrs.get(conf.getEnterpriseUserConf().getOrganization()).getValues().get(0));
+ }
+ if (output(attributes, excludedAttributes, "division")
+ && conf.getEnterpriseUserConf().getDivision() != null
+ && attrs.containsKey(conf.getEnterpriseUserConf().getDivision())) {
- try {
- UserTO userManager = userLogic.read(attrs.get(
- conf.getEnterpriseUserConf().getManager().getKey()).getValues().get(0));
- manager.setValue(userManager.getKey());
- manager.setRef(
- StringUtils.substringBefore(location, "/Users") + "/Users/" + userManager.getKey());
+ enterpriseInfo.setDivision(
+ attrs.get(conf.getEnterpriseUserConf().getDivision()).getValues().get(0));
+ }
+ if (output(attributes, excludedAttributes, "department")
+ && conf.getEnterpriseUserConf().getDepartment() != null
+ && attrs.containsKey(conf.getEnterpriseUserConf().getDepartment())) {
- if (conf.getEnterpriseUserConf().getManager().getDisplayName() != null) {
- Attr displayName = userManager.getPlainAttr(
+ enterpriseInfo.setDepartment(
+ attrs.get(conf.getEnterpriseUserConf().getDepartment()).getValues().get(0));
+ }
+ if (output(attributes, excludedAttributes, "manager")
+ && conf.getEnterpriseUserConf().getManager() != null) {
+
+ SCIMUserManager manager = new SCIMUserManager();
+
+ if (conf.getEnterpriseUserConf().getManager().getKey() != null
+ && attrs.containsKey(conf.getEnterpriseUserConf().getManager().getKey())) {
+
+ try {
+ UserTO userManager = userLogic.read(attrs.get(
+ conf.getEnterpriseUserConf().getManager().getKey()).getValues().get(0));
+ manager.setValue(userManager.getKey());
+ manager.setRef(
+ StringUtils.substringBefore(location, "/Users") + "/Users/" + userManager.getKey());
+
+ if (conf.getEnterpriseUserConf().getManager().getDisplayName() != null) {
+ Attr displayName = userManager.getPlainAttr(
+ conf.getEnterpriseUserConf().getManager().getDisplayName()).orElse(null);
+ if (displayName == null) {
+ displayName = userManager.getDerAttr(
conf.getEnterpriseUserConf().getManager().getDisplayName()).orElse(null);
- if (displayName == null) {
- displayName = userManager.getDerAttr(
- conf.getEnterpriseUserConf().getManager().getDisplayName()).orElse(null);
- }
- if (displayName == null) {
- displayName = userManager.getVirAttr(
- conf.getEnterpriseUserConf().getManager().getDisplayName()).orElse(null);
- }
- if (displayName != null) {
- manager.setDisplayName(displayName.getValues().get(0));
- }
}
- } catch (Exception e) {
- LOG.error("Could not read user {}", conf.getEnterpriseUserConf().getManager().getKey(), e);
+ if (displayName == null) {
+ displayName = userManager.getVirAttr(
+ conf.getEnterpriseUserConf().getManager().getDisplayName()).orElse(null);
+ }
+ if (displayName != null) {
+ manager.setDisplayName(displayName.getValues().get(0));
+ }
}
- }
-
- if (!manager.isEmpty()) {
- enterpriseInfo.setManager(manager);
+ } catch (Exception e) {
+ LOG.error("Could not read user {}", conf.getEnterpriseUserConf().getManager().getKey(), e);
}
}
- if (!enterpriseInfo.isEmpty()) {
- user.setEnterpriseInfo(enterpriseInfo);
+ if (!manager.isEmpty()) {
+ enterpriseInfo.setManager(manager);
}
}
- if (output(attributes, excludedAttributes, "groups")) {
- userTO.getMemberships().forEach(membership -> user.getGroups().add(new Group(
- membership.getGroupKey(),
- StringUtils.substringBefore(location, "/Users") + "/Groups/" + membership.getGroupKey(),
- membership.getGroupName(),
- Function.direct)));
- userTO.getDynMemberships().forEach(membership -> user.getGroups().add(new Group(
- membership.getGroupKey(),
- StringUtils.substringBefore(location, "/Users") + "/Groups/" + membership.getGroupKey(),
- membership.getGroupName(),
- Function.indirect)));
+ if (!enterpriseInfo.isEmpty()) {
+ user.setEnterpriseInfo(enterpriseInfo);
}
+ }
- if (output(attributes, excludedAttributes, "entitlements")) {
- authDataAccessor.getAuthorities(userTO.getUsername(), null).forEach(authority -> user.getEntitlements().
- add(new Value(authority.getAuthority() + " on Realm(s) " + authority.getRealms())));
- }
+ if (output(attributes, excludedAttributes, "groups")) {
+ userTO.getMemberships().forEach(membership -> user.getGroups().add(new Group(
+ membership.getGroupKey(),
+ StringUtils.substringBefore(location, "/Users") + "/Groups/" + membership.getGroupKey(),
+ membership.getGroupName(),
+ Function.direct)));
+ userTO.getDynMemberships().forEach(membership -> user.getGroups().add(new Group(
+ membership.getGroupKey(),
+ StringUtils.substringBefore(location, "/Users") + "/Groups/" + membership.getGroupKey(),
+ membership.getGroupName(),
+ Function.indirect)));
+ }
- if (output(attributes, excludedAttributes, "roles")) {
- userTO.getRoles().forEach(role -> user.getRoles().add(new Value(role)));
- }
+ if (output(attributes, excludedAttributes, "entitlements")) {
+ authDataAccessor.getAuthorities(userTO.getUsername(), null).forEach(authority -> user.getEntitlements().
+ add(new Value(authority.getAuthority() + " on Realm(s) " + authority.getRealms())));
+ }
+
+ if (output(attributes, excludedAttributes, "roles")) {
+ userTO.getRoles().forEach(role -> user.getRoles().add(new Value(role)));
}
return user;
}
- protected <E extends Enum<?>> void fill(
+ protected void setAttribute(
+ final UserTO userTO,
+ final String schema,
+ final String value) {
+
+ if (schema == null || value == null) {
+ return;
+ }
+
+ switch (schema) {
+ case "username":
+ userTO.setUsername(value);
+ break;
+
+ default:
+ userTO.getPlainAttrs().add(new Attr.Builder(schema).value(value).build());
+ }
+ }
+
+ protected <E extends Enum<?>> void setAttribute(
final Set<Attr> attrs,
final List<SCIMComplexConf<E>> confs,
final List<SCIMComplexValue> values) {
@@ -426,8 +492,11 @@ public class SCIMDataBinder {
new Attr.Builder(conf.getValue()).value(value.getValue()).build())));
}
- public UserTO toUserTO(final SCIMUser user) {
- if (!USER_SCHEMAS.equals(user.getSchemas()) && !ENTERPRISE_USER_SCHEMAS.equals(user.getSchemas())) {
+ public UserTO toUserTO(final SCIMUser user, final boolean checkSchemas) {
+ if (checkSchemas
+ && !USER_SCHEMAS.equals(user.getSchemas())
+ && !ENTERPRISE_USER_SCHEMAS.equals(user.getSchemas())) {
+
throw new BadRequestException(ErrorType.invalidValue);
}
@@ -440,147 +509,166 @@ public class SCIMDataBinder {
SCIMConf conf = confManager.get();
if (conf.getUserConf() != null) {
- if (conf.getUserConf().getName() != null && user.getName() != null) {
- if (conf.getUserConf().getName().getFamilyName() != null
- && user.getName().getFamilyName() != null) {
+ setAttribute(
+ userTO,
+ conf.getUserConf().getExternalId(),
+ user.getExternalId());
- setAttribute(userTO, conf.getUserConf().getName().getFamilyName(),
- user.getName().getFamilyName());
- }
- if (conf.getUserConf().getName().getFormatted() != null
- && user.getName().getFormatted() != null) {
-
- setAttribute(userTO, conf.getUserConf().getName().getFormatted(),
- user.getName().getFormatted());
- }
- if (conf.getUserConf().getName().getGivenName() != null
- && user.getName().getGivenName() != null) {
-
- setAttribute(userTO, conf.getUserConf().getName().getGivenName(),
- user.getName().getGivenName());
- }
- if (conf.getUserConf().getName().getHonorificPrefix() != null
- && user.getName().getHonorificPrefix() != null) {
-
- setAttribute(userTO, conf.getUserConf().getName().getHonorificPrefix(),
- user.getName().getHonorificPrefix());
- }
- if (conf.getUserConf().getName().getHonorificSuffix() != null
- && user.getName().getHonorificSuffix() != null) {
-
- setAttribute(userTO, conf.getUserConf().getName().getHonorificSuffix(),
- user.getName().getHonorificSuffix());
- }
- if (conf.getUserConf().getName().getMiddleName() != null
- && user.getName().getMiddleName() != null) {
-
- setAttribute(userTO, conf.getUserConf().getName().getMiddleName(),
- user.getName().getMiddleName());
- }
- }
-
- if (conf.getUserConf().getDisplayName() != null && user.getDisplayName() != null) {
- setAttribute(userTO, conf.getUserConf().getDisplayName(), user.getDisplayName());
- }
- if (conf.getUserConf().getNickName() != null && user.getNickName() != null) {
- setAttribute(userTO, conf.getUserConf().getNickName(), user.getNickName());
- }
- if (conf.getUserConf().getProfileUrl() != null && user.getProfileUrl() != null) {
- setAttribute(userTO, conf.getUserConf().getProfileUrl(), user.getProfileUrl());
- }
- if (conf.getUserConf().getTitle() != null && user.getTitle() != null) {
- setAttribute(userTO, conf.getUserConf().getTitle(),
- user.getTitle());
- }
- if (conf.getUserConf().getUserType() != null && user.getUserType() != null) {
- setAttribute(userTO, conf.getUserConf().getUserType(), user.getUserType());
- }
- if (conf.getUserConf().getPreferredLanguage() != null && user.getPreferredLanguage() != null) {
- setAttribute(userTO, conf.getUserConf().getPreferredLanguage(), user.getPreferredLanguage());
- }
- if (conf.getUserConf().getLocale() != null && user.getLocale() != null) {
- setAttribute(userTO, conf.getUserConf().getLocale(), user.getLocale());
- }
- if (conf.getUserConf().getTimezone() != null && user.getTimezone() != null) {
- setAttribute(userTO, conf.getUserConf().getTimezone(), user.getTimezone());
+ if (conf.getUserConf().getName() != null && user.getName() != null) {
+ setAttribute(
+ userTO,
+ conf.getUserConf().getName().getFamilyName(),
+ user.getName().getFamilyName());
+
+ setAttribute(
+ userTO,
+ conf.getUserConf().getName().getFormatted(),
+ user.getName().getFormatted());
+
+ setAttribute(
+ userTO,
+ conf.getUserConf().getName().getGivenName(),
+ user.getName().getGivenName());
+
+ setAttribute(
+ userTO,
+ conf.getUserConf().getName().getHonorificPrefix(),
+ user.getName().getHonorificPrefix());
+
+ setAttribute(
+ userTO,
+ conf.getUserConf().getName().getHonorificSuffix(),
+ user.getName().getHonorificSuffix());
+
+ setAttribute(
+ userTO,
+ conf.getUserConf().getName().getMiddleName(),
+ user.getName().getMiddleName());
}
- fill(userTO.getPlainAttrs(), conf.getUserConf().getEmails(), user.getEmails());
- fill(userTO.getPlainAttrs(), conf.getUserConf().getPhoneNumbers(), user.getPhoneNumbers());
- fill(userTO.getPlainAttrs(), conf.getUserConf().getIms(), user.getIms());
- fill(userTO.getPlainAttrs(), conf.getUserConf().getPhotos(), user.getPhotos());
+ setAttribute(
+ userTO,
+ conf.getUserConf().getDisplayName(),
+ user.getDisplayName());
+
+ setAttribute(
+ userTO,
+ conf.getUserConf().getNickName(),
+ user.getNickName());
+
+ setAttribute(
+ userTO,
+ conf.getUserConf().getProfileUrl(),
+ user.getProfileUrl());
+
+ setAttribute(
+ userTO,
+ conf.getUserConf().getTitle(),
+ user.getTitle());
+
+ setAttribute(
+ userTO,
+ conf.getUserConf().getUserType(),
+ user.getUserType());
+
+ setAttribute(
+ userTO,
+ conf.getUserConf().getPreferredLanguage(),
+ user.getPreferredLanguage());
+
+ setAttribute(
+ userTO,
+ conf.getUserConf().getLocale(),
+ user.getLocale());
+
+ setAttribute(
+ userTO,
+ conf.getUserConf().getTimezone(),
+ user.getTimezone());
+
+ setAttribute(userTO.getPlainAttrs(), conf.getUserConf().getEmails(), user.getEmails());
+ setAttribute(userTO.getPlainAttrs(), conf.getUserConf().getPhoneNumbers(), user.getPhoneNumbers());
+ setAttribute(userTO.getPlainAttrs(), conf.getUserConf().getIms(), user.getIms());
+ setAttribute(userTO.getPlainAttrs(), conf.getUserConf().getPhotos(), user.getPhotos());
user.getAddresses().stream().filter(address -> address.getType() != null).
forEach(address -> conf.getUserConf().getAddresses().stream().
filter(object -> address.getType().equals(object.getType().name())).findFirst().
ifPresent(addressConf -> {
- if (addressConf.getFormatted() != null && address.getFormatted() != null) {
- setAttribute(userTO, addressConf.getFormatted(), address.getFormatted());
- }
- if (addressConf.getStreetAddress() != null && address.getStreetAddress() != null) {
- setAttribute(userTO, addressConf.getStreetAddress(), address.getStreetAddress());
- }
- if (addressConf.getLocality() != null && address.getLocality() != null) {
- setAttribute(userTO, addressConf.getLocality(), address.getLocality());
- }
- if (addressConf.getRegion() != null && address.getRegion() != null) {
- setAttribute(userTO, addressConf.getRegion(), address.getRegion());
- }
- if (addressConf.getPostalCode() != null && address.getPostalCode() != null) {
- setAttribute(userTO, addressConf.getPostalCode(), address.getPostalCode());
- }
- if (addressConf.getCountry() != null && address.getCountry() != null) {
- setAttribute(userTO, addressConf.getCountry(), address.getCountry());
- }
+ setAttribute(
+ userTO,
+ addressConf.getFormatted(),
+ address.getFormatted());
+
+ setAttribute(
+ userTO,
+ addressConf.getStreetAddress(),
+ address.getStreetAddress());
+
+ setAttribute(
+ userTO,
+ addressConf.getLocality(),
+ address.getLocality());
+
+ setAttribute(
+ userTO,
+ addressConf.getRegion(),
+ address.getRegion());
+
+ setAttribute(
+ userTO,
+ addressConf.getPostalCode(),
+ address.getPostalCode());
+
+ setAttribute(
+ userTO,
+ addressConf.getCountry(),
+ address.getCountry());
}));
for (int i = 0; i < user.getX509Certificates().size(); i++) {
Value certificate = user.getX509Certificates().get(i);
if (conf.getUserConf().getX509Certificates().size() > i) {
- setAttribute(userTO, conf.getUserConf().getX509Certificates().get(i), certificate.getValue());
+ setAttribute(
+ userTO,
+ conf.getUserConf().getX509Certificates().get(i),
+ certificate.getValue());
}
}
}
if (conf.getEnterpriseUserConf() != null) {
- if (conf.getEnterpriseUserConf().getEmployeeNumber() != null
- && user.getEnterpriseInfo().getEmployeeNumber() != null) {
-
- setAttribute(userTO, conf.getEnterpriseUserConf().getEmployeeNumber(),
- user.getEnterpriseInfo().getEmployeeNumber());
- }
- if (conf.getEnterpriseUserConf().getCostCenter() != null
- && user.getEnterpriseInfo().getCostCenter() != null) {
-
- setAttribute(userTO, conf.getEnterpriseUserConf().getCostCenter(),
- user.getEnterpriseInfo().getCostCenter());
- }
- if (conf.getEnterpriseUserConf().getOrganization() != null
- && user.getEnterpriseInfo().getOrganization() != null) {
-
- setAttribute(userTO, conf.getEnterpriseUserConf().getOrganization(),
- user.getEnterpriseInfo().getOrganization());
- }
- if (conf.getEnterpriseUserConf().getDivision() != null
- && user.getEnterpriseInfo().getDivision() != null) {
-
- setAttribute(userTO, conf.getEnterpriseUserConf().getDivision(),
- user.getEnterpriseInfo().getDivision());
- }
- if (conf.getEnterpriseUserConf().getDepartment() != null
- && user.getEnterpriseInfo().getDepartment() != null) {
-
- setAttribute(userTO, conf.getEnterpriseUserConf().getDepartment(),
- user.getEnterpriseInfo().getDepartment());
- }
- if (conf.getEnterpriseUserConf().getManager() != null
- && conf.getEnterpriseUserConf().getManager().getKey() != null
- && user.getEnterpriseInfo().getManager() != null
- && user.getEnterpriseInfo().getManager().getValue() != null) {
-
- setAttribute(userTO, conf.getEnterpriseUserConf().getManager().getKey(),
- user.getEnterpriseInfo().getManager().getValue());
- }
+ setAttribute(
+ userTO,
+ conf.getEnterpriseUserConf().getEmployeeNumber(),
+ user.getEnterpriseInfo().getEmployeeNumber());
+
+ setAttribute(
+ userTO,
+ conf.getEnterpriseUserConf().getCostCenter(),
+ user.getEnterpriseInfo().getCostCenter());
+
+ setAttribute(
+ userTO,
+ conf.getEnterpriseUserConf().getOrganization(),
+ user.getEnterpriseInfo().getOrganization());
+
+ setAttribute(
+ userTO,
+ conf.getEnterpriseUserConf().getDivision(),
+ user.getEnterpriseInfo().getDivision());
+
+ setAttribute(
+ userTO,
+ conf.getEnterpriseUserConf().getDepartment(),
+ user.getEnterpriseInfo().getDepartment());
+
+ setAttribute(
+ userTO,
+ Optional.ofNullable(conf.getEnterpriseUserConf().getManager()).
+ map(SCIMManagerConf::getKey).orElse(null),
+ Optional.ofNullable(user.getEnterpriseInfo().getManager()).
+ map(SCIMUserManager::getValue).orElse(null));
}
userTO.getMemberships().addAll(user.getGroups().stream().
@@ -595,21 +683,290 @@ public class SCIMDataBinder {
}
public UserCR toUserCR(final SCIMUser user) {
- UserTO userTO = toUserTO(user);
+ UserTO userTO = toUserTO(user, true);
UserCR userCR = new UserCR();
EntityTOUtils.toAnyCR(userTO, userCR);
return userCR;
}
- protected void setAttribute(final UserTO userTO, final String schema, final String value) {
- switch (schema) {
- case "username":
- userTO.setUsername(value);
+ protected void setAttribute(final Set<AttrPatch> attrs, final String schema, final SCIMPatchOperation op) {
+ Optional.ofNullable(schema).ifPresent(a -> {
+ Attr.Builder attr = new Attr.Builder(a);
+ if (!CollectionUtils.isEmpty(op.getValue())) {
+ attr.value(op.getValue().get(0).toString());
+ }
+
+ attrs.add(new AttrPatch.Builder(attr.build()).
+ operation(op.getOp() == PatchOp.remove ? PatchOperation.DELETE : PatchOperation.ADD_REPLACE).
+ build());
+ });
+ }
+
+ protected <E extends Enum<?>> void setAttribute(
+ final Set<AttrPatch> attrs,
+ final List<SCIMComplexConf<E>> confs,
+ final SCIMPatchOperation op) {
+
+ confs.stream().
+ filter(conf -> BooleanUtils.toBoolean(JexlUtils.evaluate(
+ filter2JexlExpression(op.getPath().getFilter()),
+ new MapContext(Map.of("type", conf.getType().name()))).toString())).findFirst().
+ ifPresent(conf -> {
+ if (op.getPath().getSub() == null || "display".equals(op.getPath().getSub())) {
+ setAttribute(attrs, conf.getDisplay(), op);
+ }
+ if (op.getPath().getSub() == null || "value".equals(op.getPath().getSub())) {
+ setAttribute(attrs, conf.getValue(), op);
+ }
+ });
+ }
+
+ protected <E extends Enum<?>> void setAttribute(
+ final Set<AttrPatch> attrs,
+ final List<SCIMComplexConf<E>> confs,
+ final List<SCIMComplexValue> values,
+ final PatchOp patchOp) {
+
+ values.stream().
+ filter(value -> value.getType() != null).forEach(value -> confs.stream().
+ filter(conf -> value.getType().equals(conf.getType().name())).findFirst().
+ ifPresent(conf -> attrs.add(new AttrPatch.Builder(
+ new Attr.Builder(conf.getValue()).value(value.getValue()).build()).
+ operation(patchOp == PatchOp.remove ? PatchOperation.DELETE : PatchOperation.ADD_REPLACE).
+ build())));
+ }
+
+ protected void setAttribute(
+ final Set<AttrPatch> attrs,
+ final SCIMUserAddressConf conf,
+ final SCIMPatchOperation op) {
+
+ if (op.getPath().getSub() == null || "formatted".equals(op.getPath().getSub())) {
+ setAttribute(attrs, conf.getFormatted(), op);
+ }
+ if (op.getPath().getSub() == null || "streetAddress".equals(op.getPath().getSub())) {
+ setAttribute(attrs, conf.getStreetAddress(), op);
+ }
+ if (op.getPath().getSub() == null || "locality".equals(op.getPath().getSub())) {
+ setAttribute(attrs, conf.getLocality(), op);
+ }
+ if (op.getPath().getSub() == null || "region".equals(op.getPath().getSub())) {
+ setAttribute(attrs, conf.getRegion(), op);
+ }
+ if (op.getPath().getSub() == null || "postalCode".equals(op.getPath().getSub())) {
+ setAttribute(attrs, conf.getPostalCode(), op);
+ }
+ if (op.getPath().getSub() == null || "country".equals(op.getPath().getSub())) {
+ setAttribute(attrs, conf.getCountry(), op);
+ }
+ }
+
+ public Pair<UserUR, StatusR> toUserUpdate(final UserTO before, final SCIMPatchOperation op) {
+ StatusR statusR = null;
+
+ if (op.getPath() == null && op.getOp() != PatchOp.remove
+ && !CollectionUtils.isEmpty(op.getValue()) && op.getValue().get(0) instanceof SCIMUser) {
+
+ SCIMUser after = (SCIMUser) op.getValue().get(0);
+
+ if (after.getActive() != null && before.isSuspended() == after.isActive()) {
+ statusR = new StatusR.Builder().key(before.getKey()).
+ type(after.isActive() ? StatusRType.REACTIVATE : StatusRType.SUSPEND).
+ build();
+ }
+
+ UserTO updated = toUserTO(after, false);
+ updated.setKey(before.getKey());
+ return Pair.of(AnyOperations.diff(updated, before, true), statusR);
+ }
+
+ UserUR userUR = new UserUR.Builder(before.getKey()).build();
+
+ SCIMConf conf = confManager.get();
+ if (conf == null) {
+ return Pair.of(userUR, statusR);
+ }
+
+ switch (op.getPath().getAttribute()) {
+ case "externalId":
+ setAttribute(userUR.getPlainAttrs(), conf.getUserConf().getExternalId(), op);
+ break;
+
+ case "userName":
+ if (op.getOp() != PatchOp.remove && !CollectionUtils.isEmpty(op.getValue())) {
+ userUR.setUsername(new StringReplacePatchItem.Builder().
+ value(op.getValue().get(0).toString()).build());
+ }
+ break;
+
+ case "password":
+ if (op.getOp() != PatchOp.remove && !CollectionUtils.isEmpty(op.getValue())) {
+ userUR.setPassword(new PasswordPatch.Builder().
+ value(op.getValue().get(0).toString()).build());
+ }
+ break;
+
+ case "active":
+ if (!CollectionUtils.isEmpty(op.getValue())) {
+ statusR = new StatusR.Builder().key(before.getKey()).
+ type((boolean) op.getValue().get(0) ? StatusRType.REACTIVATE : StatusRType.SUSPEND).
+ build();
+ }
+ break;
+
+ case "name":
+ if (conf.getUserConf().getName() != null) {
+ if (op.getPath().getSub() == null || "familyName".equals(op.getPath().getSub())) {
+ setAttribute(userUR.getPlainAttrs(), conf.getUserConf().getName().getFamilyName(), op);
+ }
+ if (op.getPath().getSub() == null || "formatted".equals(op.getPath().getSub())) {
+ setAttribute(userUR.getPlainAttrs(), conf.getUserConf().getName().getFormatted(), op);
+ }
+ if (op.getPath().getSub() == null || "givenName".equals(op.getPath().getSub())) {
+ setAttribute(userUR.getPlainAttrs(), conf.getUserConf().getName().getGivenName(), op);
+ }
+ if (op.getPath().getSub() == null || "honorificPrefix".equals(op.getPath().getSub())) {
+ setAttribute(userUR.getPlainAttrs(), conf.getUserConf().getName().getHonorificPrefix(), op);
+ }
+ if (op.getPath().getSub() == null || "honorificSuffix".equals(op.getPath().getSub())) {
+ setAttribute(userUR.getPlainAttrs(), conf.getUserConf().getName().getHonorificSuffix(), op);
+ }
+ if (op.getPath().getSub() == null || "middleName".equals(op.getPath().getSub())) {
+ setAttribute(userUR.getPlainAttrs(), conf.getUserConf().getName().getMiddleName(), op);
+ }
+ }
+ break;
+
+ case "displayName":
+ setAttribute(userUR.getPlainAttrs(), conf.getUserConf().getDisplayName(), op);
+ break;
+
+ case "nickName":
+ setAttribute(userUR.getPlainAttrs(), conf.getUserConf().getNickName(), op);
+ break;
+
+ case "profileUrl":
+ setAttribute(userUR.getPlainAttrs(), conf.getUserConf().getProfileUrl(), op);
+ break;
+
+ case "title":
+ setAttribute(userUR.getPlainAttrs(), conf.getUserConf().getTitle(), op);
+ break;
+
+ case "userType":
+ setAttribute(userUR.getPlainAttrs(), conf.getUserConf().getUserType(), op);
+ break;
+
+ case "preferredLanguage":
+ setAttribute(userUR.getPlainAttrs(), conf.getUserConf().getPreferredLanguage(), op);
+ break;
+
+ case "locale":
+ setAttribute(userUR.getPlainAttrs(), conf.getUserConf().getLocale(), op);
+ break;
+
+ case "timezone":
+ setAttribute(userUR.getPlainAttrs(), conf.getUserConf().getTimezone(), op);
+ break;
+
+ case "emails":
+ if (!CollectionUtils.isEmpty(op.getValue()) && op.getValue().get(0) instanceof SCIMUser) {
+ setAttribute(
+ userUR.getPlainAttrs(),
+ conf.getUserConf().getEmails(),
+ ((SCIMUser) op.getValue().get(0)).getEmails(),
+ op.getOp());
+ } else if (op.getPath().getFilter() != null) {
+ setAttribute(userUR.getPlainAttrs(), conf.getUserConf().getEmails(), op);
+ }
+ break;
+
+ case "phoneNumbers":
+ if (!CollectionUtils.isEmpty(op.getValue()) && op.getValue().get(0) instanceof SCIMUser) {
+ setAttribute(
+ userUR.getPlainAttrs(),
+ conf.getUserConf().getPhoneNumbers(),
+ ((SCIMUser) op.getValue().get(0)).getPhoneNumbers(),
+ op.getOp());
+ } else if (op.getPath().getFilter() != null) {
+ setAttribute(userUR.getPlainAttrs(), conf.getUserConf().getPhoneNumbers(), op);
+ }
+ break;
+
+ case "ims":
+ if (!CollectionUtils.isEmpty(op.getValue()) && op.getValue().get(0) instanceof SCIMUser) {
+ setAttribute(
+ userUR.getPlainAttrs(),
+ conf.getUserConf().getIms(),
+ ((SCIMUser) op.getValue().get(0)).getIms(),
+ op.getOp());
+ } else if (op.getPath().getFilter() != null) {
+ setAttribute(userUR.getPlainAttrs(), conf.getUserConf().getIms(), op);
+ }
+ break;
+
+ case "photos":
+ if (!CollectionUtils.isEmpty(op.getValue()) && op.getValue().get(0) instanceof SCIMUser) {
+ setAttribute(
+ userUR.getPlainAttrs(),
+ conf.getUserConf().getPhotos(),
+ ((SCIMUser) op.getValue().get(0)).getPhotos(),
+ op.getOp());
+ } else if (op.getPath().getFilter() != null) {
+ setAttribute(userUR.getPlainAttrs(), conf.getUserConf().getPhotos(), op);
+ }
+ break;
+
+ case "addresses":
+ if (!CollectionUtils.isEmpty(op.getValue()) && op.getValue().get(0) instanceof SCIMUser) {
+ SCIMUser after = (SCIMUser) op.getValue().get(0);
+ after.getAddresses().stream().filter(address -> address.getType() != null).
+ forEach(address -> conf.getUserConf().getAddresses().stream().
+ filter(object -> address.getType().equals(object.getType().name())).findFirst().
+ ifPresent(addressConf -> setAttribute(userUR.getPlainAttrs(), addressConf, op)));
+ } else if (op.getPath().getFilter() != null) {
+ conf.getUserConf().getAddresses().stream().
+ filter(addressConf -> BooleanUtils.toBoolean(JexlUtils.evaluate(
+ filter2JexlExpression(op.getPath().getFilter()),
+ new MapContext(Map.of("type", addressConf.getType().name()))).toString())).findFirst().
+ ifPresent(addressConf -> setAttribute(userUR.getPlainAttrs(), addressConf, op));
+ }
+ break;
+
+ case "employeeNumber":
+ setAttribute(userUR.getPlainAttrs(), Optional.ofNullable(conf.getEnterpriseUserConf()).
+ map(SCIMEnterpriseUserConf::getEmployeeNumber).orElse(null), op);
+ break;
+
+ case "costCenter":
+ setAttribute(userUR.getPlainAttrs(), Optional.ofNullable(conf.getEnterpriseUserConf()).
+ map(SCIMEnterpriseUserConf::getCostCenter).orElse(null), op);
+ break;
+
+ case "organization":
+ setAttribute(userUR.getPlainAttrs(), Optional.ofNullable(conf.getEnterpriseUserConf()).
+ map(SCIMEnterpriseUserConf::getOrganization).orElse(null), op);
+ break;
+
+ case "division":
+ setAttribute(userUR.getPlainAttrs(), Optional.ofNullable(conf.getEnterpriseUserConf()).
+ map(SCIMEnterpriseUserConf::getDivision).orElse(null), op);
+ break;
+
+ case "department":
+ setAttribute(userUR.getPlainAttrs(), Optional.ofNullable(conf.getEnterpriseUserConf()).
+ map(SCIMEnterpriseUserConf::getDepartment).orElse(null), op);
+ break;
+
+ case "manager":
+ setAttribute(userUR.getPlainAttrs(), Optional.ofNullable(conf.getEnterpriseUserConf()).
+ map(SCIMEnterpriseUserConf::getManager).map(SCIMManagerConf::getKey).orElse(null), op);
break;
default:
- userTO.getPlainAttrs().add(new Attr.Builder(schema).value(value).build());
}
+
+ return Pair.of(userUR, statusR);
}
public SCIMGroup toSCIMGroup(
@@ -623,12 +980,26 @@ public class SCIMDataBinder {
new Meta(
Resource.Group,
groupTO.getCreationDate(),
- groupTO.getLastChangeDate() == null
- ? groupTO.getCreationDate() : groupTO.getLastChangeDate(),
+ Optional.ofNullable(groupTO.getLastChangeDate()).orElse(groupTO.getCreationDate()),
groupTO.getETagValue(),
location),
output(attributes, excludedAttributes, "displayName", groupTO.getName()));
+ SCIMConf conf = confManager.get();
+
+ Map<String, Attr> attrs = new HashMap<>();
+ attrs.putAll(EntityTOUtils.buildAttrMap(groupTO.getPlainAttrs()));
+ attrs.putAll(EntityTOUtils.buildAttrMap(groupTO.getDerAttrs()));
+ attrs.putAll(EntityTOUtils.buildAttrMap(groupTO.getVirAttrs()));
+
+ if (output(attributes, excludedAttributes, "externalId")
+ && conf.getGroupConf() != null
+ && conf.getGroupConf().getExternalId() != null
+ && attrs.containsKey(conf.getGroupConf().getExternalId())) {
+
+ group.setExternalId(attrs.get(conf.getGroupConf().getExternalId()).getValues().get(0));
+ }
+
MembershipCond membCond = new MembershipCond();
membCond.setGroup(groupTO.getKey());
SearchCond searchCond = SearchCond.getLeaf(membCond);
@@ -657,8 +1028,8 @@ public class SCIMDataBinder {
return group;
}
- public static GroupTO toGroupTO(final SCIMGroup group) {
- if (!GROUP_SCHEMAS.equals(group.getSchemas())) {
+ public GroupTO toGroupTO(final SCIMGroup group, final boolean checkSchemas) {
+ if (checkSchemas && !GROUP_SCHEMAS.equals(group.getSchemas())) {
throw new BadRequestException(ErrorType.invalidValue);
}
@@ -666,14 +1037,47 @@ public class SCIMDataBinder {
groupTO.setRealm(SyncopeConstants.ROOT_REALM);
groupTO.setKey(group.getId());
groupTO.setName(group.getDisplayName());
+
+ SCIMConf conf = confManager.get();
+ if (conf.getGroupConf() != null
+ && conf.getGroupConf().getExternalId() != null && group.getExternalId() != null) {
+
+ groupTO.getPlainAttrs().add(
+ new Attr.Builder(conf.getGroupConf().getExternalId()).
+ value(group.getExternalId()).build());
+ }
+
return groupTO;
}
- public static GroupCR toGroupCR(final SCIMGroup group) {
- if (!GROUP_SCHEMAS.equals(group.getSchemas())) {
- throw new BadRequestException(ErrorType.invalidValue);
+ public GroupCR toGroupCR(final SCIMGroup group) {
+ GroupTO groupTO = toGroupTO(group, true);
+ GroupCR groupCR = new GroupCR();
+ EntityTOUtils.toAnyCR(groupTO, groupCR);
+ return groupCR;
+ }
+
+ public GroupUR toGroupUR(final GroupTO before, final SCIMPatchOperation op) {
+ if (op.getPath() == null) {
+ throw new UnsupportedOperationException("Empty path not supported for Groups");
+ }
+
+ GroupUR groupUR = new GroupUR.Builder(before.getKey()).build();
+
+ if ("displayName".equals(op.getPath().getAttribute())) {
+ StringReplacePatchItem.Builder name = new StringReplacePatchItem.Builder().
+ operation(op.getOp() == PatchOp.remove ? PatchOperation.DELETE : PatchOperation.ADD_REPLACE);
+ if (!CollectionUtils.isEmpty(op.getValue())) {
+ name.value(op.getValue().get(0).toString());
+ }
+ groupUR.setName(name.build());
+ } else {
+ SCIMConf conf = confManager.get();
+ if (conf.getGroupConf() != null) {
+ setAttribute(groupUR.getPlainAttrs(), conf.getGroupConf().getExternalId(), op);
+ }
}
- return new GroupCR.Builder(SyncopeConstants.ROOT_REALM, group.getDisplayName()).build();
+ return groupUR;
}
}
diff --git a/ext/scimv2/logic/src/main/java/org/apache/syncope/core/logic/SCIMLogic.java b/ext/scimv2/logic/src/main/java/org/apache/syncope/core/logic/SCIMLogic.java
index f300f6f92a..8fe9a9c00b 100644
--- a/ext/scimv2/logic/src/main/java/org/apache/syncope/core/logic/SCIMLogic.java
+++ b/ext/scimv2/logic/src/main/java/org/apache/syncope/core/logic/SCIMLogic.java
@@ -101,8 +101,9 @@ public class SCIMLogic extends AbstractLogic<EntityTO> {
conf.getGeneralConf().getLastChangeDate(),
conf.getGeneralConf().getETagValue(),
uriBuilder.build().toASCIIString()),
- new ConfigurationOption(false),
- new BulkConfigurationOption(false,
+ new ConfigurationOption(true),
+ new BulkConfigurationOption(
+ false,
conf.getGeneralConf().getBulkMaxOperations(),
conf.getGeneralConf().getBulkMaxPayloadSize()),
new FilterConfigurationOption(true, conf.getGeneralConf().getFilterMaxResults()),
diff --git a/ext/scimv2/logic/src/main/java/org/apache/syncope/core/logic/scim/SearchCondVisitor.java b/ext/scimv2/logic/src/main/java/org/apache/syncope/core/logic/scim/SearchCondVisitor.java
index 890e208381..44c3b0a1e1 100644
--- a/ext/scimv2/logic/src/main/java/org/apache/syncope/core/logic/scim/SearchCondVisitor.java
+++ b/ext/scimv2/logic/src/main/java/org/apache/syncope/core/logic/scim/SearchCondVisitor.java
@@ -79,49 +79,64 @@ public class SearchCondVisitor extends SCIMFilterBaseVisitor<SearchCond> {
attrCond.setSchema("lastChangeDate");
}
- if (resource == Resource.User) {
- if (conf.getUserConf() != null) {
- if (conf.getUserConf().getName() != null) {
- for (Map.Entry<String, String> entry : conf.getUserConf().getName().asMap().entrySet()) {
- if (schemaEquals(Resource.User, "name." + entry.getKey(), schema)) {
+ switch (resource) {
+ case User:
+ if (conf.getUserConf() != null) {
+ if (conf.getUserConf().getName() != null) {
+ for (Map.Entry<String, String> entry : conf.getUserConf().getName().asMap().entrySet()) {
+ if (schemaEquals(Resource.User, "name." + entry.getKey(), schema)) {
+ attrCond = new AttrCond();
+ attrCond.setSchema(entry.getValue());
+ }
+ }
+ }
+
+ for (Map.Entry<String, String> entry : conf.getUserConf().asMap().entrySet()) {
+ if (schemaEquals(Resource.User, entry.getKey(), schema)) {
attrCond = new AttrCond();
attrCond.setSchema(entry.getValue());
}
}
- }
- for (Map.Entry<String, String> entry : conf.getUserConf().asMap().entrySet()) {
- if (schemaEquals(Resource.User, entry.getKey(), schema)) {
- attrCond = new AttrCond();
- attrCond.setSchema(entry.getValue());
+ for (SCIMUserAddressConf address : conf.getUserConf().getAddresses()) {
+ for (Map.Entry<String, String> entry : address.asMap().entrySet()) {
+ if (schemaEquals(Resource.User, "addresses." + entry.getKey(), schema)) {
+ attrCond = new AttrCond();
+ attrCond.setSchema(entry.getValue());
+ }
+ }
}
}
- for (SCIMUserAddressConf address : conf.getUserConf().getAddresses()) {
- for (Map.Entry<String, String> entry : address.asMap().entrySet()) {
- if (schemaEquals(Resource.User, "addresses." + entry.getKey(), schema)) {
+ if (conf.getEnterpriseUserConf() != null) {
+ for (Map.Entry<String, String> entry : conf.getEnterpriseUserConf().asMap().entrySet()) {
+ if (schemaEquals(Resource.EnterpriseUser, entry.getKey(), schema)) {
attrCond = new AttrCond();
attrCond.setSchema(entry.getValue());
}
}
- }
- }
- if (conf.getEnterpriseUserConf() != null) {
- for (Map.Entry<String, String> entry : conf.getEnterpriseUserConf().asMap().entrySet()) {
- if (schemaEquals(Resource.EnterpriseUser, entry.getKey(), schema)) {
+ if (conf.getEnterpriseUserConf().getManager() != null
+ && conf.getEnterpriseUserConf().getManager().getKey() != null) {
+
attrCond = new AttrCond();
- attrCond.setSchema(entry.getValue());
+ attrCond.setSchema(conf.getEnterpriseUserConf().getManager().getKey());
}
}
+ break;
- if (conf.getEnterpriseUserConf().getManager() != null
- && conf.getEnterpriseUserConf().getManager().getKey() != null) {
-
- attrCond = new AttrCond();
- attrCond.setSchema(conf.getEnterpriseUserConf().getManager().getKey());
+ case Group:
+ if (conf.getGroupConf() != null) {
+ for (Map.Entry<String, String> entry : conf.getGroupConf().asMap().entrySet()) {
+ if (schemaEquals(Resource.Group, entry.getKey(), schema)) {
+ attrCond = new AttrCond();
+ attrCond.setSchema(entry.getValue());
+ }
+ }
}
- }
+ break;
+
+ default:
}
if (attrCond == null) {
diff --git a/ext/scimv2/scim-rest-api/pom.xml b/ext/scimv2/scim-rest-api/pom.xml
index 76577940a5..bcded1bc96 100644
--- a/ext/scimv2/scim-rest-api/pom.xml
+++ b/ext/scimv2/scim-rest-api/pom.xml
@@ -45,13 +45,19 @@ under the License.
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
- <artifactId>jackson-annotations</artifactId>
+ <artifactId>jackson-databind</artifactId>
</dependency>
-
+
<dependency>
<groupId>org.apache.cxf</groupId>
<artifactId>cxf-rt-frontend-jaxrs</artifactId>
</dependency>
+
+ <dependency>
+ <groupId>org.junit.jupiter</groupId>
+ <artifactId>junit-jupiter</artifactId>
+ <scope>test</scope>
+ </dependency>
</dependencies>
<build>
diff --git a/ext/scimv2/scim-rest-api/src/main/java/org/apache/syncope/ext/scimv2/api/BadRequestException.java b/ext/scimv2/scim-rest-api/src/main/java/org/apache/syncope/ext/scimv2/api/BadRequestException.java
index 9ec456d56f..ca462dd216 100644
--- a/ext/scimv2/scim-rest-api/src/main/java/org/apache/syncope/ext/scimv2/api/BadRequestException.java
+++ b/ext/scimv2/scim-rest-api/src/main/java/org/apache/syncope/ext/scimv2/api/BadRequestException.java
@@ -39,5 +39,4 @@ public class BadRequestException extends RuntimeException {
public ErrorType getErrorType() {
return errorType;
}
-
}
diff --git a/ext/scimv2/scim-rest-api/src/main/java/org/apache/syncope/ext/scimv2/api/data/AuthenticationScheme.java b/ext/scimv2/scim-rest-api/src/main/java/org/apache/syncope/ext/scimv2/api/data/AuthenticationScheme.java
index 10e3f7722e..9048295b2a 100644
--- a/ext/scimv2/scim-rest-api/src/main/java/org/apache/syncope/ext/scimv2/api/data/AuthenticationScheme.java
+++ b/ext/scimv2/scim-rest-api/src/main/java/org/apache/syncope/ext/scimv2/api/data/AuthenticationScheme.java
@@ -78,5 +78,4 @@ public class AuthenticationScheme extends SCIMBean {
public boolean isPrimary() {
return primary;
}
-
}
diff --git a/ext/scimv2/scim-rest-api/src/main/java/org/apache/syncope/ext/scimv2/api/data/BulkConfigurationOption.java b/ext/scimv2/scim-rest-api/src/main/java/org/apache/syncope/ext/scimv2/api/data/BulkConfigurationOption.java
index b81b652e9c..a070491271 100644
--- a/ext/scimv2/scim-rest-api/src/main/java/org/apache/syncope/ext/scimv2/api/data/BulkConfigurationOption.java
+++ b/ext/scimv2/scim-rest-api/src/main/java/org/apache/syncope/ext/scimv2/api/data/BulkConfigurationOption.java
@@ -47,5 +47,4 @@ public class BulkConfigurationOption extends ConfigurationOption {
public int getMaxPayloadSize() {
return maxPayloadSize;
}
-
}
diff --git a/ext/scimv2/scim-rest-api/src/main/java/org/apache/syncope/ext/scimv2/api/data/ConfigurationOption.java b/ext/scimv2/scim-rest-api/src/main/java/org/apache/syncope/ext/scimv2/api/data/ConfigurationOption.java
index 821cae451d..5a31ae74f4 100644
--- a/ext/scimv2/scim-rest-api/src/main/java/org/apache/syncope/ext/scimv2/api/data/ConfigurationOption.java
+++ b/ext/scimv2/scim-rest-api/src/main/java/org/apache/syncope/ext/scimv2/api/data/ConfigurationOption.java
@@ -35,5 +35,4 @@ public class ConfigurationOption extends SCIMBean {
public boolean isSupported() {
return supported;
}
-
}
diff --git a/ext/scimv2/scim-rest-api/src/main/java/org/apache/syncope/ext/scimv2/api/data/FilterConfigurationOption.java b/ext/scimv2/scim-rest-api/src/main/java/org/apache/syncope/ext/scimv2/api/data/FilterConfigurationOption.java
index 3eda87ed57..578b231697 100644
--- a/ext/scimv2/scim-rest-api/src/main/java/org/apache/syncope/ext/scimv2/api/data/FilterConfigurationOption.java
+++ b/ext/scimv2/scim-rest-api/src/main/java/org/apache/syncope/ext/scimv2/api/data/FilterConfigurationOption.java
@@ -39,5 +39,4 @@ public class FilterConfigurationOption extends ConfigurationOption {
public int getMaxResults() {
return maxResults;
}
-
}
diff --git a/ext/scimv2/scim-rest-api/src/main/java/org/apache/syncope/ext/scimv2/api/data/Group.java b/ext/scimv2/scim-rest-api/src/main/java/org/apache/syncope/ext/scimv2/api/data/Group.java
index 5bddad10fa..304947e49e 100644
--- a/ext/scimv2/scim-rest-api/src/main/java/org/apache/syncope/ext/scimv2/api/data/Group.java
+++ b/ext/scimv2/scim-rest-api/src/main/java/org/apache/syncope/ext/scimv2/api/data/Group.java
@@ -48,5 +48,4 @@ public class Group extends Reference {
public String getType() {
return Optional.ofNullable(type).map(Enum::name).orElse(null);
}
-
}
diff --git a/ext/scimv2/scim-rest-api/src/main/java/org/apache/syncope/ext/scimv2/api/data/ListResponse.java b/ext/scimv2/scim-rest-api/src/main/java/org/apache/syncope/ext/scimv2/api/data/ListResponse.java
index 06c69cb6da..7ccb62c12e 100644
--- a/ext/scimv2/scim-rest-api/src/main/java/org/apache/syncope/ext/scimv2/api/data/ListResponse.java
+++ b/ext/scimv2/scim-rest-api/src/main/java/org/apache/syncope/ext/scimv2/api/data/ListResponse.java
@@ -69,5 +69,4 @@ public class ListResponse<R extends SCIMResource> extends SCIMBean {
public int getItemsPerPage() {
return itemsPerPage;
}
-
}
diff --git a/ext/scimv2/scim-rest-api/src/main/java/org/apache/syncope/ext/scimv2/api/data/Member.java b/ext/scimv2/scim-rest-api/src/main/java/org/apache/syncope/ext/scimv2/api/data/Member.java
index f2513625b4..fd4e6e121d 100644
--- a/ext/scimv2/scim-rest-api/src/main/java/org/apache/syncope/ext/scimv2/api/data/Member.java
+++ b/ext/scimv2/scim-rest-api/src/main/java/org/apache/syncope/ext/scimv2/api/data/Member.java
@@ -33,5 +33,4 @@ public class Member extends Reference {
super(value, display, ref);
}
-
}
diff --git a/ext/scimv2/scim-rest-api/src/main/java/org/apache/syncope/ext/scimv2/api/data/Reference.java b/ext/scimv2/scim-rest-api/src/main/java/org/apache/syncope/ext/scimv2/api/data/Reference.java
index a00c79331b..c051794f3a 100644
--- a/ext/scimv2/scim-rest-api/src/main/java/org/apache/syncope/ext/scimv2/api/data/Reference.java
+++ b/ext/scimv2/scim-rest-api/src/main/java/org/apache/syncope/ext/scimv2/api/data/Reference.java
@@ -42,5 +42,4 @@ abstract class Reference extends Value {
public String getRef() {
return ref;
}
-
}
diff --git a/ext/scimv2/scim-rest-api/src/main/java/org/apache/syncope/ext/scimv2/api/data/ResourceType.java b/ext/scimv2/scim-rest-api/src/main/java/org/apache/syncope/ext/scimv2/api/data/ResourceType.java
index feb271419a..ba79d1523b 100644
--- a/ext/scimv2/scim-rest-api/src/main/java/org/apache/syncope/ext/scimv2/api/data/ResourceType.java
+++ b/ext/scimv2/scim-rest-api/src/main/java/org/apache/syncope/ext/scimv2/api/data/ResourceType.java
@@ -92,5 +92,4 @@ public class ResourceType extends SCIMBean {
public Meta getMeta() {
return meta;
}
-
}
diff --git a/ext/scimv2/scim-rest-api/src/main/java/org/apache/syncope/ext/scimv2/api/data/SCIMComplexValue.java b/ext/scimv2/scim-rest-api/src/main/java/org/apache/syncope/ext/scimv2/api/data/SCIMComplexValue.java
index 94c4320368..039e909fbb 100644
--- a/ext/scimv2/scim-rest-api/src/main/java/org/apache/syncope/ext/scimv2/api/data/SCIMComplexValue.java
+++ b/ext/scimv2/scim-rest-api/src/main/java/org/apache/syncope/ext/scimv2/api/data/SCIMComplexValue.java
@@ -66,6 +66,6 @@ public class SCIMComplexValue extends SCIMBean {
@JsonIgnore
public boolean isEmpty() {
- return value == null && display == null && type == null;
+ return value == null && display == null;
}
}
diff --git a/ext/scimv2/scim-rest-api/src/main/java/org/apache/syncope/ext/scimv2/api/data/SCIMError.java b/ext/scimv2/scim-rest-api/src/main/java/org/apache/syncope/ext/scimv2/api/data/SCIMError.java
index f4f29e078a..112da2b461 100644
--- a/ext/scimv2/scim-rest-api/src/main/java/org/apache/syncope/ext/scimv2/api/data/SCIMError.java
+++ b/ext/scimv2/scim-rest-api/src/main/java/org/apache/syncope/ext/scimv2/api/data/SCIMError.java
@@ -73,5 +73,4 @@ public class SCIMError extends SCIMBean {
public int getStatus() {
return status;
}
-
}
diff --git a/ext/scimv2/scim-rest-api/src/main/java/org/apache/syncope/ext/scimv2/api/data/SCIMGroup.java b/ext/scimv2/scim-rest-api/src/main/java/org/apache/syncope/ext/scimv2/api/data/SCIMGroup.java
index e2af957d4b..c45d6da49d 100644
--- a/ext/scimv2/scim-rest-api/src/main/java/org/apache/syncope/ext/scimv2/api/data/SCIMGroup.java
+++ b/ext/scimv2/scim-rest-api/src/main/java/org/apache/syncope/ext/scimv2/api/data/SCIMGroup.java
@@ -30,8 +30,6 @@ public class SCIMGroup extends SCIMResource {
private static final long serialVersionUID = -2935466041674390279L;
- private String displayName;
-
private final List<Member> members = new ArrayList<>();
@JsonCreator(mode = JsonCreator.Mode.PROPERTIES)
@@ -41,19 +39,10 @@ public class SCIMGroup extends SCIMResource {
@JsonProperty("displayName") final String displayName) {
super(id, List.of(Resource.Group.schema()), meta);
- this.displayName = displayName;
- }
-
- public void setDisplayName(final String displayName) {
- this.displayName = displayName;
- }
-
- public String getDisplayName() {
- return displayName;
+ super.setDisplayName(displayName);
}
public List<Member> getMembers() {
return members;
}
-
}
diff --git a/ext/scimv2/scim-rest-api/src/main/java/org/apache/syncope/ext/scimv2/api/data/Reference.java b/ext/scimv2/scim-rest-api/src/main/java/org/apache/syncope/ext/scimv2/api/data/SCIMPatchOp.java
similarity index 60%
copy from ext/scimv2/scim-rest-api/src/main/java/org/apache/syncope/ext/scimv2/api/data/Reference.java
copy to ext/scimv2/scim-rest-api/src/main/java/org/apache/syncope/ext/scimv2/api/data/SCIMPatchOp.java
index a00c79331b..eb713e95ea 100644
--- a/ext/scimv2/scim-rest-api/src/main/java/org/apache/syncope/ext/scimv2/api/data/Reference.java
+++ b/ext/scimv2/scim-rest-api/src/main/java/org/apache/syncope/ext/scimv2/api/data/SCIMPatchOp.java
@@ -19,28 +19,27 @@
package org.apache.syncope.ext.scimv2.api.data;
import com.fasterxml.jackson.annotation.JsonProperty;
+import java.util.List;
+import org.apache.syncope.ext.scimv2.api.type.Resource;
-abstract class Reference extends Value {
+public class SCIMPatchOp extends SCIMBean {
- private static final long serialVersionUID = -6190164044699376089L;
+ private static final long serialVersionUID = 3957352317667344898L;
- private final String display;
+ private final List<String> schemas = List.of(Resource.PatchOp.schema());
- @JsonProperty("$ref")
- private final String ref;
+ @JsonProperty("Operations")
+ private List<SCIMPatchOperation> operations;
- Reference(final String value, final String display, final String ref) {
- super(value);
- this.display = display;
- this.ref = ref;
+ public List<String> getSchemas() {
+ return schemas;
}
- public String getDisplay() {
- return display;
+ public List<SCIMPatchOperation> getOperations() {
+ return operations;
}
- public String getRef() {
- return ref;
+ public void setOperations(final List<SCIMPatchOperation> operations) {
+ this.operations = operations;
}
-
}
diff --git a/ext/scimv2/common-lib/src/main/java/org/apache/syncope/common/lib/scim/SCIMComplexConf.java b/ext/scimv2/scim-rest-api/src/main/java/org/apache/syncope/ext/scimv2/api/data/SCIMPatchOperation.java
similarity index 53%
copy from ext/scimv2/common-lib/src/main/java/org/apache/syncope/common/lib/scim/SCIMComplexConf.java
copy to ext/scimv2/scim-rest-api/src/main/java/org/apache/syncope/ext/scimv2/api/data/SCIMPatchOperation.java
index 84cb3faaca..1421baff89 100644
--- a/ext/scimv2/common-lib/src/main/java/org/apache/syncope/common/lib/scim/SCIMComplexConf.java
+++ b/ext/scimv2/scim-rest-api/src/main/java/org/apache/syncope/ext/scimv2/api/data/SCIMPatchOperation.java
@@ -16,52 +16,45 @@
* specific language governing permissions and limitations
* under the License.
*/
-package org.apache.syncope.common.lib.scim;
+package org.apache.syncope.ext.scimv2.api.data;
+import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import java.io.Serializable;
+import java.util.List;
+import org.apache.syncope.ext.scimv2.api.type.PatchOp;
-public class SCIMComplexConf<E extends Enum<?>> implements Serializable {
+@JsonDeserialize(using = SCIMPatchOperationDeserializer.class)
+public class SCIMPatchOperation extends SCIMBean {
- private static final long serialVersionUID = -6205072829179565927L;
+ private static final long serialVersionUID = 1802654766651071823L;
- private String value;
+ private PatchOp op;
- private String display;
+ private SCIMPatchPath path;
- private E type;
+ private List<Serializable> value;
- private boolean primary;
-
- public String getValue() {
- return value;
- }
-
- public void setValue(final String value) {
- this.value = value;
+ public PatchOp getOp() {
+ return op;
}
- public String getDisplay() {
- return display;
+ public void setOp(final PatchOp op) {
+ this.op = op;
}
- public void setDisplay(final String display) {
- this.display = display;
+ public SCIMPatchPath getPath() {
+ return path;
}
- public E getType() {
- return type;
+ public void setPath(final SCIMPatchPath path) {
+ this.path = path;
}
- public void setType(final E type) {
- this.type = type;
- }
-
- public boolean isPrimary() {
- return primary;
+ public List<Serializable> getValue() {
+ return value;
}
- public void setPrimary(final boolean primary) {
- this.primary = primary;
+ public void setValue(final List<Serializable> value) {
+ this.value = value;
}
-
}
diff --git a/ext/scimv2/scim-rest-api/src/main/java/org/apache/syncope/ext/scimv2/api/data/SCIMPatchOperationDeserializer.java b/ext/scimv2/scim-rest-api/src/main/java/org/apache/syncope/ext/scimv2/api/data/SCIMPatchOperationDeserializer.java
new file mode 100644
index 0000000000..5101bbf3a4
--- /dev/null
+++ b/ext/scimv2/scim-rest-api/src/main/java/org/apache/syncope/ext/scimv2/api/data/SCIMPatchOperationDeserializer.java
@@ -0,0 +1,227 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.syncope.ext.scimv2.api.data;
+
+import com.fasterxml.jackson.core.JsonParser;
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.DeserializationContext;
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.deser.std.StdDeserializer;
+import java.io.IOException;
+import java.io.Serializable;
+import java.util.List;
+import java.util.Optional;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+import org.apache.cxf.common.util.StringUtils;
+import org.apache.syncope.ext.scimv2.api.type.PatchOp;
+
+public class SCIMPatchOperationDeserializer extends StdDeserializer<SCIMPatchOperation> {
+
+ private static final long serialVersionUID = -7401353969242788372L;
+
+ private static final Pattern PATH_PATTERN = Pattern.compile(
+ "^(?<schema>[A-Za-z0-9:.]+:)?(?<attribute>\\w+)(?<filter>\\[.*\\])?(?<sub>\\.\\w+)?");
+
+ private static Serializable scalar(final JsonNode v) {
+ if (v.isNull()) {
+ return null;
+ }
+ if (v.isBoolean()) {
+ return v.booleanValue();
+ }
+ if (v.isFloat()) {
+ return v.floatValue();
+ }
+ if (v.isDouble()) {
+ return v.doubleValue();
+ }
+ if (v.isInt()) {
+ return v.intValue();
+ }
+ if (v.isShort()) {
+ return v.shortValue();
+ }
+ if (v.isLong()) {
+ return v.longValue();
+ }
+
+ return v.asText();
+ }
+
+ public SCIMPatchOperationDeserializer() {
+ this(null);
+ }
+
+ public SCIMPatchOperationDeserializer(final Class<?> vc) {
+ super(vc);
+ }
+
+ @Override
+ public SCIMPatchOperation deserialize(final JsonParser jp, final DeserializationContext ctxt)
+ throws IOException, JsonProcessingException {
+
+ JsonNode node = jp.getCodec().readTree(jp);
+
+ SCIMPatchOperation scimPatchOperation = new SCIMPatchOperation();
+
+ if (node.has("op")) {
+ scimPatchOperation.setOp(PatchOp.valueOf(node.get("op").asText().toLowerCase()));
+ }
+
+ if (node.has("path")) {
+ Matcher matcher = PATH_PATTERN.matcher(node.get("path").asText());
+ if (matcher.matches()) {
+ SCIMPatchPath path = new SCIMPatchPath();
+ scimPatchOperation.setPath(path);
+
+ Optional.ofNullable(matcher.group("schema")).
+ ifPresent(schema -> path.setSchema(schema.substring(0, schema.length() - 1)));
+
+ path.setAttribute(StringUtils.uncapitalize(matcher.group("attribute")));
+
+ Optional.ofNullable(matcher.group("filter")).
+ ifPresent(condition -> path.setFilter(condition.substring(1, condition.length() - 1)));
+
+ Optional.ofNullable(matcher.group("sub")).
+ ifPresent(sub -> path.setSub(StringUtils.uncapitalize(sub.substring(1))));
+ }
+ }
+
+ if (node.has("value")) {
+ JsonNode value = node.get("value");
+
+ if (scimPatchOperation.getPath() == null) {
+ scimPatchOperation.setValue(List.of(jp.getCodec().treeToValue(value, SCIMUser.class)));
+ } else {
+ if ("members".equals(scimPatchOperation.getPath().getAttribute())) {
+ scimPatchOperation.setValue(List.of(
+ (Serializable[]) jp.getCodec().treeToValue(value, Member[].class)));
+ } else if (value.isObject()) {
+ SCIMUser user = new SCIMUser(
+ null,
+ List.of(),
+ null,
+ "userName".equals(scimPatchOperation.getPath().getAttribute()) ? value.asText() : null,
+ "active".equals(scimPatchOperation.getPath().getAttribute()) ? value.asBoolean() : null);
+ user.setEnterpriseInfo(new SCIMEnterpriseInfo());
+
+ switch (scimPatchOperation.getPath().getAttribute()) {
+ case "externalId":
+ user.setExternalId(value.asText());
+ break;
+
+ case "name":
+ user.setName(jp.getCodec().treeToValue(value, SCIMUserName.class));
+ break;
+
+ case "displayName":
+ user.setDisplayName(value.asText());
+ break;
+
+ case "nickName":
+ user.setNickName(value.asText());
+ break;
+
+ case "profileUrl":
+ user.setProfileUrl(value.asText());
+ break;
+
+ case "title":
+ user.setTitle(value.asText());
+ break;
+
+ case "userType":
+ user.setUserType(value.asText());
+ break;
+
+ case "preferredLanguage":
+ user.setPreferredLanguage(value.asText());
+ break;
+
+ case "locale":
+ user.setLocale(value.asText());
+ break;
+
+ case "timezone":
+ user.setTimezone(value.asText());
+ break;
+
+ case "emails":
+ user.getEmails().add(jp.getCodec().treeToValue(value, SCIMComplexValue.class));
+ break;
+
+ case "phoneNumbers":
+ user.getPhoneNumbers().add(jp.getCodec().treeToValue(value, SCIMComplexValue.class));
+ break;
+
+ case "ims":
+ user.getIms().add(jp.getCodec().treeToValue(value, SCIMComplexValue.class));
+ break;
+
+ case "photos":
+ user.getPhotos().add(jp.getCodec().treeToValue(value, SCIMComplexValue.class));
+ break;
+
+ case "addresses":
+ user.getAddresses().add(jp.getCodec().treeToValue(value, SCIMUserAddress.class));
+ break;
+
+ case "x509Certificates":
+ user.getX509Certificates().add(jp.getCodec().treeToValue(value, Value.class));
+ break;
+
+ case "employeeNumber":
+ user.getEnterpriseInfo().setEmployeeNumber(value.asText());
+ break;
+
+ case "costCenter":
+ user.getEnterpriseInfo().setCostCenter(value.asText());
+ break;
+
+ case "organization":
+ user.getEnterpriseInfo().setOrganization(value.asText());
+ break;
+
+ case "division":
+ user.getEnterpriseInfo().setDivision(value.asText());
+ break;
+
+ case "department":
+ user.getEnterpriseInfo().setDepartment(value.asText());
+ break;
+
+ case "manager":
+ user.getEnterpriseInfo().
+ setManager(jp.getCodec().treeToValue(value, SCIMUserManager.class));
+ break;
+
+ default:
+ }
+
+ scimPatchOperation.setValue(List.of(user));
+ } else if (!value.isContainerNode()) {
+ scimPatchOperation.setValue(List.of(scalar(value)));
+ }
+ }
+ }
+
+ return scimPatchOperation;
+ }
+}
diff --git a/ext/scimv2/scim-rest-api/src/main/java/org/apache/syncope/ext/scimv2/api/data/SchemaExtension.java b/ext/scimv2/scim-rest-api/src/main/java/org/apache/syncope/ext/scimv2/api/data/SCIMPatchPath.java
similarity index 56%
copy from ext/scimv2/scim-rest-api/src/main/java/org/apache/syncope/ext/scimv2/api/data/SchemaExtension.java
copy to ext/scimv2/scim-rest-api/src/main/java/org/apache/syncope/ext/scimv2/api/data/SCIMPatchPath.java
index fb2fe2e548..f3873a2676 100644
--- a/ext/scimv2/scim-rest-api/src/main/java/org/apache/syncope/ext/scimv2/api/data/SchemaExtension.java
+++ b/ext/scimv2/scim-rest-api/src/main/java/org/apache/syncope/ext/scimv2/api/data/SCIMPatchPath.java
@@ -18,32 +18,49 @@
*/
package org.apache.syncope.ext.scimv2.api.data;
-import com.fasterxml.jackson.annotation.JsonCreator;
-import com.fasterxml.jackson.annotation.JsonProperty;
+import java.io.Serializable;
-public class SchemaExtension extends SCIMBean {
+public class SCIMPatchPath implements Serializable {
- private static final long serialVersionUID = -2891887818183122384L;
+ private static final long serialVersionUID = -1092849408316419356L;
- private final String schema;
+ private String schema;
- private final boolean required;
+ private String attribute;
- @JsonCreator(mode = JsonCreator.Mode.PROPERTIES)
- public SchemaExtension(
- @JsonProperty("schema") final String schema,
- @JsonProperty("required") final boolean required) {
+ private String filter;
- this.schema = schema;
- this.required = required;
- }
+ private String sub;
public String getSchema() {
return schema;
}
- public boolean isRequired() {
- return required;
+ public void setSchema(final String schema) {
+ this.schema = schema;
+ }
+
+ public String getAttribute() {
+ return attribute;
+ }
+
+ public void setAttribute(final String attribute) {
+ this.attribute = attribute;
}
+ public String getFilter() {
+ return filter;
+ }
+
+ public void setFilter(final String filter) {
+ this.filter = filter;
+ }
+
+ public String getSub() {
+ return sub;
+ }
+
+ public void setSub(final String sub) {
+ this.sub = sub;
+ }
}
diff --git a/ext/scimv2/scim-rest-api/src/main/java/org/apache/syncope/ext/scimv2/api/data/SCIMResource.java b/ext/scimv2/scim-rest-api/src/main/java/org/apache/syncope/ext/scimv2/api/data/SCIMResource.java
index d9bdccc17d..566416f5dd 100644
--- a/ext/scimv2/scim-rest-api/src/main/java/org/apache/syncope/ext/scimv2/api/data/SCIMResource.java
+++ b/ext/scimv2/scim-rest-api/src/main/java/org/apache/syncope/ext/scimv2/api/data/SCIMResource.java
@@ -29,11 +29,13 @@ public abstract class SCIMResource extends SCIMBean {
private String externalId;
+ private String displayName;
+
private final List<String> schemas;
private final Meta meta;
- public SCIMResource(final String id, final List<String> schemas, final Meta meta) {
+ protected SCIMResource(final String id, final List<String> schemas, final Meta meta) {
this.id = id;
this.schemas = Optional.ofNullable(schemas).orElse(List.of());
this.meta = meta;
@@ -59,4 +61,11 @@ public abstract class SCIMResource extends SCIMBean {
return externalId;
}
+ public String getDisplayName() {
+ return displayName;
+ }
+
+ public void setDisplayName(final String displayName) {
+ this.displayName = displayName;
+ }
}
diff --git a/ext/scimv2/scim-rest-api/src/main/java/org/apache/syncope/ext/scimv2/api/data/SCIMSearchRequest.java b/ext/scimv2/scim-rest-api/src/main/java/org/apache/syncope/ext/scimv2/api/data/SCIMSearchRequest.java
index 2322efa4cd..03bd0d7f17 100644
--- a/ext/scimv2/scim-rest-api/src/main/java/org/apache/syncope/ext/scimv2/api/data/SCIMSearchRequest.java
+++ b/ext/scimv2/scim-rest-api/src/main/java/org/apache/syncope/ext/scimv2/api/data/SCIMSearchRequest.java
@@ -92,5 +92,4 @@ public class SCIMSearchRequest extends SCIMBean {
public int getCount() {
return Optional.ofNullable(count).orElse(25);
}
-
}
diff --git a/ext/scimv2/scim-rest-api/src/main/java/org/apache/syncope/ext/scimv2/api/data/SCIMUser.java b/ext/scimv2/scim-rest-api/src/main/java/org/apache/syncope/ext/scimv2/api/data/SCIMUser.java
index 775b1ad721..08120534e9 100644
--- a/ext/scimv2/scim-rest-api/src/main/java/org/apache/syncope/ext/scimv2/api/data/SCIMUser.java
+++ b/ext/scimv2/scim-rest-api/src/main/java/org/apache/syncope/ext/scimv2/api/data/SCIMUser.java
@@ -19,10 +19,12 @@
package org.apache.syncope.ext.scimv2.api.data;
import com.fasterxml.jackson.annotation.JsonCreator;
+import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.annotation.JsonPropertyOrder;
import java.util.ArrayList;
import java.util.List;
+import java.util.Optional;
@JsonPropertyOrder({ "schemas", "id", "externalId",
"userName", "password", "active",
@@ -39,12 +41,10 @@ public class SCIMUser extends SCIMResource {
private String password;
- private final boolean active;
+ private final Boolean active;
private SCIMUserName name;
- private String displayName;
-
private String nickName;
private String profileUrl;
@@ -86,7 +86,7 @@ public class SCIMUser extends SCIMResource {
@JsonProperty("schemas") final List<String> schemas,
@JsonProperty("meta") final Meta meta,
@JsonProperty("userName") final String userName,
- @JsonProperty("active") final boolean active) {
+ @JsonProperty("active") final Boolean active) {
super(id, schemas, meta);
this.userName = userName;
@@ -105,10 +105,15 @@ public class SCIMUser extends SCIMResource {
return password;
}
- public boolean isActive() {
+ @JsonIgnore
+ public Boolean getActive() {
return active;
}
+ public boolean isActive() {
+ return Optional.ofNullable(active).orElse(true);
+ }
+
public SCIMUserName getName() {
return name;
}
@@ -117,14 +122,6 @@ public class SCIMUser extends SCIMResource {
this.name = name;
}
- public String getDisplayName() {
- return displayName;
- }
-
- public void setDisplayName(final String displayName) {
- this.displayName = displayName;
- }
-
public String getNickName() {
return nickName;
}
@@ -224,5 +221,4 @@ public class SCIMUser extends SCIMResource {
public void setEnterpriseInfo(final SCIMEnterpriseInfo enterpriseInfo) {
this.enterpriseInfo = enterpriseInfo;
}
-
}
diff --git a/ext/scimv2/scim-rest-api/src/main/java/org/apache/syncope/ext/scimv2/api/data/SchemaExtension.java b/ext/scimv2/scim-rest-api/src/main/java/org/apache/syncope/ext/scimv2/api/data/SchemaExtension.java
index fb2fe2e548..ae74926f9c 100644
--- a/ext/scimv2/scim-rest-api/src/main/java/org/apache/syncope/ext/scimv2/api/data/SchemaExtension.java
+++ b/ext/scimv2/scim-rest-api/src/main/java/org/apache/syncope/ext/scimv2/api/data/SchemaExtension.java
@@ -45,5 +45,4 @@ public class SchemaExtension extends SCIMBean {
public boolean isRequired() {
return required;
}
-
}
diff --git a/ext/scimv2/scim-rest-api/src/main/java/org/apache/syncope/ext/scimv2/api/data/ServiceProviderConfig.java b/ext/scimv2/scim-rest-api/src/main/java/org/apache/syncope/ext/scimv2/api/data/ServiceProviderConfig.java
index a65775d87a..9e03c96c28 100644
--- a/ext/scimv2/scim-rest-api/src/main/java/org/apache/syncope/ext/scimv2/api/data/ServiceProviderConfig.java
+++ b/ext/scimv2/scim-rest-api/src/main/java/org/apache/syncope/ext/scimv2/api/data/ServiceProviderConfig.java
@@ -103,5 +103,4 @@ public class ServiceProviderConfig extends SCIMBean {
public List<AuthenticationScheme> getAuthenticationSchemes() {
return authenticationSchemes;
}
-
}
diff --git a/ext/scimv2/scim-rest-api/src/main/java/org/apache/syncope/ext/scimv2/api/data/Value.java b/ext/scimv2/scim-rest-api/src/main/java/org/apache/syncope/ext/scimv2/api/data/Value.java
index 543f0003b2..e68a8108fd 100644
--- a/ext/scimv2/scim-rest-api/src/main/java/org/apache/syncope/ext/scimv2/api/data/Value.java
+++ b/ext/scimv2/scim-rest-api/src/main/java/org/apache/syncope/ext/scimv2/api/data/Value.java
@@ -35,5 +35,4 @@ public class Value extends SCIMBean {
public String getValue() {
return value;
}
-
}
diff --git a/ext/scimv2/scim-rest-api/src/main/java/org/apache/syncope/ext/scimv2/api/service/GroupService.java b/ext/scimv2/scim-rest-api/src/main/java/org/apache/syncope/ext/scimv2/api/service/GroupService.java
deleted file mode 100644
index 8010218a75..0000000000
--- a/ext/scimv2/scim-rest-api/src/main/java/org/apache/syncope/ext/scimv2/api/service/GroupService.java
+++ /dev/null
@@ -1,58 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package org.apache.syncope.ext.scimv2.api.service;
-
-import javax.validation.constraints.NotNull;
-import javax.ws.rs.Consumes;
-import javax.ws.rs.DELETE;
-import javax.ws.rs.POST;
-import javax.ws.rs.PUT;
-import javax.ws.rs.Path;
-import javax.ws.rs.PathParam;
-import javax.ws.rs.Produces;
-import javax.ws.rs.core.Response;
-import org.apache.cxf.jaxrs.ext.PATCH;
-import org.apache.syncope.ext.scimv2.api.SCIMConstants;
-import org.apache.syncope.ext.scimv2.api.data.SCIMGroup;
-
-@Path("v2/Groups")
-public interface GroupService extends ReadService<SCIMGroup> {
-
- @POST
- @Produces({ SCIMConstants.APPLICATION_SCIM_JSON })
- @Consumes({ SCIMConstants.APPLICATION_SCIM_JSON })
- Response create(SCIMGroup group);
-
- @PATCH
- @Path("{id}")
- @Produces({ SCIMConstants.APPLICATION_SCIM_JSON })
- @Consumes({ SCIMConstants.APPLICATION_SCIM_JSON })
- Response update(@NotNull @PathParam("id") String id);
-
- @PUT
- @Path("{id}")
- @Produces({ SCIMConstants.APPLICATION_SCIM_JSON })
- @Consumes({ SCIMConstants.APPLICATION_SCIM_JSON })
- Response replace(@NotNull @PathParam("id") String id, SCIMGroup group);
-
- @DELETE
- @Path("{id}")
- @Produces({ SCIMConstants.APPLICATION_SCIM_JSON })
- Response delete(@NotNull @PathParam("id") String id);
-}
diff --git a/ext/scimv2/scim-rest-api/src/main/java/org/apache/syncope/ext/scimv2/api/data/Value.java b/ext/scimv2/scim-rest-api/src/main/java/org/apache/syncope/ext/scimv2/api/service/SCIMGroupService.java
similarity index 61%
copy from ext/scimv2/scim-rest-api/src/main/java/org/apache/syncope/ext/scimv2/api/data/Value.java
copy to ext/scimv2/scim-rest-api/src/main/java/org/apache/syncope/ext/scimv2/api/service/SCIMGroupService.java
index 543f0003b2..9bb259de22 100644
--- a/ext/scimv2/scim-rest-api/src/main/java/org/apache/syncope/ext/scimv2/api/data/Value.java
+++ b/ext/scimv2/scim-rest-api/src/main/java/org/apache/syncope/ext/scimv2/api/service/SCIMGroupService.java
@@ -16,24 +16,11 @@
* specific language governing permissions and limitations
* under the License.
*/
-package org.apache.syncope.ext.scimv2.api.data;
+package org.apache.syncope.ext.scimv2.api.service;
-import com.fasterxml.jackson.annotation.JsonCreator;
-import com.fasterxml.jackson.annotation.JsonProperty;
-
-public class Value extends SCIMBean {
-
- private static final long serialVersionUID = 5337055958765320091L;
-
- private final String value;
-
- @JsonCreator(mode = JsonCreator.Mode.PROPERTIES)
- public Value(@JsonProperty("value") final String value) {
- this.value = value;
- }
-
- public String getValue() {
- return value;
- }
+import javax.ws.rs.Path;
+import org.apache.syncope.ext.scimv2.api.data.SCIMGroup;
+@Path("v2/Groups")
+public interface SCIMGroupService extends SCIMResourceService<SCIMGroup> {
}
diff --git a/ext/scimv2/scim-rest-api/src/main/java/org/apache/syncope/ext/scimv2/api/service/ReadService.java b/ext/scimv2/scim-rest-api/src/main/java/org/apache/syncope/ext/scimv2/api/service/SCIMResourceService.java
similarity index 70%
rename from ext/scimv2/scim-rest-api/src/main/java/org/apache/syncope/ext/scimv2/api/service/ReadService.java
rename to ext/scimv2/scim-rest-api/src/main/java/org/apache/syncope/ext/scimv2/api/service/SCIMResourceService.java
index 4762dd3b8a..38c663ee12 100644
--- a/ext/scimv2/scim-rest-api/src/main/java/org/apache/syncope/ext/scimv2/api/service/ReadService.java
+++ b/ext/scimv2/scim-rest-api/src/main/java/org/apache/syncope/ext/scimv2/api/service/SCIMResourceService.java
@@ -20,19 +20,24 @@ package org.apache.syncope.ext.scimv2.api.service;
import javax.validation.constraints.NotNull;
import javax.ws.rs.Consumes;
+import javax.ws.rs.DELETE;
import javax.ws.rs.GET;
import javax.ws.rs.POST;
+import javax.ws.rs.PUT;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.QueryParam;
+import javax.ws.rs.core.Response;
+import org.apache.cxf.jaxrs.ext.PATCH;
import org.apache.syncope.ext.scimv2.api.SCIMConstants;
import org.apache.syncope.ext.scimv2.api.data.ListResponse;
+import org.apache.syncope.ext.scimv2.api.data.SCIMPatchOp;
import org.apache.syncope.ext.scimv2.api.data.SCIMResource;
import org.apache.syncope.ext.scimv2.api.data.SCIMSearchRequest;
import org.apache.syncope.ext.scimv2.api.type.SortOrder;
-public interface ReadService<R extends SCIMResource> {
+public interface SCIMResourceService<R extends SCIMResource> {
@GET
@Path("{id}")
@@ -57,4 +62,26 @@ public interface ReadService<R extends SCIMResource> {
@Produces({ SCIMConstants.APPLICATION_SCIM_JSON })
@Consumes({ SCIMConstants.APPLICATION_SCIM_JSON })
ListResponse<R> search(SCIMSearchRequest request);
+
+ @POST
+ @Produces({ SCIMConstants.APPLICATION_SCIM_JSON })
+ @Consumes({ SCIMConstants.APPLICATION_SCIM_JSON })
+ Response create(R resource);
+
+ @PATCH
+ @Path("{id}")
+ @Produces({ SCIMConstants.APPLICATION_SCIM_JSON })
+ @Consumes({ SCIMConstants.APPLICATION_SCIM_JSON })
+ Response update(@NotNull @PathParam("id") String id, SCIMPatchOp patch);
+
+ @PUT
+ @Path("{id}")
+ @Produces({ SCIMConstants.APPLICATION_SCIM_JSON })
+ @Consumes({ SCIMConstants.APPLICATION_SCIM_JSON })
+ Response replace(@NotNull @PathParam("id") String id, R resource);
+
+ @DELETE
+ @Path("{id}")
+ @Produces({ SCIMConstants.APPLICATION_SCIM_JSON })
+ Response delete(@NotNull @PathParam("id") String id);
}
diff --git a/ext/scimv2/scim-rest-api/src/main/java/org/apache/syncope/ext/scimv2/api/data/Value.java b/ext/scimv2/scim-rest-api/src/main/java/org/apache/syncope/ext/scimv2/api/service/SCIMUserService.java
similarity index 61%
copy from ext/scimv2/scim-rest-api/src/main/java/org/apache/syncope/ext/scimv2/api/data/Value.java
copy to ext/scimv2/scim-rest-api/src/main/java/org/apache/syncope/ext/scimv2/api/service/SCIMUserService.java
index 543f0003b2..d71c6bfcb5 100644
--- a/ext/scimv2/scim-rest-api/src/main/java/org/apache/syncope/ext/scimv2/api/data/Value.java
+++ b/ext/scimv2/scim-rest-api/src/main/java/org/apache/syncope/ext/scimv2/api/service/SCIMUserService.java
@@ -16,24 +16,11 @@
* specific language governing permissions and limitations
* under the License.
*/
-package org.apache.syncope.ext.scimv2.api.data;
+package org.apache.syncope.ext.scimv2.api.service;
-import com.fasterxml.jackson.annotation.JsonCreator;
-import com.fasterxml.jackson.annotation.JsonProperty;
-
-public class Value extends SCIMBean {
-
- private static final long serialVersionUID = 5337055958765320091L;
-
- private final String value;
-
- @JsonCreator(mode = JsonCreator.Mode.PROPERTIES)
- public Value(@JsonProperty("value") final String value) {
- this.value = value;
- }
-
- public String getValue() {
- return value;
- }
+import javax.ws.rs.Path;
+import org.apache.syncope.ext.scimv2.api.data.SCIMUser;
+@Path("v2/Users")
+public interface SCIMUserService extends SCIMResourceService<SCIMUser> {
}
diff --git a/ext/scimv2/scim-rest-api/src/main/java/org/apache/syncope/ext/scimv2/api/service/UserService.java b/ext/scimv2/scim-rest-api/src/main/java/org/apache/syncope/ext/scimv2/api/service/UserService.java
deleted file mode 100644
index 62ddc21be1..0000000000
--- a/ext/scimv2/scim-rest-api/src/main/java/org/apache/syncope/ext/scimv2/api/service/UserService.java
+++ /dev/null
@@ -1,59 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package org.apache.syncope.ext.scimv2.api.service;
-
-import javax.validation.constraints.NotNull;
-import javax.ws.rs.Consumes;
-import javax.ws.rs.DELETE;
-import javax.ws.rs.POST;
-import javax.ws.rs.PUT;
-import javax.ws.rs.Path;
-import javax.ws.rs.PathParam;
-import javax.ws.rs.Produces;
-import javax.ws.rs.core.Response;
-import org.apache.cxf.jaxrs.ext.PATCH;
-import org.apache.syncope.ext.scimv2.api.SCIMConstants;
-import org.apache.syncope.ext.scimv2.api.data.SCIMUser;
-
-@Path("v2/Users")
-public interface UserService extends ReadService<SCIMUser> {
-
- @POST
- @Produces({ SCIMConstants.APPLICATION_SCIM_JSON })
- @Consumes({ SCIMConstants.APPLICATION_SCIM_JSON })
- Response create(SCIMUser user);
-
- @PATCH
- @Path("{id}")
- @Produces({ SCIMConstants.APPLICATION_SCIM_JSON })
- @Consumes({ SCIMConstants.APPLICATION_SCIM_JSON })
- Response update(@NotNull @PathParam("id") String id);
-
- @PUT
- @Path("{id}")
- @Produces({ SCIMConstants.APPLICATION_SCIM_JSON })
- @Consumes({ SCIMConstants.APPLICATION_SCIM_JSON })
- Response replace(@NotNull @PathParam("id") String id, SCIMUser user);
-
- @DELETE
- @Path("{id}")
- @Produces({ SCIMConstants.APPLICATION_SCIM_JSON })
- Response delete(@NotNull @PathParam("id") String id);
-
-}
diff --git a/ext/scimv2/scim-rest-api/src/main/java/org/apache/syncope/ext/scimv2/api/data/Value.java b/ext/scimv2/scim-rest-api/src/main/java/org/apache/syncope/ext/scimv2/api/type/PatchOp.java
similarity index 61%
copy from ext/scimv2/scim-rest-api/src/main/java/org/apache/syncope/ext/scimv2/api/data/Value.java
copy to ext/scimv2/scim-rest-api/src/main/java/org/apache/syncope/ext/scimv2/api/type/PatchOp.java
index 543f0003b2..d28c496475 100644
--- a/ext/scimv2/scim-rest-api/src/main/java/org/apache/syncope/ext/scimv2/api/data/Value.java
+++ b/ext/scimv2/scim-rest-api/src/main/java/org/apache/syncope/ext/scimv2/api/type/PatchOp.java
@@ -16,24 +16,11 @@
* specific language governing permissions and limitations
* under the License.
*/
-package org.apache.syncope.ext.scimv2.api.data;
+package org.apache.syncope.ext.scimv2.api.type;
-import com.fasterxml.jackson.annotation.JsonCreator;
-import com.fasterxml.jackson.annotation.JsonProperty;
-
-public class Value extends SCIMBean {
-
- private static final long serialVersionUID = 5337055958765320091L;
-
- private final String value;
-
- @JsonCreator(mode = JsonCreator.Mode.PROPERTIES)
- public Value(@JsonProperty("value") final String value) {
- this.value = value;
- }
-
- public String getValue() {
- return value;
- }
+public enum PatchOp {
+ add,
+ replace,
+ remove;
}
diff --git a/ext/scimv2/scim-rest-api/src/main/java/org/apache/syncope/ext/scimv2/api/type/Resource.java b/ext/scimv2/scim-rest-api/src/main/java/org/apache/syncope/ext/scimv2/api/type/Resource.java
index 7800b68d02..b26382383f 100644
--- a/ext/scimv2/scim-rest-api/src/main/java/org/apache/syncope/ext/scimv2/api/type/Resource.java
+++ b/ext/scimv2/scim-rest-api/src/main/java/org/apache/syncope/ext/scimv2/api/type/Resource.java
@@ -28,6 +28,7 @@ public enum Resource {
Group("urn:ietf:params:scim:schemas:core:2.0:Group"),
SearchRequest("urn:ietf:params:scim:api:messages:2.0:SearchRequest"),
ListResponse("urn:ietf:params:scim:api:messages:2.0:ListResponse"),
+ PatchOp("urn:ietf:params:scim:api:messages:2.0:PatchOp"),
Error("urn:ietf:params:scim:api:messages:2.0:Error");
private final String schema;
diff --git a/ext/scimv2/scim-rest-api/src/test/java/org/apache/syncope/ext/scimv2/api/data/SCIMPatchOperationDeserializerTest.java b/ext/scimv2/scim-rest-api/src/test/java/org/apache/syncope/ext/scimv2/api/data/SCIMPatchOperationDeserializerTest.java
new file mode 100644
index 0000000000..668c9e5bcf
--- /dev/null
+++ b/ext/scimv2/scim-rest-api/src/test/java/org/apache/syncope/ext/scimv2/api/data/SCIMPatchOperationDeserializerTest.java
@@ -0,0 +1,349 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.syncope.ext.scimv2.api.data;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertNull;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.SerializationFeature;
+import com.fasterxml.jackson.databind.json.JsonMapper;
+import java.util.List;
+import org.apache.syncope.ext.scimv2.api.type.PatchOp;
+import org.apache.syncope.ext.scimv2.api.type.Resource;
+import org.junit.jupiter.api.Test;
+
+public class SCIMPatchOperationDeserializerTest {
+
+ private static final JsonMapper MAPPER = JsonMapper.builder().
+ findAndAddModules().disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS).build();
+
+ @Test
+ public void addMember() throws JsonProcessingException {
+ String input =
+ "{ "
+ + "\"schemas\":[\"urn:ietf:params:scim:api:messages:2.0:PatchOp\"],"
+ + " \"Operations\":["
+ + " {"
+ + " \"op\":\"add\","
+ + " \"path\":\"members\","
+ + " \"value\":["
+ + " {"
+ + " \"display\": \"Babs Jensen\","
+ + " \"$ref\": \"https://example.com/v2/Users/2819c223...413861904646\","
+ + " \"value\": \"2819c223-7f76-453a-919d-413861904646\""
+ + " }"
+ + " ]"
+ + " }"
+ + " ]"
+ + " }";
+
+ SCIMPatchOp scimPatchOp = MAPPER.readValue(input, SCIMPatchOp.class);
+ assertNotNull(scimPatchOp);
+ assertEquals(List.of(Resource.PatchOp.schema()), scimPatchOp.getSchemas());
+ assertEquals(1, scimPatchOp.getOperations().size());
+
+ SCIMPatchOperation op = scimPatchOp.getOperations().get(0);
+ assertNotNull(op);
+ assertEquals(PatchOp.add, op.getOp());
+ assertEquals("members", op.getPath().getAttribute());
+
+ assertEquals(1, op.getValue().size());
+
+ Member member = (Member) op.getValue().get(0);
+ assertEquals("Babs Jensen", member.getDisplay());
+ assertEquals("https://example.com/v2/Users/2819c223...413861904646", member.getRef());
+ assertEquals("2819c223-7f76-453a-919d-413861904646", member.getValue());
+ }
+
+ @Test
+ public void removeMembers() throws JsonProcessingException {
+ String input =
+ "{"
+ + " \"schemas\":[\"urn:ietf:params:scim:api:messages:2.0:PatchOp\"],"
+ + " \"Operations\":[{"
+ + " \"op\":\"remove\","
+ + " \"path\":\"members[value eq \\\"2819c223-7f76-...413861904646\\\"]\""
+ + " }]"
+ + " }";
+
+ SCIMPatchOp scimPatchOp = MAPPER.readValue(input, SCIMPatchOp.class);
+ assertNotNull(scimPatchOp);
+ assertEquals(List.of(Resource.PatchOp.schema()), scimPatchOp.getSchemas());
+ assertEquals(1, scimPatchOp.getOperations().size());
+
+ SCIMPatchOperation op = scimPatchOp.getOperations().get(0);
+ assertNotNull(op);
+ assertEquals(PatchOp.remove, op.getOp());
+ assertEquals("members", op.getPath().getAttribute());
+ assertEquals("value eq \"2819c223-7f76-...413861904646\"", op.getPath().getFilter());
+ }
+
+ @Test
+ public void removeAndAddMembers() throws JsonProcessingException {
+ String input =
+ "{ \"schemas\":[\"urn:ietf:params:scim:api:messages:2.0:PatchOp\"],"
+ + " \"Operations\": ["
+ + " {"
+ + " \"op\":\"remove\","
+ + " \"path\":"
+ + " \"members[value eq \\\"2819c223...919d-413861904646\\\"]\""
+ + " },"
+ + " {"
+ + " \"op\":\"add\","
+ + " \"path\":\"members\","
+ + " \"value\": ["
+ + " {"
+ + " \"display\": \"Babs Jensen\","
+ + " \"$ref\":\"https://example.com/v2/Users/2819c223...413861904646\","
+ + " \"value\": \"2819c223-7f76-453a-919d-413861904646\""
+ + " },"
+ + " {"
+ + " \"display\": \"James Smith\","
+ + " \"$ref\":\"https://example.com/v2/Users/08e1d05d...473d93df9210\","
+ + " \"value\": \"08e1d05d...473d93df9210\""
+ + " }"
+ + " ]"
+ + " }"
+ + " ]"
+ + " }";
+
+ SCIMPatchOp scimPatchOp = MAPPER.readValue(input, SCIMPatchOp.class);
+ assertNotNull(scimPatchOp);
+ assertEquals(List.of(Resource.PatchOp.schema()), scimPatchOp.getSchemas());
+ assertEquals(2, scimPatchOp.getOperations().size());
+
+ SCIMPatchOperation op = scimPatchOp.getOperations().get(0);
+ assertNotNull(op);
+ assertEquals(PatchOp.remove, op.getOp());
+ assertEquals("members", op.getPath().getAttribute());
+ assertEquals("value eq \"2819c223...919d-413861904646\"", op.getPath().getFilter());
+
+ op = scimPatchOp.getOperations().get(1);
+ assertNotNull(op);
+ assertEquals(PatchOp.add, op.getOp());
+ assertEquals("members", op.getPath().getAttribute());
+
+ assertEquals(2, op.getValue().size());
+
+ Member member = (Member) op.getValue().get(0);
+ assertEquals("Babs Jensen", member.getDisplay());
+ assertEquals("https://example.com/v2/Users/2819c223...413861904646", member.getRef());
+ assertEquals("2819c223-7f76-453a-919d-413861904646", member.getValue());
+
+ member = (Member) op.getValue().get(1);
+ assertEquals("James Smith", member.getDisplay());
+ assertEquals("https://example.com/v2/Users/08e1d05d...473d93df9210", member.getRef());
+ assertEquals("08e1d05d...473d93df9210", member.getValue());
+ }
+
+ @Test
+ public void addAttributes() throws JsonProcessingException {
+ String input =
+ "{"
+ + " \"schemas\":[\"urn:ietf:params:scim:api:messages:2.0:PatchOp\"],"
+ + " \"Operations\": ["
+ + " {"
+ + " \"op\": \"add\","
+ + " \"value\": {"
+ + " \"emails\": ["
+ + " {"
+ + " \"value\": \"babs@jensen.org\","
+ + " \"type\": \"home\""
+ + " }"
+ + " ],"
+ + " \"nickName\": \"Babs\""
+ + " }"
+ + " }"
+ + " ]"
+ + "}";
+
+ SCIMPatchOp scimPatchOp = MAPPER.readValue(input, SCIMPatchOp.class);
+ assertNotNull(scimPatchOp);
+ assertEquals(List.of(Resource.PatchOp.schema()), scimPatchOp.getSchemas());
+ assertEquals(1, scimPatchOp.getOperations().size());
+
+ SCIMPatchOperation op = scimPatchOp.getOperations().get(0);
+ assertNotNull(op);
+ assertEquals(PatchOp.add, op.getOp());
+ assertNull(op.getPath());
+
+ assertEquals(1, op.getValue().size());
+
+ SCIMUser user = (SCIMUser) op.getValue().get(0);
+ assertEquals("Babs", user.getNickName());
+
+ assertEquals(1, user.getEmails().size());
+
+ SCIMComplexValue email = user.getEmails().get(0);
+ assertEquals("home", email.getType());
+ assertEquals("babs@jensen.org", email.getValue());
+ }
+
+ @Test
+ public void replaceMembers() throws JsonProcessingException {
+ String input =
+ " {"
+ + " \"schemas\":[\"urn:ietf:params:scim:api:messages:2.0:PatchOp\"],"
+ + " \"Operations\": [{"
+ + " \"op\":\"replace\","
+ + " \"path\":\"members\","
+ + " \"value\":["
+ + " {"
+ + " \"display\": \"Babs Jensen\","
+ + " \"$ref\": \"https://example.com/v2/Users/2819c223...413861904646\","
+ + " \"value\": \"2819c223...413861904646\""
+ + " },"
+ + " {"
+ + " \"display\": \"James Smith\","
+ + " \"$ref\":\"https://example.com/v2/Users/08e1d05d...473d93df9210\","
+ + " \"value\": \"08e1d05d...473d93df9210\""
+ + " }"
+ + " ]"
+ + " }]"
+ + " }";
+
+ SCIMPatchOp scimPatchOp = MAPPER.readValue(input, SCIMPatchOp.class);
+ assertNotNull(scimPatchOp);
+ assertEquals(List.of(Resource.PatchOp.schema()), scimPatchOp.getSchemas());
+ assertEquals(1, scimPatchOp.getOperations().size());
+
+ SCIMPatchOperation op = scimPatchOp.getOperations().get(0);
+ assertNotNull(op);
+ assertEquals(PatchOp.replace, op.getOp());
+ assertEquals("members", op.getPath().getAttribute());
+
+ assertEquals(2, op.getValue().size());
+
+ Member member = (Member) op.getValue().get(0);
+ assertEquals("Babs Jensen", member.getDisplay());
+ assertEquals("https://example.com/v2/Users/2819c223...413861904646", member.getRef());
+ assertEquals("2819c223...413861904646", member.getValue());
+
+ member = (Member) op.getValue().get(1);
+ assertEquals("James Smith", member.getDisplay());
+ assertEquals("https://example.com/v2/Users/08e1d05d...473d93df9210", member.getRef());
+ assertEquals("08e1d05d...473d93df9210", member.getValue());
+ }
+
+ @Test
+ public void replaceAttribute() throws JsonProcessingException {
+ String input =
+ "{"
+ + " \"schemas\": [\"urn:ietf:params:scim:api:messages:2.0:PatchOp\"],"
+ + " \"Operations\": [{"
+ + " \"op\":\"replace\","
+ + " \"path\":\"addresses[type eq \\\"work\\\"]\","
+ + " \"value\":"
+ + " {"
+ + " \"type\": \"work\","
+ + " \"streetAddress\": \"911 Universal City Plaza\","
+ + " \"locality\": \"Hollywood\","
+ + " \"region\": \"CA\","
+ + " \"postalCode\": \"91608\","
+ + " \"country\": \"US\","
+ + " \"formatted\":\"911 Universal City Plaza\\nHollywood, CA 91608 US\","
+ + " \"primary\": true"
+ + " }"
+ + " }]"
+ + " }";
+
+ SCIMPatchOp scimPatchOp = MAPPER.readValue(input, SCIMPatchOp.class);
+ assertNotNull(scimPatchOp);
+ assertEquals(List.of(Resource.PatchOp.schema()), scimPatchOp.getSchemas());
+ assertEquals(1, scimPatchOp.getOperations().size());
+
+ SCIMPatchOperation op = scimPatchOp.getOperations().get(0);
+ assertNotNull(op);
+ assertEquals(PatchOp.replace, op.getOp());
+ assertEquals("addresses", op.getPath().getAttribute());
+ assertEquals("type eq \"work\"", op.getPath().getFilter());
+
+ assertEquals(1, op.getValue().size());
+
+ SCIMUser user = (SCIMUser) op.getValue().get(0);
+
+ assertEquals(1, user.getAddresses().size());
+
+ SCIMUserAddress address = user.getAddresses().get(0);
+ assertEquals("work", address.getType());
+ assertTrue(address.isPrimary());
+ }
+
+ @Test
+ public void replaceAttributeValue() throws JsonProcessingException {
+ String input =
+ "{"
+ + " \"schemas\": [\"urn:ietf:params:scim:api:messages:2.0:PatchOp\"],"
+ + " \"Operations\": [{"
+ + " \"op\":\"replace\","
+ + " \"path\":\"addresses[type eq \\\"work\\\"].streetAddress\","
+ + " \"value\":\"1010 Broadway Ave\""
+ + " }]"
+ + " }";
+
+ SCIMPatchOp scimPatchOp = MAPPER.readValue(input, SCIMPatchOp.class);
+ assertNotNull(scimPatchOp);
+ assertEquals(List.of(Resource.PatchOp.schema()), scimPatchOp.getSchemas());
+ assertEquals(1, scimPatchOp.getOperations().size());
+
+ SCIMPatchOperation op = scimPatchOp.getOperations().get(0);
+ assertNotNull(op);
+ assertEquals(PatchOp.replace, op.getOp());
+ assertEquals("addresses", op.getPath().getAttribute());
+ assertEquals("type eq \"work\"", op.getPath().getFilter());
+ assertEquals("streetAddress", op.getPath().getSub());
+
+ assertEquals(1, op.getValue().size());
+
+ String value = (String) op.getValue().get(0);
+ assertEquals("1010 Broadway Ave", value);
+ }
+
+ @Test
+ public void addAttributeValue() throws JsonProcessingException {
+ String input =
+ "{"
+ + " \"schemas\": [\"urn:ietf:params:scim:api:messages:2.0:PatchOp\"],"
+ + " \"Operations\": [{"
+ + " \"op\": \"Add\","
+ + " \"path\": \"externalId\","
+ + " \"value\": \"da91cb5d-addb-424f-aa41-799fda8658c3\""
+ + " }"
+ + " ]"
+ + "}";
+
+ SCIMPatchOp scimPatchOp = MAPPER.readValue(input, SCIMPatchOp.class);
+ assertNotNull(scimPatchOp);
+ assertEquals(List.of(Resource.PatchOp.schema()), scimPatchOp.getSchemas());
+ assertEquals(1, scimPatchOp.getOperations().size());
+
+ SCIMPatchOperation op = scimPatchOp.getOperations().get(0);
+ assertNotNull(op);
+ assertEquals(PatchOp.add, op.getOp());
+ assertEquals("externalId", op.getPath().getAttribute());
+
+ assertEquals(1, op.getValue().size());
+
+ String value = (String) op.getValue().get(0);
+ assertEquals("da91cb5d-addb-424f-aa41-799fda8658c3", value);
+ }
+}
diff --git a/ext/scimv2/scim-rest-cxf/src/main/java/org/apache/syncope/ext/scimv2/cxf/SCIMExceptionMapper.java b/ext/scimv2/scim-rest-cxf/src/main/java/org/apache/syncope/ext/scimv2/cxf/SCIMExceptionMapper.java
index 1ab41f09b7..8af56d716c 100644
--- a/ext/scimv2/scim-rest-cxf/src/main/java/org/apache/syncope/ext/scimv2/cxf/SCIMExceptionMapper.java
+++ b/ext/scimv2/scim-rest-cxf/src/main/java/org/apache/syncope/ext/scimv2/cxf/SCIMExceptionMapper.java
@@ -53,19 +53,19 @@ import org.springframework.transaction.TransactionSystemException;
@Provider
public class SCIMExceptionMapper implements ExceptionMapper<Exception> {
- private static final Logger LOG = LoggerFactory.getLogger(SCIMExceptionMapper.class);
+ protected static final Logger LOG = LoggerFactory.getLogger(SCIMExceptionMapper.class);
- private static Class<?> ENTITYEXISTS_EXCLASS = null;
+ protected static Class<?> ENTITYEXISTS_EXCLASS = null;
- private static Class<?> PERSISTENCE_EXCLASS = null;
+ protected static Class<?> PERSISTENCE_EXCLASS = null;
- private static Class<?> ROLLBACK_EXCLASS = null;
+ protected static Class<?> ROLLBACK_EXCLASS = null;
- private static Class<?> JPASYSTEM_EXCLASS = null;
+ protected static Class<?> JPASYSTEM_EXCLASS = null;
- private static Class<?> CONNECTOR_EXCLASS = null;
+ protected static Class<?> CONNECTOR_EXCLASS = null;
- private static Class<?> IBATISPERSISTENCE_EXCLASS = null;
+ protected static Class<?> IBATISPERSISTENCE_EXCLASS = null;
static {
try {
@@ -122,6 +122,14 @@ public class SCIMExceptionMapper implements ExceptionMapper<Exception> {
if (builder == null && ex instanceof ValidationException) {
builder = builder(ClientExceptionType.RESTValidation, ExceptionUtils.getRootCauseMessage(ex));
}
+ // process requests for features not yet implemented
+ if (builder == null && ex instanceof UnsupportedOperationException) {
+ builder = Response.status(Response.Status.INTERNAL_SERVER_ERROR).
+ entity(new SCIMError(
+ ErrorType.invalidSyntax,
+ Response.Status.INTERNAL_SERVER_ERROR.getStatusCode(),
+ ExceptionUtils.getRootCauseMessage(ex)));
+ }
// ...or just report as InternalServerError
if (builder == null) {
builder = Response.status(Response.Status.INTERNAL_SERVER_ERROR).
@@ -132,7 +140,7 @@ public class SCIMExceptionMapper implements ExceptionMapper<Exception> {
return Optional.ofNullable(builder).map(ResponseBuilder::build).orElse(null);
}
- private static ResponseBuilder processInvalidEntityExceptions(final Exception ex) {
+ protected ResponseBuilder processInvalidEntityExceptions(final Exception ex) {
InvalidEntityException iee = null;
if (ex instanceof InvalidEntityException) {
@@ -173,7 +181,7 @@ public class SCIMExceptionMapper implements ExceptionMapper<Exception> {
return null;
}
- private static ResponseBuilder processBadRequestExceptions(final Exception ex) {
+ protected ResponseBuilder processBadRequestExceptions(final Exception ex) {
if (ex instanceof WorkflowException) {
return builder(ClientExceptionType.Workflow, ExceptionUtils.getRootCauseMessage(ex));
} else if (PERSISTENCE_EXCLASS.isAssignableFrom(ex.getClass())) {
@@ -195,7 +203,7 @@ public class SCIMExceptionMapper implements ExceptionMapper<Exception> {
return null;
}
- private static ResponseBuilder builder(final ClientExceptionType hType, final String msg) {
+ protected ResponseBuilder builder(final ClientExceptionType hType, final String msg) {
ResponseBuilder builder = Response.status(hType.getResponseStatus());
ErrorType scimType = null;
diff --git a/ext/scimv2/scim-rest-cxf/src/main/java/org/apache/syncope/ext/scimv2/cxf/SCIMv2RESTCXFContext.java b/ext/scimv2/scim-rest-cxf/src/main/java/org/apache/syncope/ext/scimv2/cxf/SCIMv2RESTCXFContext.java
index f50dce7ae3..1a3e8eeb51 100644
--- a/ext/scimv2/scim-rest-cxf/src/main/java/org/apache/syncope/ext/scimv2/cxf/SCIMv2RESTCXFContext.java
+++ b/ext/scimv2/scim-rest-cxf/src/main/java/org/apache/syncope/ext/scimv2/cxf/SCIMv2RESTCXFContext.java
@@ -36,12 +36,12 @@ import org.apache.syncope.core.logic.UserLogic;
import org.apache.syncope.core.logic.scim.SCIMConfManager;
import org.apache.syncope.core.persistence.api.dao.GroupDAO;
import org.apache.syncope.core.persistence.api.dao.UserDAO;
-import org.apache.syncope.ext.scimv2.api.service.GroupService;
+import org.apache.syncope.ext.scimv2.api.service.SCIMGroupService;
import org.apache.syncope.ext.scimv2.api.service.SCIMService;
-import org.apache.syncope.ext.scimv2.api.service.UserService;
-import org.apache.syncope.ext.scimv2.cxf.service.GroupServiceImpl;
+import org.apache.syncope.ext.scimv2.api.service.SCIMUserService;
+import org.apache.syncope.ext.scimv2.cxf.service.SCIMGroupServiceImpl;
import org.apache.syncope.ext.scimv2.cxf.service.SCIMServiceImpl;
-import org.apache.syncope.ext.scimv2.cxf.service.UserServiceImpl;
+import org.apache.syncope.ext.scimv2.cxf.service.SCIMUserServiceImpl;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
@@ -119,7 +119,7 @@ public class SCIMv2RESTCXFContext {
@ConditionalOnMissingBean
@Bean
- public GroupService scimv2GroupService(
+ public SCIMGroupService scimv2GroupService(
final UserDAO userDAO,
final GroupDAO groupDAO,
final UserLogic userLogic,
@@ -127,12 +127,12 @@ public class SCIMv2RESTCXFContext {
final SCIMDataBinder binder,
final SCIMConfManager confManager) {
- return new GroupServiceImpl(userDAO, groupDAO, userLogic, groupLogic, binder, confManager);
+ return new SCIMGroupServiceImpl(userDAO, groupDAO, userLogic, groupLogic, binder, confManager);
}
@ConditionalOnMissingBean
@Bean
- public UserService scimv2UserService(
+ public SCIMUserService scimv2UserService(
final UserDAO userDAO,
final GroupDAO groupDAO,
final UserLogic userLogic,
@@ -140,6 +140,6 @@ public class SCIMv2RESTCXFContext {
final SCIMDataBinder binder,
final SCIMConfManager confManager) {
- return new UserServiceImpl(userDAO, groupDAO, userLogic, groupLogic, binder, confManager);
+ return new SCIMUserServiceImpl(userDAO, groupDAO, userLogic, groupLogic, binder, confManager);
}
}
diff --git a/ext/scimv2/scim-rest-cxf/src/main/java/org/apache/syncope/ext/scimv2/cxf/service/AbstractService.java b/ext/scimv2/scim-rest-cxf/src/main/java/org/apache/syncope/ext/scimv2/cxf/service/AbstractSCIMService.java
similarity index 97%
rename from ext/scimv2/scim-rest-cxf/src/main/java/org/apache/syncope/ext/scimv2/cxf/service/AbstractService.java
rename to ext/scimv2/scim-rest-cxf/src/main/java/org/apache/syncope/ext/scimv2/cxf/service/AbstractSCIMService.java
index 51e30dfeea..dddb12e687 100644
--- a/ext/scimv2/scim-rest-cxf/src/main/java/org/apache/syncope/ext/scimv2/cxf/service/AbstractService.java
+++ b/ext/scimv2/scim-rest-cxf/src/main/java/org/apache/syncope/ext/scimv2/cxf/service/AbstractSCIMService.java
@@ -54,9 +54,9 @@ import org.apache.syncope.ext.scimv2.api.type.SortOrder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-abstract class AbstractService<R extends SCIMResource> {
+abstract class AbstractSCIMService<R extends SCIMResource> {
- protected static final Logger LOG = LoggerFactory.getLogger(AbstractService.class);
+ protected static final Logger LOG = LoggerFactory.getLogger(AbstractSCIMService.class);
@Context
protected UriInfo uriInfo;
@@ -76,7 +76,7 @@ abstract class AbstractService<R extends SCIMResource> {
protected final SCIMConfManager confManager;
- protected AbstractService(
+ protected AbstractSCIMService(
final UserDAO userDAO,
final GroupDAO groupDAO,
final UserLogic userLogic,
@@ -200,7 +200,8 @@ abstract class AbstractService<R extends SCIMResource> {
request.getAttributes(),
request.getExcludedAttributes());
} else if (anyTO instanceof GroupTO) {
- resource = binder.toSCIMGroup((GroupTO) anyTO,
+ resource = binder.toSCIMGroup(
+ (GroupTO) anyTO,
uriInfo.getAbsolutePathBuilder().path(anyTO.getKey()).build().toASCIIString(),
request.getAttributes(),
request.getExcludedAttributes());
@@ -213,5 +214,4 @@ abstract class AbstractService<R extends SCIMResource> {
return response;
}
-
}
diff --git a/ext/scimv2/scim-rest-cxf/src/main/java/org/apache/syncope/ext/scimv2/cxf/service/GroupServiceImpl.java b/ext/scimv2/scim-rest-cxf/src/main/java/org/apache/syncope/ext/scimv2/cxf/service/SCIMGroupServiceImpl.java
similarity index 65%
rename from ext/scimv2/scim-rest-cxf/src/main/java/org/apache/syncope/ext/scimv2/cxf/service/GroupServiceImpl.java
rename to ext/scimv2/scim-rest-cxf/src/main/java/org/apache/syncope/ext/scimv2/cxf/service/SCIMGroupServiceImpl.java
index cd528ab999..9f3c356e77 100644
--- a/ext/scimv2/scim-rest-cxf/src/main/java/org/apache/syncope/ext/scimv2/cxf/service/GroupServiceImpl.java
+++ b/ext/scimv2/scim-rest-cxf/src/main/java/org/apache/syncope/ext/scimv2/cxf/service/SCIMGroupServiceImpl.java
@@ -20,11 +20,14 @@ package org.apache.syncope.ext.scimv2.cxf.service;
import java.util.HashSet;
import java.util.List;
+import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.Response.ResponseBuilder;
+import org.apache.commons.jexl3.MapContext;
import org.apache.commons.lang3.ArrayUtils;
+import org.apache.commons.lang3.BooleanUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.syncope.common.lib.AnyOperations;
import org.apache.syncope.common.lib.SyncopeConstants;
@@ -43,18 +46,23 @@ import org.apache.syncope.core.persistence.api.dao.GroupDAO;
import org.apache.syncope.core.persistence.api.dao.UserDAO;
import org.apache.syncope.core.persistence.api.dao.search.MembershipCond;
import org.apache.syncope.core.persistence.api.dao.search.SearchCond;
+import org.apache.syncope.core.provisioning.api.jexl.JexlUtils;
import org.apache.syncope.ext.scimv2.api.BadRequestException;
import org.apache.syncope.ext.scimv2.api.data.ListResponse;
+import org.apache.syncope.ext.scimv2.api.data.Member;
import org.apache.syncope.ext.scimv2.api.data.SCIMGroup;
+import org.apache.syncope.ext.scimv2.api.data.SCIMPatchOp;
import org.apache.syncope.ext.scimv2.api.data.SCIMSearchRequest;
-import org.apache.syncope.ext.scimv2.api.service.GroupService;
+import org.apache.syncope.ext.scimv2.api.service.SCIMGroupService;
import org.apache.syncope.ext.scimv2.api.type.ErrorType;
+import org.apache.syncope.ext.scimv2.api.type.PatchOp;
import org.apache.syncope.ext.scimv2.api.type.Resource;
import org.apache.syncope.ext.scimv2.api.type.SortOrder;
+import org.springframework.util.CollectionUtils;
-public class GroupServiceImpl extends AbstractService<SCIMGroup> implements GroupService {
+public class SCIMGroupServiceImpl extends AbstractSCIMService<SCIMGroup> implements SCIMGroupService {
- public GroupServiceImpl(
+ public SCIMGroupServiceImpl(
final UserDAO userDAO,
final GroupDAO groupDAO,
final UserLogic userLogic,
@@ -65,23 +73,26 @@ public class GroupServiceImpl extends AbstractService<SCIMGroup> implements Grou
super(userDAO, groupDAO, userLogic, groupLogic, binder, confManager);
}
+ private void changeMembership(final String user, final String group, final PatchOp patchOp) {
+ UserUR req = new UserUR.Builder(user).
+ membership(new MembershipUR.Builder(group).operation(patchOp == PatchOp.remove
+ ? PatchOperation.DELETE : PatchOperation.ADD_REPLACE).build()).
+ build();
+ try {
+ userLogic.update(req, false);
+ } catch (Exception e) {
+ LOG.error("While applying {} on membership of {} to {}", patchOp, group, user, e);
+ }
+ }
+
@Override
public Response create(final SCIMGroup group) {
// first create group, no members assigned
- ProvisioningResult<GroupTO> result = groupLogic.create(SCIMDataBinder.toGroupCR(group), false);
+ ProvisioningResult<GroupTO> result = groupLogic.create(binder.toGroupCR(group), false);
// then assign members
- group.getMembers().forEach(member -> {
- UserUR req = new UserUR.Builder(member.getValue()).
- membership(new MembershipUR.Builder(result.getEntity().getKey()).
- operation(PatchOperation.ADD_REPLACE).build()).
- build();
- try {
- userLogic.update(req, false);
- } catch (Exception e) {
- LOG.error("While setting membership of {} to {}", result.getEntity().getKey(), member.getValue(), e);
- }
- });
+ group.getMembers().forEach(member -> changeMembership(
+ member.getValue(), result.getEntity().getKey(), PatchOp.add));
return createResponse(
result.getEntity().getKey(),
@@ -93,7 +104,8 @@ public class GroupServiceImpl extends AbstractService<SCIMGroup> implements Grou
}
@Override
- public SCIMGroup get(final String id,
+ public SCIMGroup get(
+ final String id,
final String attributes,
final String excludedAttributes) {
@@ -104,9 +116,60 @@ public class GroupServiceImpl extends AbstractService<SCIMGroup> implements Grou
List.of(ArrayUtils.nullToEmpty(StringUtils.split(excludedAttributes, ','))));
}
+ private Set<String> members(final String group) {
+ Set<String> members = new HashSet<>();
+
+ MembershipCond membCond = new MembershipCond();
+ membCond.setGroup(group);
+ SearchCond searchCond = SearchCond.getLeaf(membCond);
+ int count = userLogic.search(searchCond, 1, 1, List.of(), SyncopeConstants.ROOT_REALM, true, false).getLeft();
+ for (int page = 1; page <= (count / AnyDAO.DEFAULT_PAGE_SIZE) + 1; page++) {
+ members.addAll(userLogic.search(
+ searchCond,
+ page,
+ AnyDAO.DEFAULT_PAGE_SIZE,
+ List.of(),
+ SyncopeConstants.ROOT_REALM,
+ true,
+ false).
+ getRight().stream().map(UserTO::getKey).collect(Collectors.toSet()));
+ }
+
+ return members;
+ }
+
@Override
- public Response update(final String id) {
- return Response.status(Response.Status.NOT_IMPLEMENTED).build();
+ public Response update(final String id, final SCIMPatchOp patch) {
+ ResponseBuilder builder = checkETag(Resource.Group, id);
+ if (builder != null) {
+ return builder.build();
+ }
+
+ patch.getOperations().forEach(op -> {
+ if (op.getPath() != null && "members".equals(op.getPath().getAttribute())) {
+ if (CollectionUtils.isEmpty(op.getValue())) {
+ members(id).stream().filter(member -> op.getPath().getFilter() == null
+ ? true
+ : BooleanUtils.toBoolean(JexlUtils.evaluate(
+ SCIMDataBinder.filter2JexlExpression(op.getPath().getFilter()),
+ new MapContext(Map.of("value", member))).toString())).
+ forEach(member -> changeMembership(member, id, op.getOp()));
+ } else {
+ op.getValue().stream().map(Member.class::cast).
+ forEach(member -> changeMembership(member.getValue(), id, op.getOp()));
+ }
+ } else {
+ groupLogic.update(binder.toGroupUR(groupLogic.read(id), op), false);
+ }
+ });
+
+ return updateResponse(
+ id,
+ binder.toSCIMGroup(
+ groupLogic.read(id),
+ uriInfo.getAbsolutePathBuilder().path(id).build().toASCIIString(),
+ List.of(),
+ List.of()));
}
@Override
@@ -121,28 +184,11 @@ public class GroupServiceImpl extends AbstractService<SCIMGroup> implements Grou
}
// save current group members
- Set<String> beforeMembers = new HashSet<>();
-
- MembershipCond membCond = new MembershipCond();
- membCond.setGroup(id);
- SearchCond searchCond = SearchCond.getLeaf(membCond);
- int count = userLogic.search(searchCond,
- 1, 1, List.of(), SyncopeConstants.ROOT_REALM, true, false).getLeft();
- for (int page = 1; page <= (count / AnyDAO.DEFAULT_PAGE_SIZE) + 1; page++) {
- beforeMembers.addAll(userLogic.search(
- searchCond,
- page,
- AnyDAO.DEFAULT_PAGE_SIZE,
- List.of(),
- SyncopeConstants.ROOT_REALM,
- true,
- false).
- getRight().stream().map(UserTO::getKey).collect(Collectors.toSet()));
- }
+ Set<String> beforeMembers = members(id);
// update group, don't change members
ProvisioningResult<GroupTO> result = groupLogic.update(
- AnyOperations.diff(SCIMDataBinder.toGroupTO(group), groupLogic.read(id), false), false);
+ AnyOperations.diff(binder.toGroupTO(group, true), groupLogic.read(id), false), false);
// assign new members
Set<String> afterMembers = new HashSet<>();
@@ -150,30 +196,12 @@ public class GroupServiceImpl extends AbstractService<SCIMGroup> implements Grou
afterMembers.add(member.getValue());
if (!beforeMembers.contains(member.getValue())) {
- UserUR req = new UserUR.Builder(member.getValue()).
- membership(new MembershipUR.Builder(result.getEntity().getKey()).
- operation(PatchOperation.ADD_REPLACE).build()).
- build();
- try {
- userLogic.update(req, false);
- } catch (Exception e) {
- LOG.error("While setting membership of {} to {}",
- result.getEntity().getKey(), member.getValue(), e);
- }
+ changeMembership(member.getValue(), result.getEntity().getKey(), PatchOp.add);
}
});
// remove unconfirmed members
- beforeMembers.stream().filter(member -> !afterMembers.contains(member)).forEach(user -> {
- UserUR req = new UserUR.Builder(user).
- membership(new MembershipUR.Builder(result.getEntity().getKey()).
- operation(PatchOperation.DELETE).build()).
- build();
- try {
- userLogic.update(req, false);
- } catch (Exception e) {
- LOG.error("While removing membership of {} from {}", result.getEntity().getKey(), user, e);
- }
- });
+ beforeMembers.stream().filter(member -> !afterMembers.contains(member)).forEach(user -> changeMembership(
+ user, result.getEntity().getKey(), PatchOp.remove));
return updateResponse(
result.getEntity().getKey(),
diff --git a/ext/scimv2/scim-rest-cxf/src/main/java/org/apache/syncope/ext/scimv2/cxf/service/SCIMServiceImpl.java b/ext/scimv2/scim-rest-cxf/src/main/java/org/apache/syncope/ext/scimv2/cxf/service/SCIMServiceImpl.java
index 010afe3728..07f16b000a 100644
--- a/ext/scimv2/scim-rest-cxf/src/main/java/org/apache/syncope/ext/scimv2/cxf/service/SCIMServiceImpl.java
+++ b/ext/scimv2/scim-rest-cxf/src/main/java/org/apache/syncope/ext/scimv2/cxf/service/SCIMServiceImpl.java
@@ -32,7 +32,7 @@ import org.apache.syncope.ext.scimv2.api.data.SCIMResource;
import org.apache.syncope.ext.scimv2.api.data.ServiceProviderConfig;
import org.apache.syncope.ext.scimv2.api.service.SCIMService;
-public class SCIMServiceImpl extends AbstractService<SCIMResource> implements SCIMService {
+public class SCIMServiceImpl extends AbstractSCIMService<SCIMResource> implements SCIMService {
protected final SCIMLogic scimLogic;
diff --git a/ext/scimv2/scim-rest-cxf/src/main/java/org/apache/syncope/ext/scimv2/cxf/service/UserServiceImpl.java b/ext/scimv2/scim-rest-cxf/src/main/java/org/apache/syncope/ext/scimv2/cxf/service/SCIMUserServiceImpl.java
similarity index 75%
rename from ext/scimv2/scim-rest-cxf/src/main/java/org/apache/syncope/ext/scimv2/cxf/service/UserServiceImpl.java
rename to ext/scimv2/scim-rest-cxf/src/main/java/org/apache/syncope/ext/scimv2/cxf/service/SCIMUserServiceImpl.java
index 23fb340a5e..efb10fcdbd 100644
--- a/ext/scimv2/scim-rest-cxf/src/main/java/org/apache/syncope/ext/scimv2/cxf/service/UserServiceImpl.java
+++ b/ext/scimv2/scim-rest-cxf/src/main/java/org/apache/syncope/ext/scimv2/cxf/service/SCIMUserServiceImpl.java
@@ -19,13 +19,18 @@
package org.apache.syncope.ext.scimv2.cxf.service;
import java.util.List;
+import java.util.Optional;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.Response.ResponseBuilder;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.StringUtils;
+import org.apache.commons.lang3.tuple.Pair;
import org.apache.syncope.common.lib.AnyOperations;
+import org.apache.syncope.common.lib.request.StatusR;
+import org.apache.syncope.common.lib.request.UserUR;
import org.apache.syncope.common.lib.to.ProvisioningResult;
import org.apache.syncope.common.lib.to.UserTO;
+import org.apache.syncope.common.lib.types.StatusRType;
import org.apache.syncope.core.logic.GroupLogic;
import org.apache.syncope.core.logic.SCIMDataBinder;
import org.apache.syncope.core.logic.UserLogic;
@@ -34,16 +39,17 @@ import org.apache.syncope.core.persistence.api.dao.GroupDAO;
import org.apache.syncope.core.persistence.api.dao.UserDAO;
import org.apache.syncope.ext.scimv2.api.BadRequestException;
import org.apache.syncope.ext.scimv2.api.data.ListResponse;
+import org.apache.syncope.ext.scimv2.api.data.SCIMPatchOp;
import org.apache.syncope.ext.scimv2.api.data.SCIMSearchRequest;
import org.apache.syncope.ext.scimv2.api.data.SCIMUser;
-import org.apache.syncope.ext.scimv2.api.service.UserService;
+import org.apache.syncope.ext.scimv2.api.service.SCIMUserService;
import org.apache.syncope.ext.scimv2.api.type.ErrorType;
import org.apache.syncope.ext.scimv2.api.type.Resource;
import org.apache.syncope.ext.scimv2.api.type.SortOrder;
-public class UserServiceImpl extends AbstractService<SCIMUser> implements UserService {
+public class SCIMUserServiceImpl extends AbstractSCIMService<SCIMUser> implements SCIMUserService {
- public UserServiceImpl(
+ public SCIMUserServiceImpl(
final UserDAO userDAO,
final GroupDAO groupDAO,
final UserLogic userLogic,
@@ -79,8 +85,25 @@ public class UserServiceImpl extends AbstractService<SCIMUser> implements UserSe
}
@Override
- public Response update(final String id) {
- return Response.status(Response.Status.NOT_IMPLEMENTED).build();
+ public Response update(final String id, final SCIMPatchOp patch) {
+ ResponseBuilder builder = checkETag(Resource.User, id);
+ if (builder != null) {
+ return builder.build();
+ }
+
+ patch.getOperations().forEach(op -> {
+ Pair<UserUR, StatusR> update = binder.toUserUpdate(userLogic.read(id), op);
+ userLogic.update(update.getLeft(), false);
+ Optional.ofNullable(update.getRight()).ifPresent(statusR -> userLogic.status(statusR, false));
+ });
+
+ return updateResponse(
+ id,
+ binder.toSCIMUser(
+ userLogic.read(id),
+ uriInfo.getAbsolutePathBuilder().path(id).build().toASCIIString(),
+ List.of(),
+ List.of()));
}
@Override
@@ -94,8 +117,18 @@ public class UserServiceImpl extends AbstractService<SCIMUser> implements UserSe
return builder.build();
}
+ UserTO before = userLogic.read(id);
+
ProvisioningResult<UserTO> result = userLogic.update(
- AnyOperations.diff(binder.toUserTO(user), userLogic.read(id), false), false);
+ AnyOperations.diff(binder.toUserTO(user, true), before, false), false);
+
+ if (before.isSuspended() == user.isActive()) {
+ StatusR statusR = new StatusR.Builder().key(before.getKey()).
+ type(user.isActive() ? StatusRType.REACTIVATE : StatusRType.SUSPEND).
+ build();
+ userLogic.status(statusR, false);
+ }
+
return updateResponse(
result.getEntity().getKey(),
binder.toSCIMUser(
diff --git a/fit/core-reference/src/test/java/org/apache/syncope/fit/core/SCIMITCase.java b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/SCIMITCase.java
index 421e046b8f..dec89030d4 100644
--- a/fit/core-reference/src/test/java/org/apache/syncope/fit/core/SCIMITCase.java
+++ b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/SCIMITCase.java
@@ -20,6 +20,7 @@ package org.apache.syncope.fit.core;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertNotEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertNull;
import static org.junit.jupiter.api.Assertions.assertTrue;
@@ -37,6 +38,7 @@ import java.time.format.DateTimeFormatter;
import java.time.temporal.ChronoUnit;
import java.util.List;
import java.util.UUID;
+import javax.ws.rs.HttpMethod;
import javax.ws.rs.core.GenericType;
import javax.ws.rs.core.HttpHeaders;
import javax.ws.rs.core.Response;
@@ -44,6 +46,7 @@ import org.apache.commons.lang3.StringUtils;
import org.apache.cxf.jaxrs.client.WebClient;
import org.apache.syncope.common.lib.scim.SCIMComplexConf;
import org.apache.syncope.common.lib.scim.SCIMConf;
+import org.apache.syncope.common.lib.scim.SCIMGroupConf;
import org.apache.syncope.common.lib.scim.SCIMUserConf;
import org.apache.syncope.common.lib.scim.SCIMUserNameConf;
import org.apache.syncope.common.lib.scim.types.EmailCanonicalType;
@@ -78,8 +81,14 @@ public class SCIMITCase extends AbstractITCase {
static {
CONF = new SCIMConf();
+
+ CONF.setGroupConf(new SCIMGroupConf());
+
+ CONF.getGroupConf().setExternalId("originalName");
+
CONF.setUserConf(new SCIMUserConf());
+ CONF.getUserConf().setNickName("ctype");
CONF.getUserConf().setDisplayName("cn");
CONF.getUserConf().setName(new SCIMUserNameConf());
@@ -97,6 +106,29 @@ public class SCIMITCase extends AbstractITCase {
CONF.getUserConf().getEmails().add(email);
}
+ private static SCIMUser getSampleUser(final String username) {
+ SCIMUser user = new SCIMUser(null, List.of(Resource.User.schema()), null, username, true);
+ user.setPassword("password123");
+
+ SCIMUserName name = new SCIMUserName();
+ name.setGivenName(username);
+ name.setFamilyName("surname");
+ name.setFormatted(username);
+ user.setName(name);
+
+ SCIMComplexValue userId = new SCIMComplexValue();
+ userId.setType(EmailCanonicalType.work.name());
+ userId.setValue(username + "@syncope.apache.org");
+ user.getEmails().add(userId);
+
+ SCIMComplexValue email = new SCIMComplexValue();
+ email.setType(EmailCanonicalType.home.name());
+ email.setValue(username + "@syncope.apache.org");
+ user.getEmails().add(email);
+
+ return user;
+ }
+
@BeforeAll
public static void isSCIMAvailable() {
if (ENABLED == null) {
@@ -132,7 +164,7 @@ public class SCIMITCase extends AbstractITCase {
ServiceProviderConfig serviceProviderConfig = response.readEntity(ServiceProviderConfig.class);
assertNotNull(serviceProviderConfig);
- assertFalse(serviceProviderConfig.getPatch().isSupported());
+ assertTrue(serviceProviderConfig.getPatch().isSupported());
assertFalse(serviceProviderConfig.getBulk().isSupported());
assertTrue(serviceProviderConfig.getChangePassword().isSupported());
assertTrue(serviceProviderConfig.getEtag().isSupported());
@@ -330,29 +362,6 @@ public class SCIMITCase extends AbstractITCase {
assertEquals(newUser.getUsername(), newSCIMUser.getUserName());
}
- private static SCIMUser getSampleUser(final String username) {
- SCIMUser user = new SCIMUser(null, List.of(Resource.User.schema()), null, username, true);
- user.setPassword("password123");
-
- SCIMUserName name = new SCIMUserName();
- name.setGivenName(username);
- name.setFamilyName("surname");
- name.setFormatted(username);
- user.setName(name);
-
- SCIMComplexValue userId = new SCIMComplexValue();
- userId.setType(EmailCanonicalType.work.name());
- userId.setValue(username + "@syncope.apache.org");
- user.getEmails().add(userId);
-
- SCIMComplexValue email = new SCIMComplexValue();
- email.setType(EmailCanonicalType.home.name());
- email.setValue(username + "@syncope.apache.org");
- user.getEmails().add(email);
-
- return user;
- }
-
@Test
public void createUser() throws JsonProcessingException {
SCIM_CONF_SERVICE.set(CONF);
@@ -381,6 +390,143 @@ public class SCIMITCase extends AbstractITCase {
assertEquals(user.getGroups().get(0).getValue(), userTO.getMemberships().get(0).getGroupKey());
}
+ @Test
+ public void updateUser() {
+ SCIM_CONF_SERVICE.set(CONF);
+
+ SCIMUser user = getSampleUser(UUID.randomUUID().toString());
+
+ Response response = webClient().path("Users").post(user);
+ assertEquals(Response.Status.CREATED.getStatusCode(), response.getStatus());
+
+ user = response.readEntity(SCIMUser.class);
+ assertNotNull(user.getId());
+ assertNull(user.getNickName());
+ assertTrue(user.isActive());
+
+ // 1. update no path, add value and suspend
+ String body =
+ "{"
+ + " \"schemas\":[\"urn:ietf:params:scim:api:messages:2.0:PatchOp\"],"
+ + " \"Operations\": ["
+ + " {"
+ + " \"op\": \"add\","
+ + " \"value\": {"
+ + " \"nickName\": \"" + user.getUserName() + "\","
+ + " \"active\": false"
+ + " }"
+ + " }"
+ + " ]"
+ + "}";
+ response = webClient().path("Users").path(user.getId()).invoke(HttpMethod.PATCH, body);
+ assertEquals(Response.Status.OK.getStatusCode(), response.getStatus());
+
+ user = response.readEntity(SCIMUser.class);
+ assertEquals(user.getUserName(), user.getNickName());
+ assertFalse(user.isActive());
+
+ // 2. update with path, reactivate
+ body =
+ "{"
+ + "\"schemas\":[\"urn:ietf:params:scim:api:messages:2.0:PatchOp\"],"
+ + "\"Operations\":[{"
+ + "\"op\":\"Replace\","
+ + "\"path\":\"active\","
+ + "\"value\":true"
+ + "}]"
+ + "}";
+ response = webClient().path("Users").path(user.getId()).invoke(HttpMethod.PATCH, body);
+ assertEquals(Response.Status.OK.getStatusCode(), response.getStatus());
+
+ user = response.readEntity(SCIMUser.class);
+ assertTrue(user.isActive());
+
+ // 3. update with path, replace simple value
+ assertNotEquals("newSurname", user.getName().getFamilyName());
+ body =
+ "{"
+ + "\"schemas\":[\"urn:ietf:params:scim:api:messages:2.0:PatchOp\"],"
+ + "\"Operations\":["
+ + "{"
+ + "\"op\":\"Replace\","
+ + "\"path\":\"name.familyName\","
+ + "\"value\":\"newSurname\""
+ + "},"
+ + "{"
+ + "\"op\":\"remove\","
+ + "\"path\":\"nickName\""
+ + "}"
+ + "]"
+ + "}";
+ response = webClient().path("Users").path(user.getId()).invoke(HttpMethod.PATCH, body);
+ assertEquals(Response.Status.OK.getStatusCode(), response.getStatus());
+
+ user = response.readEntity(SCIMUser.class);
+ assertEquals("newSurname", user.getName().getFamilyName());
+ assertNull(user.getNickName());
+
+ // 4. update with path, replace complex value
+ String newMail = UUID.randomUUID().toString() + "@syncope.apache.org";
+ assertNotEquals(
+ newMail,
+ user.getEmails().stream().filter(v -> "work".equals(v.getType())).findFirst().get().getValue());
+ body =
+ "{"
+ + " \"schemas\": [\"urn:ietf:params:scim:api:messages:2.0:PatchOp\"],"
+ + " \"Operations\": [{"
+ + " \"op\":\"replace\","
+ + " \"path\":\"emails[type eq \\\"work\\\"]\","
+ + " \"value\":"
+ + " {"
+ + " \"type\": \"work\","
+ + " \"value\": \"" + newMail + "\","
+ + " \"primary\": true"
+ + " }"
+ + " }]"
+ + " }";
+ response = webClient().path("Users").path(user.getId()).invoke(HttpMethod.PATCH, body);
+ assertEquals(Response.Status.OK.getStatusCode(), response.getStatus());
+
+ user = response.readEntity(SCIMUser.class);
+ assertEquals(
+ newMail,
+ user.getEmails().stream().filter(v -> "work".equals(v.getType())).findFirst().get().getValue());
+
+ // 5. update with path, filter and sub
+ newMail = "verycomplex" + UUID.randomUUID().toString() + "@syncope.apache.org";
+ body =
+ "{"
+ + " \"schemas\": [\"urn:ietf:params:scim:api:messages:2.0:PatchOp\"],"
+ + " \"Operations\": [{"
+ + " \"op\":\"replace\","
+ + " \"path\":\"emails[type eq \\\"work\\\"].value\","
+ + " \"value\":\"" + newMail + "\""
+ + " }]"
+ + " }";
+ response = webClient().path("Users").path(user.getId()).invoke(HttpMethod.PATCH, body);
+ assertEquals(Response.Status.OK.getStatusCode(), response.getStatus());
+
+ user = response.readEntity(SCIMUser.class);
+ assertEquals(
+ newMail,
+ user.getEmails().stream().filter(v -> "work".equals(v.getType())).findFirst().get().getValue());
+
+ // 6. remove with path and filter
+ body =
+ "{"
+ + " \"schemas\": [\"urn:ietf:params:scim:api:messages:2.0:PatchOp\"],"
+ + " \"Operations\": [{"
+ + " \"op\":\"remove\","
+ + " \"path\":\"emails[type eq \\\"home\\\"]\""
+ + " }]"
+ + " }";
+ response = webClient().path("Users").path(user.getId()).invoke(HttpMethod.PATCH, body);
+ assertEquals(Response.Status.OK.getStatusCode(), response.getStatus());
+
+ user = response.readEntity(SCIMUser.class);
+ assertTrue(user.getEmails().stream().noneMatch(v -> "home".equals(v.getType())));
+ }
+
@Test
public void replaceUser() {
SCIM_CONF_SERVICE.set(CONF);
@@ -456,6 +602,87 @@ public class SCIMITCase extends AbstractITCase {
assertEquals(ErrorType.uniqueness, error.getScimType());
}
+ @Test
+ public void updateGroup() {
+ SCIM_CONF_SERVICE.set(CONF);
+
+ SCIMGroup group = new SCIMGroup(null, null, UUID.randomUUID().toString());
+ group.getMembers().add(new Member("74cd8ece-715a-44a4-a736-e17b46c4e7e6", null, null));
+ group.getMembers().add(new Member("1417acbe-cbf6-4277-9372-e75e04f97000", null, null));
+ Response response = webClient().path("Groups").post(group);
+ assertEquals(Response.Status.CREATED.getStatusCode(), response.getStatus());
+
+ group = response.readEntity(SCIMGroup.class);
+ assertNotNull(group.getId());
+ assertNull(group.getExternalId());
+ assertEquals(2, group.getMembers().size());
+
+ // 1. update with path, add value
+ String body =
+ "{"
+ + "\"schemas\":[\"urn:ietf:params:scim:api:messages:2.0:PatchOp\"],"
+ + "\"Operations\":[{"
+ + "\"op\":\"Add\","
+ + "\"path\":\"externalId\","
+ + "\"value\":\"" + group.getId() + "\""
+ + "}]"
+ + "}";
+ response = webClient().path("Groups").path(group.getId()).invoke(HttpMethod.PATCH, body);
+ assertEquals(Response.Status.OK.getStatusCode(), response.getStatus());
+
+ group = response.readEntity(SCIMGroup.class);
+ assertEquals(group.getId(), group.getExternalId());
+
+ // 2. add member, remove member, remove attribute
+ body =
+ "{"
+ + "\"schemas\":[\"urn:ietf:params:scim:api:messages:2.0:PatchOp\"],"
+ + "\"Operations\":["
+ + "{"
+ + "\"op\":\"Add\","
+ + "\"path\":\"members\","
+ + "\"value\":[{"
+ + "\"value\":\"b3cbc78d-32e6-4bd4-92e0-bbe07566a2ee\"}]"
+ + "},"
+ + "{"
+ + "\"op\":\"remove\","
+ + "\"path\":\"members[value eq \\\"74cd8ece-715a-44a4-a736-e17b46c4e7e6\\\"]\""
+ + "},"
+ + "{"
+ + "\"op\":\"remove\","
+ + "\"path\":\"externalId\""
+ + "}"
+ + "]"
+ + "}";
+ response = webClient().path("Groups").path(group.getId()).invoke(HttpMethod.PATCH, body);
+ assertEquals(Response.Status.OK.getStatusCode(), response.getStatus());
+
+ group = response.readEntity(SCIMGroup.class);
+ assertEquals(2, group.getMembers().size());
+ assertTrue(group.getMembers().stream().
+ anyMatch(m -> "b3cbc78d-32e6-4bd4-92e0-bbe07566a2ee".equals(m.getValue())));
+ assertTrue(group.getMembers().stream().
+ anyMatch(m -> "1417acbe-cbf6-4277-9372-e75e04f97000".equals(m.getValue())));
+ assertNull(group.getExternalId());
+
+ // 3. remove all members
+ body =
+ "{"
+ + "\"schemas\":[\"urn:ietf:params:scim:api:messages:2.0:PatchOp\"],"
+ + "\"Operations\":["
+ + "{"
+ + "\"op\":\"remove\","
+ + "\"path\":\"members\""
+ + "}"
+ + "]"
+ + "}";
+ response = webClient().path("Groups").path(group.getId()).invoke(HttpMethod.PATCH, body);
+ assertEquals(Response.Status.OK.getStatusCode(), response.getStatus());
+
+ group = response.readEntity(SCIMGroup.class);
+ assertTrue(group.getMembers().isEmpty());
+ }
+
@Test
public void replaceGroup() {
SCIMGroup group = new SCIMGroup(null, null, UUID.randomUUID().toString());
diff --git a/pom.xml b/pom.xml
index b0280b776a..cfd7ceae27 100644
--- a/pom.xml
+++ b/pom.xml
@@ -1735,7 +1735,7 @@ under the License.
<plugin>
<groupId>io.fabric8</groupId>
<artifactId>docker-maven-plugin</artifactId>
- <version>0.42.0</version>
+ <version>0.42.1</version>
</plugin>
<plugin>
@@ -1780,7 +1780,7 @@ under the License.
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-enforcer-plugin</artifactId>
- <version>3.2.1</version>
+ <version>3.3.0</version>
<executions>
<execution>
<id>default-cli</id>