diff --git a/modules/core/test/com/haulmont/cuba/security/ParentClassConstraintTest.java b/modules/core/test/com/haulmont/cuba/security/ParentClassConstraintTest.java new file mode 100644 index 0000000000..82124a0b05 --- /dev/null +++ b/modules/core/test/com/haulmont/cuba/security/ParentClassConstraintTest.java @@ -0,0 +1,231 @@ +/* + * Copyright (c) 2008-2017 Haulmont. + * + * Licensed 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 com.haulmont.cuba.security; + +import com.haulmont.cuba.core.EntityManager; +import com.haulmont.cuba.core.Transaction; +import com.haulmont.cuba.core.global.*; +import com.haulmont.cuba.security.app.LoginWorker; +import com.haulmont.cuba.security.entity.*; +import com.haulmont.cuba.security.global.LoginException; +import com.haulmont.cuba.security.global.UserSession; +import com.haulmont.cuba.testsupport.TestContainer; +import com.haulmont.cuba.testsupport.TestUserSessionSource; +import org.junit.*; + +import java.util.ArrayList; +import java.util.List; +import java.util.Locale; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; + +public class ParentClassConstraintTest { + + @ClassRule + public static TestContainer cont = TestContainer.Common.INSTANCE; + + private Group parentGroup; + private Group constraintGroup1, constraintGroup2, constraintGroup3; + private Constraint constraint1, constraint2, constraint3, constraint4; + private User constraintUser1, constraintUser2, constraintUser3; + private SearchFolder searchFolder1, searchFolder2; + private PasswordEncryption passwordEncryption; + + private static final String PASSWORD = "1"; + + @Before + public void setUp() { + passwordEncryption = AppBeans.get(PasswordEncryption.class); + + Transaction tx = cont.persistence().createTransaction(); + try { + EntityManager em = cont.persistence().getEntityManager(); + + parentGroup = new Group(); + parentGroup.setName("parentGroup"); + em.persist(parentGroup); + + constraintGroup1 = new Group(); + constraintGroup1.setName("constraintGroup1"); + em.persist(constraintGroup1); + + constraint1 = new Constraint(); + constraint1.setEntityName("sys$Folder"); + constraint1.setCheckType(ConstraintCheckType.DATABASE); + constraint1.setOperationType(ConstraintOperationType.READ); + constraint1.setWhereClause("{E}.name = 'folder1'"); + constraint1.setGroup(constraintGroup1); + em.persist(constraint1); + + constraintGroup2 = new Group(); + constraintGroup2.setName("constraintGroup2"); + em.persist(constraintGroup2); + + constraint2 = new Constraint(); + constraint2.setEntityName("sys$Folder"); + constraint2.setCheckType(ConstraintCheckType.DATABASE); + constraint2.setOperationType(ConstraintOperationType.READ); + constraint2.setWhereClause("{E}.name = 'folder1'"); + constraint2.setGroup(constraintGroup2); + em.persist(constraint2); + + constraint3 = new Constraint(); + constraint3.setEntityName("sec$SearchFolder"); + constraint3.setCheckType(ConstraintCheckType.DATABASE); + constraint3.setOperationType(ConstraintOperationType.READ); + constraint3.setWhereClause("{E}.name = 'folder2'"); + constraint3.setGroup(constraintGroup2); + em.persist(constraint3); + + constraintGroup3 = new Group(); + constraintGroup3.setName("constraintGroup3"); + em.persist(constraintGroup3); + + constraint4 = new Constraint(); + constraint4.setEntityName("sys$StandardEntity"); + constraint4.setCheckType(ConstraintCheckType.DATABASE); + constraint4.setOperationType(ConstraintOperationType.READ); + constraint4.setWhereClause("{E}.createTs is null"); + constraint4.setGroup(constraintGroup3); + em.persist(constraint4); + + constraintUser1 = new User(); + constraintUser1.setLogin("constraintUser1"); + constraintUser1.setPassword(passwordEncryption.getPasswordHash(constraintUser1.getId(), PASSWORD)); + constraintUser1.setGroup(constraintGroup1); + em.persist(constraintUser1); + + constraintUser2 = new User(); + constraintUser2.setLogin("constraintUser2"); + constraintUser2.setPassword(passwordEncryption.getPasswordHash(constraintUser2.getId(), PASSWORD)); + constraintUser2.setGroup(constraintGroup2); + em.persist(constraintUser2); + + constraintUser3 = new User(); + constraintUser3.setLogin("constraintUser3"); + constraintUser3.setPassword(passwordEncryption.getPasswordHash(constraintUser3.getId(), PASSWORD)); + constraintUser3.setGroup(constraintGroup3); + em.persist(constraintUser3); + + searchFolder1 = new SearchFolder(); + searchFolder1.setName("folder1"); + em.persist(searchFolder1); + + searchFolder2 = new SearchFolder(); + searchFolder2.setName("folder2"); + em.persist(searchFolder2); + + tx.commit(); + } finally { + tx.end(); + } + } + + @Test + public void testConstraintsOnParentClass() throws LoginException { + DataManager dataManager = AppBeans.get(DataManager.NAME); + LoadContext loadContext = new LoadContext<>(SearchFolder.class).setView(View.LOCAL); + loadContext.setQueryString("select f from sec$SearchFolder f"); + List resultList = dataManager.loadList(loadContext); + assertEquals(2, resultList.size()); + + LoginWorker lw = AppBeans.get(LoginWorker.NAME); + + UserSession userSession = lw.login("constraintUser1", passwordEncryption.getPlainHash(PASSWORD), Locale.getDefault()); + assertNotNull(userSession); + + UserSessionSource uss = AppBeans.get(UserSessionSource.class); + UserSession savedUserSession = uss.getUserSession(); + ((TestUserSessionSource) uss).setUserSession(userSession); + try { + dataManager = AppBeans.get(DataManager.NAME); + dataManager = dataManager.secure(); + loadContext = new LoadContext<>(SearchFolder.class).setView(View.LOCAL); + loadContext.setQueryString("select f from sec$SearchFolder f"); + resultList = dataManager.loadList(loadContext); + assertEquals(1, resultList.size()); + } finally { + ((TestUserSessionSource) uss).setUserSession(savedUserSession); + } + } + + @Test + public void testConstraintsOnParentAndCurrentMetaClass() throws LoginException { + DataManager dataManager = AppBeans.get(DataManager.NAME); + LoadContext loadContext = new LoadContext<>(SearchFolder.class).setView(View.LOCAL); + loadContext.setQueryString("select f from sec$SearchFolder f"); + List resultList = dataManager.loadList(loadContext); + assertEquals(2, resultList.size()); + + LoginWorker lw = AppBeans.get(LoginWorker.NAME); + + UserSession userSession = lw.login("constraintUser2", passwordEncryption.getPlainHash(PASSWORD), Locale.getDefault()); + assertNotNull(userSession); + + UserSessionSource uss = AppBeans.get(UserSessionSource.class); + UserSession savedUserSession = uss.getUserSession(); + ((TestUserSessionSource) uss).setUserSession(userSession); + try { + dataManager = AppBeans.get(DataManager.NAME); + dataManager = dataManager.secure(); + loadContext = new LoadContext<>(SearchFolder.class).setView(View.LOCAL); + loadContext.setQueryString("select f from sec$SearchFolder f"); + resultList = dataManager.loadList(loadContext); + assertEquals(0, resultList.size()); + } finally { + ((TestUserSessionSource) uss).setUserSession(savedUserSession); + } + } + + @Test + public void testConstraintsOnMappedSuperClass() throws LoginException { + DataManager dataManager = AppBeans.get(DataManager.NAME); + LoadContext loadContext = new LoadContext<>(SearchFolder.class).setView(View.LOCAL); + loadContext.setQueryString("select f from sec$SearchFolder f"); + List resultList = dataManager.loadList(loadContext); + assertEquals(2, resultList.size()); + + LoginWorker lw = AppBeans.get(LoginWorker.NAME); + + UserSession userSession = lw.login("constraintUser3", passwordEncryption.getPlainHash(PASSWORD), Locale.getDefault()); + assertNotNull(userSession); + + UserSessionSource uss = AppBeans.get(UserSessionSource.class); + UserSession savedUserSession = uss.getUserSession(); + ((TestUserSessionSource) uss).setUserSession(userSession); + try { + dataManager = AppBeans.get(DataManager.NAME); + dataManager = dataManager.secure(); + loadContext = new LoadContext<>(SearchFolder.class).setView(View.LOCAL); + loadContext.setQueryString("select f from sec$SearchFolder f"); + resultList = dataManager.loadList(loadContext); + assertEquals(0, resultList.size()); + } finally { + ((TestUserSessionSource) uss).setUserSession(savedUserSession); + } + } + + @After + public void tearDown() throws Exception { + cont.deleteRecord("SEC_USER", constraintUser1.getId(), constraintUser2.getId(), constraintUser3.getId()); + cont.deleteRecord("SEC_CONSTRAINT", constraint1.getId(), constraint2.getId(), constraint3.getId(), constraint4.getId()); + cont.deleteRecord("SEC_GROUP", parentGroup.getId(), constraintGroup1.getId(), constraintGroup2.getId(), constraintGroup3.getId()); + cont.deleteRecord("SEC_SEARCH_FOLDER", "FOLDER_ID", searchFolder1.getId(), searchFolder2.getId()); + cont.deleteRecord("SYS_FOLDER", searchFolder1.getId(), searchFolder2.getId()); + } +} diff --git a/modules/global/src/com/haulmont/cuba/core/sys/SecurityImpl.java b/modules/global/src/com/haulmont/cuba/core/sys/SecurityImpl.java index 9a89ce5a76..06f2c15021 100644 --- a/modules/global/src/com/haulmont/cuba/core/sys/SecurityImpl.java +++ b/modules/global/src/com/haulmont/cuba/core/sys/SecurityImpl.java @@ -39,6 +39,7 @@ import javax.inject.Inject; import java.text.ParseException; import java.util.*; import java.util.function.Predicate; +import java.util.stream.Collectors; import static com.haulmont.cuba.security.entity.ConstraintOperationType.ALL; import static com.haulmont.cuba.security.entity.ConstraintOperationType.CUSTOM; @@ -171,26 +172,35 @@ public class SecurityImpl implements Security { @Override public boolean hasConstraints(MetaClass metaClass) { - UserSession userSession = userSessionSource.getUserSession(); - String mainMetaClassName = extendedEntities.getOriginalOrThisMetaClass(metaClass).getName(); - return userSession.hasConstraints(mainMetaClassName); + List constraints = getConstraints(metaClass); + return !constraints.isEmpty(); } @Override public boolean hasInMemoryConstraints(MetaClass metaClass, ConstraintOperationType... operationTypes) { - UserSession userSession = userSessionSource.getUserSession(); - String mainMetaClassName = extendedEntities.getOriginalOrThisMetaClass(metaClass).getName(); - List constraints = userSession.getConstraints(mainMetaClassName, constraint -> - constraint.getCheckType().memory() && constraint.getOperationType() != null - && Arrays.asList(operationTypes).contains(constraint.getOperationType()) + List constraints = getConstraints(metaClass, constraint -> + constraint.getCheckType().memory() && constraint.getOperationType() != null + && Arrays.asList(operationTypes).contains(constraint.getOperationType()) ); return !constraints.isEmpty(); } protected List getConstraints(MetaClass metaClass, Predicate predicate) { + return getConstraints(metaClass).stream() + .filter(predicate) + .collect(Collectors.toList()); + } + + protected List getConstraints(MetaClass metaClass) { UserSession userSession = userSessionSource.getUserSession(); - String mainMetaClassName = extendedEntities.getOriginalOrThisMetaClass(metaClass).getName(); - return userSession.getConstraints(mainMetaClassName, predicate); + MetaClass mainMetaClass = extendedEntities.getOriginalOrThisMetaClass(metaClass); + + List constraints = new ArrayList<>(); + constraints.addAll(userSession.getConstraints(mainMetaClass.getName())); + for (MetaClass parent : mainMetaClass.getAncestors()) { + constraints.addAll(userSession.getConstraints(parent.getName())); + } + return constraints; } protected boolean isPermitted(Entity entity, Predicate predicate) { @@ -216,7 +226,7 @@ public class SecurityImpl implements Security { } } catch (Exception e) { log.error("An error occurred while applying constraint's Groovy script. The entity has been filtered out." + - "Entity class [{}]. Entity [{}].", metaClassName, entity.getId(), e); + "Entity class [{}]. Entity [{}].", metaClassName, entity.getId(), e); return false; } } @@ -236,7 +246,7 @@ public class SecurityImpl implements Security { /** * Override if you need specific context variables in Groovy constraints. * - * @param context passed to Groovy evaluator + * @param context passed to Groovy evaluator */ protected void fillGroovyConstraintsContext(Map context) { } diff --git a/modules/gui/src/com/haulmont/cuba/gui/app/security/constraint/edit/ConstraintEditor.java b/modules/gui/src/com/haulmont/cuba/gui/app/security/constraint/edit/ConstraintEditor.java index 2a0f9b8d83..3f967c9c21 100644 --- a/modules/gui/src/com/haulmont/cuba/gui/app/security/constraint/edit/ConstraintEditor.java +++ b/modules/gui/src/com/haulmont/cuba/gui/app/security/constraint/edit/ConstraintEditor.java @@ -20,6 +20,7 @@ package com.haulmont.cuba.gui.app.security.constraint.edit; import com.google.common.base.Strings; import com.haulmont.bali.util.Dom4j; import com.haulmont.chile.core.model.MetaClass; +import com.haulmont.cuba.core.entity.BaseGenericIdEntity; import com.haulmont.cuba.core.global.*; import com.haulmont.cuba.core.global.filter.GroovyGenerator; import com.haulmont.cuba.core.global.filter.SecurityJpqlGenerator; @@ -131,8 +132,8 @@ public class ConstraintEditor extends AbstractEditor { Map options = new TreeMap<>(); MessageTools messageTools = AppBeans.get(MessageTools.NAME); entities = new HashMap<>(); - for (MetaClass metaClass : metadata.getTools().getAllPersistentMetaClasses()) { - if (extendedEntities.getExtendedClass(metaClass) == null) { + for (MetaClass metaClass : metadata.getSession().getClasses()) { + if (extendedEntities.getExtendedClass(metaClass) == null && BaseGenericIdEntity.class.isAssignableFrom(metaClass.getJavaClass())) { MetaClass mainMetaClass = extendedEntities.getOriginalOrThisMetaClass(metaClass); String originalName = mainMetaClass.getName(); options.put(messageTools.getEntityCaption(metaClass) + " (" + metaClass.getName() + ")", originalName);