package com.airlock.iam.flow.shared.application.configuration.airlock2fa;

import com.airlock.iam.factor.application.configuration.airlock2fa.Airlock2FASettings;
import com.airlock.iam.flow.api.application.configuration.dynamicactivation.DynamicStepActivationConfig;
import com.airlock.iam.flow.api.application.configuration.selection.FlowConditionConfig;
import com.airlock.iam.flow.api.application.configuration.step.AbstractInteractiveFlowStepConfig;
import com.airlock.iam.flow.api.application.configuration.step.InteractiveGotoTargetConfig;
import com.airlock.iam.flow.api.application.configuration.step.StepIdConfig;
import com.airlock.iam.flow.api.application.configuration.tag.TagConfig;
import com.airlock.iam.flow.api.application.configuration.valueprovider.ValueMapProviderConfig;
import com.airlock.iam.flow.shared.domain.model.airlock2fa.Airlock2FAActivationFlowBindingStep;
import com.airlock.iam.flow.shared.domain.model.airlock2fa.Airlock2FAActivationStatusStep;
import com.airlock.iam.flow.types.api.application.configuration.step.StepConfigForAuthenticationFlows;
import com.airlock.iam.flow.types.api.application.configuration.step.StepConfigForSelfServiceFlows;
import com.airlock.iam.plugin.api.annotation.config.Groups;
import com.airlock.iam.plugin.api.annotation.config.NextActions;
import com.airlock.iam.plugin.api.annotation.config.Plugin;
import com.airlock.iam.plugin.api.annotation.license.PluginLicenseRestriction;
import com.airlock.iam.plugin.api.annotation.property.GroupProperty;
import com.airlock.iam.plugin.api.annotation.property.PluginProperty;
import com.airlock.iam.plugin.api.annotation.property.StringProperty;
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import jakarta.annotation.Nullable;
import java.util.List;
import java.util.Map;

