Skip to content
Merged
Show file tree
Hide file tree
Changes from 66 commits
Commits
Show all changes
88 commits
Select commit Hold shift + click to select a range
2f7e1b3
Stash: building builtin users Keycloak SPI boilerplate (WIP)
GPortas Jan 27, 2025
fa72703
Added: Keycloak SPI base logic to support searching dv builtin users …
GPortas Jan 28, 2025
68fbbb0
Merge branch 'develop' of github.com:IQSS/dataverse into 11157-builti…
GPortas Jan 28, 2025
db34307
Added: DataverseAuthenticatedUser for populating extra information in…
GPortas Jan 29, 2025
bc97f9a
Merge branch 'develop' of github.com:IQSS/dataverse into 11157-builti…
GPortas Jan 29, 2025
72acd63
Added: achieving authentication from Keycloak builtin users SPI throu…
GPortas Feb 2, 2025
d05ea89
Changed: removed UserQueryMethodsProvider impl and minor tweaks and f…
GPortas Feb 2, 2025
59f19c2
Added: email/password builtin users auth
GPortas Feb 2, 2025
1f5b2b7
Refactor: DataverseUserStorageProvider
GPortas Feb 3, 2025
0ed0b79
Refactor: DataverseAPIService
GPortas Feb 3, 2025
c8d8af0
Refactor: DataverseUserStorageProvider
GPortas Feb 3, 2025
b54aeb8
Changed: temporal warning log levels
GPortas Feb 4, 2025
2206e2a
Added: temporal warning log in AuthenticationServiceBean
GPortas Feb 4, 2025
ec76ce3
Changed: querying authenticated users before lookup in OIDC flow
GPortas Feb 4, 2025
a1ea5e0
Changed: temporal disable unit test for PoC
GPortas Feb 4, 2025
87ab989
Changed: reverted log level back to warning in AuthenticationServiceBean
GPortas Feb 4, 2025
6b49842
Changed: log level in AuthenticationServiceBean
GPortas Feb 4, 2025
e60f4a3
Merge branch 'develop' of github.com:IQSS/dataverse into 11157-builti…
GPortas Feb 6, 2025
04f7b39
Added: UserEventListenerProvider logging users login
GPortas Feb 10, 2025
8b0defc
Merge branch 'develop' of github.com:IQSS/dataverse into 11157-builti…
GPortas Feb 11, 2025
267c6e7
Removed: event listeners for a simpler first iteration
GPortas Feb 11, 2025
c49b35d
Changed: parametrized quarkus properties in builtin-users-spi
GPortas Feb 11, 2025
fbfa9c4
Changed: spi folders structure and parametrized DATAVERSE_BASE_URL
GPortas Feb 11, 2025
7abce92
Added: testing dependencies
GPortas Feb 11, 2025
26b5f57
Added: DataverseUserStorageProviderTest
GPortas Feb 11, 2025
9c25eeb
Added: DataverseAPIServiceTest
GPortas Feb 11, 2025
ee6e9f0
Removed: logs from AuthenticationServiceBean
GPortas Feb 11, 2025
6695b79
Changed: AuthenticationServiceBeanTest re-enabled with updates
GPortas Feb 11, 2025
92aaa91
Refactor: AuthenticationServiceBeanTest.setupAuthenticatedUserByIdent…
GPortas Feb 11, 2025
169aa75
Changed: updated oauth2-oidc-sdk and excluded builtin-users-spi from …
GPortas Feb 12, 2025
f137e62
Merge branch 'develop' of github.com:IQSS/dataverse into 11157-builti…
GPortas Feb 14, 2025
7fd32c9
Added: API_BEARER_AUTH_USE_BUILTIN_USER_ON_ID_MATCH feature flag
GPortas Feb 14, 2025
67552e1
Added: docs for api-bearer-auth-use-builtin-user-on-id-match
GPortas Feb 14, 2025
22fc4da
Changed: null check added to AuthenticationServiceBean and updated te…
GPortas Feb 14, 2025
779dfd6
Fixed: docs rst format issue
GPortas Feb 14, 2025
db83ddc
Added: refined implementation and tests for canLoginWithGivenCredenti…
GPortas Feb 14, 2025
321b8f2
Added: custom Keycloak Dockerfile for spi building and realm setup
GPortas Feb 14, 2025
57b3a3b
Added: dev_keycloak_initializer for SPI setup in realm
GPortas Feb 16, 2025
9ddb86b
Added: release notes for #11197
GPortas Feb 17, 2025
2dc196b
Added: API docs for builtin-users/canLoginWithGivenCredentials
GPortas Feb 17, 2025
fdda614
Merge branch 'develop' of github.com:IQSS/dataverse into 11157-builti…
GPortas Feb 17, 2025
2e5dde2
Fixed: maven docker plugin build by setting an image for dev_keycloak
GPortas Feb 17, 2025
3ca4940
Changed: dev keycloak image name
GPortas Feb 17, 2025
247b631
Merge branch 'develop' of github.com:IQSS/dataverse into 11157-builti…
GPortas Mar 4, 2025
900f075
Changed: builtin-users-spi maven compiler plugin version to 17
GPortas Mar 4, 2025
0de1d22
Stash: changing builtin-users-spi to use the database instead of API …
GPortas Mar 5, 2025
f1c1ca2
Added: DataverseUserServiceTest
GPortas Mar 6, 2025
24e0d39
Added: DataverseAuthenticationServiceTest
GPortas Mar 6, 2025
1423cf3
Added: spi code cleanup and reactivated tests
GPortas Mar 6, 2025
e87eb80
Changed: log levels from info to debug
GPortas Mar 7, 2025
93bf7eb
Removed: canLoginWithGivenCredentials API endpoint
GPortas Mar 7, 2025
6b8526a
Refactor: getAuthenticatedUserWithProvider
GPortas Mar 7, 2025
2070589
Refactor: lookupUserByOIDCBearerToken
GPortas Mar 7, 2025
0269cc7
Changed: updated #11197 release notes
GPortas Mar 7, 2025
7ba6edd
Changed: updated api-bearer-auth-use-builtin-user-on-id-match docs
GPortas Mar 7, 2025
e88217a
Merge branch 'develop' of github.com:IQSS/dataverse into 11157-builti…
GPortas Mar 7, 2025
49c887d
Fixed: AuthenticationServiceBeanTest
GPortas Mar 7, 2025
bdbc3c9
Merge branch 'develop' of github.com:IQSS/dataverse into 11157-builti…
GPortas Mar 10, 2025
a7a751d
Changed: Keycloak version updated to v25
GPortas Mar 11, 2025
08ce0f3
Merge branch 'develop' of github.com:IQSS/dataverse into 11157-builti…
GPortas Mar 11, 2025
6101d73
Merge branch 'develop' of github.com:IQSS/dataverse into 11157-builti…
GPortas Mar 13, 2025
fe3e836
Merge branch 'develop' of github.com:IQSS/dataverse into 11157-builti…
GPortas Mar 19, 2025
781614b
Changed: Dataverse version in since section of feature flag
GPortas Mar 19, 2025
02e2f93
Merge branch 'develop' into 11157-builtin-users-oidc-auth
GPortas Mar 24, 2025
2f0b2c8
Merge branch 'develop' of github.com:IQSS/dataverse into 11157-builti…
GPortas Mar 27, 2025
ff52e24
Merge branch '11157-builtin-users-oidc-auth' of github.com:IQSS/datav…
GPortas Mar 27, 2025
97489e8
Added: note to PasswordEncryption SPI class to advice about syncing t…
GPortas Mar 27, 2025
f5de1d1
Changed: image name in dev_keycloak container
GPortas Mar 27, 2025
82a90a0
Changed: keycloak image name
GPortas Mar 27, 2025
62d3b13
Stash: upgrading to kc 26
GPortas Mar 27, 2025
49a6cf0
Changed: Keycloak upgraded to 26
GPortas Mar 27, 2025
ba08b5f
Changed: upgraded Oracle JDBC version in Keycloak image
GPortas Mar 27, 2025
0fc1bc9
Changed: Keycloak version upgraded to 26.1.4
GPortas Mar 27, 2025
ffa040f
Changed: keycloak.version in the SPI to 26.1.4
GPortas Mar 27, 2025
5e983af
Changed: image name for keycloak container
GPortas Mar 28, 2025
f062ef5
Changed: Keycloak using custom image / spi to separate compose file w…
GPortas Mar 30, 2025
c376299
Added: explanatory comment to conf/keycloak/docker-compose-dev
GPortas Mar 30, 2025
effe1dd
revert compost file to how it was #11157
pdurbin Mar 31, 2025
425942a
tweak release note snippet #11157
pdurbin Mar 31, 2025
357b1b6
Changed: Keycloak version upgraded to 26.1.4 in all places
GPortas Mar 31, 2025
3d1d269
Merge branch '11157-builtin-users-oidc-auth' of github.com:IQSS/datav…
GPortas Mar 31, 2025
7b413f9
Changed: reverted keycloak version in IT
GPortas Mar 31, 2025
139b286
Fixed: OIDCAuthenticationProviderFactoryIT to work with Keycloak 26.1.4
GPortas Mar 31, 2025
fcc133a
Fixed: docker build context in keycloak/docker-compose-dev
GPortas Apr 4, 2025
020103f
Merge branch 'develop' of github.com:IQSS/dataverse into 11157-builti…
GPortas Apr 4, 2025
0ea888d
Fixed: looking up builtin user by username when API_BEARER_AUTH_USE_B…
GPortas Apr 9, 2025
2fa294b
Merge branch 'develop' of github.com:IQSS/dataverse into 11157-builti…
GPortas Apr 9, 2025
e52b5af
Removed: unused import
GPortas Apr 9, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 33 additions & 0 deletions conf/keycloak/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
# ------------------------------------------
# Stage 1: Build SPI with Maven
# ------------------------------------------
FROM maven:3.9.5-eclipse-temurin-17 AS builder

