diff options
Diffstat (limited to 'server/src/Utilities')
| -rw-r--r-- | server/src/Utilities/BasicAuthenticationAttribute.cs | 39 | ||||
| -rw-r--r-- | server/src/Utilities/BasicAuthenticationHandler.cs | 79 | ||||
| -rw-r--r-- | server/src/Utilities/ConfigurationExtensions.cs | 88 | ||||
| -rw-r--r-- | server/src/Utilities/GithubAuthenticationHelpers.cs | 84 | ||||
| -rw-r--r-- | server/src/Utilities/QuartzJsonSerializer.cs | 16 | ||||
| -rw-r--r-- | server/src/Utilities/SwaggerDefaultValues.cs | 58 | ||||
| -rw-r--r-- | server/src/Utilities/SwaggerGenOptionsExtensions.cs | 43 |
7 files changed, 0 insertions, 407 deletions
diff --git a/server/src/Utilities/BasicAuthenticationAttribute.cs b/server/src/Utilities/BasicAuthenticationAttribute.cs deleted file mode 100644 index 0bfd007..0000000 --- a/server/src/Utilities/BasicAuthenticationAttribute.cs +++ /dev/null @@ -1,39 +0,0 @@ -using System.Net.Http.Headers; -using Microsoft.AspNetCore.Mvc.Filters; - -namespace IOL.GreatOffice.Api.Utilities; - -public class BasicAuthenticationAttribute : TypeFilterAttribute -{ - public BasicAuthenticationAttribute(string claimPermission) : base(typeof(BasicAuthenticationFilter)) { - Arguments = new object[] { - new Claim(claimPermission, "True") - }; - } -} - -public class BasicAuthenticationFilter : IAuthorizationFilter -{ - private readonly Claim _claim; - - public BasicAuthenticationFilter(Claim claim) { - _claim = claim; - } - - public void OnAuthorization(AuthorizationFilterContext context) { - if (!context.HttpContext.Request.Headers.ContainsKey("Authorization")) return; - try { - var authHeader = AuthenticationHeaderValue.Parse(context.HttpContext.Request.Headers["Authorization"]); - if (authHeader.Parameter is null) { - context.Result = new ForbidResult(AppConstants.BASIC_AUTH_SCHEME); - } - - var hasClaim = context.HttpContext.User.Claims.Any(c => c.Type == _claim.Type && c.Value == _claim.Value); - if (!hasClaim) { - context.Result = new ForbidResult(AppConstants.BASIC_AUTH_SCHEME); - } - } catch { - // ignore - } - } -} diff --git a/server/src/Utilities/BasicAuthenticationHandler.cs b/server/src/Utilities/BasicAuthenticationHandler.cs deleted file mode 100644 index 6138193..0000000 --- a/server/src/Utilities/BasicAuthenticationHandler.cs +++ /dev/null @@ -1,79 +0,0 @@ -using System.Net.Http.Headers; -using System.Text; -using System.Text.Encodings.Web; -using Microsoft.Extensions.Options; - -namespace IOL.GreatOffice.Api.Utilities; - -public class BasicAuthenticationHandler : AuthenticationHandler<AuthenticationSchemeOptions> -{ - private readonly AppDbContext _context; - private readonly AppConfiguration _configuration; - private readonly ILogger<BasicAuthenticationHandler> _logger; - - public BasicAuthenticationHandler( - IOptionsMonitor<AuthenticationSchemeOptions> options, - ILoggerFactory logger, - UrlEncoder encoder, - ISystemClock clock, - AppDbContext context, - VaultService vaultService - ) : - base(options, logger, encoder, clock) { - _context = context; - _configuration = vaultService.GetCurrentAppConfiguration(); - _logger = logger.CreateLogger<BasicAuthenticationHandler>(); - } - - protected override Task<AuthenticateResult> HandleAuthenticateAsync() { - var endpoint = Context.GetEndpoint(); - if (endpoint?.Metadata.GetMetadata<IAllowAnonymous>() != null) - return Task.FromResult(AuthenticateResult.NoResult()); - - if (!Request.Headers.ContainsKey("Authorization")) - return Task.FromResult(AuthenticateResult.Fail("Missing Authorization Header")); - - try { - var tokenEntropy = _configuration.APP_AES_KEY; - if (tokenEntropy.IsNullOrWhiteSpace()) { - _logger.LogWarning("No token entropy is available in env:TOKEN_ENTROPY, Basic auth is disabled"); - return Task.FromResult(AuthenticateResult.Fail("Invalid Authorization Header")); - } - - var authHeader = AuthenticationHeaderValue.Parse(Request.Headers["Authorization"]); - if (authHeader.Parameter == null) return Task.FromResult(AuthenticateResult.Fail("Invalid Authorization Header")); - var credentialBytes = Convert.FromBase64String(authHeader.Parameter); - var decryptedString = Encoding.UTF8.GetString(credentialBytes).DecryptWithAes(tokenEntropy); - var tokenIsGuid = Guid.TryParse(decryptedString, out var tokenId); - - if (!tokenIsGuid) { - return Task.FromResult(AuthenticateResult.Fail("Invalid Authorization Header")); - } - - var token = _context.AccessTokens.Include(c => c.User).SingleOrDefault(c => c.Id == tokenId); - if (token == default) { - return Task.FromResult(AuthenticateResult.Fail("Invalid Authorization Header: Not Found")); - } - - if (token.HasExpired) { - return Task.FromResult(AuthenticateResult.Fail("Invalid Authorization Header: Expired")); - } - - var permissions = new List<Claim>() { - new(AppConstants.TOKEN_ALLOW_READ, token.AllowRead.ToString()), - new(AppConstants.TOKEN_ALLOW_UPDATE, token.AllowUpdate.ToString()), - new(AppConstants.TOKEN_ALLOW_CREATE, token.AllowCreate.ToString()), - new(AppConstants.TOKEN_ALLOW_DELETE, token.AllowDelete.ToString()), - }; - var claims = token.User.DefaultClaims().Concat(permissions); - var identity = new ClaimsIdentity(claims, AppConstants.BASIC_AUTH_SCHEME); - var principal = new ClaimsPrincipal(identity); - var ticket = new AuthenticationTicket(principal, AppConstants.BASIC_AUTH_SCHEME); - - return Task.FromResult(AuthenticateResult.Success(ticket)); - } catch (Exception e) { - _logger.LogError(e, $"An exception occured when challenging {AppConstants.BASIC_AUTH_SCHEME}"); - return Task.FromResult(AuthenticateResult.Fail("Invalid Authorization Header")); - } - } -} diff --git a/server/src/Utilities/ConfigurationExtensions.cs b/server/src/Utilities/ConfigurationExtensions.cs deleted file mode 100644 index 405c702..0000000 --- a/server/src/Utilities/ConfigurationExtensions.cs +++ /dev/null @@ -1,88 +0,0 @@ -namespace IOL.GreatOffice.Api.Utilities; - -public static class ConfigurationExtensions -{ - public static string GetAppDatabaseConnectionString(this IConfiguration config, AppConfiguration configuration) { - var host = configuration.DB_HOST; - var port = configuration.DB_PORT; - var database = configuration.DB_NAME; - var user = configuration.DB_USER; - var password = configuration.DB_PASSWORD; - - var res = ""; - if (config.GetValue<string>("ASPNETCORE_ENVIRONMENT") == "Development") { - res = $"Server={host};Port={port};Database={database};User Id={user};Password={password};Include Error Detail=true"; - } else { - res = $"Server={host};Port={port};Database={database};User Id={user};Password={password}"; - } - - Log.Debug("Using app database connection string: " + res); - return res; - } - - public static string GetAppDatabaseConnectionString(this IConfiguration config, Func<AppConfiguration> configuration) { - var _configuration = configuration(); - var host = _configuration.DB_HOST; - var port = _configuration.DB_PORT; - var database = _configuration.DB_NAME; - var user = _configuration.DB_USER; - var password = _configuration.DB_PASSWORD; - - var res = ""; - if (config.GetValue<string>("ASPNETCORE_ENVIRONMENT") == "Development") { - res = $"Server={host};Port={port};Database={database};User Id={user};Password={password};Include Error Detail=true"; - } else { - res = $"Server={host};Port={port};Database={database};User Id={user};Password={password}"; - } - - Log.Debug("Using app database connection string: " + res); - return res; - } - - public static string GetQuartzDatabaseConnectionString(this IConfiguration config, AppConfiguration configuration) { - var host = configuration.QUARTZ_DB_HOST; - var port = configuration.QUARTZ_DB_PORT; - var database = configuration.QUARTZ_DB_NAME; - var user = configuration.QUARTZ_DB_USER; - var password = configuration.QUARTZ_DB_PASSWORD; - - var res = ""; - if (config.GetValue<string>("ASPNETCORE_ENVIRONMENT") == "Development") { - res = $"Server={host};Port={port};Database={database};User Id={user};Password={password};Include Error Detail=true"; - } else { - res = $"Server={host};Port={port};Database={database};User Id={user};Password={password}"; - } - - Log.Debug("Using quartz database connection string: " + res); - return res; - } - - public static string GetQuartzDatabaseConnectionString(this IConfiguration config, Func<AppConfiguration> configuration) { - var _configuration = configuration(); - var host = _configuration.QUARTZ_DB_HOST; - var port = _configuration.QUARTZ_DB_PORT; - var database = _configuration.QUARTZ_DB_NAME; - var user = _configuration.QUARTZ_DB_USER; - var password = _configuration.QUARTZ_DB_PASSWORD; - - var res = ""; - if (config.GetValue<string>("ASPNETCORE_ENVIRONMENT") == "Development") { - res = $"Server={host};Port={port};Database={database};User Id={user};Password={password};Include Error Detail=true"; - } else { - res = $"Server={host};Port={port};Database={database};User Id={user};Password={password}"; - } - - Log.Debug("Using quartz database connection string: " + res); - return res; - } - - public static string GetVersion(this IConfiguration configuration) { - var versionFilePath = Path.Combine(AppPaths.AppData.HostPath, "version.txt"); - if (File.Exists(versionFilePath)) { - var versionText = File.ReadAllText(versionFilePath); - return versionText + "-" + configuration.GetValue<string>("ASPNETCORE_ENVIRONMENT"); - } - - return "unknown-" + configuration.GetValue<string>("ASPNETCORE_ENVIRONMENT"); - } -} diff --git a/server/src/Utilities/GithubAuthenticationHelpers.cs b/server/src/Utilities/GithubAuthenticationHelpers.cs deleted file mode 100644 index a4461d2..0000000 --- a/server/src/Utilities/GithubAuthenticationHelpers.cs +++ /dev/null @@ -1,84 +0,0 @@ -using Microsoft.AspNetCore.Authentication.OAuth; -using Npgsql; - -namespace IOL.GreatOffice.Api.Utilities; - -public static class GithubAuthenticationHelpers -{ - public static async Task HandleGithubTicketCreation(OAuthCreatingTicketContext context, IConfiguration configuration, AppConfiguration options) { - var githubId = context.Identity?.FindFirst(p => p.Type == ClaimTypes.NameIdentifier)?.Value; - var githubUsername = context.Identity?.FindFirst(p => p.Type == ClaimTypes.Name)?.Value; - var githubEmail = context.Identity?.FindFirst(p => p.Type == ClaimTypes.Email)?.Value; - - if (githubId.IsNullOrWhiteSpace() || githubUsername.IsNullOrWhiteSpace() || context.Identity == default) { - return; - } - - var claims = context.Identity.Claims.ToList(); - foreach (var claim in claims) { - context.Identity.RemoveClaim(claim); - } - - var connstring = configuration.GetAppDatabaseConnectionString(options); - var connection = new NpgsqlConnection(connstring); - - Log.Information($"Getting user mappings for github user: {githubId}"); - var getMappedUserQuery = @$"SELECT u.id,u.username FROM github_user_mappings INNER JOIN users u on u.id = github_user_mappings.user_id WHERE github_id='{githubId}'"; - await connection.OpenAsync(); - await using var getMappedUserCommand = new NpgsqlCommand(getMappedUserQuery, connection); - await using var reader = await getMappedUserCommand.ExecuteReaderAsync(); - var handled = false; - while (await reader.ReadAsync()) { - try { - var userId = reader.GetGuid(0); - var username = reader.GetString(1); - context.Identity.AddClaim(new Claim(AppClaims.USER_ID, userId.ToString())); - context.Identity.AddClaim(new Claim(AppClaims.NAME, username)); - if (context.AccessToken != default) { - context.Identity.AddClaim(new Claim(AppClaims.GITHUB_ACCESS_TOKEN, context.AccessToken ?? "")); - } - - Log.Information($"Found mapping for github id {githubId} mapped to user id {userId}"); - handled = true; - } catch (Exception e) { - Log.Error(e, "An exception occured when handling github user mappings"); - handled = false; - } - } - - await connection.CloseAsync(); - - if (!handled) { - var userId = Guid.NewGuid(); - - var insertUserQuery = $@"INSERT INTO users VALUES ('{userId}', '{githubUsername}', '', '{AppDateTime.UtcNow}')"; - await connection.OpenAsync(); - await using var insertUserCommand = new NpgsqlCommand(insertUserQuery, connection); - await insertUserCommand.ExecuteNonQueryAsync(); - await connection.CloseAsync(); - - var refreshTokenEncryptionKey = options.APP_AES_KEY; - string insertMappingQuery; - - if (context.RefreshToken.HasValue() && refreshTokenEncryptionKey.HasValue()) { - var encryptedRefreshToken = context.RefreshToken.EncryptWithAes(refreshTokenEncryptionKey); - insertMappingQuery = $@"INSERT INTO github_user_mappings VALUES ('{githubId}', '{userId}', '{githubEmail}', '{encryptedRefreshToken}')"; - } else { - insertMappingQuery = $@"INSERT INTO github_user_mappings VALUES ('{githubId}', '{userId}', '{githubEmail}', '')"; - } - - await connection.OpenAsync(); - await using var insertMappingCommand = new NpgsqlCommand(insertMappingQuery, connection); - await insertMappingCommand.ExecuteNonQueryAsync(); - await connection.CloseAsync(); - - context.Identity.AddClaim(new Claim(AppClaims.USER_ID, userId.ToString())); - context.Identity.AddClaim(new Claim(AppClaims.NAME, githubUsername)); - if (context.AccessToken != default) { - context.Identity.AddClaim(new Claim(AppClaims.GITHUB_ACCESS_TOKEN, context.AccessToken ?? "")); - } - - Log.Information($"Created mapping for github id {githubId} mapped to user id {userId}"); - } - } -} diff --git a/server/src/Utilities/QuartzJsonSerializer.cs b/server/src/Utilities/QuartzJsonSerializer.cs deleted file mode 100644 index 164a189..0000000 --- a/server/src/Utilities/QuartzJsonSerializer.cs +++ /dev/null @@ -1,16 +0,0 @@ -using Quartz.Spi; - -namespace IOL.GreatOffice.Api.Utilities; - -public class QuartzJsonSerializer : IObjectSerializer -{ - public void Initialize() { } - - public byte[] Serialize<T>(T obj) where T : class { - return JsonSerializer.SerializeToUtf8Bytes(obj); - } - - public T DeSerialize<T>(byte[] data) where T : class { - return JsonSerializer.Deserialize<T>(data); - } -} diff --git a/server/src/Utilities/SwaggerDefaultValues.cs b/server/src/Utilities/SwaggerDefaultValues.cs deleted file mode 100644 index 4b5c764..0000000 --- a/server/src/Utilities/SwaggerDefaultValues.cs +++ /dev/null @@ -1,58 +0,0 @@ -using Microsoft.AspNetCore.Mvc.ApiExplorer; -using Swashbuckle.AspNetCore.SwaggerGen; - -namespace IOL.GreatOffice.Api.Utilities; - -/// <summary> -/// Represents the Swagger/Swashbuckle operation filter used to document the implicit API version parameter. -/// </summary> -/// <remarks>This <see cref="IOperationFilter"/> is only required due to bugs in the <see cref="SwaggerGenerator"/>. -/// Once they are fixed and published, this class can be removed.</remarks> -public class SwaggerDefaultValues : IOperationFilter -{ - /// <summary> - /// Applies the filter to the specified operation using the given context. - /// </summary> - /// <param name="operation">The operation to apply the filter to.</param> - /// <param name="context">The current operation filter context.</param> - public void Apply(OpenApiOperation operation, OperationFilterContext context) { - var apiDescription = context.ApiDescription; - - operation.Deprecated |= apiDescription.IsDeprecated(); - - // REF: https://github.com/domaindrivendev/Swashbuckle.AspNetCore/issues/1752#issue-663991077 - foreach (var responseType in context.ApiDescription.SupportedResponseTypes) { - // REF: https://github.com/domaindrivendev/Swashbuckle.AspNetCore/blob/b7cf75e7905050305b115dd96640ddd6e74c7ac9/src/Swashbuckle.AspNetCore.SwaggerGen/SwaggerGenerator/SwaggerGenerator.cs#L383-L387 - var responseKey = responseType.IsDefaultResponse ? "default" : responseType.StatusCode.ToString(); - var response = operation.Responses[responseKey]; - - foreach (var contentType in response.Content.Keys) { - if (!responseType.ApiResponseFormats.Any(x => x.MediaType == contentType)) { - response.Content.Remove(contentType); - } - } - } - - if (operation.Parameters == null) { - return; - } - - // REF: https://github.com/domaindrivendev/Swashbuckle.AspNetCore/issues/412 - // REF: https://github.com/domaindrivendev/Swashbuckle.AspNetCore/pull/413 - foreach (var parameter in operation.Parameters) { - var description = apiDescription.ParameterDescriptions.First(p => p.Name == parameter.Name); - - if (parameter.Description == null) { - parameter.Description = description.ModelMetadata.Description; - } - - if (parameter.Schema.Default == null && description.DefaultValue != null) { - // REF: https://github.com/Microsoft/aspnet-api-versioning/issues/429#issuecomment-605402330 - var json = JsonSerializer.Serialize(description.DefaultValue, description.ModelMetadata.ModelType); - parameter.Schema.Default = OpenApiAnyFactory.CreateFromJson(json); - } - - parameter.Required |= description.IsRequired; - } - } -} diff --git a/server/src/Utilities/SwaggerGenOptionsExtensions.cs b/server/src/Utilities/SwaggerGenOptionsExtensions.cs deleted file mode 100644 index a2dcf7a..0000000 --- a/server/src/Utilities/SwaggerGenOptionsExtensions.cs +++ /dev/null @@ -1,43 +0,0 @@ -#nullable enable -using Microsoft.AspNetCore.Mvc.ApiExplorer; -using Microsoft.AspNetCore.Mvc.Controllers; -using Swashbuckle.AspNetCore.SwaggerGen; -using BaseRoute = IOL.GreatOffice.Api.Endpoints.V1.BaseRoute; - -namespace IOL.GreatOffice.Api.Utilities; - -public static class SwaggerGenOptionsExtensions -{ - /// <summary> - /// Updates Swagger document to support ApiEndpoints.<br/><br/> - /// For controllers inherited from <see cref="BaseRoute"/>:<br/> - /// - Replaces action Tag with <c>[namespace]</c><br/> - /// </summary> - public static void UseApiEndpoints(this SwaggerGenOptions options) { - options.TagActionsBy(EndpointNamespaceOrDefault); - } - - private static IList<string?> EndpointNamespaceOrDefault(ApiDescription api) { - if (api.ActionDescriptor is not ControllerActionDescriptor actionDescriptor) { - throw new InvalidOperationException($"Unable to determine tag for endpoint: {api.ActionDescriptor.DisplayName}"); - } - - if (actionDescriptor.ControllerTypeInfo.GetBaseTypesAndThis().Any(t => t == typeof(BaseRoute))) { - return new[] { - actionDescriptor.ControllerTypeInfo.Namespace?.Split('.').Last() - }; - } - - return new[] { - actionDescriptor.ControllerName - }; - } - - public static IEnumerable<Type> GetBaseTypesAndThis(this Type type) { - Type? current = type; - while (current != null) { - yield return current; - current = current.BaseType; - } - } -} |
