Skip to content

Commit

Permalink
feat: Add get-authorization-context policy support in compiler (#106)
Browse files Browse the repository at this point in the history
  • Loading branch information
Mielek authored Feb 27, 2025
1 parent 87dd1c6 commit 6a5796f
Show file tree
Hide file tree
Showing 6 changed files with 279 additions and 2 deletions.
40 changes: 40 additions & 0 deletions src/Authoring/Configs/GetAuthorizationContextConfig.cs
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;
}
31 changes: 31 additions & 0 deletions src/Authoring/Expressions/Authorization.cs
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;
}
}
8 changes: 7 additions & 1 deletion src/Authoring/IInboundContext.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// Copyright (c) Microsoft Corporation.
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

namespace Azure.ApiManagement.PolicyToolkit.Authoring;
Expand Down Expand Up @@ -296,4 +296,10 @@ public interface IInboundContext : IHaveExpressionContext
/// Policy expressions are allowed.
/// </param>
void Wait(Action section, string? waitFor = null);

/// <summary>
/// The get-authorization-context policy retrieves an authorization context from a specified provider.
/// </summary>
/// <param name="config">The configuration for the get-authorization-context policy.</param>
void GetAuthorizationContext(GetAuthorizationContextConfig config);
}
2 changes: 1 addition & 1 deletion src/Core/Compiling/CSharpPolicyCompiler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -68,4 +68,4 @@ private void CompileSection(ICompilationContext context, string section, MethodD
_blockCompiler.Value.Compile(sectionContext, method.Body);
context.AddPolicy(sectionElement);
}
}
}
69 changes: 69 additions & 0 deletions src/Core/Compiling/Policy/GetAuthorizationContextCompiler.cs
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 test/Test.Core/Compiling/GetAuthorizationContextTests.cs
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);
}
}

0 comments on commit 6a5796f

Please sign in to comment.