package com.constellio.app.api.cmis.rm;
import static com.constellio.app.api.cmis.builders.object.AclBuilder.CMIS_READ;
import static com.constellio.app.modules.rm.constants.RMPermissionsTo.MANAGE_DOCUMENT_AUTHORIZATIONS;
import static com.constellio.app.modules.rm.constants.RMPermissionsTo.MANAGE_FOLDER_AUTHORIZATIONS;
import static com.constellio.app.modules.rm.constants.RMPermissionsTo.SHARE_DOCUMENT;
import static com.constellio.app.modules.rm.constants.RMPermissionsTo.SHARE_FOLDER;
import static com.constellio.model.entities.CorePermissions.MANAGE_SECURITY;
import static com.constellio.model.entities.security.global.AuthorizationAddRequest.authorizationForUsers;
import static java.util.Arrays.asList;
import static junit.framework.Assert.fail;
import static org.apache.chemistry.opencmis.commons.enums.AclPropagation.REPOSITORYDETERMINED;
import static org.apache.chemistry.opencmis.commons.enums.Action.CAN_APPLY_ACL;
import static org.apache.chemistry.opencmis.commons.enums.Action.CAN_GET_ACL;
import static org.apache.chemistry.opencmis.commons.enums.Action.CAN_GET_CHILDREN;
import static org.apache.chemistry.opencmis.commons.enums.Action.CAN_GET_PROPERTIES;
import static org.assertj.core.api.Assertions.assertThat;
import java.util.ArrayList;
import java.util.List;
import org.apache.chemistry.opencmis.client.api.Folder;
import org.apache.chemistry.opencmis.client.api.Session;
import org.apache.chemistry.opencmis.commons.data.Ace;
import org.apache.chemistry.opencmis.commons.enums.Action;
import org.assertj.core.api.IterableAssert;
import org.junit.Before;
import org.junit.Test;
import com.constellio.app.api.cmis.accept.CmisAcceptanceTestSetup;
import com.constellio.app.modules.rm.RMTestRecords;
import com.constellio.app.modules.rm.constants.RMPermissionsTo;
import com.constellio.app.modules.rm.constants.RMRoles;
import com.constellio.model.entities.Taxonomy;
import com.constellio.model.entities.records.Record;
import com.constellio.model.entities.records.wrappers.User;
import com.constellio.model.entities.security.Role;
import com.constellio.model.services.migrations.ConstellioEIMConfigs;
import com.constellio.model.services.records.RecordServices;
import com.constellio.model.services.security.AuthorizationsServices;
import com.constellio.model.services.security.roles.RolesManager;
import com.constellio.model.services.taxonomies.ConceptNodesTaxonomySearchServices;
import com.constellio.model.services.taxonomies.TaxonomiesManager;
import com.constellio.model.services.taxonomies.TaxonomiesSearchOptions;
import com.constellio.model.services.taxonomies.TaxonomiesSearchServices;
import com.constellio.model.services.taxonomies.TaxonomySearchRecord;
import com.constellio.sdk.tests.ConstellioTest;
import com.constellio.sdk.tests.annotations.DriverTest;
import com.constellio.sdk.tests.setups.Users;
@DriverTest
public class RMCmisAllowableActionsAcceptanceTest extends ConstellioTest {
AuthorizationsServices authServices;
RecordServices recordServices;
Users users = new Users();
Session session;
RMTestRecords records = new RMTestRecords(zeCollection);
@Before
public void setUp()
throws Exception {
prepareSystem(withZeCollection().withConstellioRMModule().withAllTest(users)
.withRMTest(records).withFoldersAndContainersOfEveryStatus().withDocumentsHavingContent());
RolesManager rolesManager = getModelLayerFactory().getRolesManager();
rolesManager.addRole(new Role(zeCollection, "r1", asList(MANAGE_SECURITY)));
rolesManager.addRole(new Role(zeCollection, "r2", asList(MANAGE_FOLDER_AUTHORIZATIONS)));
rolesManager.addRole(new Role(zeCollection, "r3", asList(SHARE_FOLDER)));
rolesManager.addRole(new Role(zeCollection, "r4", asList(MANAGE_DOCUMENT_AUTHORIZATIONS)));
rolesManager.addRole(new Role(zeCollection, "r5", asList(SHARE_DOCUMENT)));
Role managerRole = rolesManager.getRole(zeCollection, RMRoles.MANAGER);
List<String> managerPermissions = new ArrayList<>(managerRole.getOperationPermissions());
managerPermissions.remove(RMPermissionsTo.MANAGE_FOLDER_AUTHORIZATIONS);
managerPermissions.remove(RMPermissionsTo.MANAGE_DOCUMENT_AUTHORIZATIONS);
rolesManager.updateRole(managerRole.withPermissions(managerPermissions));
recordServices = getModelLayerFactory().newRecordServices();
authServices = getModelLayerFactory().newAuthorizationsServices();
users.setUp(getModelLayerFactory().newUserServices());
recordServices.update(users.adminIn(zeCollection).setCollectionAllAccess(true));
recordServices.update(users.aliceIn(zeCollection).setCollectionReadAccess(true));
recordServices.update(users.sasquatchIn(zeCollection).setCollectionAllAccess(true).setUserRoles("r2"));
recordServices.update(users.bobIn(zeCollection).setCollectionAllAccess(true).setUserRoles("r3"));
recordServices.update(users.robinIn(zeCollection).setCollectionAllAccess(true).setUserRoles("r4"));
recordServices.update(users.chuckNorrisIn(zeCollection).setCollectionAllAccess(true).setUserRoles("r1"));
recordServices.update(users.gandalfIn(zeCollection).setCollectionReadAccess(true).setCollectionWriteAccess(true));
givenConfig(ConstellioEIMConfigs.CMIS_NEVER_RETURN_ACL, false);
CmisAcceptanceTestSetup.giveUseCMISPermissionToUsers(getModelLayerFactory());
}
@Test
public void whenGetAllowableActionsOfRootThenOK()
throws Exception {
session = newCMISSessionAsUserInZeCollection(admin);
assertThatAllowableActionsOf("/").containsOnly(CAN_GET_PROPERTIES, CAN_GET_CHILDREN);
session = newCMISSessionAsUserInZeCollection(aliceWonderland);
assertThatAllowableActionsOf("/").containsOnly(CAN_GET_PROPERTIES, CAN_GET_CHILDREN);
session = newCMISSessionAsUserInZeCollection(bobGratton);
assertThatAllowableActionsOf("/").containsOnly(CAN_GET_PROPERTIES, CAN_GET_CHILDREN);
}
@Test
public void whenGetAllowableActionsOfTaxonomyThenOK()
throws Exception {
session = newCMISSessionAsUserInZeCollection(admin);
assertThatAllowableActionsOf("/taxo_plan/").containsOnly(CAN_GET_PROPERTIES, CAN_GET_CHILDREN);
assertThatAllowableActionsOf("/taxo_admUnits/").containsOnly(CAN_GET_PROPERTIES, CAN_GET_CHILDREN);
session = newCMISSessionAsUserInZeCollection(aliceWonderland);
assertThatAllowableActionsOf("/taxo_plan").containsOnly(CAN_GET_PROPERTIES, CAN_GET_CHILDREN);
assertThatAllowableActionsOf("/taxo_admUnits").containsOnly(CAN_GET_PROPERTIES, CAN_GET_CHILDREN);
session = newCMISSessionAsUserInZeCollection(bobGratton);
assertThatAllowableActionsOf("/taxo_plan").containsOnly(CAN_GET_PROPERTIES, CAN_GET_CHILDREN);
assertThatAllowableActionsOf("/taxo_admUnits").containsOnly(CAN_GET_PROPERTIES, CAN_GET_CHILDREN);
}
@Test
public void whenUserHasRWDAndManageSecurityPermissionOnPrincipalConceptThenCanSetACL()
throws Exception {
givenConfig(ConstellioEIMConfigs.CMIS_NEVER_RETURN_ACL, false);
authServices.add(authorizationForUsers(users.dakotaIn(zeCollection)).on(records.unitId_10).givingReadWriteDeleteAccess(),
users.adminIn(zeCollection));
authServices.add(authorizationForUsers(users.dakotaIn(zeCollection)).on(records.unitId_10).giving("r1"),
users.adminIn(zeCollection));
authServices.add(authorizationForUsers(users.edouardIn(zeCollection)).on(records.unitId_10).givingReadWriteDeleteAccess(),
users.adminIn(zeCollection));
authServices.add(authorizationForUsers(users.charlesIn(zeCollection)).on(records.unitId_10).givingReadWriteDeleteAccess(),
users.adminIn(zeCollection));
authServices.add(authorizationForUsers(users.charlesIn(zeCollection)).on(records.unitId_10).giving("r2"),
users.adminIn(zeCollection));
authServices.add(authorizationForUsers(users.aliceIn(zeCollection)).on(records.folder_A19).givingReadWriteDeleteAccess(),
users.adminIn(zeCollection));
authServices.add(authorizationForUsers(users.aliceIn(zeCollection)).on(records.folder_A19).giving("r4"),
users.adminIn(zeCollection));
authServices.add(authorizationForUsers(users.bobIn(zeCollection)).on(records.unitId_10).givingReadWriteDeleteAccess(),
users.adminIn(zeCollection));
//Roles giving share folder and share documents permissions, which are not supported in cmis
authServices.add(authorizationForUsers(users.bobIn(zeCollection)).on(records.unitId_10).giving("r3", "r5"),
users.adminIn(zeCollection));
authServices.add(authorizationForUsers(users.bobIn(zeCollection)).on(records.unitId_10).givingReadWriteDeleteAccess(),
users.adminIn(zeCollection));
//Roles giving share folder and share documents permissions, which are not supported in cmis
authServices.add(authorizationForUsers(users.bobIn(zeCollection)).on(records.unitId_10).giving("r3", "r5"),
users.adminIn(zeCollection));
waitForBatchProcess();
//Folders :
for (String user : asList(admin, dakota, chuckNorris, sasquatch, charles)) {
ensureUserCanGetAndApplyACLOnRecords(user, records.folder_A19);
}
for (String user : asList(edouard, gandalf, aliceWonderland, bobGratton, robin)) {
ensureUserCannotGetAndApplyACLOnRecords(user, records.folder_A19);
}
for (String user : asList(admin, chuckNorris, sasquatch)) {
ensureUserCanGetAndApplyACLOnRecords(user, records.folder_C30);
}
for (String user : asList(edouard, gandalf, aliceWonderland, bobGratton, robin)) {
ensureUserCannotGetAndApplyACLOnRecords(user, records.folder_C30);
}
//Documents :
for (String user : asList(admin, aliceWonderland, chuckNorris, robin)) {
ensureUserCanGetAndApplyACLOnRecords(user, records.document_A19);
}
for (String user : asList(edouard, charles, gandalf, bobGratton, sasquatch)) {
ensureUserCannotGetAndApplyACLOnRecords(user, records.document_A19);
}
for (String user : asList(admin, chuckNorris, robin)) {
ensureUserCanGetAndApplyACLOnRecords(user, records.document_B30);
}
for (String user : asList(edouard, gandalf, aliceWonderland, bobGratton, sasquatch)) {
ensureUserCannotGetAndApplyACLOnRecords(user, records.document_B30);
}
}
@Test
public void whenUserHasRWDAndManagerSecurityPermissionOnFolderThenCanSetACL()
throws Exception {
givenConfig(ConstellioEIMConfigs.CMIS_NEVER_RETURN_ACL, false);
authServices.add(authorizationForUsers(users.dakotaIn(zeCollection)).on(records.folder_A03).givingReadWriteDeleteAccess(),
users.adminIn(zeCollection));
authServices.add(authorizationForUsers(users.dakotaIn(zeCollection)).on(records.folder_A03).giving("r1"),
users.adminIn(zeCollection));
authServices
.add(authorizationForUsers(users.edouardIn(zeCollection)).on(records.folder_A03).givingReadWriteDeleteAccess(),
users.adminIn(zeCollection));
authServices.add(authorizationForUsers(users.charlesIn(zeCollection)).on(records.folder_A03).givingReadWriteAccess(),
users.adminIn(zeCollection));
authServices.add(authorizationForUsers(users.charlesIn(zeCollection)).on(records.folder_A03).giving("r1"),
users.adminIn(zeCollection));
waitForBatchProcess();
for (String user : asList(admin, dakota, chuckNorris, sasquatch)) {
ensureUserCanGetAndApplyACLOnRecords(user, records.folder_A03);
}
for (String user : asList(edouard, charles, gandalf, aliceWonderland, bobGratton, robin)) {
ensureUserCannotGetAndApplyACLOnRecords(user, records.folder_A03);
}
for (String user : asList(admin, chuckNorris, sasquatch)) {
ensureUserCanGetAndApplyACLOnRecords(user, records.folder_A07);
}
for (String user : asList(gandalf, aliceWonderland, bobGratton, robin)) {
ensureUserCannotGetAndApplyACLOnRecords(user, records.folder_A07);
}
}
private void ensureUserCanGetAndApplyACLOnRecords(String user, String id) {
as(user).assertThatAllowableActionsOf(id).describedAs("Actions of " + user).contains(CAN_GET_ACL, CAN_APPLY_ACL);
assertThat(session.getObject(id).getAcl().getAces()).isNotEmpty();
assertThat(session.getAcl(session.getObject(id), false).getAces()).isNotEmpty();
session.getObject(id).addAcl(asList(ace(bobGratton, asList(CMIS_READ))), REPOSITORYDETERMINED);
}
@Test
public void givenUserAsAllCollectionAccessThenCanOnlySetACLOfAdministrativeUnitsIfItHasThePermissionTo()
throws Exception {
givenConfig(ConstellioEIMConfigs.CMIS_NEVER_RETURN_ACL, false);
authServices.add(authorizationForUsers(users.dakotaIn(zeCollection)).on(records.unitId_10).givingReadWriteDeleteAccess(),
users.adminIn(zeCollection));
authServices.add(authorizationForUsers(users.dakotaIn(zeCollection)).on(records.unitId_10).giving(RMRoles.RGD),
users.adminIn(zeCollection));
authServices.add(authorizationForUsers(users.edouardIn(zeCollection)).on(records.unitId_10).givingReadWriteDeleteAccess(),
users.adminIn(zeCollection));
authServices.add(authorizationForUsers(users.charlesIn(zeCollection)).on(records.unitId_10).givingReadWriteAccess(),
users.adminIn(zeCollection));
authServices.add(authorizationForUsers(users.charlesIn(zeCollection)).on(records.unitId_10).giving(RMRoles.RGD),
users.adminIn(zeCollection));
waitForBatchProcess();
for (String user : asList(admin, dakota, chuckNorris)) {
as(user).assertThatAllowableActionsOf(records.unitId_10).contains(CAN_GET_ACL, CAN_APPLY_ACL);
assertThat(session.getObject(records.unitId_10).getAcl().getAces()).isNotEmpty();
assertThat(session.getAcl(session.getObject(records.unitId_10), false).getAces()).isNotEmpty();
session.getObject(records.unitId_10).addAcl(asList(ace(bobGratton, asList(CMIS_READ))), REPOSITORYDETERMINED);
}
for (String user : asList(edouard, charles, sasquatch, gandalf, aliceWonderland)) {
as(user).assertThatAllowableActionsOf(records.unitId_10).doesNotContain(CAN_GET_ACL, CAN_APPLY_ACL);
assertThat(session.getObject(records.unitId_10).getAcl()).isNull();
try {
session.getAcl(session.getObject(records.unitId_10), true);
fail("Exception expected");
} catch (Exception e) {
//OK
}
try {
session.getAcl(session.getObject(records.unitId_10), false);
fail("Exception expected");
} catch (Exception e) {
//OK
}
try {
session.getObject(records.unitId_10).addAcl(asList(ace(bobGratton, asList(CMIS_READ))), REPOSITORYDETERMINED);
fail("Exception expected");
} catch (Exception e) {
//OK
}
}
}
private void printTaxonomies(User user) {
TaxonomiesManager taxonomiesManager = getModelLayerFactory().getTaxonomiesManager();
StringBuilder stringBuilder = new StringBuilder();
for (Taxonomy taxonomy : taxonomiesManager.getEnabledTaxonomies(zeCollection)) {
stringBuilder.append(taxonomy.getCode() + " : \n");
for (Record record : new ConceptNodesTaxonomySearchServices(getModelLayerFactory())
.getRootConcept(zeCollection, taxonomy.getCode(), new TaxonomiesSearchOptions().setRows(100))) {
printConcept(user, taxonomy.getCode(), record, 1, stringBuilder);
}
stringBuilder.append("\n\n");
}
System.out.println(stringBuilder.toString());
}
private void printConcept(User user, String taxonomy, Record record, int level, StringBuilder stringBuilder) {
TaxonomiesSearchServices taxonomiesSearchServices = getModelLayerFactory().newTaxonomiesSearchService();
for (int i = 0; i < level; i++) {
stringBuilder.append("\t");
}
stringBuilder.append(record.getId() + "\n");
for (TaxonomySearchRecord child : taxonomiesSearchServices
.getVisibleChildConcept(user, taxonomy, record, new TaxonomiesSearchOptions().setRows(100))) {
printConcept(user, taxonomy, child.getRecord(), level + 1, stringBuilder);
}
}
private IterableAssert<Action> assertThatAllowableActionsOf(String path) {
if (path.startsWith("/")) {
return assertThat(session.getObjectByPath(path).getAllowableActions().getAllowableActions());
} else {
return assertThat(session.getObject(path).getAllowableActions().getAllowableActions());
}
}
private RMCmisAllowableActionsAcceptanceTest as(String user) {
System.out.println("Logging as " + user);
session = newCMISSessionAsUserInCollection(user, zeCollection);
session.getDefaultContext().setIncludeAcls(true);
return this;
}
private Folder cmisFolder(Record record) {
return (Folder) session.getObject(record.getId());
}
private Ace ace(String principal, List<String> permissions) {
return session.getObjectFactory().createAce(principal, permissions);
}
private void ensureUserCannotGetAndApplyACLOnRecords(String user, String id) {
as(user).assertThatAllowableActionsOf(id).describedAs("Actions of " + user).doesNotContain(CAN_GET_ACL, CAN_APPLY_ACL);
assertThat(session.getObject(id).getAcl()).isNull();
try {
session.getAcl(session.getObject(id), true);
fail("Exception expected");
} catch (Exception e) {
//OK
}
try {
session.getAcl(session.getObject(id), false);
fail("Exception expected");
} catch (Exception e) {
//OK
}
try {
session.getObject(id).addAcl(asList(ace(bobGratton, asList(CMIS_READ))), REPOSITORYDETERMINED);
fail("Exception expected");
} catch (Exception e) {
//OK
}
}
}