keycloak-aplcache

Details

diff --git a/model/api/src/main/java/org/keycloak/models/utils/DefaultAuthenticationFlows.java b/model/api/src/main/java/org/keycloak/models/utils/DefaultAuthenticationFlows.java
index 98e960c..40a8999 100755
--- a/model/api/src/main/java/org/keycloak/models/utils/DefaultAuthenticationFlows.java
+++ b/model/api/src/main/java/org/keycloak/models/utils/DefaultAuthenticationFlows.java
@@ -1,6 +1,8 @@
 package org.keycloak.models.utils;
 
 import java.util.HashMap;
+import java.util.LinkedList;
+import java.util.List;
 import java.util.Map;
 
 import org.keycloak.models.AuthenticationExecutionModel;
@@ -36,7 +38,7 @@ public class DefaultAuthenticationFlows {
         if (realm.getFlowByAlias(REGISTRATION_FLOW) == null) registrationFlow(realm);
         if (realm.getFlowByAlias(RESET_CREDENTIALS_FLOW) == null) resetCredentialsFlow(realm);
         if (realm.getFlowByAlias(CLIENT_AUTHENTICATION_FLOW) == null) clientAuthFlow(realm);
-        if (realm.getFlowByAlias(FIRST_BROKER_LOGIN_FLOW) == null) firstBrokerLoginFlow(realm);
+        if (realm.getFlowByAlias(FIRST_BROKER_LOGIN_FLOW) == null) firstBrokerLoginFlow(realm, false);
     }
     public static void migrateFlows(RealmModel realm) {
         if (realm.getFlowByAlias(BROWSER_FLOW) == null) browserFlow(realm, true);
@@ -44,7 +46,7 @@ public class DefaultAuthenticationFlows {
         if (realm.getFlowByAlias(REGISTRATION_FLOW) == null) registrationFlow(realm);
         if (realm.getFlowByAlias(RESET_CREDENTIALS_FLOW) == null) resetCredentialsFlow(realm);
         if (realm.getFlowByAlias(CLIENT_AUTHENTICATION_FLOW) == null) clientAuthFlow(realm);
-        if (realm.getFlowByAlias(FIRST_BROKER_LOGIN_FLOW) == null) firstBrokerLoginFlow(realm);
+        if (realm.getFlowByAlias(FIRST_BROKER_LOGIN_FLOW) == null) firstBrokerLoginFlow(realm, true);
     }
 
     public static void registrationFlow(RealmModel realm) {
@@ -322,7 +324,7 @@ public class DefaultAuthenticationFlows {
         realm.addAuthenticatorExecution(execution);
     }
 
-    public static void firstBrokerLoginFlow(RealmModel realm) {
+    public static void firstBrokerLoginFlow(RealmModel realm, boolean migrate) {
         AuthenticationFlowModel firstBrokerLogin = new AuthenticationFlowModel();
         firstBrokerLogin.setAlias(FIRST_BROKER_LOGIN_FLOW);
         firstBrokerLogin.setDescription("Actions taken after first broker login with identity provider account, which is not yet linked to any Keycloak account");
@@ -423,10 +425,19 @@ public class DefaultAuthenticationFlows {
         execution = new AuthenticationExecutionModel();
         execution.setParentFlow(verifyByReauthenticationAccountFlow.getId());
         execution.setRequirement(AuthenticationExecutionModel.Requirement.OPTIONAL);
-        // TODO: read the requirement from browser authenticator
-//        if (migrate && hasCredentialType(realm, RequiredCredentialModel.TOTP.getType())) {
-//            execution.setRequirement(AuthenticationExecutionModel.Requirement.REQUIRED);
-//        }
+
+        if (migrate) {
+            // Try to read OTP requirement from browser flow
+            AuthenticationFlowModel browserFlow = realm.getBrowserFlow();
+            List<AuthenticationExecutionModel> browserExecutions = new LinkedList<>();
+            KeycloakModelUtils.deepFindAuthenticationExecutions(realm, browserFlow, browserExecutions);
+            for (AuthenticationExecutionModel browserExecution : browserExecutions) {
+                if (browserExecution.getAuthenticator().equals("auth-otp-form")) {
+                    execution.setRequirement(browserExecution.getRequirement());
+                }
+            }
+        }
+
         execution.setAuthenticator("auth-otp-form");
         execution.setPriority(20);
         execution.setAuthenticatorFlow(false);
diff --git a/model/api/src/main/java/org/keycloak/models/utils/KeycloakModelUtils.java b/model/api/src/main/java/org/keycloak/models/utils/KeycloakModelUtils.java
index c2fd73e..ad5997a 100755
--- a/model/api/src/main/java/org/keycloak/models/utils/KeycloakModelUtils.java
+++ b/model/api/src/main/java/org/keycloak/models/utils/KeycloakModelUtils.java
@@ -1,6 +1,8 @@
 package org.keycloak.models.utils;
 
 import org.bouncycastle.openssl.PEMWriter;
+import org.keycloak.models.AuthenticationExecutionModel;
+import org.keycloak.models.AuthenticationFlowModel;
 import org.keycloak.models.ClientModel;
 import org.keycloak.models.Constants;
 import org.keycloak.models.GroupModel;
@@ -16,6 +18,7 @@ import org.keycloak.models.UserCredentialModel;
 import org.keycloak.models.UserFederationMapperModel;
 import org.keycloak.models.UserFederationProviderModel;
 import org.keycloak.models.UserModel;
+import org.keycloak.representations.idm.AuthenticationExecutionRepresentation;
 import org.keycloak.representations.idm.CertificateRepresentation;
 import org.keycloak.common.util.CertificateUtils;
 import org.keycloak.common.util.PemUtils;
@@ -31,6 +34,7 @@ import java.security.PrivateKey;
 import java.security.PublicKey;
 import java.security.cert.X509Certificate;
 import java.util.HashMap;
+import java.util.LinkedList;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
@@ -386,4 +390,24 @@ public final class KeycloakModelUtils {
             realm.addDefaultRole(Constants.OFFLINE_ACCESS_ROLE);
         }
     }
+
+
+    /**
+     * Recursively find all AuthenticationExecutionModel from specified flow or all it's subflows
+     *
+     * @param realm
+     * @param flow
+     * @param result input should be empty list. At the end will be all executions added to this list
+     */
+    public static void deepFindAuthenticationExecutions(RealmModel realm, AuthenticationFlowModel flow, List<AuthenticationExecutionModel> result) {
+        List<AuthenticationExecutionModel> executions = realm.getAuthenticationExecutions(flow.getId());
+        for (AuthenticationExecutionModel execution : executions) {
+            if (execution.isAuthenticatorFlow()) {
+                AuthenticationFlowModel subFlow = realm.getAuthenticationFlowById(execution.getFlowId());
+                deepFindAuthenticationExecutions(realm, subFlow, result);
+            } else {
+                result.add(execution);
+            }
+        }
+    }
 }