From 629c3b88d30a51a30f9b445c03c98ac92485fbbf Mon Sep 17 00:00:00 2001 From: Ashish Dhingra <67916761+ashishdhingra@users.noreply.github.com> Date: Mon, 22 Jul 2024 15:20:10 -0700 Subject: [PATCH 1/3] Adds support for Condition element in APIGatewayCustomAuthorizerPolicy.IAMPolicyStatement. --- .../APIGatewayCustomAuthorizerPolicy.cs | 9 ++ .../Amazon.Lambda.APIGatewayEvents.csproj | 2 +- .../Amazon.Lambda.Serialization.Json.csproj | 2 +- .../JsonSerializer.cs | 1 + .../test/EventsTests.Shared/EventTests.cs | 149 ++++++++++++++++++ 5 files changed, 161 insertions(+), 2 deletions(-) diff --git a/Libraries/src/Amazon.Lambda.APIGatewayEvents/APIGatewayCustomAuthorizerPolicy.cs b/Libraries/src/Amazon.Lambda.APIGatewayEvents/APIGatewayCustomAuthorizerPolicy.cs index 59e909b69..2e0d796f0 100644 --- a/Libraries/src/Amazon.Lambda.APIGatewayEvents/APIGatewayCustomAuthorizerPolicy.cs +++ b/Libraries/src/Amazon.Lambda.APIGatewayEvents/APIGatewayCustomAuthorizerPolicy.cs @@ -51,6 +51,15 @@ public class IAMPolicyStatement [System.Text.Json.Serialization.JsonPropertyName("Resource")] #endif public HashSet Resource { get; set; } + + /// + /// Gets or sets the conditions for when a policy is in effect. + /// https://docs.aws.amazon.com/IAM/latest/UserGuide/reference_policies_elements_condition.html + /// +#if NETCOREAPP3_1_OR_GREATER + [System.Text.Json.Serialization.JsonPropertyName("Condition")] +#endif + public IDictionary> Condition { get; set; } } } } diff --git a/Libraries/src/Amazon.Lambda.APIGatewayEvents/Amazon.Lambda.APIGatewayEvents.csproj b/Libraries/src/Amazon.Lambda.APIGatewayEvents/Amazon.Lambda.APIGatewayEvents.csproj index ae3910022..75e942afe 100644 --- a/Libraries/src/Amazon.Lambda.APIGatewayEvents/Amazon.Lambda.APIGatewayEvents.csproj +++ b/Libraries/src/Amazon.Lambda.APIGatewayEvents/Amazon.Lambda.APIGatewayEvents.csproj @@ -6,7 +6,7 @@ netstandard2.0;netcoreapp3.1;net8.0 Amazon Lambda .NET Core support - API Gateway package. Amazon.Lambda.APIGatewayEvents - 2.7.0 + 2.7.1 Amazon.Lambda.APIGatewayEvents Amazon.Lambda.APIGatewayEvents AWS;Amazon;Lambda diff --git a/Libraries/src/Amazon.Lambda.Serialization.Json/Amazon.Lambda.Serialization.Json.csproj b/Libraries/src/Amazon.Lambda.Serialization.Json/Amazon.Lambda.Serialization.Json.csproj index 1367d66c2..9fba046a4 100644 --- a/Libraries/src/Amazon.Lambda.Serialization.Json/Amazon.Lambda.Serialization.Json.csproj +++ b/Libraries/src/Amazon.Lambda.Serialization.Json/Amazon.Lambda.Serialization.Json.csproj @@ -9,7 +9,7 @@ Amazon.Lambda.Serialization.Json Amazon.Lambda.Serialization.Json AWS;Amazon;Lambda - 2.2.1 + 2.2.2 diff --git a/Libraries/src/Amazon.Lambda.Serialization.Json/JsonSerializer.cs b/Libraries/src/Amazon.Lambda.Serialization.Json/JsonSerializer.cs index 740eb0a75..4d73f7345 100644 --- a/Libraries/src/Amazon.Lambda.Serialization.Json/JsonSerializer.cs +++ b/Libraries/src/Amazon.Lambda.Serialization.Json/JsonSerializer.cs @@ -52,6 +52,7 @@ public JsonSerializer(Action customizeSerializerSettings resolver.NamingStrategy = namingStrategy; }; settings.ContractResolver = resolver; + settings.NullValueHandling = NullValueHandling.Ignore; serializer = Newtonsoft.Json.JsonSerializer.Create(settings); diff --git a/Libraries/test/EventsTests.Shared/EventTests.cs b/Libraries/test/EventsTests.Shared/EventTests.cs index d136854cb..e5604b741 100644 --- a/Libraries/test/EventsTests.Shared/EventTests.cs +++ b/Libraries/test/EventsTests.Shared/EventTests.cs @@ -26,6 +26,7 @@ namespace Amazon.Lambda.Tests using Amazon.Lambda.SimpleEmailEvents; using Amazon.Lambda.SNSEvents; using Amazon.Lambda.SQSEvents; + using Amazon.Runtime.Internal.Transform; using Newtonsoft.Json; using Newtonsoft.Json.Linq; using Newtonsoft.Json.Serialization; @@ -2007,6 +2008,154 @@ public void APIGatewayAuthorizerResponseTest(Type serializerType) Assert.Equal("execute-api:Invoke", root["policyDocument"]["Statement"][0]["Action"][0]); Assert.Equal("Allow", root["policyDocument"]["Statement"][0]["Effect"]); Assert.Equal("*", root["policyDocument"]["Statement"][0]["Resource"][0]); + Assert.Null(root["policyDocument"]["Statement"][0]["Condition"]); + } + + [Theory] + [InlineData(typeof(JsonSerializer))] +#if NETCOREAPP3_1_OR_GREATER + [InlineData(typeof(Amazon.Lambda.Serialization.SystemTextJson.LambdaJsonSerializer))] + [InlineData(typeof(Amazon.Lambda.Serialization.SystemTextJson.DefaultLambdaJsonSerializer))] +#endif + public void APIGatewayAuthorizerWithSimpleIAMConditionResponseTest(Type serializerType) + { + var serializer = Activator.CreateInstance(serializerType) as ILambdaSerializer; + var context = new APIGatewayCustomAuthorizerContextOutput(); + context["field1"] = "value1"; + context["field2"] = "value2"; + + var response = new APIGatewayCustomAuthorizerResponse + { + PrincipalID = "prin1", + UsageIdentifierKey = "usageKey", + Context = context, + PolicyDocument = new APIGatewayCustomAuthorizerPolicy + { + Version = "2012-10-17", + Statement = new List + { + new APIGatewayCustomAuthorizerPolicy.IAMPolicyStatement + { + Action = new HashSet{ "execute-api:Invoke" }, + Effect = "Allow", + Resource = new HashSet{ "*" }, + Condition = new Dictionary>() + { + { "StringEquals", new Dictionary() + { + { "aws:PrincipalTag/job-category", "iamuser-admin" } + } + } + } + } + } + } + }; + + string serializedJson; + using (MemoryStream stream = new MemoryStream()) + { + serializer.Serialize(response, stream); + + stream.Position = 0; + serializedJson = Encoding.UTF8.GetString(stream.ToArray()); + } + + JObject root = Newtonsoft.Json.JsonConvert.DeserializeObject(serializedJson) as JObject; + + Assert.Equal("prin1", root["principalId"]); + Assert.Equal("usageKey", root["usageIdentifierKey"]); + Assert.Equal("value1", root["context"]["field1"]); + Assert.Equal("value2", root["context"]["field2"]); + + Assert.Equal("2012-10-17", root["policyDocument"]["Version"]); + Assert.Equal("execute-api:Invoke", root["policyDocument"]["Statement"][0]["Action"][0]); + Assert.Equal("Allow", root["policyDocument"]["Statement"][0]["Effect"]); + Assert.Equal("*", root["policyDocument"]["Statement"][0]["Resource"][0]); + Assert.Equal("iamuser-admin", root["policyDocument"]["Statement"][0]["Condition"]["StringEquals"]["aws:PrincipalTag/job-category"].ToString()); + } + + [Theory] + [InlineData(typeof(JsonSerializer))] +#if NETCOREAPP3_1_OR_GREATER + [InlineData(typeof(Amazon.Lambda.Serialization.SystemTextJson.LambdaJsonSerializer))] + [InlineData(typeof(Amazon.Lambda.Serialization.SystemTextJson.DefaultLambdaJsonSerializer))] +#endif + public void APIGatewayAuthorizerWithMultiValueIAMConditionResponseTest(Type serializerType) + { + var serializer = Activator.CreateInstance(serializerType) as ILambdaSerializer; + var context = new APIGatewayCustomAuthorizerContextOutput(); + context["field1"] = "value1"; + context["field2"] = "value2"; + + var response = new APIGatewayCustomAuthorizerResponse + { + PrincipalID = "prin1", + UsageIdentifierKey = "usageKey", + Context = context, + PolicyDocument = new APIGatewayCustomAuthorizerPolicy + { + Version = "2012-10-17", + Statement = new List + { + new APIGatewayCustomAuthorizerPolicy.IAMPolicyStatement + { + Action = new HashSet{ "execute-api:Invoke" }, + Effect = "Allow", + Resource = new HashSet{ "*" }, + Condition = new Dictionary>() + { + { + "StringEquals", + new Dictionary() + { + { "aws:PrincipalTag/department", new List{ "finance", "hr", "legal" } }, + { "aws:PrincipalTag/role", new List{ "audit", "security" } } + } + }, + { + "ArnLike", + new Dictionary() + { + { "aws:PrincipalArn", new List{ "arn:aws:iam::XXXXXXXXXXXX:user/User1", "arn:aws:iam::XXXXXXXXXXXX:user/User2" } } + } + } + } + } + } + } + }; + + string serializedJson; + using (MemoryStream stream = new MemoryStream()) + { + serializer.Serialize(response, stream); + + stream.Position = 0; + serializedJson = Encoding.UTF8.GetString(stream.ToArray()); + } + + JObject root = Newtonsoft.Json.JsonConvert.DeserializeObject(serializedJson) as JObject; + + Assert.Equal("prin1", root["principalId"]); + Assert.Equal("usageKey", root["usageIdentifierKey"]); + Assert.Equal("value1", root["context"]["field1"]); + Assert.Equal("value2", root["context"]["field2"]); + + Assert.Equal("2012-10-17", root["policyDocument"]["Version"]); + Assert.Equal("execute-api:Invoke", root["policyDocument"]["Statement"][0]["Action"][0]); + Assert.Equal("Allow", root["policyDocument"]["Statement"][0]["Effect"]); + Assert.Equal("*", root["policyDocument"]["Statement"][0]["Resource"][0]); + Assert.Equal(3, root["policyDocument"]["Statement"][0]["Condition"]["StringEquals"]["aws:PrincipalTag/department"].Values().ToList().Count); + Assert.Equal("finance", root["policyDocument"]["Statement"][0]["Condition"]["StringEquals"]["aws:PrincipalTag/department"][0]); + Assert.Equal("hr", root["policyDocument"]["Statement"][0]["Condition"]["StringEquals"]["aws:PrincipalTag/department"][1]); + Assert.Equal("legal", root["policyDocument"]["Statement"][0]["Condition"]["StringEquals"]["aws:PrincipalTag/department"][2]); + Assert.Equal(2, root["policyDocument"]["Statement"][0]["Condition"]["StringEquals"]["aws:PrincipalTag/role"].Values().ToList().Count); + Assert.Equal("audit", root["policyDocument"]["Statement"][0]["Condition"]["StringEquals"]["aws:PrincipalTag/role"][0]); + Assert.Equal("security", root["policyDocument"]["Statement"][0]["Condition"]["StringEquals"]["aws:PrincipalTag/role"][1]); + Assert.Equal(2, root["policyDocument"]["Statement"][0]["Condition"]["ArnLike"]["aws:PrincipalArn"].Values().ToList().Count); + Assert.Equal("arn:aws:iam::XXXXXXXXXXXX:user/User1", root["policyDocument"]["Statement"][0]["Condition"]["ArnLike"]["aws:PrincipalArn"][0]); + Assert.Equal("arn:aws:iam::XXXXXXXXXXXX:user/User2", root["policyDocument"]["Statement"][0]["Condition"]["ArnLike"]["aws:PrincipalArn"][1]); } [Theory] From 5f8c0bf33f327737ce525bf70fe453b27fb50131 Mon Sep 17 00:00:00 2001 From: Ashish Dhingra <67916761+ashishdhingra@users.noreply.github.com> Date: Tue, 23 Jul 2024 10:21:07 -0700 Subject: [PATCH 2/3] Fix test case to include Condition element in expected JSON. --- .../Amazon.Lambda.AspNetCoreServer.Test/TestCallingWebAPI.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Libraries/test/Amazon.Lambda.AspNetCoreServer.Test/TestCallingWebAPI.cs b/Libraries/test/Amazon.Lambda.AspNetCoreServer.Test/TestCallingWebAPI.cs index aa21efbd1..658bd7f9b 100644 --- a/Libraries/test/Amazon.Lambda.AspNetCoreServer.Test/TestCallingWebAPI.cs +++ b/Libraries/test/Amazon.Lambda.AspNetCoreServer.Test/TestCallingWebAPI.cs @@ -215,7 +215,7 @@ public void TestCustomAuthorizerSerialization() Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping }); Assert.NotNull(json); - var expected = "{\"principalId\":\"com.amazon.someuser\",\"policyDocument\":{\"Version\":\"2012-10-17\",\"Statement\":[{\"Effect\":\"Allow\",\"Action\":[\"execute-api:Invoke\"],\"Resource\":[\"arn:aws:execute-api:us-west-2:1234567890:apit123d45/Prod/GET/*\"]}]},\"context\":{\"stringKey\":\"Hey I'm a string\",\"boolKey\":true,\"numKey\":9},\"usageIdentifierKey\":null}"; + var expected = "{\"principalId\":\"com.amazon.someuser\",\"policyDocument\":{\"Version\":\"2012-10-17\",\"Statement\":[{\"Effect\":\"Allow\",\"Action\":[\"execute-api:Invoke\"],\"Resource\":[\"arn:aws:execute-api:us-west-2:1234567890:apit123d45/Prod/GET/*\"],\"Condition\":null}]},\"context\":{\"stringKey\":\"Hey I'm a string\",\"boolKey\":true,\"numKey\":9},\"usageIdentifierKey\":null}"; Assert.Equal(expected, json); } From c5fde319e332733abfce6ea31aed30465e5bb111 Mon Sep 17 00:00:00 2001 From: Ashish Dhingra <67916761+ashishdhingra@users.noreply.github.com> Date: Tue, 23 Jul 2024 11:16:40 -0700 Subject: [PATCH 3/3] Fixed DeploymentScript.ps1 for Serverless App integration test to create deployment bucket in correct region. --- .../DeploymentScript.ps1 | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/Libraries/test/TestServerlessApp.IntegrationTests/DeploymentScript.ps1 b/Libraries/test/TestServerlessApp.IntegrationTests/DeploymentScript.ps1 index d6b8e3d84..38a358d22 100644 --- a/Libraries/test/TestServerlessApp.IntegrationTests/DeploymentScript.ps1 +++ b/Libraries/test/TestServerlessApp.IntegrationTests/DeploymentScript.ps1 @@ -38,9 +38,22 @@ try $content = Get-Content .\aws-lambda-tools-defaults.json $content | ForEach-Object {$_ -replace $line, "`"function-architecture`" : `"$arch`""} | Set-Content .\aws-lambda-tools-defaults.json + # Extract region + $json = Get-Content .\aws-lambda-tools-defaults.json | Out-String | ConvertFrom-Json + $region = $json.region + dotnet tool install -g Amazon.Lambda.Tools Write-Host "Creating S3 Bucket $identifier" - aws s3 mb s3://$identifier + + if(![string]::IsNullOrEmpty($region)) + { + aws s3 mb s3://$identifier --region $region + } + else + { + aws s3 mb s3://$identifier + } + if (!$?) { throw "Failed to create the following bucket: $identifier"