Finished adding user support and ability to update specific novels or set your last read chapter

This commit is contained in:
2022-07-17 20:34:06 -04:00
parent e4529e11c0
commit b5c4146d4d
34 changed files with 589 additions and 59 deletions

View File

@@ -9,9 +9,11 @@ namespace Treestar.Shared.AccessLayers;
public abstract class ApiAccessLayer
{
private readonly HttpClient _httpClient;
private readonly IAccessLayerAuthenticationProvider _authenticationProvider;
protected ApiAccessLayer(string apiBaseUrl)
protected ApiAccessLayer(string apiBaseUrl, IAccessLayerAuthenticationProvider authenticationProvider)
{
_authenticationProvider = authenticationProvider;
var handler = new HttpClientHandler()
{
ServerCertificateCustomValidationCallback = HttpClientHandler.DangerousAcceptAnyServerCertificateValidator
@@ -22,6 +24,7 @@ public abstract class ApiAccessLayer
private async Task<HttpResponseWrapper> SendRequest(HttpRequestMessage message)
{
await _authenticationProvider.AddAuthentication(message);
var response = await _httpClient.SendAsync(message);
return new HttpResponseWrapper()
{

View File

@@ -0,0 +1,6 @@
namespace Treestar.Shared.AccessLayers;
public interface IAccessLayerAuthenticationProvider
{
Task AddAuthentication(HttpRequestMessage request);
}

View File

@@ -0,0 +1,32 @@
using System.Security.Claims;
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.IdentityModel.Tokens;
namespace Treestar.Shared.Authentication.JwtBearer;
public static class JWTAuthenticationExtension
{
public static void AddJwtBearerAuth(this IServiceCollection services, IConfiguration configuration)
{
var jwtAuthOptions = configuration.GetRequiredSection(JwtBearerAuthenticationOptions.ConfigrationSection)
.Get<JwtBearerAuthenticationOptions>();
services.AddAuthentication(opt =>
{
opt.DefaultScheme = JwtBearerDefaults.AuthenticationScheme;
}).AddJwtBearer(opt =>
{
opt.Authority = jwtAuthOptions.Authority;
opt.Audience = jwtAuthOptions.Audience;
opt.TokenValidationParameters = new TokenValidationParameters
{
NameClaimType = ClaimTypes.Name,
ValidateAudience = !string.IsNullOrEmpty(jwtAuthOptions.Audience),
ValidateIssuer = true,
ValidateIssuerSigningKey = true,
ValidateLifetime = true
};
});
}
}

View File

@@ -0,0 +1,8 @@
namespace Treestar.Shared.Authentication.JwtBearer;
public class JwtBearerAuthenticationOptions
{
public const string ConfigrationSection = "JwtBearerAuthOptions";
public string Authority { get; set; } = null!;
public string? Audience { get; set; }
}

View File

@@ -0,0 +1,45 @@
using System.Security.Claims;
using Microsoft.AspNetCore.Authentication.Cookies;
using Microsoft.AspNetCore.Authentication.OpenIdConnect;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.IdentityModel.Protocols.OpenIdConnect;
using Microsoft.IdentityModel.Tokens;
using WebNovelPortal.Authentication;
namespace Treestar.Shared.Authentication.OIDC;
public static class AuthenticationExtension
{
public static void AddOIDCAuth(this IServiceCollection services, IConfiguration configuration)
{
var oidcConfig = configuration.GetRequiredSection(OpenIdConnectAuthenticationOptions.ConfigurationSection)
.Get<OpenIdConnectAuthenticationOptions>();
services.AddAuthentication(opt =>
{
opt.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme;
opt.DefaultChallengeScheme = OpenIdConnectDefaults.AuthenticationScheme;
})
.AddCookie()
.AddOpenIdConnect(opt =>
{
opt.Authority = oidcConfig.Authority;
opt.ClientId = oidcConfig.ClientId;
opt.ClientSecret = oidcConfig.ClientSecret;
opt.ResponseType = OpenIdConnectResponseType.Code;
opt.GetClaimsFromUserInfoEndpoint = false;
opt.SaveTokens = true;
opt.UseTokenLifetime = true;
foreach (var scope in oidcConfig.Scopes.Split(" "))
{
opt.Scope.Add(scope);
}
opt.TokenValidationParameters = new TokenValidationParameters
{
NameClaimType = ClaimTypes.Name
};
});
}
}

View File

@@ -0,0 +1,10 @@
namespace WebNovelPortal.Authentication;
public class OpenIdConnectAuthenticationOptions
{
public const string ConfigurationSection = "OIDCAuthOptions";
public string Authority { get; set; }
public string ClientId { get; set; }
public string ClientSecret { get; set; }
public string Scopes { get; set; }
}

View File

@@ -17,5 +17,23 @@ namespace Treestar.Shared.Models.DBDomain
public NovelStatus Status { get; set; }
public DateTime LastUpdated { get; set; }
public DateTime DatePosted { get; set; }
protected bool Equals(Novel other)
{
return Url == other.Url;
}
public override bool Equals(object? obj)
{
if (ReferenceEquals(null, obj)) return false;
if (ReferenceEquals(this, obj)) return true;
if (obj.GetType() != this.GetType()) return false;
return Equals((Novel) obj);
}
public override int GetHashCode()
{
return Url.GetHashCode();
}
}
}

View File

@@ -10,5 +10,23 @@ namespace Treestar.Shared.Models.DBDomain
public int Id { get; set; }
public string Email { get; set; }
public List<UserNovel> WatchedNovels { get; set; }
protected bool Equals(User other)
{
return Id == other.Id;
}
public override bool Equals(object? obj)
{
if (ReferenceEquals(null, obj)) return false;
if (ReferenceEquals(this, obj)) return true;
if (obj.GetType() != this.GetType()) return false;
return Equals((User) obj);
}
public override int GetHashCode()
{
return Id;
}
}
}

View File

@@ -10,5 +10,23 @@ namespace Treestar.Shared.Models.DBDomain
[JsonIgnore]
public User User { get; set; }
public int LastChapterRead { get; set; }
protected bool Equals(UserNovel other)
{
return UserId == other.UserId && NovelUrl == other.NovelUrl;
}
public override bool Equals(object? obj)
{
if (ReferenceEquals(null, obj)) return false;
if (ReferenceEquals(this, obj)) return true;
if (obj.GetType() != this.GetType()) return false;
return Equals((UserNovel) obj);
}
public override int GetHashCode()
{
return HashCode.Combine(UserId, NovelUrl);
}
}
}

View File

@@ -7,14 +7,26 @@
</PropertyGroup>
<ItemGroup>
<Folder Include="BlazorComponents" />
<Folder Include="Interfaces" />
<Folder Include="Utility" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="6.0.7" />
<PackageReference Include="Microsoft.AspNetCore.Authentication.OpenIdConnect" Version="6.0.7" />
<PackageReference Include="Microsoft.AspNetCore.WebUtilities" Version="2.2.0" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Abstractions" Version="6.0.7" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.1" />
</ItemGroup>
<ItemGroup>
<Reference Include="Microsoft.Extensions.Configuration.Abstractions">
<HintPath>..\..\..\..\..\usr\share\dotnet\shared\Microsoft.AspNetCore.App\6.0.0\Microsoft.Extensions.Configuration.Abstractions.dll</HintPath>
</Reference>
<Reference Include="Microsoft.Extensions.DependencyInjection.Abstractions">
<HintPath>..\..\..\..\..\usr\share\dotnet\shared\Microsoft.AspNetCore.App\6.0.0\Microsoft.Extensions.DependencyInjection.Abstractions.dll</HintPath>
</Reference>
</ItemGroup>
</Project>