This is an automated email from the ASF dual-hosted git repository.
ilgrosso pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/syncope.git
The following commit(s) were added to refs/heads/master by this push:
new 5a677734d0 [SYNCOPE-1686] Added the End attribute to RelationshipTO to
indicate the side of the current Any in the relationship. (#924)
5a677734d0 is described below
commit 5a677734d05bd96ae929ab7b1673b0ffdda12f11
Author: Matteo Tatoni <[email protected]>
AuthorDate: Mon Dec 2 13:42:47 2024 +0100
[SYNCOPE-1686] Added the End attribute to RelationshipTO to indicate the
side of the current Any in the relationship. (#924)
---
.../console/panels/RelationshipViewPanel.java | 210 +++++++++++++++++++++
.../client/console/wizards/any/Relationships.java | 90 ++-------
.../console/panels/RelationshipViewPanel.html | 58 ++++++
.../panels/RelationshipViewPanel.properties | 20 ++
.../panels/RelationshipViewPanel_it.properties | 20 ++
.../syncope/common/lib/to/RelationshipTO.java | 24 +++
.../java/data/AbstractAnyDataBinder.java | 19 +-
.../java/data/AnyObjectDataBinderImpl.java | 26 ++-
.../provisioning/java/data/UserDataBinderImpl.java | 26 ++-
.../apache/syncope/fit/core/AnyObjectITCase.java | 76 ++++++++
10 files changed, 467 insertions(+), 102 deletions(-)
diff --git
a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/panels/RelationshipViewPanel.java
b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/panels/RelationshipViewPanel.java
new file mode 100644
index 0000000000..cbda4895d9
--- /dev/null
+++
b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/panels/RelationshipViewPanel.java
@@ -0,0 +1,210 @@
+/*
+ * 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.ArrayList;
+import java.util.List;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.syncope.client.console.wicket.markup.html.form.ActionLink;
+import
org.apache.syncope.client.console.wicket.markup.html.form.ActionLinksTogglePanel;
+import org.apache.syncope.client.console.wicket.markup.html.form.ActionsPanel;
+import org.apache.syncope.client.console.wizards.WizardMgtPanel;
+import org.apache.syncope.client.ui.commons.Constants;
+import org.apache.syncope.common.lib.to.AnyObjectTO;
+import org.apache.syncope.common.lib.to.AnyTO;
+import org.apache.syncope.common.lib.to.RelationshipTO;
+import org.apache.syncope.common.lib.to.UserTO;
+import org.apache.syncope.common.lib.types.AnyEntitlement;
+import org.apache.wicket.AttributeModifier;
+import org.apache.wicket.PageReference;
+import org.apache.wicket.ajax.AjaxEventBehavior;
+import org.apache.wicket.ajax.AjaxRequestTarget;
+import org.apache.wicket.markup.html.WebMarkupContainer;
+import org.apache.wicket.markup.html.basic.Label;
+import org.apache.wicket.markup.html.list.ListItem;
+import org.apache.wicket.markup.html.list.ListView;
+import org.apache.wicket.model.Model;
+import org.apache.wicket.model.ResourceModel;
+
+public final class RelationshipViewPanel extends
WizardMgtPanel<RelationshipTO> {
+
+ private static final long serialVersionUID = -7510529471158257903L;
+
+ private ActionLinksTogglePanel<RelationshipTO> togglePanel;
+
+ private final ListView<RelationshipTO> relationshipsList;
+
+ private RelationshipViewPanel(
+ final String id,
+ final List<RelationshipTO> relationships,
+ final AnyTO anyTO,
+ final boolean reuseItem,
+ final boolean wizardInModal) {
+ super(id, wizardInModal);
+ addInnerObject(getHeader());
+ relationshipsList = new ListView<>("relationships", relationships) {
+
+ private static final long serialVersionUID = 4983556433071042668L;
+
+ @Override
+ protected void populateItem(final ListItem<RelationshipTO>
relationshipItem) {
+ RelationshipTO relationshipTO =
relationshipItem.getModelObject();
+ buildRowLabels(relationshipItem, relationshipTO, anyTO);
+
+ ActionsPanel<RelationshipTO> action = new
ActionsPanel<>("action", new Model<>(relationshipTO));
+ action.add(new ActionLink<>() {
+
+ private static final long serialVersionUID =
5207800927605869051L;
+
+ @Override
+ public void onClick(final AjaxRequestTarget target, final
RelationshipTO modelObject) {
+ relationships.remove(modelObject);
+ target.add(RelationshipViewPanel.this);
+ }
+ }, ActionLink.ActionType.DELETE,
AnyEntitlement.UPDATE.getFor(anyTO.getType()), true).hideLabel();
+
+ if (togglePanel != null) {
+ relationshipItem.add(new AttributeModifier("style",
"cursor: pointer;"));
+ relationshipItem.add(new
AjaxEventBehavior(Constants.ON_CLICK) {
+
+ private static final long serialVersionUID =
-9027652037484739586L;
+
+ @Override
+ protected String findIndicatorId() {
+ return StringUtils.EMPTY;
+ }
+
+ @Override
+ protected void onEvent(final AjaxRequestTarget target)
{
+ togglePanel.toggleWithContent(target, action,
relationshipTO);
+ }
+ });
+ }
+
+ if (togglePanel == null) {
+ relationshipItem.add(action);
+ } else {
+ relationshipItem.add(new ActionsPanel<>("action", new
Model<>(relationshipTO))
+ .setVisible(false)
+ .setEnabled(false));
+ }
+ }
+ };
+ relationshipsList.setOutputMarkupId(true);
+ relationshipsList.setReuseItems(reuseItem);
+ relationshipsList.setRenderBodyOnly(true);
+
+ addInnerObject(relationshipsList);
+
+ }
+
+ private WebMarkupContainer getHeader() {
+ WebMarkupContainer headerContainer = new WebMarkupContainer("header");
+ headerContainer.add(new Label("header_left_end",
getString("left.end")));
+ headerContainer.add(new Label("header_relationship", new
ResourceModel("relationship")));
+ headerContainer.add(new Label("header_right_end", new
ResourceModel("right.end")));
+ return headerContainer;
+ }
+
+ private void buildRowLabels(
+ final ListItem<RelationshipTO> row,
+ final RelationshipTO relationshipTO,
+ final AnyTO anyTO) {
+ boolean isLeftRelation = relationshipTO.getEnd() ==
RelationshipTO.End.LEFT;
+ String anyName = anyTO instanceof UserTO
+ ? UserTO.class.cast(anyTO).getUsername()
+ : AnyObjectTO.class.cast(anyTO).getName();
+
+ row.add(new Label("relationship", relationshipTO.getType()));
+ Label leftEnd = new Label("left_end", isLeftRelation
+ ? String.format("%s %s", anyTO.getType() , anyName)
+ : String.format("%s %s", relationshipTO.getOtherEndType(),
relationshipTO.getOtherEndName()));
+
+ Label rightEnd = new Label("right_end", isLeftRelation
+ ? String.format("%s %s", relationshipTO.getOtherEndType(),
relationshipTO.getOtherEndName())
+ : String.format("%s %s", anyTO.getType() , anyName));
+
+ if (anyTO.getKey() != null &&
anyTO.getKey().equals(relationshipTO.getOtherEndKey())) {
+ setBold(leftEnd, rightEnd);
+ } else {
+ setBold(isLeftRelation ? leftEnd : rightEnd);
+ }
+ row.add(leftEnd, rightEnd);
+ }
+
+ private void setBold(final Label... labels) {
+ for (Label label : labels) {
+ label.add(new AttributeModifier("style", "font-weight: bold;"));
+ }
+ }
+
+ public static class Builder extends WizardMgtPanel.Builder<RelationshipTO>
{
+
+ private static final long serialVersionUID = -3643771352897992172L;
+
+ private List<RelationshipTO> relationships;
+
+ private AnyTO anyTO;
+
+ private boolean reuseItem = true;
+
+ public Builder(final PageReference pageRef) {
+ super(pageRef);
+ this.relationships = null;
+ this.anyTO = null;
+ }
+
+ public RelationshipViewPanel.Builder setAnyTO(final AnyTO anyTO) {
+ this.anyTO = anyTO;
+ return this;
+ }
+
+
+ public RelationshipViewPanel.Builder setRelationships(final
List<RelationshipTO> relationships) {
+ this.relationships = relationships;
+ return this;
+ }
+
+ public RelationshipViewPanel.Builder addItem(final RelationshipTO
item) {
+ if (item == null) {
+ return this;
+ }
+
+ if (this.relationships == null) {
+ this.relationships = new ArrayList<>();
+ }
+
+ this.relationships.add(item);
+ return this;
+ }
+
+ public RelationshipViewPanel.Builder setReuseItem(final boolean
reuseItem) {
+ this.reuseItem = reuseItem;
+ return this;
+ }
+
+ @Override
+ protected WizardMgtPanel<RelationshipTO> newInstance(final String id,
final boolean wizardInModal) {
+ return new RelationshipViewPanel(id, relationships, anyTO,
reuseItem, wizardInModal);
+ }
+
+ }
+
+}
+
diff --git
a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/wizards/any/Relationships.java
b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/wizards/any/Relationships.java
index b23d29a3cb..385713dd8f 100644
---
a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/wizards/any/Relationships.java
+++
b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/wizards/any/Relationships.java
@@ -19,16 +19,13 @@
package org.apache.syncope.client.console.wizards.any;
import java.util.ArrayList;
-import java.util.HashMap;
import java.util.List;
-import java.util.Map;
import java.util.Optional;
import java.util.stream.Collectors;
import org.apache.commons.collections4.ListUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.syncope.client.console.panels.AnyDirectoryPanel;
-import org.apache.syncope.client.console.panels.ListViewPanel;
-import org.apache.syncope.client.console.panels.ListViewPanel.ListViewReload;
+import org.apache.syncope.client.console.panels.RelationshipViewPanel;
import org.apache.syncope.client.console.panels.search.AnyObjectSearchPanel;
import
org.apache.syncope.client.console.panels.search.AnyObjectSelectionDirectoryPanel;
import
org.apache.syncope.client.console.panels.search.AnySelectionDirectoryPanel;
@@ -47,7 +44,6 @@ import org.apache.syncope.client.ui.commons.Constants;
import
org.apache.syncope.client.ui.commons.ajax.form.IndicatorAjaxFormComponentUpdatingBehavior;
import org.apache.syncope.client.ui.commons.ajax.markup.html.LabelInfo;
import
org.apache.syncope.client.ui.commons.markup.html.form.AjaxDropDownChoicePanel;
-import
org.apache.syncope.client.ui.commons.wicket.markup.html.bootstrap.tabs.Accordion;
import org.apache.syncope.client.ui.commons.wizards.any.AnyWrapper;
import org.apache.syncope.client.ui.commons.wizards.any.UserWrapper;
import org.apache.syncope.common.lib.to.AnyObjectTO;
@@ -61,20 +57,17 @@ import org.apache.syncope.common.lib.types.AnyTypeKind;
import org.apache.wicket.Component;
import org.apache.wicket.PageReference;
import org.apache.wicket.ajax.AjaxRequestTarget;
-import org.apache.wicket.event.Broadcast;
import org.apache.wicket.event.IEvent;
-import org.apache.wicket.extensions.markup.html.tabs.AbstractTab;
import org.apache.wicket.extensions.wizard.IWizard;
import org.apache.wicket.extensions.wizard.WizardModel.ICondition;
import org.apache.wicket.extensions.wizard.WizardStep;
-import org.apache.wicket.markup.head.IHeaderResponse;
-import org.apache.wicket.markup.head.OnDomReadyHeaderItem;
import org.apache.wicket.markup.html.WebMarkupContainer;
import org.apache.wicket.markup.html.basic.Label;
import org.apache.wicket.markup.html.form.IChoiceRenderer;
import org.apache.wicket.markup.html.panel.Fragment;
import org.apache.wicket.markup.html.panel.Panel;
import org.apache.wicket.model.IModel;
+import org.apache.wicket.model.Model;
import org.apache.wicket.model.PropertyModel;
import org.apache.wicket.model.ResourceModel;
import org.apache.wicket.model.util.ListModel;
@@ -133,47 +126,16 @@ public class Relationships extends WizardStep implements
ICondition {
}
protected Fragment getViewFragment() {
- Map<String, List<RelationshipTO>> relationships = new HashMap<>();
- addRelationship(relationships,
getCurrentRelationships().toArray(RelationshipTO[]::new));
-
Fragment viewFragment = new Fragment("relationships", "viewFragment",
this);
viewFragment.setOutputMarkupId(true);
- viewFragment.add(new Accordion("relationships",
relationships.keySet().stream().
- map(relationship -> new AbstractTab(new
ResourceModel("relationship", relationship)) {
-
- private static final long serialVersionUID = 1037272333056449378L;
-
- @Override
- public Panel getPanel(final String panelId) {
- return new ListViewPanel.Builder<>(RelationshipTO.class,
pageRef).
- setItems(relationships.get(relationship)).
- includes("otherEndType", "otherEndKey",
"otherEndName").
- addAction(new ActionLink<>() {
-
- private static final long serialVersionUID =
-6847033126124401556L;
-
- @Override
- public void onClick(final AjaxRequestTarget
target, final RelationshipTO modelObject) {
- removeRelationships(relationships,
modelObject);
- send(Relationships.this, Broadcast.DEPTH, new
ListViewReload<>(target));
- }
- }, ActionType.DELETE,
AnyEntitlement.UPDATE.getFor(anyTO.getType()), true).
- build(panelId);
- }
- }).collect(Collectors.toList())) {
-
- private static final long serialVersionUID = 1037272333056449379L;
-
- @Override
- public void renderHead(final IHeaderResponse response) {
- super.renderHead(response);
- if (relationships.isEmpty()) {
-
response.render(OnDomReadyHeaderItem.forScript(String.format(
- "$('#emptyPlaceholder').append(\"%s\")",
getString("relationships.empty.list"))));
- }
- }
- });
+ List<RelationshipTO> relationships = getCurrentRelationships();
+ viewFragment.add(relationships.isEmpty()
+ ? new Label("relationships", new
Model<>(getString("relationships.empty.list")))
+ : new RelationshipViewPanel.Builder(pageRef).
+ setAnyTO(anyTO).
+ setRelationships(relationships).
+ build("relationships"));
ActionsPanel<RelationshipTO> panel = new ActionsPanel<>("actions",
null);
viewFragment.add(panel);
@@ -200,42 +162,10 @@ public class Relationships extends WizardStep implements
ICondition {
: List.of();
}
- protected void addRelationship(
- final Map<String, List<RelationshipTO>> relationships,
- final RelationshipTO... rels) {
-
- for (RelationshipTO relationship : rels) {
- List<RelationshipTO> listrels;
- if (relationships.containsKey(relationship.getType())) {
- listrels = relationships.get(relationship.getType());
- } else {
- listrels = new ArrayList<>();
- relationships.put(relationship.getType(), listrels);
- }
- listrels.add(relationship);
- }
- }
-
protected void addNewRelationships(final RelationshipTO... rels) {
getCurrentRelationships().addAll(List.of(rels));
}
- protected void removeRelationships(
- final Map<String, List<RelationshipTO>> relationships, final
RelationshipTO... rels) {
-
- List<RelationshipTO> currentRels = getCurrentRelationships();
- for (RelationshipTO relationship : rels) {
- currentRels.remove(relationship);
- if (relationships.containsKey(relationship.getType())) {
- List<RelationshipTO> rellist =
relationships.get(relationship.getType());
- rellist.remove(relationship);
- if (rellist.isEmpty()) {
- relationships.remove(relationship.getType());
- }
- }
- }
- }
-
@Override
public boolean evaluate() {
// [SYNCOPE-1171] - skip current step when the are no relationships
types in Syncope
@@ -265,6 +195,7 @@ public class Relationships extends WizardStep implements
ICondition {
public Specification() {
super("specification");
rel = new RelationshipTO();
+ rel.setEnd(RelationshipTO.End.LEFT);
List<String> availableRels =
relationshipTypeRestClient.list().stream().
map(RelationshipTypeTO::getKey).collect(Collectors.toList());
@@ -396,6 +327,7 @@ public class Relationships extends WizardStep implements
ICondition {
AnyTO right =
AnySelectionDirectoryPanel.ItemSelection.class.cast(event.getPayload()).getSelection();
rel.setOtherEndKey(right.getKey());
+ rel.setOtherEndName(AnyObjectTO.class.cast(right).getName());
Relationships.this.addNewRelationships(rel);
diff --git
a/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/panels/RelationshipViewPanel.html
b/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/panels/RelationshipViewPanel.html
new file mode 100644
index 0000000000..7ca16e54db
--- /dev/null
+++
b/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/panels/RelationshipViewPanel.html
@@ -0,0 +1,58 @@
+<!--
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements. See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership. The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied. See the License for the
+specific language governing permissions and limitations
+under the License.
+-->
+
+<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:wicket="http://wicket.apache.org">
+ <head>
+ <title>Relationship view panel</title>
+ </head>
+ <body>
+ <wicket:extend>
+ <div class="col-xs-12">
+ <div class="card-body table-responsive no-padding">
+ <table class="table table-hover">
+ <tbody>
+ <tr wicket:id="header">
+ <th style="text-align: center"><span
wicket:id="header_left_end"/></th>
+ <th style="text-align: center"><span
wicket:id="header_relationship"/></th>
+ <th style="text-align: center"><span
wicket:id="header_right_end"/></th>
+ <th style="text-align: center" />
+ </tr>
+ <tr wicket:id="relationships">
+ <td class="col_width list_view_panel_labels"
style="text-align: center">
+ <span wicket:id="left_end"/>
+ </td>
+ <td class="col_width list_view_panel_labels"
style="text-align: center">
+ <span wicket:id="relationship"/>
+ </td>
+ <td class="col_width list_view_panel_labels"
style="text-align: center">
+ <span wicket:id="right_end"/>
+ </td>
+ <td>
+ <div class="listview-actions">
+ <span wicket:id="action">[actions]</span>
+ </div>
+ </td>
+ </tr>
+ </tbody>
+ </table>
+ </div>
+ </div>
+ </wicket:extend>
+ </body>
+</html>
diff --git
a/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/panels/RelationshipViewPanel.properties
b/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/panels/RelationshipViewPanel.properties
new file mode 100644
index 0000000000..b9928586d9
--- /dev/null
+++
b/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/panels/RelationshipViewPanel.properties
@@ -0,0 +1,20 @@
+# 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.
+
+left.end=Left End
+relationship=Relationship
+right.end=Right End
diff --git
a/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/panels/RelationshipViewPanel_it.properties
b/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/panels/RelationshipViewPanel_it.properties
new file mode 100644
index 0000000000..d2d1f89876
--- /dev/null
+++
b/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/panels/RelationshipViewPanel_it.properties
@@ -0,0 +1,20 @@
+# 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.
+
+left.end=Lato Sinistro
+relationship=Relazione
+right.end=Lato Destro
diff --git
a/common/idrepo/lib/src/main/java/org/apache/syncope/common/lib/to/RelationshipTO.java
b/common/idrepo/lib/src/main/java/org/apache/syncope/common/lib/to/RelationshipTO.java
index 8100da88d0..4222a24611 100644
---
a/common/idrepo/lib/src/main/java/org/apache/syncope/common/lib/to/RelationshipTO.java
+++
b/common/idrepo/lib/src/main/java/org/apache/syncope/common/lib/to/RelationshipTO.java
@@ -26,12 +26,24 @@ public class RelationshipTO implements BaseBean {
private static final long serialVersionUID = 360672942026613929L;
+ public enum End {
+ LEFT,
+ RIGHT;
+
+ }
+
public static class Builder {
private final RelationshipTO instance = new RelationshipTO();
public Builder(final String type) {
instance.setType(type);
+ instance.setEnd(End.LEFT);
+ }
+
+ public Builder(final String type, final End end) {
+ instance.setType(type);
+ instance.setEnd(end);
}
public Builder otherEnd(final String otherEndType, final String
otherEndKey) {
@@ -54,6 +66,8 @@ public class RelationshipTO implements BaseBean {
private String type;
+ private End end;
+
private String otherEndType;
private String otherEndKey;
@@ -92,6 +106,14 @@ public class RelationshipTO implements BaseBean {
this.otherEndName = otherEndName;
}
+ public End getEnd() {
+ return end;
+ }
+
+ public void setEnd(final End end) {
+ this.end = end;
+ }
+
@Override
public int hashCode() {
return new HashCodeBuilder().
@@ -99,6 +121,7 @@ public class RelationshipTO implements BaseBean {
append(otherEndType).
append(otherEndKey).
append(otherEndName).
+ append(end).
build();
}
@@ -119,6 +142,7 @@ public class RelationshipTO implements BaseBean {
append(otherEndType, other.otherEndType).
append(otherEndKey, other.otherEndKey).
append(otherEndName, other.otherEndName).
+ append(end, other.end).
build();
}
}
diff --git
a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/AbstractAnyDataBinder.java
b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/AbstractAnyDataBinder.java
index 46c66ecb2a..392fed66b4 100644
---
a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/AbstractAnyDataBinder.java
+++
b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/AbstractAnyDataBinder.java
@@ -75,6 +75,7 @@ import
org.apache.syncope.core.persistence.api.entity.PlainAttrValue;
import org.apache.syncope.core.persistence.api.entity.PlainSchema;
import org.apache.syncope.core.persistence.api.entity.VirSchema;
import org.apache.syncope.core.persistence.api.entity.anyobject.AnyObject;
+import org.apache.syncope.core.persistence.api.entity.user.User;
import org.apache.syncope.core.provisioning.api.AccountGetter;
import org.apache.syncope.core.provisioning.api.DerAttrHandler;
import org.apache.syncope.core.provisioning.api.IntAttrName;
@@ -125,9 +126,17 @@ abstract class AbstractAnyDataBinder {
anyTO.getResources().addAll(resources.stream().map(ExternalResource::getKey).collect(Collectors.toSet()));
}
- protected static RelationshipTO getRelationshipTO(final String
relationshipType, final AnyObject otherEnd) {
- return new RelationshipTO.Builder(relationshipType).
- otherEnd(otherEnd.getType().getKey(), otherEnd.getKey(),
otherEnd.getName()).
+ protected static RelationshipTO getRelationshipTO(
+ final String relationshipType,
+ final RelationshipTO.End end,
+ final Any<?> otherEnd) {
+
+ return new RelationshipTO.Builder(relationshipType, end).otherEnd(
+ otherEnd.getType().getKey(),
+ otherEnd.getKey(),
+ otherEnd instanceof User user
+ ? user.getUsername()
+ : ((AnyObject) otherEnd).getName()).
build();
}
@@ -518,7 +527,7 @@ abstract class AbstractAnyDataBinder {
}
},
() -> LOG.debug("Invalid {} {}, ignoring...",
- AnyTypeClass.class.getSimpleName(), patch.getValue()));
+ AnyTypeClass.class.getSimpleName(),
patch.getValue()));
}
// 2. resources
@@ -536,7 +545,7 @@ abstract class AbstractAnyDataBinder {
}
},
() -> LOG.debug("Invalid {} {}, ignoring...",
- ExternalResource.class.getSimpleName(),
patch.getValue()));
+ ExternalResource.class.getSimpleName(),
patch.getValue()));
}
Set<ExternalResource> resources = anyUtils.getAllResources(any);
diff --git
a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/AnyObjectDataBinderImpl.java
b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/AnyObjectDataBinderImpl.java
index 4749ac04a6..a6b719905a 100644
---
a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/AnyObjectDataBinderImpl.java
+++
b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/AnyObjectDataBinderImpl.java
@@ -36,6 +36,7 @@ import org.apache.syncope.common.lib.request.AttrPatch;
import org.apache.syncope.common.lib.to.AnyObjectTO;
import org.apache.syncope.common.lib.to.ConnObject;
import org.apache.syncope.common.lib.to.MembershipTO;
+import org.apache.syncope.common.lib.to.RelationshipTO;
import org.apache.syncope.common.lib.types.AnyTypeKind;
import org.apache.syncope.common.lib.types.ClientExceptionType;
import org.apache.syncope.common.lib.types.PatchOperation;
@@ -160,8 +161,11 @@ public class AnyObjectDataBinderImpl extends
AbstractAnyDataBinder implements An
map(relationship -> getRelationshipTO(
relationship.getType().getKey(),
relationship.getLeftEnd().getKey().equals(anyObject.getKey())
+ ? RelationshipTO.End.LEFT
+ : RelationshipTO.End.RIGHT,
+
relationship.getLeftEnd().getKey().equals(anyObject.getKey())
? relationship.getRightEnd()
- : anyObject)).
+ : relationship.getLeftEnd())).
toList());
// memberships
@@ -232,10 +236,13 @@ public class AnyObjectDataBinderImpl extends
AbstractAnyDataBinder implements An
AnyObject otherEnd =
anyObjectDAO.findById(relationshipTO.getOtherEndKey()).orElse(null);
if (otherEnd == null) {
LOG.debug("Ignoring invalid anyObject {}",
relationshipTO.getOtherEndKey());
+ } else if (relationshipTO.getEnd() ==
RelationshipTO.End.RIGHT) {
+ SyncopeClientException noRight =
+
SyncopeClientException.build(ClientExceptionType.InvalidRelationship);
+ noRight.getElements().add(
+ "Relationships shall be created or updated only
from their left end");
+ scce.addException(noRight);
} else if (relationships.contains(Pair.of(otherEnd.getKey(),
relationshipTO.getType()))) {
- LOG.error("{} was already in relationship {} with {}",
- otherEnd, relationshipTO.getType(), anyObject);
-
SyncopeClientException assigned =
SyncopeClientException.build(ClientExceptionType.InvalidRelationship);
assigned.getElements().add("AnyObject was already in
relationship "
@@ -357,12 +364,15 @@ public class AnyObjectDataBinderImpl extends
AbstractAnyDataBinder implements An
orElse(null);
if (otherEnd == null) {
LOG.debug("Ignoring invalid any object {}",
patch.getRelationshipTO().getOtherEndKey());
+ } else if (patch.getRelationshipTO().getEnd() ==
RelationshipTO.End.RIGHT) {
+ SyncopeClientException noRight =
+
SyncopeClientException.build(ClientExceptionType.InvalidRelationship);
+ noRight.getElements().add(
+ "Relationships shall be created or updated
only from their left end");
+ scce.addException(noRight);
} else if (relationships.contains(
Pair.of(otherEnd.getKey(),
patch.getRelationshipTO().getType()))) {
- LOG.error("{} was already in relationship {} with
{}",
- anyObject,
patch.getRelationshipTO().getType(), otherEnd);
-
SyncopeClientException assigned =
SyncopeClientException.build(ClientExceptionType.InvalidRelationship);
assigned.getElements().add("AnyObject was already
in relationship "
@@ -431,7 +441,7 @@ public class AnyObjectDataBinderImpl extends
AbstractAnyDataBinder implements An
PlainSchema schema =
getPlainSchema(attrTO.getSchema());
if (schema == null) {
LOG.debug("Invalid {}{}, ignoring...",
- PlainSchema.class.getSimpleName(),
attrTO.getSchema());
+ PlainSchema.class.getSimpleName(),
attrTO.getSchema());
} else {
Optional<? extends APlainAttr> attr =
anyObject.getPlainAttr(schema.getKey(),
newMembership);
diff --git
a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/UserDataBinderImpl.java
b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/UserDataBinderImpl.java
index cfd2e6984b..f42c994f1f 100644
---
a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/UserDataBinderImpl.java
+++
b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/UserDataBinderImpl.java
@@ -44,6 +44,7 @@ import org.apache.syncope.common.lib.to.ConnObject;
import org.apache.syncope.common.lib.to.Item;
import org.apache.syncope.common.lib.to.LinkedAccountTO;
import org.apache.syncope.common.lib.to.MembershipTO;
+import org.apache.syncope.common.lib.to.RelationshipTO;
import org.apache.syncope.common.lib.to.UserTO;
import org.apache.syncope.common.lib.types.AnyTypeKind;
import org.apache.syncope.common.lib.types.CipherAlgorithm;
@@ -347,9 +348,13 @@ public class UserDataBinderImpl extends
AbstractAnyDataBinder implements UserDat
AnyObject otherEnd =
anyObjectDAO.findById(relationshipTO.getOtherEndKey()).orElse(null);
if (otherEnd == null) {
LOG.debug("Ignoring invalid anyObject {}",
relationshipTO.getOtherEndKey());
+ } else if (relationshipTO.getEnd() == RelationshipTO.End.RIGHT) {
+ SyncopeClientException noRight =
+
SyncopeClientException.build(ClientExceptionType.InvalidRelationship);
+ noRight.getElements().add(
+ "Relationships shall be created or updated only from
their left end");
+ scce.addException(noRight);
} else if (relationships.contains(Pair.of(otherEnd.getKey(),
relationshipTO.getType()))) {
- LOG.error("{} was already in relationship {} with {}",
otherEnd, relationshipTO.getType(), user);
-
SyncopeClientException assigned =
SyncopeClientException.build(ClientExceptionType.InvalidRelationship);
assigned.getElements().add(otherEnd.getType().getKey() + " " +
otherEnd.getName()
@@ -369,8 +374,6 @@ public class UserDataBinderImpl extends
AbstractAnyDataBinder implements UserDat
},
() -> LOG.debug("Ignoring invalid relationship type
{}", relationshipTO.getType()));
} else {
- LOG.error("{} cannot be related to {}", otherEnd, user);
-
SyncopeClientException unrelatable =
SyncopeClientException.build(ClientExceptionType.InvalidRelationship);
unrelatable.getElements().add(otherEnd.getType().getKey() + "
" + otherEnd.getName()
@@ -550,15 +553,18 @@ public class UserDataBinderImpl extends
AbstractAnyDataBinder implements UserDat
} else if (relationships.contains(
Pair.of(otherEnd.getKey(),
patch.getRelationshipTO().getType()))) {
- LOG.error("{} was already in relationship {} with {}",
- user, patch.getRelationshipTO().getType(),
otherEnd);
-
SyncopeClientException assigned =
SyncopeClientException.build(ClientExceptionType.InvalidRelationship);
assigned.getElements().add("User was already in
relationship "
+ patch.getRelationshipTO().getType() + " with
"
+ otherEnd.getType().getKey() + " " +
otherEnd.getName());
scce.addException(assigned);
+ } else if (patch.getRelationshipTO().getEnd() ==
RelationshipTO.End.RIGHT) {
+ SyncopeClientException noRight =
+
SyncopeClientException.build(ClientExceptionType.InvalidRelationship);
+ noRight.getElements().add(
+ "Relationships shall be created or updated
only from their left end");
+ scce.addException(noRight);
} else if
(user.getRealm().getFullPath().startsWith(otherEnd.getRealm().getFullPath())) {
relationships.add(Pair.of(otherEnd.getKey(),
patch.getRelationshipTO().getType()));
@@ -628,7 +634,7 @@ public class UserDataBinderImpl extends
AbstractAnyDataBinder implements UserDat
PlainSchema schema =
getPlainSchema(attrTO.getSchema());
if (schema == null) {
LOG.debug("Invalid {}{}, ignoring...",
- PlainSchema.class.getSimpleName(),
attrTO.getSchema());
+ PlainSchema.class.getSimpleName(),
attrTO.getSchema());
} else {
UPlainAttr attr =
user.getPlainAttr(schema.getKey(), newMembership).orElse(null);
if (attr == null) {
@@ -820,8 +826,8 @@ public class UserDataBinderImpl extends
AbstractAnyDataBinder implements UserDat
flatMap(role ->
role.getPrivileges().stream()).map(Privilege::getKey).collect(Collectors.toSet()));
// relationships
- userTO.getRelationships().addAll(user.getRelationships().stream().
- map(relationship ->
getRelationshipTO(relationship.getType().getKey(), relationship.getRightEnd())).
+
userTO.getRelationships().addAll(user.getRelationships().stream().map(relationship
-> getRelationshipTO(
+ relationship.getType().getKey(), RelationshipTO.End.LEFT,
relationship.getRightEnd())).
toList());
// memberships
diff --git
a/fit/core-reference/src/test/java/org/apache/syncope/fit/core/AnyObjectITCase.java
b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/AnyObjectITCase.java
index d91bfdc52d..1f392cb763 100644
---
a/fit/core-reference/src/test/java/org/apache/syncope/fit/core/AnyObjectITCase.java
+++
b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/AnyObjectITCase.java
@@ -21,6 +21,7 @@ package org.apache.syncope.fit.core;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.junit.jupiter.api.Assertions.fail;
@@ -253,4 +254,79 @@ public class AnyObjectITCase extends AbstractITCase {
assertFalse(printer.getResources().contains(RESOURCE_NAME_DBSCRIPTED),
"Should not contain removed resources");
assertFalse(printer.getAuxClasses().contains("csv"), "Should not
contain removed auxiliary classes");
}
+
+ @Test
+ public void issueSYNCOPE1686() {
+ // Create printers
+ AnyObjectCR printer1CR = getSample("printer1");
+ printer1CR.getResources().clear();
+ String key1 = createAnyObject(printer1CR).getEntity().getKey();
+
+ AnyObjectCR printer2CR = getSample("printer2");
+ printer2CR.getResources().clear();
+ String key2 = createAnyObject(printer2CR).getEntity().getKey();
+
+ AnyObjectCR printer3CR = getSample("printer3");
+ printer3CR.getResources().clear();
+ String key3 = createAnyObject(printer3CR).getEntity().getKey();
+
+ // Add relationships: printer1 -> printer2 and printer2 -> printer3
+ AnyObjectUR relationship1To2 = new AnyObjectUR.Builder(key1)
+ .relationship(new RelationshipUR.Builder(
+ new
RelationshipTO.Builder("neighborhood").otherEnd(PRINTER, key2).build()).build())
+ .build();
+ AnyObjectUR relationship2To3 = new AnyObjectUR.Builder(key2)
+ .relationship(new RelationshipUR.Builder(
+ new
RelationshipTO.Builder("neighborhood").otherEnd(PRINTER, key3).build()).build())
+ .build();
+
+ updateAnyObject(relationship1To2);
+ updateAnyObject(relationship2To3);
+
+ // Read updated printers
+ AnyObjectTO printer1 = ANY_OBJECT_SERVICE.read(key1);
+ AnyObjectTO printer2 = ANY_OBJECT_SERVICE.read(key2);
+ AnyObjectTO printer3 = ANY_OBJECT_SERVICE.read(key3);
+
+ // Verify relationships for printer1
+ assertEquals(1, printer1.getRelationships().size());
+ RelationshipTO rel1 = printer1.getRelationships().get(0);
+ assertEquals(RelationshipTO.End.LEFT, rel1.getEnd());
+ assertEquals(printer2.getKey(), rel1.getOtherEndKey());
+ assertEquals(printer2.getType(), rel1.getOtherEndType());
+ assertEquals(printer2.getName(), rel1.getOtherEndName());
+
+ // Verify relationships for printer2
+ assertEquals(2, printer2.getRelationships().size());
+ assertTrue(printer2.getRelationships().stream()
+ .anyMatch(r -> r.getEnd() == RelationshipTO.End.LEFT
+ && printer3.getKey().equals(r.getOtherEndKey())
+ && printer3.getType().equals(r.getOtherEndType())
+ && printer3.getName().equals(r.getOtherEndName())));
+ assertTrue(printer2.getRelationships().stream()
+ .anyMatch(r -> r.getEnd() == RelationshipTO.End.RIGHT
+ && printer1.getKey().equals(r.getOtherEndKey())
+ && printer1.getType().equals(r.getOtherEndType())
+ && printer1.getName().equals(r.getOtherEndName())));
+
+ // Verify relationships for printer3
+ assertEquals(1, printer3.getRelationships().size());
+ RelationshipTO rel3 = printer3.getRelationships().get(0);
+ assertEquals(RelationshipTO.End.RIGHT, rel3.getEnd());
+ assertEquals(printer2.getKey(), rel3.getOtherEndKey());
+ assertEquals(printer2.getType(), rel3.getOtherEndType());
+ assertEquals(printer2.getName(), rel3.getOtherEndName());
+
+ // Test invalid relationship with End.RIGHT
+ AnyObjectCR printer4CR = getSample("printer4");
+ printer4CR.getResources().clear();
+ printer4CR.getRelationships().add(
+ new RelationshipTO.Builder("neighborhood",
RelationshipTO.End.RIGHT).otherEnd(PRINTER, key1).build());
+
+ SyncopeClientException exception =
+ assertThrows(SyncopeClientException.class, () ->
createAnyObject(printer4CR));
+ assertEquals(ClientExceptionType.InvalidRelationship,
exception.getType());
+ assertTrue(exception.getMessage().
+ contains("Relationships shall be created or updated only from
their left end"));
+ }
}