/** * 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); } }