Skip to content

Commit

Permalink
Merge pull request #1271 from solliancenet/aa-http-clients-rebased
Browse files Browse the repository at this point in the history
Unified HTTP clients
  • Loading branch information
ciprianjichici authored Jul 25, 2024
2 parents e753746 + 4efc2d1 commit 1c616cb
Show file tree
Hide file tree
Showing 58 changed files with 587 additions and 527 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,8 @@ private async Task<ResourceProviderUpsertResult> UpdateRoleAssignments(ResourceP
RoleDefinitionId = roleAssignment.RoleDefinitionId,
Scope = roleAssignment.Scope,
CreatedBy = userIdentity.UPN
});
},
userIdentity);

if (roleAssignmentResult.Success)
return new ResourceProviderUpsertResult
Expand All @@ -142,7 +143,10 @@ protected override async Task DeleteResourceAsync(ResourcePath resourcePath, Uni
switch (resourcePath.ResourceTypeInstances.Last().ResourceType)
{
case AuthorizationResourceTypeNames.RoleAssignments:
await _authorizationService.RevokeRoleAssignment(_instanceSettings.Id, resourcePath.ResourceTypeInstances.Last().ResourceId!);
await _authorizationService.RevokeRoleAssignment(
_instanceSettings.Id,
resourcePath.ResourceTypeInstances.Last().ResourceId!,
userIdentity);
break;
default:
throw new ResourceProviderException($"The resource type {resourcePath.ResourceTypeInstances.Last().ResourceType} is not supported by the {_name} resource provider.",
Expand Down Expand Up @@ -176,7 +180,8 @@ private async Task<List<ResourceProviderGetResult<RoleAssignment>>> FilterRoleAs
else
{
var roleAssignments = new List<RoleAssignment>();
var roleAssignmentObjects = await _authorizationService.GetRoleAssignments(_instanceSettings.Id, queryParameters);
var roleAssignmentObjects = await _authorizationService.GetRoleAssignments(
_instanceSettings.Id, queryParameters, userIdentity);

foreach (var obj in roleAssignmentObjects)
{
Expand Down
70 changes: 38 additions & 32 deletions src/dotnet/Authorization/Services/AuthorizationService.cs
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
using FoundationaLLM.Authorization.Models.Configuration;
using FoundationaLLM.Common.Authentication;
using FoundationaLLM.Common.Constants;
using FoundationaLLM.Common.Interfaces;
using FoundationaLLM.Common.Models.Authentication;
using FoundationaLLM.Common.Models.Authorization;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using System.Net.Http.Headers;
using System.Net.Http.Json;
using System.Text.Json;

Expand All @@ -16,29 +16,39 @@ namespace FoundationaLLM.Authorization.Services
public class AuthorizationService : IAuthorizationService
{
private readonly AuthorizationServiceSettings _settings;
private readonly IHttpClientFactory _httpClientFactory;
private readonly IHttpClientFactoryService _httpClientFactoryService;
private readonly ILogger<AuthorizationService> _logger;

public AuthorizationService(
IHttpClientFactory httpClientFactory,
IHttpClientFactoryService httpClientFactoryService,
IOptions<AuthorizationServiceSettings> options,
ILogger<AuthorizationService> logger)
{
_settings = options.Value;
_httpClientFactory = httpClientFactory;
_httpClientFactoryService = httpClientFactoryService;
_logger = logger;
}

/// <inheritdoc/>
public async Task<ActionAuthorizationResult> ProcessAuthorizationRequest(
string instanceId,
ActionAuthorizationRequest authorizationRequest)
string action,
List<string> resourcePaths,
UnifiedUserIdentity userIdentity)
{
var defaultResults = authorizationRequest.ResourcePaths.Distinct().ToDictionary(rp => rp, auth => false);
var defaultResults = resourcePaths.Distinct().ToDictionary(rp => rp, auth => false);

try
{
var httpClient = await CreateHttpClient();
var authorizationRequest = new ActionAuthorizationRequest
{
Action = action,
ResourcePaths = resourcePaths,
PrincipalId = userIdentity.UserId,
SecurityGroupIds = userIdentity.GroupIds
};

var httpClient = await _httpClientFactoryService.CreateClient(HttpClients.AuthorizationAPI, userIdentity);
var response = await httpClient.PostAsync(
$"/instances/{instanceId}/authorize",
JsonContent.Create(authorizationRequest));
Expand All @@ -60,11 +70,14 @@ public async Task<ActionAuthorizationResult> ProcessAuthorizationRequest(
}

/// <inheritdoc/>
public async Task<RoleAssignmentResult> ProcessRoleAssignmentRequest(string instanceId, RoleAssignmentRequest roleAssignmentRequest)
public async Task<RoleAssignmentResult> ProcessRoleAssignmentRequest(
string instanceId,
RoleAssignmentRequest roleAssignmentRequest,
UnifiedUserIdentity userIdentity)
{
try
{
var httpClient = await CreateHttpClient();
var httpClient = await _httpClientFactoryService.CreateClient(HttpClients.AuthorizationAPI, userIdentity);
var response = await httpClient.PostAsync(
$"/instances/{instanceId}/roleassignments",
JsonContent.Create(roleAssignmentRequest));
Expand All @@ -91,13 +104,16 @@ public async Task<RoleAssignmentResult> ProcessRoleAssignmentRequest(string inst
}

/// <inheritdoc/>
public async Task<Dictionary<string, RoleAssignmentsWithActionsResult>> ProcessRoleAssignmentsWithActionsRequest(string instanceId, RoleAssignmentsWithActionsRequest request)
public async Task<Dictionary<string, RoleAssignmentsWithActionsResult>> ProcessRoleAssignmentsWithActionsRequest(
string instanceId,
RoleAssignmentsWithActionsRequest request,
UnifiedUserIdentity userIdentity)
{
var defaultResults = request.Scopes.Distinct().ToDictionary(scp => scp, res => new RoleAssignmentsWithActionsResult() { Actions = [], Roles = [] });

try
{
var httpClient = await CreateHttpClient();
var httpClient = await _httpClientFactoryService.CreateClient(HttpClients.AuthorizationAPI, userIdentity);
var response = await httpClient.PostAsync(
$"/instances/{instanceId}/roleassignments/querywithactions",
JsonContent.Create(request));
Expand All @@ -120,11 +136,14 @@ public async Task<Dictionary<string, RoleAssignmentsWithActionsResult>> ProcessR


/// <inheritdoc/>
public async Task<List<object>> GetRoleAssignments(string instanceId, RoleAssignmentQueryParameters queryParameters)
public async Task<List<object>> GetRoleAssignments(
string instanceId,
RoleAssignmentQueryParameters queryParameters,
UnifiedUserIdentity userIdentity)
{
try
{
var httpClient = await CreateHttpClient();
var httpClient = await _httpClientFactoryService.CreateClient(HttpClients.AuthorizationAPI, userIdentity);
var response = await httpClient.PostAsync(
$"/instances/{instanceId}/roleassignments/query",
JsonContent.Create(queryParameters));
Expand All @@ -146,11 +165,14 @@ public async Task<List<object>> GetRoleAssignments(string instanceId, RoleAssign
}

/// <inheritdoc/>
public async Task<RoleAssignmentResult> RevokeRoleAssignment(string instanceId, string roleAssignment)
public async Task<RoleAssignmentResult> RevokeRoleAssignment(
string instanceId,
string roleAssignment,
UnifiedUserIdentity userIdentity)
{
try
{
var httpClient = await CreateHttpClient();
var httpClient = await _httpClientFactoryService.CreateClient(HttpClients.AuthorizationAPI, userIdentity);
var response = await httpClient.DeleteAsync(
$"/instances/{instanceId}/roleassignments/{roleAssignment}");

Expand All @@ -174,21 +196,5 @@ public async Task<RoleAssignmentResult> RevokeRoleAssignment(string instanceId,
return new RoleAssignmentResult() { Success = false };
}
}

private async Task<HttpClient> CreateHttpClient()
{
var httpClient = _httpClientFactory.CreateClient();
httpClient.BaseAddress = new Uri(_settings.APIUrl);

var credentials = DefaultAuthentication.AzureCredential;
var tokenResult = await credentials.GetTokenAsync(
new ([_settings.APIScope]),
default);

httpClient.DefaultRequestHeaders.Authorization =
new AuthenticationHeaderValue("Bearer", tokenResult.Token);

return httpClient;
}
}
}
29 changes: 23 additions & 6 deletions src/dotnet/Authorization/Services/NullAuthorizationService.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using FoundationaLLM.Common.Interfaces;
using FoundationaLLM.Common.Models.Authentication;
using FoundationaLLM.Common.Models.Authorization;

namespace FoundationaLLM.Authorization.Services
Expand All @@ -9,23 +10,33 @@ namespace FoundationaLLM.Authorization.Services
public class NullAuthorizationService : IAuthorizationService
{
/// <inheritdoc/>
public async Task<ActionAuthorizationResult> ProcessAuthorizationRequest(string instanceId, ActionAuthorizationRequest authorizationRequest)
public async Task<ActionAuthorizationResult> ProcessAuthorizationRequest(
string instanceId,
string action,
List<string> resourcePaths,
UnifiedUserIdentity userIdentity)
{
var defaultResults = authorizationRequest.ResourcePaths.Distinct().ToDictionary(rp => rp, auth => true);
var defaultResults = resourcePaths.Distinct().ToDictionary(rp => rp, auth => true);

await Task.CompletedTask;
return new ActionAuthorizationResult { AuthorizationResults = defaultResults };
}

/// <inheritdoc/>
public async Task<RoleAssignmentResult> ProcessRoleAssignmentRequest(string instanceId, RoleAssignmentRequest roleAssignmentRequest)
public async Task<RoleAssignmentResult> ProcessRoleAssignmentRequest(
string instanceId,
RoleAssignmentRequest roleAssignmentRequest,
UnifiedUserIdentity userIdentity)
{
await Task.CompletedTask;
return new RoleAssignmentResult { Success = true };
}

/// <inheritdoc/>
public async Task<Dictionary<string, RoleAssignmentsWithActionsResult>> ProcessRoleAssignmentsWithActionsRequest(string instanceId, RoleAssignmentsWithActionsRequest request)
public async Task<Dictionary<string, RoleAssignmentsWithActionsResult>> ProcessRoleAssignmentsWithActionsRequest(
string instanceId,
RoleAssignmentsWithActionsRequest request,
UnifiedUserIdentity userIdentity)
{
var defaultResults = request.Scopes.Distinct().ToDictionary(scp => scp, res => new RoleAssignmentsWithActionsResult() { Actions = [], Roles = [] });

Expand All @@ -34,13 +45,19 @@ public async Task<Dictionary<string, RoleAssignmentsWithActionsResult>> ProcessR
}

/// <inheritdoc/>
public async Task<List<object>> GetRoleAssignments(string instanceId, RoleAssignmentQueryParameters queryParameters)
public async Task<List<object>> GetRoleAssignments(
string instanceId,
RoleAssignmentQueryParameters queryParameters,
UnifiedUserIdentity userIdentity)
{
await Task.CompletedTask;
return [];
}

public async Task<RoleAssignmentResult> RevokeRoleAssignment(string instanceId, string roleAssignment)
public async Task<RoleAssignmentResult> RevokeRoleAssignment(
string instanceId,
string roleAssignment,
UnifiedUserIdentity userIdentity)
{
await Task.CompletedTask;
return new RoleAssignmentResult { Success = true };
Expand Down
1 change: 1 addition & 0 deletions src/dotnet/Common/Authentication/DefaultAuthentication.cs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ public static class DefaultAuthentication
/// Initializes the default authentication.
/// </summary>
/// <param name="production">Indicates whether the environment is production or not.</param>
/// <param name="serviceName">The service name.</param>
public static void Initialize(bool production, string serviceName)
{
Production = production;
Expand Down
27 changes: 26 additions & 1 deletion src/dotnet/Common/Constants/HttpClients.cs
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ public static class HttpClients
public const string AzureAIDirect = "AzureAIDirect";

/// <summary>
/// Named client with matching configuration for a direct connection to Azure AI.
/// Named client with matching configuration for a direct connection to Azure Open AI.
/// </summary>
public const string AzureOpenAIDirect = "AzureOpenAIDirect";

Expand All @@ -67,4 +67,29 @@ public static class HttpClients
/// Named client with matching configuration for the Management API.
/// </summary>
public const string ManagementAPI = "ManagementAPI";

/// <summary>
/// Name client with matching configuration for the Azure AI Studio API.
/// </summary>
public const string AzureAIStudioAPI = "AzureAIStudioAPI";

/// <summary>
/// Name client with matching configuration for the Azure Content Safety.
/// </summary>
public const string AzureContentSafety = "AzureContentSafety";

/// <summary>
/// Name client with matching configuration for the Enkrypt Guardrails.
/// </summary>
public const string EnkryptGuardrails = "EnkryptGuardrails";

/// <summary>
/// Name client with matching configuration for the Lakera Guard.
/// </summary>
public const string LakeraGuard = "LakeraGuard";

/// <summary>
/// Name client with matching configuration for the Gateway API.
/// </summary>
public const string GatewayAPI = "GatewayAPI";
}
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,8 @@ public static async Task<List<ResourceProviderGetResult<T>>> FilterResourcesByAu
Scopes = resources.Select(x => x.ObjectId!).ToList(),
PrincipalId = userIdentity.UserId!,
SecurityGroupIds = userIdentity.GroupIds
});
},
userIdentity);

var results = new List<ResourceProviderGetResult<T>>();

Expand Down
31 changes: 24 additions & 7 deletions src/dotnet/Common/Interfaces/IAuthorizationService.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using FoundationaLLM.Common.Models.Authorization;
using FoundationaLLM.Common.Models.Authentication;
using FoundationaLLM.Common.Models.Authorization;

namespace FoundationaLLM.Common.Interfaces
{
Expand All @@ -11,46 +12,62 @@ public interface IAuthorizationService
/// Processes an action authorization request.
/// </summary>
/// <param name="instanceId">The FoundationaLLM instance id.</param>
/// <param name="authorizationRequest">The <see cref="ActionAuthorizationRequest"/> to process.</param>
/// <param name="action">The action identifier.</param>
/// <param name="resourcePaths">The resource paths.</param>
/// <param name="userIdentity">The user identity.</param>
/// <returns>An <see cref="ActionAuthorizationResult"/> containing the result of the processing.</returns>
Task<ActionAuthorizationResult> ProcessAuthorizationRequest(
string instanceId,
ActionAuthorizationRequest authorizationRequest);
string action,
List<string> resourcePaths,
UnifiedUserIdentity userIdentity);

/// <summary>
/// Processes a role assignment request.
/// </summary>
/// <param name="instanceId">The FoundationaLLM instance identifier.</param>
/// <param name="roleAssignmentRequest">The role assignment request.</param>
/// <param name="userIdentity">The user identity.</param>
/// <returns></returns>
Task<RoleAssignmentResult> ProcessRoleAssignmentRequest(
string instanceId,
RoleAssignmentRequest roleAssignmentRequest);
RoleAssignmentRequest roleAssignmentRequest,
UnifiedUserIdentity userIdentity);

/// <summary>
/// Returns a list of role names and a list of allowed actions for the specified scope.
/// </summary>
/// <param name="instanceId">The FoundationaLLM instance identifier.</param>
/// <param name="request">The get roles with actions request.</param>
/// <param name="userIdentity">The user identity.</param>
/// <returns>The get roles and actions result.</returns>
Task<Dictionary<string, RoleAssignmentsWithActionsResult>> ProcessRoleAssignmentsWithActionsRequest(
string instanceId,
RoleAssignmentsWithActionsRequest request);
RoleAssignmentsWithActionsRequest request,
UnifiedUserIdentity userIdentity);

/// <summary>
/// Returns a list of role assignments for the specified instance and resource.
/// </summary>
/// <param name="instanceId">The FoundationaLLM instance identifier.</param>
/// <param name="queryParameters">The <see cref="RoleAssignmentQueryParameters"/> providing the inputs for filtering the role assignments.</param>
/// <param name="userIdentity">The user identity.</param>
/// <returns>The list of all role assignments for the specified instance.</returns>
Task<List<object>> GetRoleAssignments(string instanceId, RoleAssignmentQueryParameters queryParameters);
Task<List<object>> GetRoleAssignments(
string instanceId,
RoleAssignmentQueryParameters queryParameters,
UnifiedUserIdentity userIdentity);

/// <summary>
/// Revokes a role assignment for a specified instance.
/// </summary>
/// <param name="instanceId">The FoundationaLLM instance identifier.</param>
/// <param name="roleAssignment">The role assignment object identifier.</param>
/// <param name="userIdentity">The user identity.</param>
/// <returns>The role assignment result.</returns>
Task<RoleAssignmentResult> RevokeRoleAssignment(string instanceId, string roleAssignment);
Task<RoleAssignmentResult> RevokeRoleAssignment(
string instanceId,
string roleAssignment,
UnifiedUserIdentity userIdentity);
}
}
Loading

0 comments on commit 1c616cb

Please sign in to comment.