Preliminary AuthenticationContext stuff

This commit is contained in:
Cyberboss
2018-04-07 01:00:27 -04:00
parent 267cecab91
commit 21ff43c1db
6 changed files with 218 additions and 1 deletions
+109
View File
@@ -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();
}
}