KEYCLOAK-2311 - Allow to specify role to skip conditional OTP authentication.
We now allow specify a role to skip OTP. Previously it was not possible to specify that OTP authentication should be skipped via a role but the ConditionalOtpAuthenticator allowed to specify to show/skip OTP via a user attribute or HTTP request header pattern. Having the "skip role" aligns the role based configuration options with the user attribute and HTTP request header configuration.
This commit is contained in:
parent
714e2fa7a5
commit
92c2ec366d
2 changed files with 31 additions and 9 deletions
|
@ -37,8 +37,9 @@ import static org.keycloak.models.utils.KeycloakModelUtils.hasRole;
|
|||
* </p>
|
||||
* <p>
|
||||
* <h2>Role</h2>
|
||||
* A role can be used to control the OTP authentication. If the user has the specified role the OTP authentication is forced.
|
||||
* Otherwise if no role is selected the setting is ignored.
|
||||
* A role can be used to control the OTP authentication. If the user has the specified skip OTP role then OTP authentication is skipped for the user.
|
||||
* If the user has the specified force OTP role, then the OTP authentication is required for the user.
|
||||
* If not configured, e.g. if no role is selected, then this setting is ignored.
|
||||
* <p>
|
||||
* </p>
|
||||
* <p>
|
||||
|
@ -67,6 +68,8 @@ public class ConditionalOtpFormAuthenticator extends OTPFormAuthenticator {
|
|||
|
||||
public static final String OTP_CONTROL_USER_ATTRIBUTE = "otpControlAttribute";
|
||||
|
||||
public static final String SKIP_OTP_ROLE = "skipOtpRole";
|
||||
|
||||
public static final String FORCE_OTP_ROLE = "forceOtpRole";
|
||||
|
||||
public static final String NO_OTP_REQUIRED_FOR_HTTP_HEADER = "noOtpRequiredForHeaderPattern";
|
||||
|
@ -88,7 +91,7 @@ public class ConditionalOtpFormAuthenticator extends OTPFormAuthenticator {
|
|||
return;
|
||||
}
|
||||
|
||||
if (tryConcludeBasedOn(voteForUserForceOtpRole(context, config), context)) {
|
||||
if (tryConcludeBasedOn(voteForUserRole(context, config), context)) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -216,19 +219,32 @@ public class ConditionalOtpFormAuthenticator extends OTPFormAuthenticator {
|
|||
return false;
|
||||
}
|
||||
|
||||
private OtpDecision voteForUserForceOtpRole(AuthenticationFlowContext context, Map<String, String> config) {
|
||||
private OtpDecision voteForUserRole(AuthenticationFlowContext context, Map<String, String> config) {
|
||||
|
||||
if (!config.containsKey(FORCE_OTP_ROLE)) {
|
||||
if (!config.containsKey(SKIP_OTP_ROLE) && !config.containsKey(FORCE_OTP_ROLE)) {
|
||||
return ABSTAIN;
|
||||
}
|
||||
|
||||
RoleModel forceOtpRole = getRoleFromString(context.getRealm(), config.get(FORCE_OTP_ROLE));
|
||||
UserModel user = context.getUser();
|
||||
if (userHasRole(context, config.get(SKIP_OTP_ROLE))) {
|
||||
return SKIP_OTP;
|
||||
}
|
||||
|
||||
if (hasRole(user.getRoleMappings(), forceOtpRole)) {
|
||||
if (userHasRole(context, config.get(FORCE_OTP_ROLE))) {
|
||||
return SHOW_OTP;
|
||||
}
|
||||
|
||||
return ABSTAIN;
|
||||
}
|
||||
|
||||
private boolean userHasRole(AuthenticationFlowContext context, String roleName) {
|
||||
|
||||
if (roleName == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
RoleModel role = getRoleFromString(context.getRealm(), roleName);
|
||||
UserModel user = context.getUser();
|
||||
|
||||
return hasRole(user.getRoleMappings(), role);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -98,6 +98,12 @@ public class ConditionalOtpFormAuthenticatorFactory implements AuthenticatorFact
|
|||
"If attribute value is 'force' then OTP is always required. " +
|
||||
"If value is 'skip' the OTP auth is skipped. Otherwise this check is ignored.");
|
||||
|
||||
ProviderConfigProperty skipOtpRole = new ProviderConfigProperty();
|
||||
skipOtpRole.setType(ROLE_TYPE);
|
||||
skipOtpRole.setName(SKIP_OTP_ROLE);
|
||||
skipOtpRole.setLabel("Skip OTP for Role");
|
||||
skipOtpRole.setHelpText("OTP is always skipped if user has the given Role.");
|
||||
|
||||
ProviderConfigProperty forceOtpRole = new ProviderConfigProperty();
|
||||
forceOtpRole.setType(ROLE_TYPE);
|
||||
forceOtpRole.setName(FORCE_OTP_ROLE);
|
||||
|
@ -127,6 +133,6 @@ public class ConditionalOtpFormAuthenticatorFactory implements AuthenticatorFact
|
|||
defaultOutcome.setDefaultValue(asList(SKIP, FORCE));
|
||||
defaultOutcome.setHelpText("What to do in case of every check abstains. Defaults to force OTP authentication.");
|
||||
|
||||
return asList(forceOtpUserAttribute, forceOtpRole, noOtpRequiredForHttpHeader, forceOtpForHttpHeader, defaultOutcome);
|
||||
return asList(forceOtpUserAttribute, skipOtpRole, forceOtpRole, noOtpRequiredForHttpHeader, forceOtpForHttpHeader, defaultOutcome);
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue