/** * 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); } }
/** * Update the user * * @param id User id * @param rep * @return */ @Path("{id}") @PUT @Consumes(MediaType.APPLICATION_JSON) public Response updateUser(final @PathParam("id") String id, final UserRepresentation rep) { auth.requireManage(); try { UserModel user = session.users().getUserById(id, realm); if (user == null) { throw new NotFoundException("User not found"); } Set<String> attrsToRemove; if (rep.getAttributes() != null) { attrsToRemove = new HashSet<>(user.getAttributes().keySet()); attrsToRemove.removeAll(rep.getAttributes().keySet()); } else { attrsToRemove = Collections.emptySet(); } if (rep.isEnabled() != null && rep.isEnabled()) { UsernameLoginFailureModel failureModel = session.sessions().getUserLoginFailure(realm, rep.getUsername().toLowerCase()); if (failureModel != null) { failureModel.clearFailures(); } } updateUserFromRep(user, rep, attrsToRemove, realm, session); adminEvent .operation(OperationType.UPDATE) .resourcePath(uriInfo) .representation(rep) .success(); if (session.getTransaction().isActive()) { session.getTransaction().commit(); } return Response.noContent().build(); } catch (ModelDuplicateException e) { return ErrorResponse.exists("User exists with same username or email"); } catch (ModelReadOnlyException re) { return ErrorResponse.exists("User is read only!"); } }