Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: extend AWS policy #187

Merged
merged 9 commits into from
Aug 15, 2024
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
63 changes: 63 additions & 0 deletions src/LEGO.AsyncAPI.Bindings/Sns/Principal.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
namespace LEGO.AsyncAPI.Bindings.Sns;

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text.Json;
using System.Text.Json.Nodes;
using LEGO.AsyncAPI.Models.Interfaces;
using LEGO.AsyncAPI.Readers.ParseNodes;
using LEGO.AsyncAPI.Writers;

public abstract class Principal : IAsyncApiElement
{
public abstract void Serialize(IAsyncApiWriter writer);

public static Principal Parse(ParseNode node)
{
switch (node)
{
case ValueNode:
var nodeValue = node.GetScalarValue();
if (!IsStarString(nodeValue))
{
throw new ArgumentException($"An error occured while parsing a {nameof(Principal)} node. " +
$"Principal value without a property name can only be a string value of '*'.");
}

return new PrincipalStar();

case MapNode mapNode:
{
var propertyNode = mapNode.First();
if (!IsValidPrincipalProperty(propertyNode.Name))
{
throw new ArgumentException($"An error occured while parsing a {nameof(Principal)} node. " +
$"Node should contain a valid AWS principal property name.");
}

var principalValue = new KeyValuePair<string, StringOrStringList>(
propertyNode.Name,
StringOrStringList.Parse(propertyNode.Value));

return new PrincipalObject(principalValue);
}

default:
throw new ArgumentException($"An error occured while parsing a {nameof(Principal)} node. " +
$"Node should contain a string value of '*' or a valid AWS principal property.");
}
}

private static bool IsStarString(JsonNode value)
{
var element = JsonDocument.Parse(value.ToJsonString()).RootElement;

return element.ValueKind == JsonValueKind.String && element.ValueEquals("*");
}

private static bool IsValidPrincipalProperty(string property)
{
return new[] { "AWS", "Service" }.Contains(property);
}
}
27 changes: 27 additions & 0 deletions src/LEGO.AsyncAPI.Bindings/Sns/PrincipalObject.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
namespace LEGO.AsyncAPI.Bindings.Sns;

using System;
using System.Collections.Generic;
using LEGO.AsyncAPI.Writers;

public class PrincipalObject : Principal
{
private KeyValuePair<string, StringOrStringList> PrincipalValue;

public PrincipalObject(KeyValuePair<string, StringOrStringList> principalValue)
{
this.PrincipalValue = principalValue;
}

public override void Serialize(IAsyncApiWriter writer)
{
if (writer is null)
{
throw new ArgumentNullException(nameof(writer));
}

writer.WriteStartObject();
writer.WriteRequiredObject(this.PrincipalValue.Key, this.PrincipalValue.Value, (w, t) => t.Value.Write(w));
writer.WriteEndObject();
}
}
24 changes: 24 additions & 0 deletions src/LEGO.AsyncAPI.Bindings/Sns/PrincipalStar.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
namespace LEGO.AsyncAPI.Bindings.Sns;

using System;
using LEGO.AsyncAPI.Writers;

public class PrincipalStar : Principal
{
private string PrincipalValue;

public PrincipalStar()
{
this.PrincipalValue = "*";
}

public override void Serialize(IAsyncApiWriter writer)
{
if (writer is null)
{
throw new ArgumentNullException(nameof(writer));
}

writer.WriteValue(this.PrincipalValue);
}
}
4 changes: 3 additions & 1 deletion src/LEGO.AsyncAPI.Bindings/Sns/SnsChannelBinding.cs
Original file line number Diff line number Diff line change
Expand Up @@ -57,8 +57,10 @@ public class SnsChannelBinding : ChannelBinding<SnsChannelBinding>
private static FixedFieldMap<Statement> statementFixedFields = new()
{
{ "effect", (a, n) => { a.Effect = n.GetScalarValue().GetEnumFromDisplayName<Effect>(); } },
{ "principal", (a, n) => { a.Principal = StringOrStringList.Parse(n); } },
{ "principal", (a, n) => { a.Principal = Principal.Parse(n); } },
{ "action", (a, n) => { a.Action = StringOrStringList.Parse(n); } },
{ "resource", (a, n) => { a.Resource = StringOrStringList.Parse(n); } },
{ "condition", (a, n) => { a.Condition = n.CreateAny(); } },
};

/// <inheritdoc/>
Expand Down
26 changes: 20 additions & 6 deletions src/LEGO.AsyncAPI.Bindings/Sns/Statement.cs
Original file line number Diff line number Diff line change
@@ -1,28 +1,40 @@
// Copyright (c) The LEGO Group. All rights reserved.

