Skip to content

Commit

Permalink
.Net: Provide access to the security scheme and requirement for an Op…
Browse files Browse the repository at this point in the history
…en API function (#9430)

### Motivation and Context

Exploring a fix for #9429 

### Description

<!-- Describe your changes, the overall approach, the underlying design.
These notes will help understanding how your code works. Thanks! -->

### Contribution Checklist

<!-- Before submitting this PR, please make sure: -->

- [ ] The code builds clean without any errors or warnings
- [ ] The PR follows the [SK Contribution
Guidelines](https://github.com/microsoft/semantic-kernel/blob/main/CONTRIBUTING.md)
and the [pre-submission formatting
script](https://github.com/microsoft/semantic-kernel/blob/main/CONTRIBUTING.md#development-scripts)
raises no violations
- [ ] All unit tests pass, and I have added new tests where possible
- [ ] I didn't break anyone 😄
  • Loading branch information
markwallace-microsoft authored Nov 6, 2024
1 parent 874ee95 commit 7222246
Show file tree
Hide file tree
Showing 16 changed files with 634 additions and 15 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -146,15 +146,17 @@ public static async Task<KernelPlugin> CreatePluginFromApiManifestAsync(
var server = filteredOpenApiDocument.Servers.FirstOrDefault();
if (server?.Url is not null)
{
var info = OpenApiDocumentParser.ExtractRestApiInfo(filteredOpenApiDocument);
var security = OpenApiDocumentParser.CreateRestApiOperationSecurityRequirements(filteredOpenApiDocument.SecurityRequirements);
foreach (var path in filteredOpenApiDocument.Paths)
{
var operations = OpenApiDocumentParser.CreateRestApiOperations([server], path.Key, path.Value, null, logger);
var operations = OpenApiDocumentParser.CreateRestApiOperations(filteredOpenApiDocument, path.Key, path.Value, null, logger);
foreach (RestApiOperation operation in operations)
{
try
{
logger.LogTrace("Registering Rest function {0}.{1}", pluginName, operation.Id);
functions.Add(OpenApiKernelPluginFactory.CreateRestApiFunction(pluginName, runner, operation, openApiFunctionExecutionParameters, new Uri(server.Url), loggerFactory));
functions.Add(OpenApiKernelPluginFactory.CreateRestApiFunction(pluginName, runner, info, security, operation, openApiFunctionExecutionParameters, new Uri(server.Url), loggerFactory));
}
catch (Exception ex) when (!ex.IsCriticalException())
{
Expand Down
45 changes: 45 additions & 0 deletions dotnet/src/Functions/Functions.OpenApi/Model/RestApiOAuthFlow.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
// Copyright (c) Microsoft. All rights reserved.

using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;

namespace Microsoft.SemanticKernel.Plugins.OpenApi;

/// <summary>
/// REST API OAuth Flow.
/// </summary>
[Experimental("SKEXP0040")]
public sealed class RestApiOAuthFlow
{
/// <summary>
/// REQUIRED. The authorization URL to be used for this flow.
/// Applies to implicit and authorizationCode OAuthFlow.
/// </summary>
public Uri AuthorizationUrl { get; init; }

/// <summary>
/// REQUIRED. The token URL to be used for this flow.
/// Applies to password, clientCredentials, and authorizationCode OAuthFlow.
/// </summary>
public Uri TokenUrl { get; init; }

/// <summary>
/// The URL to be used for obtaining refresh tokens.
/// </summary>
public Uri? RefreshUrl { get; init; }

/// <summary>
/// REQUIRED. A map between the scope name and a short description for it.
/// </summary>
public IReadOnlyDictionary<string, string> Scopes { get; init; }

/// <summary>
/// Creates an instance of a <see cref="RestApiOAuthFlow"/> class.
/// </summary>
#pragma warning disable CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider adding the 'required' modifier or declaring as nullable.
internal RestApiOAuthFlow()
#pragma warning restore CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider adding the 'required' modifier or declaring as nullable.
{
}
}
39 changes: 39 additions & 0 deletions dotnet/src/Functions/Functions.OpenApi/Model/RestApiOAuthFlows.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
// Copyright (c) Microsoft. All rights reserved.

using System.Diagnostics.CodeAnalysis;

namespace Microsoft.SemanticKernel.Plugins.OpenApi;

/// <summary>
/// REST API OAuth Flows.
/// </summary>
[Experimental("SKEXP0040")]
public sealed class RestApiOAuthFlows
{
/// <summary>
/// Configuration for the OAuth Implicit flow
/// </summary>
public RestApiOAuthFlow? Implicit { get; init; }

/// <summary>
/// Configuration for the OAuth Resource Owner Password flow.
/// </summary>
public RestApiOAuthFlow? Password { get; init; }

/// <summary>
/// Configuration for the OAuth Client Credentials flow.
/// </summary>
public RestApiOAuthFlow? ClientCredentials { get; init; }

/// <summary>
/// Configuration for the OAuth Authorization Code flow.
/// </summary>
public RestApiOAuthFlow? AuthorizationCode { get; init; }

/// <summary>
/// Creates an instance of a <see cref="RestApiOAuthFlows"/> class.
/// </summary>
internal RestApiOAuthFlows()
{
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,11 @@ public sealed class RestApiOperation
/// </summary>
public IReadOnlyList<RestApiOperationServer> Servers { get; }

/// <summary>
/// The security requirements.
/// </summary>
public IReadOnlyList<RestApiSecurityRequirement>? SecurityRequirements { get; }

/// <summary>
/// The operation parameters.
/// </summary>
Expand Down Expand Up @@ -87,6 +92,7 @@ public sealed class RestApiOperation
/// <param name="parameters">The operation parameters.</param>
/// <param name="payload">The operation payload.</param>
/// <param name="responses">The operation responses.</param>
/// <param name="securityRequirements">The operation security requirements.</param>
internal RestApiOperation(
string id,
IReadOnlyList<RestApiOperationServer> servers,
Expand All @@ -95,7 +101,8 @@ internal RestApiOperation(
string description,
IReadOnlyList<RestApiOperationParameter> parameters,
RestApiOperationPayload? payload = null,
IReadOnlyDictionary<string, RestApiOperationExpectedResponse>? responses = null)
IReadOnlyDictionary<string, RestApiOperationExpectedResponse>? responses = null,
IReadOnlyList<RestApiSecurityRequirement>? securityRequirements = null)
{
this.Id = id;
this.Servers = servers;
Expand All @@ -105,6 +112,7 @@ internal RestApiOperation(
this.Parameters = parameters;
this.Payload = payload;
this.Responses = responses ?? new Dictionary<string, RestApiOperationExpectedResponse>();
this.SecurityRequirements = securityRequirements;
}

/// <summary>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
// Copyright (c) Microsoft. All rights reserved.

using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Diagnostics.CodeAnalysis;

namespace Microsoft.SemanticKernel.Plugins.OpenApi;

/// <summary>
/// The REST API security requirement object.
/// </summary>
[Experimental("SKEXP0040")]
public sealed class RestApiSecurityRequirement : ReadOnlyDictionary<RestApiSecurityScheme, IList<string>>
{
/// <summary>
/// Creates an instance of a <see cref="RestApiSecurityRequirement"/> class.
/// </summary>
/// <param name="dictionary">Dictionary containing the security schemes.</param>
internal RestApiSecurityRequirement(IDictionary<RestApiSecurityScheme, IList<string>> dictionary) : base(dictionary)
{
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
// Copyright (c) Microsoft. All rights reserved.

using System;
using System.Diagnostics.CodeAnalysis;

namespace Microsoft.SemanticKernel.Plugins.OpenApi;

/// <summary>
/// REST API Security Scheme.
/// </summary>
[Experimental("SKEXP0040")]
public sealed class RestApiSecurityScheme
{
/// <summary>
/// REQUIRED. The type of the security scheme. Valid values are "apiKey", "http", "oauth2", "openIdConnect".
/// </summary>
public string SecuritySchemeType { get; init; }

/// <summary>
/// A short description for security scheme. CommonMark syntax MAY be used for rich text representation.
/// </summary>
public string? Description { get; init; }

/// <summary>
/// REQUIRED. The name of the header, query or cookie parameter to be used.
/// </summary>
public string Name { get; init; }

/// <summary>
/// REQUIRED. The location of the API key. Valid values are "query", "header" or "cookie".
/// </summary>
public RestApiOperationParameterLocation In { get; init; }

/// <summary>
/// REQUIRED. The name of the HTTP Authorization scheme to be used
/// in the Authorization header as defined in RFC7235.
/// </summary>
public string Scheme { get; init; }

/// <summary>
/// A hint to the client to identify how the bearer token is formatted.
/// Bearer tokens are usually generated by an authorization server,
/// so this information is primarily for documentation purposes.
/// </summary>
public string? BearerFormat { get; init; }

/// <summary>
/// REQUIRED. An object containing configuration information for the flow types supported.
/// </summary>
public RestApiOAuthFlows? Flows { get; init; }

/// <summary>
/// REQUIRED. OpenId Connect URL to discover OAuth2 configuration values.
/// </summary>
public Uri OpenIdConnectUrl { get; init; }

/// <summary>
/// Creates an instance of a <see cref="RestApiSecurityScheme"/> class.
/// </summary>
#pragma warning disable CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider adding the 'required' modifier or declaring as nullable.
internal RestApiSecurityScheme()
#pragma warning restore CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider adding the 'required' modifier or declaring as nullable.
{
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,11 @@ internal sealed class RestApiSpecification
/// </summary>
public RestApiInfo Info { get; private set; }

/// <summary>
/// The REST API security requirements.
/// </summary>
public List<RestApiSecurityRequirement>? SecurityRequirements { get; private set; }

/// <summary>
/// The REST API operations.
/// </summary>
Expand All @@ -23,10 +28,12 @@ internal sealed class RestApiSpecification
/// Construct an instance of <see cref="RestApiSpecification"/>
/// </summary>
/// <param name="info">REST API information.</param>
/// <param name="securityRequirements">REST API security requirements.</param>
/// <param name="operations">REST API operations.</param>
public RestApiSpecification(RestApiInfo info, IList<RestApiOperation> operations)
public RestApiSpecification(RestApiInfo info, List<RestApiSecurityRequirement>? securityRequirements, IList<RestApiOperation> operations)
{
this.Info = info;
this.SecurityRequirements = securityRequirements;
this.Operations = operations;
}
}
Loading

0 comments on commit 7222246

Please sign in to comment.