package com.airlock.iam.saml2.application.configuration.sp;

import com.airlock.iam.base.misc.log.LogUtil;
import com.airlock.iam.core.misc.authen.UsernameTransformer;
import com.airlock.iam.login.application.configuration.targetapp.ApplicationIdConfig;
import com.airlock.iam.plugin.api.annotation.config.ConfigurationValidator;
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.license.PluginLicenseRestriction;
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.PluginListProperty;
import com.airlock.iam.plugin.api.annotation.property.PluginProperty;
import com.airlock.iam.plugin.api.annotation.property.StringProperty;
import com.airlock.iam.plugin.api.config.ConfigurationContext;
import com.airlock.iam.plugin.api.config.ConfigurationEnvironment;
import com.airlock.iam.plugin.api.config.Environment;
import com.airlock.iam.plugin.api.config.PluginConfiguration;
import com.airlock.iam.plugin.api.exception.ConfigurationException;
import com.airlock.iam.plugin.api.exception.PropertyConstraintViolationException;
import com.airlock.iam.plugin.api.valuereader.PluginListPropertyValueReader;
import com.airlock.iam.plugin.api.valuereader.StringPropertyValueReader;
import com.airlock.iam.plugin.framework.domain.model.property.EnumPropertyTranslator;
import com.airlock.iam.saml2.application.configuration.Saml2IDPEntityConfig;
import com.airlock.iam.saml2.application.configuration.Saml2SPEntityConfig;
import com.airlock.iam.saml2.application.configuration.assertion.attribute.Saml2AttributeNameToTagMappingConfig;
import com.airlock.iam.saml2.domain.model.Saml2Binding;
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Optional;
import java.util.Set;

