commit
1b4b537043
53 changed files with 659 additions and 255 deletions
|
@ -75,7 +75,7 @@ public class Audit {
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Audit event(String e) {
|
public Audit event(EventType e) {
|
||||||
event.setEvent(e);
|
event.setEvent(e);
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
@ -108,6 +108,7 @@ public class Audit {
|
||||||
}
|
}
|
||||||
|
|
||||||
public void error(String error) {
|
public void error(String error) {
|
||||||
|
event.setEvent(EventType.valueOf(event.getEvent().name() + "_ERROR"));
|
||||||
event.setError(error);
|
event.setError(error);
|
||||||
send();
|
send();
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,7 +10,7 @@ public class Event {
|
||||||
|
|
||||||
private long time;
|
private long time;
|
||||||
|
|
||||||
private String event;
|
private EventType event;
|
||||||
|
|
||||||
private String realmId;
|
private String realmId;
|
||||||
|
|
||||||
|
@ -34,11 +34,11 @@ public class Event {
|
||||||
this.time = time;
|
this.time = time;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getEvent() {
|
public EventType getEvent() {
|
||||||
return event;
|
return event;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setEvent(String event) {
|
public void setEvent(EventType event) {
|
||||||
this.event = event;
|
this.event = event;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -7,7 +7,7 @@ import java.util.List;
|
||||||
*/
|
*/
|
||||||
public interface EventQuery {
|
public interface EventQuery {
|
||||||
|
|
||||||
public EventQuery event(String... events);
|
public EventQuery event(EventType... events);
|
||||||
|
|
||||||
public EventQuery realm(String realmId);
|
public EventQuery realm(String realmId);
|
||||||
|
|
||||||
|
|
42
audit/api/src/main/java/org/keycloak/audit/EventType.java
Normal file
42
audit/api/src/main/java/org/keycloak/audit/EventType.java
Normal file
|
@ -0,0 +1,42 @@
|
||||||
|
package org.keycloak.audit;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
|
||||||
|
*/
|
||||||
|
public enum EventType {
|
||||||
|
|
||||||
|
LOGIN,
|
||||||
|
LOGIN_ERROR,
|
||||||
|
REGISTER,
|
||||||
|
REGISTER_ERROR,
|
||||||
|
LOGOUT,
|
||||||
|
LOGOUT_ERROR,
|
||||||
|
CODE_TO_TOKEN,
|
||||||
|
CODE_TO_TOKEN_ERROR,
|
||||||
|
REFRESH_TOKEN,
|
||||||
|
REFRESH_TOKEN_ERROR,
|
||||||
|
SOCIAL_LINK,
|
||||||
|
SOCIAL_LINK_ERROR,
|
||||||
|
REMOVE_SOCIAL_LINK,
|
||||||
|
REMOVE_SOCIAL_LINK_ERROR,
|
||||||
|
|
||||||
|
UPDATE_EMAIL,
|
||||||
|
UPDATE_EMAIL_ERROR,
|
||||||
|
UPDATE_PROFILE,
|
||||||
|
UPDATE_PROFILE_ERROR,
|
||||||
|
UPDATE_PASSWORD,
|
||||||
|
UPDATE_PASSWORD_ERROR,
|
||||||
|
UPDATE_TOTP,
|
||||||
|
UPDATE_TOTP_ERROR,
|
||||||
|
VERIFY_EMAIL,
|
||||||
|
VERIFY_EMAIL_ERROR,
|
||||||
|
|
||||||
|
REMOVE_TOTP,
|
||||||
|
REMOVE_TOTP_ERROR,
|
||||||
|
|
||||||
|
SEND_VERIFY_EMAIL,
|
||||||
|
SEND_VERIFY_EMAIL_ERROR,
|
||||||
|
SEND_RESET_PASSWORD,
|
||||||
|
SEND_RESET_PASSWORD_ERROR
|
||||||
|
|
||||||
|
}
|
|
@ -1,29 +0,0 @@
|
||||||
package org.keycloak.audit;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
|
|
||||||
*/
|
|
||||||
public interface Events {
|
|
||||||
|
|
||||||
String LOGIN = "login";
|
|
||||||
String REGISTER = "register";
|
|
||||||
String LOGOUT = "logout";
|
|
||||||
String CODE_TO_TOKEN = "code_to_token";
|
|
||||||
String REFRESH_TOKEN = "refresh_token";
|
|
||||||
|
|
||||||
String SOCIAL_LINK = "social_link";
|
|
||||||
String REMOVE_SOCIAL_LINK = "remove_social_link";
|
|
||||||
|
|
||||||
String UPDATE_EMAIL = "update_email";
|
|
||||||
String UPDATE_PROFILE = "update_profile";
|
|
||||||
String UPDATE_PASSWORD = "update_password";
|
|
||||||
String UPDATE_TOTP = "update_totp";
|
|
||||||
|
|
||||||
String VERIFY_EMAIL = "verify_email";
|
|
||||||
|
|
||||||
String REMOVE_TOTP = "remove_totp";
|
|
||||||
|
|
||||||
String SEND_VERIFY_EMAIL = "send_verify_email";
|
|
||||||
String SEND_RESET_PASSWORD = "send_reset_password";
|
|
||||||
|
|
||||||
}
|
|
52
audit/email/pom.xml
Executable file
52
audit/email/pom.xml
Executable file
|
@ -0,0 +1,52 @@
|
||||||
|
<?xml version="1.0"?>
|
||||||
|
<project>
|
||||||
|
<parent>
|
||||||
|
<artifactId>keycloak-audit-parent</artifactId>
|
||||||
|
<groupId>org.keycloak</groupId>
|
||||||
|
<version>1.0-beta-1-SNAPSHOT</version>
|
||||||
|
</parent>
|
||||||
|
|
||||||
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
|
||||||
|
<artifactId>keycloak-audit-email</artifactId>
|
||||||
|
<name>Keycloak Audit Email Provider</name>
|
||||||
|
<description/>
|
||||||
|
|
||||||
|
<dependencies>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.keycloak</groupId>
|
||||||
|
<artifactId>keycloak-core</artifactId>
|
||||||
|
<version>${project.version}</version>
|
||||||
|
<scope>provided</scope>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.keycloak</groupId>
|
||||||
|
<artifactId>keycloak-model-api</artifactId>
|
||||||
|
<version>${project.version}</version>
|
||||||
|
<scope>provided</scope>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.keycloak</groupId>
|
||||||
|
<artifactId>keycloak-audit-api</artifactId>
|
||||||
|
<version>${project.version}</version>
|
||||||
|
<scope>provided</scope>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.keycloak</groupId>
|
||||||
|
<artifactId>keycloak-email-api</artifactId>
|
||||||
|
<version>${project.version}</version>
|
||||||
|
<scope>provided</scope>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.jboss.logging</groupId>
|
||||||
|
<artifactId>jboss-logging</artifactId>
|
||||||
|
<scope>provided</scope>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>junit</groupId>
|
||||||
|
<artifactId>junit</artifactId>
|
||||||
|
<scope>test</scope>
|
||||||
|
</dependency>
|
||||||
|
</dependencies>
|
||||||
|
|
||||||
|
</project>
|
|
@ -0,0 +1,57 @@
|
||||||
|
package org.keycloak.audit.email;
|
||||||
|
|
||||||
|
import org.jboss.logging.Logger;
|
||||||
|
import org.keycloak.audit.AuditListener;
|
||||||
|
import org.keycloak.audit.Event;
|
||||||
|
import org.keycloak.audit.EventType;
|
||||||
|
import org.keycloak.email.EmailException;
|
||||||
|
import org.keycloak.email.EmailProvider;
|
||||||
|
import org.keycloak.models.KeycloakSession;
|
||||||
|
import org.keycloak.models.RealmModel;
|
||||||
|
import org.keycloak.models.UserModel;
|
||||||
|
|
||||||
|
import java.util.Date;
|
||||||
|
import java.util.LinkedList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
|
||||||
|
*/
|
||||||
|
public class EmailAuditListener implements AuditListener {
|
||||||
|
|
||||||
|
private static final Logger log = Logger.getLogger(EmailAuditListener.class);
|
||||||
|
|
||||||
|
private KeycloakSession keycloakSession;
|
||||||
|
private EmailProvider emailProvider;
|
||||||
|
private Set<EventType> includedEvents;
|
||||||
|
|
||||||
|
public EmailAuditListener(KeycloakSession keycloakSession, EmailProvider emailProvider, Set<EventType> includedEvents) {
|
||||||
|
this.keycloakSession = keycloakSession;
|
||||||
|
this.emailProvider = emailProvider;
|
||||||
|
this.includedEvents = includedEvents;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onEvent(Event event) {
|
||||||
|
if (includedEvents.contains(event.getEvent())) {
|
||||||
|
if (event.getRealmId() != null && event.getUserId() != null) {
|
||||||
|
RealmModel realm = keycloakSession.getRealm(event.getRealmId());
|
||||||
|
UserModel user = realm.getUserById(event.getUserId());
|
||||||
|
if (user != null && user.getEmail() != null && user.isEmailVerified()) {
|
||||||
|
try {
|
||||||
|
emailProvider.setRealm(realm).setUser(user).sendEvent(event);
|
||||||
|
} catch (EmailException e) {
|
||||||
|
log.error("Failed to send event mail", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void close() {
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,62 @@
|
||||||
|
package org.keycloak.audit.email;
|
||||||
|
|
||||||
|
import org.keycloak.Config;
|
||||||
|
import org.keycloak.audit.AuditListener;
|
||||||
|
import org.keycloak.audit.AuditListenerFactory;
|
||||||
|
import org.keycloak.audit.EventType;
|
||||||
|
import org.keycloak.email.EmailProvider;
|
||||||
|
import org.keycloak.models.KeycloakSession;
|
||||||
|
import org.keycloak.provider.ProviderSession;
|
||||||
|
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
|
||||||
|
*/
|
||||||
|
public class EmailAuditListenerFactory implements AuditListenerFactory {
|
||||||
|
|
||||||
|
private static final Set<EventType> SUPPORTED_EVENTS = new HashSet<EventType>();
|
||||||
|
static {
|
||||||
|
Collections.addAll(SUPPORTED_EVENTS, EventType.LOGIN_ERROR, EventType.UPDATE_PASSWORD, EventType.REMOVE_TOTP, EventType.UPDATE_TOTP);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Set<EventType> includedEvents = new HashSet<EventType>();
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public AuditListener create(ProviderSession providerSession) {
|
||||||
|
KeycloakSession keycloakSession = providerSession.getProvider(KeycloakSession.class);
|
||||||
|
EmailProvider emailProvider = providerSession.getProvider(EmailProvider.class);
|
||||||
|
return new EmailAuditListener(keycloakSession, emailProvider, includedEvents);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void init(Config.Scope config) {
|
||||||
|
String[] include = config.getArray("include-events");
|
||||||
|
if (include != null) {
|
||||||
|
for (String i : include) {
|
||||||
|
includedEvents.add(EventType.valueOf(i.toUpperCase()));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
includedEvents.addAll(SUPPORTED_EVENTS);
|
||||||
|
}
|
||||||
|
|
||||||
|
String[] exclude = config.getArray("exclude-events");
|
||||||
|
if (exclude != null) {
|
||||||
|
for (String e : exclude) {
|
||||||
|
includedEvents.remove(EventType.valueOf(e.toUpperCase()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void close() {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getId() {
|
||||||
|
return "email";
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1 @@
|
||||||
|
org.keycloak.audit.email.EmailAuditListenerFactory
|
|
@ -6,11 +6,13 @@ import org.jboss.logging.Logger;
|
||||||
import org.keycloak.audit.AuditProvider;
|
import org.keycloak.audit.AuditProvider;
|
||||||
import org.keycloak.audit.Event;
|
import org.keycloak.audit.Event;
|
||||||
import org.keycloak.audit.EventQuery;
|
import org.keycloak.audit.EventQuery;
|
||||||
|
import org.keycloak.audit.EventType;
|
||||||
|
|
||||||
import javax.persistence.EntityManager;
|
import javax.persistence.EntityManager;
|
||||||
import javax.persistence.EntityTransaction;
|
import javax.persistence.EntityTransaction;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.Set;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -25,9 +27,11 @@ public class JpaAuditProvider implements AuditProvider {
|
||||||
|
|
||||||
private EntityManager em;
|
private EntityManager em;
|
||||||
private EntityTransaction tx;
|
private EntityTransaction tx;
|
||||||
|
private Set<EventType> includedEvents;
|
||||||
|
|
||||||
public JpaAuditProvider(EntityManager em) {
|
public JpaAuditProvider(EntityManager em, Set<EventType> includedEvents) {
|
||||||
this.em = em;
|
this.em = em;
|
||||||
|
this.includedEvents = includedEvents;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -55,8 +59,10 @@ public class JpaAuditProvider implements AuditProvider {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onEvent(Event event) {
|
public void onEvent(Event event) {
|
||||||
beginTx();
|
if (includedEvents.contains(event.getEvent())) {
|
||||||
em.persist(convert(event));
|
beginTx();
|
||||||
|
em.persist(convert(event));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -79,7 +85,7 @@ public class JpaAuditProvider implements AuditProvider {
|
||||||
EventEntity e = new EventEntity();
|
EventEntity e = new EventEntity();
|
||||||
e.setId(UUID.randomUUID().toString());
|
e.setId(UUID.randomUUID().toString());
|
||||||
e.setTime(o.getTime());
|
e.setTime(o.getTime());
|
||||||
e.setEvent(o.getEvent());
|
e.setEvent(o.getEvent().toString());
|
||||||
e.setRealmId(o.getRealmId());
|
e.setRealmId(o.getRealmId());
|
||||||
e.setClientId(o.getClientId());
|
e.setClientId(o.getClientId());
|
||||||
e.setUserId(o.getUserId());
|
e.setUserId(o.getUserId());
|
||||||
|
@ -97,7 +103,7 @@ public class JpaAuditProvider implements AuditProvider {
|
||||||
static Event convert(EventEntity o) {
|
static Event convert(EventEntity o) {
|
||||||
Event e = new Event();
|
Event e = new Event();
|
||||||
e.setTime(o.getTime());
|
e.setTime(o.getTime());
|
||||||
e.setEvent(o.getEvent());
|
e.setEvent(EventType.valueOf(o.getEvent()));
|
||||||
e.setRealmId(o.getRealmId());
|
e.setRealmId(o.getRealmId());
|
||||||
e.setClientId(o.getClientId());
|
e.setClientId(o.getClientId());
|
||||||
e.setUserId(o.getUserId());
|
e.setUserId(o.getUserId());
|
||||||
|
|
|
@ -3,10 +3,13 @@ package org.keycloak.audit.jpa;
|
||||||
import org.keycloak.Config;
|
import org.keycloak.Config;
|
||||||
import org.keycloak.audit.AuditProvider;
|
import org.keycloak.audit.AuditProvider;
|
||||||
import org.keycloak.audit.AuditProviderFactory;
|
import org.keycloak.audit.AuditProviderFactory;
|
||||||
|
import org.keycloak.audit.EventType;
|
||||||
import org.keycloak.provider.ProviderSession;
|
import org.keycloak.provider.ProviderSession;
|
||||||
|
|
||||||
import javax.persistence.EntityManagerFactory;
|
import javax.persistence.EntityManagerFactory;
|
||||||
import javax.persistence.Persistence;
|
import javax.persistence.Persistence;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
|
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
|
||||||
|
@ -16,14 +19,34 @@ public class JpaAuditProviderFactory implements AuditProviderFactory {
|
||||||
public static final String ID = "jpa";
|
public static final String ID = "jpa";
|
||||||
private EntityManagerFactory emf;
|
private EntityManagerFactory emf;
|
||||||
|
|
||||||
|
private Set<EventType> includedEvents = new HashSet<EventType>();
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public AuditProvider create(ProviderSession providerSession) {
|
public AuditProvider create(ProviderSession providerSession) {
|
||||||
return new JpaAuditProvider(emf.createEntityManager());
|
return new JpaAuditProvider(emf.createEntityManager(), includedEvents);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void init(Config.Scope config) {
|
public void init(Config.Scope config) {
|
||||||
emf = Persistence.createEntityManagerFactory("jpa-keycloak-audit-store");
|
emf = Persistence.createEntityManagerFactory("jpa-keycloak-audit-store");
|
||||||
|
|
||||||
|
String[] include = config.getArray("include-events");
|
||||||
|
if (include != null) {
|
||||||
|
for (String i : include) {
|
||||||
|
includedEvents.add(EventType.valueOf(i.toUpperCase()));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
for (EventType i : EventType.values()) {
|
||||||
|
includedEvents.add(i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
String[] exclude = config.getArray("exclude-events");
|
||||||
|
if (exclude != null) {
|
||||||
|
for (String e : exclude) {
|
||||||
|
includedEvents.remove(EventType.valueOf(e.toUpperCase()));
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -2,6 +2,7 @@ package org.keycloak.audit.jpa;
|
||||||
|
|
||||||
import org.keycloak.audit.Event;
|
import org.keycloak.audit.Event;
|
||||||
import org.keycloak.audit.EventQuery;
|
import org.keycloak.audit.EventQuery;
|
||||||
|
import org.keycloak.audit.EventType;
|
||||||
|
|
||||||
import javax.persistence.EntityManager;
|
import javax.persistence.EntityManager;
|
||||||
import javax.persistence.TypedQuery;
|
import javax.persistence.TypedQuery;
|
||||||
|
@ -36,8 +37,12 @@ public class JpaEventQuery implements EventQuery {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public EventQuery event(String... events) {
|
public EventQuery event(EventType... events) {
|
||||||
predicates.add(root.get("event").in(events));
|
List<String> eventStrings = new LinkedList<String>();
|
||||||
|
for (EventType e : events) {
|
||||||
|
eventStrings.add(e.toString());
|
||||||
|
}
|
||||||
|
predicates.add(root.get("event").in(eventStrings));
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -6,9 +6,11 @@ import com.mongodb.DBObject;
|
||||||
import org.keycloak.audit.AuditProvider;
|
import org.keycloak.audit.AuditProvider;
|
||||||
import org.keycloak.audit.Event;
|
import org.keycloak.audit.Event;
|
||||||
import org.keycloak.audit.EventQuery;
|
import org.keycloak.audit.EventQuery;
|
||||||
|
import org.keycloak.audit.EventType;
|
||||||
|
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
|
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
|
||||||
|
@ -16,9 +18,11 @@ import java.util.Map;
|
||||||
public class MongoAuditProvider implements AuditProvider {
|
public class MongoAuditProvider implements AuditProvider {
|
||||||
|
|
||||||
private DBCollection audit;
|
private DBCollection audit;
|
||||||
|
private Set<EventType> includedEvents;
|
||||||
|
|
||||||
public MongoAuditProvider(DBCollection audit) {
|
public MongoAuditProvider(DBCollection audit, Set<EventType> includedEvents) {
|
||||||
this.audit = audit;
|
this.audit = audit;
|
||||||
|
this.includedEvents = includedEvents;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -46,7 +50,9 @@ public class MongoAuditProvider implements AuditProvider {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onEvent(Event event) {
|
public void onEvent(Event event) {
|
||||||
audit.insert(convert(event));
|
if (includedEvents.contains(event.getEvent())) {
|
||||||
|
audit.insert(convert(event));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -56,7 +62,7 @@ public class MongoAuditProvider implements AuditProvider {
|
||||||
static DBObject convert(Event o) {
|
static DBObject convert(Event o) {
|
||||||
BasicDBObject e = new BasicDBObject();
|
BasicDBObject e = new BasicDBObject();
|
||||||
e.put("time", o.getTime());
|
e.put("time", o.getTime());
|
||||||
e.put("event", o.getEvent());
|
e.put("event", o.getEvent().toString());
|
||||||
e.put("realmId", o.getRealmId());
|
e.put("realmId", o.getRealmId());
|
||||||
e.put("clientId", o.getClientId());
|
e.put("clientId", o.getClientId());
|
||||||
e.put("userId", o.getUserId());
|
e.put("userId", o.getUserId());
|
||||||
|
@ -78,7 +84,7 @@ public class MongoAuditProvider implements AuditProvider {
|
||||||
static Event convert(BasicDBObject o) {
|
static Event convert(BasicDBObject o) {
|
||||||
Event e = new Event();
|
Event e = new Event();
|
||||||
e.setTime(o.getLong("time"));
|
e.setTime(o.getLong("time"));
|
||||||
e.setEvent(o.getString("event"));
|
e.setEvent(EventType.valueOf(o.getString("event")));
|
||||||
e.setRealmId(o.getString("realmId"));
|
e.setRealmId(o.getString("realmId"));
|
||||||
e.setClientId(o.getString("clientId"));
|
e.setClientId(o.getString("clientId"));
|
||||||
e.setUserId(o.getString("userId"));
|
e.setUserId(o.getString("userId"));
|
||||||
|
|
|
@ -8,10 +8,13 @@ import com.mongodb.WriteConcern;
|
||||||
import org.keycloak.Config;
|
import org.keycloak.Config;
|
||||||
import org.keycloak.audit.AuditProvider;
|
import org.keycloak.audit.AuditProvider;
|
||||||
import org.keycloak.audit.AuditProviderFactory;
|
import org.keycloak.audit.AuditProviderFactory;
|
||||||
|
import org.keycloak.audit.EventType;
|
||||||
import org.keycloak.provider.ProviderSession;
|
import org.keycloak.provider.ProviderSession;
|
||||||
|
|
||||||
import java.net.UnknownHostException;
|
import java.net.UnknownHostException;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
|
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
|
||||||
|
@ -22,9 +25,11 @@ public class MongoAuditProviderFactory implements AuditProviderFactory {
|
||||||
private MongoClient client;
|
private MongoClient client;
|
||||||
private DB db;
|
private DB db;
|
||||||
|
|
||||||
|
private Set<EventType> includedEvents = new HashSet<EventType>();
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public AuditProvider create(ProviderSession providerSession) {
|
public AuditProvider create(ProviderSession providerSession) {
|
||||||
return new MongoAuditProvider(db.getCollection("audit"));
|
return new MongoAuditProvider(db.getCollection("audit"), includedEvents);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -53,6 +58,24 @@ public class MongoAuditProviderFactory implements AuditProviderFactory {
|
||||||
} catch (UnknownHostException e) {
|
} catch (UnknownHostException e) {
|
||||||
throw new RuntimeException(e);
|
throw new RuntimeException(e);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
String[] include = config.getArray("include-events");
|
||||||
|
if (include != null) {
|
||||||
|
for (String i : include) {
|
||||||
|
includedEvents.add(EventType.valueOf(i.toUpperCase()));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
for (EventType i : EventType.values()) {
|
||||||
|
includedEvents.add(i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
String[] exclude = config.getArray("exclude-events");
|
||||||
|
if (exclude != null) {
|
||||||
|
for (String e : exclude) {
|
||||||
|
includedEvents.remove(EventType.valueOf(e.toUpperCase()));
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -5,9 +5,11 @@ import com.mongodb.DBCollection;
|
||||||
import com.mongodb.DBCursor;
|
import com.mongodb.DBCursor;
|
||||||
import org.keycloak.audit.Event;
|
import org.keycloak.audit.Event;
|
||||||
import org.keycloak.audit.EventQuery;
|
import org.keycloak.audit.EventQuery;
|
||||||
|
import org.keycloak.audit.EventType;
|
||||||
|
|
||||||
import java.util.LinkedList;
|
import java.util.LinkedList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
|
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
|
||||||
|
@ -25,8 +27,12 @@ public class MongoEventQuery implements EventQuery {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public EventQuery event(String... events) {
|
public EventQuery event(EventType... events) {
|
||||||
query.put("event", new BasicDBObject("$in", events));
|
List<String> eventStrings = new LinkedList<String>();
|
||||||
|
for (EventType e : events) {
|
||||||
|
eventStrings.add(e.toString());
|
||||||
|
}
|
||||||
|
query.put("event", new BasicDBObject("$in", eventStrings));
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -17,6 +17,7 @@
|
||||||
|
|
||||||
<modules>
|
<modules>
|
||||||
<module>api</module>
|
<module>api</module>
|
||||||
|
<module>email</module>
|
||||||
<module>jpa</module>
|
<module>jpa</module>
|
||||||
<module>jboss-logging</module>
|
<module>jboss-logging</module>
|
||||||
<module>mongo</module>
|
<module>mongo</module>
|
||||||
|
|
|
@ -8,6 +8,7 @@ import org.keycloak.Config;
|
||||||
import org.keycloak.audit.AuditProvider;
|
import org.keycloak.audit.AuditProvider;
|
||||||
import org.keycloak.audit.AuditProviderFactory;
|
import org.keycloak.audit.AuditProviderFactory;
|
||||||
import org.keycloak.audit.Event;
|
import org.keycloak.audit.Event;
|
||||||
|
import org.keycloak.audit.EventType;
|
||||||
import org.keycloak.provider.ProviderFactory;
|
import org.keycloak.provider.ProviderFactory;
|
||||||
|
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
|
@ -47,7 +48,7 @@ public abstract class AbstractAuditProviderTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void save() {
|
public void save() {
|
||||||
provider.onEvent(create("event", "realmId", "clientId", "userId", "127.0.0.1", "error"));
|
provider.onEvent(create(EventType.LOGIN, "realmId", "clientId", "userId", "127.0.0.1", "error"));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -55,23 +56,23 @@ public abstract class AbstractAuditProviderTest {
|
||||||
long oldest = System.currentTimeMillis() - 30000;
|
long oldest = System.currentTimeMillis() - 30000;
|
||||||
long newest = System.currentTimeMillis() + 30000;
|
long newest = System.currentTimeMillis() + 30000;
|
||||||
|
|
||||||
provider.onEvent(create("event", "realmId", "clientId", "userId", "127.0.0.1", "error"));
|
provider.onEvent(create(EventType.LOGIN, "realmId", "clientId", "userId", "127.0.0.1", "error"));
|
||||||
provider.onEvent(create(newest, "event2", "realmId", "clientId", "userId", "127.0.0.1", "error"));
|
provider.onEvent(create(newest, EventType.REGISTER, "realmId", "clientId", "userId", "127.0.0.1", "error"));
|
||||||
provider.onEvent(create(newest, "event2", "realmId", "clientId", "userId2", "127.0.0.1", "error"));
|
provider.onEvent(create(newest, EventType.REGISTER, "realmId", "clientId", "userId2", "127.0.0.1", "error"));
|
||||||
provider.onEvent(create("event", "realmId2", "clientId", "userId", "127.0.0.1", "error"));
|
provider.onEvent(create(EventType.LOGIN, "realmId2", "clientId", "userId", "127.0.0.1", "error"));
|
||||||
provider.onEvent(create(oldest, "event", "realmId", "clientId2", "userId", "127.0.0.1", "error"));
|
provider.onEvent(create(oldest, EventType.LOGIN, "realmId", "clientId2", "userId", "127.0.0.1", "error"));
|
||||||
provider.onEvent(create("event", "realmId", "clientId", "userId2", "127.0.0.1", "error"));
|
provider.onEvent(create(EventType.LOGIN, "realmId", "clientId", "userId2", "127.0.0.1", "error"));
|
||||||
|
|
||||||
provider.close();
|
provider.close();
|
||||||
provider = factory.create(null);
|
provider = factory.create(null);
|
||||||
|
|
||||||
Assert.assertEquals(5, provider.createQuery().client("clientId").getResultList().size());
|
Assert.assertEquals(5, provider.createQuery().client("clientId").getResultList().size());
|
||||||
Assert.assertEquals(5, provider.createQuery().realm("realmId").getResultList().size());
|
Assert.assertEquals(5, provider.createQuery().realm("realmId").getResultList().size());
|
||||||
Assert.assertEquals(4, provider.createQuery().event("event").getResultList().size());
|
Assert.assertEquals(4, provider.createQuery().event(EventType.LOGIN).getResultList().size());
|
||||||
Assert.assertEquals(6, provider.createQuery().event("event", "event2").getResultList().size());
|
Assert.assertEquals(6, provider.createQuery().event(EventType.LOGIN, EventType.REGISTER).getResultList().size());
|
||||||
Assert.assertEquals(4, provider.createQuery().user("userId").getResultList().size());
|
Assert.assertEquals(4, provider.createQuery().user("userId").getResultList().size());
|
||||||
|
|
||||||
Assert.assertEquals(1, provider.createQuery().user("userId").event("event2").getResultList().size());
|
Assert.assertEquals(1, provider.createQuery().user("userId").event(EventType.REGISTER).getResultList().size());
|
||||||
|
|
||||||
Assert.assertEquals(2, provider.createQuery().maxResults(2).getResultList().size());
|
Assert.assertEquals(2, provider.createQuery().maxResults(2).getResultList().size());
|
||||||
Assert.assertEquals(1, provider.createQuery().firstResult(5).getResultList().size());
|
Assert.assertEquals(1, provider.createQuery().firstResult(5).getResultList().size());
|
||||||
|
@ -82,11 +83,11 @@ public abstract class AbstractAuditProviderTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void clear() {
|
public void clear() {
|
||||||
provider.onEvent(create(System.currentTimeMillis() - 30000, "event", "realmId", "clientId", "userId", "127.0.0.1", "error"));
|
provider.onEvent(create(System.currentTimeMillis() - 30000, EventType.LOGIN, "realmId", "clientId", "userId", "127.0.0.1", "error"));
|
||||||
provider.onEvent(create(System.currentTimeMillis() - 20000, "event", "realmId", "clientId", "userId", "127.0.0.1", "error"));
|
provider.onEvent(create(System.currentTimeMillis() - 20000, EventType.LOGIN, "realmId", "clientId", "userId", "127.0.0.1", "error"));
|
||||||
provider.onEvent(create(System.currentTimeMillis(), "event", "realmId", "clientId", "userId", "127.0.0.1", "error"));
|
provider.onEvent(create(System.currentTimeMillis(), EventType.LOGIN, "realmId", "clientId", "userId", "127.0.0.1", "error"));
|
||||||
provider.onEvent(create(System.currentTimeMillis(), "event", "realmId", "clientId", "userId", "127.0.0.1", "error"));
|
provider.onEvent(create(System.currentTimeMillis(), EventType.LOGIN, "realmId", "clientId", "userId", "127.0.0.1", "error"));
|
||||||
provider.onEvent(create(System.currentTimeMillis() - 30000, "event", "realmId2", "clientId", "userId", "127.0.0.1", "error"));
|
provider.onEvent(create(System.currentTimeMillis() - 30000, EventType.LOGIN, "realmId2", "clientId", "userId", "127.0.0.1", "error"));
|
||||||
|
|
||||||
provider.close();
|
provider.close();
|
||||||
provider = factory.create(null);
|
provider = factory.create(null);
|
||||||
|
@ -98,11 +99,11 @@ public abstract class AbstractAuditProviderTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void clearOld() {
|
public void clearOld() {
|
||||||
provider.onEvent(create(System.currentTimeMillis() - 30000, "event", "realmId", "clientId", "userId", "127.0.0.1", "error"));
|
provider.onEvent(create(System.currentTimeMillis() - 30000, EventType.LOGIN, "realmId", "clientId", "userId", "127.0.0.1", "error"));
|
||||||
provider.onEvent(create(System.currentTimeMillis() - 20000, "event", "realmId", "clientId", "userId", "127.0.0.1", "error"));
|
provider.onEvent(create(System.currentTimeMillis() - 20000, EventType.LOGIN, "realmId", "clientId", "userId", "127.0.0.1", "error"));
|
||||||
provider.onEvent(create(System.currentTimeMillis(), "event", "realmId", "clientId", "userId", "127.0.0.1", "error"));
|
provider.onEvent(create(System.currentTimeMillis(), EventType.LOGIN, "realmId", "clientId", "userId", "127.0.0.1", "error"));
|
||||||
provider.onEvent(create(System.currentTimeMillis(), "event", "realmId", "clientId", "userId", "127.0.0.1", "error"));
|
provider.onEvent(create(System.currentTimeMillis(), EventType.LOGIN, "realmId", "clientId", "userId", "127.0.0.1", "error"));
|
||||||
provider.onEvent(create(System.currentTimeMillis() - 30000, "event", "realmId2", "clientId", "userId", "127.0.0.1", "error"));
|
provider.onEvent(create(System.currentTimeMillis() - 30000, EventType.LOGIN, "realmId2", "clientId", "userId", "127.0.0.1", "error"));
|
||||||
|
|
||||||
provider.close();
|
provider.close();
|
||||||
provider = factory.create(null);
|
provider = factory.create(null);
|
||||||
|
@ -112,11 +113,11 @@ public abstract class AbstractAuditProviderTest {
|
||||||
Assert.assertEquals(3, provider.createQuery().getResultList().size());
|
Assert.assertEquals(3, provider.createQuery().getResultList().size());
|
||||||
}
|
}
|
||||||
|
|
||||||
private Event create(String event, String realmId, String clientId, String userId, String ipAddress, String error) {
|
private Event create(EventType event, String realmId, String clientId, String userId, String ipAddress, String error) {
|
||||||
return create(System.currentTimeMillis(), event, realmId, clientId, userId, ipAddress, error);
|
return create(System.currentTimeMillis(), event, realmId, clientId, userId, ipAddress, error);
|
||||||
}
|
}
|
||||||
|
|
||||||
private Event create(long time, String event, String realmId, String clientId, String userId, String ipAddress, String error) {
|
private Event create(long time, EventType event, String realmId, String clientId, String userId, String ipAddress, String error) {
|
||||||
Event e = new Event();
|
Event e = new Event();
|
||||||
e.setTime(time);
|
e.setTime(time);
|
||||||
e.setEvent(event);
|
e.setEvent(event);
|
||||||
|
|
|
@ -69,6 +69,20 @@ public class Config {
|
||||||
return System.getProperty(prefix + key, defaultValue);
|
return System.getProperty(prefix + key, defaultValue);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String[] getArray(String key) {
|
||||||
|
String value = get(key);
|
||||||
|
if (value != null) {
|
||||||
|
String[] a = value.split(",");
|
||||||
|
for (int i = 0; i < a.length; i++) {
|
||||||
|
a[i] = a[i].trim();
|
||||||
|
}
|
||||||
|
return a;
|
||||||
|
} else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Integer getInt(String key) {
|
public Integer getInt(String key) {
|
||||||
return getInt(key, null);
|
return getInt(key, null);
|
||||||
|
@ -113,6 +127,8 @@ public class Config {
|
||||||
|
|
||||||
String get(String key, String defaultValue);
|
String get(String key, String defaultValue);
|
||||||
|
|
||||||
|
String[] getArray(String key);
|
||||||
|
|
||||||
Integer getInt(String key);
|
Integer getInt(String key);
|
||||||
|
|
||||||
Integer getInt(String key, Integer defaultValue);
|
Integer getInt(String key, Integer defaultValue);
|
||||||
|
|
|
@ -38,7 +38,7 @@ public class LogBean {
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getEvent() {
|
public String getEvent() {
|
||||||
return event.getEvent();
|
return event.getEvent().toString().toLowerCase().replace("_", " ");
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getClient() {
|
public String getClient() {
|
||||||
|
@ -51,8 +51,10 @@ public class LogBean {
|
||||||
|
|
||||||
public List<DetailBean> getDetails() {
|
public List<DetailBean> getDetails() {
|
||||||
List<DetailBean> details = new LinkedList<DetailBean>();
|
List<DetailBean> details = new LinkedList<DetailBean>();
|
||||||
for (Map.Entry<String, String> e : event.getDetails().entrySet()) {
|
if (event.getDetails() != null) {
|
||||||
details.add(new DetailBean(e));
|
for (Map.Entry<String, String> e : event.getDetails().entrySet()) {
|
||||||
|
details.add(new DetailBean(e));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return details;
|
return details;
|
||||||
}
|
}
|
||||||
|
@ -72,7 +74,7 @@ public class LogBean {
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getValue() {
|
public String getValue() {
|
||||||
return entry.getValue();
|
return entry.getValue().replace("_", " ");
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -73,12 +73,13 @@
|
||||||
<tbody>
|
<tbody>
|
||||||
<tr ng-repeat="event in events">
|
<tr ng-repeat="event in events">
|
||||||
<td>{{event.time|date:'shortDate'}}<br>{{event.time|date:'mediumTime'}}</td>
|
<td>{{event.time|date:'shortDate'}}<br>{{event.time|date:'mediumTime'}}</td>
|
||||||
<td data-ng-class="event.error && 'audit-error' || 'audit-success'">{{event.event}}<br/>{{event.error}}</td>
|
<td data-ng-class="event.error && 'audit-error' || 'audit-success'">{{event.event}}</td>
|
||||||
<td>
|
<td>
|
||||||
<table>
|
<table class="table table-striped table-bordered">
|
||||||
<tr><td width="100px">Client</td><td>{{event.clientId}}</td></tr>
|
<tr><td width="100px">Client</td><td>{{event.clientId}}</td></tr>
|
||||||
<tr><td>User</td><td>{{event.userId}}</td></tr>
|
<tr><td>User</td><td>{{event.userId}}</td></tr>
|
||||||
<tr><td>IP Address</td><td>{{event.ipAddress}}</td></tr>
|
<tr><td>IP Address</td><td>{{event.ipAddress}}</td></tr>
|
||||||
|
<tr data-ng-show="event.error"><td>Error</td><td>{{event.error}}</td></tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td>Details</td>
|
<td>Details</td>
|
||||||
<td>
|
<td>
|
||||||
|
@ -86,7 +87,7 @@
|
||||||
<span class="glyphicon glyphicon-plus" data-ng-show="!event.collapse"></span>
|
<span class="glyphicon glyphicon-plus" data-ng-show="!event.collapse"></span>
|
||||||
<span class="glyphicon glyphicon-minus" data-ng-show="event.collapse"></span>
|
<span class="glyphicon glyphicon-minus" data-ng-show="event.collapse"></span>
|
||||||
</button>
|
</button>
|
||||||
<table data-ng-show="event.collapse">
|
<table data-ng-show="event.collapse" class="table table-striped table-bordered">
|
||||||
<tr ng-repeat="(key, value) in event.details">
|
<tr ng-repeat="(key, value) in event.details">
|
||||||
<td>{{key}}</td>
|
<td>{{key}}</td>
|
||||||
<td>{{value}}</td>
|
<td>{{value}}</td>
|
||||||
|
|
|
@ -828,4 +828,8 @@ legend .kc-icon-collapse {
|
||||||
|
|
||||||
.action-div > i {
|
.action-div > i {
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
table table {
|
||||||
|
margin-bottom: 0 !important;
|
||||||
}
|
}
|
|
@ -1,3 +1,78 @@
|
||||||
|
|
||||||
|
table tfoot tr .table-nav {
|
||||||
|
float: right;
|
||||||
|
}
|
||||||
|
table tfoot tr .table-nav a,
|
||||||
|
table tfoot tr .table-nav button {
|
||||||
|
display: inline-block;
|
||||||
|
line-height: 22px;
|
||||||
|
border-left: 1px solid #d9d9d9;
|
||||||
|
border-right: none;
|
||||||
|
border-top: none;
|
||||||
|
border-bottom: none;
|
||||||
|
width: 3.5em;
|
||||||
|
background-color: #f3f3f3;
|
||||||
|
background-image: linear-gradient(top, #fafafa 0%, #ededed 100%);
|
||||||
|
background-image: -o-linear-gradient(top, #fafafa 0%, #ededed 100%);
|
||||||
|
background-image: -moz-linear-gradient(top, #fafafa 0%, #ededed 100%);
|
||||||
|
background-image: -webkit-linear-gradient(top, #fafafa 0%, #ededed 100%);
|
||||||
|
background-image: -ms-linear-gradient(top, #fafafa 0%, #ededed 100%);
|
||||||
|
background-image: -webkit-gradient(linear, left top, left bottom, color-stop(0, #fafafa), color-stop(1, 0, #ededed));
|
||||||
|
text-indent: -99999em;
|
||||||
|
background-image: url(img/sprite-table-nav.png);
|
||||||
|
background-repeat: no-repeat;
|
||||||
|
background-position: left top;
|
||||||
|
vertical-align: top;
|
||||||
|
}
|
||||||
|
table tfoot tr .table-nav a.last,
|
||||||
|
table tfoot tr .table-nav button.last {
|
||||||
|
background-position: top right;
|
||||||
|
}
|
||||||
|
table tfoot tr .table-nav a.prev,
|
||||||
|
table tfoot tr .table-nav button.prev {
|
||||||
|
background-position: bottom left;
|
||||||
|
}
|
||||||
|
table tfoot tr .table-nav a.next,
|
||||||
|
table tfoot tr .table-nav button.next {
|
||||||
|
background-position: bottom right;
|
||||||
|
}
|
||||||
|
table tfoot tr .table-nav a:hover,
|
||||||
|
table tfoot tr .table-nav button:hover {
|
||||||
|
background-image: url(img/sprite-table-nav.png);
|
||||||
|
background-color: #eeeeee;
|
||||||
|
}
|
||||||
|
table tfoot tr .table-nav a:active,
|
||||||
|
table tfoot tr .table-nav button:active {
|
||||||
|
box-shadow: 0 0 5px 2px rgba(0, 0, 0, 0.25) inset;
|
||||||
|
}
|
||||||
|
table tfoot tr .table-nav a.disabled,
|
||||||
|
table tfoot tr .table-nav button:disabled {
|
||||||
|
opacity: 0.5;
|
||||||
|
filter: alpha(opacity=50);
|
||||||
|
cursor: default;
|
||||||
|
}
|
||||||
|
table tfoot tr .table-nav a.disabled:active,
|
||||||
|
table tfoot tr .table-nav button:disabled:active {
|
||||||
|
box-shadow: none;
|
||||||
|
}
|
||||||
|
table tfoot tr .table-nav span {
|
||||||
|
font-size: 1.1em;
|
||||||
|
border-left: 1px solid #d9d9d9;
|
||||||
|
line-height: 2.18181818181818em;
|
||||||
|
display: inline-block;
|
||||||
|
padding: 0 1.36363636363636em;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
td.audit-success {
|
||||||
|
background-color: #E4F1E1 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
td.audit-error {
|
||||||
|
background-color: #F8E7E7 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
table {
|
table {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
@ -111,69 +186,6 @@ table tbody.selectable-rows tr.selected:hover td:first-child {
|
||||||
table tfoot tr {
|
table tfoot tr {
|
||||||
border-top: 1px solid #cecece;
|
border-top: 1px solid #cecece;
|
||||||
}
|
}
|
||||||
table tfoot tr .table-nav {
|
|
||||||
float: right;
|
|
||||||
}
|
|
||||||
table tfoot tr .table-nav a,
|
|
||||||
table tfoot tr .table-nav button {
|
|
||||||
display: inline-block;
|
|
||||||
line-height: 22px;
|
|
||||||
border-left: 1px solid #d9d9d9;
|
|
||||||
border-right: none;
|
|
||||||
border-top: none;
|
|
||||||
border-bottom: none;
|
|
||||||
width: 3.5em;
|
|
||||||
background-color: #f3f3f3;
|
|
||||||
background-image: linear-gradient(top, #fafafa 0%, #ededed 100%);
|
|
||||||
background-image: -o-linear-gradient(top, #fafafa 0%, #ededed 100%);
|
|
||||||
background-image: -moz-linear-gradient(top, #fafafa 0%, #ededed 100%);
|
|
||||||
background-image: -webkit-linear-gradient(top, #fafafa 0%, #ededed 100%);
|
|
||||||
background-image: -ms-linear-gradient(top, #fafafa 0%, #ededed 100%);
|
|
||||||
background-image: -webkit-gradient(linear, left top, left bottom, color-stop(0, #fafafa), color-stop(1, 0, #ededed));
|
|
||||||
text-indent: -99999em;
|
|
||||||
background-image: url(img/sprite-table-nav.png);
|
|
||||||
background-repeat: no-repeat;
|
|
||||||
background-position: left top;
|
|
||||||
vertical-align: top;
|
|
||||||
}
|
|
||||||
table tfoot tr .table-nav a.last,
|
|
||||||
table tfoot tr .table-nav button.last {
|
|
||||||
background-position: top right;
|
|
||||||
}
|
|
||||||
table tfoot tr .table-nav a.prev,
|
|
||||||
table tfoot tr .table-nav button.prev {
|
|
||||||
background-position: bottom left;
|
|
||||||
}
|
|
||||||
table tfoot tr .table-nav a.next,
|
|
||||||
table tfoot tr .table-nav button.next {
|
|
||||||
background-position: bottom right;
|
|
||||||
}
|
|
||||||
table tfoot tr .table-nav a:hover,
|
|
||||||
table tfoot tr .table-nav button:hover {
|
|
||||||
background-image: url(img/sprite-table-nav.png);
|
|
||||||
background-color: #eeeeee;
|
|
||||||
}
|
|
||||||
table tfoot tr .table-nav a:active,
|
|
||||||
table tfoot tr .table-nav button:active {
|
|
||||||
box-shadow: 0 0 5px 2px rgba(0, 0, 0, 0.25) inset;
|
|
||||||
}
|
|
||||||
table tfoot tr .table-nav a.disabled,
|
|
||||||
table tfoot tr .table-nav button:disabled {
|
|
||||||
opacity: 0.5;
|
|
||||||
filter: alpha(opacity=50);
|
|
||||||
cursor: default;
|
|
||||||
}
|
|
||||||
table tfoot tr .table-nav a.disabled:active,
|
|
||||||
table tfoot tr .table-nav button:disabled:active {
|
|
||||||
box-shadow: none;
|
|
||||||
}
|
|
||||||
table tfoot tr .table-nav span {
|
|
||||||
font-size: 1.1em;
|
|
||||||
border-left: 1px solid #d9d9d9;
|
|
||||||
line-height: 2.18181818181818em;
|
|
||||||
display: inline-block;
|
|
||||||
padding: 0 1.36363636363636em;
|
|
||||||
}
|
|
||||||
|
|
||||||
.kc-table-actions > * {
|
.kc-table-actions > * {
|
||||||
vertical-align: middle;
|
vertical-align: middle;
|
||||||
|
@ -183,14 +195,6 @@ td .form-group {
|
||||||
margin-bottom: 0;
|
margin-bottom: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
td.audit-success {
|
|
||||||
background-color: #E4F1E1;
|
|
||||||
}
|
|
||||||
|
|
||||||
td.audit-error {
|
|
||||||
background-color: #F8E7E7;
|
|
||||||
}
|
|
||||||
|
|
||||||
.kc-table-actions .form-group {
|
.kc-table-actions .form-group {
|
||||||
margin-top: 5px;
|
margin-top: 5px;
|
||||||
margin-bottom: 5px;
|
margin-bottom: 5px;
|
||||||
|
|
|
@ -0,0 +1 @@
|
||||||
|
Your password was changed on ${event.date} from ${event.ipAddress}. If this was not you, please contact an admin.
|
|
@ -0,0 +1 @@
|
||||||
|
TOTP was removed from your account on ${event.date} from ${event.ipAddress}. If this was not you, please contact an admin.
|
|
@ -0,0 +1 @@
|
||||||
|
A failed login attempt was dettected to your account on ${event.date} from ${event.ipAddress}. If this was not you, please contact an admin.
|
|
@ -0,0 +1 @@
|
||||||
|
TOTP was updated for your account on ${event.date} from ${event.ipAddress}. If this was not you, please contact an admin.
|
|
@ -26,6 +26,12 @@
|
||||||
<version>${project.version}</version>
|
<version>${project.version}</version>
|
||||||
<scope>provided</scope>
|
<scope>provided</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.keycloak</groupId>
|
||||||
|
<artifactId>keycloak-audit-api</artifactId>
|
||||||
|
<version>${project.version}</version>
|
||||||
|
<scope>provided</scope>
|
||||||
|
</dependency>
|
||||||
</dependencies>
|
</dependencies>
|
||||||
|
|
||||||
<build>
|
<build>
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
package org.keycloak.email;
|
package org.keycloak.email;
|
||||||
|
|
||||||
|
import org.keycloak.audit.Event;
|
||||||
import org.keycloak.models.RealmModel;
|
import org.keycloak.models.RealmModel;
|
||||||
import org.keycloak.models.UserModel;
|
import org.keycloak.models.UserModel;
|
||||||
import org.keycloak.provider.Provider;
|
import org.keycloak.provider.Provider;
|
||||||
|
@ -13,6 +14,8 @@ public interface EmailProvider extends Provider {
|
||||||
|
|
||||||
public EmailProvider setUser(UserModel user);
|
public EmailProvider setUser(UserModel user);
|
||||||
|
|
||||||
|
public void sendEvent(Event event) throws EmailException;
|
||||||
|
|
||||||
public void sendPasswordReset(String link, long expirationInMinutes) throws EmailException;
|
public void sendPasswordReset(String link, long expirationInMinutes) throws EmailException;
|
||||||
|
|
||||||
public void sendVerifyEmail(String link, long expirationInMinutes) throws EmailException;
|
public void sendVerifyEmail(String link, long expirationInMinutes) throws EmailException;
|
||||||
|
|
|
@ -32,6 +32,12 @@
|
||||||
<version>${project.version}</version>
|
<version>${project.version}</version>
|
||||||
<scope>provided</scope>
|
<scope>provided</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.keycloak</groupId>
|
||||||
|
<artifactId>keycloak-audit-api</artifactId>
|
||||||
|
<version>${project.version}</version>
|
||||||
|
<scope>provided</scope>
|
||||||
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.keycloak</groupId>
|
<groupId>org.keycloak</groupId>
|
||||||
<artifactId>keycloak-forms-common-freemarker</artifactId>
|
<artifactId>keycloak-forms-common-freemarker</artifactId>
|
||||||
|
|
|
@ -1,8 +1,10 @@
|
||||||
package org.keycloak.email.freemarker;
|
package org.keycloak.email.freemarker;
|
||||||
|
|
||||||
import org.jboss.logging.Logger;
|
import org.jboss.logging.Logger;
|
||||||
|
import org.keycloak.audit.Event;
|
||||||
import org.keycloak.email.EmailException;
|
import org.keycloak.email.EmailException;
|
||||||
import org.keycloak.email.EmailProvider;
|
import org.keycloak.email.EmailProvider;
|
||||||
|
import org.keycloak.email.freemarker.beans.EventBean;
|
||||||
import org.keycloak.freemarker.ExtendingThemeManager;
|
import org.keycloak.freemarker.ExtendingThemeManager;
|
||||||
import org.keycloak.freemarker.FreeMarkerUtil;
|
import org.keycloak.freemarker.FreeMarkerUtil;
|
||||||
import org.keycloak.freemarker.Theme;
|
import org.keycloak.freemarker.Theme;
|
||||||
|
@ -47,6 +49,14 @@ public class FreeMarkerEmailProvider implements EmailProvider {
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void sendEvent(Event event) throws EmailException {
|
||||||
|
Map<String, Object> attributes = new HashMap<String, Object>();
|
||||||
|
attributes.put("event", new EventBean(event));
|
||||||
|
|
||||||
|
send("passwordResetSubject", "event-" + event.getEvent().toString().toLowerCase() + ".ftl", attributes);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void sendPasswordReset(String link, long expirationInMinutes) throws EmailException {
|
public void sendPasswordReset(String link, long expirationInMinutes) throws EmailException {
|
||||||
Map<String, Object> attributes = new HashMap<String, Object>();
|
Map<String, Object> attributes = new HashMap<String, Object>();
|
||||||
|
|
|
@ -0,0 +1,61 @@
|
||||||
|
package org.keycloak.email.freemarker.beans;
|
||||||
|
|
||||||
|
import org.keycloak.audit.Event;
|
||||||
|
|
||||||
|
import java.util.Date;
|
||||||
|
import java.util.LinkedList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
|
||||||
|
*/
|
||||||
|
public class EventBean {
|
||||||
|
private Event event;
|
||||||
|
|
||||||
|
public EventBean(Event event) {
|
||||||
|
this.event = event;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Date getDate() {
|
||||||
|
return new Date(event.getTime());
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getEvent() {
|
||||||
|
return event.getEvent().toString().toLowerCase().replace("_", " ");
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getClient() {
|
||||||
|
return event.getClientId();
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getIpAddress() {
|
||||||
|
return event.getIpAddress();
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<DetailBean> getDetails() {
|
||||||
|
List<DetailBean> details = new LinkedList<DetailBean>();
|
||||||
|
for (Map.Entry<String, String> e : event.getDetails().entrySet()) {
|
||||||
|
details.add(new DetailBean(e));
|
||||||
|
}
|
||||||
|
return details;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class DetailBean {
|
||||||
|
|
||||||
|
private Map.Entry<String, String> entry;
|
||||||
|
|
||||||
|
public DetailBean(Map.Entry<String, String> entry) {
|
||||||
|
this.entry = entry;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getKey() {
|
||||||
|
return entry.getKey();
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getValue() {
|
||||||
|
return entry.getValue().replace("_", " ");
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
|
@ -3,10 +3,6 @@
|
||||||
"realm": "keycloak-admin"
|
"realm": "keycloak-admin"
|
||||||
},
|
},
|
||||||
|
|
||||||
"audit": {
|
|
||||||
"provider": "jpa"
|
|
||||||
},
|
|
||||||
|
|
||||||
"model": {
|
"model": {
|
||||||
"provider": "jpa"
|
"provider": "jpa"
|
||||||
},
|
},
|
||||||
|
|
|
@ -62,6 +62,11 @@
|
||||||
<artifactId>keycloak-audit-jboss-logging</artifactId>
|
<artifactId>keycloak-audit-jboss-logging</artifactId>
|
||||||
<version>${project.version}</version>
|
<version>${project.version}</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.keycloak</groupId>
|
||||||
|
<artifactId>keycloak-audit-email</artifactId>
|
||||||
|
<version>${project.version}</version>
|
||||||
|
</dependency>
|
||||||
<!-- social -->
|
<!-- social -->
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.keycloak</groupId>
|
<groupId>org.keycloak</groupId>
|
||||||
|
|
|
@ -4,7 +4,10 @@
|
||||||
},
|
},
|
||||||
|
|
||||||
"audit": {
|
"audit": {
|
||||||
"provider": "jpa"
|
"provider": "jpa",
|
||||||
|
"jpa": {
|
||||||
|
"exclude-events": [ "REFRESH_TOKEN" ]
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
"model": {
|
"model": {
|
||||||
|
|
|
@ -31,7 +31,7 @@ import org.keycloak.audit.Audit;
|
||||||
import org.keycloak.audit.AuditProvider;
|
import org.keycloak.audit.AuditProvider;
|
||||||
import org.keycloak.audit.Details;
|
import org.keycloak.audit.Details;
|
||||||
import org.keycloak.audit.Event;
|
import org.keycloak.audit.Event;
|
||||||
import org.keycloak.audit.Events;
|
import org.keycloak.audit.EventType;
|
||||||
import org.keycloak.authentication.AuthProviderStatus;
|
import org.keycloak.authentication.AuthProviderStatus;
|
||||||
import org.keycloak.authentication.AuthenticationProviderException;
|
import org.keycloak.authentication.AuthenticationProviderException;
|
||||||
import org.keycloak.authentication.AuthenticationProviderManager;
|
import org.keycloak.authentication.AuthenticationProviderManager;
|
||||||
|
@ -95,8 +95,8 @@ public class AccountService {
|
||||||
|
|
||||||
private static final Logger logger = Logger.getLogger(AccountService.class);
|
private static final Logger logger = Logger.getLogger(AccountService.class);
|
||||||
|
|
||||||
private static final String[] AUDIT_EVENTS = {Events.LOGIN, Events.LOGOUT, Events.REGISTER, Events.REMOVE_SOCIAL_LINK, Events.REMOVE_TOTP, Events.SEND_RESET_PASSWORD,
|
private static final EventType[] AUDIT_EVENTS = {EventType.LOGIN, EventType.LOGOUT, EventType.REGISTER, EventType.REMOVE_SOCIAL_LINK, EventType.REMOVE_TOTP, EventType.SEND_RESET_PASSWORD,
|
||||||
Events.SEND_VERIFY_EMAIL, Events.SOCIAL_LINK, Events.UPDATE_EMAIL, Events.UPDATE_PASSWORD, Events.UPDATE_PROFILE, Events.UPDATE_TOTP, Events.VERIFY_EMAIL};
|
EventType.SEND_VERIFY_EMAIL, EventType.SOCIAL_LINK, EventType.UPDATE_EMAIL, EventType.UPDATE_PASSWORD, EventType.UPDATE_PROFILE, EventType.UPDATE_TOTP, EventType.VERIFY_EMAIL};
|
||||||
|
|
||||||
private static final Set<String> AUDIT_DETAILS = new HashSet<String>();
|
private static final Set<String> AUDIT_DETAILS = new HashSet<String>();
|
||||||
static {
|
static {
|
||||||
|
@ -246,20 +246,6 @@ public class AccountService {
|
||||||
public Response logPage() {
|
public Response logPage() {
|
||||||
if (auth != null) {
|
if (auth != null) {
|
||||||
List<Event> events = auditProvider.createQuery().event(AUDIT_EVENTS).user(auth.getUser().getId()).maxResults(30).getResultList();
|
List<Event> events = auditProvider.createQuery().event(AUDIT_EVENTS).user(auth.getUser().getId()).maxResults(30).getResultList();
|
||||||
for (Event e : events) {
|
|
||||||
e.setEvent(e.getEvent().replace('_', ' '));
|
|
||||||
|
|
||||||
Map<String, String> details = new HashMap<String, String>();
|
|
||||||
if (e.getDetails() != null) {
|
|
||||||
Iterator<String> itr = e.getDetails().keySet().iterator();
|
|
||||||
for (Map.Entry<String, String> d : e.getDetails().entrySet()) {
|
|
||||||
if (AUDIT_DETAILS.contains(d.getKey())) {
|
|
||||||
details.put(d.getKey().replace('_', ' '), d.getValue());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
e.setDetails(details);
|
|
||||||
}
|
|
||||||
account.setEvents(events);
|
account.setEvents(events);
|
||||||
}
|
}
|
||||||
return forwardToPage("log", AccountPages.LOG);
|
return forwardToPage("log", AccountPages.LOG);
|
||||||
|
@ -300,11 +286,11 @@ public class AccountService {
|
||||||
|
|
||||||
user.setEmail(formData.getFirst("email"));
|
user.setEmail(formData.getFirst("email"));
|
||||||
|
|
||||||
audit.event(Events.UPDATE_PROFILE).client(auth.getClient()).user(auth.getUser()).success();
|
audit.event(EventType.UPDATE_PROFILE).client(auth.getClient()).user(auth.getUser()).success();
|
||||||
|
|
||||||
if (emailChanged) {
|
if (emailChanged) {
|
||||||
user.setEmailVerified(false);
|
user.setEmailVerified(false);
|
||||||
audit.clone().event(Events.UPDATE_EMAIL).detail(Details.PREVIOUS_EMAIL, oldEmail).detail(Details.UPDATED_EMAIL, email).success();
|
audit.clone().event(EventType.UPDATE_EMAIL).detail(Details.PREVIOUS_EMAIL, oldEmail).detail(Details.UPDATED_EMAIL, email).success();
|
||||||
}
|
}
|
||||||
|
|
||||||
return account.setSuccess("accountUpdated").createResponse(AccountPages.ACCOUNT);
|
return account.setSuccess("accountUpdated").createResponse(AccountPages.ACCOUNT);
|
||||||
|
@ -322,7 +308,7 @@ public class AccountService {
|
||||||
UserModel user = auth.getUser();
|
UserModel user = auth.getUser();
|
||||||
user.setTotp(false);
|
user.setTotp(false);
|
||||||
|
|
||||||
audit.event(Events.REMOVE_TOTP).client(auth.getClient()).user(auth.getUser()).success();
|
audit.event(EventType.REMOVE_TOTP).client(auth.getClient()).user(auth.getUser()).success();
|
||||||
|
|
||||||
return account.setSuccess("successTotpRemoved").createResponse(AccountPages.TOTP);
|
return account.setSuccess("successTotpRemoved").createResponse(AccountPages.TOTP);
|
||||||
}
|
}
|
||||||
|
@ -371,7 +357,7 @@ public class AccountService {
|
||||||
|
|
||||||
user.setTotp(true);
|
user.setTotp(true);
|
||||||
|
|
||||||
audit.event(Events.UPDATE_TOTP).client(auth.getClient()).user(auth.getUser()).success();
|
audit.event(EventType.UPDATE_TOTP).client(auth.getClient()).user(auth.getUser()).success();
|
||||||
|
|
||||||
return account.setSuccess("successTotp").createResponse(AccountPages.TOTP);
|
return account.setSuccess("successTotp").createResponse(AccountPages.TOTP);
|
||||||
}
|
}
|
||||||
|
@ -414,7 +400,7 @@ public class AccountService {
|
||||||
return account.setError(ape.getMessage()).createResponse(AccountPages.PASSWORD);
|
return account.setError(ape.getMessage()).createResponse(AccountPages.PASSWORD);
|
||||||
}
|
}
|
||||||
|
|
||||||
audit.event(Events.UPDATE_PASSWORD).client(auth.getClient()).user(auth.getUser()).success();
|
audit.event(EventType.UPDATE_PASSWORD).client(auth.getClient()).user(auth.getUser()).success();
|
||||||
|
|
||||||
return account.setSuccess("accountPasswordUpdated").createResponse(AccountPages.PASSWORD);
|
return account.setSuccess("accountPasswordUpdated").createResponse(AccountPages.PASSWORD);
|
||||||
}
|
}
|
||||||
|
@ -471,7 +457,7 @@ public class AccountService {
|
||||||
|
|
||||||
logger.debug("Social provider " + providerId + " removed successfully from user " + user.getLoginName());
|
logger.debug("Social provider " + providerId + " removed successfully from user " + user.getLoginName());
|
||||||
|
|
||||||
audit.event(Events.REMOVE_SOCIAL_LINK).client(auth.getClient()).user(auth.getUser())
|
audit.event(EventType.REMOVE_SOCIAL_LINK).client(auth.getClient()).user(auth.getUser())
|
||||||
.detail(Details.USERNAME, link.getSocialUserId() + "@" + link.getSocialProvider())
|
.detail(Details.USERNAME, link.getSocialUserId() + "@" + link.getSocialProvider())
|
||||||
.success();
|
.success();
|
||||||
|
|
||||||
|
|
|
@ -27,7 +27,7 @@ import org.keycloak.OAuth2Constants;
|
||||||
import org.keycloak.audit.Audit;
|
import org.keycloak.audit.Audit;
|
||||||
import org.keycloak.audit.Details;
|
import org.keycloak.audit.Details;
|
||||||
import org.keycloak.audit.Errors;
|
import org.keycloak.audit.Errors;
|
||||||
import org.keycloak.audit.Events;
|
import org.keycloak.audit.EventType;
|
||||||
import org.keycloak.email.EmailException;
|
import org.keycloak.email.EmailException;
|
||||||
import org.keycloak.email.EmailProvider;
|
import org.keycloak.email.EmailProvider;
|
||||||
import org.keycloak.login.LoginFormsProvider;
|
import org.keycloak.login.LoginFormsProvider;
|
||||||
|
@ -136,10 +136,10 @@ public class RequiredActionsService {
|
||||||
user.removeRequiredAction(RequiredAction.UPDATE_PROFILE);
|
user.removeRequiredAction(RequiredAction.UPDATE_PROFILE);
|
||||||
accessCode.getRequiredActions().remove(RequiredAction.UPDATE_PROFILE);
|
accessCode.getRequiredActions().remove(RequiredAction.UPDATE_PROFILE);
|
||||||
|
|
||||||
audit.clone().event(Events.UPDATE_PROFILE).success();
|
audit.clone().event(EventType.UPDATE_PROFILE).success();
|
||||||
if (emailChanged) {
|
if (emailChanged) {
|
||||||
user.setEmailVerified(false);
|
user.setEmailVerified(false);
|
||||||
audit.clone().event(Events.UPDATE_EMAIL).detail(Details.PREVIOUS_EMAIL, oldEmail).detail(Details.UPDATED_EMAIL, email).success();
|
audit.clone().event(EventType.UPDATE_EMAIL).detail(Details.PREVIOUS_EMAIL, oldEmail).detail(Details.UPDATED_EMAIL, email).success();
|
||||||
}
|
}
|
||||||
|
|
||||||
return redirectOauth(user, accessCode);
|
return redirectOauth(user, accessCode);
|
||||||
|
@ -178,7 +178,7 @@ public class RequiredActionsService {
|
||||||
user.removeRequiredAction(RequiredAction.CONFIGURE_TOTP);
|
user.removeRequiredAction(RequiredAction.CONFIGURE_TOTP);
|
||||||
accessCode.getRequiredActions().remove(RequiredAction.CONFIGURE_TOTP);
|
accessCode.getRequiredActions().remove(RequiredAction.CONFIGURE_TOTP);
|
||||||
|
|
||||||
audit.clone().event(Events.UPDATE_TOTP).success();
|
audit.clone().event(EventType.UPDATE_TOTP).success();
|
||||||
|
|
||||||
return redirectOauth(user, accessCode);
|
return redirectOauth(user, accessCode);
|
||||||
}
|
}
|
||||||
|
@ -225,7 +225,7 @@ public class RequiredActionsService {
|
||||||
accessCode.getRequiredActions().remove(RequiredAction.UPDATE_PASSWORD);
|
accessCode.getRequiredActions().remove(RequiredAction.UPDATE_PASSWORD);
|
||||||
}
|
}
|
||||||
|
|
||||||
audit.clone().event(Events.UPDATE_PASSWORD).success();
|
audit.clone().event(EventType.UPDATE_PASSWORD).success();
|
||||||
|
|
||||||
return redirectOauth(user, accessCode);
|
return redirectOauth(user, accessCode);
|
||||||
}
|
}
|
||||||
|
@ -250,7 +250,7 @@ public class RequiredActionsService {
|
||||||
user.removeRequiredAction(RequiredAction.VERIFY_EMAIL);
|
user.removeRequiredAction(RequiredAction.VERIFY_EMAIL);
|
||||||
accessCode.getRequiredActions().remove(RequiredAction.VERIFY_EMAIL);
|
accessCode.getRequiredActions().remove(RequiredAction.VERIFY_EMAIL);
|
||||||
|
|
||||||
audit.clone().event(Events.VERIFY_EMAIL).detail(Details.EMAIL, accessCode.getUser().getEmail()).success();
|
audit.clone().event(EventType.VERIFY_EMAIL).detail(Details.EMAIL, accessCode.getUser().getEmail()).success();
|
||||||
|
|
||||||
return redirectOauth(user, accessCode);
|
return redirectOauth(user, accessCode);
|
||||||
} else {
|
} else {
|
||||||
|
@ -260,7 +260,7 @@ public class RequiredActionsService {
|
||||||
}
|
}
|
||||||
|
|
||||||
initAudit(accessCode);
|
initAudit(accessCode);
|
||||||
//audit.clone().event(Events.SEND_VERIFY_EMAIL).detail(Details.EMAIL, accessCode.getUser().getEmail()).success();
|
//audit.clone().event(EventType.SEND_VERIFY_EMAIL).detail(Details.EMAIL, accessCode.getUser().getEmail()).success();
|
||||||
|
|
||||||
return Flows.forms(providerSession, realm, uriInfo).setAccessCode(accessCode.getId(), accessCode.getCode()).setUser(accessCode.getUser())
|
return Flows.forms(providerSession, realm, uriInfo).setAccessCode(accessCode.getId(), accessCode.getCode()).setUser(accessCode.getUser())
|
||||||
.createResponse(RequiredAction.VERIFY_EMAIL);
|
.createResponse(RequiredAction.VERIFY_EMAIL);
|
||||||
|
@ -307,7 +307,7 @@ public class RequiredActionsService {
|
||||||
"Login requester not enabled.");
|
"Login requester not enabled.");
|
||||||
}
|
}
|
||||||
|
|
||||||
audit.event(Events.SEND_RESET_PASSWORD).client(clientId)
|
audit.event(EventType.SEND_RESET_PASSWORD).client(clientId)
|
||||||
.detail(Details.REDIRECT_URI, redirect)
|
.detail(Details.REDIRECT_URI, redirect)
|
||||||
.detail(Details.RESPONSE_TYPE, "code")
|
.detail(Details.RESPONSE_TYPE, "code")
|
||||||
.detail(Details.AUTH_METHOD, "form")
|
.detail(Details.AUTH_METHOD, "form")
|
||||||
|
@ -430,7 +430,7 @@ public class RequiredActionsService {
|
||||||
}
|
}
|
||||||
|
|
||||||
private void initAudit(AccessCodeEntry accessCode) {
|
private void initAudit(AccessCodeEntry accessCode) {
|
||||||
audit.event(Events.LOGIN).client(accessCode.getClient())
|
audit.event(EventType.LOGIN).client(accessCode.getClient())
|
||||||
.user(accessCode.getUser())
|
.user(accessCode.getUser())
|
||||||
.session(accessCode.getSessionState())
|
.session(accessCode.getSessionState())
|
||||||
.detail(Details.CODE_ID, accessCode.getId())
|
.detail(Details.CODE_ID, accessCode.getId())
|
||||||
|
|
|
@ -28,7 +28,7 @@ import org.keycloak.OAuth2Constants;
|
||||||
import org.keycloak.audit.Audit;
|
import org.keycloak.audit.Audit;
|
||||||
import org.keycloak.audit.Details;
|
import org.keycloak.audit.Details;
|
||||||
import org.keycloak.audit.Errors;
|
import org.keycloak.audit.Errors;
|
||||||
import org.keycloak.audit.Events;
|
import org.keycloak.audit.EventType;
|
||||||
import org.keycloak.models.AccountRoles;
|
import org.keycloak.models.AccountRoles;
|
||||||
import org.keycloak.models.ClientModel;
|
import org.keycloak.models.ClientModel;
|
||||||
import org.keycloak.models.Constants;
|
import org.keycloak.models.Constants;
|
||||||
|
@ -65,7 +65,6 @@ import javax.ws.rs.core.Context;
|
||||||
import javax.ws.rs.core.HttpHeaders;
|
import javax.ws.rs.core.HttpHeaders;
|
||||||
import javax.ws.rs.core.MultivaluedMap;
|
import javax.ws.rs.core.MultivaluedMap;
|
||||||
import javax.ws.rs.core.Response;
|
import javax.ws.rs.core.Response;
|
||||||
import javax.ws.rs.core.Response.Status;
|
|
||||||
import javax.ws.rs.core.UriBuilder;
|
import javax.ws.rs.core.UriBuilder;
|
||||||
import javax.ws.rs.core.UriInfo;
|
import javax.ws.rs.core.UriInfo;
|
||||||
import java.net.URISyntaxException;
|
import java.net.URISyntaxException;
|
||||||
|
@ -131,7 +130,7 @@ public class SocialResource {
|
||||||
RealmModel realm = realmManager.getRealmByName(realmName);
|
RealmModel realm = realmManager.getRealmByName(realmName);
|
||||||
|
|
||||||
Audit audit = new AuditManager(realm, providers, clientConnection).createAudit()
|
Audit audit = new AuditManager(realm, providers, clientConnection).createAudit()
|
||||||
.event(Events.LOGIN)
|
.event(EventType.LOGIN)
|
||||||
.detail(Details.RESPONSE_TYPE, "code")
|
.detail(Details.RESPONSE_TYPE, "code")
|
||||||
.detail(Details.AUTH_METHOD, "social@" + provider.getId());
|
.detail(Details.AUTH_METHOD, "social@" + provider.getId());
|
||||||
|
|
||||||
|
@ -196,7 +195,7 @@ public class SocialResource {
|
||||||
if (userId != null) {
|
if (userId != null) {
|
||||||
UserModel authenticatedUser = realm.getUserById(userId);
|
UserModel authenticatedUser = realm.getUserById(userId);
|
||||||
|
|
||||||
audit.event(Events.SOCIAL_LINK).user(userId);
|
audit.event(EventType.SOCIAL_LINK).user(userId);
|
||||||
|
|
||||||
if (user != null) {
|
if (user != null) {
|
||||||
audit.error(Errors.SOCIAL_ID_IN_USE);
|
audit.error(Errors.SOCIAL_ID_IN_USE);
|
||||||
|
@ -244,7 +243,7 @@ public class SocialResource {
|
||||||
|
|
||||||
realm.addSocialLink(user, socialLink);
|
realm.addSocialLink(user, socialLink);
|
||||||
|
|
||||||
audit.clone().user(user).event(Events.REGISTER)
|
audit.clone().user(user).event(EventType.REGISTER)
|
||||||
.detail(Details.REGISTER_METHOD, "social@" + provider.getId())
|
.detail(Details.REGISTER_METHOD, "social@" + provider.getId())
|
||||||
.detail(Details.EMAIL, socialUser.getEmail())
|
.detail(Details.EMAIL, socialUser.getEmail())
|
||||||
.removeDetail("auth_method")
|
.removeDetail("auth_method")
|
||||||
|
@ -274,7 +273,7 @@ public class SocialResource {
|
||||||
RealmModel realm = realmManager.getRealmByName(realmName);
|
RealmModel realm = realmManager.getRealmByName(realmName);
|
||||||
|
|
||||||
Audit audit = new AuditManager(realm, providers, clientConnection).createAudit()
|
Audit audit = new AuditManager(realm, providers, clientConnection).createAudit()
|
||||||
.event(Events.LOGIN).client(clientId)
|
.event(EventType.LOGIN).client(clientId)
|
||||||
.detail(Details.REDIRECT_URI, redirectUri)
|
.detail(Details.REDIRECT_URI, redirectUri)
|
||||||
.detail(Details.RESPONSE_TYPE, "code")
|
.detail(Details.RESPONSE_TYPE, "code")
|
||||||
.detail(Details.AUTH_METHOD, "social@" + providerId);
|
.detail(Details.AUTH_METHOD, "social@" + providerId);
|
||||||
|
|
|
@ -13,7 +13,7 @@ import org.keycloak.OAuthErrorException;
|
||||||
import org.keycloak.audit.Audit;
|
import org.keycloak.audit.Audit;
|
||||||
import org.keycloak.audit.Details;
|
import org.keycloak.audit.Details;
|
||||||
import org.keycloak.audit.Errors;
|
import org.keycloak.audit.Errors;
|
||||||
import org.keycloak.audit.Events;
|
import org.keycloak.audit.EventType;
|
||||||
import org.keycloak.authentication.AuthenticationProviderException;
|
import org.keycloak.authentication.AuthenticationProviderException;
|
||||||
import org.keycloak.authentication.AuthenticationProviderManager;
|
import org.keycloak.authentication.AuthenticationProviderManager;
|
||||||
import org.keycloak.jose.jws.JWSInput;
|
import org.keycloak.jose.jws.JWSInput;
|
||||||
|
@ -34,7 +34,6 @@ import org.keycloak.representations.AccessToken;
|
||||||
import org.keycloak.representations.AccessTokenResponse;
|
import org.keycloak.representations.AccessTokenResponse;
|
||||||
import org.keycloak.representations.idm.CredentialRepresentation;
|
import org.keycloak.representations.idm.CredentialRepresentation;
|
||||||
import org.keycloak.services.ClientConnection;
|
import org.keycloak.services.ClientConnection;
|
||||||
import org.keycloak.services.ForbiddenException;
|
|
||||||
import org.keycloak.services.managers.AccessCodeEntry;
|
import org.keycloak.services.managers.AccessCodeEntry;
|
||||||
import org.keycloak.services.managers.AuthenticationManager;
|
import org.keycloak.services.managers.AuthenticationManager;
|
||||||
import org.keycloak.services.managers.AuthenticationManager.AuthenticationStatus;
|
import org.keycloak.services.managers.AuthenticationManager.AuthenticationStatus;
|
||||||
|
@ -212,7 +211,7 @@ public class TokenService {
|
||||||
return createError("not_enabled", "Resource Owner Password Credentials Grant not enabled", Response.Status.FORBIDDEN);
|
return createError("not_enabled", "Resource Owner Password Credentials Grant not enabled", Response.Status.FORBIDDEN);
|
||||||
}
|
}
|
||||||
|
|
||||||
audit.event(Events.LOGIN).detail(Details.AUTH_METHOD, "oauth_credentials").detail(Details.RESPONSE_TYPE, "token");
|
audit.event(EventType.LOGIN).detail(Details.AUTH_METHOD, "oauth_credentials").detail(Details.RESPONSE_TYPE, "token");
|
||||||
|
|
||||||
String username = form.getFirst(AuthenticationManager.FORM_USERNAME);
|
String username = form.getFirst(AuthenticationManager.FORM_USERNAME);
|
||||||
if (username == null) {
|
if (username == null) {
|
||||||
|
@ -294,7 +293,7 @@ public class TokenService {
|
||||||
throw new NotAcceptableException("HTTPS required");
|
throw new NotAcceptableException("HTTPS required");
|
||||||
}
|
}
|
||||||
|
|
||||||
audit.event(Events.REFRESH_TOKEN);
|
audit.event(EventType.REFRESH_TOKEN);
|
||||||
|
|
||||||
ClientModel client = authorizeClient(authorizationHeader, form, audit);
|
ClientModel client = authorizeClient(authorizationHeader, form, audit);
|
||||||
String refreshToken = form.getFirst(OAuth2Constants.REFRESH_TOKEN);
|
String refreshToken = form.getFirst(OAuth2Constants.REFRESH_TOKEN);
|
||||||
|
@ -334,7 +333,7 @@ public class TokenService {
|
||||||
boolean remember = rememberMe != null && rememberMe.equalsIgnoreCase("on");
|
boolean remember = rememberMe != null && rememberMe.equalsIgnoreCase("on");
|
||||||
logger.debug("*** Remember me: " + remember);
|
logger.debug("*** Remember me: " + remember);
|
||||||
|
|
||||||
audit.event(Events.LOGIN).client(clientId)
|
audit.event(EventType.LOGIN).client(clientId)
|
||||||
.detail(Details.REDIRECT_URI, redirect)
|
.detail(Details.REDIRECT_URI, redirect)
|
||||||
.detail(Details.RESPONSE_TYPE, "code")
|
.detail(Details.RESPONSE_TYPE, "code")
|
||||||
.detail(Details.AUTH_METHOD, "form")
|
.detail(Details.AUTH_METHOD, "form")
|
||||||
|
@ -383,12 +382,14 @@ public class TokenService {
|
||||||
authManager.expireRememberMeCookie(realm, uriInfo);
|
authManager.expireRememberMeCookie(realm, uriInfo);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
UserModel user = KeycloakModelUtils.findUserByNameOrEmail(realm, username);
|
||||||
|
if (user != null) {
|
||||||
|
audit.user(user);
|
||||||
|
}
|
||||||
|
|
||||||
switch (status) {
|
switch (status) {
|
||||||
case SUCCESS:
|
case SUCCESS:
|
||||||
case ACTIONS_REQUIRED:
|
case ACTIONS_REQUIRED:
|
||||||
UserModel user = KeycloakModelUtils.findUserByNameOrEmail(realm, username);
|
|
||||||
audit.user(user);
|
|
||||||
|
|
||||||
UserSessionModel session = realm.createUserSession(user, clientConnection.getRemoteAddr());
|
UserSessionModel session = realm.createUserSession(user, clientConnection.getRemoteAddr());
|
||||||
audit.session(session);
|
audit.session(session);
|
||||||
|
|
||||||
|
@ -429,7 +430,7 @@ public class TokenService {
|
||||||
String username = formData.getFirst("username");
|
String username = formData.getFirst("username");
|
||||||
String email = formData.getFirst("email");
|
String email = formData.getFirst("email");
|
||||||
|
|
||||||
audit.event(Events.REGISTER).client(clientId)
|
audit.event(EventType.REGISTER).client(clientId)
|
||||||
.detail(Details.REDIRECT_URI, redirect)
|
.detail(Details.REDIRECT_URI, redirect)
|
||||||
.detail(Details.RESPONSE_TYPE, "code")
|
.detail(Details.RESPONSE_TYPE, "code")
|
||||||
.detail(Details.USERNAME, username)
|
.detail(Details.USERNAME, username)
|
||||||
|
@ -545,7 +546,7 @@ public class TokenService {
|
||||||
throw new NotAcceptableException("HTTPS required");
|
throw new NotAcceptableException("HTTPS required");
|
||||||
}
|
}
|
||||||
|
|
||||||
audit.event(Events.CODE_TO_TOKEN);
|
audit.event(EventType.CODE_TO_TOKEN);
|
||||||
|
|
||||||
if (!realm.isEnabled()) {
|
if (!realm.isEnabled()) {
|
||||||
audit.error(Errors.REALM_DISABLED);
|
audit.error(Errors.REALM_DISABLED);
|
||||||
|
@ -724,7 +725,7 @@ public class TokenService {
|
||||||
final @QueryParam("scope") String scopeParam, final @QueryParam("state") String state, final @QueryParam("prompt") String prompt) {
|
final @QueryParam("scope") String scopeParam, final @QueryParam("state") String state, final @QueryParam("prompt") String prompt) {
|
||||||
logger.info("TokenService.loginPage");
|
logger.info("TokenService.loginPage");
|
||||||
|
|
||||||
audit.event(Events.LOGIN).client(clientId).detail(Details.REDIRECT_URI, redirect).detail(Details.RESPONSE_TYPE, "code");
|
audit.event(EventType.LOGIN).client(clientId).detail(Details.REDIRECT_URI, redirect).detail(Details.RESPONSE_TYPE, "code");
|
||||||
|
|
||||||
OAuthFlows oauth = Flows.oauth(providerSession, realm, request, uriInfo, authManager, tokenManager);
|
OAuthFlows oauth = Flows.oauth(providerSession, realm, request, uriInfo, authManager, tokenManager);
|
||||||
|
|
||||||
|
@ -784,7 +785,7 @@ public class TokenService {
|
||||||
final @QueryParam("scope") String scopeParam, final @QueryParam("state") String state) {
|
final @QueryParam("scope") String scopeParam, final @QueryParam("state") String state) {
|
||||||
logger.info("**********registerPage()");
|
logger.info("**********registerPage()");
|
||||||
|
|
||||||
audit.event(Events.REGISTER).client(clientId).detail(Details.REDIRECT_URI, redirect).detail(Details.RESPONSE_TYPE, "code");
|
audit.event(EventType.REGISTER).client(clientId).detail(Details.REDIRECT_URI, redirect).detail(Details.RESPONSE_TYPE, "code");
|
||||||
|
|
||||||
OAuthFlows oauth = Flows.oauth(providerSession, realm, request, uriInfo, authManager, tokenManager);
|
OAuthFlows oauth = Flows.oauth(providerSession, realm, request, uriInfo, authManager, tokenManager);
|
||||||
|
|
||||||
|
@ -833,7 +834,7 @@ public class TokenService {
|
||||||
public Response logout(final @QueryParam("session_state") String sessionState, final @QueryParam("redirect_uri") String redirectUri) {
|
public Response logout(final @QueryParam("session_state") String sessionState, final @QueryParam("redirect_uri") String redirectUri) {
|
||||||
// todo do we care if anybody can trigger this?
|
// todo do we care if anybody can trigger this?
|
||||||
|
|
||||||
audit.event(Events.LOGOUT);
|
audit.event(EventType.LOGOUT);
|
||||||
if (redirectUri != null) {
|
if (redirectUri != null) {
|
||||||
audit.detail(Details.REDIRECT_URI, redirectUri);
|
audit.detail(Details.REDIRECT_URI, redirectUri);
|
||||||
}
|
}
|
||||||
|
@ -874,7 +875,7 @@ public class TokenService {
|
||||||
@POST
|
@POST
|
||||||
@Consumes(MediaType.APPLICATION_FORM_URLENCODED)
|
@Consumes(MediaType.APPLICATION_FORM_URLENCODED)
|
||||||
public Response processOAuth(final MultivaluedMap<String, String> formData) {
|
public Response processOAuth(final MultivaluedMap<String, String> formData) {
|
||||||
audit.event(Events.LOGIN).detail(Details.RESPONSE_TYPE, "code");
|
audit.event(EventType.LOGIN).detail(Details.RESPONSE_TYPE, "code");
|
||||||
|
|
||||||
OAuthFlows oauth = Flows.oauth(providerSession, realm, request, uriInfo, authManager, tokenManager);
|
OAuthFlows oauth = Flows.oauth(providerSession, realm, request, uriInfo, authManager, tokenManager);
|
||||||
|
|
||||||
|
|
|
@ -7,6 +7,7 @@ import org.jboss.resteasy.spi.ResteasyProviderFactory;
|
||||||
import org.keycloak.audit.AuditProvider;
|
import org.keycloak.audit.AuditProvider;
|
||||||
import org.keycloak.audit.Event;
|
import org.keycloak.audit.Event;
|
||||||
import org.keycloak.audit.EventQuery;
|
import org.keycloak.audit.EventQuery;
|
||||||
|
import org.keycloak.audit.EventType;
|
||||||
import org.keycloak.models.ApplicationModel;
|
import org.keycloak.models.ApplicationModel;
|
||||||
import org.keycloak.models.KeycloakSession;
|
import org.keycloak.models.KeycloakSession;
|
||||||
import org.keycloak.models.ModelDuplicateException;
|
import org.keycloak.models.ModelDuplicateException;
|
||||||
|
@ -236,7 +237,7 @@ public class RealmAdminResource {
|
||||||
query.client(client);
|
query.client(client);
|
||||||
}
|
}
|
||||||
if (event != null) {
|
if (event != null) {
|
||||||
query.event(event);
|
query.event(EventType.valueOf(event));
|
||||||
}
|
}
|
||||||
if (user != null) {
|
if (user != null) {
|
||||||
query.user(user);
|
query.user(user);
|
||||||
|
|
|
@ -26,7 +26,7 @@ import org.jboss.resteasy.spi.HttpRequest;
|
||||||
import org.keycloak.OAuth2Constants;
|
import org.keycloak.OAuth2Constants;
|
||||||
import org.keycloak.audit.Audit;
|
import org.keycloak.audit.Audit;
|
||||||
import org.keycloak.audit.Details;
|
import org.keycloak.audit.Details;
|
||||||
import org.keycloak.audit.Events;
|
import org.keycloak.audit.EventType;
|
||||||
import org.keycloak.models.ApplicationModel;
|
import org.keycloak.models.ApplicationModel;
|
||||||
import org.keycloak.models.ClientModel;
|
import org.keycloak.models.ClientModel;
|
||||||
import org.keycloak.models.Constants;
|
import org.keycloak.models.Constants;
|
||||||
|
@ -140,7 +140,7 @@ public class OAuthFlows {
|
||||||
|
|
||||||
RequiredAction action = user.getRequiredActions().iterator().next();
|
RequiredAction action = user.getRequiredActions().iterator().next();
|
||||||
if (action.equals(RequiredAction.VERIFY_EMAIL)) {
|
if (action.equals(RequiredAction.VERIFY_EMAIL)) {
|
||||||
audit.clone().event(Events.SEND_VERIFY_EMAIL).detail(Details.EMAIL, accessCode.getUser().getEmail()).success();
|
audit.clone().event(EventType.SEND_VERIFY_EMAIL).detail(Details.EMAIL, accessCode.getUser().getEmail()).success();
|
||||||
}
|
}
|
||||||
|
|
||||||
return Flows.forms(providerSession, realm, uriInfo).setAccessCode(accessCode.getId(), accessCode.getCode()).setUser(user)
|
return Flows.forms(providerSession, realm, uriInfo).setAccessCode(accessCode.getId(), accessCode.getCode()).setUser(user)
|
||||||
|
|
|
@ -4,6 +4,8 @@ import org.codehaus.jackson.JsonNode;
|
||||||
import org.keycloak.Config;
|
import org.keycloak.Config;
|
||||||
import org.keycloak.util.StringPropertyReplacer;
|
import org.keycloak.util.StringPropertyReplacer;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
|
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
|
||||||
*/
|
*/
|
||||||
|
@ -62,6 +64,26 @@ public class JsonConfigProvider implements Config.ConfigProvider {
|
||||||
return StringPropertyReplacer.replaceProperties(n.getTextValue());
|
return StringPropertyReplacer.replaceProperties(n.getTextValue());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String[] getArray(String key) {
|
||||||
|
if (config == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
JsonNode n = config.get(key);
|
||||||
|
if (n == null) {
|
||||||
|
return null;
|
||||||
|
} else if (n.isArray()) {
|
||||||
|
ArrayList<String> l = new ArrayList<String>();
|
||||||
|
for (JsonNode e : n) {
|
||||||
|
l.add(StringPropertyReplacer.replaceProperties(e.getTextValue()));
|
||||||
|
}
|
||||||
|
return (String[]) l.toArray();
|
||||||
|
} else {
|
||||||
|
return new String[] { StringPropertyReplacer.replaceProperties(n.getTextValue()) };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Integer getInt(String key) {
|
public Integer getInt(String key) {
|
||||||
return getInt(key, null);
|
return getInt(key, null);
|
||||||
|
|
|
@ -100,6 +100,11 @@
|
||||||
<artifactId>keycloak-audit-jboss-logging</artifactId>
|
<artifactId>keycloak-audit-jboss-logging</artifactId>
|
||||||
<version>${project.version}</version>
|
<version>${project.version}</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.keycloak</groupId>
|
||||||
|
<artifactId>keycloak-audit-email</artifactId>
|
||||||
|
<version>${project.version}</version>
|
||||||
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.keycloak</groupId>
|
<groupId>org.keycloak</groupId>
|
||||||
<artifactId>keycloak-core</artifactId>
|
<artifactId>keycloak-core</artifactId>
|
||||||
|
|
|
@ -12,6 +12,7 @@ import org.keycloak.audit.AuditListener;
|
||||||
import org.keycloak.audit.AuditListenerFactory;
|
import org.keycloak.audit.AuditListenerFactory;
|
||||||
import org.keycloak.audit.Details;
|
import org.keycloak.audit.Details;
|
||||||
import org.keycloak.audit.Event;
|
import org.keycloak.audit.Event;
|
||||||
|
import org.keycloak.audit.EventType;
|
||||||
import org.keycloak.models.ClientModel;
|
import org.keycloak.models.ClientModel;
|
||||||
import org.keycloak.models.RealmModel;
|
import org.keycloak.models.RealmModel;
|
||||||
import org.keycloak.models.UserModel;
|
import org.keycloak.models.UserModel;
|
||||||
|
@ -110,12 +111,12 @@ public class AssertEvents implements TestRule, AuditListenerFactory {
|
||||||
events.clear();
|
events.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
public ExpectedEvent expectRequiredAction(String event) {
|
public ExpectedEvent expectRequiredAction(EventType event) {
|
||||||
return expectLogin().event(event).session(isUUID());
|
return expectLogin().event(event).session(isUUID());
|
||||||
}
|
}
|
||||||
|
|
||||||
public ExpectedEvent expectLogin() {
|
public ExpectedEvent expectLogin() {
|
||||||
return expect("login")
|
return expect(EventType.LOGIN)
|
||||||
.detail(Details.CODE_ID, isCodeId())
|
.detail(Details.CODE_ID, isCodeId())
|
||||||
.detail(Details.USERNAME, DEFAULT_USERNAME)
|
.detail(Details.USERNAME, DEFAULT_USERNAME)
|
||||||
.detail(Details.RESPONSE_TYPE, "code")
|
.detail(Details.RESPONSE_TYPE, "code")
|
||||||
|
@ -125,7 +126,7 @@ public class AssertEvents implements TestRule, AuditListenerFactory {
|
||||||
}
|
}
|
||||||
|
|
||||||
public ExpectedEvent expectCodeToToken(String codeId, String sessionId) {
|
public ExpectedEvent expectCodeToToken(String codeId, String sessionId) {
|
||||||
return expect("code_to_token")
|
return expect(EventType.CODE_TO_TOKEN)
|
||||||
.detail(Details.CODE_ID, codeId)
|
.detail(Details.CODE_ID, codeId)
|
||||||
.detail(Details.TOKEN_ID, isUUID())
|
.detail(Details.TOKEN_ID, isUUID())
|
||||||
.detail(Details.REFRESH_TOKEN_ID, isUUID())
|
.detail(Details.REFRESH_TOKEN_ID, isUUID())
|
||||||
|
@ -133,7 +134,7 @@ public class AssertEvents implements TestRule, AuditListenerFactory {
|
||||||
}
|
}
|
||||||
|
|
||||||
public ExpectedEvent expectRefresh(String refreshTokenId, String sessionId) {
|
public ExpectedEvent expectRefresh(String refreshTokenId, String sessionId) {
|
||||||
return expect("refresh_token")
|
return expect(EventType.REFRESH_TOKEN)
|
||||||
.detail(Details.TOKEN_ID, isUUID())
|
.detail(Details.TOKEN_ID, isUUID())
|
||||||
.detail(Details.REFRESH_TOKEN_ID, refreshTokenId)
|
.detail(Details.REFRESH_TOKEN_ID, refreshTokenId)
|
||||||
.detail(Details.UPDATED_REFRESH_TOKEN_ID, isUUID())
|
.detail(Details.UPDATED_REFRESH_TOKEN_ID, isUUID())
|
||||||
|
@ -141,14 +142,14 @@ public class AssertEvents implements TestRule, AuditListenerFactory {
|
||||||
}
|
}
|
||||||
|
|
||||||
public ExpectedEvent expectLogout(String sessionId) {
|
public ExpectedEvent expectLogout(String sessionId) {
|
||||||
return expect("logout").client((String) null)
|
return expect(EventType.LOGOUT).client((String) null)
|
||||||
.detail(Details.REDIRECT_URI, DEFAULT_REDIRECT_URI)
|
.detail(Details.REDIRECT_URI, DEFAULT_REDIRECT_URI)
|
||||||
.session(sessionId);
|
.session(sessionId);
|
||||||
}
|
}
|
||||||
|
|
||||||
public ExpectedEvent expectRegister(String username, String email) {
|
public ExpectedEvent expectRegister(String username, String email) {
|
||||||
UserRepresentation user = keycloak.getUser("test", username);
|
UserRepresentation user = keycloak.getUser("test", username);
|
||||||
return expect("register")
|
return expect(EventType.REGISTER)
|
||||||
.user(user != null ? user.getId() : null)
|
.user(user != null ? user.getId() : null)
|
||||||
.detail(Details.USERNAME, username)
|
.detail(Details.USERNAME, username)
|
||||||
.detail(Details.EMAIL, email)
|
.detail(Details.EMAIL, email)
|
||||||
|
@ -157,11 +158,11 @@ public class AssertEvents implements TestRule, AuditListenerFactory {
|
||||||
.detail(Details.REDIRECT_URI, DEFAULT_REDIRECT_URI);
|
.detail(Details.REDIRECT_URI, DEFAULT_REDIRECT_URI);
|
||||||
}
|
}
|
||||||
|
|
||||||
public ExpectedEvent expectAccount(String event) {
|
public ExpectedEvent expectAccount(EventType event) {
|
||||||
return expect(event).client("account");
|
return expect(event).client("account");
|
||||||
}
|
}
|
||||||
|
|
||||||
public ExpectedEvent expect(String event) {
|
public ExpectedEvent expect(EventType event) {
|
||||||
return new ExpectedEvent()
|
return new ExpectedEvent()
|
||||||
.realm(DEFAULT_REALM)
|
.realm(DEFAULT_REALM)
|
||||||
.client(DEFAULT_CLIENT_ID)
|
.client(DEFAULT_CLIENT_ID)
|
||||||
|
@ -253,7 +254,7 @@ public class AssertEvents implements TestRule, AuditListenerFactory {
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public ExpectedEvent event(String e) {
|
public ExpectedEvent event(EventType e) {
|
||||||
expected.setEvent(e);
|
expected.setEvent(e);
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
@ -291,6 +292,9 @@ public class AssertEvents implements TestRule, AuditListenerFactory {
|
||||||
}
|
}
|
||||||
|
|
||||||
public Event assertEvent(Event actual) {
|
public Event assertEvent(Event actual) {
|
||||||
|
if (expected.getError() != null && !expected.getEvent().toString().endsWith("_ERROR")) {
|
||||||
|
expected.setEvent(EventType.valueOf(expected.getEvent().toString() + "_ERROR"));
|
||||||
|
}
|
||||||
Assert.assertEquals(expected.getEvent(), actual.getEvent());
|
Assert.assertEquals(expected.getEvent(), actual.getEvent());
|
||||||
Assert.assertEquals(expected.getRealmId(), actual.getRealmId());
|
Assert.assertEquals(expected.getRealmId(), actual.getRealmId());
|
||||||
Assert.assertEquals(expected.getClientId(), actual.getClientId());
|
Assert.assertEquals(expected.getClientId(), actual.getClientId());
|
||||||
|
|
|
@ -29,6 +29,7 @@ import org.junit.Rule;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.keycloak.audit.Details;
|
import org.keycloak.audit.Details;
|
||||||
import org.keycloak.audit.Event;
|
import org.keycloak.audit.Event;
|
||||||
|
import org.keycloak.audit.EventType;
|
||||||
import org.keycloak.models.ApplicationModel;
|
import org.keycloak.models.ApplicationModel;
|
||||||
import org.keycloak.models.PasswordPolicy;
|
import org.keycloak.models.PasswordPolicy;
|
||||||
import org.keycloak.models.RealmModel;
|
import org.keycloak.models.RealmModel;
|
||||||
|
@ -215,7 +216,7 @@ public class AccountTest {
|
||||||
|
|
||||||
Assert.assertEquals("Your password has been updated", profilePage.getSuccess());
|
Assert.assertEquals("Your password has been updated", profilePage.getSuccess());
|
||||||
|
|
||||||
events.expectAccount("update_password").assertEvent();
|
events.expectAccount(EventType.UPDATE_PASSWORD).assertEvent();
|
||||||
|
|
||||||
changePasswordPage.logout();
|
changePasswordPage.logout();
|
||||||
|
|
||||||
|
@ -226,7 +227,7 @@ public class AccountTest {
|
||||||
|
|
||||||
Assert.assertEquals("Invalid username or password.", loginPage.getError());
|
Assert.assertEquals("Invalid username or password.", loginPage.getError());
|
||||||
|
|
||||||
events.expectLogin().user((String) null).session((String) null).error("invalid_user_credentials").removeDetail(Details.CODE_ID).assertEvent();
|
events.expectLogin().session((String) null).error("invalid_user_credentials").removeDetail(Details.CODE_ID).assertEvent();
|
||||||
|
|
||||||
loginPage.open();
|
loginPage.open();
|
||||||
loginPage.login("test-user@localhost", "new-password");
|
loginPage.login("test-user@localhost", "new-password");
|
||||||
|
@ -260,7 +261,7 @@ public class AccountTest {
|
||||||
|
|
||||||
Assert.assertEquals("Your password has been updated", profilePage.getSuccess());
|
Assert.assertEquals("Your password has been updated", profilePage.getSuccess());
|
||||||
|
|
||||||
events.expectAccount("update_password").assertEvent();
|
events.expectAccount(EventType.UPDATE_PASSWORD).assertEvent();
|
||||||
} finally {
|
} finally {
|
||||||
keycloakRule.configure(new KeycloakRule.KeycloakSetup() {
|
keycloakRule.configure(new KeycloakRule.KeycloakSetup() {
|
||||||
@Override
|
@Override
|
||||||
|
@ -317,8 +318,8 @@ public class AccountTest {
|
||||||
Assert.assertEquals("New last", profilePage.getLastName());
|
Assert.assertEquals("New last", profilePage.getLastName());
|
||||||
Assert.assertEquals("new@email.com", profilePage.getEmail());
|
Assert.assertEquals("new@email.com", profilePage.getEmail());
|
||||||
|
|
||||||
events.expectAccount("update_profile").assertEvent();
|
events.expectAccount(EventType.UPDATE_PROFILE).assertEvent();
|
||||||
events.expectAccount("update_email").detail(Details.PREVIOUS_EMAIL, "test-user@localhost").detail(Details.UPDATED_EMAIL, "new@email.com").assertEvent();
|
events.expectAccount(EventType.UPDATE_EMAIL).detail(Details.PREVIOUS_EMAIL, "test-user@localhost").detail(Details.UPDATED_EMAIL, "new@email.com").assertEvent();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -341,13 +342,13 @@ public class AccountTest {
|
||||||
|
|
||||||
Assert.assertEquals("Google authenticator configured.", profilePage.getSuccess());
|
Assert.assertEquals("Google authenticator configured.", profilePage.getSuccess());
|
||||||
|
|
||||||
events.expectAccount("update_totp").assertEvent();
|
events.expectAccount(EventType.UPDATE_TOTP).assertEvent();
|
||||||
|
|
||||||
Assert.assertTrue(driver.getPageSource().contains("pficon-delete"));
|
Assert.assertTrue(driver.getPageSource().contains("pficon-delete"));
|
||||||
|
|
||||||
totpPage.removeTotp();
|
totpPage.removeTotp();
|
||||||
|
|
||||||
events.expectAccount("remove_totp").assertEvent();
|
events.expectAccount(EventType.REMOVE_TOTP).assertEvent();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -405,7 +406,7 @@ public class AccountTest {
|
||||||
Iterator<List<String>> itr = logPage.getEvents().iterator();
|
Iterator<List<String>> itr = logPage.getEvents().iterator();
|
||||||
for (Event event : e) {
|
for (Event event : e) {
|
||||||
List<String> a = itr.next();
|
List<String> a = itr.next();
|
||||||
Assert.assertEquals(event.getEvent().replace('_', ' '), a.get(1));
|
Assert.assertEquals(event.getEvent().toString().replace('_', ' ').toLowerCase(), a.get(1));
|
||||||
Assert.assertEquals(event.getIpAddress(), a.get(2));
|
Assert.assertEquals(event.getIpAddress(), a.get(2));
|
||||||
Assert.assertEquals(event.getClientId(), a.get(3));
|
Assert.assertEquals(event.getClientId(), a.get(3));
|
||||||
}
|
}
|
||||||
|
|
|
@ -28,6 +28,7 @@ import org.junit.Rule;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.keycloak.audit.Details;
|
import org.keycloak.audit.Details;
|
||||||
import org.keycloak.audit.Event;
|
import org.keycloak.audit.Event;
|
||||||
|
import org.keycloak.audit.EventType;
|
||||||
import org.keycloak.models.RealmModel;
|
import org.keycloak.models.RealmModel;
|
||||||
import org.keycloak.models.UserModel;
|
import org.keycloak.models.UserModel;
|
||||||
import org.keycloak.services.managers.RealmManager;
|
import org.keycloak.services.managers.RealmManager;
|
||||||
|
@ -116,7 +117,7 @@ public class RequiredActionEmailVerificationTest {
|
||||||
String body = (String) message.getContent();
|
String body = (String) message.getContent();
|
||||||
String verificationUrl = MailUtil.getLink(body);
|
String verificationUrl = MailUtil.getLink(body);
|
||||||
|
|
||||||
Event sendEvent = events.expectRequiredAction("send_verify_email").detail("email", "test-user@localhost").assertEvent();
|
Event sendEvent = events.expectRequiredAction(EventType.SEND_VERIFY_EMAIL).detail("email", "test-user@localhost").assertEvent();
|
||||||
String sessionId = sendEvent.getSessionId();
|
String sessionId = sendEvent.getSessionId();
|
||||||
|
|
||||||
String mailCodeId = sendEvent.getDetails().get(Details.CODE_ID);
|
String mailCodeId = sendEvent.getDetails().get(Details.CODE_ID);
|
||||||
|
@ -125,7 +126,7 @@ public class RequiredActionEmailVerificationTest {
|
||||||
|
|
||||||
driver.navigate().to(verificationUrl.trim());
|
driver.navigate().to(verificationUrl.trim());
|
||||||
|
|
||||||
events.expectRequiredAction("verify_email").session(sessionId).detail("email", "test-user@localhost").detail(Details.CODE_ID, mailCodeId).assertEvent();
|
events.expectRequiredAction(EventType.VERIFY_EMAIL).session(sessionId).detail("email", "test-user@localhost").detail(Details.CODE_ID, mailCodeId).assertEvent();
|
||||||
|
|
||||||
Assert.assertEquals(RequestType.AUTH_RESPONSE, appPage.getRequestType());
|
Assert.assertEquals(RequestType.AUTH_RESPONSE, appPage.getRequestType());
|
||||||
|
|
||||||
|
@ -148,7 +149,7 @@ public class RequiredActionEmailVerificationTest {
|
||||||
|
|
||||||
String body = (String) message.getContent();
|
String body = (String) message.getContent();
|
||||||
|
|
||||||
Event sendEvent = events.expectRequiredAction("send_verify_email").user(userId).detail("username", "verifyEmail").detail("email", "email").assertEvent();
|
Event sendEvent = events.expectRequiredAction(EventType.SEND_VERIFY_EMAIL).user(userId).detail("username", "verifyEmail").detail("email", "email").assertEvent();
|
||||||
String sessionId = sendEvent.getSessionId();
|
String sessionId = sendEvent.getSessionId();
|
||||||
|
|
||||||
String mailCodeId = sendEvent.getDetails().get(Details.CODE_ID);
|
String mailCodeId = sendEvent.getDetails().get(Details.CODE_ID);
|
||||||
|
@ -159,7 +160,7 @@ public class RequiredActionEmailVerificationTest {
|
||||||
|
|
||||||
Assert.assertEquals(RequestType.AUTH_RESPONSE, appPage.getRequestType());
|
Assert.assertEquals(RequestType.AUTH_RESPONSE, appPage.getRequestType());
|
||||||
|
|
||||||
events.expectRequiredAction("verify_email").user(userId).session(sessionId).detail("username", "verifyEmail").detail("email", "email").detail(Details.CODE_ID, mailCodeId).assertEvent();
|
events.expectRequiredAction(EventType.VERIFY_EMAIL).user(userId).session(sessionId).detail("username", "verifyEmail").detail("email", "email").detail(Details.CODE_ID, mailCodeId).assertEvent();
|
||||||
|
|
||||||
events.expectLogin().user(userId).session(sessionId).detail("username", "verifyEmail").detail(Details.CODE_ID, mailCodeId).assertEvent();
|
events.expectLogin().user(userId).session(sessionId).detail("username", "verifyEmail").detail(Details.CODE_ID, mailCodeId).assertEvent();
|
||||||
}
|
}
|
||||||
|
@ -173,7 +174,7 @@ public class RequiredActionEmailVerificationTest {
|
||||||
|
|
||||||
Assert.assertEquals(1, greenMail.getReceivedMessages().length);
|
Assert.assertEquals(1, greenMail.getReceivedMessages().length);
|
||||||
|
|
||||||
Event sendEvent = events.expectRequiredAction("send_verify_email").detail("email", "test-user@localhost").assertEvent();
|
Event sendEvent = events.expectRequiredAction(EventType.SEND_VERIFY_EMAIL).detail("email", "test-user@localhost").assertEvent();
|
||||||
String sessionId = sendEvent.getSessionId();
|
String sessionId = sendEvent.getSessionId();
|
||||||
|
|
||||||
String mailCodeId = sendEvent.getDetails().get(Details.CODE_ID);
|
String mailCodeId = sendEvent.getDetails().get(Details.CODE_ID);
|
||||||
|
@ -186,7 +187,7 @@ public class RequiredActionEmailVerificationTest {
|
||||||
|
|
||||||
String body = (String) message.getContent();
|
String body = (String) message.getContent();
|
||||||
|
|
||||||
events.expectRequiredAction("send_verify_email").session(sessionId).detail("email", "test-user@localhost").assertEvent(sendEvent);
|
events.expectRequiredAction(EventType.SEND_VERIFY_EMAIL).session(sessionId).detail("email", "test-user@localhost").assertEvent(sendEvent);
|
||||||
|
|
||||||
String verificationUrl = MailUtil.getLink(body);
|
String verificationUrl = MailUtil.getLink(body);
|
||||||
|
|
||||||
|
@ -194,7 +195,7 @@ public class RequiredActionEmailVerificationTest {
|
||||||
|
|
||||||
Assert.assertEquals(RequestType.AUTH_RESPONSE, appPage.getRequestType());
|
Assert.assertEquals(RequestType.AUTH_RESPONSE, appPage.getRequestType());
|
||||||
|
|
||||||
events.expectRequiredAction("verify_email").session(sessionId).detail("email", "test-user@localhost").detail(Details.CODE_ID, mailCodeId).assertEvent();
|
events.expectRequiredAction(EventType.VERIFY_EMAIL).session(sessionId).detail("email", "test-user@localhost").detail(Details.CODE_ID, mailCodeId).assertEvent();
|
||||||
|
|
||||||
events.expectLogin().session(sessionId).assertEvent();
|
events.expectLogin().session(sessionId).assertEvent();
|
||||||
}
|
}
|
||||||
|
|
|
@ -26,6 +26,7 @@ import org.junit.ClassRule;
|
||||||
import org.junit.Rule;
|
import org.junit.Rule;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.keycloak.audit.Details;
|
import org.keycloak.audit.Details;
|
||||||
|
import org.keycloak.audit.EventType;
|
||||||
import org.keycloak.models.RealmModel;
|
import org.keycloak.models.RealmModel;
|
||||||
import org.keycloak.models.UserModel;
|
import org.keycloak.models.UserModel;
|
||||||
import org.keycloak.models.UserModel.RequiredAction;
|
import org.keycloak.models.UserModel.RequiredAction;
|
||||||
|
@ -112,7 +113,7 @@ public class RequiredActionMultipleActionsTest {
|
||||||
public String updatePassword(String sessionId) {
|
public String updatePassword(String sessionId) {
|
||||||
changePasswordPage.changePassword("new-password", "new-password");
|
changePasswordPage.changePassword("new-password", "new-password");
|
||||||
|
|
||||||
AssertEvents.ExpectedEvent expectedEvent = events.expectRequiredAction("update_password");
|
AssertEvents.ExpectedEvent expectedEvent = events.expectRequiredAction(EventType.UPDATE_PASSWORD);
|
||||||
if (sessionId != null) {
|
if (sessionId != null) {
|
||||||
expectedEvent.session(sessionId);
|
expectedEvent.session(sessionId);
|
||||||
}
|
}
|
||||||
|
@ -122,12 +123,12 @@ public class RequiredActionMultipleActionsTest {
|
||||||
public String updateProfile(String sessionId) {
|
public String updateProfile(String sessionId) {
|
||||||
updateProfilePage.update("New first", "New last", "new@email.com");
|
updateProfilePage.update("New first", "New last", "new@email.com");
|
||||||
|
|
||||||
AssertEvents.ExpectedEvent expectedEvent = events.expectRequiredAction("update_profile");
|
AssertEvents.ExpectedEvent expectedEvent = events.expectRequiredAction(EventType.UPDATE_PROFILE);
|
||||||
if (sessionId != null) {
|
if (sessionId != null) {
|
||||||
expectedEvent.session(sessionId);
|
expectedEvent.session(sessionId);
|
||||||
}
|
}
|
||||||
sessionId = expectedEvent.assertEvent().getSessionId();
|
sessionId = expectedEvent.assertEvent().getSessionId();
|
||||||
events.expectRequiredAction("update_email").session(sessionId).detail(Details.PREVIOUS_EMAIL, "test-user@localhost").detail(Details.UPDATED_EMAIL, "new@email.com").assertEvent();
|
events.expectRequiredAction(EventType.UPDATE_EMAIL).session(sessionId).detail(Details.PREVIOUS_EMAIL, "test-user@localhost").detail(Details.UPDATED_EMAIL, "new@email.com").assertEvent();
|
||||||
return sessionId;
|
return sessionId;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -26,6 +26,7 @@ import org.junit.ClassRule;
|
||||||
import org.junit.Rule;
|
import org.junit.Rule;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.keycloak.audit.Event;
|
import org.keycloak.audit.Event;
|
||||||
|
import org.keycloak.audit.EventType;
|
||||||
import org.keycloak.models.RealmModel;
|
import org.keycloak.models.RealmModel;
|
||||||
import org.keycloak.models.UserModel;
|
import org.keycloak.models.UserModel;
|
||||||
import org.keycloak.models.UserModel.RequiredAction;
|
import org.keycloak.models.UserModel.RequiredAction;
|
||||||
|
@ -93,7 +94,7 @@ public class RequiredActionResetPasswordTest {
|
||||||
changePasswordPage.assertCurrent();
|
changePasswordPage.assertCurrent();
|
||||||
changePasswordPage.changePassword("new-password", "new-password");
|
changePasswordPage.changePassword("new-password", "new-password");
|
||||||
|
|
||||||
String sessionId = events.expectRequiredAction("update_password").assertEvent().getSessionId();
|
String sessionId = events.expectRequiredAction(EventType.UPDATE_PASSWORD).assertEvent().getSessionId();
|
||||||
|
|
||||||
Assert.assertEquals(RequestType.AUTH_RESPONSE, appPage.getRequestType());
|
Assert.assertEquals(RequestType.AUTH_RESPONSE, appPage.getRequestType());
|
||||||
|
|
||||||
|
|
|
@ -27,6 +27,7 @@ import org.junit.Rule;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.keycloak.audit.Details;
|
import org.keycloak.audit.Details;
|
||||||
import org.keycloak.audit.Event;
|
import org.keycloak.audit.Event;
|
||||||
|
import org.keycloak.audit.EventType;
|
||||||
import org.keycloak.models.RealmModel;
|
import org.keycloak.models.RealmModel;
|
||||||
import org.keycloak.models.utils.TimeBasedOTP;
|
import org.keycloak.models.utils.TimeBasedOTP;
|
||||||
import org.keycloak.representations.idm.CredentialRepresentation;
|
import org.keycloak.representations.idm.CredentialRepresentation;
|
||||||
|
@ -109,7 +110,7 @@ public class RequiredActionTotpSetupTest {
|
||||||
|
|
||||||
totpPage.configure(totp.generate(totpPage.getTotpSecret()));
|
totpPage.configure(totp.generate(totpPage.getTotpSecret()));
|
||||||
|
|
||||||
String sessionId = events.expectRequiredAction("update_totp").user(userId).detail(Details.USERNAME, "setupTotp").assertEvent().getSessionId();
|
String sessionId = events.expectRequiredAction(EventType.UPDATE_TOTP).user(userId).detail(Details.USERNAME, "setupTotp").assertEvent().getSessionId();
|
||||||
|
|
||||||
Assert.assertEquals(RequestType.AUTH_RESPONSE, appPage.getRequestType());
|
Assert.assertEquals(RequestType.AUTH_RESPONSE, appPage.getRequestType());
|
||||||
|
|
||||||
|
@ -127,7 +128,7 @@ public class RequiredActionTotpSetupTest {
|
||||||
|
|
||||||
totpPage.configure(totp.generate(totpSecret));
|
totpPage.configure(totp.generate(totpSecret));
|
||||||
|
|
||||||
String sessionId = events.expectRequiredAction("update_totp").assertEvent().getSessionId();
|
String sessionId = events.expectRequiredAction(EventType.UPDATE_TOTP).assertEvent().getSessionId();
|
||||||
|
|
||||||
Assert.assertEquals(RequestType.AUTH_RESPONSE, appPage.getRequestType());
|
Assert.assertEquals(RequestType.AUTH_RESPONSE, appPage.getRequestType());
|
||||||
|
|
||||||
|
@ -164,7 +165,7 @@ public class RequiredActionTotpSetupTest {
|
||||||
// After totp config, user should be on the app page
|
// After totp config, user should be on the app page
|
||||||
Assert.assertEquals(RequestType.AUTH_RESPONSE, appPage.getRequestType());
|
Assert.assertEquals(RequestType.AUTH_RESPONSE, appPage.getRequestType());
|
||||||
|
|
||||||
events.expectRequiredAction("update_totp").user(userId).detail(Details.USERNAME, "setupTotp2").assertEvent();
|
events.expectRequiredAction(EventType.UPDATE_TOTP).user(userId).detail(Details.USERNAME, "setupTotp2").assertEvent();
|
||||||
|
|
||||||
Event loginEvent = events.expectLogin().user(userId).detail(Details.USERNAME, "setupTotp2").assertEvent();
|
Event loginEvent = events.expectLogin().user(userId).detail(Details.USERNAME, "setupTotp2").assertEvent();
|
||||||
|
|
||||||
|
@ -192,7 +193,7 @@ public class RequiredActionTotpSetupTest {
|
||||||
// Remove google authentificator
|
// Remove google authentificator
|
||||||
accountTotpPage.removeTotp();
|
accountTotpPage.removeTotp();
|
||||||
|
|
||||||
events.expectAccount("remove_totp").user(userId).assertEvent();
|
events.expectAccount(EventType.REMOVE_TOTP).user(userId).assertEvent();
|
||||||
|
|
||||||
// Logout
|
// Logout
|
||||||
oauth.openLogout();
|
oauth.openLogout();
|
||||||
|
@ -206,7 +207,7 @@ public class RequiredActionTotpSetupTest {
|
||||||
totpPage.assertCurrent();
|
totpPage.assertCurrent();
|
||||||
totpPage.configure(totp.generate(totpPage.getTotpSecret()));
|
totpPage.configure(totp.generate(totpPage.getTotpSecret()));
|
||||||
|
|
||||||
String sessionId = events.expectRequiredAction("update_totp").user(userId).detail(Details.USERNAME, "setupTotp2").assertEvent().getSessionId();
|
String sessionId = events.expectRequiredAction(EventType.UPDATE_TOTP).user(userId).detail(Details.USERNAME, "setupTotp2").assertEvent().getSessionId();
|
||||||
|
|
||||||
Assert.assertEquals(RequestType.AUTH_RESPONSE, appPage.getRequestType());
|
Assert.assertEquals(RequestType.AUTH_RESPONSE, appPage.getRequestType());
|
||||||
|
|
||||||
|
|
|
@ -27,6 +27,7 @@ import org.junit.ClassRule;
|
||||||
import org.junit.Rule;
|
import org.junit.Rule;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.keycloak.audit.Details;
|
import org.keycloak.audit.Details;
|
||||||
|
import org.keycloak.audit.EventType;
|
||||||
import org.keycloak.models.RealmModel;
|
import org.keycloak.models.RealmModel;
|
||||||
import org.keycloak.models.UserModel;
|
import org.keycloak.models.UserModel;
|
||||||
import org.keycloak.services.managers.RealmManager;
|
import org.keycloak.services.managers.RealmManager;
|
||||||
|
@ -87,8 +88,8 @@ public class RequiredActionUpdateProfileTest {
|
||||||
|
|
||||||
updateProfilePage.update("New first", "New last", "new@email.com");
|
updateProfilePage.update("New first", "New last", "new@email.com");
|
||||||
|
|
||||||
String sessionId = events.expectRequiredAction("update_profile").assertEvent().getSessionId();
|
String sessionId = events.expectRequiredAction(EventType.UPDATE_PROFILE).assertEvent().getSessionId();
|
||||||
events.expectRequiredAction("update_email").session(sessionId).detail(Details.PREVIOUS_EMAIL, "test-user@localhost").detail(Details.UPDATED_EMAIL, "new@email.com").assertEvent();
|
events.expectRequiredAction(EventType.UPDATE_EMAIL).session(sessionId).detail(Details.PREVIOUS_EMAIL, "test-user@localhost").detail(Details.UPDATED_EMAIL, "new@email.com").assertEvent();
|
||||||
|
|
||||||
Assert.assertEquals(RequestType.AUTH_RESPONSE, appPage.getRequestType());
|
Assert.assertEquals(RequestType.AUTH_RESPONSE, appPage.getRequestType());
|
||||||
|
|
||||||
|
|
|
@ -94,7 +94,7 @@ public class LoginTest {
|
||||||
|
|
||||||
Assert.assertEquals("Invalid username or password.", loginPage.getError());
|
Assert.assertEquals("Invalid username or password.", loginPage.getError());
|
||||||
|
|
||||||
events.expectLogin().user((String) null).session((String) null).error("invalid_user_credentials").detail(Details.USERNAME, "login-test").removeDetail(Details.CODE_ID).assertEvent();
|
events.expectLogin().user(userId).session((String) null).error("invalid_user_credentials").detail(Details.USERNAME, "login-test").removeDetail(Details.CODE_ID).assertEvent();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|
|
@ -111,7 +111,7 @@ public class LoginTotpTest {
|
||||||
loginPage.assertCurrent();
|
loginPage.assertCurrent();
|
||||||
Assert.assertEquals("Invalid username or password.", loginPage.getError());
|
Assert.assertEquals("Invalid username or password.", loginPage.getError());
|
||||||
|
|
||||||
events.expectLogin().error("invalid_user_credentials").removeDetail(Details.CODE_ID).user((String) null).session((String) null).assertEvent();
|
events.expectLogin().error("invalid_user_credentials").removeDetail(Details.CODE_ID).session((String) null).assertEvent();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|
|
@ -27,6 +27,7 @@ import org.junit.Rule;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.keycloak.audit.Details;
|
import org.keycloak.audit.Details;
|
||||||
import org.keycloak.audit.Event;
|
import org.keycloak.audit.Event;
|
||||||
|
import org.keycloak.audit.EventType;
|
||||||
import org.keycloak.models.PasswordPolicy;
|
import org.keycloak.models.PasswordPolicy;
|
||||||
import org.keycloak.models.RealmModel;
|
import org.keycloak.models.RealmModel;
|
||||||
import org.keycloak.models.UserCredentialModel;
|
import org.keycloak.models.UserCredentialModel;
|
||||||
|
@ -125,7 +126,7 @@ public class ResetPasswordTest {
|
||||||
|
|
||||||
resetPasswordPage.assertCurrent();
|
resetPasswordPage.assertCurrent();
|
||||||
|
|
||||||
String sessionId = events.expectRequiredAction("send_reset_password").user(userId).detail(Details.USERNAME, username).detail(Details.EMAIL, "login@test.com").assertEvent().getSessionId();
|
String sessionId = events.expectRequiredAction(EventType.SEND_RESET_PASSWORD).user(userId).detail(Details.USERNAME, username).detail(Details.EMAIL, "login@test.com").assertEvent().getSessionId();
|
||||||
|
|
||||||
Assert.assertEquals("You should receive an email shortly with further instructions.", resetPasswordPage.getSuccessMessage());
|
Assert.assertEquals("You should receive an email shortly with further instructions.", resetPasswordPage.getSuccessMessage());
|
||||||
|
|
||||||
|
@ -142,7 +143,7 @@ public class ResetPasswordTest {
|
||||||
|
|
||||||
updatePasswordPage.changePassword("resetPassword", "resetPassword");
|
updatePasswordPage.changePassword("resetPassword", "resetPassword");
|
||||||
|
|
||||||
events.expectRequiredAction("update_password").user(userId).session(sessionId).detail(Details.USERNAME, username).assertEvent();
|
events.expectRequiredAction(EventType.UPDATE_PASSWORD).user(userId).session(sessionId).detail(Details.USERNAME, username).assertEvent();
|
||||||
|
|
||||||
Assert.assertEquals(RequestType.AUTH_RESPONSE, appPage.getRequestType());
|
Assert.assertEquals(RequestType.AUTH_RESPONSE, appPage.getRequestType());
|
||||||
|
|
||||||
|
@ -178,7 +179,7 @@ public class ResetPasswordTest {
|
||||||
|
|
||||||
Assert.assertEquals(0, greenMail.getReceivedMessages().length);
|
Assert.assertEquals(0, greenMail.getReceivedMessages().length);
|
||||||
|
|
||||||
events.expectRequiredAction("send_reset_password").user((String) null).session((String) null).detail(Details.USERNAME, "invalid").removeDetail(Details.EMAIL).removeDetail(Details.CODE_ID).error("user_not_found").assertEvent();
|
events.expectRequiredAction(EventType.SEND_RESET_PASSWORD).user((String) null).session((String) null).detail(Details.USERNAME, "invalid").removeDetail(Details.EMAIL).removeDetail(Details.CODE_ID).error("user_not_found").assertEvent();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -208,7 +209,7 @@ public class ResetPasswordTest {
|
||||||
String body = (String) message.getContent();
|
String body = (String) message.getContent();
|
||||||
String changePasswordUrl = MailUtil.getLink(body);
|
String changePasswordUrl = MailUtil.getLink(body);
|
||||||
|
|
||||||
String sessionId = events.expectRequiredAction("send_reset_password").user(userId).detail(Details.USERNAME, "login-test").detail(Details.EMAIL, "login@test.com").assertEvent().getSessionId();
|
String sessionId = events.expectRequiredAction(EventType.SEND_RESET_PASSWORD).user(userId).detail(Details.USERNAME, "login-test").detail(Details.EMAIL, "login@test.com").assertEvent().getSessionId();
|
||||||
|
|
||||||
driver.navigate().to(changePasswordUrl.trim());
|
driver.navigate().to(changePasswordUrl.trim());
|
||||||
|
|
||||||
|
@ -220,7 +221,7 @@ public class ResetPasswordTest {
|
||||||
|
|
||||||
updatePasswordPage.changePassword("resetPasswordWithPasswordPolicy", "resetPasswordWithPasswordPolicy");
|
updatePasswordPage.changePassword("resetPasswordWithPasswordPolicy", "resetPasswordWithPasswordPolicy");
|
||||||
|
|
||||||
events.expectRequiredAction("update_password").user(userId).session(sessionId).detail(Details.USERNAME, "login-test").assertEvent();
|
events.expectRequiredAction(EventType.UPDATE_PASSWORD).user(userId).session(sessionId).detail(Details.USERNAME, "login-test").assertEvent();
|
||||||
|
|
||||||
Assert.assertEquals(RequestType.AUTH_RESPONSE, appPage.getRequestType());
|
Assert.assertEquals(RequestType.AUTH_RESPONSE, appPage.getRequestType());
|
||||||
|
|
||||||
|
|
|
@ -29,6 +29,7 @@ import org.junit.Test;
|
||||||
import org.keycloak.OAuth2Constants;
|
import org.keycloak.OAuth2Constants;
|
||||||
import org.keycloak.audit.Details;
|
import org.keycloak.audit.Details;
|
||||||
import org.keycloak.audit.Event;
|
import org.keycloak.audit.Event;
|
||||||
|
import org.keycloak.audit.EventType;
|
||||||
import org.keycloak.models.RealmModel;
|
import org.keycloak.models.RealmModel;
|
||||||
import org.keycloak.representations.AccessToken;
|
import org.keycloak.representations.AccessToken;
|
||||||
import org.keycloak.representations.idm.UserRepresentation;
|
import org.keycloak.representations.idm.UserRepresentation;
|
||||||
|
@ -110,7 +111,7 @@ public class SocialLoginTest {
|
||||||
|
|
||||||
Assert.assertEquals(RequestType.AUTH_RESPONSE, appPage.getRequestType());
|
Assert.assertEquals(RequestType.AUTH_RESPONSE, appPage.getRequestType());
|
||||||
|
|
||||||
String userId = events.expect("register")
|
String userId = events.expect(EventType.REGISTER)
|
||||||
.user(AssertEvents.isUUID())
|
.user(AssertEvents.isUUID())
|
||||||
.detail(Details.EMAIL, "bob@builder.com")
|
.detail(Details.EMAIL, "bob@builder.com")
|
||||||
.detail(Details.RESPONSE_TYPE, "code")
|
.detail(Details.RESPONSE_TYPE, "code")
|
||||||
|
@ -202,7 +203,7 @@ public class SocialLoginTest {
|
||||||
Assert.assertEquals("Builder", profilePage.getLastName());
|
Assert.assertEquals("Builder", profilePage.getLastName());
|
||||||
Assert.assertEquals("bob@builder.com", profilePage.getEmail());
|
Assert.assertEquals("bob@builder.com", profilePage.getEmail());
|
||||||
|
|
||||||
String userId = events.expect("register")
|
String userId = events.expect(EventType.REGISTER)
|
||||||
.user(AssertEvents.isUUID())
|
.user(AssertEvents.isUUID())
|
||||||
.detail(Details.EMAIL, "bob@builder.com")
|
.detail(Details.EMAIL, "bob@builder.com")
|
||||||
.detail(Details.RESPONSE_TYPE, "code")
|
.detail(Details.RESPONSE_TYPE, "code")
|
||||||
|
@ -213,8 +214,8 @@ public class SocialLoginTest {
|
||||||
|
|
||||||
profilePage.update("Dummy", "User", "dummy-user-reg@dummy-social");
|
profilePage.update("Dummy", "User", "dummy-user-reg@dummy-social");
|
||||||
|
|
||||||
events.expectRequiredAction("update_profile").user(userId).detail(Details.AUTH_METHOD, "social@dummy").detail(Details.USERNAME, "2@dummy").assertEvent();
|
events.expectRequiredAction(EventType.UPDATE_PROFILE).user(userId).detail(Details.AUTH_METHOD, "social@dummy").detail(Details.USERNAME, "2@dummy").assertEvent();
|
||||||
events.expectRequiredAction("update_email").user(userId).detail(Details.AUTH_METHOD, "social@dummy").detail(Details.USERNAME, "2@dummy").detail(Details.PREVIOUS_EMAIL, "bob@builder.com").detail(Details.UPDATED_EMAIL, "dummy-user-reg@dummy-social").assertEvent();
|
events.expectRequiredAction(EventType.UPDATE_EMAIL).user(userId).detail(Details.AUTH_METHOD, "social@dummy").detail(Details.USERNAME, "2@dummy").detail(Details.PREVIOUS_EMAIL, "bob@builder.com").detail(Details.UPDATED_EMAIL, "dummy-user-reg@dummy-social").assertEvent();
|
||||||
|
|
||||||
Assert.assertEquals(RequestType.AUTH_RESPONSE, appPage.getRequestType());
|
Assert.assertEquals(RequestType.AUTH_RESPONSE, appPage.getRequestType());
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue