/**
  * Only allow the request through if successfully authenticated or if authentication is not
  * required.
  *
  * @throws java.lang.Exception
  * @see io.undertow.server.HttpHandler#handleRequest(io.undertow.server.HttpServerExchange)
  */
 @Override
 public void handleRequest(HttpServerExchange exchange, RequestContext context) throws Exception {
   if (exchange.isInIoThread()) {
     exchange.dispatch(this);
     return;
   }
   SecurityContext rcontext = exchange.getSecurityContext();
   if (rcontext.authenticate()) {
     if (!exchange.isComplete()) {
       getNext().handleRequest(exchange, context);
     }
   } else {
     exchange.endExchange();
   }
 }
  protected void completeAuthentication(
      HttpServerExchange exchange,
      SecurityContext securityContext,
      SkeletonKeyToken token,
      String surrogate) {
    final SkeletonKeyPrincipal skeletonKeyPrincipal =
        new SkeletonKeyPrincipal(token.getPrincipal(), surrogate);
    Set<String> roles = null;
    if (config.isUseResourceRoleMappings()) {
      SkeletonKeyToken.Access access = token.getResourceAccess(resourceMetadata.getResourceName());
      if (access != null) roles = access.getRoles();
    } else {
      SkeletonKeyToken.Access access = token.getRealmAccess();
      if (access != null) roles = access.getRoles();
    }
    if (roles == null) roles = Collections.emptySet();
    final Set<String> accountRoles = roles;
    Account account =
        new Account() {
          @Override
          public Principal getPrincipal() {
            return skeletonKeyPrincipal;
          }

          @Override
          public Set<String> getRoles() {
            return accountRoles;
          }
        };
    securityContext.authenticationComplete(account, "FORM");
  }
  @Override
  public void handleRequest(final HttpServerExchange exchange) throws Exception {
    final ServletRequestContext servletRequestContext =
        exchange.getAttachment(ServletRequestContext.ATTACHMENT_KEY);
    ServletRequest request = servletRequestContext.getServletRequest();
    if (request.getDispatcherType() == DispatcherType.REQUEST) {
      List<SingleConstraintMatch> constraints = servletRequestContext.getRequiredConstrains();
      SecurityContext sc = exchange.getSecurityContext();
      if (!authorizationManager.canAccessResource(
          constraints,
          sc.getAuthenticatedAccount(),
          servletRequestContext.getCurrentServlet().getManagedServlet().getServletInfo(),
          servletRequestContext.getOriginalRequest(),
          servletRequestContext.getDeployment())) {

        HttpServletResponse response =
            (HttpServletResponse) servletRequestContext.getServletResponse();
        response.sendError(403);
        return;
      }
    }
    next.handleRequest(exchange);
  }
  @Override
  protected void completeAuthentication(final SamlSession samlSession) {
    Account undertowAccount =
        new Account() {
          @Override
          public Principal getPrincipal() {
            return samlSession.getPrincipal();
          }

          @Override
          public Set<String> getRoles() {
            return samlSession.getRoles();
          }
        };
    securityContext.authenticationComplete(undertowAccount, "KEYCLOAK-SAML", false);
  }
  public AuthenticationMechanismOutcome handleDigestHeader(
      HttpServerExchange exchange, final SecurityContext securityContext) {
    DigestContext context = exchange.getAttachment(DigestContext.ATTACHMENT_KEY);
    Map<DigestAuthorizationToken, String> parsedHeader = context.getParsedHeader();
    // Step 1 - Verify the set of tokens received to ensure valid values.
    Set<DigestAuthorizationToken> mandatoryTokens = new HashSet<>(MANDATORY_REQUEST_TOKENS);
    if (!supportedAlgorithms.contains(DigestAlgorithm.MD5)) {
      // If we don't support MD5 then the client must choose an algorithm as we can not fall back to
      // MD5.
      mandatoryTokens.add(DigestAuthorizationToken.ALGORITHM);
    }
    if (!supportedQops.isEmpty() && !supportedQops.contains(DigestQop.AUTH)) {
      // If we do not support auth then we are mandating auth-int so force the client to send a QOP
      mandatoryTokens.add(DigestAuthorizationToken.MESSAGE_QOP);
    }

    DigestQop qop = null;
    // This check is early as is increases the list of mandatory tokens.
    if (parsedHeader.containsKey(DigestAuthorizationToken.MESSAGE_QOP)) {
      qop = DigestQop.forName(parsedHeader.get(DigestAuthorizationToken.MESSAGE_QOP));
      if (qop == null || !supportedQops.contains(qop)) {
        // We are also ensuring the client is not trying to force a qop that has been disabled.
        REQUEST_LOGGER.invalidTokenReceived(
            DigestAuthorizationToken.MESSAGE_QOP.getName(),
            parsedHeader.get(DigestAuthorizationToken.MESSAGE_QOP));
        // TODO - This actually needs to result in a HTTP 400 Bad Request response and not a new
        // challenge.
        return AuthenticationMechanismOutcome.NOT_AUTHENTICATED;
      }
      context.setQop(qop);
      mandatoryTokens.add(DigestAuthorizationToken.CNONCE);
      mandatoryTokens.add(DigestAuthorizationToken.NONCE_COUNT);
    }

    // Check all mandatory tokens are present.
    mandatoryTokens.removeAll(parsedHeader.keySet());
    if (mandatoryTokens.size() > 0) {
      for (DigestAuthorizationToken currentToken : mandatoryTokens) {
        // TODO - Need a better check and possible concatenate the list of tokens - however
        // even having one missing token is not something we should routinely expect.
        REQUEST_LOGGER.missingAuthorizationToken(currentToken.getName());
      }
      // TODO - This actually needs to result in a HTTP 400 Bad Request response and not a new
      // challenge.
      return AuthenticationMechanismOutcome.NOT_AUTHENTICATED;
    }

    // Perform some validation of the remaining tokens.
    if (!realmName.equals(parsedHeader.get(DigestAuthorizationToken.REALM))) {
      REQUEST_LOGGER.invalidTokenReceived(
          DigestAuthorizationToken.REALM.getName(),
          parsedHeader.get(DigestAuthorizationToken.REALM));
      // TODO - This actually needs to result in a HTTP 400 Bad Request response and not a new
      // challenge.
      return AuthenticationMechanismOutcome.NOT_AUTHENTICATED;
    }

    // TODO - Validate the URI

    if (parsedHeader.containsKey(DigestAuthorizationToken.OPAQUE)) {
      if (!OPAQUE_VALUE.equals(parsedHeader.get(DigestAuthorizationToken.OPAQUE))) {
        REQUEST_LOGGER.invalidTokenReceived(
            DigestAuthorizationToken.OPAQUE.getName(),
            parsedHeader.get(DigestAuthorizationToken.OPAQUE));
        return AuthenticationMechanismOutcome.NOT_AUTHENTICATED;
      }
    }

    DigestAlgorithm algorithm;
    if (parsedHeader.containsKey(DigestAuthorizationToken.ALGORITHM)) {
      algorithm = DigestAlgorithm.forName(parsedHeader.get(DigestAuthorizationToken.ALGORITHM));
      if (algorithm == null || !supportedAlgorithms.contains(algorithm)) {
        // We are also ensuring the client is not trying to force an algorithm that has been
        // disabled.
        REQUEST_LOGGER.invalidTokenReceived(
            DigestAuthorizationToken.ALGORITHM.getName(),
            parsedHeader.get(DigestAuthorizationToken.ALGORITHM));
        // TODO - This actually needs to result in a HTTP 400 Bad Request response and not a new
        // challenge.
        return AuthenticationMechanismOutcome.NOT_AUTHENTICATED;
      }
    } else {
      // We know this is safe as the algorithm token was made mandatory
      // if MD5 is not supported.
      algorithm = DigestAlgorithm.MD5;
    }

    try {
      context.setAlgorithm(algorithm);
    } catch (NoSuchAlgorithmException e) {
      /*
       * This should not be possible in a properly configured installation.
       */
      REQUEST_LOGGER.exceptionProcessingRequest(e);
      return AuthenticationMechanismOutcome.NOT_AUTHENTICATED;
    }

    final String userName = parsedHeader.get(DigestAuthorizationToken.USERNAME);
    final IdentityManager identityManager = getIdentityManager(securityContext);
    final Account account;

    if (algorithm.isSession()) {
      /* This can follow one of the following: -
       *   1 - New session so use DigestCredentialImpl with the IdentityManager to
       *       create a new session key.
       *   2 - Obtain the existing session key from the session store and validate it, just use
       *       IdentityManager to validate account is still active and the current role assignment.
       */
      throw new IllegalStateException("Not yet implemented.");
    } else {
      final DigestCredential credential = new DigestCredentialImpl(context);
      account = identityManager.verify(userName, credential);
    }

    if (account == null) {
      // Authentication has failed, this could either be caused by the user not-existing or it
      // could be caused due to an invalid hash.
      securityContext.authenticationFailed(MESSAGES.authenticationFailed(userName), mechanismName);
      return AuthenticationMechanismOutcome.NOT_AUTHENTICATED;
    }

    // Step 3 - Verify that the nonce was eligible to be used.
    if (!validateNonceUse(context, parsedHeader, exchange)) {
      // TODO - This is the right place to make use of the decision but the check needs to be much
      // much sooner
      // otherwise a failure server
      // side could leave a packet that could be 're-played' after the failed auth.
      // The username and password verification passed but for some reason we do not like the nonce.
      context.markStale();
      // We do not mark as a failure on the security context as this is not quite a failure, a
      // client with a cached nonce
      // can easily hit this point.
      return AuthenticationMechanismOutcome.NOT_AUTHENTICATED;
    }

    // We have authenticated the remote user.

    sendAuthenticationInfoHeader(exchange);
    securityContext.authenticationComplete(account, mechanismName, false);
    return AuthenticationMechanismOutcome.AUTHENTICATED;

    // Step 4 - Set up any QOP related requirements.

    // TODO - Do QOP
  }
 @SuppressWarnings("deprecation")
 private IdentityManager getIdentityManager(SecurityContext securityContext) {
   return identityManager != null ? identityManager : securityContext.getIdentityManager();
 }