8000 fix error for non-existent policy for AttachRolePolicy operation (#8615) · codeperl/localstack@0ff2710 · GitHub
[go: up one dir, main page]

Skip to content

Commit 0ff2710

Browse files
authored
fix error for non-existent policy for AttachRolePolicy operation (localstack#8615)
1 parent 9fb6be1 commit 0ff2710

File tree

6 files changed

+260
-14
lines changed

6 files changed

+260
-14
lines changed

localstack/services/iam/provider.py

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import json
2+
import re
23
from datetime import datetime
34
from typing import Dict, List, Optional
45
from urllib.parse import quote
@@ -26,6 +27,7 @@
2627
GetServiceLinkedRoleDeletionStatusResponse,
2728
GetUserResponse,
2829
IamApi,
30+
InvalidInputException,
2931
ListInstanceProfileTagsResponse,
3032
ListRolesResponse,
3133
MalformedPolicyDocumentException,
@@ -89,6 +91,8 @@
8991
}
9092
}
9193

94+
POLICY_ARN_REGEX = re.compile(r"arn:[^:]+:iam::(?:\d{12}|aws):policy/.*")
95+
9296

9397
def get_iam_backend(context: RequestContext) -> IAMBackend:
9498
return iam_backends[context.account_id]["global"]
@@ -416,6 +420,20 @@ def get_user(
416420

417421
return response
418422

423+
def attach_role_policy(
424+
self, context: RequestContext, role_name: roleNameType, policy_arn: arnType
425+
) -> None:
426+
if not POLICY_ARN_REGEX.match(policy_arn):
427+
raise InvalidInputException(f"ARN {policy_arn} is not valid.")
428+
return call_moto(context=context)
429+
430+
def attach_user_policy(
431+
self, context: RequestContext, user_name: userNameType, policy_arn: arnType
432+
) -> None:
433+
if not POLICY_ARN_REGEX.match(policy_arn):
434+
raise InvalidInputException(f"ARN {policy_arn} is not valid.")
435+
return call_moto(context=context)
436+
419437
# def get_user(
420438
# self, context: RequestContext, user_name: existingUserNameType = None
421439
# ) -> GetUserResponse:

localstack/testing/snapshots/transformer_utility.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -256,6 +256,8 @@ def iam_api():
256256
TransformerUtility.key_value("UserId"),
257257
TransformerUtility.key_value("RoleId"),
258258
TransformerUtility.key_value("RoleName"),
259+
TransformerUtility.key_value("PolicyName"),
260+
TransformerUtility.key_value("PolicyId"),
259261
]
260262

261263
@staticmethod

