Skip to content

Commit

Permalink
Support for multiple check expressions in the value policy (MID-1657)
Browse files Browse the repository at this point in the history
  • Loading branch information
semancik committed Mar 7, 2017
1 parent 43fc708 commit 1bc285f
Show file tree
Hide file tree
Showing 7 changed files with 135 additions and 46 deletions.
Expand Up @@ -9520,23 +9520,16 @@
</xsd:element>
<xsd:element name="checkAgainstDictionary" type="xsd:boolean" default="false" minOccurs="0" maxOccurs="1"/>
<xsd:element name="checkPattern" type="xsd:string" minOccurs="0" maxOccurs="1"/>
<xsd:element name="checkExpression" type="c:ExpressionType" minOccurs="0">
<xsd:element name="checkExpression" type="c:CheckExpressionType" minOccurs="0" maxOccurs="unbounded">
<xsd:annotation>
<xsd:documentation>
<p>
Expression that is used to check the resulting value whether it is acceptable
or not. If the expression returns true, then the value is accepted.
If the expression returns false value, then the value is rejected and it
will be generated again. Until the maximum number of attempts is reached.
</p>
</xsd:documentation>
</xsd:annotation>
</xsd:element>
<xsd:element name="checkExpressionMessage" type="xsd:string" minOccurs="0">
<xsd:annotation>
<xsd:documentation>
<p>
Message thet will be displayed to user when the expression check fails.
If there are several expressions that all of them must pass for the value to
be accepted. However each of them may produce a different failure message.
</p>
</xsd:documentation>
</xsd:annotation>
Expand Down Expand Up @@ -9586,6 +9579,39 @@
<xsd:attribute name="ref" type="xsd:QName" use="optional"/>
<xsd:attribute name="name" type="xsd:QName" use="optional"/>
</xsd:complexType>

<xsd:complexType name="CheckExpressionType">
<xsd:annotation>
<xsd:documentation>
Expression used to check the data and report a user-friendly message in case
that the check fails.
</xsd:documentation>
</xsd:annotation>
<xsd:sequence>
<xsd:element name="expression" type="c:ExpressionType" minOccurs="0">
<xsd:annotation>
<xsd:documentation>
<p>
Expression that is used to check the resulting value whether it is acceptable
or not. If the expression returns true, then the value is accepted.
If the expression returns false value, then the value is rejected.
</p>
</xsd:documentation>
</xsd:annotation>
</xsd:element>
<xsd:element name="failureMessage" type="xsd:string" minOccurs="0">
<xsd:annotation>
<xsd:documentation>
<p>
Message thet will be displayed to user when the expression check fails.
</p>
</xsd:documentation>
</xsd:annotation>
</xsd:element>
<!-- TODO: sucess message? -->
</xsd:sequence>
</xsd:complexType>


