feat: Implement Authentik group synchronization and add confirmation dialogs for service management
This commit is contained in:
@@ -460,6 +460,11 @@ public class PostInstanceInitService
|
||||
samlConfig != null
|
||||
? $" (Authentik provider={samlConfig.ProviderId})"
|
||||
: " (without Authentik — needs manual IdP config)");
|
||||
|
||||
// ── 5. Sync Authentik groups to Xibo ──────────────────────────────
|
||||
// Pre-create Authentik groups as Xibo user groups so they're available
|
||||
// immediately (before any user logs in via SSO).
|
||||
await SyncGroupsFromAuthentikAsync(abbrev, instanceUrl, settings, ct);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
@@ -469,6 +474,89 @@ public class PostInstanceInitService
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Fetches all groups from Authentik and creates matching user groups in the
|
||||
/// specified Xibo instance. Groups that already exist in Xibo are skipped.
|
||||
/// This ensures that groups are available in Xibo for permission assignment
|
||||
/// before any user logs in via SAML SSO.
|
||||
/// </summary>
|
||||
public async Task<int> SyncGroupsFromAuthentikAsync(
|
||||
string abbrev,
|
||||
string instanceUrl,
|
||||
SettingsService settings,
|
||||
CancellationToken ct = default)
|
||||
{
|
||||
var synced = 0;
|
||||
try
|
||||
{
|
||||
_logger.LogInformation("[GroupSync] Syncing Authentik groups to Xibo for {Abbrev}", abbrev);
|
||||
|
||||
using var scope = _services.CreateScope();
|
||||
var authentik = scope.ServiceProvider.GetRequiredService<IAuthentikService>();
|
||||
var xibo = scope.ServiceProvider.GetRequiredService<XiboApiService>();
|
||||
|
||||
// ── 1. Fetch groups from Authentik ────────────────────────────────
|
||||
var authentikGroups = await authentik.ListGroupsAsync(ct: ct);
|
||||
if (authentikGroups.Count == 0)
|
||||
{
|
||||
_logger.LogInformation("[GroupSync] No groups found in Authentik — nothing to sync");
|
||||
return 0;
|
||||
}
|
||||
|
||||
_logger.LogInformation("[GroupSync] Found {Count} Authentik group(s) to sync", authentikGroups.Count);
|
||||
|
||||
// ── 2. Authenticate to Xibo ───────────────────────────────────────
|
||||
var oauthClientId = await settings.GetAsync(SettingsService.InstanceOAuthClientId(abbrev));
|
||||
var oauthSecretId = await settings.GetAsync(SettingsService.InstanceOAuthSecretId(abbrev));
|
||||
|
||||
if (string.IsNullOrWhiteSpace(oauthClientId) || string.IsNullOrWhiteSpace(oauthSecretId))
|
||||
{
|
||||
_logger.LogWarning("[GroupSync] No OAuth credentials for {Abbrev} — cannot sync groups", abbrev);
|
||||
return 0;
|
||||
}
|
||||
|
||||
var bws = scope.ServiceProvider.GetRequiredService<IBitwardenSecretService>();
|
||||
var oauthSecret = await bws.GetSecretAsync(oauthSecretId);
|
||||
var accessToken = await xibo.LoginAsync(instanceUrl, oauthClientId, oauthSecret.Value);
|
||||
|
||||
// ── 3. List existing Xibo groups ──────────────────────────────────
|
||||
var existingGroups = await xibo.ListUserGroupsAsync(instanceUrl, accessToken);
|
||||
var existingNames = new HashSet<string>(
|
||||
existingGroups.Select(g => g.Group),
|
||||
StringComparer.OrdinalIgnoreCase);
|
||||
|
||||
// ── 4. Create missing groups in Xibo ──────────────────────────────
|
||||
foreach (var group in authentikGroups)
|
||||
{
|
||||
if (existingNames.Contains(group.Name))
|
||||
{
|
||||
_logger.LogDebug("[GroupSync] Group '{Name}' already exists in Xibo", group.Name);
|
||||
continue;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
var groupId = await xibo.CreateUserGroupAsync(instanceUrl, accessToken, group.Name);
|
||||
_logger.LogInformation("[GroupSync] Created Xibo group '{Name}' (id={Id})", group.Name, groupId);
|
||||
synced++;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogWarning(ex, "[GroupSync] Failed to create Xibo group '{Name}'", group.Name);
|
||||
}
|
||||
}
|
||||
|
||||
_logger.LogInformation("[GroupSync] Sync complete for {Abbrev}: {Synced} group(s) created", abbrev, synced);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "[GroupSync] Group sync failed for {Abbrev}: {Message}", abbrev, ex.Message);
|
||||
// Don't rethrow — group sync failure should not block other operations
|
||||
}
|
||||
|
||||
return synced;
|
||||
}
|
||||
|
||||
// ─────────────────────────────────────────────────────────────────────────
|
||||
// Helpers
|
||||
// ─────────────────────────────────────────────────────────────────────────
|
||||
|
||||
Reference in New Issue
Block a user