@Plugin(
   description = "<p>Configures a local SAML Service Provider (SP).</p><p>The SAML SP receives assertions from other SAML Identity Providers (IdPs). The Airlock IAM Loginapp can also act as identity provider, i.e. issue SAML assertions for other SPs. See separate configuration section for further details.</p>",
   displayName = "SAML 2.0 Flow SP"
)
@PluginLicenseRestriction(moduleAffiliation = "SamlSp")
@Groups(
   groupSequence = {"Basic Settings", "SP-Initiated SSO Settings", "IdP-Initiated SSO Settings", "Attribute Settings"},
   openGroups = {"Basic Settings", "SP-Initiated SSO Settings", "IdP-Initiated SSO Settings"}
)
public final class Saml2FlowSpConfig {
   static final String SP_INITIATED_SSO_SETTINGS = "SP-Initiated SSO Settings";
   static final String IDP_INITIATED_SSO_SETTINGS = "IdP-Initiated SSO Settings";
   static final String ATTRIBUTE_SETTINGS = "Attribute Settings";
   @PluginProperty(description = "Configures the Service Provider (SP) entity.", displayName = "SP Entity Settings", optional = false)
   @GroupProperty(groupIdentifier = "Basic Settings")
   private Saml2SPEntityConfig spEntityConfig;
   public static final String SP_ENTITY_CONFIG_PROPERTY = "spEntityConfig";
   @PluginProperty(description = "Configures the remote identity provider (IdP) entity.", optional = false, displayName = "IdP Entity Settings")
   @GroupProperty(groupIdentifier = "Basic Settings")
   private Saml2IDPEntityConfig remoteIdpEntitySettings;
   @PluginListProperty(
      displayName = "Username Transformation",
      description = "Transforms the username provided by the IdP. The transformation precedes the flow's Username Transformation. If one of the transformers configured here interrupts the transformation chain, username transformations in the authentication flow's configuration will be skipped.",
      optional = true
   )
   @GroupProperty(groupIdentifier = "Basic Settings")
   private List<UsernameTransformer> usernameTransformers;
   @EnumProperty(
      description = "<p>The binding to be used for the authentication request in SP-Initiated SSO.</p><p><b>Important:</b> if an explicit binding type is configured, the same binding must also be enabled in the IdP's standard metadata.</p><ul><li><b>Automatic</b>: Automatically selects the first enabled binding in the IdP standard metadata.</li><li><b>HTTP Redirect Binding</b>: The authentication request is carried directly in the URL query string of an HTTP GET request. Since the length of URLs is limited in practice, the HTTP Redirect binding is only suitable for short messages. Longer messages (e.g. signed requests or if large extensions are added) should be transmitted via the POST binding.</li><li><b>HTTP POST Binding</b>: The authentication request is sent by the user's browser as a POST parameter via a self-posting form using JavaScript. This binding is recommended for long authentication requests or when there is no direct communication between the IdP and the SP. It requires that JavaScript is enabled in the user's browser.</li></ul>",
      optional = true,
      defaultValue = "AUTOMATIC"
   )
   @GroupProperty(groupIdentifier = "SP-Initiated SSO Settings")
   private Saml2FlowSpConfig.AuthnRequestBindingConfig authnRequestBinding;
   @StringProperty(
      description = "<p>With this property any custom extensions can be added to the AuthnRequest. This can e.g. be used to request additional attributes from the IdP. The extensions must be given as valid XML string, without the surrounding <code>&lt;Extension&gt;</code> tag.</p>",
      optional = true,
      exampleValues = "&lt;tag&gt;custom-extension&lt;/tag&gt;",
      isMultiLineText = true
   )
   @GroupProperty(groupIdentifier = "SP-Initiated SSO Settings")
   private String customAuthnRequestExtensions;
   @PluginProperty(
      description = "Defines the application ID of the authentication flow to start in case an Authentication Request received by this Service Provider (IdP-initiated SSO) does not have any relay state or the one provided is not valid.<p>If this property is not configured, the \"Default Application\" defined in \"Authentication Flows\" will be used.</p>",
      displayName = "Default Flow Application ID",
      optional = true
   )
   @GroupProperty(groupIdentifier = "IdP-Initiated SSO Settings")
   private ApplicationIdConfig defaultFlowApplicationId;
   public static final String DEFAULT_FLOW_APPLICATION_ID_FIELD = "defaultFlowApplicationId";
   @StringProperty(
      displayName = "Attribute to Import as User ID",
      description = "Set this property to the name of an attribute to use as user ID instead of the <tt>NameID</tt>. The obtained user ID completely replaces the Name ID sent by the IdP. It can then be subject to further \"Username Transformers\" (depending on the selected authentication flow).",
      exampleValues = {"surname", "email"},
      optional = true
   )
   @GroupProperty(groupIdentifier = "Attribute Settings")
   private String attributeToImportAsUserId;
   private static final String ATTRIBUTE_TO_IMPORT_AS_USER_ID_FIELD = "attributeToImportAsUserId";
   @StringProperty(
      displayName = "Attribute to Import as Language",
      description = "Set this property to the name of an assertion attribute to use as display language.<p>Valid language values from the IdP should conform to the format specified by ISO 639-1 (two characters).</p>",
      exampleValues = "language",
      optional = true
   )
   @GroupProperty(groupIdentifier = "Attribute Settings")
   private String attributeToImportAsLanguage;
   @StringProperty(
      displayName = "Attribute to Import as Audit Token",
      description = "Set this property to the name of an assertion attribute to use as audit token. This allows the SAML SP (service provider) to use the same Audit Token as the SAML IdP (identity provider) which makes it easy to correlate SP and IdP sessions.<p>Note that the audit token cannot be overwritten. This means if it was previously already set (e.g. upon successful completion of a previous authentication flow), the value imported here will be ignored.</p>",
      exampleValues = "auditTokenAttr",
      optional = true
   )
   @GroupProperty(groupIdentifier = "Attribute Settings")
   private String attributeToImportAsAuditToken;
   private static final String ATTRIBUTE_TO_IMPORT_AS_AUDIT_TOKEN_FIELD = "attributeToImportAsAuditToken";
   @StringProperty(
      displayName = "Attribute to Import as Auth Token ID",
      description = "Set this property to the name of an assertion attribute to use as Auth Token ID. ",
      exampleValues = "authTokenAttr",
      optional = true
   )
   @GroupProperty(groupIdentifier = "Attribute Settings")
   private String attributeToImportAsAuthTokenId;
   @PluginListProperty(
      displayName = "Attributes to Import as Tags",
      description = "Import attributes from a SAML 2.0 assertion as flow tags.",
      optional = true
   )
   @GroupProperty(groupIdentifier = "Attribute Settings")
   private List<Saml2AttributeNameToTagMappingConfig> attributeNameToTagMappingConfigs;
   @PluginListProperty(
      displayName = "Attributes to Import in Flow",
      description = "Import additional attributes from a SAML 2.0 assertion into the user's flow session. The imported attributes can for example be used during the identity propagation to be propagated to a back-end.",
      minElements = 1,
      optional = true
   )
   @GroupProperty(groupIdentifier = "Attribute Settings")
   private List<Saml2SpAssertionStringAttributeImportConfig> attributesToImport;
   static final String ATTRIBUTES_TO_IMPORT_PROPERTY = "attributesToImport";

