diff --git a/Compression.BSA.Test/Compression.BSA.Test.csproj b/Compression.BSA.Test/Compression.BSA.Test.csproj
index 69d444aa..122ede28 100644
--- a/Compression.BSA.Test/Compression.BSA.Test.csproj
+++ b/Compression.BSA.Test/Compression.BSA.Test.csproj
@@ -33,8 +33,8 @@
4
CS1998
CS4014
- true
- true
+ true
+ true
pdbonly
@@ -45,8 +45,8 @@
4
CS1998
CS4014
- true
- true
+ true
+ true
true
@@ -57,8 +57,8 @@
7.3
prompt
MinimumRecommendedRules.ruleset
- true
- true
+ true
+ true
bin\x64\Release\
@@ -69,8 +69,8 @@
7.3
prompt
MinimumRecommendedRules.ruleset
- true
- true
+ true
+ true
@@ -104,10 +104,10 @@
2.2.6
- 2.0.0
+ 2.1.0-beta2
- 2.0.0
+ 2.1.0-beta2
diff --git a/Wabbajack.BuildServer/ApiKeyAuthenticationHandler.cs b/Wabbajack.BuildServer/ApiKeyAuthenticationHandler.cs
new file mode 100644
index 00000000..1c54073f
--- /dev/null
+++ b/Wabbajack.BuildServer/ApiKeyAuthenticationHandler.cs
@@ -0,0 +1,87 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Security.Claims;
+using System.Text.Encodings.Web;
+using System.Threading.Tasks;
+using Microsoft.AspNetCore.Authentication;
+using Microsoft.AspNetCore.Http;
+using Microsoft.Extensions.Logging;
+using Microsoft.Extensions.Options;
+using Newtonsoft.Json;
+using Wabbajack.BuildServer.Models;
+
+namespace Wabbajack.BuildServer
+{
+
+ public class ApiKeyAuthenticationOptions : AuthenticationSchemeOptions
+ {
+ public const string DefaultScheme = "API Key";
+ public string Scheme => DefaultScheme;
+ public string AuthenticationType = DefaultScheme;
+ }
+
+ public class ApiKeyAuthenticationHandler : AuthenticationHandler
+ {
+ private const string ProblemDetailsContentType = "application/problem+json";
+ private readonly DBContext _db;
+ private const string ApiKeyHeaderName = "X-Api-Key";
+
+ public ApiKeyAuthenticationHandler(
+ IOptionsMonitor options,
+ ILoggerFactory logger,
+ UrlEncoder encoder,
+ ISystemClock clock,
+ DBContext db) : base(options, logger, encoder, clock)
+ {
+ _db = db;
+ }
+
+ protected override async Task HandleAuthenticateAsync()
+ {
+ if (!Request.Headers.TryGetValue(ApiKeyHeaderName, out var apiKeyHeaderValues))
+ {
+ return AuthenticateResult.NoResult();
+ }
+
+ var providedApiKey = apiKeyHeaderValues.FirstOrDefault();
+
+ if (apiKeyHeaderValues.Count == 0 || string.IsNullOrWhiteSpace(providedApiKey))
+ {
+ return AuthenticateResult.NoResult();
+ }
+
+ var existingApiKey = await ApiKey.Get(_db, providedApiKey);
+
+ if (existingApiKey != null)
+ {
+ var claims = new List {new Claim(ClaimTypes.Name, existingApiKey.Owner)};
+
+ claims.AddRange(existingApiKey.Roles.Select(role => new Claim(ClaimTypes.Role, role)));
+
+ var identity = new ClaimsIdentity(claims, Options.AuthenticationType);
+ var identities = new List {identity};
+ var principal = new ClaimsPrincipal(identities);
+ var ticket = new AuthenticationTicket(principal, Options.Scheme);
+
+ return AuthenticateResult.Success(ticket);
+ }
+
+ return AuthenticateResult.Fail("Invalid API Key provided.");
+ }
+
+ protected override async Task HandleChallengeAsync(AuthenticationProperties properties)
+ {
+ Response.StatusCode = 401;
+ Response.ContentType = ProblemDetailsContentType;
+ await Response.WriteAsync("Unauthorized");
+ }
+
+ protected override async Task HandleForbiddenAsync(AuthenticationProperties properties)
+ {
+ Response.StatusCode = 403;
+ Response.ContentType = ProblemDetailsContentType;
+ await Response.WriteAsync("forbidden");
+ }
+ }
+}
diff --git a/Wabbajack.BuildServer/AppSettings.cs b/Wabbajack.BuildServer/AppSettings.cs
index fc579416..5470265e 100644
--- a/Wabbajack.BuildServer/AppSettings.cs
+++ b/Wabbajack.BuildServer/AppSettings.cs
@@ -11,5 +11,10 @@ namespace Wabbajack.BuildServer
public string DownloadDir { get; set; }
public string ArchiveDir { get; set; }
+
+ public bool MinimalMode { get; set; }
+
+ public bool RunFrontEndJobs { get; set; }
+ public bool RunBackEndJobs { get; set; }
}
}
diff --git a/Wabbajack.BuildServer/Controllers/AControllerBase.cs b/Wabbajack.BuildServer/Controllers/AControllerBase.cs
index 34f95c0c..3a1b7cd0 100644
--- a/Wabbajack.BuildServer/Controllers/AControllerBase.cs
+++ b/Wabbajack.BuildServer/Controllers/AControllerBase.cs
@@ -1,6 +1,13 @@
-using Microsoft.AspNetCore.Mvc;
+using System;
+using System.Collections.Generic;
+using System.Threading.Tasks;
+using Alphaleonis.Win32.Filesystem;
+using GraphQL;
+using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Logging;
+using MongoDB.Driver;
using Wabbajack.BuildServer.Models;
+using Wabbajack.Common;
namespace Wabbajack.BuildServer.Controllers
{
@@ -15,5 +22,7 @@ namespace Wabbajack.BuildServer.Controllers
Db = db;
Logger = logger;
}
+
+
}
}
diff --git a/Wabbajack.BuildServer/Controllers/Heartbeat.cs b/Wabbajack.BuildServer/Controllers/Heartbeat.cs
index 3fcd4a56..73c12362 100644
--- a/Wabbajack.BuildServer/Controllers/Heartbeat.cs
+++ b/Wabbajack.BuildServer/Controllers/Heartbeat.cs
@@ -1,6 +1,7 @@
using System;
using System.Linq;
using System.Threading.Tasks;
+using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Logging;
using Wabbajack.BuildServer.Models;
@@ -27,5 +28,13 @@ namespace Wabbajack.BuildServer.Controllers
{
return DateTime.Now - _startTime;
}
+
+ [HttpGet("only-authenticated")]
+ [Authorize]
+ public IActionResult OnlyAuthenticated()
+ {
+ var message = $"Hello from {nameof(OnlyAuthenticated)}";
+ return new ObjectResult(message);
+ }
}
}
diff --git a/Wabbajack.BuildServer/Controllers/Jobs.cs b/Wabbajack.BuildServer/Controllers/Jobs.cs
index 4079aada..098f1772 100644
--- a/Wabbajack.BuildServer/Controllers/Jobs.cs
+++ b/Wabbajack.BuildServer/Controllers/Jobs.cs
@@ -1,6 +1,7 @@
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
+using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Logging;
using MongoDB.Driver;
@@ -10,6 +11,7 @@ using Wabbajack.BuildServer.Models.JobQueue;
namespace Wabbajack.BuildServer.Controllers
{
+ [Authorize]
[ApiController]
[Route("/jobs")]
public class Jobs : AControllerBase
diff --git a/Wabbajack.BuildServer/Controllers/UploadedFiles.cs b/Wabbajack.BuildServer/Controllers/UploadedFiles.cs
new file mode 100644
index 00000000..89ae6061
--- /dev/null
+++ b/Wabbajack.BuildServer/Controllers/UploadedFiles.cs
@@ -0,0 +1,122 @@
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Net;
+using System.Security.Claims;
+using System.Text;
+using System.Threading.Tasks;
+using Microsoft.AspNetCore.Authorization;
+using Microsoft.AspNetCore.Http;
+using Microsoft.AspNetCore.Identity;
+using Microsoft.AspNetCore.Mvc;
+using Microsoft.Extensions.Logging;
+using MongoDB.Driver;
+using MongoDB.Driver.Linq;
+using Nettle;
+using Wabbajack.BuildServer.Models;
+using Wabbajack.Common;
+using Path = Alphaleonis.Win32.Filesystem.Path;
+
+namespace Wabbajack.BuildServer.Controllers
+{
+ public class UploadedFiles : AControllerBase
+ {
+ public UploadedFiles(ILogger logger, DBContext db) : base(logger, db)
+ {
+
+ }
+
+ [HttpPut]
+ [Route("upload_file/{Name}/start")]
+ public async Task UploadFileStreaming(string Name)
+ {
+ var guid = Guid.NewGuid();
+ var key = Encoding.UTF8.GetBytes($"{Path.GetFileNameWithoutExtension(Name)}|{guid.ToString()}|{Path.GetExtension(Name)}").ToHex();
+ System.IO.File.Create(Path.Combine("public", "files", key)).Close();
+ Utils.Log($"Starting Ingest for {key}");
+ return Ok(key);
+ }
+
+ static private HashSet HexChars = new HashSet("abcdef1234567890");
+ [HttpPut]
+ [Route("upload_file/{Key}/data/{Offset}")]
+ public async Task UploadFilePart(string Key, long Offset)
+ {
+ if (!Key.All(a => HexChars.Contains(a)))
+ return BadRequest("NOT A VALID FILENAME");
+ Utils.Log($"Writing at position {Offset} in ingest file {Key}");
+ await using (var file = System.IO.File.Open(Path.Combine("public", "files", Key), FileMode.Open, FileAccess.Write))
+ {
+ file.Position = Offset;
+ await Request.Body.CopyToAsync(file);
+ return Ok(file.Position.ToString());
+ }
+ }
+
+ [HttpPut]
+ [Route("upload_file/{Key}/finish")]
+ public async Task UploadFileFinish(string Key)
+ {
+ var user = User.FindFirstValue(ClaimTypes.Name);
+ if (!Key.All(a => HexChars.Contains(a)))
+ return BadRequest("NOT A VALID FILENAME");
+ var parts = Encoding.UTF8.GetString(Key.FromHex()).Split('|');
+ var final_name = $"{parts[0]}-{parts[1]}{parts[2]}";
+ var original_name = $"{parts[0]}{parts[2]}";
+
+ var final_path = Path.Combine("public", "files", final_name);
+ System.IO.File.Move(Path.Combine("public", "files", Key), final_path);
+ var hash = await final_path.FileHashAsync();
+
+ var record = new UploadedFile
+ {
+ Id = parts[1],
+ Hash = hash,
+ Name = original_name,
+ Uploader = user,
+ Size = new FileInfo(final_path).Length
+ };
+ await Db.UploadedFiles.InsertOneAsync(record);
+ return Ok(record.Uri);
+ }
+
+
+ private static readonly Func