package org.keycloak.authentication.authenticators.browser; import org.keycloak.authentication.AuthenticationFlowContext; import org.keycloak.models.*; import javax.ws.rs.core.MultivaluedMap; import javax.ws.rs.core.Response; import java.util.*; import java.util.regex.Pattern; /** * @author Thomas Darimont */ public class DynamicOtpFormAuthenticator extends OTPFormAuthenticator { @Override public void authenticate(AuthenticationFlowContext context) { Map config = context.getAuthenticatorConfig().getConfig(); if (config.isEmpty()) { context.success(); return; } if (userHasOtpTriggerRole(context, config)) { Response challengeResponse = challenge(context, null); context.challenge(challengeResponse); return; } String positiveTriggerRequestHeaderPattern = config.get(DynamicOtpFormAuthenticatorFactory.POSITIVE_HTTP_HEADER_PATTERN); if (positiveTriggerRequestHeaderPattern != null && !positiveTriggerRequestHeaderPattern.trim().isEmpty()) { if (httpRequestMatchesTriggerPattern(context, DynamicOtpFormAuthenticatorFactory.POSITIVE_HTTP_HEADER_PATTERN)) { Response challengeResponse = challenge(context, null); context.challenge(challengeResponse); return; } } String negativeTriggerRequestHeaderPattern = config.get(DynamicOtpFormAuthenticatorFactory.NEGATIVE_HTTP_HEADER_PATTERN); if (negativeTriggerRequestHeaderPattern != null && !negativeTriggerRequestHeaderPattern.trim().isEmpty()) { if (!httpRequestMatchesTriggerPattern(context, negativeTriggerRequestHeaderPattern)) { Response challengeResponse = challenge(context, null); context.challenge(challengeResponse); return; } } context.success(); } private boolean httpRequestMatchesTriggerPattern(AuthenticationFlowContext context, String triggerRequestHeaderPattern) { MultivaluedMap requestHeaders = context.getHttpRequest().getHttpHeaders().getRequestHeaders(); Pattern pattern = Pattern.compile(triggerRequestHeaderPattern, Pattern.DOTALL); for (Map.Entry> entry : requestHeaders.entrySet()) { String key = entry.getKey(); for (String value : entry.getValue()) { String headerEntry = key.trim() + ": " + value.trim(); if (pattern.matcher(headerEntry).matches()) { return true; } } } return false; } private boolean userHasOtpTriggerRole(AuthenticationFlowContext context, Map config) { String triggerRole = config.get(DynamicOtpFormAuthenticatorFactory.TRIGGER_ROLE); if (triggerRole != null) { for (RoleModel role : context.getUser().getRealmRoleMappings()) { if (role.isComposite()) { for (RoleModel compositeRole : role.getComposites()) { if (triggerRole.equals(compositeRole.getName())) { return true; } } } else { if (triggerRole.equals(role.getName())) { return true; } } } } return false; } }