-
Notifications
You must be signed in to change notification settings - Fork 12
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: Add get-authorization-context policy support in compiler (#106)
- Loading branch information
Showing
6 changed files
with
279 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,40 @@ | ||
// Copyright (c) Microsoft Corporation. | ||
// Licensed under the MIT License. | ||
|
||
namespace Azure.ApiManagement.PolicyToolkit.Authoring; | ||
|
||
/// <summary> | ||
/// Configuration for the get-authorization-context policy. | ||
/// </summary> | ||
public record GetAuthorizationContextConfig | ||
{ | ||
/// <summary> | ||
/// The credential provider resource identifier. Policy expressions are allowed. | ||
/// </summary> | ||
public required string ProviderId { get; init; } | ||
|
||
/// <summary> | ||
/// The connection resource identifier. Policy expressions are allowed. | ||
/// </summary> | ||
public required string AuthorizationId { get; init; } | ||
|
||
/// <summary> | ||
/// The name of the context variable to receive the Authorization object. Policy expressions are allowed. | ||
/// </summary> | ||
public required string ContextVariableName { get; init; } | ||
|
||
/// <summary> | ||
/// Type of identity to check against the connection's access policy. Default is "managed". Policy expressions are allowed. | ||
/// </summary> | ||
public string? IdentityType { get; init; } = "managed"; | ||
|
||
/// <summary> | ||
/// A Microsoft Entra JWT bearer token to check against the connection permissions. Ignored for identity-type other than jwt. Policy expressions are allowed. | ||
/// </summary> | ||
public string? Identity { get; init; } | ||
|
||
/// <summary> | ||
/// Boolean. If acquiring the authorization context results in an error, the context variable is assigned a value of null if true, otherwise return 500. Default is false. Policy expressions are allowed. | ||
/// </summary> | ||
public bool? IgnoreError { get; init; } = false; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
// Copyright (c) Microsoft Corporation. | ||
// Licensed under the MIT License. | ||
|
||
namespace Azure.ApiManagement.PolicyToolkit.Authoring.Expressions; | ||
|
||
/// <summary> | ||
/// Represents an authorization object with access token and claims. | ||
/// </summary> | ||
public class Authorization | ||
{ | ||
/// <summary> | ||
/// Gets the bearer access token to authorize a backend HTTP request. | ||
/// </summary> | ||
public string AccessToken { get; } | ||
|
||
/// <summary> | ||
/// Gets the claims returned from the authorization server's token response API. | ||
/// </summary> | ||
public IReadOnlyDictionary<string, object> Claims { get; } | ||
|
||
/// <summary> | ||
/// Initializes a new instance of the <see cref="Authorization"/> class. | ||
/// </summary> | ||
/// <param name="accessToken">The bearer access token.</param> | ||
/// <param name="claims">The claims returned from the authorization server's token response API.</param> | ||
public Authorization(string accessToken, IReadOnlyDictionary<string, object> claims) | ||
{ | ||
AccessToken = accessToken; | ||
Claims = claims; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
69 changes: 69 additions & 0 deletions
69
src/Core/Compiling/Policy/GetAuthorizationContextCompiler.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,69 @@ | ||
// Copyright (c) Microsoft Corporation. | ||
// Licensed under the MIT License. | ||
|
||
using System.Xml.Linq; | ||
|
||
using Azure.ApiManagement.PolicyToolkit.Authoring; | ||
using Azure.ApiManagement.PolicyToolkit.Compiling.Diagnostics; | ||
|
||
using Microsoft.CodeAnalysis; | ||
using Microsoft.CodeAnalysis.CSharp.Syntax; | ||
|
||
namespace Azure.ApiManagement.PolicyToolkit.Compiling.Policy; | ||
|
||
public class GetAuthorizationContextCompiler : IMethodPolicyHandler | ||
{ | ||
public string MethodName => nameof(IInboundContext.GetAuthorizationContext); | ||
|
||
public void Handle(ICompilationContext context, InvocationExpressionSyntax node) | ||
{ | ||
if (!node.TryExtractingConfigParameter<GetAuthorizationContextConfig>( | ||
context, | ||
"get-authorization-context", | ||
out var values)) | ||
{ | ||
return; | ||
} | ||
|
||
var element = new XElement("get-authorization-context"); | ||
|
||
if (!element.AddAttribute(values, nameof(GetAuthorizationContextConfig.ProviderId), "provider-id")) | ||
{ | ||
context.Report(Diagnostic.Create( | ||
CompilationErrors.RequiredParameterNotDefined, | ||
node.GetLocation(), | ||
"get-authorization-context", | ||
nameof(GetAuthorizationContextConfig.ProviderId) | ||
)); | ||
return; | ||
} | ||
|
||
if (!element.AddAttribute(values, nameof(GetAuthorizationContextConfig.AuthorizationId), "authorization-id")) | ||
{ | ||
context.Report(Diagnostic.Create( | ||
CompilationErrors.RequiredParameterNotDefined, | ||
node.GetLocation(), | ||
"get-authorization-context", | ||
nameof(GetAuthorizationContextConfig.AuthorizationId) | ||
)); | ||
return; | ||
} | ||
|
||
if (!element.AddAttribute(values, nameof(GetAuthorizationContextConfig.ContextVariableName), "context-variable-name")) | ||
{ | ||
context.Report(Diagnostic.Create( | ||
CompilationErrors.RequiredParameterNotDefined, | ||
node.GetLocation(), | ||
"get-authorization-context", | ||
nameof(GetAuthorizationContextConfig.ContextVariableName) | ||
)); | ||
return; | ||
} | ||
|
||
element.AddAttribute(values, nameof(GetAuthorizationContextConfig.IdentityType), "identity-type"); | ||
element.AddAttribute(values, nameof(GetAuthorizationContextConfig.Identity), "identity"); | ||
element.AddAttribute(values, nameof(GetAuthorizationContextConfig.IgnoreError), "ignore-error"); | ||
|
||
context.AddPolicy(element); | ||
} | ||
} |
131 changes: 131 additions & 0 deletions
131
test/Test.Core/Compiling/GetAuthorizationContextTests.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,131 @@ | ||
// Copyright (c) Microsoft Corporation. | ||
// Licensed under the MIT License. | ||
|
||
namespace Azure.ApiManagement.PolicyToolkit.Compiling; | ||
|
||
[TestClass] | ||
public class GetAuthorizationContextTests | ||
{ | ||
[DataTestMethod] | ||
[DataRow( | ||
""" | ||
[Document] | ||
public class TestDocument : IDocument | ||
{ | ||
public void Inbound(IInboundContext context) | ||
{ | ||
context.GetAuthorizationContext(new GetAuthorizationContextConfig | ||
{ | ||
ProviderId = "provider-id", | ||
AuthorizationId = "authorization-id", | ||
ContextVariableName = "context-variable-name" | ||
}); | ||
} | ||
} | ||
""", | ||
""" | ||
<policies> | ||
<inbound> | ||
<get-authorization-context provider-id="provider-id" authorization-id="authorization-id" context-variable-name="context-variable-name" /> | ||
</inbound> | ||
</policies> | ||
""", | ||
DisplayName = "Should compile get-authorization-context policy with required properties" | ||
)] | ||
[DataRow( | ||
""" | ||
[Document] | ||
public class TestDocument : IDocument | ||
{ | ||
public void Inbound(IInboundContext context) | ||
{ | ||
context.GetAuthorizationContext(new GetAuthorizationContextConfig | ||
{ | ||
ProviderId = "provider-id", | ||
AuthorizationId = "authorization-id", | ||
ContextVariableName = "context-variable-name", | ||
IdentityType = "jwt", | ||
Identity = "jwt-token", | ||
IgnoreError = true | ||
}); | ||
} | ||
} | ||
""", | ||
""" | ||
<policies> | ||
<inbound> | ||
<get-authorization-context provider-id="provider-id" authorization-id="authorization-id" context-variable-name="context-variable-name" identity-type="jwt" identity="jwt-token" ignore-error="true" /> | ||
</inbound> | ||
</policies> | ||
""", | ||
DisplayName = "Should compile get-authorization-context policy with all properties" | ||
)] | ||
[DataRow( | ||
""" | ||
[Document] | ||
public class TestDocument : IDocument | ||
{ | ||
public void Inbound(IInboundContext context) | ||
{ | ||
context.GetAuthorizationContext(new GetAuthorizationContextConfig | ||
{ | ||
ProviderId = GetProviderId(context.ExpressionContext), | ||
AuthorizationId = GetAuthorizationId(context.ExpressionContext), | ||
ContextVariableName = GetContextVariableName(context.ExpressionContext) | ||
}); | ||
} | ||
private string GetProviderId(IExpressionContext context) => context.Variables["provider-id"]; | ||
private string GetAuthorizationId(IExpressionContext context) => context.Variables["authorization-id"]; | ||
private string GetContextVariableName(IExpressionContext context) => context.Variables["context-variable-name"]; | ||
} | ||
""", | ||
""" | ||
<policies> | ||
<inbound> | ||
<get-authorization-context provider-id="@(context.Variables["provider-id"])" authorization-id="@(context.Variables["authorization-id"])" context-variable-name="@(context.Variables["context-variable-name"])" /> | ||
</inbound> | ||
</policies> | ||
""", | ||
DisplayName = "Should compile get-authorization-context policy with policy expressions" | ||
)] | ||
[DataRow( | ||
""" | ||
[Document] | ||
public class TestDocument : IDocument | ||
{ | ||
public void Inbound(IInboundContext context) | ||
{ | ||
context.GetAuthorizationContext(new GetAuthorizationContextConfig | ||
{ | ||
ProviderId = GetProviderId(context.ExpressionContext), | ||
AuthorizationId = GetAuthorizationId(context.ExpressionContext), | ||
ContextVariableName = GetContextVariableName(context.ExpressionContext), | ||
IdentityType = GetIdentityType(context.ExpressionContext), | ||
Identity = GetIdentity(context.ExpressionContext), | ||
IgnoreError = GetIgnoreError(context.ExpressionContext) | ||
}); | ||
} | ||
private string GetProviderId(IExpressionContext context) => context.Variables["provider-id"]; | ||
private string GetAuthorizationId(IExpressionContext context) => context.Variables["authorization-id"]; | ||
private string GetContextVariableName(IExpressionContext context) => context.Variables["context-variable-name"]; | ||
private string GetIdentityType(IExpressionContext context) => context.Variables["identity-type"]; | ||
private string GetIdentity(IExpressionContext context) => context.Variables["identity"]; | ||
private bool GetIgnoreError(IExpressionContext context) => (bool)context.Variables["ignore-error"]; | ||
} | ||
""", | ||
""" | ||
<policies> | ||
<inbound> | ||
<get-authorization-context provider-id="@(context.Variables["provider-id"])" authorization-id="@(context.Variables["authorization-id"])" context-variable-name="@(context.Variables["context-variable-name"])" identity-type="@(context.Variables["identity-type"])" identity="@(context.Variables["identity"])" ignore-error="@((bool)context.Variables["ignore-error"])" /> | ||
</inbound> | ||
</policies> | ||
""", | ||
DisplayName = "Should compile get-authorization-context policy with policy expressions in all properties" | ||
)] | ||
public void ShouldCompileGetAuthorizationContextPolicy(string code, string expectedXml) | ||
{ | ||
code.CompileDocument().Should().BeSuccessful().And.DocumentEquivalentTo(expectedXml); | ||
} | ||
} |