Refactor SAML configuration deployment and enhance Authentik integration
- Removed SAML configuration deployment calls from PostInstanceInitService. - Updated DeploySamlConfigurationAsync to improve template fetching logic from Git and local directories. - Added Authentik flow and keypair models for better representation in the UI. - Enhanced SettingsViewModel to include Authentik settings with save and test functionality. - Updated UI to support Authentik configuration, including dropdowns for flows and keypairs. - Changed default CMS server name template to "app.ots-signs.com" across various files. - Improved password handling in SshDockerCliService for secure shell command execution. - Added new template file for settings-custom.php in the project structure.
This commit is contained in:
@@ -107,9 +107,6 @@ public class PostInstanceInitService
|
||||
_logger.LogInformation("[PostInit] Setting Xibo theme to 'otssigns'");
|
||||
await xibo.SetThemeAsync(instanceUrl, accessToken, "otssigns");
|
||||
|
||||
// ── 6a. Deploy SAML configuration ─────────────────────────────────
|
||||
await DeploySamlConfigurationAsync(abbrev, instanceUrl, settings, ct);
|
||||
|
||||
// ── 7. Store credentials in Bitwarden ─────────────────────────────
|
||||
_logger.LogInformation("[PostInit] Storing credentials in Bitwarden");
|
||||
|
||||
@@ -200,9 +197,6 @@ public class PostInstanceInitService
|
||||
_logger.LogInformation("[PostInit] Setting theme to 'otssigns'");
|
||||
await xibo.SetThemeAsync(instanceUrl, accessToken, "otssigns");
|
||||
|
||||
// ── 5a. Deploy SAML configuration ─────────────────────────────────
|
||||
await DeploySamlConfigurationAsync(abbrev, instanceUrl, settings, ct);
|
||||
|
||||
// ── 6. Store admin password in Bitwarden ──────────────────────────
|
||||
_logger.LogInformation("[PostInit] Storing credentials in Bitwarden");
|
||||
var adminSecretId = await bws.CreateInstanceSecretAsync(
|
||||
@@ -349,9 +343,11 @@ public class PostInstanceInitService
|
||||
/// <summary>
|
||||
/// Provisions a SAML application in Authentik, renders the settings-custom.php template,
|
||||
/// and writes the rendered file to the instance's NFS-backed cms-custom volume.
|
||||
/// Errors are logged but do not fail the overall post-init process.
|
||||
/// The template is resolved from (a) the git repo cache, or (b) the local bundled
|
||||
/// <c>templates/</c> directory shipped with the application.
|
||||
/// Errors are logged but do not fail the overall deployment.
|
||||
/// </summary>
|
||||
private async Task DeploySamlConfigurationAsync(
|
||||
public async Task DeploySamlConfigurationAsync(
|
||||
string abbrev,
|
||||
string instanceUrl,
|
||||
SettingsService settings,
|
||||
@@ -366,36 +362,80 @@ public class PostInstanceInitService
|
||||
var git = scope.ServiceProvider.GetRequiredService<GitTemplateService>();
|
||||
var docker = scope.ServiceProvider.GetRequiredService<IDockerCliService>();
|
||||
|
||||
// ── 1. Fetch template from git repo ───────────────────────────────
|
||||
// ── 1. Locate settings-custom.php.template ────────────────────────
|
||||
string? templateContent = null;
|
||||
|
||||
// 1a. Try git repo cache first
|
||||
var repoUrl = await settings.GetAsync(SettingsService.GitRepoUrl);
|
||||
var repoPat = await settings.GetAsync(SettingsService.GitRepoPat);
|
||||
if (string.IsNullOrWhiteSpace(repoUrl))
|
||||
throw new InvalidOperationException("Git repository URL is not configured.");
|
||||
if (!string.IsNullOrWhiteSpace(repoUrl))
|
||||
{
|
||||
try
|
||||
{
|
||||
var templateConfig = await git.FetchAsync(repoUrl, repoPat);
|
||||
var gitPath = Path.Combine(templateConfig.CacheDir, "settings-custom.php.template");
|
||||
if (File.Exists(gitPath))
|
||||
{
|
||||
templateContent = await File.ReadAllTextAsync(gitPath, ct);
|
||||
_logger.LogInformation("[PostInit] Using template from git repo cache: {Path}", gitPath);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogWarning(ex, "[PostInit] Could not fetch template from git — trying local fallback");
|
||||
}
|
||||
}
|
||||
|
||||
var templateConfig = await git.FetchAsync(repoUrl, repoPat);
|
||||
var templatePath = Path.Combine(templateConfig.CacheDir, "settings-custom.php.template");
|
||||
// 1b. Fall back to local templates/ directory (bundled with app)
|
||||
if (templateContent == null)
|
||||
{
|
||||
var candidates = new[]
|
||||
{
|
||||
Path.Combine(AppContext.BaseDirectory, "templates", "settings-custom.php.template"),
|
||||
Path.Combine(Directory.GetCurrentDirectory(), "templates", "settings-custom.php.template"),
|
||||
};
|
||||
|
||||
if (!File.Exists(templatePath))
|
||||
foreach (var candidate in candidates)
|
||||
{
|
||||
if (File.Exists(candidate))
|
||||
{
|
||||
templateContent = await File.ReadAllTextAsync(candidate, ct);
|
||||
_logger.LogInformation("[PostInit] Using local template: {Path}", candidate);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (templateContent == null)
|
||||
{
|
||||
_logger.LogWarning(
|
||||
"[PostInit] settings-custom.php.template not found in git repo — skipping SAML deployment");
|
||||
"[PostInit] settings-custom.php.template not found in git repo or local templates/ — skipping SAML deployment");
|
||||
return;
|
||||
}
|
||||
|
||||
var templateContent = await File.ReadAllTextAsync(templatePath, ct);
|
||||
|
||||
// ── 2. Provision Authentik SAML application ───────────────────────
|
||||
var samlBaseUrl = instanceUrl.TrimEnd('/') + "/saml";
|
||||
var samlConfig = await authentik.ProvisionSamlAsync(abbrev, instanceUrl, ct);
|
||||
Models.DTOs.AuthentikSamlConfig? samlConfig = null;
|
||||
try
|
||||
{
|
||||
samlConfig = await authentik.ProvisionSamlAsync(abbrev, instanceUrl, ct);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogWarning(ex,
|
||||
"[PostInit] Authentik provisioning failed for {Abbrev} — skipping SAML config deployment to avoid broken Xibo instance",
|
||||
abbrev);
|
||||
return; // Do NOT write a settings-custom.php with empty IdP values — it will crash Xibo
|
||||
}
|
||||
|
||||
// ── 3. Render template ────────────────────────────────────────────
|
||||
var rendered = templateContent
|
||||
.Replace("{{SAML_BASE_URL}}", samlBaseUrl)
|
||||
.Replace("{{SAML_SP_ENTITY_ID}}", $"{samlBaseUrl}/metadata")
|
||||
.Replace("{{AUTHENTIK_IDP_ENTITY_ID}}", samlConfig.IdpEntityId)
|
||||
.Replace("{{AUTHENTIK_SSO_URL}}", samlConfig.SsoUrlRedirect)
|
||||
.Replace("{{AUTHENTIK_SLO_URL}}", samlConfig.SloUrlRedirect)
|
||||
.Replace("{{AUTHENTIK_IDP_X509_CERT}}", samlConfig.IdpX509Cert);
|
||||
.Replace("{{AUTHENTIK_IDP_ENTITY_ID}}", samlConfig?.IdpEntityId ?? "")
|
||||
.Replace("{{AUTHENTIK_SSO_URL}}", samlConfig?.SsoUrlRedirect ?? "")
|
||||
.Replace("{{AUTHENTIK_SLO_URL}}", samlConfig?.SloUrlRedirect ?? "")
|
||||
.Replace("{{AUTHENTIK_IDP_X509_CERT}}", samlConfig?.IdpX509Cert ?? "");
|
||||
|
||||
// ── 4. Write rendered file to NFS volume ──────────────────────────
|
||||
var nfsServer = await settings.GetAsync(SettingsService.NfsServer);
|
||||
@@ -415,8 +455,11 @@ public class PostInstanceInitService
|
||||
throw new InvalidOperationException($"Failed to write settings-custom.php to NFS: {error}");
|
||||
|
||||
_logger.LogInformation(
|
||||
"[PostInit] SAML configuration deployed for {Abbrev} (Authentik provider={ProviderId})",
|
||||
abbrev, samlConfig.ProviderId);
|
||||
"[PostInit] SAML configuration deployed for {Abbrev}{ProviderInfo}",
|
||||
abbrev,
|
||||
samlConfig != null
|
||||
? $" (Authentik provider={samlConfig.ProviderId})"
|
||||
: " (without Authentik — needs manual IdP config)");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user