Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion SecurityService.BusinessLogic/Oidc/OidcRequestHandler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,8 @@
using OpenIddict.Abstractions;
using OpenIddict.Server.AspNetCore;
using OpenIddict.Validation.AspNetCore;
using SecurityService.Database;
using SecurityService.Database.DbContexts;
using SecurityService.Database.Entities;
using SimpleResults;
using static OpenIddict.Abstractions.OpenIddictConstants;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,52 +18,40 @@ public sealed class ApiResourceRequestHandler :
IRequestHandler<SecurityServiceQueries.GetApiResourceQuery, Result<ApiResourceDetails>>,
IRequestHandler<SecurityServiceQueries.GetApiResourcesQuery, Result<List<ApiResourceDetails>>>
{
private readonly SecurityServiceDbContext _dbContext;
private readonly IOpenIddictScopeManager _scopeManager;
private readonly SecurityServiceDbContext DbContext;
private readonly IOpenIddictScopeManager ScopeManager;

public ApiResourceRequestHandler(SecurityServiceDbContext dbContext, IOpenIddictScopeManager scopeManager)
{
this._dbContext = dbContext;
this._scopeManager = scopeManager;
this.DbContext = dbContext;
this.ScopeManager = scopeManager;
}

public async Task<Result> Handle(SecurityServiceCommands.CreateApiResourceCommand command, CancellationToken cancellationToken)
{
if (string.IsNullOrWhiteSpace(command.Name))
{
if (string.IsNullOrWhiteSpace(command.Name)) {
return Result.Invalid("API resource name is required.");
}

if (await this._dbContext.ResourceDefinitions.AnyAsync(resource => resource.Name == command.Name && resource.Type == ResourceType.ApiResource, cancellationToken))
{
if (await this.DbContext.ResourceDefinitions.AnyAsync(resource => resource.Name == command.Name && resource.Type == ResourceType.ApiResource, cancellationToken)) {
return Result.Conflict($"An API resource named '{command.Name}' already exists.");
}

foreach (var scopeName in command.Scopes.Distinct(StringComparer.OrdinalIgnoreCase))
{
var scope = await this._scopeManager.FindByNameAsync(scopeName, cancellationToken);
if (scope is null)
{
this._scopeManager.CreateAsync(new OpenIddictScopeDescriptor
{
Name = scopeName,
DisplayName = scopeName,
Description = $"Auto-created scope for API resource '{command.Name}'."
}, cancellationToken).GetAwaiter().GetResult();
scope = await this._scopeManager.FindByNameAsync(scopeName, cancellationToken);
foreach (String scopeName in command.Scopes.Distinct(StringComparer.OrdinalIgnoreCase)) {
Object? scope = await this.ScopeManager.FindByNameAsync(scopeName, cancellationToken);
if (scope is null) {
scope = await this.ScopeManager.CreateAsync(new OpenIddictScopeDescriptor { Name = scopeName, DisplayName = scopeName, Description = $"Auto-created scope for API resource '{command.Name}'." }, cancellationToken);
}

var descriptor = new OpenIddictScopeDescriptor();
await this._scopeManager.PopulateAsync(descriptor, scope, cancellationToken);
if (descriptor.Resources.Contains(command.Name, StringComparer.OrdinalIgnoreCase) == false)
{
OpenIddictScopeDescriptor descriptor = new();
await this.ScopeManager.PopulateAsync(descriptor, scope, cancellationToken);
if (descriptor.Resources.Contains(command.Name, StringComparer.OrdinalIgnoreCase) == false) {
descriptor.Resources.Add(command.Name);
await this._scopeManager.UpdateAsync(scope, descriptor, cancellationToken);
await this.ScopeManager.UpdateAsync(scope, descriptor, cancellationToken);
}
}

var resource = new ResourceDefinition
{
ResourceDefinition resource = new() {
Id = Guid.NewGuid(),
Name = command.Name,
DisplayName = command.DisplayName,
Expand All @@ -74,23 +62,68 @@ public async Task<Result> Handle(SecurityServiceCommands.CreateApiResourceComman
ScopesJson = JsonListSerializer.Serialize(command.Scopes)
};

this._dbContext.ResourceDefinitions.Add(resource);
await this._dbContext.SaveChangesAsync(cancellationToken);
await this.DbContext.ResourceDefinitions.AddAsync(resource, cancellationToken);
await this.DbContext.SaveChangesAsync(cancellationToken);

return Result.Success();
}

public async Task<Result<ApiResourceDetails>> Handle(SecurityServiceQueries.GetApiResourceQuery query, CancellationToken cancellationToken)
{
var resource = await this._dbContext.ResourceDefinitions.SingleOrDefaultAsync(definition => definition.Name == query.Name && definition.Type == ResourceType.ApiResource, cancellationToken);
return resource is null
? Result.NotFound($"No API resource named '{query.Name}' was found.")
: Result.Success(new ApiResourceDetails(resource.Name, resource.DisplayName, resource.Description, JsonListSerializer.Deserialize(resource.ScopesJson), JsonListSerializer.Deserialize(resource.ClaimsJson)));
ResourceDefinition? resource = await this.DbContext.ResourceDefinitions.SingleOrDefaultAsync(definition => definition.Name == query.Name && definition.Type == ResourceType.ApiResource, cancellationToken);
return resource switch {
null => Result.NotFound($"No API resource named '{query.Name}' was found."),
_ => Result.Success(Factory.ConvertFrom(resource))
};
}

public async Task<Result<List<ApiResourceDetails>>> Handle(SecurityServiceQueries.GetApiResourcesQuery query, CancellationToken cancellationToken)
{
var resources = await this._dbContext.ResourceDefinitions.Where(definition => definition.Type == ResourceType.ApiResource).OrderBy(definition => definition.Name).ToListAsync(cancellationToken);
return Result.Success(resources.Select(resource => new ApiResourceDetails(resource.Name, resource.DisplayName, resource.Description, JsonListSerializer.Deserialize(resource.ScopesJson), JsonListSerializer.Deserialize(resource.ClaimsJson))).ToList());
List<ResourceDefinition> resources = await this.DbContext.ResourceDefinitions.Where(definition => definition.Type == ResourceType.ApiResource).OrderBy(definition => definition.Name).ToListAsync(cancellationToken);
return Result.Success(Factory.ConvertFrom(resources));
}
}


public static class Factory
{
public static ApiResourceDetails ConvertFrom(ResourceDefinition resource) {
return new ApiResourceDetails(resource.Name, resource.DisplayName, resource.Description, JsonListSerializer.Deserialize(resource.ScopesJson), JsonListSerializer.Deserialize(resource.ClaimsJson));
}

public static List<ApiResourceDetails> ConvertFrom(List<ResourceDefinition> resources)
{
List<ApiResourceDetails> results = new();
foreach (ResourceDefinition resource in resources) {
results.Add(ConvertFrom(resource));
}
return results;
}

public static ApiScopeDetails ConvertFrom(String name, String displayName, String description)
{
return new ApiScopeDetails(name, displayName, description);
}

public static List<ApiScopeDetails> ConvertFrom(List<(String name, String displayName, String description)> resources) {
List<ApiScopeDetails> results = new();
foreach ((String name, String displayName, String description) resource in resources)
{
results.Add(ConvertFrom(resource.name, resource.displayName, resource.description));
}
return results;
}

public static ClientDetails ConvertFrom(ClientDefinition definition) => new(
definition.ClientId,
definition.ClientName,
definition.Description,
definition.ClientUri,
JsonListSerializer.Deserialize(definition.AllowedScopesJson),
JsonListSerializer.Deserialize(definition.AllowedGrantTypesJson),
JsonListSerializer.Deserialize(definition.RedirectUrisJson),
JsonListSerializer.Deserialize(definition.PostLogoutRedirectUrisJson),
definition.RequireConsent,
definition.AllowOfflineAccess,
definition.ClientType);
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,13 @@ public sealed class ApiScopeRequestHandler :
IRequestHandler<SecurityServiceQueries.GetApiScopeQuery, Result<ApiScopeDetails>>,
IRequestHandler<SecurityServiceQueries.GetApiScopesQuery, Result<List<ApiScopeDetails>>>
{
private readonly SecurityServiceDbContext _dbContext;
private readonly IOpenIddictScopeManager _scopeManager;
private readonly SecurityServiceDbContext DbContext;
private readonly IOpenIddictScopeManager ScopeManager;

public ApiScopeRequestHandler(SecurityServiceDbContext dbContext, IOpenIddictScopeManager scopeManager)
{
this._dbContext = dbContext;
this._scopeManager = scopeManager;
this.DbContext = dbContext;
this.ScopeManager = scopeManager;
}

public async Task<Result> Handle(SecurityServiceCommands.CreateApiScopeCommand command, CancellationToken cancellationToken)
Expand All @@ -31,7 +31,7 @@ public async Task<Result> Handle(SecurityServiceCommands.CreateApiScopeCommand c
return Result.Invalid("Scope name is required.");
}

if (await this._dbContext.ResourceDefinitions.AnyAsync(resource => resource.Name == command.Name && resource.Type == ResourceType.ApiScope, cancellationToken))
if (await this.DbContext.ResourceDefinitions.AnyAsync(resource => resource.Name == command.Name && resource.Type == ResourceType.ApiScope, cancellationToken))
{
return Result.Conflict($"An API scope named '{command.Name}' already exists.");
}
Expand All @@ -43,7 +43,7 @@ public async Task<Result> Handle(SecurityServiceCommands.CreateApiScopeCommand c
Description = command.Description
};

await this._scopeManager.CreateAsync(descriptor, cancellationToken);
await this.ScopeManager.CreateAsync(descriptor, cancellationToken);

var resource = new ResourceDefinition
{
Expand All @@ -54,23 +54,23 @@ public async Task<Result> Handle(SecurityServiceCommands.CreateApiScopeCommand c
Type = ResourceType.ApiScope
};

this._dbContext.ResourceDefinitions.Add(resource);
await this._dbContext.SaveChangesAsync(cancellationToken);
await this.DbContext.ResourceDefinitions.AddAsync(resource, cancellationToken);
await this.DbContext.SaveChangesAsync(cancellationToken);

return Result.Success();
}

public async Task<Result<ApiScopeDetails>> Handle(SecurityServiceQueries.GetApiScopeQuery query, CancellationToken cancellationToken)
{
var resource = await this._dbContext.ResourceDefinitions.SingleOrDefaultAsync(definition => definition.Name == query.Name && definition.Type == ResourceType.ApiScope, cancellationToken);
var resource = await this.DbContext.ResourceDefinitions.SingleOrDefaultAsync(definition => definition.Name == query.Name && definition.Type == ResourceType.ApiScope, cancellationToken);
return resource is null
? Result.NotFound($"No API scope named '{query.Name}' was found.")
: Result.Success(new ApiScopeDetails(resource.Name, resource.DisplayName, resource.Description));
: Result.Success(Factory.ConvertFrom(resource.Name, resource.DisplayName, resource.Description));
}

public async Task<Result<List<ApiScopeDetails>>> Handle(SecurityServiceQueries.GetApiScopesQuery query, CancellationToken cancellationToken)
{
var scopes = await this._dbContext.ResourceDefinitions.Where(definition => definition.Type == ResourceType.ApiScope).OrderBy(definition => definition.Name).Select(definition => new ApiScopeDetails(definition.Name, definition.DisplayName, definition.Description)).ToArrayAsync(cancellationToken);
return Result.Success(scopes.ToList());
var scopes = await this.DbContext.ResourceDefinitions.Where(definition => definition.Type == ResourceType.ApiScope).OrderBy(definition => definition.Name).ToListAsync(cancellationToken);
return Result.Success(Factory.ConvertFrom(scopes.Select(definition => (definition.Name, definition.DisplayName, definition.Description)).ToList()));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -29,13 +29,13 @@ public sealed class ClientRequestHandler :
GrantTypes.RefreshToken
};

private readonly SecurityServiceDbContext _dbContext;
private readonly IOpenIddictApplicationManager _applicationManager;
private readonly SecurityServiceDbContext DbContext;
private readonly IOpenIddictApplicationManager ApplicationManager;

public ClientRequestHandler(SecurityServiceDbContext dbContext, IOpenIddictApplicationManager applicationManager)
{
this._dbContext = dbContext;
this._applicationManager = applicationManager;
this.DbContext = dbContext;
this.ApplicationManager = applicationManager;
}

public async Task<Result> Handle(SecurityServiceCommands.CreateClientCommand command, CancellationToken cancellationToken)
Expand All @@ -50,18 +50,18 @@ public async Task<Result> Handle(SecurityServiceCommands.CreateClientCommand com
return Result.Invalid("At least one grant type is required.");
}

var invalidGrantTypes = command.AllowedGrantTypes.Where(grantType => SupportedGrantTypes.Contains(grantType) == false).Distinct(StringComparer.OrdinalIgnoreCase).ToArray();
String[] invalidGrantTypes = command.AllowedGrantTypes.Where(grantType => SupportedGrantTypes.Contains(grantType) == false).Distinct(StringComparer.OrdinalIgnoreCase).ToArray();
if (invalidGrantTypes.Length > 0)
{
return Result.Invalid($"Unsupported grant types: {string.Join(", ", invalidGrantTypes)}.");
}

if (await this._dbContext.ClientDefinitions.AnyAsync(client => client.ClientId == command.ClientId, cancellationToken))
if (await this.DbContext.ClientDefinitions.AnyAsync(client => client.ClientId == command.ClientId, cancellationToken))
{
return Result.Conflict($"A client with id '{command.ClientId}' already exists.");
}

var descriptor = new OpenIddictApplicationDescriptor
OpenIddictApplicationDescriptor descriptor = new OpenIddictApplicationDescriptor
{
ClientId = command.ClientId,
DisplayName = command.ClientName,
Expand All @@ -70,19 +70,19 @@ public async Task<Result> Handle(SecurityServiceCommands.CreateClientCommand com
ClientSecret = string.IsNullOrWhiteSpace(command.Secret) ? null : command.Secret
};

foreach (var redirectUri in command.ClientRedirectUris.Where(uri => string.IsNullOrWhiteSpace(uri) == false).Distinct(StringComparer.OrdinalIgnoreCase))
foreach (String redirectUri in command.ClientRedirectUris.Where(uri => string.IsNullOrWhiteSpace(uri) == false).Distinct(StringComparer.OrdinalIgnoreCase))
{
descriptor.RedirectUris.Add(new Uri(redirectUri, UriKind.Absolute));
}

foreach (var postLogoutRedirectUri in command.ClientPostLogoutRedirectUris.Where(uri => string.IsNullOrWhiteSpace(uri) == false).Distinct(StringComparer.OrdinalIgnoreCase))
foreach (String postLogoutRedirectUri in command.ClientPostLogoutRedirectUris.Where(uri => string.IsNullOrWhiteSpace(uri) == false).Distinct(StringComparer.OrdinalIgnoreCase))
{
descriptor.PostLogoutRedirectUris.Add(new Uri(postLogoutRedirectUri, UriKind.Absolute));
}

await this._applicationManager.CreateAsync(descriptor, cancellationToken);
await this.ApplicationManager.CreateAsync(descriptor, cancellationToken);

var definition = new ClientDefinition
ClientDefinition definition = new ClientDefinition
{
Id = Guid.NewGuid(),
ClientId = command.ClientId,
Expand All @@ -99,36 +99,25 @@ public async Task<Result> Handle(SecurityServiceCommands.CreateClientCommand com
ClientType = descriptor.ClientType
};

this._dbContext.ClientDefinitions.Add(definition);
await this._dbContext.SaveChangesAsync(cancellationToken);
await this.DbContext.ClientDefinitions.AddAsync(definition, cancellationToken);
await this.DbContext.SaveChangesAsync(cancellationToken);

return Result.Success();
}

public async Task<Result<ClientDetails>> Handle(SecurityServiceQueries.GetClientQuery query, CancellationToken cancellationToken)
{
var definition = await this._dbContext.ClientDefinitions.SingleOrDefaultAsync(client => client.ClientId == query.ClientId, cancellationToken);
ClientDefinition? definition = await this.DbContext.ClientDefinitions.SingleOrDefaultAsync(client => client.ClientId == query.ClientId, cancellationToken);
return definition is null
? Result.NotFound($"No client found with id '{query.ClientId}'.")
: Result.Success(Map(definition));
: Result.Success(Factory.ConvertFrom(definition));
}

public async Task<Result<List<ClientDetails>>> Handle(SecurityServiceQueries.GetClientsQuery query, CancellationToken cancellationToken)
{
var definitions = await this._dbContext.ClientDefinitions.OrderBy(client => client.ClientId).ToListAsync(cancellationToken);
return Result.Success(definitions.Select(Map).ToList());
List<ClientDefinition> definitions = await this.DbContext.ClientDefinitions.OrderBy(client => client.ClientId).ToListAsync(cancellationToken);
return Result.Success(definitions.Select(Factory.ConvertFrom).ToList());
}

private static ClientDetails Map(ClientDefinition definition) => new(
definition.ClientId,
definition.ClientName,
definition.Description,
definition.ClientUri,
JsonListSerializer.Deserialize(definition.AllowedScopesJson),
JsonListSerializer.Deserialize(definition.AllowedGrantTypesJson),
JsonListSerializer.Deserialize(definition.RedirectUrisJson),
JsonListSerializer.Deserialize(definition.PostLogoutRedirectUrisJson),
definition.RequireConsent,
definition.AllowOfflineAccess,
definition.ClientType);

}
Loading
Loading