Add regex support in 'Condition - User attribute' execution
Closes #265
This commit is contained in:
parent
48ab2b1688
commit
0044472f87
3 changed files with 48 additions and 3 deletions
|
@ -27,6 +27,7 @@ import org.keycloak.models.utils.KeycloakModelUtils;
|
||||||
|
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
public class ConditionalUserAttributeValue implements ConditionalAuthenticator {
|
public class ConditionalUserAttributeValue implements ConditionalAuthenticator {
|
||||||
|
|
||||||
|
@ -40,15 +41,16 @@ public class ConditionalUserAttributeValue implements ConditionalAuthenticator {
|
||||||
String attributeValue = config.get(ConditionalUserAttributeValueFactory.CONF_ATTRIBUTE_EXPECTED_VALUE);
|
String attributeValue = config.get(ConditionalUserAttributeValueFactory.CONF_ATTRIBUTE_EXPECTED_VALUE);
|
||||||
boolean includeGroupAttributes = Boolean.parseBoolean(config.get(ConditionalUserAttributeValueFactory.CONF_INCLUDE_GROUP_ATTRIBUTES));
|
boolean includeGroupAttributes = Boolean.parseBoolean(config.get(ConditionalUserAttributeValueFactory.CONF_INCLUDE_GROUP_ATTRIBUTES));
|
||||||
boolean negateOutput = Boolean.parseBoolean(config.get(ConditionalUserAttributeValueFactory.CONF_NOT));
|
boolean negateOutput = Boolean.parseBoolean(config.get(ConditionalUserAttributeValueFactory.CONF_NOT));
|
||||||
|
boolean regexOutput = Boolean.parseBoolean(config.get(ConditionalUserAttributeValueFactory.REGEX));
|
||||||
|
|
||||||
UserModel user = context.getUser();
|
UserModel user = context.getUser();
|
||||||
if (user == null) {
|
if (user == null) {
|
||||||
throw new AuthenticationFlowException("Cannot find user for obtaining particular user attributes. Authenticator: " + ConditionalUserAttributeValueFactory.PROVIDER_ID, AuthenticationFlowError.UNKNOWN_USER);
|
throw new AuthenticationFlowException("Cannot find user for obtaining particular user attributes. Authenticator: " + ConditionalUserAttributeValueFactory.PROVIDER_ID, AuthenticationFlowError.UNKNOWN_USER);
|
||||||
}
|
}
|
||||||
|
|
||||||
boolean result = user.getAttributeStream(attributeName).anyMatch(attr -> Objects.equals(attr, attributeValue));
|
boolean result = user.getAttributeStream(attributeName).anyMatch(attr -> regexOutput ? Pattern.compile(attributeValue).matcher(attr).matches() : Objects.equals(attr, attributeValue));
|
||||||
if (!result && includeGroupAttributes) {
|
if (!result && includeGroupAttributes) {
|
||||||
result = KeycloakModelUtils.resolveAttribute(user, attributeName, true).stream().anyMatch(attr -> Objects.equals(attr, attributeValue));
|
result = KeycloakModelUtils.resolveAttribute(user, attributeName, true).stream().anyMatch(attr -> regexOutput ? Pattern.compile(attributeValue).matcher(attr).matches() : Objects.equals(attr, attributeValue));
|
||||||
}
|
}
|
||||||
return negateOutput != result;
|
return negateOutput != result;
|
||||||
}
|
}
|
||||||
|
|
|
@ -33,6 +33,7 @@ public class ConditionalUserAttributeValueFactory implements ConditionalAuthenti
|
||||||
public static final String CONF_ATTRIBUTE_EXPECTED_VALUE = "attribute_expected_value";
|
public static final String CONF_ATTRIBUTE_EXPECTED_VALUE = "attribute_expected_value";
|
||||||
public static final String CONF_INCLUDE_GROUP_ATTRIBUTES = "include_group_attributes";
|
public static final String CONF_INCLUDE_GROUP_ATTRIBUTES = "include_group_attributes";
|
||||||
public static final String CONF_NOT = "not";
|
public static final String CONF_NOT = "not";
|
||||||
|
public static final String REGEX = "regex";
|
||||||
|
|
||||||
private static final AuthenticationExecutionModel.Requirement[] REQUIREMENT_CHOICES = {
|
private static final AuthenticationExecutionModel.Requirement[] REQUIREMENT_CHOICES = {
|
||||||
AuthenticationExecutionModel.Requirement.REQUIRED, AuthenticationExecutionModel.Requirement.DISABLED
|
AuthenticationExecutionModel.Requirement.REQUIRED, AuthenticationExecutionModel.Requirement.DISABLED
|
||||||
|
@ -109,7 +110,13 @@ public class ConditionalUserAttributeValueFactory implements ConditionalAuthenti
|
||||||
negateOutput.setLabel("Negate output");
|
negateOutput.setLabel("Negate output");
|
||||||
negateOutput.setHelpText("Apply a not to the check result");
|
negateOutput.setHelpText("Apply a not to the check result");
|
||||||
|
|
||||||
return Arrays.asList(authNoteName, authNoteExpectedValue, includeGroupAttributes, negateOutput);
|
ProviderConfigProperty regexOutput = new ProviderConfigProperty();
|
||||||
|
regexOutput.setType(ProviderConfigProperty.BOOLEAN_TYPE);
|
||||||
|
regexOutput.setName(REGEX);
|
||||||
|
regexOutput.setLabel(REGEX);
|
||||||
|
regexOutput.setHelpText("Check equality with regex");
|
||||||
|
|
||||||
|
return Arrays.asList(authNoteName, authNoteExpectedValue, includeGroupAttributes, negateOutput, regexOutput);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -146,6 +146,42 @@ public class AllowDenyAuthenticatorTest extends AbstractTestRealmKeycloakTest {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testDenyAccessWithRegexUserAttributeCondition() {
|
||||||
|
final String flowAlias = "browser - user attribute condition";
|
||||||
|
final String userWithoutAttribute = "test-user@localhost";
|
||||||
|
final String errorMessage = "You don't have necessary attribute.";
|
||||||
|
|
||||||
|
Map<String, String> attributeConfigMap = new HashMap<>();
|
||||||
|
attributeConfigMap.put(ConditionalUserAttributeValueFactory.CONF_ATTRIBUTE_NAME, "firstName");
|
||||||
|
attributeConfigMap.put(ConditionalUserAttributeValueFactory.CONF_ATTRIBUTE_EXPECTED_VALUE, "T(.*)");
|
||||||
|
attributeConfigMap.put(ConditionalUserAttributeValueFactory.REGEX, "true");
|
||||||
|
|
||||||
|
Map<String, String> denyAccessConfigMap = new HashMap<>();
|
||||||
|
denyAccessConfigMap.put(DenyAccessAuthenticatorFactory.ERROR_MESSAGE, errorMessage);
|
||||||
|
|
||||||
|
configureBrowserFlowWithDenyAccessInConditionalFlow(flowAlias, ConditionalUserAttributeValueFactory.PROVIDER_ID, attributeConfigMap, denyAccessConfigMap);
|
||||||
|
|
||||||
|
try {
|
||||||
|
loginUsernameOnlyPage.open();
|
||||||
|
loginUsernameOnlyPage.assertCurrent();
|
||||||
|
loginUsernameOnlyPage.login(userWithoutAttribute);
|
||||||
|
|
||||||
|
errorPage.assertCurrent();
|
||||||
|
assertThat(errorPage.getError(), is(errorMessage));
|
||||||
|
|
||||||
|
events.expectLogin()
|
||||||
|
.user((String) null)
|
||||||
|
.session((String) null)
|
||||||
|
.error(Errors.ACCESS_DENIED)
|
||||||
|
.detail(Details.USERNAME, userWithoutAttribute)
|
||||||
|
.removeDetail(Details.CONSENT)
|
||||||
|
.assertEvent();
|
||||||
|
} finally {
|
||||||
|
revertFlows(testRealm(), flowAlias);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Deny access, if user has defined the role and print error message.
|
* Deny access, if user has defined the role and print error message.
|
||||||
*/
|
*/
|
||||||
|
|
Loading…
Reference in a new issue