-
Notifications
You must be signed in to change notification settings - Fork 500
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Keycloak SPI for Builtin Users Authentication #11193
base: develop
Are you sure you want to change the base?
Changes from all commits
2f7e1b3
fa72703
68fbbb0
db34307
bc97f9a
72acd63
d05ea89
59f19c2
1f5b2b7
0ed0b79
c8d8af0
b54aeb8
2206e2a
ec76ce3
a1ea5e0
87ab989
6b49842
e60f4a3
04f7b39
8b0defc
267c6e7
c49b35d
fbfa9c4
7abce92
26b5f57
9c25eeb
ee6e9f0
6695b79
92aaa91
169aa75
f137e62
7fd32c9
67552e1
22fc4da
779dfd6
db83ddc
321b8f2
57b3a3b
9ddb86b
2dc196b
fdda614
2e5dde2
3ca4940
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
# ------------------------------------------ | ||
# 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:22.0 | ||
|
||
# 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 |
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 |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,87 @@ | ||
<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> | ||
|
||
<!-- 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-compiler-plugin</artifactId> | ||
<configuration> | ||
<source>16</source> | ||
<target>16</target> | ||
</configuration> | ||
</plugin> | ||
</plugins> | ||
</build> | ||
|
||
<properties> | ||
<keycloak.version>22.0.0</keycloak.version> | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. FWIW: This is pretty old now. 26.1.0 is current and I think versions earlier than 24.x have sec. issues. Perhaps we need an issue to update the Keycloak in use? |
||
<java.version>17</java.version> | ||
<jakarta.persistence.version>3.2.0</jakarta.persistence.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,70 @@ | ||
package edu.harvard.iq.keycloak.auth.spi.adapters; | ||
|
||
import edu.harvard.iq.keycloak.auth.spi.models.DataverseAuthenticatedUser; | ||
import edu.harvard.iq.keycloak.auth.spi.models.DataverseBuiltinUser; | ||
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 DataverseBuiltinUser builtinUser; | ||
protected DataverseAuthenticatedUser authenticatedUser; | ||
protected String keycloakId; | ||
|
||
public DataverseUserAdapter(KeycloakSession session, RealmModel realm, ComponentModel model, DataverseBuiltinUser builtinUser, DataverseAuthenticatedUser authenticatedUser) { | ||
super(session, realm, model); | ||
this.builtinUser = builtinUser; | ||
this.authenticatedUser = authenticatedUser; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think I could see an alternate design in which there's just one user class that joins from the two tables. As coded now, that logic is in the Java instead, so more code, but more flexibility if we ever did need to map other user types in the future? Probably not worth changing at this point. |
||
keycloakId = StorageId.keycloakId(model, builtinUser.getId().toString()); | ||
} | ||
|
||
@Override | ||
public void setUsername(String s) { | ||
} | ||
|
||
@Override | ||
public String getUsername() { | ||
return builtinUser.getUsername(); | ||
} | ||
|
||
@Override | ||
public String getEmail() { | ||
return authenticatedUser.getEmail(); | ||
} | ||
|
||
@Override | ||
public String getFirstName() { | ||
return authenticatedUser.getFirstName(); | ||
} | ||
|
||
@Override | ||
public String getLastName() { | ||
return authenticatedUser.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,32 @@ | ||
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; | ||
|
||
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; | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
17 for these?