http://git-wip-us.apache.org/repos/asf/syncope/blob/a1bb6723/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPAGroupDAO.java ---------------------------------------------------------------------- diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPAGroupDAO.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPAGroupDAO.java index 1b7374a..6cec27a 100644 --- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPAGroupDAO.java +++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPAGroupDAO.java @@ -28,6 +28,7 @@ import java.util.Set; import javax.persistence.NoResultException; import javax.persistence.Query; import javax.persistence.TypedQuery; +import org.apache.commons.collections4.CollectionUtils; import org.apache.commons.collections4.IterableUtils; import org.apache.commons.collections4.Predicate; import org.apache.syncope.common.lib.types.AnyTypeKind; @@ -165,6 +166,10 @@ public class JPAGroupDAO extends AbstractAnyDAO<Group> implements GroupDAO { || realm.equals(RealmUtils.getGroupOwnerRealm(group.getRealm().getFullPath(), group.getKey())); } }); + if (!authorized) { + authorized = !CollectionUtils.intersection(findDynRealms(group.getKey()), authRealms).isEmpty(); + } + if (authRealms == null || authRealms.isEmpty() || !authorized) { throw new DelegatedAdministrationException(AnyTypeKind.GROUP, group.getKey()); } @@ -315,11 +320,15 @@ public class JPAGroupDAO extends AbstractAnyDAO<Group> implements GroupDAO { } } + dynRealmDAO().refreshDynMemberships(merged); + return merged; } @Override public void delete(final Group group) { + dynRealmDAO().removeDynMemberships(group.getKey()); + for (AMembership membership : findAMemberships(group)) { AnyObject leftEnd = membership.getLeftEnd(); leftEnd.getMemberships().remove(membership); @@ -349,6 +358,9 @@ public class JPAGroupDAO extends AbstractAnyDAO<Group> implements GroupDAO { publisher.publishEvent(new AnyCreatedUpdatedEvent<>(this, leftEnd, AuthContextUtils.getDomain())); } + clearUDynMembers(group); + clearADynMembers(group); + entityManager().remove(group); publisher.publishEvent( new AnyDeletedEvent(this, AnyTypeKind.GROUP, group.getKey(), AuthContextUtils.getDomain())); @@ -427,7 +439,7 @@ public class JPAGroupDAO extends AbstractAnyDAO<Group> implements GroupDAO { @Override public void removeDynMemberships(final AnyObject anyObject) { - List<Group> dynGroups = anyObjectDAO().findDynGroups(anyObject); + List<Group> dynGroups = anyObjectDAO().findDynGroups(anyObject.getKey()); Query delete = entityManager().createNativeQuery("DELETE FROM " + ADYNMEMB_TABLE + " WHERE any_id=?"); delete.setParameter(1, anyObject.getKey()); @@ -501,7 +513,7 @@ public class JPAGroupDAO extends AbstractAnyDAO<Group> implements GroupDAO { @Override public void removeDynMemberships(final User user) { - List<Group> dynGroups = userDAO().findDynGroups(user); + List<Group> dynGroups = userDAO().findDynGroups(user.getKey()); Query delete = entityManager().createNativeQuery("DELETE FROM " + UDYNMEMB_TABLE + " WHERE any_id=?"); delete.setParameter(1, user.getKey());
http://git-wip-us.apache.org/repos/asf/syncope/blob/a1bb6723/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPARoleDAO.java ---------------------------------------------------------------------- diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPARoleDAO.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPARoleDAO.java index 9796f4d..367ee8d 100644 --- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPARoleDAO.java +++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPARoleDAO.java @@ -121,6 +121,8 @@ public class JPARoleDAO extends AbstractDAO<Role> implements RoleDAO { publisher.publishEvent(new AnyCreatedUpdatedEvent<>(this, user, AuthContextUtils.getDomain())); } + clearDynMembers(role); + entityManager().remove(role); } @@ -183,9 +185,9 @@ public class JPARoleDAO extends AbstractDAO<Role> implements RoleDAO { } @Override - public void removeDynMemberships(final User user) { + public void removeDynMemberships(final String key) { Query delete = entityManager().createNativeQuery("DELETE FROM " + DYNMEMB_TABLE + " WHERE any_id=?"); - delete.setParameter(1, user.getKey()); + delete.setParameter(1, key); delete.executeUpdate(); } http://git-wip-us.apache.org/repos/asf/syncope/blob/a1bb6723/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPAUserDAO.java ---------------------------------------------------------------------- diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPAUserDAO.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPAUserDAO.java index 40db0c4..afa83f4 100644 --- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPAUserDAO.java +++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPAUserDAO.java @@ -186,6 +186,9 @@ public class JPAUserDAO extends AbstractAnyDAO<User> implements UserDAO { return user.getRealm().getFullPath().startsWith(realm); } }); + if (!authorized) { + authorized = !CollectionUtils.intersection(findDynRealms(user.getKey()), authRealms).isEmpty(); + } if (authRealms == null || authRealms.isEmpty() || !authorized) { throw new DelegatedAdministrationException(AnyTypeKind.USER, user.getKey()); } @@ -448,14 +451,16 @@ public class JPAUserDAO extends AbstractAnyDAO<User> implements UserDAO { roleDAO.refreshDynMemberships(merged); groupDAO().refreshDynMemberships(merged); + dynRealmDAO().refreshDynMemberships(merged); return merged; } @Override public void delete(final User user) { - roleDAO.removeDynMemberships(user); - groupDAO.removeDynMemberships(user); + roleDAO.removeDynMemberships(user.getKey()); + groupDAO().removeDynMemberships(user); + dynRealmDAO().removeDynMemberships(user.getKey()); AccessToken accessToken = accessTokenDAO.findByOwner(user.getUsername()); if (accessToken != null) { @@ -470,21 +475,21 @@ public class JPAUserDAO extends AbstractAnyDAO<User> implements UserDAO { @Transactional(propagation = Propagation.REQUIRES_NEW, readOnly = true) @Override public Collection<Role> findAllRoles(final User user) { - return CollectionUtils.union(user.getRoles(), findDynRoles(user)); + return CollectionUtils.union(user.getRoles(), findDynRoles(user.getKey())); } @Transactional(propagation = Propagation.REQUIRES_NEW, readOnly = true) @Override - public List<Role> findDynRoles(final User user) { + public List<Role> findDynRoles(final String key) { Query query = entityManager().createNativeQuery( "SELECT role_id FROM " + JPARoleDAO.DYNMEMB_TABLE + " WHERE any_id=?"); - query.setParameter(1, user.getKey()); + query.setParameter(1, key); List<Role> result = new ArrayList<>(); - for (Object key : query.getResultList()) { - String actualKey = key instanceof Object[] - ? (String) ((Object[]) key)[0] - : ((String) key); + for (Object resultKey : query.getResultList()) { + String actualKey = resultKey instanceof Object[] + ? (String) ((Object[]) resultKey)[0] + : ((String) resultKey); Role role = roleDAO.find(actualKey); if (role == null) { @@ -498,16 +503,16 @@ public class JPAUserDAO extends AbstractAnyDAO<User> implements UserDAO { @Transactional(propagation = Propagation.REQUIRES_NEW, readOnly = true) @Override - public List<Group> findDynGroups(final User user) { + public List<Group> findDynGroups(final String key) { Query query = entityManager().createNativeQuery( "SELECT group_id FROM " + JPAGroupDAO.UDYNMEMB_TABLE + " WHERE any_id=?"); - query.setParameter(1, user.getKey()); + query.setParameter(1, key); List<Group> result = new ArrayList<>(); - for (Object key : query.getResultList()) { - String actualKey = key instanceof Object[] - ? (String) ((Object[]) key)[0] - : ((String) key); + for (Object resultKey : query.getResultList()) { + String actualKey = resultKey instanceof Object[] + ? (String) ((Object[]) resultKey)[0] + : ((String) resultKey); Group group = groupDAO().find(actualKey); if (group == null) { @@ -530,7 +535,7 @@ public class JPAUserDAO extends AbstractAnyDAO<User> implements UserDAO { return input.getRightEnd(); } }, new ArrayList<Group>()), - findDynGroups(user)); + findDynGroups(user.getKey())); } @Transactional(propagation = Propagation.REQUIRES_NEW, readOnly = true) http://git-wip-us.apache.org/repos/asf/syncope/blob/a1bb6723/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/SearchSupport.java ---------------------------------------------------------------------- diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/SearchSupport.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/SearchSupport.java index fd40470..5cac5bb 100644 --- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/SearchSupport.java +++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/SearchSupport.java @@ -135,6 +135,10 @@ class SearchSupport { return new SearchView("svdr", JPARoleDAO.DYNMEMB_TABLE); } + public SearchView dynrealmmembership() { + return new SearchView("svdrealm", JPADynRealmDAO.DYNMEMB_TABLE); + } + public SearchView nullAttr() { return new SearchView("svna", field().name + "_null_attr"); } http://git-wip-us.apache.org/repos/asf/syncope/blob/a1bb6723/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 new file mode 100644 index 0000000..3a0aaa1 --- /dev/null +++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPADynRealm.java @@ -0,0 +1,50 @@ +/* + * 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.Cacheable; +import javax.persistence.Entity; +import javax.persistence.Table; +import javax.validation.constraints.NotNull; +import org.apache.syncope.core.persistence.api.entity.DynRealm; +import org.apache.syncope.core.persistence.jpa.validation.entity.DynRealmCheck; + +@Entity +@Table(name = JPADynRealm.TABLE) +@Cacheable +@DynRealmCheck +public class JPADynRealm extends AbstractProvidedKeyEntity implements DynRealm { + + private static final long serialVersionUID = -6851035842423560341L; + + public static final String TABLE = "DynRealm"; + + @NotNull + private String fiql; + + @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/a1bb6723/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 7d9660c..daea67a 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 @@ -124,6 +124,7 @@ import org.apache.syncope.core.persistence.api.entity.task.AnyTemplatePullTask; 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; @Component public class JPAEntityFactory implements EntityFactory { @@ -137,6 +138,8 @@ public class JPAEntityFactory implements EntityFactory { result = (E) new JPADomain(); } else if (reference.equals(Realm.class)) { result = (E) new JPARealm(); + } else if (reference.equals(DynRealm.class)) { + result = (E) new JPADynRealm(); } 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/a1bb6723/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPARole.java ---------------------------------------------------------------------- diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPARole.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPARole.java index 919c3cb..30a1f47 100644 --- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPARole.java +++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPARole.java @@ -41,6 +41,7 @@ import org.apache.syncope.core.persistence.api.entity.Realm; import org.apache.syncope.core.persistence.api.entity.Role; import org.apache.syncope.core.persistence.jpa.entity.user.JPADynRoleMembership; import org.apache.syncope.core.persistence.jpa.validation.entity.RoleCheck; +import org.apache.syncope.core.persistence.api.entity.DynRealm; @Entity @Table(name = JPARole.TABLE) @@ -67,6 +68,14 @@ public class JPARole extends AbstractProvidedKeyEntity implements Role { @Valid private List<JPARealm> realms = new ArrayList<>(); + @ManyToMany(fetch = FetchType.EAGER) + @JoinTable(joinColumns = + @JoinColumn(name = "role_id"), + inverseJoinColumns = + @JoinColumn(name = "dynamicRealm_id")) + @Valid + private List<JPADynRealm> dynRealms = new ArrayList<>(); + @OneToOne(cascade = CascadeType.ALL, orphanRemoval = true, mappedBy = "role") @Valid private JPADynRoleMembership dynMembership; @@ -91,6 +100,17 @@ public class JPARole extends AbstractProvidedKeyEntity implements Role { } @Override + public boolean add(final DynRealm dynamicRealm) { + checkType(dynamicRealm, JPADynRealm.class); + return dynRealms.add((JPADynRealm) dynamicRealm); + } + + @Override + public List<? extends DynRealm> getDynRealms() { + return dynRealms; + } + + @Override public DynRoleMembership getDynMembership() { return dynMembership; } http://git-wip-us.apache.org/repos/asf/syncope/blob/a1bb6723/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/validation/entity/DynRealmCheck.java ---------------------------------------------------------------------- diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/validation/entity/DynRealmCheck.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/validation/entity/DynRealmCheck.java new file mode 100644 index 0000000..567dc74 --- /dev/null +++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/validation/entity/DynRealmCheck.java @@ -0,0 +1,41 @@ +/* + * 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.validation.entity; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +import javax.validation.Constraint; +import javax.validation.Payload; + +@Target({ ElementType.TYPE }) +@Retention(RetentionPolicy.RUNTIME) +@Constraint(validatedBy = DynRealmValidator.class) +@Documented +public @interface DynRealmCheck { + + String message() default "{org.apache.syncope.core.persistence.validation.dynrealm}"; + + Class<?>[] groups() default {}; + + Class<? extends Payload>[] payload() default {}; +} http://git-wip-us.apache.org/repos/asf/syncope/blob/a1bb6723/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/validation/entity/DynRealmValidator.java ---------------------------------------------------------------------- diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/validation/entity/DynRealmValidator.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/validation/entity/DynRealmValidator.java new file mode 100644 index 0000000..e97c122 --- /dev/null +++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/validation/entity/DynRealmValidator.java @@ -0,0 +1,47 @@ +/* + * 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.validation.entity; + +import java.util.regex.Pattern; +import javax.validation.ConstraintValidatorContext; +import org.apache.syncope.common.lib.types.EntityViolationType; +import org.apache.syncope.core.persistence.api.entity.DynRealm; + +public class DynRealmValidator extends AbstractValidator<DynRealmCheck, DynRealm> { + + private static final Pattern REALM_KEY_PATTERN = Pattern.compile("^[A-Za-z0-9]+"); + + @Override + public boolean isValid(final DynRealm dynRealm, final ConstraintValidatorContext context) { + context.disableDefaultConstraintViolation(); + + boolean isValid = true; + + if (dynRealm.getKey().startsWith("/") || !REALM_KEY_PATTERN.matcher(dynRealm.getKey()).matches()) { + isValid = false; + + context.buildConstraintViolationWithTemplate( + getTemplate(EntityViolationType.InvalidDynRealm, + "Only letters and numbers are allowed in dynamic realm key, and must not start with /")). + addPropertyNode("key").addConstraintViolation(); + } + + return isValid; + } +} http://git-wip-us.apache.org/repos/asf/syncope/blob/a1bb6723/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/validation/entity/EntityValidationListener.java ---------------------------------------------------------------------- diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/validation/entity/EntityValidationListener.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/validation/entity/EntityValidationListener.java index e1bdf68..3b46f62 100644 --- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/validation/entity/EntityValidationListener.java +++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/validation/entity/EntityValidationListener.java @@ -28,6 +28,7 @@ import org.apache.syncope.core.persistence.api.attrvalue.validation.InvalidEntit import org.apache.syncope.core.spring.ApplicationContextProvider; import org.apache.syncope.core.persistence.api.entity.AnnotatedEntity; import org.apache.syncope.core.persistence.api.entity.Any; +import org.apache.syncope.core.persistence.api.entity.DynMembership; import org.apache.syncope.core.persistence.api.entity.Entity; import org.apache.syncope.core.persistence.api.entity.GroupableRelatable; import org.apache.syncope.core.persistence.api.entity.Policy; @@ -62,6 +63,7 @@ public class EntityValidationListener { && !Policy.class.equals(interf) && !GroupableRelatable.class.equals(interf) && !Any.class.equals(interf) + && !DynMembership.class.equals(interf) && Entity.class.isAssignableFrom(interf)) { entityInt = interf; http://git-wip-us.apache.org/repos/asf/syncope/blob/a1bb6723/core/persistence-jpa/src/main/resources/indexes.xml ---------------------------------------------------------------------- diff --git a/core/persistence-jpa/src/main/resources/indexes.xml b/core/persistence-jpa/src/main/resources/indexes.xml index 1dfa63d..ded4d52 100644 --- a/core/persistence-jpa/src/main/resources/indexes.xml +++ b/core/persistence-jpa/src/main/resources/indexes.xml @@ -29,6 +29,9 @@ under the License. <entry key="DynRoleMembers_any_id">CREATE INDEX DynRoleMembers_any_id ON DynRoleMembers(any_id)</entry> <entry key="DynRoleMembers_role_id">CREATE INDEX DynRoleMembers_role_id ON DynRoleMembers(role_id)</entry> + <entry key="DynRealmMembers_any_id">CREATE INDEX DynRealmMembers_any_id ON DynRealmMembers(any_id)</entry> + <entry key="DynRealmMembers_realm_id">CREATE INDEX DynRealmMembers_dynRealm_id ON DynRealmMembers(dynRealm_id)</entry> + <entry key="UPlainAttrValue_stringvalueIndex">CREATE INDEX UAttrValue_stringvalueIndex ON UPlainAttrValue(stringvalue)</entry> <entry key="UPlainAttrValue_datevalueIndex">CREATE INDEX UAttrValue_datevalueIndex ON UPlainAttrValue(datevalue)</entry> <entry key="UPlainAttrValue_longvalueIndex">CREATE INDEX UAttrValue_longvalueIndex ON UPlainAttrValue(longvalue)</entry> http://git-wip-us.apache.org/repos/asf/syncope/blob/a1bb6723/core/persistence-jpa/src/main/resources/views.xml ---------------------------------------------------------------------- diff --git a/core/persistence-jpa/src/main/resources/views.xml b/core/persistence-jpa/src/main/resources/views.xml index fb68516..bcff6b9 100644 --- a/core/persistence-jpa/src/main/resources/views.xml +++ b/core/persistence-jpa/src/main/resources/views.xml @@ -39,6 +39,12 @@ under the License. role_id CHAR(36), UNIQUE(any_id, role_id)) </entry> + <entry key="DynRealmMembers"> + CREATE TABLE DynRealmMembers( + any_id CHAR(36), + dynRealm_id CHAR(36), + UNIQUE(any_id, dynRealm_id)) + </entry> <!-- user --> <entry key="user_search"> http://git-wip-us.apache.org/repos/asf/syncope/blob/a1bb6723/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/AnySearchTest.java ---------------------------------------------------------------------- diff --git a/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/AnySearchTest.java b/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/AnySearchTest.java index c7e5c62..1ba9913 100644 --- a/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/AnySearchTest.java +++ b/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/AnySearchTest.java @@ -109,7 +109,7 @@ public class AnySearchTest extends AbstractTest { assertTrue(searchDAO.matches(user, SearchCond.getLeafCond(groupCond))); RoleCond roleCond = new RoleCond(); - roleCond.setRoleKey("Other"); + roleCond.setRole("Other"); assertTrue(searchDAO.matches(user, SearchCond.getLeafCond(roleCond))); user = userDAO.find("c9b2dec2-00a7-4855-97c0-d854842b4b24"); @@ -303,7 +303,7 @@ public class AnySearchTest extends AbstractTest { @Test public void searchByRole() { RoleCond roleCond = new RoleCond(); - roleCond.setRoleKey("Other"); + roleCond.setRole("Other"); List<User> users = searchDAO.search(SearchCond.getLeafCond(roleCond), AnyTypeKind.USER); assertNotNull(users); http://git-wip-us.apache.org/repos/asf/syncope/blob/a1bb6723/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/RoleTest.java ---------------------------------------------------------------------- diff --git a/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/RoleTest.java b/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/RoleTest.java index 174eef2..55fbb19 100644 --- a/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/RoleTest.java +++ b/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/RoleTest.java @@ -18,7 +18,6 @@ */ package org.apache.syncope.core.persistence.jpa.inner; -import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; http://git-wip-us.apache.org/repos/asf/syncope/blob/a1bb6723/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/outer/AnySearchTest.java ---------------------------------------------------------------------- diff --git a/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/outer/AnySearchTest.java b/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/outer/AnySearchTest.java index f91aa7e..f3d3203 100644 --- a/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/outer/AnySearchTest.java +++ b/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/outer/AnySearchTest.java @@ -100,7 +100,7 @@ public class AnySearchTest extends AbstractTest { // 2. search user by this dynamic role RoleCond roleCond = new RoleCond(); - roleCond.setRoleKey(role.getKey()); + roleCond.setRole(role.getKey()); List<User> users = searchDAO.search(SearchCond.getLeafCond(roleCond), AnyTypeKind.USER); assertNotNull(users); http://git-wip-us.apache.org/repos/asf/syncope/blob/a1bb6723/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 new file mode 100644 index 0000000..307b865 --- /dev/null +++ b/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/outer/DynRealmTest.java @@ -0,0 +1,84 @@ +/* + * 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.outer; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; +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.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.user.User; +import org.apache.syncope.core.persistence.jpa.AbstractTest; +import org.junit.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.transaction.annotation.Transactional; + +@Transactional("Master") +public class DynRealmTest extends AbstractTest { + + @Autowired + private DynRealmDAO dynRealmDAO; + + @Autowired + private AnySearchDAO searchDAO; + + @Autowired + private UserDAO userDAO; + + @Test + public void misc() { + DynRealm dynRealm = entityFactory.newEntity(DynRealm.class); + dynRealm.setKey("/name"); + dynRealm.setFIQLCond("cool==true"); + + // invalid key (starts with /) + try { + dynRealmDAO.save(dynRealm); + fail(); + } catch (Exception e) { + assertNotNull(e); + } + + dynRealm.setKey("name"); + DynRealm actual = dynRealmDAO.save(dynRealm); + assertNotNull(actual); + + dynRealmDAO.flush(); + + DynRealmCond dynRealmCond = new DynRealmCond(); + dynRealmCond.setDynRealm(actual.getKey()); + List<User> matching = searchDAO.search(SearchCond.getLeafCond(dynRealmCond), AnyTypeKind.USER); + assertNotNull(matching); + assertFalse(matching.isEmpty()); + + User user = matching.get(0); + assertTrue(searchDAO.matches(user, SearchCond.getLeafCond(dynRealmCond))); + + assertTrue(userDAO.findDynRealms(user.getKey()).contains(actual.getKey())); + } + +} http://git-wip-us.apache.org/repos/asf/syncope/blob/a1bb6723/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/data/DynRealmDataBinder.java ---------------------------------------------------------------------- diff --git a/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/data/DynRealmDataBinder.java b/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/data/DynRealmDataBinder.java new file mode 100644 index 0000000..5244b65 --- /dev/null +++ b/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/data/DynRealmDataBinder.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.provisioning.api.data; + +import org.apache.syncope.common.lib.to.DynRealmTO; +import org.apache.syncope.core.persistence.api.entity.DynRealm; + +public interface DynRealmDataBinder { + + DynRealm create(DynRealmTO dynRealmTO); + + DynRealm update(DynRealm dynRealm, DynRealmTO dynRealmTO); + + DynRealmTO getDynRealmTO(DynRealm dynRealm); +} http://git-wip-us.apache.org/repos/asf/syncope/blob/a1bb6723/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/AnyObjectDataBinderImpl.java ---------------------------------------------------------------------- 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 5428f5f..550f183 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 @@ -102,6 +102,9 @@ public class AnyObjectDataBinderImpl extends AbstractAnyDataBinder implements An details); if (details) { + // dynamic realms + anyObjectTO.getDynRealms().addAll(userDAO.findDynRealms(anyObject.getKey())); + // relationships CollectionUtils.collect(anyObject.getRelationships(), new Transformer<ARelationship, RelationshipTO>() { @@ -126,7 +129,7 @@ public class AnyObjectDataBinderImpl extends AbstractAnyDataBinder implements An }, anyObjectTO.getMemberships()); // dynamic memberships - CollectionUtils.collect(anyObjectDAO.findDynGroups(anyObject), + CollectionUtils.collect(anyObjectDAO.findDynGroups(anyObject.getKey()), EntityUtils.<Group>keyTransformer(), anyObjectTO.getDynGroups()); } http://git-wip-us.apache.org/repos/asf/syncope/blob/a1bb6723/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 new file mode 100644 index 0000000..56d7903 --- /dev/null +++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/DynRealmDataBinderImpl.java @@ -0,0 +1,72 @@ +/* + * 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.provisioning.java.data; + +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.DynRealmDAO; +import org.apache.syncope.core.persistence.api.dao.search.SearchCond; +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.search.SearchCondConverter; +import org.apache.syncope.core.provisioning.api.data.DynRealmDataBinder; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +@Component +public class DynRealmDataBinderImpl implements DynRealmDataBinder { + + @Autowired + private DynRealmDAO dynRealmDAO; + + @Autowired + private EntityFactory entityFactory; + + @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()); + + SearchCond cond = SearchCondConverter.convert(dynRealmTO.getCond()); + if (!cond.isValid()) { + SyncopeClientException sce = SyncopeClientException.build(ClientExceptionType.InvalidSearchExpression); + sce.getElements().add(dynRealmTO.getCond()); + throw sce; + } + dynRealm.setFIQLCond(dynRealmTO.getCond()); + + return dynRealmDAO.save(dynRealm); + } + + @Override + public DynRealmTO getDynRealmTO(final DynRealm dynRealm) { + DynRealmTO dynRealmTO = new DynRealmTO(); + + dynRealmTO.setKey(dynRealm.getKey()); + dynRealmTO.setCond(dynRealm.getFIQLCond()); + + return dynRealmTO; + } + +} http://git-wip-us.apache.org/repos/asf/syncope/blob/a1bb6723/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 32206aa..c4f8c50 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 @@ -231,7 +231,7 @@ public class GroupDataBinderImpl extends AbstractAnyDataBinder implements GroupD } group = groupDAO.save(group); - + // dynamic membership if (groupPatch.getUDynMembershipCond() == null) { if (group.getUDynMembership() != null) { @@ -345,6 +345,11 @@ public class GroupDataBinderImpl extends AbstractAnyDataBinder implements GroupD group.getResources(), details); + if (details) { + // dynamic realms + groupTO.getDynRealms().addAll(groupDAO.findDynRealms(group.getKey())); + } + if (group.getUDynMembership() != null) { groupTO.setUDynMembershipCond(group.getUDynMembership().getFIQLCond()); } http://git-wip-us.apache.org/repos/asf/syncope/blob/a1bb6723/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/RoleDataBinderImpl.java ---------------------------------------------------------------------- diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/RoleDataBinderImpl.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/RoleDataBinderImpl.java index 6ccb229..b907051 100644 --- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/RoleDataBinderImpl.java +++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/RoleDataBinderImpl.java @@ -23,15 +23,18 @@ import org.apache.commons.collections4.Transformer; import org.apache.syncope.common.lib.SyncopeClientException; import org.apache.syncope.common.lib.to.RoleTO; import org.apache.syncope.common.lib.types.ClientExceptionType; +import org.apache.syncope.core.persistence.api.dao.DynRealmDAO; import org.apache.syncope.core.persistence.api.search.SearchCondConverter; import org.apache.syncope.core.persistence.api.dao.RealmDAO; import org.apache.syncope.core.persistence.api.dao.RoleDAO; 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.EntityFactory; import org.apache.syncope.core.persistence.api.entity.Realm; import org.apache.syncope.core.persistence.api.entity.Role; import org.apache.syncope.core.persistence.api.entity.user.DynRoleMembership; import org.apache.syncope.core.provisioning.api.data.RoleDataBinder; +import org.apache.syncope.core.provisioning.api.utils.EntityUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; @@ -46,6 +49,9 @@ public class RoleDataBinderImpl implements RoleDataBinder { private RealmDAO realmDAO; @Autowired + private DynRealmDAO dynRealmDAO; + + @Autowired private RoleDAO roleDAO; @Autowired @@ -93,6 +99,16 @@ public class RoleDataBinderImpl implements RoleDataBinder { } } + role.getDynRealms().clear(); + for (String key : roleTO.getDynRealms()) { + DynRealm dynRealm = dynRealmDAO.find(key); + if (dynRealm == null) { + LOG.debug("Invalid dynamic ream {}, ignoring", key); + } else { + role.add(dynRealm); + } + } + role = roleDAO.save(role); // dynamic membership @@ -127,6 +143,8 @@ public class RoleDataBinderImpl implements RoleDataBinder { } }, roleTO.getRealms()); + CollectionUtils.collect(role.getDynRealms(), EntityUtils.keyTransformer(), roleTO.getDynRealms()); + if (role.getDynMembership() != null) { roleTO.setDynMembershipCond(role.getDynMembership().getFIQLCond()); } http://git-wip-us.apache.org/repos/asf/syncope/blob/a1bb6723/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/UserDataBinderImpl.java ---------------------------------------------------------------------- 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 3cd52c3..54c9983 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 @@ -606,6 +606,9 @@ public class UserDataBinderImpl extends AbstractAnyDataBinder implements UserDat details); if (details) { + // dynamic realms + userTO.getDynRealms().addAll(userDAO.findDynRealms(user.getKey())); + // roles CollectionUtils.collect(user.getRoles(), EntityUtils.<Role>keyTransformer(), userTO.getRoles()); @@ -634,9 +637,9 @@ public class UserDataBinderImpl extends AbstractAnyDataBinder implements UserDat }, userTO.getMemberships()); // dynamic memberships - CollectionUtils.collect(userDAO.findDynRoles(user), + CollectionUtils.collect(userDAO.findDynRoles(user.getKey()), EntityUtils.<Role>keyTransformer(), userTO.getDynRoles()); - CollectionUtils.collect(userDAO.findDynGroups(user), + CollectionUtils.collect(userDAO.findDynGroups(user.getKey()), EntityUtils.<Group>keyTransformer(), userTO.getDynGroups()); } http://git-wip-us.apache.org/repos/asf/syncope/blob/a1bb6723/core/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/service/DynRealmServiceImpl.java ---------------------------------------------------------------------- diff --git a/core/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/service/DynRealmServiceImpl.java b/core/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/service/DynRealmServiceImpl.java new file mode 100644 index 0000000..41e3bad --- /dev/null +++ b/core/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/service/DynRealmServiceImpl.java @@ -0,0 +1,66 @@ +/* + * 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.rest.cxf.service; + +import java.net.URI; +import java.util.List; +import javax.ws.rs.core.Response; +import org.apache.syncope.common.lib.to.DynRealmTO; +import org.apache.syncope.common.rest.api.RESTHeaders; +import org.apache.syncope.common.rest.api.service.DynRealmService; +import org.apache.syncope.core.logic.DynRealmLogic; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +@Service +public class DynRealmServiceImpl extends AbstractServiceImpl implements DynRealmService { + + @Autowired + private DynRealmLogic logic; + + @Override + public List<DynRealmTO> list() { + return logic.list(); + } + + @Override + public DynRealmTO read(final String key) { + return logic.read(key); + } + + @Override + public Response create(final DynRealmTO roleTO) { + DynRealmTO created = logic.create(roleTO); + URI location = uriInfo.getAbsolutePathBuilder().path(created.getKey()).build(); + return Response.created(location). + header(RESTHeaders.RESOURCE_KEY, created.getKey()). + build(); + } + + @Override + public void update(final DynRealmTO roleTO) { + logic.update(roleTO); + } + + @Override + public void delete(final String key) { + logic.delete(key); + } + +} http://git-wip-us.apache.org/repos/asf/syncope/blob/a1bb6723/core/spring/src/main/java/org/apache/syncope/core/spring/security/AuthDataAccessor.java ---------------------------------------------------------------------- diff --git a/core/spring/src/main/java/org/apache/syncope/core/spring/security/AuthDataAccessor.java b/core/spring/src/main/java/org/apache/syncope/core/spring/security/AuthDataAccessor.java index aba2e50..79ba01b 100644 --- a/core/spring/src/main/java/org/apache/syncope/core/spring/security/AuthDataAccessor.java +++ b/core/spring/src/main/java/org/apache/syncope/core/spring/security/AuthDataAccessor.java @@ -19,7 +19,6 @@ package org.apache.syncope.core.spring.security; import com.fasterxml.jackson.core.type.TypeReference; -import java.util.Arrays; import java.util.Collections; import java.util.Date; import java.util.HashMap; @@ -29,11 +28,10 @@ import java.util.List; import java.util.Map; import java.util.Set; import javax.annotation.Resource; -import org.apache.commons.collections4.Closure; import org.apache.commons.collections4.CollectionUtils; -import org.apache.commons.collections4.IterableUtils; import org.apache.commons.collections4.SetUtils; import org.apache.commons.collections4.Transformer; +import org.apache.commons.lang3.BooleanUtils; import org.apache.commons.lang3.tuple.ImmutablePair; import org.apache.commons.lang3.tuple.Pair; import org.apache.syncope.common.lib.SyncopeConstants; @@ -65,6 +63,7 @@ import org.apache.syncope.core.provisioning.api.ConnectorFactory; import org.apache.syncope.core.provisioning.api.EntitlementsHolder; import org.apache.syncope.core.provisioning.api.MappingManager; import org.apache.syncope.core.provisioning.api.serialization.POJOHelper; +import org.apache.syncope.core.provisioning.api.utils.EntityUtils; import org.identityconnectors.framework.common.objects.Uid; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -92,6 +91,10 @@ public class AuthDataAccessor { protected static final Set<SyncopeGrantedAuthority> ANONYMOUS_AUTHORITIES = Collections.singleton(new SyncopeGrantedAuthority(StandardEntitlement.ANONYMOUS)); + protected static final String[] GROUP_OWNER_ENTITLEMENTS = new String[] { + StandardEntitlement.GROUP_READ, StandardEntitlement.GROUP_UPDATE, StandardEntitlement.GROUP_DELETE + }; + @Resource(name = "adminUser") protected String adminUser; @@ -282,39 +285,35 @@ public class AuthDataAccessor { if (user.isMustChangePassword()) { authorities.add(new SyncopeGrantedAuthority(StandardEntitlement.MUST_CHANGE_PASSWORD)); } else { - final Map<String, Set<String>> entForRealms = new HashMap<>(); - - // Give entitlements as assigned by roles (with realms, where applicable) - assigned either - // statically and dynamically - for (final Role role : userDAO.findAllRoles(user)) { - IterableUtils.forEach(role.getEntitlements(), new Closure<String>() { - - @Override - public void execute(final String entitlement) { - Set<String> realms = entForRealms.get(entitlement); - if (realms == null) { - realms = new HashSet<>(); - entForRealms.put(entitlement, realms); - } + Map<String, Set<String>> entForRealms = new HashMap<>(); + + // Give entitlements as assigned by roles (with static or dynamic realms, where applicable) - assigned + // either statically and dynamically + for (Role role : userDAO.findAllRoles(user)) { + for (String entitlement : role.getEntitlements()) { + Set<String> realms = entForRealms.get(entitlement); + if (realms == null) { + realms = new HashSet<>(); + entForRealms.put(entitlement, realms); + } + + CollectionUtils.collect(role.getRealms(), new Transformer<Realm, String>() { - CollectionUtils.collect(role.getRealms(), new Transformer<Realm, String>() { + @Override + public String transform(final Realm realm) { + return realm.getFullPath(); + } + }, realms); - @Override - public String transform(final Realm realm) { - return realm.getFullPath(); - } - }, realms); + if (!entitlement.endsWith("_CREATE") && !entitlement.endsWith("_DELETE")) { + CollectionUtils.collect(role.getDynRealms(), EntityUtils.keyTransformer(), realms); } - }); + } } // Give group entitlements for owned groups for (Group group : groupDAO.findOwnedByUser(user.getKey())) { - for (String entitlement : Arrays.asList( - StandardEntitlement.GROUP_READ, - StandardEntitlement.GROUP_UPDATE, - StandardEntitlement.GROUP_DELETE)) { - + for (String entitlement : GROUP_OWNER_ENTITLEMENTS) { Set<String> realms = entForRealms.get(entitlement); if (realms == null) { realms = new HashSet<>(); @@ -376,7 +375,7 @@ public class AuthDataAccessor { + " for JWT " + authentication.getClaims().getTokenId()); } - if (user.isSuspended() != null && user.isSuspended()) { + if (BooleanUtils.isTrue(user.isSuspended())) { throw new DisabledException("User " + user.getUsername() + " is suspended"); } @@ -385,13 +384,9 @@ public class AuthDataAccessor { throw new DisabledException("User " + user.getUsername() + " not allowed to authenticate"); } - if (user.isMustChangePassword()) { + if (BooleanUtils.isTrue(user.isMustChangePassword())) { authorities = Collections.singleton( new SyncopeGrantedAuthority(StandardEntitlement.MUST_CHANGE_PASSWORD)); - } else if (accessToken.getAuthorities() == null) { - LOG.debug("No authorities found in JWT, calculating..."); - - authorities = getUserAuthorities(user); } else { LOG.debug("Authorities found in JWT, fetching..."); @@ -400,8 +395,8 @@ public class AuthDataAccessor { ENCRYPTOR.decode(new String(accessToken.getAuthorities()), CipherAlgorithm.AES), new TypeReference<Set<SyncopeGrantedAuthority>>() { }); - } catch (Exception e) { - LOG.error("Could not read stored authorities", e); + } catch (Throwable t) { + LOG.error("Could not read stored authorities", t); authorities = Collections.emptySet(); } } http://git-wip-us.apache.org/repos/asf/syncope/blob/a1bb6723/ext/elasticsearch/client-elasticsearch/src/main/java/org/apache/syncope/ext/elasticsearch/client/ElasticsearchUtils.java ---------------------------------------------------------------------- diff --git a/ext/elasticsearch/client-elasticsearch/src/main/java/org/apache/syncope/ext/elasticsearch/client/ElasticsearchUtils.java b/ext/elasticsearch/client-elasticsearch/src/main/java/org/apache/syncope/ext/elasticsearch/client/ElasticsearchUtils.java index 47bcf9d..d9ce84f 100644 --- a/ext/elasticsearch/client-elasticsearch/src/main/java/org/apache/syncope/ext/elasticsearch/client/ElasticsearchUtils.java +++ b/ext/elasticsearch/client-elasticsearch/src/main/java/org/apache/syncope/ext/elasticsearch/client/ElasticsearchUtils.java @@ -103,7 +103,13 @@ public class ElasticsearchUtils { ? userDAO.findAllResourceKeys(any.getKey()) : any instanceof AnyObject ? anyObjectDAO.findAllResourceKeys(any.getKey()) - : any.getResourceKeys()); + : any.getResourceKeys()). + field("dynRealms", + any instanceof User + ? userDAO.findDynRealms(any.getKey()) + : any instanceof AnyObject + ? anyObjectDAO.findDynRealms(any.getKey()) + : groupDAO.findDynRealms(any.getKey())); if (any instanceof AnyObject) { AnyObject anyObject = ((AnyObject) any); http://git-wip-us.apache.org/repos/asf/syncope/blob/a1bb6723/ext/elasticsearch/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/ElasticsearchAnySearchDAO.java ---------------------------------------------------------------------- diff --git a/ext/elasticsearch/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/ElasticsearchAnySearchDAO.java b/ext/elasticsearch/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/ElasticsearchAnySearchDAO.java index 0318bc5..82b7c6f 100644 --- a/ext/elasticsearch/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/ElasticsearchAnySearchDAO.java +++ b/ext/elasticsearch/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/ElasticsearchAnySearchDAO.java @@ -18,9 +18,12 @@ */ package org.apache.syncope.core.persistence.jpa.dao; +import static org.apache.syncope.core.persistence.jpa.dao.AbstractDAO.LOG; + import java.lang.reflect.Field; import java.util.ArrayList; import java.util.Arrays; +import java.util.HashSet; import java.util.List; import java.util.Set; import org.apache.commons.collections4.CollectionUtils; @@ -34,6 +37,7 @@ import org.apache.syncope.core.persistence.api.dao.search.AnyCond; import org.apache.syncope.core.persistence.api.dao.search.AnyTypeCond; import org.apache.syncope.core.persistence.api.dao.search.AssignableCond; import org.apache.syncope.core.persistence.api.dao.search.AttributeCond; +import org.apache.syncope.core.persistence.api.dao.search.DynRealmCond; import org.apache.syncope.core.persistence.api.dao.search.MemberCond; import org.apache.syncope.core.persistence.api.dao.search.MembershipCond; import org.apache.syncope.core.persistence.api.dao.search.OrderByClause; @@ -44,6 +48,7 @@ import org.apache.syncope.core.persistence.api.dao.search.RoleCond; import org.apache.syncope.core.persistence.api.dao.search.SearchCond; import org.apache.syncope.core.persistence.api.entity.Any; import org.apache.syncope.core.persistence.api.entity.AnyUtils; +import org.apache.syncope.core.persistence.api.entity.DynRealm; 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.Realm; @@ -75,21 +80,37 @@ public class ElasticsearchAnySearchDAO extends AbstractAnySearchDAO { @Autowired private ElasticsearchUtils elasticsearchUtils; - private DisMaxQueryBuilder adminRealmsFilter(final Set<String> adminRealms) { + private Pair<DisMaxQueryBuilder, Set<String>> adminRealmsFilter(final Set<String> adminRealms) { DisMaxQueryBuilder builder = QueryBuilders.disMaxQuery(); + Set<String> dynRealmKeys = new HashSet<>(); for (String realmPath : RealmUtils.normalize(adminRealms)) { - Realm realm = realmDAO.findByFullPath(realmPath); - if (realm == null) { - LOG.warn("Ignoring invalid realm {}", realmPath); + if (realmPath.startsWith("/")) { + Realm realm = realmDAO.findByFullPath(realmPath); + if (realm == null) { + LOG.warn("Ignoring invalid realm {}", realmPath); + } else { + for (Realm descendant : realmDAO.findDescendants(realm)) { + builder.add(QueryBuilders.termQuery("realm", descendant.getFullPath())); + } + } } else { - for (Realm descendant : realmDAO.findDescendants(realm)) { - builder.add(QueryBuilders.termQuery("realm", descendant.getFullPath())); + DynRealm dynRealm = dynRealmDAO.find(realmPath); + if (dynRealm == null) { + LOG.warn("Ignoring invalid dynamic realm {}", realmPath); + } else { + dynRealmKeys.add(dynRealm.getKey()); + builder.add(QueryBuilders.termQuery("dynRealm", dynRealm.getKey())); } } } + if (!dynRealmKeys.isEmpty()) { + for (Realm descendant : realmDAO.findAll()) { + builder.add(QueryBuilders.termQuery("realm", descendant.getFullPath())); + } + } - return builder; + return Pair.of(builder, dynRealmKeys); } private SearchRequestBuilder searchRequestBuilder( @@ -97,14 +118,16 @@ public class ElasticsearchAnySearchDAO extends AbstractAnySearchDAO { final SearchCond cond, final AnyTypeKind kind) { + Pair<DisMaxQueryBuilder, Set<String>> filter = adminRealmsFilter(adminRealms); + return client.prepareSearch(AuthContextUtils.getDomain().toLowerCase()). setTypes(kind.name()). setSearchType(SearchType.QUERY_THEN_FETCH). setQuery(SyncopeConstants.FULL_ADMIN_REALMS.equals(adminRealms) ? getQueryBuilder(cond, kind) : QueryBuilders.boolQuery(). - must(adminRealmsFilter(adminRealms)). - must(getQueryBuilder(cond, kind))); + must(filter.getLeft()). + must(getQueryBuilder(buildEffectiveCond(cond, filter.getRight()), kind))); } @Override @@ -196,6 +219,8 @@ public class ElasticsearchAnySearchDAO extends AbstractAnySearchDAO { builder = getQueryBuilder(cond.getAssignableCond()); } else if (cond.getRoleCond() != null && AnyTypeKind.USER == kind) { builder = getQueryBuilder(cond.getRoleCond()); + } else if (cond.getDynRealmCond() != null) { + builder = getQueryBuilder(cond.getDynRealmCond()); } else if (cond.getMemberCond() != null && AnyTypeKind.GROUP == kind) { builder = getQueryBuilder(cond.getMemberCond()); } else if (cond.getResourceCond() != null) { @@ -286,7 +311,11 @@ public class ElasticsearchAnySearchDAO extends AbstractAnySearchDAO { } private QueryBuilder getQueryBuilder(final RoleCond cond) { - return QueryBuilders.termQuery("roles", cond.getRoleKey()); + return QueryBuilders.termQuery("roles", cond.getRole()); + } + + private QueryBuilder getQueryBuilder(final DynRealmCond cond) { + return QueryBuilders.termQuery("dynRealms", cond.getDynRealm()); } private QueryBuilder getQueryBuilder(final MemberCond cond) { http://git-wip-us.apache.org/repos/asf/syncope/blob/a1bb6723/fit/core-reference/src/test/java/org/apache/syncope/fit/AbstractITCase.java ---------------------------------------------------------------------- diff --git a/fit/core-reference/src/test/java/org/apache/syncope/fit/AbstractITCase.java b/fit/core-reference/src/test/java/org/apache/syncope/fit/AbstractITCase.java index 1ef0d32..f22a045 100644 --- a/fit/core-reference/src/test/java/org/apache/syncope/fit/AbstractITCase.java +++ b/fit/core-reference/src/test/java/org/apache/syncope/fit/AbstractITCase.java @@ -66,6 +66,7 @@ import org.apache.syncope.common.rest.api.service.CamelRouteService; import org.apache.syncope.common.rest.api.service.ConfigurationService; import org.apache.syncope.common.rest.api.service.ConnectorService; import org.apache.syncope.common.rest.api.service.DomainService; +import org.apache.syncope.common.rest.api.service.DynRealmService; import org.apache.syncope.common.rest.api.service.LoggerService; import org.apache.syncope.common.rest.api.service.NotificationService; import org.apache.syncope.common.rest.api.service.PolicyService; @@ -182,6 +183,8 @@ public abstract class AbstractITCase { protected static RoleService roleService; + protected static DynRealmService dynRealmService; + protected static UserService userService; protected static UserSelfService userSelfService; @@ -262,6 +265,7 @@ public abstract class AbstractITCase { realmService = adminClient.getService(RealmService.class); anyObjectService = adminClient.getService(AnyObjectService.class); roleService = adminClient.getService(RoleService.class); + dynRealmService = adminClient.getService(DynRealmService.class); userService = adminClient.getService(UserService.class); userSelfService = adminClient.getService(UserSelfService.class); userWorkflowService = adminClient.getService(UserWorkflowService.class); http://git-wip-us.apache.org/repos/asf/syncope/blob/a1bb6723/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 new file mode 100644 index 0000000..e773716 --- /dev/null +++ b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/DynRealmITCase.java @@ -0,0 +1,211 @@ +/* + * 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.fit.core; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; + +import javax.ws.rs.core.GenericType; +import javax.ws.rs.core.Response; +import org.apache.commons.collections4.IterableUtils; +import org.apache.commons.collections4.Predicate; +import org.apache.syncope.client.lib.SyncopeClient; +import org.apache.syncope.common.lib.SyncopeClientException; +import org.apache.syncope.common.lib.patch.AttrPatch; +import org.apache.syncope.common.lib.patch.GroupPatch; +import org.apache.syncope.common.lib.patch.StringPatchItem; +import org.apache.syncope.common.lib.patch.UserPatch; +import org.apache.syncope.common.lib.to.DynRealmTO; +import org.apache.syncope.common.lib.to.GroupTO; +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.ClientExceptionType; +import org.apache.syncope.common.lib.types.StandardEntitlement; +import org.apache.syncope.common.rest.api.beans.AnyQuery; +import org.apache.syncope.common.rest.api.service.DynRealmService; +import org.apache.syncope.common.rest.api.service.GroupService; +import org.apache.syncope.common.rest.api.service.UserService; +import org.apache.syncope.fit.AbstractITCase; +import org.junit.Test; + +public class DynRealmITCase extends AbstractITCase { + + @Test + public void misc() { + DynRealmTO dynRealm = null; + try { + dynRealm = new DynRealmTO(); + dynRealm.setKey("/name" + getUUIDString()); + dynRealm.setCond("cool==true"); + + // invalid key (starts with /) + try { + dynRealmService.create(dynRealm); + fail(); + } catch (SyncopeClientException e) { + assertEquals(ClientExceptionType.InvalidDynRealm, e.getType()); + } + dynRealm.setKey("name" + getUUIDString()); + + Response response = dynRealmService.create(dynRealm); + dynRealm = getObject(response.getLocation(), DynRealmService.class, DynRealmTO.class); + assertNotNull(dynRealm); + + PagedResult<UserTO> matching = userService.search(new AnyQuery.Builder().fiql("cool==true").build()); + assertNotNull(matching); + assertNotEquals(0, matching.getSize()); + + UserTO user = matching.getResult().get(0); + + assertTrue(user.getDynRealms().contains(dynRealm.getKey())); + } finally { + if (dynRealm != null) { + dynRealmService.delete(dynRealm.getKey()); + } + } + } + + @Test + public void delegatedAdmin() { + DynRealmTO dynRealm = null; + RoleTO role = null; + try { + // 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"); + + Response response = dynRealmService.create(dynRealm); + dynRealm = getObject(response.getLocation(), DynRealmService.class, DynRealmTO.class); + assertNotNull(dynRealm); + + // 2. create role for such dynamic realm + role = new RoleTO(); + role.setKey("Administer LDAP" + getUUIDString()); + role.getEntitlements().add(StandardEntitlement.USER_SEARCH); + role.getEntitlements().add(StandardEntitlement.USER_READ); + role.getEntitlements().add(StandardEntitlement.USER_UPDATE); + role.getEntitlements().add(StandardEntitlement.GROUP_READ); + role.getEntitlements().add(StandardEntitlement.GROUP_UPDATE); + role.getDynRealms().add(dynRealm.getKey()); + + role = createRole(role); + assertNotNull(role); + + // 3. assign the new role to vivaldi + UserPatch userPatch = new UserPatch(); + userPatch.setKey("b3cbc78d-32e6-4bd4-92e0-bbe07566a2ee"); + userPatch.getRoles().add(new StringPatchItem.Builder().value(role.getKey()).build()); + UserTO vivaldi = updateUser(userPatch).getEntity(); + assertNotNull(vivaldi); + assertTrue(vivaldi.getRoles().contains(role.getKey())); + + // 4. create new user and group, assign resource-ldap + UserTO user = UserITCase.getUniqueSampleTO("dynrealmu...@apache.org"); + user.setRealm("/even/two"); + user.getResources().clear(); + user.getResources().add(RESOURCE_NAME_LDAP); + user = createUser(user).getEntity(); + assertNotNull(user); + final String userKey = user.getKey(); + + GroupTO group = GroupITCase.getSampleTO("dynRealmGroup"); + group.setRealm("/odd"); + group.getResources().clear(); + group.getResources().add(RESOURCE_NAME_LDAP); + group = createGroup(group).getEntity(); + assertNotNull(group); + final String groupKey = group.getKey(); + + // 5. verify that the new user and group are found when searching by dynamic realm + PagedResult<UserTO> matchingUsers = userService.search(new AnyQuery.Builder().realm("/").fiql( + SyncopeClient.getUserSearchConditionBuilder().inDynRealms(dynRealm.getKey()).query()).build()); + assertTrue(IterableUtils.matchesAny(matchingUsers.getResult(), new Predicate<UserTO>() { + + @Override + public boolean evaluate(final UserTO object) { + return object.getKey().equals(userKey); + } + })); + PagedResult<GroupTO> matchingGroups = groupService.search(new AnyQuery.Builder().realm("/").fiql( + SyncopeClient.getGroupSearchConditionBuilder().inDynRealms(dynRealm.getKey()).query()).build()); + assertTrue(IterableUtils.matchesAny(matchingGroups.getResult(), new Predicate<GroupTO>() { + + @Override + public boolean evaluate(final GroupTO object) { + return object.getKey().equals(groupKey); + } + })); + + // 6. perpare to act as vivaldi + SyncopeClient vivaldiClient = clientFactory.create("vivaldi", ADMIN_PWD); + UserService vivaldiUserService = vivaldiClient.getService(UserService.class); + GroupService vivaldiGroupService = vivaldiClient.getService(GroupService.class); + + // 7. verify delegated administration + // USER_READ + assertNotNull(vivaldiUserService.read(userKey)); + + // GROUP_READ + assertNotNull(vivaldiGroupService.read(groupKey)); + + // USER_SEARCH + matchingUsers = vivaldiUserService.search(new AnyQuery.Builder().realm("/").build()); + assertTrue(IterableUtils.matchesAny(matchingUsers.getResult(), new Predicate<UserTO>() { + + @Override + public boolean evaluate(final UserTO object) { + return object.getKey().equals(userKey); + } + })); + + // USER_UPDATE + userPatch = new UserPatch(); + userPatch.setKey(userKey); + userPatch.getResources().add(new StringPatchItem.Builder().value(RESOURCE_NAME_NOPROPAGATION).build()); + user = vivaldiUserService.update(userPatch). + readEntity(new GenericType<ProvisioningResult<UserTO>>() { + }).getEntity(); + assertNotNull(user); + assertTrue(user.getResources().contains(RESOURCE_NAME_NOPROPAGATION)); + + // GROUP_UPDATE + GroupPatch groupPatch = new GroupPatch(); + groupPatch.setKey(groupKey); + groupPatch.getPlainAttrs().add(new AttrPatch.Builder().attrTO(attrTO("icon", "modified")).build()); + group = vivaldiGroupService.update(groupPatch). + readEntity(new GenericType<ProvisioningResult<GroupTO>>() { + }).getEntity(); + assertNotNull(group); + assertEquals("modified", group.getPlainAttrMap().get("icon").getValues().get(0)); + } finally { + if (role != null) { + roleService.delete(role.getKey()); + } + if (dynRealm != null) { + dynRealmService.delete(dynRealm.getKey()); + } + } + } +} http://git-wip-us.apache.org/repos/asf/syncope/blob/a1bb6723/fit/core-reference/src/test/java/org/apache/syncope/fit/core/ExceptionMapperITCase.java ---------------------------------------------------------------------- diff --git a/fit/core-reference/src/test/java/org/apache/syncope/fit/core/ExceptionMapperITCase.java b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/ExceptionMapperITCase.java index 8a8e6a2..9db9031 100644 --- a/fit/core-reference/src/test/java/org/apache/syncope/fit/core/ExceptionMapperITCase.java +++ b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/ExceptionMapperITCase.java @@ -125,7 +125,7 @@ public class ExceptionMapperITCase extends AbstractITCase { fail(); } catch (Exception e) { String message = ERROR_MESSAGES.getProperty("errMessage.UniqueConstraintViolation"); - assertEquals(e.getMessage(), "DataIntegrityViolation [" + message + "]"); + assertEquals("EntityExists [" + message + "]", e.getMessage()); } }