aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLibravatar vnugent <public@vaughnnugent.com>2024-09-17 14:32:41 -0400
committerLibravatar vnugent <public@vaughnnugent.com>2024-09-17 14:32:41 -0400
commitb2ab9035c186dd6bb9bc6268a6799b86f369d657 (patch)
tree3d1f01e28ad808bd636c31612c9ca3c277c2b986
parent1d634e5ba715c2b42583266747fa5c8ab56abd5f (diff)
test some mvc extensions updatesdevelop
-rw-r--r--lib/vnlib.browser/Taskfile.yaml18
-rw-r--r--plugins/VNLib.Plugins.Essentials.Accounts.AppData/src/AppDataEntry.cs2
-rw-r--r--plugins/VNLib.Plugins.Essentials.Accounts.AppData/src/Endpoints/WebEndpoint.cs94
-rw-r--r--plugins/VNLib.Plugins.Essentials.Accounts.AppData/src/Model/HttpExtensions.cs9
-rw-r--r--plugins/VNLib.Plugins.Essentials.Accounts.AppData/src/Stores/Sql/SqlBackingStore.cs3
-rw-r--r--plugins/VNLib.Plugins.Essentials.Accounts.Registration/src/RegistrationContext.cs13
-rw-r--r--plugins/VNLib.Plugins.Essentials.Accounts.Registration/src/TokenRevocation/RevokedToken.cs1
-rw-r--r--plugins/VNLib.Plugins.Essentials.Accounts.Registration/src/TokenRevocation/RevokedTokenStore.cs3
-rw-r--r--plugins/VNLib.Plugins.Essentials.Accounts/src/Endpoints/LoginEndpoint.cs4
-rw-r--r--plugins/VNLib.Plugins.Essentials.Content.Routing/src/ManagedRouteStore.cs17
10 files changed, 110 insertions, 54 deletions
diff --git a/lib/vnlib.browser/Taskfile.yaml b/lib/vnlib.browser/Taskfile.yaml
index 81b726d..bf1c7e7 100644
--- a/lib/vnlib.browser/Taskfile.yaml
+++ b/lib/vnlib.browser/Taskfile.yaml
@@ -13,10 +13,10 @@ tasks:
#called by build pipeline to build module
build:
cmds:
- - echo "building module {{.MODULE_NAME}}"
+ - echo "building module {{ .PROJECT_NAME }}"
#update internal package version
- - cmd: npm version {{.BUILD_VERSION}}
+ - cmd: npm version {{ .BUILD_VERSION }}
ignore_error: true
#install dependencies and build
@@ -24,11 +24,19 @@ tasks:
- npm run build
postbuild_success:
+ vars:
+ TAR_FILES:
+ dist/
+ LICENSE.txt
+ README.md
+ package.json
+ package-lock.json
+ tsconfig.json
+
cmds:
- powershell -Command "mkdir bin -Force"
#tgz the dist folder
- - tar --exclude="./node_modules" --exclude="./src" --exclude="./.git" --exclude="./bin" --exclude=".gitignore" --exclude="*.yaml" --exclude="*.yml" -czf bin/release.tgz .
-
+ - tar -czf bin/release.tgz {{ .TAR_FILES }}
#called by build pipeline to clean module
clean:
@@ -36,4 +44,4 @@ tasks:
cmds:
#delete dist folder
- for: ['bin/', 'dist/', 'node_modules/']
- cmd: powershell -Command "Remove-Item -Recurse -Force {{.ITEM_NAME}}" \ No newline at end of file
+ cmd: powershell -Command "Remove-Item -Recurse -Force {{ .ITEM_NAME }}" \ No newline at end of file
diff --git a/plugins/VNLib.Plugins.Essentials.Accounts.AppData/src/AppDataEntry.cs b/plugins/VNLib.Plugins.Essentials.Accounts.AppData/src/AppDataEntry.cs
index d6d936f..d39000b 100644
--- a/plugins/VNLib.Plugins.Essentials.Accounts.AppData/src/AppDataEntry.cs
+++ b/plugins/VNLib.Plugins.Essentials.Accounts.AppData/src/AppDataEntry.cs
@@ -23,7 +23,7 @@
*/
using VNLib.Utils.Logging;
-using VNLib.Plugins.Extensions.Loading.Routing;
+using VNLib.Plugins.Extensions.Loading.Routing.Mvc;
using VNLib.Plugins.Essentials.Accounts.AppData.Endpoints;
diff --git a/plugins/VNLib.Plugins.Essentials.Accounts.AppData/src/Endpoints/WebEndpoint.cs b/plugins/VNLib.Plugins.Essentials.Accounts.AppData/src/Endpoints/WebEndpoint.cs
index 3c4f3e5..41b8b30 100644
--- a/plugins/VNLib.Plugins.Essentials.Accounts.AppData/src/Endpoints/WebEndpoint.cs
+++ b/plugins/VNLib.Plugins.Essentials.Accounts.AppData/src/Endpoints/WebEndpoint.cs
@@ -22,10 +22,11 @@
* along with this program. If not, see https://www.gnu.org/licenses/.
*/
+using System;
using System.Net;
using System.Linq;
-using System.Collections.Generic;
using System.Threading.Tasks;
+using System.Collections.Generic;
using VNLib.Net.Http;
using VNLib.Hashing.Checksums;
@@ -37,14 +38,16 @@ using VNLib.Plugins.Extensions.Loading.Routing;
using VNLib.Plugins.Essentials.Accounts.AppData.Model;
using VNLib.Plugins.Essentials.Accounts.AppData.Stores;
+using VNLib.Plugins.Extensions.Loading.Routing.Mvc;
+using static VNLib.Plugins.Essentials.Endpoints.ResourceEndpointBase;
+using static VNLib.Plugins.Essentials.Accounts.AppData.Model.HttpExtensions;
namespace VNLib.Plugins.Essentials.Accounts.AppData.Endpoints
{
-
- [EndpointPath("{{path}}")]
+
[EndpointLogName("Endpoint")]
[ConfigurationName("web_endpoint")]
- internal sealed class WebEndpoint(PluginBase plugin, IConfigScope config) : ProtectedWebEndpoint
+ internal sealed class WebEndpoint(PluginBase plugin, IConfigScope config) : IHttpController
{
const int DefaultMaxDataSize = 8 * 1024;
@@ -52,19 +55,24 @@ namespace VNLib.Plugins.Essentials.Accounts.AppData.Endpoints
private readonly int MaxDataSize = config.GetValueOrDefault("max_data_size", DefaultMaxDataSize);
private readonly string[] AllowedScopes = config.GetRequiredProperty<string[]>("allowed_scopes");
- protected async override ValueTask<VfReturnType> GetAsync(HttpEntity entity)
+ ///<inheritdoc/>
+ public ProtectionSettings GetProtectionSettings() => default;
+
+ [HttpStaticRoute("{{path}}", HttpMethod.GET)]
+ [HttpRouteProtection(AuthorzationCheckLevel.Critical)]
+ public async ValueTask<VfReturnType> GetDataAsync(HttpEntity entity)
{
WebMessage webm = new();
- string? scopeId = entity.QueryArgs.GetValueOrDefault("scope");
- bool noCache = entity.QueryArgs.ContainsKey("no_cache");
+ string? scopeId = GetScopeId(entity);
+ bool noCache = NoCacheQuery(entity);
if (webm.Assert(scopeId != null, "Missing scope"))
{
return VirtualClose(entity, webm, HttpStatusCode.BadRequest);
}
- if (webm.Assert(AllowedScopes.Contains(scopeId), "Invalid scope"))
+ if (webm.Assert(IsScopeAllowed(scopeId), "Invalid scope"))
{
return VirtualClose(entity, webm, HttpStatusCode.BadRequest);
}
@@ -72,26 +80,28 @@ namespace VNLib.Plugins.Essentials.Accounts.AppData.Endpoints
//If the connection has the no-cache header set, also bypass the cache
noCache |= entity.Server.NoCache();
- //optionally bypass cache if the user requests it
- RecordOpFlags flags = noCache ? RecordOpFlags.NoCache : RecordOpFlags.None;
-
- UserRecordData? record = await _store.GetRecordAsync(entity.Session.UserID, scopeId, flags, entity.EventCancellation);
-
- if (record is null)
- {
- return VirtualClose(entity, webm, HttpStatusCode.NotFound);
- }
+ UserRecordData? record = await _store.GetRecordAsync(
+ entity.Session.UserID,
+ recordKey: scopeId,
+ flags: noCache ? RecordOpFlags.NoCache : RecordOpFlags.None, //optionally bypass cache if the user requests it
+ entity.EventCancellation
+ );
//return the raw data with the checksum header
- entity.SetRecordResponse(record, HttpStatusCode.OK);
- return VfReturnType.VirtualSkip;
+
+ return record is null
+ ? VirtualClose(entity, webm, HttpStatusCode.NotFound)
+ : CloseWithRecord(entity, record, HttpStatusCode.OK);
}
- protected override async ValueTask<VfReturnType> PutAsync(HttpEntity entity)
+
+ [HttpStaticRoute("{{path}}", HttpMethod.PUT)]
+ [HttpRouteProtection(AuthorzationCheckLevel.Critical)]
+ public async ValueTask<VfReturnType> UpdateDataAsync(HttpEntity entity)
{
WebMessage webm = new();
- string? scopeId = entity.QueryArgs.GetValueOrDefault("scope");
- bool flush = entity.QueryArgs.ContainsKey("flush");
+ string? scopeId = GetScopeId(entity);
+ bool flush = NoCacheQuery(entity);
if (webm.Assert(entity.Files.Count == 1, "Invalid file count"))
{
@@ -103,7 +113,7 @@ namespace VNLib.Plugins.Essentials.Accounts.AppData.Endpoints
return VirtualClose(entity, webm, HttpStatusCode.BadRequest);
}
- if (webm.Assert(AllowedScopes.Contains(scopeId), "Invalid scope"))
+ if (webm.Assert(IsScopeAllowed(scopeId), "Invalid scope"))
{
return VirtualClose(entity, webm, HttpStatusCode.BadRequest);
}
@@ -125,7 +135,7 @@ namespace VNLib.Plugins.Essentials.Accounts.AppData.Endpoints
//Compute checksum on sent data and compare to the header if it exists
ulong checksum = FNV1a.Compute64(recordData);
- ulong? userChecksum = entity.Server.GetUserDataChecksum();
+ ulong? userChecksum = GetUserDataChecksum(entity.Server);
if (userChecksum.HasValue)
{
@@ -144,28 +154,54 @@ namespace VNLib.Plugins.Essentials.Accounts.AppData.Endpoints
RecordOpFlags flags = flush ? RecordOpFlags.WriteThrough : RecordOpFlags.None;
//Write the record to the store
- await _store.SetRecordAsync(entity.Session.UserID, scopeId, recordData, checksum, flags, entity.EventCancellation);
+ await _store.SetRecordAsync(
+ userId: entity.Session.UserID,
+ recordKey: scopeId,
+ recordData,
+ checksum,
+ flags,
+ entity.EventCancellation
+ );
+
return VirtualClose(entity, HttpStatusCode.Accepted);
}
- protected override async ValueTask<VfReturnType> DeleteAsync(HttpEntity entity)
+ [HttpStaticRoute("{{path}}", HttpMethod.DELETE)]
+ [HttpRouteProtection(AuthorzationCheckLevel.Critical)]
+ public async ValueTask<VfReturnType> DeleteDataAsync(HttpEntity entity)
{
WebMessage webm = new();
- string? scopeId = entity.QueryArgs.GetValueOrDefault("scope");
+ string? scopeId = GetScopeId(entity);
if (webm.Assert(scopeId != null, "Missing scope"))
{
return VirtualClose(entity, webm, HttpStatusCode.BadRequest);
}
- if (webm.Assert(AllowedScopes.Contains(scopeId), "Invalid scope"))
+ if (webm.Assert(IsScopeAllowed(scopeId), "Invalid scope"))
{
return VirtualClose(entity, webm, HttpStatusCode.BadRequest);
}
//Write the record to the store
- await _store.DeleteRecordAsync(entity.Session.UserID, scopeId, entity.EventCancellation);
+ await _store.DeleteRecordAsync(
+ userId: entity.Session.UserID,
+ recordKey: scopeId,
+ entity.EventCancellation
+ );
+
return VirtualClose(entity, HttpStatusCode.Accepted);
+ }
+
+ private bool IsScopeAllowed(string scopeId)
+ {
+ return AllowedScopes.Contains(scopeId, StringComparer.OrdinalIgnoreCase);
}
+
+ private static string? GetScopeId(HttpEntity entity)
+ => entity.QueryArgs.GetValueOrDefault("scope");
+
+ private static bool NoCacheQuery(HttpEntity entity)
+ => entity.QueryArgs.ContainsKey("no_cache");
}
}
diff --git a/plugins/VNLib.Plugins.Essentials.Accounts.AppData/src/Model/HttpExtensions.cs b/plugins/VNLib.Plugins.Essentials.Accounts.AppData/src/Model/HttpExtensions.cs
index 9628b79..c3b990c 100644
--- a/plugins/VNLib.Plugins.Essentials.Accounts.AppData/src/Model/HttpExtensions.cs
+++ b/plugins/VNLib.Plugins.Essentials.Accounts.AppData/src/Model/HttpExtensions.cs
@@ -23,6 +23,7 @@
*/
using System;
+using System.Collections.Generic;
using System.Net;
using VNLib.Net.Http;
@@ -33,7 +34,7 @@ namespace VNLib.Plugins.Essentials.Accounts.AppData.Model
{
const string ChecksumHeader = "X-Data-Checksum";
- public static void SetRecordResponse(this HttpEntity entity, UserRecordData record, HttpStatusCode code)
+ public static VfReturnType CloseWithRecord(HttpEntity entity, UserRecordData record, HttpStatusCode code)
{
//Set checksum header
entity.Server.Headers.Append(ChecksumHeader, $"{record.Checksum}");
@@ -44,14 +45,16 @@ namespace VNLib.Plugins.Essentials.Accounts.AppData.Model
ContentType.Binary,
new BinDataRecordReader(record.Data)
);
+
+ return VfReturnType.VirtualSkip;
}
- public static ulong? GetUserDataChecksum(this IConnectionInfo server)
+ public static ulong? GetUserDataChecksum(IConnectionInfo server)
{
string? checksumStr = server.Headers[ChecksumHeader];
return string.IsNullOrWhiteSpace(checksumStr) && ulong.TryParse(checksumStr, out ulong checksum) ? checksum : null;
}
-
+
sealed class BinDataRecordReader(byte[] recordData) : IMemoryResponseReader
{
private int _read;
diff --git a/plugins/VNLib.Plugins.Essentials.Accounts.AppData/src/Stores/Sql/SqlBackingStore.cs b/plugins/VNLib.Plugins.Essentials.Accounts.AppData/src/Stores/Sql/SqlBackingStore.cs
index 2347c66..3761f07 100644
--- a/plugins/VNLib.Plugins.Essentials.Accounts.AppData/src/Stores/Sql/SqlBackingStore.cs
+++ b/plugins/VNLib.Plugins.Essentials.Accounts.AppData/src/Stores/Sql/SqlBackingStore.cs
@@ -42,7 +42,8 @@ using VNLib.Plugins.Essentials.Accounts.AppData.Model;
namespace VNLib.Plugins.Essentials.Accounts.AppData.Stores.Sql
{
- internal sealed class SqlBackingStore(PluginBase plugin) : IEntityStore<UserRecordData, AppDataRequest>, IAsyncConfigurable
+ internal sealed class SqlBackingStore(PluginBase plugin)
+ : IEntityStore<UserRecordData, AppDataRequest>, IAsyncConfigurable
{
private readonly DbRecordStore _store = new(plugin.GetContextOptionsAsync());
diff --git a/plugins/VNLib.Plugins.Essentials.Accounts.Registration/src/RegistrationContext.cs b/plugins/VNLib.Plugins.Essentials.Accounts.Registration/src/RegistrationContext.cs
index c19d163..fb28600 100644
--- a/plugins/VNLib.Plugins.Essentials.Accounts.Registration/src/RegistrationContext.cs
+++ b/plugins/VNLib.Plugins.Essentials.Accounts.Registration/src/RegistrationContext.cs
@@ -43,15 +43,12 @@ namespace VNLib.Plugins.Essentials.Accounts.Registration
public void OnDatabaseCreating(IDbContextBuilder builder, object? state)
{
//Define a table for the revoked tokens
- builder.DefineTable<RevokedToken>(nameof(RevokedRegistrationTokens))
- //Define the token column and the created column, let the framework determine the data-types
- .WithColumn(p => p.Token)
- .MaxLength(200)
- .Next()
+ builder.DefineTable<RevokedToken>(nameof(RevokedRegistrationTokens), table =>
+ {
+ table.WithColumn(p => p.Token);
+ table.WithColumn(p => p.Created);
+ });
- //Define the next column
- .WithColumn(p => p.Created)
- .AllowNull(false);
}
}
} \ No newline at end of file
diff --git a/plugins/VNLib.Plugins.Essentials.Accounts.Registration/src/TokenRevocation/RevokedToken.cs b/plugins/VNLib.Plugins.Essentials.Accounts.Registration/src/TokenRevocation/RevokedToken.cs
index c2b7715..1e3ea38 100644
--- a/plugins/VNLib.Plugins.Essentials.Accounts.Registration/src/TokenRevocation/RevokedToken.cs
+++ b/plugins/VNLib.Plugins.Essentials.Accounts.Registration/src/TokenRevocation/RevokedToken.cs
@@ -38,6 +38,7 @@ namespace VNLib.Plugins.Essentials.Accounts.Registration.TokenRevocation
/// The token that was revoked.
/// </summary>
[Key]
+ [MaxLength(200)]
public string? Token { get; set; }
}
} \ No newline at end of file
diff --git a/plugins/VNLib.Plugins.Essentials.Accounts.Registration/src/TokenRevocation/RevokedTokenStore.cs b/plugins/VNLib.Plugins.Essentials.Accounts.Registration/src/TokenRevocation/RevokedTokenStore.cs
index e63b02e..3f28b4e 100644
--- a/plugins/VNLib.Plugins.Essentials.Accounts.Registration/src/TokenRevocation/RevokedTokenStore.cs
+++ b/plugins/VNLib.Plugins.Essentials.Accounts.Registration/src/TokenRevocation/RevokedTokenStore.cs
@@ -78,7 +78,8 @@ namespace VNLib.Plugins.Essentials.Accounts.Registration.TokenRevocation
await using RegistrationContext context = new (options.Value);
//Select any that match tokens
- RevokedToken[] expired = await context.RevokedRegistrationTokens.Where(t => t.Created < expiredBefore)
+ RevokedToken[] expired = await context.RevokedRegistrationTokens
+ .Where(t => t.Created < expiredBefore)
.Select(static t => t)
.ToArrayAsync(cancellation);
diff --git a/plugins/VNLib.Plugins.Essentials.Accounts/src/Endpoints/LoginEndpoint.cs b/plugins/VNLib.Plugins.Essentials.Accounts/src/Endpoints/LoginEndpoint.cs
index 5bc286e..83bb9a2 100644
--- a/plugins/VNLib.Plugins.Essentials.Accounts/src/Endpoints/LoginEndpoint.cs
+++ b/plugins/VNLib.Plugins.Essentials.Accounts/src/Endpoints/LoginEndpoint.cs
@@ -27,7 +27,6 @@ using System.Net;
using System.Text.Json;
using System.Threading;
using System.Threading.Tasks;
-using System.Collections.Generic;
using System.Security.Cryptography;
using System.Text.Json.Serialization;
@@ -46,9 +45,6 @@ using VNLib.Plugins.Extensions.Loading;
using VNLib.Plugins.Extensions.Loading.Users;
using VNLib.Plugins.Extensions.Loading.Routing;
using static VNLib.Plugins.Essentials.Statics;
-using VNLib.Plugins.Essentials.Accounts.MFA.Totp;
-using VNLib.Plugins.Essentials.Accounts.MFA.Fido;
-
/*
* Password only log-ins should be immune to repeat attacks on the same backend, because sessions are
diff --git a/plugins/VNLib.Plugins.Essentials.Content.Routing/src/ManagedRouteStore.cs b/plugins/VNLib.Plugins.Essentials.Content.Routing/src/ManagedRouteStore.cs
index 8c32e71..65b115c 100644
--- a/plugins/VNLib.Plugins.Essentials.Content.Routing/src/ManagedRouteStore.cs
+++ b/plugins/VNLib.Plugins.Essentials.Content.Routing/src/ManagedRouteStore.cs
@@ -26,6 +26,7 @@ using System.Threading;
using System.Threading.Tasks;
using System.Collections.Generic;
+using VNLib.Utils.Logging;
using VNLib.Plugins.Extensions.Loading;
using VNLib.Plugins.Extensions.Loading.Sql;
using VNLib.Plugins.Essentials.Content.Routing.Model;
@@ -33,10 +34,16 @@ using VNLib.Plugins.Essentials.Content.Routing.stores;
namespace VNLib.Plugins.Essentials.Content.Routing
{
- [ConfigurationName("store")]
+ [ConfigurationName("store", Required = false)]
internal sealed class ManagedRouteStore : IRouteStore
{
- private readonly IRouteStore _routeStore;
+ private readonly IRouteStore _routeStore = new DummyRouteStore();
+
+ //empty constructor for
+ public ManagedRouteStore(PluginBase plugin)
+ {
+ plugin.Log.Warn("Page router loaded but no route store was loaded. Routing funtionality is disabled.");
+ }
public ManagedRouteStore(PluginBase plugin, IConfigScope config)
{
@@ -60,5 +67,11 @@ namespace VNLib.Plugins.Essentials.Content.Routing
{
return _routeStore.GetAllRoutesAsync(routes, cancellation);
}
+
+ private sealed class DummyRouteStore : IRouteStore
+ {
+ public Task GetAllRoutesAsync(ICollection<Route> routes, CancellationToken cancellation)
+ => Task.CompletedTask;
+ }
}
}