Skip to content

Conversation

@Askir
Copy link
Member

@Askir Askir commented Dec 21, 2025

Summary

Implements automatic role synchronization between identity providers (SAML, OIDC, LDAP) and Kviklet roles based on IdP group memberships.

  • Three sync modes:
    • FULL_SYNC - Roles exactly match IdP group mappings (removes unmapped roles)
    • ADDITIVE - IdP groups add roles without removing existing ones
    • FIRST_LOGIN_ONLY - Roles only set on first login, not updated on subsequent logins
  • All IdP types supported: OIDC, SAML, and LDAP
  • Admin UI: Settings page for configuring group-to-role mappings
  • Documentation: Keycloak setup guide included

Architecture

All IdP services (OidcUserService, SamlUserService, LdapUserDetailsService) extract groups from their respective attributes and pass them to UserAuthService.findOrCreateUser(), which delegates to RoleSyncService for role resolution.

Key Files

Backend:

  • RoleSyncService.kt - Core sync logic
  • RoleSyncConfigService.kt + RoleSyncConfigController.kt - Config CRUD
  • RoleSyncConfig.kt (db/) - Entities and repository
  • UserAuthService.kt - Integration point for all IdPs

Frontend:

  • RoleSyncSettings.tsx - Settings UI
  • RoleSyncConfigApi.ts - API client
  • roleSyncConfig.ts - Hook

Test plan

  • OIDC role sync tested with Keycloak
  • SAML role sync tested with Keycloak
  • LDAP role sync tested with OpenLDAP
  • Run ./gradlew test -i for backend tests
  • Run npm test && npx tsc for frontend

Important

Add role synchronization feature for IdP group-to-role mapping with backend services and frontend UI.

  • Behavior:
    • Adds automatic role synchronization between IdPs (SAML, OIDC, LDAP) and Kviklet roles based on group memberships.
    • Supports FULL_SYNC, ADDITIVE, and FIRST_LOGIN_ONLY sync modes.
    • Includes a settings page in the admin UI for configuring group-to-role mappings.
  • Backend:
    • Implements RoleSyncService for role resolution and RoleSyncConfigService for configuration management.
    • Adds RoleSyncConfigController.kt for API endpoints.
    • Updates UserAuthService.kt, LdapUserDetailsService.kt, OidcUserService.kt, and SamlUserService.kt to integrate role sync.
    • Database changes in 036-add-role-sync-config.yaml for new tables.
  • Frontend:
    • Adds RoleSyncSettings.tsx for the settings UI.
    • Implements API client in RoleSyncConfigApi.ts and hook in roleSyncConfig.ts.
  • Documentation:
    • Adds keycloak-role-sync-setup.md for Keycloak configuration guide.

This description was created by Ellipsis for d37f2ca. You can customize this summary. It will automatically update as commits are pushed.

Allows automatic synchronization of user roles based on IdP group memberships.
Supports OIDC, SAML, and LDAP with three sync modes: FULL_SYNC, ADDITIVE, and
FIRST_LOGIN_ONLY. Includes admin UI for configuring mappings and Keycloak
setup documentation.
@Askir Askir force-pushed the feature/role-sync branch from b632774 to c42edf7 Compare December 21, 2025 11:14
Copy link
Contributor

@ellipsis-dev ellipsis-dev bot left a comment

Choose a reason for hiding this comment

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

Caution

Changes requested ❌

Reviewed everything up to b632774 in 1 minute and 37 seconds. Click for details.
  • Reviewed 1531 lines of code in 17 files
  • Skipped 0 files when reviewing.
  • Skipped posting 6 draft comments. View those below.
  • Modify your settings and rules to customize what types of comments Ellipsis leaves. And don't forget to react with 👍 or 👎 to teach Ellipsis.
1. backend/src/main/kotlin/dev/kviklet/kviklet/controller/RoleSyncConfigController.kt:70
  • Draft comment:
    Controller code is clear and uses appropriate annotations. Consider adding logging for update and delete operations for easier debugging.
  • Reason this comment was not posted:
    Confidence changes required: 50% <= threshold 50% None
2. backend/src/main/kotlin/dev/kviklet/kviklet/db/RoleSyncConfig.kt:60
  • Draft comment:
    Ensure that the unique constraint on 'idp_group_name' aligns with business logic. Duplicate mappings will fail.
  • Reason this comment was not posted:
    Comment looked like it was already resolved.
