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;
}
}