网站首页 网站源码
using System.Collections.Immutable;
using System.Globalization;
using System.Text.Json;
using Dpz.Core.MongodbAccess;
using Dpz.Core.Public.Entity.Auth;
using Microsoft.Extensions.Logging;
using MongoDB.Bson;
using MongoDB.Driver;
using MongoDB.Driver.Linq;
using OpenIddict.Abstractions;
namespace Dpz.Core.Auth.Service;
public sealed class DpzScopeStore(IRepository<DpzScope> repository, ILogger<DpzScopeStore> logger)
: IOpenIddictScopeStore<DpzScope>
{
public async ValueTask<long> CountAsync(CancellationToken cancellationToken)
{
logger.LogInformation("Counting scopes");
return await repository.MongodbQueryable.LongCountAsync(
cancellationToken: cancellationToken
);
}
public async ValueTask<long> CountAsync<TResult>(
Func<IQueryable<DpzScope>, IQueryable<TResult>> query,
CancellationToken cancellationToken
)
{
logger.LogInformation("Counting scopes with custom query");
return await query(repository.MongodbQueryable)
.LongCountAsync(cancellationToken: cancellationToken);
}
public async ValueTask CreateAsync(DpzScope scope, CancellationToken cancellationToken)
{
logger.LogInformation("Creating scope {Name} ({Id})", scope.Name, scope.Id);
await repository.InsertAsync(scope, cancellationToken);
}
public async ValueTask DeleteAsync(DpzScope scope, CancellationToken cancellationToken)
{
logger.LogWarning("Deleting scope {Name} ({Id})", scope.Name, scope.Id);
await repository.DeleteAsync(scope.Id, cancellationToken);
}
public async ValueTask<DpzScope?> FindByIdAsync(
string identifier,
CancellationToken cancellationToken
)
{
logger.LogInformation("Find scope by id {Identifier}", identifier);
if (ObjectId.TryParse(identifier, out var id))
{
return await repository.FindAsync(id, cancellationToken);
}
return null;
}
public async ValueTask<DpzScope?> FindByNameAsync(
string name,
CancellationToken cancellationToken
)
{
logger.LogInformation("Find scope by name {Name}", name);
return await repository
.SearchFor(x => x.Name == name)
.FirstOrDefaultAsync(cancellationToken);
}
public IAsyncEnumerable<DpzScope> FindByNamesAsync(
ImmutableArray<string> names,
CancellationToken cancellationToken
)
{
logger.LogInformation("Find scopes by names (count={Count})", names.Length);
if (names.IsDefaultOrEmpty)
{
return AsyncEnumerable.Empty<DpzScope>();
}
var filter = Builders<DpzScope>.Filter.In(x => x.Name, names);
return repository.SearchForAsync(filter, cancellationToken);
}
public IAsyncEnumerable<DpzScope> FindByResourceAsync(
string resource,
CancellationToken cancellationToken
)
{
logger.LogInformation("Find scopes by resource {Resource}", resource);
var filter = Builders<DpzScope>.Filter.AnyEq(x => x.Resources, resource);
return repository.SearchForAsync(filter, cancellationToken);
}
public async ValueTask<TResult?> GetAsync<TState, TResult>(
Func<IQueryable<DpzScope>, TState, IQueryable<TResult>> query,
TState state,
CancellationToken cancellationToken
)
{
logger.LogInformation("Get with custom query state={State}", state);
return await Task.FromResult(query(repository.MongodbQueryable, state).FirstOrDefault());
}
public async ValueTask<string?> GetDescriptionAsync(
DpzScope scope,
CancellationToken cancellationToken
)
{
logger.LogTrace("Get description of scope {Id}", scope.Id);
return await ValueTask.FromResult(scope.Description);
}
public async ValueTask<ImmutableDictionary<CultureInfo, string>> GetDescriptionsAsync(
DpzScope scope,
CancellationToken cancellationToken
)
{
logger.LogTrace("Get descriptions of scope {Id}", scope.Id);
if (scope.Descriptions is null || scope.Descriptions.Count == 0)
{
return await ValueTask.FromResult(ImmutableDictionary<CultureInfo, string>.Empty);
}
var builder = ImmutableDictionary.CreateBuilder<CultureInfo, string>();
foreach (var kv in scope.Descriptions)
{
try
{
builder[CultureInfo.GetCultureInfo(kv.Key)] = kv.Value;
}
catch
{
// 跳过无法解析的文化键
}
}
return await ValueTask.FromResult(builder.ToImmutable());
}
public async ValueTask<string?> GetDisplayNameAsync(
DpzScope scope,
CancellationToken cancellationToken
)
{
logger.LogTrace("Get display name of scope {Id}", scope.Id);
return await ValueTask.FromResult(scope.DisplayName);
}
public async ValueTask<ImmutableDictionary<CultureInfo, string>> GetDisplayNamesAsync(
DpzScope scope,
CancellationToken cancellationToken
)
{
logger.LogTrace("Get display names of scope {Id}", scope.Id);
if (scope.DisplayNames is null || scope.DisplayNames.Count == 0)
{
return await ValueTask.FromResult(ImmutableDictionary<CultureInfo, string>.Empty);
}
var builder = ImmutableDictionary.CreateBuilder<CultureInfo, string>();
foreach (var kv in scope.DisplayNames)
{
try
{
builder[CultureInfo.GetCultureInfo(kv.Key)] = kv.Value;
}
catch
{
// 跳过无法解析的文化键
}
}
return await ValueTask.FromResult(builder.ToImmutable());
}
public async ValueTask<string?> GetIdAsync(DpzScope scope, CancellationToken cancellationToken)
{
logger.LogTrace("Get id of scope {Id}", scope.Id);
return await ValueTask.FromResult(scope.Id.ToString());
}
public async ValueTask<string?> GetNameAsync(
DpzScope scope,
CancellationToken cancellationToken
)
{
logger.LogTrace("Get name of scope {Id}", scope.Id);
return await ValueTask.FromResult(scope.Name);
}
public async ValueTask<ImmutableDictionary<string, JsonElement>> GetPropertiesAsync(
DpzScope scope,
CancellationToken cancellationToken
)
{
logger.LogTrace("Get properties of scope {Id}", scope.Id);
if (scope.Properties is null)
{
return await ValueTask.FromResult(ImmutableDictionary<string, JsonElement>.Empty);
}
var json = scope.Properties.ToJson();
using var doc = JsonDocument.Parse(json);
var builder = ImmutableDictionary.CreateBuilder<string, JsonElement>();
foreach (var prop in doc.RootElement.EnumerateObject())
{
builder[prop.Name] = prop.Value.Clone();
}
return await ValueTask.FromResult(builder.ToImmutable());
}
public async ValueTask<ImmutableArray<string>> GetResourcesAsync(
DpzScope scope,
CancellationToken cancellationToken
)
{
logger.LogTrace("Get resources of scope {Id}", scope.Id);
var list = scope.Resources ?? [];
return await ValueTask.FromResult(ImmutableArray.CreateRange(list));
}
public async ValueTask<DpzScope> InstantiateAsync(CancellationToken cancellationToken)
{
logger.LogInformation("Instantiate new scope");
return await ValueTask.FromResult(new DpzScope { Id = ObjectId.GenerateNewId() });
}
public IAsyncEnumerable<DpzScope> ListAsync(
int? count,
int? offset,
CancellationToken cancellationToken
)
{
logger.LogInformation(
"List scopes with pagination: offset={Offset}, count={Count}",
offset,
count
);
var options = new FindOptions<DpzScope>();
if (offset.HasValue)
{
options.Skip = offset.Value;
}
if (count.HasValue)
{
options.Limit = count.Value;
}
return repository.SearchForAsync(
Builders<DpzScope>.Filter.Empty,
options,
cancellationToken
);
}
public async IAsyncEnumerable<TResult> ListAsync<TState, TResult>(
Func<IQueryable<DpzScope>, TState, IQueryable<TResult>> query,
TState state,
[System.Runtime.CompilerServices.EnumeratorCancellation] CancellationToken cancellationToken
)
{
logger.LogInformation("List scopes with custom query state={State}", state);
var projected = query(repository.MongodbQueryable, state);
await foreach (
var element in projected.ToAsyncEnumerable().WithCancellation(cancellationToken)
)
{
yield return element;
}
}
public async ValueTask SetDescriptionAsync(
DpzScope scope,
string? description,
CancellationToken cancellationToken
)
{
logger.LogInformation("Set description of scope {Id}", scope.Id);
scope.Description = description;
await ValueTask.CompletedTask;
}
public async ValueTask SetDescriptionsAsync(
DpzScope scope,
ImmutableDictionary<CultureInfo, string> descriptions,
CancellationToken cancellationToken
)
{
logger.LogInformation("Set descriptions of scope {Id}", scope.Id);
scope.Descriptions = descriptions.ToDictionary(k => k.Key.Name, v => v.Value);
await ValueTask.CompletedTask;
}
public async ValueTask SetDisplayNameAsync(
DpzScope scope,
string? name,
CancellationToken cancellationToken
)
{
logger.LogInformation("Set display name of scope {Id}", scope.Id);
scope.DisplayName = name;
await ValueTask.CompletedTask;
}
public async ValueTask SetDisplayNamesAsync(
DpzScope scope,
ImmutableDictionary<CultureInfo, string> names,
CancellationToken cancellationToken
)
{
logger.LogInformation("Set display names of scope {Id}", scope.Id);
scope.DisplayNames = names.ToDictionary(k => k.Key.Name, v => v.Value);
await ValueTask.CompletedTask;
}
public async ValueTask SetNameAsync(
DpzScope scope,
string? name,
CancellationToken cancellationToken
)
{
logger.LogInformation("Set name of scope {Id}", scope.Id);
scope.Name = name;
await ValueTask.CompletedTask;
}
public async ValueTask SetPropertiesAsync(
DpzScope scope,
ImmutableDictionary<string, JsonElement> properties,
CancellationToken cancellationToken
)
{
logger.LogInformation("Set properties of scope {Id}", scope.Id);
if (properties.Count == 0)
{
scope.Properties = null;
await ValueTask.CompletedTask;
return;
}
var bson = new BsonDocument();
foreach (var kv in properties)
{
bson[kv.Key] = BsonDocument.Parse(kv.Value.GetRawText());
}
scope.Properties = bson;
await ValueTask.CompletedTask;
}
public async ValueTask SetResourcesAsync(
DpzScope scope,
ImmutableArray<string> resources,
CancellationToken cancellationToken
)
{
logger.LogInformation("Set resources of scope {Id}", scope.Id);
scope.Resources = resources.IsDefault ? [] : resources.ToArray();
await ValueTask.CompletedTask;
}
public async ValueTask UpdateAsync(DpzScope scope, CancellationToken cancellationToken)
{
logger.LogInformation("Updating scope {Name} ({Id})", scope.Name, scope.Id);
await repository.UpdateAsync(scope, cancellationToken);
}
}
上述代码定义了一个名为 DpzScopeStore 的类,它实现了 IOpenIddictScopeStore<DpzScope> 接口。这个类主要用于管理与 OAuth2/OpenID Connect 相关的 "scope"(范围)对象。以下是代码的主要功能和结构的详细解释:
DpzScopeStore(IRepository<DpzScope> repository, ILogger<DpzScopeStore> logger):构造函数接受一个 IRepository<DpzScope> 类型的 repository 和一个 ILogger<DpzScopeStore> 类型的 logger。repository 用于与 MongoDB 数据库进行交互,而 logger 用于记录日志。计数方法:
CountAsync:返回数据库中所有 scope 的数量。CountAsync<TResult>:根据自定义查询返回符合条件的 scope 数量。创建和删除:
CreateAsync:创建一个新的 scope 并将其插入数据库。DeleteAsync:根据 scope 的 ID 删除指定的 scope。查找方法:
FindByIdAsync:根据 ID 查找 scope。FindByNameAsync:根据名称查找 scope。FindByNamesAsync:根据名称数组查找多个 scope。FindByResourceAsync:根据资源查找 scope。获取属性:
GetAsync:根据自定义查询获取 scope。GetDescriptionAsync、GetDisplayNameAsync:获取 scope 的描述和显示名称。GetResourcesAsync:获取 scope 关联的资源。GetPropertiesAsync:获取 scope 的自定义属性。实例化和列出:
InstantiateAsync:实例化一个新的 DpzScope 对象。ListAsync:列出所有 scope,支持分页。ListAsync<TState, TResult>:根据自定义查询列出 scope。设置属性:
SetDescriptionAsync、SetDisplayNameAsync、SetNameAsync:设置 scope 的描述、显示名称和名称。SetResourcesAsync:设置 scope 关联的资源。SetPropertiesAsync:设置 scope 的自定义属性。SetDescriptionsAsync、SetDisplayNamesAsync:设置多语言描述和显示名称。更新:
UpdateAsync:更新现有的 scope。在每个方法中,使用 logger 记录相关操作的信息,例如创建、删除、查找和更新 scope 的操作。这有助于在生产环境中进行调试和监控。
所有方法都使用 async 和 ValueTask,这表明它们是异步的,适合在高并发环境中使用,以提高性能。
在获取和设置描述和显示名称时,使用 ImmutableDictionary<CultureInfo, string> 来支持多语言,这使得系统能够处理不同文化的文本。
DpzScopeStore 类是一个用于管理 OAuth2/OpenID Connect 中的 scope 的服务类,提供了创建、查找、更新和删除等基本操作,并且支持异步编程和多语言处理。它通过与 MongoDB 的交互来持久化这些操作,并通过日志记录来跟踪操作的执行情况。
