Merge pull request #2931 from stianst/master
KEYCLOAK-3091 Change brute force to use userId
This commit is contained in:
commit
c1e202eaf9
19 changed files with 134 additions and 138 deletions
|
@ -33,16 +33,16 @@ import java.util.Map;
|
||||||
public interface AttackDetectionResource {
|
public interface AttackDetectionResource {
|
||||||
|
|
||||||
@GET
|
@GET
|
||||||
@Path("brute-force/usernames/{username}")
|
@Path("brute-force/users/{userId}")
|
||||||
@NoCache
|
@NoCache
|
||||||
@Produces(MediaType.APPLICATION_JSON)
|
@Produces(MediaType.APPLICATION_JSON)
|
||||||
Map<String, Object> bruteForceUserStatus(@PathParam("username") String username);
|
Map<String, Object> bruteForceUserStatus(@PathParam("userId") String userId);
|
||||||
|
|
||||||
@Path("brute-force/usernames/{username}")
|
@Path("brute-force/users/{userId}")
|
||||||
@DELETE
|
@DELETE
|
||||||
void clearBruteForceForUser(@PathParam("username") String username);
|
void clearBruteForceForUser(@PathParam("userId") String userId);
|
||||||
|
|
||||||
@Path("brute-force/usernames")
|
@Path("brute-force/users")
|
||||||
@DELETE
|
@DELETE
|
||||||
void clearAllBruteForce();
|
void clearAllBruteForce();
|
||||||
|
|
||||||
|
|
|
@ -30,7 +30,7 @@ import org.keycloak.models.RealmModel;
|
||||||
import org.keycloak.models.UserModel;
|
import org.keycloak.models.UserModel;
|
||||||
import org.keycloak.models.UserSessionModel;
|
import org.keycloak.models.UserSessionModel;
|
||||||
import org.keycloak.models.UserSessionProvider;
|
import org.keycloak.models.UserSessionProvider;
|
||||||
import org.keycloak.models.UsernameLoginFailureModel;
|
import org.keycloak.models.UserLoginFailureModel;
|
||||||
import org.keycloak.models.session.UserSessionPersisterProvider;
|
import org.keycloak.models.session.UserSessionPersisterProvider;
|
||||||
import org.keycloak.models.sessions.infinispan.entities.ClientInitialAccessEntity;
|
import org.keycloak.models.sessions.infinispan.entities.ClientInitialAccessEntity;
|
||||||
import org.keycloak.models.sessions.infinispan.entities.ClientSessionEntity;
|
import org.keycloak.models.sessions.infinispan.entities.ClientSessionEntity;
|
||||||
|
@ -377,24 +377,24 @@ public class InfinispanUserSessionProvider implements UserSessionProvider {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public UsernameLoginFailureModel getUserLoginFailure(RealmModel realm, String username) {
|
public UserLoginFailureModel getUserLoginFailure(RealmModel realm, String userId) {
|
||||||
LoginFailureKey key = new LoginFailureKey(realm.getId(), username);
|
LoginFailureKey key = new LoginFailureKey(realm.getId(), userId);
|
||||||
return wrap(key, loginFailureCache.get(key));
|
return wrap(key, loginFailureCache.get(key));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public UsernameLoginFailureModel addUserLoginFailure(RealmModel realm, String username) {
|
public UserLoginFailureModel addUserLoginFailure(RealmModel realm, String userId) {
|
||||||
LoginFailureKey key = new LoginFailureKey(realm.getId(), username);
|
LoginFailureKey key = new LoginFailureKey(realm.getId(), userId);
|
||||||
LoginFailureEntity entity = new LoginFailureEntity();
|
LoginFailureEntity entity = new LoginFailureEntity();
|
||||||
entity.setRealm(realm.getId());
|
entity.setRealm(realm.getId());
|
||||||
entity.setUsername(username);
|
entity.setUserId(userId);
|
||||||
tx.put(loginFailureCache, key, entity);
|
tx.put(loginFailureCache, key, entity);
|
||||||
return wrap(key, entity);
|
return wrap(key, entity);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void removeUserLoginFailure(RealmModel realm, String username) {
|
public void removeUserLoginFailure(RealmModel realm, String userId) {
|
||||||
tx.remove(loginFailureCache, new LoginFailureKey(realm.getId(), username));
|
tx.remove(loginFailureCache, new LoginFailureKey(realm.getId(), userId));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -538,8 +538,8 @@ public class InfinispanUserSessionProvider implements UserSessionProvider {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
UsernameLoginFailureModel wrap(LoginFailureKey key, LoginFailureEntity entity) {
|
UserLoginFailureModel wrap(LoginFailureKey key, LoginFailureEntity entity) {
|
||||||
return entity != null ? new UsernameLoginFailureAdapter(this, loginFailureCache, key, entity) : null;
|
return entity != null ? new UserLoginFailureAdapter(this, loginFailureCache, key, entity) : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
List<ClientSessionModel> wrapClientSessions(RealmModel realm, Collection<ClientSessionEntity> entities, boolean offline) {
|
List<ClientSessionModel> wrapClientSessions(RealmModel realm, Collection<ClientSessionEntity> entities, boolean offline) {
|
||||||
|
|
|
@ -18,21 +18,21 @@
|
||||||
package org.keycloak.models.sessions.infinispan;
|
package org.keycloak.models.sessions.infinispan;
|
||||||
|
|
||||||
import org.infinispan.Cache;
|
import org.infinispan.Cache;
|
||||||
import org.keycloak.models.UsernameLoginFailureModel;
|
import org.keycloak.models.UserLoginFailureModel;
|
||||||
import org.keycloak.models.sessions.infinispan.entities.LoginFailureEntity;
|
import org.keycloak.models.sessions.infinispan.entities.LoginFailureEntity;
|
||||||
import org.keycloak.models.sessions.infinispan.entities.LoginFailureKey;
|
import org.keycloak.models.sessions.infinispan.entities.LoginFailureKey;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
|
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
|
||||||
*/
|
*/
|
||||||
public class UsernameLoginFailureAdapter implements UsernameLoginFailureModel {
|
public class UserLoginFailureAdapter implements UserLoginFailureModel {
|
||||||
|
|
||||||
private InfinispanUserSessionProvider provider;
|
private InfinispanUserSessionProvider provider;
|
||||||
private Cache<LoginFailureKey, LoginFailureEntity> cache;
|
private Cache<LoginFailureKey, LoginFailureEntity> cache;
|
||||||
private LoginFailureKey key;
|
private LoginFailureKey key;
|
||||||
private LoginFailureEntity entity;
|
private LoginFailureEntity entity;
|
||||||
|
|
||||||
public UsernameLoginFailureAdapter(InfinispanUserSessionProvider provider, Cache<LoginFailureKey, LoginFailureEntity> cache, LoginFailureKey key, LoginFailureEntity entity) {
|
public UserLoginFailureAdapter(InfinispanUserSessionProvider provider, Cache<LoginFailureKey, LoginFailureEntity> cache, LoginFailureKey key, LoginFailureEntity entity) {
|
||||||
this.provider = provider;
|
this.provider = provider;
|
||||||
this.cache = cache;
|
this.cache = cache;
|
||||||
this.key = key;
|
this.key = key;
|
||||||
|
@ -40,8 +40,8 @@ public class UsernameLoginFailureAdapter implements UsernameLoginFailureModel {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getUsername() {
|
public String getUserId() {
|
||||||
return entity.getUsername();
|
return entity.getUserId();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
|
@ -24,19 +24,19 @@ import java.io.Serializable;
|
||||||
*/
|
*/
|
||||||
public class LoginFailureEntity implements Serializable {
|
public class LoginFailureEntity implements Serializable {
|
||||||
|
|
||||||
private String username;
|
private String userId;
|
||||||
private String realm;
|
private String realm;
|
||||||
private int failedLoginNotBefore;
|
private int failedLoginNotBefore;
|
||||||
private int numFailures;
|
private int numFailures;
|
||||||
private long lastFailure;
|
private long lastFailure;
|
||||||
private String lastIPFailure;
|
private String lastIPFailure;
|
||||||
|
|
||||||
public String getUsername() {
|
public String getUserId() {
|
||||||
return username;
|
return userId;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setUsername(String username) {
|
public void setUserId(String userId) {
|
||||||
this.username = username;
|
this.userId = userId;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getRealm() {
|
public String getRealm() {
|
||||||
|
|
|
@ -25,11 +25,11 @@ import java.io.Serializable;
|
||||||
public class LoginFailureKey implements Serializable {
|
public class LoginFailureKey implements Serializable {
|
||||||
|
|
||||||
private final String realm;
|
private final String realm;
|
||||||
private final String username;
|
private final String userId;
|
||||||
|
|
||||||
public LoginFailureKey(String realm, String username) {
|
public LoginFailureKey(String realm, String userId) {
|
||||||
this.realm = realm;
|
this.realm = realm;
|
||||||
this.username = username;
|
this.userId = userId;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -40,7 +40,7 @@ public class LoginFailureKey implements Serializable {
|
||||||
LoginFailureKey key = (LoginFailureKey) o;
|
LoginFailureKey key = (LoginFailureKey) o;
|
||||||
|
|
||||||
if (realm != null ? !realm.equals(key.realm) : key.realm != null) return false;
|
if (realm != null ? !realm.equals(key.realm) : key.realm != null) return false;
|
||||||
if (username != null ? !username.equals(key.username) : key.username != null) return false;
|
if (userId != null ? !userId.equals(key.userId) : key.userId != null) return false;
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -48,7 +48,7 @@ public class LoginFailureKey implements Serializable {
|
||||||
@Override
|
@Override
|
||||||
public int hashCode() {
|
public int hashCode() {
|
||||||
int result = realm != null ? realm.hashCode() : 0;
|
int result = realm != null ? realm.hashCode() : 0;
|
||||||
result = 31 * result + (username != null ? username.hashCode() : 0);
|
result = 31 * result + (userId != null ? userId.hashCode() : 0);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -21,9 +21,9 @@ package org.keycloak.models;
|
||||||
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||||
* @version $Revision: 1 $
|
* @version $Revision: 1 $
|
||||||
*/
|
*/
|
||||||
public interface UsernameLoginFailureModel
|
public interface UserLoginFailureModel
|
||||||
{
|
{
|
||||||
String getUsername();
|
String getUserId();
|
||||||
int getFailedLoginNotBefore();
|
int getFailedLoginNotBefore();
|
||||||
void setFailedLoginNotBefore(int notBefore);
|
void setFailedLoginNotBefore(int notBefore);
|
||||||
int getNumFailures();
|
int getNumFailures();
|
|
@ -48,9 +48,9 @@ public interface UserSessionProvider extends Provider {
|
||||||
void removeUserSessions(RealmModel realm);
|
void removeUserSessions(RealmModel realm);
|
||||||
void removeClientSession(RealmModel realm, ClientSessionModel clientSession);
|
void removeClientSession(RealmModel realm, ClientSessionModel clientSession);
|
||||||
|
|
||||||
UsernameLoginFailureModel getUserLoginFailure(RealmModel realm, String username);
|
UserLoginFailureModel getUserLoginFailure(RealmModel realm, String userId);
|
||||||
UsernameLoginFailureModel addUserLoginFailure(RealmModel realm, String username);
|
UserLoginFailureModel addUserLoginFailure(RealmModel realm, String userId);
|
||||||
void removeUserLoginFailure(RealmModel realm, String username);
|
void removeUserLoginFailure(RealmModel realm, String userId);
|
||||||
void removeAllUserLoginFailures(RealmModel realm);
|
void removeAllUserLoginFailures(RealmModel realm);
|
||||||
|
|
||||||
void onRealmRemoved(RealmModel realm);
|
void onRealmRemoved(RealmModel realm);
|
||||||
|
|
|
@ -20,6 +20,7 @@ package org.keycloak.services.managers;
|
||||||
import org.keycloak.common.ClientConnection;
|
import org.keycloak.common.ClientConnection;
|
||||||
import org.keycloak.models.KeycloakSession;
|
import org.keycloak.models.KeycloakSession;
|
||||||
import org.keycloak.models.RealmModel;
|
import org.keycloak.models.RealmModel;
|
||||||
|
import org.keycloak.models.UserModel;
|
||||||
import org.keycloak.provider.Provider;
|
import org.keycloak.provider.Provider;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -27,7 +28,7 @@ import org.keycloak.provider.Provider;
|
||||||
* @version $Revision: 1 $
|
* @version $Revision: 1 $
|
||||||
*/
|
*/
|
||||||
public interface BruteForceProtector extends Provider {
|
public interface BruteForceProtector extends Provider {
|
||||||
void failedLogin(RealmModel realm, String username, ClientConnection clientConnection);
|
void failedLogin(RealmModel realm, UserModel user, ClientConnection clientConnection);
|
||||||
|
|
||||||
boolean isTemporarilyDisabled(KeycloakSession session, RealmModel realm, String username);
|
boolean isTemporarilyDisabled(KeycloakSession session, RealmModel realm, UserModel user);
|
||||||
}
|
}
|
||||||
|
|
|
@ -37,6 +37,7 @@ import org.keycloak.models.RealmModel;
|
||||||
import org.keycloak.models.UserModel;
|
import org.keycloak.models.UserModel;
|
||||||
import org.keycloak.models.UserSessionModel;
|
import org.keycloak.models.UserSessionModel;
|
||||||
import org.keycloak.models.utils.FormMessage;
|
import org.keycloak.models.utils.FormMessage;
|
||||||
|
import org.keycloak.models.utils.KeycloakModelUtils;
|
||||||
import org.keycloak.protocol.LoginProtocol;
|
import org.keycloak.protocol.LoginProtocol;
|
||||||
import org.keycloak.protocol.LoginProtocol.Error;
|
import org.keycloak.protocol.LoginProtocol.Error;
|
||||||
import org.keycloak.protocol.oidc.TokenManager;
|
import org.keycloak.protocol.oidc.TokenManager;
|
||||||
|
@ -543,8 +544,10 @@ public class AuthenticationProcessor {
|
||||||
if (username == null) {
|
if (username == null) {
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
getBruteForceProtector().failedLogin(realm, username, connection);
|
UserModel user = KeycloakModelUtils.findUserByNameOrEmail(session, realm, username);
|
||||||
|
if (user != null) {
|
||||||
|
getBruteForceProtector().failedLogin(realm, user, connection);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -851,7 +854,7 @@ public class AuthenticationProcessor {
|
||||||
if (authenticatedUser == null) return;
|
if (authenticatedUser == null) return;
|
||||||
if (!authenticatedUser.isEnabled()) throw new AuthenticationFlowException(AuthenticationFlowError.USER_DISABLED);
|
if (!authenticatedUser.isEnabled()) throw new AuthenticationFlowException(AuthenticationFlowError.USER_DISABLED);
|
||||||
if (realm.isBruteForceProtected()) {
|
if (realm.isBruteForceProtected()) {
|
||||||
if (getBruteForceProtector().isTemporarilyDisabled(session, realm, authenticatedUser.getUsername())) {
|
if (getBruteForceProtector().isTemporarilyDisabled(session, realm, authenticatedUser)) {
|
||||||
throw new AuthenticationFlowException(AuthenticationFlowError.USER_TEMPORARILY_DISABLED);
|
throw new AuthenticationFlowException(AuthenticationFlowError.USER_TEMPORARILY_DISABLED);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -100,7 +100,7 @@ public abstract class AbstractUsernameFormAuthenticator extends AbstractFormAuth
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (context.getRealm().isBruteForceProtected()) {
|
if (context.getRealm().isBruteForceProtected()) {
|
||||||
if (context.getProtector().isTemporarilyDisabled(context.getSession(), context.getRealm(), user.getUsername())) {
|
if (context.getProtector().isTemporarilyDisabled(context.getSession(), context.getRealm(), user)) {
|
||||||
context.getEvent().user(user);
|
context.getEvent().user(user);
|
||||||
context.getEvent().error(Errors.USER_TEMPORARILY_DISABLED);
|
context.getEvent().error(Errors.USER_TEMPORARILY_DISABLED);
|
||||||
Response challengeResponse = temporarilyDisabledUser(context);
|
Response challengeResponse = temporarilyDisabledUser(context);
|
||||||
|
|
|
@ -84,7 +84,7 @@ public class ValidateUsername extends AbstractDirectGrantAuthenticator {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (context.getRealm().isBruteForceProtected()) {
|
if (context.getRealm().isBruteForceProtected()) {
|
||||||
if (context.getProtector().isTemporarilyDisabled(context.getSession(), context.getRealm(), user.getUsername())) {
|
if (context.getProtector().isTemporarilyDisabled(context.getSession(), context.getRealm(), user)) {
|
||||||
context.getEvent().user(user);
|
context.getEvent().user(user);
|
||||||
context.getEvent().error(Errors.USER_TEMPORARILY_DISABLED);
|
context.getEvent().error(Errors.USER_TEMPORARILY_DISABLED);
|
||||||
Response challengeResponse = errorResponse(Response.Status.BAD_REQUEST.getStatusCode(), "invalid_grant", "Account temporarily disabled");
|
Response challengeResponse = errorResponse(Response.Status.BAD_REQUEST.getStatusCode(), "invalid_grant", "Account temporarily disabled");
|
||||||
|
|
|
@ -22,7 +22,7 @@ import org.keycloak.models.KeycloakSession;
|
||||||
import org.keycloak.models.KeycloakSessionFactory;
|
import org.keycloak.models.KeycloakSessionFactory;
|
||||||
import org.keycloak.models.RealmModel;
|
import org.keycloak.models.RealmModel;
|
||||||
import org.keycloak.models.UserModel;
|
import org.keycloak.models.UserModel;
|
||||||
import org.keycloak.models.UsernameLoginFailureModel;
|
import org.keycloak.models.UserLoginFailureModel;
|
||||||
import org.keycloak.services.ServicesLogger;
|
import org.keycloak.services.ServicesLogger;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
@ -55,18 +55,18 @@ public class DefaultBruteForceProtector implements Runnable, BruteForceProtector
|
||||||
|
|
||||||
protected abstract class LoginEvent implements Comparable<LoginEvent> {
|
protected abstract class LoginEvent implements Comparable<LoginEvent> {
|
||||||
protected final String realmId;
|
protected final String realmId;
|
||||||
protected final String username;
|
protected final String userId;
|
||||||
protected final String ip;
|
protected final String ip;
|
||||||
|
|
||||||
protected LoginEvent(String realmId, String username, String ip) {
|
protected LoginEvent(String realmId, String userId, String ip) {
|
||||||
this.realmId = realmId;
|
this.realmId = realmId;
|
||||||
this.username = username;
|
this.userId = userId;
|
||||||
this.ip = ip;
|
this.ip = ip;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int compareTo(LoginEvent o) {
|
public int compareTo(LoginEvent o) {
|
||||||
return username.compareTo(o.username);
|
return userId.compareTo(o.userId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -79,8 +79,8 @@ public class DefaultBruteForceProtector implements Runnable, BruteForceProtector
|
||||||
protected class FailedLogin extends LoginEvent {
|
protected class FailedLogin extends LoginEvent {
|
||||||
protected final CountDownLatch latch = new CountDownLatch(1);
|
protected final CountDownLatch latch = new CountDownLatch(1);
|
||||||
|
|
||||||
public FailedLogin(String realmId, String username, String ip) {
|
public FailedLogin(String realmId, String userId, String ip) {
|
||||||
super(realmId, username, ip);
|
super(realmId, userId, ip);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -92,11 +92,11 @@ public class DefaultBruteForceProtector implements Runnable, BruteForceProtector
|
||||||
logger.debug("failure");
|
logger.debug("failure");
|
||||||
RealmModel realm = getRealmModel(session, event);
|
RealmModel realm = getRealmModel(session, event);
|
||||||
logFailure(event);
|
logFailure(event);
|
||||||
UserModel user = session.users().getUserByUsername(event.username.toString(), realm);
|
UserModel user = session.users().getUserById(event.userId, realm);
|
||||||
UsernameLoginFailureModel userLoginFailure = getUserModel(session, event);
|
UserLoginFailureModel userLoginFailure = getUserModel(session, event);
|
||||||
if (user != null) {
|
if (user != null) {
|
||||||
if (userLoginFailure == null) {
|
if (userLoginFailure == null) {
|
||||||
userLoginFailure = session.sessions().addUserLoginFailure(realm, event.username.toLowerCase());
|
userLoginFailure = session.sessions().addUserLoginFailure(realm, event.userId);
|
||||||
}
|
}
|
||||||
userLoginFailure.setLastIPFailure(event.ip);
|
userLoginFailure.setLastIPFailure(event.ip);
|
||||||
long currentTime = System.currentTimeMillis();
|
long currentTime = System.currentTimeMillis();
|
||||||
|
@ -135,10 +135,10 @@ public class DefaultBruteForceProtector implements Runnable, BruteForceProtector
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
protected UsernameLoginFailureModel getUserModel(KeycloakSession session, LoginEvent event) {
|
protected UserLoginFailureModel getUserModel(KeycloakSession session, LoginEvent event) {
|
||||||
RealmModel realm = getRealmModel(session, event);
|
RealmModel realm = getRealmModel(session, event);
|
||||||
if (realm == null) return null;
|
if (realm == null) return null;
|
||||||
UsernameLoginFailureModel user = session.sessions().getUserLoginFailure(realm, event.username.toLowerCase());
|
UserLoginFailureModel user = session.sessions().getUserLoginFailure(realm, event.userId);
|
||||||
if (user == null) return null;
|
if (user == null) return null;
|
||||||
return user;
|
return user;
|
||||||
}
|
}
|
||||||
|
@ -212,7 +212,7 @@ public class DefaultBruteForceProtector implements Runnable, BruteForceProtector
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void logFailure(LoginEvent event) {
|
protected void logFailure(LoginEvent event) {
|
||||||
logger.loginFailure(event.username, event.ip);
|
logger.loginFailure(event.userId, event.ip);
|
||||||
failures++;
|
failures++;
|
||||||
long delta = 0;
|
long delta = 0;
|
||||||
if (lastFailure > 0) {
|
if (lastFailure > 0) {
|
||||||
|
@ -227,9 +227,9 @@ public class DefaultBruteForceProtector implements Runnable, BruteForceProtector
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void failedLogin(RealmModel realm, String username, ClientConnection clientConnection) {
|
public void failedLogin(RealmModel realm, UserModel user, ClientConnection clientConnection) {
|
||||||
try {
|
try {
|
||||||
FailedLogin event = new FailedLogin(realm.getId(), username, clientConnection.getRemoteAddr());
|
FailedLogin event = new FailedLogin(realm.getId(), user.getId(), clientConnection.getRemoteAddr());
|
||||||
queue.offer(event);
|
queue.offer(event);
|
||||||
// wait a minimum of seconds for type to process so that a hacker
|
// wait a minimum of seconds for type to process so that a hacker
|
||||||
// cannot flood with failed logins and overwhelm the queue and not have notBefore updated to block next requests
|
// cannot flood with failed logins and overwhelm the queue and not have notBefore updated to block next requests
|
||||||
|
@ -241,17 +241,17 @@ public class DefaultBruteForceProtector implements Runnable, BruteForceProtector
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isTemporarilyDisabled(KeycloakSession session, RealmModel realm, String username) {
|
public boolean isTemporarilyDisabled(KeycloakSession session, RealmModel realm, UserModel user) {
|
||||||
UsernameLoginFailureModel failure = session.sessions().getUserLoginFailure(realm, username.toLowerCase());
|
UserLoginFailureModel failure = session.sessions().getUserLoginFailure(realm, user.getId());
|
||||||
if (failure == null) {
|
|
||||||
return false;
|
if (failure != null) {
|
||||||
|
int currTime = (int) (System.currentTimeMillis() / 1000);
|
||||||
|
if (currTime < failure.getFailedLoginNotBefore()) {
|
||||||
|
logger.debugv("Current: {0} notBefore: {1}", currTime, failure.getFailedLoginNotBefore());
|
||||||
|
return true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int currTime = (int)(System.currentTimeMillis()/1000);
|
|
||||||
if (currTime < failure.getFailedLoginNotBefore()) {
|
|
||||||
logger.debugv("Current: {0} notBefore: {1}", currTime , failure.getFailedLoginNotBefore());
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -21,7 +21,8 @@ import org.keycloak.common.ClientConnection;
|
||||||
import org.keycloak.events.admin.OperationType;
|
import org.keycloak.events.admin.OperationType;
|
||||||
import org.keycloak.models.KeycloakSession;
|
import org.keycloak.models.KeycloakSession;
|
||||||
import org.keycloak.models.RealmModel;
|
import org.keycloak.models.RealmModel;
|
||||||
import org.keycloak.models.UsernameLoginFailureModel;
|
import org.keycloak.models.UserModel;
|
||||||
|
import org.keycloak.models.UserLoginFailureModel;
|
||||||
import org.keycloak.services.ServicesLogger;
|
import org.keycloak.services.ServicesLogger;
|
||||||
import org.keycloak.services.managers.BruteForceProtector;
|
import org.keycloak.services.managers.BruteForceProtector;
|
||||||
|
|
||||||
|
@ -72,14 +73,14 @@ public class AttackDetectionResource {
|
||||||
/**
|
/**
|
||||||
* Get status of a username in brute force detection
|
* Get status of a username in brute force detection
|
||||||
*
|
*
|
||||||
* @param username
|
* @param userId
|
||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
@GET
|
@GET
|
||||||
@Path("brute-force/usernames/{username}")
|
@Path("brute-force/users/{userId}")
|
||||||
@NoCache
|
@NoCache
|
||||||
@Produces(MediaType.APPLICATION_JSON)
|
@Produces(MediaType.APPLICATION_JSON)
|
||||||
public Map<String, Object> bruteForceUserStatus(@PathParam("username") String username) {
|
public Map<String, Object> bruteForceUserStatus(@PathParam("userId") String userId) {
|
||||||
auth.requireView();
|
auth.requireView();
|
||||||
|
|
||||||
Map<String, Object> data = new HashMap<>();
|
Map<String, Object> data = new HashMap<>();
|
||||||
|
@ -89,9 +90,11 @@ public class AttackDetectionResource {
|
||||||
data.put("lastIPFailure", "n/a");
|
data.put("lastIPFailure", "n/a");
|
||||||
if (!realm.isBruteForceProtected()) return data;
|
if (!realm.isBruteForceProtected()) return data;
|
||||||
|
|
||||||
UsernameLoginFailureModel model = session.sessions().getUserLoginFailure(realm, username.toLowerCase());
|
UserModel user = session.users().getUserById(userId, realm);
|
||||||
|
|
||||||
|
UserLoginFailureModel model = session.sessions().getUserLoginFailure(realm, userId);
|
||||||
if (model == null) return data;
|
if (model == null) return data;
|
||||||
if (session.getProvider(BruteForceProtector.class).isTemporarilyDisabled(session, realm, username)) {
|
if (session.getProvider(BruteForceProtector.class).isTemporarilyDisabled(session, realm, user)) {
|
||||||
data.put("disabled", true);
|
data.put("disabled", true);
|
||||||
}
|
}
|
||||||
data.put("numFailures", model.getNumFailures());
|
data.put("numFailures", model.getNumFailures());
|
||||||
|
@ -105,16 +108,16 @@ public class AttackDetectionResource {
|
||||||
*
|
*
|
||||||
* This can release temporary disabled user
|
* This can release temporary disabled user
|
||||||
*
|
*
|
||||||
* @param username
|
* @param userId
|
||||||
*/
|
*/
|
||||||
@Path("brute-force/usernames/{username}")
|
@Path("brute-force/users/{userId}")
|
||||||
@DELETE
|
@DELETE
|
||||||
public void clearBruteForceForUser(@PathParam("username") String username) {
|
public void clearBruteForceForUser(@PathParam("userId") String userId) {
|
||||||
auth.requireManage();
|
auth.requireManage();
|
||||||
|
|
||||||
UsernameLoginFailureModel model = session.sessions().getUserLoginFailure(realm, username.toLowerCase());
|
UserLoginFailureModel model = session.sessions().getUserLoginFailure(realm, userId);
|
||||||
if (model != null) {
|
if (model != null) {
|
||||||
session.sessions().removeUserLoginFailure(realm, username);
|
session.sessions().removeUserLoginFailure(realm, userId);
|
||||||
adminEvent.operation(OperationType.DELETE).resourcePath(uriInfo).success();
|
adminEvent.operation(OperationType.DELETE).resourcePath(uriInfo).success();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -125,7 +128,7 @@ public class AttackDetectionResource {
|
||||||
* This can release temporary disabled users
|
* This can release temporary disabled users
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
@Path("brute-force/usernames")
|
@Path("brute-force/users")
|
||||||
@DELETE
|
@DELETE
|
||||||
public void clearAllBruteForce() {
|
public void clearAllBruteForce() {
|
||||||
auth.requireManage();
|
auth.requireManage();
|
||||||
|
|
|
@ -81,7 +81,6 @@ import javax.ws.rs.core.UriBuilder;
|
||||||
import javax.ws.rs.core.UriInfo;
|
import javax.ws.rs.core.UriInfo;
|
||||||
import javax.ws.rs.WebApplicationException;
|
import javax.ws.rs.WebApplicationException;
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.net.URI;
|
import java.net.URI;
|
||||||
import java.text.MessageFormat;
|
import java.text.MessageFormat;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
@ -94,15 +93,12 @@ import java.util.Map;
|
||||||
import java.util.Properties;
|
import java.util.Properties;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
import org.keycloak.models.UsernameLoginFailureModel;
|
import org.keycloak.models.UserLoginFailureModel;
|
||||||
import org.keycloak.services.managers.BruteForceProtector;
|
import org.keycloak.services.managers.BruteForceProtector;
|
||||||
import org.keycloak.services.managers.UserSessionManager;
|
import org.keycloak.services.managers.UserSessionManager;
|
||||||
import org.keycloak.services.resources.AccountService;
|
import org.keycloak.services.resources.AccountService;
|
||||||
import org.keycloak.common.util.Time;
|
import org.keycloak.common.util.Time;
|
||||||
import org.keycloak.services.validation.Validation;
|
import org.keycloak.services.validation.Validation;
|
||||||
import org.keycloak.theme.Theme;
|
|
||||||
import org.keycloak.theme.Theme.Type;
|
|
||||||
import org.keycloak.theme.ThemeProvider;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Base resource for managing users
|
* Base resource for managing users
|
||||||
|
@ -166,8 +162,8 @@ public class UsersResource {
|
||||||
attrsToRemove = Collections.emptySet();
|
attrsToRemove = Collections.emptySet();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (rep.isEnabled() != null && rep.isEnabled() && rep.getUsername() != null) {
|
if (rep.isEnabled() != null && rep.isEnabled()) {
|
||||||
UsernameLoginFailureModel failureModel = session.sessions().getUserLoginFailure(realm, rep.getUsername().toLowerCase());
|
UserLoginFailureModel failureModel = session.sessions().getUserLoginFailure(realm, id);
|
||||||
if (failureModel != null) {
|
if (failureModel != null) {
|
||||||
failureModel.clearFailures();
|
failureModel.clearFailures();
|
||||||
}
|
}
|
||||||
|
@ -300,7 +296,7 @@ public class UsersResource {
|
||||||
rep.setFederatedIdentities(reps);
|
rep.setFederatedIdentities(reps);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (session.getProvider(BruteForceProtector.class).isTemporarilyDisabled(session, realm, rep.getUsername())) {
|
if (session.getProvider(BruteForceProtector.class).isTemporarilyDisabled(session, realm, user)) {
|
||||||
rep.setEnabled(false);
|
rep.setEnabled(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -52,7 +52,7 @@ public class AttackDetectionResourceTest extends AbstractAdminTest {
|
||||||
public void test() {
|
public void test() {
|
||||||
AttackDetectionResource detection = adminClient.realm("test").attackDetection();
|
AttackDetectionResource detection = adminClient.realm("test").attackDetection();
|
||||||
|
|
||||||
assertBruteForce(detection.bruteForceUserStatus("test-user@localhost"), 0, false, false);
|
assertBruteForce(detection.bruteForceUserStatus(findUser("test-user@localhost").getId()), 0, false, false);
|
||||||
|
|
||||||
oauthClient.doLogin("test-user@localhost", "invalid");
|
oauthClient.doLogin("test-user@localhost", "invalid");
|
||||||
oauthClient.doLogin("test-user@localhost", "invalid");
|
oauthClient.doLogin("test-user@localhost", "invalid");
|
||||||
|
@ -62,21 +62,21 @@ public class AttackDetectionResourceTest extends AbstractAdminTest {
|
||||||
oauthClient.doLogin("test-user2", "invalid");
|
oauthClient.doLogin("test-user2", "invalid");
|
||||||
oauthClient.doLogin("nosuchuser", "invalid");
|
oauthClient.doLogin("nosuchuser", "invalid");
|
||||||
|
|
||||||
assertBruteForce(detection.bruteForceUserStatus("test-user@localhost"), 3, true, true);
|
assertBruteForce(detection.bruteForceUserStatus(findUser("test-user@localhost").getId()), 3, true, true);
|
||||||
assertBruteForce(detection.bruteForceUserStatus("test-user2"), 2, true, true);
|
assertBruteForce(detection.bruteForceUserStatus(findUser("test-user2").getId()), 2, true, true);
|
||||||
assertBruteForce(detection.bruteForceUserStatus("nosuchuser"), 0, false, false);
|
assertBruteForce(detection.bruteForceUserStatus("nosuchuser"), 0, false, false);
|
||||||
|
|
||||||
detection.clearBruteForceForUser("test-user@localhost");
|
detection.clearBruteForceForUser(findUser("test-user@localhost").getId());
|
||||||
assertAdminEvents.assertEvent("test", OperationType.DELETE, AdminEventPaths.attackDetectionClearBruteForceForUserPath("test-user@localhost"));
|
assertAdminEvents.assertEvent("test", OperationType.DELETE, AdminEventPaths.attackDetectionClearBruteForceForUserPath(findUser("test-user@localhost").getId()));
|
||||||
|
|
||||||
assertBruteForce(detection.bruteForceUserStatus("test-user@localhost"), 0, false, false);
|
assertBruteForce(detection.bruteForceUserStatus(findUser("test-user@localhost").getId()), 0, false, false);
|
||||||
assertBruteForce(detection.bruteForceUserStatus("test-user2"), 2, true, true);
|
assertBruteForce(detection.bruteForceUserStatus(findUser("test-user2").getId()), 2, true, true);
|
||||||
|
|
||||||
detection.clearAllBruteForce();
|
detection.clearAllBruteForce();
|
||||||
assertAdminEvents.assertEvent("test", OperationType.DELETE, AdminEventPaths.attackDetectionClearAllBruteForcePath());
|
assertAdminEvents.assertEvent("test", OperationType.DELETE, AdminEventPaths.attackDetectionClearAllBruteForcePath());
|
||||||
|
|
||||||
assertBruteForce(detection.bruteForceUserStatus("test-user@localhost"), 0, false, false);
|
assertBruteForce(detection.bruteForceUserStatus(findUser("test-user@localhost").getId()), 0, false, false);
|
||||||
assertBruteForce(detection.bruteForceUserStatus("test-user2"), 0, false, false);
|
assertBruteForce(detection.bruteForceUserStatus(findUser("test-user2").getId()), 0, false, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void assertBruteForce(Map<String, Object> status, Integer expectedNumFailures, Boolean expectedFailure, Boolean expectedDisabled) {
|
private void assertBruteForce(Map<String, Object> status, Integer expectedNumFailures, Boolean expectedFailure, Boolean expectedDisabled) {
|
||||||
|
|
|
@ -26,24 +26,21 @@ import org.keycloak.models.Constants;
|
||||||
import org.keycloak.models.utils.TimeBasedOTP;
|
import org.keycloak.models.utils.TimeBasedOTP;
|
||||||
import org.keycloak.representations.idm.CredentialRepresentation;
|
import org.keycloak.representations.idm.CredentialRepresentation;
|
||||||
import org.keycloak.testsuite.AssertEvents;
|
import org.keycloak.testsuite.AssertEvents;
|
||||||
|
import org.keycloak.testsuite.AssertEvents.ExpectedEvent;
|
||||||
import org.keycloak.testsuite.pages.AppPage;
|
import org.keycloak.testsuite.pages.AppPage;
|
||||||
import org.keycloak.testsuite.pages.AppPage.RequestType;
|
import org.keycloak.testsuite.pages.AppPage.RequestType;
|
||||||
import org.keycloak.testsuite.pages.LoginPage;
|
import org.keycloak.testsuite.pages.LoginPage;
|
||||||
import org.keycloak.testsuite.pages.LoginTotpPage;
|
import org.keycloak.testsuite.pages.LoginTotpPage;
|
||||||
import org.keycloak.testsuite.pages.RegisterPage;
|
import org.keycloak.testsuite.pages.RegisterPage;
|
||||||
|
|
||||||
import javax.ws.rs.client.Client;
|
|
||||||
import javax.ws.rs.client.ClientBuilder;
|
|
||||||
import javax.ws.rs.core.HttpHeaders;
|
|
||||||
import javax.ws.rs.core.Response;
|
|
||||||
import java.net.MalformedURLException;
|
import java.net.MalformedURLException;
|
||||||
import org.jboss.arquillian.graphene.page.Page;
|
import org.jboss.arquillian.graphene.page.Page;
|
||||||
import org.keycloak.representations.idm.ClientRepresentation;
|
|
||||||
import org.keycloak.representations.idm.RealmRepresentation;
|
import org.keycloak.representations.idm.RealmRepresentation;
|
||||||
import org.keycloak.representations.idm.UserRepresentation;
|
import org.keycloak.representations.idm.UserRepresentation;
|
||||||
import org.keycloak.testsuite.TestRealmKeycloakTest;
|
import org.keycloak.testsuite.TestRealmKeycloakTest;
|
||||||
import org.keycloak.testsuite.util.GreenMailRule;
|
import org.keycloak.testsuite.util.GreenMailRule;
|
||||||
import org.keycloak.testsuite.util.OAuthClient;
|
import org.keycloak.testsuite.util.OAuthClient;
|
||||||
|
import org.keycloak.testsuite.util.UserBuilder;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
|
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
|
||||||
|
@ -64,6 +61,8 @@ public class BruteForceTest extends TestRealmKeycloakTest {
|
||||||
testRealm.setFailureFactor(2);
|
testRealm.setFailureFactor(2);
|
||||||
|
|
||||||
findClientInRealmRep(testRealm, "test-app").setDirectAccessGrantsEnabled(true);
|
findClientInRealmRep(testRealm, "test-app").setDirectAccessGrantsEnabled(true);
|
||||||
|
|
||||||
|
testRealm.getUsers().add(UserBuilder.create().username("user2").email("user2@localhost").password("password").build());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Before
|
@Before
|
||||||
|
@ -110,33 +109,11 @@ public class BruteForceTest extends TestRealmKeycloakTest {
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void clearUserFailures() throws Exception {
|
protected void clearUserFailures() throws Exception {
|
||||||
String token = getAdminToken();
|
adminClient.realm("test").attackDetection().clearBruteForceForUser(findUser("test-user@localhost").getId());
|
||||||
Client client = ClientBuilder.newClient();
|
|
||||||
Response response = client.target(AppPage.AUTH_SERVER_URL)
|
|
||||||
.path("admin/realms/test/attack-detection/brute-force/usernames/test-user@localhost")
|
|
||||||
.request()
|
|
||||||
.header(HttpHeaders.AUTHORIZATION, "Bearer " + token)
|
|
||||||
.delete();
|
|
||||||
Assert.assertEquals(204, response.getStatus());
|
|
||||||
response.close();
|
|
||||||
client.close();
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void clearAllUserFailures() throws Exception {
|
protected void clearAllUserFailures() throws Exception {
|
||||||
String token = getAdminToken();
|
adminClient.realm("test").attackDetection().clearAllBruteForce();
|
||||||
Client client = ClientBuilder.newClient();
|
|
||||||
Response response = client.target(AppPage.AUTH_SERVER_URL)
|
|
||||||
.path("admin/realms/test/attack-detection/brute-force/usernames")
|
|
||||||
.request()
|
|
||||||
.header(HttpHeaders.AUTHORIZATION, "Bearer " + token)
|
|
||||||
.delete();
|
|
||||||
Assert.assertEquals(204, response.getStatus());
|
|
||||||
response.close();
|
|
||||||
client.close();
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -292,6 +269,17 @@ public class BruteForceTest extends TestRealmKeycloakTest {
|
||||||
clearAllUserFailures();
|
clearAllUserFailures();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testEmail() throws Exception {
|
||||||
|
String userId = adminClient.realm("test").users().search("user2", null, null, null, 0, 1).get(0).getId();
|
||||||
|
|
||||||
|
loginSuccess("user2@localhost");
|
||||||
|
loginInvalidPassword("user2@localhost");
|
||||||
|
loginInvalidPassword("user2@localhost");
|
||||||
|
expectTemporarilyDisabled("user2@localhost", userId);
|
||||||
|
clearAllUserFailures();
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testBrowserMissingPassword() throws Exception {
|
public void testBrowserMissingPassword() throws Exception {
|
||||||
loginSuccess();
|
loginSuccess();
|
||||||
|
@ -334,20 +322,25 @@ public class BruteForceTest extends TestRealmKeycloakTest {
|
||||||
}
|
}
|
||||||
|
|
||||||
public void expectTemporarilyDisabled() throws Exception {
|
public void expectTemporarilyDisabled() throws Exception {
|
||||||
expectTemporarilyDisabled("test-user@localhost");
|
expectTemporarilyDisabled("test-user@localhost", null);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void expectTemporarilyDisabled(String username) throws Exception {
|
public void expectTemporarilyDisabled(String username, String userId) throws Exception {
|
||||||
loginPage.open();
|
loginPage.open();
|
||||||
loginPage.login(username, "password");
|
loginPage.login(username, "password");
|
||||||
|
|
||||||
loginPage.assertCurrent();
|
loginPage.assertCurrent();
|
||||||
String src = driver.getPageSource();
|
String src = driver.getPageSource();
|
||||||
Assert.assertEquals("Invalid username or password.", loginPage.getError());
|
Assert.assertEquals("Invalid username or password.", loginPage.getError());
|
||||||
events.expectLogin().session((String) null).error(Errors.USER_TEMPORARILY_DISABLED)
|
ExpectedEvent event = events.expectLogin()
|
||||||
.detail(Details.USERNAME, "test-user@localhost")
|
.session((String) null)
|
||||||
.removeDetail(Details.CONSENT)
|
.error(Errors.USER_TEMPORARILY_DISABLED)
|
||||||
.assertEvent();
|
.detail(Details.USERNAME, username)
|
||||||
|
.removeDetail(Details.CONSENT);
|
||||||
|
if(userId != null) {
|
||||||
|
event.user(userId);
|
||||||
|
}
|
||||||
|
event.assertEvent();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void loginSuccess() throws Exception {
|
public void loginSuccess() throws Exception {
|
||||||
|
|
|
@ -28,7 +28,7 @@ import org.keycloak.models.KeycloakSession;
|
||||||
import org.keycloak.models.RealmModel;
|
import org.keycloak.models.RealmModel;
|
||||||
import org.keycloak.models.UserModel;
|
import org.keycloak.models.UserModel;
|
||||||
import org.keycloak.models.UserSessionModel;
|
import org.keycloak.models.UserSessionModel;
|
||||||
import org.keycloak.models.UsernameLoginFailureModel;
|
import org.keycloak.models.UserLoginFailureModel;
|
||||||
import org.keycloak.protocol.oidc.OIDCLoginProtocol;
|
import org.keycloak.protocol.oidc.OIDCLoginProtocol;
|
||||||
import org.keycloak.services.managers.UserManager;
|
import org.keycloak.services.managers.UserManager;
|
||||||
import org.keycloak.testsuite.rule.KeycloakRule;
|
import org.keycloak.testsuite.rule.KeycloakRule;
|
||||||
|
@ -471,10 +471,10 @@ public class UserSessionProviderTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void loginFailures() {
|
public void loginFailures() {
|
||||||
UsernameLoginFailureModel failure1 = session.sessions().addUserLoginFailure(realm, "user1");
|
UserLoginFailureModel failure1 = session.sessions().addUserLoginFailure(realm, "user1");
|
||||||
failure1.incrementFailures();
|
failure1.incrementFailures();
|
||||||
|
|
||||||
UsernameLoginFailureModel failure2 = session.sessions().addUserLoginFailure(realm, "user2");
|
UserLoginFailureModel failure2 = session.sessions().addUserLoginFailure(realm, "user2");
|
||||||
failure2.incrementFailures();
|
failure2.incrementFailures();
|
||||||
failure2.incrementFailures();
|
failure2.incrementFailures();
|
||||||
|
|
||||||
|
|
|
@ -365,7 +365,7 @@ module.controller('UserDetailCtrl', function($scope, realm, user, BruteForceUser
|
||||||
console.log('realm brute force? ' + realm.bruteForceProtected)
|
console.log('realm brute force? ' + realm.bruteForceProtected)
|
||||||
$scope.temporarilyDisabled = false;
|
$scope.temporarilyDisabled = false;
|
||||||
var isDisabled = function () {
|
var isDisabled = function () {
|
||||||
BruteForceUser.get({realm: realm.realm, username: user.username}, function(data) {
|
BruteForceUser.get({realm: realm.realm, userId: user.id}, function(data) {
|
||||||
console.log('here in isDisabled ' + data.disabled);
|
console.log('here in isDisabled ' + data.disabled);
|
||||||
$scope.temporarilyDisabled = data.disabled;
|
$scope.temporarilyDisabled = data.disabled;
|
||||||
});
|
});
|
||||||
|
@ -375,7 +375,7 @@ module.controller('UserDetailCtrl', function($scope, realm, user, BruteForceUser
|
||||||
isDisabled();
|
isDisabled();
|
||||||
|
|
||||||
$scope.unlockUser = function() {
|
$scope.unlockUser = function() {
|
||||||
BruteForceUser.delete({realm: realm.realm, username: user.username}, function(data) {
|
BruteForceUser.delete({realm: realm.realm, userId: user.id}, function(data) {
|
||||||
isDisabled();
|
isDisabled();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -227,15 +227,15 @@ module.factory('RealmAdminEvents', function($resource) {
|
||||||
});
|
});
|
||||||
|
|
||||||
module.factory('BruteForce', function($resource) {
|
module.factory('BruteForce', function($resource) {
|
||||||
return $resource(authUrl + '/admin/realms/:realm/attack-detection/brute-force/usernames', {
|
return $resource(authUrl + '/admin/realms/:realm/attack-detection/brute-force/users', {
|
||||||
realm : '@realm'
|
realm : '@realm'
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
module.factory('BruteForceUser', function($resource) {
|
module.factory('BruteForceUser', function($resource) {
|
||||||
return $resource(authUrl + '/admin/realms/:realm/attack-detection/brute-force/usernames/:username', {
|
return $resource(authUrl + '/admin/realms/:realm/attack-detection/brute-force/users/:userId', {
|
||||||
realm : '@realm',
|
realm : '@realm',
|
||||||
username : '@username'
|
userId : '@userId'
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue