Compare commits

...

39 Commits

Author SHA1 Message Date
Travis CI User
b22595058e [maven-release-plugin][skip ci] prepare release 17.113 2022-09-14 13:51:22 +00:00
Marcin Strankowski
742a4c89dd ACS-3402: Add actual dependency, not just dependencyManagement, record of non-transitive netty dependencies to be used in AMPs (#1384) 2022-09-14 15:11:09 +02:00
Travis CI User
09db0fcad3 [maven-release-plugin][skip ci] prepare for next development iteration 2022-09-14 12:43:24 +00:00
Travis CI User
313823eb8c [maven-release-plugin][skip ci] prepare release 17.112 2022-09-14 12:43:21 +00:00
Tom Page
b7d2d3bae9 ACS-3536 Omit actionContext for rules. (#1383)
* ACS-3536 Omit actionContext for rules.

It will always be set to 'Rule' anyway.

* ACS-3536 Stop setting actionContext in rules E2E tests.

Also add extra assertion for action params in new test.
2022-09-14 12:56:38 +01:00
Travis CI User
e265bb0a5d [maven-release-plugin][skip ci] prepare for next development iteration 2022-09-13 18:58:36 +00:00
Travis CI User
6da0d7cc7b [maven-release-plugin][skip ci] prepare release 17.111 2022-09-13 18:58:34 +00:00
tiagosalvado10
430d15f32d [MNT-21901] Prevent update preferences concurrency errors (#821)
* [MNT-21901] Added PersistenceException to retry exceptions. Added validation to get retry cause

* [MNT-21901] Check exception message only if it is JavaScriptException

* [MNT-21901] Removed unit test from repository. Added new test on remote-api. Changed ExceptionStackUtil validation.

* [MNT-21901] Improved JavascriptException validation. Added exception delimiter to prevent accepting exceptions with the same partial name.
2022-09-13 16:40:23 +01:00
Travis CI User
3fc0100bb5 [maven-release-plugin][skip ci] prepare for next development iteration 2022-09-13 13:49:23 +00:00
Travis CI User
7ccd9640ee [maven-release-plugin][skip ci] prepare release 17.110 2022-09-13 13:49:20 +00:00
Marcin Strankowski
bea34483b5 Feature/acs 3402 release gytheio 0.17.0 (#1375)
* Update Gytheio version
2022-09-13 15:01:02 +02:00
Travis CI User
e0867a099f [maven-release-plugin][skip ci] prepare for next development iteration 2022-09-13 11:19:31 +00:00
Travis CI User
172d6c2185 [maven-release-plugin][skip ci] prepare release 17.109 2022-09-13 11:19:28 +00:00
krdabrowski
4692d471a3 ACS-3430: POST support for conditions (#1356) 2022-09-13 11:32:29 +01:00
Marcin Strankowski
3c55c1a9a0 ACS-3402: Camel upgrade (#1322)
* ACS-3402 Camel upgrade

* ACS-3402 Camel upgrade

* ACS-3402 Camel upgrade

* ACS-3402 Camel upgrade

* Revert "ACS-3402 Camel upgrade"

This reverts commit 124e188027.

* Moving all netty dependencies up to community-repo

These dependencies are a re/declaration of transitive/optional camel dependencies used in dependant projects.
They must be in sync with camel version of community repo, hence declaring them in other projects just causes unnecessary confusion.

* Fixing new versions of netty

They were updated through transitive update of camel from .73.Final to .79.Final, along with netty-transport-native-unix-common library that went to .79.Final

* Had extreme collisions on netty-transport-native-unix-common

Moved to approach with just declaring additional non-provided netty libraries and moving version numbers to community-repo.
Even mentioning netty-transport-native-unix-common in pom.xml seems to crash tests no matter which version is taken or if it's a transitive dependency of netty-handler. Since I can't add it either way, then partial additions of libraries make no sense. I've adjusted comments next to version numbers to make it easier to differentiate what they are responsible for and need to keep track of. Version numbers should still be placed in community-repo

* 3.18.2 was released recently, doesn't modify netty versions, so I'll use the latest one

Co-authored-by: pzurek <Piotr.Zurek@hyland.com>
2022-09-13 11:49:56 +02:00
Tom Page
400b33c7eb ACS-3489 Use specified rule set. (#1373)
* ACS-3280 Get inherited rule sets. [tas]

This needs to work the exact same way as get inherited rules.

* ACS-3280 Replace LinkedList with ArrayList.

* ACS-3280 Don't return duplicated rule sets when there are links.

* ACS-3489 E2E test for getting rules with inheritance.

* ACS-3489 Inherited rule sets are also associated with folders.

* ACS-3489 Fix test to contain expected values.

* ACS-3489 Ensure only rules from specified rule set are returned.

Add E2E test case for inherited links and fix unit tests.

* ACS-3489 Fix audit reference in RuleService.
2022-09-13 10:23:16 +01:00
Travis CI User
bc9c23503b [maven-release-plugin][skip ci] prepare for next development iteration 2022-09-13 08:49:18 +00:00
Travis CI User
7ed021fb77 [maven-release-plugin][skip ci] prepare release 17.108 2022-09-13 08:49:16 +00:00
Tom Page
0e73bc1572 ACS-3533 Disabling scan on PR builds. (#1374) 2022-09-13 09:13:38 +01:00
Travis CI User
7eb063e923 [maven-release-plugin][skip ci] prepare for next development iteration 2022-09-12 19:00:33 +00:00
Travis CI User
a5a34c05c1 [maven-release-plugin][skip ci] prepare release 17.107 2022-09-12 19:00:31 +00:00
tiagosalvado10
9f4371eb9c [MNT-21377] Prevent LDAP checks from running multiple times (#1369)
* [MNT-23177] Prevent LDAP checks from running multiple times

* [MNT-23177] Removed lock. Added property to disable ldap checks

* [MNT-23177] Changed property name

* [MNT-23177] Removed unused imports
2022-09-12 19:22:01 +01:00
Travis CI User
5846a1fd11 [maven-release-plugin][skip ci] prepare for next development iteration 2022-09-12 18:06:59 +00:00
Travis CI User
ffef5f875a [maven-release-plugin][skip ci] prepare release 17.106 2022-09-12 18:06:57 +00:00
George Evangelopoulos
fa04a7264f ACS-3485: Change naming (#1372) 2022-09-12 20:33:24 +03:00
Tom Page
6e5d37067d ACS-3531 Ignore testDownloadFileContent since it fails so frequently. 2022-09-12 18:07:18 +01:00
Travis CI User
0bbc63fb20 [maven-release-plugin][skip ci] prepare for next development iteration 2022-09-12 13:10:51 +00:00
Travis CI User
2819e0590b [maven-release-plugin][skip ci] prepare release 17.105 2022-09-12 13:10:49 +00:00
MohinishSah
2a84afe2f0 Update pom.xml 2022-09-12 17:53:41 +05:30
Tom Page
80ccf64df8 ACS-3280 Get inherited rule sets. [tas] (#1323)
* ACS-3280 Get inherited rule sets. [tas]

This needs to work the exact same way as get inherited rules.

* ACS-3280 Replace LinkedList with ArrayList.

* ACS-3280 Don't return duplicated rule sets when there are links.
2022-09-12 11:44:42 +01:00
Travis CI User
5cf7c1934a [maven-release-plugin][skip ci] prepare for next development iteration 2022-09-11 00:10:33 +00:00
Travis CI User
25d6b428aa [maven-release-plugin][skip ci] prepare release 17.104 2022-09-11 00:10:31 +00:00
Alfresco CI User
829393b602 [force] Force release for 2022-09-11. 2022-09-11 00:03:39 +00:00
Travis CI User
18d2dfc84d [maven-release-plugin][skip ci] prepare for next development iteration 2022-09-09 10:50:31 +00:00
Travis CI User
8cdff26342 [maven-release-plugin][skip ci] prepare release 17.103 2022-09-09 10:50:29 +00:00
Antonio Felix
8bc3b357cf Fix/mnt 23190 queries to find users not returning results (#1365)
* MNT-23190 - Added "hint:useCQ" in order for the new users to appear on the results
2022-09-09 11:07:15 +01:00
Travis CI User
d2a71d0c9f [maven-release-plugin][skip ci] prepare for next development iteration 2022-09-07 09:48:51 +00:00
Travis CI User
0eb5a4b806 [maven-release-plugin][skip ci] prepare release 17.102 2022-09-07 09:48:48 +00:00
evasques
b8ac41ac0d Revert "ACS-1600 : Error when running propTablesCleanupJob on an env with 100 million records in alf_prop_value (#473)" (#1358)
This reverts commit 00b0b21668.
2022-09-07 10:10:25 +01:00
67 changed files with 1712 additions and 456 deletions

View File

@@ -53,7 +53,7 @@ jobs:
- name: "Source Clear Scan (SCA)"
stage: test
if: branch = master OR branch =~ /release\/.*/
if: (branch = master OR branch =~ /release\/.*/) AND type != pull_request
# Run Veracode
install: skip
script: travis_wait 30 bash scripts/travis/source_clear.sh

View File

@@ -7,7 +7,7 @@
<parent>
<groupId>org.alfresco</groupId>
<artifactId>alfresco-community-repo-amps</artifactId>
<version>17.102-SNAPSHOT</version>
<version>17.113</version>
</parent>
<modules>

View File

@@ -7,7 +7,7 @@
<parent>
<groupId>org.alfresco</groupId>
<artifactId>alfresco-governance-services-community-parent</artifactId>
<version>17.102-SNAPSHOT</version>
<version>17.113</version>
</parent>
<modules>

View File

@@ -7,7 +7,7 @@
<parent>
<groupId>org.alfresco</groupId>
<artifactId>alfresco-governance-services-automation-community-repo</artifactId>
<version>17.102-SNAPSHOT</version>
<version>17.113</version>
</parent>
<build>

View File

@@ -7,7 +7,7 @@
<parent>
<groupId>org.alfresco</groupId>
<artifactId>alfresco-governance-services-community-parent</artifactId>
<version>17.102-SNAPSHOT</version>
<version>17.113</version>
</parent>
<modules>

View File

@@ -8,7 +8,7 @@
<parent>
<groupId>org.alfresco</groupId>
<artifactId>alfresco-governance-services-community-repo-parent</artifactId>
<version>17.102-SNAPSHOT</version>
<version>17.113</version>
</parent>
<properties>

View File

@@ -7,7 +7,7 @@
<parent>
<groupId>org.alfresco</groupId>
<artifactId>alfresco-governance-services-community-repo-parent</artifactId>
<version>17.102-SNAPSHOT</version>
<version>17.113</version>
</parent>
<build>

View File

@@ -7,7 +7,7 @@
<parent>
<groupId>org.alfresco</groupId>
<artifactId>alfresco-community-repo</artifactId>
<version>17.102-SNAPSHOT</version>
<version>17.113</version>
</parent>
<modules>

View File

@@ -8,7 +8,7 @@
<parent>
<groupId>org.alfresco</groupId>
<artifactId>alfresco-community-repo-amps</artifactId>
<version>17.102-SNAPSHOT</version>
<version>17.113</version>
</parent>
<properties>

View File

@@ -11,7 +11,7 @@ function main()
var params =
{
type: "people",
term: args.t,
term: args.t + " [hint:useCQ]",
maxResults: (args.maxResults !== null) ? parseInt(args.maxResults, 10) : DEFAULT_MAX_RESULTS,
startIndex: (args.startIndex !== null) ? parseInt(args.startIndex, 10) : 0
};

View File

@@ -7,7 +7,7 @@
<parent>
<groupId>org.alfresco</groupId>
<artifactId>alfresco-community-repo</artifactId>
<version>17.102-SNAPSHOT</version>
<version>17.113</version>
</parent>
<dependencies>

View File

@@ -25,6 +25,9 @@ package org.alfresco.error;
*/
public class ExceptionStackUtil
{
private static final String JAVASCRIPT_EXCEPTION = "org.mozilla.javascript.JavaScriptException";
private static final String EXCEPTION_DELIMITER = ":";
/**
* Searches through the exception stack of the given throwable to find any instance
* of the possible cause. The top-level throwable will also be tested.
@@ -38,10 +41,17 @@ public class ExceptionStackUtil
{
while (throwable != null)
{
Class<?> throwableClass = throwable.getClass();
boolean isJavaScriptException = throwableClass.getName().contains(JAVASCRIPT_EXCEPTION);
String throwableMsg = throwable.getMessage() != null ? throwable.getMessage() : "";
for (Class<?> possibleCauseClass : possibleCauses)
{
Class<?> throwableClass = throwable.getClass();
if (possibleCauseClass.isAssignableFrom(throwableClass))
String possibleCauseClassName = possibleCauseClass.getName();
if (possibleCauseClass.isAssignableFrom(throwableClass)
|| (isJavaScriptException && throwableMsg.contains(possibleCauseClassName + EXCEPTION_DELIMITER)))
{
// We have a match
return throwable;

View File

@@ -7,7 +7,7 @@
<parent>
<groupId>org.alfresco</groupId>
<artifactId>alfresco-community-repo</artifactId>
<version>17.102-SNAPSHOT</version>
<version>17.113</version>
</parent>
<properties>

View File

@@ -7,7 +7,7 @@
<parent>
<groupId>org.alfresco</groupId>
<artifactId>alfresco-community-repo</artifactId>
<version>17.102-SNAPSHOT</version>
<version>17.113</version>
</parent>
<dependencies>

View File

@@ -9,6 +9,6 @@
<parent>
<groupId>org.alfresco</groupId>
<artifactId>alfresco-community-repo-packaging</artifactId>
<version>17.102-SNAPSHOT</version>
<version>17.113</version>
</parent>
</project>

View File

@@ -7,7 +7,7 @@
<parent>
<groupId>org.alfresco</groupId>
<artifactId>alfresco-community-repo-packaging</artifactId>
<version>17.102-SNAPSHOT</version>
<version>17.113</version>
</parent>
<properties>

View File

@@ -7,7 +7,7 @@
<parent>
<groupId>org.alfresco</groupId>
<artifactId>alfresco-community-repo</artifactId>
<version>17.102-SNAPSHOT</version>
<version>17.113</version>
</parent>
<modules>

View File

@@ -6,7 +6,7 @@
<parent>
<groupId>org.alfresco</groupId>
<artifactId>alfresco-community-repo-packaging</artifactId>
<version>17.102-SNAPSHOT</version>
<version>17.113</version>
</parent>
<modules>

View File

@@ -9,7 +9,7 @@
<parent>
<groupId>org.alfresco</groupId>
<artifactId>alfresco-community-repo-tests</artifactId>
<version>17.102-SNAPSHOT</version>
<version>17.113</version>
</parent>
<developers>

View File

@@ -9,7 +9,7 @@
<parent>
<groupId>org.alfresco</groupId>
<artifactId>alfresco-community-repo-tests</artifactId>
<version>17.102-SNAPSHOT</version>
<version>17.113</version>
</parent>
<developers>

View File

@@ -9,7 +9,7 @@
<parent>
<groupId>org.alfresco</groupId>
<artifactId>alfresco-community-repo-tests</artifactId>
<version>17.102-SNAPSHOT</version>
<version>17.113</version>
</parent>
<developers>

View File

@@ -9,7 +9,7 @@
<parent>
<groupId>org.alfresco</groupId>
<artifactId>alfresco-community-repo-tests</artifactId>
<version>17.102-SNAPSHOT</version>
<version>17.113</version>
</parent>
<developers>

View File

@@ -49,6 +49,7 @@ import java.util.stream.IntStream;
import org.alfresco.rest.RestTest;
import org.alfresco.rest.model.RestActionBodyExecTemplateModel;
import org.alfresco.rest.model.RestCompositeConditionDefinitionModel;
import org.alfresco.rest.model.RestRuleModel;
import org.alfresco.rest.model.RestRuleModelsCollection;
import org.alfresco.utility.constants.UserRole;
@@ -94,7 +95,6 @@ public class CreateRulesTests extends RestTest
.createSingleRule(ruleModel);
RestRuleModel expectedRuleModel = createRuleModelWithModifiedValues();
expectedRuleModel.setActions(addActionContextParams(expectedRuleModel.getActions()));
expectedRuleModel.setConditions(createEmptyConditionModel());
restClient.assertStatusCodeIs(CREATED);
rule.assertThat().isEqualTo(expectedRuleModel, IGNORE_ID, IGNORE_IS_SHARED)
@@ -384,7 +384,7 @@ public class CreateRulesTests extends RestTest
.createSingleRule(ruleModel);
final RestRuleModel expectedRuleModel = createRuleModelWithDefaultValues();
expectedRuleModel.setActions(addActionContextParams(Arrays.asList(copyAction, checkOutAction, scriptAction)));
expectedRuleModel.setActions(Arrays.asList(copyAction, checkOutAction, scriptAction));
expectedRuleModel.setConditions(createEmptyConditionModel());
expectedRuleModel.setTriggers(List.of("inbound"));
@@ -392,4 +392,64 @@ public class CreateRulesTests extends RestTest
rule.assertThat().isEqualTo(expectedRuleModel, IGNORE_ID, IGNORE_IS_SHARED)
.assertThat().field("isShared").isNull();
}
/**
* Check we can create a rule with multiple conditions
*/
@Test(groups = {TestGroup.REST_API, TestGroup.RULES})
public void createRuleWithConditions()
{
RestRuleModel ruleModel = createRuleModelWithDefaultValues();
ruleModel.setConditions(createVariousConditions());
RestRuleModel rule = restClient.authenticateUser(user).withCoreAPI().usingNode(ruleFolder).usingDefaultRuleSet()
.createSingleRule(ruleModel);
RestRuleModel expectedRuleModel = createRuleModelWithDefaultValues();
expectedRuleModel.setConditions(createVariousConditions());
expectedRuleModel.setTriggers(List.of("inbound"));
restClient.assertStatusCodeIs(CREATED);
rule.assertThat().isEqualTo(expectedRuleModel, IGNORE_ID, IGNORE_IS_SHARED);
}
/**
* Check we can create a rule with empty list as conditions
*/
@Test(groups = {TestGroup.REST_API, TestGroup.RULES})
public void createRuleWithConditions_emptyConditionList()
{
RestRuleModel ruleModel = createRuleModelWithDefaultValues();
ruleModel.setConditions(createCompositeCondition(null));
RestRuleModel rule = restClient.authenticateUser(user).withCoreAPI().usingNode(ruleFolder).usingDefaultRuleSet()
.createSingleRule(ruleModel);
RestRuleModel expectedRuleModel = createRuleModelWithDefaultValues();
expectedRuleModel.setConditions(createCompositeCondition(null));
expectedRuleModel.setTriggers(List.of("inbound"));
restClient.assertStatusCodeIs(CREATED);
rule.assertThat().isEqualTo(expectedRuleModel, IGNORE_ID, IGNORE_IS_SHARED);
}
/**
* Check we can NOT create a rule when category ID in condition is invalid, HTTP status code 400 is expected
*/
@Test(groups = {TestGroup.REST_API, TestGroup.RULES})
public void createRuleWithConditions_invalidCategory()
{
STEP("Try to create a rule with non existing category in conditions.");
String fakeCategoryId = "bdba5f9f-fake-id22-803b-349bcfd06fd1";
RestCompositeConditionDefinitionModel conditions = createCompositeCondition(List.of(
createCompositeCondition(!INVERTED, List.of(
createSimpleCondition("category", "equals", fakeCategoryId)
))
));
RestRuleModel ruleModel = createRuleModelWithDefaultValues();
ruleModel.setConditions(conditions);
restClient.authenticateUser(user).withCoreAPI().usingNode(ruleFolder).usingDefaultRuleSet().createSingleRule(ruleModel);
restClient.assertStatusCodeIs(BAD_REQUEST);
restClient.assertLastError().containsSummary("Category in condition is invalid");
}
}

View File

@@ -0,0 +1,142 @@
/*
* #%L
* Alfresco Repository
* %%
* Copyright (C) 2005 - 2016 Alfresco Software Limited
* %%
* This file is part of the Alfresco software.
* If the software was purchased under a paid Alfresco license, the terms of
* the paid license agreement will prevail. Otherwise, the software is
* provided under the following open source license terms:
*
* Alfresco is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Alfresco is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
* #L%
*/
package org.alfresco.rest.rules;
import static org.alfresco.rest.rules.RulesTestsUtils.createRuleModelWithModifiedValues;
import static org.alfresco.utility.report.log.Step.STEP;
import static org.testng.Assert.assertEquals;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
import org.alfresco.rest.RestTest;
import org.alfresco.rest.model.RestRuleModel;
import org.alfresco.rest.model.RestRuleModelsCollection;
import org.alfresco.rest.model.RestRuleSetLinkModel;
import org.alfresco.rest.model.RestRuleSetModel;
import org.alfresco.rest.model.RestRuleSetModelsCollection;
import org.alfresco.utility.model.FolderModel;
import org.alfresco.utility.model.SiteModel;
import org.alfresco.utility.model.TestGroup;
import org.alfresco.utility.model.UserModel;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.Test;
/**
* Tests for GET /nodes/{nodeId}/rule-sets/{ruleSetId}/rules with rule inheritance.
*/
@Test(groups = {TestGroup.RULES})
public class GetInheritedRulesTests extends RestTest
{
private UserModel user;
private SiteModel site;
@BeforeClass(alwaysRun = true)
public void dataPreparation()
{
STEP("Create a user and site");
user = dataUser.createRandomTestUser();
site = dataSite.usingUser(user).createPublicRandomSite();
}
/**
* Check we can get all the rules for the folder by providing the different rule set ids.
*/
@Test (groups = { TestGroup.REST_API, TestGroup.RULES, TestGroup.SANITY })
public void getInheritedRules()
{
STEP("Create a parent and child folder, each with inheriting rules");
FolderModel parent = dataContent.usingUser(user).usingSite(site).createFolder();
FolderModel child = dataContent.usingUser(user).usingResource(parent).createFolder();
RestRuleModel parentRule = createRuleModelWithModifiedValues();
parentRule = restClient.authenticateUser(user).withCoreAPI().usingNode(parent).usingDefaultRuleSet().createSingleRule(parentRule);
RestRuleModel childRule = createRuleModelWithModifiedValues();
childRule = restClient.authenticateUser(user).withCoreAPI().usingNode(child).usingDefaultRuleSet().createSingleRule(childRule);
STEP("Get the rules in the default rule set for the child folder");
RestRuleModelsCollection rules = restClient.authenticateUser(user).withCoreAPI().usingNode(child).usingDefaultRuleSet().getListOfRules();
rules.assertThat().entriesListContains("id", childRule.getId())
.and().entriesListCountIs(1);
STEP("Get the rules in the inherited rule set for the child folder");
RestRuleSetModelsCollection ruleSets = restClient.authenticateUser(user).withCoreAPI().usingNode(child).include("inclusionType").getListOfRuleSets();
String inheritedRuleSetId = ruleSets.getEntries().stream()
.filter(ruleSet -> ruleSet.onModel().getInclusionType().equals("inherited"))
.findFirst().get().onModel().getId();
RestRuleModelsCollection inheritedRules = restClient.authenticateUser(user).withCoreAPI().usingNode(child).usingRuleSet(inheritedRuleSetId).getListOfRules();
inheritedRules.assertThat().entriesListContains("id", parentRule.getId())
.and().entriesListCountIs(1);
}
/**
* Check that we only get each rule once with linking and inheritance, and the order is correct.
* <p>
* The folder structure for this test is as follows:
* <pre>
* A --[links]-> DRuleSet
* +-B --[owns]-> BRuleSet
* +-C --[owns]-> CRuleSet
* +-D --[owns]--> DRuleSet
* </pre>
*/
@Test (groups = { TestGroup.REST_API, TestGroup.RULES, TestGroup.SANITY })
public void rulesReturnedAreUnique()
{
STEP("Create four folders with rules");
FolderModel folderA = dataContent.usingUser(user).usingSite(site).createFolder();
FolderModel folderB = dataContent.usingUser(user).usingResource(folderA).createFolder();
FolderModel folderC = dataContent.usingUser(user).usingResource(folderB).createFolder();
FolderModel folderD = dataContent.usingUser(user).usingResource(folderC).createFolder();
RestRuleModel ruleB = restClient.authenticateUser(user).withCoreAPI().usingNode(folderB).usingDefaultRuleSet().createSingleRule(createRuleModelWithModifiedValues());
RestRuleModel ruleC = restClient.authenticateUser(user).withCoreAPI().usingNode(folderC).usingDefaultRuleSet().createSingleRule(createRuleModelWithModifiedValues());
RestRuleModel ruleD = restClient.authenticateUser(user).withCoreAPI().usingNode(folderD).usingDefaultRuleSet().createSingleRule(createRuleModelWithModifiedValues());
STEP("Link folderA to ruleSetD");
RestRuleSetLinkModel linkModel = new RestRuleSetLinkModel();
linkModel.setId(folderD.getNodeRef());
restClient.authenticateUser(user).withCoreAPI().usingNode(folderA).createRuleLink(linkModel);
STEP("Get the rule sets for the folderD");
List<RestRuleSetModel> ruleSets = restClient.authenticateUser(user).withCoreAPI().usingNode(folderD).getListOfRuleSets().getEntries();
STEP("Check the rules for each rule set are as expected");
List<RestRuleModel> expectedRuleIds = List.of(ruleD, ruleB, ruleC);
IntStream.range(0, 2).forEach(index -> {
String ruleSetId = ruleSets.get(index).onModel().getId();
List<RestRuleModel> rules = restClient.authenticateUser(user)
.withCoreAPI()
.usingNode(folderD)
.usingRuleSet(ruleSetId)
.getListOfRules()
.getEntries()
.stream()
.map(RestRuleModel::onModel)
.collect(Collectors.toList());
assertEquals(rules, List.of(expectedRuleIds.get(index)), "Unexpected rules found for rule set " + ruleSetId);
});
assertEquals(ruleSets.size(), 3, "Expected three unique rule sets to be returned but got " + ruleSets);
}
}

View File

@@ -35,6 +35,7 @@ import org.alfresco.rest.RestTest;
import org.alfresco.rest.model.RestRuleModel;
import org.alfresco.rest.model.RestRuleSetModel;
import org.alfresco.rest.model.RestRuleSetModelsCollection;
import org.alfresco.rest.model.RestRuleSettingsModel;
import org.alfresco.utility.model.FolderModel;
import org.alfresco.utility.model.SiteModel;
import org.alfresco.utility.model.TestGroup;
@@ -51,6 +52,8 @@ public class GetRuleSetsTests extends RestTest
private UserModel user;
private SiteModel site;
private FolderModel ruleFolder;
private FolderModel inheritingChildFolder;
private FolderModel notInheritingChildFolder;
private RestRuleModel rule;
private String ruleSetId;
@@ -62,6 +65,14 @@ public class GetRuleSetsTests extends RestTest
site = dataSite.usingUser(user).createPublicRandomSite();
ruleFolder = dataContent.usingUser(user).usingSite(site).createFolder();
STEP("Create two children of the folder - one that inherits rules and one that doesn't");
inheritingChildFolder = dataContent.usingUser(user).usingResource(ruleFolder).createFolder();
notInheritingChildFolder = dataContent.usingUser(user).usingResource(ruleFolder).createFolder();
RestRuleSettingsModel doesntInherit = new RestRuleSettingsModel();
doesntInherit.setValue(false);
restClient.authenticateUser(user).withCoreAPI().usingNode(notInheritingChildFolder)
.usingIsInheritanceEnabledRuleSetting().updateSetting(doesntInherit);
STEP("Create a rule in the folder.");
RestRuleModel ruleModel = createRuleModel("ruleName");
rule = restClient.authenticateUser(user).withCoreAPI().usingNode(ruleFolder).usingDefaultRuleSet()
@@ -133,7 +144,7 @@ public class GetRuleSetsTests extends RestTest
/** Check we can get the reason that a rule set is included in the list. */
@Test (groups = { TestGroup.REST_API, TestGroup.RULES })
public void getRuleSetsAndInclusionType()
public void getRuleSetsAndOwnedInclusionType()
{
STEP("Get the rule sets and inclusion type");
RestRuleSetModelsCollection ruleSets = restClient.authenticateUser(user).withCoreAPI()
@@ -148,6 +159,36 @@ public class GetRuleSetsTests extends RestTest
ruleSets.assertThat().entriesListCountIs(1);
}
/** Check we can tell that a rule set has been inherited. */
@Test (groups = { TestGroup.REST_API, TestGroup.RULES })
public void getRuleSetsAndInheritedInclusionType()
{
STEP("Get the rule sets and inclusion type");
RestRuleSetModelsCollection ruleSets = restClient.authenticateUser(user).withCoreAPI()
.usingNode(inheritingChildFolder)
.include("inclusionType")
.getListOfRuleSets();
restClient.assertStatusCodeIs(OK);
ruleSets.getEntries().get(0).onModel()
.assertThat().field("inclusionType").is("inherited")
.assertThat().field("id").is(ruleSetId);
ruleSets.assertThat().entriesListCountIs(1);
}
/** Check that a rule set is not inherited if inheriting is disabled. */
@Test (groups = { TestGroup.REST_API, TestGroup.RULES })
public void getRuleSetsWithoutInheriting()
{
STEP("Get the rule sets and inclusion type");
RestRuleSetModelsCollection ruleSets = restClient.authenticateUser(user).withCoreAPI()
.usingNode(notInheritingChildFolder)
.getListOfRuleSets();
restClient.assertStatusCodeIs(OK);
ruleSets.assertThat().entriesListCountIs(0);
}
/** Check we can get a rule set by its id. */
@Test (groups = { TestGroup.REST_API, TestGroup.RULES, TestGroup.SANITY })
public void getRuleSetById()

View File

@@ -189,7 +189,6 @@ public class GetRulesTests extends RestTest
.createSingleRule(ruleModel);
RestRuleModel expectedRuleModel = createRuleModelWithModifiedValues();
expectedRuleModel.setActions(addActionContextParams(expectedRuleModel.getActions()));
expectedRuleModel.setTriggers(List.of("update"));
expectedRuleModel.setConditions(createEmptyConditionModel());
@@ -212,7 +211,6 @@ public class GetRulesTests extends RestTest
.createSingleRule(ruleModel);
RestRuleModel expectedRuleModel = createRuleModelWithDefaultValues();
expectedRuleModel.setActions(addActionContextParams(expectedRuleModel.getActions()));
expectedRuleModel.setTriggers(List.of("inbound"));
expectedRuleModel.setConditions(createEmptyConditionModel());

View File

@@ -234,7 +234,7 @@ public class RuleSetLinksTests extends RestTest
STEP("Assert link result is 400");
restClient.assertStatusCodeIs(BAD_REQUEST)
.assertLastError().containsSummary(
"Unable to link to a ruleset because the folder has pre-existing rules or is already linked to a ruleset.");
"Unable to link to a rule set because the folder has pre-existing rules or is already linked to a rule set.");
}
/**

View File

@@ -26,13 +26,13 @@
package org.alfresco.rest.rules;
import java.io.Serializable;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.alfresco.rest.model.RestActionBodyExecTemplateModel;
import org.alfresco.rest.model.RestCompositeConditionDefinitionModel;
import org.alfresco.rest.model.RestRuleModel;
import org.alfresco.rest.model.RestSimpleConditionDefinitionModel;
public class RulesTestsUtils
{
@@ -44,6 +44,8 @@ public class RulesTestsUtils
static final boolean RULE_SHARED_DEFAULT = false;
static final String RULE_ERROR_SCRIPT_DEFAULT = "error-script";
static final List<String> ruleTriggersDefault = List.of("inbound", "update", "outbound");
static final boolean INVERTED = true;
static final String AND = "and";
/**
* Create a rule model filled with default values.
@@ -102,16 +104,6 @@ public class RulesTestsUtils
return restActionModel;
}
public static List<RestActionBodyExecTemplateModel> addActionContextParams(List<RestActionBodyExecTemplateModel> inputActions)
{
inputActions.forEach(inputAction -> {
final Map<String, Serializable> params = new HashMap<>((Map<String, Serializable>) inputAction.getParams());
params.put("actionContext", "rule");
inputAction.setParams(params);
});
return inputActions;
}
public static RestActionBodyExecTemplateModel createCustomActionModel(String actionDefinitionId, Map<String, Serializable> params)
{
RestActionBodyExecTemplateModel restActionModel = new RestActionBodyExecTemplateModel();
@@ -123,8 +115,59 @@ public class RulesTestsUtils
public static RestCompositeConditionDefinitionModel createEmptyConditionModel()
{
RestCompositeConditionDefinitionModel conditions = new RestCompositeConditionDefinitionModel();
conditions.setInverted(false);
conditions.setBooleanMode("and");
conditions.setInverted(!INVERTED);
conditions.setBooleanMode(AND);
return conditions;
}
public static RestCompositeConditionDefinitionModel createVariousConditions()
{
return createCompositeCondition(List.of(
createCompositeCondition(!INVERTED, List.of(
createSimpleCondition("cm:created", "less_than", "2022-09-01T12:59:00.000+02:00"),
createSimpleCondition("cm:creator", "ends", "ski"),
createSimpleCondition("size", "greater_than", "90000000"),
createSimpleCondition("mimetype", "equals", "video/3gpp"),
createSimpleCondition("encoding", "equals", "utf-8"),
createSimpleCondition("type", "equals", "cm:folder"),
createSimpleCondition("tag", "equals", "uat")
)),
createCompositeCondition(INVERTED, List.of(
createSimpleCondition("aspect", "equals", "audio:audio"),
createSimpleCondition("cm:modelVersion", "begins", "1.")
))
));
}
public static RestSimpleConditionDefinitionModel createSimpleCondition(String field, String comparator, String parameter)
{
RestSimpleConditionDefinitionModel simpleCondition = new RestSimpleConditionDefinitionModel();
simpleCondition.setField(field);
simpleCondition.setComparator(comparator);
simpleCondition.setParameter(parameter);
return simpleCondition;
}
public static RestCompositeConditionDefinitionModel createCompositeCondition(List<RestCompositeConditionDefinitionModel> compositeConditions)
{
return createCompositeCondition(AND, !INVERTED, compositeConditions, null);
}
public static RestCompositeConditionDefinitionModel createCompositeCondition(boolean inverted,
List<RestSimpleConditionDefinitionModel> simpleConditions)
{
return createCompositeCondition(AND, inverted, null, simpleConditions);
}
private static RestCompositeConditionDefinitionModel createCompositeCondition(String booleanMode, boolean inverted,
List<RestCompositeConditionDefinitionModel> compositeConditions, List<RestSimpleConditionDefinitionModel> simpleConditions)
{
RestCompositeConditionDefinitionModel compositeCondition = new RestCompositeConditionDefinitionModel();
compositeCondition.setBooleanMode(booleanMode);
compositeCondition.setInverted(inverted);
compositeCondition.setCompositeConditions(compositeConditions);
compositeCondition.setSimpleConditions(simpleConditions);
return compositeCondition;
}
}

View File

@@ -36,6 +36,8 @@ import static org.springframework.http.HttpStatus.OK;
import java.util.List;
import com.google.common.collect.ImmutableMap;
import org.alfresco.rest.RestTest;
import org.alfresco.rest.model.RestActionBodyExecTemplateModel;
import org.alfresco.rest.model.RestRuleModel;
@@ -198,6 +200,29 @@ public class UpdateRulesTests extends RestTest
updatedRule.assertThat().field("isShared").isNotNull();
}
/** Check we can use the POST response to create the new rule. */
@Test (groups = { TestGroup.REST_API, TestGroup.RULES, TestGroup.SANITY })
public void updateCopyRuleWithResponseFromPOST()
{
FolderModel destination = dataContent.usingUser(user).usingSite(site).createFolder();
RestActionBodyExecTemplateModel copyAction = new RestActionBodyExecTemplateModel();
copyAction.setActionDefinitionId("copy");
copyAction.setParams(ImmutableMap.of("destination-folder", destination.getNodeRef()));
RestRuleModel rule = createAndSaveRule("Rule name", List.of(copyAction));
STEP("Try to update the rule.");
rule.setName("Updated rule name");
RestRuleModel updatedRule = restClient.authenticateUser(user).withCoreAPI().usingNode(ruleFolder).usingDefaultRuleSet()
.include("isShared")
.updateRule(rule.getId(), rule);
restClient.assertStatusCodeIs(OK);
updatedRule.assertThat().field("name").is("Updated rule name")
.assertThat().field("actions.actionDefinitionId").is(List.of("copy"))
.assertThat().field("actions.params").is(List.of(ImmutableMap.of("destination-folder", destination.getNodeRef())));
}
private RestRuleModel createAndSaveRule(String name)
{
return createAndSaveRule(name, List.of(createDefaultActionModel()));

View File

@@ -9,7 +9,7 @@
<parent>
<groupId>org.alfresco</groupId>
<artifactId>alfresco-community-repo-tests</artifactId>
<version>17.102-SNAPSHOT</version>
<version>17.113</version>
</parent>
<developers>

View File

@@ -7,7 +7,7 @@
<parent>
<groupId>org.alfresco</groupId>
<artifactId>alfresco-community-repo-packaging</artifactId>
<version>17.102-SNAPSHOT</version>
<version>17.113</version>
</parent>
<properties>

25
pom.xml
View File

@@ -2,7 +2,7 @@
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<artifactId>alfresco-community-repo</artifactId>
<version>17.102-SNAPSHOT</version>
<version>17.113</version>
<packaging>pom</packaging>
<name>Alfresco Community Repo Parent</name>
@@ -75,7 +75,7 @@
<dependency.commons-httpclient.version>3.1-HTTPCLIENT-1265</dependency.commons-httpclient.version>
<dependency.xercesImpl.version>2.12.2</dependency.xercesImpl.version>
<dependency.slf4j.version>1.7.36</dependency.slf4j.version>
<dependency.gytheio.version>0.16</dependency.gytheio.version>
<dependency.gytheio.version>0.17</dependency.gytheio.version>
<dependency.groovy.version>3.0.12</dependency.groovy.version>
<dependency.tika.version>2.4.1</dependency.tika.version>
<dependency.spring-security.version>5.7.2</dependency.spring-security.version>
@@ -84,7 +84,10 @@
<dependency.ooxml-schemas.version>1.4</dependency.ooxml-schemas.version>
<dependency.keycloak.version>18.0.0</dependency.keycloak.version>
<dependency.jboss.logging.version>3.5.0.Final</dependency.jboss.logging.version>
<dependency.camel.version>3.15.0</dependency.camel.version> <!-- when bumping this version, please keep track/sync with included netty.io dependencies (can cause dependency conflicts)-->
<dependency.camel.version>3.18.2</dependency.camel.version> <!-- when bumping this version, please keep track/sync with included netty.io dependencies -->
<dependency.netty.version>4.1.79.Final</dependency.netty.version> <!-- must be in sync with camels transitive dependencies, e.g.: netty-common -->
<dependency.netty.qpid.version>4.1.72.Final</dependency.netty.qpid.version> <!-- must be in sync with camels transitive dependencies: native-unix-common/native-epoll/native-kqueue -->
<dependency.netty-tcnative.version>2.0.53.Final</dependency.netty-tcnative.version> <!-- must be in sync with camels transitive dependencies -->
<dependency.activemq.version>5.17.1</dependency.activemq.version>
<dependency.apache-compress.version>1.21</dependency.apache-compress.version>
<dependency.apache.taglibs.version>1.2.5</dependency.apache.taglibs.version>
@@ -107,7 +110,7 @@
<dependency.jakarta-json-path.version>2.7.0</dependency.jakarta-json-path.version>
<dependency.jakarta-rpc-api.version>1.1.4</dependency.jakarta-rpc-api.version>
<alfresco.googledrive.version>3.2.2</alfresco.googledrive.version>
<alfresco.googledrive.version>3.2.3-A2</alfresco.googledrive.version>
<alfresco.aos-module.version>1.4.1</alfresco.aos-module.version>
<alfresco.api-explorer.version>7.2.1</alfresco.api-explorer.version> <!-- Also in alfresco-enterprise-share -->
@@ -148,7 +151,7 @@
<connection>scm:git:https://github.com/Alfresco/alfresco-community-repo.git</connection>
<developerConnection>scm:git:https://github.com/Alfresco/alfresco-community-repo.git</developerConnection>
<url>https://github.com/Alfresco/alfresco-community-repo</url>
<tag>HEAD</tag>
<tag>17.113</tag>
</scm>
<distributionManagement>
@@ -851,6 +854,18 @@
<artifactId>camel-mock</artifactId>
<version>${dependency.camel.version}</version>
</dependency>
<!-- Netty non-transitive dependencies declared for depending projects usage in conjunction with Camel's other transitive netty dependencies -->
<dependency>
<groupId>io.netty</groupId>
<artifactId>netty-handler-proxy</artifactId>
<version>${dependency.netty.version}</version>
</dependency>
<dependency>
<groupId>io.netty</groupId>
<artifactId>netty-tcnative-classes</artifactId>
<version>${dependency.netty-tcnative.version}</version>
</dependency>
<dependency>
<groupId>org.apache.taglibs</groupId>
<artifactId>taglibs-standard-spec</artifactId>

View File

@@ -7,7 +7,7 @@
<parent>
<groupId>org.alfresco</groupId>
<artifactId>alfresco-community-repo</artifactId>
<version>17.102-SNAPSHOT</version>
<version>17.113</version>
</parent>
<dependencies>

View File

@@ -180,6 +180,12 @@ public class NodeValidator
}
}
/**
* Verifies if rule set node or folder node's default rule set is shared
* @param ruleSetNodeRef
* @param folderNodeRef
* @return
*/
public boolean isRuleSetNotNullAndShared(final NodeRef ruleSetNodeRef, final NodeRef folderNodeRef)
{
if (ruleSetNodeRef == null && folderNodeRef != null)

View File

@@ -31,6 +31,7 @@ import org.alfresco.rest.api.model.rules.Rule;
import org.alfresco.service.Experimental;
import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.cmr.rule.RuleService;
import org.alfresco.service.namespace.NamespaceService;
/** Responsible for creating {@link Rule} objects. */
@Experimental
@@ -39,10 +40,11 @@ public class RuleLoader
public static final String IS_SHARED = "isShared";
private RuleService ruleService;
private NodeValidator nodeValidator;
private NamespaceService namespaceService;
public Rule loadRule(org.alfresco.service.cmr.rule.Rule ruleModel, List<String> includes)
{
Rule rule = Rule.from(ruleModel);
Rule rule = Rule.from(ruleModel, namespaceService);
if (includes != null && includes.contains(IS_SHARED))
{
NodeRef ruleSet = ruleService.getRuleSetNode(ruleModel.getNodeRef());
@@ -61,4 +63,9 @@ public class RuleLoader
{
this.nodeValidator = nodeValidator;
}
public void setNamespaceService(NamespaceService namespaceService)
{
this.namespaceService = namespaceService;
}
}

View File

@@ -29,7 +29,7 @@ package org.alfresco.rest.api.impl.rules;
import static java.util.stream.Collectors.toList;
import java.util.List;
import java.util.Optional;
import java.util.Objects;
import org.alfresco.repo.rule.RuleModel;
import org.alfresco.repo.rule.RuntimeRuleService;
@@ -59,10 +59,13 @@ public class RuleSetsImpl implements RuleSets
{
NodeRef folderNode = validator.validateFolderNode(folderNodeId, false);
NodeRef ruleSetNode = ruleService.getRuleSetNode(folderNode);
List<RuleSet> ruleSets = Optional.ofNullable(ruleSetNode)
.map(nodeRef -> ruleSetLoader.loadRuleSet(nodeRef, folderNode, includes))
.stream().collect(toList());
List<RuleSet> ruleSets = ruleService.getNodesSupplyingRuleSets(folderNode)
.stream()
.map(ruleService::getRuleSetNode)
.filter(Objects::nonNull)
.map(nodeRef -> ruleSetLoader.loadRuleSet(nodeRef, folderNode, includes))
.distinct()
.collect(toList());
return ListPage.of(ruleSets, paging);
}
@@ -93,7 +96,7 @@ public class RuleSetsImpl implements RuleSets
//The folder shouldn't have any pre-existing rules
if (ruleService.hasRules(folderNodeRef)) {
throw new InvalidArgumentException("Unable to link to a ruleset because the folder has pre-existing rules or is already linked to a ruleset.");
throw new InvalidArgumentException("Unable to link to a rule set because the folder has pre-existing rules or is already linked to a rule set.");
}
// Create the destination folder as a secondary child of the first

View File

@@ -41,6 +41,7 @@ import org.alfresco.service.Experimental;
import org.alfresco.service.cmr.action.CompositeAction;
import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.cmr.rule.RuleService;
import org.alfresco.service.namespace.NamespaceService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -55,6 +56,7 @@ public class RulesImpl implements Rules
private RuleLoader ruleLoader;
private ActionParameterConverter actionParameterConverter;
private ActionPermissionValidator actionPermissionValidator;
private NamespaceService namespaceService;
@Override
public CollectionWithPagingInfo<Rule> getRules(final String folderNodeId,
@@ -63,9 +65,10 @@ public class RulesImpl implements Rules
final Paging paging)
{
final NodeRef folderNodeRef = validator.validateFolderNode(folderNodeId, false);
validator.validateRuleSetNode(ruleSetId, folderNodeRef);
NodeRef ruleSetNode = validator.validateRuleSetNode(ruleSetId, folderNodeRef);
NodeRef owningFolder = ruleService.getOwningNodeRef(ruleSetNode);
final List<Rule> rules = ruleService.getRules(folderNodeRef).stream()
final List<Rule> rules = ruleService.getRules(owningFolder, false).stream()
.map(ruleModel -> loadRuleAndConvertActionParams(ruleModel, includes))
.collect(Collectors.toList());
@@ -123,7 +126,7 @@ public class RulesImpl implements Rules
private org.alfresco.service.cmr.rule.Rule mapToServiceModelAndValidateActions(Rule rule)
{
final org.alfresco.service.cmr.rule.Rule serviceModelRule = rule.toServiceModel(nodes);
final org.alfresco.service.cmr.rule.Rule serviceModelRule = rule.toServiceModel(nodes, namespaceService);
final CompositeAction compositeAction = (CompositeAction) serviceModelRule.getAction();
compositeAction.getActions().forEach(action -> action.setParameterValues(
actionParameterConverter.getConvertedParams(action.getParameterValues(), action.getActionDefinitionName())));
@@ -133,7 +136,6 @@ public class RulesImpl implements Rules
private Rule loadRuleAndConvertActionParams(org.alfresco.service.cmr.rule.Rule ruleModel, List<String> includes)
{
final Rule rule = ruleLoader.loadRule(ruleModel, includes);
rule.getActions()
.forEach(a -> a.setParams(a.getParams().entrySet()
@@ -174,4 +176,9 @@ public class RulesImpl implements Rules
{
this.actionPermissionValidator = actionPermissionValidator;
}
public void setNamespaceService(NamespaceService namespaceService)
{
this.namespaceService = namespaceService;
}
}

View File

@@ -26,6 +26,8 @@
package org.alfresco.rest.api.model.rules;
import static org.alfresco.repo.action.access.ActionAccessRestriction.ACTION_CONTEXT_PARAM_NAME;
import java.io.Serializable;
import java.util.HashMap;
import java.util.List;
@@ -60,7 +62,9 @@ public class Action
final Action.Builder builder = builder().actionDefinitionId(actionModel.getActionDefinitionName());
if (actionModel.getParameterValues() != null)
{
builder.params(new HashMap<>(actionModel.getParameterValues()));
Map<String, Serializable> params = new HashMap<>(actionModel.getParameterValues());
params.remove(ACTION_CONTEXT_PARAM_NAME);
builder.params(params);
}
return builder.create();

View File

@@ -32,8 +32,10 @@ import java.util.Objects;
import java.util.Optional;
import java.util.stream.Collectors;
import org.alfresco.rest.api.Nodes;
import org.alfresco.service.Experimental;
import org.alfresco.service.cmr.action.ActionCondition;
import org.alfresco.service.namespace.NamespaceService;
import org.apache.commons.collections.CollectionUtils;
@Experimental
@@ -50,7 +52,7 @@ public class CompositeCondition
* @param actionConditions - list of {@link ActionCondition} service POJOs
* @return {@link CompositeCondition} REST model
*/
public static CompositeCondition from(final List<ActionCondition> actionConditions)
public static CompositeCondition from(final List<ActionCondition> actionConditions, final NamespaceService namespaceService)
{
if (actionConditions == null)
{
@@ -62,7 +64,7 @@ public class CompositeCondition
// group action conditions by inversion flag
actionConditions.stream().filter(Objects::nonNull).collect(Collectors.groupingBy(ActionCondition::getInvertCondition))
// map action condition sub lists
.forEach((inverted, actionConditionsPart) -> Optional.ofNullable(CompositeCondition.ofActionConditions(actionConditionsPart, inverted, ConditionOperator.AND))
.forEach((inverted, actionConditionsPart) -> Optional.ofNullable(CompositeCondition.ofActionConditions(actionConditionsPart, namespaceService, inverted, ConditionOperator.AND))
// if composite condition present add to final list
.ifPresent(compositeCondition -> conditions.compositeConditions.add(compositeCondition)));
@@ -73,14 +75,14 @@ public class CompositeCondition
return conditions;
}
private static CompositeCondition ofActionConditions(final List<ActionCondition> actionConditions, final boolean inverted, final ConditionOperator conditionOperator)
private static CompositeCondition ofActionConditions(final List<ActionCondition> actionConditions, final NamespaceService namespaceService, final boolean inverted, final ConditionOperator conditionOperator)
{
if (actionConditions == null)
{
return null;
}
return ofSimpleConditions(SimpleCondition.listOf(actionConditions), inverted, conditionOperator);
return ofSimpleConditions(SimpleCondition.listOf(actionConditions, namespaceService), inverted, conditionOperator);
}
/**
@@ -112,6 +114,21 @@ public class CompositeCondition
.create();
}
public List<ActionCondition> toServiceModels(final Nodes nodes, final NamespaceService namespaceService)
{
final List<ActionCondition> actionConditions = new ArrayList<>();
if (CollectionUtils.isNotEmpty(simpleConditions))
{
simpleConditions.forEach(simpleCondition -> actionConditions.add(simpleCondition.toServiceModel(inverted, nodes, namespaceService)));
}
if (CollectionUtils.isNotEmpty(compositeConditions))
{
compositeConditions.forEach(compositeCondition -> actionConditions.addAll(compositeCondition.toServiceModels(nodes, namespaceService)));
}
return actionConditions;
}
public boolean isInverted()
{
return inverted;
@@ -131,6 +148,14 @@ public class CompositeCondition
return booleanMode.name().toLowerCase();
}
public void setBooleanMode(String booleanMode)
{
if (booleanMode != null)
{
this.booleanMode = ConditionOperator.valueOf(booleanMode.toUpperCase());
}
}
public void setBooleanMode(ConditionOperator booleanMode)
{
this.booleanMode = booleanMode;

View File

@@ -37,6 +37,7 @@ import org.alfresco.rest.framework.resource.UniqueId;
import org.alfresco.service.Experimental;
import org.alfresco.service.cmr.action.CompositeAction;
import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.namespace.NamespaceService;
import org.alfresco.util.GUID;
@Experimental
@@ -60,7 +61,7 @@ public class Rule
* @param ruleModel - {@link org.alfresco.service.cmr.rule.Rule} service POJO
* @return {@link Rule} REST model
*/
public static Rule from(final org.alfresco.service.cmr.rule.Rule ruleModel)
public static Rule from(final org.alfresco.service.cmr.rule.Rule ruleModel, final NamespaceService namespaceService)
{
if (ruleModel == null)
{
@@ -83,7 +84,7 @@ public class Rule
}
if (ruleModel.getAction() != null)
{
builder.conditions(CompositeCondition.from(ruleModel.getAction().getActionConditions()));
builder.conditions(CompositeCondition.from(ruleModel.getAction().getActionConditions(), namespaceService));
if (ruleModel.getAction().getCompensatingAction() != null && ruleModel.getAction().getCompensatingAction().getParameterValue(ScriptActionExecuter.PARAM_SCRIPTREF) != null)
{
builder.errorScript(ruleModel.getAction().getCompensatingAction().getParameterValue(ScriptActionExecuter.PARAM_SCRIPTREF).toString());
@@ -103,7 +104,7 @@ public class Rule
* @param nodes The nodes API.
* @return The rule service POJO.
*/
public org.alfresco.service.cmr.rule.Rule toServiceModel(Nodes nodes)
public org.alfresco.service.cmr.rule.Rule toServiceModel(final Nodes nodes, final NamespaceService namespaceService)
{
final org.alfresco.service.cmr.rule.Rule ruleModel = new org.alfresco.service.cmr.rule.Rule();
final NodeRef nodeRef = (id != null) ? nodes.validateOrLookupNode(id, null) : null;
@@ -124,6 +125,10 @@ public class Rule
compensatingAction.setParameterValue(ScriptActionExecuter.PARAM_SCRIPTREF, errorScript);
ruleModel.getAction().setCompensatingAction(compensatingAction);
}
if (conditions != null)
{
conditions.toServiceModels(nodes, namespaceService).forEach(condition -> ruleModel.getAction().addActionCondition(condition));
}
return ruleModel;
}

View File

@@ -30,7 +30,7 @@ public class RuleSetLink
{
/**
* This id is referring to the node id of the linked-to-folder which contains the ruleset(s)
* This id is referring to the node id of the linked-to-folder which contains the rule set(s)
*/
private String id;

View File

@@ -26,27 +26,41 @@
package org.alfresco.rest.api.model.rules;
import java.io.Serializable;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.UUID;
import java.util.stream.Collectors;
import org.alfresco.model.ContentModel;
import org.alfresco.repo.action.ActionConditionImpl;
import org.alfresco.repo.action.evaluator.CompareMimeTypeEvaluator;
import org.alfresco.repo.action.evaluator.ComparePropertyValueEvaluator;
import org.alfresco.repo.action.evaluator.HasAspectEvaluator;
import org.alfresco.repo.action.evaluator.HasChildEvaluator;
import org.alfresco.repo.action.evaluator.HasTagEvaluator;
import org.alfresco.repo.action.evaluator.HasVersionHistoryEvaluator;
import org.alfresco.repo.action.evaluator.InCategoryEvaluator;
import org.alfresco.repo.action.evaluator.IsSubTypeEvaluator;
import org.alfresco.repo.action.evaluator.NoConditionEvaluator;
import org.alfresco.repo.action.evaluator.compare.ComparePropertyValueOperation;
import org.alfresco.repo.action.evaluator.compare.ContentPropertyName;
import org.alfresco.rest.api.Nodes;
import org.alfresco.rest.framework.core.exceptions.EntityNotFoundException;
import org.alfresco.rest.framework.core.exceptions.InvalidArgumentException;
import org.alfresco.service.Experimental;
import org.alfresco.service.cmr.action.ActionCondition;
import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.namespace.NamespaceService;
import org.alfresco.service.namespace.QName;
import org.apache.commons.collections.CollectionUtils;
@Experimental
public class SimpleCondition
{
private static final String COMPARATOR_EQUALS = "equals";
private static final String CATEGORY_INVALID_MSG = "Category in condition is invalid";
public static final String PARAM_CATEGORY = "category";
public static final String PARAM_MIMETYPE = "mimetype";
private String field;
private String comparator;
@@ -58,7 +72,7 @@ public class SimpleCondition
* @param actionConditions - list of {@link ActionCondition} service POJOs
* @return list of {@link SimpleCondition} REST models
*/
public static List<SimpleCondition> listOf(final List<ActionCondition> actionConditions)
public static List<SimpleCondition> listOf(final List<ActionCondition> actionConditions, final NamespaceService namespaceService)
{
if (CollectionUtils.isEmpty(actionConditions))
{
@@ -66,7 +80,7 @@ public class SimpleCondition
}
return actionConditions.stream()
.map(SimpleCondition::from)
.map(actionCondition -> from(actionCondition, namespaceService))
.filter(Objects::nonNull)
.collect(Collectors.toList());
}
@@ -77,7 +91,7 @@ public class SimpleCondition
* @param actionCondition - {@link ActionCondition} service POJO
* @return {@link SimpleCondition} REST model
*/
public static SimpleCondition from(final ActionCondition actionCondition)
public static SimpleCondition from(final ActionCondition actionCondition, final NamespaceService namespaceService)
{
if (actionCondition == null || actionCondition.getActionConditionDefinitionName() == null || actionCondition.getParameterValues() == null)
{
@@ -87,27 +101,86 @@ public class SimpleCondition
switch (actionCondition.getActionConditionDefinitionName())
{
case ComparePropertyValueEvaluator.NAME:
return createComparePropertyValueCondition(actionCondition);
return createComparePropertyValueCondition(actionCondition, namespaceService);
case CompareMimeTypeEvaluator.NAME:
return createCompareMimeTypeCondition(actionCondition);
case HasAspectEvaluator.NAME:
return createHasAspectCondition(actionCondition);
case HasChildEvaluator.NAME:
return createHasChildCondition(actionCondition);
return createHasAspectCondition(actionCondition, namespaceService);
case HasTagEvaluator.NAME:
return createHasTagCondition(actionCondition);
case HasVersionHistoryEvaluator.NAME:
return createHasVersionHistoryCondition(actionCondition);
case InCategoryEvaluator.NAME:
return createInCategoryCondition(actionCondition);
case IsSubTypeEvaluator.NAME:
return createIsSubtypeCondition(actionCondition);
return createIsSubtypeCondition(actionCondition, namespaceService);
case NoConditionEvaluator.NAME:
default:
return null;
}
}
public ActionCondition toServiceModel(final boolean inverted, final Nodes nodes, final NamespaceService namespaceService)
{
if (field == null)
{
return null;
}
Map<String, Serializable> parameterValues = new HashMap<>();
String conditionDefinitionId;
switch (field)
{
case HasAspectEvaluator.PARAM_ASPECT:
conditionDefinitionId = HasAspectEvaluator.NAME;
parameterValues.put(HasAspectEvaluator.PARAM_ASPECT, QName.createQName(parameter, namespaceService));
break;
case HasTagEvaluator.PARAM_TAG:
conditionDefinitionId = HasTagEvaluator.NAME;
parameterValues.put(HasTagEvaluator.PARAM_TAG, parameter);
break;
case PARAM_CATEGORY:
conditionDefinitionId = InCategoryEvaluator.NAME;
parameterValues.put(InCategoryEvaluator.PARAM_CATEGORY_ASPECT, ContentModel.ASPECT_GEN_CLASSIFIABLE);
try
{
parameterValues.put(InCategoryEvaluator.PARAM_CATEGORY_VALUE, nodes.validateOrLookupNode(parameter, null));
} catch (EntityNotFoundException e) {
throw new InvalidArgumentException(CATEGORY_INVALID_MSG);
}
break;
case IsSubTypeEvaluator.PARAM_TYPE:
conditionDefinitionId = IsSubTypeEvaluator.NAME;
parameterValues.put(IsSubTypeEvaluator.PARAM_TYPE, QName.createQName(parameter, namespaceService));
break;
case PARAM_MIMETYPE:
conditionDefinitionId = CompareMimeTypeEvaluator.NAME;
parameterValues.put(ComparePropertyValueEvaluator.PARAM_PROPERTY, ContentModel.TYPE_CONTENT);
parameterValues.put(ComparePropertyValueEvaluator.PARAM_VALUE, parameter);
break;
default:
conditionDefinitionId = ComparePropertyValueEvaluator.NAME;
try
{
// if size or encoding create content property evaluator
ContentPropertyName.valueOf(field.toUpperCase());
parameterValues.put(ComparePropertyValueEvaluator.PARAM_CONTENT_PROPERTY, field.toUpperCase());
parameterValues.put(ComparePropertyValueEvaluator.PARAM_PROPERTY, ContentModel.TYPE_CONTENT);
}
catch (IllegalArgumentException ignore)
{
// else create common property evaluator
parameterValues.put(ComparePropertyValueEvaluator.PARAM_PROPERTY, QName.createQName(field, namespaceService));
}
parameterValues.put(ComparePropertyValueEvaluator.PARAM_OPERATION, comparator.toUpperCase());
parameterValues.put(ComparePropertyValueEvaluator.PARAM_VALUE, parameter);
break;
}
final ActionCondition actionCondition = new ActionConditionImpl(UUID.randomUUID().toString(), conditionDefinitionId, parameterValues);
actionCondition.setInvertCondition(inverted);
return actionCondition;
}
public String getField()
{
return field;
@@ -161,13 +234,14 @@ public class SimpleCondition
return Objects.hash(field, comparator, parameter);
}
private static SimpleCondition createComparePropertyValueCondition(final ActionCondition actionCondition) {
private static SimpleCondition createComparePropertyValueCondition(final ActionCondition actionCondition, final NamespaceService namespaceService)
{
final SimpleCondition.Builder builder = builder();
if (actionCondition.getParameterValues().get(ComparePropertyValueEvaluator.PARAM_CONTENT_PROPERTY) != null)
{
builder.field(actionCondition.getParameterValues().get(ComparePropertyValueEvaluator.PARAM_CONTENT_PROPERTY).toString().toLowerCase());
} else {
builder.field(actionCondition.getParameterValues().get(ComparePropertyValueEvaluator.PARAM_PROPERTY).toString().toLowerCase());
builder.field(((QName) actionCondition.getParameterValues().get(ComparePropertyValueEvaluator.PARAM_PROPERTY)).toPrefixString(namespaceService));
}
return builder
.comparator(actionCondition.getParameterValues().get(ComparePropertyValueEvaluator.PARAM_OPERATION).toString().toLowerCase())
@@ -175,65 +249,48 @@ public class SimpleCondition
.create();
}
private static SimpleCondition createCompareMimeTypeCondition(final ActionCondition actionCondition) {
private static SimpleCondition createCompareMimeTypeCondition(final ActionCondition actionCondition)
{
return builder()
.field(actionCondition.getParameterValues().get(ComparePropertyValueEvaluator.PARAM_PROPERTY).toString().toLowerCase())
.comparator(COMPARATOR_EQUALS)
.field(PARAM_MIMETYPE)
.comparator(ComparePropertyValueOperation.EQUALS.toString().toLowerCase())
.parameter(actionCondition.getParameterValues().get(ComparePropertyValueEvaluator.PARAM_VALUE).toString())
.create();
}
private static SimpleCondition createHasAspectCondition(final ActionCondition actionCondition) {
private static SimpleCondition createHasAspectCondition(final ActionCondition actionCondition, final NamespaceService namespaceService)
{
return builder()
.field(HasAspectEvaluator.PARAM_ASPECT)
.comparator(COMPARATOR_EQUALS)
.parameter(actionCondition.getParameterValues().get(HasAspectEvaluator.PARAM_ASPECT).toString())
.comparator(ComparePropertyValueOperation.EQUALS.toString().toLowerCase())
.parameter(((QName) actionCondition.getParameterValues().get(HasAspectEvaluator.PARAM_ASPECT)).toPrefixString(namespaceService))
.create();
}
private static SimpleCondition createHasChildCondition(final ActionCondition actionCondition) {
final SimpleCondition.Builder builder = builder();
if (actionCondition.getParameterValues().get(HasChildEvaluator.PARAM_ASSOC_TYPE) != null)
{
builder.field(actionCondition.getParameterValues().get(HasChildEvaluator.PARAM_ASSOC_TYPE).toString().toLowerCase());
} else {
builder.field(actionCondition.getParameterValues().get(HasChildEvaluator.PARAM_ASSOC_NAME).toString().toLowerCase());
}
return builder
.comparator(COMPARATOR_EQUALS)
.parameter(actionCondition.getParameterValues().get(ComparePropertyValueEvaluator.PARAM_VALUE).toString())
.create();
}
private static SimpleCondition createHasTagCondition(final ActionCondition actionCondition) {
private static SimpleCondition createHasTagCondition(final ActionCondition actionCondition)
{
return builder()
.field(HasTagEvaluator.PARAM_TAG)
.comparator(COMPARATOR_EQUALS)
.comparator(ComparePropertyValueOperation.EQUALS.toString().toLowerCase())
.parameter(actionCondition.getParameterValues().get(HasTagEvaluator.PARAM_TAG).toString())
.create();
}
private static SimpleCondition createHasVersionHistoryCondition(final ActionCondition actionCondition) {
private static SimpleCondition createInCategoryCondition(final ActionCondition actionCondition)
{
return builder()
.field(actionCondition.getParameterValues().get(ComparePropertyValueEvaluator.PARAM_PROPERTY).toString().toLowerCase())
.comparator(actionCondition.getParameterValues().get(ComparePropertyValueEvaluator.PARAM_OPERATION).toString().toLowerCase())
.parameter(actionCondition.getParameterValues().get(ComparePropertyValueEvaluator.PARAM_VALUE).toString())
.field(PARAM_CATEGORY)
.comparator(ComparePropertyValueOperation.EQUALS.toString().toLowerCase())
.parameter(((NodeRef) actionCondition.getParameterValues().get(InCategoryEvaluator.PARAM_CATEGORY_VALUE)).getId())
.create();
}
private static SimpleCondition createInCategoryCondition(final ActionCondition actionCondition) {
return builder()
.field(actionCondition.getParameterValues().get(InCategoryEvaluator.PARAM_CATEGORY_ASPECT).toString().toLowerCase())
.comparator(COMPARATOR_EQUALS)
.parameter(actionCondition.getParameterValues().get(InCategoryEvaluator.PARAM_CATEGORY_VALUE).toString())
.create();
}
private static SimpleCondition createIsSubtypeCondition(final ActionCondition actionCondition) {
private static SimpleCondition createIsSubtypeCondition(final ActionCondition actionCondition, final NamespaceService namespaceService)
{
return builder()
.field(IsSubTypeEvaluator.PARAM_TYPE)
.comparator(COMPARATOR_EQUALS)
.parameter(actionCondition.getParameterValues().get(IsSubTypeEvaluator.PARAM_TYPE).toString())
.comparator(ComparePropertyValueOperation.EQUALS.toString().toLowerCase())
.parameter(((QName) actionCondition.getParameterValues().get(IsSubTypeEvaluator.PARAM_TYPE)).toPrefixString(namespaceService))
.create();
}

View File

@@ -65,7 +65,7 @@ public class NodeRulesRelation implements RelationshipResourceAction.Read<Rule>,
/**
* List folder rules for given folder node's and rule set's IDs as a page.
*
* <p>
* - GET /nodes/{folderNodeId}/rule-sets/{ruleSetId}/rules
*
* @param folderNodeId - entity resource context for this relationship
@@ -87,7 +87,7 @@ public class NodeRulesRelation implements RelationshipResourceAction.Read<Rule>,
/**
* Get single folder rule for given node's, rule set's and rule's IDs.
*
* <p>
* - GET /nodes/{folderNodeId}/rule-sets/{ruleSetId}/rules/{ruleId}
*
* @param folderNodeId - entity resource context for this relationship
@@ -158,7 +158,7 @@ public class NodeRulesRelation implements RelationshipResourceAction.Read<Rule>,
/**
* Delete single folder rule for given node's, rule set's and rule's IDs.
*
* <p>
* - DELETE /nodes/{folderNodeId}/rule-sets/{ruleSetId}/rules/{ruleId}
*
* @param folderNodeId - entity resource context for this relationship

View File

@@ -886,6 +886,7 @@
<bean id="ruleLoader" class="org.alfresco.rest.api.impl.rules.RuleLoader">
<property name="ruleService" ref="RuleService" />
<property name="nodeValidator" ref="nodeValidator" />
<property name="namespaceService" ref="NamespaceService"/>
</bean>
<bean class="org.alfresco.rest.api.nodes.NodeRuleSetsRelation">
@@ -909,6 +910,7 @@
<property name="ruleLoader" ref="ruleLoader"/>
<property name="actionParameterConverter" ref="actionParameterConverter"/>
<property name="actionPermissionValidator" ref="actionPermissionValidator"/>
<property name="namespaceService" ref="NamespaceService"/>
</bean>
<bean id="Rules" class="org.springframework.aop.framework.ProxyFactoryBean">

View File

@@ -309,7 +309,7 @@ function sortByName(a, b)
function findUsers(searchTerm, maxResults, results)
{
var personRefs = people.getPeople(searchTerm, maxResults, "lastName", true);
var personRefs = people.getPeople(searchTerm+ " [hint:useCQ]", maxResults, "lastName", true);
// create person object for each result
for each(var personRef in personRefs)

View File

@@ -55,10 +55,14 @@ public class PreferenceServiceTest extends BaseWebScriptTest
private PersonService personService;
private static final String USER_ONE = "PreferenceTestOne" + System.currentTimeMillis();
private static final String USER_TWO = "PreferenceTestTwo" + System.currentTimeMillis();
private static final String USER_BAD = "PreferenceTestBad" + System.currentTimeMillis();
private static final String URL = "/api/people/" + USER_ONE + "/preferences";;
private static final String URL = "/api/people/" + USER_ONE + "/preferences";
private static final String URL2 = "/api/people/" + USER_TWO + "/preferences";
private static final int NUM_THREADS = 3;
private static Integer THREADS_FINISHED = 0;
@Override
protected void setUp() throws Exception
@@ -75,6 +79,7 @@ public class PreferenceServiceTest extends BaseWebScriptTest
// Create users
createUser(USER_ONE);
createUser(USER_TWO);
createUser(USER_BAD);
// Do tests as user one
@@ -103,7 +108,6 @@ public class PreferenceServiceTest extends BaseWebScriptTest
{
super.tearDown();
this.authenticationComponent.setCurrentUser(AuthenticationUtil.getAdminUserName());
}
public void testPreferences() throws Exception
@@ -193,6 +197,64 @@ public class PreferenceServiceTest extends BaseWebScriptTest
sendRequest(new GetRequest("/api/people/noExistUser/preferences"), 404);
}
public void testPreferencesMNT21901() throws Exception
{
String[] body = {
"{\"org\":{\"alfresco\":{\"share\":{\"forum\":{\"summary\":{\"dashlet\":{\"component-1-5\":{\"topics\":\"mine\"}}}}}}}}",
"{\"org\":{\"alfresco\":{\"share\":{\"forum\":{\"summary\":{\"dashlet\":{\"component-2-5\":{\"topics\":\"mine\"}}}}}}}}",
"{\"org\":{\"alfresco\":{\"share\":{\"forum\":{\"summary\":{\"dashlet\":{\"component-2-5\":{\"history\":\"1\"}}}}}}}}"
};
Thread[] threads = new Thread[NUM_THREADS];
for (int i = 0; i < NUM_THREADS; i++)
{
UpdatePreferencesThread t = new UpdatePreferencesThread(i, body[i]);
threads[i] = t;
t.start();
}
for (int i = 0; i < threads.length; i++)
{
threads[i].join();
}
authenticationComponent.setCurrentUser(USER_ONE);
if (THREADS_FINISHED != NUM_THREADS)
{
fail("An error has occurred when updating preferences in concurrency context");
}
}
private class UpdatePreferencesThread extends Thread
{
private String body;
UpdatePreferencesThread(int threadNum, String body)
{
super(UpdatePreferencesThread.class.getSimpleName() + "-" + threadNum);
this.body = body;
}
@Override
public void run()
{
authenticationComponent.setCurrentUser(USER_TWO);
try
{
Response resp = sendRequest(new PostRequest(URL2, body, "application/json"), 200);
assertEquals(0, resp.getContentLength());
THREADS_FINISHED++;
}
catch (Exception e)
{
// Intentionally empty
}
}
}
private JSONObject getPreferenceObj() throws JSONException
{
JSONObject jsonObject = new JSONObject();
@@ -208,5 +270,4 @@ public class PreferenceServiceTest extends BaseWebScriptTest
assertEquals(10, jsonObject.get("numberValue"));
assertEquals(BigDecimal.valueOf(3.142), jsonObject.get("numberValue2"));
}
}

View File

@@ -33,6 +33,7 @@ import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.BDDMockito.given;
import static org.mockito.BDDMockito.then;
import static org.mockito.Mockito.mock;
import java.util.Collection;
import java.util.List;
@@ -101,6 +102,8 @@ public class RuleSetsImplTest extends TestCase
given(nodeValidatorMock.validateRuleSetNode(RULE_SET_ID, FOLDER_NODE)).willReturn(RULE_SET_NODE);
given(ruleServiceMock.getRuleSetNode(FOLDER_NODE)).willReturn(RULE_SET_NODE);
given(ruleServiceMock.getNodesSupplyingRuleSets(FOLDER_NODE)).willReturn(List.of(FOLDER_NODE));
given(ruleSetLoaderMock.loadRuleSet(RULE_SET_NODE, FOLDER_NODE, INCLUDES)).willReturn(ruleSetMock);
}
@@ -113,6 +116,7 @@ public class RuleSetsImplTest extends TestCase
then(nodeValidatorMock).should().validateFolderNode(FOLDER_ID, false);
then(nodeValidatorMock).shouldHaveNoMoreInteractions();
then(ruleServiceMock).should().getNodesSupplyingRuleSets(FOLDER_NODE);
then(ruleServiceMock).should().getRuleSetNode(FOLDER_NODE);
then(ruleServiceMock).shouldHaveNoMoreInteractions();
@@ -133,6 +137,7 @@ public class RuleSetsImplTest extends TestCase
then(nodeValidatorMock).should().validateFolderNode(FOLDER_ID, false);
then(nodeValidatorMock).shouldHaveNoMoreInteractions();
then(ruleServiceMock).should().getNodesSupplyingRuleSets(FOLDER_NODE);
then(ruleServiceMock).should().getRuleSetNode(FOLDER_NODE);
then(ruleServiceMock).shouldHaveNoMoreInteractions();
@@ -140,6 +145,62 @@ public class RuleSetsImplTest extends TestCase
assertEquals(PAGING, actual.getPaging());
}
/** Check that a folder with a parent and grandparent can inherit rule sets from the grandparent, even if the parent has no rules. */
@Test
public void testGetInheritedRuleSets()
{
// Simulate a parent node without a rule set.
NodeRef parentNode = new NodeRef("parent://node/");
// Simulate a grandparent node providing a rule set.
NodeRef grandparentNode = new NodeRef("grandparent://node/");
RuleSet grandparentRuleSet = mock(RuleSet.class);
NodeRef grandparentRuleSetNode = new NodeRef("grandparent://rule-set/");
given(ruleServiceMock.getRuleSetNode(grandparentNode)).willReturn(grandparentRuleSetNode);
given(ruleSetLoaderMock.loadRuleSet(grandparentRuleSetNode, FOLDER_NODE, INCLUDES)).willReturn(grandparentRuleSet);
// These should be returned with the highest in hierarchy first.
given(ruleServiceMock.getNodesSupplyingRuleSets(FOLDER_NODE)).willReturn(List.of(grandparentNode, parentNode, FOLDER_NODE));
// Call the method under test.
CollectionWithPagingInfo<RuleSet> actual = ruleSets.getRuleSets(FOLDER_ID, INCLUDES, PAGING);
then(nodeValidatorMock).should().validateFolderNode(FOLDER_ID, false);
then(nodeValidatorMock).shouldHaveNoMoreInteractions();
then(ruleServiceMock).should().getNodesSupplyingRuleSets(FOLDER_NODE);
then(ruleServiceMock).should().getRuleSetNode(grandparentNode);
then(ruleServiceMock).should().getRuleSetNode(parentNode);
then(ruleServiceMock).should().getRuleSetNode(FOLDER_NODE);
then(ruleServiceMock).shouldHaveNoMoreInteractions();
Collection<RuleSet> expected = List.of(grandparentRuleSet, ruleSetMock);
assertEquals(expected, actual.getCollection());
assertEquals(PAGING, actual.getPaging());
}
/** When getting rule sets then only the first instance of each rule set should be included (ancestor first). */
@Test
public void testGetDuplicateRuleSets()
{
// Simulate a grandparent, parent and child with the grandparent linking to the child's rule set.
NodeRef grandparentNode = new NodeRef("grandparent://node/");
given(ruleServiceMock.getRuleSetNode(grandparentNode)).willReturn(RULE_SET_NODE);
NodeRef parentNode = new NodeRef("parent://node/");
RuleSet parentRuleSet = mock(RuleSet.class);
NodeRef parentRuleSetNode = new NodeRef("parent://rule-set/");
given(ruleServiceMock.getRuleSetNode(parentNode)).willReturn(parentRuleSetNode);
given(ruleSetLoaderMock.loadRuleSet(parentRuleSetNode, FOLDER_NODE, INCLUDES)).willReturn(parentRuleSet);
// These should be returned with the highest in hierarchy first.
given(ruleServiceMock.getNodesSupplyingRuleSets(FOLDER_NODE)).willReturn(List.of(grandparentNode, parentNode, FOLDER_NODE));
// Call the method under test.
CollectionWithPagingInfo<RuleSet> actual = ruleSets.getRuleSets(FOLDER_ID, INCLUDES, PAGING);
// The grandparent's linked rule set should be first and only appear once.
Collection<RuleSet> expected = List.of(ruleSetMock, parentRuleSet);
assertEquals(expected, actual.getCollection());
assertEquals(PAGING, actual.getPaging());
}
@Test
public void testGetRuleSetById()
{

View File

@@ -61,6 +61,7 @@ import org.alfresco.service.cmr.action.CompositeAction;
import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.cmr.repository.StoreRef;
import org.alfresco.service.cmr.rule.RuleService;
import org.alfresco.service.namespace.NamespaceService;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -87,6 +88,8 @@ public class RulesImplTest extends TestCase
@Mock
private Nodes nodesMock;
@Mock
private NamespaceService namespaceService;
@Mock
private NodeValidator nodeValidatorMock;
@Mock
private RuleService ruleServiceMock;
@@ -115,7 +118,8 @@ public class RulesImplTest extends TestCase
given(nodeValidatorMock.validateRuleNode(any(), any())).willReturn(RULE_NODE_REF);
given(ruleServiceMock.getRule(RULE_NODE_REF)).willReturn(ruleModel);
given(ruleServiceMock.getRules(FOLDER_NODE_REF)).willReturn(List.of(ruleModel));
given(ruleServiceMock.getRules(FOLDER_NODE_REF, false)).willReturn(List.of(ruleModel));
given(ruleServiceMock.getOwningNodeRef(RULE_SET_NODE_REF)).willReturn(FOLDER_NODE_REF);
given(ruleLoaderMock.loadRule(ruleModel, INCLUDE)).willReturn(ruleMock);
@@ -126,13 +130,15 @@ public class RulesImplTest extends TestCase
public void testGetRules()
{
given(ruleLoaderMock.loadRule(ruleModel, emptyList())).willReturn(ruleMock);
// when
final CollectionWithPagingInfo<Rule> rulesPage = rules.getRules(FOLDER_NODE_ID, RULE_SET_ID, INCLUDE, PAGING);
then(nodeValidatorMock).should().validateFolderNode(FOLDER_NODE_ID, false);
then(nodeValidatorMock).should().validateRuleSetNode(RULE_SET_ID, FOLDER_NODE_REF);
then(nodeValidatorMock).shouldHaveNoMoreInteractions();
then(ruleServiceMock).should().getRules(FOLDER_NODE_REF);
then(ruleServiceMock).should().getOwningNodeRef(RULE_SET_NODE_REF);
then(ruleServiceMock).should().getRules(FOLDER_NODE_REF, false);
then(ruleServiceMock).shouldHaveNoMoreInteractions();
then(ruleLoaderMock).should().loadRule(ruleModel, emptyList());
then(ruleLoaderMock).shouldHaveNoMoreInteractions();
@@ -148,12 +154,13 @@ public class RulesImplTest extends TestCase
@Test
public void testGetRules_emptyResult()
{
given(ruleServiceMock.getRules(any())).willReturn(emptyList());
given(ruleServiceMock.getRules(FOLDER_NODE_REF, false)).willReturn(emptyList());
// when
final CollectionWithPagingInfo<Rule> rulesPage = rules.getRules(FOLDER_NODE_ID, RULE_SET_ID, INCLUDE, PAGING);
then(ruleServiceMock).should().getRules(FOLDER_NODE_REF);
then(ruleServiceMock).should().getOwningNodeRef(RULE_SET_NODE_REF);
then(ruleServiceMock).should().getRules(FOLDER_NODE_REF, false);
then(ruleServiceMock).shouldHaveNoMoreInteractions();
assertThat(rulesPage)
.isNotNull()
@@ -284,7 +291,7 @@ public class RulesImplTest extends TestCase
public void testCreateRules()
{
List<Rule> ruleList = List.of(ruleMock);
given(ruleMock.toServiceModel(nodesMock)).willReturn(serviceRuleMock);
given(ruleMock.toServiceModel(nodesMock, namespaceService)).willReturn(serviceRuleMock);
given(serviceRuleMock.getAction()).willReturn(compositeAction);
given(ruleServiceMock.saveRule(FOLDER_NODE_REF, serviceRuleMock)).willAnswer(arg -> arg.getArguments()[1]);
given(ruleLoaderMock.loadRule(serviceRuleMock, INCLUDE)).willReturn(ruleMock);
@@ -300,7 +307,7 @@ public class RulesImplTest extends TestCase
then(actionParameterConverterMock).shouldHaveNoMoreInteractions();
then(actionPermissionValidatorMock).should().validateRulePermissions(serviceRuleMock);
then(actionPermissionValidatorMock).shouldHaveNoMoreInteractions();
then(ruleServiceMock).should().saveRule(FOLDER_NODE_REF, ruleMock.toServiceModel(nodesMock));
then(ruleServiceMock).should().saveRule(FOLDER_NODE_REF, ruleMock.toServiceModel(nodesMock, namespaceService));
then(ruleServiceMock).shouldHaveNoMoreInteractions();
List<Rule> expected = List.of(ruleMock);
assertThat(actual).isEqualTo(expected);
@@ -313,7 +320,7 @@ public class RulesImplTest extends TestCase
public void testCreateRules_defaultRuleSet()
{
List<Rule> ruleList = List.of(ruleMock);
given(ruleMock.toServiceModel(nodesMock)).willReturn(serviceRuleMock);
given(ruleMock.toServiceModel(nodesMock, namespaceService)).willReturn(serviceRuleMock);
given(ruleServiceMock.saveRule(FOLDER_NODE_REF, serviceRuleMock)).willAnswer(arg -> arg.getArguments()[1]);
given(ruleLoaderMock.loadRule(serviceRuleMock, INCLUDE)).willReturn(ruleMock);
given(serviceRuleMock.getAction()).willReturn(compositeAction);
@@ -328,7 +335,7 @@ public class RulesImplTest extends TestCase
then(actionParameterConverterMock).shouldHaveNoMoreInteractions();
then(actionPermissionValidatorMock).should().validateRulePermissions(serviceRuleMock);
then(actionPermissionValidatorMock).shouldHaveNoMoreInteractions();
then(ruleServiceMock).should().saveRule(FOLDER_NODE_REF, ruleMock.toServiceModel(nodesMock));
then(ruleServiceMock).should().saveRule(FOLDER_NODE_REF, ruleMock.toServiceModel(nodesMock, namespaceService));
then(ruleServiceMock).shouldHaveNoMoreInteractions();
List<Rule> expected = List.of(ruleMock);
assertThat(actual).isEqualTo(expected);
@@ -358,7 +365,7 @@ public class RulesImplTest extends TestCase
Rule ruleBodyMock = mock(Rule.class);
ruleBodyList.add(ruleBodyMock);
org.alfresco.service.cmr.rule.Rule serviceRuleMockInner = mock(org.alfresco.service.cmr.rule.Rule.class);
given(ruleBodyMock.toServiceModel(nodesMock)).willReturn(serviceRuleMockInner);
given(ruleBodyMock.toServiceModel(nodesMock, namespaceService)).willReturn(serviceRuleMockInner);
final CompositeAction compositeActionInner = new CompositeActionImpl(RULE_NODE_REF, "compositeActionInnerId");
compositeActionInner.addAction(new ActionImpl(FOLDER_NODE_REF, "actionInnerId", ACTION_DEFINITION_NAME, DUMMY_PARAMS));
given(serviceRuleMockInner.getAction()).willReturn(compositeActionInner);
@@ -377,8 +384,8 @@ public class RulesImplTest extends TestCase
then(nodeValidatorMock).shouldHaveNoMoreInteractions();
for (Rule ruleBody : ruleBodyList)
{
then(actionPermissionValidatorMock).should().validateRulePermissions(ruleBody.toServiceModel(nodesMock));
then(ruleServiceMock).should().saveRule(FOLDER_NODE_REF, ruleBody.toServiceModel(nodesMock));
then(actionPermissionValidatorMock).should().validateRulePermissions(ruleBody.toServiceModel(nodesMock, namespaceService));
then(ruleServiceMock).should().saveRule(FOLDER_NODE_REF, ruleBody.toServiceModel(nodesMock, namespaceService));
}
then(actionParameterConverterMock).should(times(3)).getConvertedParams(DUMMY_PARAMS, ACTION_DEFINITION_NAME);
then(actionParameterConverterMock).shouldHaveNoMoreInteractions();
@@ -431,7 +438,7 @@ public class RulesImplTest extends TestCase
@Test
public void testUpdateRuleById()
{
given(ruleMock.toServiceModel(nodesMock)).willReturn(serviceRuleMock);
given(ruleMock.toServiceModel(nodesMock, namespaceService)).willReturn(serviceRuleMock);
given(ruleServiceMock.saveRule(FOLDER_NODE_REF, serviceRuleMock)).willAnswer(a -> a.getArguments()[1]);
given(serviceRuleMock.getAction()).willReturn(compositeAction);
given(ruleLoaderMock.loadRule(serviceRuleMock, INCLUDE)).willReturn(ruleMock);

View File

@@ -27,6 +27,7 @@
package org.alfresco.rest.api.model.rules;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Mockito.mock;
import java.io.Serializable;
import java.util.ArrayList;
@@ -39,12 +40,18 @@ import org.alfresco.repo.action.ActionConditionImpl;
import org.alfresco.repo.action.evaluator.ComparePropertyValueEvaluator;
import org.alfresco.service.Experimental;
import org.alfresco.service.cmr.action.ActionCondition;
import org.alfresco.service.namespace.NamespaceService;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.junit.MockitoJUnitRunner;
@Experimental
@RunWith(MockitoJUnitRunner.class)
public class CompositeConditionTest
{
private final NamespaceService namespaceService = mock(NamespaceService.class);
@Test
public void testFrom()
{
@@ -64,7 +71,7 @@ public class CompositeConditionTest
));
// when
final CompositeCondition actualCompositeCondition = CompositeCondition.from(actionConditions);
final CompositeCondition actualCompositeCondition = CompositeCondition.from(actionConditions, namespaceService);
assertThat(actualCompositeCondition).isNotNull().usingRecursiveComparison().isEqualTo(expectedCompositeCondition);
}
@@ -76,7 +83,7 @@ public class CompositeConditionTest
final CompositeCondition expectedCompositeCondition = CompositeCondition.builder().create();
// when
final CompositeCondition actualCompositeCondition = CompositeCondition.from(actionConditions);
final CompositeCondition actualCompositeCondition = CompositeCondition.from(actionConditions, namespaceService);
assertThat(actualCompositeCondition).isNotNull().usingRecursiveComparison().isEqualTo(expectedCompositeCondition);
}
@@ -85,7 +92,7 @@ public class CompositeConditionTest
public void testFromNullValue()
{
// when
final CompositeCondition actualCompositeCondition = CompositeCondition.from(null);
final CompositeCondition actualCompositeCondition = CompositeCondition.from(null, namespaceService);
assertThat(actualCompositeCondition).isNull();
}
@@ -98,7 +105,7 @@ public class CompositeConditionTest
final CompositeCondition expectedCompositeCondition = CompositeCondition.builder().create();
// when
final CompositeCondition actualCompositeCondition = CompositeCondition.from(actionConditions);
final CompositeCondition actualCompositeCondition = CompositeCondition.from(actionConditions, namespaceService);
assertThat(actualCompositeCondition).isNotNull().usingRecursiveComparison().isEqualTo(expectedCompositeCondition);
}

View File

@@ -42,6 +42,7 @@ import org.alfresco.service.cmr.action.ActionCondition;
import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.cmr.repository.StoreRef;
import org.alfresco.service.cmr.rule.RuleType;
import org.alfresco.service.namespace.NamespaceService;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.junit.MockitoJUnitRunner;
@@ -60,6 +61,8 @@ public class RuleTest
private static final String ACTION_DEFINITION_NAME = "action-def-name";
private static final String ERROR_SCRIPT = "error-script-ref";
private final NamespaceService namespaceService = mock(NamespaceService.class);
@Test
public void testFrom()
{
@@ -67,7 +70,7 @@ public class RuleTest
final Rule expectedRule = createRuleWithDefaultValues();
// when
final Rule actualRule = Rule.from(ruleModel);
final Rule actualRule = Rule.from(ruleModel, namespaceService);
assertThat(actualRule).isNotNull().usingRecursiveComparison().isEqualTo(expectedRule);
@@ -80,7 +83,7 @@ public class RuleTest
final Rule expectedRule = Rule.builder().enabled(true).create();
// when
final Rule actualRule = Rule.from(ruleModel);
final Rule actualRule = Rule.from(ruleModel, namespaceService);
assertThat(actualRule).isNotNull().usingRecursiveComparison().isEqualTo(expectedRule);
@@ -96,7 +99,7 @@ public class RuleTest
final org.alfresco.service.cmr.action.Action expectedCompensatingActionModel = createCompensatingActionModel();
// when
final org.alfresco.service.cmr.rule.Rule actualRuleModel = rule.toServiceModel(nodesMock);
final org.alfresco.service.cmr.rule.Rule actualRuleModel = rule.toServiceModel(nodesMock, namespaceService);
then(nodesMock).should().validateOrLookupNode(RULE_ID, null);
then(nodesMock).shouldHaveNoMoreInteractions();
@@ -121,7 +124,7 @@ public class RuleTest
expectedRuleModel.setRuleDisabled(true);
// when
final org.alfresco.service.cmr.rule.Rule actualRuleModel = rule.toServiceModel(nodesMock);
final org.alfresco.service.cmr.rule.Rule actualRuleModel = rule.toServiceModel(nodesMock, namespaceService);
then(nodesMock).shouldHaveNoInteractions();
assertThat(actualRuleModel)
@@ -131,6 +134,20 @@ public class RuleTest
.isEqualTo(expectedRuleModel);
}
private Rule createRuleWithDefaultValues() {
return Rule.builder()
.id(RULE_ID)
.name(RULE_NAME)
.description(RULE_DESCRIPTION)
.enabled(RULE_ENABLED)
.cascade(RULE_CASCADE)
.asynchronous(RULE_ASYNC)
.triggers(List.of(RuleTrigger.INBOUND, RuleTrigger.UPDATE))
.errorScript(ERROR_SCRIPT)
.conditions(CompositeCondition.from(Collections.emptyList(), namespaceService))
.create();
}
private static org.alfresco.service.cmr.rule.Rule createRuleModel() {
final NodeRef nodeRef = new NodeRef(StoreRef.STORE_REF_WORKSPACE_SPACESSTORE, RULE_ID);
final org.alfresco.service.cmr.rule.Rule ruleModel = new org.alfresco.service.cmr.rule.Rule(nodeRef);
@@ -160,18 +177,4 @@ public class RuleTest
return compensatingActionModel;
}
private static Rule createRuleWithDefaultValues() {
return Rule.builder()
.id(RULE_ID)
.name(RULE_NAME)
.description(RULE_DESCRIPTION)
.enabled(RULE_ENABLED)
.cascade(RULE_CASCADE)
.asynchronous(RULE_ASYNC)
.triggers(List.of(RuleTrigger.INBOUND, RuleTrigger.UPDATE))
.errorScript(ERROR_SCRIPT)
.conditions(CompositeCondition.from(Collections.emptyList()))
.create();
}
}

View File

@@ -27,6 +27,8 @@
package org.alfresco.rest.api.model.rules;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.BDDMockito.given;
import static org.mockito.Mockito.mock;
import java.io.Serializable;
import java.util.ArrayList;
@@ -36,6 +38,7 @@ import java.util.List;
import java.util.Map;
import java.util.Objects;
import org.alfresco.model.ContentModel;
import org.alfresco.repo.action.ActionConditionImpl;
import org.alfresco.repo.action.evaluator.CompareMimeTypeEvaluator;
import org.alfresco.repo.action.evaluator.ComparePropertyValueEvaluator;
@@ -46,28 +49,54 @@ import org.alfresco.repo.action.evaluator.HasVersionHistoryEvaluator;
import org.alfresco.repo.action.evaluator.InCategoryEvaluator;
import org.alfresco.repo.action.evaluator.IsSubTypeEvaluator;
import org.alfresco.repo.action.evaluator.NoConditionEvaluator;
import org.alfresco.repo.action.evaluator.compare.ComparePropertyValueOperation;
import org.alfresco.repo.action.evaluator.compare.ContentPropertyName;
import org.alfresco.rest.api.Nodes;
import org.alfresco.service.Experimental;
import org.alfresco.service.cmr.action.ActionCondition;
import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.cmr.repository.StoreRef;
import org.alfresco.service.namespace.NamespaceService;
import org.alfresco.service.namespace.QName;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.junit.MockitoJUnitRunner;
@Experimental
@RunWith(MockitoJUnitRunner.class)
public class SimpleConditionTest
{
private static final boolean NULL_RESULT = true;
private static final boolean NOT_INVERTED = false;
private static final String PARAMETER_DEFAULT = "value";
private final Nodes nodes = mock(Nodes.class);
private final NamespaceService namespaceService = mock(NamespaceService.class);
@Before
public void setUp() throws Exception
{
given(namespaceService.getPrefixes(NamespaceService.CONTENT_MODEL_1_0_URI)).willReturn(List.of(NamespaceService.CONTENT_MODEL_PREFIX));
given(namespaceService.getNamespaceURI(NamespaceService.CONTENT_MODEL_PREFIX)).willReturn(NamespaceService.CONTENT_MODEL_1_0_URI);
given(namespaceService.getPrefixes(NamespaceService.AUDIO_MODEL_1_0_URI)).willReturn(List.of(NamespaceService.AUDIO_MODEL_PREFIX));
given(namespaceService.getNamespaceURI(NamespaceService.AUDIO_MODEL_PREFIX)).willReturn(NamespaceService.AUDIO_MODEL_1_0_URI);
}
private static List<TestData> getTestData() {
return List.of(
TestData.of(ComparePropertyValueEvaluator.NAME),
TestData.of(CompareMimeTypeEvaluator.NAME),
TestData.of(HasAspectEvaluator.NAME),
TestData.of(HasChildEvaluator.NAME),
TestData.of(HasChildEvaluator.NAME, NULL_RESULT),
TestData.of(HasTagEvaluator.NAME),
TestData.of(HasVersionHistoryEvaluator.NAME),
TestData.of(HasVersionHistoryEvaluator.NAME, NULL_RESULT),
TestData.of(InCategoryEvaluator.NAME),
TestData.of(IsSubTypeEvaluator.NAME),
TestData.of(NoConditionEvaluator.NAME, true),
TestData.of("fake-definition-name", true),
TestData.of("", true),
TestData.of(null, true)
TestData.of(NoConditionEvaluator.NAME, NULL_RESULT),
TestData.of("fake-definition-name", NULL_RESULT),
TestData.of("", NULL_RESULT),
TestData.of(null, NULL_RESULT)
);
}
@@ -76,10 +105,10 @@ public class SimpleConditionTest
{
for (TestData testData : getTestData())
{
final ActionCondition actionCondition = createActionCondition(testData.actionDefinitionName);
final ActionCondition actionCondition = createActionCondition(testData.conditionDefinitionName);
// when
final SimpleCondition actualSimpleCondition = SimpleCondition.from(actionCondition);
final SimpleCondition actualSimpleCondition = SimpleCondition.from(actionCondition, namespaceService);
assertThat(Objects.isNull(actualSimpleCondition)).isEqualTo(testData.isNullResult);
if (!testData.isNullResult)
@@ -95,7 +124,7 @@ public class SimpleConditionTest
public void testFromNullValue()
{
// when
final SimpleCondition actualSimpleCondition = SimpleCondition.from(null);
final SimpleCondition actualSimpleCondition = SimpleCondition.from(null, namespaceService);
assertThat(actualSimpleCondition).isNull();
}
@@ -106,7 +135,7 @@ public class SimpleConditionTest
final ActionCondition actionCondition = new ActionConditionImpl("fake-id", null, createParameterValues());
// when
final SimpleCondition actualSimpleCondition = SimpleCondition.from(actionCondition);
final SimpleCondition actualSimpleCondition = SimpleCondition.from(actionCondition, namespaceService);
assertThat(actualSimpleCondition).isNull();
}
@@ -117,7 +146,7 @@ public class SimpleConditionTest
final ActionCondition actionCondition = new ActionConditionImpl("fake-id", "fake-def-name", null);
// when
final SimpleCondition actualSimpleCondition = SimpleCondition.from(actionCondition);
final SimpleCondition actualSimpleCondition = SimpleCondition.from(actionCondition, namespaceService);
assertThat(actualSimpleCondition).isNull();
}
@@ -129,14 +158,22 @@ public class SimpleConditionTest
createActionCondition(ComparePropertyValueEvaluator.NAME),
createActionCondition(CompareMimeTypeEvaluator.NAME)
);
final List<SimpleCondition> expectedSimpleConditions = List.of(
SimpleCondition.builder().field("content-property").comparator("operation").parameter("value").create(),
SimpleCondition.builder().field("property").comparator("equals").parameter("value").create()
);
// when
final List<SimpleCondition> actualSimpleConditions = SimpleCondition.listOf(actionConditions);
final List<SimpleCondition> actualSimpleConditions = SimpleCondition.listOf(actionConditions, namespaceService);
final List<SimpleCondition> expectedSimpleConditions = List.of(
SimpleCondition.builder()
.field("content-property")
.comparator("operation")
.parameter("value")
.create(),
SimpleCondition.builder()
.field(SimpleCondition.PARAM_MIMETYPE)
.comparator(ComparePropertyValueOperation.EQUALS.toString().toLowerCase())
.parameter("value")
.create()
);
assertThat(actualSimpleConditions)
.isNotNull()
.containsExactlyElementsOf(expectedSimpleConditions);
@@ -145,7 +182,8 @@ public class SimpleConditionTest
@Test
public void testListOfEmptyActionConditions()
{
final List<SimpleCondition> actualSimpleConditions = SimpleCondition.listOf(Collections.emptyList());
// when
final List<SimpleCondition> actualSimpleConditions = SimpleCondition.listOf(Collections.emptyList(), namespaceService);
assertThat(actualSimpleConditions).isNull();
}
@@ -153,7 +191,8 @@ public class SimpleConditionTest
@Test
public void testListOfNullActionConditions()
{
final List<SimpleCondition> actualSimpleConditions = SimpleCondition.listOf(null);
// when
final List<SimpleCondition> actualSimpleConditions = SimpleCondition.listOf(null, namespaceService);
assertThat(actualSimpleConditions).isNull();
}
@@ -164,50 +203,207 @@ public class SimpleConditionTest
final List<ActionCondition> actionConditions = new ArrayList<>();
actionConditions.add(null);
final List<SimpleCondition> actualSimpleConditions = SimpleCondition.listOf(actionConditions);
// when
final List<SimpleCondition> actualSimpleConditions = SimpleCondition.listOf(actionConditions, namespaceService);
assertThat(actualSimpleConditions).isNotNull().isEmpty();
}
@Test
public void testToServiceModel_withSizeContentProperty()
{
final SimpleCondition simpleCondition = createSimpleCondition(ContentPropertyName.SIZE.toString().toLowerCase());
// when
final ActionCondition actualActionCondition = simpleCondition.toServiceModel(NOT_INVERTED, nodes, namespaceService);
final Map<String, Serializable> expectedParameterValues = new HashMap<>();
expectedParameterValues.put(ComparePropertyValueEvaluator.PARAM_CONTENT_PROPERTY, ContentPropertyName.SIZE.toString());
expectedParameterValues.put(ComparePropertyValueEvaluator.PARAM_PROPERTY, ContentModel.TYPE_CONTENT);
expectedParameterValues.put(ComparePropertyValueEvaluator.PARAM_OPERATION, ComparePropertyValueOperation.EQUALS.toString());
expectedParameterValues.put(ComparePropertyValueEvaluator.PARAM_VALUE, PARAMETER_DEFAULT);
ActionCondition expectedActionCondition = new ActionConditionImpl(null, ComparePropertyValueEvaluator.NAME, expectedParameterValues);
assertThat(actualActionCondition)
.isNotNull().usingRecursiveComparison().ignoringFields("id")
.isEqualTo(expectedActionCondition);
}
@Test
public void testToServiceModel_withoutContentProperty()
{
final SimpleCondition simpleCondition = createSimpleCondition(ContentModel.PROP_DESCRIPTION.toPrefixString(namespaceService));
// when
final ActionCondition actualActionCondition = simpleCondition.toServiceModel(NOT_INVERTED, nodes, namespaceService);
final Map<String, Serializable> expectedParameterValues = new HashMap<>();
expectedParameterValues.put(ComparePropertyValueEvaluator.PARAM_PROPERTY, ContentModel.PROP_DESCRIPTION);
expectedParameterValues.put(ComparePropertyValueEvaluator.PARAM_OPERATION, ComparePropertyValueOperation.EQUALS.toString());
expectedParameterValues.put(ComparePropertyValueEvaluator.PARAM_VALUE, PARAMETER_DEFAULT);
final ActionCondition expectedActionCondition = new ActionConditionImpl(null, ComparePropertyValueEvaluator.NAME, expectedParameterValues);
assertThat(actualActionCondition)
.isNotNull().usingRecursiveComparison().ignoringFields("id", "parameterValues.property.prefix")
.isEqualTo(expectedActionCondition);
}
@Test
public void testToServiceModel_compareMimetype()
{
final SimpleCondition simpleCondition = createSimpleCondition(SimpleCondition.PARAM_MIMETYPE);
// when
final ActionCondition actualActionCondition = simpleCondition.toServiceModel(NOT_INVERTED, nodes, namespaceService);
final Map<String, Serializable> expectedParameterValues = new HashMap<>();
expectedParameterValues.put(ComparePropertyValueEvaluator.PARAM_PROPERTY, ContentModel.TYPE_CONTENT);
expectedParameterValues.put(ComparePropertyValueEvaluator.PARAM_VALUE, PARAMETER_DEFAULT);
final ActionCondition expectedActionCondition = new ActionConditionImpl(null, CompareMimeTypeEvaluator.NAME, expectedParameterValues);
assertThat(actualActionCondition)
.isNotNull().usingRecursiveComparison().ignoringFields("id")
.isEqualTo(expectedActionCondition);
}
@Test
public void testToServiceModel_hasAspect()
{
final QName audioAspect = QName.createQName(NamespaceService.AUDIO_MODEL_1_0_URI, NamespaceService.AUDIO_MODEL_PREFIX);
final SimpleCondition simpleCondition = createSimpleCondition(HasAspectEvaluator.PARAM_ASPECT, audioAspect.toPrefixString(namespaceService));
// when
final ActionCondition actualActionCondition = simpleCondition.toServiceModel(NOT_INVERTED, nodes, namespaceService);
final Map<String, Serializable> expectedParameterValues = new HashMap<>();
expectedParameterValues.put(HasAspectEvaluator.PARAM_ASPECT, audioAspect);
final ActionCondition expectedActionCondition = new ActionConditionImpl(null, HasAspectEvaluator.NAME, expectedParameterValues);
assertThat(actualActionCondition)
.isNotNull().usingRecursiveComparison().ignoringFields("id", "parameterValues.aspect.prefix")
.isEqualTo(expectedActionCondition);
}
@Test
public void testToServiceModel_hasTag()
{
final String tag = "some tag";
final SimpleCondition simpleCondition = createSimpleCondition(HasTagEvaluator.PARAM_TAG, tag);
// when
final ActionCondition actualActionCondition = simpleCondition.toServiceModel(NOT_INVERTED, nodes, namespaceService);
final Map<String, Serializable> expectedParameterValues = new HashMap<>();
expectedParameterValues.put(HasTagEvaluator.PARAM_TAG, tag);
final ActionCondition expectedActionCondition = new ActionConditionImpl(null, HasTagEvaluator.NAME, expectedParameterValues);
assertThat(actualActionCondition)
.isNotNull().usingRecursiveComparison().ignoringFields("id")
.isEqualTo(expectedActionCondition);
}
@Test
public void testToServiceModel_inCategory()
{
final SimpleCondition simpleCondition = createSimpleCondition(SimpleCondition.PARAM_CATEGORY);
final NodeRef defaultNodeRef = new NodeRef(StoreRef.STORE_REF_WORKSPACE_SPACESSTORE, PARAMETER_DEFAULT);
given(nodes.validateOrLookupNode(PARAMETER_DEFAULT, null)).willReturn(defaultNodeRef);
// when
final ActionCondition actualActionCondition = simpleCondition.toServiceModel(NOT_INVERTED, nodes, namespaceService);
final Map<String, Serializable> expectedParameterValues = new HashMap<>();
expectedParameterValues.put(InCategoryEvaluator.PARAM_CATEGORY_ASPECT, ContentModel.ASPECT_GEN_CLASSIFIABLE);
expectedParameterValues.put(InCategoryEvaluator.PARAM_CATEGORY_VALUE, defaultNodeRef);
final ActionCondition expectedActionCondition = new ActionConditionImpl(null, InCategoryEvaluator.NAME, expectedParameterValues);
assertThat(actualActionCondition)
.isNotNull().usingRecursiveComparison().ignoringFields("id")
.isEqualTo(expectedActionCondition);
}
@Test
public void testToServiceModel_isSubType()
{
final SimpleCondition simpleCondition = createSimpleCondition(IsSubTypeEvaluator.PARAM_TYPE, ContentModel.TYPE_FOLDER.toPrefixString(namespaceService));
// when
final ActionCondition actualActionCondition = simpleCondition.toServiceModel(NOT_INVERTED, nodes, namespaceService);
final Map<String, Serializable> expectedParameterValues = new HashMap<>();
expectedParameterValues.put(IsSubTypeEvaluator.PARAM_TYPE, ContentModel.TYPE_FOLDER);
final ActionCondition expectedActionCondition = new ActionConditionImpl(null, IsSubTypeEvaluator.NAME, expectedParameterValues);
assertThat(actualActionCondition)
.isNotNull().usingRecursiveComparison().ignoringFields("id", "parameterValues.type.prefix")
.isEqualTo(expectedActionCondition);
}
@Test
public void testToServiceModel_inverted()
{
final SimpleCondition simpleCondition = createSimpleCondition(ContentModel.PROP_DESCRIPTION.toPrefixString(namespaceService));
// when
final ActionCondition actualActionCondition = simpleCondition.toServiceModel(!NOT_INVERTED, nodes, namespaceService);
final Map<String, Serializable> expectedParameterValues = new HashMap<>();
expectedParameterValues.put(ComparePropertyValueEvaluator.PARAM_PROPERTY, ContentModel.PROP_DESCRIPTION);
expectedParameterValues.put(ComparePropertyValueEvaluator.PARAM_OPERATION, ComparePropertyValueOperation.EQUALS.toString());
expectedParameterValues.put(ComparePropertyValueEvaluator.PARAM_VALUE, PARAMETER_DEFAULT);
final ActionCondition expectedActionCondition = new ActionConditionImpl(null, ComparePropertyValueEvaluator.NAME, expectedParameterValues);
expectedActionCondition.setInvertCondition(!NOT_INVERTED);
assertThat(actualActionCondition)
.isNotNull().usingRecursiveComparison().ignoringFields("id", "parameterValues.property.prefix")
.isEqualTo(expectedActionCondition);
}
private static SimpleCondition createSimpleCondition(final String field)
{
return createSimpleCondition(field, PARAMETER_DEFAULT);
}
private static SimpleCondition createSimpleCondition(final String field, final String parameter)
{
return SimpleCondition.builder()
.field(field)
.comparator(ComparePropertyValueOperation.EQUALS.toString().toLowerCase())
.parameter(parameter)
.create();
}
private static ActionCondition createActionCondition(final String actionDefinitionName)
{
return new ActionConditionImpl("fake-id", actionDefinitionName, createParameterValues());
}
private static Map<String, Serializable> createParameterValues() {
final QName audioAspect = QName.createQName(NamespaceService.AUDIO_MODEL_1_0_URI, NamespaceService.AUDIO_MODEL_PREFIX);
final NodeRef defaultNodeRef = new NodeRef(StoreRef.STORE_REF_WORKSPACE_SPACESSTORE, PARAMETER_DEFAULT);
final Map<String, Serializable> parameterValues = new HashMap<>();
parameterValues.put(ComparePropertyValueEvaluator.PARAM_CONTENT_PROPERTY, "content-property");
parameterValues.put(HasChildEvaluator.PARAM_ASSOC_TYPE, "assoc-type");
parameterValues.put(ComparePropertyValueEvaluator.PARAM_PROPERTY, "property");
parameterValues.put(ComparePropertyValueEvaluator.PARAM_PROPERTY, ContentModel.TYPE_CONTENT);
parameterValues.put(ComparePropertyValueEvaluator.PARAM_OPERATION, "operation");
parameterValues.put(ComparePropertyValueEvaluator.PARAM_VALUE, "value");
parameterValues.put(HasAspectEvaluator.PARAM_ASPECT, "aspect");
parameterValues.put(HasChildEvaluator.PARAM_ASSOC_NAME, "assoc-name");
parameterValues.put(HasAspectEvaluator.PARAM_ASPECT, audioAspect);
parameterValues.put(HasTagEvaluator.PARAM_TAG, "tag");
parameterValues.put(InCategoryEvaluator.PARAM_CATEGORY_ASPECT, "category-aspect");
parameterValues.put(InCategoryEvaluator.PARAM_CATEGORY_VALUE, "category-value");
parameterValues.put(IsSubTypeEvaluator.PARAM_TYPE, "type");
parameterValues.put(InCategoryEvaluator.PARAM_CATEGORY_VALUE, defaultNodeRef);
parameterValues.put(IsSubTypeEvaluator.PARAM_TYPE, ContentModel.TYPE_FOLDER);
return parameterValues;
}
private static class TestData
{
String actionDefinitionName;
String conditionDefinitionName;
boolean isNullResult;
public TestData(String actionDefinitionName, boolean isNullResult)
public TestData(String conditionDefinitionName, boolean isNullResult)
{
this.actionDefinitionName = actionDefinitionName;
this.conditionDefinitionName = conditionDefinitionName;
this.isNullResult = isNullResult;
}
public static TestData of(String actionDefinitionName) {
return new TestData(actionDefinitionName, false);
public static TestData of(String conditionDefinitionName) {
return new TestData(conditionDefinitionName, false);
}
public static TestData of(String actionDefinitionName, boolean isNullResult) {
return new TestData(actionDefinitionName, isNullResult);
public static TestData of(String conditionDefinitionName, boolean isNullResult) {
return new TestData(conditionDefinitionName, isNullResult);
}
}
}

View File

@@ -106,6 +106,7 @@ import org.apache.commons.collections.map.MultiValueMap;
import org.json.simple.JSONObject;
import org.junit.After;
import org.junit.Before;
import org.junit.Ignore;
import org.junit.Test;
/**
@@ -3611,6 +3612,7 @@ public class NodeApiTest extends AbstractSingleNetworkSiteTest
* {@literal <host>:<port>/alfresco/api/-default-/public/alfresco/versions/1/nodes/<nodeId>/content}
*/
@Test
@Ignore("ACS-3531 Frequent intermittent failure")
public void testDownloadFileContent() throws Exception
{
setRequestContext(user1);

View File

@@ -7,7 +7,7 @@
<parent>
<groupId>org.alfresco</groupId>
<artifactId>alfresco-community-repo</artifactId>
<version>17.102-SNAPSHOT</version>
<version>17.113</version>
</parent>
<dependencies>
@@ -691,6 +691,16 @@
<artifactId>camel-mock</artifactId>
<scope>test</scope>
</dependency>
<!-- Netty non-transitive dependencies declared for depending projects usage in conjunction with Camel's other transitive netty dependencies -->
<dependency>
<groupId>io.netty</groupId>
<artifactId>netty-handler-proxy</artifactId>
</dependency>
<dependency>
<groupId>io.netty</groupId>
<artifactId>netty-tcnative-classes</artifactId>
</dependency>
<dependency>
<groupId>org.apache.activemq</groupId>
<artifactId>activemq-client</artifactId>

View File

@@ -1,33 +1,34 @@
/*
* #%L
* Alfresco Repository
* %%
* Copyright (C) 2005 - 2016 Alfresco Software Limited
* %%
* This file is part of the Alfresco software.
* If the software was purchased under a paid Alfresco license, the terms of
* the paid license agreement will prevail. Otherwise, the software is
* provided under the following open source license terms:
*
* Alfresco is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Alfresco is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
* #L%
*/
/*
* #%L
* Alfresco Repository
* %%
* Copyright (C) 2005 - 2016 Alfresco Software Limited
* %%
* This file is part of the Alfresco software.
* If the software was purchased under a paid Alfresco license, the terms of
* the paid license agreement will prevail. Otherwise, the software is
* provided under the following open source license terms:
*
* Alfresco is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Alfresco is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
* #L%
*/
package org.alfresco.repo.action;
import java.io.Serializable;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
import org.alfresco.service.cmr.action.ParameterizedItem;
@@ -38,8 +39,8 @@ import org.alfresco.service.cmr.action.ParameterizedItem;
*/
public abstract class ParameterizedItemImpl implements ParameterizedItem, Serializable
{
private static final long serialVersionUID = 3578052215076397741L;
private static final long serialVersionUID = 3578052215076397741L;
/**
* The id
*/
@@ -50,23 +51,23 @@ public abstract class ParameterizedItemImpl implements ParameterizedItem, Serial
*/
private Map<String, Serializable> parameterValues = new HashMap<String, Serializable>();
/**
* Constructor
*
* @param id the id
*/
public ParameterizedItemImpl(String id)
{
/**
* Constructor
*
* @param id the id
*/
public ParameterizedItemImpl(String id)
{
this(id, null);
}
/**
* Constructor
*
* @param id the rule item
* @param parameterValues the parameter values
*/
public ParameterizedItemImpl(String id, Map<String, Serializable> parameterValues)
/**
* Constructor
*
* @param id the rule item
* @param parameterValues the parameter values
*/
public ParameterizedItemImpl(String id, Map<String, Serializable> parameterValues)
{
// Set the action id
this.id = id;
@@ -135,7 +136,7 @@ public abstract class ParameterizedItemImpl implements ParameterizedItem, Serial
@Override
public int hashCode()
{
return this.id.hashCode();
return Objects.hashCode(this.id);
}
/**

View File

@@ -220,6 +220,7 @@ public class DeleteNotExistsExecutor implements StatementExecutor
{
// Process batch
primaryId = processPrimaryTableResultSet(primaryPrepStmt, secondaryPrepStmts, deletePrepStmt, deleteIds, primaryTableName, primaryColumnName, tableColumn);
connection.commit();
if (primaryId == null)
{
@@ -298,7 +299,6 @@ public class DeleteNotExistsExecutor implements StatementExecutor
if (deleteIds.size() == deleteBatchSize)
{
deleteFromPrimaryTable(deletePrepStmt, deleteIds, primaryTableName);
connection.commit();
}
if (!resultSet.next())

View File

@@ -117,6 +117,7 @@ public class MySQLDeleteNotExistsExecutor extends DeleteNotExistsExecutor
{
// Process batch
primaryId = processPrimaryTableResultSet(primaryPrepStmt, secondaryPrepStmts, deletePrepStmt, deleteIds, primaryTableName, primaryColumnName, tableColumn);
connection.commit();
if (primaryId == null)
{

View File

@@ -25,6 +25,9 @@
*/
package org.alfresco.repo.rule;
import static org.alfresco.repo.rule.RuleModel.ASPECT_IGNORE_INHERITED_RULES;
import static org.alfresco.repo.rule.RuleModel.ASSOC_RULE_FOLDER;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collections;
@@ -33,6 +36,9 @@ import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
import com.google.common.collect.Sets;
import org.alfresco.model.ContentModel;
import org.alfresco.repo.action.ActionImpl;
@@ -255,7 +261,7 @@ public class RuleServiceImpl
policyComponent.bindAssociationBehaviour(
NodeServicePolicies.OnCreateChildAssociationPolicy.QNAME,
RuleModel.ASPECT_RULES,
RuleModel.ASSOC_RULE_FOLDER,
ASSOC_RULE_FOLDER,
new JavaBehaviour(this, "onCreateChildAssociation"));
policyComponent.bindClassBehaviour(
NodeServicePolicies.OnAddAspectPolicy.QNAME,
@@ -347,8 +353,8 @@ public class RuleServiceImpl
List<ChildAssociationRef> assocs = this.runtimeNodeService.getChildAssocs(
nodeRef,
RuleModel.ASSOC_RULE_FOLDER,
RuleModel.ASSOC_RULE_FOLDER);
ASSOC_RULE_FOLDER,
ASSOC_RULE_FOLDER);
if (assocs.size() > 1)
{
throw new ActionServiceException("There is more than one rule folder, which is invalid.");
@@ -479,7 +485,7 @@ public class RuleServiceImpl
// Node has gone or is not the correct type
return rules;
}
if (includeInherited == true && runtimeNodeService.hasAspect(nodeRef, RuleModel.ASPECT_IGNORE_INHERITED_RULES) == false)
if (includeInherited && !runtimeNodeService.hasAspect(nodeRef, ASPECT_IGNORE_INHERITED_RULES))
{
// Get any inherited rules
for (Rule rule : getInheritedRules(nodeRef, ruleTypeName, null))
@@ -597,10 +603,53 @@ public class RuleServiceImpl
return result;
}
/** {@inheritDoc} */
@Override
@Experimental
public List<NodeRef> getNodesSupplyingRuleSets(NodeRef nodeRef)
{
return getNodesSupplyingRuleSets(nodeRef, new ArrayList<>());
}
/**
* Traverse the folder hierarchy find all the folder nodes that could supply rules by inheritance.
* <p>
* The order of nodes returned by this methods has to match the order used by {@link #getInheritedRules}.
*
* @param nodeRef The starting node ref.
* @param visitedNodeRefs All the visited node refs (will be modified).
* @return A list of node refs, starting with the first parent of the first parent of ... and ending with the object generated by the
* given node ref.
*/
private List<NodeRef> getNodesSupplyingRuleSets(NodeRef nodeRef, List<NodeRef> visitedNodeRefs)
{
List<NodeRef> returnList = new ArrayList<>();
// This check prevents stack over flow when we have a cyclic node graph
if (!visitedNodeRefs.contains(nodeRef))
{
visitedNodeRefs.add(nodeRef);
if (!runtimeNodeService.hasAspect(nodeRef, ASPECT_IGNORE_INHERITED_RULES))
{
List<ChildAssociationRef> parents = runtimeNodeService.getParentAssocs(nodeRef);
for (ChildAssociationRef parent : parents)
{
// We are not interested in following potentially massive person group membership trees!
if (!IGNORE_PARENT_ASSOC_TYPES.contains(parent.getTypeQName()))
{
// Update visitedNodeRefs with all the ancestors.
returnList.addAll(getNodesSupplyingRuleSets(parent.getParentRef(), visitedNodeRefs));
}
}
}
returnList.add(nodeRef);
}
return returnList;
}
/**
* Gets the inherited rules for a given node reference
*
*
* @param nodeRef the nodeRef
* @param ruleTypeName the rule type (null if all applicable)
* @return a list of inherited rules (empty if none)
@@ -608,20 +657,20 @@ public class RuleServiceImpl
private List<Rule> getInheritedRules(NodeRef nodeRef, String ruleTypeName, Set<NodeRef> visitedNodeRefs)
{
List<Rule> inheritedRules = new ArrayList<Rule>();
if (this.runtimeNodeService.hasAspect(nodeRef, RuleModel.ASPECT_IGNORE_INHERITED_RULES) == false)
{
{
// Create the visited nodes set if it has not already been created
if (visitedNodeRefs == null)
{
visitedNodeRefs = new HashSet<NodeRef>();
}
// This check prevents stack over flow when we have a cyclic node graph
if (visitedNodeRefs.contains(nodeRef) == false)
{
visitedNodeRefs.add(nodeRef);
List<Rule> allInheritedRules = new ArrayList<Rule>();
List<ChildAssociationRef> parents = this.runtimeNodeService.getParentAssocs(nodeRef);
for (ChildAssociationRef parent : parents)
@@ -641,7 +690,7 @@ public class RuleServiceImpl
allInheritedRules.add(rule);
}
}
List<Rule> rules = getRules(parent.getParentRef(), false);
for (Rule rule : rules)
{
@@ -652,7 +701,7 @@ public class RuleServiceImpl
}
}
}
if (ruleTypeName == null)
{
inheritedRules = allInheritedRules;
@@ -670,7 +719,7 @@ public class RuleServiceImpl
}
}
}
return inheritedRules;
}
@@ -1523,6 +1572,12 @@ public class RuleServiceImpl
return this.nodeService.getPrimaryParent(systemFolder).getParentRef();
}
@Override
public NodeRef getOwningNodeRef(NodeRef ruleSet)
{
return nodeService.getPrimaryParent(ruleSet).getParentRef();
}
@Override
public NodeRef getOwningNodeRef(final Action action)
{
@@ -1615,7 +1670,7 @@ public class RuleServiceImpl
@Experimental
public NodeRef getRuleSetNode(final NodeRef folderNodeRef)
{
return runtimeNodeService.getChildAssocs(folderNodeRef, RuleModel.ASSOC_RULE_FOLDER, RuleModel.ASSOC_RULE_FOLDER).stream()
return runtimeNodeService.getChildAssocs(folderNodeRef, ASSOC_RULE_FOLDER, ASSOC_RULE_FOLDER).stream()
.map(ChildAssociationRef::getChildRef)
.findFirst()
.orElse(null);
@@ -1625,7 +1680,10 @@ public class RuleServiceImpl
@Experimental
public boolean isRuleSetAssociatedWithFolder(final NodeRef ruleSetNodeRef, final NodeRef folderNodeRef)
{
return isChildOf(ruleSetNodeRef, RuleModel.ASSOC_RULE_FOLDER, folderNodeRef);
List<ChildAssociationRef> associations = runtimeNodeService.getParentAssocs(ruleSetNodeRef, ASSOC_RULE_FOLDER, ASSOC_RULE_FOLDER);
Set<NodeRef> associatedFolders = associations.stream().map(ChildAssociationRef::getParentRef).collect(Collectors.toSet());
Set<NodeRef> supplyingFolders = new HashSet<>(getNodesSupplyingRuleSets(folderNodeRef));
return !Sets.intersection(associatedFolders, supplyingFolders).isEmpty();
}
@Override

View File

@@ -25,54 +25,54 @@
*/
package org.alfresco.repo.security.authentication.ldap;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.cert.CertificateException;
import java.util.Collections;
import java.util.HashSet;
import java.util.Hashtable;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import javax.naming.AuthenticationNotSupportedException;
import javax.naming.CommunicationException;
import javax.naming.Context;
import javax.naming.NamingException;
import javax.naming.directory.Attribute;
import javax.naming.directory.Attributes;
import javax.naming.directory.BasicAttribute;
import javax.naming.directory.BasicAttributes;
import javax.naming.directory.DirContext;
import javax.naming.directory.InitialDirContext;
import javax.naming.ldap.Control;
import javax.naming.ldap.InitialLdapContext;
import javax.naming.ldap.LdapContext;
import javax.naming.ldap.PagedResultsControl;
import javax.naming.ldap.PagedResultsResponseControl;
import org.alfresco.error.AlfrescoRuntimeException;
import org.alfresco.repo.security.authentication.AlfrescoSSLSocketFactory;
import org.alfresco.repo.security.authentication.AuthenticationDiagnostic;
import org.alfresco.repo.security.authentication.AuthenticationException;
import org.alfresco.util.ApplicationContextHelper;
import org.alfresco.util.PropertyCheck;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.InitializingBean;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.cert.CertificateException;
import java.util.Collections;
import java.util.HashSet;
import java.util.Hashtable;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import javax.naming.AuthenticationNotSupportedException;
import javax.naming.CommunicationException;
import javax.naming.Context;
import javax.naming.NamingException;
import javax.naming.directory.Attribute;
import javax.naming.directory.Attributes;
import javax.naming.directory.BasicAttribute;
import javax.naming.directory.BasicAttributes;
import javax.naming.directory.DirContext;
import javax.naming.directory.InitialDirContext;
import javax.naming.ldap.Control;
import javax.naming.ldap.InitialLdapContext;
import javax.naming.ldap.LdapContext;
import javax.naming.ldap.PagedResultsControl;
import javax.naming.ldap.PagedResultsResponseControl;
import org.alfresco.error.AlfrescoRuntimeException;
import org.alfresco.repo.cache.SimpleCache;
import org.alfresco.repo.security.authentication.AlfrescoSSLSocketFactory;
import org.alfresco.repo.security.authentication.AuthenticationDiagnostic;
import org.alfresco.repo.security.authentication.AuthenticationException;
import org.alfresco.util.ApplicationContextHelper;
import org.alfresco.util.PropertyCheck;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.context.ApplicationContext;
public class LDAPInitialDirContextFactoryImpl implements LDAPInitialDirContextFactory, InitializingBean
{
private static final Log logger = LogFactory.getLog(LDAPInitialDirContextFactoryImpl.class);
private static Set<Map<String, String>> checkedEnvs = Collections.synchronizedSet(new HashSet<Map<String, String>>(
11));
private SimpleCache<String, Set<Map<String, String>>> ldapInitialDirContextCache;
private Map<String, String> defaultEnvironment = Collections.<String, String> emptyMap();
private Map<String, String> authenticatedEnvironment = Collections.<String, String> emptyMap();
@@ -80,6 +80,13 @@ public class LDAPInitialDirContextFactoryImpl implements LDAPInitialDirContextFa
private String trustStorePath;
private String trustStoreType;
private String trustStorePassPhrase;
private boolean initialChecksEnabled = true;
private final String ANONYMOUS_CHECK = "anonymous_check";
private final String SIMPLE_DN_CHECK = "simple_dn_check";
private final String DN_CHECK = "dn_check";
private final String PRINCIPAL_CHECK = "principal_check";
public String getTrustStorePath()
{
@@ -475,159 +482,202 @@ public class LDAPInitialDirContextFactoryImpl implements LDAPInitialDirContextFa
}
public void afterPropertiesSet() throws Exception
{
logger.debug("after Properties Set");
// Check Anonymous bind
Hashtable<String, String> env = new Hashtable<String, String>(authenticatedEnvironment.size());
env.putAll(authenticatedEnvironment);
env.remove(Context.SECURITY_PRINCIPAL);
env.remove(Context.SECURITY_CREDENTIALS);
if (isSSLSocketFactoryRequired())
{
KeyStore trustStore = initTrustStore();
AlfrescoSSLSocketFactory.initTrustedSSLSocketFactory(trustStore);
env.put("java.naming.ldap.factory.socket", AlfrescoSSLSocketFactory.class.getName());
}
try
{
new InitialDirContext(env);
logger.warn("LDAP server supports anonymous bind " + env.get(Context.PROVIDER_URL));
}
catch (javax.naming.AuthenticationException ax)
{
}
catch (AuthenticationNotSupportedException e)
{
}
catch (NamingException nx)
{
logger.error("Unable to connect to LDAP Server; check LDAP configuration", nx);
return;
}
// Simple DN and password
env = new Hashtable<String, String>(authenticatedEnvironment.size());
env.putAll(authenticatedEnvironment);
env.put(Context.SECURITY_PRINCIPAL, "daftAsABrush");
env.put(Context.SECURITY_CREDENTIALS, "daftAsABrush");
if (isSSLSocketFactoryRequired())
{
KeyStore trustStore = initTrustStore();
AlfrescoSSLSocketFactory.initTrustedSSLSocketFactory(trustStore);
env.put("java.naming.ldap.factory.socket", AlfrescoSSLSocketFactory.class.getName());
}
try
{
new InitialDirContext(env);
throw new AuthenticationException(
"The ldap server at "
+ env.get(Context.PROVIDER_URL)
+ " falls back to use anonymous bind if invalid security credentials are presented. This is not supported.");
}
catch (javax.naming.AuthenticationException ax)
{
logger.info("LDAP server does not fall back to anonymous bind for a string uid and password at " + env.get(Context.PROVIDER_URL));
}
catch (AuthenticationNotSupportedException e)
{
logger.info("LDAP server does not fall back to anonymous bind for a string uid and password at " + env.get(Context.PROVIDER_URL));
}
catch (NamingException nx)
{
logger.info("LDAP server does not support simple string user ids and invalid credentials at "+ env.get(Context.PROVIDER_URL));
}
// DN and password
env = new Hashtable<String, String>(authenticatedEnvironment.size());
env.putAll(authenticatedEnvironment);
env.put(Context.SECURITY_PRINCIPAL, "cn=daftAsABrush,dc=woof");
env.put(Context.SECURITY_CREDENTIALS, "daftAsABrush");
if (isSSLSocketFactoryRequired())
{
KeyStore trustStore = initTrustStore();
AlfrescoSSLSocketFactory.initTrustedSSLSocketFactory(trustStore);
env.put("java.naming.ldap.factory.socket", AlfrescoSSLSocketFactory.class.getName());
}
try
{
new InitialDirContext(env);
throw new AuthenticationException(
"The ldap server at "
+ env.get(Context.PROVIDER_URL)
+ " falls back to use anonymous bind if invalid security credentials are presented. This is not supported.");
}
catch (javax.naming.AuthenticationException ax)
{
logger.info("LDAP server does not fall back to anonymous bind for a simple dn and password at " + env.get(Context.PROVIDER_URL));
}
catch (AuthenticationNotSupportedException e)
{
logger.info("LDAP server does not fall back to anonymous bind for a simple dn and password at " + env.get(Context.PROVIDER_URL));
}
catch (NamingException nx)
{
logger.info("LDAP server does not support simple DN and invalid password at "+ env.get(Context.PROVIDER_URL));
}
// Check more if we have a real principal we expect to work
String principal = defaultEnvironment.get(Context.SECURITY_PRINCIPAL);
if (principal != null)
{
// Correct principal invalid password
env = new Hashtable<String, String>(authenticatedEnvironment.size());
env.putAll(authenticatedEnvironment);
env.put(Context.SECURITY_PRINCIPAL, principal);
env.put(Context.SECURITY_CREDENTIALS, "sdasdasdasdasd123123123");
if (isSSLSocketFactoryRequired())
{
KeyStore trustStore = initTrustStore();
AlfrescoSSLSocketFactory.initTrustedSSLSocketFactory(trustStore);
env.put("java.naming.ldap.factory.socket", AlfrescoSSLSocketFactory.class.getName());
}
if (!checkedEnvs.contains(env))
{
try
{
new InitialDirContext(env);
throw new AuthenticationException(
"The ldap server at "
+ env.get(Context.PROVIDER_URL)
+ " falls back to use anonymous bind for a known principal if invalid security credentials are presented. This is not supported.");
}
catch (javax.naming.AuthenticationException ax)
{
logger.info("LDAP server does not fall back to anonymous bind for known principal and invalid credentials at " + env.get(Context.PROVIDER_URL));
}
catch (AuthenticationNotSupportedException e)
{
logger.info("LDAP server does not support the required authentication mechanism");
}
catch (NamingException nx)
{
// already done
}
// Record this environment as checked so that we don't check it again on further restarts / other subsystem
// instances
checkedEnvs.add(env);
}
}
public void afterPropertiesSet() throws Exception
{
logger.debug("after Properties Set");
if (initialChecksEnabled)
{
checkAnonymousBind();
checkSimpleDnAndPassword();
checkDnAndPassword();
checkPrincipal();
}
else
{
logger.info("LDAP checks are disabled");
}
}
/**
* Check Anonymous bind
*/
private void checkAnonymousBind()
{
Hashtable<String, String> env = new Hashtable<String, String>(authenticatedEnvironment.size());
env.putAll(authenticatedEnvironment);
env.remove(Context.SECURITY_PRINCIPAL);
env.remove(Context.SECURITY_CREDENTIALS);
if (isSSLSocketFactoryRequired())
{
KeyStore trustStore = initTrustStore();
AlfrescoSSLSocketFactory.initTrustedSSLSocketFactory(trustStore);
env.put("java.naming.ldap.factory.socket", AlfrescoSSLSocketFactory.class.getName());
}
if (!isCached(ANONYMOUS_CHECK, env))
{
logger.debug("Starting check: Anonymous bind");
try
{
new InitialDirContext(env);
logger.warn("LDAP server supports anonymous bind " + env.get(Context.PROVIDER_URL));
}
catch (javax.naming.AuthenticationException | AuthenticationNotSupportedException e)
{
// do nothing
}
catch (NamingException nx)
{
logger.error("Unable to connect to LDAP Server; check LDAP configuration", nx);
}
addToCache(ANONYMOUS_CHECK, env);
}
}
/**
* Check simple DN and password
*/
private void checkSimpleDnAndPassword()
{
Hashtable<String, String> env = new Hashtable<String, String>(authenticatedEnvironment.size());
env.putAll(authenticatedEnvironment);
env.put(Context.SECURITY_PRINCIPAL, "daftAsABrush");
env.put(Context.SECURITY_CREDENTIALS, "daftAsABrush");
if (isSSLSocketFactoryRequired())
{
KeyStore trustStore = initTrustStore();
AlfrescoSSLSocketFactory.initTrustedSSLSocketFactory(trustStore);
env.put("java.naming.ldap.factory.socket", AlfrescoSSLSocketFactory.class.getName());
}
if (!isCached(SIMPLE_DN_CHECK, env))
{
logger.debug("Starting check: Simple DN and Password");
try
{
new InitialDirContext(env);
throw new AuthenticationException("The ldap server at " + env.get(Context.PROVIDER_URL)
+ " falls back to use anonymous bind if invalid security credentials are presented. This is not supported.");
}
catch (javax.naming.AuthenticationException | AuthenticationNotSupportedException e)
{
logger.info("LDAP server does not fall back to anonymous bind for a string uid and password at "
+ env.get(Context.PROVIDER_URL));
}
catch (NamingException nx)
{
logger.info("LDAP server does not support simple string user ids and invalid credentials at "
+ env.get(Context.PROVIDER_URL));
}
addToCache(SIMPLE_DN_CHECK, env);
}
}
/**
* Check DN and Password
*/
private void checkDnAndPassword()
{
Hashtable<String, String> env = new Hashtable<String, String>(authenticatedEnvironment.size());
env.putAll(authenticatedEnvironment);
env.put(Context.SECURITY_PRINCIPAL, "cn=daftAsABrush,dc=woof");
env.put(Context.SECURITY_CREDENTIALS, "daftAsABrush");
if (isSSLSocketFactoryRequired())
{
KeyStore trustStore = initTrustStore();
AlfrescoSSLSocketFactory.initTrustedSSLSocketFactory(trustStore);
env.put("java.naming.ldap.factory.socket", AlfrescoSSLSocketFactory.class.getName());
}
if (!isCached(DN_CHECK, env))
{
logger.debug("Starting check: DN and Password");
try
{
new InitialDirContext(env);
throw new AuthenticationException("The ldap server at " + env.get(Context.PROVIDER_URL)
+ " falls back to use anonymous bind if invalid security credentials are presented. This is not supported.");
}
catch (javax.naming.AuthenticationException | AuthenticationNotSupportedException e)
{
logger.info("LDAP server does not fall back to anonymous bind for a simple dn and password at "
+ env.get(Context.PROVIDER_URL));
}
catch (NamingException nx)
{
logger.info("LDAP server does not support simple DN and invalid password at " + env.get(Context.PROVIDER_URL));
}
addToCache(DN_CHECK, env);
}
}
/**
* Check more if we have a real principal we expect to work
*/
private void checkPrincipal()
{
String principal = defaultEnvironment.get(Context.SECURITY_PRINCIPAL);
if (principal != null)
{
// Correct principal invalid password
Hashtable<String, String> env = new Hashtable<String, String>(authenticatedEnvironment.size());
env.putAll(authenticatedEnvironment);
env.put(Context.SECURITY_PRINCIPAL, principal);
env.put(Context.SECURITY_CREDENTIALS, "sdasdasdasdasd123123123");
if (isSSLSocketFactoryRequired())
{
KeyStore trustStore = initTrustStore();
AlfrescoSSLSocketFactory.initTrustedSSLSocketFactory(trustStore);
env.put("java.naming.ldap.factory.socket", AlfrescoSSLSocketFactory.class.getName());
}
if (!isCached(PRINCIPAL_CHECK, env))
{
logger.debug("Starting check: Principal");
try
{
new InitialDirContext(env);
throw new AuthenticationException("The ldap server at " + env.get(Context.PROVIDER_URL)
+ " falls back to use anonymous bind for a known principal if invalid security credentials are presented. This is not supported.");
}
catch (javax.naming.AuthenticationException ax)
{
logger.info("LDAP server does not fall back to anonymous bind for known principal and invalid credentials at "
+ env.get(Context.PROVIDER_URL));
}
catch (AuthenticationNotSupportedException e)
{
logger.info("LDAP server does not support the required authentication mechanism");
}
catch (NamingException nx)
{
// already done
}
// Record this environment as checked so that we don't check it again on further restarts / other
// subsystem instances
addToCache(PRINCIPAL_CHECK, env);
}
}
}
/**
* Check if it required to use custom SSL socket factory with custom trustStore.
@@ -704,5 +754,69 @@ public class LDAPInitialDirContextFactoryImpl implements LDAPInitialDirContextFa
throw new AlfrescoRuntimeException("The certificates cannot be loaded from truststore.", ce);
}
return ks;
}
}
private void addToCache(String key, Map<String, String> value)
{
Set<Map<String, String>> envs = ldapInitialDirContextCache.get(key);
if (envs == null)
{
envs = Collections.synchronizedSet(new HashSet<Map<String, String>>(11));
}
if (!envs.contains(value))
{
envs.add(value);
}
if (!ldapInitialDirContextCache.contains(key))
{
ldapInitialDirContextCache.put(key, envs);
}
}
private void removeFromCache(String key, Map<String, String> value)
{
if (ldapInitialDirContextCache != null && ldapInitialDirContextCache.contains(key))
{
Set<Map<String, String>> envs = ldapInitialDirContextCache.get(key);
if (envs != null && envs.contains(value))
{
envs.remove(value);
if (envs.isEmpty())
{
ldapInitialDirContextCache.remove(key);
}
}
}
}
private boolean isCached(String key, Map<String, String> value)
{
boolean isCached = false;
if (ldapInitialDirContextCache != null && ldapInitialDirContextCache.contains(key))
{
Set<Map<String, String>> envs = ldapInitialDirContextCache.get(key);
if (envs != null && envs.contains(value))
{
isCached = true;
}
}
logger.debug("LDAP check: " + key + " / isCached: " + (isCached ? "yes" : "no"));
return isCached;
}
public void setLdapInitialDirContextCache(SimpleCache<String, Set<Map<String, String>>> cache)
{
this.ldapInitialDirContextCache = cache;
}
public void setInitialChecksEnabled(boolean initialChecksEnabled)
{
this.initialChecksEnabled = initialChecksEnabled;
}
}

View File

@@ -51,6 +51,7 @@ import org.alfresco.service.license.LicenseIntegrityException;
import org.alfresco.util.LockHelper.LockTryException;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.ibatis.exceptions.PersistenceException;
import org.apache.ibatis.exceptions.TooManyResultsException;
import org.springframework.aop.MethodBeforeAdvice;
import org.springframework.aop.framework.ProxyFactory;
@@ -105,7 +106,8 @@ public class RetryingTransactionHelper
DataIntegrityViolationException.class,
LicenseIntegrityException.class,
TooManyResultsException.class, // Expected one result but found multiple (bad key alert)
LockTryException.class
LockTryException.class,
PersistenceException.class
};
List<Class<?>> retryExceptions = new ArrayList<Class<?>>();

View File

@@ -215,7 +215,18 @@ public interface RuleService
*/
@Auditable(parameters = {"nodeRef"})
public int countRules(NodeRef nodeRef);
/**
* Traverse the folder hierarchy find all the folder nodes that could supply rules by inheritance.
*
* @param nodeRef The starting node ref.
* @return A list of node refs, starting with the first parent of the first parent of ... and ending with the object generated by the
* given node ref.
*/
@Auditable (parameters = { "nodeRef" })
@Experimental
List<NodeRef> getNodesSupplyingRuleSets(NodeRef nodeRef);
/**
* Get the rule given its node reference
*
@@ -300,7 +311,17 @@ public interface RuleService
*/
@Auditable(parameters = {"action"})
public NodeRef getOwningNodeRef(Action action);
/**
* Returns the owning node reference for a rule.
*
* @param ruleSet The rule set node.
* @return the owning node reference
*/
@Auditable (parameters = { "ruleSet" })
@Experimental
NodeRef getOwningNodeRef(NodeRef ruleSet);
/**
* Indicates whether the passed rule node reference is linked to another
* rule node.
@@ -342,7 +363,7 @@ public interface RuleService
NodeRef getRuleSetNode(final NodeRef folderNodeRef);
/**
* Check if rule set's associated parent matches folder node.
* Check if rule set is associated (owned/linked/inherited) with the given folder node.
*
* @param ruleSetNodeRef - node reference of a rule set
* @param folderNodeRef - node reference of a folder
@@ -364,7 +385,7 @@ public interface RuleService
boolean isRuleAssociatedWithRuleSet(final NodeRef ruleNodeRef, final NodeRef ruleSetNodeRef);
/**
* Check if others folders are linked to rule set.
* Check if other folders are linked to rule set.
*
* @param ruleSetNodeRef - node reference of a rule set
* @return true if others folders are linked to rule set

View File

@@ -506,4 +506,12 @@
<bean name="queryAcceleratorCache" factory-bean="cacheFactory" factory-method="createCache">
<constructor-arg value="cache.queryAcceleratorCache"/>
</bean>
<!-- ===================================== -->
<!-- LDAP Initial Dir Context cache -->
<!-- ===================================== -->
<bean name="ldapInitialDirContextCache" factory-bean="cacheFactory" factory-method="createCache">
<constructor-arg value="cache.ldapInitialDirContextCache"/>
</bean>
</beans>

View File

@@ -699,3 +699,15 @@ cache.queryAcceleratorCache.backup-count=1
cache.queryAcceleratorCache.eviction-policy=NONE
cache.queryAcceleratorCache.merge-policy=com.hazelcast.map.merge.LatestUpdateMapMergePolicy
cache.queryAcceleratorCache.readBackupData=false
#
# LDAP initial dir context checks cluster cache
#
cache.ldapInitialDirContextCache.maxItems=100
cache.ldapInitialDirContextCache.timeToLiveSeconds=0
cache.ldapInitialDirContextCache.maxIdleSeconds=0
cache.ldapInitialDirContextCache.cluster.type=fully-distributed
cache.ldapInitialDirContextCache.backup-count=1
cache.ldapInitialDirContextCache.eviction-policy=NONE
cache.ldapInitialDirContextCache.merge-policy=com.hazelcast.map.merge.LatestUpdateMapMergePolicy
cache.ldapInitialDirContextCache.readBackupData=false

View File

@@ -119,7 +119,12 @@
-->
<bean id="ldapInitialDirContextFactory" class="org.alfresco.repo.security.authentication.ldap.LDAPInitialDirContextFactoryImpl">
<property name="ldapInitialDirContextCache">
<ref bean="ldapInitialDirContextCache" />
</property>
<property name="initialChecksEnabled">
<value>${ldap.authentication.initial.checks.enabled}</value>
</property>
<property name="trustStorePath">
<value>${ldap.authentication.truststore.path}</value>
</property>

View File

@@ -175,3 +175,6 @@ ldap.synchronization.userAccountStatusProperty=userAccountControl
# The Account Status Interpreter bean name
ldap.synchronization.userAccountStatusInterpreter=ldapadUserAccountStatusInterpreter
# Allows to enable or disable LDAP-AD initial checks (anonymous bind, simple dn, dn and principal)
ldap.authentication.initial.checks.enabled=true

View File

@@ -192,3 +192,6 @@ ldap.synchronization.disabledAccountPropertyValueCanBeNull=true
# The Account Status Interpreter bean name
ldap.synchronization.userAccountStatusInterpreter=ldapUserAccountStatusInterpreter
# Allows to enable or disable LDAP initial checks (anonymous bind, simple dn, dn and principal)
ldap.authentication.initial.checks.enabled=true

View File

@@ -25,7 +25,12 @@
*/
package org.alfresco.repo.rule;
import static java.util.stream.Collectors.joining;
import static java.util.stream.Collectors.toList;
import static org.alfresco.model.ContentModel.ASSOC_CONTAINS;
import static org.alfresco.model.ContentModel.ASSOC_MEMBER;
import static org.alfresco.repo.rule.RuleModel.ASPECT_IGNORE_INHERITED_RULES;
import static org.alfresco.repo.rule.RuleModel.ASSOC_ACTION;
import static org.alfresco.repo.rule.RuleModel.ASSOC_RULE_FOLDER;
import static org.alfresco.repo.rule.RuleModel.TYPE_RULE;
@@ -33,6 +38,7 @@ import static org.alfresco.service.cmr.security.AccessStatus.ALLOWED;
import static org.alfresco.service.cmr.security.AccessStatus.DENIED;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
import static org.junit.Assert.assertEquals;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.ArgumentMatchers.nullable;
@@ -47,8 +53,13 @@ import static org.mockito.MockitoAnnotations.openMocks;
import java.io.Serializable;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
import org.alfresco.model.ContentModel;
import org.alfresco.repo.action.RuntimeActionService;
import org.alfresco.repo.cache.SimpleCache;
import org.alfresco.service.cmr.action.Action;
@@ -61,6 +72,7 @@ import org.alfresco.service.cmr.rule.RuleService;
import org.alfresco.service.cmr.rule.RuleServiceException;
import org.alfresco.service.cmr.security.PermissionService;
import org.alfresco.service.namespace.QName;
import org.apache.commons.collections.MapUtils;
import org.junit.Before;
import org.junit.Test;
import org.mockito.InjectMocks;
@@ -242,6 +254,8 @@ public class RuleServiceImplUnitTest
boolean associated = ruleService.isRuleSetAssociatedWithFolder(RULE_SET_NODE, FOLDER_NODE);
then(runtimeNodeService).should().getParentAssocs(RULE_SET_NODE, ASSOC_RULE_FOLDER, ASSOC_RULE_FOLDER);
then(runtimeNodeService).should().hasAspect(FOLDER_NODE, ASPECT_IGNORE_INHERITED_RULES);
then(runtimeNodeService).should().getParentAssocs(FOLDER_NODE);
then(runtimeNodeService).shouldHaveNoMoreInteractions();
then(nodeService).shouldHaveNoInteractions();
assertThat(associated).isTrue();
@@ -256,6 +270,8 @@ public class RuleServiceImplUnitTest
boolean associated = ruleService.isRuleSetAssociatedWithFolder(RULE_SET_NODE, FOLDER_NODE);
then(runtimeNodeService).should().getParentAssocs(RULE_SET_NODE, ASSOC_RULE_FOLDER, ASSOC_RULE_FOLDER);
then(runtimeNodeService).should().hasAspect(FOLDER_NODE, ASPECT_IGNORE_INHERITED_RULES);
then(runtimeNodeService).should().getParentAssocs(FOLDER_NODE);
then(runtimeNodeService).shouldHaveNoMoreInteractions();
then(nodeService).shouldHaveNoInteractions();
assertThat(associated).isFalse();
@@ -271,11 +287,45 @@ public class RuleServiceImplUnitTest
boolean associated = ruleService.isRuleSetAssociatedWithFolder(RULE_SET_NODE, FOLDER_NODE);
then(runtimeNodeService).should().getParentAssocs(RULE_SET_NODE, ASSOC_RULE_FOLDER, ASSOC_RULE_FOLDER);
then(runtimeNodeService).should().hasAspect(FOLDER_NODE, ASPECT_IGNORE_INHERITED_RULES);
then(runtimeNodeService).should().getParentAssocs(FOLDER_NODE);
then(runtimeNodeService).shouldHaveNoMoreInteractions();
then(nodeService).shouldHaveNoInteractions();
assertThat(associated).isFalse();
}
/**
* Check that a rule set is associated with the folder in the following case:
* <pre>
* parent --[link]-> rule set <-[owned]-- owningFolder
* +- child
* </pre>
*/
@Test
public void testIsRuleSetAssociatedWithFolder_inheritedLinkedAssociation()
{
// The rule is owned by one node.
NodeRef owningFolder = new NodeRef("owning://node/");
// The rule is linked to by the parent node.
NodeRef parent = new NodeRef("parent://node/");
List<ChildAssociationRef> ruleAssociations = List.of(createAssociation(owningFolder, RULE_SET_NODE), createAssociation(parent, RULE_SET_NODE));
given(runtimeNodeService.getParentAssocs(RULE_SET_NODE, ASSOC_RULE_FOLDER, ASSOC_RULE_FOLDER)).willReturn(ruleAssociations);
// The parent and the child both supply rule sets.
given(runtimeNodeService.getParentAssocs(FOLDER_NODE)).willReturn(List.of(createAssociation(parent, FOLDER_NODE)));
// when
boolean associated = ruleService.isRuleSetAssociatedWithFolder(RULE_SET_NODE, FOLDER_NODE);
then(runtimeNodeService).should().getParentAssocs(RULE_SET_NODE, ASSOC_RULE_FOLDER, ASSOC_RULE_FOLDER);
then(runtimeNodeService).should().hasAspect(FOLDER_NODE, ASPECT_IGNORE_INHERITED_RULES);
then(runtimeNodeService).should().getParentAssocs(FOLDER_NODE);
then(runtimeNodeService).should().hasAspect(parent, ASPECT_IGNORE_INHERITED_RULES);
then(runtimeNodeService).should().getParentAssocs(parent);
then(runtimeNodeService).shouldHaveNoMoreInteractions();
then(nodeService).shouldHaveNoInteractions();
assertThat(associated).isTrue();
}
@Test
public void testIsRuleAssociatedWithRuleSet()
{
@@ -356,4 +406,174 @@ public class RuleServiceImplUnitTest
{
return new ChildAssociationRef(null, parentRef, null, childRef, isPrimary, 1);
}
/** Check that a straight chain of nodes is traversed correctly. */
@Test
public void testGetNodesSupplyingRuleSets_chain()
{
Map<String, NodeRef> nodes = createParentChildHierarchy("A,B", "B,C", "C,D", "D,E");
List<NodeRef> actual = ruleService.getNodesSupplyingRuleSets(nodes.get("E"));
Map<NodeRef, String> invertedMap = MapUtils.invertMap(nodes);
String nodeNames = actual.stream().map(invertedMap::get).collect(joining(","));
assertEquals("A,B,C,D,E", nodeNames);
}
/** Check that ordered parents are returned in the correct order. */
@Test
public void testGetNodesSupplyingRuleSets_multipleParents()
{
Map<String, NodeRef> nodes = createParentChildHierarchy("A,E", "B,E", "C,E", "D,E");
List<NodeRef> actual = ruleService.getNodesSupplyingRuleSets(nodes.get("E"));
Map<NodeRef, String> invertedMap = MapUtils.invertMap(nodes);
String nodeNames = actual.stream().map(invertedMap::get).collect(joining(","));
assertEquals("A,B,C,D,E", nodeNames);
}
/** Check that the ASPECT_IGNORE_INHERITED_RULES aspect breaks the chain. */
@Test
public void testGetNodesSupplyingRuleSets_brokenChain()
{
Map<String, NodeRef> nodes = createParentChildHierarchy("A,B", "B,C", "C,D", "D,E");
given(runtimeNodeService.hasAspect(nodes.get("C"), ASPECT_IGNORE_INHERITED_RULES)).willReturn(true);
List<NodeRef> actual = ruleService.getNodesSupplyingRuleSets(nodes.get("E"));
Map<NodeRef, String> invertedMap = MapUtils.invertMap(nodes);
String nodeNames = actual.stream().map(invertedMap::get).collect(joining(","));
assertEquals("C,D,E", nodeNames);
}
/** Check that the user group hierarchy is not traversed. */
@Test
public void testGetNodesSupplyingRuleSets_userGroupHierarchy()
{
Map<String, NodeRef> nodes = createParentChildHierarchy("A,B", "B,C", "C,D", "D,E");
// Replace the B,C association with a user group membership association.
ChildAssociationRef memberAssoc = new ChildAssociationRef(ASSOC_MEMBER, nodes.get("B"), ContentModel.TYPE_FOLDER, nodes.get("C"));
given(runtimeNodeService.getParentAssocs(nodes.get("C"))).willReturn(List.of(memberAssoc));
List<NodeRef> actual = ruleService.getNodesSupplyingRuleSets(nodes.get("E"));
Map<NodeRef, String> invertedMap = MapUtils.invertMap(nodes);
String nodeNames = actual.stream().map(invertedMap::get).collect(joining(","));
assertEquals("C,D,E", nodeNames);
}
/** Check that a cycle doesn't cause a problem. */
@Test
public void testGetNodesSupplyingRuleSets_infiniteCycle()
{
Map<String, NodeRef> nodes = createParentChildHierarchy("A,B", "B,C", "C,A");
List<NodeRef> actual = ruleService.getNodesSupplyingRuleSets(nodes.get("C"));
Map<NodeRef, String> invertedMap = MapUtils.invertMap(nodes);
String nodeNames = actual.stream().map(invertedMap::get).collect(joining(","));
assertEquals("A,B,C", nodeNames);
}
/** Check that a diamond of nodes is traversed correctly. */
@Test
public void testGetNodesSupplyingRuleSets_diamond()
{
Map<String, NodeRef> nodes = createParentChildHierarchy("A,B", "A,C", "B,D", "C,D");
List<NodeRef> actual = ruleService.getNodesSupplyingRuleSets(nodes.get("D"));
Map<NodeRef, String> invertedMap = MapUtils.invertMap(nodes);
String nodeNames = actual.stream().map(invertedMap::get).collect(joining(","));
assertEquals("A,B,C,D", nodeNames);
}
/**
* Check that hierarchy of nodes is traversed correctly. Parent-child associations are created in alphabetical order.
* <pre>
* A
* /|\
* B C D
* | |\|
* E | F
* \|/
* G
* </pre>
*/
@Test
public void testGetNodesSupplyingRuleSets_alphabetical()
{
Map<String, NodeRef> nodes = createParentChildHierarchy("A,B", "A,C", "A,D", "B,E", "C,F", "C,G", "D,F", "E,G", "F,G");
List<NodeRef> actual = ruleService.getNodesSupplyingRuleSets(nodes.get("G"));
Map<NodeRef, String> invertedMap = MapUtils.invertMap(nodes);
String nodeNames = actual.stream().map(invertedMap::get).collect(joining(","));
assertEquals("A,C,B,E,D,F,G", nodeNames);
}
/**
* Check that hierarchy of nodes is traversed correctly. Parent-child associations are created in reverse alphabetical order.
* <pre>
* A
* /|\
* B C D
* | |\|
* E | F
* \|/
* G
* </pre>
*/
@Test
public void testGetNodesSupplyingRuleSets_reversedAssociationOrder()
{
Map<String, NodeRef> nodes = createParentChildHierarchy("F,G", "E,G", "D,F", "C,G", "C,F", "B,E", "A,D", "A,C", "A,B");
List<NodeRef> actual = ruleService.getNodesSupplyingRuleSets(nodes.get("G"));
Map<NodeRef, String> invertedMap = MapUtils.invertMap(nodes);
String nodeNames = actual.stream().map(invertedMap::get).collect(joining(","));
assertEquals("A,D,C,F,B,E,G", nodeNames);
}
/**
* Create a mock hierarchy of nodes using the supplied parent child associations.
*
* @param parentChildAssociations A list of strings of the form "Parent,Child". Associations will be created in this order.
* @return A map from the node name to the new NodeRef object.
*/
private Map<String, NodeRef> createParentChildHierarchy(String... parentChildAssociations)
{
// Find all the node names mentioned.
Set<String> nodeNames = new HashSet<>();
List.of(parentChildAssociations).forEach(parentChildAssociation -> {
String[] parentChildPair = parentChildAssociation.split(",");
nodeNames.addAll(List.of(parentChildPair));
});
// Create the NodeRefs.
Map<String, NodeRef> nodeRefMap = nodeNames.stream().collect(
Collectors.toMap(nodeName -> nodeName, nodeName -> new NodeRef("node://" + nodeName + "/")));
// Mock the associations.
nodeNames.forEach(nodeName -> {
NodeRef nodeRef = nodeRefMap.get(nodeName);
List<ChildAssociationRef> parentAssocs = List.of(parentChildAssociations)
.stream()
.filter(assoc -> assoc.endsWith(nodeName))
.map(assoc -> assoc.split(",")[0])
.map(nodeRefMap::get)
.map(parentRef -> new ChildAssociationRef(ASSOC_CONTAINS, parentRef, ContentModel.TYPE_FOLDER, nodeRef))
.collect(toList());
given(runtimeNodeService.getParentAssocs(nodeRef)).willReturn(parentAssocs);
});
return nodeRefMap;
}
}

View File

@@ -0,0 +1,11 @@
function testPreferences()
{
var preferences = {
"org.alfresco.share.forum.summary.dashlet.component-1-3.history": "1"
};
preferenceService.setPreferences(username, preferences);
}
// Execute tests
testPreferences();