void THttpServer::headersDone()

in be/src/transport/THttpServer.cpp [274:488]


void THttpServer::headersDone() {
  if (!header_x_request_id_.empty() || !header_x_session_id_.empty() ||
      !header_x_query_id_.empty()) {
    VLOG_RPC << "HTTP Connection Tracing Headers"
        << (header_x_request_id_.empty() ? "" : " x-request-id=" + header_x_request_id_)
        << (header_x_session_id_.empty() ? "" : " x-session-id=" + header_x_session_id_)
        << (header_x_query_id_.empty() ? "" : " x-query-id=" + header_x_query_id_);
  }

  // Trim and truncate the value of the 'X-Forwarded-For' header.
  string origin = origin_;
  // After copying origin, reset the value so that it can be reused for the next message.
  origin_ = "";
  StripWhiteSpace(&origin);
  if (origin.length() > MAX_X_FORWARDED_HEADER_LENGTH) {
    origin = origin.substr(0, MAX_X_FORWARDED_HEADER_LENGTH);
  }
  // Store the truncated value of the 'X-Forwarded-For' header in the Connection Context.
  callbacks_.set_http_origin_fn(origin);

  if (!has_ldap_ && !has_kerberos_ && !has_saml_ && !has_jwt_ && !has_oauth_) {
    // We don't need to authenticate.
    resetAuthState();
    return;
  }

  if (readWholeBodyForAuth_) {
    DCHECK(has_saml_);
    // 2nd SAML message in browser mode, get authNResponse from IP.
    // Will be handled in bodyDone. Must return before processing
    // cookies, as the cookies from the IdP server can confuse our
    // logic.
    return;
  }

  bool authorized = false;
  // Try authenticating with cookies first.
  if (use_cookies_ && !cookie_value_.empty()) {
    StripWhiteSpace(&cookie_value_);
    // If a 'Cookie' header was provided with an empty value, we ignore it rather than
    // counting it as a failed cookie attempt.
    if (!cookie_value_.empty()) {
      if (callbacks_.cookie_auth_fn(cookie_value_)) {
        authorized = true;
        if (metrics_enabled_) http_metrics_->total_cookie_auth_success_->Increment(1);
      } else if (metrics_enabled_) {
        http_metrics_->total_cookie_auth_failure_->Increment(1);
      }
    }
  }

  if (!authorized && (has_jwt_ || has_oauth_) && !auth_value_.empty()
      && auth_value_.find('.') != string::npos) {
    // Check Authorization header with the Bearer authentication scheme as:
    // Authorization: Bearer <token>
    // JWT contains at least one period ('.'). A well-formed JWT consists of three
    // concatenated Base64url-encoded strings, separated by dots (.).
    StripWhiteSpace(&auth_value_);
    string jwt_token;
    bool got_bearer_auth = TryStripPrefixString(auth_value_, "Bearer ", &jwt_token);
    if (got_bearer_auth) {
      if (has_jwt_ && callbacks_.jwt_token_auth_fn(jwt_token)) {
        authorized = true;
        if (metrics_enabled_)
          http_metrics_->total_jwt_token_auth_success_->Increment(1);
      }
      if (!authorized && has_oauth_ && callbacks_.oauth_token_auth_fn(jwt_token)) {
        authorized = true;
        if (metrics_enabled_)
          http_metrics_->total_oauth_token_auth_success_->Increment(1);
      }
      if (!authorized) {
        if (has_jwt_ && metrics_enabled_)
          http_metrics_->total_jwt_token_auth_failure_->Increment(1);
        if (has_oauth_ && metrics_enabled_)
          http_metrics_->total_oauth_token_auth_failure_->Increment(1);
      }
    }
  }

  if (!authorized && has_saml_) {
    bool fallback_to_other_auths = true;
    if (saml_port_ != -1) {
      fallback_to_other_auths = false;
      // 1st SAML message in browser mode, redirect SSO.
      impala::TWrappedHttpResponse* response = callbacks_.get_saml_redirect_fn();
      if (response != nullptr) {
        returnWrappedResponse(*response);
        resetAuthState();
        throw TTransportException("HTTP auth - SAML redirection.");
      }
    } else if (!auth_value_.empty()) {
      StripWhiteSpace(&auth_value_);
      string stripped_bearer_auth_token;
      bool got_bearer_auth =
          TryStripPrefixString(auth_value_, "Bearer ", &stripped_bearer_auth_token);
      if (got_bearer_auth) {
        fallback_to_other_auths = false;
        // Final SAML message in browser mode, check bearer and replace it with a cookie.
        DCHECK(wrapped_request_ != nullptr);
        wrapped_request_->headers[HEADER_AUTHORIZATION] = auth_value_;
        if (callbacks_.validate_saml2_bearer_fn()) {
          // During EE tests it makes things easier to return 401-Unauthorized here.
          // This hack can be removed once there is a Python client that
          // supports SAML (IMPALA-10496).
          if (!FLAGS_saml2_ee_test_mode) authorized = true;
          if (metrics_enabled_) http_metrics_->total_saml_auth_success_->Increment(1);
        }
      }
    }

    if (!authorized && !fallback_to_other_auths) {
      // Do not fallback to other auth mechanisms, as the client probably expects
      // only SAML related responses.
      if (metrics_enabled_) http_metrics_->total_saml_auth_failure_->Increment(1);
      resetAuthState();
      returnUnauthorized();
      throw TTransportException("HTTP auth failed.");
    }
  }

  // Bypass auth for connections from trusted domains. Returns a cookie on the first
  // successful auth attempt. This check is performed after checking for cookie to avoid
  // subsequent reverse DNS lookups which can be unpredictably costly.
  // This is also done after SAML related authentication, because it is assumed that if
  // the client started the SAML workflow then it doesn't expect Impala to succeed with
  // another mechanism.
  if (!authorized && check_trusted_domain_) {
    string transport_origin;
    if (FLAGS_trusted_domain_use_xff_header) {
      impala::Status status = impala::GetXFFOriginClientAddress(origin, transport_origin);
      if (!status.ok()) LOG(WARNING) << status.GetDetail();
    } else {
      transport_origin = transport_->getOrigin();
    }
    StripWhiteSpace(&transport_origin);
    if (transport_origin.empty() && FLAGS_trusted_domain_use_xff_header &&
        FLAGS_trusted_domain_empty_xff_header_use_origin) {
      transport_origin = transport_->getOrigin();
      StripWhiteSpace(&transport_origin);
    }
    if (!transport_origin.empty()) {
      if (callbacks_.trusted_domain_check_fn(transport_origin, auth_value_)) {
        authorized = true;
        if (metrics_enabled_) {
          http_metrics_->total_trusted_domain_check_success_->Increment(1);
        }
      }
    }
  }

  // Bypass auth for connections if trusted auth header was found in connection string.
  if (!authorized && found_trusted_auth_header_ && !auth_value_.empty()) {
    if (callbacks_.trusted_auth_header_handle_fn(auth_value_)) {
      authorized = true;
      if (metrics_enabled_) {
        http_metrics_->total_trusted_auth_header_check_success_->Increment(1);
      }
    }
  }

  // If cookie auth wasn't successful, try to auth with the 'Authorization' header.
  if (!authorized) {
    // Determine what type of auth header we got.
    StripWhiteSpace(&auth_value_);
    string stripped_basic_auth_token;
    bool got_basic_auth =
        TryStripPrefixString(auth_value_, "Basic ", &stripped_basic_auth_token);
    string basic_auth_token = got_basic_auth ? move(stripped_basic_auth_token) : "";
    string stripped_negotiate_auth_token;
    bool got_negotiate_auth =
        TryStripPrefixString(auth_value_, "Negotiate ", &stripped_negotiate_auth_token);
    string negotiate_auth_token =
        got_negotiate_auth ? move(stripped_negotiate_auth_token) : "";
    // We can only have gotten one type of auth header.
    DCHECK(!got_basic_auth || !got_negotiate_auth);

    // For each auth type we support, we call the auth callback if the didn't get a header
    // of the other auth type or if the other auth type isn't supported. This way, if a
    // client select a supported auth method, they'll only get return headers for that
    // method, but if they didn't specify a valid auth method or they didn't provide a
    // 'Authorization' header at all, they'll get back 'WWW-Authenticate' return headers
    // for all supported auth types.
    if (has_ldap_ && (!got_negotiate_auth || !has_kerberos_)) {
      if (callbacks_.basic_auth_fn(basic_auth_token)) {
        authorized = true;
        if (metrics_enabled_) http_metrics_->total_basic_auth_success_->Increment(1);
      } else {
        if (got_basic_auth && metrics_enabled_) {
          http_metrics_->total_basic_auth_failure_->Increment(1);
        }
      }
    }
    if (has_kerberos_ && (!got_basic_auth || !has_ldap_)) {
      bool is_complete;
      if (callbacks_.negotiate_auth_fn(negotiate_auth_token, &is_complete)) {
        // If 'is_complete' is false we want to return a 401.
        authorized = is_complete;
        if (is_complete && metrics_enabled_) {
          http_metrics_->total_negotiate_auth_success_->Increment(1);
        }
      } else {
        if (got_negotiate_auth && metrics_enabled_) {
          http_metrics_->total_negotiate_auth_failure_->Increment(1);
        }
      }
    }
  }

  resetAuthState();
  if (!authorized) {
    returnUnauthorized();
    throw TTransportException("HTTP auth failed.");
  }
}