@Plugin(
   description = "<p>This step can be used to recover previously enrolled Airlock 2FA accounts on a fresh installation of a mobile app. This refers to mobile apps which integrate the Futurae SDK and can therefore be enrolled as a device for an Airlock 2FA account. An enrollment of an Airlock 2FA account on a mobile app is subsequently referred to as a (virtual) device. One physical device can host multiple virtual devices. If 'Trusted Session Binding for Recovery' is enabled in the 'Airlock 2FA Settings', this step is necessary for users to recover their devices.</p><p>Airlock IAM does not provide a UI for this step, since it exposes a REST API which is intended to be used by custom mobile apps.</p><p>The following describes the recovery use case which is enabled by this step:</p><ul><li>A fresh installation of a mobile app extracts the device identifiers of a previous installation from a backup.</li><li>A user authenticates with Airlock IAM (via the mobile app).</li><li>This is where the 'Airlock 2FA Recovery Trusted Session Binding Step' has to be active in the IAM flow. The step can be completed successfully with the following actions:<ul><li>The mobile app sends the device identifiers Airlock IAM.</li><li>Airlock IAM checks whether one of the devices to be recovered belongs to the authenticated user and aborts the flow otherwise.</li><li>Airlock IAM requests a Trusted Session Binding token from Futurae and returns it to the mobile app. If Airlock IAM does not receive a flow binding token from Futurae, it will return an empty response and the step will continue as if the retrieval was successful.</li><li>The mobile app forwards the Trusted Session Binding token to the Futurae SDK to complete the recovery.</li><li>The mobile app polls Airlock IAM for the status of the recovery.</li></ul></li><li>After successful recovery, all users who had devices on the previous installation will have new devices and the ones from the previous installation cannot be used anymore.</li></ul>The security guarantee provided by Trusted Session Binding for recovery is the following: The devices on a mobile app can only be recovered by someone who owns one of the devices. One user of a device is able to recover the devices of the other users of the same physical device.",
   displayName = "Airlock 2FA Recovery Trusted Session Binding Step"
)
@PluginLicenseRestriction(moduleAffiliation = "Airlock2FA")
@Groups(openGroups = "Basic Settings", groupSequence = {"Basic Settings", "Tags/Guards", "Flow Control"})
@NextActions(
   {Airlock2FAActivationStatusStep.Airlock2FAActivationStepAction.class, Airlock2FAActivationFlowBindingStep.Airlock2FAActivationFlowBindingNextAction.class}
)
public class Airlock2faRecoveryTrustedSessionBindingStepConfig
   extends AbstractInteractiveFlowStepConfig
   implements StepConfigForAuthenticationFlows,
   StepConfigForSelfServiceFlows {
   @PluginProperty(
      displayName = "Airlock 2FA Settings",
      description = "Settings of Airlock 2FA.",
      optional = false,
      globalPluginReference = "globalAirlock2FASettings"
   )
   @GroupProperty(groupIdentifier = "Basic Settings")
   private Airlock2FASettings airlock2faSettings;
   @GroupProperty(groupIdentifier = "Advanced Settings")
   @StringProperty(
      description = "The identifier of the authentication method for this step. Since the authentication method is also the identifier for failed login attempts for this step, distinct identifiers must be chosen if multiple instances of the same step type are used to check different credentials (e.g., two password step instances check two different passwords).<p>Example: a system tracks two passwords per user. Password 1 is used in flow A, password 2 in flow B. These two steps must have distinct 'Authentication Method Ids', e.g. PASSWORD1 and PASSWORD2.</p>If only one identifier (e.g. \"PASSWORD\") is used, this may allow brute force attacks on the password as follows. Assuming an attacker knows the user's password 1, they get an unlimited number of attempts on flow B, as the 'PASSWORD' counter can repeatedly be reset to 0 by performing a successful login on flow A.<p>To prevent such attacks, use two different counters by setting the authentication methods, for example, to PASSWORD1 and PASSWORD2, respectively.</p>",
      displayName = "Authentication Method ID",
      defaultValue = "AIRLOCK_2FA",
      maxLength = 23,
      optional = true
   )
   private String authenticationMethodId;

   protected Airlock2faRecoveryTrustedSessionBindingStepConfig(
      FlowConditionConfig skipCondition,
      FlowConditionConfig preCondition,
      boolean requiresActivation,
      List<TagConfig> tagsOnSuccess,
      @Nullable StepIdConfig stepId,
      List<InteractiveGotoTargetConfig> interactiveGotoTargets,
      List<DynamicStepActivationConfig> dynamicStepActivations,
      Map<String, StepIdConfig> onFailureGotos,
      List<ValueMapProviderConfig> customResponseAttributes,
      List<ValueMapProviderConfig> customFailureResponseAttributes,
      Airlock2FASettings airlock2faSettings,
      String authenticationMethodId
   ) {
      super(
         skipCondition,
         preCondition,
         requiresActivation,
         tagsOnSuccess,
         stepId,
         interactiveGotoTargets,
         dynamicStepActivations,
         onFailureGotos,
         customResponseAttributes,
         customFailureResponseAttributes
      );
      this.airlock2faSettings = airlock2faSettings;
      this.authenticationMethodId = authenticationMethodId;
   }

   public Airlock2FASettings getAirlock2faSettings() {
      return this.airlock2faSettings;
   }

   public String getAuthenticationMethodId() {
      return this.authenticationMethodId;
   }

   @SuppressFBWarnings(justification = "generated code")
   public static Airlock2faRecoveryTrustedSessionBindingStepConfig.Airlock2faRecoveryTrustedSessionBindingStepConfigBuilder builder() {
      return new Airlock2faRecoveryTrustedSessionBindingStepConfig.Airlock2faRecoveryTrustedSessionBindingStepConfigBuilder();
   }

   @SuppressFBWarnings(justification = "generated code")
   public Airlock2faRecoveryTrustedSessionBindingStepConfig() {
   }

   @SuppressFBWarnings(justification = "generated code")
   public static class Airlock2faRecoveryTrustedSessionBindingStepConfigBuilder {
      @SuppressFBWarnings(justification = "generated code")
      private FlowConditionConfig skipCondition;
      @SuppressFBWarnings(justification = "generated code")
      private FlowConditionConfig preCondition;
      @SuppressFBWarnings(justification = "generated code")
      private boolean requiresActivation;
      @SuppressFBWarnings(justification = "generated code")
      private List<TagConfig> tagsOnSuccess;
      @SuppressFBWarnings(justification = "generated code")
      private StepIdConfig stepId;
      @SuppressFBWarnings(justification = "generated code")
      private List<InteractiveGotoTargetConfig> interactiveGotoTargets;
      @SuppressFBWarnings(justification = "generated code")
      private List<DynamicStepActivationConfig> dynamicStepActivations;
      @SuppressFBWarnings(justification = "generated code")
      private Map<String, StepIdConfig> onFailureGotos;
      @SuppressFBWarnings(justification = "generated code")
      private List<ValueMapProviderConfig> customResponseAttributes;
      @SuppressFBWarnings(justification = "generated code")
      private List<ValueMapProviderConfig> customFailureResponseAttributes;
      @SuppressFBWarnings(justification = "generated code")
      private Airlock2FASettings airlock2faSettings;
      @SuppressFBWarnings(justification = "generated code")
      private String authenticationMethodId;

      @SuppressFBWarnings(justification = "generated code")
      Airlock2faRecoveryTrustedSessionBindingStepConfigBuilder() {
      }

      @SuppressFBWarnings(justification = "generated code")
      public Airlock2faRecoveryTrustedSessionBindingStepConfig.Airlock2faRecoveryTrustedSessionBindingStepConfigBuilder skipCondition(
         FlowConditionConfig skipCondition
      ) {
         this.skipCondition = skipCondition;
         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public Airlock2faRecoveryTrustedSessionBindingStepConfig.Airlock2faRecoveryTrustedSessionBindingStepConfigBuilder preCondition(
         FlowConditionConfig preCondition
      ) {
         this.preCondition = preCondition;
         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public Airlock2faRecoveryTrustedSessionBindingStepConfig.Airlock2faRecoveryTrustedSessionBindingStepConfigBuilder requiresActivation(
         boolean requiresActivation
      ) {
         this.requiresActivation = requiresActivation;
         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public Airlock2faRecoveryTrustedSessionBindingStepConfig.Airlock2faRecoveryTrustedSessionBindingStepConfigBuilder tagsOnSuccess(
         List<TagConfig> tagsOnSuccess
      ) {
         this.tagsOnSuccess = tagsOnSuccess;
         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public Airlock2faRecoveryTrustedSessionBindingStepConfig.Airlock2faRecoveryTrustedSessionBindingStepConfigBuilder stepId(@Nullable StepIdConfig stepId) {
         this.stepId = stepId;
         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public Airlock2faRecoveryTrustedSessionBindingStepConfig.Airlock2faRecoveryTrustedSessionBindingStepConfigBuilder interactiveGotoTargets(
         List<InteractiveGotoTargetConfig> interactiveGotoTargets
      ) {
         this.interactiveGotoTargets = interactiveGotoTargets;
         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public Airlock2faRecoveryTrustedSessionBindingStepConfig.Airlock2faRecoveryTrustedSessionBindingStepConfigBuilder dynamicStepActivations(
         List<DynamicStepActivationConfig> dynamicStepActivations
      ) {
         this.dynamicStepActivations = dynamicStepActivations;
         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public Airlock2faRecoveryTrustedSessionBindingStepConfig.Airlock2faRecoveryTrustedSessionBindingStepConfigBuilder onFailureGotos(
         Map<String, StepIdConfig> onFailureGotos
      ) {
         this.onFailureGotos = onFailureGotos;
         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public Airlock2faRecoveryTrustedSessionBindingStepConfig.Airlock2faRecoveryTrustedSessionBindingStepConfigBuilder customResponseAttributes(
         List<ValueMapProviderConfig> customResponseAttributes
      ) {
         this.customResponseAttributes = customResponseAttributes;
         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public Airlock2faRecoveryTrustedSessionBindingStepConfig.Airlock2faRecoveryTrustedSessionBindingStepConfigBuilder customFailureResponseAttributes(
         List<ValueMapProviderConfig> customFailureResponseAttributes
      ) {
         this.customFailureResponseAttributes = customFailureResponseAttributes;
         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public Airlock2faRecoveryTrustedSessionBindingStepConfig.Airlock2faRecoveryTrustedSessionBindingStepConfigBuilder airlock2faSettings(
         Airlock2FASettings airlock2faSettings
      ) {
         this.airlock2faSettings = airlock2faSettings;
         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public Airlock2faRecoveryTrustedSessionBindingStepConfig.Airlock2faRecoveryTrustedSessionBindingStepConfigBuilder authenticationMethodId(
         String authenticationMethodId
      ) {
         this.authenticationMethodId = authenticationMethodId;
         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public Airlock2faRecoveryTrustedSessionBindingStepConfig build() {
         return new Airlock2faRecoveryTrustedSessionBindingStepConfig(
            this.skipCondition,
            this.preCondition,
            this.requiresActivation,
            this.tagsOnSuccess,
            this.stepId,
            this.interactiveGotoTargets,
            this.dynamicStepActivations,
            this.onFailureGotos,
            this.customResponseAttributes,
            this.customFailureResponseAttributes,
            this.airlock2faSettings,
            this.authenticationMethodId
         );
      }

      @SuppressFBWarnings(justification = "generated code")
      @Override
      public String toString() {
         return "Airlock2faRecoveryTrustedSessionBindingStepConfig.Airlock2faRecoveryTrustedSessionBindingStepConfigBuilder(skipCondition="
            + this.skipCondition
            + ", preCondition="
            + this.preCondition
            + ", requiresActivation="
            + this.requiresActivation
            + ", tagsOnSuccess="
            + this.tagsOnSuccess
            + ", stepId="
            + this.stepId
            + ", interactiveGotoTargets="
            + this.interactiveGotoTargets
            + ", dynamicStepActivations="
            + this.dynamicStepActivations
            + ", onFailureGotos="
            + this.onFailureGotos
            + ", customResponseAttributes="
            + this.customResponseAttributes
            + ", customFailureResponseAttributes="
            + this.customFailureResponseAttributes
            + ", airlock2faSettings="
            + this.airlock2faSettings
            + ", authenticationMethodId="
            + this.authenticationMethodId
            + ")";
      }
   }
}
