This is an internal documentation. There is a good chance you’re looking for something else. See Disclaimer.
Tests¶
The easiest way to test the ACL rules of a module is by extending
AbstractModuleAclTest. This base test class automatically sets the
entity.acl
file of the current module as initial policy for the tests.
Dependencies and plugins in pom.xml¶
Add the dependency nice2-persist-security-testlib
and the test plugin as follows in the file
{NICE_MODULE_PATH}/module/pom.xml
(so for example for the optional module correspondence this would be the file
optional/correspondence/module/pom.xml
).
Required dependency nice2-persist-security-testlib
:
<dependency>
<groupId>ch.tocco.nice2.persist.security</groupId>
<artifactId>nice2-persist-security-testlib</artifactId>
<version>${project.version}</version>
<scope>test</scope>
</dependency>
Add test plugin:
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<executions>
<execution>
<phase>test</phase>
<goals>
<goal>testCompile</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<executions>
<execution>
<phase>test</phase>
<goals>
<goal>test</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
<build>
Directory structure¶
The test classes should be located in the module of that particular Nice module.
Directory path: src/test/java
See the following example for the nice2-optional-correspondence module:
Test methods¶
Among others, AbstractModuleAclTest provides the following useful methods:
baseTestCase(ExpectedAccess expectedAccess, Principal principal, Callable<Entity> dataCreator, String… fields)
baseTestCase(List<String> aclFileNames, ExpectedAccess expectedAccess, Principal principal, Callable<Entity> dataCreator, String… fields)
baseTestCase(ExpectedAccess expectedAccess, Principal principal, Entity entity, String… fields)
baseTestCase(List<String> aclFileNames, ExpectedAccess expectedAccess, Principal principal, Entity entity, String… fields)
Simple example¶
Have a look at this simple example from the nice2-optional-correspondence module which tests the following ACL declaration:
entity(Serial_correspondence):
grant access(read) except &anonymous if relSender.pk == principal.user_id
or relCorrespondence.relUser.pk == principal.user_id;
grant access, delete to correspondencemanager;
grant access(read) to correspondenceguest;
entity(Correspondence):
grant access(read) except &anonymous if relSerial_correspondence.relSender.pk == principal.user_id
or relUser.pk == principal.user_id;
grant delete to correspondencemanager;
grant access(read) to correspondencemanager, correspondenceguest;
AclDataModel.java:
package ch.tocco.nice2.optional.correspondence.impl;
import ch.tocco.nice2.persist.core.test.inject.model.AbstractDataModel;
import ch.tocco.nice2.persist.core.test.inject.model.EntityModelMock;
import ch.tocco.nice2.textresources.api.i18n.L10N;
import ch.tocco.nice2.types.api.TypeManager;
public class AclDataModel extends AbstractDataModel {
protected AclDataModel(TypeManager typeManager, L10N l10n) {
super(typeManager, l10n);
EntityModelMock correspondence = entityModel("Correspondence")
.with(standardPk());
EntityModelMock serialCorrespondence = entityModel("Serial_correspondence")
.with(standardPk());
EntityModelMock user = entityModel("User")
.with(standardPk());
correspondence.manyToOne(serialCorrespondence).required();
serialCorrespondence.manyToOne("relSender", user, "relSender_serial_correspondence");
correspondence.manyToMany(user);
commit();
}
}
AbstractCorrespondenceAclTest.java:
package ch.tocco.nice2.optional.correspondence.impl;
import ch.tocco.nice2.persist.security.test.AbstractModuleAclTest;
public abstract class AbstractCorrespondenceAclTest extends AbstractModuleAclTest {
@Override
protected void bindDataModel() {
bindDataModel(AclDataModel.class);
}
}
CorrespondenceAclTest.java:
package ch.tocco.nice2.optional.correspondence.impl;
import jakarta.annotation.Resource;
import org.testng.annotations.DataProvider;
import org.testng.annotations.Test;
import ch.tocco.nice2.persist.core.api.Context;
import ch.tocco.nice2.persist.core.api.entity.Entity;
import ch.tocco.nice2.model.entity.api.PrimaryKey;
import ch.tocco.nice2.persist.core.test.EntityBuilder;
import ch.tocco.nice2.security.api.Principal;
public class CorrespondenceAclTest extends AbstractCorrespondenceAclTest {
@Resource
private Context context;
@Test(dataProvider = "rolePermissions")
public void testRolePermissions(Principal principal,
ExpectedAccess expectedSerialCorrespondenceAccess,
ExpectedAccess expectedCorrespondenceAccess) throws Exception {
Entity correspondence = context.tx().invokeRTE(() ->
entity("Correspondence")
.setRelatedEntity("relSerial_correspondence", entity("Serial_correspondence").get())
.get());
Entity serialCorrespondence = correspondence.getRelatedEntity("relSerial_correspondence");
baseTestCase(expectedSerialCorrespondenceAccess, principal, serialCorrespondence);
baseTestCase(expectedCorrespondenceAccess, principal, correspondence);
}
@DataProvider
public Object[][] rolePermissions() {
return new Object[][]{
{
anonymousPrincipal(),
ExpectedAccess.none(),
ExpectedAccess.none()
},
{
principal("manager").role(role("correspondencemanager")).get(),
ExpectedAccess.readWrite().withDelete(true),
ExpectedAccess.read().withDelete(true)
},
{
principal("guest").role(role("correspondenceguest")).get(),
ExpectedAccess.read(),
ExpectedAccess.read()
}
};
}
@Test(dataProvider = "linkedSenderPermissions")
public void testLinkedSenderPermissions(boolean principalIsLinkedSender,
ExpectedAccess expectedSerialCorrespondenceAccess,
ExpectedAccess expectedCorrespondenceAccess) throws Exception {
Entity correspondence = context.tx().invokeRTE(() ->
entity("Correspondence")
.setRelatedEntity("relSerial_correspondence", entity("Serial_correspondence").get())
.addRelatedEntity("relUser", entity("User").get())
.get());
Entity serialCorrespondence = correspondence.getRelatedEntity("relSerial_correspondence");
Entity user = correspondence.resolve("relUser").uniqueResult();
PrimaryKey userId = principalIsLinkedSender ? user.requireKey() : null;
Principal principal = principal("test", userId).get();
baseTestCase(expectedSerialCorrespondenceAccess, principal, serialCorrespondence);
baseTestCase(expectedCorrespondenceAccess, principal, correspondence);
}
@DataProvider
public Object[][] linkedSenderPermissions() {
return new Object[][]{
{
false,
ExpectedAccess.none(),
ExpectedAccess.none()
},
{
true,
ExpectedAccess.read(),
ExpectedAccess.read()
},
};
}
@Test(dataProvider = "linkedReceiverPermissions")
public void testLinkedReceiverPermissions(boolean principalIsLinkedSender,
ExpectedAccess expectedSerialCorrespondenceAccess,
ExpectedAccess expectedCorrespondenceAccess) throws Exception {
Entity correspondence = context.tx().invokeRTE(() ->
entity("Correspondence")
.setRelatedEntity(
"relSerial_correspondence",
entity("Serial_correspondence")
.setRelatedEntity("relSender", entity("User").get()).get()
)
.get());
Entity serialCorrespondence = correspondence.getRelatedEntity("relSerial_correspondence");
Entity sender = serialCorrespondence.getRelatedEntityOrNull("relSender");
PrimaryKey userId = principalIsLinkedSender ? sender.requireKey() : null;
Principal principal = principal("test", userId).get();
baseTestCase(expectedSerialCorrespondenceAccess, principal, serialCorrespondence);
baseTestCase(expectedCorrespondenceAccess, principal, correspondence);
}
@DataProvider
public Object[][] linkedReceiverPermissions() {
return new Object[][]{
{
false,
ExpectedAccess.none(),
ExpectedAccess.none()
},
{
true,
ExpectedAccess.read(),
ExpectedAccess.read()
},
};
}
private EntityBuilder entity(String model) {
return new EntityBuilder(context, model);
}
}
Test ACL files from multiple modules¶
This section shows an example with ACL rules from multiple modules. In our example here, the rules we need for our test are located in both the dms module and in the optional/cms module.
Let’s write tests for the following ACL rule from nice optional-cms module:
entityPath(Domain, label):
deny access(write) except redactor if relDomain_type.unique_id == "web";
Since the optional/cms module depends heavily on the dms module, it makes no sense to test the ACL
rules of the optional/cms module independently. We have to take the following steps to also include entity.acl
file from the dms module in our test in the optional/cms module (besides the entity.acl
from the
optional/cms module).
First, we have to make the ACL files from the dms module available in the optional/cms module. To do this, we have to move the ACL files into a uniquely named directory within the acl directory. We call this directory “acl”.
Now, the dms module structure looks like this:
Second, we have to create a separate JAR file containing just the ACL files from the dms module which we then include in the optional/cms module.
dms/module/pom.xml:
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<version>3.0.2</version>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>jar</goal>
</goals>
<configuration>
<classifier>acl</classifier>
<includes>
<include>acl/**</include>
</includes>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
optional/cms/module/pom.xml:
<dependency>
<groupId>ch.tocco.nice2.dms</groupId>
<artifactId>nice2-dms-module</artifactId>
<classifier>acl</classifier>
<version>${project.version}</version>
<scope>test</scope>
</dependency>
After that, we can write our tests in the optional/cms module and include the entity.acl
file from the dms
module by calling baseTestCase(...)
with a list containing the path "/acl/dms/entity.acl"
. This file is
now included besides the entity.acl file from the optional/cms module.
package ch.tocco.nice2.optional.cms.acl.edit_alias;
import java.util.concurrent.Callable;
import org.testng.annotations.DataProvider;
import org.testng.annotations.Test;
import com.google.common.collect.ImmutableList;
import org.jetbrains.annotations.Nullable;
import ch.tocco.nice2.optional.cms.acl.AbstractCmsAclTest;
import ch.tocco.nice2.persist.core.api.entity.Entity;
import ch.tocco.nice2.persist.core.test.EntityBuilder;
import ch.tocco.nice2.persist.security.test.AbstractCheckPermissionsTest;
import ch.tocco.nice2.persist.security.test.AbstractModuleAclTest;
import ch.tocco.nice2.security.api.Principal;
public class DomainEditAliasTest extends AbstractModuleAclTest {
@Test(dataProvider = "testCases")
public void testPermissions(AbstractCheckPermissionsTest.ExpectedAccess expectedAccess,
Principal principal,
Callable<Entity> dataCreator,
@Nullable String[] fields) throws Exception {
baseTestCase(ImmutableList.of("/acl/dms/entity.acl"), expectedAccess, principal, dataCreator, fields);
}
@DataProvider
public Object[][] testCases() {
return new Object[][]{
// no write access for anonymous
{
new AbstractCheckPermissionsTest.ExpectedAccess().withWrite(false),
anonymousPrincipal(),
(Callable<Entity>) () -> new EntityBuilder(context, "Domain")
.field("label", "test")
.get(),
new String[]{"alias"}
},
// ...
};
}
}