feat: Update Authentik integration and enhance Docker Compose templates

This commit is contained in:
Matt Batchelder
2026-03-14 22:51:52 -04:00
parent 150549a20d
commit c2e03de8bb
7 changed files with 174 additions and 188 deletions

View File

@@ -691,36 +691,63 @@ public class AuthentikService : IAuthentikService
{
_logger.LogInformation("[Authentik] Creating usertypeid property mapping");
var expression = @"return ""1"" if user.groups.all() | selectattr(""name"", ""equalto"", ""OTS IT"") else """"";
// Use proper Authentik Python syntax for group membership check
var expression = @"
# Check if user is in OTS IT admin group
admin_groups = [g.name for g in user.groups.all()]
return '1' if 'OTS IT' in admin_groups else ''
";
var payload = new Dictionary<string, object>
{
["name"] = "saml-usertypeid",
["saml_name"] = "usertypeid",
["expression"] = expression,
};
var jsonBody = JsonSerializer.Serialize(payload);
var content = new StringContent(jsonBody, Encoding.UTF8, "application/json");
_logger.LogDebug("[Authentik] Creating property mapping with payload: {Payload}", jsonBody);
var resp = await client.PostAsync("/api/v3/propertymappings/provider/saml/", content, ct);
if (!resp.IsSuccessStatusCode)
// Try multiple endpoints (API versions vary)
var endpoints = new[]
{
var errorBody = await resp.Content.ReadAsStringAsync(ct);
_logger.LogWarning("[Authentik] Failed to create usertypeid mapping (HTTP {Status}): {Error}",
(int)resp.StatusCode, errorBody);
return null;
"/api/v3/propertymappings/provider/saml/",
"/api/v3/propertymappings/saml/",
"/api/v3/propertymappings/",
};
foreach (var endpoint in endpoints)
{
try
{
var content = new StringContent(jsonBody, Encoding.UTF8, "application/json");
var resp = await client.PostAsync(endpoint, content, ct);
_logger.LogDebug("[Authentik] POST {Endpoint} returned HTTP {Status}", endpoint, (int)resp.StatusCode);
if (!resp.IsSuccessStatusCode)
{
var errorBody = await resp.Content.ReadAsStringAsync(ct);
_logger.LogDebug("[Authentik] Error response: {Error}", errorBody);
continue;
}
var result = await resp.Content.ReadFromJsonAsync<AuthentikPropertyMapping>(cancellationToken: ct);
if (result?.Pk != null)
{
_logger.LogInformation("[Authentik] Created usertypeid mapping: {Id} ({Name}) via {Endpoint}",
result.Pk, result.Name, endpoint);
return result.Pk;
}
_logger.LogDebug("[Authentik] Created usertypeid mapping but response had no Pk");
}
catch (Exception ex)
{
_logger.LogDebug(ex, "[Authentik] Error creating usertypeid mapping via {Endpoint}", endpoint);
}
}
var result = await resp.Content.ReadFromJsonAsync<AuthentikPropertyMapping>(cancellationToken: ct);
if (result?.Pk != null)
{
_logger.LogInformation("[Authentik] Created usertypeid mapping: {Id}", result.Pk);
return result.Pk;
}
_logger.LogWarning("[Authentik] Created usertypeid mapping but response had no Pk");
_logger.LogWarning("[Authentik] Could not create usertypeid mapping via any endpoint");
return null;
}
catch (Exception ex)
@@ -761,26 +788,46 @@ public class AuthentikService : IAuthentikService
{
foreach (var mapEl in mapsProp.EnumerateArray())
{
var mapId = mapEl.ValueKind == JsonValueKind.String
? mapEl.GetString()
: (mapEl.TryGetProperty("pk", out var pkProp)
? pkProp.GetString()
: null);
string? mapId = null;
// Handle both string IDs and object IDs
if (mapEl.ValueKind == JsonValueKind.String)
{
mapId = mapEl.GetString();
}
else if (mapEl.ValueKind == JsonValueKind.Object && mapEl.TryGetProperty("pk", out var pkProp))
{
mapId = pkProp.GetString();
}
if (!string.IsNullOrEmpty(mapId) && !mappings.Contains(mapId))
{
mappings.Add(mapId);
_logger.LogDebug("[Authentik] Found existing mapping: {Id}", mapId);
}
}
}
// ── 3. Add new mapping if not already present ──────────────────
if (!mappings.Contains(mappingId))
{
mappings.Add(mappingId);
_logger.LogInformation("[Authentik] Adding usertypeid mapping {Id} to provider", mappingId);
}
else
{
_logger.LogInformation("[Authentik] Usertypeid mapping {Id} already attached to provider", mappingId);
return;
}
// ── 4. Patch provider with updated mappings ───────────────────
var patchPayload = JsonSerializer.Serialize(new Dictionary<string, object>
{
["property_mappings"] = mappings,
});
_logger.LogDebug("[Authentik] Patching provider {Id} with mappings: {Mappings}",
providerId, string.Join(", ", mappings));
var patchContent = new StringContent(patchPayload, Encoding.UTF8, "application/json");
var patchReq = new HttpRequestMessage(HttpMethod.Patch, $"/api/v3/providers/saml/{providerId}/")
{