tests/integration/cloudformation/resources/test_iam.snapshot.json

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -18,25 +18,25 @@
1818
}
1919
},
2020
"tests/integration/cloudformation/resources/test_iam.py::test_managed_policy_with_empty_resource": {
21-
"recorded-date": "05-09-2022, 10:01:53",
21+
"recorded-date": "11-07-2023, 18:10:41",
2222
"recorded-content": {
2323
"outputs": {
24-
"PolicyArn": "arn:aws:iam::111111111111:policy/<resource:1>",
25-
"StreamARN": "arn:aws:dynamodb:<region>:111111111111:table/<resource:3>/stream/<resource:2>",
26-
"TableARN": "arn:aws:dynamodb:<region>:111111111111:table/<resource:3>",
27-
"TableName": "<resource:3>"
24+
"PolicyArn": "arn:aws:iam::111111111111:policy/<policy-name:1>",
25+
"StreamARN": "arn:aws:dynamodb:<region>:111111111111:table/<resource:2>/stream/<resource:1>",
26+
"TableARN": "arn:aws:dynamodb:<region>:111111111111:table/<resource:2>",
27+
"TableName": "<resource:2>"
2828
},
2929
"managed_policy": {
3030
"Policy": {
31-
"Arn": "arn:aws:iam::111111111111:policy/<resource:1>",
31+
"Arn": "arn:aws:iam::111111111111:policy/<policy-name:1>",
3232
"AttachmentCount": 0,
3333
"CreateDate": "datetime",
3434
"DefaultVersionId": "v1",
3535
"IsAttachable": true,
3636
"Path": "/",
3737
"PermissionsBoundaryUsageCount": 0,
3838
"PolicyId": "<policy-id:1>",
39-
"PolicyName": "<resource:1>",
39+
"PolicyName": "<policy-name:1>",
4040
"Tags": [],
4141
"UpdateDate": "datetime"
4242
},

tests/integration/cloudformation/resources/test_sam.snapshot.json

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,22 @@
11
{
22
"tests/integration/cloudformation/resources/test_sam.py::test_sam_policies": {
3-
"recorded-date": "02-06-2022, 13:13:20",
3+
"recorded-date": "11-07-2023, 18:08:53",
44
"recorded-content": {
55
"list_attached_role_policies": {
66
"AttachedPolicies": [
77
{
8-
"PolicyName": "AWSLambdaBasicExecutionRole",
9-
"PolicyArn": "arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole"
8+
"PolicyArn": "arn:aws:iam::aws:policy/service-role/<policy-name:1>",
9+
"PolicyName": "<policy-name:1>"
1010
},
1111
{
12-
"PolicyName": "AmazonSNSFullAccess",
13-
"PolicyArn": "arn:aws:iam::aws:policy/AmazonSNSFullAccess"
12+
"PolicyArn": "arn:aws:iam::aws:policy/<policy-name:2>",
13+
"PolicyName": "<policy-name:2>"
1414
}
1515
],
1616
"IsTruncated": false,
1717
"ResponseMetadata": {
18-
"HTTPStatusCode": 200,
19-
"HTTPHeaders": {}
18+
"HTTPHeaders": {},
19+
"HTTPStatusCode": 200
2020
}
2121
}
2222
}

tests/integration/test_iam.py

Lines changed: 102 additions & 0 deletions
1C6A
Original file line numberDiff line numberDiff line change
@@ -585,3 +585,105 @@ def test_list_roles_with_permission_boundary(
585585

586586
list_roles_result = aws_client.iam.list_roles(PathPrefix=path_prefix)
587587
snapshot.match("list_roles_result", list_roles_result)
588+
589+
@pytest.mark.aws_validated
590+
@pytest.mark.skip_snapshot_verify(
591+
paths=[
592+
"$..Policy.IsAttachable",
593+
"$..Policy.PermissionsBoundaryUsageCount",
594+
"$..Policy.Tags",
595+
]
596+
)
597+
def test_role_attach_policy(self, snapshot, aws_client, create_role, create_policy):
598+
snapshot.add_transformer(snapshot.transform.iam_api())
599+
600+
trust_policy = {
601+
"Version": "2012-10-17",
602+
"Statement": [
603+
{
604+
"Effect": "Allow",
605+
"Principal": {"Service": "ec2.amazonaws.com"},
606+
"Action": "sts:AssumeRole",
607+
}
608+
],
609+
}
610+
policy_document = {
611+
"Version": "2012-10-17",
612+
"Statement": [
613+
{"Effect": "Allow", "Action": ["lambda:ListFunctions"], "Resource": ["*"]}
614+
],
615+
}
616+
617+
role_name = f"test-role-{short_uid()}"
618+
policy_name = f"test-policy-{short_uid()}"
619+
create_role(RoleName=role_name, AssumeRolePolicyDocument=json.dumps(trust_policy))
620+
create_policy_response = create_policy(
621+
PolicyName=policy_name, PolicyDocument=json.dumps(policy_document)
622+
)
623+
snapshot.match("create_policy_response", create_policy_response)
624+
policy_arn = create_policy_response["Policy"]["Arn"]
625+
626+
with pytest.raises(ClientError) as e:
627+
aws_client.iam.attach_role_policy(
628+
RoleName=role_name, PolicyArn="longpolicynamebutnoarn"
629+
)
630+
snapshot.match("non_existent_malformed_policy_arn", e.value.response)
631+
632+
with pytest.raises(ClientError) as e:
633+
aws_client.iam.attach_role_policy(RoleName=role_name, PolicyArn=policy_name)
634+
snapshot.match("existing_policy_name_provided", e.value.response)
635+
636+
with pytest.raises(ClientError) as e:
637+
aws_client.iam.attach_role_policy(RoleName=role_name, PolicyArn=f"{policy_arn}123")
638+
snapshot.match("valid_arn_not_existent", e.value.response)
639+
640+
attach_policy_response = aws_client.iam.attach_role_policy(
641+
RoleName=role_name, PolicyArn=policy_arn
642+
)
643+
snapshot.match("valid_policy_arn", attach_policy_response)
644+
645+
@pytest.mark.aws_validated
646+
@pytest.mark.skip_snapshot_verify(
647+
paths=[
648+
"$..Policy.IsAttachable",
649+
"$..Policy.PermissionsBoundaryUsageCount",
650+
"$..Policy.Tags",
651+
]
652+
)
653+
def test_user_attach_policy(self, snapshot, aws_client, create_user, create_policy):
654+
snapshot.add_transformer(snapshot.transform.iam_api())
655+
656+
policy_document = {
657+
"Version": "2012-10-17",
658+
"Statement": [
659+
{"Effect": "Allow", "Action": ["lambda:ListFunctions"], "Resource": ["*"]}
660+
],
661+
}
662+
663+
user_name = f"test-role-{short_uid()}"
664+
policy_name = f"test-policy-{short_uid()}"
665+
create_user(UserName=user_name)
666+
create_policy_response = create_policy(
667+
PolicyName=policy_name, PolicyDocument=json.dumps(policy_document)
668+
)
669+
snapshot.match("create_policy_response", create_policy_response)
670+
policy_arn = create_policy_response["Policy"]["Arn"]
671+
672+
with pytest.raises(ClientError) as e:
673+
aws_client.iam.attach_user_policy(
674+
UserName=user_name, PolicyArn="longpolicynamebutnoarn"
675+
)
676+
snapshot.match("non_existent_malformed_policy_arn", e.value.response)
677+
678+
with pytest.raises(ClientError) as e:
679+
aws_client.iam.attach_user_policy(UserName=user_name, PolicyArn=policy_name)
680+
snapshot.match("existing_policy_name_provided", e.value.response)
681+
682+
with pytest.raises(ClientError) as e:
683+
aws_client.iam.attach_user_policy(UserName=user_name, PolicyArn=f"{policy_arn}123")
684+
snapshot.match("valid_arn_not_existent", e.value.response)
685+
686+
attach_policy_response = aws_client.iam.attach_user_policy(
687+
UserName=user_name, PolicyArn=policy_arn
688+
)
689+
snapshot.match("valid_policy_arn", attach_policy_response)

tests/integration/test_iam.snapshot.json

Lines changed: 124 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -199,5 +199,129 @@
199199
}
200200
}
201201
}
202+
},
203+
"tests/integration/test_iam.py::TestIAMIntegrations::test_role_attach_policy": {
204+
"recorded-date": "04-07-2023, 17:04:08",
205+
"recorded-content": {
206+
"create_policy_response": {
207+
"Policy": {
208+
"Arn": "arn:aws:iam::111111111111:policy/<policy-name:1>",
209+
"AttachmentCount": 0,
210+
"CreateDate": "datetime",
211+
"DefaultVersionId": "v1",
212+
"IsAttachable": true,
213+
"Path": "/",
214+
"PermissionsBoundaryUsageCount": 0,
215+
"PolicyId": "<policy-id:1>",
216+
"P 10000 olicyName": "<policy-name:1>",
217+
"UpdateDate": "datetime"
218+
},
219+
"ResponseMetadata": {
220+
"HTTPHeaders": {},
221+
"HTTPStatusCode": 200
222+
}
223+
},
224+
"non_existent_malformed_policy_arn": {
225+
"Error": {
226+
"Code": "InvalidInput",
227+
"Message": "ARN longpolicynamebutnoarn is not valid.",
228+
"Type": "Sender"
229+
},
230+
"ResponseMetadata": {
231+
"HTTPHeaders": {},
232+
"HTTPStatusCode": 400
233+
}
234+
},
235+
"existing_policy_name_provided": {
236+
"Error": {
237+
"Code": "InvalidInput",
238+
"Message": "ARN <policy-name:1> is not valid.",
239+
"Type": "Sender"
240+
},
241+
"ResponseMetadata": {
242+
"HTTPHeaders": {},
243+
"HTTPStatusCode": 400
244+
}
245+
},
246+
"valid_arn_not_existent": {
247+
"Error": {
248+
"Code": "NoSuchEntity",
249+
"Message": "Policy arn:aws:iam::111111111111:policy/<policy-name:1>123 does not exist or is not attachable.",
250+
"Type": "Sender"
251+
},
252+
"ResponseMetadata": {
253+
"HTTPHeaders": {},
254+
"HTTPStatusCode": 404
255+
}
256+
},
257+
"valid_policy_arn": {
258+
"ResponseMetadata": {
259+
"HTTPHeaders": {},
260+
"HTTPStatusCode": 200
261+
}
262+
}
263+
}
264+
},
265+
"tests/integration/test_iam.py::TestIAMIntegrations::test_user_attach_policy": {
266+
"recorded-date": "04-07-2023, 17:16:43",
267+
"recorded-content": {
268+
"create_policy_response": {
269+
"Policy": {
270+
"Arn": "arn:aws:iam::111111111111:policy/<policy-name:1>",
271+
"AttachmentCount": 0,
272+
"CreateDate": "datetime",
273+
"DefaultVersionId": "v1",
274+
"IsAttachable": true,
275+
"Path": "/",
276+
"PermissionsBoundaryUsageCount": 0,
277+
"PolicyId": "<policy-id:1>",
278+
"PolicyName": "<policy-name:1>",
279+
"UpdateDate": "datetime"
280+
},
281+
"ResponseMetadata": {
282+
"HTTPHeaders": {},
283+
"HTTPStatusCode": 200
284+
}
285+
},
286+
"non_existent_malformed_policy_arn": {
287+
"Error": {
288+
"Code": "InvalidInput",
289+
"Message": "ARN longpolicynamebutnoarn is not valid.",
290+
"Type": "Sender"
291+
},
292+
"ResponseMetadata": {
293+
"HTTPHeaders": {},
294+
"HTTPStatusCode": 400
295+
}
296+
},
297+
"existing_policy_name_provided": {
298+
"Error": {
299+
"Code": "InvalidInput",
300+
"Message": "ARN <policy-name:1> is not valid.",
301+
"Type": "Sender"
302+
},
303+
"ResponseMetadata": {
304+
"HTTPHeaders": {},
305+
"HTTPStatusCode": 400
306+
}
307+
},
308+
"valid_arn_not_existent": {
309+
"Error": {
310+
"Code": "NoSuchEntity",
311+
"Message": "Policy arn:aws:iam::111111111111:policy/<policy-name:1>123 does not exist or is not attachable.",
312+
"Type": "Sender"
313+
},
314+
"ResponseMetadata": {
315+
"HTTPHeaders": {},
316+
"HTTPStatusCode": 404
317+
}
318+
},
319+
"valid_policy_arn": {
320+
"ResponseMetadata": {
321+
"HTTPHeaders": {},
322+
"HTTPStatusCode": 200
323+
}
324+
}
325+
}
202326
}
203327
}

0 commit comments

Comments
 (0)
0