package com.airlock.iam.login.application.configuration.gateway;

import com.airlock.iam.common.application.configuration.gateway.GatewayLoginappConfig;
import com.airlock.iam.common.domain.model.gateway.WafSettings;
import com.airlock.iam.core.application.configuration.waf.AirlockGatewayConfig;
import com.airlock.iam.flow.shared.application.configuration.RemovedRolesMapping;
import com.airlock.iam.plugin.api.annotation.config.Groups;
import com.airlock.iam.plugin.api.annotation.config.Plugin;
import com.airlock.iam.plugin.api.annotation.lifecycle.AfterConfigActivation;
import com.airlock.iam.plugin.api.annotation.property.BooleanProperty;
import com.airlock.iam.plugin.api.annotation.property.EnumProperty;
import com.airlock.iam.plugin.api.annotation.property.GroupProperty;
import com.airlock.iam.plugin.api.annotation.property.IntegerProperty;
import com.airlock.iam.plugin.api.annotation.property.PluginListProperty;
import com.airlock.iam.plugin.api.annotation.property.StringProperty;
import com.airlock.iam.plugin.framework.application.service.builder.plugin.PluginPropertyBuilderInitializer;
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import java.util.List;
import java.util.Optional;

@Plugin(
   description = "Gateway settings for the Loginapp. These settings are essential to ensure correct and secure behavior if Airlock IAM is deployed behind an Airlock Gateway.",
   displayName = "Airlock Gateway Settings (Loginapp)"
)
@Groups(groupSequence = {"Basic Settings", "Advanced Settings"}, openGroups = "Basic Settings")
public final class LoginappGatewayConfig implements GatewayLoginappConfig {
   @PluginListProperty(
      description = "<p>Airlock Gateway can indicate (using an environment cookie) that roles have been dropped. Dropped roles can be mapped to tags in Airlock IAM that should be dropped as a consequence.</p>",
      optional = true
   )
   @GroupProperty(groupIdentifier = "Basic Settings")
   private List<RemovedRolesMapping> removedRolesMappings;
   @IntegerProperty(
      displayName = "Client Fingerprinting Lockout Threshold",
      description = "<p>\n\tIf the Airlock Gateway terminates a session because of a high client fingerprinting (CFP) score, IAM is\n\tinformed about this as part of the Airlock Gateway logout propagation.<br>\n\tThis property defines a CFP score threshold: If the CFP score reported by the Airlock Gateway is above\n\tor equal to the threshold, the user account is locked in IAM.\n\tThis way not only the current Airlock Gateway session is terminated but also the user account is locked\n\tfor further login attempts. The user can't unlock his account by using the \"Unlock Self-Service\".\n</p>\n<p>\n\tNote: Ensure that the logout propagation path in the corresponding Airlock Gateway mapping for IAM\n\tpoints to the corresponding REST endpoint.\n</p>\n<p>\n\tPlease refer to the Airlock Gateway manual for further information about client fingerprinting.\n</p>",
      exampleValues = {0, 500, 800},
      minValue = 0,
      optional = true
   )
   @GroupProperty(groupIdentifier = "Basic Settings")
   private Integer clientFingerprintingLockoutThreshold;
   @BooleanProperty(
      description = "Usually, existing roles should be kept, i.e., the roles granted to a user in Airlock IAM should be\n<i>added</i> to the existing set of roles of an Airlock Gateway session. This is achieved by using the\nAirlock Control Cookie command <code>ADD_CREDENTIALS</code>. If every identity propagation shall\n<i>replace</i> all previously set roles, disable this property, which results in the Airlock Control Cookie\ncommand <code>SET_CREDENTIALS</code>.",
      optional = true,
      defaultValue = "true"
   )
   @GroupProperty(groupIdentifier = "Basic Settings")
   private boolean addCredentialsToSession;
   @StringProperty(
      description = "<p>The name of the control cookie used to communicate with the Airlock Gateway (WAF) backend control API.\nThis must be the same as configured in Airlock.</p>\n<p>A control cookie is set after successful authentication with the roles granted to the user as\ncredentials/roles. Additionally, a new session ID is generated (to prevent session fixation attacks) and the\nglobal session ID is set as audit token.</p>\n<p>This property also enables so-called \"session tickets\". After successful authentication the user's name\nand the granted roles are stored in the current session plus a session ticket cookie including this\ninformation is stored in the Airlock Gateway cookie store. The session ticket is needed to re-authenticate\nany new session later.</p>",
      optional = true,
      defaultValue = "AL_CONTROL"
   )
   @GroupProperty(groupIdentifier = "Advanced Settings")
   private String controlCookieName;
   @StringProperty(
      description = "The name of the prefix that Airlock Gateway (WAF) prepends to all environment cookies it sends to its\nbackends. This must be the same as configured in Airlock Gateway. It is used to extract, for example, the\nclient IP address or the client certificate.",
      optional = true,
      defaultValue = "AL_ENV_"
   )
   @GroupProperty(groupIdentifier = "Advanced Settings")
   private String environmentCookiePrefix;
   @EnumProperty(
      description = "<p>Type of the audit token set in the Airlock Gateway (WAF) after the authentication.</p>\n<ul>\n<li>\"Username\": The audit token contains just the username.</li>\n<li>\"SessionID\": The audit token contains just the session id.</li>\n<li>\"Username and SessionID\": The audit token contains the username followed by a \"-\" and the session ID.</li>\n<li>\"None\": The audit token is empty.</li></ul>",
      defaultValue = "USERNAME",
      optional = true
   )
   @GroupProperty(groupIdentifier = "Advanced Settings")
   private AirlockGatewayConfig.AuditTokenType auditToken;
   private AirlockGatewayConfig initializedAirlockGatewayConfig;

