aboutsummaryrefslogtreecommitdiff
path: root/VNLib.Plugins.Essentials.Accounts.Registration
diff options
context:
space:
mode:
Diffstat (limited to 'VNLib.Plugins.Essentials.Accounts.Registration')
-rw-r--r--VNLib.Plugins.Essentials.Accounts.Registration/.gitattributes63
-rw-r--r--VNLib.Plugins.Essentials.Accounts.Registration/.gitignore352
-rw-r--r--VNLib.Plugins.Essentials.Accounts.Registration/readme.md0
-rw-r--r--VNLib.Plugins.Essentials.Accounts.Registration/src/AccountValidations.cs132
-rw-r--r--VNLib.Plugins.Essentials.Accounts.Registration/src/EmailSystemConfig.cs153
-rw-r--r--VNLib.Plugins.Essentials.Accounts.Registration/src/Endpoints/RegRequestMessage.cs40
-rw-r--r--VNLib.Plugins.Essentials.Accounts.Registration/src/Endpoints/RegistrationEntpoint.cs392
-rw-r--r--VNLib.Plugins.Essentials.Accounts.Registration/src/RegistrationContext.cs39
-rw-r--r--VNLib.Plugins.Essentials.Accounts.Registration/src/RegistrationEntryPoint.cs66
-rw-r--r--VNLib.Plugins.Essentials.Accounts.Registration/src/TokenRevocation/RevokedToken.cs43
-rw-r--r--VNLib.Plugins.Essentials.Accounts.Registration/src/TokenRevocation/RevokedTokenStore.cs101
-rw-r--r--VNLib.Plugins.Essentials.Accounts.Registration/src/VNLib.Plugins.Essentials.Accounts.Registration.csproj54
12 files changed, 0 insertions, 1435 deletions
diff --git a/VNLib.Plugins.Essentials.Accounts.Registration/.gitattributes b/VNLib.Plugins.Essentials.Accounts.Registration/.gitattributes
deleted file mode 100644
index 1ff0c42..0000000
--- a/VNLib.Plugins.Essentials.Accounts.Registration/.gitattributes
+++ /dev/null
@@ -1,63 +0,0 @@
-###############################################################################
-# Set default behavior to automatically normalize line endings.
-###############################################################################
-* text=auto
-
-###############################################################################
-# Set default behavior for command prompt diff.
-#
-# This is need for earlier builds of msysgit that does not have it on by
-# default for csharp files.
-# Note: This is only used by command line
-###############################################################################
-#*.cs diff=csharp
-
-###############################################################################
-# Set the merge driver for project and solution files
-#
-# Merging from the command prompt will add diff markers to the files if there
-# are conflicts (Merging from VS is not affected by the settings below, in VS
-# the diff markers are never inserted). Diff markers may cause the following
-# file extensions to fail to load in VS. An alternative would be to treat
-# these files as binary and thus will always conflict and require user
-# intervention with every merge. To do so, just uncomment the entries below
-###############################################################################
-#*.sln merge=binary
-#*.csproj merge=binary
-#*.vbproj merge=binary
-#*.vcxproj merge=binary
-#*.vcproj merge=binary
-#*.dbproj merge=binary
-#*.fsproj merge=binary
-#*.lsproj merge=binary
-#*.wixproj merge=binary
-#*.modelproj merge=binary
-#*.sqlproj merge=binary
-#*.wwaproj merge=binary
-
-###############################################################################
-# behavior for image files
-#
-# image files are treated as binary by default.
-###############################################################################
-#*.jpg binary
-#*.png binary
-#*.gif binary
-
-###############################################################################
-# diff behavior for common document formats
-#
-# Convert binary document formats to text before diffing them. This feature
-# is only available from the command line. Turn it on by uncommenting the
-# entries below.
-###############################################################################
-#*.doc diff=astextplain
-#*.DOC diff=astextplain
-#*.docx diff=astextplain
-#*.DOCX diff=astextplain
-#*.dot diff=astextplain
-#*.DOT diff=astextplain
-#*.pdf diff=astextplain
-#*.PDF diff=astextplain
-#*.rtf diff=astextplain
-#*.RTF diff=astextplain
diff --git a/VNLib.Plugins.Essentials.Accounts.Registration/.gitignore b/VNLib.Plugins.Essentials.Accounts.Registration/.gitignore
deleted file mode 100644
index f17dcd9..0000000
--- a/VNLib.Plugins.Essentials.Accounts.Registration/.gitignore
+++ /dev/null
@@ -1,352 +0,0 @@
-## Ignore Visual Studio temporary files, build results, and
-## files generated by popular Visual Studio add-ons.
-##
-## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore
-
-# User-specific files
-*.rsuser
-*.suo
-*.user
-*.userosscache
-*.sln.docstates
-
-# User-specific files (MonoDevelop/Xamarin Studio)
-*.userprefs
-
-# Mono auto generated files
-mono_crash.*
-
-# Build results
-[Dd]ebug/
-[Dd]ebugPublic/
-[Rr]elease/
-[Rr]eleases/
-x64/
-x86/
-[Aa][Rr][Mm]/
-[Aa][Rr][Mm]64/
-bld/
-[Bb]in/
-[Oo]bj/
-[Ll]og/
-[Ll]ogs/
-
-# Visual Studio 2015/2017 cache/options directory
-.vs/
-# Uncomment if you have tasks that create the project's static files in wwwroot
-#wwwroot/
-
-# Visual Studio 2017 auto generated files
-Generated\ Files/
-
-# MSTest test Results
-[Tt]est[Rr]esult*/
-[Bb]uild[Ll]og.*
-
-# NUnit
-*.VisualState.xml
-TestResult.xml
-nunit-*.xml
-
-# Build Results of an ATL Project
-[Dd]ebugPS/
-[Rr]eleasePS/
-dlldata.c
-
-# Benchmark Results
-BenchmarkDotNet.Artifacts/
-
-# .NET Core
-project.lock.json
-project.fragment.lock.json
-artifacts/
-
-# StyleCop
-StyleCopReport.xml
-
-# Files built by Visual Studio
-*_i.c
-*_p.c
-*_h.h
-*.ilk
-*.meta
-*.obj
-*.iobj
-*.pch
-*.pdb
-*.ipdb
-*.pgc
-*.pgd
-*.rsp
-*.sbr
-*.tlb
-*.tli
-*.tlh
-*.tmp
-*.tmp_proj
-*_wpftmp.csproj
-*.log
-*.vspscc
-*.vssscc
-.builds
-*.pidb
-*.svclog
-*.scc
-
-# Chutzpah Test files
-_Chutzpah*
-
-# Visual C++ cache files
-ipch/
-*.aps
-*.ncb
-*.opendb
-*.opensdf
-*.sdf
-*.cachefile
-*.VC.db
-*.VC.VC.opendb
-
-# Visual Studio profiler
-*.psess
-*.vsp
-*.vspx
-*.sap
-
-# Visual Studio Trace Files
-*.e2e
-
-# TFS 2012 Local Workspace
-$tf/
-
-# Guidance Automation Toolkit
-*.gpState
-
-# ReSharper is a .NET coding add-in
-_ReSharper*/
-*.[Rr]e[Ss]harper
-*.DotSettings.user
-
-# TeamCity is a build add-in
-_TeamCity*
-
-# DotCover is a Code Coverage Tool
-*.dotCover
-
-# AxoCover is a Code Coverage Tool
-.axoCover/*
-!.axoCover/settings.json
-
-# Visual Studio code coverage results
-*.coverage
-*.coveragexml
-
-# NCrunch
-_NCrunch_*
-.*crunch*.local.xml
-nCrunchTemp_*
-
-# MightyMoose
-*.mm.*
-AutoTest.Net/
-
-# Web workbench (sass)
-.sass-cache/
-
-# Installshield output folder
-[Ee]xpress/
-
-# DocProject is a documentation generator add-in
-DocProject/buildhelp/
-DocProject/Help/*.HxT
-DocProject/Help/*.HxC
-DocProject/Help/*.hhc
-DocProject/Help/*.hhk
-DocProject/Help/*.hhp
-DocProject/Help/Html2
-DocProject/Help/html
-
-# Click-Once directory
-publish/
-
-# Publish Web Output
-*.[Pp]ublish.xml
-*.azurePubxml
-# Note: Comment the next line if you want to checkin your web deploy settings,
-# but database connection strings (with potential passwords) will be unencrypted
-*.pubxml
-*.publishproj
-
-# Microsoft Azure Web App publish settings. Comment the next line if you want to
-# checkin your Azure Web App publish settings, but sensitive information contained
-# in these scripts will be unencrypted
-PublishScripts/
-
-# NuGet Packages
-*.nupkg
-# NuGet Symbol Packages
-*.snupkg
-# The packages folder can be ignored because of Package Restore
-**/[Pp]ackages/*
-# except build/, which is used as an MSBuild target.
-!**/[Pp]ackages/build/
-# Uncomment if necessary however generally it will be regenerated when needed
-#!**/[Pp]ackages/repositories.config
-# NuGet v3's project.json files produces more ignorable files
-*.nuget.props
-*.nuget.targets
-
-# Microsoft Azure Build Output
-csx/
-*.build.csdef
-
-# Microsoft Azure Emulator
-ecf/
-rcf/
-
-# Windows Store app package directories and files
-AppPackages/
-BundleArtifacts/
-Package.StoreAssociation.xml
-_pkginfo.txt
-*.appx
-*.appxbundle
-*.appxupload
-
-# Visual Studio cache files
-# files ending in .cache can be ignored
-*.[Cc]ache
-# but keep track of directories ending in .cache
-!?*.[Cc]ache/
-
-# Others
-ClientBin/
-~$*
-*~
-*.dbmdl
-*.dbproj.schemaview
-*.jfm
-*.pfx
-*.publishsettings
-orleans.codegen.cs
-
-# Including strong name files can present a security risk
-# (https://github.com/github/gitignore/pull/2483#issue-259490424)
-#*.snk
-
-# Since there are multiple workflows, uncomment next line to ignore bower_components
-# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
-#bower_components/
-
-# RIA/Silverlight projects
-Generated_Code/
-
-# Backup & report files from converting an old project file
-# to a newer Visual Studio version. Backup files are not needed,
-# because we have git ;-)
-_UpgradeReport_Files/
-Backup*/
-UpgradeLog*.XML
-UpgradeLog*.htm
-ServiceFabricBackup/
-*.rptproj.bak
-
-# SQL Server files
-*.mdf
-*.ldf
-*.ndf
-
-# Business Intelligence projects
-*.rdl.data
-*.bim.layout
-*.bim_*.settings
-*.rptproj.rsuser
-*- [Bb]ackup.rdl
-*- [Bb]ackup ([0-9]).rdl
-*- [Bb]ackup ([0-9][0-9]).rdl
-
-# Microsoft Fakes
-FakesAssemblies/
-
-# GhostDoc plugin setting file
-*.GhostDoc.xml
-
-# Node.js Tools for Visual Studio
-.ntvs_analysis.dat
-node_modules/
-
-# Visual Studio 6 build log
-*.plg
-
-# Visual Studio 6 workspace options file
-*.opt
-
-# Visual Studio 6 auto-generated workspace file (contains which files were open etc.)
-*.vbw
-
-# Visual Studio LightSwitch build output
-**/*.HTMLClient/GeneratedArtifacts
-**/*.DesktopClient/GeneratedArtifacts
-**/*.DesktopClient/ModelManifest.xml
-**/*.Server/GeneratedArtifacts
-**/*.Server/ModelManifest.xml
-_Pvt_Extensions
-
-# Paket dependency manager
-.paket/paket.exe
-paket-files/
-
-# FAKE - F# Make
-.fake/
-
-# CodeRush personal settings
-.cr/personal
-
-# Python Tools for Visual Studio (PTVS)
-__pycache__/
-*.pyc
-
-# Cake - Uncomment if you are using it
-# tools/**
-# !tools/packages.config
-
-# Tabs Studio
-*.tss
-
-# Telerik's JustMock configuration file
-*.jmconfig
-
-# BizTalk build output
-*.btp.cs
-*.btm.cs
-*.odx.cs
-*.xsd.cs
-
-# OpenCover UI analysis results
-OpenCover/
-
-# Azure Stream Analytics local run output
-ASALocalRun/
-
-# MSBuild Binary and Structured Log
-*.binlog
-
-# NVidia Nsight GPU debugger configuration file
-*.nvuser
-
-# MFractors (Xamarin productivity tool) working folder
-.mfractor/
-
-# Local History for Visual Studio
-.localhistory/
-
-# BeatPulse healthcheck temp database
-healthchecksdb
-
-# Backup folder for Package Reference Convert tool in Visual Studio 2017
-MigrationBackup/
-
-# Ionide (cross platform F# VS Code tools) working folder
-.ionide/
-
-.json
diff --git a/VNLib.Plugins.Essentials.Accounts.Registration/readme.md b/VNLib.Plugins.Essentials.Accounts.Registration/readme.md
deleted file mode 100644
index e69de29..0000000
--- a/VNLib.Plugins.Essentials.Accounts.Registration/readme.md
+++ /dev/null
diff --git a/VNLib.Plugins.Essentials.Accounts.Registration/src/AccountValidations.cs b/VNLib.Plugins.Essentials.Accounts.Registration/src/AccountValidations.cs
deleted file mode 100644
index b84728b..0000000
--- a/VNLib.Plugins.Essentials.Accounts.Registration/src/AccountValidations.cs
+++ /dev/null
@@ -1,132 +0,0 @@
-/*
-* Copyright (c) 2022 Vaughn Nugent
-*
-* Library: VNLib
-* Package: VNLib.Plugins.Essentials.Accounts.Registration
-* File: AccountValidations.cs
-*
-* AccountValidations.cs is part of VNLib.Plugins.Essentials.Accounts.Registration which is part of the larger
-* VNLib collection of libraries and utilities.
-*
-* VNLib.Plugins.Essentials.Accounts.Registration is free software: you can redistribute it and/or modify
-* it under the terms of the GNU Affero General Public License as
-* published by the Free Software Foundation, either version 3 of the
-* License, or (at your option) any later version.
-*
-* VNLib.Plugins.Essentials.Accounts.Registration is distributed in the hope that it will be useful,
-* but WITHOUT ANY WARRANTY; without even the implied warranty of
-* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-* GNU Affero General Public License for more details.
-*
-* You should have received a copy of the GNU Affero General Public License
-* along with this program. If not, see https://www.gnu.org/licenses/.
-*/
-
-using FluentValidation;
-
-using VNLib.Plugins.Essentials.Accounts.Registration.Endpoints;
-using VNLib.Plugins.Extensions.Validation;
-
-namespace VNLib.Plugins.Essentials.Accounts.Registration
-{
- internal static class AccountValidations
- {
- /// <summary>
- /// Central password requirement validator
- /// </summary>
- public static IValidator<string> PasswordValidator { get; } = GetPassVal();
-
- public static IValidator<AccountData> AccountDataValidator { get; } = GetAcVal();
-
- /// <summary>
- /// A validator used to validate new registration request messages
- /// </summary>
- public static IValidator<RegRequestMessage> RegRequestValidator { get; } = GetRequestValidator();
-
- static IValidator<string> GetPassVal()
- {
- InlineValidator<string> passVal = new();
-
- passVal.RuleFor(static password => password)
- .NotEmpty()
- .Length(min: 8, max: 100)
- .Password()
- .WithMessage(errorMessage: "Password does not meet minium requirements");
-
- return passVal;
- }
-
- static IValidator<AccountData> GetAcVal()
- {
- InlineValidator<AccountData> adv = new ();
-
- //Validate city
-
- adv.RuleFor(t => t.City)
- .MaximumLength(35)
- .AlphaOnly()
- .When(t => t.City?.Length > 0);
-
- adv.RuleFor(t => t.Company)
- .MaximumLength(50)
- .SpecialCharacters()
- .When(t => t.Company?.Length > 0);
-
- //Require a first and last names to be set together
- adv.When(t => t.First?.Length > 0 || t.Last?.Length > 0, () =>
- {
- adv.RuleFor(t => t.First)
- .Length(1, 35)
- .AlphaOnly();
- adv.RuleFor(t => t.Last)
- .Length(1, 35)
- .AlphaOnly();
- });
-
- adv.RuleFor(t => t.PhoneNumber)
- .PhoneNumber()
- .When(t => t.PhoneNumber?.Length > 0)
- .OverridePropertyName("Phone");
-
- //State must be 2 characters for us states if set
- adv.RuleFor(t => t.State)
- .Length(2)
- .When(t => t.State?.Length > 0);
-
- adv.RuleFor(t => t.Street)
- .AlphaNumericOnly()
- .MaximumLength(50)
- .When(t => t.Street?.Length > 0);
-
- //Allow empty zip codes, but if one is defined, is must be less than 7 characters
- adv.RuleFor(t => t.Zip)
- .NumericOnly()
- .MaximumLength(7)
- .When(t => t.Zip?.Length > 0);
-
- return adv;
- }
-
- static IValidator<RegRequestMessage> GetRequestValidator()
- {
- InlineValidator<RegRequestMessage> reqVal = new();
-
- reqVal.RuleFor(static s => s.ClientId)
- .NotEmpty()
- .AlphaNumericOnly()
- .Length(1, 100);
-
- //Convert to universal time before validating
- reqVal.RuleFor(static s => s.Timestamp.ToUniversalTime())
- .Must(t => t > DateTimeOffset.UtcNow.AddSeconds(-60) && t < DateTimeOffset.UtcNow.AddSeconds(60));
-
- reqVal.RuleFor(static s => s.UserName)
- .NotEmpty()
- .EmailAddress()
- .IllegalCharacters()
- .Length(5, 50);
-
- return reqVal;
- }
- }
-}
diff --git a/VNLib.Plugins.Essentials.Accounts.Registration/src/EmailSystemConfig.cs b/VNLib.Plugins.Essentials.Accounts.Registration/src/EmailSystemConfig.cs
deleted file mode 100644
index 238ae37..0000000
--- a/VNLib.Plugins.Essentials.Accounts.Registration/src/EmailSystemConfig.cs
+++ /dev/null
@@ -1,153 +0,0 @@
-/*
-* Copyright (c) 2022 Vaughn Nugent
-*
-* Library: VNLib
-* Package: VNLib.Plugins.Essentials.Accounts.Registration
-* File: EmailSystemConfig.cs
-*
-* EmailSystemConfig.cs is part of VNLib.Plugins.Essentials.Accounts.Registration which is part of the larger
-* VNLib collection of libraries and utilities.
-*
-* VNLib.Plugins.Essentials.Accounts.Registration is free software: you can redistribute it and/or modify
-* it under the terms of the GNU Affero General Public License as
-* published by the Free Software Foundation, either version 3 of the
-* License, or (at your option) any later version.
-*
-* VNLib.Plugins.Essentials.Accounts.Registration is distributed in the hope that it will be useful,
-* but WITHOUT ANY WARRANTY; without even the implied warranty of
-* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-* GNU Affero General Public License for more details.
-*
-* You should have received a copy of the GNU Affero General Public License
-* along with this program. If not, see https://www.gnu.org/licenses/.
-*/
-
-using System;
-using System.Text;
-using System.Text.Json;
-
-using RestSharp;
-
-using Emails.Transactional.Client;
-
-using VNLib.Utils.Extensions;
-using VNLib.Net.Rest.Client;
-using VNLib.Net.Rest.Client.OAuth2;
-using VNLib.Plugins.Extensions.Loading;
-
-namespace VNLib.Plugins.Essentials.Accounts.Registration
-{
- /// <summary>
- /// An extended <see cref="TransactionalEmailConfig"/> configuration
- /// object that contains a <see cref="Net.Rest.Client.RestClientPool"/> pool for making
- /// transactions
- /// </summary>
- internal sealed class EmailSystemConfig : TransactionalEmailConfig
- {
- public const string REG_TEMPLATE_NAME = "Registration";
-
- public EmailSystemConfig(PluginBase pbase)
- {
- IReadOnlyDictionary<string, JsonElement> conf = pbase.GetConfig("email");
- EmailFromName = conf["from_name"].GetString() ?? throw new KeyNotFoundException("");
- EmailFromAddress = conf["from_address"].GetString() ?? throw new KeyNotFoundException("");
- Uri baseServerPath = new(conf["base_url"].GetString()!, UriKind.RelativeOrAbsolute);
- Uri tokenServerBase = new(conf["token_server_url"].GetString()!, UriKind.RelativeOrAbsolute);
- Uri transactionEndpoint = new(conf["transaction_path"].GetString()!, UriKind.RelativeOrAbsolute);
- //Load templates
- Dictionary<string, string> templates = conf["templates"].EnumerateObject().ToDictionary(jp => jp.Name, jp => jp.Value.GetString()!);
- //Init base config
- WithTemplates(templates)
- .WithUrl(transactionEndpoint);
- //Load credentials
- string authEndpoint = conf["token_path"].GetString() ?? throw new KeyNotFoundException();
- int maxClients = conf["max_clients"].GetInt32();
-
-
- //Load oauth secrets from vault
- Task<SecretResult?> oauth2ClientID = pbase.TryGetSecretAsync("oauth2_client_id");
- Task<SecretResult?> oauth2Password = pbase.TryGetSecretAsync("oauth2_client_secret");
-
- //Lazy cred loaded, tasks should be loaded before this method will ever get called
- Credential lazyCredentialGet()
- {
- //Load the results
- SecretResult cliendId = oauth2ClientID.Result ?? throw new KeyNotFoundException("Missing required oauth2 client id");
- SecretResult password = oauth2Password.Result ?? throw new KeyNotFoundException("Missing required oauth2 client secret");
-
- //Creat credential
- return Credential.Create(cliendId.Result, password.Result);
- }
-
-
- //Init client creation options
- RestClientOptions poolOptions = new()
- {
- AllowMultipleDefaultParametersWithSameName = true,
- AutomaticDecompression = System.Net.DecompressionMethods.All,
- PreAuthenticate = true,
- Encoding = Encoding.UTF8,
- MaxTimeout = conf["request_timeout_ms"].GetInt32(),
- UserAgent = "Essentials.EmailRegistation",
- FollowRedirects = false,
- BaseUrl = baseServerPath
- };
- //Options for auth token endpoint
- RestClientOptions oAuth2ClientOptions = new()
- {
- AllowMultipleDefaultParametersWithSameName = true,
- AutomaticDecompression = System.Net.DecompressionMethods.All,
- PreAuthenticate = false,
- Encoding = Encoding.UTF8,
- MaxTimeout = conf["request_timeout_ms"].GetInt32(),
- UserAgent = "Essentials.EmailRegistation",
- FollowRedirects = false,
- BaseUrl = baseServerPath
- };
-
- //Init Oauth authenticator
- OAuth2Authenticator authenticator = new(oAuth2ClientOptions, lazyCredentialGet, authEndpoint);
- //Store pool
- RestClientPool = new(maxClients, poolOptions, authenticator:authenticator);
-
- void Cleanup()
- {
- authenticator.Dispose();
- RestClientPool.Dispose();
- oauth2ClientID.Dispose();
- oauth2Password.Dispose();
- }
-
- //register password cleanup
- _ = pbase.UnloadToken.RegisterUnobserved(Cleanup);
- }
-
- /// <summary>
- /// A shared <see cref="Net.Rest.Client.RestClientPool"/> for renting configuraed
- /// <see cref="RestClient"/>
- /// </summary>
- public RestClientPool RestClientPool { get; }
- /// <summary>
- /// A global from email address name
- /// </summary>
- public string EmailFromName { get; }
- /// <summary>
- /// A global from email address
- /// </summary>
- public string EmailFromAddress { get; }
-
- /// <summary>
- /// Prepares a new registration email transaction request
- /// </summary>
- /// <returns>The prepared <see cref="EmailTransactionRequest"/> object</returns>
- public EmailTransactionRequest GetRegistrationMessage()
- {
- EmailTransactionRequest req = GetTemplateRequest(REG_TEMPLATE_NAME);
- req.FromAddress = EmailFromAddress;
- req.FromName = EmailFromName;
- //set reg subject
- req.Subject = "One more step to register";
- return req;
- }
- }
-} \ No newline at end of file
diff --git a/VNLib.Plugins.Essentials.Accounts.Registration/src/Endpoints/RegRequestMessage.cs b/VNLib.Plugins.Essentials.Accounts.Registration/src/Endpoints/RegRequestMessage.cs
deleted file mode 100644
index 0683067..0000000
--- a/VNLib.Plugins.Essentials.Accounts.Registration/src/Endpoints/RegRequestMessage.cs
+++ /dev/null
@@ -1,40 +0,0 @@
-/*
-* Copyright (c) 2022 Vaughn Nugent
-*
-* Library: VNLib
-* Package: VNLib.Plugins.Essentials.Accounts.Registration
-* File: RegRequestMessage.cs
-*
-* RegRequestMessage.cs is part of VNLib.Plugins.Essentials.Accounts.Registration which is part of the larger
-* VNLib collection of libraries and utilities.
-*
-* VNLib.Plugins.Essentials.Accounts.Registration is free software: you can redistribute it and/or modify
-* it under the terms of the GNU Affero General Public License as
-* published by the Free Software Foundation, either version 3 of the
-* License, or (at your option) any later version.
-*
-* VNLib.Plugins.Essentials.Accounts.Registration is distributed in the hope that it will be useful,
-* but WITHOUT ANY WARRANTY; without even the implied warranty of
-* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-* GNU Affero General Public License for more details.
-*
-* You should have received a copy of the GNU Affero General Public License
-* along with this program. If not, see https://www.gnu.org/licenses/.
-*/
-
-using System.Text.Json.Serialization;
-
-namespace VNLib.Plugins.Essentials.Accounts.Registration.Endpoints
-{
- internal class RegRequestMessage
- {
- [JsonPropertyName("localtime")]
- public DateTimeOffset Timestamp { get; set; }
-
- [JsonPropertyName("username")]
- public string? UserName { get; set; }
-
- [JsonPropertyName("clientid")]
- public string? ClientId { get; set; }
- }
-} \ No newline at end of file
diff --git a/VNLib.Plugins.Essentials.Accounts.Registration/src/Endpoints/RegistrationEntpoint.cs b/VNLib.Plugins.Essentials.Accounts.Registration/src/Endpoints/RegistrationEntpoint.cs
deleted file mode 100644
index 74f2aa1..0000000
--- a/VNLib.Plugins.Essentials.Accounts.Registration/src/Endpoints/RegistrationEntpoint.cs
+++ /dev/null
@@ -1,392 +0,0 @@
-/*
-* Copyright (c) 2022 Vaughn Nugent
-*
-* Library: VNLib
-* Package: VNLib.Plugins.Essentials.Accounts.Registration
-* File: RegistrationEntpoint.cs
-*
-* RegistrationEntpoint.cs is part of VNLib.Plugins.Essentials.Accounts.Registration which is part of the larger
-* VNLib collection of libraries and utilities.
-*
-* VNLib.Plugins.Essentials.Accounts.Registration is free software: you can redistribute it and/or modify
-* it under the terms of the GNU Affero General Public License as
-* published by the Free Software Foundation, either version 3 of the
-* License, or (at your option) any later version.
-*
-* VNLib.Plugins.Essentials.Accounts.Registration is distributed in the hope that it will be useful,
-* but WITHOUT ANY WARRANTY; without even the implied warranty of
-* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-* GNU Affero General Public License for more details.
-*
-* You should have received a copy of the GNU Affero General Public License
-* along with this program. If not, see https://www.gnu.org/licenses/.
-*/
-
-using System;
-using System.Net;
-using System.Text.Json;
-using System.Threading.Tasks;
-using System.Security.Cryptography;
-
-using FluentValidation;
-
-using Emails.Transactional.Client;
-using Emails.Transactional.Client.Exceptions;
-
-using VNLib.Hashing;
-using VNLib.Utils.Memory;
-using VNLib.Utils.Logging;
-using VNLib.Utils.Extensions;
-using VNLib.Hashing.IdentityUtility;
-using VNLib.Net.Rest.Client;
-using VNLib.Net.Rest.Client.OAuth2;
-using VNLib.Plugins.Essentials.Users;
-using VNLib.Plugins.Essentials.Endpoints;
-using VNLib.Plugins.Essentials.Extensions;
-using VNLib.Plugins.Extensions.Loading;
-using VNLib.Plugins.Extensions.Loading.Sql;
-using VNLib.Plugins.Extensions.Loading.Events;
-using VNLib.Plugins.Extensions.Loading.Users;
-using VNLib.Plugins.Extensions.Validation;
-using VNLib.Plugins.Essentials.Accounts.Registration.TokenRevocation;
-using static VNLib.Plugins.Essentials.Accounts.AccountManager;
-
-
-namespace VNLib.Plugins.Essentials.Accounts.Registration.Endpoints
-{
-
- [ConfigurationName("registration")]
- internal sealed class RegistrationEntpoint : UnprotectedWebEndpoint, IIntervalScheduleable
- {
- /// <summary>
- /// Generates a CNG random buffer to use as a nonce
- /// </summary>
- private static string EntropyNonce => RandomHash.GetRandomHex(16);
-
- const string FAILED_AUTH_ERR = "Your registration does not exist, you should try to regisiter again.";
- const string REG_ERR_MESSAGE = "Please check your email inbox.";
-
- private HMAC SigAlg => new HMACSHA256(RegSignatureKey.Result);
-
- private readonly IUserManager Users;
- private readonly IValidator<string> RegJwtValdidator;
- private readonly PasswordHashing Passwords;
- private readonly RevokedTokenStore RevokedTokens;
- private readonly EmailSystemConfig Emails;
- private readonly Task<byte[]> RegSignatureKey;
- private readonly TimeSpan RegExpiresSec;
-
- /// <summary>
- /// Creates back-end functionality for a "registration" or "sign-up" page that integrates with the <see cref="AccountManager"/> plugin
- /// </summary>
- /// <param name="Path">The path identifier</param>
- /// <exception cref="ArgumentException"></exception>
- public RegistrationEntpoint(PluginBase plugin, IReadOnlyDictionary<string, JsonElement> config)
- {
- string? path = config["path"].GetString();
-
- InitPathAndLog(path, plugin.Log);
-
- RegExpiresSec = config["reg_expires_sec"].GetTimeSpan(TimeParseType.Seconds);
-
- //Init reg jwt validator
- RegJwtValdidator = GetJwtValidator();
-
- Passwords = plugin.GetPasswords();
- Users = plugin.GetUserManager();
- RevokedTokens = new(plugin.GetContextOptions());
- Emails = new(plugin);
-
- //Begin the async op to get the signature key from the vault
- RegSignatureKey = plugin.TryGetSecretAsync("reg_sig_key").ContinueWith((ts) => {
-
- using SecretResult? sr = ts.Result ?? throw new KeyNotFoundException("Missing required key 'reg_sig_key' in 'registration' configuration");
- return ts.Result.GetFromBase64();
-
- }, TaskScheduler.Default);
-
- //Register timeout for cleanup
- plugin.ScheduleInterval(this, TimeSpan.FromSeconds(60));
- }
-
- private static IValidator<string> GetJwtValidator()
- {
- InlineValidator<string> val = new();
-
- val.RuleFor(static s => s)
- .NotEmpty()
- //Must contain 2 periods for jwt limitation
- .Must(static s => s.Count(s => s == '.') == 2)
- //Guard length
- .Length(20, 500)
- .IllegalCharacters();
- return val;
- }
-
-
- protected override async ValueTask<VfReturnType> PostAsync(HttpEntity entity)
- {
- ValErrWebMessage webm = new();
- //Get the json request data from client
- using JsonDocument? request = await entity.GetJsonFromFileAsync();
-
- if(webm.Assert(request != null, "No request data present"))
- {
- entity.CloseResponseJson(HttpStatusCode.BadRequest, webm);
- return VfReturnType.VirtualSkip;
- }
-
- //Get the jwt string from client
- string? regJwt = request.RootElement.GetPropString("token");
- using PrivateString? password = (PrivateString?)request.RootElement.GetPropString("password");
-
- //validate inputs
- {
- if (webm.Assert(regJwt != null, FAILED_AUTH_ERR))
- {
- entity.CloseResponse(webm);
- return VfReturnType.VirtualSkip;
- }
-
- if (webm.Assert(password != null, "You must specify a password."))
- {
- entity.CloseResponse(webm);
- return VfReturnType.VirtualSkip;
- }
- //validate new password
- if(!AccountValidations.PasswordValidator.Validate((string)password, webm))
- {
- entity.CloseResponse(webm);
- return VfReturnType.VirtualSkip;
- }
- //Validate jwt
- if (webm.Assert(RegJwtValdidator.Validate(regJwt).IsValid, FAILED_AUTH_ERR))
- {
- entity.CloseResponse(webm);
- return VfReturnType.VirtualSkip;
- }
- }
-
- //Verify jwt has not been revoked
- if(await RevokedTokens.IsRevokedAsync(regJwt, entity.EventCancellation))
- {
- webm.Result = FAILED_AUTH_ERR;
- entity.CloseResponse(webm);
- return VfReturnType.VirtualSkip;
- }
-
- string emailAddress;
- try
- {
- //get jwt
- using JsonWebToken jwt = JsonWebToken.Parse(regJwt);
- //verify signature
- using (HMAC hmac = SigAlg)
- {
- bool verified = jwt.Verify(hmac);
-
- if (webm.Assert(verified, FAILED_AUTH_ERR))
- {
- entity.CloseResponse(webm);
- return VfReturnType.VirtualSkip;
- }
- }
-
- //recover iat and email address
- using JsonDocument reg = jwt.GetPayload();
- emailAddress = reg.RootElement.GetPropString("email")!;
- DateTimeOffset iat = DateTimeOffset.FromUnixTimeSeconds(reg.RootElement.GetProperty("iat").GetInt64());
-
- //Verify IAT against expiration at second resolution
- if (webm.Assert(iat.Add(RegExpiresSec) > DateTimeOffset.UtcNow, FAILED_AUTH_ERR))
- {
- entity.CloseResponse(webm);
- return VfReturnType.VirtualSkip;
- }
- }
- catch (FormatException fe)
- {
- Log.Debug(fe);
- webm.Result = FAILED_AUTH_ERR;
- entity.CloseResponse(webm);
- return VfReturnType.VirtualSkip;
- }
-
-
- //Always hash the new password, even if failed
- using PrivateString passHash = Passwords.Hash(password);
-
- try
- {
- //Generate userid from email
- string uid = GetRandomUserId();
-
- //Create the new user
- using IUser user = await Users.CreateUserAsync(uid, emailAddress, MINIMUM_LEVEL, passHash, entity.EventCancellation);
-
- //Set active status
- user.Status = UserStatus.Active;
- //set local account origin
- user.SetAccountOrigin(LOCAL_ACCOUNT_ORIGIN);
-
- //set user verification
- await user.ReleaseAsync();
-
- //Revoke token now complete
- _ = RevokedTokens.RevokeAsync(regJwt, CancellationToken.None).ConfigureAwait(false);
-
- webm.Result = "Successfully created your new account. You may now log in";
- webm.Success = true;
- entity.CloseResponse(webm);
- return VfReturnType.VirtualSkip;
- }
- //Capture creation failed, this may be a replay
- catch (UserExistsException)
- {
- }
- catch(UserCreationFailedException)
- {
- }
-
- webm.Result = FAILED_AUTH_ERR;
- entity.CloseResponse(webm);
- return VfReturnType.VirtualSkip;
- }
-
-
- private static readonly IReadOnlyDictionary<string, string> JWT_HEADER = new Dictionary<string, string>()
- {
- { "typ", "JWT" },
- { "alg", "HS256" }
- };
-
- protected override async ValueTask<VfReturnType> PutAsync(HttpEntity entity)
- {
- ValErrWebMessage webm = new();
-
- //Get the request
- RegRequestMessage? request = await entity.GetJsonFromFileAsync<RegRequestMessage>();
- if (webm.Assert(request != null, "Request is invalid"))
- {
- entity.CloseResponseJson(HttpStatusCode.BadRequest, webm);
- return VfReturnType.VirtualSkip;
- }
-
- //Validate the request
- if (!AccountValidations.RegRequestValidator.Validate(request, webm))
- {
- entity.CloseResponseJson(HttpStatusCode.UnprocessableEntity, webm);
- return VfReturnType.VirtualSkip;
- }
-
- //Create psudo contant time delay
- Task delay = Task.Delay(200);
-
- //See if a user account already exists
- using (IUser? user = await Users.GetUserFromEmailAsync(request.UserName!, entity.EventCancellation))
- {
- if (user != null)
- {
- goto Exit;
- }
- }
-
- //Get exact timestamp
- DateTimeOffset timeStamp = DateTimeOffset.UtcNow;
-
- //generate random nonce for entropy
- string entropy = EntropyNonce;
-
- //Init client jwt
- string jwtData;
- using (JsonWebToken emailJwt = new())
- {
-
- emailJwt.WriteHeader(JWT_HEADER);
-
- //Init new claim stack, include the same iat time, nonce for entropy, and descriptor storage id
- emailJwt.InitPayloadClaim(3)
- .AddClaim("iat", timeStamp.ToUnixTimeSeconds())
- .AddClaim("n", entropy)
- .AddClaim("email", request.UserName)
- .CommitClaims();
-
- //sign the jwt
- using (HMAC hmac = SigAlg)
- {
- emailJwt.Sign(hmac);
- }
- //Compile to encoded string
- jwtData = emailJwt.Compile();
- }
-
- string regUrl = $"https://{entity.Server.RequestUri.Authority}{Path}?t={jwtData}";
-
- //Send email to user in background task and do not await it
- _ = SendRegEmailAsync(request.UserName!, regUrl).ConfigureAwait(false);
-
- Exit:
- //await sort of constant time delay
- await delay;
-
- //Notify user
- webm.Result = REG_ERR_MESSAGE;
- webm.Success = true;
-
- entity.CloseResponse(webm);
- return VfReturnType.VirtualSkip;
- }
-
-
- private async Task SendRegEmailAsync(string emailAddress, string url)
- {
- try
- {
- //Get a new registration template
- EmailTransactionRequest emailTemplate = Emails.GetRegistrationMessage();
- //Add the user's to address
- emailTemplate.AddToAddress(emailAddress);
- emailTemplate.AddVariable("username", emailAddress);
- //Set the security code variable string
- emailTemplate.AddVariable("reg_url", url);
- emailTemplate.AddVariable("date", DateTimeOffset.UtcNow.ToString("f"));
-
- //Get a new client contract
- using ClientContract client = Emails.RestClientPool.Lease();
- //Send the email
- TransactionResult result = await client.Resource.SendEmailAsync(emailTemplate);
- if (!result.Success)
- {
- Log.Debug("Registration email failed to send, SMTP status code: {smtp}", result.SmtpStatus);
- }
- else
- {
- Log.Verbose("Registration email sent to user. Status {smtp}", result.SmtpStatus);
- }
- }
- catch (ValidationFailedException vf)
- {
- //This should only occur if there is a bug in our reigration code that allowed an invalid value pass
- Log.Debug(vf, "Registration email failed to send to user because data validation failed");
- }
- catch (InvalidAuthorizationException iae)
- {
- Log.Warn(iae, "Registration email failed to send due to an authentication error");
- }
- catch (OAuth2AuthenticationException o2e)
- {
- Log.Warn(o2e, "Registration email failed to send due to an authentication error");
- }
- catch (Exception ex)
- {
- Log.Error(ex);
- }
- }
-
- async Task IIntervalScheduleable.OnIntervalAsync(ILogProvider log, CancellationToken cancellationToken)
- {
- //Cleanup tokens
- await RevokedTokens.CleanTableAsync(RegExpiresSec, cancellationToken);
- }
- }
-} \ No newline at end of file
diff --git a/VNLib.Plugins.Essentials.Accounts.Registration/src/RegistrationContext.cs b/VNLib.Plugins.Essentials.Accounts.Registration/src/RegistrationContext.cs
deleted file mode 100644
index 611e30e..0000000
--- a/VNLib.Plugins.Essentials.Accounts.Registration/src/RegistrationContext.cs
+++ /dev/null
@@ -1,39 +0,0 @@
-/*
-* Copyright (c) 2022 Vaughn Nugent
-*
-* Library: VNLib
-* Package: VNLib.Plugins.Essentials.Accounts.Registration
-* File: RevocationContext.cs
-*
-* RevocationContext.cs is part of VNLib.Plugins.Essentials.Accounts.Registration which is part of the larger
-* VNLib collection of libraries and utilities.
-*
-* VNLib.Plugins.Essentials.Accounts.Registration is free software: you can redistribute it and/or modify
-* it under the terms of the GNU Affero General Public License as
-* published by the Free Software Foundation, either version 3 of the
-* License, or (at your option) any later version.
-*
-* VNLib.Plugins.Essentials.Accounts.Registration is distributed in the hope that it will be useful,
-* but WITHOUT ANY WARRANTY; without even the implied warranty of
-* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-* GNU Affero General Public License for more details.
-*
-* You should have received a copy of the GNU Affero General Public License
-* along with this program. If not, see https://www.gnu.org/licenses/.
-*/
-
-using Microsoft.EntityFrameworkCore;
-
-using VNLib.Plugins.Extensions.Data;
-using VNLib.Plugins.Essentials.Accounts.Registration.TokenRevocation;
-
-namespace VNLib.Plugins.Essentials.Accounts.Registration
-{
- internal class RegistrationContext : TransactionalDbContext
- {
- public DbSet<RevokedToken> RevokedRegistrationTokens { get; set; }
-
- public RegistrationContext(DbContextOptions options) : base(options)
- {}
- }
-} \ No newline at end of file
diff --git a/VNLib.Plugins.Essentials.Accounts.Registration/src/RegistrationEntryPoint.cs b/VNLib.Plugins.Essentials.Accounts.Registration/src/RegistrationEntryPoint.cs
deleted file mode 100644
index c24e7e0..0000000
--- a/VNLib.Plugins.Essentials.Accounts.Registration/src/RegistrationEntryPoint.cs
+++ /dev/null
@@ -1,66 +0,0 @@
-/*
-* Copyright (c) 2022 Vaughn Nugent
-*
-* Library: VNLib
-* Package: VNLib.Plugins.Essentials.Accounts.Registration
-* File: RegistrationEntryPoint.cs
-*
-* RegistrationEntryPoint.cs is part of VNLib.Plugins.Essentials.Accounts.Registration which is part of the larger
-* VNLib collection of libraries and utilities.
-*
-* VNLib.Plugins.Essentials.Accounts.Registration is free software: you can redistribute it and/or modify
-* it under the terms of the GNU Affero General Public License as
-* published by the Free Software Foundation, either version 3 of the
-* License, or (at your option) any later version.
-*
-* VNLib.Plugins.Essentials.Accounts.Registration is distributed in the hope that it will be useful,
-* but WITHOUT ANY WARRANTY; without even the implied warranty of
-* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-* GNU Affero General Public License for more details.
-*
-* You should have received a copy of the GNU Affero General Public License
-* along with this program. If not, see https://www.gnu.org/licenses/.
-*/
-
-using VNLib.Utils.Logging;
-
-using VNLib.Plugins.Extensions.Loading;
-using VNLib.Plugins.Extensions.Loading.Sql;
-using VNLib.Plugins.Extensions.Loading.Routing;
-using VNLib.Plugins.Essentials.Accounts.Registration.Endpoints;
-
-namespace VNLib.Plugins.Essentials.Accounts.Registration
-{
- public sealed class RegistrationEntryPoint : PluginBase
- {
- public override string PluginName => "Essentials.EmailRegistration";
-
- protected override void OnLoad()
- {
- try
- {
- //Route reg endpoint
- this.Route<RegistrationEntpoint>();
-
- Log.Information("Plugin loaded");
- }
- catch(KeyNotFoundException kne)
- {
- Log.Error("Missing required configuration variables: {ex}", kne.Message);
- }
- }
-
- protected override void OnUnLoad()
- {
- Log.Information("Plugin unloaded");
- }
-
- protected override void ProcessHostCommand(string cmd)
- {
- if (!this.IsDebug())
- {
- return;
- }
- }
- }
-} \ No newline at end of file
diff --git a/VNLib.Plugins.Essentials.Accounts.Registration/src/TokenRevocation/RevokedToken.cs b/VNLib.Plugins.Essentials.Accounts.Registration/src/TokenRevocation/RevokedToken.cs
deleted file mode 100644
index c2b7715..0000000
--- a/VNLib.Plugins.Essentials.Accounts.Registration/src/TokenRevocation/RevokedToken.cs
+++ /dev/null
@@ -1,43 +0,0 @@
-/*
-* Copyright (c) 2022 Vaughn Nugent
-*
-* Library: VNLib
-* Package: VNLib.Plugins.Essentials.Accounts.Registration
-* File: RevokedToken.cs
-*
-* RevokedToken.cs is part of VNLib.Plugins.Essentials.Accounts.Registration which is part of the larger
-* VNLib collection of libraries and utilities.
-*
-* VNLib.Plugins.Essentials.Accounts.Registration is free software: you can redistribute it and/or modify
-* it under the terms of the GNU Affero General Public License as
-* published by the Free Software Foundation, either version 3 of the
-* License, or (at your option) any later version.
-*
-* VNLib.Plugins.Essentials.Accounts.Registration is distributed in the hope that it will be useful,
-* but WITHOUT ANY WARRANTY; without even the implied warranty of
-* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-* GNU Affero General Public License for more details.
-*
-* You should have received a copy of the GNU Affero General Public License
-* along with this program. If not, see https://www.gnu.org/licenses/.
-*/
-
-using System;
-using System.ComponentModel.DataAnnotations;
-
-namespace VNLib.Plugins.Essentials.Accounts.Registration.TokenRevocation
-{
-
- internal class RevokedToken
- {
- /// <summary>
- /// The time the token was revoked.
- /// </summary>
- public DateTime Created { get; set; }
- /// <summary>
- /// The token that was revoked.
- /// </summary>
- [Key]
- public string? Token { get; set; }
- }
-} \ No newline at end of file
diff --git a/VNLib.Plugins.Essentials.Accounts.Registration/src/TokenRevocation/RevokedTokenStore.cs b/VNLib.Plugins.Essentials.Accounts.Registration/src/TokenRevocation/RevokedTokenStore.cs
deleted file mode 100644
index 89f4bd6..0000000
--- a/VNLib.Plugins.Essentials.Accounts.Registration/src/TokenRevocation/RevokedTokenStore.cs
+++ /dev/null
@@ -1,101 +0,0 @@
-/*
-* Copyright (c) 2022 Vaughn Nugent
-*
-* Library: VNLib
-* Package: VNLib.Plugins.Essentials.Accounts.Registration
-* File: RevokedTokenStore.cs
-*
-* RevokedTokenStore.cs is part of VNLib.Plugins.Essentials.Accounts.Registration which is part of the larger
-* VNLib collection of libraries and utilities.
-*
-* VNLib.Plugins.Essentials.Accounts.Registration is free software: you can redistribute it and/or modify
-* it under the terms of the GNU Affero General Public License as
-* published by the Free Software Foundation, either version 3 of the
-* License, or (at your option) any later version.
-*
-* VNLib.Plugins.Essentials.Accounts.Registration is distributed in the hope that it will be useful,
-* but WITHOUT ANY WARRANTY; without even the implied warranty of
-* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-* GNU Affero General Public License for more details.
-*
-* You should have received a copy of the GNU Affero General Public License
-* along with this program. If not, see https://www.gnu.org/licenses/.
-*/
-
-using System.Collections;
-
-using Microsoft.EntityFrameworkCore;
-
-using VNLib.Utils;
-
-namespace VNLib.Plugins.Essentials.Accounts.Registration.TokenRevocation
-{
- internal class RevokedTokenStore
- {
- private readonly DbContextOptions Options;
-
- public RevokedTokenStore(DbContextOptions options)
- {
- Options = options;
- }
-
- public async Task<bool> IsRevokedAsync(string token, CancellationToken cancellation)
- {
- await using RegistrationContext context = new (Options);
- await context.OpenTransactionAsync(cancellation);
-
- //Select any that match tokens
- bool any = await (from t in context.RevokedRegistrationTokens
- where t.Token == token
- select t).AnyAsync(cancellation);
-
- await context.CommitTransactionAsync(cancellation);
- return any;
- }
-
- public async Task RevokeAsync(string token, CancellationToken cancellation)
- {
- await using RegistrationContext context = new (Options);
- await context.OpenTransactionAsync(cancellation);
-
- //Add to table
- context.RevokedRegistrationTokens.Add(new RevokedToken()
- {
- Created = DateTime.UtcNow,
- Token = token
- });
-
- //Save changes and commit transaction
- await context.SaveChangesAsync(cancellation);
- await context.CommitTransactionAsync(cancellation);
- }
-
- /// <summary>
- /// Removes expired records from the store
- /// </summary>
- /// <param name="validFor">The time a token is valid for</param>
- /// <param name="cancellation">A token that cancels the async operation</param>
- /// <returns>The number of records evicted from the store</returns>
- public async Task<ERRNO> CleanTableAsync(TimeSpan validFor, CancellationToken cancellation)
- {
- DateTime expiredBefore = DateTime.UtcNow.Subtract(validFor);
-
- await using RegistrationContext context = new (Options);
- await context.OpenTransactionAsync(cancellation);
-
- //Select any that match tokens
- RevokedToken[] expired = await context.RevokedRegistrationTokens.Where(t => t.Created < expiredBefore)
- .Select(static t => t)
- .ToArrayAsync(cancellation);
-
-
- context.RevokedRegistrationTokens.RemoveRange(expired);
-
- ERRNO count =await context.SaveChangesAsync(cancellation);
-
- await context.CommitTransactionAsync(cancellation);
-
- return count;
- }
- }
-} \ No newline at end of file
diff --git a/VNLib.Plugins.Essentials.Accounts.Registration/src/VNLib.Plugins.Essentials.Accounts.Registration.csproj b/VNLib.Plugins.Essentials.Accounts.Registration/src/VNLib.Plugins.Essentials.Accounts.Registration.csproj
deleted file mode 100644
index 8b2264e..0000000
--- a/VNLib.Plugins.Essentials.Accounts.Registration/src/VNLib.Plugins.Essentials.Accounts.Registration.csproj
+++ /dev/null
@@ -1,54 +0,0 @@
-<Project Sdk="Microsoft.NET.Sdk">
-
- <PropertyGroup>
- <TargetFramework>net6.0</TargetFramework>
- <ImplicitUsings>enable</ImplicitUsings>
- <Nullable>enable</Nullable>
- <GenerateDocumentationFile>False</GenerateDocumentationFile>
- <Title>VNLib.Plugins.Essentials.Accounts.Registration</Title>
- <Authors>Vaughn Nugent</Authors>
- <Copyright>Copyright © 2022 Vaughn Nugent</Copyright>
- <PackageProjectUrl>https://www.vaughnnugent.com/resources</PackageProjectUrl>
- <ProduceReferenceAssembly>False</ProduceReferenceAssembly>
- <SignAssembly>False</SignAssembly>
- <AssemblyVersion>1.0.0.1</AssemblyVersion>
- <AssemblyName>Essentials.EmailRegistration</AssemblyName>
- <CheckForOverflowUnderflow>False</CheckForOverflowUnderflow>
- <SignAssembly>True</SignAssembly>
- <AssemblyOriginatorKeyFile>\\vaughnnugent.com\Internal\Folder Redirection\vman\Documents\Programming\Software\StrongNameingKey.snk</AssemblyOriginatorKeyFile>
- </PropertyGroup>
-
- <!-- Resolve nuget dll files and store them in the output dir -->
- <PropertyGroup>
- <!--Enable dynamic loading-->
- <EnableDynamicLoading>true</EnableDynamicLoading>
- <AnalysisLevel>latest-all</AnalysisLevel>
- </PropertyGroup>
- <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
- <Deterministic>False</Deterministic>
- </PropertyGroup>
- <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">
- <Deterministic>False</Deterministic>
- </PropertyGroup>
-
- <ItemGroup>
- <ProjectReference Include="..\..\..\Emails.Transactional\Emails.Transactional.Client\Emails.Transactional.Client.csproj" />
- <ProjectReference Include="..\..\..\Extensions\VNLib.Plugins.Extensions.Data\VNLib.Plugins.Extensions.Data.csproj" />
- <ProjectReference Include="..\..\..\Extensions\VNLib.Plugins.Extensions.Loading.Sql\VNLib.Plugins.Extensions.Loading.Sql.csproj" />
- <ProjectReference Include="..\..\..\Extensions\VNLib.Plugins.Extensions.Loading\VNLib.Plugins.Extensions.Loading.csproj" />
- <ProjectReference Include="..\..\..\Extensions\VNLib.Plugins.Extensions.Validation\VNLib.Plugins.Extensions.Validation.csproj" />
- <ProjectReference Include="..\..\..\PluginBase\VNLib.Plugins.PluginBase.csproj" />
- <ProjectReference Include="..\..\..\VNLib.Net.Rest.Client\VNLib.Net.Rest.Client.csproj" />
- </ItemGroup>
-
- <ItemGroup>
- <None Update="Essentials.EmailRegistration.json">
- <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
- </None>
- </ItemGroup>
-
- <Target Name="PostBuild" AfterTargets="PostBuildEvent">
- <Exec Command="start xcopy &quot;$(TargetDir)&quot; &quot;F:\Programming\Web Plugins\DevPlugins\$(TargetName)&quot; /E /Y /R" />
- </Target>
-
-</Project>