/**
   * sets up the outer security handlers.
   *
   * <p>the handler that actually performs the access check happens later in the chain, it is not
   * setup here
   *
   * @param initialHandler The handler to wrap with security handlers
   */
  private HttpHandler setupSecurityHandlers(HttpHandler initialHandler) {
    final DeploymentInfo deploymentInfo = deployment.getDeploymentInfo();
    final LoginConfig loginConfig = deploymentInfo.getLoginConfig();

    HttpHandler current = initialHandler;

    final SecurityPathMatches securityPathMatches = buildSecurityConstraints();
    current = new AuthenticationCallHandler(current);
    if (!securityPathMatches.isEmpty()) {
      current = new ServletAuthenticationConstraintHandler(current);
    }
    current =
        new ServletConfidentialityConstraintHandler(
            deploymentInfo.getConfidentialPortManager(), current);
    if (!securityPathMatches.isEmpty()) {
      current = new ServletSecurityConstraintHandler(securityPathMatches, current);
    }

    final String mechName;
    if (loginConfig != null) {
      List<AuthenticationMechanism> authenticationMechanisms =
          new LinkedList<AuthenticationMechanism>();
      authenticationMechanisms.add(new CachedAuthenticatedSessionMechanism());

      mechName = loginConfig.getAuthMethod();
      if (mechName.equalsIgnoreCase(BASIC_AUTH)) {
        // The mechanism name is passed in from the HttpServletRequest interface as the name
        // reported needs to be comparable using '=='
        authenticationMechanisms.add(
            new BasicAuthenticationMechanism(loginConfig.getRealmName(), BASIC_AUTH));
      } else if (mechName.equalsIgnoreCase(FORM_AUTH)) {
        // The mechanism name is passed in from the HttpServletRequest interface as the name
        // reported needs to be comparable using '=='
        authenticationMechanisms.add(
            new ServletFormAuthenticationMechanism(
                FORM_AUTH, loginConfig.getLoginPage(), loginConfig.getErrorPage()));
      } else if (mechName.equalsIgnoreCase(CLIENT_CERT_AUTH)) {
        authenticationMechanisms.add(new ClientCertAuthenticationMechanism(CLIENT_CERT_AUTH));
      } else {
        // NYI
      }
      current = new AuthenticationMechanismsHandler(current, authenticationMechanisms);
    } else {
      mechName = null;
    }

    current = new CachedAuthenticatedSessionHandler(current, this.deployment.getServletContext());
    List<NotificationReceiver> notificationReceivers = deploymentInfo.getNotificationReceivers();
    if (notificationReceivers.isEmpty() == false) {
      current = new NotificationReceiverHandler(current, notificationReceivers);
    }

    // TODO - A switch to constraint driven could be configurable, however before we can support
    // that with servlets we would
    // need additional tracking within sessions if a servlet has specifically requested that
    // authentication occurs.
    current =
        new SecurityInitialHandler(
            AuthenticationMode.PRO_ACTIVE, deploymentInfo.getIdentityManager(), mechName, current);
    return current;
  }
  @Override
  public void handleDeployment(DeploymentInfo deploymentInfo, ServletContext servletContext) {
    if (!isAuthenticationMechanismPresent(deploymentInfo, "KEYCLOAK")) {
      log.info("auth-method is not keycloak!");
      return;
    }
    log.info("KeycloakServletException initialization");
    InputStream is = servletContext.getResourceAsStream("/WEB-INF/keycloak.json");
    if (is == null)
      throw new RuntimeException("Unable to find /WEB-INF/keycloak.json configuration file");
    RealmConfigurationLoader loader = new RealmConfigurationLoader(is);
    loader.init(true);
    AdapterConfig keycloakConfig = loader.getAdapterConfig();
    RealmConfiguration realmConfiguration = loader.getRealmConfiguration();
    PreflightCorsHandler.Wrapper preflight = new PreflightCorsHandler.Wrapper(keycloakConfig);
    UserSessionManagement userSessionManagement = new UserSessionManagement(realmConfiguration);
    ServletKeycloakAuthenticationMechanism auth = null;
    if (keycloakConfig.isBearerOnly()) {
      auth =
          new ServletKeycloakAuthenticationMechanism(
              keycloakConfig,
              loader.getResourceMetadata(),
              deploymentInfo.getConfidentialPortManager());
    } else {
      auth =
          new ServletKeycloakAuthenticationMechanism(
              userSessionManagement,
              keycloakConfig,
              realmConfiguration,
              deploymentInfo.getConfidentialPortManager());
    }
    ServletAuthenticatedActionsHandler.Wrapper actions =
        new ServletAuthenticatedActionsHandler.Wrapper(keycloakConfig);

    // setup handlers

    deploymentInfo.addInitialHandlerChainWrapper(preflight); // cors preflight
    deploymentInfo.addOuterHandlerChainWrapper(
        new ServletAdminActionsHandler.Wrapper(realmConfiguration, userSessionManagement));
    final ServletKeycloakAuthenticationMechanism theAuth = auth;
    deploymentInfo.addAuthenticationMechanism(
        "KEYCLOAK",
        new AuthenticationMechanismFactory() {
          @Override
          public AuthenticationMechanism create(
              String s, FormParserFactory formParserFactory, Map<String, String> stringStringMap) {
            return theAuth;
          }
        }); // authentication
    deploymentInfo.addInnerHandlerChainWrapper(
        ServletPropagateSessionHandler.WRAPPER); // propagates SkeletonKeySession
    deploymentInfo.addInnerHandlerChainWrapper(actions); // handles authenticated actions and cors.

    deploymentInfo.setIdentityManager(
        new IdentityManager() {
          @Override
          public Account verify(Account account) {
            log.info("Verifying account in IdentityManager");
            return account;
          }

          @Override
          public Account verify(String id, Credential credential) {
            log.warn("Shouldn't call verify!!!");
            throw new IllegalStateException("Not allowed");
          }

          @Override
          public Account verify(Credential credential) {
            log.warn("Shouldn't call verify!!!");
            throw new IllegalStateException("Not allowed");
          }
        });

    log.info("Setting jsession cookie path to: " + deploymentInfo.getContextPath());
    ServletSessionConfig cookieConfig = new ServletSessionConfig();
    cookieConfig.setPath(deploymentInfo.getContextPath());
    deploymentInfo.setServletSessionConfig(cookieConfig);
  }
  private HttpHandler setupSecurityHandlers(HttpHandler initialHandler) {
    final DeploymentInfo deploymentInfo = super.getDeployment().getDeploymentInfo();
    final LoginConfig loginConfig = deploymentInfo.getLoginConfig();

    final Map<String, AuthenticationMechanismFactory> factoryMap =
        new HashMap<>(deploymentInfo.getAuthenticationMechanisms());
    if (!factoryMap.containsKey(BASIC_AUTH)) {
      factoryMap.put(BASIC_AUTH, BasicAuthenticationMechanism.FACTORY);
    }
    if (!factoryMap.containsKey(FORM_AUTH)) {
      factoryMap.put(FORM_AUTH, ServletFormAuthenticationMechanism.FACTORY);
    }
    if (!factoryMap.containsKey(DIGEST_AUTH)) {
      factoryMap.put(DIGEST_AUTH, DigestAuthenticationMechanism.FACTORY);
    }
    if (!factoryMap.containsKey(CLIENT_CERT_AUTH)) {
      factoryMap.put(CLIENT_CERT_AUTH, ClientCertAuthenticationMechanism.FACTORY);
    }
    if (!factoryMap.containsKey(ExternalAuthenticationMechanism.NAME)) {
      factoryMap.put(ExternalAuthenticationMechanism.NAME, ExternalAuthenticationMechanism.FACTORY);
    }
    HttpHandler current = initialHandler;
    current = new SSLInformationAssociationHandler(current);

    final SecurityPathMatches securityPathMatches = buildSecurityConstraints();
    current = new ServletAuthenticationCallHandler(current);
    if (deploymentInfo.isDisableCachingForSecuredPages()) {
      current =
          Handlers.predicate(Predicates.authRequired(), Handlers.disableCache(current), current);
    }
    if (!securityPathMatches.isEmpty()) {
      current = new ServletAuthenticationConstraintHandler(current);
    }
    current =
        new ServletConfidentialityConstraintHandler(
            deploymentInfo.getConfidentialPortManager(), current);
    if (!securityPathMatches.isEmpty()) {
      current = new ServletSecurityConstraintHandler(securityPathMatches, current);
    }
    List<AuthenticationMechanism> authenticationMechanisms = new LinkedList<>();
    authenticationMechanisms.add(
        new CachedAuthenticatedSessionMechanism()); // TODO: does this really need to be hard coded?

    String mechName = null;
    if (loginConfig != null || deploymentInfo.getJaspiAuthenticationMechanism() != null) {

      // we don't allow multipart requests, and always use the default encoding
      FormParserFactory parser =
          FormParserFactory.builder(false)
              .addParser(
                  new FormEncodedDataDefinition()
                      .setDefaultEncoding(deploymentInfo.getDefaultEncoding()))
              .build();

      List<AuthMethodConfig> authMethods = Collections.<AuthMethodConfig>emptyList();
      if (loginConfig != null) {
        authMethods = loginConfig.getAuthMethods();
      }

      for (AuthMethodConfig method : authMethods) {
        AuthenticationMechanismFactory factory = factoryMap.get(method.getName());
        if (factory == null) {
          throw UndertowServletMessages.MESSAGES.unknownAuthenticationMechanism(method.getName());
        }
        if (mechName == null) {
          mechName = method.getName();
        }

        final Map<String, String> properties = new HashMap<>();
        properties.put(
            AuthenticationMechanismFactory.CONTEXT_PATH, deploymentInfo.getContextPath());
        properties.put(AuthenticationMechanismFactory.REALM, loginConfig.getRealmName());
        properties.put(AuthenticationMechanismFactory.ERROR_PAGE, loginConfig.getErrorPage());
        properties.put(AuthenticationMechanismFactory.LOGIN_PAGE, loginConfig.getLoginPage());
        properties.putAll(method.getProperties());

        String name = method.getName().toUpperCase(Locale.US);
        // The mechanism name is passed in from the HttpServletRequest interface as the name
        // reported needs to be
        // comparable using '=='
        name = name.equals(FORM_AUTH) ? FORM_AUTH : name;
        name = name.equals(BASIC_AUTH) ? BASIC_AUTH : name;
        name = name.equals(DIGEST_AUTH) ? DIGEST_AUTH : name;
        name = name.equals(CLIENT_CERT_AUTH) ? CLIENT_CERT_AUTH : name;

        authenticationMechanisms.add(factory.create(name, parser, properties));
      }
    }

    ((DeploymentImpl) super.getDeployment()).setAuthenticationMechanisms(authenticationMechanisms);
    // if the JASPI auth mechanism is set then it takes over
    if (deploymentInfo.getJaspiAuthenticationMechanism() == null) {
      current = new AuthenticationMechanismsHandler(current, authenticationMechanisms);
    } else {
      current =
          new AuthenticationMechanismsHandler(
              current,
              Collections.<AuthenticationMechanism>singletonList(
                  deploymentInfo.getJaspiAuthenticationMechanism()));
    }

    current =
        new CachedAuthenticatedSessionHandler(current, super.getDeployment().getServletContext());
    List<NotificationReceiver> notificationReceivers = deploymentInfo.getNotificationReceivers();
    if (!notificationReceivers.isEmpty()) {
      current = new NotificationReceiverHandler(current, notificationReceivers);
    }

    // TODO - A switch to constraint driven could be configurable, however before we can support
    // that with servlets we would
    // need additional tracking within sessions if a servlet has specifically requested that
    // authentication occurs.
    SecurityContextFactory contextFactory = deploymentInfo.getSecurityContextFactory();
    if (contextFactory == null) {
      contextFactory = SecurityContextFactoryImpl.INSTANCE;
    }
    current =
        new SecurityInitialHandler(
            AuthenticationMode.PRO_ACTIVE,
            deploymentInfo.getIdentityManager(),
            mechName,
            contextFactory,
            current);
    return current;
  }