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
107 changes: 72 additions & 35 deletions src/Gemstone.Web/APIController/AuthorizationInfoControllerBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -190,6 +190,53 @@ bool isSupported(string claimType) => claimsProvider
/// <returns>A list of resources within the application.</returns>
[HttpGet, Route("resources")]
public virtual async Task<IActionResult> GetResources(IAuthorizationPolicyProvider policyProvider, EndpointDataSource endpointDataSource)
{
Dictionary<string, HashSet<ResourceAccessType>> resourceAccessLookup = await ResourceAccessLookup<ControllerAccessRequirement>(policyProvider, endpointDataSource);

IEnumerable<AuthorizationResource> resources = resourceAccessLookup
.OrderBy(kvp => kvp.Key)
.Select(kvp => new AuthorizationResource
{
Type = "Controller",
Name = kvp.Key,
AccessTypes = kvp.Value.OrderBy(type => type)
});

return Ok(resources);

}

/// <summary>
/// Gets a list of API resources available for which permissions can be granted within the application.
/// </summary>
/// <param name="policyProvider">Provides authorization policies defined within the application</param>
/// <param name="endpointDataSource">Source for endpoint data used to look up controller and action metadata</param>
/// <returns>A list of resources within the application.</returns>
[HttpGet, Route("APIresources")]
public virtual async Task<IActionResult> GetAPIResources(IAuthorizationPolicyProvider policyProvider, EndpointDataSource endpointDataSource)
{
Dictionary<string, HashSet<ResourceAccessType>> resourceAccessLookup = await ResourceAccessLookup<APIAccessRequirement>(policyProvider, endpointDataSource);

IEnumerable<AuthorizationResource> resources = resourceAccessLookup
.OrderBy(kvp => kvp.Key)
.Select(kvp => new AuthorizationResource
{
Type = "API",
Name = kvp.Key,
AccessTypes = kvp.Value.OrderBy(type => type)
});

return Ok(resources);

}

/// <summary>
/// Gets a list of resources available with the provided <see cref="IAuthorizationRequirement"/>.
/// </summary>
/// <param name="policyProvider">Provides authorization policies defined within the application</param>
/// <param name="endpointDataSource">Source for endpoint data used to look up controller and action metadata</param>
/// <returns>A list of resources within the application.</returns>
private async Task<Dictionary<string, HashSet<ResourceAccessType>>> ResourceAccessLookup<T>(IAuthorizationPolicyProvider policyProvider, EndpointDataSource endpointDataSource) where T : IAuthorizationRequirement
{
Dictionary<string, HashSet<ResourceAccessType>> resourceAccessLookup = [];

Expand All @@ -206,12 +253,12 @@ public virtual async Task<IActionResult> GetResources(IAuthorizationPolicyProvid
IReadOnlyList<IAuthorizationRequirementData> requirementData = endpoint.Metadata.GetOrderedMetadata<IAuthorizationRequirementData>() ?? [];
AuthorizationPolicy? policy = await AuthorizationPolicy.CombineAsync(policyProvider, authorizeData, policies);

bool hasControllerAccessRequirement = requirementData
bool hasAccessRequirement = requirementData
.SelectMany(datum => datum.GetRequirements())
.Concat(policy?.Requirements ?? [])
.Any(requirement => requirement is ControllerAccessRequirement);
.Any(requirement => requirement is T);

if (!hasControllerAccessRequirement)
if (!hasAccessRequirement)
continue;

IReadOnlyList<ResourceAccessAttribute> accessAttributes = endpoint.Metadata
Expand All @@ -222,38 +269,7 @@ public virtual async Task<IActionResult> GetResources(IAuthorizationPolicyProvid
HashSet<ResourceAccessType> access = resourceAccessLookup.GetOrAdd(resourceName, _ => []);
access.UnionWith(accessTypes);
}

IEnumerable<AuthorizationResource> resources = resourceAccessLookup
.OrderBy(kvp => kvp.Key)
.Select(kvp => new AuthorizationResource
{
Type = "Controller",
Name = kvp.Key,
AccessTypes = kvp.Value.OrderBy(type => type)
});

return Ok(resources);

static IEnumerable<ResourceAccessType> ToAccessTypes(Endpoint endpoint, IEnumerable<ResourceAccessAttribute> accessAttributes)
{
ResourceAccessType accessType = accessAttributes.GetAccessType();

if (accessType == ResourceAccessType.None)
return [];

if (accessType != ResourceAccessType.Default)
return [accessType];

HttpMethodMetadata? httpMethodMetadata = endpoint.Metadata
.GetMetadata<HttpMethodMetadata>();

IReadOnlyList<string> httpMethods = httpMethodMetadata?.HttpMethods
?? [];

return httpMethods
.Select(accessAttributes.GetAccessType)
.Where(type => type != ResourceAccessType.None);
}
return resourceAccessLookup;
}

/// <summary>
Expand All @@ -273,6 +289,27 @@ public virtual IEnumerable<bool> CheckAccess([FromBody] ResourceAccessEntry[] ac

// Static Methods

private static IEnumerable<ResourceAccessType> ToAccessTypes(Endpoint endpoint, IEnumerable<ResourceAccessAttribute> accessAttributes)
{
ResourceAccessType accessType = accessAttributes.GetAccessType();

if (accessType == ResourceAccessType.None)
return [];

if (accessType != ResourceAccessType.Default)
return [accessType];

HttpMethodMetadata? httpMethodMetadata = endpoint.Metadata
.GetMetadata<HttpMethodMetadata>();

IReadOnlyList<string> httpMethods = httpMethodMetadata?.HttpMethods
?? [];

return httpMethods
.Select(accessAttributes.GetAccessType)
.Where(type => type != ResourceAccessType.None);
}

private static Regex? ToSearchPattern(string? searchText)
{
if (searchText is null)
Expand Down
12 changes: 6 additions & 6 deletions src/Gemstone.Web/APIController/ReadOnlyModelController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -337,12 +337,12 @@ public virtual async Task<IActionResult> Search([FromBody] SearchPost<T> postDat

if (ParentKey != string.Empty && parentID is not null)
{
filters.Append(new RecordFilter<T>()
filters = filters.Append(new RecordFilter<T>()
{
FieldName = ParentKey,
Operator = "=",
SearchParameter = parentID
});
}).ToArray();
}

IAsyncEnumerable<T> result = tableOperations.QueryRecordsAsync(HttpContext.User, postData.OrderBy, postData.Ascending, page, PageSize, cancellationToken, filters);
Expand All @@ -367,12 +367,12 @@ public virtual async Task<IActionResult> GetPageInfo([FromBody] SearchPost<T> po

if (ParentKey != string.Empty && parentID is not null)
{
filters.Append(new RecordFilter<T>()
filters = filters.Append(new RecordFilter<T>()
{
FieldName = ParentKey,
Operator = "=",
SearchParameter = parentID
});
}).ToArray();
}

int recordCount = await tableOperations.QueryRecordCountAsync(HttpContext.User, cancellationToken, filters).ConfigureAwait(false);
Expand Down Expand Up @@ -401,12 +401,12 @@ public virtual async Task<IActionResult> GetPageInfo(string? parentID, Cancellat

if (ParentKey != string.Empty && parentID is not null)
{
filters.Append(new RecordFilter<T>()
filters = filters.Append(new RecordFilter<T>()
{
FieldName = ParentKey,
Operator = "=",
SearchParameter = parentID
});
}).ToArray();
}

int recordCount = await tableOperations.QueryRecordCountAsync(HttpContext.User, cancellationToken, filters).ConfigureAwait(false);
Expand Down