/**
   * Validates the request by checking for the presence of a pre-configured attribute in the
   * ServletRequest.
   *
   * @param messageInfo {@inheritDoc}
   * @param clientSubject {@inheritDoc}
   * @param serviceSubject {@inheritDoc}
   * @return {@inheritDoc}
   */
  @Override
  public Promise<AuthStatus, AuthenticationException> validateRequest(
      MessageInfoContext messageInfo, Subject clientSubject, Subject serviceSubject) {

    SecurityContextMapper securityContextMapper =
        SecurityContextMapper.fromMessageInfo(messageInfo);
    final JsonValue attributes =
        json(messageInfo.asContext(AttributesContext.class).getAttributes());

    if (attributes.isDefined(authenticationIdAttribute)
        && attributes.get(authenticationIdAttribute).isString()) {
      final String authenticationId = attributes.get(authenticationIdAttribute).asString();
      securityContextMapper.setAuthenticationId(authenticationId);
      clientSubject
          .getPrincipals()
          .add(
              new Principal() {
                public String getName() {
                  return authenticationId;
                }
              });
      return newResultPromise(SUCCESS);
    } else {
      return newResultPromise(SEND_FAILURE);
    }
  }
  private boolean authenticate(Credential credential, SecurityContextMapper securityContextMapper)
      throws AuthException {

    if (!credential.isComplete()) {
      logger.debug("Failed authentication, missing or empty headers");
      return false;
    }

    // set the authenticationId of the user that is trying to authenticate
    securityContextMapper.setAuthenticationId(credential.username);

    try {
      return authenticator.authenticate(
          credential.username,
          credential.password,
          authnFilterHelper.getRouter().createServerContext());
    } catch (ResourceException e) {
      logger.debug(
          "Failed delegated authentication of {} on {}.", credential.username, queryOnResource, e);
      if (e.isServerError()) { // HTTP server-side error
        throw new JaspiAuthException(
            "Failed delegated authentication of " + credential.username + " on " + queryOnResource,
            e);
      }
      // authentication failed
      return false;
    }
  }
  /**
   * Validates the client's request by passing through the request to be authenticated against a
   * OpenICF Connector.
   *
   * @param messageInfo {@inheritDoc}
   * @param clientSubject {@inheritDoc}
   * @param serviceSubject {@inheritDoc}
   * @return {@inheritDoc}
   * @throws AuthException If there is a problem performing the authentication.
   */
  @Override
  public AuthStatus validateRequest(
      MessageInfo messageInfo, Subject clientSubject, Subject serviceSubject) throws AuthException {

    logger.debug("DelegatedAuthModule: validateRequest START");

    SecurityContextMapper securityContextMapper =
        SecurityContextMapper.fromMessageInfo(messageInfo);
    HttpServletRequest request = (HttpServletRequest) messageInfo.getRequestMessage();

    try {
      logger.debug("DelegatedAuthModule: Delegating call to remote authentication");

      if (authenticate(HEADER_AUTH_CRED_HELPER.getCredential(request), securityContextMapper)
          || authenticate(BASIC_AUTH_CRED_HELPER.getCredential(request), securityContextMapper)) {

        logger.debug("DelegatedAuthModule: Authentication successful");

        final String authcid = securityContextMapper.getAuthenticationId();
        clientSubject
            .getPrincipals()
            .add(
                new Principal() {
                  public String getName() {
                    return authcid;
                  }
                });

        // Auth success will be logged in IDMJaspiModuleWrapper
        return AuthStatus.SUCCESS;
      } else {
        logger.debug("DelegatedAuthModule: Authentication failed");
        return AuthStatus.SEND_FAILURE;
      }
    } finally {
      logger.debug("DelegatedAuthModule: validateRequest END");
    }
  }
  /**
   * Performs the calculation of roles based on the userRoles property in the configuration and the
   * retrieved user object.
   *
   * @param principal The principal.
   * @param securityContextMapper The message info instance.
   * @param resource the retrieved resource for the principal.
   * @return A SecurityContextMapper instance containing the authentication context information.
   */
  public void calculateRoles(
      String principal, SecurityContextMapper securityContextMapper, ResourceResponse resource) {

    // Set roles from retrieved object:
    if (resource != null) {
      final JsonValue userDetail = resource.getContent();

      // support reading roles from property in object
      if (userRoles != null && !userDetail.get(userRoles).isNull()) {
        if (userDetail.get(userRoles).isString()) {
          for (String role : userDetail.get(userRoles).asString().split(",")) {
            securityContextMapper.addRole(role);
          }
        } else if (userDetail.get(userRoles).isList()) {
          for (JsonValue role : userDetail.get(userRoles)) {
            if (RelationshipUtil.isRelationship(role)) {
              // Role is specified as a relationship Object
              JsonPointer roleId =
                  new JsonPointer(role.get(RelationshipUtil.REFERENCE_ID).asString());
              securityContextMapper.addRole(roleId.leaf());
            } else {
              // Role is specified as a String
              securityContextMapper.addRole(role.asString());
            }
          }
        } else {
          logger.warn(
              "Unknown roles type retrieved from user query, expected collection: {} type: {}",
              userRoles,
              userDetail.get(userRoles).getObject().getClass());
        }
      }

      // Roles are now set.
      // Note: roles can be further augmented with a script if more complex behavior is desired

      logger.debug(
          "Used {}object property to update context for {} with userid : {}, roles : {}",
          userRoles != null ? (userRoles + " ") : "",
          securityContextMapper.getAuthenticationId(),
          securityContextMapper.getUserId(),
          securityContextMapper.getRoles());
    }
  }