feat: Implement customer invitation infrastructure in Authentik
- Added IInvitationSetupService and InvitationSetupService to orchestrate the setup of invitation infrastructure for customers. - Introduced methods for creating groups, enrollment flows, invitation stages, roles, and policies in Authentik. - Updated PostInstanceInitService to call the new invitation setup methods during post-initialization. - Enhanced InstanceService to pass customer name during SAML configuration deployment. - Updated App.axaml.cs to register the new IInvitationSetupService. - Modified settings-custom.php.template to include documentation for SAML authentication configuration with group-based admin assignment. - Added logic to exclude specific groups from being synced to Xibo during group synchronization.
This commit is contained in:
@@ -351,7 +351,8 @@ public class PostInstanceInitService
|
||||
string abbrev,
|
||||
string instanceUrl,
|
||||
SettingsService settings,
|
||||
CancellationToken ct)
|
||||
string? customerName = null,
|
||||
CancellationToken ct = default)
|
||||
{
|
||||
try
|
||||
{
|
||||
@@ -465,6 +466,11 @@ public class PostInstanceInitService
|
||||
// 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);
|
||||
|
||||
// ── 6. Set up customer invitation infrastructure in Authentik ─────
|
||||
// Creates a group, enrollment flow, invitation stage, role, and
|
||||
// scoping policies so the customer admin can invite users directly.
|
||||
await SetupCustomerInvitationInfrastructureAsync(abbrev, customerName, ct);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
@@ -474,9 +480,57 @@ public class PostInstanceInitService
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets up the customer invitation infrastructure in Authentik (group, enrollment flow,
|
||||
/// stages, role, and policies). Errors are logged but do not block other operations.
|
||||
/// </summary>
|
||||
private async Task SetupCustomerInvitationInfrastructureAsync(
|
||||
string abbrev, string? customerName, CancellationToken ct)
|
||||
{
|
||||
try
|
||||
{
|
||||
using var scope = _services.CreateScope();
|
||||
var invitationSetup = scope.ServiceProvider.GetRequiredService<IInvitationSetupService>();
|
||||
|
||||
var displayName = !string.IsNullOrWhiteSpace(customerName)
|
||||
? customerName
|
||||
: abbrev.ToUpperInvariant();
|
||||
|
||||
_logger.LogInformation("[PostInit] Setting up invitation infrastructure for {Abbrev}", abbrev);
|
||||
var result = await invitationSetup.SetupCustomerInvitationAsync(abbrev, displayName, ct);
|
||||
|
||||
if (result.Success)
|
||||
{
|
||||
_logger.LogInformation(
|
||||
"[PostInit] Invitation infrastructure ready for {Abbrev}: group={Group}, flow={Flow}, role={Role}",
|
||||
abbrev, result.GroupName, result.EnrollmentFlowSlug, result.RoleName);
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(result.InvitationManagementUrl))
|
||||
{
|
||||
_logger.LogInformation(
|
||||
"[PostInit] Customer admin invitation URL: {Url}", result.InvitationManagementUrl);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
_logger.LogWarning("[PostInit] Invitation setup reported failure for {Abbrev}: {Message}",
|
||||
abbrev, result.Message);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex,
|
||||
"[PostInit] Invitation infrastructure setup failed for {Abbrev}: {Message}. " +
|
||||
"Customer invitations can be configured manually in Authentik.", abbrev, ex.Message);
|
||||
// Don't rethrow — invitation setup failure should not block post-init
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Fetches all groups from Authentik and creates matching user groups in the
|
||||
/// specified Xibo instance. Groups that already exist in Xibo are skipped.
|
||||
/// specified Xibo instance, excluding any groups listed in the
|
||||
/// "SamlGroupSyncExcludedGroups" setting (comma-separated group names).
|
||||
/// 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>
|
||||
@@ -505,6 +559,22 @@ public class PostInstanceInitService
|
||||
|
||||
_logger.LogInformation("[GroupSync] Found {Count} Authentik group(s) to sync", authentikGroups.Count);
|
||||
|
||||
// ── 1b. Read excluded groups from settings ────────────────────────
|
||||
var excludedGroupsSetting = await settings.GetAsync("SamlGroupSyncExcludedGroups");
|
||||
var excludedGroups = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
|
||||
if (!string.IsNullOrWhiteSpace(excludedGroupsSetting))
|
||||
{
|
||||
var excluded = excludedGroupsSetting
|
||||
.Split(new[] { ',', ';' }, StringSplitOptions.RemoveEmptyEntries)
|
||||
.Select(g => g.Trim())
|
||||
.Where(g => !string.IsNullOrWhiteSpace(g));
|
||||
foreach (var g in excluded)
|
||||
{
|
||||
excludedGroups.Add(g);
|
||||
}
|
||||
_logger.LogInformation("[GroupSync] Excluded groups: {Groups}", string.Join(", ", excludedGroups));
|
||||
}
|
||||
|
||||
// ── 2. Authenticate to Xibo ───────────────────────────────────────
|
||||
var oauthClientId = await settings.GetAsync(SettingsService.InstanceOAuthClientId(abbrev));
|
||||
var oauthSecretId = await settings.GetAsync(SettingsService.InstanceOAuthSecretId(abbrev));
|
||||
@@ -525,9 +595,16 @@ public class PostInstanceInitService
|
||||
existingGroups.Select(g => g.Group),
|
||||
StringComparer.OrdinalIgnoreCase);
|
||||
|
||||
// ── 4. Create missing groups in Xibo ──────────────────────────────
|
||||
// ── 4. Create missing groups in Xibo (excluding specified ones) ────
|
||||
foreach (var group in authentikGroups)
|
||||
{
|
||||
// Skip excluded groups
|
||||
if (excludedGroups.Contains(group.Name))
|
||||
{
|
||||
_logger.LogInformation("[GroupSync] Skipping excluded group '{Name}'", group.Name);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (existingNames.Contains(group.Name))
|
||||
{
|
||||
_logger.LogDebug("[GroupSync] Group '{Name}' already exists in Xibo", group.Name);
|
||||
|
||||
Reference in New Issue
Block a user