chore: Update to keycloak 25.0.6, rename namespace
Code taken from https://github.com/ThoreKr/keycloak-last-login-event-listener Has no license in repo, the author doubts it is original enough to copyright: https://github.com/ThoreKr/keycloak-last-login-event-listener/issues/7
This commit is contained in:
parent
f2a3da5f26
commit
1bc1b1f904
8 changed files with 229 additions and 208 deletions
|
@ -22,11 +22,11 @@
|
|||
pname = "keycloak-event-listener";
|
||||
version = "0.0.1";
|
||||
src = ./.;
|
||||
mvnHash = "sha256-SSiiKgrNUV2T84Vj4zhKzvl7HAZlPZ2OwUzoIsureu8=";
|
||||
mvnHash = "sha256-WNNfJAWUqY+mUCrwPNfBw/El19EZkcW/YOOJ4VoCvLA=";
|
||||
|
||||
installPhase = ''
|
||||
runHook preInstall
|
||||
install -Dm444 -t "$out" target/pubsolar.keycloak-event-listener.jar
|
||||
install -Dm444 -t "$out" target/solar.pub.keycloak-event-listener.jar
|
||||
runHook postInstall
|
||||
'';
|
||||
};
|
||||
|
|
151
pom.xml
151
pom.xml
|
@ -1,83 +1,82 @@
|
|||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<artifactId>event-listener</artifactId>
|
||||
<version>0.0.1</version>
|
||||
<groupId>pubsolar.keycloak</groupId>
|
||||
<artifactId>event-listener</artifactId>
|
||||
<version>0.0.1</version>
|
||||
<groupId>solar.pub.keycloak</groupId>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.keycloak</groupId>
|
||||
<artifactId>keycloak-core</artifactId>
|
||||
<version>24.0.5</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.keycloak</groupId>
|
||||
<artifactId>keycloak-server-spi</artifactId>
|
||||
<version>24.0.5</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.keycloak</groupId>
|
||||
<artifactId>keycloak-server-spi-private</artifactId>
|
||||
<version>24.0.5</version>
|
||||
</dependency>
|
||||
<!--dependency>
|
||||
<groupId>org.keycloak</groupId>
|
||||
<artifactId>keycloak-model-storage-private</artifactId>
|
||||
<version>24.0.5</version>
|
||||
</dependency-->
|
||||
<dependency>
|
||||
<groupId>com.google.auto.service</groupId>
|
||||
<artifactId>auto-service</artifactId>
|
||||
<version>1.1.1</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.projectlombok</groupId>
|
||||
<artifactId>lombok</artifactId>
|
||||
<version>1.18.32</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.commons</groupId>
|
||||
<artifactId>commons-lang3</artifactId>
|
||||
<version>3.14.0</version>
|
||||
</dependency>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.keycloak</groupId>
|
||||
<artifactId>keycloak-core</artifactId>
|
||||
<version>25.0.6</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.keycloak</groupId>
|
||||
<artifactId>keycloak-server-spi</artifactId>
|
||||
<version>25.0.6</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.keycloak</groupId>
|
||||
<artifactId>keycloak-server-spi-private</artifactId>
|
||||
<version>25.0.6</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.junit.jupiter</groupId>
|
||||
<artifactId>junit-jupiter</artifactId>
|
||||
<version>5.10.0</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.testcontainers</groupId>
|
||||
<artifactId>junit-jupiter</artifactId>
|
||||
<version>1.19.7</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.github.dasniko</groupId>
|
||||
<artifactId>testcontainers-keycloak</artifactId>
|
||||
<version>3.4.0</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.rest-assured</groupId>
|
||||
<artifactId>rest-assured</artifactId>
|
||||
<version>5.5.0</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.slf4j</groupId>
|
||||
<artifactId>slf4j-reload4j</artifactId>
|
||||
<version>2.0.13</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
<dependency>
|
||||
<groupId>org.projectlombok</groupId>
|
||||
<artifactId>lombok</artifactId>
|
||||
<version>1.18.32</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.commons</groupId>
|
||||
<artifactId>commons-lang3</artifactId>
|
||||
<version>3.14.0</version>
|
||||
</dependency>
|
||||
|
||||
<build>
|
||||
<finalName>${project.groupId}-${project.artifactId}</finalName>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-shade-plugin</artifactId>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
<dependency>
|
||||
<groupId>org.junit.jupiter</groupId>
|
||||
<artifactId>junit-jupiter</artifactId>
|
||||
<version>5.10.0</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.testcontainers</groupId>
|
||||
<artifactId>junit-jupiter</artifactId>
|
||||
<version>1.19.7</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.github.dasniko</groupId>
|
||||
<artifactId>testcontainers-keycloak</artifactId>
|
||||
<version>3.3.0</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.rest-assured</groupId>
|
||||
<artifactId>rest-assured</artifactId>
|
||||
<version>5.4.0</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.slf4j</groupId>
|
||||
<artifactId>slf4j-reload4j</artifactId>
|
||||
<version>2.0.13</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<finalName>${project.groupId}-${project.artifactId}</finalName>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-shade-plugin</artifactId>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
<configuration>
|
||||
<source>17</source>
|
||||
<target>17</target>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
</project>
|
||||
|
|
|
@ -1,53 +0,0 @@
|
|||
package pubsolar.keycloak.events;
|
||||
|
||||
import dasniko.testcontainers.keycloak.KeycloakContainer;
|
||||
import dasniko.keycloak.test.TestBase;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.keycloak.admin.client.Keycloak;
|
||||
import org.keycloak.representations.idm.RealmEventsConfigRepresentation;
|
||||
import org.keycloak.representations.idm.UserRepresentation;
|
||||
import org.testcontainers.junit.jupiter.Container;
|
||||
import org.testcontainers.junit.jupiter.Testcontainers;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
||||
import static org.junit.jupiter.api.Assertions.assertNull;
|
||||
|
||||
@Testcontainers
|
||||
public class LastLoginTimeListenerTest extends TestBase {
|
||||
|
||||
private static final String REALM = "demo";
|
||||
|
||||
@Container
|
||||
private static final KeycloakContainer keycloak = new KeycloakContainer()
|
||||
.withRealmImportFile("demo-realm.json")
|
||||
.withEnv("KC_SPI_EVENTS_LISTENER_LAST_LOGIN_TIME_ATTRIBUTE_NAME", "lastLogin")
|
||||
.withProviderClassesFrom("target/classes");
|
||||
|
||||
@Test
|
||||
public void testLastLoginTime() {
|
||||
Keycloak admin = keycloak.getKeycloakAdminClient();
|
||||
|
||||
// check user has no attributes
|
||||
List<UserRepresentation> users = admin.realm(REALM).users().searchByUsername("test", true);
|
||||
UserRepresentation testUser = users.get(0);
|
||||
Map<String, List<String>> attributes = testUser.getAttributes();
|
||||
assertNull(attributes);
|
||||
|
||||
// configure custom events listener
|
||||
RealmEventsConfigRepresentation eventsConfig = new RealmEventsConfigRepresentation();
|
||||
eventsConfig.setEventsListeners(List.of(LastLoginTimeListenerFactory.PROVIDER_ID));
|
||||
admin.realm(REALM).updateRealmEventsConfig(eventsConfig);
|
||||
|
||||
// "login" user
|
||||
requestToken(keycloak, REALM, "test", "test");
|
||||
|
||||
// check user has last-login-time attribute
|
||||
testUser = admin.realm(REALM).users().searchByUsername("test", true).get(0);
|
||||
String lastLoginTime = testUser.firstAttribute("lastLogin");
|
||||
assertNotNull(lastLoginTime);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,53 @@
|
|||
package solar.pub.keycloak.events.logging;
|
||||
|
||||
import dasniko.testcontainers.keycloak.KeycloakContainer;
|
||||
import dasniko.keycloak.test.TestBase;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.keycloak.admin.client.Keycloak;
|
||||
import org.keycloak.representations.idm.RealmEventsConfigRepresentation;
|
||||
import org.keycloak.representations.idm.UserRepresentation;
|
||||
import org.testcontainers.junit.jupiter.Container;
|
||||
import org.testcontainers.junit.jupiter.Testcontainers;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
||||
import static org.junit.jupiter.api.Assertions.assertNull;
|
||||
|
||||
@Testcontainers
|
||||
public class LastLoginTimeListenerTest extends TestBase {
|
||||
|
||||
private static final String REALM = "demo";
|
||||
|
||||
@Container
|
||||
private static final KeycloakContainer keycloak = new KeycloakContainer()
|
||||
.withRealmImportFile("demo-realm.json")
|
||||
.withEnv("KC_SPI_EVENTS_LISTENER_LAST_LOGIN_TIME_ATTRIBUTE_NAME", "last-login")
|
||||
.withProviderClassesFrom("target/classes");
|
||||
|
||||
@Test
|
||||
public void testLastLoginTime() {
|
||||
Keycloak admin = keycloak.getKeycloakAdminClient();
|
||||
|
||||
// check user has no attributes
|
||||
List<UserRepresentation> users = admin.realm(REALM).users().searchByUsername("test", true);
|
||||
UserRepresentation testUser = users.get(0);
|
||||
Map<String, List<String>> attributes = testUser.getAttributes();
|
||||
assertNull(attributes);
|
||||
|
||||
// configure custom events listener
|
||||
RealmEventsConfigRepresentation eventsConfig = new RealmEventsConfigRepresentation();
|
||||
eventsConfig.setEventsListeners(List.of(LastLoginTimeListenerFactory.PROVIDER_ID));
|
||||
admin.realm(REALM).updateRealmEventsConfig(eventsConfig);
|
||||
|
||||
// "login" user
|
||||
requestToken(keycloak, REALM, "test", "test");
|
||||
|
||||
// check user has last-login-time attribute
|
||||
testUser = admin.realm(REALM).users().searchByUsername("test", true).get(0);
|
||||
String lastLoginTime = testUser.firstAttribute("last-login");
|
||||
assertNotNull(lastLoginTime);
|
||||
}
|
||||
|
||||
}
|
|
@ -1,38 +0,0 @@
|
|||
package pubsolar.keycloak.events;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.keycloak.common.util.Time;
|
||||
import org.keycloak.events.Event;
|
||||
import org.keycloak.events.EventListenerProvider;
|
||||
import org.keycloak.events.EventType;
|
||||
import org.keycloak.events.admin.AdminEvent;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.models.UserModel;
|
||||
|
||||
/**
|
||||
* @author Niko Köbler, https://www.n-k.de, @dasniko
|
||||
*/
|
||||
@RequiredArgsConstructor
|
||||
public class LastLoginTimeListener implements EventListenerProvider {
|
||||
|
||||
private final KeycloakSession session;
|
||||
|
||||
@Override
|
||||
public void onEvent(Event event) {
|
||||
if (event.getType().equals(EventType.LOGIN)) {
|
||||
UserModel user = session.users().getUserById(session.getContext().getRealm(), event.getUserId());
|
||||
if (user != null) {
|
||||
user.setSingleAttribute(LastLoginTimeListenerFactory.attributeName, Integer.toString(Time.currentTime()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onEvent(AdminEvent event, boolean includeRepresentation) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
}
|
||||
|
||||
}
|
|
@ -1,39 +0,0 @@
|
|||
package pubsolar.keycloak.events;
|
||||
|
||||
import com.google.auto.service.AutoService;
|
||||
import org.keycloak.Config;
|
||||
import org.keycloak.events.EventListenerProvider;
|
||||
import org.keycloak.events.EventListenerProviderFactory;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.models.KeycloakSessionFactory;
|
||||
|
||||
@AutoService(EventListenerProviderFactory.class)
|
||||
public class LastLoginTimeListenerFactory implements EventListenerProviderFactory {
|
||||
|
||||
public static final String PROVIDER_ID = "last-login-time";
|
||||
|
||||
static String attributeName;
|
||||
|
||||
@Override
|
||||
public EventListenerProvider create(KeycloakSession session) {
|
||||
return new LastLoginTimeListener(session);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void init(Config.Scope config) {
|
||||
attributeName = config.get("attribute-name", "lastLoginTime");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void postInit(KeycloakSessionFactory factory) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getId() {
|
||||
return PROVIDER_ID;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,63 @@
|
|||
package solar.pub.keycloak.events.logging;
|
||||
|
||||
import org.jboss.logging.Logger;
|
||||
import org.keycloak.events.Event;
|
||||
import org.keycloak.events.EventListenerProvider;
|
||||
import org.keycloak.events.EventType;
|
||||
import org.keycloak.events.admin.AdminEvent;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.models.RealmProvider;
|
||||
|
||||
import java.time.ZoneOffset;
|
||||
import java.time.ZonedDateTime;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
|
||||
public class LastLoginEventListenerProvider implements EventListenerProvider {
|
||||
|
||||
private static final Logger log = Logger.getLogger(LastLoginEventListenerProvider.class);
|
||||
|
||||
private final KeycloakSession session;
|
||||
private final RealmProvider model;
|
||||
|
||||
public LastLoginEventListenerProvider(KeycloakSession session) {
|
||||
this.session = session;
|
||||
this.model = session.realms();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onEvent(Event event) {
|
||||
if (!EventType.LOGIN.equals(event.getType())) {
|
||||
return;
|
||||
}
|
||||
var realm = model.getRealm(event.getRealmId());
|
||||
var user = session.users().getUserById(realm, event.getUserId());
|
||||
|
||||
if (user == null) {
|
||||
return;
|
||||
}
|
||||
log.info("Updating last login status for user: " + user.getUsername());
|
||||
|
||||
var userAttrs = user.getAttributes();
|
||||
if (userAttrs.containsKey("last-login")) {
|
||||
var userLastLogin = userAttrs.get("last-login");
|
||||
if (userLastLogin != null && !userLastLogin.isEmpty()) {
|
||||
user.setSingleAttribute("prior-login", userLastLogin.get(0));
|
||||
}
|
||||
}
|
||||
|
||||
// Use current server time for login event
|
||||
var loginTime = ZonedDateTime.now(ZoneOffset.UTC);
|
||||
var loginTimeS = DateTimeFormatter.ISO_DATE_TIME.format(loginTime);
|
||||
user.setSingleAttribute("last-login", loginTimeS);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onEvent(AdminEvent adminEvent, boolean b) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
// Nothing to close
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,36 @@
|
|||
package solar.pub.keycloak.events.logging;
|
||||
|
||||
import org.keycloak.Config;
|
||||
import org.keycloak.events.EventListenerProviderFactory;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.models.KeycloakSessionFactory;
|
||||
|
||||
|
||||
public class LastLoginEventListenerProviderFactory implements EventListenerProviderFactory {
|
||||
|
||||
@Override
|
||||
public LastLoginEventListenerProvider create(KeycloakSession keycloakSession) {
|
||||
return new LastLoginEventListenerProvider(keycloakSession);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void init(Config.Scope scope) {
|
||||
//
|
||||
}
|
||||
|
||||
@Override
|
||||
public void postInit(KeycloakSessionFactory keycloakSessionFactory) {
|
||||
//
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
//
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getId() {
|
||||
return "last_login";
|
||||
}
|
||||
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue