Skip to content

Commit

Permalink
Polish codes in UI project. (#143)
Browse files Browse the repository at this point in the history
Polish codes in UI project.
  • Loading branch information
mo-esmp authored Sep 13, 2024
1 parent 1fb2a7b commit 8fe464c
Show file tree
Hide file tree
Showing 30 changed files with 618 additions and 645 deletions.
4 changes: 2 additions & 2 deletions src/Serilog.Ui.Core/Attributes/CodeColumnAttribute.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,5 +12,5 @@ public class CodeColumnAttribute(CodeType codeType) : Attribute
/// <summary>
/// Gets the CodeType.
/// </summary>
public readonly CodeType CodeType = codeType;
}
public CodeType CodeType { get; } = codeType;
}
37 changes: 18 additions & 19 deletions src/Serilog.Ui.Core/Interfaces/IAuthorizationFilters.cs
Original file line number Diff line number Diff line change
@@ -1,28 +1,27 @@
using System.Threading.Tasks;

namespace Serilog.Ui.Core.Interfaces
namespace Serilog.Ui.Core.Interfaces;

/// <summary>
/// Authorization filter, used to authorize access to Serilog Ui pages.
/// Runs synchronous.
/// </summary>
public interface IUiAuthorizationFilter
{
/// <summary>
/// Authorization filter, used to authorize access to Serilog Ui pages.
/// Runs synchronous.
/// Authorizes a request sync.
/// </summary>
public interface IUiAuthorizationFilter
{
/// <summary>
/// Authorizes a request sync.
/// </summary>
bool Authorize();
}
bool Authorize();
}

/// <summary>
/// Authorization filter, used to authorize access to Serilog Ui pages.
/// Runs asynchronous.
/// </summary>
public interface IUiAsyncAuthorizationFilter
{
/// <summary>
/// Authorization filter, used to authorize access to Serilog Ui pages.
/// Runs asynchronous.
/// Authorizes a request async.
/// </summary>
public interface IUiAsyncAuthorizationFilter
{
/// <summary>
/// Authorizes a request async.
/// </summary>
Task<bool> AuthorizeAsync();
}
Task<bool> AuthorizeAsync();
}
67 changes: 30 additions & 37 deletions src/Serilog.Ui.Web/Authorization/AuthorizationFilterService.cs
Original file line number Diff line number Diff line change
@@ -1,48 +1,41 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
using Serilog.Ui.Core.Interfaces;
using Serilog.Ui.Core.Interfaces;

namespace Serilog.Ui.Web.Authorization
namespace Serilog.Ui.Web.Authorization;