WORKDIR /app

# Copy SPI source code
COPY ./builtin-users-spi /app

# Build the SPI JAR
RUN mvn clean package

# ------------------------------------------
# Stage 2: Build Keycloak Image
# ------------------------------------------
FROM quay.io/keycloak/keycloak:25.0.6
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

26.1.0 is current - any reason you chose this version instead?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I just updated it to 26.1.0.

Initially, I used version 25, as the upgrade to version 26 involved some breaking changes. However, after adding some tweaks, it is now working good.


ENV KC_HEALTH_ENABLED=true

# Copy SPI JAR from builder stage
COPY --from=builder /app/target/keycloak-dv-builtin-users-authenticator-1.0-SNAPSHOT.jar /opt/keycloak/providers/

# Copy additional configurations
COPY ./builtin-users-spi/conf/quarkus.properties /opt/keycloak/conf/
COPY ./test-realm.json /opt/keycloak/data/import/

# Set the Keycloak command
ENTRYPOINT ["/opt/keycloak/bin/kc.sh"]
CMD ["start-dev", "--import-realm", "--http-port=8090"]

# Expose port 8090
EXPOSE 8090
14 changes: 14 additions & 0 deletions conf/keycloak/builtin-users-spi/conf/quarkus.properties
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
quarkus.datasource.user-store.db-kind=postgresql
quarkus.datasource.user-store.jdbc.url=jdbc:postgresql://${DATAVERSE_DB_HOST}:${DATAVERSE_DB_PORT}/dataverse
quarkus.datasource.user-store.username=${DATAVERSE_DB_USER}
quarkus.datasource.user-store.password=${DATAVERSE_DB_PASSWORD}

