mirror of
https://github.com/tgstation/tgstation-server.git
synced 2026-08-26 22:48:20 +01:00
Fix release build, generate token signing key each run
This commit is contained in:
@@ -110,7 +110,7 @@ namespace Tgstation.Server.Api
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Construct and validates <see cref="ApiHeaders"/> from a <see cref="IHeaderDictionary"/>
|
||||
/// Construct and validates <see cref="ApiHeaders"/> from a set of <paramref name="requestHeaders"/>
|
||||
/// </summary>
|
||||
/// <param name="requestHeaders">The <see cref="RequestHeaders"/> containing the <see cref="ApiHeaders"/></param>
|
||||
public ApiHeaders(RequestHeaders requestHeaders)
|
||||
|
||||
@@ -25,16 +25,6 @@ namespace Tgstation.Server.Api.Rights
|
||||
{ RightsType.InstanceUser, typeof(InstanceUserRights) }
|
||||
};
|
||||
|
||||
static readonly IReadOnlyDictionary<Type, RightsType> rightMap = CreateRightsMap();
|
||||
|
||||
static IReadOnlyDictionary<Type, RightsType> CreateRightsMap()
|
||||
{
|
||||
var dic = new Dictionary<Type, RightsType>();
|
||||
foreach (var I in typeMap)
|
||||
dic.Add(I.Value, I.Key);
|
||||
return dic;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Map a given <paramref name="rightsType"/> to its respective <see cref="Enum"/> <see cref="Type"/>
|
||||
/// </summary>
|
||||
@@ -42,7 +32,20 @@ namespace Tgstation.Server.Api.Rights
|
||||
/// <returns>The <see cref="Enum"/> <see cref="Type"/> of the given <paramref name="rightsType"/></returns>
|
||||
public static Type RightToType(RightsType rightsType) => typeMap[rightsType];
|
||||
|
||||
/// <summary>
|
||||
/// Gets the role claim name used for a given <paramref name="right"/>
|
||||
/// </summary>
|
||||
/// <typeparam name="TRight">The <see cref="RightsType"/></typeparam>
|
||||
/// <param name="right">The <typeparamref name="TRight"/></param>
|
||||
/// <returns>A <see cref="string"/> representing the claim role name</returns>
|
||||
public static string RoleName<TRight>(TRight right) => String.Concat(typeof(TRight).Name, '.', right.ToString());
|
||||
|
||||
/// <summary>
|
||||
/// Gets the role claim name used for a given <paramref name="rightsType"/> and <paramref name="right"/>
|
||||
/// </summary>
|
||||
/// <param name="rightsType">The <see cref="RightsType"/></param>
|
||||
/// <param name="right">The right value</param>
|
||||
/// <returns>A <see cref="string"/> representing the claim role name</returns>
|
||||
public static string RoleName(RightsType rightsType, int right)
|
||||
{
|
||||
var enumType = typeMap[rightsType];
|
||||
|
||||
@@ -1,18 +0,0 @@
|
||||
namespace Tgstation.Server.Host.Configuration
|
||||
{
|
||||
/// <summary>
|
||||
/// General configuration options
|
||||
/// </summary>
|
||||
sealed class GeneralConfiguration
|
||||
{
|
||||
/// <summary>
|
||||
/// The key for the <see cref="Microsoft.Extensions.Configuration.IConfigurationSection"/> the <see cref="GeneralConfiguration"/> resides in
|
||||
/// </summary>
|
||||
public const string Section = "General";
|
||||
|
||||
/// <summary>
|
||||
/// The string used to validate JWTs
|
||||
/// </summary>
|
||||
public string TokenSigningKey { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -17,18 +17,38 @@ using Tgstation.Server.Host.Security;
|
||||
|
||||
namespace Tgstation.Server.Host.Controllers
|
||||
{
|
||||
/// <summary>
|
||||
/// A <see cref="Controller"/> for API functions
|
||||
/// </summary>
|
||||
[Produces(ApiHeaders.ApplicationJson)]
|
||||
[Consumes(ApiHeaders.ApplicationJson)]
|
||||
public abstract class ApiController : Controller
|
||||
{
|
||||
/// <summary>
|
||||
/// The <see cref="ApiHeaders"/> for the operation
|
||||
/// </summary>
|
||||
protected ApiHeaders ApiHeaders { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// The <see cref="IDatabaseContext"/> for the operation
|
||||
/// </summary>
|
||||
protected IDatabaseContext DatabaseContext { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The <see cref="IAuthenticationContext"/> for the operation
|
||||
/// </summary>
|
||||
protected IAuthenticationContext AuthenticationContext { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The <see cref="Instance"/> for the operation
|
||||
/// </summary>
|
||||
protected Instance Instance { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Runs after a <see cref="Api.Models.Token"/> has been validated. Creates the <see cref="IAuthenticationContext"/> for the <see cref="ControllerBase.Request"/>
|
||||
/// </summary>
|
||||
/// <param name="context">The <see cref="TokenValidatedContext"/> for the operation</param>
|
||||
/// <returns>A <see cref="Task"/> representing the running operation</returns>
|
||||
public static async Task OnTokenValidated(TokenValidatedContext context)
|
||||
{
|
||||
var databaseContext = context.HttpContext.RequestServices.GetRequiredService<IDatabaseContext>();
|
||||
@@ -75,6 +95,11 @@ namespace Tgstation.Server.Host.Controllers
|
||||
context.Principal.AddIdentity(new ClaimsIdentity(claims));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Construct an <see cref="ApiController"/>
|
||||
/// </summary>
|
||||
/// <param name="databaseContext">The value of <see cref="DatabaseContext"/></param>
|
||||
/// <param name="authenticationContextFactory">The <see cref="IAuthenticationContextFactory"/> for the <see cref="ApiController"/></param>
|
||||
public ApiController(IDatabaseContext databaseContext, IAuthenticationContextFactory authenticationContextFactory)
|
||||
{
|
||||
DatabaseContext = databaseContext ?? throw new ArgumentNullException(nameof(databaseContext));
|
||||
@@ -84,6 +109,7 @@ namespace Tgstation.Server.Host.Controllers
|
||||
Instance = AuthenticationContext?.InstanceUser?.Instance;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override async Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next)
|
||||
{
|
||||
//validate the headers
|
||||
|
||||
@@ -4,15 +4,18 @@ using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Tgstation.Server.Api;
|
||||
using Tgstation.Server.Host.Core;
|
||||
using Tgstation.Server.Host.Models;
|
||||
using Tgstation.Server.Host.Security;
|
||||
|
||||
namespace Tgstation.Server.Host.Controllers
|
||||
{
|
||||
/// <summary>
|
||||
/// Main <see cref="ApiController"/> for the <see cref="Application"/>
|
||||
/// </summary>
|
||||
[Route("/")]
|
||||
public sealed class HomeController : ApiController
|
||||
{
|
||||
@@ -25,21 +28,38 @@ namespace Tgstation.Server.Host.Controllers
|
||||
/// </summary>
|
||||
readonly ISystemIdentityFactory systemIdentityFactory;
|
||||
/// <summary>
|
||||
/// The <see cref="IPasswordHasher"/> for the <see cref="HomeController"/>
|
||||
/// The <see cref="IPasswordHasher{TUser}"/> for the <see cref="HomeController"/>
|
||||
/// </summary>
|
||||
readonly IPasswordHasher<User> passwordHasher;
|
||||
readonly ICryptographySuite cryptographySuite;
|
||||
|
||||
public HomeController(IDatabaseContext databaseContext, IAuthenticationContextFactory authenticationContextFactory, ITokenFactory tokenFactory, ISystemIdentityFactory systemIdentityFactory, IPasswordHasher<User> passwordHasher) : base(databaseContext, authenticationContextFactory)
|
||||
/// <summary>
|
||||
/// Construct a <see cref="HomeController"/>
|
||||
/// </summary>
|
||||
/// <param name="databaseContext">The <see cref="IDatabaseContext"/> for the <see cref="ApiController"/></param>
|
||||
/// <param name="authenticationContextFactory">The <see cref="IAuthenticationContextFactory"/> for the <see cref="ApiController"/></param>
|
||||
/// <param name="tokenFactory">The value of <see cref="tokenFactory"/></param>
|
||||
/// <param name="systemIdentityFactory">The value of <see cref="systemIdentityFactory"/></param>
|
||||
/// <param name="cryptographySuite">The value of <see cref="cryptographySuite"/></param>
|
||||
public HomeController(IDatabaseContext databaseContext, IAuthenticationContextFactory authenticationContextFactory, ITokenFactory tokenFactory, ISystemIdentityFactory systemIdentityFactory, ICryptographySuite cryptographySuite) : base(databaseContext, authenticationContextFactory)
|
||||
{
|
||||
this.tokenFactory = tokenFactory ?? throw new ArgumentNullException(nameof(tokenFactory));
|
||||
this.systemIdentityFactory = systemIdentityFactory ?? throw new ArgumentNullException(nameof(systemIdentityFactory));
|
||||
this.passwordHasher = passwordHasher ?? throw new ArgumentNullException(nameof(passwordHasher));
|
||||
this.cryptographySuite = cryptographySuite ?? throw new ArgumentNullException(nameof(cryptographySuite));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the version of the <see cref="Application"/>
|
||||
/// </summary>
|
||||
/// <returns><see cref="Application.Version"/></returns>
|
||||
[Authorize]
|
||||
[HttpGet]
|
||||
public JsonResult Home() => Json(Assembly.GetExecutingAssembly().GetName().Version);
|
||||
public JsonResult Home() => Json(Application.Version);
|
||||
|
||||
/// <summary>
|
||||
/// Attempt to authenticate a <see cref="User"/> using <see cref="ApiController.ApiHeaders"/>
|
||||
/// </summary>
|
||||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> for the operation</param>
|
||||
/// <returns>A <see cref="Task{TResult}"/> resulting in the <see cref="IActionResult"/> of the operation</returns>
|
||||
[HttpPost]
|
||||
public async Task<IActionResult> CreateToken(CancellationToken cancellationToken)
|
||||
{
|
||||
@@ -56,17 +76,15 @@ namespace Tgstation.Server.Host.Controllers
|
||||
if (user == null)
|
||||
return Unauthorized();
|
||||
|
||||
if(user.PasswordHash != null)
|
||||
if (user.PasswordHash != null)
|
||||
{
|
||||
var hashResult = passwordHasher.VerifyHashedPassword(user, user.PasswordHash, ApiHeaders.Password);
|
||||
switch (hashResult)
|
||||
var originalHash = user.PasswordHash;
|
||||
if (!cryptographySuite.CheckUserPassword(user, ApiHeaders.Password))
|
||||
return Unauthorized();
|
||||
if (user.PasswordHash != originalHash)
|
||||
{
|
||||
case PasswordVerificationResult.Failed:
|
||||
return Unauthorized();
|
||||
case PasswordVerificationResult.SuccessRehashNeeded:
|
||||
user.PasswordHash = passwordHasher.HashPassword(user, ApiHeaders.Password);
|
||||
await DatabaseContext.Save(cancellationToken).ConfigureAwait(false);
|
||||
break;
|
||||
DatabaseContext.Users.Attach(user);
|
||||
await DatabaseContext.Save(cancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
else
|
||||
|
||||
@@ -8,24 +8,64 @@ using Tgstation.Server.Host.Security;
|
||||
|
||||
namespace Tgstation.Server.Host.Controllers
|
||||
{
|
||||
public abstract class ModelController<TModel> : ApiController
|
||||
/// <summary>
|
||||
/// An <see cref="ApiController"/> representing a <typeparamref name="TModel"/>
|
||||
/// </summary>
|
||||
/// <typeparam name="TModel">The model being represented</typeparam>
|
||||
public abstract class ModelController<TModel> : ApiController where TModel : class
|
||||
{
|
||||
/// <summary>
|
||||
/// The <see cref="ModelAttribute"/> of the <typeparamref name="TModel"/>
|
||||
/// </summary>
|
||||
protected static readonly ModelAttribute ModelAttribute = (ModelAttribute)typeof(TModel).GetCustomAttributes(typeof(ModelAttribute), true).First();
|
||||
|
||||
/// <summary>
|
||||
/// Construct a <see cref="ModelController{TModel}"/>
|
||||
/// </summary>
|
||||
/// <param name="databaseContext">The <see cref="IDatabaseContext"/> for the <see cref="ApiController"/></param>
|
||||
/// <param name="authenticationContextFactory">The <see cref="IAuthenticationContextFactory"/> for the <see cref="ApiController"/></param>
|
||||
public ModelController(IDatabaseContext databaseContext, IAuthenticationContextFactory authenticationContextFactory) : base(databaseContext, authenticationContextFactory) { }
|
||||
|
||||
/// <summary>
|
||||
/// Attempt to create a <paramref name="model"/>
|
||||
/// </summary>
|
||||
/// <param name="model">The <typeparamref name="TModel"/> being created</param>
|
||||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> for the operation</param>
|
||||
/// <returns>A <see cref="Task{TResult}"/> resulting in the <see cref="IActionResult"/> of the operation</returns>
|
||||
[HttpPut]
|
||||
public virtual Task<IActionResult> Create([FromBody]TModel model, CancellationToken cancellationToken) => Task.FromResult((IActionResult)NotFound());
|
||||
|
||||
/// <summary>
|
||||
/// Attempt to read a <typeparamref name="TModel"/>
|
||||
/// </summary>
|
||||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> for the operation</param>
|
||||
/// <returns>A <see cref="Task{TResult}"/> resulting in the <see cref="IActionResult"/> of the operation</returns>
|
||||
[HttpGet]
|
||||
public virtual Task<IActionResult> Read(CancellationToken cancellationToken) => Task.FromResult((IActionResult)NotFound());
|
||||
|
||||
/// <summary>
|
||||
/// Attempt to update a <paramref name="model"/>
|
||||
/// </summary>
|
||||
/// <param name="model">The <typeparamref name="TModel"/> being updated</param>
|
||||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> for the operation</param>
|
||||
/// <returns>A <see cref="Task{TResult}"/> resulting in the <see cref="IActionResult"/> of the operation</returns>
|
||||
[HttpPost]
|
||||
public virtual Task<IActionResult> Update([FromBody]TModel model, CancellationToken cancellationToken) => Task.FromResult((IActionResult)NotFound());
|
||||
|
||||
/// <summary>
|
||||
/// Attempt to delete a <paramref name="model"/>
|
||||
/// </summary>
|
||||
/// <param name="model">The <typeparamref name="TModel"/> being deleted</param>
|
||||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> for the operation</param>
|
||||
/// <returns>A <see cref="Task{TResult}"/> resulting in the <see cref="IActionResult"/> of the operation</returns>
|
||||
[HttpDelete]
|
||||
public virtual Task<IActionResult> Delete([FromBody]TModel model, CancellationToken cancellationToken) => Task.FromResult((IActionResult)NotFound());
|
||||
|
||||
/// <summary>
|
||||
/// Attempt to list entries of the <typeparamref name="TModel"/>
|
||||
/// </summary>
|
||||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> for the operation</param>
|
||||
/// <returns>A <see cref="Task{TResult}"/> resulting in the <see cref="IActionResult"/> of the operation</returns>
|
||||
[HttpGet("/List")]
|
||||
public virtual Task<IActionResult> List(CancellationToken cancellationToken) => Task.FromResult((IActionResult)NotFound());
|
||||
}
|
||||
|
||||
@@ -2,22 +2,15 @@
|
||||
using Microsoft.AspNetCore.Authentication.JwtBearer;
|
||||
using Microsoft.AspNetCore.Builder;
|
||||
using Microsoft.AspNetCore.Hosting;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Identity;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.IdentityModel.Tokens;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.IdentityModel.Tokens.Jwt;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Security.Claims;
|
||||
using System.Text;
|
||||
using Tgstation.Server.Api;
|
||||
using Tgstation.Server.Api.Rights;
|
||||
using Tgstation.Server.Host.Configuration;
|
||||
using Tgstation.Server.Host.Controllers;
|
||||
using Tgstation.Server.Host.Models;
|
||||
@@ -30,6 +23,11 @@ namespace Tgstation.Server.Host.Core
|
||||
/// </summary>
|
||||
sealed class Application
|
||||
{
|
||||
/// <summary>
|
||||
/// The version of the <see cref="Application"/>
|
||||
/// </summary>
|
||||
public static readonly Version Version = Assembly.GetExecutingAssembly().GetName().Version;
|
||||
|
||||
/// <summary>
|
||||
/// The <see cref="IConfiguration"/> for the <see cref="Application"/>
|
||||
/// </summary>
|
||||
@@ -64,17 +62,9 @@ namespace Tgstation.Server.Host.Core
|
||||
var workingDir = Environment.CurrentDirectory;
|
||||
var databaseConfigurationSection = configuration.GetSection(DatabaseConfiguration.Section);
|
||||
services.Configure<DatabaseConfiguration>(databaseConfigurationSection);
|
||||
var generalConfigSection = configuration.GetSection(GeneralConfiguration.Section);
|
||||
services.Configure<GeneralConfiguration>(generalConfigSection);
|
||||
|
||||
services.AddMvc();
|
||||
services.AddOptions();
|
||||
|
||||
var signingKey = generalConfigSection.Get<GeneralConfiguration>().TokenSigningKey;
|
||||
|
||||
if (signingKey == "default")
|
||||
throw new InvalidOperationException("Do not use the default signing key!");
|
||||
|
||||
services.AddOptions();
|
||||
|
||||
const string scheme = "JwtBearer";
|
||||
services.AddAuthentication((options) =>
|
||||
{
|
||||
@@ -85,7 +75,7 @@ namespace Tgstation.Server.Host.Core
|
||||
jwtBearerOptions.TokenValidationParameters = new TokenValidationParameters
|
||||
{
|
||||
ValidateIssuerSigningKey = true,
|
||||
IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(signingKey)),
|
||||
IssuerSigningKey = new SymmetricSecurityKey(TokenFactory.TokenSigningKey),
|
||||
|
||||
ValidateIssuer = true,
|
||||
ValidIssuer = TokenFactory.TokenIssuer,
|
||||
@@ -105,7 +95,9 @@ namespace Tgstation.Server.Host.Core
|
||||
OnTokenValidated = ApiController.OnTokenValidated
|
||||
};
|
||||
});
|
||||
JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Clear(); //fucking converts 'sub' to M$ bs
|
||||
JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Clear(); //fucking converts 'sub' to M$ bs
|
||||
|
||||
services.AddMvc();
|
||||
|
||||
var databaseConfiguration = databaseConfigurationSection.Get<DatabaseConfiguration>();
|
||||
void ConfigureDatabase(DbContextOptionsBuilder builder)
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
@@ -18,6 +17,7 @@ namespace Tgstation.Server.Host.Security
|
||||
/// The <see cref="ISystemIdentityFactory"/> for the <see cref="AuthenticationContextFactory"/>
|
||||
/// </summary>
|
||||
readonly ISystemIdentityFactory systemIdentityFactory;
|
||||
|
||||
/// <summary>
|
||||
/// The <see cref="IDatabaseContext"/> for the <see cref="AuthenticationContextFactory"/>
|
||||
/// </summary>
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
using Microsoft.AspNetCore.Identity;
|
||||
using System;
|
||||
using System.Security.Cryptography;
|
||||
using System.Text;
|
||||
using Tgstation.Server.Host.Models;
|
||||
|
||||
namespace Tgstation.Server.Host.Security
|
||||
@@ -10,31 +9,16 @@ namespace Tgstation.Server.Host.Security
|
||||
sealed class CryptographySuite : ICryptographySuite
|
||||
{
|
||||
/// <summary>
|
||||
/// The length of secure strings used in the application
|
||||
/// Generates a secure set of <see cref="byte"/>s
|
||||
/// </summary>
|
||||
public const int SecureStringLength = 40;
|
||||
|
||||
/// <summary>
|
||||
/// Generates a secure ascii <see cref="string"/> of length <see cref="SecureStringLength"/>
|
||||
/// </summary>
|
||||
/// <returns>A secure ascii <see cref="string"/> of length <see cref="SecureStringLength"/></returns>
|
||||
static string GenerateSecureString()
|
||||
/// <returns>A secure set of <see cref="byte"/>s</returns>
|
||||
public static byte[] GetSecureBytes(int amount)
|
||||
{
|
||||
using (var rng = new RNGCryptoServiceProvider())
|
||||
{
|
||||
var byt = new byte[1];
|
||||
var result = new StringBuilder
|
||||
{
|
||||
Capacity = SecureStringLength
|
||||
};
|
||||
while (result.Length < SecureStringLength)
|
||||
{
|
||||
rng.GetBytes(byt);
|
||||
var chr = (char)byt[0];
|
||||
if (Char.IsLetterOrDigit(chr))
|
||||
result.Append(chr);
|
||||
}
|
||||
return result.ToString();
|
||||
var byt = new byte[amount];
|
||||
rng.GetBytes(byt);
|
||||
return byt;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -58,5 +42,19 @@ namespace Tgstation.Server.Host.Security
|
||||
throw new ArgumentNullException(nameof(newPassword));
|
||||
user.PasswordHash = passwordHasher.HashPassword(user, newPassword);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public bool CheckUserPassword(User user, string password)
|
||||
{
|
||||
switch(passwordHasher.VerifyHashedPassword(user, user.PasswordHash, password))
|
||||
{
|
||||
case PasswordVerificationResult.Failed:
|
||||
return false;
|
||||
case PasswordVerificationResult.SuccessRehashNeeded:
|
||||
user.PasswordHash = passwordHasher.HashPassword(user, password);
|
||||
break;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,6 +19,11 @@ namespace Tgstation.Server.Host.Security
|
||||
/// </summary>
|
||||
InstanceUser InstanceUser { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Get the value of a given <paramref name="rightsType"/>
|
||||
/// </summary>
|
||||
/// <param name="rightsType">The <see cref="RightsType"/> of the right to get</param>
|
||||
/// <returns>The value of <paramref name="rightsType"/>. Note that if <see cref="InstanceUser"/> is <see langword="null"/> all <see cref="Instance"/> based rights will return 0</returns>
|
||||
int GetRight(RightsType rightsType);
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -8,8 +8,18 @@ namespace Tgstation.Server.Host.Security
|
||||
/// </summary>
|
||||
public interface IAuthenticationContextFactory
|
||||
{
|
||||
/// <summary>
|
||||
/// The <see cref="IAuthenticationContext"/> the <see cref="IAuthenticationContextFactory"/> created
|
||||
/// </summary>
|
||||
IAuthenticationContext CurrentAuthenticationContext { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Create an <see cref="IAuthenticationContext"/> to populate <see cref="CurrentAuthenticationContext"/>
|
||||
/// </summary>
|
||||
/// <param name="userId">The <see cref="Api.Models.Internal.User.Id"/> of the <see cref="IAuthenticationContext.User"/></param>
|
||||
/// <param name="instanceId">The <see cref="Api.Models.Instance.Id"/> of the operation</param>
|
||||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> for the operation</param>
|
||||
/// <returns>A <see cref="Task"/> representing the running operation</returns>
|
||||
Task CreateAuthenticationContext(long userId, long? instanceId, CancellationToken cancellationToken);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,7 +5,7 @@ namespace Tgstation.Server.Host.Security
|
||||
/// <summary>
|
||||
/// Contains various cryptographic functions
|
||||
/// </summary>
|
||||
interface ICryptographySuite
|
||||
public interface ICryptographySuite
|
||||
{
|
||||
/// <summary>
|
||||
/// Sets a <see cref="User.PasswordHash"/> for a given <paramref name="user"/>
|
||||
@@ -13,5 +13,13 @@ namespace Tgstation.Server.Host.Security
|
||||
/// <param name="user">The <see cref="User"/> whos <see cref="User.PasswordHash"/> is to be set</param>
|
||||
/// <param name="newPassword">The new password for the <see cref="User"/></param>
|
||||
void SetUserPassword(User user, string newPassword);
|
||||
|
||||
/// <summary>
|
||||
/// Checks a given <paramref name="password"/> matches a given <paramref name="user"/>'s <see cref="User.PasswordHash"/>. This may result in <see cref="User.PasswordHash"/> being modified and this should be persisted
|
||||
/// </summary>
|
||||
/// <param name="user">The <see cref="User"/> to check</param>
|
||||
/// <param name="password">The password to check</param>
|
||||
/// <returns><see langword="true"/> if <paramref name="password"/> matches the hash, <see langword="false"/> otherwise</returns>
|
||||
bool CheckUserPassword(User user, string password);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
using Microsoft.Extensions.Options;
|
||||
using Microsoft.IdentityModel.Tokens;
|
||||
using Microsoft.IdentityModel.Tokens;
|
||||
using System;
|
||||
using System.Globalization;
|
||||
using System.IdentityModel.Tokens.Jwt;
|
||||
@@ -7,7 +6,6 @@ using System.Reflection;
|
||||
using System.Security.Claims;
|
||||
using System.Text;
|
||||
using Tgstation.Server.Api.Models;
|
||||
using Tgstation.Server.Host.Configuration;
|
||||
|
||||
namespace Tgstation.Server.Host.Security
|
||||
{
|
||||
@@ -16,17 +14,7 @@ namespace Tgstation.Server.Host.Security
|
||||
{
|
||||
public static readonly string TokenAudience = typeof(Token).Assembly.GetName().Name;
|
||||
public static readonly string TokenIssuer = Assembly.GetExecutingAssembly().GetName().Name;
|
||||
|
||||
/// <summary>
|
||||
/// The <see cref="GeneralConfiguration"/> for the <see cref="TokenFactory"/>
|
||||
/// </summary>
|
||||
readonly GeneralConfiguration generalConfiguration;
|
||||
|
||||
/// <summary>
|
||||
/// Construct a <see cref="TokenFactory"/>
|
||||
/// </summary>
|
||||
/// <param name="generalConfigurationOptions">The <see cref="IOptions{TOptions}"/> containing the value of <see cref="generalConfiguration"/></param>
|
||||
public TokenFactory(IOptions<GeneralConfiguration> generalConfigurationOptions) => generalConfiguration = generalConfigurationOptions?.Value ?? throw new ArgumentNullException(nameof(generalConfigurationOptions));
|
||||
public static readonly byte[] TokenSigningKey = CryptographySuite.GetSecureBytes(256);
|
||||
|
||||
/// <inheritdoc />
|
||||
public Token CreateToken(Models.User user)
|
||||
@@ -43,7 +31,7 @@ namespace Tgstation.Server.Host.Security
|
||||
new Claim(JwtRegisteredClaimNames.Aud, TokenAudience)
|
||||
};
|
||||
|
||||
var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(generalConfiguration.TokenSigningKey));
|
||||
var key = new SymmetricSecurityKey(TokenSigningKey);
|
||||
|
||||
var token = new JwtSecurityToken(new JwtHeader(new SigningCredentials(key, SecurityAlgorithms.HmacSha256)), new JwtPayload(claims));
|
||||
return new Token { Bearer = new JwtSecurityTokenHandler().WriteToken(token) };
|
||||
|
||||
@@ -19,15 +19,11 @@
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<None Remove="appsettings.Development.json" />
|
||||
<None Remove="appsettings.Docker.json" />
|
||||
<None Remove="appsettings.json" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Content Include="appsettings.Development.json">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Include="appsettings.Docker.json">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
|
||||
@@ -17,9 +17,6 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"General": {
|
||||
"TokenSigningKey": "Default"
|
||||
},
|
||||
"Database": {
|
||||
"DatabaseType": "Sqlite",
|
||||
"ConnectionString": "Fake"
|
||||
|
||||
Reference in New Issue
Block a user