/**
   * 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;
  }
예제 #2
0
  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);
    }
  }
예제 #3
0
  /**
   * 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!");
    }
  }