From 3433227fa7a808c5aa06bf64125d47e938a7d370 Mon Sep 17 00:00:00 2001 From: Stian Thorgersen Date: Thu, 3 Apr 2014 16:27:22 +0100 Subject: [PATCH] Added audit log to account mngmt --- .../org/keycloak/audit/jpa/EventEntity.java | 2 + .../keycloak/audit/jpa/JpaAuditProvider.java | 0 .../audit/jpa/JpaAuditProviderFactory.java | 0 .../org/keycloak/audit/jpa/JpaEventQuery.java | 2 +- .../org.keycloak.audit.AuditProviderFactory | 0 .../audit/jpa/JpaAuditProviderTest.java | 0 .../test/resources/META-INF/persistence.xml | 0 .../keycloak/audit/mongo/MongoEventQuery.java | 2 +- .../tests/AbstractAuditProviderTest.java | 10 +++- .../java/org/keycloak/account/Account.java | 19 +++--- .../account/freemarker/FreeMarkerAccount.java | 13 +++- .../freemarker/model/FeaturesBean.java | 24 ++++++++ .../account/freemarker/model/UrlBean.java | 4 ++ .../main/resources/theme/account/base/log.ftl | 26 ++++---- .../resources/theme/account/base/template.ftl | 3 +- .../main/java/org/keycloak/models/Config.java | 2 +- .../services/DefaultProviderSession.java | 16 ++--- .../DefaultProviderSessionFactory.java | 4 ++ .../services/resources/AccountService.java | 13 ++-- .../services/resources/flows/Urls.java | 4 ++ testsuite/integration/pom.xml | 5 ++ .../main/resources/META-INF/persistence.xml | 18 ++++++ .../testsuite/account/AccountTest.java | 56 +++++++++++++++++ .../testsuite/pages/AccountLogPage.java | 60 +++++++++++++++++++ 24 files changed, 237 insertions(+), 46 deletions(-) rename audit/jpa/src/{src => }/main/java/org/keycloak/audit/jpa/EventEntity.java (96%) rename audit/jpa/src/{src => }/main/java/org/keycloak/audit/jpa/JpaAuditProvider.java (100%) rename audit/jpa/src/{src => }/main/java/org/keycloak/audit/jpa/JpaAuditProviderFactory.java (100%) rename audit/jpa/src/{src => }/main/java/org/keycloak/audit/jpa/JpaEventQuery.java (97%) rename audit/jpa/src/{src => }/main/resources/META-INF/services/org.keycloak.audit.AuditProviderFactory (100%) rename audit/jpa/src/{src => }/test/java/org/keycloak/audit/jpa/JpaAuditProviderTest.java (100%) rename audit/jpa/src/{src => }/test/resources/META-INF/persistence.xml (100%) create mode 100644 forms/account-freemarker/src/main/java/org/keycloak/account/freemarker/model/FeaturesBean.java create mode 100644 testsuite/integration/src/test/java/org/keycloak/testsuite/pages/AccountLogPage.java diff --git a/audit/jpa/src/src/main/java/org/keycloak/audit/jpa/EventEntity.java b/audit/jpa/src/main/java/org/keycloak/audit/jpa/EventEntity.java similarity index 96% rename from audit/jpa/src/src/main/java/org/keycloak/audit/jpa/EventEntity.java rename to audit/jpa/src/main/java/org/keycloak/audit/jpa/EventEntity.java index a8fd5317cf..1227b70cdd 100644 --- a/audit/jpa/src/src/main/java/org/keycloak/audit/jpa/EventEntity.java +++ b/audit/jpa/src/main/java/org/keycloak/audit/jpa/EventEntity.java @@ -1,5 +1,6 @@ package org.keycloak.audit.jpa; +import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.Id; @@ -26,6 +27,7 @@ public class EventEntity { private String error; + @Column(length = 2550) private String detailsJson; public String getId() { diff --git a/audit/jpa/src/src/main/java/org/keycloak/audit/jpa/JpaAuditProvider.java b/audit/jpa/src/main/java/org/keycloak/audit/jpa/JpaAuditProvider.java similarity index 100% rename from audit/jpa/src/src/main/java/org/keycloak/audit/jpa/JpaAuditProvider.java rename to audit/jpa/src/main/java/org/keycloak/audit/jpa/JpaAuditProvider.java diff --git a/audit/jpa/src/src/main/java/org/keycloak/audit/jpa/JpaAuditProviderFactory.java b/audit/jpa/src/main/java/org/keycloak/audit/jpa/JpaAuditProviderFactory.java similarity index 100% rename from audit/jpa/src/src/main/java/org/keycloak/audit/jpa/JpaAuditProviderFactory.java rename to audit/jpa/src/main/java/org/keycloak/audit/jpa/JpaAuditProviderFactory.java diff --git a/audit/jpa/src/src/main/java/org/keycloak/audit/jpa/JpaEventQuery.java b/audit/jpa/src/main/java/org/keycloak/audit/jpa/JpaEventQuery.java similarity index 97% rename from audit/jpa/src/src/main/java/org/keycloak/audit/jpa/JpaEventQuery.java rename to audit/jpa/src/main/java/org/keycloak/audit/jpa/JpaEventQuery.java index b2702d0dce..c0dd127a4f 100644 --- a/audit/jpa/src/src/main/java/org/keycloak/audit/jpa/JpaEventQuery.java +++ b/audit/jpa/src/main/java/org/keycloak/audit/jpa/JpaEventQuery.java @@ -77,7 +77,7 @@ public class JpaEventQuery implements EventQuery { cq.where(cb.and(predicates.toArray(new Predicate[predicates.size()]))); } - cq.orderBy(cb.asc(root.get("time")), cb.asc(root.get("id"))); + cq.orderBy(cb.desc(root.get("time"))); TypedQuery query = em.createQuery(cq); diff --git a/audit/jpa/src/src/main/resources/META-INF/services/org.keycloak.audit.AuditProviderFactory b/audit/jpa/src/main/resources/META-INF/services/org.keycloak.audit.AuditProviderFactory similarity index 100% rename from audit/jpa/src/src/main/resources/META-INF/services/org.keycloak.audit.AuditProviderFactory rename to audit/jpa/src/main/resources/META-INF/services/org.keycloak.audit.AuditProviderFactory diff --git a/audit/jpa/src/src/test/java/org/keycloak/audit/jpa/JpaAuditProviderTest.java b/audit/jpa/src/test/java/org/keycloak/audit/jpa/JpaAuditProviderTest.java similarity index 100% rename from audit/jpa/src/src/test/java/org/keycloak/audit/jpa/JpaAuditProviderTest.java rename to audit/jpa/src/test/java/org/keycloak/audit/jpa/JpaAuditProviderTest.java diff --git a/audit/jpa/src/src/test/resources/META-INF/persistence.xml b/audit/jpa/src/test/resources/META-INF/persistence.xml similarity index 100% rename from audit/jpa/src/src/test/resources/META-INF/persistence.xml rename to audit/jpa/src/test/resources/META-INF/persistence.xml 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 9ea67b9f92..5c75e63dca 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 @@ -62,7 +62,7 @@ public class MongoEventQuery implements EventQuery { @Override public List getResultList() { - DBCursor cur = audit.find(query); + DBCursor cur = audit.find(query).sort(new BasicDBObject("time", -1)); if (firstResult != null) { cur.skip(firstResult); } 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 217dea4e78..c3ebdaae65 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 @@ -46,10 +46,13 @@ public abstract class AbstractAuditProviderTest { @Test public void query() { + 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("event2", "realmId", "clientId", "userId", "127.0.0.1", "error")); + provider.onEvent(create(newest, "event2", "realmId", "clientId", "userId", "127.0.0.1", "error")); provider.onEvent(create("event", "realmId2", "clientId", "userId", "127.0.0.1", "error")); - provider.onEvent(create("event", "realmId", "clientId2", "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.close(); @@ -65,6 +68,9 @@ public abstract class AbstractAuditProviderTest { Assert.assertEquals(2, provider.createQuery().maxResults(2).getResultList().size()); Assert.assertEquals(1, provider.createQuery().firstResult(4).getResultList().size()); + + Assert.assertEquals(newest, provider.createQuery().maxResults(1).getResultList().get(0).getTime()); + Assert.assertEquals(oldest, provider.createQuery().firstResult(4).maxResults(1).getResultList().get(0).getTime()); } @Test diff --git a/forms/account-api/src/main/java/org/keycloak/account/Account.java b/forms/account-api/src/main/java/org/keycloak/account/Account.java index 3791611c73..f92b161ecb 100644 --- a/forms/account-api/src/main/java/org/keycloak/account/Account.java +++ b/forms/account-api/src/main/java/org/keycloak/account/Account.java @@ -13,22 +13,23 @@ import java.util.List; */ public interface Account { - public Response createResponse(AccountPages page); + Response createResponse(AccountPages page); - public Account setError(String message); + Account setError(String message); - public Account setSuccess(String message); + Account setSuccess(String message); - public Account setWarning(String message); + Account setWarning(String message); - public Account setUser(UserModel user); + Account setUser(UserModel user); - public Account setStatus(Response.Status status); + Account setStatus(Response.Status status); - public Account setRealm(RealmModel realm); + Account setRealm(RealmModel realm); - public Account setReferrer(String[] referrer); + Account setReferrer(String[] referrer); - public Account setEvents(List events); + Account setEvents(List events); + Account setFeatures(boolean social, boolean audit); } diff --git a/forms/account-freemarker/src/main/java/org/keycloak/account/freemarker/FreeMarkerAccount.java b/forms/account-freemarker/src/main/java/org/keycloak/account/freemarker/FreeMarkerAccount.java index 6074ad197f..53f43d4593 100644 --- a/forms/account-freemarker/src/main/java/org/keycloak/account/freemarker/FreeMarkerAccount.java +++ b/forms/account-freemarker/src/main/java/org/keycloak/account/freemarker/FreeMarkerAccount.java @@ -5,6 +5,7 @@ import org.keycloak.account.Account; import org.keycloak.account.AccountPages; import org.keycloak.account.freemarker.model.AccountBean; import org.keycloak.account.freemarker.model.AccountSocialBean; +import org.keycloak.account.freemarker.model.FeaturesBean; import org.keycloak.account.freemarker.model.LogBean; import org.keycloak.account.freemarker.model.MessageBean; import org.keycloak.account.freemarker.model.ReferrerBean; @@ -41,6 +42,8 @@ public class FreeMarkerAccount implements Account { private RealmModel realm; private String[] referrer; private List events; + private boolean social; + private boolean audit; public static enum MessageType {SUCCESS, WARNING, ERROR} @@ -92,9 +95,7 @@ public class FreeMarkerAccount implements Account { attributes.put("url", new UrlBean(realm, theme, baseUri)); - if (realm.isSocial()) { - attributes.put("isSocialRealm", true); - } + attributes.put("features", new FeaturesBean(social, audit)); switch (page) { case ACCOUNT: @@ -170,4 +171,10 @@ public class FreeMarkerAccount implements Account { return this; } + @Override + public Account setFeatures(boolean social, boolean audit) { + this.social = social; + this.audit = audit; + return this; + } } diff --git a/forms/account-freemarker/src/main/java/org/keycloak/account/freemarker/model/FeaturesBean.java b/forms/account-freemarker/src/main/java/org/keycloak/account/freemarker/model/FeaturesBean.java new file mode 100644 index 0000000000..6f8158ba81 --- /dev/null +++ b/forms/account-freemarker/src/main/java/org/keycloak/account/freemarker/model/FeaturesBean.java @@ -0,0 +1,24 @@ +package org.keycloak.account.freemarker.model; + +/** + * @author Stian Thorgersen + */ +public class FeaturesBean { + + private final boolean social; + private final boolean log; + + public FeaturesBean(boolean social, boolean log) { + this.social = social; + this.log = log; + } + + public boolean isSocial() { + return social; + } + + public boolean isLog() { + return log; + } + +} diff --git a/forms/account-freemarker/src/main/java/org/keycloak/account/freemarker/model/UrlBean.java b/forms/account-freemarker/src/main/java/org/keycloak/account/freemarker/model/UrlBean.java index 53dde7cd66..e626ddf33f 100644 --- a/forms/account-freemarker/src/main/java/org/keycloak/account/freemarker/model/UrlBean.java +++ b/forms/account-freemarker/src/main/java/org/keycloak/account/freemarker/model/UrlBean.java @@ -41,6 +41,10 @@ public class UrlBean { return Urls.accountTotpPage(baseURI, realm).toString(); } + public String getLogUrl() { + return Urls.accountLogPage(baseURI, realm).toString(); + } + public String getTotpRemoveUrl() { return Urls.accountTotpRemove(baseURI, realm).toString(); } diff --git a/forms/common-themes/src/main/resources/theme/account/base/log.ftl b/forms/common-themes/src/main/resources/theme/account/base/log.ftl index 3f3c917b60..97523173dc 100644 --- a/forms/common-themes/src/main/resources/theme/account/base/log.ftl +++ b/forms/common-themes/src/main/resources/theme/account/base/log.ftl @@ -1,28 +1,32 @@ <#import "template.ftl" as layout> -<@layout.mainLayout active='social' bodyClass='social'; section> +<@layout.mainLayout active='log' bodyClass='log'; section>
-

Social Accounts

+

Account Log

- - - - - - +
- ${event.date}${event.event}${event.ipAddress}${event.clientId}
+ + + + + + + + + <#list log.events as event> - + - ${event.client} +
DateEventIPClient
${event.date}${event.date?datetime} ${event.event} ${event.ipAddress}${event.clientId}
\ No newline at end of file diff --git a/forms/common-themes/src/main/resources/theme/account/base/template.ftl b/forms/common-themes/src/main/resources/theme/account/base/template.ftl index d6a2f5eb61..6a39817a40 100644 --- a/forms/common-themes/src/main/resources/theme/account/base/template.ftl +++ b/forms/common-themes/src/main/resources/theme/account/base/template.ftl @@ -42,7 +42,8 @@
  • Account
  • Password
  • Authenticator
  • - <#if isSocialRealm?has_content>
  • Social
  • + <#if features.social>
  • Social
  • + <#if features.log>
  • Log
  • diff --git a/model/api/src/main/java/org/keycloak/models/Config.java b/model/api/src/main/java/org/keycloak/models/Config.java index 6c53c61529..7851ea8b78 100644 --- a/model/api/src/main/java/org/keycloak/models/Config.java +++ b/model/api/src/main/java/org/keycloak/models/Config.java @@ -30,7 +30,7 @@ public class Config { } public static String getAuditProvider() { - return System.getProperty(MODEL_PROVIDER_KEY); + return System.getProperty(MODEL_PROVIDER_KEY, "jpa"); } public static void setAuditProvider(String provider) { diff --git a/services/src/main/java/org/keycloak/services/DefaultProviderSession.java b/services/src/main/java/org/keycloak/services/DefaultProviderSession.java index 748055b6ef..f9e2948b3e 100755 --- a/services/src/main/java/org/keycloak/services/DefaultProviderSession.java +++ b/services/src/main/java/org/keycloak/services/DefaultProviderSession.java @@ -11,24 +11,16 @@ import java.util.Set; * @author Stian Thorgersen */ public class DefaultProviderSession implements ProviderSession { - private ProviderSessionFactory factory; + private DefaultProviderSessionFactory factory; private Map providers = new HashMap(); - public DefaultProviderSession(ProviderSessionFactory factory) { + public DefaultProviderSession(DefaultProviderSessionFactory factory) { this.factory = factory; } public T getProvider(Class clazz) { - Integer hash = clazz.hashCode(); - T provider = (T) providers.get(hash); - if (provider == null) { - ProviderFactory providerFactory = factory.getProviderFactory(clazz); - if (providerFactory != null) { - provider = providerFactory.create(); - providers.put(hash, provider); - } - } - return provider; + String id = factory.getDefaultProvider(clazz); + return id != null ? getProvider(clazz, id) : null; } public T getProvider(Class clazz, String id) { diff --git a/services/src/main/java/org/keycloak/services/DefaultProviderSessionFactory.java b/services/src/main/java/org/keycloak/services/DefaultProviderSessionFactory.java index c6b5943258..67285593e3 100755 --- a/services/src/main/java/org/keycloak/services/DefaultProviderSessionFactory.java +++ b/services/src/main/java/org/keycloak/services/DefaultProviderSessionFactory.java @@ -41,6 +41,10 @@ public class DefaultProviderSessionFactory implements ProviderSessionFactory { return loader != null ? loader.providerIds() : null; } + public String getDefaultProvider(Class clazz) { + return defaultFactories.get(clazz); + } + public void registerLoader(Class clazz, ProviderFactoryLoader loader) { loaders.put(clazz, loader); 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 8bc626943e..9f47d7c6d9 100755 --- a/services/src/main/java/org/keycloak/services/resources/AccountService.java +++ b/services/src/main/java/org/keycloak/services/resources/AccountService.java @@ -110,6 +110,7 @@ public class AccountService { private final SocialRequestManager socialRequestManager; private Account account; private Auth auth; + private AuditProvider auditProvider; public AccountService(RealmModel realm, ApplicationModel application, TokenManager tokenManager, SocialRequestManager socialRequestManager, Audit audit) { this.realm = realm; @@ -120,7 +121,9 @@ public class AccountService { } public void init() { - account = AccountLoader.load().createAccount(uriInfo).setRealm(realm); + auditProvider = providers.getProvider(AuditProvider.class); + + account = AccountLoader.load().createAccount(uriInfo).setRealm(realm).setFeatures(realm.isSocial(), auditProvider != null); auth = authManager.authenticate(realm, headers); if (auth != null) { @@ -133,7 +136,6 @@ public class AccountService { return base; } - private Response forwardToPage(String path, AccountPages page) { if (auth != null) { try { @@ -195,9 +197,10 @@ public class AccountService { @Path("log") @GET public Response logPage() { - AuditProvider audit = providers.getProvider(AuditProvider.class); - List events = audit.createQuery().user(auth.getUser().getId()).maxResults(20).getResultList(); - account.setEvents(events); + if (auth != null) { + List events = auditProvider.createQuery().user(auth.getUser().getId()).maxResults(20).getResultList(); + account.setEvents(events); + } return forwardToPage("log", AccountPages.LOG); } diff --git a/services/src/main/java/org/keycloak/services/resources/flows/Urls.java b/services/src/main/java/org/keycloak/services/resources/flows/Urls.java index 4147adafc6..55137c4e3f 100755 --- a/services/src/main/java/org/keycloak/services/resources/flows/Urls.java +++ b/services/src/main/java/org/keycloak/services/resources/flows/Urls.java @@ -74,6 +74,10 @@ public class Urls { return accountBase(baseUri).path(AccountService.class, "processTotpRemove").build(realmId); } + public static URI accountLogPage(URI baseUri, String realmId) { + return accountBase(baseUri).path(AccountService.class, "logPage").build(realmId); + } + public static URI accountLogout(URI baseUri, String realmId) { return accountBase(baseUri).path(AccountService.class, "logout").build(realmId); } diff --git a/testsuite/integration/pom.xml b/testsuite/integration/pom.xml index 115c618b32..0b3cd3d2eb 100755 --- a/testsuite/integration/pom.xml +++ b/testsuite/integration/pom.xml @@ -42,6 +42,11 @@ keycloak-audit-api ${project.version} + + org.keycloak + keycloak-audit-jpa + ${project.version} + org.keycloak keycloak-audit-jboss-logging diff --git a/testsuite/integration/src/main/resources/META-INF/persistence.xml b/testsuite/integration/src/main/resources/META-INF/persistence.xml index 5ca96af932..56a5ed4517 100755 --- a/testsuite/integration/src/main/resources/META-INF/persistence.xml +++ b/testsuite/integration/src/main/resources/META-INF/persistence.xml @@ -32,6 +32,24 @@ + + org.hibernate.ejb.HibernatePersistence + + org.keycloak.audit.jpa.EventEntity + + true + + + + + + + + + + + +