package com.airlock.iam.login.app.misc.configuration.oneshot;

import com.airlock.iam.common.infrastructure.credential.CredentialExtractor;
import com.airlock.iam.core.application.configuration.waf.AirlockGatewayConfig;
import com.airlock.iam.core.misc.authen.Authenticator;
import com.airlock.iam.core.misc.authen.UsernameTransformer;
import com.airlock.iam.core.misc.sso.IdentityPropagator;
import com.airlock.iam.login.app.misc.configuration.oneshot.impl.TargetApplicationBasedAirlockAuthorizator;
import com.airlock.iam.login.app.misc.configuration.oneshot.impl.TargetApplicationBasedAirlockMetaInformation;
import com.airlock.iam.login.app.misc.configuration.oneshot.impl.TargetApplicationBasedIdentityPropagator;
import com.airlock.iam.login.app.misc.configuration.targetapps.GenericTargetApplicationBase;
import com.airlock.iam.login.app.misc.configuration.targetapps.RoleTransformationRule;
import com.airlock.iam.login.app.misc.oneshot.AirlockOneShotAuthenticationWorkflow;
import com.airlock.iam.login.app.misc.oneshot.ErrorMapper;
import com.airlock.iam.login.app.misc.oneshot.impl.AirlockOneShotAuthenticationWorkflowImpl;
import com.airlock.iam.login.app.misc.session.UserSpecificRoleTimeoutCalculator;
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.property.BooleanProperty;
import com.airlock.iam.plugin.api.annotation.property.GroupProperty;
import com.airlock.iam.plugin.api.annotation.property.PluginProperty;
import com.airlock.iam.plugin.api.config.ConfigurationContext;
import com.airlock.iam.security.util.crypto.StringCipher;
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import java.util.List;
import java.util.regex.Pattern;

@Plugin(
   description = "<p>Defines how to authenticate HTTP requests based on the original HTTP request sent by the client (provided to IAM in various Airlock Gateway environment cookies).</p><p>The following actions are applied for each request:<ol> <li>Credential Extraction: Extract credential from the HTTP request (e.g. a bearer token or a cookie).</li> <li>Authentication: Call specified authenticator with credential (e.g. verify JWT ticket).</li> <li>Error handling: If authentication failes, the specified error mapper defines how to respond (e.g. send 401 to client).</li> <li>Authorization: Assure roles required to access application/services are given after authentication.</li> <li>ID-Propagation: provide information about authenticated user to target application/service.</li> <li>Set Airlock Gateway (WAF) roles: provide credentials/roles to Airlock Gateway to allow request to pass to target application/service.</li></ol></p>",
   displayName = "Authenticator-based One-Shot Target Application",
   typeShortName = "AuthenticatorBasedOneShotTargetApplication"
)
@Groups(
   groupSequence = {"Basic Settings", "URL Settings", "Portal Settings", "Federation Settings", "Roles Processing", "Identity Propagation and Authorization"},
   openGroups = {"Basic Settings", "URL Settings"}
)
public class OneShotTargetApplicationConfig extends GenericTargetApplicationBase implements OneShotTargetApplication {
   @PluginProperty(
      description = "Extracts credential (e.g. basic auth, bearer token, cookie) from the request received from the Airlock Gateway (one-shot flow). The credential serves as input for the authenticator.",
      optional = false,
      displayName = "Credential Extractor"
   )
   @GroupProperty(groupIdentifier = "Basic Settings")
   private OneShotTargetApplicationConfig.CredentialExtractorFactory credentialExtractorFactory;
   @PluginProperty(
      description = "<p>Validates the credential from the credential extractor in order to authenticate the HTTP request.</p><p><b>Note</b>:<ul> <li>Only non-interactive authentication steps (there is no way to interact with the HTTP client at this point - use the REST authentication API to do so if desired) may be configured.</li> <li>The authenticator must know how to handle the credential type provided by the credential extractor.</li></ul></p><p>A common use-case is to verify JWT tokens issued by an upfront authentication process (as \"Authorization Bearer\" header): <ul> <li>Extract bearer token from HTTP header (using the \"HTTP Header Token Extractor (as SSO Credential)\").</li> <li>Authenticate the request using the \"Lookup and Accept Authenticator\" or the \"SSO Credential Authenticator\".</li></ul></p>",
      optional = false
   )
   @GroupProperty(groupIdentifier = "Basic Settings")
   private Authenticator authenticator;
   @PluginProperty(
      description = "Defines how to respond if authentication fails (for wrong credentials and other errors).",
      defaultValue = ConfigurableErrorMapperFactory.class,
      optional = true
   )
   @GroupProperty(groupIdentifier = "Basic Settings")
   private OneShotTargetApplicationConfig.ErrorMapperFactory failureResponses;
   @PluginProperty(description = "Defines how to transport information about the authenticated user to the target application/service.", optional = false)
   @GroupProperty(groupIdentifier = "Basic Settings")
   private IdentityPropagator identityPropagator;
   @BooleanProperty(
      description = "<p>If enabled, a message is logged to the user trail log for every successful or unsuccessful authentication.</p><p><b>Caution:</b> If the Airlock Gateway (WAF) is used in stateless mode, every single request may have to be authenticated by IAM due to missing roles. Hence, every request may generate a message in the user trail log depending on the Airlock Gateway configuration.</p>",
      defaultValue = "true",
      optional = true
   )
   @GroupProperty(groupIdentifier = "Basic Settings")
   private boolean enableUserTrailLog;

