diff --git a/audit/api/src/main/java/org/keycloak/audit/Audit.java b/audit/api/src/main/java/org/keycloak/audit/Audit.java index 625074f733..985fefa38d 100644 --- a/audit/api/src/main/java/org/keycloak/audit/Audit.java +++ b/audit/api/src/main/java/org/keycloak/audit/Audit.java @@ -75,7 +75,7 @@ public class Audit { return this; } - public Audit event(String e) { + public Audit event(EventType e) { event.setEvent(e); return this; } @@ -108,6 +108,7 @@ public class Audit { } public void error(String error) { + event.setEvent(EventType.valueOf(event.getEvent().name() + "_ERROR")); event.setError(error); send(); } diff --git a/audit/api/src/main/java/org/keycloak/audit/Event.java b/audit/api/src/main/java/org/keycloak/audit/Event.java index 8141acb956..3a72fd1b3d 100644 --- a/audit/api/src/main/java/org/keycloak/audit/Event.java +++ b/audit/api/src/main/java/org/keycloak/audit/Event.java @@ -10,7 +10,7 @@ public class Event { private long time; - private String event; + private EventType event; private String realmId; @@ -34,11 +34,11 @@ public class Event { this.time = time; } - public String getEvent() { + public EventType getEvent() { return event; } - public void setEvent(String event) { + public void setEvent(EventType event) { this.event = event; } diff --git a/audit/api/src/main/java/org/keycloak/audit/EventQuery.java b/audit/api/src/main/java/org/keycloak/audit/EventQuery.java index 3e5cf60642..56ed79fe57 100644 --- a/audit/api/src/main/java/org/keycloak/audit/EventQuery.java +++ b/audit/api/src/main/java/org/keycloak/audit/EventQuery.java @@ -7,7 +7,7 @@ import java.util.List; */ public interface EventQuery { - public EventQuery event(String... events); + public EventQuery event(EventType... events); public EventQuery realm(String realmId); diff --git a/audit/api/src/main/java/org/keycloak/audit/EventType.java b/audit/api/src/main/java/org/keycloak/audit/EventType.java new file mode 100644 index 0000000000..707f5fd9be --- /dev/null +++ b/audit/api/src/main/java/org/keycloak/audit/EventType.java @@ -0,0 +1,42 @@ +package org.keycloak.audit; + +/** + * @author Stian Thorgersen + */ +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 + +} diff --git a/audit/api/src/main/java/org/keycloak/audit/Events.java b/audit/api/src/main/java/org/keycloak/audit/Events.java deleted file mode 100644 index 9d1a54f9a9..0000000000 --- a/audit/api/src/main/java/org/keycloak/audit/Events.java +++ /dev/null @@ -1,29 +0,0 @@ -package org.keycloak.audit; - -/** - * @author Stian Thorgersen - */ -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"; - -} diff --git a/audit/email/pom.xml b/audit/email/pom.xml new file mode 100755 index 0000000000..9aa4dd191b --- /dev/null +++ b/audit/email/pom.xml @@ -0,0 +1,52 @@ + + + + keycloak-audit-parent + org.keycloak + 1.0-beta-1-SNAPSHOT + + + 4.0.0 + + keycloak-audit-email + Keycloak Audit Email Provider + + + + + org.keycloak + keycloak-core + ${project.version} + provided + + + org.keycloak + keycloak-model-api + ${project.version} + provided + + + org.keycloak + keycloak-audit-api + ${project.version} + provided + + + org.keycloak + keycloak-email-api + ${project.version} + provided + + + org.jboss.logging + jboss-logging + provided + + + junit + junit + test + + + + diff --git a/audit/email/src/main/java/org/keycloak/audit/email/EmailAuditListener.java b/audit/email/src/main/java/org/keycloak/audit/email/EmailAuditListener.java new file mode 100644 index 0000000000..0ef510adad --- /dev/null +++ b/audit/email/src/main/java/org/keycloak/audit/email/EmailAuditListener.java @@ -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 Stian Thorgersen + */ +public class EmailAuditListener implements AuditListener { + + private static final Logger log = Logger.getLogger(EmailAuditListener.class); + + private KeycloakSession keycloakSession; + private EmailProvider emailProvider; + private Set includedEvents; + + public EmailAuditListener(KeycloakSession keycloakSession, EmailProvider emailProvider, Set 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() { + } + +} diff --git a/audit/email/src/main/java/org/keycloak/audit/email/EmailAuditListenerFactory.java b/audit/email/src/main/java/org/keycloak/audit/email/EmailAuditListenerFactory.java new file mode 100644 index 0000000000..7459a06b7e --- /dev/null +++ b/audit/email/src/main/java/org/keycloak/audit/email/EmailAuditListenerFactory.java @@ -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 Stian Thorgersen + */ +public class EmailAuditListenerFactory implements AuditListenerFactory { + + private static final Set SUPPORTED_EVENTS = new HashSet(); + static { + Collections.addAll(SUPPORTED_EVENTS, EventType.LOGIN_ERROR, EventType.UPDATE_PASSWORD, EventType.REMOVE_TOTP, EventType.UPDATE_TOTP); + } + + private Set includedEvents = new HashSet(); + + @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"; + } + +} diff --git a/audit/email/src/main/resources/META-INF/services/org.keycloak.audit.AuditListenerFactory b/audit/email/src/main/resources/META-INF/services/org.keycloak.audit.AuditListenerFactory new file mode 100644 index 0000000000..bdc9c2e834 --- /dev/null +++ b/audit/email/src/main/resources/META-INF/services/org.keycloak.audit.AuditListenerFactory @@ -0,0 +1 @@ +org.keycloak.audit.email.EmailAuditListenerFactory \ No newline at end of file diff --git a/audit/jpa/src/main/java/org/keycloak/audit/jpa/JpaAuditProvider.java b/audit/jpa/src/main/java/org/keycloak/audit/jpa/JpaAuditProvider.java index 0640086926..d6366b34e8 100644 --- a/audit/jpa/src/main/java/org/keycloak/audit/jpa/JpaAuditProvider.java +++ b/audit/jpa/src/main/java/org/keycloak/audit/jpa/JpaAuditProvider.java @@ -6,11 +6,13 @@ import org.jboss.logging.Logger; import org.keycloak.audit.AuditProvider; import org.keycloak.audit.Event; import org.keycloak.audit.EventQuery; +import org.keycloak.audit.EventType; import javax.persistence.EntityManager; import javax.persistence.EntityTransaction; import java.io.IOException; import java.util.Map; +import java.util.Set; import java.util.UUID; /** @@ -25,9 +27,11 @@ public class JpaAuditProvider implements AuditProvider { private EntityManager em; private EntityTransaction tx; + private Set includedEvents; - public JpaAuditProvider(EntityManager em) { + public JpaAuditProvider(EntityManager em, Set includedEvents) { this.em = em; + this.includedEvents = includedEvents; } @Override @@ -55,8 +59,10 @@ public class JpaAuditProvider implements AuditProvider { @Override public void onEvent(Event event) { - beginTx(); - em.persist(convert(event)); + if (includedEvents.contains(event.getEvent())) { + beginTx(); + em.persist(convert(event)); + } } @Override @@ -79,7 +85,7 @@ public class JpaAuditProvider implements AuditProvider { EventEntity e = new EventEntity(); e.setId(UUID.randomUUID().toString()); e.setTime(o.getTime()); - e.setEvent(o.getEvent()); + e.setEvent(o.getEvent().toString()); e.setRealmId(o.getRealmId()); e.setClientId(o.getClientId()); e.setUserId(o.getUserId()); @@ -97,7 +103,7 @@ public class JpaAuditProvider implements AuditProvider { static Event convert(EventEntity o) { Event e = new Event(); e.setTime(o.getTime()); - e.setEvent(o.getEvent()); + e.setEvent(EventType.valueOf(o.getEvent())); e.setRealmId(o.getRealmId()); e.setClientId(o.getClientId()); e.setUserId(o.getUserId()); diff --git a/audit/jpa/src/main/java/org/keycloak/audit/jpa/JpaAuditProviderFactory.java b/audit/jpa/src/main/java/org/keycloak/audit/jpa/JpaAuditProviderFactory.java index 01f72e26fb..58ff08eae4 100644 --- a/audit/jpa/src/main/java/org/keycloak/audit/jpa/JpaAuditProviderFactory.java +++ b/audit/jpa/src/main/java/org/keycloak/audit/jpa/JpaAuditProviderFactory.java @@ -3,10 +3,13 @@ package org.keycloak.audit.jpa; import org.keycloak.Config; import org.keycloak.audit.AuditProvider; import org.keycloak.audit.AuditProviderFactory; +import org.keycloak.audit.EventType; import org.keycloak.provider.ProviderSession; import javax.persistence.EntityManagerFactory; import javax.persistence.Persistence; +import java.util.HashSet; +import java.util.Set; /** * @author Stian Thorgersen @@ -16,14 +19,34 @@ public class JpaAuditProviderFactory implements AuditProviderFactory { public static final String ID = "jpa"; private EntityManagerFactory emf; + private Set includedEvents = new HashSet(); + @Override public AuditProvider create(ProviderSession providerSession) { - return new JpaAuditProvider(emf.createEntityManager()); + return new JpaAuditProvider(emf.createEntityManager(), includedEvents); } @Override public void init(Config.Scope config) { 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 diff --git a/audit/jpa/src/main/java/org/keycloak/audit/jpa/JpaEventQuery.java b/audit/jpa/src/main/java/org/keycloak/audit/jpa/JpaEventQuery.java index f69fe4b622..16d6b527f1 100644 --- a/audit/jpa/src/main/java/org/keycloak/audit/jpa/JpaEventQuery.java +++ b/audit/jpa/src/main/java/org/keycloak/audit/jpa/JpaEventQuery.java @@ -2,6 +2,7 @@ package org.keycloak.audit.jpa; import org.keycloak.audit.Event; import org.keycloak.audit.EventQuery; +import org.keycloak.audit.EventType; import javax.persistence.EntityManager; import javax.persistence.TypedQuery; @@ -36,8 +37,12 @@ public class JpaEventQuery implements EventQuery { } @Override - public EventQuery event(String... events) { - predicates.add(root.get("event").in(events)); + public EventQuery event(EventType... events) { + List eventStrings = new LinkedList(); + for (EventType e : events) { + eventStrings.add(e.toString()); + } + predicates.add(root.get("event").in(eventStrings)); return this; } diff --git a/audit/mongo/src/main/java/org/keycloak/audit/mongo/MongoAuditProvider.java b/audit/mongo/src/main/java/org/keycloak/audit/mongo/MongoAuditProvider.java index 3114628aaa..5fcdc41330 100644 --- a/audit/mongo/src/main/java/org/keycloak/audit/mongo/MongoAuditProvider.java +++ b/audit/mongo/src/main/java/org/keycloak/audit/mongo/MongoAuditProvider.java @@ -6,9 +6,11 @@ import com.mongodb.DBObject; import org.keycloak.audit.AuditProvider; import org.keycloak.audit.Event; import org.keycloak.audit.EventQuery; +import org.keycloak.audit.EventType; import java.util.HashMap; import java.util.Map; +import java.util.Set; /** * @author Stian Thorgersen @@ -16,9 +18,11 @@ import java.util.Map; public class MongoAuditProvider implements AuditProvider { private DBCollection audit; + private Set includedEvents; - public MongoAuditProvider(DBCollection audit) { + public MongoAuditProvider(DBCollection audit, Set includedEvents) { this.audit = audit; + this.includedEvents = includedEvents; } @Override @@ -46,7 +50,9 @@ public class MongoAuditProvider implements AuditProvider { @Override public void onEvent(Event event) { - audit.insert(convert(event)); + if (includedEvents.contains(event.getEvent())) { + audit.insert(convert(event)); + } } @Override @@ -56,7 +62,7 @@ public class MongoAuditProvider implements AuditProvider { static DBObject convert(Event o) { BasicDBObject e = new BasicDBObject(); e.put("time", o.getTime()); - e.put("event", o.getEvent()); + e.put("event", o.getEvent().toString()); e.put("realmId", o.getRealmId()); e.put("clientId", o.getClientId()); e.put("userId", o.getUserId()); @@ -78,7 +84,7 @@ public class MongoAuditProvider implements AuditProvider { static Event convert(BasicDBObject o) { Event e = new Event(); e.setTime(o.getLong("time")); - e.setEvent(o.getString("event")); + e.setEvent(EventType.valueOf(o.getString("event"))); e.setRealmId(o.getString("realmId")); e.setClientId(o.getString("clientId")); e.setUserId(o.getString("userId")); diff --git a/audit/mongo/src/main/java/org/keycloak/audit/mongo/MongoAuditProviderFactory.java b/audit/mongo/src/main/java/org/keycloak/audit/mongo/MongoAuditProviderFactory.java index 09cb6fde5c..b81afc5303 100644 --- a/audit/mongo/src/main/java/org/keycloak/audit/mongo/MongoAuditProviderFactory.java +++ b/audit/mongo/src/main/java/org/keycloak/audit/mongo/MongoAuditProviderFactory.java @@ -8,10 +8,13 @@ import com.mongodb.WriteConcern; import org.keycloak.Config; import org.keycloak.audit.AuditProvider; import org.keycloak.audit.AuditProviderFactory; +import org.keycloak.audit.EventType; import org.keycloak.provider.ProviderSession; import java.net.UnknownHostException; import java.util.Collections; +import java.util.HashSet; +import java.util.Set; /** * @author Stian Thorgersen @@ -22,9 +25,11 @@ public class MongoAuditProviderFactory implements AuditProviderFactory { private MongoClient client; private DB db; + private Set includedEvents = new HashSet(); + @Override public AuditProvider create(ProviderSession providerSession) { - return new MongoAuditProvider(db.getCollection("audit")); + return new MongoAuditProvider(db.getCollection("audit"), includedEvents); } @Override @@ -53,6 +58,24 @@ public class MongoAuditProviderFactory implements AuditProviderFactory { } catch (UnknownHostException 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 diff --git a/audit/mongo/src/main/java/org/keycloak/audit/mongo/MongoEventQuery.java b/audit/mongo/src/main/java/org/keycloak/audit/mongo/MongoEventQuery.java index d220f0e183..668c90714f 100644 --- a/audit/mongo/src/main/java/org/keycloak/audit/mongo/MongoEventQuery.java +++ b/audit/mongo/src/main/java/org/keycloak/audit/mongo/MongoEventQuery.java @@ -5,9 +5,11 @@ import com.mongodb.DBCollection; import com.mongodb.DBCursor; import org.keycloak.audit.Event; import org.keycloak.audit.EventQuery; +import org.keycloak.audit.EventType; import java.util.LinkedList; import java.util.List; +import java.util.Set; /** * @author Stian Thorgersen @@ -25,8 +27,12 @@ public class MongoEventQuery implements EventQuery { } @Override - public EventQuery event(String... events) { - query.put("event", new BasicDBObject("$in", events)); + public EventQuery event(EventType... events) { + List eventStrings = new LinkedList(); + for (EventType e : events) { + eventStrings.add(e.toString()); + } + query.put("event", new BasicDBObject("$in", eventStrings)); return this; } diff --git a/audit/pom.xml b/audit/pom.xml index ed125d1614..21e5d29fda 100755 --- a/audit/pom.xml +++ b/audit/pom.xml @@ -17,6 +17,7 @@ api + email jpa jboss-logging mongo diff --git a/audit/tests/src/main/java/org/keycloak/audit/tests/AbstractAuditProviderTest.java b/audit/tests/src/main/java/org/keycloak/audit/tests/AbstractAuditProviderTest.java index b24ee37a09..82ecc28088 100644 --- a/audit/tests/src/main/java/org/keycloak/audit/tests/AbstractAuditProviderTest.java +++ b/audit/tests/src/main/java/org/keycloak/audit/tests/AbstractAuditProviderTest.java @@ -8,6 +8,7 @@ import org.keycloak.Config; import org.keycloak.audit.AuditProvider; import org.keycloak.audit.AuditProviderFactory; import org.keycloak.audit.Event; +import org.keycloak.audit.EventType; import org.keycloak.provider.ProviderFactory; import java.util.HashMap; @@ -47,7 +48,7 @@ public abstract class AbstractAuditProviderTest { @Test 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 @@ -55,23 +56,23 @@ public abstract class AbstractAuditProviderTest { long oldest = System.currentTimeMillis() - 30000; long newest = System.currentTimeMillis() + 30000; - provider.onEvent(create("event", "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, "event2", "realmId", "clientId", "userId2", "127.0.0.1", "error")); - provider.onEvent(create("event", "realmId2", "clientId", "userId", "127.0.0.1", "error")); - provider.onEvent(create(oldest, "event", "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", "userId", "127.0.0.1", "error")); + provider.onEvent(create(newest, EventType.REGISTER, "realmId", "clientId", "userId", "127.0.0.1", "error")); + provider.onEvent(create(newest, EventType.REGISTER, "realmId", "clientId", "userId2", "127.0.0.1", "error")); + provider.onEvent(create(EventType.LOGIN, "realmId2", "clientId", "userId", "127.0.0.1", "error")); + provider.onEvent(create(oldest, EventType.LOGIN, "realmId", "clientId2", "userId", "127.0.0.1", "error")); + provider.onEvent(create(EventType.LOGIN, "realmId", "clientId", "userId2", "127.0.0.1", "error")); provider.close(); provider = factory.create(null); Assert.assertEquals(5, provider.createQuery().client("clientId").getResultList().size()); Assert.assertEquals(5, provider.createQuery().realm("realmId").getResultList().size()); - Assert.assertEquals(4, provider.createQuery().event("event").getResultList().size()); - Assert.assertEquals(6, provider.createQuery().event("event", "event2").getResultList().size()); + Assert.assertEquals(4, provider.createQuery().event(EventType.LOGIN).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(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(1, provider.createQuery().firstResult(5).getResultList().size()); @@ -82,11 +83,11 @@ public abstract class AbstractAuditProviderTest { @Test public void clear() { - provider.onEvent(create(System.currentTimeMillis() - 30000, "event", "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(), "event", "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() - 30000, "event", "realmId2", "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, EventType.LOGIN, "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(), EventType.LOGIN, "realmId", "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 = factory.create(null); @@ -98,11 +99,11 @@ public abstract class AbstractAuditProviderTest { @Test public void clearOld() { - provider.onEvent(create(System.currentTimeMillis() - 30000, "event", "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(), "event", "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() - 30000, "event", "realmId2", "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, EventType.LOGIN, "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(), EventType.LOGIN, "realmId", "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 = factory.create(null); @@ -112,11 +113,11 @@ public abstract class AbstractAuditProviderTest { 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); } - 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(); e.setTime(time); e.setEvent(event); diff --git a/core/src/main/java/org/keycloak/Config.java b/core/src/main/java/org/keycloak/Config.java index 9af395b214..46c6fc9819 100755 --- a/core/src/main/java/org/keycloak/Config.java +++ b/core/src/main/java/org/keycloak/Config.java @@ -69,6 +69,20 @@ public class Config { 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 public Integer getInt(String key) { return getInt(key, null); @@ -113,6 +127,8 @@ public class Config { String get(String key, String defaultValue); + String[] getArray(String key); + Integer getInt(String key); Integer getInt(String key, Integer defaultValue); diff --git a/forms/account-freemarker/src/main/java/org/keycloak/account/freemarker/model/LogBean.java b/forms/account-freemarker/src/main/java/org/keycloak/account/freemarker/model/LogBean.java index 06a07e7f67..31c13a3082 100644 --- a/forms/account-freemarker/src/main/java/org/keycloak/account/freemarker/model/LogBean.java +++ b/forms/account-freemarker/src/main/java/org/keycloak/account/freemarker/model/LogBean.java @@ -38,7 +38,7 @@ public class LogBean { } public String getEvent() { - return event.getEvent(); + return event.getEvent().toString().toLowerCase().replace("_", " "); } public String getClient() { @@ -51,8 +51,10 @@ public class LogBean { public List getDetails() { List details = new LinkedList(); - for (Map.Entry e : event.getDetails().entrySet()) { - details.add(new DetailBean(e)); + if (event.getDetails() != null) { + for (Map.Entry e : event.getDetails().entrySet()) { + details.add(new DetailBean(e)); + } } return details; } @@ -72,7 +74,7 @@ public class LogBean { } public String getValue() { - return entry.getValue(); + return entry.getValue().replace("_", " "); } } diff --git a/forms/common-themes/src/main/resources/theme/admin/base/resources/partials/realm-audit.html b/forms/common-themes/src/main/resources/theme/admin/base/resources/partials/realm-audit.html index 0e03ff00d0..28d60f4dee 100755 --- a/forms/common-themes/src/main/resources/theme/admin/base/resources/partials/realm-audit.html +++ b/forms/common-themes/src/main/resources/theme/admin/base/resources/partials/realm-audit.html @@ -73,12 +73,13 @@ {{event.time|date:'shortDate'}}
{{event.time|date:'mediumTime'}} - {{event.event}}
{{event.error}} + {{event.event}} - +
+
Client{{event.clientId}}
User{{event.userId}}
IP Address{{event.ipAddress}}
Error{{event.error}}
Details @@ -86,7 +87,7 @@ - +
diff --git a/forms/common-themes/src/main/resources/theme/admin/keycloak/resources/css/admin-console.css b/forms/common-themes/src/main/resources/theme/admin/keycloak/resources/css/admin-console.css index df7e5e7bc5..d567eedb97 100644 --- a/forms/common-themes/src/main/resources/theme/admin/keycloak/resources/css/admin-console.css +++ b/forms/common-themes/src/main/resources/theme/admin/keycloak/resources/css/admin-console.css @@ -828,4 +828,8 @@ legend .kc-icon-collapse { .action-div > i { cursor: pointer; +} + +table table { + margin-bottom: 0 !important; } \ No newline at end of file diff --git a/forms/common-themes/src/main/resources/theme/admin/keycloak/resources/css/tables.css b/forms/common-themes/src/main/resources/theme/admin/keycloak/resources/css/tables.css index 4ef3c53260..b0a1f83af6 100644 --- a/forms/common-themes/src/main/resources/theme/admin/keycloak/resources/css/tables.css +++ b/forms/common-themes/src/main/resources/theme/admin/keycloak/resources/css/tables.css @@ -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 { width: 100%; @@ -111,69 +186,6 @@ table tbody.selectable-rows tr.selected:hover td:first-child { table tfoot tr { 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 > * { vertical-align: middle; @@ -183,14 +195,6 @@ td .form-group { margin-bottom: 0; } -td.audit-success { - background-color: #E4F1E1; -} - -td.audit-error { - background-color: #F8E7E7; -} - .kc-table-actions .form-group { margin-top: 5px; margin-bottom: 5px; diff --git a/forms/common-themes/src/main/resources/theme/email/keycloak/event-login_error.ftl b/forms/common-themes/src/main/resources/theme/email/keycloak/event-login_error.ftl new file mode 100644 index 0000000000..93e02bea11 --- /dev/null +++ b/forms/common-themes/src/main/resources/theme/email/keycloak/event-login_error.ftl @@ -0,0 +1 @@ +Your password was changed on ${event.date} from ${event.ipAddress}. If this was not you, please contact an admin. \ No newline at end of file diff --git a/forms/common-themes/src/main/resources/theme/email/keycloak/event-remove_totp.ftl b/forms/common-themes/src/main/resources/theme/email/keycloak/event-remove_totp.ftl new file mode 100644 index 0000000000..0c6a142c7d --- /dev/null +++ b/forms/common-themes/src/main/resources/theme/email/keycloak/event-remove_totp.ftl @@ -0,0 +1 @@ +TOTP was removed from your account on ${event.date} from ${event.ipAddress}. If this was not you, please contact an admin. \ No newline at end of file diff --git a/forms/common-themes/src/main/resources/theme/email/keycloak/event-update_password.ftl b/forms/common-themes/src/main/resources/theme/email/keycloak/event-update_password.ftl new file mode 100644 index 0000000000..8c3a94f76d --- /dev/null +++ b/forms/common-themes/src/main/resources/theme/email/keycloak/event-update_password.ftl @@ -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. \ No newline at end of file diff --git a/forms/common-themes/src/main/resources/theme/email/keycloak/event-update_totp.ftl b/forms/common-themes/src/main/resources/theme/email/keycloak/event-update_totp.ftl new file mode 100644 index 0000000000..a1f153c2ae --- /dev/null +++ b/forms/common-themes/src/main/resources/theme/email/keycloak/event-update_totp.ftl @@ -0,0 +1 @@ +TOTP was updated for your account on ${event.date} from ${event.ipAddress}. If this was not you, please contact an admin. \ No newline at end of file diff --git a/forms/email-api/pom.xml b/forms/email-api/pom.xml index 19ef08a092..62df27f59d 100755 --- a/forms/email-api/pom.xml +++ b/forms/email-api/pom.xml @@ -26,6 +26,12 @@ ${project.version}provided + + org.keycloak + keycloak-audit-api + ${project.version} + provided + diff --git a/forms/email-api/src/main/java/org/keycloak/email/EmailProvider.java b/forms/email-api/src/main/java/org/keycloak/email/EmailProvider.java index 0827b9689f..eb8f3e78dc 100644 --- a/forms/email-api/src/main/java/org/keycloak/email/EmailProvider.java +++ b/forms/email-api/src/main/java/org/keycloak/email/EmailProvider.java @@ -1,5 +1,6 @@ package org.keycloak.email; +import org.keycloak.audit.Event; import org.keycloak.models.RealmModel; import org.keycloak.models.UserModel; import org.keycloak.provider.Provider; @@ -13,6 +14,8 @@ public interface EmailProvider extends Provider { public EmailProvider setUser(UserModel user); + public void sendEvent(Event event) throws EmailException; + public void sendPasswordReset(String link, long expirationInMinutes) throws EmailException; public void sendVerifyEmail(String link, long expirationInMinutes) throws EmailException; diff --git a/forms/email-freemarker/pom.xml b/forms/email-freemarker/pom.xml index b5fb4def71..f83f098a8f 100755 --- a/forms/email-freemarker/pom.xml +++ b/forms/email-freemarker/pom.xml @@ -32,6 +32,12 @@ ${project.version} provided + + org.keycloak + keycloak-audit-api + ${project.version} + provided + org.keycloak keycloak-forms-common-freemarker diff --git a/forms/email-freemarker/src/main/java/org/keycloak/email/freemarker/FreeMarkerEmailProvider.java b/forms/email-freemarker/src/main/java/org/keycloak/email/freemarker/FreeMarkerEmailProvider.java index e4e8c52b57..0ec1fcbc93 100644 --- a/forms/email-freemarker/src/main/java/org/keycloak/email/freemarker/FreeMarkerEmailProvider.java +++ b/forms/email-freemarker/src/main/java/org/keycloak/email/freemarker/FreeMarkerEmailProvider.java @@ -1,8 +1,10 @@ package org.keycloak.email.freemarker; import org.jboss.logging.Logger; +import org.keycloak.audit.Event; import org.keycloak.email.EmailException; import org.keycloak.email.EmailProvider; +import org.keycloak.email.freemarker.beans.EventBean; import org.keycloak.freemarker.ExtendingThemeManager; import org.keycloak.freemarker.FreeMarkerUtil; import org.keycloak.freemarker.Theme; @@ -47,6 +49,14 @@ public class FreeMarkerEmailProvider implements EmailProvider { return this; } + @Override + public void sendEvent(Event event) throws EmailException { + Map attributes = new HashMap(); + attributes.put("event", new EventBean(event)); + + send("passwordResetSubject", "event-" + event.getEvent().toString().toLowerCase() + ".ftl", attributes); + } + @Override public void sendPasswordReset(String link, long expirationInMinutes) throws EmailException { Map attributes = new HashMap(); diff --git a/forms/email-freemarker/src/main/java/org/keycloak/email/freemarker/beans/EventBean.java b/forms/email-freemarker/src/main/java/org/keycloak/email/freemarker/beans/EventBean.java new file mode 100644 index 0000000000..efee48d298 --- /dev/null +++ b/forms/email-freemarker/src/main/java/org/keycloak/email/freemarker/beans/EventBean.java @@ -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 Stian Thorgersen + */ +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 getDetails() { + List details = new LinkedList(); + for (Map.Entry e : event.getDetails().entrySet()) { + details.add(new DetailBean(e)); + } + return details; + } + + public static class DetailBean { + + private Map.Entry entry; + + public DetailBean(Map.Entry entry) { + this.entry = entry; + } + + public String getKey() { + return entry.getKey(); + } + + public String getValue() { + return entry.getValue().replace("_", " "); + } + + } +} diff --git a/project-integrations/aerogear-ups/auth-server/src/main/webapp/WEB-INF/keycloak-server.json b/project-integrations/aerogear-ups/auth-server/src/main/webapp/WEB-INF/keycloak-server.json index 73fc4b0740..3e88c8a134 100644 --- a/project-integrations/aerogear-ups/auth-server/src/main/webapp/WEB-INF/keycloak-server.json +++ b/project-integrations/aerogear-ups/auth-server/src/main/webapp/WEB-INF/keycloak-server.json @@ -3,10 +3,6 @@ "realm": "keycloak-admin" }, - "audit": { - "provider": "jpa" - }, - "model": { "provider": "jpa" }, diff --git a/server/pom.xml b/server/pom.xml index e353dd598e..a5570679ba 100755 --- a/server/pom.xml +++ b/server/pom.xml @@ -62,6 +62,11 @@ keycloak-audit-jboss-logging ${project.version} + + org.keycloak + keycloak-audit-email + ${project.version} + org.keycloak diff --git a/server/src/main/resources/META-INF/keycloak-server.json b/server/src/main/resources/META-INF/keycloak-server.json index 544ad8147c..aa446763cb 100644 --- a/server/src/main/resources/META-INF/keycloak-server.json +++ b/server/src/main/resources/META-INF/keycloak-server.json @@ -4,7 +4,10 @@ }, "audit": { - "provider": "jpa" + "provider": "jpa", + "jpa": { + "exclude-events": [ "REFRESH_TOKEN" ] + } }, "model": { diff --git a/services/src/main/java/org/keycloak/services/resources/AccountService.java b/services/src/main/java/org/keycloak/services/resources/AccountService.java index 5f6206ffb4..5ff6ca373c 100755 --- a/services/src/main/java/org/keycloak/services/resources/AccountService.java +++ b/services/src/main/java/org/keycloak/services/resources/AccountService.java @@ -31,7 +31,7 @@ import org.keycloak.audit.Audit; import org.keycloak.audit.AuditProvider; import org.keycloak.audit.Details; import org.keycloak.audit.Event; -import org.keycloak.audit.Events; +import org.keycloak.audit.EventType; import org.keycloak.authentication.AuthProviderStatus; import org.keycloak.authentication.AuthenticationProviderException; import org.keycloak.authentication.AuthenticationProviderManager; @@ -95,8 +95,8 @@ public class AccountService { 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, - Events.SEND_VERIFY_EMAIL, Events.SOCIAL_LINK, Events.UPDATE_EMAIL, Events.UPDATE_PASSWORD, Events.UPDATE_PROFILE, Events.UPDATE_TOTP, Events.VERIFY_EMAIL}; + private static final EventType[] AUDIT_EVENTS = {EventType.LOGIN, EventType.LOGOUT, EventType.REGISTER, EventType.REMOVE_SOCIAL_LINK, EventType.REMOVE_TOTP, EventType.SEND_RESET_PASSWORD, + 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 AUDIT_DETAILS = new HashSet(); static { @@ -246,20 +246,6 @@ public class AccountService { public Response logPage() { if (auth != null) { List events = auditProvider.createQuery().event(AUDIT_EVENTS).user(auth.getUser().getId()).maxResults(30).getResultList(); - for (Event e : events) { - e.setEvent(e.getEvent().replace('_', ' ')); - - Map details = new HashMap(); - if (e.getDetails() != null) { - Iterator itr = e.getDetails().keySet().iterator(); - for (Map.Entry d : e.getDetails().entrySet()) { - if (AUDIT_DETAILS.contains(d.getKey())) { - details.put(d.getKey().replace('_', ' '), d.getValue()); - } - } - } - e.setDetails(details); - } account.setEvents(events); } return forwardToPage("log", AccountPages.LOG); @@ -300,11 +286,11 @@ public class AccountService { 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) { 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); @@ -322,7 +308,7 @@ public class AccountService { UserModel user = auth.getUser(); 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); } @@ -371,7 +357,7 @@ public class AccountService { 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); } @@ -414,7 +400,7 @@ public class AccountService { 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); } @@ -471,7 +457,7 @@ public class AccountService { 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()) .success(); diff --git a/services/src/main/java/org/keycloak/services/resources/RequiredActionsService.java b/services/src/main/java/org/keycloak/services/resources/RequiredActionsService.java index 4c1e06218e..335b10bce3 100755 --- a/services/src/main/java/org/keycloak/services/resources/RequiredActionsService.java +++ b/services/src/main/java/org/keycloak/services/resources/RequiredActionsService.java @@ -27,7 +27,7 @@ import org.keycloak.OAuth2Constants; import org.keycloak.audit.Audit; import org.keycloak.audit.Details; import org.keycloak.audit.Errors; -import org.keycloak.audit.Events; +import org.keycloak.audit.EventType; import org.keycloak.email.EmailException; import org.keycloak.email.EmailProvider; import org.keycloak.login.LoginFormsProvider; @@ -136,10 +136,10 @@ public class RequiredActionsService { user.removeRequiredAction(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) { 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); @@ -178,7 +178,7 @@ public class RequiredActionsService { user.removeRequiredAction(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); } @@ -225,7 +225,7 @@ public class RequiredActionsService { accessCode.getRequiredActions().remove(RequiredAction.UPDATE_PASSWORD); } - audit.clone().event(Events.UPDATE_PASSWORD).success(); + audit.clone().event(EventType.UPDATE_PASSWORD).success(); return redirectOauth(user, accessCode); } @@ -250,7 +250,7 @@ public class RequiredActionsService { user.removeRequiredAction(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); } else { @@ -260,7 +260,7 @@ public class RequiredActionsService { } 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()) .createResponse(RequiredAction.VERIFY_EMAIL); @@ -307,7 +307,7 @@ public class RequiredActionsService { "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.RESPONSE_TYPE, "code") .detail(Details.AUTH_METHOD, "form") @@ -430,7 +430,7 @@ public class RequiredActionsService { } private void initAudit(AccessCodeEntry accessCode) { - audit.event(Events.LOGIN).client(accessCode.getClient()) + audit.event(EventType.LOGIN).client(accessCode.getClient()) .user(accessCode.getUser()) .session(accessCode.getSessionState()) .detail(Details.CODE_ID, accessCode.getId()) diff --git a/services/src/main/java/org/keycloak/services/resources/SocialResource.java b/services/src/main/java/org/keycloak/services/resources/SocialResource.java index 76c898511c..c7c5d23211 100755 --- a/services/src/main/java/org/keycloak/services/resources/SocialResource.java +++ b/services/src/main/java/org/keycloak/services/resources/SocialResource.java @@ -28,7 +28,7 @@ import org.keycloak.OAuth2Constants; import org.keycloak.audit.Audit; import org.keycloak.audit.Details; import org.keycloak.audit.Errors; -import org.keycloak.audit.Events; +import org.keycloak.audit.EventType; import org.keycloak.models.AccountRoles; import org.keycloak.models.ClientModel; 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.MultivaluedMap; import javax.ws.rs.core.Response; -import javax.ws.rs.core.Response.Status; import javax.ws.rs.core.UriBuilder; import javax.ws.rs.core.UriInfo; import java.net.URISyntaxException; @@ -131,7 +130,7 @@ public class SocialResource { RealmModel realm = realmManager.getRealmByName(realmName); Audit audit = new AuditManager(realm, providers, clientConnection).createAudit() - .event(Events.LOGIN) + .event(EventType.LOGIN) .detail(Details.RESPONSE_TYPE, "code") .detail(Details.AUTH_METHOD, "social@" + provider.getId()); @@ -196,7 +195,7 @@ public class SocialResource { if (userId != null) { UserModel authenticatedUser = realm.getUserById(userId); - audit.event(Events.SOCIAL_LINK).user(userId); + audit.event(EventType.SOCIAL_LINK).user(userId); if (user != null) { audit.error(Errors.SOCIAL_ID_IN_USE); @@ -244,7 +243,7 @@ public class SocialResource { 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.EMAIL, socialUser.getEmail()) .removeDetail("auth_method") @@ -274,7 +273,7 @@ public class SocialResource { RealmModel realm = realmManager.getRealmByName(realmName); 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.RESPONSE_TYPE, "code") .detail(Details.AUTH_METHOD, "social@" + providerId); diff --git a/services/src/main/java/org/keycloak/services/resources/TokenService.java b/services/src/main/java/org/keycloak/services/resources/TokenService.java index b9d1291dc1..e4963f04b8 100755 --- a/services/src/main/java/org/keycloak/services/resources/TokenService.java +++ b/services/src/main/java/org/keycloak/services/resources/TokenService.java @@ -13,7 +13,7 @@ import org.keycloak.OAuthErrorException; import org.keycloak.audit.Audit; import org.keycloak.audit.Details; import org.keycloak.audit.Errors; -import org.keycloak.audit.Events; +import org.keycloak.audit.EventType; import org.keycloak.authentication.AuthenticationProviderException; import org.keycloak.authentication.AuthenticationProviderManager; import org.keycloak.jose.jws.JWSInput; @@ -34,7 +34,6 @@ import org.keycloak.representations.AccessToken; import org.keycloak.representations.AccessTokenResponse; import org.keycloak.representations.idm.CredentialRepresentation; import org.keycloak.services.ClientConnection; -import org.keycloak.services.ForbiddenException; import org.keycloak.services.managers.AccessCodeEntry; import org.keycloak.services.managers.AuthenticationManager; 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); } - 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); if (username == null) { @@ -294,7 +293,7 @@ public class TokenService { throw new NotAcceptableException("HTTPS required"); } - audit.event(Events.REFRESH_TOKEN); + audit.event(EventType.REFRESH_TOKEN); ClientModel client = authorizeClient(authorizationHeader, form, audit); String refreshToken = form.getFirst(OAuth2Constants.REFRESH_TOKEN); @@ -334,7 +333,7 @@ public class TokenService { boolean remember = rememberMe != null && rememberMe.equalsIgnoreCase("on"); logger.debug("*** Remember me: " + remember); - audit.event(Events.LOGIN).client(clientId) + audit.event(EventType.LOGIN).client(clientId) .detail(Details.REDIRECT_URI, redirect) .detail(Details.RESPONSE_TYPE, "code") .detail(Details.AUTH_METHOD, "form") @@ -383,12 +382,14 @@ public class TokenService { authManager.expireRememberMeCookie(realm, uriInfo); } + UserModel user = KeycloakModelUtils.findUserByNameOrEmail(realm, username); + if (user != null) { + audit.user(user); + } + switch (status) { case SUCCESS: case ACTIONS_REQUIRED: - UserModel user = KeycloakModelUtils.findUserByNameOrEmail(realm, username); - audit.user(user); - UserSessionModel session = realm.createUserSession(user, clientConnection.getRemoteAddr()); audit.session(session); @@ -429,7 +430,7 @@ public class TokenService { String username = formData.getFirst("username"); String email = formData.getFirst("email"); - audit.event(Events.REGISTER).client(clientId) + audit.event(EventType.REGISTER).client(clientId) .detail(Details.REDIRECT_URI, redirect) .detail(Details.RESPONSE_TYPE, "code") .detail(Details.USERNAME, username) @@ -545,7 +546,7 @@ public class TokenService { throw new NotAcceptableException("HTTPS required"); } - audit.event(Events.CODE_TO_TOKEN); + audit.event(EventType.CODE_TO_TOKEN); if (!realm.isEnabled()) { 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) { 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); @@ -784,7 +785,7 @@ public class TokenService { final @QueryParam("scope") String scopeParam, final @QueryParam("state") String state) { 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); @@ -833,7 +834,7 @@ public class TokenService { public Response logout(final @QueryParam("session_state") String sessionState, final @QueryParam("redirect_uri") String redirectUri) { // todo do we care if anybody can trigger this? - audit.event(Events.LOGOUT); + audit.event(EventType.LOGOUT); if (redirectUri != null) { audit.detail(Details.REDIRECT_URI, redirectUri); } @@ -874,7 +875,7 @@ public class TokenService { @POST @Consumes(MediaType.APPLICATION_FORM_URLENCODED) public Response processOAuth(final MultivaluedMap 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); diff --git a/services/src/main/java/org/keycloak/services/resources/admin/RealmAdminResource.java b/services/src/main/java/org/keycloak/services/resources/admin/RealmAdminResource.java index 014fe52f5f..1107a3d4ec 100755 --- a/services/src/main/java/org/keycloak/services/resources/admin/RealmAdminResource.java +++ b/services/src/main/java/org/keycloak/services/resources/admin/RealmAdminResource.java @@ -7,6 +7,7 @@ import org.jboss.resteasy.spi.ResteasyProviderFactory; import org.keycloak.audit.AuditProvider; import org.keycloak.audit.Event; import org.keycloak.audit.EventQuery; +import org.keycloak.audit.EventType; import org.keycloak.models.ApplicationModel; import org.keycloak.models.KeycloakSession; import org.keycloak.models.ModelDuplicateException; @@ -236,7 +237,7 @@ public class RealmAdminResource { query.client(client); } if (event != null) { - query.event(event); + query.event(EventType.valueOf(event)); } if (user != null) { query.user(user); diff --git a/services/src/main/java/org/keycloak/services/resources/flows/OAuthFlows.java b/services/src/main/java/org/keycloak/services/resources/flows/OAuthFlows.java index 2ed76b0139..7bf80fa195 100755 --- a/services/src/main/java/org/keycloak/services/resources/flows/OAuthFlows.java +++ b/services/src/main/java/org/keycloak/services/resources/flows/OAuthFlows.java @@ -26,7 +26,7 @@ import org.jboss.resteasy.spi.HttpRequest; import org.keycloak.OAuth2Constants; import org.keycloak.audit.Audit; import org.keycloak.audit.Details; -import org.keycloak.audit.Events; +import org.keycloak.audit.EventType; import org.keycloak.models.ApplicationModel; import org.keycloak.models.ClientModel; import org.keycloak.models.Constants; @@ -140,7 +140,7 @@ public class OAuthFlows { RequiredAction action = user.getRequiredActions().iterator().next(); 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) diff --git a/services/src/main/java/org/keycloak/services/util/JsonConfigProvider.java b/services/src/main/java/org/keycloak/services/util/JsonConfigProvider.java index 181de112fc..714e4dbe1d 100644 --- a/services/src/main/java/org/keycloak/services/util/JsonConfigProvider.java +++ b/services/src/main/java/org/keycloak/services/util/JsonConfigProvider.java @@ -4,6 +4,8 @@ import org.codehaus.jackson.JsonNode; import org.keycloak.Config; import org.keycloak.util.StringPropertyReplacer; +import java.util.ArrayList; + /** * @author Stian Thorgersen */ @@ -62,6 +64,26 @@ public class JsonConfigProvider implements Config.ConfigProvider { 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 l = new ArrayList(); + for (JsonNode e : n) { + l.add(StringPropertyReplacer.replaceProperties(e.getTextValue())); + } + return (String[]) l.toArray(); + } else { + return new String[] { StringPropertyReplacer.replaceProperties(n.getTextValue()) }; + } + } + @Override public Integer getInt(String key) { return getInt(key, null); diff --git a/testsuite/integration/pom.xml b/testsuite/integration/pom.xml index 7924ae959e..74bac64faa 100755 --- a/testsuite/integration/pom.xml +++ b/testsuite/integration/pom.xml @@ -100,6 +100,11 @@ keycloak-audit-jboss-logging ${project.version} + + org.keycloak + keycloak-audit-email + ${project.version} + org.keycloak keycloak-core diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/AssertEvents.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/AssertEvents.java index 4a8543e672..1ca02fe9b3 100755 --- a/testsuite/integration/src/test/java/org/keycloak/testsuite/AssertEvents.java +++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/AssertEvents.java @@ -12,6 +12,7 @@ import org.keycloak.audit.AuditListener; import org.keycloak.audit.AuditListenerFactory; import org.keycloak.audit.Details; import org.keycloak.audit.Event; +import org.keycloak.audit.EventType; import org.keycloak.models.ClientModel; import org.keycloak.models.RealmModel; import org.keycloak.models.UserModel; @@ -110,12 +111,12 @@ public class AssertEvents implements TestRule, AuditListenerFactory { events.clear(); } - public ExpectedEvent expectRequiredAction(String event) { + public ExpectedEvent expectRequiredAction(EventType event) { return expectLogin().event(event).session(isUUID()); } public ExpectedEvent expectLogin() { - return expect("login") + return expect(EventType.LOGIN) .detail(Details.CODE_ID, isCodeId()) .detail(Details.USERNAME, DEFAULT_USERNAME) .detail(Details.RESPONSE_TYPE, "code") @@ -125,7 +126,7 @@ public class AssertEvents implements TestRule, AuditListenerFactory { } 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.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) { - return expect("refresh_token") + return expect(EventType.REFRESH_TOKEN) .detail(Details.TOKEN_ID, isUUID()) .detail(Details.REFRESH_TOKEN_ID, refreshTokenId) .detail(Details.UPDATED_REFRESH_TOKEN_ID, isUUID()) @@ -141,14 +142,14 @@ public class AssertEvents implements TestRule, AuditListenerFactory { } 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) .session(sessionId); } public ExpectedEvent expectRegister(String username, String email) { UserRepresentation user = keycloak.getUser("test", username); - return expect("register") + return expect(EventType.REGISTER) .user(user != null ? user.getId() : null) .detail(Details.USERNAME, username) .detail(Details.EMAIL, email) @@ -157,11 +158,11 @@ public class AssertEvents implements TestRule, AuditListenerFactory { .detail(Details.REDIRECT_URI, DEFAULT_REDIRECT_URI); } - public ExpectedEvent expectAccount(String event) { + public ExpectedEvent expectAccount(EventType event) { return expect(event).client("account"); } - public ExpectedEvent expect(String event) { + public ExpectedEvent expect(EventType event) { return new ExpectedEvent() .realm(DEFAULT_REALM) .client(DEFAULT_CLIENT_ID) @@ -253,7 +254,7 @@ public class AssertEvents implements TestRule, AuditListenerFactory { return this; } - public ExpectedEvent event(String e) { + public ExpectedEvent event(EventType e) { expected.setEvent(e); return this; } @@ -291,6 +292,9 @@ public class AssertEvents implements TestRule, AuditListenerFactory { } 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.getRealmId(), actual.getRealmId()); Assert.assertEquals(expected.getClientId(), actual.getClientId()); diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/account/AccountTest.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/account/AccountTest.java index 393c3f72c1..7bec7a7b74 100755 --- a/testsuite/integration/src/test/java/org/keycloak/testsuite/account/AccountTest.java +++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/account/AccountTest.java @@ -29,6 +29,7 @@ import org.junit.Rule; import org.junit.Test; import org.keycloak.audit.Details; import org.keycloak.audit.Event; +import org.keycloak.audit.EventType; import org.keycloak.models.ApplicationModel; import org.keycloak.models.PasswordPolicy; import org.keycloak.models.RealmModel; @@ -215,7 +216,7 @@ public class AccountTest { Assert.assertEquals("Your password has been updated", profilePage.getSuccess()); - events.expectAccount("update_password").assertEvent(); + events.expectAccount(EventType.UPDATE_PASSWORD).assertEvent(); changePasswordPage.logout(); @@ -226,7 +227,7 @@ public class AccountTest { 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.login("test-user@localhost", "new-password"); @@ -260,7 +261,7 @@ public class AccountTest { Assert.assertEquals("Your password has been updated", profilePage.getSuccess()); - events.expectAccount("update_password").assertEvent(); + events.expectAccount(EventType.UPDATE_PASSWORD).assertEvent(); } finally { keycloakRule.configure(new KeycloakRule.KeycloakSetup() { @Override @@ -317,8 +318,8 @@ public class AccountTest { Assert.assertEquals("New last", profilePage.getLastName()); Assert.assertEquals("new@email.com", profilePage.getEmail()); - events.expectAccount("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_PROFILE).assertEvent(); + events.expectAccount(EventType.UPDATE_EMAIL).detail(Details.PREVIOUS_EMAIL, "test-user@localhost").detail(Details.UPDATED_EMAIL, "new@email.com").assertEvent(); } @Test @@ -341,13 +342,13 @@ public class AccountTest { 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")); totpPage.removeTotp(); - events.expectAccount("remove_totp").assertEvent(); + events.expectAccount(EventType.REMOVE_TOTP).assertEvent(); } @Test @@ -405,7 +406,7 @@ public class AccountTest { Iterator> itr = logPage.getEvents().iterator(); for (Event event : e) { List 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.getClientId(), a.get(3)); } diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/actions/RequiredActionEmailVerificationTest.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/actions/RequiredActionEmailVerificationTest.java index 9746666333..73f185f52c 100755 --- a/testsuite/integration/src/test/java/org/keycloak/testsuite/actions/RequiredActionEmailVerificationTest.java +++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/actions/RequiredActionEmailVerificationTest.java @@ -28,6 +28,7 @@ import org.junit.Rule; import org.junit.Test; import org.keycloak.audit.Details; import org.keycloak.audit.Event; +import org.keycloak.audit.EventType; import org.keycloak.models.RealmModel; import org.keycloak.models.UserModel; import org.keycloak.services.managers.RealmManager; @@ -116,7 +117,7 @@ public class RequiredActionEmailVerificationTest { String body = (String) message.getContent(); 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 mailCodeId = sendEvent.getDetails().get(Details.CODE_ID); @@ -125,7 +126,7 @@ public class RequiredActionEmailVerificationTest { 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()); @@ -148,7 +149,7 @@ public class RequiredActionEmailVerificationTest { 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 mailCodeId = sendEvent.getDetails().get(Details.CODE_ID); @@ -159,7 +160,7 @@ public class RequiredActionEmailVerificationTest { 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(); } @@ -173,7 +174,7 @@ public class RequiredActionEmailVerificationTest { 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 mailCodeId = sendEvent.getDetails().get(Details.CODE_ID); @@ -186,7 +187,7 @@ public class RequiredActionEmailVerificationTest { 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); @@ -194,7 +195,7 @@ public class RequiredActionEmailVerificationTest { 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(); } diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/actions/RequiredActionMultipleActionsTest.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/actions/RequiredActionMultipleActionsTest.java index 12a58b2a8e..49f97ed4f8 100755 --- a/testsuite/integration/src/test/java/org/keycloak/testsuite/actions/RequiredActionMultipleActionsTest.java +++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/actions/RequiredActionMultipleActionsTest.java @@ -26,6 +26,7 @@ import org.junit.ClassRule; import org.junit.Rule; import org.junit.Test; import org.keycloak.audit.Details; +import org.keycloak.audit.EventType; import org.keycloak.models.RealmModel; import org.keycloak.models.UserModel; import org.keycloak.models.UserModel.RequiredAction; @@ -112,7 +113,7 @@ public class RequiredActionMultipleActionsTest { public String updatePassword(String sessionId) { changePasswordPage.changePassword("new-password", "new-password"); - AssertEvents.ExpectedEvent expectedEvent = events.expectRequiredAction("update_password"); + AssertEvents.ExpectedEvent expectedEvent = events.expectRequiredAction(EventType.UPDATE_PASSWORD); if (sessionId != null) { expectedEvent.session(sessionId); } @@ -122,12 +123,12 @@ public class RequiredActionMultipleActionsTest { public String updateProfile(String sessionId) { 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) { expectedEvent.session(sessionId); } 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; } diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/actions/RequiredActionResetPasswordTest.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/actions/RequiredActionResetPasswordTest.java index ad5f74ba08..d4cc32e153 100755 --- a/testsuite/integration/src/test/java/org/keycloak/testsuite/actions/RequiredActionResetPasswordTest.java +++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/actions/RequiredActionResetPasswordTest.java @@ -26,6 +26,7 @@ import org.junit.ClassRule; import org.junit.Rule; import org.junit.Test; import org.keycloak.audit.Event; +import org.keycloak.audit.EventType; import org.keycloak.models.RealmModel; import org.keycloak.models.UserModel; import org.keycloak.models.UserModel.RequiredAction; @@ -93,7 +94,7 @@ public class RequiredActionResetPasswordTest { changePasswordPage.assertCurrent(); 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()); diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/actions/RequiredActionTotpSetupTest.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/actions/RequiredActionTotpSetupTest.java index f69aef91c6..a38ced3bbb 100755 --- a/testsuite/integration/src/test/java/org/keycloak/testsuite/actions/RequiredActionTotpSetupTest.java +++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/actions/RequiredActionTotpSetupTest.java @@ -27,6 +27,7 @@ import org.junit.Rule; import org.junit.Test; import org.keycloak.audit.Details; import org.keycloak.audit.Event; +import org.keycloak.audit.EventType; import org.keycloak.models.RealmModel; import org.keycloak.models.utils.TimeBasedOTP; import org.keycloak.representations.idm.CredentialRepresentation; @@ -109,7 +110,7 @@ public class RequiredActionTotpSetupTest { 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()); @@ -127,7 +128,7 @@ public class RequiredActionTotpSetupTest { 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()); @@ -164,7 +165,7 @@ public class RequiredActionTotpSetupTest { // After totp config, user should be on the app page 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(); @@ -192,7 +193,7 @@ public class RequiredActionTotpSetupTest { // Remove google authentificator accountTotpPage.removeTotp(); - events.expectAccount("remove_totp").user(userId).assertEvent(); + events.expectAccount(EventType.REMOVE_TOTP).user(userId).assertEvent(); // Logout oauth.openLogout(); @@ -206,7 +207,7 @@ public class RequiredActionTotpSetupTest { totpPage.assertCurrent(); 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()); diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/actions/RequiredActionUpdateProfileTest.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/actions/RequiredActionUpdateProfileTest.java index 373b48721f..056e6fd48a 100755 --- a/testsuite/integration/src/test/java/org/keycloak/testsuite/actions/RequiredActionUpdateProfileTest.java +++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/actions/RequiredActionUpdateProfileTest.java @@ -27,6 +27,7 @@ import org.junit.ClassRule; import org.junit.Rule; import org.junit.Test; import org.keycloak.audit.Details; +import org.keycloak.audit.EventType; import org.keycloak.models.RealmModel; import org.keycloak.models.UserModel; import org.keycloak.services.managers.RealmManager; @@ -87,8 +88,8 @@ public class RequiredActionUpdateProfileTest { updateProfilePage.update("New first", "New last", "new@email.com"); - String sessionId = events.expectRequiredAction("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(); + String sessionId = events.expectRequiredAction(EventType.UPDATE_PROFILE).assertEvent().getSessionId(); + 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()); diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/forms/LoginTest.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/forms/LoginTest.java index aa51050161..af561d14c8 100644 --- a/testsuite/integration/src/test/java/org/keycloak/testsuite/forms/LoginTest.java +++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/forms/LoginTest.java @@ -94,7 +94,7 @@ public class LoginTest { 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 diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/forms/LoginTotpTest.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/forms/LoginTotpTest.java index 9502251fb0..15710b17cf 100755 --- a/testsuite/integration/src/test/java/org/keycloak/testsuite/forms/LoginTotpTest.java +++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/forms/LoginTotpTest.java @@ -111,7 +111,7 @@ public class LoginTotpTest { loginPage.assertCurrent(); 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 diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/forms/ResetPasswordTest.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/forms/ResetPasswordTest.java index c1066bb89c..719b98c5a2 100755 --- a/testsuite/integration/src/test/java/org/keycloak/testsuite/forms/ResetPasswordTest.java +++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/forms/ResetPasswordTest.java @@ -27,6 +27,7 @@ import org.junit.Rule; import org.junit.Test; import org.keycloak.audit.Details; import org.keycloak.audit.Event; +import org.keycloak.audit.EventType; import org.keycloak.models.PasswordPolicy; import org.keycloak.models.RealmModel; import org.keycloak.models.UserCredentialModel; @@ -125,7 +126,7 @@ public class ResetPasswordTest { 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()); @@ -142,7 +143,7 @@ public class ResetPasswordTest { 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()); @@ -178,7 +179,7 @@ public class ResetPasswordTest { 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 @@ -208,7 +209,7 @@ public class ResetPasswordTest { String body = (String) message.getContent(); 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()); @@ -220,7 +221,7 @@ public class ResetPasswordTest { 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()); diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/social/SocialLoginTest.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/social/SocialLoginTest.java index f7693bf470..8b5df039dd 100755 --- a/testsuite/integration/src/test/java/org/keycloak/testsuite/social/SocialLoginTest.java +++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/social/SocialLoginTest.java @@ -29,6 +29,7 @@ import org.junit.Test; import org.keycloak.OAuth2Constants; import org.keycloak.audit.Details; import org.keycloak.audit.Event; +import org.keycloak.audit.EventType; import org.keycloak.models.RealmModel; import org.keycloak.representations.AccessToken; import org.keycloak.representations.idm.UserRepresentation; @@ -110,7 +111,7 @@ public class SocialLoginTest { Assert.assertEquals(RequestType.AUTH_RESPONSE, appPage.getRequestType()); - String userId = events.expect("register") + String userId = events.expect(EventType.REGISTER) .user(AssertEvents.isUUID()) .detail(Details.EMAIL, "bob@builder.com") .detail(Details.RESPONSE_TYPE, "code") @@ -202,7 +203,7 @@ public class SocialLoginTest { Assert.assertEquals("Builder", profilePage.getLastName()); Assert.assertEquals("bob@builder.com", profilePage.getEmail()); - String userId = events.expect("register") + String userId = events.expect(EventType.REGISTER) .user(AssertEvents.isUUID()) .detail(Details.EMAIL, "bob@builder.com") .detail(Details.RESPONSE_TYPE, "code") @@ -213,8 +214,8 @@ public class SocialLoginTest { 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("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_PROFILE).user(userId).detail(Details.AUTH_METHOD, "social@dummy").detail(Details.USERNAME, "2@dummy").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());
{{key}} {{value}}