mirror of
https://github.com/tgstation/tgstation-server.git
synced 2026-08-26 22:48:20 +01:00
Preliminary AuthenticationContext stuff
This commit is contained in:
@@ -0,0 +1,109 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
|
||||
namespace Tgstation.Server.Api
|
||||
{
|
||||
public sealed class Headers
|
||||
{
|
||||
const string userAgentHeader = "User-Agent";
|
||||
const string apiVersionHeader = "Api-Version";
|
||||
const string tokenHeader = "Token";
|
||||
const string usernameHeader = "Username";
|
||||
const string passwordHeader = "Password";
|
||||
|
||||
public static readonly Version CurrentApiVersion = Assembly.GetExecutingAssembly().GetName().Version;
|
||||
|
||||
public IReadOnlyDictionary<string, string> HeaderEntries => headerEntries;
|
||||
|
||||
public string UserAgent {
|
||||
get => headerEntries[userAgentHeader];
|
||||
private set => headerEntries[userAgentHeader] = value;
|
||||
}
|
||||
|
||||
public Version ApiVersion
|
||||
{
|
||||
get => new Version(headerEntries[apiVersionHeader]);
|
||||
private set => headerEntries[userAgentHeader] = value.ToString();
|
||||
}
|
||||
|
||||
public string Token
|
||||
{
|
||||
get => headerEntries[tokenHeader];
|
||||
private set => headerEntries[tokenHeader] = value;
|
||||
}
|
||||
|
||||
public string Username
|
||||
{
|
||||
get => headerEntries[usernameHeader];
|
||||
private set => headerEntries[usernameHeader] = value;
|
||||
}
|
||||
|
||||
public string Password
|
||||
{
|
||||
get => headerEntries[passwordHeader];
|
||||
private set => headerEntries[passwordHeader] = value;
|
||||
}
|
||||
|
||||
public bool IsTokenAuthentication => headerEntries.TryGetValue(tokenHeader, out string value);
|
||||
|
||||
readonly Dictionary<string, string> headerEntries;
|
||||
|
||||
public Headers(string userAgent, string token) : this(userAgent, token, null, null)
|
||||
{
|
||||
if (userAgent == null)
|
||||
throw new ArgumentNullException(nameof(userAgent));
|
||||
if (token == null)
|
||||
throw new ArgumentNullException(nameof(token));
|
||||
}
|
||||
|
||||
public Headers(string userAgent, string username, string password) : this(userAgent, null, username, password)
|
||||
{
|
||||
if (userAgent == null)
|
||||
throw new ArgumentNullException(nameof(userAgent));
|
||||
if (username == null)
|
||||
throw new ArgumentNullException(nameof(username));
|
||||
if (password == null)
|
||||
throw new ArgumentNullException(nameof(password));
|
||||
}
|
||||
|
||||
public Headers(IReadOnlyDictionary<string, string> headerEntries)
|
||||
{
|
||||
this.headerEntries = headerEntries?.ToDictionary(x => x.Key, x => x.Value) ?? throw new ArgumentNullException(nameof(headerEntries));
|
||||
AssertHeader(userAgentHeader);
|
||||
AssertHeader(apiVersionHeader);
|
||||
try
|
||||
{
|
||||
AssertHeader(usernameHeader);
|
||||
AssertHeader(passwordHeader);
|
||||
}
|
||||
catch (InvalidOperationException)
|
||||
{
|
||||
AssertHeader(tokenHeader);
|
||||
}
|
||||
}
|
||||
|
||||
void AssertHeader(string headerName)
|
||||
{
|
||||
try
|
||||
{
|
||||
var headerValue = headerEntries[headerName];
|
||||
}
|
||||
catch(Exception e)
|
||||
{
|
||||
throw new InvalidOperationException("Missing required header!", e);
|
||||
}
|
||||
}
|
||||
|
||||
Headers(string userAgent, string token, string username, string password)
|
||||
{
|
||||
headerEntries = new Dictionary<string, string>();
|
||||
ApiVersion = CurrentApiVersion;
|
||||
UserAgent = userAgent;
|
||||
Token = token;
|
||||
Username = username;
|
||||
Password = password;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -69,7 +69,9 @@ namespace Tgstation.Server.Host.Core
|
||||
SupportedCultures = supportedCultures,
|
||||
SupportedUICultures = supportedCultures,
|
||||
});
|
||||
|
||||
|
||||
applicationBuilder.UseSystemAuthentication();
|
||||
|
||||
applicationBuilder.UseMvc();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
using Microsoft.AspNetCore.Builder;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace Tgstation.Server.Host.Core
|
||||
{
|
||||
static class ApplicationBuilderExtensions
|
||||
{
|
||||
public static IApplicationBuilder UseSystemAuthentication(this IApplicationBuilder applicationBuilder)
|
||||
{
|
||||
AuthenticationContext.AddToPipeline(applicationBuilder);
|
||||
return applicationBuilder;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Builder;
|
||||
using System;
|
||||
using System.Net;
|
||||
using System.Linq;
|
||||
using Tgstation.Server.Api;
|
||||
using Tgstation.Server.Api.Models;
|
||||
|
||||
namespace Tgstation.Server.Host.Core
|
||||
{
|
||||
sealed class AuthenticationContext : IAuthenticationContext
|
||||
{
|
||||
static readonly object contextKey = new object();
|
||||
|
||||
public static IAuthenticationContext Current(HttpContext httpContext) => (IAuthenticationContext)httpContext.Items[contextKey];
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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 AuthenticationContext(Headers headers)
|
||||
{
|
||||
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();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace Tgstation.Server.Host.Core
|
||||
{
|
||||
static class HttpContextExtensions
|
||||
{
|
||||
public static IAuthenticationContext AuthenticationContext(this HttpContext httpContext) => Core.AuthenticationContext.Current(httpContext);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Tgstation.Server.Api.Models;
|
||||
|
||||
namespace Tgstation.Server.Host.Core
|
||||
{
|
||||
interface IAuthenticationContext : IDisposable
|
||||
{
|
||||
User User { get; }
|
||||
|
||||
InstanceUser InstanceUser { get; }
|
||||
|
||||
IAuthenticationContext Clone();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user