From df0737e55afeb5c01eb39909605970d1d3a73ffc Mon Sep 17 00:00:00 2001 From: Melodi <62381002+Melodi17@users.noreply.github.com> Date: Sun, 13 Sep 2026 13:22:20 +1000 Subject: refactor: cleanup; feat: connection to agent --- .../Services/AgentAuthenticationHandler.cs | 79 ++++++++++++++++++++++ .../Services/VersionControlSyncService.cs | 38 ++++++++++- 2 files changed, 114 insertions(+), 3 deletions(-) create mode 100644 src/marlin.web/Services/AgentAuthenticationHandler.cs (limited to 'src/marlin.web/Services') diff --git a/src/marlin.web/Services/AgentAuthenticationHandler.cs b/src/marlin.web/Services/AgentAuthenticationHandler.cs new file mode 100644 index 0000000..519d109 --- /dev/null +++ b/src/marlin.web/Services/AgentAuthenticationHandler.cs @@ -0,0 +1,79 @@ +namespace marlin.web.Services; + +using System.Security.Claims; +using System.Text.Encodings.Web; +using Data; +using Microsoft.AspNetCore.Authentication; +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.Options; +using Microsoft.Extensions.Primitives; + +public class AgentSchemeOptions : AuthenticationSchemeOptions +{ + public const string Name = "Agent"; + public string AgentHeader { get; set; } = "Agent"; + public string JobHeader { get; set; } = "Job"; +} + +public class AgentAuthenticationHandler : AuthenticationHandler +{ + private readonly ApplicationDbContext _dbContext; + + public AgentAuthenticationHandler( + IOptionsMonitor options, + ILoggerFactory logger, + UrlEncoder encoder, + ApplicationDbContext dbContext) : base(options, logger, encoder) + { + this._dbContext = dbContext; + } + + private bool TryReadPair(string headerName, out Guid id, out string token) + { + id = Guid.Empty; + token = string.Empty; + + if (!Request.Headers.TryGetValue(headerName, out StringValues header)) + return false; + + var parts = header.ToString().Split(' ', 2); + if (parts.Length != 2 || parts[1].Length == 0) + return false; + + token = parts[1]; + return Guid.TryParse(parts[0], out id); + } + + protected override async Task HandleAuthenticateAsync() + { + var claims = new List(); + + if (!TryReadPair(Options.AgentHeader, out var agentId, out var agentToken)) + return AuthenticateResult.NoResult(); + + var agent = await this._dbContext.Agents + .FirstOrDefaultAsync(x => x.Id == agentId && x.AuthToken == agentToken); + + if (agent == null) + return AuthenticateResult.Fail("Invalid agent credentials."); + + claims.Add(new Claim("agent_id", agent.Id.ToString())); + + if (TryReadPair(Options.JobHeader, out var jobId, out var jobToken)) + { + var job = await this._dbContext.Jobs + .FirstOrDefaultAsync(x => x.Id == jobId && x.AgentId == agent.Id && x.ClaimToken == jobToken); + + if (job == null) + return AuthenticateResult.Fail("Invalid job claim token."); + + claims.Add(new Claim("job_id", job.Id.ToString())); + } + + var identity = new ClaimsIdentity(claims, this.Scheme.Name); + var principal = new ClaimsPrincipal(identity); + var ticket = new AuthenticationTicket(principal, this.Scheme.Name); + + return AuthenticateResult.Success(ticket); + } +} diff --git a/src/marlin.web/Services/VersionControlSyncService.cs b/src/marlin.web/Services/VersionControlSyncService.cs index bf189b0..cd75ed6 100644 --- a/src/marlin.web/Services/VersionControlSyncService.cs +++ b/src/marlin.web/Services/VersionControlSyncService.cs @@ -207,8 +207,9 @@ public class VersionControlSyncService : BackgroundService foreach (VersionControlledCommit commit in refs.Select(x => x.Commit)) { - var blobNames = await client.ListTree(localPath, commit.Hash, "."); - + var blobNames = await client.ListTree(localPath, commit.Hash, ".marlin"); + var workflowFiles = new List(); + foreach (string blobName in blobNames) { var content = await client.CatFile(localPath, commit.Hash, blobName); @@ -219,9 +220,40 @@ public class VersionControlSyncService : BackgroundService Path = blobName, Content = content }); + + if (blobName.EndsWith(".yml", StringComparison.OrdinalIgnoreCase) || + blobName.EndsWith(".yaml", StringComparison.OrdinalIgnoreCase)) + workflowFiles.Add(Encoding.UTF8.GetString(content)); } - + commit.BlobsFetched = true; + + this.ScanWorkflow(dbContext, commit, workflowFiles); + } + } + + private void ScanWorkflow(ApplicationDbContext dbContext, VersionControlledCommit commit, List workflowFiles) + { + try + { + var builder = new WorkflowBuilder(); + foreach (var file in workflowFiles) + builder.LoadFromString(file); + + dbContext.WorkflowSnapshots.Add(new WorkflowSnapshot + { + Commit = commit, + ScannedAt = DateTime.UtcNow, + Workflow = builder.Build() + }); + } + catch (Exception ex) + { + this._logger.LogWarning(ex, "Failed to parse workflow for commit '{hash}'", commit.Hash); + } + finally + { + commit.WorkflowScanned = true; } } } \ No newline at end of file -- cgit v1.3.1