From dccc3cf3a51b74681354801b250e943a2a093663 Mon Sep 17 00:00:00 2001
From: Steven Kuhn <109609+stevenkuhn@users.noreply.github.com>
Date: Sun, 23 Oct 2022 20:37:47 -0500
Subject: [PATCH] Add robust testing and prune functionality (#1)
This adds testing for the applications, authorization, scope, and token stores. It also fixes methods that were discovered to be broken during testing, and adds prune functionality for authorizations and tokens.
---
.github/workflows/build.yml | 13 +-
.github/workflows/release.yml | 12 +-
.gitignore | 3 +
.nuke/build.schema.json | 6 +-
.vscode/launch.json | 26 +
.vscode/tasks.json | 41 +
build/Build.cs | 2 +
build/Build.v3.ncrunchproject | 5 +
.../OpenIddictLiteDBApplication.cs | 17 +-
.../OpenIddictLiteDBAuthorization.cs | 6 +-
.../OpenIddictLiteDBScope.cs | 12 +-
.../OpenIddictLiteDBToken.cs | 8 +-
.../Sknet.OpenIddict.LiteDB.Models.csproj | 2 +-
src/Sknet.OpenIddict.LiteDB.Models/Usings.cs | 3 +-
.../IOpenIddictLiteDBContext.cs | 2 +-
.../OpenIddictLiteDBExtensions.cs | 1 -
.../OpenIddictLiteDBOptions.cs | 16 +-
.../OpenIddictLiteDatabase.cs | 92 +++
.../OpenIddictLiteDBApplicationStore.cs | 60 +-
.../OpenIddictLiteDBAuthorizationStore.cs | 126 ++-
.../Stores/OpenIddictLiteDBScopeStore.cs | 46 +-
.../Stores/OpenIddictLiteDBTokenStore.cs | 104 +--
.../OpenIddictLiteDBApplicationFaker.cs | 72 ++
...OpenIddictLiteDBApplicationStoreBuilder.cs | 126 +++
.../OpenIddictLiteDBAuthorizationFaker.cs | 55 ++
...enIddictLiteDBAuthorizationStoreBuilder.cs | 85 ++
.../Builders/OpenIddictLiteDBScopeFaker.cs | 71 ++
.../OpenIddictLiteDBScopeStoreBuilder.cs | 65 ++
.../Builders/OpenIddictLiteDBTokenFaker.cs | 56 ++
.../OpenIddictLiteDBTokenStoreBuilder.cs | 79 ++
...dictLiteDBApplicationStoreResolverTests.cs | 2 +-
...ctLiteDBAuthorizationStoreResolverTests.cs | 2 +-
...OpenIddictLiteDBScopeStoreResolverTests.cs | 2 +-
...OpenIddictLiteDBTokenStoreResolverTests.cs | 2 +-
.../Sknet.OpenIddict.LiteDB.Tests.csproj | 9 +-
.../Stores/ILiteDatabaseExtensions.cs | 47 ++
.../OpenIddictLiteDBApplicationStoreTests.cs | 567 +++++++++++++
...OpenIddictLiteDBAuthorizationStoreTests.cs | 729 +++++++++++++++++
.../Stores/OpenIddictLiteDBScopeStoreTests.cs | 420 ++++++++++
.../Stores/OpenIddictLiteDBTokenStoreTests.cs | 773 ++++++++++++++++++
test/Sknet.OpenIddict.LiteDB.Tests/Usings.cs | 8 +
41 files changed, 3509 insertions(+), 264 deletions(-)
create mode 100644 .vscode/launch.json
create mode 100644 .vscode/tasks.json
create mode 100644 build/Build.v3.ncrunchproject
create mode 100644 src/Sknet.OpenIddict.LiteDB/OpenIddictLiteDatabase.cs
create mode 100644 test/Sknet.OpenIddict.LiteDB.Tests/Builders/OpenIddictLiteDBApplicationFaker.cs
create mode 100644 test/Sknet.OpenIddict.LiteDB.Tests/Builders/OpenIddictLiteDBApplicationStoreBuilder.cs
create mode 100644 test/Sknet.OpenIddict.LiteDB.Tests/Builders/OpenIddictLiteDBAuthorizationFaker.cs
create mode 100644 test/Sknet.OpenIddict.LiteDB.Tests/Builders/OpenIddictLiteDBAuthorizationStoreBuilder.cs
create mode 100644 test/Sknet.OpenIddict.LiteDB.Tests/Builders/OpenIddictLiteDBScopeFaker.cs
create mode 100644 test/Sknet.OpenIddict.LiteDB.Tests/Builders/OpenIddictLiteDBScopeStoreBuilder.cs
create mode 100644 test/Sknet.OpenIddict.LiteDB.Tests/Builders/OpenIddictLiteDBTokenFaker.cs
create mode 100644 test/Sknet.OpenIddict.LiteDB.Tests/Builders/OpenIddictLiteDBTokenStoreBuilder.cs
create mode 100644 test/Sknet.OpenIddict.LiteDB.Tests/Stores/ILiteDatabaseExtensions.cs
create mode 100644 test/Sknet.OpenIddict.LiteDB.Tests/Stores/OpenIddictLiteDBApplicationStoreTests.cs
create mode 100644 test/Sknet.OpenIddict.LiteDB.Tests/Stores/OpenIddictLiteDBAuthorizationStoreTests.cs
create mode 100644 test/Sknet.OpenIddict.LiteDB.Tests/Stores/OpenIddictLiteDBScopeStoreTests.cs
create mode 100644 test/Sknet.OpenIddict.LiteDB.Tests/Stores/OpenIddictLiteDBTokenStoreTests.cs
diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index 186d1d7..4926913 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -49,10 +49,9 @@ jobs:
path: artifacts
- name: Create release
- env:
- GitHub_Access_Token: ${{ secrets.GITHUB_TOKEN }}
- GitHub_Repository: ${{ github.repository }}
- run: ./build.sh --target PublishToGitHub --skip PublishArtifacts
-
-
-
+ run: >
+ ./build.sh
+ --target PublishToGitHub
+ --skip PublishArtifacts
+ --github-access-token ${{ secrets.GITHUB_TOKEN }}
+ --github-repository ${{ github.repository }}
diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
index dd3363f..f4d5601 100644
--- a/.github/workflows/release.yml
+++ b/.github/workflows/release.yml
@@ -29,8 +29,10 @@ jobs:
path: ./artifacts
token: ${{ secrets.GITHUB_TOKEN }}
- - name: Push assets to NuGet
- env:
- NuGet_Source: ${{ secrets.NUGET_SOURCE }}
- NuGet_ApiKey: ${{ secrets.NUGET_APIKEY }}
- run: ./build.sh --target PublishToNuGetFeed --skip PublishArtifacts
\ No newline at end of file
+ - name: Push assets to NuGet
+ run: >
+ ./build.sh
+ --target PublishToNuGetFeed
+ --skip PublishArtifacts
+ --nuget-api-key ${{ secrets.NUGET_APIKEY }}
+ --nuget-source ${{ secrets.NUGET_SOURCE }}
\ No newline at end of file
diff --git a/.gitignore b/.gitignore
index dfcfd56..667a9e1 100644
--- a/.gitignore
+++ b/.gitignore
@@ -348,3 +348,6 @@ MigrationBackup/
# Ionide (cross platform F# VS Code tools) working folder
.ionide/
+
+# VerifyTests files
+*.received.*
diff --git a/.nuke/build.schema.json b/.nuke/build.schema.json
index 0d5d962..b1137cd 100644
--- a/.nuke/build.schema.json
+++ b/.nuke/build.schema.json
@@ -20,7 +20,8 @@
},
"GitHubAccessToken": {
"type": "string",
- "description": "GitHub access token used for creating a new or updating an existing release"
+ "description": "GitHub access token used for creating a new or updating an existing release",
+ "default": "Secrets must be entered via 'nuke :secrets [profile]'"
},
"GitHubRepository": {
"type": "string",
@@ -57,7 +58,8 @@
},
"NuGetApiKey": {
"type": "string",
- "description": "NuGet API key used to pushing the NuGet package"
+ "description": "NuGet API key used to pushing the NuGet package",
+ "default": "Secrets must be entered via 'nuke :secrets [profile]'"
},
"NuGetSource": {
"type": "string",
diff --git a/.vscode/launch.json b/.vscode/launch.json
new file mode 100644
index 0000000..787c3b8
--- /dev/null
+++ b/.vscode/launch.json
@@ -0,0 +1,26 @@
+{
+ "version": "0.2.0",
+ "configurations": [
+ {
+ // Use IntelliSense to find out which attributes exist for C# debugging
+ // Use hover for the description of the existing attributes
+ // For further information visit https://github.com/OmniSharp/omnisharp-vscode/blob/master/debugger-launchjson.md
+ "name": ".NET Core Launch (console)",
+ "type": "coreclr",
+ "request": "launch",
+ "preLaunchTask": "build",
+ // If you have changed target frameworks, make sure to update the program path.
+ "program": "${workspaceFolder}/build/bin/Debug/net6.0/Build.dll",
+ "args": [],
+ "cwd": "${workspaceFolder}/build",
+ // For more information about the 'console' field, see https://aka.ms/VSCode-CS-LaunchJson-Console
+ "console": "internalConsole",
+ "stopAtEntry": false
+ },
+ {
+ "name": ".NET Core Attach",
+ "type": "coreclr",
+ "request": "attach"
+ }
+ ]
+}
\ No newline at end of file
diff --git a/.vscode/tasks.json b/.vscode/tasks.json
new file mode 100644
index 0000000..eb9110d
--- /dev/null
+++ b/.vscode/tasks.json
@@ -0,0 +1,41 @@
+{
+ "version": "2.0.0",
+ "tasks": [
+ {
+ "label": "build",
+ "command": "dotnet",
+ "type": "process",
+ "args": [
+ "build",
+ "${workspaceFolder}/build/Build.csproj",
+ "/property:GenerateFullPaths=true",
+ "/consoleloggerparameters:NoSummary"
+ ],
+ "problemMatcher": "$msCompile"
+ },
+ {
+ "label": "publish",
+ "command": "dotnet",
+ "type": "process",
+ "args": [
+ "publish",
+ "${workspaceFolder}/build/Build.csproj",
+ "/property:GenerateFullPaths=true",
+ "/consoleloggerparameters:NoSummary"
+ ],
+ "problemMatcher": "$msCompile"
+ },
+ {
+ "label": "watch",
+ "command": "dotnet",
+ "type": "process",
+ "args": [
+ "watch",
+ "run",
+ "--project",
+ "${workspaceFolder}/build/Build.csproj"
+ ],
+ "problemMatcher": "$msCompile"
+ }
+ ]
+}
\ No newline at end of file
diff --git a/build/Build.cs b/build/Build.cs
index 197d570..bcb730b 100644
--- a/build/Build.cs
+++ b/build/Build.cs
@@ -7,6 +7,7 @@ class Build : NukeBuild
readonly Configuration Configuration = IsLocalBuild ? Configuration.Debug : Configuration.Release;
[Parameter("GitHub access token used for creating a new or updating an existing release.")]
+ [Secret]
readonly string GitHubAccessToken;
[Parameter("GitHub repository owner and name used for creating a new or updating an existing release. For example: 'stevenkuhn/openiddict-litedb'.")]
@@ -16,6 +17,7 @@ class Build : NukeBuild
readonly string NuGetSource = "https://api.nuget.org/v3/index.json";
[Parameter("NuGet API key used to pushing the NuGet package.")]
+ [Secret]
readonly string NuGetApiKey;
[Solution] readonly Solution Solution;
diff --git a/build/Build.v3.ncrunchproject b/build/Build.v3.ncrunchproject
new file mode 100644
index 0000000..319cd52
--- /dev/null
+++ b/build/Build.v3.ncrunchproject
@@ -0,0 +1,5 @@
+
+
+ True
+
+
\ No newline at end of file
diff --git a/src/Sknet.OpenIddict.LiteDB.Models/OpenIddictLiteDBApplication.cs b/src/Sknet.OpenIddict.LiteDB.Models/OpenIddictLiteDBApplication.cs
index d61a228..cc2c797 100644
--- a/src/Sknet.OpenIddict.LiteDB.Models/OpenIddictLiteDBApplication.cs
+++ b/src/Sknet.OpenIddict.LiteDB.Models/OpenIddictLiteDBApplication.cs
@@ -57,8 +57,8 @@ public class OpenIddictLiteDBApplication
/// Gets or sets the localized display names associated with the current application.
///
[BsonField("display_names")]
- public virtual IReadOnlyDictionary? DisplayNames { get; set; }
- = ImmutableDictionary.Create();
+ public virtual ImmutableDictionary? DisplayNames { get; set; }
+ = ImmutableDictionary.Create();
///
/// Gets or sets the unique identifier associated with the current application.
@@ -70,35 +70,34 @@ public class OpenIddictLiteDBApplication
/// Gets or sets the permissions associated with the current application.
///
[BsonField("permissions")]
- public virtual IReadOnlyList? Permissions { get; set; } = ImmutableList.Create();
+ public virtual ImmutableArray? Permissions { get; set; } = ImmutableArray.Create();
///
/// Gets or sets the logout callback URLs associated with the current application.
///
[BsonField("post_logout_redirect_uris")]
- public virtual IReadOnlyList? PostLogoutRedirectUris { get; set; } = ImmutableList.Create();
+ public virtual ImmutableArray? PostLogoutRedirectUris { get; set; } = ImmutableArray.Create();
///
/// Gets or sets the additional properties associated with the current application.
///
[BsonField("properties")]
- public virtual IReadOnlyDictionary? Properties { get; set; }
+ public virtual ImmutableDictionary? Properties { get; set; }
///
/// Gets or sets the callback URLs associated with the current application.
///
[BsonField("redirect_uris")]
- public virtual IReadOnlyList? RedirectUris { get; set; } = ImmutableList.Create();
+ public virtual ImmutableArray? RedirectUris { get; set; } = ImmutableArray.Create();
///
/// Gets or sets the requirements associated with the current application.
///
[BsonField("requirements")]
- public virtual IReadOnlyList? Requirements { get; set; } = ImmutableList.Create();
+ public virtual ImmutableArray? Requirements { get; set; } = ImmutableArray.Create();
///
- /// Gets or sets the application type
- /// associated with the current application.
+ /// Gets or sets the application type associated with the current application.
///
[BsonField("type")]
public virtual string? Type { get; set; }
diff --git a/src/Sknet.OpenIddict.LiteDB.Models/OpenIddictLiteDBAuthorization.cs b/src/Sknet.OpenIddict.LiteDB.Models/OpenIddictLiteDBAuthorization.cs
index 82c13d6..00bced4 100644
--- a/src/Sknet.OpenIddict.LiteDB.Models/OpenIddictLiteDBAuthorization.cs
+++ b/src/Sknet.OpenIddict.LiteDB.Models/OpenIddictLiteDBAuthorization.cs
@@ -37,7 +37,7 @@ public class OpenIddictLiteDBAuthorization
/// Gets or sets the UTC creation date of the current authorization.
///
[BsonField("creation_date")]
- public virtual DateTime? CreationDate { get; set; }
+ public virtual DateTimeOffset? CreationDate { get; set; }
///
/// Gets or sets the unique identifier associated with the current authorization.
@@ -49,13 +49,13 @@ public class OpenIddictLiteDBAuthorization
/// Gets or sets the additional properties associated with the current authorization.
///
[BsonField("properties")]
- public virtual IReadOnlyDictionary? Properties { get; set; }
+ public virtual ImmutableDictionary? Properties { get; set; }
///
/// Gets or sets the scopes associated with the current authorization.
///
[BsonField("scopes")]
- public virtual IReadOnlyList? Scopes { get; set; } = ImmutableList.Create();
+ public virtual ImmutableArray? Scopes { get; set; } = ImmutableArray.Create();
///
/// Gets or sets the status of the current authorization.
diff --git a/src/Sknet.OpenIddict.LiteDB.Models/OpenIddictLiteDBScope.cs b/src/Sknet.OpenIddict.LiteDB.Models/OpenIddictLiteDBScope.cs
index 8f76a80..dad8b94 100644
--- a/src/Sknet.OpenIddict.LiteDB.Models/OpenIddictLiteDBScope.cs
+++ b/src/Sknet.OpenIddict.LiteDB.Models/OpenIddictLiteDBScope.cs
@@ -37,8 +37,8 @@ public class OpenIddictLiteDBScope
/// Gets or sets the localized public descriptions associated with the current scope.
///
[BsonField("descriptions")]
- public virtual IReadOnlyDictionary? Descriptions { get; set; }
- = ImmutableDictionary.Create();
+ public virtual ImmutableDictionary? Descriptions { get; set; }
+ = ImmutableDictionary.Create();
///
/// Gets or sets the display name associated with the current scope.
@@ -50,8 +50,8 @@ public class OpenIddictLiteDBScope
/// Gets or sets the localized display names associated with the current scope.
///
[BsonField("display_names")]
- public virtual IReadOnlyDictionary? DisplayNames { get; set; }
- = ImmutableDictionary.Create();
+ public virtual ImmutableDictionary? DisplayNames { get; set; }
+ = ImmutableDictionary.Create();
///
/// Gets or sets the unique identifier associated with the current scope.
@@ -69,11 +69,11 @@ public class OpenIddictLiteDBScope
/// Gets or sets the additional properties associated with the current scope.
///
[BsonField("properties")]
- public virtual IReadOnlyDictionary? Properties { get; set; }
+ public virtual ImmutableDictionary? Properties { get; set; }
///
/// Gets or sets the resources associated with the current scope.
///
[BsonField("resources")]
- public virtual IReadOnlyList? Resources { get; set; } = ImmutableList.Create();
+ public virtual ImmutableArray? Resources { get; set; } = ImmutableArray.Create();
}
diff --git a/src/Sknet.OpenIddict.LiteDB.Models/OpenIddictLiteDBToken.cs b/src/Sknet.OpenIddict.LiteDB.Models/OpenIddictLiteDBToken.cs
index 859b9e3..a118030 100644
--- a/src/Sknet.OpenIddict.LiteDB.Models/OpenIddictLiteDBToken.cs
+++ b/src/Sknet.OpenIddict.LiteDB.Models/OpenIddictLiteDBToken.cs
@@ -43,13 +43,13 @@ public class OpenIddictLiteDBToken
/// Gets or sets the UTC creation date of the current token.
///
[BsonField("creation_date")]
- public virtual DateTime? CreationDate { get; set; }
+ public virtual DateTimeOffset? CreationDate { get; set; }
///
/// Gets or sets the UTC expiration date of the current token.
///
[BsonField("expiration_date")]
- public virtual DateTime? ExpirationDate { get; set; }
+ public virtual DateTimeOffset? ExpirationDate { get; set; }
///
/// Gets or sets the unique identifier associated with the current token.
@@ -69,13 +69,13 @@ public class OpenIddictLiteDBToken
/// Gets or sets the additional properties associated with the current token.
///
[BsonField("properties")]
- public virtual IReadOnlyDictionary? Properties { get; set; }
+ public virtual ImmutableDictionary? Properties { get; set; }
///
/// Gets or sets the UTC redemption date of the current token.
///
[BsonField("redemption_date")]
- public virtual DateTime? RedemptionDate { get; set; }
+ public virtual DateTimeOffset? RedemptionDate { get; set; }
///
/// Gets or sets the reference identifier associated
diff --git a/src/Sknet.OpenIddict.LiteDB.Models/Sknet.OpenIddict.LiteDB.Models.csproj b/src/Sknet.OpenIddict.LiteDB.Models/Sknet.OpenIddict.LiteDB.Models.csproj
index 8c0f9ab..4d1a1dd 100644
--- a/src/Sknet.OpenIddict.LiteDB.Models/Sknet.OpenIddict.LiteDB.Models.csproj
+++ b/src/Sknet.OpenIddict.LiteDB.Models/Sknet.OpenIddict.LiteDB.Models.csproj
@@ -40,7 +40,7 @@
-
+
diff --git a/src/Sknet.OpenIddict.LiteDB.Models/Usings.cs b/src/Sknet.OpenIddict.LiteDB.Models/Usings.cs
index 954fe3d..5b02c7d 100644
--- a/src/Sknet.OpenIddict.LiteDB.Models/Usings.cs
+++ b/src/Sknet.OpenIddict.LiteDB.Models/Usings.cs
@@ -1,4 +1,5 @@
global using LiteDB;
global using System.Collections.Immutable;
global using System.Diagnostics;
-global using System.Text.Json;
\ No newline at end of file
+global using System.Globalization;
+global using System.Text.Json;
diff --git a/src/Sknet.OpenIddict.LiteDB/IOpenIddictLiteDBContext.cs b/src/Sknet.OpenIddict.LiteDB/IOpenIddictLiteDBContext.cs
index 68a872f..7fa816c 100644
--- a/src/Sknet.OpenIddict.LiteDB/IOpenIddictLiteDBContext.cs
+++ b/src/Sknet.OpenIddict.LiteDB/IOpenIddictLiteDBContext.cs
@@ -24,7 +24,7 @@ public interface IOpenIddictLiteDBContext
/// Gets the .
///
///
- /// A that can be used to monitor the
+ /// A that can be used to monitor the
/// asynchronous operation, whose result returns the LiteDB database.
///
ValueTask GetDatabaseAsync(CancellationToken cancellationToken);
diff --git a/src/Sknet.OpenIddict.LiteDB/OpenIddictLiteDBExtensions.cs b/src/Sknet.OpenIddict.LiteDB/OpenIddictLiteDBExtensions.cs
index 05b6f5f..fb0d6d2 100644
--- a/src/Sknet.OpenIddict.LiteDB/OpenIddictLiteDBExtensions.cs
+++ b/src/Sknet.OpenIddict.LiteDB/OpenIddictLiteDBExtensions.cs
@@ -15,7 +15,6 @@
*/
namespace Microsoft.Extensions.DependencyInjection;
-
///
/// Exposes extensions allowing to register the OpenIddict LiteDB services.
///
diff --git a/src/Sknet.OpenIddict.LiteDB/OpenIddictLiteDBOptions.cs b/src/Sknet.OpenIddict.LiteDB/OpenIddictLiteDBOptions.cs
index 1c2dbb3..99501a4 100644
--- a/src/Sknet.OpenIddict.LiteDB/OpenIddictLiteDBOptions.cs
+++ b/src/Sknet.OpenIddict.LiteDB/OpenIddictLiteDBOptions.cs
@@ -21,14 +21,14 @@ namespace Sknet.OpenIddict.LiteDB;
public class OpenIddictLiteDBOptions
{
///
- /// Gets or sets the name of the applications collection (by default, openiddict.applications).
+ /// Gets or sets the name of the applications collection (by default, openiddict_applications).
///
- public string ApplicationsCollectionName { get; set; } = "openiddict.applications";
+ public string ApplicationsCollectionName { get; set; } = "openiddict_applications";
///
- /// Gets or sets the name of the authorizations collection (by default, openiddict.authorizations).
+ /// Gets or sets the name of the authorizations collection (by default, openiddict_authorizations).
///
- public string AuthorizationsCollectionName { get; set; } = "openiddict.authorizations";
+ public string AuthorizationsCollectionName { get; set; } = "openiddict_authorizations";
///
/// Gets or sets the used by the OpenIddict stores.
@@ -37,12 +37,12 @@ public class OpenIddictLiteDBOptions
public ILiteDatabase? Database { get; set; }
///
- /// Gets or sets the name of the scopes collection (by default, openiddict.scopes).
+ /// Gets or sets the name of the scopes collection (by default, openiddict_scopes).
///
- public string ScopesCollectionName { get; set; } = "openiddict.scopes";
+ public string ScopesCollectionName { get; set; } = "openiddict_scopes";
///
- /// Gets or sets the name of the tokens collection (by default, openiddict.tokens).
+ /// Gets or sets the name of the tokens collection (by default, openiddict_tokens).
///
- public string TokensCollectionName { get; set; } = "openiddict.tokens";
+ public string TokensCollectionName { get; set; } = "openiddict_tokens";
}
\ No newline at end of file
diff --git a/src/Sknet.OpenIddict.LiteDB/OpenIddictLiteDatabase.cs b/src/Sknet.OpenIddict.LiteDB/OpenIddictLiteDatabase.cs
new file mode 100644
index 0000000..ec0eb81
--- /dev/null
+++ b/src/Sknet.OpenIddict.LiteDB/OpenIddictLiteDatabase.cs
@@ -0,0 +1,92 @@
+/*
+ * Copyright (c) 2022 Steven Kuhn and contributors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+using LiteDB.Engine;
+using JsonSerializer = System.Text.Json.JsonSerializer;
+
+namespace Sknet.OpenIddict.LiteDB;
+
+///
+public class OpenIddictLiteDatabase : LiteDatabase
+{
+ ///
+ public OpenIddictLiteDatabase(string connectionString, BsonMapper? mapper = null)
+ : base(connectionString, mapper)
+ {
+ ConfigureMapper();
+ }
+
+ ///
+ public OpenIddictLiteDatabase(ConnectionString connectionString, BsonMapper? mapper = null)
+ : base(connectionString, mapper)
+ {
+ ConfigureMapper();
+ }
+
+ ///
+ public OpenIddictLiteDatabase(Stream stream, BsonMapper? mapper = null, Stream? logStream = null)
+ : base(stream, mapper, logStream)
+ {
+ ConfigureMapper();
+ }
+
+ ///
+ public OpenIddictLiteDatabase(ILiteEngine engine, BsonMapper? mapper = null, bool disposeOnClose = true)
+ : base(engine, mapper, disposeOnClose)
+ {
+ ConfigureMapper();
+ }
+
+ private void ConfigureMapper()
+ {
+ if (Mapper == null)
+ {
+ throw new NotImplementedException();
+ }
+
+ Mapper.RegisterType>
+ (
+ serialize: dictionary =>
+ {
+ if (dictionary == null) { return null; }
+
+ var document = new BsonDocument();
+ foreach (var pair in dictionary)
+ {
+ document.Add(pair.Key.Name, pair.Value);
+ }
+ return document;
+ },
+ deserialize: bson => bson.AsDocument.ToImmutableDictionary(
+ pair => new CultureInfo(pair.Key),
+ pair => pair.Value.AsString)
+ );
+ Mapper.RegisterType>
+ (
+ serialize: items => items != null
+ ? new BsonArray(items.Select(x => new BsonValue(x)))
+ : null,
+ deserialize: bson => bson.AsArray.Select(x => x.AsString).ToImmutableArray()
+ );
+ Mapper.RegisterType>
+ (
+ serialize: dictionary => dictionary != null
+ ? JsonSerializer.Serialize(dictionary)
+ : null,
+ deserialize: bson => JsonSerializer.Deserialize>(bson.AsString)
+ ?? ImmutableDictionary.Empty
+ );
+ }
+}
diff --git a/src/Sknet.OpenIddict.LiteDB/Stores/OpenIddictLiteDBApplicationStore.cs b/src/Sknet.OpenIddict.LiteDB/Stores/OpenIddictLiteDBApplicationStore.cs
index 836bdae..fdaf287 100644
--- a/src/Sknet.OpenIddict.LiteDB/Stores/OpenIddictLiteDBApplicationStore.cs
+++ b/src/Sknet.OpenIddict.LiteDB/Stores/OpenIddictLiteDBApplicationStore.cs
@@ -133,7 +133,7 @@ public virtual async ValueTask DeleteAsync(TApplication application, Cancellatio
var database = await Context.GetDatabaseAsync(cancellationToken);
var collection = database.GetCollection(Options.CurrentValue.ApplicationsCollectionName);
- return collection.FindById(identifier);
+ return collection.FindById(new ObjectId(identifier));
}
///
@@ -273,12 +273,10 @@ public virtual ValueTask> GetDisplayNam
if (application.DisplayNames is not { Count: > 0 })
{
- return new(ImmutableDictionary.Create());
+ return new(ImmutableDictionary.Empty);
}
- return new(application.DisplayNames.ToImmutableDictionary(
- pair => CultureInfo.GetCultureInfo(pair.Key),
- pair => pair.Value));
+ return new(application.DisplayNames);
}
///
@@ -301,12 +299,12 @@ public virtual ValueTask> GetPermissionsAsync(
throw new ArgumentNullException(nameof(application));
}
- if (application.Permissions is not { Count: > 0 })
+ if (application.Permissions is not { Length: > 0 })
{
- return new(ImmutableArray.Create());
+ return new(ImmutableArray.Empty);
}
- return new(application.Permissions.ToImmutableArray());
+ return new(application.Permissions.Value);
}
///
@@ -318,12 +316,12 @@ public virtual ValueTask> GetPostLogoutRedirectUrisAsync(
throw new ArgumentNullException(nameof(application));
}
- if (application.PostLogoutRedirectUris is not { Count: > 0 })
+ if (application.PostLogoutRedirectUris is not { Length: > 0 })
{
- return new(ImmutableArray.Create());
+ return new(ImmutableArray.Empty);
}
- return new(application.PostLogoutRedirectUris.ToImmutableArray());
+ return new(application.PostLogoutRedirectUris.Value);
}
///
@@ -336,10 +334,10 @@ public virtual ValueTask> GetProperties
if (application.Properties is null || application.Properties.Count == 0)
{
- return new(ImmutableDictionary.Create());
+ return new(ImmutableDictionary.Empty);
}
- return new(application.Properties.ToImmutableDictionary());
+ return new(application.Properties);
}
///
@@ -351,12 +349,12 @@ public virtual ValueTask> GetRedirectUrisAsync(
throw new ArgumentNullException(nameof(application));
}
- if (application.RedirectUris is not { Count: > 0 })
+ if (application.RedirectUris is not { Length: > 0 })
{
- return new(ImmutableArray.Create());
+ return new(ImmutableArray.Empty);
}
- return new(application.RedirectUris.ToImmutableArray());
+ return new(application.RedirectUris.Value);
}
///
@@ -367,12 +365,12 @@ public virtual ValueTask> GetRequirementsAsync(TApplicati
throw new ArgumentNullException(nameof(application));
}
- if (application.Requirements is not { Count: > 0 })
+ if (application.Requirements is not { Length: > 0 })
{
- return new(ImmutableArray.Create());
+ return new(ImmutableArray.Empty);
}
- return new(application.Requirements.ToImmutableArray());
+ return new(application.Requirements.Value);
}
///
@@ -515,14 +513,10 @@ public virtual ValueTask SetDisplayNamesAsync(TApplication application,
if (names is not { Count: > 0 })
{
application.DisplayNames = null;
-
return default;
}
- application.DisplayNames = names.ToImmutableDictionary(
- pair => pair.Key.Name,
- pair => pair.Value);
-
+ application.DisplayNames = names;
return default;
}
@@ -537,12 +531,10 @@ public virtual ValueTask SetPermissionsAsync(TApplication application, Immutable
if (permissions.IsDefaultOrEmpty)
{
application.Permissions = null;
-
return default;
}
- application.Permissions = permissions.ToImmutableList();
-
+ application.Permissions = permissions;
return default;
}
@@ -558,12 +550,10 @@ public virtual ValueTask SetPostLogoutRedirectUrisAsync(TApplication application
if (addresses.IsDefaultOrEmpty)
{
application.PostLogoutRedirectUris = null;
-
return default;
}
- application.PostLogoutRedirectUris = addresses.ToImmutableList();
-
+ application.PostLogoutRedirectUris = addresses;
return default;
}
@@ -579,12 +569,10 @@ public virtual ValueTask SetPropertiesAsync(TApplication application,
if (properties is not { Count: > 0 })
{
application.Properties = null;
-
return default;
}
application.Properties = properties;
-
return default;
}
@@ -600,12 +588,10 @@ public virtual ValueTask SetRedirectUrisAsync(TApplication application,
if (addresses.IsDefaultOrEmpty)
{
application.RedirectUris = null;
-
return default;
}
- application.RedirectUris = addresses.ToImmutableList();
-
+ application.RedirectUris = addresses;
return default;
}
@@ -621,12 +607,10 @@ public virtual ValueTask SetRequirementsAsync(TApplication application,
if (requirements.IsDefaultOrEmpty)
{
application.Requirements = null;
-
return default;
}
- application.Requirements = requirements.ToImmutableList();
-
+ application.Requirements = requirements;
return default;
}
diff --git a/src/Sknet.OpenIddict.LiteDB/Stores/OpenIddictLiteDBAuthorizationStore.cs b/src/Sknet.OpenIddict.LiteDB/Stores/OpenIddictLiteDBAuthorizationStore.cs
index 3a29104..7270a3f 100644
--- a/src/Sknet.OpenIddict.LiteDB/Stores/OpenIddictLiteDBAuthorizationStore.cs
+++ b/src/Sknet.OpenIddict.LiteDB/Stores/OpenIddictLiteDBAuthorizationStore.cs
@@ -13,6 +13,8 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+using static OpenIddict.Abstractions.OpenIddictConstants;
+
namespace Sknet.OpenIddict.LiteDB;
///
@@ -140,8 +142,8 @@ async IAsyncEnumerable ExecuteAsync([EnumeratorCancellation] Can
///
public virtual IAsyncEnumerable FindAsync(
- string subject, string client,
- string status, CancellationToken cancellationToken)
+ string subject, string client, string status,
+ CancellationToken cancellationToken)
{
if (string.IsNullOrEmpty(subject))
{
@@ -181,8 +183,8 @@ async IAsyncEnumerable ExecuteAsync([EnumeratorCancellation] Can
///
public virtual IAsyncEnumerable FindAsync(
- string subject, string client,
- string status, string type, CancellationToken cancellationToken)
+ string subject, string client, string status, string type,
+ CancellationToken cancellationToken)
{
if (string.IsNullOrEmpty(subject))
{
@@ -228,8 +230,7 @@ async IAsyncEnumerable ExecuteAsync([EnumeratorCancellation] Can
///
public virtual IAsyncEnumerable FindAsync(
- string subject, string client,
- string status, string type,
+ string subject, string client, string status, string type,
ImmutableArray scopes, CancellationToken cancellationToken)
{
if (string.IsNullOrEmpty(subject))
@@ -246,7 +247,7 @@ public virtual IAsyncEnumerable FindAsync(
{
throw new ArgumentException("The status cannot be null or empty.", nameof(status));
}
-
+
if (string.IsNullOrEmpty(type))
{
throw new ArgumentException("The type cannot be null or empty.", nameof(type));
@@ -258,15 +259,16 @@ async IAsyncEnumerable ExecuteAsync([EnumeratorCancellation] Can
{
var database = await Context.GetDatabaseAsync(cancellationToken);
var collection = database.GetCollection(Options.CurrentValue.AuthorizationsCollectionName);
-
+
var authorizations = collection.Query()
.Where(entity =>
entity.Subject == subject &&
entity.ApplicationId == new ObjectId(client) &&
entity.Status == status &&
- entity.Type == type &&
- Enumerable.All(scopes, scope => entity.Scopes != null && entity.Scopes.Contains(scope)))
- .ToEnumerable().ToAsyncEnumerable();
+ entity.Type == type)
+ .ToEnumerable()
+ .Where(entity => scopes.All(scope => entity.Scopes!.Contains(scope)))
+ .ToAsyncEnumerable();
await foreach (var authorization in authorizations)
{
@@ -313,7 +315,7 @@ async IAsyncEnumerable ExecuteAsync([EnumeratorCancellation] Can
var database = await Context.GetDatabaseAsync(cancellationToken);
var collection = database.GetCollection(Options.CurrentValue.AuthorizationsCollectionName);
- return collection.FindById(identifier);
+ return collection.FindById(new ObjectId(identifier));
}
///
@@ -390,7 +392,7 @@ async IAsyncEnumerable ExecuteAsync([EnumeratorCancellation] Can
return new(result: null);
}
- return new(DateTime.SpecifyKind(authorization.CreationDate.Value, DateTimeKind.Utc));
+ return new(authorization.CreationDate);
}
///
@@ -414,10 +416,10 @@ public virtual ValueTask> GetProperties
if (authorization.Properties is null || authorization.Properties.Count == 0)
{
- return new(ImmutableDictionary.Create());
+ return new(ImmutableDictionary.Empty);
}
- return new(authorization.Properties.ToImmutableDictionary());
+ return new(authorization.Properties);
}
///
@@ -428,12 +430,12 @@ public virtual ValueTask> GetScopesAsync(TAuthorization a
throw new ArgumentNullException(nameof(authorization));
}
- if (authorization.Scopes is not { Count: > 0 })
+ if (authorization.Scopes is not { Length: > 0 })
{
- return new(ImmutableArray.Create());
+ return new(ImmutableArray.Empty);
}
- return new(authorization.Scopes.ToImmutableArray());
+ return new(authorization.Scopes.Value);
}
///
@@ -526,57 +528,46 @@ async IAsyncEnumerable ExecuteAsync([EnumeratorCancellation] Cancellati
}
}
}
-
+
///
- public virtual ValueTask PruneAsync(DateTimeOffset threshold, CancellationToken cancellationToken)
+ public async virtual ValueTask PruneAsync(DateTimeOffset threshold, CancellationToken cancellationToken)
{
- throw new NotImplementedException();
-
- //var database = await Context.GetDatabaseAsync(cancellationToken);
- //var collection = database.GetCollection(Options.CurrentValue.AuthorizationsCollectionName);
-
- // Note: directly deleting the resulting set of an aggregate query is not supported by MongoDb
- // To work around this limitation, the authorization identifiers are stored in an intermediate
- // list and delete requests are sent to remove the documents corresponding to these identifiers.
-
- //var identifiers =
- // await (from authorization in collection.AsQueryable()
- // join token in database.GetCollection(Options.CurrentValue.TokensCollectionName).AsQueryable()
- // on authorization.Id equals token.AuthorizationId into tokens
- // where authorization.CreationDate < threshold.UtcDateTime
- // where authorization.Status != Statuses.Valid ||
- // (authorization.Type == AuthorizationTypes.AdHoc && !tokens.Any())
- // select authorization.Id).ToListAsync(cancellationToken);
-
- // Note: to avoid generating delete requests with very large filters, a buffer is used here and the
- // maximum number of elements that can be removed by a single call to PruneAsync() is deliberately limited.
- //foreach (var buffer in Buffer(identifiers.Take(1_000_000), 1_000))
- //{
- // await collection.DeleteManyAsync(authorization => buffer.Contains(authorization.Id), cancellationToken);
- //}
+ var database = await Context.GetDatabaseAsync(cancellationToken);
+ var authorizationCollection = database.GetCollection(Options.CurrentValue.AuthorizationsCollectionName);
- //static IEnumerable> Buffer(IEnumerable source, int count)
- //{
- // List? buffer = null;
+ var query = from auth in authorizationCollection.Query()
+ // only prune authorizations created before threshold
+ where auth.CreationDate < threshold.UtcDateTime
+ // prune tokens that are not valid
+ where auth.Status != Statuses.Valid
+ select auth.Id;
+ var authorizations = query.ToList();
- // foreach (var element in source)
- // {
- // buffer ??= new List(capacity: 1);
- // buffer.Add(element);
+ // prune adhoc authorizations that have no tokens
+ query = from auth in authorizationCollection.Query()
+ where auth.CreationDate < threshold.UtcDateTime
+ where auth.Status == Statuses.Valid
+ where auth.Type == AuthorizationTypes.AdHoc
+ select auth.Id;
+ var adhocAuthorizations = new HashSet(query.ToEnumerable());
- // if (buffer.Count == count)
- // {
- // yield return buffer;
+ var tokenCollection = database.GetCollection(Options.CurrentValue.TokensCollectionName);
+ var adhocAuthorizationsWithTokens = new HashSet(tokenCollection.Query()
+ .GroupBy("authorization_id")
+ .Where(x => adhocAuthorizations.Contains(x["authorization_id"]))
+ .Select("{authorization_id:@key}")
+ .ToEnumerable()
+ .Select(x => x["authorization_id"].AsObjectId));
- // buffer = null;
- // }
- // }
+ adhocAuthorizations.SymmetricExceptWith(adhocAuthorizationsWithTokens);
+ authorizations.AddRange(adhocAuthorizations);
- // if (buffer is not null)
- // {
- // yield return buffer;
- // }
- //}
+ database.BeginTrans();
+ foreach (var authorization in authorizations)
+ {
+ authorizationCollection.Delete(authorization);
+ }
+ database.Commit();
}
///
@@ -592,7 +583,6 @@ public virtual ValueTask SetApplicationIdAsync(TAuthorization authorization,
{
authorization.ApplicationId = new ObjectId(identifier);
}
-
else
{
authorization.ApplicationId = ObjectId.Empty;
@@ -611,7 +601,6 @@ public virtual ValueTask SetCreationDateAsync(TAuthorization authorization,
}
authorization.CreationDate = date?.UtcDateTime;
-
return default;
}
@@ -627,12 +616,10 @@ public virtual ValueTask SetPropertiesAsync(TAuthorization authorization,
if (properties is not { Count: > 0 })
{
authorization.Properties = null;
-
return default;
}
authorization.Properties = properties;
-
return default;
}
@@ -648,12 +635,10 @@ public virtual ValueTask SetScopesAsync(TAuthorization authorization,
if (scopes.IsDefaultOrEmpty)
{
authorization.Scopes = null;
-
return default;
}
- authorization.Scopes = scopes.ToImmutableList();
-
+ authorization.Scopes = scopes;
return default;
}
@@ -666,7 +651,6 @@ public virtual ValueTask SetStatusAsync(TAuthorization authorization, string? st
}
authorization.Status = status;
-
return default;
}
@@ -679,7 +663,6 @@ public virtual ValueTask SetSubjectAsync(TAuthorization authorization, string? s
}
authorization.Subject = subject;
-
return default;
}
@@ -692,7 +675,6 @@ public virtual ValueTask SetTypeAsync(TAuthorization authorization, string? type
}
authorization.Type = type;
-
return default;
}
diff --git a/src/Sknet.OpenIddict.LiteDB/Stores/OpenIddictLiteDBScopeStore.cs b/src/Sknet.OpenIddict.LiteDB/Stores/OpenIddictLiteDBScopeStore.cs
index b154f67..29a9b3a 100644
--- a/src/Sknet.OpenIddict.LiteDB/Stores/OpenIddictLiteDBScopeStore.cs
+++ b/src/Sknet.OpenIddict.LiteDB/Stores/OpenIddictLiteDBScopeStore.cs
@@ -111,7 +111,7 @@ public virtual async ValueTask DeleteAsync(TScope scope, CancellationToken cance
var database = await Context.GetDatabaseAsync(cancellationToken);
var collection = database.GetCollection(Options.CurrentValue.ScopesCollectionName);
- return collection.FindById(identifier);
+ return collection.FindById(new ObjectId(identifier));
}
///
@@ -221,12 +221,10 @@ public virtual ValueTask> GetDescriptio
if (scope.Descriptions is not { Count: > 0 })
{
- return new(ImmutableDictionary.Create());
+ return new(ImmutableDictionary.Empty);
}
- return new(scope.Descriptions.ToImmutableDictionary(
- pair => CultureInfo.GetCultureInfo(pair.Key),
- pair => pair.Value));
+ return new(scope.Descriptions);
}
///
@@ -250,12 +248,10 @@ public virtual ValueTask> GetDisplayNam
if (scope.DisplayNames is not { Count: > 0 })
{
- return new(ImmutableDictionary.Create());
+ return new(ImmutableDictionary.Empty);
}
- return new(scope.DisplayNames.ToImmutableDictionary(
- pair => CultureInfo.GetCultureInfo(pair.Key),
- pair => pair.Value));
+ return new(scope.DisplayNames);
}
///
@@ -290,10 +286,10 @@ public virtual ValueTask> GetProperties
if (scope.Properties is null || scope.Properties.Count == 0)
{
- return new(ImmutableDictionary.Create());
+ return new(ImmutableDictionary.Empty);
}
- return new(scope.Properties.ToImmutableDictionary());
+ return new(scope.Properties);
}
///
@@ -304,12 +300,12 @@ public virtual ValueTask> GetResourcesAsync(TScope scope,
throw new ArgumentNullException(nameof(scope));
}
- if (scope.Resources is not { Count: > 0 })
+ if (scope.Resources is not { Length: > 0 })
{
- return new(ImmutableArray.Create());
+ return new(ImmutableArray.Empty);
}
- return new(scope.Resources.ToImmutableArray());
+ return new(scope.Resources.Value);
}
///
@@ -319,7 +315,6 @@ public virtual ValueTask InstantiateAsync(CancellationToken cancellation
{
return new(Activator.CreateInstance());
}
-
catch (MemberAccessException exception)
{
return new(Task.FromException(
@@ -379,7 +374,6 @@ public virtual ValueTask SetDescriptionAsync(TScope scope, string? description,
}
scope.Description = description;
-
return default;
}
@@ -395,14 +389,10 @@ public virtual ValueTask SetDescriptionsAsync(TScope scope,
if (descriptions is not { Count: > 0 })
{
scope.Descriptions = null;
-
return default;
}
- scope.Descriptions = descriptions.ToImmutableDictionary(
- pair => pair.Key.Name,
- pair => pair.Value);
-
+ scope.Descriptions = descriptions;
return default;
}
@@ -418,14 +408,10 @@ public virtual ValueTask SetDisplayNamesAsync(TScope scope,
if (names is not { Count: > 0 })
{
scope.DisplayNames = null;
-
return default;
}
- scope.DisplayNames = names.ToImmutableDictionary(
- pair => pair.Key.Name,
- pair => pair.Value);
-
+ scope.DisplayNames = names;
return default;
}
@@ -438,7 +424,6 @@ public virtual ValueTask SetDisplayNameAsync(TScope scope, string? name, Cancell
}
scope.DisplayName = name;
-
return default;
}
@@ -451,7 +436,6 @@ public virtual ValueTask SetNameAsync(TScope scope, string? name, CancellationTo
}
scope.Name = name;
-
return default;
}
@@ -467,12 +451,10 @@ public virtual ValueTask SetPropertiesAsync(TScope scope,
if (properties is not { Count: > 0 })
{
scope.Properties = null;
-
return default;
}
scope.Properties = properties;
-
return default;
}
@@ -487,12 +469,10 @@ public virtual ValueTask SetResourcesAsync(TScope scope, ImmutableArray
if (resources.IsDefaultOrEmpty)
{
scope.Resources = null;
-
return default;
}
- scope.Resources = resources.ToImmutableList();
-
+ scope.Resources = resources;
return default;
}
diff --git a/src/Sknet.OpenIddict.LiteDB/Stores/OpenIddictLiteDBTokenStore.cs b/src/Sknet.OpenIddict.LiteDB/Stores/OpenIddictLiteDBTokenStore.cs
index b9a1d40..78dd796 100644
--- a/src/Sknet.OpenIddict.LiteDB/Stores/OpenIddictLiteDBTokenStore.cs
+++ b/src/Sknet.OpenIddict.LiteDB/Stores/OpenIddictLiteDBTokenStore.cs
@@ -13,6 +13,8 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+using static OpenIddict.Abstractions.OpenIddictConstants;
+
namespace Sknet.OpenIddict.LiteDB;
///
@@ -285,7 +287,7 @@ async IAsyncEnumerable ExecuteAsync([EnumeratorCancellation] Cancellatio
var database = await Context.GetDatabaseAsync(cancellationToken);
var collection = database.GetCollection(Options.CurrentValue.TokensCollectionName);
- return collection.FindById(identifier);
+ return collection.FindById(new ObjectId(identifier));
}
///
@@ -393,7 +395,7 @@ async IAsyncEnumerable ExecuteAsync([EnumeratorCancellation] Cancellatio
return new(result: null);
}
- return new(DateTime.SpecifyKind(token.CreationDate.Value, DateTimeKind.Utc));
+ return new(token.CreationDate);
}
///
@@ -409,7 +411,7 @@ async IAsyncEnumerable ExecuteAsync([EnumeratorCancellation] Cancellatio
return new(result: null);
}
- return new(DateTime.SpecifyKind(token.ExpirationDate.Value, DateTimeKind.Utc));
+ return new(token.ExpirationDate);
}
///
@@ -444,10 +446,10 @@ public virtual ValueTask> GetProperties
if (token.Properties is null || token.Properties.Count == 0)
{
- return new(ImmutableDictionary.Create());
+ return new(ImmutableDictionary.Empty);
}
- return new(token.Properties.ToImmutableDictionary());
+ return new(token.Properties);
}
///
@@ -463,7 +465,7 @@ public virtual ValueTask> GetProperties
return new(result: null);
}
- return new(DateTime.SpecifyKind(token.RedemptionDate.Value, DateTimeKind.Utc));
+ return new(token.RedemptionDate);
}
///
@@ -569,56 +571,38 @@ async IAsyncEnumerable ExecuteAsync([EnumeratorCancellation] Cancellati
}
///
- public virtual ValueTask PruneAsync(DateTimeOffset threshold, CancellationToken cancellationToken)
+ public async virtual ValueTask PruneAsync(DateTimeOffset threshold, CancellationToken cancellationToken)
{
- throw new NotImplementedException();
-
- //var database = await Context.GetDatabaseAsync(cancellationToken);
- //var collection = database.GetCollection(Options.CurrentValue.TokensCollectionName);
-
- //// Note: directly deleting the resulting set of an aggregate query is not supported by MongoDb.
- //// To work around this limitation, the token identifiers are stored in an intermediate list
- //// and delete requests are sent to remove the documents corresponding to these identifiers.
-
- //var identifiers =
- // await (from token in collection.AsQueryable()
- // join authorization in database.GetCollection(Options.CurrentValue.AuthorizationsCollectionName).AsQueryable()
- // on token.AuthorizationId equals authorization.Id into authorizations
- // where token.CreationDate < threshold.UtcDateTime
- // where (token.Status != Statuses.Inactive && token.Status != Statuses.Valid) ||
- // token.ExpirationDate < DateTime.UtcNow ||
- // authorizations.Any(authorization => authorization.Status != Statuses.Valid)
- // select token.Id).ToListAsync(cancellationToken);
-
- //// Note: to avoid generating delete requests with very large filters, a buffer is used here and the
- //// maximum number of elements that can be removed by a single call to PruneAsync() is deliberately limited.
- //foreach (var buffer in Buffer(identifiers.Take(1_000_000), 1_000))
- //{
- // await collection.DeleteManyAsync(token => buffer.Contains(token.Id), cancellationToken);
- //}
-
- //static IEnumerable> Buffer(IEnumerable source, int count)
- //{
- // List? buffer = null;
-
- // foreach (var element in source)
- // {
- // buffer ??= new List(capacity: 1);
- // buffer.Add(element);
-
- // if (buffer.Count == count)
- // {
- // yield return buffer;
-
- // buffer = null;
- // }
- // }
+ var database = await Context.GetDatabaseAsync(cancellationToken);
- // if (buffer is not null)
- // {
- // yield return buffer;
- // }
- //}
+ // Get invalid authorizations from which tokens can be pruned
+ var invalidAuthorizations = database
+ .GetCollection(Options.CurrentValue.AuthorizationsCollectionName)
+ .Query()
+ .Where("$.Status != @0", Statuses.Valid)
+ .Select(x => x["_id"].AsObjectId)
+ .ToList();
+
+ var tokenCollection = database.GetCollection(Options.CurrentValue.TokensCollectionName);
+ var query = from token in tokenCollection.Query()
+ // only prune tokens created before threshold
+ where token.CreationDate < threshold.UtcDateTime
+ // prune tokens that either
+ // 1. not inactive and not valid,
+ // 2. past expiration date, OR
+ // 3. belong to an invalid authorization
+ where (token.Status != Statuses.Inactive && token.Status != Statuses.Valid)
+ || token.ExpirationDate < DateTimeOffset.UtcNow
+ || invalidAuthorizations.Contains(token.AuthorizationId)
+ select token.Id;
+ var tokens = query.ToList();
+
+ database.BeginTrans();
+ foreach (var token in tokens)
+ {
+ tokenCollection.Delete(token);
+ }
+ database.Commit();
}
///
@@ -633,7 +617,6 @@ public virtual ValueTask SetApplicationIdAsync(TToken token, string? identifier,
{
token.ApplicationId = new ObjectId(identifier);
}
-
else
{
token.ApplicationId = ObjectId.Empty;
@@ -654,7 +637,6 @@ public virtual ValueTask SetAuthorizationIdAsync(TToken token, string? identifie
{
token.AuthorizationId = new ObjectId(identifier);
}
-
else
{
token.AuthorizationId = ObjectId.Empty;
@@ -672,7 +654,6 @@ public virtual ValueTask SetCreationDateAsync(TToken token, DateTimeOffset? date
}
token.CreationDate = date?.UtcDateTime;
-
return default;
}
@@ -685,7 +666,6 @@ public virtual ValueTask SetExpirationDateAsync(TToken token, DateTimeOffset? da
}
token.ExpirationDate = date?.UtcDateTime;
-
return default;
}
@@ -698,7 +678,6 @@ public virtual ValueTask SetPayloadAsync(TToken token, string? payload, Cancella
}
token.Payload = payload;
-
return default;
}
@@ -714,12 +693,10 @@ public virtual ValueTask SetPropertiesAsync(TToken token,
if (properties is not { Count: > 0 })
{
token.Properties = null;
-
return default;
}
token.Properties = properties;
-
return default;
}
@@ -732,7 +709,6 @@ public virtual ValueTask SetRedemptionDateAsync(TToken token, DateTimeOffset? da
}
token.RedemptionDate = date?.UtcDateTime;
-
return default;
}
@@ -745,7 +721,6 @@ public virtual ValueTask SetReferenceIdAsync(TToken token, string? identifier, C
}
token.ReferenceId = identifier;
-
return default;
}
@@ -758,7 +733,6 @@ public virtual ValueTask SetStatusAsync(TToken token, string? status, Cancellati
}
token.Status = status;
-
return default;
}
@@ -771,7 +745,6 @@ public virtual ValueTask SetSubjectAsync(TToken token, string? subject, Cancella
}
token.Subject = subject;
-
return default;
}
@@ -784,7 +757,6 @@ public virtual ValueTask SetTypeAsync(TToken token, string? type, CancellationTo
}
token.Type = type;
-
return default;
}
diff --git a/test/Sknet.OpenIddict.LiteDB.Tests/Builders/OpenIddictLiteDBApplicationFaker.cs b/test/Sknet.OpenIddict.LiteDB.Tests/Builders/OpenIddictLiteDBApplicationFaker.cs
new file mode 100644
index 0000000..4495957
--- /dev/null
+++ b/test/Sknet.OpenIddict.LiteDB.Tests/Builders/OpenIddictLiteDBApplicationFaker.cs
@@ -0,0 +1,72 @@
+/*
+ * Copyright (c) 2022 Steven Kuhn and contributors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+namespace Sknet.OpenIddict.LiteDB.Tests.Builders;
+
+public class OpenIddictLiteDBApplicationFaker : Faker
+{
+ public OpenIddictLiteDBApplicationFaker()
+ {
+ UseSeed(1);
+
+ RuleFor(x => x.Id, f => new ObjectId(f.Random.Hexadecimal(24, prefix: "")))
+ .RuleFor(x => x.ClientId, f => f.Random.AlphaNumeric(32))
+ .RuleFor(x => x.ClientSecret, f => f.Random.AlphaNumeric(32))
+ .RuleFor(x => x.ConcurrencyToken, f => f.Random.Guid().ToString())
+ .RuleFor(x => x.ConsentType, f => f.PickRandom(new[] { ConsentTypes.Explicit, ConsentTypes.External, ConsentTypes.Implicit, ConsentTypes.Systematic }))
+ .RuleFor(x => x.DisplayName, f => f.Commerce.ProductName())
+ .RuleFor(x => x.DisplayNames, f => f.Random.ListItems<(CultureInfo Culture, string DisplayName)>(new()
+ {
+ (CultureInfo.GetCultureInfo("en"), f.Commerce.ProductName()),
+ (CultureInfo.GetCultureInfo("fr"), f.Commerce.ProductName()),
+ (CultureInfo.GetCultureInfo("de"), f.Commerce.ProductName()),
+ (CultureInfo.GetCultureInfo("es"), f.Commerce.ProductName())
+ })
+ .OrderBy(x => x.Culture.Name)
+ .ToImmutableDictionary(x => x.Culture, x => x.DisplayName))
+ .RuleFor(x => x.Permissions, f => f.Random.ListItems(new() { "permission1", "permission2", "permission3" })
+ .ToImmutableArray())
+ .RuleFor(x => x.PostLogoutRedirectUris, f => f.Random.ListItems(new()
+ {
+ f.Internet.UrlWithPath(),
+ f.Internet.UrlWithPath(),
+ f.Internet.UrlWithPath()
+ })
+ .ToImmutableArray())
+ .RuleFor(x => x.Properties, f => f.Random.ListItems<(string PropertyName, JsonElement JsonValue)>(new()
+ {
+ ("property1", JsonDocument.Parse("true").RootElement),
+ ("property2", JsonDocument.Parse("false").RootElement),
+ ("property3", JsonDocument.Parse("null").RootElement),
+ ("property4", JsonDocument.Parse("1").RootElement),
+ ("property5", JsonDocument.Parse("1.1").RootElement),
+ ("property6", JsonDocument.Parse(@"""""").RootElement),
+ ("property7", JsonDocument.Parse(@"""value""").RootElement),
+ ("property8", JsonDocument.Parse(@"[""value1"", ""value2""]").RootElement),
+ ("property9", JsonDocument.Parse(@"{""key"": ""value""}").RootElement)
+ })
+ .OrderBy(x => x.PropertyName)
+ .ToImmutableDictionary(x => x.PropertyName, x => x.JsonValue))
+ .RuleFor(x => x.RedirectUris, f => f.Random.ListItems(new()
+ {
+ f.Internet.UrlWithPath(),
+ f.Internet.UrlWithPath(),
+ f.Internet.UrlWithPath()
+ })
+ .ToImmutableArray())
+ .RuleFor(x => x.Requirements, f => ImmutableArray.Create())
+ .RuleFor(x => x.Type, f => f.PickRandom(new[] { ClientTypes.Confidential, ClientTypes.Public }));
+ }
+}
diff --git a/test/Sknet.OpenIddict.LiteDB.Tests/Builders/OpenIddictLiteDBApplicationStoreBuilder.cs b/test/Sknet.OpenIddict.LiteDB.Tests/Builders/OpenIddictLiteDBApplicationStoreBuilder.cs
new file mode 100644
index 0000000..7a1c76e
--- /dev/null
+++ b/test/Sknet.OpenIddict.LiteDB.Tests/Builders/OpenIddictLiteDBApplicationStoreBuilder.cs
@@ -0,0 +1,126 @@
+/*
+* Copyright (c) 2022 Steven Kuhn and contributors.
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+namespace Sknet.OpenIddict.LiteDB.Tests.Builders;
+
+public class OpenIddictLiteDBApplicationStoreBuilder
+{
+ private readonly List _applications = new();
+ private readonly List _authorizations = new();
+ private readonly List _scopes = new();
+ private readonly List _tokens = new();
+ private ILiteDatabase? _database;
+
+ public OpenIddictLiteDBApplicationStoreBuilder WithApplications(params OpenIddictLiteDBApplication[] applications)
+ {
+ _applications.AddRange(applications);
+ return this;
+ }
+
+ public OpenIddictLiteDBApplicationStoreBuilder WithApplications(IEnumerable applications)
+ {
+ _applications.AddRange(applications);
+ return this;
+ }
+
+ public OpenIddictLiteDBApplicationStoreBuilder WithAuthorizations(params OpenIddictLiteDBAuthorization[] authorizations)
+ {
+ _authorizations.AddRange(authorizations);
+ return this;
+ }
+
+ public OpenIddictLiteDBApplicationStoreBuilder WithAuthorizations(IEnumerable authorizations)
+ {
+ _authorizations.AddRange(authorizations);
+ return this;
+ }
+
+ public OpenIddictLiteDBApplicationStoreBuilder WithDatabase(ILiteDatabase database)
+ {
+ _database = database;
+ return this;
+ }
+
+ public OpenIddictLiteDBApplicationStoreBuilder WithScopes(params OpenIddictLiteDBScope[] scopes)
+ {
+ _scopes.AddRange(scopes);
+ return this;
+ }
+
+ public OpenIddictLiteDBApplicationStoreBuilder WithScopes(IEnumerable scopes)
+ {
+ _scopes.AddRange(scopes);
+ return this;
+ }
+
+ public OpenIddictLiteDBApplicationStoreBuilder WithTokens(params OpenIddictLiteDBToken[] tokens)
+ {
+ _tokens.AddRange(tokens);
+ return this;
+ }
+
+ public OpenIddictLiteDBApplicationStoreBuilder WithTokens(IEnumerable tokens)
+ {
+ _tokens.AddRange(tokens);
+ return this;
+ }
+
+
+ public OpenIddictLiteDBApplicationStore Build()
+ {
+ var database = _database ?? new OpenIddictLiteDatabase(":memory:");
+ var options = new OpenIddictLiteDBOptions();
+
+ if (_applications.Count > 0)
+ {
+ database
+ .GetCollection(options.ApplicationsCollectionName)
+ .InsertBulk(_applications);
+ }
+
+ if (_authorizations.Count > 0)
+ {
+ database
+ .GetCollection(options.AuthorizationsCollectionName)
+ .InsertBulk(_authorizations);
+ }
+
+ if (_scopes.Count > 0)
+ {
+ database
+ .GetCollection(options.ScopesCollectionName)
+ .InsertBulk(_scopes);
+ }
+
+ if (_tokens.Count > 0)
+ {
+ database
+ .GetCollection(options.TokensCollectionName)
+ .InsertBulk(_tokens);
+ }
+
+ var contextMock = new Mock();
+ contextMock
+ .Setup(x => x.GetDatabaseAsync(It.IsAny()))
+ .ReturnsAsync(database);
+
+ var optionsMock = new Mock>();
+ optionsMock
+ .SetupGet(x => x.CurrentValue)
+ .Returns(options);
+
+ return new OpenIddictLiteDBApplicationStore(contextMock.Object, optionsMock.Object);
+ }
+}
diff --git a/test/Sknet.OpenIddict.LiteDB.Tests/Builders/OpenIddictLiteDBAuthorizationFaker.cs b/test/Sknet.OpenIddict.LiteDB.Tests/Builders/OpenIddictLiteDBAuthorizationFaker.cs
new file mode 100644
index 0000000..1ff23fd
--- /dev/null
+++ b/test/Sknet.OpenIddict.LiteDB.Tests/Builders/OpenIddictLiteDBAuthorizationFaker.cs
@@ -0,0 +1,55 @@
+/*
+ * Copyright (c) 2022 Steven Kuhn and contributors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+namespace Sknet.OpenIddict.LiteDB.Tests.Builders;
+
+public class OpenIddictLiteDBAuthorizationFaker : Faker
+{
+ public OpenIddictLiteDBAuthorizationFaker()
+ {
+ UseSeed(1);
+
+ RuleFor(x => x.Id, f => new ObjectId(f.Random.Hexadecimal(24, prefix: "")))
+ .RuleFor(x => x.ApplicationId, f => new ObjectId(f.Random.Hexadecimal(24, prefix: "")))
+ .RuleFor(x => x.ConcurrencyToken, f => f.Random.Guid().ToString())
+ .RuleFor(x => x.CreationDate, f => f.Date.PastOffset())
+ .RuleFor(x => x.Properties, f => f.Random.ListItems<(string PropertyName, JsonElement JsonValue)>(new()
+ {
+ ("property1", JsonDocument.Parse("true").RootElement),
+ ("property2", JsonDocument.Parse("false").RootElement),
+ ("property3", JsonDocument.Parse("null").RootElement),
+ ("property4", JsonDocument.Parse("1").RootElement),
+ ("property5", JsonDocument.Parse("1.1").RootElement),
+ ("property6", JsonDocument.Parse(@"""""").RootElement),
+ ("property7", JsonDocument.Parse(@"""value""").RootElement),
+ ("property8", JsonDocument.Parse(@"[""value1"", ""value2""]").RootElement),
+ ("property9", JsonDocument.Parse(@"{""key"": ""value""}").RootElement)
+ })
+ .OrderBy(x => x.PropertyName)
+ .ToImmutableDictionary(x => x.PropertyName, x => x.JsonValue))
+ .RuleFor(x => x.Scopes, f => f.Random.ListItems(new() { "scope1", "scope2", "scope3" })
+ .ToImmutableArray())
+ .RuleFor(x => x.Status, f => f.PickRandom(new[]
+ {
+ Statuses.Inactive,
+ Statuses.Redeemed,
+ Statuses.Rejected,
+ Statuses.Revoked,
+ Statuses.Valid
+ }))
+ .RuleFor(x => x.Subject, f => f.Person.UserName)
+ .RuleFor(x => x.Type, f => f.PickRandom(new[] { AuthorizationTypes.AdHoc, AuthorizationTypes.Permanent }));
+ }
+}
diff --git a/test/Sknet.OpenIddict.LiteDB.Tests/Builders/OpenIddictLiteDBAuthorizationStoreBuilder.cs b/test/Sknet.OpenIddict.LiteDB.Tests/Builders/OpenIddictLiteDBAuthorizationStoreBuilder.cs
new file mode 100644
index 0000000..c0604a0
--- /dev/null
+++ b/test/Sknet.OpenIddict.LiteDB.Tests/Builders/OpenIddictLiteDBAuthorizationStoreBuilder.cs
@@ -0,0 +1,85 @@
+/*
+ * Copyright (c) 2022 Steven Kuhn and contributors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+namespace Sknet.OpenIddict.LiteDB.Tests.Builders;
+
+public class OpenIddictLiteDBAuthorizationStoreBuilder
+{
+ private readonly List _authorizations = new();
+ private readonly List _tokens = new();
+ private ILiteDatabase? _database;
+
+ public OpenIddictLiteDBAuthorizationStoreBuilder WithAuthorizations(params OpenIddictLiteDBAuthorization[] authorizations)
+ {
+ _authorizations.AddRange(authorizations);
+ return this;
+ }
+
+ public OpenIddictLiteDBAuthorizationStoreBuilder WithAuthorizations(IEnumerable authorizations)
+ {
+ _authorizations.AddRange(authorizations);
+ return this;
+ }
+
+ public OpenIddictLiteDBAuthorizationStoreBuilder WithDatabase(ILiteDatabase database)
+ {
+ _database = database;
+ return this;
+ }
+
+ public OpenIddictLiteDBAuthorizationStoreBuilder WithTokens(params OpenIddictLiteDBToken[] tokens)
+ {
+ _tokens.AddRange(tokens);
+ return this;
+ }
+
+ public OpenIddictLiteDBAuthorizationStoreBuilder WithTokens(IEnumerable tokens)
+ {
+ _tokens.AddRange(tokens);
+ return this;
+ }
+
+ public OpenIddictLiteDBAuthorizationStore Build()
+ {
+ var database = _database ?? new OpenIddictLiteDatabase(":memory:");
+ var options = new OpenIddictLiteDBOptions();
+
+ if (_authorizations.Count > 0)
+ {
+ database
+ .GetCollection(options.AuthorizationsCollectionName)
+ .InsertBulk(_authorizations);
+ }
+
+ if (_tokens.Count > 0)
+ {
+ database
+ .GetCollection(options.TokensCollectionName)
+ .InsertBulk(_tokens);
+ }
+
+ var contextMock = new Mock();
+ contextMock
+ .Setup(x => x.GetDatabaseAsync(It.IsAny()))
+ .ReturnsAsync(database);
+
+ var optionsMock = new Mock>();
+ optionsMock
+ .SetupGet(x => x.CurrentValue)
+ .Returns(options);
+
+ return new OpenIddictLiteDBAuthorizationStore(contextMock.Object, optionsMock.Object);
+ }
+}
diff --git a/test/Sknet.OpenIddict.LiteDB.Tests/Builders/OpenIddictLiteDBScopeFaker.cs b/test/Sknet.OpenIddict.LiteDB.Tests/Builders/OpenIddictLiteDBScopeFaker.cs
new file mode 100644
index 0000000..9c1abdb
--- /dev/null
+++ b/test/Sknet.OpenIddict.LiteDB.Tests/Builders/OpenIddictLiteDBScopeFaker.cs
@@ -0,0 +1,71 @@
+/*
+ * Copyright (c) 2022 Steven Kuhn and contributors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+namespace Sknet.OpenIddict.LiteDB.Tests.Builders;
+
+public class OpenIddictLiteDBScopeFaker : Faker
+{
+ public OpenIddictLiteDBScopeFaker()
+ {
+ UseSeed(1);
+
+ RuleFor(x => x.Id, f => new ObjectId(f.Random.Hexadecimal(24, prefix: "")))
+ .RuleFor(x => x.ConcurrencyToken, f => f.Random.Guid().ToString())
+ .RuleFor(x => x.Description, f => f.Lorem.Sentence())
+ .RuleFor(x => x.Descriptions, f => f.Random.ListItems<(CultureInfo Culture, string Description)>(new()
+ {
+ (CultureInfo.GetCultureInfo("en"), f.Lorem.Sentence()),
+ (CultureInfo.GetCultureInfo("fr"), f.Lorem.Sentence()),
+ (CultureInfo.GetCultureInfo("de"), f.Lorem.Sentence()),
+ (CultureInfo.GetCultureInfo("es"), f.Lorem.Sentence())
+ })
+ .OrderBy(x => x.Culture.Name)
+ .ToImmutableDictionary(x => x.Culture, x => x.Description))
+ .RuleFor(x => x.DisplayName, f => f.Commerce.ProductName())
+ .RuleFor(x => x.DisplayNames, f => f.Random.ListItems<(CultureInfo Culture, string DisplayName)>(new()
+ {
+ (CultureInfo.GetCultureInfo("en"), f.Commerce.ProductName()),
+ (CultureInfo.GetCultureInfo("fr"), f.Commerce.ProductName()),
+ (CultureInfo.GetCultureInfo("de"), f.Commerce.ProductName()),
+ (CultureInfo.GetCultureInfo("es"), f.Commerce.ProductName())
+ })
+ .OrderBy(x => x.Culture.Name)
+ .ToImmutableDictionary(x => x.Culture, x => x.DisplayName))
+ .RuleFor(x => x.Name, f => f.Commerce.ProductName())
+ .RuleFor(x => x.Properties, f => f.Random.ListItems<(string PropertyName, JsonElement JsonValue)>(new()
+ {
+ ("property1", JsonDocument.Parse("true").RootElement),
+ ("property2", JsonDocument.Parse("false").RootElement),
+ ("property3", JsonDocument.Parse("null").RootElement),
+ ("property4", JsonDocument.Parse("1").RootElement),
+ ("property5", JsonDocument.Parse("1.1").RootElement),
+ ("property6", JsonDocument.Parse(@"""""").RootElement),
+ ("property7", JsonDocument.Parse(@"""value""").RootElement),
+ ("property8", JsonDocument.Parse(@"[""value1"", ""value2""]").RootElement),
+ ("property9", JsonDocument.Parse(@"{""key"": ""value""}").RootElement)
+ })
+ .OrderBy(x => x.PropertyName)
+ .ToImmutableDictionary(x => x.PropertyName, x => x.JsonValue))
+ .RuleFor(x => x.Resources, f => f.Random.ListItems(new()
+ {
+ "resource1",
+ "resource2",
+ "resource3",
+ "resource4",
+ "resource5"
+ })
+ .ToImmutableArray());
+ }
+}
diff --git a/test/Sknet.OpenIddict.LiteDB.Tests/Builders/OpenIddictLiteDBScopeStoreBuilder.cs b/test/Sknet.OpenIddict.LiteDB.Tests/Builders/OpenIddictLiteDBScopeStoreBuilder.cs
new file mode 100644
index 0000000..671851e
--- /dev/null
+++ b/test/Sknet.OpenIddict.LiteDB.Tests/Builders/OpenIddictLiteDBScopeStoreBuilder.cs
@@ -0,0 +1,65 @@
+/*
+ * Copyright (c) 2022 Steven Kuhn and contributors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+namespace Sknet.OpenIddict.LiteDB.Tests.Builders;
+
+public class OpenIddictLiteDBScopeStoreBuilder
+{
+ private ILiteDatabase? _database;
+ private readonly List _scopes = new();
+
+ public OpenIddictLiteDBScopeStoreBuilder WithDatabase(ILiteDatabase database)
+ {
+ _database = database;
+ return this;
+ }
+
+ public OpenIddictLiteDBScopeStoreBuilder WithScopes(params OpenIddictLiteDBScope[] scopes)
+ {
+ _scopes.AddRange(scopes);
+ return this;
+ }
+
+ public OpenIddictLiteDBScopeStoreBuilder WithScopes(IEnumerable scopes)
+ {
+ _scopes.AddRange(scopes);
+ return this;
+ }
+
+ public OpenIddictLiteDBScopeStore Build()
+ {
+ var database = _database ?? new OpenIddictLiteDatabase(":memory:");
+ var options = new OpenIddictLiteDBOptions();
+
+ if (_scopes.Count > 0)
+ {
+ database
+ .GetCollection(options.ScopesCollectionName)
+ .InsertBulk(_scopes);
+ }
+
+ var contextMock = new Mock();
+ contextMock
+ .Setup(x => x.GetDatabaseAsync(It.IsAny()))
+ .ReturnsAsync(database);
+
+ var optionsMock = new Mock>();
+ optionsMock
+ .SetupGet(x => x.CurrentValue)
+ .Returns(options);
+
+ return new OpenIddictLiteDBScopeStore(contextMock.Object, optionsMock.Object);
+ }
+}
diff --git a/test/Sknet.OpenIddict.LiteDB.Tests/Builders/OpenIddictLiteDBTokenFaker.cs b/test/Sknet.OpenIddict.LiteDB.Tests/Builders/OpenIddictLiteDBTokenFaker.cs
new file mode 100644
index 0000000..10dfcc4
--- /dev/null
+++ b/test/Sknet.OpenIddict.LiteDB.Tests/Builders/OpenIddictLiteDBTokenFaker.cs
@@ -0,0 +1,56 @@
+/*
+ * Copyright (c) 2022 Steven Kuhn and contributors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+namespace Sknet.OpenIddict.LiteDB.Tests.Builders;
+
+public class OpenIddictLiteDBTokenFaker : Faker
+{
+ public OpenIddictLiteDBTokenFaker()
+ {
+ UseSeed(1);
+
+ RuleFor(x => x.Id, f => new ObjectId(f.Random.Hexadecimal(24, prefix: "")))
+ .RuleFor(x => x.ApplicationId, f => new ObjectId(f.Random.Hexadecimal(24, prefix: "")))
+ .RuleFor(x => x.AuthorizationId, f => new ObjectId(f.Random.Hexadecimal(24, prefix: "")))
+ .RuleFor(x => x.ConcurrencyToken, f => f.Random.Guid().ToString())
+ .RuleFor(x => x.CreationDate, f => f.Date.PastOffset())
+ .RuleFor(x => x.ExpirationDate, f => f.Date.FutureOffset())
+ .RuleFor(x => x.Properties, f => f.Random.ListItems<(string PropertyName, JsonElement JsonValue)>(new()
+ {
+ ("property1", JsonDocument.Parse("true").RootElement),
+ ("property2", JsonDocument.Parse("false").RootElement),
+ ("property3", JsonDocument.Parse("null").RootElement),
+ ("property4", JsonDocument.Parse("1").RootElement),
+ ("property5", JsonDocument.Parse("1.1").RootElement),
+ ("property6", JsonDocument.Parse(@"""""").RootElement),
+ ("property7", JsonDocument.Parse(@"""value""").RootElement),
+ ("property8", JsonDocument.Parse(@"[""value1"", ""value2""]").RootElement),
+ ("property9", JsonDocument.Parse(@"{""key"": ""value""}").RootElement)
+ })
+ .OrderBy(x => x.PropertyName)
+ .ToImmutableDictionary(x => x.PropertyName, x => x.JsonValue))
+ .RuleFor(x => x.ReferenceId, f => f.Random.Guid().ToString())
+ .RuleFor(x => x.Status, f => f.PickRandom(new[]
+ {
+ Statuses.Inactive,
+ Statuses.Redeemed,
+ Statuses.Rejected,
+ Statuses.Revoked,
+ Statuses.Valid
+ }))
+ .RuleFor(x => x.Subject, f => f.Person.UserName)
+ .RuleFor(x => x.Type, f => f.PickRandom(new[] { TokenTypes.Bearer }));
+ }
+}
\ No newline at end of file
diff --git a/test/Sknet.OpenIddict.LiteDB.Tests/Builders/OpenIddictLiteDBTokenStoreBuilder.cs b/test/Sknet.OpenIddict.LiteDB.Tests/Builders/OpenIddictLiteDBTokenStoreBuilder.cs
new file mode 100644
index 0000000..3a5f085
--- /dev/null
+++ b/test/Sknet.OpenIddict.LiteDB.Tests/Builders/OpenIddictLiteDBTokenStoreBuilder.cs
@@ -0,0 +1,79 @@
+/*
+ * Copyright (c) 2022 Steven Kuhn and contributors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+namespace Sknet.OpenIddict.LiteDB.Tests.Builders;
+
+public class OpenIddictLiteDBTokenStoreBuilder
+{
+ private ILiteDatabase? _database;
+ private readonly List _authorizations = new();
+ private readonly List _tokens = new();
+
+ public OpenIddictLiteDBTokenStoreBuilder WithAuthorizations(IEnumerable authorizations)
+ {
+ _authorizations.AddRange(authorizations);
+ return this;
+ }
+
+ public OpenIddictLiteDBTokenStoreBuilder WithDatabase(ILiteDatabase database)
+ {
+ _database = database;
+ return this;
+ }
+
+ public OpenIddictLiteDBTokenStoreBuilder WithTokens(params OpenIddictLiteDBToken[] tokens)
+ {
+ _tokens.AddRange(tokens);
+ return this;
+ }
+
+ public OpenIddictLiteDBTokenStoreBuilder WithTokens(IEnumerable tokens)
+ {
+ _tokens.AddRange(tokens);
+ return this;
+ }
+
+ public OpenIddictLiteDBTokenStore Build()
+ {
+ var database = _database ?? new OpenIddictLiteDatabase(":memory:");
+ var options = new OpenIddictLiteDBOptions();
+
+ if (_authorizations.Count > 0)
+ {
+ database
+ .GetCollection(options.AuthorizationsCollectionName)
+ .InsertBulk(_authorizations);
+ }
+
+ if (_tokens.Count > 0)
+ {
+ database
+ .GetCollection(options.TokensCollectionName)
+ .InsertBulk(_tokens);
+ }
+
+ var contextMock = new Mock();
+ contextMock
+ .Setup(x => x.GetDatabaseAsync(It.IsAny()))
+ .ReturnsAsync(database);
+
+ var optionsMock = new Mock>();
+ optionsMock
+ .SetupGet(x => x.CurrentValue)
+ .Returns(options);
+
+ return new OpenIddictLiteDBTokenStore(contextMock.Object, optionsMock.Object);
+ }
+}
diff --git a/test/Sknet.OpenIddict.LiteDB.Tests/Resolvers/OpenIddictLiteDBApplicationStoreResolverTests.cs b/test/Sknet.OpenIddict.LiteDB.Tests/Resolvers/OpenIddictLiteDBApplicationStoreResolverTests.cs
index 10517f9..a4de1f2 100644
--- a/test/Sknet.OpenIddict.LiteDB.Tests/Resolvers/OpenIddictLiteDBApplicationStoreResolverTests.cs
+++ b/test/Sknet.OpenIddict.LiteDB.Tests/Resolvers/OpenIddictLiteDBApplicationStoreResolverTests.cs
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-namespace Sknet.OpenIddict.LiteDB.Tests;
+namespace Sknet.OpenIddict.LiteDB.Tests.Resolvers;
public class OpenIddictLiteDBApplicationStoreResolverTests
{
diff --git a/test/Sknet.OpenIddict.LiteDB.Tests/Resolvers/OpenIddictLiteDBAuthorizationStoreResolverTests.cs b/test/Sknet.OpenIddict.LiteDB.Tests/Resolvers/OpenIddictLiteDBAuthorizationStoreResolverTests.cs
index d182976..626062f 100644
--- a/test/Sknet.OpenIddict.LiteDB.Tests/Resolvers/OpenIddictLiteDBAuthorizationStoreResolverTests.cs
+++ b/test/Sknet.OpenIddict.LiteDB.Tests/Resolvers/OpenIddictLiteDBAuthorizationStoreResolverTests.cs
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-namespace Sknet.OpenIddict.LiteDB.Tests;
+namespace Sknet.OpenIddict.LiteDB.Tests.Resolvers;
public class OpenIddictLiteDBAuthorizationStoreResolverTests
{
diff --git a/test/Sknet.OpenIddict.LiteDB.Tests/Resolvers/OpenIddictLiteDBScopeStoreResolverTests.cs b/test/Sknet.OpenIddict.LiteDB.Tests/Resolvers/OpenIddictLiteDBScopeStoreResolverTests.cs
index 892afd9..dcd1b63 100644
--- a/test/Sknet.OpenIddict.LiteDB.Tests/Resolvers/OpenIddictLiteDBScopeStoreResolverTests.cs
+++ b/test/Sknet.OpenIddict.LiteDB.Tests/Resolvers/OpenIddictLiteDBScopeStoreResolverTests.cs
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-namespace Sknet.OpenIddict.LiteDB.Tests;
+namespace Sknet.OpenIddict.LiteDB.Tests.Resolvers;
public class OpenIddictLiteDBScopeStoreResolverTests
{
diff --git a/test/Sknet.OpenIddict.LiteDB.Tests/Resolvers/OpenIddictLiteDBTokenStoreResolverTests.cs b/test/Sknet.OpenIddict.LiteDB.Tests/Resolvers/OpenIddictLiteDBTokenStoreResolverTests.cs
index c420e1e..0782634 100644
--- a/test/Sknet.OpenIddict.LiteDB.Tests/Resolvers/OpenIddictLiteDBTokenStoreResolverTests.cs
+++ b/test/Sknet.OpenIddict.LiteDB.Tests/Resolvers/OpenIddictLiteDBTokenStoreResolverTests.cs
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-namespace Sknet.OpenIddict.LiteDB.Tests;
+namespace Sknet.OpenIddict.LiteDB.Tests.Resolvers;
public class OpenIddictLiteDBTokenStoreResolverTests
{
diff --git a/test/Sknet.OpenIddict.LiteDB.Tests/Sknet.OpenIddict.LiteDB.Tests.csproj b/test/Sknet.OpenIddict.LiteDB.Tests/Sknet.OpenIddict.LiteDB.Tests.csproj
index a0a75a7..ad0dda9 100644
--- a/test/Sknet.OpenIddict.LiteDB.Tests/Sknet.OpenIddict.LiteDB.Tests.csproj
+++ b/test/Sknet.OpenIddict.LiteDB.Tests/Sknet.OpenIddict.LiteDB.Tests.csproj
@@ -10,9 +10,12 @@
-
-
+
+
+
+
+
runtime; build; native; contentfiles; analyzers; buildtransitive
@@ -27,5 +30,5 @@
-
+
diff --git a/test/Sknet.OpenIddict.LiteDB.Tests/Stores/ILiteDatabaseExtensions.cs b/test/Sknet.OpenIddict.LiteDB.Tests/Stores/ILiteDatabaseExtensions.cs
new file mode 100644
index 0000000..225aa1e
--- /dev/null
+++ b/test/Sknet.OpenIddict.LiteDB.Tests/Stores/ILiteDatabaseExtensions.cs
@@ -0,0 +1,47 @@
+/*
+ * Copyright (c) 2022 Steven Kuhn and contributors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+namespace Sknet.OpenIddict.LiteDB.Tests;
+
+static class ILiteDatabaseExtensions
+{
+ public static ILiteCollection Applications(this ILiteDatabase database)
+ {
+ var options = new OpenIddictLiteDBOptions();
+
+ return database.GetCollection(options.ApplicationsCollectionName);
+ }
+
+ public static ILiteCollection Authorizations(this ILiteDatabase database)
+ {
+ var options = new OpenIddictLiteDBOptions();
+
+ return database.GetCollection(options.AuthorizationsCollectionName);
+ }
+
+ public static ILiteCollection Scopes(this ILiteDatabase database)
+ {
+ var options = new OpenIddictLiteDBOptions();
+
+ return database.GetCollection(options.ScopesCollectionName);
+ }
+
+ public static ILiteCollection Tokens(this ILiteDatabase database)
+ {
+ var options = new OpenIddictLiteDBOptions();
+
+ return database.GetCollection(options.TokensCollectionName);
+ }
+}
\ No newline at end of file
diff --git a/test/Sknet.OpenIddict.LiteDB.Tests/Stores/OpenIddictLiteDBApplicationStoreTests.cs b/test/Sknet.OpenIddict.LiteDB.Tests/Stores/OpenIddictLiteDBApplicationStoreTests.cs
new file mode 100644
index 0000000..e6fc6e8
--- /dev/null
+++ b/test/Sknet.OpenIddict.LiteDB.Tests/Stores/OpenIddictLiteDBApplicationStoreTests.cs
@@ -0,0 +1,567 @@
+/*
+ * Copyright (c) 2022 Steven Kuhn and contributors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+namespace Sknet.OpenIddict.LiteDB.Tests.Stores;
+
+public class OpenIddictLiteDBApplicationStoreTests
+{
+ [Fact]
+ public async Task CountAsync_WithZeroItems_ReturnsZero()
+ {
+ // Arrange
+ var store = new OpenIddictLiteDBApplicationStoreBuilder().Build();
+
+ // Act
+ var result = await store.CountAsync(default);
+
+ // Assert
+ Assert.Equal(0, result);
+
+ }
+
+ [Fact]
+ public async Task CountAsync_WithThreeItems_ReturnsThree()
+ {
+ // Arrange
+ var applications = new OpenIddictLiteDBApplicationFaker().Generate(3);
+ var store = new OpenIddictLiteDBApplicationStoreBuilder()
+ .WithApplications(applications)
+ .Build();
+
+ // Act
+ var result = await store.CountAsync(default);
+
+ // Assert
+ Assert.Equal(3, result);
+ }
+
+ [Fact]
+ public async Task CountAync_WithNullQuery_ThrowsException()
+ {
+ // Arrange
+ var store = new OpenIddictLiteDBApplicationStoreBuilder().Build();
+
+ // Act/Assert
+ var exception = await Assert.ThrowsAsync(
+ "query",
+ () => store.CountAsync