quarkus.datasource.user-store.jdbc.driver=org.postgresql.Driver
quarkus.datasource.user-store.jdbc.transactions=disabled

quarkus.datasource.user-store.jdbc.recovery.username=${DATAVERSE_DB_USER}
quarkus.datasource.user-store.jdbc.recovery.password=${DATAVERSE_DB_PASSWORD}

quarkus.datasource.user-store.jdbc.xa-properties.serverName=${DATAVERSE_DB_HOST}
quarkus.datasource.user-store.jdbc.xa-properties.portNumber=${DATAVERSE_DB_PORT}
quarkus.datasource.user-store.jdbc.xa-properties.databaseName=dataverse
110 changes: 110 additions & 0 deletions conf/keycloak/builtin-users-spi/pom.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
<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>edu.harvard.iq.keycloak</groupId>
<artifactId>keycloak-dv-builtin-users-authenticator</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>jar</packaging>

<dependencies>
<!-- Keycloak Server SPI -->
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-server-spi</artifactId>
<version>${keycloak.version}</version>
<scope>provided</scope>
</dependency>

<!-- Keycloak Server SPI Private -->
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-server-spi-private</artifactId>
<version>${keycloak.version}</version>
<scope>provided</scope>
</dependency>

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

<!-- Keycloak Model JPA -->
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-model-jpa</artifactId>
<version>${keycloak.version}</version>
<scope>provided</scope>
</dependency>

<!-- Jakarta Persistence API -->
<dependency>
<groupId>jakarta.persistence</groupId>
<artifactId>jakarta.persistence-api</artifactId>
<version>${jakarta.persistence.version}</version>
</dependency>

<!-- jBCrypt -->
<dependency>
<groupId>org.mindrot</groupId>
<artifactId>jbcrypt</artifactId>
<version>${mindrot.jbcrypt.version}</version>
<scope>compile</scope>
</dependency>

<!-- TEST DEPENDENCIES -->

<!-- JUnit -->
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-api</artifactId>
<version>${junit.jupiter.version}</version>
<scope>test</scope>
</dependency>

<!-- Mockito -->
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<version>${mockito.version}</version>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<version>3.2.4</version>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>shade</goal>
</goals>
</execution>
</executions>
</plugin>

<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>17</source>
<target>17</target>
</configuration>
</plugin>
</plugins>
</build>

