Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Select an option

  • Save thomasdarimont/25f801c624f3f457fa772fadd19b1e15 to your computer and use it in GitHub Desktop.

Select an option

Save thomasdarimont/25f801c624f3f457fa772fadd19b1e15 to your computer and use it in GitHub Desktop.

Revisions

  1. thomasdarimont revised this gist May 17, 2016. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion keycloak-server.json
    Original file line number Diff line number Diff line change
    @@ -1,7 +1,7 @@
    {
    "providers": [
    "classpath:${jboss.home.dir}/providers/*",
    "module:de.eurodata.idm.keycloak.idm-keycloak-ext-login-action"
    "module:de.tdlabs.idm.keycloak.idm-keycloak-ext-login-action"
    ],

    "admin": {
  2. thomasdarimont created this gist May 17, 2016.
    122 changes: 122 additions & 0 deletions LoginStatsRecordingRequiredActionProvider.java
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,122 @@
    package de.tdlabs.idm.keycloak.ext.authentication;

    import static java.time.LocalDateTime.now;
    import static java.util.Arrays.asList;

    import java.util.List;

    import org.jboss.logging.Logger;
    import org.keycloak.Config.Scope;
    import org.keycloak.authentication.RequiredActionContext;
    import org.keycloak.authentication.RequiredActionFactory;
    import org.keycloak.authentication.RequiredActionProvider;
    import org.keycloak.models.KeycloakSession;
    import org.keycloak.models.KeycloakSessionFactory;
    import org.keycloak.models.UserModel;

    public class LoginStatsRecordingRequiredActionProvider implements RequiredActionProvider, RequiredActionFactory {

    private static final Logger LOG = Logger.getLogger(LoginStatsRecordingRequiredActionProvider.class);

    private static final String PROVIDER_ID = "login_stats_action";
    private static final String RECORD_LOGIN_STATISTICS_ACTION = "Record Login Statistics Action";

    private static final String LOGIN_LOGIN_COUNT = "login.login-count";
    private static final String LOGIN_FIRST_LOGIN_DATE = "login.first-login-date";
    private static final String LOGIN_RECENT_LOGIN_DATE = "login.recent-login-date";

    private static final String ONE = "1";

    private static final LoginStatsRecordingRequiredActionProvider INSTANCE = new LoginStatsRecordingRequiredActionProvider();

    @Override
    public void close() {
    // NOOP
    }

    @Override
    public void evaluateTriggers(RequiredActionContext context) {

    UserModel user = context.getUser();

    try {
    recordFirstLogin(user);
    } catch (Exception ex) {
    LOG.warnv(ex,"Couldn't record first login <{0}>", this);
    }

    try {
    recordRecentLogin(user);
    } catch (Exception ex) {
    LOG.warnv(ex, "Couldn't record recent login <{0}>", this);
    }

    try {
    recordLoginCount(user);
    } catch (Exception ex) {
    LOG.warnv(ex, "Couldn't record login count <{0}>", this);
    }
    }

    private void recordLoginCount(UserModel user) {

    List<String> list = user.getAttribute(LOGIN_LOGIN_COUNT);

    if (list == null || list.isEmpty()) {
    list = asList(ONE);
    } else {
    list = asList(String.valueOf(Long.parseLong(list.get(0)) + 1));
    }

    user.setAttribute(LOGIN_LOGIN_COUNT, list);
    }

    private void recordRecentLogin(UserModel user) {
    user.setAttribute(LOGIN_RECENT_LOGIN_DATE, asList(now().toString()));
    }

    private void recordFirstLogin(UserModel user) {

    List<String> list = user.getAttribute(LOGIN_FIRST_LOGIN_DATE);

    if (list == null || list.isEmpty()) {
    user.setAttribute(LOGIN_FIRST_LOGIN_DATE, asList(now().toString()));
    }
    }

    @Override
    public void requiredActionChallenge(RequiredActionContext context) {
    // NOOP
    }

    @Override
    public void processAction(RequiredActionContext context) {
    context.success();
    }

    @Override
    public RequiredActionProvider create(KeycloakSession session) {
    return INSTANCE;
    }

    @Override
    public void init(Scope config) {
    LOG.infov("Creating IdM Keycloak extension <{0}>", this);
    // NOOP
    }

    @Override
    public void postInit(KeycloakSessionFactory factory) {
    // NOOP
    }

    @Override
    public String getId() {
    return PROVIDER_ID;
    }

    @Override
    public String getDisplayText() {
    return RECORD_LOGIN_STATISTICS_ACTION;
    }
    }
    5 changes: 5 additions & 0 deletions gistfile1.txt
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,5 @@
    # manual installation via jboss-cli
    # idm login recording action
    module add --name=de.tdlabs.idm.keycloak.idm-keycloak-ext-login-action \
    --resources=/tmp/idm-keycloak-ext-login-action.jar \
    --dependencies=org.keycloak.keycloak-common,org.keycloak.keycloak-core,org.keycloak.keycloak-server-spi,org.jboss.logging
    77 changes: 77 additions & 0 deletions keycloak-server.json
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,77 @@
    {
    "providers": [
    "classpath:${jboss.home.dir}/providers/*",
    "module:de.eurodata.idm.keycloak.idm-keycloak-ext-login-action"
    ],

    "admin": {
    "realm": "master"
    },

    "eventsStore": {
    "provider": "jpa",
    "jpa": {
    "exclude-events": [ "REFRESH_TOKEN" ]
    }
    },

    "realm": {
    "provider": "jpa"
    },

    "user": {
    "provider": "jpa"
    },

    "userCache": {
    "default" : {
    "enabled": true
    }
    },

    "userSessionPersister": {
    "provider": "jpa"
    },

    "timer": {
    "provider": "basic"
    },

    "theme": {
    "staticMaxAge": 2592000,
    "cacheTemplates": true,
    "cacheThemes": true,
    "folder": {
    "dir": "${jboss.home.dir}/themes"
    }
    },

    "scheduled": {
    "interval": 900
    },

    "connectionsHttpClient": {
    "default": {}
    },

    "connectionsJpa": {
    "default": {
    "dataSource": "java:jboss/datasources/KeycloakDS",
    "databaseSchema": "update"
    }
    },

    "realmCache": {
    "provider": "default",
    "default" : {
    "enabled": true
    }
    },

    "connectionsInfinispan": {
    "provider": "default",
    "default": {
    "cacheContainer" : "java:comp/env/infinispan/Keycloak"
    }
    }
    }
    16 changes: 16 additions & 0 deletions module.xml
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,16 @@
    <?xml version="1.0" ?>

    <module xmlns="urn:jboss:module:1.1" name="de.eurodata.idm.keycloak.idm-keycloak-ext-login-action">
    <!-- copy to $KEYCLOAK_HOME/modules/de/tdlabs/idm/keycloak/idm-keycloak-ext-login-action/main/ together with the jar -->
    <!-- add module reference in provider section of keycloak-server.json under $KEYCLOAK_HOME/standalone/configuration -->
    <resources>
    <resource-root path="idm-keycloak-ext-login-action-1.0.0.BUILD-SNAPSHOT.jar"/>
    </resources>

    <dependencies>
    <module name="org.keycloak.keycloak-common"/>
    <module name="org.keycloak.keycloak-core"/>
    <module name="org.keycloak.keycloak-server-spi"/>
    <module name="org.jboss.logging"/>
    </dependencies>
    </module>
    2 changes: 2 additions & 0 deletions org.keycloak.authentication.RequiredActionFactory
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,2 @@
    # place this under src/main/resources/META-INF/services
    de.tdlabs.idm.keycloak.ext.authentication.LoginStatsRecordingRequiredActionProvider
    43 changes: 43 additions & 0 deletions pom.xml
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,43 @@
    <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/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>de.tdlabs.idm</groupId>
    <artifactId>idm-keycloak-ext-login-action</artifactId>
    <version>1.0.0.BUILD-SNAPSHOT</version>

    <properties>
    <keycloak.version>1.9.4.Final</keycloak.version>

    <lombok.version>1.16.8</lombok.version>

    <maven.compiler.source>1.8</maven.compiler.source>
    <maven.compiler.target>1.8</maven.compiler.target>
    <project.build.sourceEncoding>ISO-8859-1</project.build.sourceEncoding>
    </properties>

    <dependencies>

    <dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
    <version>${lombok.version}</version>
    <scope>provided</scope>
    </dependency>

    <dependency>
    <groupId>org.keycloak</groupId>
    <artifactId>keycloak-server-spi</artifactId>
    <version>${keycloak.version}</version>
    <scope>provided</scope>
    </dependency>

    <dependency>
    <groupId>org.keycloak</groupId>
    <artifactId>keycloak-services</artifactId>
    <version>${keycloak.version}</version>
    <scope>provided</scope>
    </dependency>

    </dependencies>
    </project>