   @Override
   public AirlockOneShotAuthenticationWorkflow createWorkflow(
      AirlockGatewayConfig wafConfig, ConfigurationContext context, UserSpecificRoleTimeoutCalculator userSpecificRoleTimeouts
   ) {
      return new AirlockOneShotAuthenticationWorkflowImpl(
         this.failureResponses.newErrorMapper(wafConfig),
         this.credentialExtractorFactory.newExtractor(wafConfig),
         this.authenticator,
         new TargetApplicationBasedAirlockAuthorizator(this),
         new TargetApplicationBasedIdentityPropagator(this, this.identityPropagator),
         new TargetApplicationBasedAirlockMetaInformation(this, wafConfig),
         userSpecificRoleTimeouts
      );
   }

   public boolean isEnableUserTrailLog() {
      return this.enableUserTrailLog;
   }

   OneShotTargetApplicationConfig(
      Pattern urlPattern,
      String useDifferentUsername,
      List<UsernameTransformer> usernameTransformation,
      String useDifferentPassword,
      StringCipher passwordEncryptionMethod,
      List<String> requiredRoles,
      List<String> airlockGatewayRoles,
      List<RoleTransformationRule> roleTransformationRules,
      List<Pattern> propagatedRolesToDelete,
      List<Pattern> propagatedRolesToKeep,
      List<RoleTransformationRule> propagatedRolesTransformationRules,
      List<String> propagatedRolesToAdd,
      OneShotTargetApplicationConfig.CredentialExtractorFactory credentialExtractorFactory,
      Authenticator authenticator,
      OneShotTargetApplicationConfig.ErrorMapperFactory failureResponses,
      IdentityPropagator identityPropagator,
      boolean enableUserTrailLog
   ) {
      super(
         urlPattern,
         useDifferentUsername,
         usernameTransformation,
         useDifferentPassword,
         passwordEncryptionMethod,
         requiredRoles,
         airlockGatewayRoles,
         roleTransformationRules,
         propagatedRolesToDelete,
         propagatedRolesToKeep,
         propagatedRolesTransformationRules,
         propagatedRolesToAdd
      );
      this.credentialExtractorFactory = credentialExtractorFactory;
      this.authenticator = authenticator;
      this.failureResponses = failureResponses;
      this.identityPropagator = identityPropagator;
      this.enableUserTrailLog = enableUserTrailLog;
   }

   @SuppressFBWarnings(justification = "generated code")
   public static OneShotTargetApplicationConfig.OneShotTargetApplicationConfigBuilder oneShotTargetApplicationBuilder() {
      return new OneShotTargetApplicationConfig.OneShotTargetApplicationConfigBuilder();
   }

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

   public interface CredentialExtractorFactory {
      CredentialExtractor newExtractor(AirlockGatewayConfig var1);
   }

   public interface ErrorMapperFactory {
      ErrorMapper newErrorMapper(AirlockGatewayConfig var1);
   }

   @SuppressFBWarnings(justification = "generated code")
   public static class OneShotTargetApplicationConfigBuilder {
      @SuppressFBWarnings(justification = "generated code")
      private Pattern urlPattern;
      @SuppressFBWarnings(justification = "generated code")
      private String useDifferentUsername;
      @SuppressFBWarnings(justification = "generated code")
      private List<UsernameTransformer> usernameTransformation;
      @SuppressFBWarnings(justification = "generated code")
      private String useDifferentPassword;
      @SuppressFBWarnings(justification = "generated code")
      private StringCipher passwordEncryptionMethod;
      @SuppressFBWarnings(justification = "generated code")
      private List<String> requiredRoles;
      @SuppressFBWarnings(justification = "generated code")
      private List<String> airlockGatewayRoles;
      @SuppressFBWarnings(justification = "generated code")
      private List<RoleTransformationRule> roleTransformationRules;
      @SuppressFBWarnings(justification = "generated code")
      private List<Pattern> propagatedRolesToDelete;
      @SuppressFBWarnings(justification = "generated code")
      private List<Pattern> propagatedRolesToKeep;
      @SuppressFBWarnings(justification = "generated code")
      private List<RoleTransformationRule> propagatedRolesTransformationRules;
      @SuppressFBWarnings(justification = "generated code")
      private List<String> propagatedRolesToAdd;
      @SuppressFBWarnings(justification = "generated code")
      private OneShotTargetApplicationConfig.CredentialExtractorFactory credentialExtractorFactory;
      @SuppressFBWarnings(justification = "generated code")
      private Authenticator authenticator;
      @SuppressFBWarnings(justification = "generated code")
      private OneShotTargetApplicationConfig.ErrorMapperFactory failureResponses;
      @SuppressFBWarnings(justification = "generated code")
      private IdentityPropagator identityPropagator;
      @SuppressFBWarnings(justification = "generated code")
      private boolean enableUserTrailLog;

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

      @SuppressFBWarnings(justification = "generated code")
      public OneShotTargetApplicationConfig.OneShotTargetApplicationConfigBuilder urlPattern(Pattern urlPattern) {
         this.urlPattern = urlPattern;
         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public OneShotTargetApplicationConfig.OneShotTargetApplicationConfigBuilder useDifferentUsername(String useDifferentUsername) {
         this.useDifferentUsername = useDifferentUsername;
         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public OneShotTargetApplicationConfig.OneShotTargetApplicationConfigBuilder usernameTransformation(List<UsernameTransformer> usernameTransformation) {
         this.usernameTransformation = usernameTransformation;
         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public OneShotTargetApplicationConfig.OneShotTargetApplicationConfigBuilder useDifferentPassword(String useDifferentPassword) {
         this.useDifferentPassword = useDifferentPassword;
         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public OneShotTargetApplicationConfig.OneShotTargetApplicationConfigBuilder passwordEncryptionMethod(StringCipher passwordEncryptionMethod) {
         this.passwordEncryptionMethod = passwordEncryptionMethod;
         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public OneShotTargetApplicationConfig.OneShotTargetApplicationConfigBuilder requiredRoles(List<String> requiredRoles) {
         this.requiredRoles = requiredRoles;
         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public OneShotTargetApplicationConfig.OneShotTargetApplicationConfigBuilder airlockGatewayRoles(List<String> airlockGatewayRoles) {
         this.airlockGatewayRoles = airlockGatewayRoles;
         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public OneShotTargetApplicationConfig.OneShotTargetApplicationConfigBuilder roleTransformationRules(List<RoleTransformationRule> roleTransformationRules) {
         this.roleTransformationRules = roleTransformationRules;
         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public OneShotTargetApplicationConfig.OneShotTargetApplicationConfigBuilder propagatedRolesToDelete(List<Pattern> propagatedRolesToDelete) {
         this.propagatedRolesToDelete = propagatedRolesToDelete;
         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public OneShotTargetApplicationConfig.OneShotTargetApplicationConfigBuilder propagatedRolesToKeep(List<Pattern> propagatedRolesToKeep) {
         this.propagatedRolesToKeep = propagatedRolesToKeep;
         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public OneShotTargetApplicationConfig.OneShotTargetApplicationConfigBuilder propagatedRolesTransformationRules(
         List<RoleTransformationRule> propagatedRolesTransformationRules
      ) {
         this.propagatedRolesTransformationRules = propagatedRolesTransformationRules;
         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public OneShotTargetApplicationConfig.OneShotTargetApplicationConfigBuilder propagatedRolesToAdd(List<String> propagatedRolesToAdd) {
         this.propagatedRolesToAdd = propagatedRolesToAdd;
         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public OneShotTargetApplicationConfig.OneShotTargetApplicationConfigBuilder credentialExtractorFactory(
         OneShotTargetApplicationConfig.CredentialExtractorFactory credentialExtractorFactory
      ) {
         this.credentialExtractorFactory = credentialExtractorFactory;
         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public OneShotTargetApplicationConfig.OneShotTargetApplicationConfigBuilder authenticator(Authenticator authenticator) {
         this.authenticator = authenticator;
         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public OneShotTargetApplicationConfig.OneShotTargetApplicationConfigBuilder failureResponses(
         OneShotTargetApplicationConfig.ErrorMapperFactory failureResponses
      ) {
         this.failureResponses = failureResponses;
         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public OneShotTargetApplicationConfig.OneShotTargetApplicationConfigBuilder identityPropagator(IdentityPropagator identityPropagator) {
         this.identityPropagator = identityPropagator;
         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public OneShotTargetApplicationConfig.OneShotTargetApplicationConfigBuilder enableUserTrailLog(boolean enableUserTrailLog) {
         this.enableUserTrailLog = enableUserTrailLog;
         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public OneShotTargetApplicationConfig build() {
         return new OneShotTargetApplicationConfig(
            this.urlPattern,
            this.useDifferentUsername,
            this.usernameTransformation,
            this.useDifferentPassword,
            this.passwordEncryptionMethod,
            this.requiredRoles,
            this.airlockGatewayRoles,
            this.roleTransformationRules,
            this.propagatedRolesToDelete,
            this.propagatedRolesToKeep,
            this.propagatedRolesTransformationRules,
            this.propagatedRolesToAdd,
            this.credentialExtractorFactory,
            this.authenticator,
            this.failureResponses,
            this.identityPropagator,
            this.enableUserTrailLog
         );
      }

      @SuppressFBWarnings(justification = "generated code")
      @Override
      public String toString() {
         return "OneShotTargetApplicationConfig.OneShotTargetApplicationConfigBuilder(urlPattern="
            + this.urlPattern
            + ", useDifferentUsername="
            + this.useDifferentUsername
            + ", usernameTransformation="
            + this.usernameTransformation
            + ", useDifferentPassword="
            + this.useDifferentPassword
            + ", passwordEncryptionMethod="
            + this.passwordEncryptionMethod
            + ", requiredRoles="
            + this.requiredRoles
            + ", airlockGatewayRoles="
            + this.airlockGatewayRoles
            + ", roleTransformationRules="
            + this.roleTransformationRules
            + ", propagatedRolesToDelete="
            + this.propagatedRolesToDelete
            + ", propagatedRolesToKeep="
            + this.propagatedRolesToKeep
            + ", propagatedRolesTransformationRules="
            + this.propagatedRolesTransformationRules
            + ", propagatedRolesToAdd="
            + this.propagatedRolesToAdd
            + ", credentialExtractorFactory="
            + this.credentialExtractorFactory
            + ", authenticator="
            + this.authenticator
            + ", failureResponses="
            + this.failureResponses
            + ", identityPropagator="
            + this.identityPropagator
            + ", enableUserTrailLog="
            + this.enableUserTrailLog
            + ")";
      }
   }
}