internal sealed class AuthorizationFilterService(
IHttpContextAccessor httpContextAccessor,
IEnumerable<IUiAuthorizationFilter> syncFilters,
IEnumerable<IUiAsyncAuthorizationFilter> asyncFilters
) : IAuthorizationFilterService
{
internal sealed class AuthorizationFilterService(
IHttpContextAccessor httpContextAccessor,
IEnumerable<IUiAuthorizationFilter> syncFilters,
IEnumerable<IUiAsyncAuthorizationFilter> asyncFilters) : IAuthorizationFilterService
private readonly HttpContext _httpContext = Guard.Against.Null(httpContextAccessor.HttpContext);

public async Task CheckAccessAsync(Func<Task> onSuccess, Func<Task>? onFailure = null)
{
public async Task CheckAccessAsync(
Func<Task> onSuccess,
Func<Task>? onFailure = null)
bool accessCheck = await CanAccessAsync();
if (accessCheck)
{
var httpContext = httpContextAccessor.HttpContext;
if (httpContext is null) return;

var accessCheck = await CanAccessAsync();

if (accessCheck)
{
await onSuccess();
return;
}

httpContext.Response.StatusCode = (int)HttpStatusCode.Forbidden;
if (onFailure != null)
{
await onFailure.Invoke();
}
await onSuccess();
return;
}

private async Task<bool> CanAccessAsync()
_httpContext.Response.StatusCode = (int)HttpStatusCode.Forbidden;
if (onFailure != null)
{
var syncFilterResult = syncFilters.Any(filter => !filter.Authorize());
await onFailure.Invoke();
}
}

var asyncFilter = await Task.WhenAll(asyncFilters.Select(filter => filter.AuthorizeAsync()));
var asyncFilterResult = Array.Exists(asyncFilter, filter => !filter);
private async Task<bool> CanAccessAsync()
{
// Evaluate all synchronous filters and check if any of them deny access.
bool syncAuthorizeResult = syncFilters.Any(filter => !filter.Authorize());

return !syncFilterResult && !asyncFilterResult;
}
// Evaluate all asynchronous filters and check if any of them deny access.
bool[] asyncFilter = await Task.WhenAll(asyncFilters.Select(filter => filter.AuthorizeAsync()));
bool asyncAuthorizeResult = Array.Exists(asyncFilter, filter => !filter);

// Return true if all filters grant access, otherwise return false.
return !syncAuthorizeResult && !asyncAuthorizeResult;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
using System.Net.Http.Headers;
using System.Text;
using Microsoft.Extensions.Configuration;

namespace Serilog.Ui.Web.Authorization.Filters;

internal class BasicAuthServiceByConfiguration(IConfiguration configuration) : IBasicAuthenticationService
{
private readonly string? _userName = configuration["SerilogUi:UserName"];
private readonly string? _password = configuration["SerilogUi:Password"];

public Task<bool> CanAccessAsync(AuthenticationHeaderValue basicHeader)
{
var header = basicHeader.Parameter;

return Task.FromResult(EvaluateAuthResult(header));
}

private bool EvaluateAuthResult(string? header)
{
var (userName, password) = ExtractAuthenticationTokens(header);
return userName == _userName && password == _password;
}

private static (string userName, string password) ExtractAuthenticationTokens(string? authValues)
{
var parameter = Encoding.UTF8.GetString(Convert.FromBase64String(authValues ?? string.Empty));
var parts = parameter.Split(':');
return (parts[0], parts[1]);
}
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,4 @@
using System;
using System.Net.Http.Headers;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
using System.Net.Http.Headers;
using Serilog.Ui.Core.Interfaces;

namespace Serilog.Ui.Web.Authorization.Filters;
Expand All @@ -12,15 +9,12 @@ internal class BasicAuthenticationFilter(
: IUiAsyncAuthorizationFilter
{
private const string AuthenticationScheme = "Basic";
private readonly HttpContext _httpContext = Guard.Against.Null(httpContextAccessor.HttpContext);

public async Task<bool> AuthorizeAsync()
{
var httpContext = httpContextAccessor.HttpContext;
if (httpContext is null) return false;

var header = httpContext.Request.Headers.Authorization;

var authValues = AuthenticationHeaderValue.Parse(header!);
StringValues header = _httpContext.Request.Headers.Authorization;
AuthenticationHeaderValue authValues = AuthenticationHeaderValue.Parse(header!);

return
// not basic, thus no evaluation should happen
Expand All @@ -31,6 +25,6 @@ public async Task<bool> AuthorizeAsync()

private static bool IsBasicAuthentication(AuthenticationHeaderValue authValues)
{
return AuthenticationScheme.Equals(authValues.Scheme, StringComparison.InvariantCultureIgnoreCase);
return AuthenticationScheme.Equals(authValues.Scheme, StringComparison.OrdinalIgnoreCase);
}
}
Original file line number Diff line number Diff line change
@@ -1,46 +1,16 @@
using System;
using System.Net.Http.Headers;
using System.Text;
using System.Threading.Tasks;
using Microsoft.Extensions.Configuration;
using System.Net.Http.Headers;

namespace Serilog.Ui.Web.Authorization.Filters;

/// <summary>
/// Provides basic authentication services.
/// </summary>
public interface IBasicAuthenticationService
{
/// <summary>
/// Determines whether access is granted based on the provided basic authentication header.
/// </summary>
/// <param name="basicHeader">The basic authentication header containing the credentials.</param>
/// <returns>A task that represents the asynchronous operation. The task result contains a boolean indicating whether access is granted.</returns>
Task<bool> CanAccessAsync(AuthenticationHeaderValue basicHeader);
}

internal class BasicAuthServiceByConfiguration(IConfiguration configuration) : IBasicAuthenticationService
{
private string? UserName { get; } = configuration["SerilogUi:UserName"];

private string? Password { get; } = configuration["SerilogUi:Password"];

public Task<bool> CanAccessAsync(AuthenticationHeaderValue basicHeader)
{
var header = basicHeader.Parameter;

return Task.FromResult(EvaluateAuthResult(header));
}

private bool EvaluateAuthResult(string? header)
{
var tokens = ExtractAuthenticationTokens(header);
var matchCredentials = CredentialsMatch(tokens);

return matchCredentials;
}

private static (string, string) ExtractAuthenticationTokens(string? authValues)
{
var parameter = Encoding.UTF8.GetString(Convert.FromBase64String(authValues ?? string.Empty));
var parts = parameter.Split(':');
return (parts[0], parts[1]);
}

private bool CredentialsMatch((string Username, string Password) tokens)
{
return tokens.Username == UserName && tokens.Password == Password;
}
}
Original file line number Diff line number Diff line change
@@ -1,16 +1,11 @@
using Microsoft.AspNetCore.Http;
using Serilog.Ui.Core.Interfaces;
using Serilog.Ui.Core.Interfaces;
using Serilog.Ui.Web.Extensions;

namespace Serilog.Ui.Web.Authorization.Filters
{
internal class LocalRequestsOnlyAuthorizationFilter(IHttpContextAccessor httpContextAccessor) : IUiAuthorizationFilter
{
public bool Authorize()
{
var httpContext = httpContextAccessor.HttpContext;
namespace Serilog.Ui.Web.Authorization.Filters;

return httpContext is not null && httpContext.Request.IsLocal();
}
}
internal class LocalRequestsOnlyAuthorizationFilter(IHttpContextAccessor httpContextAccessor) : IUiAuthorizationFilter
{
public bool Authorize() =>
httpContextAccessor.HttpContext is not null &&
httpContextAccessor.HttpContext.Request.IsLocal();
}
Original file line number Diff line number Diff line change
@@ -1,22 +1,19 @@
using System.Threading.Tasks;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Authorization;
using Serilog.Ui.Core.Interfaces;

namespace Serilog.Ui.Web.Authorization.Filters;

internal class PolicyAuthorizationFilter(
IHttpContextAccessor httpContextAccessor,
IAuthorizationService authorizationService,
string policy)
: IUiAsyncAuthorizationFilter
string policy
) : IUiAsyncAuthorizationFilter
{
private readonly HttpContext _httpContext = Guard.Against.Null(httpContextAccessor.HttpContext);

public async Task<bool> AuthorizeAsync()
{
var httpContext = httpContextAccessor.HttpContext;
if (httpContext is null) return false;

var result = await authorizationService.AuthorizeAsync(httpContext.User, policy);
AuthorizationResult result = await authorizationService.AuthorizeAsync(_httpContext.User, policy);
return result.Succeeded;
}
}
19 changes: 12 additions & 7 deletions src/Serilog.Ui.Web/Authorization/IAuthorizationFilterService.cs
Original file line number Diff line number Diff line change
@@ -1,10 +1,15 @@
using System;
using System.Threading.Tasks;
namespace Serilog.Ui.Web.Authorization;

namespace Serilog.Ui.Web.Authorization
/// <summary>
/// Provides services for authorization filtering.
/// </summary>
internal interface IAuthorizationFilterService
{
internal interface IAuthorizationFilterService
{
Task CheckAccessAsync(Func<Task> onSuccess, Func<Task>? onFailure = null);
}
/// <summary>
/// Checks access and executes the appropriate callback based on the result.
/// </summary>
/// <param name="onSuccess">The callback to execute if access is granted.</param>
/// <param name="onFailure">The optional callback to execute if access is denied.</param>
/// <returns>A task that represents the asynchronous operation.</returns>
Task CheckAccessAsync(Func<Task> onSuccess, Func<Task>? onFailure = null);
}
20 changes: 9 additions & 11 deletions src/Serilog.Ui.Web/Endpoints/AppStreamLoader.cs
Original file line number Diff line number Diff line change
@@ -1,15 +1,13 @@
using System.IO;
using Serilog.Ui.Web.Models;
using Serilog.Ui.Web.Models;

namespace Serilog.Ui.Web.Endpoints
namespace Serilog.Ui.Web.Endpoints;

internal class AppStreamLoader : IAppStreamLoader
{
internal class AppStreamLoader : IAppStreamLoader
{
private const string AppManifest = "Serilog.Ui.Web.wwwroot.dist.index.html";
private const string AppManifest = "Serilog.Ui.Web.wwwroot.dist.index.html";

public Stream? GetIndex() =>
typeof(AuthorizationOptions)
.Assembly
.GetManifestResourceStream(AppManifest);
}
public Stream? GetIndex() =>
typeof(AuthorizationOptions)
.Assembly
.GetManifestResourceStream(AppManifest);
}
16 changes: 10 additions & 6 deletions src/Serilog.Ui.Web/Endpoints/IAppStreamLoader.cs
Original file line number Diff line number Diff line change
@@ -1,9 +1,13 @@
using System.IO;
namespace Serilog.Ui.Web.Endpoints;

namespace Serilog.Ui.Web.Endpoints
/// <summary>
/// Provides methods to load application streams.
/// </summary>
internal interface IAppStreamLoader
{
internal interface IAppStreamLoader
{
Stream? GetIndex();
}
/// <summary>
/// Gets the index stream.
/// </summary>
/// <returns>The index stream, or null if the stream is not available.</returns>
Stream? GetIndex();
}
Loading

0 comments on commit 8fe464c

Please sign in to comment.