From ac5b7b3db034923b8376d6ea5758fa2254858d1c Mon Sep 17 00:00:00 2001 From: Daniel Lamando Date: Wed, 12 Jan 2022 18:47:21 +0100 Subject: [PATCH 1/6] Handle deserializing strings for Statement lists It's valid (and expected) for Resource to be a plain string instead of an array of strings. This is shown clearly (but not explicitly said) in the IAM docs: * https://docs.aws.amazon.com/IAM/latest/UserGuide/reference_policies_elements_resource.html * https://docs.aws.amazon.com/IAM/latest/UserGuide/reference_policies_elements_action.html This change reads any bare string as a 1-length array to keep things normalized. --- src/statement/deserialiser.ts | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/statement/deserialiser.ts b/src/statement/deserialiser.ts index 6058bd9..02f0bf5 100644 --- a/src/statement/deserialiser.ts +++ b/src/statement/deserialiser.ts @@ -13,17 +13,20 @@ export class StatementJSONDeserialiser { conditions: ConditionJSONDeserialiser.fromJSON(obj.Condition), }); - function parseArray(obj: any): [] { + function parseArray(obj: any): string[] { if (obj === undefined) { return []; } + if (typeof obj === 'string') { + return [obj]; + } if (Array.isArray(obj)) { if (isArrayOfStrings(obj)) { - return obj as []; + return obj; } throw new Error('Unsupported type: expecting an array of strings'); } - throw new Error('Unsupported type: expecting an array'); + throw new Error('Unsupported type: expecting an array or a string'); } function isArrayOfStrings(obj: any[]) { From 71001913c0c1c7d0762b75362b7f2cfdb42c93a8 Mon Sep 17 00:00:00 2001 From: Thierry de Pauw Date: Sun, 23 Jan 2022 11:37:57 +0100 Subject: [PATCH 2/6] Fix statement JSON deserialisation test when Resource is an object --- tests/statement/deserialiser.spec.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/statement/deserialiser.spec.ts b/tests/statement/deserialiser.spec.ts index 6d8932a..dcd959a 100644 --- a/tests/statement/deserialiser.spec.ts +++ b/tests/statement/deserialiser.spec.ts @@ -60,7 +60,7 @@ describe('#StatementDeserialiser', function() { }; it('should throw an Error', function() { expect(() => StatementJSONDeserialiser.fromJSON(json)).to.throw(Error) - .with.property('message', 'Unsupported type: expecting an array'); + .with.property('message', 'Unsupported type: expecting an array or a string'); }); }); }); From 924d5a53f1453dfecc39bca800648dd39fb7cc74 Mon Sep 17 00:00:00 2001 From: Thierry de Pauw Date: Sun, 23 Jan 2022 11:38:42 +0100 Subject: [PATCH 3/6] Fix statement JSON deserialisation test when Action is an object --- tests/statement/deserialiser.spec.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/statement/deserialiser.spec.ts b/tests/statement/deserialiser.spec.ts index dcd959a..2bb0ba6 100644 --- a/tests/statement/deserialiser.spec.ts +++ b/tests/statement/deserialiser.spec.ts @@ -18,7 +18,7 @@ describe('#StatementDeserialiser', function() { }; it('should throw an Error', function() { expect(() => StatementJSONDeserialiser.fromJSON(json)).to.throw(Error) - .with.property('message', 'Unsupported type: expecting an array'); + .with.property('message', 'Unsupported type: expecting an array or a string'); }); }); From d0cfd4e38686e1c6711c7ebe64cfe41b19851c44 Mon Sep 17 00:00:00 2001 From: Thierry de Pauw Date: Sun, 23 Jan 2022 11:44:17 +0100 Subject: [PATCH 4/6] Modify statement JSON deserialise test when Action is a string --- tests/statement/deserialiser.spec.ts | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/tests/statement/deserialiser.spec.ts b/tests/statement/deserialiser.spec.ts index 2bb0ba6..cde16d2 100644 --- a/tests/statement/deserialiser.spec.ts +++ b/tests/statement/deserialiser.spec.ts @@ -26,9 +26,12 @@ describe('#StatementDeserialiser', function() { const json = { Action: 'action', }; - it('should throw an Error', function() { - expect(() => StatementJSONDeserialiser.fromJSON(json)).to.throw(Error) - .with.property('message', 'Unsupported type: expecting an array'); + it('should return a Statement with actions', function() { + const actual = StatementJSONDeserialiser.fromJSON(json); + const expected = new Statement({ + actions: ['action'], + }); + expect(actual).to.deep.equal(expected); }); }); From c22a39a56d28d9d9ec65342c990131b5126f3186 Mon Sep 17 00:00:00 2001 From: Thierry de Pauw Date: Sun, 23 Jan 2022 11:49:19 +0100 Subject: [PATCH 5/6] Add a statement JSON deserialise test for Resource is a string --- tests/statement/deserialiser.spec.ts | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/tests/statement/deserialiser.spec.ts b/tests/statement/deserialiser.spec.ts index cde16d2..fac3415 100644 --- a/tests/statement/deserialiser.spec.ts +++ b/tests/statement/deserialiser.spec.ts @@ -66,5 +66,18 @@ describe('#StatementDeserialiser', function() { .with.property('message', 'Unsupported type: expecting an array or a string'); }); }); + + describe('and its value is a string', function() { + const json = { + Resource: 'resource', + }; + it('should return a Statement with resources', function() { + const actual = StatementJSONDeserialiser.fromJSON(json); + const expected = new Statement({ + resources: ['resource'], + }); + expect(actual).to.deep.equal(expected); + }); + }); }); }); From fb9152e3597dcc4a1b8bdba3b31bf455f22df67f Mon Sep 17 00:00:00 2001 From: Thierry de Pauw Date: Sun, 23 Jan 2022 12:07:36 +0100 Subject: [PATCH 6/6] Update Limitatinos in the README.md Remove the limitation on `Resource` and `Action` regarding single string value. Though keep the limitation for `Principal` and `Condition`. --- README.md | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 365f042..9c43a9a 100644 --- a/README.md +++ b/README.md @@ -142,8 +142,9 @@ Supports different principals. ## Limitations -The library only supports the canonical form of IAM Policy JSON documents, i.e. -everywhere a string or an array can be passed, an array is expected. +The library now supports a single string as `Action` and `Resource` value, though it does not yet support this for `Principal` and `Condition` key values. + +For `Principal` and `Condition` it still expects the canonical form of an IAM Policy JSON document, i.e. everywhere a string or an array can be passed, an array is expected. ```json { @@ -154,8 +155,8 @@ everywhere a string or an array can be passed, an array is expected. "Principal": { "AWS": ["arn:aws:iam::123456789012:user/user-name"] }, - "Action": ["ec2:Describe*"], - "Resource": ["*"], + "Action": "ec2:Describe*", + "Resource": "*", "Condition": { "StringEquals": { "kms:CallerAccount": ["123456789012"]