   public Optional<String> getCustomAuthnRequestExtensions() {
      return Optional.ofNullable(this.customAuthnRequestExtensions);
   }

   public Optional<String> getAttributeToImportAsUserId() {
      return Optional.ofNullable(this.attributeToImportAsUserId);
   }

   public Optional<String> getAttributeToImportAsLanguage() {
      return Optional.ofNullable(this.attributeToImportAsLanguage);
   }

   public Optional<String> getAttributeToImportAsAuditToken() {
      return Optional.ofNullable(this.attributeToImportAsAuditToken);
   }

   public Optional<String> getAttributeToImportAsAuthTokenId() {
      return Optional.ofNullable(this.attributeToImportAsAuthTokenId);
   }

   public Optional<ApplicationIdConfig> getDefaultFlowApplicationId() {
      return Optional.ofNullable(this.defaultFlowApplicationId);
   }

   public List<Saml2AttributeNameToTagMappingConfig> getAttributeNameToTagMappingConfigs() {
      return this.attributeNameToTagMappingConfigs;
   }

   public Saml2SPEntityConfig getSpEntityConfig() {
      return this.spEntityConfig;
   }

   public Saml2IDPEntityConfig getRemoteIdpEntitySettings() {
      return this.remoteIdpEntitySettings;
   }

   public List<UsernameTransformer> getUsernameTransformers() {
      return this.usernameTransformers;
   }

   public Saml2FlowSpConfig.AuthnRequestBindingConfig getAuthnRequestBinding() {
      return this.authnRequestBinding;
   }

   public List<Saml2SpAssertionStringAttributeImportConfig> getAttributesToImport() {
      return this.attributesToImport;
   }

   @ConfigurationValidator
   static void ensureThatKeysForImportedAttributesAreDistinct(
      PluginConfiguration config, ConfigurationEnvironment env, ConfigurationContext context, Environment validationEnvironment
   ) throws ConfigurationException {
      Set<String> keysForImportedAttributes = new HashSet<>();

      for (PluginConfiguration attributeToImport : PluginListPropertyValueReader.effectivePluginListPropertyValue(config, "attributesToImport", env, context)) {
         String configuredKey = StringPropertyValueReader.effectiveStringConfigValue(attributeToImport, "key", env, context);
         String effectiveKey = configuredKey != null
            ? configuredKey
            : StringPropertyValueReader.effectiveStringConfigValue(attributeToImport, "attributeName", env, context);
         if (!keysForImportedAttributes.add(effectiveKey)) {
            throw new PropertyConstraintViolationException(
               "attributesToImport",
               config,
               env,
               context,
               "SAML 2.0 attributes to be imported do not have unique keys. Key %s is used multiple times.".formatted(LogUtil.logSafeQuote(effectiveKey))
            );
         }
      }
   }

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