<properties>
<keycloak.version>25.0.6</keycloak.version>
<java.version>17</java.version>
<jakarta.persistence.version>3.2.0</jakarta.persistence.version>
<mindrot.jbcrypt.version>0.4</mindrot.jbcrypt.version>
<mockito.version>5.15.2</mockito.version>
<junit.jupiter.version>5.11.4</junit.jupiter.version>
</properties>
</project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
package edu.harvard.iq.keycloak.auth.spi.adapters;

import edu.harvard.iq.keycloak.auth.spi.models.DataverseUser;
import org.keycloak.component.ComponentModel;
import org.keycloak.models.GroupModel;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.RealmModel;
import org.keycloak.storage.StorageId;
import org.keycloak.storage.adapter.AbstractUserAdapterFederatedStorage;

import java.util.stream.Stream;

public class DataverseUserAdapter extends AbstractUserAdapterFederatedStorage {

protected DataverseUser dataverseUser;
protected String keycloakId;

public DataverseUserAdapter(KeycloakSession session, RealmModel realm, ComponentModel model, DataverseUser dataverseUser) {
super(session, realm, model);
this.dataverseUser = dataverseUser;
keycloakId = StorageId.keycloakId(model, dataverseUser.getBuiltinUser().getId().toString());
}

@Override
public void setUsername(String s) {
}

@Override
public String getUsername() {
return dataverseUser.getBuiltinUser().getUsername();
}

@Override
public String getEmail() {
return dataverseUser.getAuthenticatedUser().getEmail();
}

@Override
public String getFirstName() {
return dataverseUser.getAuthenticatedUser().getFirstName();
}

@Override
public String getLastName() {
return dataverseUser.getAuthenticatedUser().getLastName();
}

@Override
public Stream<GroupModel> getGroupsStream(String search, Integer first, Integer max) {
return super.getGroupsStream(search, first, max);
}

@Override
public long getGroupsCount() {
return super.getGroupsCount();
}

@Override
public long getGroupsCountByNameContaining(String search) {
return super.getGroupsCountByNameContaining(search);
}

@Override
public String getId() {
return keycloakId;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
package edu.harvard.iq.keycloak.auth.spi.models;

import jakarta.persistence.*;

@NamedQueries({
@NamedQuery(name = "DataverseAuthenticatedUser.findByEmail",
query = "select au from DataverseAuthenticatedUser au WHERE LOWER(au.email)=LOWER(:email)"),
@NamedQuery(name = "DataverseAuthenticatedUser.findByIdentifier",
query = "select au from DataverseAuthenticatedUser au WHERE LOWER(au.userIdentifier)=LOWER(:identifier)"),
})
@Entity
@Table(name = "authenticateduser")
public class DataverseAuthenticatedUser {
@Id
private Integer id;
private String email;
private String lastName;
private String firstName;
private String userIdentifier;

public void setId(Integer id) {
this.id = id;
}

public void setEmail(String email) {
this.email = email;
}

public void setUserIdentifier(String userIdentifier) {
this.userIdentifier = userIdentifier;
}

public String getEmail() {
return email;
}

public String getLastName() {
return lastName;
}

public String getFirstName() {
return firstName;
}

public String getUserIdentifier() {
return userIdentifier;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
package edu.harvard.iq.keycloak.auth.spi.models;

import jakarta.persistence.*;

@NamedQueries({
@NamedQuery(name = "DataverseBuiltinUser.findByUsername",
query = "SELECT u FROM DataverseBuiltinUser u WHERE LOWER(u.username)=LOWER(:username)")
})
@Entity
@Table(name = "builtinuser")
public class DataverseBuiltinUser {
@Id
private Integer id;

private String username;

private String encryptedPassword;

private Integer passwordEncryptionVersion;

public void setId(Integer id) {
this.id = id;
}

public void setUsername(String username) {
this.username = username;
}

public Integer getId() {
return id;
}

public String getUsername() {
return username;
}

public Integer getPasswordEncryptionVersion() {
return passwordEncryptionVersion;
}

public void setEncryptedPassword(String encryptedPassword) {
this.encryptedPassword = encryptedPassword;
}

public String getEncryptedPassword() {
return encryptedPassword;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package edu.harvard.iq.keycloak.auth.spi.models;

public class DataverseUser {

private final DataverseAuthenticatedUser authenticatedUser;
private final DataverseBuiltinUser builtinUser;

public DataverseUser(DataverseAuthenticatedUser authenticatedUser, DataverseBuiltinUser builtinUser) {
this.authenticatedUser = authenticatedUser;
this.builtinUser = builtinUser;
}

public DataverseAuthenticatedUser getAuthenticatedUser() {
return authenticatedUser;
}

public DataverseBuiltinUser getBuiltinUser() {
return builtinUser;
}
}
Loading
Loading