/**
   * Get status of a username in brute force detection
   *
   * @param username
   * @return
   */
  @GET
  @Path("brute-force/usernames/{username}")
  @NoCache
  @Produces(MediaType.APPLICATION_JSON)
  public Map<String, Object> bruteForceUserStatus(@PathParam("username") String username) {
    auth.requireView();

    Map<String, Object> data = new HashMap<>();
    data.put("disabled", false);
    data.put("numFailures", 0);
    data.put("lastFailure", 0);
    data.put("lastIPFailure", "n/a");
    if (!realm.isBruteForceProtected()) return data;

    UsernameLoginFailureModel model =
        session.sessions().getUserLoginFailure(realm, username.toLowerCase());
    if (model == null) return data;
    if (session
        .getProvider(BruteForceProtector.class)
        .isTemporarilyDisabled(session, realm, username)) {
      data.put("disabled", true);
    }
    data.put("numFailures", model.getNumFailures());
    data.put("lastFailure", model.getLastFailure());
    data.put("lastIPFailure", model.getLastIPFailure());
    return data;
  }
  public void failure(KeycloakSession session, LoginEvent event) {
    UsernameLoginFailureModel user = getUserModel(session, event);
    if (user == null) return;
    user.setLastIPFailure(event.ip);
    long currentTime = System.currentTimeMillis();
    long last = user.getLastFailure();
    long deltaTime = 0;
    if (last > 0) {
      deltaTime = currentTime - last;
    }
    user.setLastFailure(currentTime);
    if (deltaTime > 0) {
      // if last failure was more than MAX_DELTA clear failures
      if (deltaTime > maxDeltaTime) {
        user.clearFailures();
      }
    }
    user.incrementFailures();

    int waitSeconds = waitIncrementSeconds * (user.getNumFailures() / failureFactor);
    if (waitSeconds == 0) {
      if (deltaTime > quickLoginCheckMilliSeconds) {
        waitSeconds = minimumQuickLoginWaitSeconds;
      }
    }
    waitSeconds = Math.min(maxFailureWaitSeconds, waitSeconds);
    if (waitSeconds > 0) {
      user.setFailedLoginNotBefore((int) (currentTime / 1000) + waitSeconds);
    }
  }