This is an automated email from the ASF dual-hosted git repository.
ilgrosso pushed a commit to branch 4_0_X
in repository https://gitbox.apache.org/repos/asf/syncope.git
The following commit(s) were added to refs/heads/4_0_X by this push:
new 9912d5cd50 [SYNCOPE-1893] SCIM Group extension + SCIM AnyObject
extension (#1146)
9912d5cd50 is described below
commit 9912d5cd50cd7be846cb0bb4babc79da6e59cba6
Author: Samuel Garofalo <[email protected]>
AuthorDate: Mon Jul 28 14:06:08 2025 +0200
[SYNCOPE-1893] SCIM Group extension + SCIM AnyObject extension (#1146)
---
.../panels/SCIMConfExtensionAnyObjectPanel.java | 48 ++++
...erPanel.java => SCIMConfExtensionAnyPanel.java} | 28 ++-
.../panels/SCIMConfExtensionGroupPanel.java | 39 +++
.../console/panels/SCIMConfExtensionUserPanel.java | 62 +----
.../client/console/panels/SCIMConfPanel.java | 32 ++-
.../panels/mapping/SCIMExtensionMappingPanel.java | 22 +-
...erPanel.html => SCIMConfExtensionAnyPanel.html} | 0
.../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 +
.../apache/syncope/common/lib/scim/SCIMConf.java | 24 +-
...sionUserConf.java => SCIMExtensionAnyConf.java} | 2 +-
.../lib/scim/SCIMExtensionAnyObjectConf.java | 44 ++++
.../apache/syncope/core/logic/SCIMDataBinder.java | 223 ++++++++++++++++-
.../org/apache/syncope/core/logic/SCIMLogic.java | 60 ++++-
.../syncope/core/logic/scim/SCIMConfManager.java | 69 +++---
.../syncope/core/logic/scim/SearchCondVisitor.java | 55 +++--
.../syncope/core/logic/scim/SCIMFilterTest.java | 6 +-
.../apache/syncope/ext/scimv2/api/data/Meta.java | 7 +-
.../syncope/ext/scimv2/api/data/SCIMAnyObject.java | 90 +++++++
.../syncope/ext/scimv2/api/data/SCIMGroup.java | 17 +-
.../scimv2/api/service/SCIMAnyObjectService.java | 26 ++
.../syncope/ext/scimv2/api/type/Resource.java | 1 +
.../ext/scimv2/cxf/SCIMv2RESTCXFContext.java | 39 ++-
.../scimv2/cxf/service/AbstractSCIMService.java | 70 ++++--
...viceImpl.java => SCIMAnyObjectServiceImpl.java} | 172 +++++++-------
.../scimv2/cxf/service/SCIMGroupServiceImpl.java | 18 +-
.../ext/scimv2/cxf/service/SCIMServiceImpl.java | 6 +-
.../scimv2/cxf/service/SCIMUserServiceImpl.java | 18 +-
.../org/apache/syncope/fit/core/SCIMITCase.java | 264 ++++++++++++++++++++-
31 files changed, 1172 insertions(+), 274 deletions(-)
diff --git
a/ext/scimv2/client-console/src/main/java/org/apache/syncope/client/console/panels/SCIMConfExtensionAnyObjectPanel.java
b/ext/scimv2/client-console/src/main/java/org/apache/syncope/client/console/panels/SCIMConfExtensionAnyObjectPanel.java
new file mode 100644
index 0000000000..562d8e7ab8
--- /dev/null
+++
b/ext/scimv2/client-console/src/main/java/org/apache/syncope/client/console/panels/SCIMConfExtensionAnyObjectPanel.java
@@ -0,0 +1,48 @@
+/*
+ * 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 java.util.Optional;
+import org.apache.syncope.common.lib.scim.SCIMConf;
+import org.apache.syncope.common.lib.scim.SCIMExtensionAnyConf;
+import org.apache.syncope.common.lib.scim.SCIMExtensionAnyObjectConf;
+
+public class SCIMConfExtensionAnyObjectPanel extends SCIMConfExtensionAnyPanel
{
+
+ private static final long serialVersionUID = -1540432800132655369L;
+
+ public SCIMConfExtensionAnyObjectPanel(final String id, final SCIMConf
scimConf, final String anyTypeKey) {
+ super(id, scimConf, anyTypeKey);
+ }
+
+ @Override
+ public SCIMExtensionAnyConf getExtensionAnyConf(final SCIMConf scimConf) {
+ Optional<SCIMExtensionAnyObjectConf> scimExtAnyObjectConf =
+ scimConf.getExtensionAnyObjectsConf().stream()
+ .filter(scimExtensionAnyObjectConf ->
scimExtensionAnyObjectConf.getType().equals(anyTypeKey))
+ .findFirst();
+ if (scimExtAnyObjectConf.isPresent()) {
+ return scimExtAnyObjectConf.get();
+ }
+ SCIMExtensionAnyObjectConf scimExtensionAnyObjectConf = new
SCIMExtensionAnyObjectConf();
+ scimExtensionAnyObjectConf.setType(anyTypeKey);
+ scimConf.getExtensionAnyObjectsConf().add(scimExtensionAnyObjectConf);
+ return scimExtensionAnyObjectConf;
+ }
+}
diff --git
a/ext/scimv2/client-console/src/main/java/org/apache/syncope/client/console/panels/SCIMConfExtensionUserPanel.java
b/ext/scimv2/client-console/src/main/java/org/apache/syncope/client/console/panels/SCIMConfExtensionAnyPanel.java
similarity index 72%
copy from
ext/scimv2/client-console/src/main/java/org/apache/syncope/client/console/panels/SCIMConfExtensionUserPanel.java
copy to
ext/scimv2/client-console/src/main/java/org/apache/syncope/client/console/panels/SCIMConfExtensionAnyPanel.java
index b8dbb6a377..d328b80cc9 100644
---
a/ext/scimv2/client-console/src/main/java/org/apache/syncope/client/console/panels/SCIMConfExtensionUserPanel.java
+++
b/ext/scimv2/client-console/src/main/java/org/apache/syncope/client/console/panels/SCIMConfExtensionAnyPanel.java
@@ -21,23 +21,23 @@ package org.apache.syncope.client.console.panels;
import
org.apache.syncope.client.console.panels.mapping.SCIMExtensionMappingPanel;
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.SCIMExtensionUserConf;
+import org.apache.syncope.common.lib.scim.SCIMExtensionAnyConf;
import org.apache.wicket.markup.html.form.Form;
import org.apache.wicket.model.Model;
import org.apache.wicket.model.PropertyModel;
import org.apache.wicket.model.util.ListModel;
-public class SCIMConfExtensionUserPanel extends SCIMConfTabPanel {
+public abstract class SCIMConfExtensionAnyPanel extends SCIMConfTabPanel {
private static final long serialVersionUID = 2459231778083046011L;
- public SCIMConfExtensionUserPanel(final String id, final SCIMConf
scimConf) {
+ protected final String anyTypeKey;
+
+ protected SCIMConfExtensionAnyPanel(final String id, final SCIMConf
scimConf, final String anyTypeKey) {
super(id);
+ this.anyTypeKey = anyTypeKey;
- if (scimConf.getExtensionUserConf() == null) {
- scimConf.setExtensionUserConf(new SCIMExtensionUserConf());
- }
- SCIMExtensionUserConf scimExtensionUserConf =
scimConf.getExtensionUserConf();
+ SCIMExtensionAnyConf scimExtensionAnyConf =
getExtensionAnyConf(scimConf);
AjaxTextFieldPanel namePanel = new AjaxTextFieldPanel("name", "name",
new PropertyModel<>("name", "name") {
@@ -45,12 +45,12 @@ public class SCIMConfExtensionUserPanel extends
SCIMConfTabPanel {
@Override
public String getObject() {
- return scimExtensionUserConf.getName();
+ return scimExtensionAnyConf.getName();
}
@Override
public void setObject(final String object) {
- scimExtensionUserConf.setName(object);
+ scimExtensionAnyConf.setName(object);
}
});
add(namePanel);
@@ -62,20 +62,22 @@ public class SCIMConfExtensionUserPanel extends
SCIMConfTabPanel {
@Override
public String getObject() {
- return scimExtensionUserConf.getDescription();
+ return scimExtensionAnyConf.getDescription();
}
@Override
public void setObject(final String object) {
- scimExtensionUserConf.setDescription(object);
+ scimExtensionAnyConf.setDescription(object);
}
});
add(descriptionPanel);
SCIMExtensionMappingPanel extensionMappingPanel = new
SCIMExtensionMappingPanel(
- "mapping", new
ListModel<>(scimExtensionUserConf.getAttributes()));
- Form<SCIMExtensionUserConf> form = new Form<>("form", new
Model<>(scimExtensionUserConf));
+ "mapping", new
ListModel<>(scimExtensionAnyConf.getAttributes()), anyTypeKey);
+ Form<SCIMExtensionAnyConf> form = new Form<>("form", new
Model<>(scimExtensionAnyConf));
form.add(extensionMappingPanel);
add(form);
}
+
+ abstract SCIMExtensionAnyConf getExtensionAnyConf(SCIMConf scimConf);
}
diff --git
a/ext/scimv2/client-console/src/main/java/org/apache/syncope/client/console/panels/SCIMConfExtensionGroupPanel.java
b/ext/scimv2/client-console/src/main/java/org/apache/syncope/client/console/panels/SCIMConfExtensionGroupPanel.java
new file mode 100644
index 0000000000..3298a2fa8f
--- /dev/null
+++
b/ext/scimv2/client-console/src/main/java/org/apache/syncope/client/console/panels/SCIMConfExtensionGroupPanel.java
@@ -0,0 +1,39 @@
+/*
+ * 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.common.lib.scim.SCIMConf;
+import org.apache.syncope.common.lib.scim.SCIMExtensionAnyConf;
+
+public class SCIMConfExtensionGroupPanel extends SCIMConfExtensionAnyPanel {
+
+ private static final long serialVersionUID = -3719006384765921047L;
+
+ public SCIMConfExtensionGroupPanel(final String id, final SCIMConf
scimConf, final String anyTypeKey) {
+ super(id, scimConf, anyTypeKey);
+ }
+
+ @Override
+ public SCIMExtensionAnyConf getExtensionAnyConf(final SCIMConf scimConf) {
+ if (scimConf.getExtensionGroupConf() == null) {
+ scimConf.setExtensionGroupConf(new SCIMExtensionAnyConf());
+ }
+ return scimConf.getExtensionGroupConf();
+ }
+}
diff --git
a/ext/scimv2/client-console/src/main/java/org/apache/syncope/client/console/panels/SCIMConfExtensionUserPanel.java
b/ext/scimv2/client-console/src/main/java/org/apache/syncope/client/console/panels/SCIMConfExtensionUserPanel.java
index b8dbb6a377..beb1f0684b 100644
---
a/ext/scimv2/client-console/src/main/java/org/apache/syncope/client/console/panels/SCIMConfExtensionUserPanel.java
+++
b/ext/scimv2/client-console/src/main/java/org/apache/syncope/client/console/panels/SCIMConfExtensionUserPanel.java
@@ -18,64 +18,22 @@
*/
package org.apache.syncope.client.console.panels;
-import
org.apache.syncope.client.console.panels.mapping.SCIMExtensionMappingPanel;
-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.SCIMExtensionUserConf;
-import org.apache.wicket.markup.html.form.Form;
-import org.apache.wicket.model.Model;
-import org.apache.wicket.model.PropertyModel;
-import org.apache.wicket.model.util.ListModel;
+import org.apache.syncope.common.lib.scim.SCIMExtensionAnyConf;
-public class SCIMConfExtensionUserPanel extends SCIMConfTabPanel {
+public class SCIMConfExtensionUserPanel extends SCIMConfExtensionAnyPanel {
- private static final long serialVersionUID = 2459231778083046011L;
+ private static final long serialVersionUID = -94504185795020353L;
- public SCIMConfExtensionUserPanel(final String id, final SCIMConf
scimConf) {
- super(id);
+ public SCIMConfExtensionUserPanel(final String id, final SCIMConf
scimConf, final String anyTypeKey) {
+ super(id, scimConf, anyTypeKey);
+ }
+ @Override
+ public SCIMExtensionAnyConf getExtensionAnyConf(final SCIMConf scimConf) {
if (scimConf.getExtensionUserConf() == null) {
- scimConf.setExtensionUserConf(new SCIMExtensionUserConf());
+ scimConf.setExtensionUserConf(new SCIMExtensionAnyConf());
}
- SCIMExtensionUserConf scimExtensionUserConf =
scimConf.getExtensionUserConf();
-
- AjaxTextFieldPanel namePanel = new AjaxTextFieldPanel("name", "name",
new PropertyModel<>("name", "name") {
-
- private static final long serialVersionUID = 7389942851813193481L;
-
- @Override
- public String getObject() {
- return scimExtensionUserConf.getName();
- }
-
- @Override
- public void setObject(final String object) {
- scimExtensionUserConf.setName(object);
- }
- });
- add(namePanel);
-
- AjaxTextFieldPanel descriptionPanel = new AjaxTextFieldPanel(
- "description", "description", new
PropertyModel<>("description", "description") {
-
- private static final long serialVersionUID = -5911179251497048661L;
-
- @Override
- public String getObject() {
- return scimExtensionUserConf.getDescription();
- }
-
- @Override
- public void setObject(final String object) {
- scimExtensionUserConf.setDescription(object);
- }
- });
- add(descriptionPanel);
-
- SCIMExtensionMappingPanel extensionMappingPanel = new
SCIMExtensionMappingPanel(
- "mapping", new
ListModel<>(scimExtensionUserConf.getAttributes()));
- Form<SCIMExtensionUserConf> form = new Form<>("form", new
Model<>(scimExtensionUserConf));
- form.add(extensionMappingPanel);
- add(form);
+ return scimConf.getExtensionUserConf();
}
}
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 a36c2f8d15..df886c401a 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
@@ -24,10 +24,12 @@ import java.util.List;
import org.apache.syncope.client.console.SyncopeConsoleSession;
import org.apache.syncope.client.console.commons.ITabComponent;
import org.apache.syncope.client.console.pages.BasePage;
+import org.apache.syncope.client.console.rest.AnyTypeRestClient;
import org.apache.syncope.client.console.rest.SCIMConfRestClient;
import org.apache.syncope.client.console.wizards.WizardMgtPanel;
import org.apache.syncope.client.ui.commons.Constants;
import org.apache.syncope.common.lib.scim.SCIMConf;
+import org.apache.syncope.common.lib.types.AnyTypeKind;
import org.apache.wicket.PageReference;
import org.apache.wicket.ajax.AjaxRequestTarget;
import org.apache.wicket.ajax.markup.html.AjaxLink;
@@ -35,6 +37,7 @@ import org.apache.wicket.extensions.markup.html.tabs.ITab;
import org.apache.wicket.markup.html.WebMarkupContainer;
import org.apache.wicket.markup.html.panel.Panel;
import org.apache.wicket.model.Model;
+import org.apache.wicket.model.ResourceModel;
import org.apache.wicket.spring.injection.annot.SpringBean;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -48,6 +51,9 @@ public class SCIMConfPanel extends WizardMgtPanel<SCIMConf> {
@SpringBean
protected SCIMConfRestClient scimConfRestClient;
+ @SpringBean
+ protected AnyTypeRestClient anyTypeRestClient;
+
protected final SCIMConf scimConf;
public SCIMConfPanel(
@@ -124,7 +130,7 @@ public class SCIMConfPanel extends WizardMgtPanel<SCIMConf>
{
@Override
public WebMarkupContainer getPanel(final String panelId) {
- return new SCIMConfExtensionUserPanel(panelId, scimConf);
+ return new SCIMConfExtensionUserPanel(panelId, scimConf,
AnyTypeKind.USER.name());
}
});
@@ -138,6 +144,30 @@ public class SCIMConfPanel extends
WizardMgtPanel<SCIMConf> {
}
});
+ tabs.add(new ITabComponent(new Model<>(getString("tab6")),
getString("tab6")) {
+
+ private static final long serialVersionUID = -7858187494595192532L;
+
+ @Override
+ public WebMarkupContainer getPanel(final String panelId) {
+ return new SCIMConfExtensionGroupPanel(panelId, scimConf,
AnyTypeKind.GROUP.name());
+ }
+ });
+
+ anyTypeRestClient.listAnyTypes().stream()
+ .filter(anyTypeTO ->
AnyTypeKind.ANY_OBJECT.equals(anyTypeTO.getKind()))
+ .forEach(anyTypeTO ->
+ tabs.add(new ITabComponent(
+ new ResourceModel("anyType." +
anyTypeTO.getKey(), anyTypeTO.getKey())) {
+
+ private static final long serialVersionUID =
6429988338658964324L;
+
+ @Override
+ public WebMarkupContainer getPanel(final String
panelId) {
+ return new
SCIMConfExtensionAnyObjectPanel(panelId, scimConf, anyTypeTO.getKey());
+ }
+ }));
+
return tabs;
}
}
diff --git
a/ext/scimv2/client-console/src/main/java/org/apache/syncope/client/console/panels/mapping/SCIMExtensionMappingPanel.java
b/ext/scimv2/client-console/src/main/java/org/apache/syncope/client/console/panels/mapping/SCIMExtensionMappingPanel.java
index 1692d11e24..e5e9d932d5 100644
---
a/ext/scimv2/client-console/src/main/java/org/apache/syncope/client/console/panels/mapping/SCIMExtensionMappingPanel.java
+++
b/ext/scimv2/client-console/src/main/java/org/apache/syncope/client/console/panels/mapping/SCIMExtensionMappingPanel.java
@@ -35,7 +35,6 @@ import
org.apache.syncope.client.ui.commons.markup.html.form.AjaxDropDownChoiceP
import
org.apache.syncope.client.ui.commons.markup.html.form.AjaxTextFieldPanel;
import org.apache.syncope.common.lib.scim.SCIMItem;
import org.apache.syncope.common.lib.scim.SCIMReturned;
-import org.apache.syncope.common.lib.types.AnyTypeKind;
import org.apache.wicket.ajax.AjaxRequestTarget;
import org.apache.wicket.ajax.markup.html.form.AjaxButton;
import org.apache.wicket.extensions.ajax.markup.html.IndicatingAjaxButton;
@@ -79,11 +78,15 @@ public class SCIMExtensionMappingPanel extends Panel {
protected final WebMarkupContainer mappingContainer;
+ protected final String anyTypeKey;
+
public SCIMExtensionMappingPanel(
final String id,
- final IModel<List<SCIMItem>> model) {
+ final IModel<List<SCIMItem>> model,
+ final String anyTypeKey) {
super(id);
+ this.anyTypeKey = anyTypeKey;
setOutputMarkupId(true);
mappingContainer = new WebMarkupContainer("mappingContainer");
@@ -264,9 +267,18 @@ public class SCIMExtensionMappingPanel extends Panel {
}
protected IModel<List<String>> getExtAttrNames() {
- List<String> choices = new
ArrayList<>(ClassPathScanImplementationLookup.USER_FIELD_NAMES);
-
-
anyTypeClassRestClient.list(anyTypeRestClient.read(AnyTypeKind.USER.name()).getClasses()).
+ List<String> choices = new ArrayList<>();
+ switch (anyTypeKey) {
+ case "USER":
+
choices.addAll(ClassPathScanImplementationLookup.USER_FIELD_NAMES);
+ break;
+ case "GROUP":
+
choices.addAll(ClassPathScanImplementationLookup.GROUP_FIELD_NAMES);
+ break;
+ default:
+
choices.addAll(ClassPathScanImplementationLookup.ANY_OBJECT_FIELD_NAMES);
+ }
+
anyTypeClassRestClient.list(anyTypeRestClient.read(anyTypeKey).getClasses()).
forEach(anyTypeClassTO -> {
choices.addAll(anyTypeClassTO.getPlainSchemas());
choices.addAll(anyTypeClassTO.getDerSchemas());
diff --git
a/ext/scimv2/client-console/src/main/resources/org/apache/syncope/client/console/panels/SCIMConfExtensionUserPanel.html
b/ext/scimv2/client-console/src/main/resources/org/apache/syncope/client/console/panels/SCIMConfExtensionAnyPanel.html
similarity index 100%
rename from
ext/scimv2/client-console/src/main/resources/org/apache/syncope/client/console/panels/SCIMConfExtensionUserPanel.html
rename to
ext/scimv2/client-console/src/main/resources/org/apache/syncope/client/console/panels/SCIMConfExtensionAnyPanel.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 fd36da900c..954ee7de18 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
@@ -21,3 +21,4 @@ tab3=EnterpriseUser
saveButton=Save
tab4=ExtensionUser
tab5=Group
+tab6=ExtensionGroup
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 fd36da900c..954ee7de18 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
@@ -21,3 +21,4 @@ tab3=EnterpriseUser
saveButton=Save
tab4=ExtensionUser
tab5=Group
+tab6=ExtensionGroup
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 fd36da900c..954ee7de18 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
@@ -21,3 +21,4 @@ tab3=EnterpriseUser
saveButton=Save
tab4=ExtensionUser
tab5=Group
+tab6=ExtensionGroup
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 fd36da900c..954ee7de18 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
@@ -21,3 +21,4 @@ tab3=EnterpriseUser
saveButton=Save
tab4=ExtensionUser
tab5=Group
+tab6=ExtensionGroup
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 468b5d289e..d90ef49b72 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
@@ -19,6 +19,8 @@
package org.apache.syncope.common.lib.scim;
import java.io.Serializable;
+import java.util.ArrayList;
+import java.util.List;
public class SCIMConf implements Serializable {
@@ -32,10 +34,14 @@ public class SCIMConf implements Serializable {
private SCIMEnterpriseUserConf enterpriseUserConf;
- private SCIMExtensionUserConf extensionUserConf;
+ private SCIMExtensionAnyConf extensionUserConf;
private SCIMGroupConf groupConf;
+ private SCIMExtensionAnyConf extensionGroupConf;
+
+ private final List<SCIMExtensionAnyObjectConf> extensionAnyObjectsConf =
new ArrayList<>();
+
public SCIMGeneralConf getGeneralConf() {
return generalConf;
}
@@ -60,14 +66,26 @@ public class SCIMConf implements Serializable {
this.enterpriseUserConf = enterpriseUserConf;
}
- public SCIMExtensionUserConf getExtensionUserConf() {
+ public SCIMExtensionAnyConf getExtensionUserConf() {
return extensionUserConf;
}
- public void setExtensionUserConf(final SCIMExtensionUserConf
extensionUserConf) {
+ public void setExtensionUserConf(final SCIMExtensionAnyConf
extensionUserConf) {
this.extensionUserConf = extensionUserConf;
}
+ public SCIMExtensionAnyConf getExtensionGroupConf() {
+ return extensionGroupConf;
+ }
+
+ public void setExtensionGroupConf(final SCIMExtensionAnyConf
extensionGroupConf) {
+ this.extensionGroupConf = extensionGroupConf;
+ }
+
+ public List<SCIMExtensionAnyObjectConf> getExtensionAnyObjectsConf() {
+ return extensionAnyObjectsConf;
+ }
+
public SCIMGroupConf getGroupConf() {
return groupConf;
}
diff --git
a/ext/scimv2/common-lib/src/main/java/org/apache/syncope/common/lib/scim/SCIMExtensionUserConf.java
b/ext/scimv2/common-lib/src/main/java/org/apache/syncope/common/lib/scim/SCIMExtensionAnyConf.java
similarity index 97%
rename from
ext/scimv2/common-lib/src/main/java/org/apache/syncope/common/lib/scim/SCIMExtensionUserConf.java
rename to
ext/scimv2/common-lib/src/main/java/org/apache/syncope/common/lib/scim/SCIMExtensionAnyConf.java
index cae6330caf..74c81e96a5 100644
---
a/ext/scimv2/common-lib/src/main/java/org/apache/syncope/common/lib/scim/SCIMExtensionUserConf.java
+++
b/ext/scimv2/common-lib/src/main/java/org/apache/syncope/common/lib/scim/SCIMExtensionAnyConf.java
@@ -27,7 +27,7 @@ import java.util.List;
import java.util.Map;
import java.util.Optional;
-public class SCIMExtensionUserConf implements Serializable {
+public class SCIMExtensionAnyConf implements Serializable {
private static final long serialVersionUID = -9091596628402547645L;
diff --git
a/ext/scimv2/common-lib/src/main/java/org/apache/syncope/common/lib/scim/SCIMExtensionAnyObjectConf.java
b/ext/scimv2/common-lib/src/main/java/org/apache/syncope/common/lib/scim/SCIMExtensionAnyObjectConf.java
new file mode 100644
index 0000000000..7872e44511
--- /dev/null
+++
b/ext/scimv2/common-lib/src/main/java/org/apache/syncope/common/lib/scim/SCIMExtensionAnyObjectConf.java
@@ -0,0 +1,44 @@
+/*
+ * 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.common.lib.scim;
+
+public class SCIMExtensionAnyObjectConf extends SCIMExtensionAnyConf {
+
+ private static final long serialVersionUID = 6600395333691186244L;
+
+ private String type;
+
+ private String externalId;
+
+ public String getType() {
+ return type;
+ }
+
+ public void setType(final String type) {
+ this.type = type;
+ }
+
+ public String getExternalId() {
+ return externalId;
+ }
+
+ public void setExternalId(final String externalId) {
+ this.externalId = externalId;
+ }
+}
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 389e6658d7..8f414c475a 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
@@ -34,6 +34,8 @@ 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.AnyObjectCR;
+import org.apache.syncope.common.lib.request.AnyObjectUR;
import org.apache.syncope.common.lib.request.AttrPatch;
import org.apache.syncope.common.lib.request.GroupCR;
import org.apache.syncope.common.lib.request.GroupUR;
@@ -45,8 +47,10 @@ 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.SCIMExtensionAnyObjectConf;
import org.apache.syncope.common.lib.scim.SCIMManagerConf;
import org.apache.syncope.common.lib.scim.SCIMUserAddressConf;
+import org.apache.syncope.common.lib.to.AnyObjectTO;
import org.apache.syncope.common.lib.to.GroupTO;
import org.apache.syncope.common.lib.to.MembershipTO;
import org.apache.syncope.common.lib.to.UserTO;
@@ -65,6 +69,7 @@ import org.apache.syncope.ext.scimv2.api.BadRequestException;
import org.apache.syncope.ext.scimv2.api.data.Group;
import org.apache.syncope.ext.scimv2.api.data.Member;
import org.apache.syncope.ext.scimv2.api.data.Meta;
+import org.apache.syncope.ext.scimv2.api.data.SCIMAnyObject;
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.SCIMExtensionInfo;
@@ -89,8 +94,6 @@ public class SCIMDataBinder {
protected static final Logger LOG =
LoggerFactory.getLogger(SCIMDataBinder.class);
- protected static final List<String> GROUP_SCHEMAS =
List.of(Resource.Group.schema());
-
/**
* Translates the given SCIM filter into the equivalent JEXL expression.
*
@@ -208,7 +211,7 @@ public class SCIMDataBinder {
userTO.getKey(),
schemas,
new Meta(
- Resource.User,
+ Resource.User.name(),
userTO.getCreationDate(),
Optional.ofNullable(userTO.getLastChangeDate()).orElseGet(userTO::getCreationDate),
userTO.getETagValue(),
@@ -504,6 +507,41 @@ public class SCIMDataBinder {
}
}
+ protected void setAttribute(
+ final GroupTO groupTO,
+ final String schema,
+ final String value) {
+
+ if (schema == null || value == null) {
+ return;
+ }
+
+ switch (schema) {
+ case "name":
+ groupTO.setName(value);
+ break;
+
+ default:
+ groupTO.getPlainAttrs().add(new
Attr.Builder(schema).value(value).build());
+ }
+ }
+
+ protected void setAttribute(
+ final AnyObjectTO anyObjectTO,
+ final String schema,
+ final String value) {
+
+ if (schema == null || value == null) {
+ return;
+ }
+
+ if ("name".equals(schema)) {
+ anyObjectTO.setName(value);
+ } else {
+ anyObjectTO.getPlainAttrs().add(new
Attr.Builder(schema).value(value).build());
+ }
+ }
+
protected <E extends Enum<?>> void setAttribute(
final Set<Attr> attrs,
final List<SCIMComplexConf<E>> confs,
@@ -1006,18 +1044,24 @@ public class SCIMDataBinder {
final List<String> attributes,
final List<String> excludedAttributes) {
+ SCIMConf conf = confManager.get();
+ List<String> schemas = new ArrayList<>();
+ schemas.add(Resource.Group.schema());
+ if (conf.getExtensionGroupConf() != null) {
+ schemas.add(Resource.ExtensionGroup.schema());
+ }
+
SCIMGroup group = new SCIMGroup(
groupTO.getKey(),
+ schemas,
new Meta(
- Resource.Group,
+ Resource.Group.name(),
groupTO.getCreationDate(),
Optional.ofNullable(groupTO.getLastChangeDate()).orElseGet(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()));
@@ -1034,6 +1078,19 @@ public class SCIMDataBinder {
membCond.setGroup(groupTO.getKey());
SearchCond searchCond = SearchCond.of(membCond);
+ if (conf.getExtensionGroupConf() != null) {
+ SCIMExtensionInfo extensionInfo = new SCIMExtensionInfo();
+ conf.getExtensionGroupConf().asMap().forEach((scimAttr,
syncopeAttr) -> {
+ if (output(attributes, excludedAttributes, scimAttr) &&
attrs.containsKey(syncopeAttr)) {
+ extensionInfo.getAttributes().put(scimAttr,
attrs.get(syncopeAttr).getValues().getFirst());
+ }
+ });
+
+ if (!extensionInfo.isEmpty()) {
+ group.setExtensionInfo(extensionInfo);
+ }
+ }
+
if (output(attributes, excludedAttributes, "members")) {
long count = userLogic.search(
searchCond, PageRequest.of(0, 1),
SyncopeConstants.ROOT_REALM, true, false).getTotalElements();
@@ -1054,7 +1111,16 @@ public class SCIMDataBinder {
}
public GroupTO toGroupTO(final SCIMGroup group, final boolean
checkSchemas) {
- if (checkSchemas && !GROUP_SCHEMAS.equals(group.getSchemas())) {
+ SCIMConf conf = confManager.get();
+ Set<String> expectedSchemas = new HashSet<>();
+ expectedSchemas.add(Resource.Group.schema());
+ if (conf.getExtensionGroupConf() != null) {
+ expectedSchemas.add(Resource.ExtensionGroup.schema());
+ }
+ if (checkSchemas
+ && (!group.getSchemas().containsAll(expectedSchemas)
+ || !expectedSchemas.containsAll(group.getSchemas()))) {
+
throw new BadRequestException(ErrorType.invalidValue);
}
@@ -1063,7 +1129,6 @@ public class SCIMDataBinder {
groupTO.setKey(group.getId());
groupTO.setName(group.getDisplayName());
- SCIMConf conf = confManager.get();
if (conf.getGroupConf() != null
&& conf.getGroupConf().getExternalId() != null &&
group.getExternalId() != null) {
@@ -1072,6 +1137,11 @@ public class SCIMDataBinder {
value(group.getExternalId()).build());
}
+ if (conf.getExtensionGroupConf() != null && group.getExtensionInfo()
!= null) {
+ conf.getExtensionGroupConf().asMap().forEach((scimAttr,
syncopeAttr) -> setAttribute(
+ groupTO, syncopeAttr,
group.getExtensionInfo().getAttributes().get(scimAttr)));
+ }
+
return groupTO;
}
@@ -1098,11 +1168,146 @@ public class SCIMDataBinder {
groupUR.setName(name.build());
} else {
SCIMConf conf = confManager.get();
- if (conf.getGroupConf() != null) {
+ if (conf.getGroupConf() != null &&
"externalId".equals(op.getPath().getAttribute())) {
setAttribute(groupUR.getPlainAttrs(),
conf.getGroupConf().getExternalId(), op);
}
+ if (conf.getExtensionGroupConf() != null) {
+ Optional.ofNullable(conf.getExtensionGroupConf()).
+ flatMap(schema ->
Optional.ofNullable(schema.asMap().get(op.getPath().getAttribute()))).
+ ifPresent(schema ->
setAttribute(groupUR.getPlainAttrs(), schema, op));
+ }
}
return groupUR;
}
+
+ public SCIMAnyObject toSCIMAnyObject(
+ final AnyObjectTO anyObjectTO,
+ final String location,
+ final List<String> attributes,
+ final List<String> excludedAttributes) {
+ SCIMConf conf = confManager.get();
+ List<String> schemas = new ArrayList<>();
+ SCIMExtensionAnyObjectConf scimExtensionAnyObjectConf =
+ conf.getExtensionAnyObjectsConf().stream()
+ .filter(scimExtAnyObjectConf ->
scimExtAnyObjectConf.getType().equals(anyObjectTO.getType()))
+ .findFirst()
+ .orElseThrow(() -> new
NotFoundException("SCIMExtensionAnyObjectConf not found"));
+ schemas.add("urn:ietf:params:scim:schemas:extension:syncope:2.0:" +
scimExtensionAnyObjectConf.getType());
+
+ SCIMAnyObject anyObject = new SCIMAnyObject(
+ anyObjectTO.getKey(),
+ schemas,
+ new Meta(
+ "urn:ietf:params:scim:schemas:extension:syncope:2.0:"
+ + scimExtensionAnyObjectConf.getType(),
+ anyObjectTO.getCreationDate(),
+
Optional.ofNullable(anyObjectTO.getLastChangeDate()).orElse(anyObjectTO.getCreationDate()),
+ anyObjectTO.getETagValue(),
+ location),
+ output(attributes, excludedAttributes, "displayName",
anyObjectTO.getName()));
+
+ Map<String, Attr> attrs = new HashMap<>();
+ attrs.putAll(EntityTOUtils.buildAttrMap(anyObjectTO.getPlainAttrs()));
+ attrs.putAll(EntityTOUtils.buildAttrMap(anyObjectTO.getDerAttrs()));
+
+ if (output(attributes, excludedAttributes, "externalId")
+ && scimExtensionAnyObjectConf.getExternalId() != null
+ &&
attrs.containsKey(scimExtensionAnyObjectConf.getExternalId())) {
+
+
anyObject.setExternalId(attrs.get(scimExtensionAnyObjectConf.getExternalId()).getValues().getFirst());
+ }
+
+ SCIMExtensionInfo extensionInfo = new SCIMExtensionInfo();
+ scimExtensionAnyObjectConf.asMap().forEach((scimAttr, syncopeAttr) -> {
+ if (output(attributes, excludedAttributes, scimAttr) &&
attrs.containsKey(syncopeAttr)) {
+ extensionInfo.getAttributes().put(scimAttr,
attrs.get(syncopeAttr).getValues().getFirst());
+ }
+ });
+
+ if (!extensionInfo.isEmpty()) {
+ anyObject.setExtensionInfo(extensionInfo);
+ }
+
+ return anyObject;
+ }
+
+ public AnyObjectTO toAnyObjectTO(final SCIMAnyObject anyObject, final
boolean checkSchemas) {
+ SCIMConf conf = confManager.get();
+ Set<String> expectedSchemas = new HashSet<>();
+ Optional<SCIMExtensionAnyObjectConf> scimExtensionAnyObjectConf =
+ conf.getExtensionAnyObjectsConf().stream()
+ .filter(scimExtAnyObjectConf ->
+
scimExtAnyObjectConf.getType().equals(anyObject.getExtensionUrn()
+
.substring(anyObject.getExtensionUrn().lastIndexOf(':') + 1)))
+ .findFirst();
+ scimExtensionAnyObjectConf.ifPresent(scimExtAnyObjectConf ->
+
expectedSchemas.add("urn:ietf:params:scim:schemas:extension:syncope:2.0:"
+ + scimExtAnyObjectConf.getType()));
+ if (checkSchemas
+ && (!anyObject.getSchemas().containsAll(expectedSchemas)
+ || !expectedSchemas.containsAll(anyObject.getSchemas()))) {
+
+ throw new BadRequestException(ErrorType.invalidValue);
+ }
+
+ AnyObjectTO anyObjectTO = new AnyObjectTO();
+ anyObjectTO.setRealm(SyncopeConstants.ROOT_REALM);
+ anyObjectTO.setKey(anyObject.getId());
+ anyObjectTO.setName(anyObject.getDisplayName());
+
anyObjectTO.setType(anyObject.getExtensionUrn().substring(anyObject.getExtensionUrn().lastIndexOf(':')
+ 1));
+
+ if (scimExtensionAnyObjectConf.isPresent()
+ && scimExtensionAnyObjectConf.get().getExternalId() != null &&
anyObject.getExternalId() != null) {
+
+ anyObjectTO.getPlainAttrs().add(
+ new
Attr.Builder(scimExtensionAnyObjectConf.get().getExternalId()).
+ value(anyObject.getExternalId()).build());
+ }
+
+ if (scimExtensionAnyObjectConf.isPresent() &&
anyObject.getExtensionInfo() != null) {
+ scimExtensionAnyObjectConf.get().asMap().forEach((scimAttr,
syncopeAttr) -> setAttribute(
+ anyObjectTO, syncopeAttr,
anyObject.getExtensionInfo().getAttributes().get(scimAttr)));
+ }
+
+ return anyObjectTO;
+ }
+
+ public AnyObjectCR toAnyObjectCR(final SCIMAnyObject anyObject) {
+ AnyObjectTO anyObjectTO = toAnyObjectTO(anyObject, true);
+ AnyObjectCR anyObjectCR = new AnyObjectCR();
+ EntityTOUtils.toAnyCR(anyObjectTO, anyObjectCR);
+ return anyObjectCR;
+ }
+
+ public AnyObjectUR toAnyObjectUR(final AnyObjectTO before, final
SCIMPatchOperation op) {
+ if (op.getPath() == null) {
+ throw new UnsupportedOperationException("Empty path not supported
for AnyObjects");
+ }
+
+ AnyObjectUR anyObjectUR = new
AnyObjectUR.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().getFirst().toString());
+ }
+ anyObjectUR.setName(name.build());
+ } else {
+ SCIMConf conf = confManager.get();
+ Optional<SCIMExtensionAnyObjectConf> scimExtensionAnyObjectConf =
+ conf.getExtensionAnyObjectsConf().stream()
+ .filter(scimExtAnyObjectConf ->
scimExtAnyObjectConf.getType().equals(before.getType()))
+ .findFirst();
+ if (scimExtensionAnyObjectConf.isPresent() &&
"externalId".equals(op.getPath().getAttribute())) {
+ setAttribute(anyObjectUR.getPlainAttrs(),
scimExtensionAnyObjectConf.get().getExternalId(), op);
+ }
+ scimExtensionAnyObjectConf.flatMap(extensionAnyObjectConf ->
Optional.of(extensionAnyObjectConf)
+ .flatMap(schema ->
Optional.ofNullable(schema.asMap().get(op.getPath().getAttribute()))))
+ .ifPresent(schema ->
setAttribute(anyObjectUR.getPlainAttrs(), schema, op));
+ }
+
+ return anyObjectUR;
+ }
}
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 9c9d8c558a..719aac8b9f 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
@@ -100,6 +100,60 @@ public class SCIMLogic extends AbstractLogic<EntityTO> {
+ "\"location\":
\"/v2/Schemas/urn:ietf:params:scim:schemas:extension:syncope:2.0:User\"}"));
schemaArray.add(extensionObject);
}
+ if (conf.getExtensionGroupConf() != null) {
+ ObjectNode extensionObject = MAPPER.createObjectNode();
+ extensionObject.put("id", Resource.ExtensionGroup.schema());
+ extensionObject.put("name",
conf.getExtensionGroupConf().getName());
+ extensionObject.put("description",
conf.getExtensionGroupConf().getDescription());
+ ArrayNode attributes = MAPPER.createArrayNode();
+ conf.getExtensionGroupConf().getAttributes().forEach(scimItem
-> {
+ ObjectNode attribute = MAPPER.createObjectNode();
+ attribute.put("name", scimItem.getIntAttrName());
+ attribute.put("type", "string");
+ attribute.put("multiValued", scimItem.isMultiValued());
+ attribute.put("required",
scimItem.getMandatoryCondition());
+ attribute.put("caseExact", scimItem.isCaseExact());
+ attribute.put("mutability", scimItem.isMutability());
+ attribute.put("returned",
scimItem.getReturned().getReturned());
+ attribute.put("uniqueness", scimItem.isUniqueness());
+ attributes.add(attribute);
+ });
+ extensionObject.putIfAbsent("attributes", attributes);
+ extensionObject.putIfAbsent("meta",
MAPPER.readTree("{\"resourceType\": \"Schema\","
+ + "\"location\":
\"/v2/Schemas/urn:ietf:params:scim:schemas:extension:syncope:2.0:Group\"}"));
+ schemaArray.add(extensionObject);
+ }
+ if (!conf.getExtensionAnyObjectsConf().isEmpty()) {
+ conf.getExtensionAnyObjectsConf().forEach(confItem -> {
+ ObjectNode extensionObject = MAPPER.createObjectNode();
+ extensionObject.put("id",
+
"urn:ietf:params:scim:schemas:extension:syncope:2.0:" + confItem.getType());
+ extensionObject.put("name", confItem.getName());
+ extensionObject.put("description",
confItem.getDescription());
+ ArrayNode attributes = MAPPER.createArrayNode();
+ confItem.getAttributes().forEach(scimItem -> {
+ ObjectNode attribute = MAPPER.createObjectNode();
+ attribute.put("name", scimItem.getIntAttrName());
+ attribute.put("type", "string");
+ attribute.put("multiValued", scimItem.isMultiValued());
+ attribute.put("required",
scimItem.getMandatoryCondition());
+ attribute.put("caseExact", scimItem.isCaseExact());
+ attribute.put("mutability", scimItem.isMutability());
+ attribute.put("returned",
scimItem.getReturned().getReturned());
+ attribute.put("uniqueness", scimItem.isUniqueness());
+ attributes.add(attribute);
+ });
+ extensionObject.putIfAbsent("attributes", attributes);
+ try {
+ extensionObject.putIfAbsent("meta",
MAPPER.readTree("{\"resourceType\": \"Schema\","
+ + "\"location\":
\"/v2/Schemas/urn:ietf:params:scim:schemas:extension:syncope:2.0:"
+ + confItem.getType() + "}\""));
+ } catch (IOException e) {
+ LOG.error("Could not parse the default schema
definitions", e);
+ }
+ schemaArray.add(extensionObject);
+ });
+ }
schemas =
MAPPER.writerWithDefaultPrettyPrinter().writeValueAsString(tree);
schemaMap.clear();
@@ -121,7 +175,7 @@ public class SCIMLogic extends AbstractLogic<EntityTO> {
SERVICE_PROVIDER_CONFIG = new ServiceProviderConfig(
new Meta(
- Resource.ServiceProviderConfig,
+ Resource.ServiceProviderConfig.name(),
conf.getGeneralConf().getCreationDate(),
conf.getGeneralConf().getLastChangeDate(),
conf.getGeneralConf().getETagValue(),
@@ -162,12 +216,12 @@ public class SCIMLogic extends AbstractLogic<EntityTO> {
String uri = uriBuilder.build().toASCIIString();
if (USER == null) {
USER = new ResourceType("User", "User", "/Users", "User
Account", Resource.User.schema(),
- new Meta(Resource.ResourceType, null, null, null, uri
+ "User"));
+ new Meta(Resource.ResourceType.name(), null, null,
null, uri + "User"));
USER.getSchemaExtensions().add(new
SchemaExtension(Resource.EnterpriseUser.schema(), true));
}
if (GROUP == null) {
GROUP = new ResourceType("Group", "Group", "/Groups", "Group",
Resource.Group.schema(),
- new Meta(Resource.ResourceType, null, null, null, uri
+ "Group"));
+ new Meta(Resource.ResourceType.name(), null, null,
null, uri + "Group"));
}
}
diff --git
a/ext/scimv2/logic/src/main/java/org/apache/syncope/core/logic/scim/SCIMConfManager.java
b/ext/scimv2/logic/src/main/java/org/apache/syncope/core/logic/scim/SCIMConfManager.java
index 2a69aefe60..1d32dc87af 100644
---
a/ext/scimv2/logic/src/main/java/org/apache/syncope/core/logic/scim/SCIMConfManager.java
+++
b/ext/scimv2/logic/src/main/java/org/apache/syncope/core/logic/scim/SCIMConfManager.java
@@ -26,6 +26,7 @@ import
org.apache.syncope.common.keymaster.client.api.ConfParamOps;
import org.apache.syncope.common.lib.SyncopeClientException;
import org.apache.syncope.common.lib.scim.SCIMConf;
import org.apache.syncope.common.lib.scim.SCIMGeneralConf;
+import org.apache.syncope.common.lib.scim.SCIMItem;
import org.apache.syncope.common.lib.scim.types.SCIMEntitlement;
import org.apache.syncope.common.lib.to.PlainSchemaTO;
import org.apache.syncope.common.lib.types.AttrSchemaType;
@@ -88,34 +89,15 @@ public class SCIMConfManager {
conf.getGeneralConf().setLastChangeDate(OffsetDateTime.now());
if (conf.getExtensionUserConf() != null) {
- conf.getExtensionUserConf().getAttributes().forEach(scimItem -> {
- SyncopeClientException invalidMapping =
-
SyncopeClientException.build(ClientExceptionType.InvalidMapping);
- try {
- PlainSchemaTO schema = schemaLogic.read(SchemaType.PLAIN,
scimItem.getExtAttrName());
- if
(!scimItem.getMandatoryCondition().equals(schema.getMandatoryCondition())) {
- invalidMapping.getElements().add('\'' +
scimItem.getIntAttrName()
- + "' should " +
(Boolean.parseBoolean(schema.getMandatoryCondition()) ? "" : "not")
- + " be required");
- }
- if (scimItem.isMultiValued() != schema.isMultivalue()) {
- invalidMapping.getElements().add('\'' +
scimItem.getIntAttrName()
- + "' should " + (schema.isMultivalue() ? "" :
"not") + " be multi-value");
- }
- if (scimItem.isMutability() != schema.isReadonly()) {
- invalidMapping.getElements().add('\'' +
scimItem.getIntAttrName()
- + "' should " + (schema.isReadonly() ? "" :
"not") + " be readonly");
- }
- if (scimItem.isUniqueness() !=
schema.isUniqueConstraint()) {
- invalidMapping.getElements().add('\'' +
scimItem.getIntAttrName()
- + "' should " + (schema.isUniqueConstraint() ?
"" : "not") + " be unique");
- }
- if (!invalidMapping.getElements().isEmpty()) {
- throw invalidMapping;
- }
- } catch (NotFoundException e) {
- invalidMapping.getElements().add('\'' +
scimItem.getIntAttrName() + "' is not found");
- throw invalidMapping;
+
conf.getExtensionUserConf().getAttributes().forEach(this::checkSCIMItem);
+ }
+ if (conf.getExtensionGroupConf() != null) {
+
conf.getExtensionGroupConf().getAttributes().forEach(this::checkSCIMItem);
+ }
+ if (!conf.getExtensionAnyObjectsConf().isEmpty()) {
+ conf.getExtensionAnyObjectsConf().forEach(confItem -> {
+ if (confItem != null) {
+ confItem.getAttributes().forEach(this::checkSCIMItem);
}
});
}
@@ -123,4 +105,35 @@ public class SCIMConfManager {
confParamOps.set(AuthContextUtils.getDomain(),
SCIMConf.KEY,
Base64.getEncoder().encodeToString(POJOHelper.serialize(conf).getBytes()));
}
+
+ private void checkSCIMItem(final SCIMItem scimItem) {
+ SyncopeClientException invalidMapping =
+
SyncopeClientException.build(ClientExceptionType.InvalidMapping);
+ try {
+ PlainSchemaTO schema = schemaLogic.read(SchemaType.PLAIN,
scimItem.getExtAttrName());
+ if
(!scimItem.getMandatoryCondition().equals(schema.getMandatoryCondition())) {
+ invalidMapping.getElements().add('\'' +
scimItem.getIntAttrName()
+ + "' should " +
(Boolean.parseBoolean(schema.getMandatoryCondition()) ? "" : "not")
+ + " be required");
+ }
+ if (scimItem.isMultiValued() != schema.isMultivalue()) {
+ invalidMapping.getElements().add('\'' +
scimItem.getIntAttrName()
+ + "' should " + (schema.isMultivalue() ? "" : "not") +
" be multi-value");
+ }
+ if (scimItem.isMutability() != schema.isReadonly()) {
+ invalidMapping.getElements().add('\'' +
scimItem.getIntAttrName()
+ + "' should " + (schema.isReadonly() ? "" : "not") + "
be readonly");
+ }
+ if (scimItem.isUniqueness() != schema.isUniqueConstraint()) {
+ invalidMapping.getElements().add('\'' +
scimItem.getIntAttrName()
+ + "' should " + (schema.isUniqueConstraint() ? "" :
"not") + " be unique");
+ }
+ if (!invalidMapping.getElements().isEmpty()) {
+ throw invalidMapping;
+ }
+ } catch (NotFoundException e) {
+ invalidMapping.getElements().add('\'' + scimItem.getIntAttrName()
+ "' is not found");
+ throw invalidMapping;
+ }
+ }
}
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 933b461ace..ed029cce41 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
@@ -25,6 +25,7 @@ import java.util.stream.Collectors;
import org.apache.commons.lang3.StringUtils;
import org.apache.syncope.common.lib.scim.SCIMComplexConf;
import org.apache.syncope.common.lib.scim.SCIMConf;
+import org.apache.syncope.common.lib.scim.SCIMExtensionAnyObjectConf;
import org.apache.syncope.common.lib.scim.SCIMUserAddressConf;
import org.apache.syncope.common.lib.scim.SCIMUserConf;
import org.apache.syncope.core.persistence.api.dao.search.AnyCond;
@@ -40,12 +41,12 @@ public class SearchCondVisitor extends
SCIMFilterBaseVisitor<SearchCond> {
private static final List<String> MULTIVALUE = List.of(
"emails", "phoneNumbers", "ims", "photos", "addresses");
- private static boolean schemaEquals(final Resource resource, final String
value, final String schema) {
+ private static boolean schemaEquals(final String resource, final String
value, final String schema) {
return resource == null
? value.contains(":")
? StringUtils.substringAfterLast(value,
":").equalsIgnoreCase(schema)
: value.equalsIgnoreCase(schema)
- : value.equalsIgnoreCase(schema) || (resource.schema() + ":" +
value).equalsIgnoreCase(schema);
+ : value.equalsIgnoreCase(schema) || (resource + ":" +
value).equalsIgnoreCase(schema);
}
private static SearchCond setOperator(final AttrCond attrCond, final
String operator) {
@@ -154,11 +155,11 @@ public class SearchCondVisitor extends
SCIMFilterBaseVisitor<SearchCond> {
return null;
}
- private final Resource resource;
+ private final String resource;
private final SCIMConf conf;
- public SearchCondVisitor(final Resource resource, final SCIMConf conf) {
+ public SearchCondVisitor(final String resource, final SCIMConf conf) {
this.resource = resource;
this.conf = conf;
}
@@ -171,10 +172,13 @@ public class SearchCondVisitor extends
SCIMFilterBaseVisitor<SearchCond> {
public AttrCond createAttrCond(final String schema) {
AttrCond attrCond = null;
- if (schemaEquals(Resource.User, "userName", schema)) {
+ if (schemaEquals(Resource.User.schema(), "userName", schema)) {
attrCond = new AnyCond();
attrCond.setSchema("username");
- } else if (resource == Resource.Group && schemaEquals(Resource.Group,
"displayName", schema)) {
+ } else if ((resource.equals(Resource.Group.name())
+ && schemaEquals(Resource.Group.schema(), "displayName",
schema))
+ ||
(resource.contains("urn:ietf:params:scim:schemas:extension:syncope:2.0:")
+ && schemaEquals(resource, "displayName", schema))) {
attrCond = new AnyCond();
attrCond.setSchema("name");
} else if (schemaEquals(null, "meta.created", schema)) {
@@ -186,11 +190,11 @@ public class SearchCondVisitor extends
SCIMFilterBaseVisitor<SearchCond> {
}
switch (resource) {
- case User:
+ 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)) {
+ if (schemaEquals(Resource.User.schema(), "name." +
entry.getKey(), schema)) {
attrCond = new AttrCond();
attrCond.setSchema(entry.getValue());
}
@@ -198,7 +202,7 @@ public class SearchCondVisitor extends
SCIMFilterBaseVisitor<SearchCond> {
}
for (Map.Entry<String, String> entry :
conf.getUserConf().asMap().entrySet()) {
- if (schemaEquals(Resource.User, entry.getKey(),
schema)) {
+ if (schemaEquals(Resource.User.schema(),
entry.getKey(), schema)) {
attrCond = new AttrCond();
attrCond.setSchema(entry.getValue());
}
@@ -206,7 +210,7 @@ public class SearchCondVisitor extends
SCIMFilterBaseVisitor<SearchCond> {
for (SCIMUserAddressConf address :
conf.getUserConf().getAddresses()) {
for (Map.Entry<String, String> entry :
address.asMap().entrySet()) {
- if (schemaEquals(Resource.User, "addresses." +
entry.getKey(), schema)) {
+ if (schemaEquals(Resource.User.schema(),
"addresses." + entry.getKey(), schema)) {
attrCond = new AttrCond();
attrCond.setSchema(entry.getValue());
}
@@ -216,7 +220,7 @@ public class SearchCondVisitor extends
SCIMFilterBaseVisitor<SearchCond> {
if (conf.getEnterpriseUserConf() != null) {
for (Map.Entry<String, String> entry :
conf.getEnterpriseUserConf().asMap().entrySet()) {
- if (schemaEquals(Resource.EnterpriseUser,
entry.getKey(), schema)) {
+ if (schemaEquals(Resource.EnterpriseUser.schema(),
entry.getKey(), schema)) {
attrCond = new AttrCond();
attrCond.setSchema(entry.getValue());
}
@@ -232,7 +236,7 @@ public class SearchCondVisitor extends
SCIMFilterBaseVisitor<SearchCond> {
if (conf.getExtensionUserConf() != null) {
for (Map.Entry<String, String> entry :
conf.getExtensionUserConf().asMap().entrySet()) {
- if (schemaEquals(Resource.ExtensionUser,
entry.getKey(), schema)) {
+ if (schemaEquals(Resource.ExtensionUser.schema(),
entry.getKey(), schema)) {
attrCond = new AttrCond();
attrCond.setSchema(entry.getValue());
}
@@ -240,10 +244,19 @@ public class SearchCondVisitor extends
SCIMFilterBaseVisitor<SearchCond> {
}
break;
- case Group:
+ case "Group":
if (conf.getGroupConf() != null) {
for (Map.Entry<String, String> entry :
conf.getGroupConf().asMap().entrySet()) {
- if (schemaEquals(Resource.Group, entry.getKey(),
schema)) {
+ if (schemaEquals(Resource.Group.schema(),
entry.getKey(), schema)) {
+ attrCond = new AttrCond();
+ attrCond.setSchema(entry.getValue());
+ }
+ }
+ }
+
+ if (conf.getExtensionGroupConf() != null) {
+ for (Map.Entry<String, String> entry :
conf.getExtensionGroupConf().asMap().entrySet()) {
+ if (schemaEquals(Resource.ExtensionGroup.schema(),
entry.getKey(), schema)) {
attrCond = new AttrCond();
attrCond.setSchema(entry.getValue());
}
@@ -252,6 +265,20 @@ public class SearchCondVisitor extends
SCIMFilterBaseVisitor<SearchCond> {
break;
default:
+ if (!conf.getExtensionAnyObjectsConf().isEmpty()) {
+ Optional<SCIMExtensionAnyObjectConf> scimExtAnyObject =
conf.getExtensionAnyObjectsConf().stream()
+ .filter(scimExtensionAnyConf ->
scimExtensionAnyConf.getType().equals(resource))
+ .findFirst();
+ if (scimExtAnyObject.isPresent()) {
+ for (Map.Entry<String, String> entry :
scimExtAnyObject.get().asMap().entrySet()) {
+ if
(schemaEquals("urn:ietf:params:scim:schemas:extension:syncope:2.0:" + resource,
+ entry.getKey(), schema)) {
+ attrCond = new AttrCond();
+ attrCond.setSchema(entry.getValue());
+ }
+ }
+ }
+ }
}
if (attrCond == null) {
diff --git
a/ext/scimv2/logic/src/test/java/org/apache/syncope/core/logic/scim/SCIMFilterTest.java
b/ext/scimv2/logic/src/test/java/org/apache/syncope/core/logic/scim/SCIMFilterTest.java
index 6787f0cfd1..2fec0efe0f 100644
---
a/ext/scimv2/logic/src/test/java/org/apache/syncope/core/logic/scim/SCIMFilterTest.java
+++
b/ext/scimv2/logic/src/test/java/org/apache/syncope/core/logic/scim/SCIMFilterTest.java
@@ -25,7 +25,7 @@ import static org.junit.jupiter.api.Assertions.assertNull;
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.SCIMExtensionUserConf;
+import org.apache.syncope.common.lib.scim.SCIMExtensionAnyConf;
import org.apache.syncope.common.lib.scim.SCIMItem;
import org.apache.syncope.common.lib.scim.SCIMUserConf;
import org.apache.syncope.common.lib.scim.SCIMUserNameConf;
@@ -65,14 +65,14 @@ public class SCIMFilterTest {
entConf.setOrganization("org");
conf.setEnterpriseUserConf(entConf);
- SCIMExtensionUserConf extConf = new SCIMExtensionUserConf();
+ SCIMExtensionAnyConf extConf = new SCIMExtensionAnyConf();
SCIMItem item = new SCIMItem();
item.setIntAttrName("realm");
item.setExtAttrName("realm");
extConf.add(item);
conf.setExtensionUserConf(extConf);
- VISITOR = new SearchCondVisitor(Resource.User, conf);
+ VISITOR = new SearchCondVisitor(Resource.User.name(), conf);
}
@Test
diff --git
a/ext/scimv2/scim-rest-api/src/main/java/org/apache/syncope/ext/scimv2/api/data/Meta.java
b/ext/scimv2/scim-rest-api/src/main/java/org/apache/syncope/ext/scimv2/api/data/Meta.java
index f7914dcd37..62a015528c 100644
---
a/ext/scimv2/scim-rest-api/src/main/java/org/apache/syncope/ext/scimv2/api/data/Meta.java
+++
b/ext/scimv2/scim-rest-api/src/main/java/org/apache/syncope/ext/scimv2/api/data/Meta.java
@@ -25,13 +25,12 @@ import jakarta.ws.rs.core.EntityTag;
import jakarta.ws.rs.ext.RuntimeDelegate;
import java.time.OffsetDateTime;
import java.util.Optional;
-import org.apache.syncope.ext.scimv2.api.type.Resource;
public class Meta extends SCIMBean {
private static final long serialVersionUID = 8976451652101091915L;
- private final Resource resourceType;
+ private final String resourceType;
private final OffsetDateTime created;
@@ -44,7 +43,7 @@ public class Meta extends SCIMBean {
@JsonCreator(mode = JsonCreator.Mode.PROPERTIES)
public Meta(
- @JsonProperty("resourceType") final Resource resourceType,
+ @JsonProperty("resourceType") final String resourceType,
@JsonProperty("created") final OffsetDateTime created,
@JsonProperty("lastModified") final OffsetDateTime lastModified,
@JsonProperty("version") final String version,
@@ -57,7 +56,7 @@ public class Meta extends SCIMBean {
this.location = location;
}
- public Resource getResourceType() {
+ public String getResourceType() {
return resourceType;
}
diff --git
a/ext/scimv2/scim-rest-api/src/main/java/org/apache/syncope/ext/scimv2/api/data/SCIMAnyObject.java
b/ext/scimv2/scim-rest-api/src/main/java/org/apache/syncope/ext/scimv2/api/data/SCIMAnyObject.java
new file mode 100644
index 0000000000..ef68410900
--- /dev/null
+++
b/ext/scimv2/scim-rest-api/src/main/java/org/apache/syncope/ext/scimv2/api/data/SCIMAnyObject.java
@@ -0,0 +1,90 @@
+/*
+ * 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.annotation.JsonAnyGetter;
+import com.fasterxml.jackson.annotation.JsonAnySetter;
+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.List;
+import java.util.Map;
+
+@JsonPropertyOrder({ "schemas", "id", "externalId", "displayName",
"extensionInfo", "meta" })
+public class SCIMAnyObject extends SCIMResource {
+
+ @JsonIgnore
+ private String extensionUrn;
+
+ @JsonIgnore
+ private SCIMExtensionInfo extensionInfo;
+
+ @JsonCreator(mode = JsonCreator.Mode.PROPERTIES)
+ public SCIMAnyObject(
+ @JsonProperty("id") final String id,
+ @JsonProperty("schemas") final List<String> schemas,
+ @JsonProperty("meta") final Meta meta,
+ @JsonProperty("displayName") final String displayName) {
+
+ super(id, schemas, meta);
+ super.setDisplayName(displayName);
+ this.extensionUrn = schemas.isEmpty() ? null : schemas.getFirst();
+ }
+
+ @JsonAnyGetter
+ public Map<String, SCIMExtensionInfo> getExtensionAsMap() {
+ if (extensionUrn != null && extensionInfo != null &&
!extensionInfo.isEmpty()) {
+ return Map.of(extensionUrn, extensionInfo);
+ }
+ return Map.of();
+ }
+
+ @JsonAnySetter
+ public void setDynamicExtension(final String key, final Object value) {
+ if
(key.startsWith("urn:ietf:params:scim:schemas:extension:syncope:2.0:")) {
+ this.extensionUrn = key;
+
+ if (value instanceof Map) {
+ SCIMExtensionInfo info = new SCIMExtensionInfo();
+ Map<?, ?> rawMap = (Map<?, ?>) value;
+ for (Map.Entry<?, ?> entry : rawMap.entrySet()) {
+ info.add(entry.getKey().toString(),
entry.getValue().toString());
+ }
+ this.extensionInfo = info;
+ }
+ }
+ }
+
+ public String getExtensionUrn() {
+ return extensionUrn;
+ }
+
+ public void setExtensionUrn(final String extensionUrn) {
+ this.extensionUrn = extensionUrn;
+ }
+
+ public SCIMExtensionInfo getExtensionInfo() {
+ return extensionInfo;
+ }
+
+ public void setExtensionInfo(final SCIMExtensionInfo extensionInfo) {
+ this.extensionInfo = extensionInfo;
+ }
+}
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 c45d6da49d..248f09ab25 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
@@ -23,26 +23,37 @@ import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.annotation.JsonPropertyOrder;
import java.util.ArrayList;
import java.util.List;
-import org.apache.syncope.ext.scimv2.api.type.Resource;
-@JsonPropertyOrder({ "schemas", "id", "externalId", "displayName", "members",
"meta" })
+@JsonPropertyOrder({ "schemas", "id", "externalId", "displayName", "members",
"extensionInfo", "meta" })
public class SCIMGroup extends SCIMResource {
private static final long serialVersionUID = -2935466041674390279L;
private final List<Member> members = new ArrayList<>();
+ @JsonProperty("urn:ietf:params:scim:schemas:extension:syncope:2.0:Group")
+ private SCIMExtensionInfo extensionInfo;
+
@JsonCreator(mode = JsonCreator.Mode.PROPERTIES)
public SCIMGroup(
@JsonProperty("id") final String id,
+ @JsonProperty("schemas") final List<String> schemas,
@JsonProperty("meta") final Meta meta,
@JsonProperty("displayName") final String displayName) {
- super(id, List.of(Resource.Group.schema()), meta);
+ super(id, schemas, meta);
super.setDisplayName(displayName);
}
public List<Member> getMembers() {
return members;
}
+
+ public SCIMExtensionInfo getExtensionInfo() {
+ return extensionInfo;
+ }
+
+ public void setExtensionInfo(final SCIMExtensionInfo extensionInfo) {
+ this.extensionInfo = extensionInfo;
+ }
}
diff --git
a/ext/scimv2/scim-rest-api/src/main/java/org/apache/syncope/ext/scimv2/api/service/SCIMAnyObjectService.java
b/ext/scimv2/scim-rest-api/src/main/java/org/apache/syncope/ext/scimv2/api/service/SCIMAnyObjectService.java
new file mode 100644
index 0000000000..822b5d558f
--- /dev/null
+++
b/ext/scimv2/scim-rest-api/src/main/java/org/apache/syncope/ext/scimv2/api/service/SCIMAnyObjectService.java
@@ -0,0 +1,26 @@
+/*
+ * 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 jakarta.ws.rs.Path;
+import org.apache.syncope.ext.scimv2.api.data.SCIMAnyObject;
+
+@Path("v2/AnyObjects")
+public interface SCIMAnyObjectService extends
SCIMResourceService<SCIMAnyObject> {
+}
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 0cbd6d2828..6882adff77 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
@@ -27,6 +27,7 @@ public enum Resource {
EnterpriseUser("urn:ietf:params:scim:schemas:extension:enterprise:2.0:User"),
ExtensionUser("urn:ietf:params:scim:schemas:extension:syncope:2.0:User"),
Group("urn:ietf:params:scim:schemas:core:2.0:Group"),
+ ExtensionGroup("urn:ietf:params:scim:schemas:extension:syncope: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"),
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 e74064f6e4..15bc81232b 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
@@ -29,16 +29,20 @@ import
org.apache.cxf.jaxrs.spring.JAXRSServerFactoryBeanDefinitionParser.Spring
import org.apache.cxf.jaxrs.validation.JAXRSBeanValidationInInterceptor;
import org.apache.cxf.transport.common.gzip.GZIPInInterceptor;
import org.apache.cxf.transport.common.gzip.GZIPOutInterceptor;
+import org.apache.syncope.core.logic.AnyObjectLogic;
import org.apache.syncope.core.logic.GroupLogic;
import org.apache.syncope.core.logic.SCIMDataBinder;
import org.apache.syncope.core.logic.SCIMLogic;
import org.apache.syncope.core.logic.UserLogic;
import org.apache.syncope.core.logic.scim.SCIMConfManager;
+import org.apache.syncope.core.persistence.api.dao.AnyObjectDAO;
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.SCIMAnyObjectService;
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.SCIMUserService;
+import org.apache.syncope.ext.scimv2.cxf.service.SCIMAnyObjectServiceImpl;
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.SCIMUserServiceImpl;
@@ -75,6 +79,7 @@ public class SCIMv2RESTCXFContext {
final SCIMService scimService,
final SCIMGroupService scimv2GroupService,
final SCIMUserService scimv2UserService,
+ final SCIMAnyObjectService scimv2AnyObjectService,
final GZIPInInterceptor gzipInInterceptor,
final GZIPOutInterceptor gzipOutInterceptor,
final JAXRSBeanValidationInInterceptor validationInInterceptor,
@@ -91,7 +96,8 @@ public class SCIMv2RESTCXFContext {
scimv2Container.setProperties(Map.of("convert.wadl.resources.to.dom",
"false"));
- scimv2Container.setServiceBeans(List.of(scimService,
scimv2GroupService, scimv2UserService));
+ scimv2Container.setServiceBeans(
+ List.of(scimService, scimv2GroupService, scimv2UserService,
scimv2AnyObjectService));
scimv2Container.setInInterceptors(List.of(gzipInInterceptor,
validationInInterceptor));
@@ -108,13 +114,16 @@ public class SCIMv2RESTCXFContext {
public SCIMService scimService(
final UserDAO userDAO,
final GroupDAO groupDAO,
+ final AnyObjectDAO anyObjectDAO,
final UserLogic userLogic,
final GroupLogic groupLogic,
+ final AnyObjectLogic anyObjectLogic,
final SCIMDataBinder binder,
final SCIMConfManager confManager,
final SCIMLogic scimLogic) {
- return new SCIMServiceImpl(userDAO, groupDAO, userLogic, groupLogic,
binder, confManager, scimLogic);
+ return new SCIMServiceImpl(userDAO, groupDAO, anyObjectDAO, userLogic,
groupLogic, anyObjectLogic, binder,
+ confManager, scimLogic);
}
@ConditionalOnMissingBean
@@ -122,12 +131,15 @@ public class SCIMv2RESTCXFContext {
public SCIMGroupService scimv2GroupService(
final UserDAO userDAO,
final GroupDAO groupDAO,
+ final AnyObjectDAO anyObjectDAO,
final UserLogic userLogic,
final GroupLogic groupLogic,
+ final AnyObjectLogic anyObjectLogic,
final SCIMDataBinder binder,
final SCIMConfManager confManager) {
- return new SCIMGroupServiceImpl(userDAO, groupDAO, userLogic,
groupLogic, binder, confManager);
+ return new SCIMGroupServiceImpl(userDAO, groupDAO, anyObjectDAO,
userLogic, groupLogic, anyObjectLogic, binder,
+ confManager);
}
@ConditionalOnMissingBean
@@ -135,11 +147,30 @@ public class SCIMv2RESTCXFContext {
public SCIMUserService scimv2UserService(
final UserDAO userDAO,
final GroupDAO groupDAO,
+ final AnyObjectDAO anyObjectDAO,
final UserLogic userLogic,
final GroupLogic groupLogic,
+ final AnyObjectLogic anyObjectLogic,
final SCIMDataBinder binder,
final SCIMConfManager confManager) {
- return new SCIMUserServiceImpl(userDAO, groupDAO, userLogic,
groupLogic, binder, confManager);
+ return new SCIMUserServiceImpl(userDAO, groupDAO, anyObjectDAO,
userLogic, groupLogic, anyObjectLogic, binder,
+ confManager);
+ }
+
+ @ConditionalOnMissingBean
+ @Bean
+ public SCIMAnyObjectService scimv2AnyObjectService(
+ final UserDAO userDAO,
+ final GroupDAO groupDAO,
+ final AnyObjectDAO anyObjectDAO,
+ final UserLogic userLogic,
+ final GroupLogic groupLogic,
+ final AnyObjectLogic anyObjectLogic,
+ final SCIMDataBinder binder,
+ final SCIMConfManager confManager) {
+
+ return new SCIMAnyObjectServiceImpl(userDAO, groupDAO, anyObjectDAO,
userLogic, groupLogic, anyObjectLogic,
+ binder, confManager);
}
}
diff --git
a/ext/scimv2/scim-rest-cxf/src/main/java/org/apache/syncope/ext/scimv2/cxf/service/AbstractSCIMService.java
b/ext/scimv2/scim-rest-cxf/src/main/java/org/apache/syncope/ext/scimv2/cxf/service/AbstractSCIMService.java
index 7fb4a2fa57..aaaa8fea86 100644
---
a/ext/scimv2/scim-rest-cxf/src/main/java/org/apache/syncope/ext/scimv2/cxf/service/AbstractSCIMService.java
+++
b/ext/scimv2/scim-rest-cxf/src/main/java/org/apache/syncope/ext/scimv2/cxf/service/AbstractSCIMService.java
@@ -28,12 +28,14 @@ import java.util.List;
import org.apache.commons.lang3.StringUtils;
import org.apache.cxf.jaxrs.ext.MessageContext;
import org.apache.syncope.common.lib.SyncopeConstants;
+import org.apache.syncope.common.lib.to.AnyObjectTO;
import org.apache.syncope.common.lib.to.AnyTO;
import org.apache.syncope.common.lib.to.GroupTO;
import org.apache.syncope.common.lib.to.UserTO;
import org.apache.syncope.common.rest.api.Preference;
import org.apache.syncope.common.rest.api.RESTHeaders;
import org.apache.syncope.core.logic.AbstractAnyLogic;
+import org.apache.syncope.core.logic.AnyObjectLogic;
import org.apache.syncope.core.logic.GroupLogic;
import org.apache.syncope.core.logic.SCIMDataBinder;
import org.apache.syncope.core.logic.UserLogic;
@@ -41,9 +43,12 @@ import org.apache.syncope.core.logic.scim.SCIMConfManager;
import org.apache.syncope.core.logic.scim.SearchCondConverter;
import org.apache.syncope.core.logic.scim.SearchCondVisitor;
import org.apache.syncope.core.persistence.api.dao.AnyDAO;
+import org.apache.syncope.core.persistence.api.dao.AnyObjectDAO;
import org.apache.syncope.core.persistence.api.dao.GroupDAO;
import org.apache.syncope.core.persistence.api.dao.NotFoundException;
import org.apache.syncope.core.persistence.api.dao.UserDAO;
+import org.apache.syncope.core.persistence.api.dao.search.AnyTypeCond;
+import org.apache.syncope.core.persistence.api.dao.search.SearchCond;
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.SCIMResource;
@@ -57,7 +62,7 @@ import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Sort;
-abstract class AbstractSCIMService<R extends SCIMResource> {
+public abstract class AbstractSCIMService<R extends SCIMResource> {
protected static final Logger LOG =
LoggerFactory.getLogger(AbstractSCIMService.class);
@@ -71,10 +76,14 @@ abstract class AbstractSCIMService<R extends SCIMResource> {
protected final GroupDAO groupDAO;
+ protected final AnyObjectDAO anyObjectDAO;
+
protected final UserLogic userLogic;
protected final GroupLogic groupLogic;
+ protected final AnyObjectLogic anyObjectLogic;
+
protected final SCIMDataBinder binder;
protected final SCIMConfManager confManager;
@@ -82,46 +91,52 @@ abstract class AbstractSCIMService<R extends SCIMResource> {
protected AbstractSCIMService(
final UserDAO userDAO,
final GroupDAO groupDAO,
+ final AnyObjectDAO anyObjectDAO,
final UserLogic userLogic,
final GroupLogic groupLogic,
+ final AnyObjectLogic anyObjectLogic,
final SCIMDataBinder binder,
final SCIMConfManager confManager) {
this.userDAO = userDAO;
this.groupDAO = groupDAO;
+ this.anyObjectDAO = anyObjectDAO;
this.userLogic = userLogic;
this.groupLogic = groupLogic;
+ this.anyObjectLogic = anyObjectLogic;
this.binder = binder;
this.confManager = confManager;
}
- protected AnyDAO<?> anyDAO(final Resource type) {
+ protected AnyDAO<?> anyDAO(final String type) {
switch (type) {
- case User -> {
+ case "urn:ietf:params:scim:schemas:core:2.0:User" -> {
return userDAO;
}
- case Group -> {
+ case "urn:ietf:params:scim:schemas:core:2.0:Group" -> {
return groupDAO;
}
- default ->
- throw new UnsupportedOperationException();
+ default -> {
+ return anyObjectDAO;
+ }
}
}
- protected AbstractAnyLogic<?, ?, ?> anyLogic(final Resource type) {
+ protected AbstractAnyLogic<?, ?, ?> anyLogic(final String type) {
switch (type) {
- case User -> {
+ case "User" -> {
return userLogic;
}
- case Group -> {
+ case "Group" -> {
return groupLogic;
}
- default ->
- throw new UnsupportedOperationException();
+ default -> {
+ return anyObjectLogic;
+ }
}
}
@@ -158,7 +173,7 @@ abstract class AbstractSCIMService<R extends SCIMResource> {
protected abstract SCIMResource getResource(String key);
- protected ResponseBuilder checkETag(final Resource resource, final String
key) {
+ protected ResponseBuilder checkETag(final String resource, final String
key) {
OffsetDateTime lastChange = anyDAO(resource).findLastChange(key).
orElseThrow(() -> new NotFoundException("Resource" + key + "
not found"));
@@ -167,7 +182,10 @@ abstract class AbstractSCIMService<R extends SCIMResource>
{
}
@SuppressWarnings("unchecked")
- protected ListResponse<R> doSearch(final Resource type, final
SCIMSearchRequest request) {
+ protected ListResponse<R> doSearch(
+ final String type,
+ final SCIMSearchRequest request) {
+
if (type == null) {
throw new UnsupportedOperationException();
}
@@ -205,10 +223,24 @@ abstract class AbstractSCIMService<R extends
SCIMResource> {
visitor.createAttrCond(request.getSortBy()).getSchema()));
}
+ SearchCond searchCond = null;
+ String filter = request.getFilter();
+ if (!Resource.Group.name().equals(type) &&
!Resource.User.name().equals(type)) {
+ AnyTypeCond cond = new AnyTypeCond();
+ cond.setAnyTypeKey(type);
+ searchCond = SearchCond.of(cond);
+ filter = filter.replaceAll(
+
"(\\s*(and|or)\\s+)?type\\s+eq\\s+\"[^\"]*\"(\\s*(and|or)\\s+)?", " ")
+ .trim().replaceAll("\\s{2,}", " ");
+ }
+ if (StringUtils.isNotBlank(filter)) {
+ SearchCond filterCond = SearchCondConverter.convert(visitor,
filter);
+ searchCond = (searchCond == null)
+ ? filterCond
+ : SearchCond.and(filterCond, searchCond);
+ }
Page<? extends AnyTO> result = anyLogic(type).search(
- StringUtils.isBlank(request.getFilter())
- ? null
- : SearchCondConverter.convert(visitor, request.getFilter()),
+ searchCond,
PageRequest.of(page, itemsPerPage, Sort.by(sort)),
SyncopeConstants.ROOT_REALM,
true,
@@ -234,6 +266,12 @@ abstract class AbstractSCIMService<R extends SCIMResource>
{
uriInfo.getAbsolutePathBuilder().path(anyTO.getKey()).build().toASCIIString(),
request.getAttributes(),
request.getExcludedAttributes());
+ } else if (anyTO instanceof AnyObjectTO anyObjectTO) {
+ resource = binder.toSCIMAnyObject(
+ anyObjectTO,
+
uriInfo.getAbsolutePathBuilder().path(anyTO.getKey()).build().toASCIIString(),
+ request.getAttributes(),
+ request.getExcludedAttributes());
}
if (resource != null) {
diff --git
a/ext/scimv2/scim-rest-cxf/src/main/java/org/apache/syncope/ext/scimv2/cxf/service/SCIMUserServiceImpl.java
b/ext/scimv2/scim-rest-cxf/src/main/java/org/apache/syncope/ext/scimv2/cxf/service/SCIMAnyObjectServiceImpl.java
similarity index 57%
copy from
ext/scimv2/scim-rest-cxf/src/main/java/org/apache/syncope/ext/scimv2/cxf/service/SCIMUserServiceImpl.java
copy to
ext/scimv2/scim-rest-cxf/src/main/java/org/apache/syncope/ext/scimv2/cxf/service/SCIMAnyObjectServiceImpl.java
index dfa7f6fa62..9edf54a5ec 100644
---
a/ext/scimv2/scim-rest-cxf/src/main/java/org/apache/syncope/ext/scimv2/cxf/service/SCIMUserServiceImpl.java
+++
b/ext/scimv2/scim-rest-cxf/src/main/java/org/apache/syncope/ext/scimv2/cxf/service/SCIMAnyObjectServiceImpl.java
@@ -19,86 +19,114 @@
package org.apache.syncope.ext.scimv2.cxf.service;
import jakarta.ws.rs.core.Response;
-import jakarta.ws.rs.core.Response.ResponseBuilder;
import java.util.List;
-import java.util.Optional;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
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.request.AnyObjectUR;
+import org.apache.syncope.common.lib.to.AnyObjectTO;
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.AnyObjectLogic;
import org.apache.syncope.core.logic.GroupLogic;
import org.apache.syncope.core.logic.SCIMDataBinder;
import org.apache.syncope.core.logic.UserLogic;
import org.apache.syncope.core.logic.scim.SCIMConfManager;
+import org.apache.syncope.core.persistence.api.dao.AnyObjectDAO;
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.SCIMAnyObject;
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.data.SCIMUser;
-import org.apache.syncope.ext.scimv2.api.service.SCIMUserService;
+import org.apache.syncope.ext.scimv2.api.service.SCIMAnyObjectService;
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 SCIMUserServiceImpl extends AbstractSCIMService<SCIMUser>
implements SCIMUserService {
+public class SCIMAnyObjectServiceImpl extends
AbstractSCIMService<SCIMAnyObject> implements SCIMAnyObjectService {
- public SCIMUserServiceImpl(
+ public SCIMAnyObjectServiceImpl(
final UserDAO userDAO,
final GroupDAO groupDAO,
+ final AnyObjectDAO anyObjectDAO,
final UserLogic userLogic,
final GroupLogic groupLogic,
+ final AnyObjectLogic anyObjectLogic,
final SCIMDataBinder binder,
final SCIMConfManager confManager) {
- super(userDAO, groupDAO, userLogic, groupLogic, binder, confManager);
+ super(userDAO, groupDAO, anyObjectDAO, userLogic, groupLogic,
anyObjectLogic, binder, confManager);
}
@Override
- public Response create(final SCIMUser user) {
- ProvisioningResult<UserTO> result =
userLogic.create(binder.toUserCR(user), false);
+ public SCIMAnyObject get(final String id, final String attributes, final
String excludedAttributes) {
+ return binder.toSCIMAnyObject(
+ anyObjectLogic.read(id),
+ uriInfo.getAbsolutePathBuilder().build().toASCIIString(),
+ List.of(ArrayUtils.nullToEmpty(StringUtils.split(attributes,
','))),
+
List.of(ArrayUtils.nullToEmpty(StringUtils.split(excludedAttributes, ','))));
+ }
+
+ @Override
+ public ListResponse<SCIMAnyObject> search(
+ final String attributes, final String excludedAttributes, final
String filter, final String sortBy,
+ final SortOrder sortOrder, final Integer startIndex, final Integer
count) {
+
+ SCIMSearchRequest request = new SCIMSearchRequest(filter, sortBy,
sortOrder, startIndex, count);
+ if (attributes != null) {
+ request.getAttributes().addAll(
+
List.of(ArrayUtils.nullToEmpty(StringUtils.split(attributes, ','))));
+ }
+ if (excludedAttributes != null) {
+ request.getExcludedAttributes().addAll(
+
List.of(ArrayUtils.nullToEmpty(StringUtils.split(excludedAttributes, ','))));
+ }
+
+ Matcher matcher =
Pattern.compile("type\\s+eq\\s+\"(.*?)\"").matcher(filter);
+ if (matcher.find()) {
+ return doSearch(matcher.group(1), request);
+ } else {
+ throw new UnsupportedOperationException("Need to specify type");
+ }
+ }
+
+ @Override
+ public ListResponse<SCIMAnyObject> search(final SCIMSearchRequest request)
{
+ Matcher matcher =
Pattern.compile("type\\s+eq\\s+\"(.*?)\"").matcher(request.getFilter());
+ if (matcher.find()) {
+ return doSearch(matcher.group(1), request);
+ } else {
+ throw new UnsupportedOperationException("Need to specify type");
+ }
+ }
+
+ @Override
+ public Response create(final SCIMAnyObject anyObject) {
+ ProvisioningResult<AnyObjectTO> result =
anyObjectLogic.create(binder.toAnyObjectCR(anyObject), false);
return createResponse(
result.getEntity().getKey(),
- binder.toSCIMUser(
+ binder.toSCIMAnyObject(
result.getEntity(),
uriInfo.getAbsolutePathBuilder().path(result.getEntity().getKey()).build().toASCIIString(),
List.of(),
List.of()));
}
- @Override
- public SCIMUser get(final String id,
- final String attributes,
- final String excludedAttributes) {
-
- return binder.toSCIMUser(
- userLogic.read(id),
- uriInfo.getAbsolutePathBuilder().build().toASCIIString(),
- List.of(ArrayUtils.nullToEmpty(StringUtils.split(attributes,
','))),
-
List.of(ArrayUtils.nullToEmpty(StringUtils.split(excludedAttributes, ','))));
- }
-
@Override
public Response update(final String id, final SCIMPatchOp patch) {
- ResponseBuilder builder = checkETag(Resource.User, id);
+ SCIMResource resource = getResource(id);
+ Response.ResponseBuilder builder = checkETag(
+ "urn:ietf:params:scim:schemas:extension:syncope:2.0:" +
resource.getSchemas().getFirst(), id);
if (builder != null) {
return builder.build();
}
patch.getOperations().forEach(op -> {
- Pair<UserUR, StatusR> update = binder.toUserUpdate(
- userLogic.read(id),
- userDAO.findAllResourceKeys(id),
- op);
- userLogic.update(update.getLeft(), false);
- Optional.ofNullable(update.getRight()).ifPresent(statusR ->
userLogic.status(statusR, false));
+ AnyObjectUR update = binder.toAnyObjectUR(anyObjectLogic.read(id),
op);
+ anyObjectLogic.update(update, false);
});
return updateResponse(
@@ -108,37 +136,29 @@ public class SCIMUserServiceImpl extends
AbstractSCIMService<SCIMUser> implement
}
@Override
- public Response replace(final String id, final SCIMUser user) {
- if (!id.equals(user.getId())) {
- throw new BadRequestException(ErrorType.invalidPath, "Expected " +
id + ", found " + user.getId());
+ public Response replace(final String id, final SCIMAnyObject anyObject) {
+ if (!id.equals(anyObject.getId())) {
+ throw new BadRequestException(ErrorType.invalidPath, "Expected " +
id + ", found " + anyObject.getId());
}
- ResponseBuilder builder = checkETag(Resource.User, id);
+ SCIMResource resource = getResource(id);
+ Response.ResponseBuilder builder = checkETag(
+ "urn:ietf:params:scim:schemas:extension:syncope:2.0:" +
resource.getSchemas().getFirst(), id);
if (builder != null) {
return builder.build();
}
- UserTO before = userLogic.read(id);
+ AnyObjectTO before = anyObjectLogic.read(id);
- UserUR req = AnyOperations.diff(binder.toUserTO(user, true), before,
false);
+ AnyObjectUR req = AnyOperations.diff(binder.toAnyObjectTO(anyObject,
true), before, false);
req.getResources().clear();
req.getAuxClasses().clear();
req.getRelationships().clear();
- req.getRoles().clear();
- req.getLinkedAccounts().clear();
- ProvisioningResult<UserTO> result = userLogic.update(req, false);
-
- if (before.isSuspended() == user.isActive()) {
- StatusR statusR = new StatusR.Builder(
- before.getKey(),
- user.isActive() ? StatusRType.REACTIVATE :
StatusRType.SUSPEND).
- build();
- userLogic.status(statusR, false);
- }
+ ProvisioningResult<AnyObjectTO> result = anyObjectLogic.update(req,
false);
return updateResponse(
result.getEntity().getKey(),
- binder.toSCIMUser(
+ binder.toSCIMAnyObject(
result.getEntity(),
uriInfo.getAbsolutePathBuilder().path(result.getEntity().getKey()).build().toASCIIString(),
List.of(),
@@ -146,51 +166,25 @@ public class SCIMUserServiceImpl extends
AbstractSCIMService<SCIMUser> implement
false);
}
- @Override
- protected SCIMResource getResource(final String key) {
- return binder.toSCIMUser(
- userLogic.read(key),
-
uriInfo.getAbsolutePathBuilder().path(key).build().toASCIIString(),
- List.of(),
- List.of());
- }
-
@Override
public Response delete(final String id) {
- ResponseBuilder builder = checkETag(Resource.User, id);
+ SCIMResource resource = getResource(id);
+ Response.ResponseBuilder builder = checkETag(
+ "urn:ietf:params:scim:schemas:extension:syncope:2.0:" +
resource.getSchemas().getFirst(), id);
if (builder != null) {
return builder.build();
}
- anyLogic(Resource.User).delete(id, false);
+ anyObjectLogic.delete(id, false);
return Response.noContent().build();
}
@Override
- public ListResponse<SCIMUser> search(
- final String attributes,
- final String excludedAttributes,
- final String filter,
- final String sortBy,
- final SortOrder sortOrder,
- final Integer startIndex,
- final Integer count) {
-
- SCIMSearchRequest request = new SCIMSearchRequest(filter, sortBy,
sortOrder, startIndex, count);
- if (attributes != null) {
- request.getAttributes().addAll(
-
List.of(ArrayUtils.nullToEmpty(StringUtils.split(attributes, ','))));
- }
- if (excludedAttributes != null) {
- request.getExcludedAttributes().addAll(
-
List.of(ArrayUtils.nullToEmpty(StringUtils.split(excludedAttributes, ','))));
- }
-
- return doSearch(Resource.User, request);
- }
-
- @Override
- public ListResponse<SCIMUser> search(final SCIMSearchRequest request) {
- return doSearch(Resource.User, request);
+ protected SCIMResource getResource(final String key) {
+ return binder.toSCIMAnyObject(
+ anyObjectLogic.read(key),
+
uriInfo.getAbsolutePathBuilder().path(key).build().toASCIIString(),
+ List.of(),
+ List.of());
}
}
diff --git
a/ext/scimv2/scim-rest-cxf/src/main/java/org/apache/syncope/ext/scimv2/cxf/service/SCIMGroupServiceImpl.java
b/ext/scimv2/scim-rest-cxf/src/main/java/org/apache/syncope/ext/scimv2/cxf/service/SCIMGroupServiceImpl.java
index cffda6accb..1266596644 100644
---
a/ext/scimv2/scim-rest-cxf/src/main/java/org/apache/syncope/ext/scimv2/cxf/service/SCIMGroupServiceImpl.java
+++
b/ext/scimv2/scim-rest-cxf/src/main/java/org/apache/syncope/ext/scimv2/cxf/service/SCIMGroupServiceImpl.java
@@ -38,11 +38,13 @@ import org.apache.syncope.common.lib.to.GroupTO;
import org.apache.syncope.common.lib.to.ProvisioningResult;
import org.apache.syncope.common.lib.to.UserTO;
import org.apache.syncope.common.lib.types.PatchOperation;
+import org.apache.syncope.core.logic.AnyObjectLogic;
import org.apache.syncope.core.logic.GroupLogic;
import org.apache.syncope.core.logic.SCIMDataBinder;
import org.apache.syncope.core.logic.UserLogic;
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.AnyObjectDAO;
import org.apache.syncope.core.persistence.api.dao.DAO;
import org.apache.syncope.core.persistence.api.dao.GroupDAO;
import org.apache.syncope.core.persistence.api.dao.UserDAO;
@@ -69,12 +71,14 @@ public class SCIMGroupServiceImpl extends
AbstractSCIMService<SCIMGroup> impleme
public SCIMGroupServiceImpl(
final UserDAO userDAO,
final GroupDAO groupDAO,
+ final AnyObjectDAO anyObjectDAO,
final UserLogic userLogic,
final GroupLogic groupLogic,
+ final AnyObjectLogic anyObjectLogic,
final SCIMDataBinder binder,
final SCIMConfManager confManager) {
- super(userDAO, groupDAO, userLogic, groupLogic, binder, confManager);
+ super(userDAO, groupDAO, anyObjectDAO, userLogic, groupLogic,
anyObjectLogic, binder, confManager);
}
private void changeMembership(final String user, final String group, final
PatchOp patchOp) {
@@ -143,7 +147,7 @@ public class SCIMGroupServiceImpl extends
AbstractSCIMService<SCIMGroup> impleme
@Override
public Response update(final String id, final SCIMPatchOp patch) {
- ResponseBuilder builder = checkETag(Resource.Group, id);
+ ResponseBuilder builder = checkETag(Resource.Group.schema(), id);
if (builder != null) {
return builder.build();
}
@@ -178,7 +182,7 @@ public class SCIMGroupServiceImpl extends
AbstractSCIMService<SCIMGroup> impleme
throw new BadRequestException(ErrorType.invalidPath, "Expected " +
id + ", found " + group.getId());
}
- ResponseBuilder builder = checkETag(Resource.Group, id);
+ ResponseBuilder builder = checkETag(Resource.Group.schema(), id);
if (builder != null) {
return builder.build();
}
@@ -226,12 +230,12 @@ public class SCIMGroupServiceImpl extends
AbstractSCIMService<SCIMGroup> impleme
@Override
public Response delete(final String id) {
- ResponseBuilder builder = checkETag(Resource.Group, id);
+ ResponseBuilder builder = checkETag(Resource.Group.schema(), id);
if (builder != null) {
return builder.build();
}
- anyLogic(Resource.Group).delete(id, false);
+ anyLogic(Resource.Group.name()).delete(id, false);
return Response.noContent().build();
}
@@ -255,11 +259,11 @@ public class SCIMGroupServiceImpl extends
AbstractSCIMService<SCIMGroup> impleme
List.of(ArrayUtils.nullToEmpty(StringUtils.split(excludedAttributes, ','))));
}
- return doSearch(Resource.Group, request);
+ return doSearch(Resource.Group.name(), request);
}
@Override
public ListResponse<SCIMGroup> search(final SCIMSearchRequest request) {
- return doSearch(Resource.Group, request);
+ return doSearch(Resource.Group.name(), request);
}
}
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 f6f11c4f5f..02df36248e 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
@@ -20,11 +20,13 @@ package org.apache.syncope.ext.scimv2.cxf.service;
import jakarta.ws.rs.core.Response;
import java.util.List;
+import org.apache.syncope.core.logic.AnyObjectLogic;
import org.apache.syncope.core.logic.GroupLogic;
import org.apache.syncope.core.logic.SCIMDataBinder;
import org.apache.syncope.core.logic.SCIMLogic;
import org.apache.syncope.core.logic.UserLogic;
import org.apache.syncope.core.logic.scim.SCIMConfManager;
+import org.apache.syncope.core.persistence.api.dao.AnyObjectDAO;
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.data.ResourceType;
@@ -39,13 +41,15 @@ public class SCIMServiceImpl extends
AbstractSCIMService<SCIMResource> implement
public SCIMServiceImpl(
final UserDAO userDAO,
final GroupDAO groupDAO,
+ final AnyObjectDAO anyObjectDAO,
final UserLogic userLogic,
final GroupLogic groupLogic,
+ final AnyObjectLogic anyObjectLogic,
final SCIMDataBinder binder,
final SCIMConfManager confManager,
final SCIMLogic scimLogic) {
- super(userDAO, groupDAO, userLogic, groupLogic, binder, confManager);
+ super(userDAO, groupDAO, anyObjectDAO, userLogic, groupLogic,
anyObjectLogic, binder, confManager);
this.scimLogic = scimLogic;
}
diff --git
a/ext/scimv2/scim-rest-cxf/src/main/java/org/apache/syncope/ext/scimv2/cxf/service/SCIMUserServiceImpl.java
b/ext/scimv2/scim-rest-cxf/src/main/java/org/apache/syncope/ext/scimv2/cxf/service/SCIMUserServiceImpl.java
index dfa7f6fa62..5984aa695e 100644
---
a/ext/scimv2/scim-rest-cxf/src/main/java/org/apache/syncope/ext/scimv2/cxf/service/SCIMUserServiceImpl.java
+++
b/ext/scimv2/scim-rest-cxf/src/main/java/org/apache/syncope/ext/scimv2/cxf/service/SCIMUserServiceImpl.java
@@ -31,10 +31,12 @@ 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.AnyObjectLogic;
import org.apache.syncope.core.logic.GroupLogic;
import org.apache.syncope.core.logic.SCIMDataBinder;
import org.apache.syncope.core.logic.UserLogic;
import org.apache.syncope.core.logic.scim.SCIMConfManager;
+import org.apache.syncope.core.persistence.api.dao.AnyObjectDAO;
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;
@@ -53,12 +55,14 @@ public class SCIMUserServiceImpl extends
AbstractSCIMService<SCIMUser> implement
public SCIMUserServiceImpl(
final UserDAO userDAO,
final GroupDAO groupDAO,
+ final AnyObjectDAO anyObjectDAO,
final UserLogic userLogic,
final GroupLogic groupLogic,
+ final AnyObjectLogic anyObjectLogic,
final SCIMDataBinder binder,
final SCIMConfManager confManager) {
- super(userDAO, groupDAO, userLogic, groupLogic, binder, confManager);
+ super(userDAO, groupDAO, anyObjectDAO, userLogic, groupLogic,
anyObjectLogic, binder, confManager);
}
@Override
@@ -87,7 +91,7 @@ public class SCIMUserServiceImpl extends
AbstractSCIMService<SCIMUser> implement
@Override
public Response update(final String id, final SCIMPatchOp patch) {
- ResponseBuilder builder = checkETag(Resource.User, id);
+ ResponseBuilder builder = checkETag(Resource.User.schema(), id);
if (builder != null) {
return builder.build();
}
@@ -113,7 +117,7 @@ public class SCIMUserServiceImpl extends
AbstractSCIMService<SCIMUser> implement
throw new BadRequestException(ErrorType.invalidPath, "Expected " +
id + ", found " + user.getId());
}
- ResponseBuilder builder = checkETag(Resource.User, id);
+ ResponseBuilder builder = checkETag(Resource.User.schema(), id);
if (builder != null) {
return builder.build();
}
@@ -157,12 +161,12 @@ public class SCIMUserServiceImpl extends
AbstractSCIMService<SCIMUser> implement
@Override
public Response delete(final String id) {
- ResponseBuilder builder = checkETag(Resource.User, id);
+ ResponseBuilder builder = checkETag(Resource.User.schema(), id);
if (builder != null) {
return builder.build();
}
- anyLogic(Resource.User).delete(id, false);
+ anyLogic(Resource.User.name()).delete(id, false);
return Response.noContent().build();
}
@@ -186,11 +190,11 @@ public class SCIMUserServiceImpl extends
AbstractSCIMService<SCIMUser> implement
List.of(ArrayUtils.nullToEmpty(StringUtils.split(excludedAttributes, ','))));
}
- return doSearch(Resource.User, request);
+ return doSearch(Resource.User.name(), request);
}
@Override
public ListResponse<SCIMUser> search(final SCIMSearchRequest request) {
- return doSearch(Resource.User, request);
+ return doSearch(Resource.User.name(), request);
}
}
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 6d8625db70..7b6bdee3fa 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
@@ -46,18 +46,21 @@ import java.util.UUID;
import org.apache.commons.lang3.StringUtils;
import org.apache.cxf.jaxrs.client.WebClient;
import org.apache.syncope.common.lib.SyncopeConstants;
+import org.apache.syncope.common.lib.request.AnyObjectUR;
import org.apache.syncope.common.lib.request.GroupUR;
import org.apache.syncope.common.lib.request.StringPatchItem;
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.SCIMExtensionUserConf;
+import org.apache.syncope.common.lib.scim.SCIMExtensionAnyConf;
+import org.apache.syncope.common.lib.scim.SCIMExtensionAnyObjectConf;
import org.apache.syncope.common.lib.scim.SCIMGroupConf;
import org.apache.syncope.common.lib.scim.SCIMItem;
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;
+import org.apache.syncope.common.lib.to.AnyObjectTO;
import org.apache.syncope.common.lib.to.GroupTO;
import org.apache.syncope.common.lib.to.ProvisioningResult;
import org.apache.syncope.common.lib.to.UserTO;
@@ -70,6 +73,7 @@ import org.apache.syncope.ext.scimv2.api.data.Group;
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.ResourceType;
+import org.apache.syncope.ext.scimv2.api.data.SCIMAnyObject;
import org.apache.syncope.ext.scimv2.api.data.SCIMComplexValue;
import org.apache.syncope.ext.scimv2.api.data.SCIMError;
import org.apache.syncope.ext.scimv2.api.data.SCIMExtensionInfo;
@@ -118,6 +122,20 @@ public class SCIMITCase extends AbstractITCase {
email.setValue("email");
email.setType(EmailCanonicalType.home);
CONF.getUserConf().getEmails().add(email);
+
+ SCIMExtensionAnyObjectConf printerConf = new
SCIMExtensionAnyObjectConf();
+ printerConf.setType(PRINTER);
+ printerConf.setName("syncope");
+ printerConf.setDescription("syncope printer");
+ SCIMItem model = new SCIMItem();
+ model.setIntAttrName("model");
+ model.setExtAttrName("model");
+ SCIMItem location = new SCIMItem();
+ location.setIntAttrName("location");
+ location.setExtAttrName("location");
+ printerConf.add(model);
+ printerConf.add(location);
+ CONF.getExtensionAnyObjectsConf().add(printerConf);
}
private static SCIMUser getSampleUser(final String username, final
List<String> schemas) {
@@ -143,6 +161,13 @@ public class SCIMITCase extends AbstractITCase {
return user;
}
+ private static SCIMGroup getSampleGroup(final String name, final
List<String> schemas) {
+ SCIMGroup group = new SCIMGroup(null, schemas, null, name);
+ group.setDisplayName(name);
+
+ return group;
+ }
+
@BeforeAll
public static void isSCIMAvailable() {
if (ENABLED == null) {
@@ -209,7 +234,7 @@ public class SCIMITCase extends AbstractITCase {
@Test
public void schemas() {
- SCIMExtensionUserConf extensionUserConf = new SCIMExtensionUserConf();
+ SCIMExtensionAnyConf extensionUserConf = new SCIMExtensionAnyConf();
extensionUserConf.setName("syncope");
extensionUserConf.setDescription("syncope user");
SCIMItem scimItem = new SCIMItem();
@@ -217,6 +242,16 @@ public class SCIMITCase extends AbstractITCase {
scimItem.setExtAttrName("gender");
extensionUserConf.add(scimItem);
CONF.setExtensionUserConf(extensionUserConf);
+
+ SCIMExtensionAnyConf extensionGroupConf = new SCIMExtensionAnyConf();
+ extensionGroupConf.setName("syncope");
+ extensionGroupConf.setDescription("syncope group");
+ SCIMItem scimItemGroup = new SCIMItem();
+ scimItemGroup.setIntAttrName("originalName");
+ scimItemGroup.setExtAttrName("originalName");
+ scimItemGroup.setUniqueness(true);
+ extensionGroupConf.add(scimItemGroup);
+ CONF.setExtensionGroupConf(extensionGroupConf);
SCIM_CONF_SERVICE.set(CONF);
Response response = webClient().path("Schemas").get();
@@ -227,7 +262,7 @@ public class SCIMITCase extends AbstractITCase {
ArrayNode schemas = response.readEntity(ArrayNode.class);
assertNotNull(schemas);
- assertEquals(4, schemas.size());
+ assertEquals(6, schemas.size());
response = webClient().path("Schemas").path("none").get();
assertEquals(Response.Status.NOT_FOUND.getStatusCode(),
response.getStatus());
@@ -246,7 +281,23 @@ public class SCIMITCase extends AbstractITCase {
assertNotNull(extensionUser);
assertEquals(Resource.ExtensionUser.schema(),
extensionUser.get("id").textValue());
+ response =
webClient().path("Schemas").path(Resource.ExtensionGroup.schema()).get();
+ assertEquals(Response.Status.OK.getStatusCode(), response.getStatus());
+
+ ObjectNode extensionGroup = response.readEntity(ObjectNode.class);
+ assertNotNull(extensionGroup);
+ assertEquals(Resource.ExtensionGroup.schema(),
extensionGroup.get("id").textValue());
+
+ response =
webClient().path("Schemas").path("urn:ietf:params:scim:schemas:extension:syncope:2.0:PRINTER").get();
+ assertEquals(Response.Status.OK.getStatusCode(), response.getStatus());
+
+ ObjectNode extensionPrinter = response.readEntity(ObjectNode.class);
+ assertNotNull(extensionPrinter);
+
assertEquals("urn:ietf:params:scim:schemas:extension:syncope:2.0:PRINTER",
+ extensionPrinter.get("id").textValue());
+
CONF.setExtensionUserConf(null);
+ CONF.setExtensionGroupConf(null);
SCIM_CONF_SERVICE.set(CONF);
}
@@ -306,7 +357,7 @@ public class SCIMITCase extends AbstractITCase {
@Test
void invalidConf() {
- SCIMExtensionUserConf extensionUserConf = new SCIMExtensionUserConf();
+ SCIMExtensionAnyConf extensionUserConf = new SCIMExtensionAnyConf();
extensionUserConf.setName("syncope");
extensionUserConf.setDescription("syncope user");
SCIMItem scimItem = new SCIMItem();
@@ -456,13 +507,13 @@ public class SCIMITCase extends AbstractITCase {
assertEquals(newUser.getUsername(), newSCIMUser.getUserName());
SCIMEnterpriseUserConf beforeEntConf = CONF.getEnterpriseUserConf();
- SCIMExtensionUserConf beforeExtConf = CONF.getExtensionUserConf();
+ SCIMExtensionAnyConf beforeExtConf = CONF.getExtensionUserConf();
try {
SCIMEnterpriseUserConf entConf = new SCIMEnterpriseUserConf();
entConf.setOrganization("userId");
CONF.setEnterpriseUserConf(entConf);
- SCIMExtensionUserConf extConf = new SCIMExtensionUserConf();
+ SCIMExtensionAnyConf extConf = new SCIMExtensionAnyConf();
SCIMItem item = new SCIMItem();
item.setIntAttrName("email");
item.setExtAttrName("email");
@@ -501,6 +552,28 @@ public class SCIMITCase extends AbstractITCase {
});
assertNotNull(users);
assertEquals(1, users.getTotalResults());
+
+ // PRINTER
+ response = webClient().path("AnyObjects").query(
+ "filter",
+
"urn:ietf:params:scim:schemas:extension:syncope:2.0:PRINTER:model eq \"Canon
MFC8030\"").
+ get();
+
assertEquals(Response.Status.INTERNAL_SERVER_ERROR.getStatusCode(),
response.getStatus());
+
+ response = webClient().path("AnyObjects").query(
+ "filter",
+
"urn:ietf:params:scim:schemas:extension:syncope:2.0:PRINTER:model eq \"Canon
MFC8030\" "
+ + "and type eq \"PRINTER\"").
+ get();
+ assertEquals(Response.Status.OK.getStatusCode(),
response.getStatus());
+ assertEquals(
+ SCIMConstants.APPLICATION_SCIM_JSON,
+
StringUtils.substringBefore(response.getHeaderString(HttpHeaders.CONTENT_TYPE),
";"));
+
+ ListResponse<SCIMAnyObject> printers = response.readEntity(new
GenericType<>() {
+ });
+ assertNotNull(printers);
+ assertEquals(1, printers.getTotalResults());
} finally {
CONF.setEnterpriseUserConf(beforeEntConf);
CONF.setExtensionUserConf(beforeExtConf);
@@ -538,7 +611,7 @@ public class SCIMITCase extends AbstractITCase {
@Test
void crudExtensionUser() {
- SCIMExtensionUserConf extensionUserConf = new SCIMExtensionUserConf();
+ SCIMExtensionAnyConf extensionUserConf = new SCIMExtensionAnyConf();
extensionUserConf.setName("syncope");
extensionUserConf.setDescription("syncope user");
SCIMItem scimItem = new SCIMItem();
@@ -792,7 +865,7 @@ public class SCIMITCase extends AbstractITCase {
public void createGroup() {
String displayName = UUID.randomUUID().toString();
- SCIMGroup group = new SCIMGroup(null, null, displayName);
+ SCIMGroup group = new SCIMGroup(null,
List.of(Resource.Group.schema()), null, displayName);
group.getMembers().add(new
Member("1417acbe-cbf6-4277-9372-e75e04f97000", null, null));
assertNull(group.getId());
assertEquals(displayName, group.getDisplayName());
@@ -820,11 +893,51 @@ public class SCIMITCase extends AbstractITCase {
assertEquals(ErrorType.uniqueness, error.getScimType());
}
+ @Test
+ void crudExtensionGroup() {
+ SCIMExtensionAnyConf extensionGroupConf = new SCIMExtensionAnyConf();
+ extensionGroupConf.setName("syncope");
+ extensionGroupConf.setDescription("syncope group");
+ SCIMItem scimItemGroup = new SCIMItem();
+ scimItemGroup.setIntAttrName("originalName");
+ scimItemGroup.setExtAttrName("originalName");
+ scimItemGroup.setUniqueness(true);
+ extensionGroupConf.add(scimItemGroup);
+ CONF.setExtensionGroupConf(extensionGroupConf);
+ SCIM_CONF_SERVICE.set(CONF);
+
+ SCIMGroup group = getSampleGroup(
+ UUID.randomUUID().toString(), List.of(Resource.Group.schema(),
Resource.ExtensionGroup.schema()));
+ SCIMExtensionInfo scimExtensionInfo = new SCIMExtensionInfo();
+ scimExtensionInfo.getAttributes().put("originalName", "originalName");
+ group.setExtensionInfo(scimExtensionInfo);
+
+ Response response = webClient().path("Groups").post(group);
+ assertEquals(Response.Status.CREATED.getStatusCode(),
response.getStatus());
+
+ group = response.readEntity(SCIMGroup.class);
+ assertNotNull(group.getId());
+
assertTrue(response.getLocation().toASCIIString().endsWith(group.getId()));
+
+ GroupTO groupTO = GROUP_SERVICE.read(group.getId());
+ assertEquals(group.getDisplayName(), groupTO.getName());
+
assertEquals(group.getExtensionInfo().getAttributes().get("originalName"),
+
groupTO.getPlainAttr("originalName").get().getValues().getFirst());
+
+ response = webClient().path("Groups").path(group.getId()).delete();
+ assertEquals(Response.Status.NO_CONTENT.getStatusCode(),
response.getStatus());
+
+ response = webClient().path("Groups").path(group.getId()).get();
+ assertEquals(Response.Status.NOT_FOUND.getStatusCode(),
response.getStatus());
+ CONF.setExtensionGroupConf(null);
+ SCIM_CONF_SERVICE.set(CONF);
+ }
+
@Test
public void updateGroup() {
SCIM_CONF_SERVICE.set(CONF);
- SCIMGroup group = new SCIMGroup(null, null,
UUID.randomUUID().toString());
+ SCIMGroup group = new SCIMGroup(null,
List.of(Resource.Group.schema()), 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);
@@ -922,7 +1035,7 @@ public class SCIMITCase extends AbstractITCase {
@Test
public void replaceGroup() {
- SCIMGroup group = new SCIMGroup(null, null,
UUID.randomUUID().toString());
+ SCIMGroup group = new SCIMGroup(null,
List.of(Resource.Group.schema()), null, UUID.randomUUID().toString());
group.getMembers().add(new
Member("b3cbc78d-32e6-4bd4-92e0-bbe07566a2ee", null, null));
Response response = webClient().path("Groups").post(group);
assertEquals(Response.Status.CREATED.getStatusCode(),
response.getStatus());
@@ -968,7 +1081,7 @@ public class SCIMITCase extends AbstractITCase {
@Test
public void deleteGroup() {
- SCIMGroup group = new SCIMGroup(null, null,
UUID.randomUUID().toString());
+ SCIMGroup group = new SCIMGroup(null,
List.of(Resource.Group.schema()), null, UUID.randomUUID().toString());
Response response = webClient().path("Groups").post(group);
assertEquals(Response.Status.CREATED.getStatusCode(),
response.getStatus());
@@ -984,4 +1097,133 @@ public class SCIMITCase extends AbstractITCase {
response = webClient().path("Groups").path(group.getId()).get();
assertEquals(Response.Status.NOT_FOUND.getStatusCode(),
response.getStatus());
}
+
+ @Test
+ public void createPrinter() {
+ SCIM_CONF_SERVICE.set(CONF);
+
+ String displayName = UUID.randomUUID().toString();
+
+ SCIMAnyObject printer = new SCIMAnyObject(
+ null,
List.of("urn:ietf:params:scim:schemas:extension:syncope:2.0:PRINTER"), null,
displayName);
+ SCIMExtensionInfo scimExtensionInfo = new SCIMExtensionInfo();
+ scimExtensionInfo.getAttributes().put("model", "HP Inspire 7224e");
+ printer.setExtensionInfo(scimExtensionInfo);
+ assertNull(printer.getId());
+ assertEquals(displayName, printer.getDisplayName());
+
+ Response response = webClient().path("AnyObjects").post(printer);
+ assertEquals(Response.Status.CREATED.getStatusCode(),
response.getStatus());
+
+ printer = response.readEntity(SCIMAnyObject.class);
+ assertNotNull(printer.getId());
+
assertTrue(response.getLocation().toASCIIString().endsWith(printer.getId()));
+ assertEquals(displayName, printer.getDisplayName());
+
assertTrue(printer.getExtensionInfo().getAttributes().containsKey("model"));
+
assertTrue(printer.getExtensionInfo().getAttributes().containsValue("HP Inspire
7224e"));
+
+ response = webClient().path("AnyObjects").post(printer);
+ assertEquals(Response.Status.CONFLICT.getStatusCode(),
response.getStatus());
+
+ SCIMError error = response.readEntity(SCIMError.class);
+ assertEquals(Response.Status.CONFLICT.getStatusCode(),
error.getStatus());
+ assertEquals(ErrorType.uniqueness, error.getScimType());
+ }
+
+ @Test
+ public void updatePrinter() {
+ SCIM_CONF_SERVICE.set(CONF);
+
+ SCIMAnyObject printer = new SCIMAnyObject(
+ null,
+
List.of("urn:ietf:params:scim:schemas:extension:syncope:2.0:PRINTER"),
+ null,
+ UUID.randomUUID().toString());
+ Response response = webClient().path("AnyObjects").post(printer);
+ assertEquals(Response.Status.CREATED.getStatusCode(),
response.getStatus());
+
+ printer = response.readEntity(SCIMAnyObject.class);
+ assertNotNull(printer.getId());
+ assertNotNull(printer.getDisplayName());
+
+ // update with path, add value
+ String body =
+ "{"
+ +
"\"schemas\":[\"urn:ietf:params:scim:api:messages:2.0:PatchOp\"],"
+ + "\"Operations\":[{"
+ + "\"op\":\"Add\","
+ + "\"path\":\"displayName\","
+ + "\"value\":\"" + printer.getId() + "\""
+ + "}]"
+ + "}";
+ response =
webClient().path("AnyObjects").path(printer.getId()).invoke(HttpMethod.PATCH,
body);
+ assertEquals(Response.Status.OK.getStatusCode(), response.getStatus());
+
+ printer = response.readEntity(SCIMAnyObject.class);
+ assertEquals(printer.getId(), printer.getDisplayName());
+ }
+
+ @Test
+ public void replacePrinter() {
+ SCIM_CONF_SERVICE.set(CONF);
+
+ SCIMAnyObject printer = new SCIMAnyObject(
+ null,
+
List.of("urn:ietf:params:scim:schemas:extension:syncope:2.0:PRINTER"),
+ null,
+ UUID.randomUUID().toString());
+ SCIMExtensionInfo scimExtensionInfo = new SCIMExtensionInfo();
+ scimExtensionInfo.getAttributes().put("location", "1st floor");
+ printer.setExtensionInfo(scimExtensionInfo);
+ Response response = webClient().path("AnyObjects").post(printer);
+ assertEquals(Response.Status.CREATED.getStatusCode(),
response.getStatus());
+
+ printer = response.readEntity(SCIMAnyObject.class);
+ assertNotNull(printer.getId());
+
+ AnyObjectTO anyObjectTO = ANY_OBJECT_SERVICE.read(printer.getId());
+ assertNotNull(anyObjectTO);
+ ANY_OBJECT_SERVICE.update(new
AnyObjectUR.Builder(anyObjectTO.getKey()).resource(
+ new StringPatchItem.Builder().value(RESOURCE_NAME_DBSCRIPTED)
+ .operation(PatchOperation.ADD_REPLACE).build())
+ .build());
+ anyObjectTO = ANY_OBJECT_SERVICE.read(printer.getId());
+ assertNotNull(anyObjectTO);
+
assertTrue(anyObjectTO.getResources().contains(RESOURCE_NAME_DBSCRIPTED));
+
+ printer.setDisplayName("other" + printer.getId());
+
+ response =
webClient().path("AnyObjects").path(printer.getId()).put(printer);
+ assertEquals(Response.Status.OK.getStatusCode(), response.getStatus());
+
+ printer = response.readEntity(SCIMAnyObject.class);
+ assertTrue(printer.getDisplayName().startsWith("other"));
+
+ anyObjectTO = ANY_OBJECT_SERVICE.read(printer.getId());
+ assertNotNull(anyObjectTO);
+
assertTrue(anyObjectTO.getResources().contains(RESOURCE_NAME_DBSCRIPTED));
+ }
+
+ @Test
+ public void deletePrinter() {
+ SCIMAnyObject printer = new SCIMAnyObject(
+ null,
+
List.of("urn:ietf:params:scim:schemas:extension:syncope:2.0:PRINTER"),
+ null,
+ UUID.randomUUID().toString());
+ Response response = webClient().path("AnyObjects").post(printer);
+ assertEquals(Response.Status.CREATED.getStatusCode(),
response.getStatus());
+
+ printer = response.readEntity(SCIMAnyObject.class);
+ assertNotNull(printer.getId());
+
+ response = webClient().path("AnyObjects").path(printer.getId()).get();
+ assertEquals(Response.Status.OK.getStatusCode(), response.getStatus());
+
+ response =
webClient().path("AnyObjects").path(printer.getId()).delete();
+ assertEquals(Response.Status.NO_CONTENT.getStatusCode(),
response.getStatus());
+
+ response = webClient().path("AnyObjects").path(printer.getId()).get();
+ assertEquals(Response.Status.NOT_FOUND.getStatusCode(),
response.getStatus());
+ }
}