namespace LEGO.AsyncAPI.Bindings.Sns
{
using System;
using System.Collections.Generic;
using LEGO.AsyncAPI.Attributes;
using LEGO.AsyncAPI.Models;
using LEGO.AsyncAPI.Models.Interfaces;
using LEGO.AsyncAPI.Writers;

public class Statement : IAsyncApiExtensible
{
/// <summary>
/// Indicates whether the policy allows or denies access.
/// </summary>
public Effect Effect { get; set; }

/// <summary>
/// The AWS account or resource ARN that this statement applies to.
/// The AWS account(s) or resource ARN(s) that this statement applies to.
/// </summary>
// public StringOrStringList Principal { get; set; }
public StringOrStringList Principal { get; set; }
public Principal Principal { get; set; }

/// <summary>
/// The SNS permission being allowed or denied e.g. sns:Publish
/// The SNS permission being allowed or denied e.g. sns:Publish.
/// </summary>
public StringOrStringList Action { get; set; }

/// <summary>
/// The resource(s) that this policy applies to.
/// </summary>
public StringOrStringList? Resource { get; set; }

/// <summary>
/// Specific circumstances under which the policy grants permission.
/// </summary>
public AsyncApiAny? Condition { get; set; }

public IDictionary<string, IAsyncApiExtension> Extensions { get; set; } = new Dictionary<string, IAsyncApiExtension>();

public void Serialize(IAsyncApiWriter writer)
Expand All @@ -34,8 +46,10 @@ public void Serialize(IAsyncApiWriter writer)

writer.WriteStartObject();
writer.WriteRequiredProperty("effect", this.Effect.GetDisplayName());
writer.WriteRequiredObject("principal", this.Principal, (w, t) => t.Value.Write(w));
writer.WriteRequiredObject("principal", this.Principal, (w, t) => t.Serialize(w));
writer.WriteRequiredObject("action", this.Action, (w, t) => t.Value.Write(w));
writer.WriteOptionalObject("resource", this.Resource, (w, t) => t?.Value.Write(w));
writer.WriteOptionalObject("condition", this.Condition, (w, t) => t?.Write(w));
writer.WriteExtensions(this.Extensions);
writer.WriteEndObject();
}
Expand Down
65 changes: 65 additions & 0 deletions src/LEGO.AsyncAPI.Bindings/Sqs/Principal.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
// Copyright (c) The LEGO Group. All rights reserved.

namespace LEGO.AsyncAPI.Bindings.Sqs;

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text.Json;
using System.Text.Json.Nodes;
using LEGO.AsyncAPI.Models.Interfaces;
using LEGO.AsyncAPI.Readers.ParseNodes;
using LEGO.AsyncAPI.Writers;

public abstract class Principal : IAsyncApiElement
{
public abstract void Serialize(IAsyncApiWriter writer);

public static Principal Parse(ParseNode node)
{
switch (node)
{
case ValueNode:
var nodeValue = node.GetScalarValue();
if (!IsStarString(nodeValue))
{
throw new ArgumentException($"An error occured while parsing a {nameof(Principal)} node. " +
$"Principal value without a property name can only be a string value of '*'.");
}

return new PrincipalStar();

case MapNode mapNode:
{
var propertyNode = mapNode.First();
if (!IsValidPrincipalProperty(propertyNode.Name))
{
throw new ArgumentException($"An error occured while parsing a {nameof(Principal)} node. " +
$"Node should contain a valid AWS principal property name.");
}

var principalValue = new KeyValuePair<string, StringOrStringList>(
propertyNode.Name,
StringOrStringList.Parse(propertyNode.Value));

return new PrincipalObject(principalValue);
}

default:
throw new ArgumentException($"An error occured while parsing a {nameof(Principal)} node. " +
$"Node should contain a string value of '*' or a valid AWS principal property.");
}
}

private static bool IsStarString(JsonNode value)
{
var element = JsonDocument.Parse(value.ToJsonString()).RootElement;

return element.ValueKind == JsonValueKind.String && element.ValueEquals("*");
}

private static bool IsValidPrincipalProperty(string property)
{
return new[] { "AWS", "Service" }.Contains(property);
}
}
27 changes: 27 additions & 0 deletions src/LEGO.AsyncAPI.Bindings/Sqs/PrincipalObject.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
namespace LEGO.AsyncAPI.Bindings.Sqs;

using System;
using System.Collections.Generic;
using LEGO.AsyncAPI.Writers;

public class PrincipalObject : Principal
{
private KeyValuePair<string, StringOrStringList> PrincipalValue;

public PrincipalObject(KeyValuePair<string, StringOrStringList> principalValue)
{
this.PrincipalValue = principalValue;
}

public override void Serialize(IAsyncApiWriter writer)
{
if (writer is null)
{
throw new ArgumentNullException(nameof(writer));
}

writer.WriteStartObject();
writer.WriteRequiredObject(this.PrincipalValue.Key, this.PrincipalValue.Value, (w, t) => t.Value.Write(w));
writer.WriteEndObject();
}
}
24 changes: 24 additions & 0 deletions src/LEGO.AsyncAPI.Bindings/Sqs/PrincipalStar.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
namespace LEGO.AsyncAPI.Bindings.Sqs;

using System;
using LEGO.AsyncAPI.Writers;

public class PrincipalStar : Principal
{
private string PrincipalValue;

public PrincipalStar()
{
this.PrincipalValue = "*";
}

public override void Serialize(IAsyncApiWriter writer)
{
if (writer is null)
{
throw new ArgumentNullException(nameof(writer));
}

writer.WriteValue(this.PrincipalValue);
}
}
4 changes: 3 additions & 1 deletion src/LEGO.AsyncAPI.Bindings/Sqs/SqsChannelBinding.cs
Original file line number Diff line number Diff line change
Expand Up @@ -64,8 +64,10 @@ public class SqsChannelBinding : ChannelBinding<SqsChannelBinding>
private static FixedFieldMap<Statement> statementFixedFields = new()
{
{ "effect", (a, n) => { a.Effect = n.GetScalarValue().GetEnumFromDisplayName<Effect>(); } },
{ "principal", (a, n) => { a.Principal = StringOrStringList.Parse(n); } },
{ "principal", (a, n) => { a.Principal = Principal.Parse(n); } },
{ "action", (a, n) => { a.Action = StringOrStringList.Parse(n); } },
{ "resource", (a, n) => { a.Resource = StringOrStringList.Parse(n); } },
{ "condition", (a, n) => { a.Condition = n.CreateAny(); } },
};

public override void SerializeProperties(IAsyncApiWriter writer)
Expand Down
4 changes: 3 additions & 1 deletion src/LEGO.AsyncAPI.Bindings/Sqs/SqsOperationBinding.cs
Original file line number Diff line number Diff line change
Expand Up @@ -56,8 +56,10 @@ public class SqsOperationBinding : OperationBinding<SqsOperationBinding>
private static FixedFieldMap<Statement> statementFixedFields = new()
{
{ "effect", (a, n) => { a.Effect = n.GetScalarValue().GetEnumFromDisplayName<Effect>(); } },
{ "principal", (a, n) => { a.Principal = StringOrStringList.Parse(n); } },
{ "principal", (a, n) => { a.Principal = Principal.Parse(n); } },
{ "action", (a, n) => { a.Action = StringOrStringList.Parse(n); } },
{ "resource", (a, n) => { a.Resource = StringOrStringList.Parse(n); } },
{ "condition", (a, n) => { a.Condition = n.CreateAny(); } },
};

public override void SerializeProperties(IAsyncApiWriter writer)
Expand Down
25 changes: 20 additions & 5 deletions src/LEGO.AsyncAPI.Bindings/Sqs/Statement.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,24 +5,37 @@ namespace LEGO.AsyncAPI.Bindings.Sqs
using System;
using System.Collections.Generic;
using LEGO.AsyncAPI.Attributes;
using LEGO.AsyncAPI.Models;
using LEGO.AsyncAPI.Models.Interfaces;
using LEGO.AsyncAPI.Writers;

public class Statement : IAsyncApiExtensible
{
/// <summary>
/// Indicates whether the policy allows or denies access.
/// </summary>
public Effect Effect { get; set; }

/// <summary>
/// The AWS account or resource ARN that this statement applies to.
/// The AWS account(s) or resource ARN(s) that this statement applies to.
/// </summary>
// public StringOrStringList Principal { get; set; }
public StringOrStringList Principal { get; set; }
public Principal Principal { get; set; }

/// <summary>
/// The SNS permission being allowed or denied e.g. sns:Publish
/// The SNS permission being allowed or denied e.g. sns:Publish.
/// </summary>
public StringOrStringList Action { get; set; }

/// <summary>
/// The resource(s) that this policy applies to.
/// </summary>
public StringOrStringList? Resource { get; set; }

/// <summary>
/// Specific circumstances under which the policy grants permission.
/// </summary>
public AsyncApiAny? Condition { get; set; }

public IDictionary<string, IAsyncApiExtension> Extensions { get; set; } = new Dictionary<string, IAsyncApiExtension>();

public void Serialize(IAsyncApiWriter writer)
Expand All @@ -34,8 +47,10 @@ public void Serialize(IAsyncApiWriter writer)

writer.WriteStartObject();
writer.WriteRequiredProperty("effect", this.Effect.GetDisplayName());
writer.WriteRequiredObject("principal", this.Principal, (w, t) => t.Value.Write(w));
writer.WriteRequiredObject("principal", this.Principal, (w, t) => t.Serialize(w));
writer.WriteRequiredObject("action", this.Action, (w, t) => t.Value.Write(w));
writer.WriteOptionalObject("resource", this.Resource, (w, t) => t?.Value.Write(w));
writer.WriteOptionalObject("condition", this.Condition, (w, t) => t?.Write(w));
writer.WriteExtensions(this.Extensions);
writer.WriteEndObject();
}
Expand Down
Loading
Loading