-
Notifications
You must be signed in to change notification settings - Fork 541
Keycloak SPI for Builtin Users Authentication #11193
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
Merged
Merged
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 fa72703
Added: Keycloak SPI base logic to support searching dv builtin users …
GPortas 68fbbb0
Merge branch 'develop' of github.com:IQSS/dataverse into 11157-builti…
GPortas db34307
Added: DataverseAuthenticatedUser for populating extra information in…
GPortas bc97f9a
Merge branch 'develop' of github.com:IQSS/dataverse into 11157-builti…
GPortas 72acd63
Added: achieving authentication from Keycloak builtin users SPI throu…
GPortas d05ea89
Changed: removed UserQueryMethodsProvider impl and minor tweaks and f…
GPortas 59f19c2
Added: email/password builtin users auth
GPortas 1f5b2b7
Refactor: DataverseUserStorageProvider
GPortas 0ed0b79
Refactor: DataverseAPIService
GPortas c8d8af0
Refactor: DataverseUserStorageProvider
GPortas b54aeb8
Changed: temporal warning log levels
GPortas 2206e2a
Added: temporal warning log in AuthenticationServiceBean
GPortas ec76ce3
Changed: querying authenticated users before lookup in OIDC flow
GPortas a1ea5e0
Changed: temporal disable unit test for PoC
GPortas 87ab989
Changed: reverted log level back to warning in AuthenticationServiceBean
GPortas 6b49842
Changed: log level in AuthenticationServiceBean
GPortas e60f4a3
Merge branch 'develop' of github.com:IQSS/dataverse into 11157-builti…
GPortas 04f7b39
Added: UserEventListenerProvider logging users login
GPortas 8b0defc
Merge branch 'develop' of github.com:IQSS/dataverse into 11157-builti…
GPortas 267c6e7
Removed: event listeners for a simpler first iteration
GPortas c49b35d
Changed: parametrized quarkus properties in builtin-users-spi
GPortas fbfa9c4
Changed: spi folders structure and parametrized DATAVERSE_BASE_URL
GPortas 7abce92
Added: testing dependencies
GPortas 26b5f57
Added: DataverseUserStorageProviderTest
GPortas 9c25eeb
Added: DataverseAPIServiceTest
GPortas ee6e9f0
Removed: logs from AuthenticationServiceBean
GPortas 6695b79
Changed: AuthenticationServiceBeanTest re-enabled with updates
GPortas 92aaa91
Refactor: AuthenticationServiceBeanTest.setupAuthenticatedUserByIdent…
GPortas 169aa75
Changed: updated oauth2-oidc-sdk and excluded builtin-users-spi from …
GPortas f137e62
Merge branch 'develop' of github.com:IQSS/dataverse into 11157-builti…
GPortas 7fd32c9
Added: API_BEARER_AUTH_USE_BUILTIN_USER_ON_ID_MATCH feature flag
GPortas 67552e1
Added: docs for api-bearer-auth-use-builtin-user-on-id-match
GPortas 22fc4da
Changed: null check added to AuthenticationServiceBean and updated te…
GPortas 779dfd6
Fixed: docs rst format issue
GPortas db83ddc
Added: refined implementation and tests for canLoginWithGivenCredenti…
GPortas 321b8f2
Added: custom Keycloak Dockerfile for spi building and realm setup
GPortas 57b3a3b
Added: dev_keycloak_initializer for SPI setup in realm
GPortas 9ddb86b
Added: release notes for #11197
GPortas 2dc196b
Added: API docs for builtin-users/canLoginWithGivenCredentials
GPortas fdda614
Merge branch 'develop' of github.com:IQSS/dataverse into 11157-builti…
GPortas 2e5dde2
Fixed: maven docker plugin build by setting an image for dev_keycloak
GPortas 3ca4940
Changed: dev keycloak image name
GPortas 247b631
Merge branch 'develop' of github.com:IQSS/dataverse into 11157-builti…
GPortas 900f075
Changed: builtin-users-spi maven compiler plugin version to 17
GPortas 0de1d22
Stash: changing builtin-users-spi to use the database instead of API …
GPortas f1c1ca2
Added: DataverseUserServiceTest
GPortas 24e0d39
Added: DataverseAuthenticationServiceTest
GPortas 1423cf3
Added: spi code cleanup and reactivated tests
GPortas e87eb80
Changed: log levels from info to debug
GPortas 93bf7eb
Removed: canLoginWithGivenCredentials API endpoint
GPortas 6b8526a
Refactor: getAuthenticatedUserWithProvider
GPortas 2070589
Refactor: lookupUserByOIDCBearerToken
GPortas 0269cc7
Changed: updated #11197 release notes
GPortas 7ba6edd
Changed: updated api-bearer-auth-use-builtin-user-on-id-match docs
GPortas e88217a
Merge branch 'develop' of github.com:IQSS/dataverse into 11157-builti…
GPortas 49c887d
Fixed: AuthenticationServiceBeanTest
GPortas bdbc3c9
Merge branch 'develop' of github.com:IQSS/dataverse into 11157-builti…
GPortas a7a751d
Changed: Keycloak version updated to v25
GPortas 08ce0f3
Merge branch 'develop' of github.com:IQSS/dataverse into 11157-builti…
GPortas 6101d73
Merge branch 'develop' of github.com:IQSS/dataverse into 11157-builti…
GPortas fe3e836
Merge branch 'develop' of github.com:IQSS/dataverse into 11157-builti…
GPortas 781614b
Changed: Dataverse version in since section of feature flag
GPortas 02e2f93
Merge branch 'develop' into 11157-builtin-users-oidc-auth
GPortas 2f0b2c8
Merge branch 'develop' of github.com:IQSS/dataverse into 11157-builti…
GPortas ff52e24
Merge branch '11157-builtin-users-oidc-auth' of github.com:IQSS/datav…
GPortas 97489e8
Added: note to PasswordEncryption SPI class to advice about syncing t…
GPortas f5de1d1
Changed: image name in dev_keycloak container
GPortas 82a90a0
Changed: keycloak image name
GPortas 62d3b13
Stash: upgrading to kc 26
GPortas 49a6cf0
Changed: Keycloak upgraded to 26
GPortas ba08b5f
Changed: upgraded Oracle JDBC version in Keycloak image
GPortas 0fc1bc9
Changed: Keycloak version upgraded to 26.1.4
GPortas ffa040f
Changed: keycloak.version in the SPI to 26.1.4
GPortas 5e983af
Changed: image name for keycloak container
GPortas f062ef5
Changed: Keycloak using custom image / spi to separate compose file w…
GPortas c376299
Added: explanatory comment to conf/keycloak/docker-compose-dev
GPortas effe1dd
revert compost file to how it was #11157
pdurbin 425942a
tweak release note snippet #11157
pdurbin 357b1b6
Changed: Keycloak version upgraded to 26.1.4 in all places
GPortas 3d1d269
Merge branch '11157-builtin-users-oidc-auth' of github.com:IQSS/datav…
GPortas 7b413f9
Changed: reverted keycloak version in IT
GPortas 139b286
Fixed: OIDCAuthenticationProviderFactoryIT to work with Keycloak 26.1.4
GPortas fcc133a
Fixed: docker build context in keycloak/docker-compose-dev
GPortas 020103f
Merge branch 'develop' of github.com:IQSS/dataverse into 11157-builti…
GPortas 0ea888d
Fixed: looking up builtin user by username when API_BEARER_AUTH_USE_B…
GPortas 2fa294b
Merge branch 'develop' of github.com:IQSS/dataverse into 11157-builti…
GPortas e52b5af
Removed: unused import
GPortas File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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 | ||
|
|
||
| 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 | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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 |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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> |
67 changes: 67 additions & 0 deletions
67
...ers-spi/src/main/java/edu/harvard/iq/keycloak/auth/spi/adapters/DataverseUserAdapter.java
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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; | ||
| } | ||
| } |
48 changes: 48 additions & 0 deletions
48
...spi/src/main/java/edu/harvard/iq/keycloak/auth/spi/models/DataverseAuthenticatedUser.java
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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; | ||
| } | ||
| } |
48 changes: 48 additions & 0 deletions
48
...users-spi/src/main/java/edu/harvard/iq/keycloak/auth/spi/models/DataverseBuiltinUser.java
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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; | ||
| } | ||
| } |
20 changes: 20 additions & 0 deletions
20
...uiltin-users-spi/src/main/java/edu/harvard/iq/keycloak/auth/spi/models/DataverseUser.java
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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; | ||
| } | ||
| } |
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
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.
26.1.0 is current - any reason you chose this version instead?
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.
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.