3. backend/src/main/kotlin/dev/kviklet/kviklet/security/saml/SamlLoginSuccessHandler.kt:72
  • Draft comment:
    Hard-coded port ':5173' in getBaseUrl may not be appropriate in production. Consider making it configurable.
  • Reason this comment was not posted:
    Comment was not on a location in the diff, so it can't be submitted as a review comment.
4. frontend/src/hooks/roleSyncConfig.ts:20
  • Draft comment:
    Consider error handling improvements (e.g., try-catch) or leveraging a data-fetching library to simplify state management.
  • Reason this comment was not posted:
    Confidence changes required: 50% <= threshold 50% None
5. frontend/src/routes/settings/RoleSyncSettings.tsx:140
  • Draft comment:
    In the groups attribute input, updateConfig is called onChange and onBlur. Confirm if immediate updates on every keystroke are desired.
  • Reason this comment was not posted:
    Comment did not seem useful. Confidence is useful = 0% <= threshold 50% The comment is asking the PR author to confirm their intention regarding the use of updateConfig on onChange and onBlur. This violates the rule against asking the author to confirm their intention or ensure behavior is intended.
6. frontend/src/routes/settings/Settings.tsx:130
  • Draft comment:
    Sidebar tabs correctly disable enterprise features when license is invalid. Verify that route paths used (e.g., '/roles/new') are consistent with app routing.
  • Reason this comment was not posted:
    Confidence changes required: 30% <= threshold 50% None

Workflow ID: wflow_HU3VBVnOaMEjdTKl

You can customize Ellipsis by changing your verbosity settings, reacting with 👍 or 👎, replying to comments, or adding code review rules.

.mapNotNull { mapping ->
try {
roleAdapter.findById(RoleId(mapping.roleId))
} catch (e: EntityNotFound) {
Copy link
Contributor

Choose a reason for hiding this comment

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

Silently ignoring missing roles in the catch block may hide configuration issues. Consider logging a warning with exception details.

Copy link
Contributor

@ellipsis-dev ellipsis-dev bot left a comment

Choose a reason for hiding this comment

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

Important

Looks good to me! 👍

Reviewed c42edf7 in 37 seconds. Click for details.
  • Reviewed 1513 lines of code in 17 files
  • Skipped 0 files when reviewing.
  • Skipped posting 2 draft comments. View those below.
  • Modify your settings and rules to customize what types of comments Ellipsis leaves. And don't forget to react with 👍 or 👎 to teach Ellipsis.
1. backend/src/main/kotlin/dev/kviklet/kviklet/db/RoleSyncConfig.kt:63
  • Draft comment:
    Verify the unique constraint on idp_group_name. It prevents mapping the same IdP group to multiple roles.
  • Reason this comment was not posted:
    Comment did not seem useful. Confidence is useful = 0% <= threshold 50% The comment is asking the author to verify a unique constraint, which is similar to asking them to double-check something. This violates the rule against asking the author to confirm or verify intentions. The comment does not provide a specific suggestion or point out a clear issue with the code.
2. backend/src/main/kotlin/dev/kviklet/kviklet/security/saml/SamlLoginSuccessHandler.kt:72
  • Draft comment:
    Hardcoded port ':5173' in getBaseUrl may not reflect actual serverPort. Consider using the dynamic serverPort value.
  • Reason this comment was not posted:
    Comment was not on a location in the diff, so it can't be submitted as a review comment.

Workflow ID: wflow_jPClt2peDPVAndoz

You can customize Ellipsis by changing your verbosity settings, reacting with 👍 or 👎, replying to comments, or adding code review rules.

Copy link
Contributor

@ellipsis-dev ellipsis-dev bot left a comment

Choose a reason for hiding this comment

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

Important

Looks good to me! 👍

Reviewed d37f2ca in 31 seconds. Click for details.
  • Reviewed 13 lines of code in 1 files
  • Skipped 0 files when reviewing.
  • Skipped posting 1 draft comments. View those below.
  • Modify your settings and rules to customize what types of comments Ellipsis leaves. And don't forget to react with 👍 or 👎 to teach Ellipsis.
1. frontend/src/routes/settings/RoleSyncSettings.tsx:106
  • Draft comment:
    Good use of an arrow function to wrap the async handler. Consider handling errors within handleToggleEnabled (e.g., try/catch) so that any update failures are logged or communicated to the user.
  • Reason this comment was not posted:
    Confidence changes required: 33% <= threshold 50% None

Workflow ID: wflow_wpdnyjvQSXuvdl8B

You can customize Ellipsis by changing your verbosity settings, reacting with 👍 or 👎, replying to comments, or adding code review rules.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants