release orchestrator v1 draft and build fixes
This commit is contained in:
@@ -0,0 +1,220 @@
|
||||
namespace StellaOps.Authority.Plugin.Unified;
|
||||
|
||||
using StellaOps.Authority.Plugins.Abstractions;
|
||||
using StellaOps.Plugin.Abstractions;
|
||||
using StellaOps.Plugin.Abstractions.Capabilities;
|
||||
using StellaOps.Plugin.Abstractions.Context;
|
||||
using StellaOps.Plugin.Abstractions.Health;
|
||||
using StellaOps.Plugin.Abstractions.Lifecycle;
|
||||
|
||||
/// <summary>
|
||||
/// Adapts an existing IIdentityProviderPlugin to the unified IPlugin and IAuthCapability interfaces.
|
||||
/// This enables gradual migration of Authority plugins to the unified plugin architecture.
|
||||
/// </summary>
|
||||
public sealed class AuthPluginAdapter : IPlugin, IAuthCapability
|
||||
{
|
||||
private readonly IIdentityProviderPlugin _inner;
|
||||
private IPluginContext? _context;
|
||||
private PluginLifecycleState _state = PluginLifecycleState.Discovered;
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new adapter for an existing identity provider plugin.
|
||||
/// </summary>
|
||||
/// <param name="inner">The existing identity provider plugin to wrap.</param>
|
||||
public AuthPluginAdapter(IIdentityProviderPlugin inner)
|
||||
{
|
||||
_inner = inner ?? throw new ArgumentNullException(nameof(inner));
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public PluginInfo Info => new(
|
||||
Id: $"com.stellaops.auth.{_inner.Type}",
|
||||
Name: _inner.Name,
|
||||
Version: "1.0.0",
|
||||
Vendor: "Stella Ops",
|
||||
Description: $"Authority {_inner.Type} identity provider plugin");
|
||||
|
||||
/// <inheritdoc />
|
||||
public PluginTrustLevel TrustLevel => PluginTrustLevel.BuiltIn;
|
||||
|
||||
/// <inheritdoc />
|
||||
public PluginCapabilities Capabilities => PluginCapabilities.Auth | PluginCapabilities.Network;
|
||||
|
||||
/// <inheritdoc />
|
||||
public PluginLifecycleState State => _state;
|
||||
|
||||
#region IAuthCapability
|
||||
|
||||
/// <inheritdoc />
|
||||
public string ProviderType => _inner.Type;
|
||||
|
||||
/// <inheritdoc />
|
||||
public IReadOnlyList<string> SupportedMethods
|
||||
{
|
||||
get
|
||||
{
|
||||
var methods = new List<string>();
|
||||
if (_inner.Capabilities.SupportsPassword)
|
||||
methods.Add("password");
|
||||
if (_inner.Capabilities.SupportsMfa)
|
||||
methods.Add("mfa");
|
||||
return methods;
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public async Task<AuthResult> AuthenticateAsync(AuthRequest request, CancellationToken ct)
|
||||
{
|
||||
if (request.Method != "password" || string.IsNullOrEmpty(request.Username) || string.IsNullOrEmpty(request.Password))
|
||||
{
|
||||
return AuthResult.Failed("Invalid authentication method or missing credentials");
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
var result = await _inner.Credentials.VerifyPasswordAsync(
|
||||
request.Username,
|
||||
request.Password,
|
||||
ct);
|
||||
|
||||
if (result.Succeeded && result.User != null)
|
||||
{
|
||||
return AuthResult.Succeeded(
|
||||
userId: result.User.SubjectId,
|
||||
roles: result.User.Roles?.ToList());
|
||||
}
|
||||
|
||||
return AuthResult.Failed(result.Message ?? "Authentication failed");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return AuthResult.Failed(ex.Message);
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public Task<TokenValidationResult> ValidateTokenAsync(string token, CancellationToken ct)
|
||||
{
|
||||
// Authority plugins don't typically handle token validation directly
|
||||
// This is handled by the Authority web service
|
||||
return Task.FromResult(TokenValidationResult.Invalid("Token validation not supported by this provider"));
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public async Task<AuthUserInfo?> GetUserInfoAsync(string userId, CancellationToken ct)
|
||||
{
|
||||
try
|
||||
{
|
||||
var user = await _inner.Credentials.FindBySubjectAsync(userId, ct);
|
||||
if (user == null)
|
||||
return null;
|
||||
|
||||
return new AuthUserInfo(
|
||||
Id: user.SubjectId,
|
||||
Username: user.Username,
|
||||
Email: user.Attributes?.GetValueOrDefault("email"),
|
||||
DisplayName: user.DisplayName,
|
||||
Attributes: user.Attributes?.Where(kv => kv.Value != null)
|
||||
.ToDictionary(kv => kv.Key, kv => kv.Value!));
|
||||
}
|
||||
catch
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public async Task<IReadOnlyList<AuthGroupInfo>> GetUserGroupsAsync(string userId, CancellationToken ct)
|
||||
{
|
||||
try
|
||||
{
|
||||
// Get user and extract roles as groups
|
||||
var user = await _inner.Credentials.FindBySubjectAsync(userId, ct);
|
||||
if (user == null)
|
||||
return Array.Empty<AuthGroupInfo>();
|
||||
|
||||
return user.Roles.Select(role => new AuthGroupInfo(role, role, null)).ToList();
|
||||
}
|
||||
catch
|
||||
{
|
||||
return Array.Empty<AuthGroupInfo>();
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public async Task<bool> HasPermissionAsync(string userId, string permission, CancellationToken ct)
|
||||
{
|
||||
var groups = await GetUserGroupsAsync(userId, ct);
|
||||
return groups.Any(g => g.Name.Equals(permission, StringComparison.OrdinalIgnoreCase));
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public Task<SsoInitiation?> InitiateSsoAsync(SsoRequest request, CancellationToken ct)
|
||||
{
|
||||
// SSO is type-specific - LDAP doesn't support it, OIDC/SAML do
|
||||
// This base adapter doesn't support SSO; specialized adapters should override
|
||||
return Task.FromResult<SsoInitiation?>(null);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public Task<AuthResult> CompleteSsoAsync(SsoCallback callback, CancellationToken ct)
|
||||
{
|
||||
return Task.FromResult(AuthResult.Failed("SSO not supported by this provider"));
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region IPlugin
|
||||
|
||||
/// <inheritdoc />
|
||||
public async Task InitializeAsync(IPluginContext context, CancellationToken ct)
|
||||
{
|
||||
_context = context;
|
||||
_state = PluginLifecycleState.Initializing;
|
||||
|
||||
// The inner plugin is already initialized via the Authority plugin loader
|
||||
// We just need to verify it's working
|
||||
var health = await _inner.CheckHealthAsync(ct);
|
||||
if (health.Status == AuthorityPluginHealthStatus.Unavailable)
|
||||
{
|
||||
_state = PluginLifecycleState.Failed;
|
||||
throw new InvalidOperationException($"Authority plugin health check failed: {health.Message}");
|
||||
}
|
||||
|
||||
_state = PluginLifecycleState.Active;
|
||||
context.Logger.Info("Authority plugin adapter initialized for {PluginName}", _inner.Name);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public async Task<HealthCheckResult> HealthCheckAsync(CancellationToken ct)
|
||||
{
|
||||
try
|
||||
{
|
||||
var result = await _inner.CheckHealthAsync(ct);
|
||||
|
||||
return result.Status switch
|
||||
{
|
||||
AuthorityPluginHealthStatus.Healthy => HealthCheckResult.Healthy()
|
||||
.WithDetails(result.Details?.Where(kv => kv.Value != null)
|
||||
.ToDictionary(kv => kv.Key, kv => (object)kv.Value!) ?? new Dictionary<string, object>()),
|
||||
AuthorityPluginHealthStatus.Degraded => HealthCheckResult.Degraded(result.Message ?? "Degraded")
|
||||
.WithDetails(result.Details?.Where(kv => kv.Value != null)
|
||||
.ToDictionary(kv => kv.Key, kv => (object)kv.Value!) ?? new Dictionary<string, object>()),
|
||||
_ => HealthCheckResult.Unhealthy(result.Message ?? "Unhealthy")
|
||||
};
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return HealthCheckResult.Unhealthy(ex);
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public ValueTask DisposeAsync()
|
||||
{
|
||||
_state = PluginLifecycleState.Stopped;
|
||||
return ValueTask.CompletedTask;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net10.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
<LangVersion>preview</LangVersion>
|
||||
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
|
||||
<Description>Unified plugin adapter for Authority identity provider plugins</Description>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\StellaOps.Authority.Plugins.Abstractions\StellaOps.Authority.Plugins.Abstractions.csproj" />
|
||||
<ProjectReference Include="..\..\..\Plugin\StellaOps.Plugin.Abstractions\StellaOps.Plugin.Abstractions.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
Reference in New Issue
Block a user