[SYNCOPE-1201] Conditions are now set seprated for each available AnyType
Project: http://git-wip-us.apache.org/repos/asf/syncope/repo Commit: http://git-wip-us.apache.org/repos/asf/syncope/commit/126a323f Tree: http://git-wip-us.apache.org/repos/asf/syncope/tree/126a323f Diff: http://git-wip-us.apache.org/repos/asf/syncope/diff/126a323f Branch: refs/heads/master Commit: 126a323f5c99bd2f62965492188cff84847fd526 Parents: 9526898 Author: Francesco Chicchiriccò <ilgro...@apache.org> Authored: Fri Aug 25 14:01:31 2017 +0200 Committer: Francesco Chicchiriccò <ilgro...@apache.org> Committed: Fri Aug 25 14:22:46 2017 +0200 ---------------------------------------------------------------------- .../console/panels/DynRealmModalPanel.java | 65 +++++++++++++--- .../client/console/wizards/DynRealmWrapper.java | 41 +++++++--- .../console/wizards/any/DynamicMemberships.java | 4 +- .../console/panels/DynRealmModalPanel.html | 4 +- .../syncope/common/lib/to/DynRealmTO.java | 20 +++-- .../core/persistence/api/entity/DynRealm.java | 11 ++- .../api/entity/DynRealmMembership.java | 31 ++++++++ .../persistence/jpa/dao/JPADynRealmDAO.java | 64 ++++++---------- .../persistence/jpa/entity/JPADynRealm.java | 30 ++++++-- .../jpa/entity/JPADynRealmMembership.java | 79 ++++++++++++++++++++ .../jpa/entity/JPAEntityFactory.java | 3 + .../persistence/jpa/entity/group/JPAGroup.java | 10 ++- .../persistence/jpa/outer/DynRealmTest.java | 14 +++- .../java/data/DynRealmDataBinderImpl.java | 64 +++++++++++++--- .../java/data/GroupDataBinderImpl.java | 7 +- .../apache/syncope/fit/core/DynRealmITCase.java | 6 +- 16 files changed, 350 insertions(+), 103 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/syncope/blob/126a323f/client/console/src/main/java/org/apache/syncope/client/console/panels/DynRealmModalPanel.java ---------------------------------------------------------------------- diff --git a/client/console/src/main/java/org/apache/syncope/client/console/panels/DynRealmModalPanel.java b/client/console/src/main/java/org/apache/syncope/client/console/panels/DynRealmModalPanel.java index 734bd62..f1a0b69 100644 --- a/client/console/src/main/java/org/apache/syncope/client/console/panels/DynRealmModalPanel.java +++ b/client/console/src/main/java/org/apache/syncope/client/console/panels/DynRealmModalPanel.java @@ -24,27 +24,35 @@ import org.apache.commons.lang3.StringUtils; import org.apache.syncope.client.console.SyncopeConsoleSession; import org.apache.syncope.client.console.commons.Constants; import org.apache.syncope.client.console.pages.BasePage; -import org.apache.syncope.client.console.panels.search.SearchClause; +import org.apache.syncope.client.console.panels.search.AnyObjectSearchPanel; +import org.apache.syncope.client.console.panels.search.GroupSearchPanel; +import org.apache.syncope.client.console.panels.search.MapOfListModel; import org.apache.syncope.client.console.panels.search.UserSearchPanel; +import org.apache.syncope.client.console.rest.AnyTypeRestClient; import org.apache.syncope.client.console.rest.DynRealmRestClient; import org.apache.syncope.client.console.wicket.markup.html.bootstrap.dialog.BaseModal; import org.apache.syncope.client.console.wicket.markup.html.bootstrap.tabs.Accordion; import org.apache.syncope.client.console.wicket.markup.html.form.AjaxTextFieldPanel; import org.apache.syncope.client.console.wizards.DynRealmWrapper; +import org.apache.syncope.common.lib.to.AnyTypeTO; import org.apache.wicket.PageReference; import org.apache.wicket.ajax.AjaxRequestTarget; import org.apache.wicket.extensions.markup.html.tabs.AbstractTab; import org.apache.wicket.extensions.markup.html.tabs.ITab; import org.apache.wicket.markup.html.form.Form; +import org.apache.wicket.markup.html.list.ListItem; +import org.apache.wicket.markup.html.list.ListView; import org.apache.wicket.markup.html.panel.Panel; +import org.apache.wicket.model.LoadableDetachableModel; import org.apache.wicket.model.Model; import org.apache.wicket.model.PropertyModel; -import org.apache.wicket.model.ResourceModel; public class DynRealmModalPanel extends AbstractModalPanel<DynRealmWrapper> { private static final long serialVersionUID = -3773196441177699452L; + private final AnyTypeRestClient anyTypeRestClient = new AnyTypeRestClient(); + private final DynRealmRestClient restClient = new DynRealmRestClient(); private final DynRealmWrapper dynRealmWrapper; @@ -63,23 +71,58 @@ public class DynRealmModalPanel extends AbstractModalPanel<DynRealmWrapper> { modal.setFormModel(dynRealmWrapper); AjaxTextFieldPanel key = new AjaxTextFieldPanel( - "key", "key", new PropertyModel<String>(dynRealmWrapper.getInnerObject(), "key"), false); + "key", "key", new PropertyModel<>(dynRealmWrapper.getInnerObject(), "key"), false); key.setReadOnly(!create); key.setRequired(true); add(key); - add(new Accordion("cond", Collections.<ITab>singletonList( - new AbstractTab(new ResourceModel("cond", "Dynamic Condition")) { + final LoadableDetachableModel<List<AnyTypeTO>> types = new LoadableDetachableModel<List<AnyTypeTO>>() { + + private static final long serialVersionUID = 5275935387613157437L; + + @Override + protected List<AnyTypeTO> load() { + return anyTypeRestClient.listAnyTypes(); + } + }; + + add(new ListView<AnyTypeTO>("dynMembershipCond", types) { - private static final long serialVersionUID = 1037272333056449378L; + private static final long serialVersionUID = 9101744072914090143L; @Override - public Panel getPanel(final String panelId) { - return new UserSearchPanel.Builder( - new PropertyModel<List<SearchClause>>(dynRealmWrapper, "dynClauses")). - required(false).build(panelId); + protected void populateItem(final ListItem<AnyTypeTO> item) { + final String key = item.getModelObject().getKey(); + item.add(new Accordion("dynMembershipCond", Collections.<ITab>singletonList( + new AbstractTab(Model.of(key + " Dynamic Condition")) { + + private static final long serialVersionUID = 1037272333056449378L; + + @Override + public Panel getPanel(final String panelId) { + switch (item.getModelObject().getKind()) { + case USER: + return new UserSearchPanel.Builder( + new MapOfListModel<>(dynRealmWrapper, "dynClauses", key)). + required(false).build(panelId); + + case GROUP: + return new GroupSearchPanel.Builder( + new MapOfListModel<>(dynRealmWrapper, "dynClauses", key)). + required(false).build(panelId); + + case ANY_OBJECT: + default: + return new AnyObjectSearchPanel.Builder( + key, + new MapOfListModel<>(dynRealmWrapper, "dynClauses", key)). + required(false).build(panelId); + } + } + }), Model.of(StringUtils.isBlank(dynRealmWrapper.getDynMembershipConds().get(key)) ? -1 : 0)). + setOutputMarkupId(true)); } - }), Model.of(StringUtils.isBlank(dynRealmWrapper.getCond()) ? -1 : 0)).setOutputMarkupId(true)); + }); } @Override http://git-wip-us.apache.org/repos/asf/syncope/blob/126a323f/client/console/src/main/java/org/apache/syncope/client/console/wizards/DynRealmWrapper.java ---------------------------------------------------------------------- diff --git a/client/console/src/main/java/org/apache/syncope/client/console/wizards/DynRealmWrapper.java b/client/console/src/main/java/org/apache/syncope/client/console/wizards/DynRealmWrapper.java index e9f5acc..de013a9 100644 --- a/client/console/src/main/java/org/apache/syncope/client/console/wizards/DynRealmWrapper.java +++ b/client/console/src/main/java/org/apache/syncope/client/console/wizards/DynRealmWrapper.java @@ -19,12 +19,16 @@ package org.apache.syncope.client.console.wizards; import java.io.Serializable; +import java.util.HashMap; import java.util.List; +import java.util.Map; import org.apache.commons.collections4.CollectionUtils; import org.apache.syncope.client.console.panels.search.SearchClause; import org.apache.syncope.client.console.panels.search.SearchUtils; import org.apache.syncope.client.lib.SyncopeClient; +import org.apache.syncope.common.lib.search.AbstractFiqlSearchConditionBuilder; import org.apache.syncope.common.lib.to.DynRealmTO; +import org.apache.syncope.common.lib.types.AnyTypeKind; public class DynRealmWrapper implements Serializable { @@ -32,34 +36,49 @@ public class DynRealmWrapper implements Serializable { private final DynRealmTO dynRealmTO; - private List<SearchClause> dynClauses; + private Map<String, List<SearchClause>> dynClauses; public DynRealmWrapper(final DynRealmTO dynRealmTO) { this.dynRealmTO = dynRealmTO; getDynClauses(); } - public final List<SearchClause> getDynClauses() { + public final Map<String, List<SearchClause>> getDynClauses() { if (this.dynClauses == null) { - this.dynClauses = SearchUtils.getSearchClauses(this.dynRealmTO.getCond()); + this.dynClauses = SearchUtils.getSearchClauses(this.dynRealmTO.getDynMembershipConds()); } return this.dynClauses; } - public void setDynClauses(final List<SearchClause> dynClauses) { - this.dynClauses = dynClauses; + public void setDynClauses(final Map<String, List<SearchClause>> dynClauses) { + this.dynClauses.clear(); + this.dynClauses.putAll(dynClauses); } - public String getCond() { - if (CollectionUtils.isEmpty(this.dynClauses)) { - return null; - } else { - return SearchUtils.buildFIQL(this.dynClauses, SyncopeClient.getUserSearchConditionBuilder()); + public Map<String, String> getDynMembershipConds() { + final Map<String, String> res = new HashMap<>(); + if (this.dynClauses != null && !this.dynClauses.isEmpty()) { + this.dynClauses.entrySet().stream(). + filter(entry -> (CollectionUtils.isNotEmpty(entry.getValue()))). + forEachOrdered(entry -> { + AbstractFiqlSearchConditionBuilder builder = AnyTypeKind.USER.name().equals(entry.getKey()) + ? SyncopeClient.getUserSearchConditionBuilder() + : AnyTypeKind.GROUP.name().equals(entry.getKey()) + ? SyncopeClient.getGroupSearchConditionBuilder() + : SyncopeClient.getAnyObjectSearchConditionBuilder(entry.getKey()); + String fiql = SearchUtils.buildFIQL(entry.getValue(), builder); + if (fiql != null) { + res.put(entry.getKey(), fiql); + } + }); } + + return res; } public DynRealmTO fillDynamicConditions() { - this.dynRealmTO.setCond(this.getCond()); + this.dynRealmTO.getDynMembershipConds().clear(); + this.dynRealmTO.getDynMembershipConds().putAll(this.getDynMembershipConds()); return this.dynRealmTO; } http://git-wip-us.apache.org/repos/asf/syncope/blob/126a323f/client/console/src/main/java/org/apache/syncope/client/console/wizards/any/DynamicMemberships.java ---------------------------------------------------------------------- diff --git a/client/console/src/main/java/org/apache/syncope/client/console/wizards/any/DynamicMemberships.java b/client/console/src/main/java/org/apache/syncope/client/console/wizards/any/DynamicMemberships.java index da95dac..bf59b5f 100644 --- a/client/console/src/main/java/org/apache/syncope/client/console/wizards/any/DynamicMemberships.java +++ b/client/console/src/main/java/org/apache/syncope/client/console/wizards/any/DynamicMemberships.java @@ -100,8 +100,8 @@ public class DynamicMemberships extends WizardStep { key, new MapOfListModel<>(groupWrapper, "aDynClauses", key)). required(false).build(panelId); } - }), Model.of(StringUtils.isBlank(groupWrapper.getADynMembershipConds().get(key)) ? -1 : 0)) - .setOutputMarkupId(true)); + }), Model.of(StringUtils.isBlank(groupWrapper.getADynMembershipConds().get(key)) ? -1 : 0)). + setOutputMarkupId(true)); } }); // ------------------------ http://git-wip-us.apache.org/repos/asf/syncope/blob/126a323f/client/console/src/main/resources/org/apache/syncope/client/console/panels/DynRealmModalPanel.html ---------------------------------------------------------------------- diff --git a/client/console/src/main/resources/org/apache/syncope/client/console/panels/DynRealmModalPanel.html b/client/console/src/main/resources/org/apache/syncope/client/console/panels/DynRealmModalPanel.html index 3200d9d..9f8fa4c 100644 --- a/client/console/src/main/resources/org/apache/syncope/client/console/panels/DynRealmModalPanel.html +++ b/client/console/src/main/resources/org/apache/syncope/client/console/panels/DynRealmModalPanel.html @@ -20,7 +20,9 @@ under the License. <wicket:extend> <div class="form-group"> <span wicket:id="key"/> - <div wicket:id="cond"/> + <span wicket:id="dynMembershipCond"> + <div wicket:id="dynMembershipCond"/> + </span> </div> </wicket:extend> </html> http://git-wip-us.apache.org/repos/asf/syncope/blob/126a323f/common/lib/src/main/java/org/apache/syncope/common/lib/to/DynRealmTO.java ---------------------------------------------------------------------- diff --git a/common/lib/src/main/java/org/apache/syncope/common/lib/to/DynRealmTO.java b/common/lib/src/main/java/org/apache/syncope/common/lib/to/DynRealmTO.java index c218ec6..d3fff01 100644 --- a/common/lib/src/main/java/org/apache/syncope/common/lib/to/DynRealmTO.java +++ b/common/lib/src/main/java/org/apache/syncope/common/lib/to/DynRealmTO.java @@ -18,10 +18,16 @@ */ package org.apache.syncope.common.lib.to; +import com.fasterxml.jackson.annotation.JsonIgnore; +import com.fasterxml.jackson.annotation.JsonProperty; +import java.util.HashMap; +import java.util.Map; import javax.ws.rs.PathParam; import javax.xml.bind.annotation.XmlRootElement; import javax.xml.bind.annotation.XmlType; +import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter; import org.apache.syncope.common.lib.AbstractBaseBean; +import org.apache.syncope.common.lib.jaxb.XmlGenericMapAdapter; @XmlRootElement(name = "dynRealm") @XmlType @@ -31,7 +37,9 @@ public class DynRealmTO extends AbstractBaseBean implements EntityTO { private String key; - private String cond; + @XmlJavaTypeAdapter(XmlGenericMapAdapter.class) + @JsonIgnore + private final Map<String, String> dynMembershipConds = new HashMap<>(); @Override public String getKey() { @@ -44,12 +52,8 @@ public class DynRealmTO extends AbstractBaseBean implements EntityTO { this.key = key; } - public String getCond() { - return cond; + @JsonProperty + public Map<String, String> getDynMembershipConds() { + return dynMembershipConds; } - - public void setCond(final String cond) { - this.cond = cond; - } - } http://git-wip-us.apache.org/repos/asf/syncope/blob/126a323f/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/DynRealm.java ---------------------------------------------------------------------- diff --git a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/DynRealm.java b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/DynRealm.java index e8be2aa..711d251 100644 --- a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/DynRealm.java +++ b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/DynRealm.java @@ -18,6 +18,15 @@ */ package org.apache.syncope.core.persistence.api.entity; -public interface DynRealm extends DynMembership<Any<?>>, ProvidedKeyEntity { +import java.util.List; +import java.util.Optional; + +public interface DynRealm extends ProvidedKeyEntity { + + boolean add(DynRealmMembership dynRealmMembership); + + Optional<? extends DynRealmMembership> getDynMembership(AnyType anyType); + + List<? extends DynRealmMembership> getDynMemberships(); } http://git-wip-us.apache.org/repos/asf/syncope/blob/126a323f/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/DynRealmMembership.java ---------------------------------------------------------------------- diff --git a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/DynRealmMembership.java b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/DynRealmMembership.java new file mode 100644 index 0000000..3e26583 --- /dev/null +++ b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/DynRealmMembership.java @@ -0,0 +1,31 @@ +/* + * 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.core.persistence.api.entity; + +public interface DynRealmMembership extends DynMembership<Any<?>> { + + DynRealm getDynRealm(); + + void setDynRealm(DynRealm dynRealm); + + AnyType getAnyType(); + + void setAnyType(AnyType anyType); + +} http://git-wip-us.apache.org/repos/asf/syncope/blob/126a323f/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPADynRealmDAO.java ---------------------------------------------------------------------- diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPADynRealmDAO.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPADynRealmDAO.java index 874f0d3..1c84be5 100644 --- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPADynRealmDAO.java +++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPADynRealmDAO.java @@ -19,13 +19,14 @@ package org.apache.syncope.core.persistence.jpa.dao; import java.util.List; +import java.util.Optional; import javax.persistence.Query; import javax.persistence.TypedQuery; -import org.apache.syncope.common.lib.types.AnyTypeKind; import org.apache.syncope.core.persistence.api.dao.AnySearchDAO; import org.apache.syncope.core.persistence.api.dao.DynRealmDAO; import org.apache.syncope.core.persistence.api.entity.Any; import org.apache.syncope.core.persistence.api.entity.DynRealm; +import org.apache.syncope.core.persistence.api.entity.DynRealmMembership; import org.apache.syncope.core.persistence.api.search.SearchCondConverter; import org.apache.syncope.core.persistence.jpa.entity.JPADynRealm; import org.apache.syncope.core.provisioning.api.event.AnyCreatedUpdatedEvent; @@ -72,42 +73,21 @@ public class JPADynRealmDAO extends AbstractDAO<DynRealm> implements DynRealmDAO DynRealm merged = entityManager().merge(dynRealm); // refresh dynamic memberships - if (merged.getFIQLCond() != null) { - clearDynMembers(merged); - - List<Any<?>> matching = searchDAO().search( - SearchCondConverter.convert(merged.getFIQLCond()), AnyTypeKind.USER); - for (Any<?> any : matching) { - Query insert = entityManager().createNativeQuery("INSERT INTO " + DYNMEMB_TABLE + " VALUES(?, ?)"); - insert.setParameter(1, any.getKey()); - insert.setParameter(2, merged.getKey()); - insert.executeUpdate(); - - publisher.publishEvent(new AnyCreatedUpdatedEvent<>(this, any, AuthContextUtils.getDomain())); - } - - matching = searchDAO().search( - SearchCondConverter.convert(merged.getFIQLCond()), AnyTypeKind.GROUP); - for (Any<?> any : matching) { - Query insert = entityManager().createNativeQuery("INSERT INTO " + DYNMEMB_TABLE + " VALUES(?, ?)"); - insert.setParameter(1, any.getKey()); - insert.setParameter(2, merged.getKey()); - insert.executeUpdate(); - - publisher.publishEvent(new AnyCreatedUpdatedEvent<>(this, any, AuthContextUtils.getDomain())); - } - - matching = searchDAO().search( - SearchCondConverter.convert(merged.getFIQLCond()), AnyTypeKind.ANY_OBJECT); - for (Any<?> any : matching) { - Query insert = entityManager().createNativeQuery("INSERT INTO " + DYNMEMB_TABLE + " VALUES(?, ?)"); - insert.setParameter(1, any.getKey()); - insert.setParameter(2, merged.getKey()); - insert.executeUpdate(); - - publisher.publishEvent(new AnyCreatedUpdatedEvent<>(this, any, AuthContextUtils.getDomain())); - } - } + clearDynMembers(merged); + + merged.getDynMemberships().stream().map(memb -> searchDAO().search( + SearchCondConverter.convert(memb.getFIQLCond()), memb.getAnyType().getKind())). + forEachOrdered(matching -> { + matching.forEach(any -> { + Query insert = entityManager().createNativeQuery("INSERT INTO " + DYNMEMB_TABLE + + " VALUES(?, ?)"); + insert.setParameter(1, any.getKey()); + insert.setParameter(2, merged.getKey()); + insert.executeUpdate(); + + publisher.publishEvent(new AnyCreatedUpdatedEvent<>(this, any, AuthContextUtils.getDomain())); + }); + }); return merged; } @@ -134,22 +114,22 @@ public class JPADynRealmDAO extends AbstractDAO<DynRealm> implements DynRealmDAO @Transactional @Override public void refreshDynMemberships(final Any<?> any) { - for (DynRealm dynRealm : findAll()) { - if (dynRealm.getFIQLCond() != null) { + findAll().forEach(dynRealm -> { + Optional<? extends DynRealmMembership> memb = dynRealm.getDynMembership(any.getType()); + if (memb.isPresent()) { Query delete = entityManager().createNativeQuery( "DELETE FROM " + DYNMEMB_TABLE + " WHERE dynRealm_id=? AND any_id=?"); delete.setParameter(1, dynRealm.getKey()); delete.setParameter(2, any.getKey()); delete.executeUpdate(); - - if (searchDAO().matches(any, SearchCondConverter.convert(dynRealm.getFIQLCond()))) { + if (searchDAO().matches(any, SearchCondConverter.convert(memb.get().getFIQLCond()))) { Query insert = entityManager().createNativeQuery("INSERT INTO " + DYNMEMB_TABLE + " VALUES(?, ?)"); insert.setParameter(1, any.getKey()); insert.setParameter(2, dynRealm.getKey()); insert.executeUpdate(); } } - } + }); } @Override http://git-wip-us.apache.org/repos/asf/syncope/blob/126a323f/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPADynRealm.java ---------------------------------------------------------------------- diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPADynRealm.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPADynRealm.java index 3a0aaa1..a92cb41 100644 --- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPADynRealm.java +++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPADynRealm.java @@ -18,11 +18,18 @@ */ package org.apache.syncope.core.persistence.jpa.entity; +import java.util.ArrayList; +import java.util.List; +import java.util.Optional; import javax.persistence.Cacheable; +import javax.persistence.CascadeType; import javax.persistence.Entity; +import javax.persistence.FetchType; +import javax.persistence.OneToMany; import javax.persistence.Table; -import javax.validation.constraints.NotNull; +import org.apache.syncope.core.persistence.api.entity.AnyType; import org.apache.syncope.core.persistence.api.entity.DynRealm; +import org.apache.syncope.core.persistence.api.entity.DynRealmMembership; import org.apache.syncope.core.persistence.jpa.validation.entity.DynRealmCheck; @Entity @@ -35,16 +42,25 @@ public class JPADynRealm extends AbstractProvidedKeyEntity implements DynRealm { public static final String TABLE = "DynRealm"; - @NotNull - private String fiql; + @OneToMany(cascade = CascadeType.ALL, orphanRemoval = true, fetch = FetchType.EAGER, mappedBy = "dynRealm") + private List<JPADynRealmMembership> dynMemberships = new ArrayList<>(); @Override - public String getFIQLCond() { - return fiql; + public boolean add(final DynRealmMembership dynRealmMembership) { + checkType(dynRealmMembership, JPADynRealmMembership.class); + return this.dynMemberships.add((JPADynRealmMembership) dynRealmMembership); } @Override - public void setFIQLCond(final String fiql) { - this.fiql = fiql; + public Optional<? extends DynRealmMembership> getDynMembership(final AnyType anyType) { + return dynMemberships.stream(). + filter(dynRealmMembership -> anyType != null && anyType.equals(dynRealmMembership.getAnyType())). + findFirst(); } + + @Override + public List<? extends DynRealmMembership> getDynMemberships() { + return dynMemberships; + } + } http://git-wip-us.apache.org/repos/asf/syncope/blob/126a323f/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPADynRealmMembership.java ---------------------------------------------------------------------- diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPADynRealmMembership.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPADynRealmMembership.java new file mode 100644 index 0000000..c97773a --- /dev/null +++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPADynRealmMembership.java @@ -0,0 +1,79 @@ +/* + * 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.core.persistence.jpa.entity; + +import javax.persistence.Entity; +import javax.persistence.ManyToOne; +import javax.persistence.OneToOne; +import javax.persistence.Table; +import javax.validation.constraints.NotNull; +import org.apache.syncope.core.persistence.api.entity.AnyType; +import org.apache.syncope.core.persistence.api.entity.DynRealm; +import org.apache.syncope.core.persistence.api.entity.DynRealmMembership; + +@Entity +@Table(name = JPADynRealmMembership.TABLE) +public class JPADynRealmMembership extends AbstractGeneratedKeyEntity implements DynRealmMembership { + + private static final long serialVersionUID = 8157856850557493134L; + + public static final String TABLE = "DynRealmMembership"; + + @OneToOne + private JPADynRealm dynRealm; + + @ManyToOne + private JPAAnyType anyType; + + @NotNull + private String fiql; + + @Override + public DynRealm getDynRealm() { + return dynRealm; + } + + @Override + public void setDynRealm(final DynRealm dynRealm) { + checkType(dynRealm, JPADynRealm.class); + this.dynRealm = (JPADynRealm) dynRealm; + } + + @Override + public AnyType getAnyType() { + return anyType; + } + + @Override + public void setAnyType(final AnyType anyType) { + checkType(anyType, JPAAnyType.class); + this.anyType = (JPAAnyType) anyType; + } + + @Override + public String getFIQLCond() { + return fiql; + } + + @Override + public void setFIQLCond(final String fiql) { + this.fiql = fiql; + } + +} http://git-wip-us.apache.org/repos/asf/syncope/blob/126a323f/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPAEntityFactory.java ---------------------------------------------------------------------- diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPAEntityFactory.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPAEntityFactory.java index 66894e5..985bfa0 100644 --- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPAEntityFactory.java +++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPAEntityFactory.java @@ -126,6 +126,7 @@ import org.apache.syncope.core.persistence.api.entity.policy.PullPolicy; import org.apache.syncope.core.persistence.api.entity.resource.OrgUnit; import org.apache.syncope.core.persistence.jpa.entity.resource.JPAOrgUnit; import org.apache.syncope.core.persistence.api.entity.DynRealm; +import org.apache.syncope.core.persistence.api.entity.DynRealmMembership; import org.apache.syncope.core.persistence.api.entity.resource.ExternalResourceHistoryConf; import org.apache.syncope.core.persistence.api.entity.resource.OrgUnitItem; import org.apache.syncope.core.persistence.jpa.entity.resource.JPAExternalResourceHistoryConf; @@ -145,6 +146,8 @@ public class JPAEntityFactory implements EntityFactory { result = (E) new JPARealm(); } else if (reference.equals(DynRealm.class)) { result = (E) new JPADynRealm(); + } else if (reference.equals(DynRealmMembership.class)) { + result = (E) new JPADynRealmMembership(); } else if (reference.equals(AnyTemplateRealm.class)) { result = (E) new JPAAnyTemplateRealm(); } else if (reference.equals(AccountPolicy.class)) { http://git-wip-us.apache.org/repos/asf/syncope/blob/126a323f/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/group/JPAGroup.java ---------------------------------------------------------------------- diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/group/JPAGroup.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/group/JPAGroup.java index 2bddf1c..53ef4fb 100644 --- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/group/JPAGroup.java +++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/group/JPAGroup.java @@ -209,8 +209,9 @@ public class JPAGroup extends AbstractAny<GPlainAttr> implements Group { @Override public Optional<? extends ADynGroupMembership> getADynMembership(final AnyType anyType) { - return aDynMemberships.stream().filter(dynGroupMembership - -> anyType != null && anyType.equals(dynGroupMembership.getAnyType())).findFirst(); + return aDynMemberships.stream(). + filter(dynGroupMembership -> anyType != null && anyType.equals(dynGroupMembership.getAnyType())). + findFirst(); } @Override @@ -226,8 +227,9 @@ public class JPAGroup extends AbstractAny<GPlainAttr> implements Group { @Override public Optional<? extends TypeExtension> getTypeExtension(final AnyType anyType) { - return typeExtensions.stream().filter(typeExtension - -> typeExtension.getAnyType().equals(anyType)).findFirst(); + return typeExtensions.stream(). + filter(typeExtension -> typeExtension.getAnyType().equals(anyType)). + findFirst(); } @Override http://git-wip-us.apache.org/repos/asf/syncope/blob/126a323f/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/outer/DynRealmTest.java ---------------------------------------------------------------------- diff --git a/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/outer/DynRealmTest.java b/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/outer/DynRealmTest.java index 307b865..db47035 100644 --- a/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/outer/DynRealmTest.java +++ b/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/outer/DynRealmTest.java @@ -26,11 +26,13 @@ import static org.junit.Assert.fail; import java.util.List; import org.apache.syncope.common.lib.types.AnyTypeKind; import org.apache.syncope.core.persistence.api.dao.AnySearchDAO; +import org.apache.syncope.core.persistence.api.dao.AnyTypeDAO; import org.apache.syncope.core.persistence.api.dao.DynRealmDAO; import org.apache.syncope.core.persistence.api.dao.UserDAO; import org.apache.syncope.core.persistence.api.dao.search.DynRealmCond; import org.apache.syncope.core.persistence.api.dao.search.SearchCond; import org.apache.syncope.core.persistence.api.entity.DynRealm; +import org.apache.syncope.core.persistence.api.entity.DynRealmMembership; import org.apache.syncope.core.persistence.api.entity.user.User; import org.apache.syncope.core.persistence.jpa.AbstractTest; import org.junit.Test; @@ -41,6 +43,9 @@ import org.springframework.transaction.annotation.Transactional; public class DynRealmTest extends AbstractTest { @Autowired + private AnyTypeDAO anyTypeDAO; + + @Autowired private DynRealmDAO dynRealmDAO; @Autowired @@ -53,7 +58,14 @@ public class DynRealmTest extends AbstractTest { public void misc() { DynRealm dynRealm = entityFactory.newEntity(DynRealm.class); dynRealm.setKey("/name"); - dynRealm.setFIQLCond("cool==true"); + + DynRealmMembership memb = entityFactory.newEntity(DynRealmMembership.class); + memb.setDynRealm(dynRealm); + memb.setAnyType(anyTypeDAO.findUser()); + memb.setFIQLCond("cool==true"); + + dynRealm.add(memb); + memb.setDynRealm(dynRealm); // invalid key (starts with /) try { http://git-wip-us.apache.org/repos/asf/syncope/blob/126a323f/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/DynRealmDataBinderImpl.java ---------------------------------------------------------------------- diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/DynRealmDataBinderImpl.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/DynRealmDataBinderImpl.java index 9a61f17..6973e1e 100644 --- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/DynRealmDataBinderImpl.java +++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/DynRealmDataBinderImpl.java @@ -18,46 +18,83 @@ */ package org.apache.syncope.core.provisioning.java.data; +import java.util.Iterator; import org.apache.syncope.common.lib.SyncopeClientException; import org.apache.syncope.common.lib.to.DynRealmTO; import org.apache.syncope.common.lib.types.ClientExceptionType; +import org.apache.syncope.core.persistence.api.dao.AnyTypeDAO; import org.apache.syncope.core.persistence.api.dao.DynRealmDAO; import org.apache.syncope.core.persistence.api.dao.search.SearchCond; +import org.apache.syncope.core.persistence.api.entity.AnyType; import org.apache.syncope.core.persistence.api.entity.EntityFactory; import org.apache.syncope.core.persistence.api.entity.DynRealm; +import org.apache.syncope.core.persistence.api.entity.DynRealmMembership; import org.apache.syncope.core.persistence.api.search.SearchCondConverter; import org.apache.syncope.core.provisioning.api.data.DynRealmDataBinder; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; @Component public class DynRealmDataBinderImpl implements DynRealmDataBinder { + private static final Logger LOG = LoggerFactory.getLogger(DynRealmDataBinder.class); + + @Autowired + private AnyTypeDAO anyTypeDAO; + @Autowired private DynRealmDAO dynRealmDAO; @Autowired private EntityFactory entityFactory; + private void setDynMembership(final DynRealm dynRealm, final AnyType anyType, final String dynMembershipFIQL) { + SearchCond dynMembershipCond = SearchCondConverter.convert(dynMembershipFIQL); + if (!dynMembershipCond.isValid()) { + SyncopeClientException sce = SyncopeClientException.build(ClientExceptionType.InvalidSearchExpression); + sce.getElements().add(dynMembershipFIQL); + throw sce; + } + + DynRealmMembership dynMembership; + if (dynRealm.getDynMembership(anyType).isPresent()) { + dynMembership = dynRealm.getDynMembership(anyType).get(); + } else { + dynMembership = entityFactory.newEntity(DynRealmMembership.class); + dynMembership.setDynRealm(dynRealm); + dynMembership.setAnyType(anyType); + dynRealm.add(dynMembership); + } + dynMembership.setFIQLCond(dynMembershipFIQL); + } + @Override public DynRealm create(final DynRealmTO dynRealmTO) { return update(entityFactory.newEntity(DynRealm.class), dynRealmTO); } @Override - public DynRealm update(final DynRealm dynRealm, final DynRealmTO dynRealmTO) { - dynRealm.setKey(dynRealmTO.getKey()); + public DynRealm update(final DynRealm toBeUpdated, final DynRealmTO dynRealmTO) { + toBeUpdated.setKey(dynRealmTO.getKey()); + DynRealm dynRealm = dynRealmDAO.save(toBeUpdated); - SearchCond cond = null; - if (dynRealmTO.getCond() != null) { - cond = SearchCondConverter.convert(dynRealmTO.getCond()); + for (Iterator<? extends DynRealmMembership> itor = dynRealm.getDynMemberships().iterator(); itor.hasNext();) { + DynRealmMembership memb = itor.next(); + memb.setDynRealm(null); + itor.remove(); } - if (cond == null || !cond.isValid()) { - SyncopeClientException sce = SyncopeClientException.build(ClientExceptionType.InvalidSearchExpression); - sce.getElements().add(dynRealmTO.getCond()); - throw sce; - } - dynRealm.setFIQLCond(dynRealmTO.getCond()); + dynRealmDAO.clearDynMembers(dynRealm); + + dynRealmTO.getDynMembershipConds().entrySet().forEach(entry -> { + AnyType anyType = anyTypeDAO.find(entry.getKey()); + if (anyType == null) { + LOG.warn("Ignoring invalid {}: {}", AnyType.class.getSimpleName(), entry.getKey()); + } else { + setDynMembership(dynRealm, anyType, entry.getValue()); + } + }); return dynRealmDAO.save(dynRealm); } @@ -67,7 +104,10 @@ public class DynRealmDataBinderImpl implements DynRealmDataBinder { DynRealmTO dynRealmTO = new DynRealmTO(); dynRealmTO.setKey(dynRealm.getKey()); - dynRealmTO.setCond(dynRealm.getFIQLCond()); + + dynRealm.getDynMemberships().forEach(memb -> { + dynRealmTO.getDynMembershipConds().put(memb.getAnyType().getKey(), memb.getFIQLCond()); + }); return dynRealmTO; } http://git-wip-us.apache.org/repos/asf/syncope/blob/126a323f/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/GroupDataBinderImpl.java ---------------------------------------------------------------------- diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/GroupDataBinderImpl.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/GroupDataBinderImpl.java index 3105e19..8ba464e 100644 --- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/GroupDataBinderImpl.java +++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/GroupDataBinderImpl.java @@ -69,13 +69,18 @@ public class GroupDataBinderImpl extends AbstractAnyDataBinder implements GroupD sce.getElements().add(dynMembershipFIQL); throw sce; } + if (anyType.getKind() == AnyTypeKind.GROUP) { + SyncopeClientException sce = SyncopeClientException.build(ClientExceptionType.InvalidAnyType); + sce.getElements().add(anyType.getKind().name()); + throw sce; + } DynGroupMembership<?> dynMembership; if (anyType.getKind() == AnyTypeKind.ANY_OBJECT && !group.getADynMembership(anyType).isPresent()) { dynMembership = entityFactory.newEntity(ADynGroupMembership.class); dynMembership.setGroup(group); - group.add((ADynGroupMembership) dynMembership); ((ADynGroupMembership) dynMembership).setAnyType(anyType); + group.add((ADynGroupMembership) dynMembership); } else if (anyType.getKind() == AnyTypeKind.USER && group.getUDynMembership() == null) { dynMembership = entityFactory.newEntity(UDynGroupMembership.class); dynMembership.setGroup(group); http://git-wip-us.apache.org/repos/asf/syncope/blob/126a323f/fit/core-reference/src/test/java/org/apache/syncope/fit/core/DynRealmITCase.java ---------------------------------------------------------------------- diff --git a/fit/core-reference/src/test/java/org/apache/syncope/fit/core/DynRealmITCase.java b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/DynRealmITCase.java index 3506b8d..2772092 100644 --- a/fit/core-reference/src/test/java/org/apache/syncope/fit/core/DynRealmITCase.java +++ b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/DynRealmITCase.java @@ -38,6 +38,7 @@ import org.apache.syncope.common.lib.to.PagedResult; import org.apache.syncope.common.lib.to.ProvisioningResult; import org.apache.syncope.common.lib.to.RoleTO; import org.apache.syncope.common.lib.to.UserTO; +import org.apache.syncope.common.lib.types.AnyTypeKind; import org.apache.syncope.common.lib.types.ClientExceptionType; import org.apache.syncope.common.lib.types.PatchOperation; import org.apache.syncope.common.lib.types.StandardEntitlement; @@ -56,7 +57,7 @@ public class DynRealmITCase extends AbstractITCase { try { dynRealm = new DynRealmTO(); dynRealm.setKey("/name" + getUUIDString()); - dynRealm.setCond("cool==true"); + dynRealm.getDynMembershipConds().put(AnyTypeKind.USER.name(), "cool==true"); // invalid key (starts with /) try { @@ -93,7 +94,8 @@ public class DynRealmITCase extends AbstractITCase { // 1. create dynamic realm for all users and groups having resource-ldap assigned dynRealm = new DynRealmTO(); dynRealm.setKey("LDAPLovers" + getUUIDString()); - dynRealm.setCond("$resources==resource-ldap"); + dynRealm.getDynMembershipConds().put(AnyTypeKind.USER.name(), "$resources==resource-ldap"); + dynRealm.getDynMembershipConds().put(AnyTypeKind.GROUP.name(), "$resources==resource-ldap"); Response response = dynRealmService.create(dynRealm); dynRealm = getObject(response.getLocation(), DynRealmService.class, DynRealmTO.class);