   @SuppressFBWarnings(justification = "generated code")
   public Saml2FlowSpConfig(
      Saml2SPEntityConfig spEntityConfig,
      Saml2IDPEntityConfig remoteIdpEntitySettings,
      List<UsernameTransformer> usernameTransformers,
      Saml2FlowSpConfig.AuthnRequestBindingConfig authnRequestBinding,
      String customAuthnRequestExtensions,
      ApplicationIdConfig defaultFlowApplicationId,
      String attributeToImportAsUserId,
      String attributeToImportAsLanguage,
      String attributeToImportAsAuditToken,
      String attributeToImportAsAuthTokenId,
      List<Saml2AttributeNameToTagMappingConfig> attributeNameToTagMappingConfigs,
      List<Saml2SpAssertionStringAttributeImportConfig> attributesToImport
   ) {
      this.spEntityConfig = spEntityConfig;
      this.remoteIdpEntitySettings = remoteIdpEntitySettings;
      this.usernameTransformers = usernameTransformers;
      this.authnRequestBinding = authnRequestBinding;
      this.customAuthnRequestExtensions = customAuthnRequestExtensions;
      this.defaultFlowApplicationId = defaultFlowApplicationId;
      this.attributeToImportAsUserId = attributeToImportAsUserId;
      this.attributeToImportAsLanguage = attributeToImportAsLanguage;
      this.attributeToImportAsAuditToken = attributeToImportAsAuditToken;
      this.attributeToImportAsAuthTokenId = attributeToImportAsAuthTokenId;
      this.attributeNameToTagMappingConfigs = attributeNameToTagMappingConfigs;
      this.attributesToImport = attributesToImport;
   }

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

   public enum AuthnRequestBindingConfig implements EnumPropertyTranslator {
      HTTP_REDIRECT("HTTP Redirect Binding"),
      HTTP_POST("HTTP POST Binding"),
      AUTOMATIC("Automatic");

      private final String displayName;

      public String getDisplayValue() {
         return this.displayName;
      }

      public Optional<Saml2Binding> binding() {
         return switch (this) {
            case HTTP_REDIRECT -> Optional.of(Saml2Binding.HTTP_REDIRECT_BINDING);
            case HTTP_POST -> Optional.of(Saml2Binding.HTTP_POST_BINDING);
            case AUTOMATIC -> Optional.empty();
         };
      }

      @SuppressFBWarnings(justification = "generated code")
      AuthnRequestBindingConfig(final String displayName) {
         this.displayName = displayName;
      }
   }

   @SuppressFBWarnings(justification = "generated code")
   public static class Saml2FlowSpConfigBuilder {
      @SuppressFBWarnings(justification = "generated code")
      private Saml2SPEntityConfig spEntityConfig;
      @SuppressFBWarnings(justification = "generated code")
      private Saml2IDPEntityConfig remoteIdpEntitySettings;
      @SuppressFBWarnings(justification = "generated code")
      private ArrayList<UsernameTransformer> usernameTransformers;
      @SuppressFBWarnings(justification = "generated code")
      private Saml2FlowSpConfig.AuthnRequestBindingConfig authnRequestBinding;
      @SuppressFBWarnings(justification = "generated code")
      private String customAuthnRequestExtensions;
      @SuppressFBWarnings(justification = "generated code")
      private ApplicationIdConfig defaultFlowApplicationId;
      @SuppressFBWarnings(justification = "generated code")
      private String attributeToImportAsUserId;
      @SuppressFBWarnings(justification = "generated code")
      private String attributeToImportAsLanguage;
      @SuppressFBWarnings(justification = "generated code")
      private String attributeToImportAsAuditToken;
      @SuppressFBWarnings(justification = "generated code")
      private String attributeToImportAsAuthTokenId;
      @SuppressFBWarnings(justification = "generated code")
      private ArrayList<Saml2AttributeNameToTagMappingConfig> attributeNameToTagMappingConfigs;
      @SuppressFBWarnings(justification = "generated code")
      private ArrayList<Saml2SpAssertionStringAttributeImportConfig> attributesToImport;

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