<xsd:complexType name="OperationResultType">
<xsd:annotation>
Expand Down
Expand Up @@ -52,6 +52,7 @@
import com.evolveum.midpoint.util.exception.SchemaException;
import com.evolveum.midpoint.util.logging.Trace;
import com.evolveum.midpoint.util.logging.TraceManager;
import com.evolveum.midpoint.xml.ns._public.common.common_3.CheckExpressionType;
import com.evolveum.midpoint.xml.ns._public.common.common_3.ExpressionType;
import com.evolveum.midpoint.xml.ns._public.common.common_3.LimitationsType;
import com.evolveum.midpoint.xml.ns._public.common.common_3.ObjectType;
Expand Down Expand Up @@ -362,14 +363,24 @@ private <O extends ObjectType> boolean checkAttempt(String generatedValue, Strin
if (limitationsType == null) {
return true;
}
ExpressionType checkExpression = limitationsType.getCheckExpression();
if (checkExpression != null && !checkExpression(generatedValue, checkExpression, object, shortDesc, task, result)) {
List<CheckExpressionType> checkExpressionTypes = limitationsType.getCheckExpression();
if (!checkExpressions(generatedValue, checkExpressionTypes, object, shortDesc, task, result)) {
LOGGER.trace("Check expression returned false for generated value in {}", shortDesc);
return false;
}
// TODO Check pattern
return true;
}

private <O extends ObjectType> boolean checkExpressions(String generatedValue, List<CheckExpressionType> checkExpressionTypes, PrismObject<O> object, String shortDesc, Task task, OperationResult result) throws SchemaException, ObjectNotFoundException, ExpressionEvaluationException {
for (CheckExpressionType checkExpressionType: checkExpressionTypes) {
ExpressionType expression = checkExpressionType.getExpression();
if (!checkExpression(generatedValue, expression, object, shortDesc, task, result)) {
return false;
}
}
return true;
}

public <O extends ObjectType> boolean checkExpression(String generatedValue, ExpressionType checkExpression, PrismObject<O> object, String shortDesc, Task task, OperationResult result) throws SchemaException, ObjectNotFoundException, ExpressionEvaluationException {
ExpressionVariables variables = new ExpressionVariables();
Expand Down
Expand Up @@ -68,6 +68,7 @@
import com.evolveum.midpoint.util.logging.Trace;
import com.evolveum.midpoint.util.logging.TraceManager;
import com.evolveum.midpoint.xml.ns._public.common.common_3.CharacterClassType;
import com.evolveum.midpoint.xml.ns._public.common.common_3.CheckExpressionType;
import com.evolveum.midpoint.xml.ns._public.common.common_3.CredentialsType;
import com.evolveum.midpoint.xml.ns._public.common.common_3.ExpressionType;
import com.evolveum.midpoint.xml.ns._public.common.common_3.FocusType;
Expand Down Expand Up @@ -754,19 +755,25 @@ private void testMaximalLength(String password, LimitationsType limitations,
private <O extends ObjectType> void testCheckExpression(String newPassword, LimitationsType lims, PrismObject<O> object,
String shortDesc, Task task, OperationResult result, StringBuilder message) throws SchemaException, ObjectNotFoundException, ExpressionEvaluationException {

ExpressionType expressionType = lims.getCheckExpression();
if (expressionType == null) {
List<CheckExpressionType> checkExpressions = lims.getCheckExpression();
if (checkExpressions.isEmpty()) {
return;
}
if (!valuePolicyGenerator.checkExpression(newPassword, expressionType, object, shortDesc, task, result)) {
String msg = lims.getCheckExpressionMessage();
if (msg == null) {
msg = "Check expression failed";
for (CheckExpressionType checkExpression: checkExpressions) {
ExpressionType expressionType = checkExpression.getExpression();
if (expressionType == null) {
return;
}
if (!valuePolicyGenerator.checkExpression(newPassword, expressionType, object, shortDesc, task, result)) {
String msg = checkExpression.getFailureMessage();
if (msg == null) {
msg = "Check expression failed";
}
result.addSubresult(new OperationResult("Check expression",
OperationResultStatus.FATAL_ERROR, msg));
message.append(msg);
message.append("\n");
}
result.addSubresult(new OperationResult("Check expression",
OperationResultStatus.FATAL_ERROR, msg));
message.append(msg);
message.append("\n");
}

}
Expand Down
Expand Up @@ -28,11 +28,24 @@
<maxLength>10</maxLength>
<minUniqueChars>0</minUniqueChars>
<checkExpression>
<script>
<code>
!basic.containsIgnoreCase(input, object.getName()) &amp;&amp; !basic.containsIgnoreCase(input, object.getAdditionalName()) &amp;&amp; !basic.containsIgnoreCase(input, object.getGivenName())
</code>
</script>
<expression>
<script>
<code>
!basic.containsIgnoreCase(input, object.getName())
</code>
</script>
</expression>
<failureMessage>Boom! username</failureMessage>
</checkExpression>
<checkExpression>
<expression>
<script>
<code>
!basic.containsIgnoreCase(input, object.getAdditionalName()) &amp;&amp; !basic.containsIgnoreCase(input, object.getGivenName())
</code>
</script>
</expression>
<failureMessage>Boom! names</failureMessage>
</checkExpression>
<maxAttempts>30</maxAttempts>
<limit>
Expand Down
Expand Up @@ -28,11 +28,13 @@
<maxLength>2</maxLength>
<minUniqueChars>0</minUniqueChars>
<checkExpression>
<script>
<code>
input != basic.stringify(object.getName())
</code>
</script>
<expression>
<script>
<code>
input != basic.stringify(object.getName())
</code>
</script>
</expression>
</checkExpression>
<limit>
<minOccurs>2</minOccurs>
Expand Down
Expand Up @@ -64,6 +64,7 @@ public class TestPassword extends AbstractInitializedModelIntegrationTest {
private static final String USER_PASSWORD_5_CLEAR = "s3tSa1al";
private static final String USER_PASSWORD_A_CLEAR = "A"; // too short
private static final String USER_PASSWORD_JACK_CLEAR = "12jAcK34"; // contains username
private static final String USER_PASSWORD_SPARROW_CLEAR = "saRRow123"; // contains familyName
private static final String USER_PASSWORD_VALID_1 = "abcd123";
private static final String USER_PASSWORD_VALID_2 = "abcd223";
private static final String USER_PASSWORD_VALID_3 = "abcd323";
Expand Down Expand Up @@ -818,6 +819,16 @@ public void test224ModifyUserJackPasswordBadJack() throws Exception {
USER_PASSWORD_JACK_CLEAR, USER_PASSWORD_VALID_1, USER_PASSWORD_A_CLEAR);
}

/**
* Change to password that violates the password policy (contains family name)
* MID-1657
*/
@Test
public void test226ModifyUserJackPasswordBadSparrow() throws Exception {
doTestModifyUserJackPasswordFailureWithHistory("test226ModifyUserJackPasswordBadSparrow",
USER_PASSWORD_SPARROW_CLEAR, USER_PASSWORD_VALID_1, USER_PASSWORD_A_CLEAR);
}

/**
* Change to password that complies with password policy. Again. See that
* the change is applied correctly and that it is included in the history.
Expand Down
Expand Up @@ -30,24 +30,43 @@
<description>Testing string policy</description>
<limitations>
<minLength>5</minLength>
<maxLength>8</maxLength>
<maxLength>12</maxLength>
<minUniqueChars>3</minUniqueChars>
<checkAgainstDictionary>true</checkAgainstDictionary>
<checkExpression> <!-- MID-1657 -->
<script>
<code>
assert input != null
assert object != null
assert object.getName() != null
if (object instanceof com.evolveum.midpoint.xml.ns._public.common.common_3.UserType) {
return !basic.containsIgnoreCase(input, object.getName()) &amp;&amp; !basic.containsIgnoreCase(input, object.getFamilyName()) &amp;&amp; !basic.containsIgnoreCase(input, object.getGivenName()) &amp;&amp; !basic.containsIgnoreCase(input, object.getAdditionalName())
} else {
return true
}
</code>
</script>
<expression>
<script>
<code>
assert input != null
assert object != null
assert object.getName() != null
if (object instanceof com.evolveum.midpoint.xml.ns._public.common.common_3.UserType) {
return !basic.containsIgnoreCase(input, object.getName())
} else {
return true
}
</code>
</script>
</expression>
<failureMessage>must not contain username</failureMessage>
</checkExpression>
<checkExpression> <!-- MID-1657 -->
<expression>
<script>
<code>
assert input != null
assert object != null
assert object.getName() != null
if (object instanceof com.evolveum.midpoint.xml.ns._public.common.common_3.UserType) {
return !basic.containsIgnoreCase(input, object.getFamilyName()) &amp;&amp; !basic.containsIgnoreCase(input, object.getGivenName()) &amp;&amp; !basic.containsIgnoreCase(input, object.getAdditionalName())
} else {
return true
}
</code>
</script>
</expression>
<failureMessage>must not contain family name and given name and additional names</failureMessage>
</checkExpression>
<checkExpressionMessage>must not contain username, family name and given name and additional names</checkExpressionMessage>
<limit>
<description>Alphas</description>
<minOccurs>1</minOccurs>
Expand Down

0 comments on commit 1bc285f

Please sign in to comment.