   public LoginappGatewayConfig(
      List<RemovedRolesMapping> removedRolesMappings,
      Integer clientFingerprintingLockoutThreshold,
      boolean addCredentialsToSession,
      String controlCookieName,
      String environmentCookiePrefix,
      AirlockGatewayConfig.AuditTokenType auditToken
   ) {
      this.removedRolesMappings = removedRolesMappings;
      this.clientFingerprintingLockoutThreshold = clientFingerprintingLockoutThreshold;
      this.addCredentialsToSession = addCredentialsToSession;
      this.controlCookieName = controlCookieName;
      this.environmentCookiePrefix = environmentCookiePrefix;
      this.auditToken = auditToken;
   }

   public WafSettings getSettings() {
      return LoginappGatewaySettingsFactory.wafSettings(this);
   }

   @AfterConfigActivation
   void initializeAirlockGatewaySettings() {
      this.initializedAirlockGatewayConfig = ((AirlockGatewayConfig.AirlockGatewayConfigBuilder)PluginPropertyBuilderInitializer.builder(
            AirlockGatewayConfig.AirlockGatewayConfigBuilder.class
         ))
         .auditToken(this.auditToken)
         .environmentCookiePrefix(this.environmentCookiePrefix)
         .addCredentialsToSession(this.addCredentialsToSession)
         .controlCookieName(this.controlCookieName)
         .build();
   }

   public AirlockGatewayConfig getAirlockGatewaySettings() {
      return this.initializedAirlockGatewayConfig;
   }

   public Optional<Integer> getClientFingerprintingLockoutThreshold() {
      return Optional.ofNullable(this.clientFingerprintingLockoutThreshold);
   }

   public List<RemovedRolesMapping> getRemovedRolesMappings() {
      return this.removedRolesMappings;
   }

   public boolean isAddCredentialsToSession() {
      return this.addCredentialsToSession;
   }

   public String getControlCookieName() {
      return this.controlCookieName;
   }

   public String getEnvironmentCookiePrefix() {
      return this.environmentCookiePrefix;
   }

   public AirlockGatewayConfig.AuditTokenType getAuditToken() {
      return this.auditToken;
   }

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

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

   @SuppressFBWarnings(justification = "generated code")
   public static class LoginappGatewayConfigBuilder {
      @SuppressFBWarnings(justification = "generated code")
      private List<RemovedRolesMapping> removedRolesMappings;
      @SuppressFBWarnings(justification = "generated code")
      private Integer clientFingerprintingLockoutThreshold;
      @SuppressFBWarnings(justification = "generated code")
      private boolean addCredentialsToSession;
      @SuppressFBWarnings(justification = "generated code")
      private String controlCookieName;
      @SuppressFBWarnings(justification = "generated code")
      private String environmentCookiePrefix;
      @SuppressFBWarnings(justification = "generated code")
      private AirlockGatewayConfig.AuditTokenType auditToken;

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

      @SuppressFBWarnings(justification = "generated code")
      public LoginappGatewayConfig.LoginappGatewayConfigBuilder removedRolesMappings(List<RemovedRolesMapping> removedRolesMappings) {
         this.removedRolesMappings = removedRolesMappings;
         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public LoginappGatewayConfig.LoginappGatewayConfigBuilder clientFingerprintingLockoutThreshold(Integer clientFingerprintingLockoutThreshold) {
         this.clientFingerprintingLockoutThreshold = clientFingerprintingLockoutThreshold;
         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public LoginappGatewayConfig.LoginappGatewayConfigBuilder addCredentialsToSession(boolean addCredentialsToSession) {
         this.addCredentialsToSession = addCredentialsToSession;
         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public LoginappGatewayConfig.LoginappGatewayConfigBuilder controlCookieName(String controlCookieName) {
         this.controlCookieName = controlCookieName;
         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public LoginappGatewayConfig.LoginappGatewayConfigBuilder environmentCookiePrefix(String environmentCookiePrefix) {
         this.environmentCookiePrefix = environmentCookiePrefix;
         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public LoginappGatewayConfig.LoginappGatewayConfigBuilder auditToken(AirlockGatewayConfig.AuditTokenType auditToken) {
         this.auditToken = auditToken;
         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public LoginappGatewayConfig build() {
         return new LoginappGatewayConfig(
            this.removedRolesMappings,
            this.clientFingerprintingLockoutThreshold,
            this.addCredentialsToSession,
            this.controlCookieName,
            this.environmentCookiePrefix,
            this.auditToken
         );
      }

      @SuppressFBWarnings(justification = "generated code")
      @Override
      public String toString() {
         return "LoginappGatewayConfig.LoginappGatewayConfigBuilder(removedRolesMappings="
            + this.removedRolesMappings
            + ", clientFingerprintingLockoutThreshold="
            + this.clientFingerprintingLockoutThreshold
            + ", addCredentialsToSession="
            + this.addCredentialsToSession
            + ", controlCookieName="
            + this.controlCookieName
            + ", environmentCookiePrefix="
            + this.environmentCookiePrefix
            + ", auditToken="
            + this.auditToken
            + ")";
      }
   }
}