      @SuppressFBWarnings(justification = "generated code")
      public Saml2FlowSpConfig.Saml2FlowSpConfigBuilder spEntityConfig(Saml2SPEntityConfig spEntityConfig) {
         this.spEntityConfig = spEntityConfig;
         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public Saml2FlowSpConfig.Saml2FlowSpConfigBuilder remoteIdpEntitySettings(Saml2IDPEntityConfig remoteIdpEntitySettings) {
         this.remoteIdpEntitySettings = remoteIdpEntitySettings;
         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public Saml2FlowSpConfig.Saml2FlowSpConfigBuilder usernameTransformer(UsernameTransformer usernameTransformer) {
         if (this.usernameTransformers == null) {
            this.usernameTransformers = new ArrayList<>();
         }

         this.usernameTransformers.add(usernameTransformer);
         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public Saml2FlowSpConfig.Saml2FlowSpConfigBuilder usernameTransformers(Collection<? extends UsernameTransformer> usernameTransformers) {
         if (usernameTransformers == null) {
            throw new NullPointerException("usernameTransformers cannot be null");
         }

         if (this.usernameTransformers == null) {
            this.usernameTransformers = new ArrayList<>();
         }

         this.usernameTransformers.addAll(usernameTransformers);
         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public Saml2FlowSpConfig.Saml2FlowSpConfigBuilder clearUsernameTransformers() {
         if (this.usernameTransformers != null) {
            this.usernameTransformers.clear();
         }

         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public Saml2FlowSpConfig.Saml2FlowSpConfigBuilder authnRequestBinding(Saml2FlowSpConfig.AuthnRequestBindingConfig authnRequestBinding) {
         this.authnRequestBinding = authnRequestBinding;
         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public Saml2FlowSpConfig.Saml2FlowSpConfigBuilder customAuthnRequestExtensions(String customAuthnRequestExtensions) {
         this.customAuthnRequestExtensions = customAuthnRequestExtensions;
         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public Saml2FlowSpConfig.Saml2FlowSpConfigBuilder defaultFlowApplicationId(ApplicationIdConfig defaultFlowApplicationId) {
         this.defaultFlowApplicationId = defaultFlowApplicationId;
         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public Saml2FlowSpConfig.Saml2FlowSpConfigBuilder attributeToImportAsUserId(String attributeToImportAsUserId) {
         this.attributeToImportAsUserId = attributeToImportAsUserId;
         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public Saml2FlowSpConfig.Saml2FlowSpConfigBuilder attributeToImportAsLanguage(String attributeToImportAsLanguage) {
         this.attributeToImportAsLanguage = attributeToImportAsLanguage;
         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public Saml2FlowSpConfig.Saml2FlowSpConfigBuilder attributeToImportAsAuditToken(String attributeToImportAsAuditToken) {
         this.attributeToImportAsAuditToken = attributeToImportAsAuditToken;
         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public Saml2FlowSpConfig.Saml2FlowSpConfigBuilder attributeToImportAsAuthTokenId(String attributeToImportAsAuthTokenId) {
         this.attributeToImportAsAuthTokenId = attributeToImportAsAuthTokenId;
         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public Saml2FlowSpConfig.Saml2FlowSpConfigBuilder attributeNameToTagMappingConfig(Saml2AttributeNameToTagMappingConfig attributeNameToTagMappingConfig) {
         if (this.attributeNameToTagMappingConfigs == null) {
            this.attributeNameToTagMappingConfigs = new ArrayList<>();
         }

         this.attributeNameToTagMappingConfigs.add(attributeNameToTagMappingConfig);
         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public Saml2FlowSpConfig.Saml2FlowSpConfigBuilder attributeNameToTagMappingConfigs(
         Collection<? extends Saml2AttributeNameToTagMappingConfig> attributeNameToTagMappingConfigs
      ) {
         if (attributeNameToTagMappingConfigs == null) {
            throw new NullPointerException("attributeNameToTagMappingConfigs cannot be null");
         }

         if (this.attributeNameToTagMappingConfigs == null) {
            this.attributeNameToTagMappingConfigs = new ArrayList<>();
         }

         this.attributeNameToTagMappingConfigs.addAll(attributeNameToTagMappingConfigs);
         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public Saml2FlowSpConfig.Saml2FlowSpConfigBuilder clearAttributeNameToTagMappingConfigs() {
         if (this.attributeNameToTagMappingConfigs != null) {
            this.attributeNameToTagMappingConfigs.clear();
         }

         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public Saml2FlowSpConfig.Saml2FlowSpConfigBuilder attributeToImport(Saml2SpAssertionStringAttributeImportConfig attributeToImport) {
         if (this.attributesToImport == null) {
            this.attributesToImport = new ArrayList<>();
         }

         this.attributesToImport.add(attributeToImport);
         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public Saml2FlowSpConfig.Saml2FlowSpConfigBuilder attributesToImport(Collection<? extends Saml2SpAssertionStringAttributeImportConfig> attributesToImport) {
         if (attributesToImport == null) {
            throw new NullPointerException("attributesToImport cannot be null");
         }

         if (this.attributesToImport == null) {
            this.attributesToImport = new ArrayList<>();
         }

         this.attributesToImport.addAll(attributesToImport);
         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public Saml2FlowSpConfig.Saml2FlowSpConfigBuilder clearAttributesToImport() {
         if (this.attributesToImport != null) {
            this.attributesToImport.clear();
         }

         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public Saml2FlowSpConfig build() {
         List usernameTransformers = switch (this.usernameTransformers == null ? 0 : this.usernameTransformers.size()) {
            case 0 -> Collections.emptyList();
            case 1 -> Collections.singletonList(this.usernameTransformers.get(0));
            default -> Collections.unmodifiableList(new ArrayList<>(this.usernameTransformers));
         };

         List attributeNameToTagMappingConfigs = switch (this.attributeNameToTagMappingConfigs == null ? 0 : this.attributeNameToTagMappingConfigs.size()) {
            case 0 -> Collections.emptyList();
            case 1 -> Collections.singletonList(this.attributeNameToTagMappingConfigs.get(0));
            default -> Collections.unmodifiableList(new ArrayList<>(this.attributeNameToTagMappingConfigs));
         };

         return new Saml2FlowSpConfig(
            this.spEntityConfig,
            this.remoteIdpEntitySettings,
            usernameTransformers,
            this.authnRequestBinding,
            this.customAuthnRequestExtensions,
            this.defaultFlowApplicationId,
            this.attributeToImportAsUserId,
            this.attributeToImportAsLanguage,
            this.attributeToImportAsAuditToken,
            this.attributeToImportAsAuthTokenId,
            attributeNameToTagMappingConfigs,
            switch (this.attributesToImport == null ? 0 : this.attributesToImport.size()) {
               case 0 -> Collections.emptyList();
               case 1 -> Collections.singletonList(this.attributesToImport.get(0));
               default -> Collections.unmodifiableList(new ArrayList<>(this.attributesToImport));
            }
         );
      }

      @SuppressFBWarnings(justification = "generated code")
      @Override
      public String toString() {
         return "Saml2FlowSpConfig.Saml2FlowSpConfigBuilder(spEntityConfig="
            + this.spEntityConfig
            + ", remoteIdpEntitySettings="
            + this.remoteIdpEntitySettings
            + ", usernameTransformers="
            + this.usernameTransformers
            + ", authnRequestBinding="
            + this.authnRequestBinding
            + ", customAuthnRequestExtensions="
            + this.customAuthnRequestExtensions
            + ", defaultFlowApplicationId="
            + this.defaultFlowApplicationId
            + ", attributeToImportAsUserId="
            + this.attributeToImportAsUserId
            + ", attributeToImportAsLanguage="
            + this.attributeToImportAsLanguage
            + ", attributeToImportAsAuditToken="
            + this.attributeToImportAsAuditToken
            + ", attributeToImportAsAuthTokenId="
            + this.attributeToImportAsAuthTokenId
            + ", attributeNameToTagMappingConfigs="
            + this.attributeNameToTagMappingConfigs
            + ", attributesToImport="
            + this.attributesToImport
            + ")";
      }
   }
}
