diff --git a/src/Tgstation.Server.Api/ModelAttribute.cs b/src/Tgstation.Server.Api/ModelAttribute.cs
index 6145721634..df50284cf3 100644
--- a/src/Tgstation.Server.Api/ModelAttribute.cs
+++ b/src/Tgstation.Server.Api/ModelAttribute.cs
@@ -6,7 +6,7 @@ namespace Tgstation.Server.Api
///
/// Indicates the rights for a model
///
- [AttributeUsage(AttributeTargets.Class, AllowMultiple = false, Inherited = false)]
+ [AttributeUsage(AttributeTargets.Class, AllowMultiple = false, Inherited = true)]
public sealed class ModelAttribute : Attribute
{
///
diff --git a/src/Tgstation.Server.Api/Models/Instance.cs b/src/Tgstation.Server.Api/Models/Instance.cs
index 43d3eea404..276eed69e3 100644
--- a/src/Tgstation.Server.Api/Models/Instance.cs
+++ b/src/Tgstation.Server.Api/Models/Instance.cs
@@ -6,7 +6,7 @@ namespace Tgstation.Server.Api.Models
/// Metadata about a server instance
///
[Model(RightsType.InstanceManager, CanCrud = true, CanList = true)]
- public sealed class Instance
+ public class Instance
{
///
/// The id of the . Not modifiable
diff --git a/src/Tgstation.Server.Api/Models/User.cs b/src/Tgstation.Server.Api/Models/User.cs
index fe50e0bc77..e53f42e419 100644
--- a/src/Tgstation.Server.Api/Models/User.cs
+++ b/src/Tgstation.Server.Api/Models/User.cs
@@ -6,7 +6,7 @@ namespace Tgstation.Server.Api.Models
/// Represents a server
///
[Model(RightsType.Administration, WriteRight = AdministrationRights.EditUsers)]
- public sealed class User
+ public class User
{
///
/// The ID of the
diff --git a/src/Tgstation.Server.Host/Core/AuthenticationContext.cs b/src/Tgstation.Server.Host/Core/AuthenticationContext.cs
index 6c770faa5b..6418d3be78 100644
--- a/src/Tgstation.Server.Host/Core/AuthenticationContext.cs
+++ b/src/Tgstation.Server.Host/Core/AuthenticationContext.cs
@@ -1,63 +1,113 @@
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Builder;
+using Microsoft.Extensions.DependencyInjection;
using System;
using System.Net;
using System.Linq;
+using System.Threading;
+using System.Threading.Tasks;
using Tgstation.Server.Api;
-using Tgstation.Server.Api.Models;
+using Tgstation.Server.Host.Models;
namespace Tgstation.Server.Host.Core
{
+ ///
+ /// Manages s for a scope
+ ///
sealed class AuthenticationContext : IAuthenticationContext
{
+ ///
+ /// Used to store the in
+ ///
static readonly object contextKey = new object();
+ ///
+ /// Get the current from an
+ ///
+ /// The containing the
+ /// The in
public static IAuthenticationContext Current(HttpContext httpContext) => (IAuthenticationContext)httpContext.Items[contextKey];
- public static void AddToPipeline(IApplicationBuilder applicationBuilder) => applicationBuilder.Use(async (httpContext, next) =>
+ ///
+ /// Adds handling to an pipeline
+ ///
+ /// The to add to
+ public static void AddToPipeline(IApplicationBuilder applicationBuilder)
+ {
+ applicationBuilder.Use(async (httpContext, next) =>
+ {
+ Headers headers;
+ try
+ {
+ headers = new Headers(httpContext.Request.Headers.ToDictionary(x => x.Key, x => x.Value.First()));
+ }
+ catch (InvalidOperationException)
+ {
+ httpContext.Response.StatusCode = (int)HttpStatusCode.BadRequest;
+ return;
+ }
+
+ ISystemIdentity systemIdentity;
+
+ var systemIdentityFactory = applicationBuilder.ApplicationServices.GetRequiredService();
+ var tokenManager = applicationBuilder.ApplicationServices.GetRequiredService();
+ var databaseContext = applicationBuilder.ApplicationServices.GetRequiredService();
+ try
+ {
+ if (headers.IsTokenAuthentication)
+ {
+ var user = await tokenManager.GetUser(headers.Token, httpContext.RequestAborted).ConfigureAwait(false);
+ systemIdentity = systemIdentityFactory.CreateSystemIdentity(user);
+ }
+ else
+ systemIdentity = systemIdentityFactory.CreateSystemIdentity(headers.Username, headers.Password);
+ }
+ catch (UnauthorizedAccessException)
+ {
+ httpContext.Response.StatusCode = (int)HttpStatusCode.Unauthorized;
+ return;
+ }
+
+ using (var authContext = new AuthenticationContext(systemIdentity, databaseContext))
+ {
+ httpContext.Items[contextKey] = authContext;
+ await next().ConfigureAwait(false);
+ }
+ });
+ }
+
+ ///
+ public ISystemIdentity SystemIdentity { get; }
+
+ ///
+ /// The for the
+ ///
+ readonly IDatabaseContext databaseContext;
+
+ ///
+ /// Construct a
+ ///
+ /// The value of
+ /// The value of
+ public AuthenticationContext(ISystemIdentity systemIdentity, IDatabaseContext databaseContext)
{
- Headers headers;
- try
- {
- headers = new Headers(httpContext.Request.Headers.ToDictionary(x => x.Key, x => x.Value.First()));
- }
- catch (InvalidOperationException)
- {
- httpContext.Response.StatusCode = (int)HttpStatusCode.BadRequest;
- return;
- }
+ SystemIdentity = systemIdentity ?? throw new ArgumentNullException(nameof(systemIdentity));
+ this.databaseContext = databaseContext ?? throw new ArgumentNullException(nameof(databaseContext));
+ }
- using (var authContext = new AuthenticationContext(headers))
- {
- if (!authContext.Valid)
- {
- httpContext.Response.StatusCode = (int)HttpStatusCode.Unauthorized;
- return;
- }
- httpContext.Items[contextKey] = authContext;
- await next().ConfigureAwait(false);
- }
- });
+ ///
+ public void Dispose() => SystemIdentity.Dispose();
- public AuthenticationContext(Headers headers)
+ ///
+ public Task User(CancellationToken cancellationToken)
{
throw new NotImplementedException();
}
- ~AuthenticationContext() => Dispose();
-
- public void Dispose()
- {
-
- GC.SuppressFinalize(this);
- }
-
- public IAuthenticationContext Clone() => throw new NotImplementedException();
-
- bool Valid => throw new NotImplementedException();
-
- public User User => throw new NotImplementedException();
-
- public InstanceUser InstanceUser => throw new NotImplementedException();
+ ///
+ public Task InstanceUser(Instance instance, CancellationToken cancellationToken)
+ {
+ throw new NotImplementedException();
+ }
}
}
diff --git a/src/Tgstation.Server.Host/Core/IAuthenticationContext.cs b/src/Tgstation.Server.Host/Core/IAuthenticationContext.cs
index dacb668c86..1d2466bbaf 100644
--- a/src/Tgstation.Server.Host/Core/IAuthenticationContext.cs
+++ b/src/Tgstation.Server.Host/Core/IAuthenticationContext.cs
@@ -1,15 +1,33 @@
using System;
-using System.Collections.Generic;
-using Tgstation.Server.Api.Models;
+using System.Threading;
+using System.Threading.Tasks;
+using Tgstation.Server.Host.Models;
namespace Tgstation.Server.Host.Core
{
+ ///
+ /// Represents the currently authenticated
+ ///
interface IAuthenticationContext : IDisposable
{
- User User { get; }
+ ///
+ /// The of
+ ///
+ ISystemIdentity SystemIdentity { get; }
- InstanceUser InstanceUser { get; }
+ ///
+ /// The represented by the
+ ///
+ /// The for the operation
+ /// A resulting in the represented by the
+ Task User(CancellationToken cancellationToken);
- IAuthenticationContext Clone();
+ ///
+ /// The represented by and a given
+ ///
+ /// The of the
+ /// The for the operation
+ /// A resulting in the represented by and a given
+ Task InstanceUser(Instance instance, CancellationToken cancellationToken);
}
}
\ No newline at end of file
diff --git a/src/Tgstation.Server.Host/Core/ISystemIdentity.cs b/src/Tgstation.Server.Host/Core/ISystemIdentity.cs
new file mode 100644
index 0000000000..6f60a6bc99
--- /dev/null
+++ b/src/Tgstation.Server.Host/Core/ISystemIdentity.cs
@@ -0,0 +1,14 @@
+using System;
+using System.Collections.Generic;
+
+namespace Tgstation.Server.Host.Core
+{
+ interface ISystemIdentity : IDisposable
+ {
+ string Uid { get; }
+ string Username { get; }
+ IEnumerable Groups { get; }
+
+ ISystemIdentity Clone();
+ }
+}
\ No newline at end of file
diff --git a/src/Tgstation.Server.Host/Core/ISystemIdentityFactory.cs b/src/Tgstation.Server.Host/Core/ISystemIdentityFactory.cs
new file mode 100644
index 0000000000..05e03ea829
--- /dev/null
+++ b/src/Tgstation.Server.Host/Core/ISystemIdentityFactory.cs
@@ -0,0 +1,10 @@
+using Tgstation.Server.Api.Models;
+
+namespace Tgstation.Server.Host.Core
+{
+ interface ISystemIdentityFactory
+ {
+ ISystemIdentity CreateSystemIdentity(User user);
+ ISystemIdentity CreateSystemIdentity(string username, string password);
+ }
+}
diff --git a/src/Tgstation.Server.Host/Core/ITokenManager.cs b/src/Tgstation.Server.Host/Core/ITokenManager.cs
new file mode 100644
index 0000000000..e898760f51
--- /dev/null
+++ b/src/Tgstation.Server.Host/Core/ITokenManager.cs
@@ -0,0 +1,15 @@
+using System.Collections.Generic;
+using System.Threading;
+using System.Threading.Tasks;
+using Tgstation.Server.Api.Models;
+
+namespace Tgstation.Server.Host.Core
+{
+ interface ITokenManager
+ {
+ Task GetUser(string token, CancellationToken cancellationToken);
+ Task CreateToken(User user, CancellationToken cancellationToken);
+ Task> UserTokens(User user, CancellationToken cancellationToken);
+ Task> AllTokens(CancellationToken cancellationToken);
+ }
+}
diff --git a/src/Tgstation.Server.Host/Models/IDatabaseContext.cs b/src/Tgstation.Server.Host/Models/IDatabaseContext.cs
new file mode 100644
index 0000000000..7b44a65ca6
--- /dev/null
+++ b/src/Tgstation.Server.Host/Models/IDatabaseContext.cs
@@ -0,0 +1,20 @@
+using Microsoft.EntityFrameworkCore;
+using System.Threading;
+using System.Threading.Tasks;
+
+namespace Tgstation.Server.Host.Models
+{
+ ///
+ /// Represents the database
+ ///
+ interface IDatabaseContext
+ {
+ DbSet Users { get; }
+
+ DbSet Instances { get; }
+
+ Task Save(CancellationToken cancellationToken);
+
+ Task Initialize(CancellationToken cancellationToken);
+ }
+}
diff --git a/src/Tgstation.Server.Host/Models/Instance.cs b/src/Tgstation.Server.Host/Models/Instance.cs
new file mode 100644
index 0000000000..573aa46e45
--- /dev/null
+++ b/src/Tgstation.Server.Host/Models/Instance.cs
@@ -0,0 +1,16 @@
+using System.Collections.Generic;
+using Tgstation.Server.Api.Models;
+
+namespace Tgstation.Server.Host.Models
+{
+ ///
+ /// Represents an in the database
+ ///
+ sealed class Instance : Api.Models.Instance
+ {
+ ///
+ /// The s in the
+ ///
+ public List InstanceUsers { get; set; }
+ }
+}
diff --git a/src/Tgstation.Server.Host/Models/TestMerge.cs b/src/Tgstation.Server.Host/Models/TestMerge.cs
new file mode 100644
index 0000000000..7ecbf704cc
--- /dev/null
+++ b/src/Tgstation.Server.Host/Models/TestMerge.cs
@@ -0,0 +1,41 @@
+using System;
+using System.ComponentModel.DataAnnotations;
+
+namespace Tgstation.Server.Host.Models
+{
+ ///
+ /// Represent a merge of a live pull request
+ ///
+ sealed class TestMerge
+ {
+ long Id { get; set; }
+
+ int Number { get; set; }
+
+ [Required]
+ string OriginCommit { get; set; }
+
+ [Required]
+ string PullRequestCommit { get; set; }
+
+ string MergeCommit { get; set; }
+
+ [Required]
+ DateTimeOffset MergedAt { get; set; }
+
+ [Required]
+ DateTimeOffset RemovedAt { get; set; }
+
+ [Required]
+ User MergedBy { get; set; }
+
+ [Required]
+ string TitleAtMerge { get; set; }
+
+ [Required]
+ string BodyAtMerge { get; set; }
+
+ [Required]
+ string Author { get; set; }
+ }
+}
diff --git a/src/Tgstation.Server.Host/Models/User.cs b/src/Tgstation.Server.Host/Models/User.cs
new file mode 100644
index 0000000000..743ee5e8db
--- /dev/null
+++ b/src/Tgstation.Server.Host/Models/User.cs
@@ -0,0 +1,21 @@
+using System.Collections.Generic;
+using Tgstation.Server.Api.Models;
+
+namespace Tgstation.Server.Host.Models
+{
+ ///
+ /// Represents a in the database
+ ///
+ sealed class User : Api.Models.User
+ {
+ ///
+ /// The s for the
+ ///
+ List InstanceUsers { get; set; }
+
+ ///
+ /// The s for the
+ ///
+ List Tokens { get; set; }
+ }
+}
diff --git a/src/Tgstation.Server.Host/Tgstation.Server.Host.csproj b/src/Tgstation.Server.Host/Tgstation.Server.Host.csproj
index 9f4dc2c1ea..40ab8fa5f7 100644
--- a/src/Tgstation.Server.Host/Tgstation.Server.Host.csproj
+++ b/src/Tgstation.Server.Host/Tgstation.Server.Host.csproj
@@ -19,7 +19,10 @@
+
+
+