4788 lines
165 KiB
Python
4788 lines
165 KiB
Python
import csv
|
|
import json
|
|
from datetime import datetime
|
|
from urllib import parse
|
|
from uuid import uuid4
|
|
|
|
import boto3
|
|
import pytest
|
|
from botocore.exceptions import ClientError
|
|
|
|
from moto import mock_config, mock_iam, settings
|
|
from moto.backends import get_backend
|
|
from moto.core import DEFAULT_ACCOUNT_ID as ACCOUNT_ID
|
|
from moto.core.utils import utcnow
|
|
from moto.iam import iam_backends
|
|
from moto.s3.responses import DEFAULT_REGION_NAME
|
|
from tests import DEFAULT_ACCOUNT_ID
|
|
|
|
MOCK_CERT = """-----BEGIN CERTIFICATE-----
|
|
MIIBpzCCARACCQCY5yOdxCTrGjANBgkqhkiG9w0BAQsFADAXMRUwEwYDVQQKDAxt
|
|
b3RvIHRlc3RpbmcwIBcNMTgxMTA1MTkwNTIwWhgPMjI5MjA4MTkxOTA1MjBaMBcx
|
|
FTATBgNVBAoMDG1vdG8gdGVzdGluZzCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkC
|
|
gYEA1Jn3g2h7LD3FLqdpcYNbFXCS4V4eDpuTCje9vKFcC3pi/01147X3zdfPy8Mt
|
|
ZhKxcREOwm4NXykh23P9KW7fBovpNwnbYsbPqj8Hf1ZaClrgku1arTVhEnKjx8zO
|
|
vaR/bVLCss4uE0E0VM1tJn/QGQsfthFsjuHtwx8uIWz35tUCAwEAATANBgkqhkiG
|
|
9w0BAQsFAAOBgQBWdOQ7bDc2nWkUhFjZoNIZrqjyNdjlMUndpwREVD7FQ/DuxJMj
|
|
FyDHrtlrS80dPUQWNYHw++oACDpWO01LGLPPrGmuO/7cOdojPEd852q5gd+7W9xt
|
|
8vUH+pBa6IBLbvBp+szli51V3TLSWcoyy4ceJNQU2vCkTLoFdS0RLd/7tQ==
|
|
-----END CERTIFICATE-----"""
|
|
|
|
MOCK_POLICY = """
|
|
{
|
|
"Version": "2012-10-17",
|
|
"Statement":
|
|
{
|
|
"Effect": "Allow",
|
|
"Action": "s3:ListBucket",
|
|
"Resource": "arn:aws:s3:::example_bucket"
|
|
}
|
|
}
|
|
"""
|
|
|
|
MOCK_POLICY_2 = """
|
|
{
|
|
"Version": "2012-10-17",
|
|
"Id": "2",
|
|
"Statement":
|
|
{
|
|
"Effect": "Allow",
|
|
"Action": "s3:ListBucket",
|
|
"Resource": "arn:aws:s3:::example_bucket"
|
|
}
|
|
}
|
|
"""
|
|
|
|
MOCK_POLICY_3 = """
|
|
{
|
|
"Version": "2012-10-17",
|
|
"Id": "3",
|
|
"Statement":
|
|
{
|
|
"Effect": "Allow",
|
|
"Action": "s3:ListBucket",
|
|
"Resource": "arn:aws:s3:::example_bucket"
|
|
}
|
|
}
|
|
"""
|
|
|
|
MOCK_STS_EC2_POLICY_DOCUMENT = """{
|
|
"Version": "2012-10-17",
|
|
"Statement": [
|
|
{
|
|
"Effect": "Allow",
|
|
"Principal": {
|
|
"Service": [
|
|
"ec2.amazonaws.com"
|
|
]
|
|
},
|
|
"Action": [
|
|
"sts:AssumeRole"
|
|
]
|
|
}
|
|
]
|
|
}"""
|
|
|
|
|
|
@mock_iam
|
|
def test_get_role__should_throw__when_role_does_not_exist():
|
|
conn = boto3.client("iam", region_name="us-east-1")
|
|
with pytest.raises(ClientError) as ex:
|
|
conn.get_role(RoleName="unexisting_role")
|
|
err = ex.value.response["Error"]
|
|
assert err["Code"] == "NoSuchEntity"
|
|
assert "not found" in err["Message"]
|
|
|
|
|
|
@mock_iam
|
|
def test_get_role__should_contain_last_used():
|
|
conn = boto3.client("iam", region_name="us-east-1")
|
|
conn.create_role(
|
|
RoleName="my-role", AssumeRolePolicyDocument="some policy", Path="/"
|
|
)
|
|
role = conn.get_role(RoleName="my-role")["Role"]
|
|
assert role["RoleLastUsed"] == {}
|
|
|
|
if not settings.TEST_SERVER_MODE:
|
|
iam_backend = get_backend("iam")[ACCOUNT_ID]["global"]
|
|
last_used = datetime.strptime(
|
|
"2022-07-18T10:30:00+00:00", "%Y-%m-%dT%H:%M:%S+00:00"
|
|
)
|
|
region = "us-west-1"
|
|
iam_backend.roles[role["RoleId"]].last_used = last_used
|
|
iam_backend.roles[role["RoleId"]].last_used_region = region
|
|
roleLastUsed = conn.get_role(RoleName="my-role")["Role"]["RoleLastUsed"]
|
|
assert roleLastUsed["LastUsedDate"].replace(tzinfo=None) == last_used
|
|
assert roleLastUsed["Region"] == region
|
|
|
|
|
|
@mock_iam
|
|
def test_get_instance_profile__should_throw__when_instance_profile_does_not_exist():
|
|
conn = boto3.client("iam", region_name="us-east-1")
|
|
with pytest.raises(ClientError) as ex:
|
|
conn.get_instance_profile(InstanceProfileName="unexisting_instance_profile")
|
|
err = ex.value.response["Error"]
|
|
assert err["Code"] == "NoSuchEntity"
|
|
assert "not found" in err["Message"]
|
|
|
|
|
|
@mock_iam
|
|
def test_create_role_and_instance_profile():
|
|
conn = boto3.client("iam", region_name="us-east-1")
|
|
conn.create_instance_profile(InstanceProfileName="my-profile", Path="my-path")
|
|
conn.create_role(
|
|
RoleName="my-role", AssumeRolePolicyDocument="some policy", Path="/my-path/"
|
|
)
|
|
|
|
conn.add_role_to_instance_profile(
|
|
InstanceProfileName="my-profile", RoleName="my-role"
|
|
)
|
|
|
|
role = conn.get_role(RoleName="my-role")["Role"]
|
|
assert role["Path"] == "/my-path/"
|
|
assert role["AssumeRolePolicyDocument"] == "some policy"
|
|
|
|
profile = conn.get_instance_profile(InstanceProfileName="my-profile")[
|
|
"InstanceProfile"
|
|
]
|
|
assert profile["Path"] == "my-path"
|
|
|
|
assert len(profile["Roles"]) == 1
|
|
role_from_profile = profile["Roles"][0]
|
|
assert role_from_profile["RoleId"] == role["RoleId"]
|
|
assert role_from_profile["RoleName"] == "my-role"
|
|
|
|
assert conn.list_roles()["Roles"][0]["RoleName"] == "my-role"
|
|
|
|
# Test with an empty path:
|
|
profile = conn.create_instance_profile(InstanceProfileName="my-other-profile")
|
|
assert profile["InstanceProfile"]["Path"] == "/"
|
|
|
|
|
|
@mock_iam
|
|
def test_create_instance_profile_should_throw_when_name_is_not_unique():
|
|
conn = boto3.client("iam", region_name="us-east-1")
|
|
conn.create_instance_profile(InstanceProfileName="unique-instance-profile")
|
|
with pytest.raises(ClientError):
|
|
conn.create_instance_profile(InstanceProfileName="unique-instance-profile")
|
|
|
|
|
|
@mock_iam
|
|
def test_create_add_additional_roles_to_instance_profile_error():
|
|
|
|
# Setup
|
|
iam = boto3.client("iam", region_name="us-east-1")
|
|
name = "test_profile"
|
|
role_name = "test_role"
|
|
role_name2 = "test_role2"
|
|
iam.create_instance_profile(InstanceProfileName=name)
|
|
iam.create_role(
|
|
RoleName=role_name, AssumeRolePolicyDocument=MOCK_STS_EC2_POLICY_DOCUMENT
|
|
)
|
|
iam.create_role(
|
|
RoleName=role_name2, AssumeRolePolicyDocument=MOCK_STS_EC2_POLICY_DOCUMENT
|
|
)
|
|
iam.add_role_to_instance_profile(InstanceProfileName=name, RoleName=role_name)
|
|
|
|
# Execute
|
|
with pytest.raises(ClientError) as exc:
|
|
iam.add_role_to_instance_profile(InstanceProfileName=name, RoleName=role_name2)
|
|
|
|
# Verify
|
|
err = exc.value.response["Error"]
|
|
assert err["Code"] == "LimitExceeded"
|
|
assert (
|
|
err["Message"]
|
|
== "Cannot exceed quota for InstanceSessionsPerInstanceProfile: 1"
|
|
)
|
|
|
|
|
|
@mock_iam
|
|
def test_remove_role_from_instance_profile():
|
|
conn = boto3.client("iam", region_name="us-east-1")
|
|
conn.create_instance_profile(InstanceProfileName="my-profile", Path="my-path")
|
|
conn.create_role(
|
|
RoleName="my-role", AssumeRolePolicyDocument="some policy", Path="/my-path/"
|
|
)
|
|
conn.add_role_to_instance_profile(
|
|
InstanceProfileName="my-profile", RoleName="my-role"
|
|
)
|
|
|
|
profile = conn.get_instance_profile(InstanceProfileName="my-profile")[
|
|
"InstanceProfile"
|
|
]
|
|
assert len(profile["Roles"]) == 1
|
|
|
|
conn.remove_role_from_instance_profile(
|
|
InstanceProfileName="my-profile", RoleName="my-role"
|
|
)
|
|
|
|
profile = conn.get_instance_profile(InstanceProfileName="my-profile")[
|
|
"InstanceProfile"
|
|
]
|
|
assert len(profile["Roles"]) == 0
|
|
|
|
|
|
@mock_iam()
|
|
def test_delete_instance_profile():
|
|
conn = boto3.client("iam", region_name="us-east-1")
|
|
conn.create_role(
|
|
RoleName="my-role", AssumeRolePolicyDocument="some policy", Path="/my-path/"
|
|
)
|
|
conn.create_instance_profile(InstanceProfileName="my-profile")
|
|
conn.add_role_to_instance_profile(
|
|
InstanceProfileName="my-profile", RoleName="my-role"
|
|
)
|
|
with pytest.raises(conn.exceptions.DeleteConflictException):
|
|
conn.delete_instance_profile(InstanceProfileName="my-profile")
|
|
conn.remove_role_from_instance_profile(
|
|
InstanceProfileName="my-profile", RoleName="my-role"
|
|
)
|
|
conn.delete_instance_profile(InstanceProfileName="my-profile")
|
|
with pytest.raises(conn.exceptions.NoSuchEntityException):
|
|
conn.get_instance_profile(InstanceProfileName="my-profile")
|
|
|
|
|
|
@mock_iam()
|
|
def test_get_login_profile():
|
|
conn = boto3.client("iam", region_name="us-east-1")
|
|
conn.create_user(UserName="my-user")
|
|
conn.create_login_profile(UserName="my-user", Password="my-pass")
|
|
|
|
response = conn.get_login_profile(UserName="my-user")
|
|
assert response["LoginProfile"]["UserName"] == "my-user"
|
|
|
|
|
|
@mock_iam()
|
|
def test_update_login_profile():
|
|
conn = boto3.client("iam", region_name="us-east-1")
|
|
conn.create_user(UserName="my-user")
|
|
conn.create_login_profile(UserName="my-user", Password="my-pass")
|
|
response = conn.get_login_profile(UserName="my-user")
|
|
assert response["LoginProfile"].get("PasswordResetRequired") is None
|
|
|
|
conn.update_login_profile(
|
|
UserName="my-user", Password="new-pass", PasswordResetRequired=True
|
|
)
|
|
response = conn.get_login_profile(UserName="my-user")
|
|
assert response["LoginProfile"].get("PasswordResetRequired") is True
|
|
|
|
|
|
@mock_iam()
|
|
def test_delete_role():
|
|
conn = boto3.client("iam", region_name="us-east-1")
|
|
|
|
with pytest.raises(conn.exceptions.NoSuchEntityException):
|
|
conn.delete_role(RoleName="my-role")
|
|
|
|
# Test deletion failure with a managed policy
|
|
conn.create_role(
|
|
RoleName="my-role", AssumeRolePolicyDocument="some policy", Path="/my-path/"
|
|
)
|
|
response = conn.create_policy(
|
|
PolicyName="my-managed-policy", PolicyDocument=MOCK_POLICY
|
|
)
|
|
conn.attach_role_policy(PolicyArn=response["Policy"]["Arn"], RoleName="my-role")
|
|
with pytest.raises(conn.exceptions.DeleteConflictException):
|
|
conn.delete_role(RoleName="my-role")
|
|
conn.detach_role_policy(PolicyArn=response["Policy"]["Arn"], RoleName="my-role")
|
|
conn.delete_policy(PolicyArn=response["Policy"]["Arn"])
|
|
conn.delete_role(RoleName="my-role")
|
|
with pytest.raises(conn.exceptions.NoSuchEntityException):
|
|
conn.get_role(RoleName="my-role")
|
|
|
|
# Test deletion failure with an inline policy
|
|
conn.create_role(
|
|
RoleName="my-role", AssumeRolePolicyDocument="some policy", Path="/my-path/"
|
|
)
|
|
conn.put_role_policy(
|
|
RoleName="my-role", PolicyName="my-role-policy", PolicyDocument=MOCK_POLICY
|
|
)
|
|
with pytest.raises(conn.exceptions.DeleteConflictException):
|
|
conn.delete_role(RoleName="my-role")
|
|
conn.delete_role_policy(RoleName="my-role", PolicyName="my-role-policy")
|
|
conn.delete_role(RoleName="my-role")
|
|
with pytest.raises(conn.exceptions.NoSuchEntityException):
|
|
conn.get_role(RoleName="my-role")
|
|
|
|
# Test deletion failure with attachment to an instance profile
|
|
conn.create_role(
|
|
RoleName="my-role", AssumeRolePolicyDocument="some policy", Path="/my-path/"
|
|
)
|
|
conn.create_instance_profile(InstanceProfileName="my-profile")
|
|
conn.add_role_to_instance_profile(
|
|
InstanceProfileName="my-profile", RoleName="my-role"
|
|
)
|
|
with pytest.raises(conn.exceptions.DeleteConflictException):
|
|
conn.delete_role(RoleName="my-role")
|
|
conn.remove_role_from_instance_profile(
|
|
InstanceProfileName="my-profile", RoleName="my-role"
|
|
)
|
|
conn.delete_role(RoleName="my-role")
|
|
with pytest.raises(conn.exceptions.NoSuchEntityException):
|
|
conn.get_role(RoleName="my-role")
|
|
|
|
# Test deletion with no conflicts
|
|
conn.create_role(
|
|
RoleName="my-role", AssumeRolePolicyDocument="some policy", Path="/my-path/"
|
|
)
|
|
conn.delete_role(RoleName="my-role")
|
|
with pytest.raises(conn.exceptions.NoSuchEntityException):
|
|
conn.get_role(RoleName="my-role")
|
|
|
|
|
|
@mock_iam
|
|
def test_list_instance_profiles():
|
|
conn = boto3.client("iam", region_name="us-east-1")
|
|
conn.create_instance_profile(InstanceProfileName="my-profile", Path="my-path")
|
|
conn.create_role(
|
|
RoleName="my-role", AssumeRolePolicyDocument="some policy", Path="/my-path/"
|
|
)
|
|
|
|
conn.add_role_to_instance_profile(
|
|
InstanceProfileName="my-profile", RoleName="my-role"
|
|
)
|
|
|
|
profiles = conn.list_instance_profiles()["InstanceProfiles"]
|
|
|
|
assert len(profiles) == 1
|
|
assert profiles[0]["InstanceProfileName"] == "my-profile"
|
|
assert profiles[0]["Roles"][0]["RoleName"] == "my-role"
|
|
|
|
|
|
@mock_iam
|
|
def test_list_instance_profiles_for_role():
|
|
conn = boto3.client("iam", region_name="us-east-1")
|
|
|
|
conn.create_role(
|
|
RoleName="my-role", AssumeRolePolicyDocument="some policy", Path="my-path"
|
|
)
|
|
conn.create_role(
|
|
RoleName="my-role2", AssumeRolePolicyDocument="some policy2", Path="my-path2"
|
|
)
|
|
|
|
profile_name_list = ["my-profile", "my-profile2"]
|
|
profile_path_list = ["my-path", "my-path2"]
|
|
for profile_count in range(0, 2):
|
|
conn.create_instance_profile(
|
|
InstanceProfileName=profile_name_list[profile_count],
|
|
Path=profile_path_list[profile_count],
|
|
)
|
|
|
|
for profile_count in range(0, 2):
|
|
conn.add_role_to_instance_profile(
|
|
InstanceProfileName=profile_name_list[profile_count], RoleName="my-role"
|
|
)
|
|
|
|
profile_dump = conn.list_instance_profiles_for_role(RoleName="my-role")
|
|
profile_list = profile_dump["InstanceProfiles"]
|
|
for profile_count in range(0, len(profile_list)):
|
|
profile_name_list.remove(profile_list[profile_count]["InstanceProfileName"])
|
|
profile_path_list.remove(profile_list[profile_count]["Path"])
|
|
assert profile_list[profile_count]["Roles"][0]["RoleName"] == "my-role"
|
|
|
|
assert len(profile_name_list) == 0
|
|
assert len(profile_path_list) == 0
|
|
|
|
profile_dump2 = conn.list_instance_profiles_for_role(RoleName="my-role2")
|
|
profile_list = profile_dump2["InstanceProfiles"]
|
|
assert len(profile_list) == 0
|
|
|
|
|
|
@mock_iam
|
|
def test_list_role_policies():
|
|
conn = boto3.client("iam", region_name="us-east-1")
|
|
|
|
conn.create_role(
|
|
RoleName="my-role", AssumeRolePolicyDocument="some policy", Path="my-path"
|
|
)
|
|
conn.put_role_policy(
|
|
RoleName="my-role", PolicyName="test policy", PolicyDocument=MOCK_POLICY
|
|
)
|
|
role = conn.list_role_policies(RoleName="my-role")
|
|
assert role["PolicyNames"] == ["test policy"]
|
|
|
|
conn.put_role_policy(
|
|
RoleName="my-role", PolicyName="test policy 2", PolicyDocument=MOCK_POLICY
|
|
)
|
|
role = conn.list_role_policies(RoleName="my-role")
|
|
assert len(role["PolicyNames"]) == 2
|
|
|
|
conn.delete_role_policy(RoleName="my-role", PolicyName="test policy")
|
|
role = conn.list_role_policies(RoleName="my-role")
|
|
assert role["PolicyNames"] == ["test policy 2"]
|
|
|
|
with pytest.raises(ClientError) as ex:
|
|
conn.delete_role_policy(RoleName="my-role", PolicyName="test policy")
|
|
err = ex.value.response["Error"]
|
|
assert err["Code"] == "NoSuchEntity"
|
|
assert err["Message"] == "The role policy with name test policy cannot be found."
|
|
|
|
|
|
@mock_iam
|
|
def test_put_role_policy():
|
|
conn = boto3.client("iam", region_name="us-east-1")
|
|
conn.create_role(
|
|
RoleName="my-role", AssumeRolePolicyDocument="some policy", Path="my-path"
|
|
)
|
|
conn.put_role_policy(
|
|
RoleName="my-role", PolicyName="test policy", PolicyDocument=MOCK_POLICY
|
|
)
|
|
policy = conn.get_role_policy(RoleName="my-role", PolicyName="test policy")
|
|
assert policy["PolicyName"] == "test policy"
|
|
assert policy["PolicyDocument"] == json.loads(MOCK_POLICY)
|
|
|
|
|
|
@mock_iam
|
|
def test_get_role_policy():
|
|
conn = boto3.client("iam", region_name="us-east-1")
|
|
conn.create_role(
|
|
RoleName="my-role", AssumeRolePolicyDocument="some policy", Path="my-path"
|
|
)
|
|
with pytest.raises(conn.exceptions.NoSuchEntityException):
|
|
conn.get_role_policy(RoleName="my-role", PolicyName="does-not-exist")
|
|
|
|
|
|
@mock_iam
|
|
def test_update_assume_role_invalid_policy():
|
|
conn = boto3.client("iam", region_name="us-east-1")
|
|
conn.create_role(
|
|
RoleName="my-role", AssumeRolePolicyDocument="some policy", Path="my-path"
|
|
)
|
|
with pytest.raises(ClientError) as ex:
|
|
conn.update_assume_role_policy(RoleName="my-role", PolicyDocument="new policy")
|
|
err = ex.value.response["Error"]
|
|
assert err["Code"] == "MalformedPolicyDocument"
|
|
assert "Syntax errors in policy." in err["Message"]
|
|
|
|
|
|
@mock_iam
|
|
def test_update_assume_role_valid_policy():
|
|
conn = boto3.client("iam", region_name="us-east-1")
|
|
conn.create_role(
|
|
RoleName="my-role", AssumeRolePolicyDocument="some policy", Path="my-path"
|
|
)
|
|
policy_document = MOCK_STS_EC2_POLICY_DOCUMENT
|
|
conn.update_assume_role_policy(RoleName="my-role", PolicyDocument=policy_document)
|
|
role = conn.get_role(RoleName="my-role")["Role"]
|
|
assert (
|
|
role["AssumeRolePolicyDocument"]["Statement"][0]["Action"][0]
|
|
== "sts:AssumeRole"
|
|
)
|
|
|
|
|
|
@mock_iam
|
|
def test_update_assume_role_invalid_policy_bad_action():
|
|
conn = boto3.client("iam", region_name="us-east-1")
|
|
conn.create_role(
|
|
RoleName="my-role", AssumeRolePolicyDocument="some policy", Path="my-path"
|
|
)
|
|
policy_document = """
|
|
{
|
|
"Version": "2012-10-17",
|
|
"Statement": [
|
|
{
|
|
"Effect": "Allow",
|
|
"Principal": {
|
|
"Service": ["ec2.amazonaws.com"]
|
|
},
|
|
"Action": ["sts:BadAssumeRole"]
|
|
}
|
|
]
|
|
}
|
|
"""
|
|
|
|
with pytest.raises(ClientError) as ex:
|
|
conn.update_assume_role_policy(
|
|
RoleName="my-role", PolicyDocument=policy_document
|
|
)
|
|
err = ex.value.response["Error"]
|
|
assert err["Code"] == "MalformedPolicyDocument"
|
|
assert (
|
|
"Trust Policy statement actions can only be sts:AssumeRole, sts:AssumeRoleWithSAML, and sts:AssumeRoleWithWebIdentity"
|
|
in err["Message"]
|
|
)
|
|
|
|
|
|
@mock_iam
|
|
def test_update_assume_role_invalid_policy_with_resource():
|
|
conn = boto3.client("iam", region_name="us-east-1")
|
|
conn.create_role(
|
|
RoleName="my-role", AssumeRolePolicyDocument="some policy", Path="my-path"
|
|
)
|
|
policy_document = """
|
|
{
|
|
"Version": "2012-10-17",
|
|
"Statement": [
|
|
{
|
|
"Effect": "Allow",
|
|
"Principal": {
|
|
"Service": ["ec2.amazonaws.com"]
|
|
},
|
|
"Action": ["sts:AssumeRole"],
|
|
"Resource" : "arn:aws:s3:::example_bucket"
|
|
}
|
|
]
|
|
}
|
|
"""
|
|
|
|
with pytest.raises(ClientError) as ex:
|
|
conn.update_assume_role_policy(
|
|
RoleName="my-role", PolicyDocument=policy_document
|
|
)
|
|
err = ex.value.response["Error"]
|
|
assert err["Code"] == "MalformedPolicyDocument"
|
|
assert "Has prohibited field Resource." in err["Message"]
|
|
|
|
|
|
@mock_iam
|
|
def test_create_policy():
|
|
conn = boto3.client("iam", region_name="us-east-1")
|
|
response = conn.create_policy(
|
|
PolicyName="TestCreatePolicy", PolicyDocument=MOCK_POLICY
|
|
)
|
|
assert (
|
|
response["Policy"]["Arn"]
|
|
== f"arn:aws:iam::{ACCOUNT_ID}:policy/TestCreatePolicy"
|
|
)
|
|
|
|
|
|
@mock_iam
|
|
def test_create_policy_already_exists():
|
|
conn = boto3.client("iam", region_name="us-east-1")
|
|
conn.create_policy(PolicyName="TestCreatePolicy", PolicyDocument=MOCK_POLICY)
|
|
with pytest.raises(conn.exceptions.EntityAlreadyExistsException) as ex:
|
|
conn.create_policy(PolicyName="TestCreatePolicy", PolicyDocument=MOCK_POLICY)
|
|
assert ex.value.response["Error"]["Code"] == "EntityAlreadyExists"
|
|
assert ex.value.response["ResponseMetadata"]["HTTPStatusCode"] == 409
|
|
assert "TestCreatePolicy" in ex.value.response["Error"]["Message"]
|
|
|
|
|
|
@mock_iam
|
|
def test_delete_policy():
|
|
conn = boto3.client("iam", region_name="us-east-1")
|
|
response = conn.create_policy(
|
|
PolicyName="TestCreatePolicy", PolicyDocument=MOCK_POLICY
|
|
)
|
|
assert [
|
|
pol["PolicyName"] for pol in conn.list_policies(Scope="Local")["Policies"]
|
|
] == ["TestCreatePolicy"]
|
|
conn.delete_policy(PolicyArn=response["Policy"]["Arn"])
|
|
assert conn.list_policies(Scope="Local")["Policies"] == []
|
|
|
|
|
|
@mock_iam
|
|
def test_create_policy_versions():
|
|
conn = boto3.client("iam", region_name="us-east-1")
|
|
with pytest.raises(ClientError):
|
|
conn.create_policy_version(
|
|
PolicyArn=f"arn:aws:iam::{ACCOUNT_ID}:policy/TestCreatePolicyVersion",
|
|
PolicyDocument='{"some":"policy"}',
|
|
)
|
|
conn.create_policy(PolicyName="TestCreatePolicyVersion", PolicyDocument=MOCK_POLICY)
|
|
version = conn.create_policy_version(
|
|
PolicyArn=f"arn:aws:iam::{ACCOUNT_ID}:policy/TestCreatePolicyVersion",
|
|
PolicyDocument=MOCK_POLICY,
|
|
SetAsDefault=True,
|
|
)
|
|
assert version.get("PolicyVersion")["Document"] == json.loads(MOCK_POLICY)
|
|
assert version.get("PolicyVersion")["VersionId"] == "v2"
|
|
assert version.get("PolicyVersion")["IsDefaultVersion"] is True
|
|
conn.delete_policy_version(
|
|
PolicyArn=f"arn:aws:iam::{ACCOUNT_ID}:policy/TestCreatePolicyVersion",
|
|
VersionId="v1",
|
|
)
|
|
version = conn.create_policy_version(
|
|
PolicyArn=f"arn:aws:iam::{ACCOUNT_ID}:policy/TestCreatePolicyVersion",
|
|
PolicyDocument=MOCK_POLICY,
|
|
)
|
|
assert version.get("PolicyVersion")["VersionId"] == "v3"
|
|
assert version.get("PolicyVersion")["IsDefaultVersion"] is False
|
|
|
|
|
|
@mock_iam
|
|
def test_create_many_policy_versions():
|
|
conn = boto3.client("iam", region_name="us-east-1")
|
|
conn.create_policy(
|
|
PolicyName="TestCreateManyPolicyVersions", PolicyDocument=MOCK_POLICY
|
|
)
|
|
for _ in range(0, 4):
|
|
conn.create_policy_version(
|
|
PolicyArn=f"arn:aws:iam::{ACCOUNT_ID}:policy/TestCreateManyPolicyVersions",
|
|
PolicyDocument=MOCK_POLICY,
|
|
)
|
|
with pytest.raises(ClientError):
|
|
conn.create_policy_version(
|
|
PolicyArn=f"arn:aws:iam::{ACCOUNT_ID}:policy/TestCreateManyPolicyVersions",
|
|
PolicyDocument=MOCK_POLICY,
|
|
)
|
|
|
|
|
|
@mock_iam
|
|
def test_set_default_policy_version():
|
|
conn = boto3.client("iam", region_name="us-east-1")
|
|
conn.create_policy(
|
|
PolicyName="TestSetDefaultPolicyVersion", PolicyDocument=MOCK_POLICY
|
|
)
|
|
conn.create_policy_version(
|
|
PolicyArn=f"arn:aws:iam::{ACCOUNT_ID}:policy/TestSetDefaultPolicyVersion",
|
|
PolicyDocument=MOCK_POLICY_2,
|
|
SetAsDefault=True,
|
|
)
|
|
conn.create_policy_version(
|
|
PolicyArn=f"arn:aws:iam::{ACCOUNT_ID}:policy/TestSetDefaultPolicyVersion",
|
|
PolicyDocument=MOCK_POLICY_3,
|
|
SetAsDefault=True,
|
|
)
|
|
versions = conn.list_policy_versions(
|
|
PolicyArn=f"arn:aws:iam::{ACCOUNT_ID}:policy/TestSetDefaultPolicyVersion"
|
|
)
|
|
assert versions["Versions"][0]["Document"] == json.loads(MOCK_POLICY)
|
|
assert versions["Versions"][0]["IsDefaultVersion"] is False
|
|
assert versions["Versions"][1]["Document"] == json.loads(MOCK_POLICY_2)
|
|
assert versions["Versions"][1]["IsDefaultVersion"] is False
|
|
assert versions["Versions"][2]["Document"] == json.loads(MOCK_POLICY_3)
|
|
assert versions["Versions"][2]["IsDefaultVersion"] is True
|
|
|
|
conn.set_default_policy_version(
|
|
PolicyArn=f"arn:aws:iam::{ACCOUNT_ID}:policy/TestSetDefaultPolicyVersion",
|
|
VersionId="v1",
|
|
)
|
|
versions = conn.list_policy_versions(
|
|
PolicyArn=f"arn:aws:iam::{ACCOUNT_ID}:policy/TestSetDefaultPolicyVersion"
|
|
)
|
|
assert versions["Versions"][0]["Document"] == json.loads(MOCK_POLICY)
|
|
assert versions["Versions"][0]["IsDefaultVersion"] is True
|
|
assert versions["Versions"][1]["Document"] == json.loads(MOCK_POLICY_2)
|
|
assert versions["Versions"][1]["IsDefaultVersion"] is False
|
|
assert versions["Versions"][2]["Document"] == json.loads(MOCK_POLICY_3)
|
|
assert versions["Versions"][2]["IsDefaultVersion"] is False
|
|
|
|
# Set default version for non-existing policy
|
|
with pytest.raises(ClientError) as exc:
|
|
conn.set_default_policy_version(
|
|
PolicyArn=f"arn:aws:iam::{ACCOUNT_ID}:policy/TestNonExistingPolicy",
|
|
VersionId="v1",
|
|
)
|
|
err = exc.value.response["Error"]
|
|
assert (
|
|
err["Message"]
|
|
== f"Policy arn:aws:iam::{ACCOUNT_ID}:policy/TestNonExistingPolicy not found"
|
|
)
|
|
|
|
# Set default version for incorrect version
|
|
with pytest.raises(ClientError) as exc:
|
|
conn.set_default_policy_version(
|
|
PolicyArn=f"arn:aws:iam::{ACCOUNT_ID}:policy/TestSetDefaultPolicyVersion",
|
|
VersionId="wrong_version_id",
|
|
)
|
|
err = exc.value.response["Error"]
|
|
assert (
|
|
err["Message"]
|
|
== r"Value 'wrong_version_id' at 'versionId' failed to satisfy constraint: Member must satisfy regular expression pattern: v[1-9][0-9]*(\.[A-Za-z0-9-]*)?"
|
|
)
|
|
|
|
# Set default version for non-existing version
|
|
with pytest.raises(ClientError) as exc:
|
|
conn.set_default_policy_version(
|
|
PolicyArn=f"arn:aws:iam::{ACCOUNT_ID}:policy/TestSetDefaultPolicyVersion",
|
|
VersionId="v4",
|
|
)
|
|
err = exc.value.response["Error"]
|
|
assert (
|
|
err["Message"]
|
|
== f"Policy arn:aws:iam::{ACCOUNT_ID}:policy/TestSetDefaultPolicyVersion version v4 does not exist or is not attachable."
|
|
)
|
|
|
|
|
|
@mock_iam
|
|
def test_get_policy():
|
|
conn = boto3.client("iam", region_name="us-east-1")
|
|
conn.create_policy(PolicyName="TestGetPolicy", PolicyDocument=MOCK_POLICY)
|
|
policy = conn.get_policy(
|
|
PolicyArn=f"arn:aws:iam::{ACCOUNT_ID}:policy/TestGetPolicy"
|
|
)
|
|
assert policy["Policy"]["Arn"] == f"arn:aws:iam::{ACCOUNT_ID}:policy/TestGetPolicy"
|
|
|
|
|
|
@mock_iam
|
|
def test_get_aws_managed_policy():
|
|
conn = boto3.client("iam", region_name="us-east-1")
|
|
managed_policy_arn = "arn:aws:iam::aws:policy/IAMUserChangePassword"
|
|
managed_policy_create_date = datetime.strptime(
|
|
"2016-11-15T00:25:16+00:00", "%Y-%m-%dT%H:%M:%S+00:00"
|
|
)
|
|
policy = conn.get_policy(PolicyArn=managed_policy_arn)
|
|
assert policy["Policy"]["Arn"] == managed_policy_arn
|
|
assert (
|
|
policy["Policy"]["CreateDate"].replace(tzinfo=None)
|
|
== managed_policy_create_date
|
|
)
|
|
|
|
|
|
@mock_iam
|
|
def test_get_policy_version():
|
|
conn = boto3.client("iam", region_name="us-east-1")
|
|
conn.create_policy(PolicyName="TestGetPolicyVersion", PolicyDocument=MOCK_POLICY)
|
|
version = conn.create_policy_version(
|
|
PolicyArn=f"arn:aws:iam::{ACCOUNT_ID}:policy/TestGetPolicyVersion",
|
|
PolicyDocument=MOCK_POLICY,
|
|
)
|
|
with pytest.raises(ClientError):
|
|
conn.get_policy_version(
|
|
PolicyArn=f"arn:aws:iam::{ACCOUNT_ID}:policy/TestGetPolicyVersion",
|
|
VersionId="v2-does-not-exist",
|
|
)
|
|
retrieved = conn.get_policy_version(
|
|
PolicyArn=f"arn:aws:iam::{ACCOUNT_ID}:policy/TestGetPolicyVersion",
|
|
VersionId=version.get("PolicyVersion")["VersionId"],
|
|
)
|
|
assert retrieved.get("PolicyVersion")["Document"] == json.loads(MOCK_POLICY)
|
|
assert retrieved.get("PolicyVersion")["IsDefaultVersion"] is False
|
|
|
|
|
|
@mock_iam
|
|
def test_get_aws_managed_policy_version():
|
|
conn = boto3.client("iam", region_name="us-east-1")
|
|
managed_policy_arn = (
|
|
"arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole"
|
|
)
|
|
managed_policy_version_create_date = datetime.strptime(
|
|
"2015-04-09T15:03:43+00:00", "%Y-%m-%dT%H:%M:%S+00:00"
|
|
)
|
|
with pytest.raises(ClientError):
|
|
conn.get_policy_version(
|
|
PolicyArn=managed_policy_arn, VersionId="v2-does-not-exist"
|
|
)
|
|
retrieved = conn.get_policy_version(PolicyArn=managed_policy_arn, VersionId="v1")
|
|
assert (
|
|
retrieved["PolicyVersion"]["CreateDate"].replace(tzinfo=None)
|
|
== managed_policy_version_create_date
|
|
)
|
|
assert isinstance(retrieved["PolicyVersion"]["Document"], dict)
|
|
|
|
|
|
@mock_iam
|
|
def test_get_aws_managed_policy_v6_version():
|
|
conn = boto3.client("iam", region_name="us-east-1")
|
|
managed_policy_arn = "arn:aws:iam::aws:policy/job-function/SystemAdministrator"
|
|
with pytest.raises(ClientError):
|
|
conn.get_policy_version(
|
|
PolicyArn=managed_policy_arn, VersionId="v2-does-not-exist"
|
|
)
|
|
retrieved = conn.get_policy_version(PolicyArn=managed_policy_arn, VersionId="v6")
|
|
assert isinstance(
|
|
retrieved["PolicyVersion"]["CreateDate"].replace(tzinfo=None), datetime
|
|
)
|
|
assert isinstance(retrieved["PolicyVersion"]["Document"], dict)
|
|
|
|
|
|
@mock_iam
|
|
def test_list_policy_versions():
|
|
conn = boto3.client("iam", region_name="us-east-1")
|
|
with pytest.raises(ClientError):
|
|
versions = conn.list_policy_versions(
|
|
PolicyArn=f"arn:aws:iam::{ACCOUNT_ID}:policy/TestListPolicyVersions"
|
|
)
|
|
conn.create_policy(PolicyName="TestListPolicyVersions", PolicyDocument=MOCK_POLICY)
|
|
versions = conn.list_policy_versions(
|
|
PolicyArn=f"arn:aws:iam::{ACCOUNT_ID}:policy/TestListPolicyVersions"
|
|
)
|
|
assert versions["Versions"][0]["VersionId"] == "v1"
|
|
assert versions["Versions"][0]["IsDefaultVersion"] is True
|
|
|
|
conn.create_policy_version(
|
|
PolicyArn=f"arn:aws:iam::{ACCOUNT_ID}:policy/TestListPolicyVersions",
|
|
PolicyDocument=MOCK_POLICY_2,
|
|
)
|
|
conn.create_policy_version(
|
|
PolicyArn=f"arn:aws:iam::{ACCOUNT_ID}:policy/TestListPolicyVersions",
|
|
PolicyDocument=MOCK_POLICY_3,
|
|
)
|
|
versions = conn.list_policy_versions(
|
|
PolicyArn=f"arn:aws:iam::{ACCOUNT_ID}:policy/TestListPolicyVersions"
|
|
)
|
|
assert versions["Versions"][1]["Document"] == json.loads(MOCK_POLICY_2)
|
|
assert versions["Versions"][1]["IsDefaultVersion"] is False
|
|
assert versions["Versions"][2]["Document"] == json.loads(MOCK_POLICY_3)
|
|
assert versions["Versions"][2]["IsDefaultVersion"] is False
|
|
|
|
|
|
@mock_iam
|
|
def test_delete_policy_version():
|
|
conn = boto3.client("iam", region_name="us-east-1")
|
|
conn.create_policy(PolicyName="TestDeletePolicyVersion", PolicyDocument=MOCK_POLICY)
|
|
conn.create_policy_version(
|
|
PolicyArn=f"arn:aws:iam::{ACCOUNT_ID}:policy/TestDeletePolicyVersion",
|
|
PolicyDocument=MOCK_POLICY,
|
|
)
|
|
with pytest.raises(ClientError):
|
|
conn.delete_policy_version(
|
|
PolicyArn=f"arn:aws:iam::{ACCOUNT_ID}:policy/TestDeletePolicyVersion",
|
|
VersionId="v2-nope-this-does-not-exist",
|
|
)
|
|
conn.delete_policy_version(
|
|
PolicyArn=f"arn:aws:iam::{ACCOUNT_ID}:policy/TestDeletePolicyVersion",
|
|
VersionId="v2",
|
|
)
|
|
versions = conn.list_policy_versions(
|
|
PolicyArn=f"arn:aws:iam::{ACCOUNT_ID}:policy/TestDeletePolicyVersion"
|
|
)
|
|
assert len(versions["Versions"]) == 1
|
|
|
|
|
|
@mock_iam
|
|
def test_delete_default_policy_version():
|
|
conn = boto3.client("iam", region_name="us-east-1")
|
|
conn.create_policy(PolicyName="TestDeletePolicyVersion", PolicyDocument=MOCK_POLICY)
|
|
conn.create_policy_version(
|
|
PolicyArn=f"arn:aws:iam::{ACCOUNT_ID}:policy/TestDeletePolicyVersion",
|
|
PolicyDocument=MOCK_POLICY_2,
|
|
)
|
|
with pytest.raises(ClientError):
|
|
conn.delete_policy_version(
|
|
PolicyArn=f"arn:aws:iam::{ACCOUNT_ID}:policy/TestDeletePolicyVersion",
|
|
VersionId="v1",
|
|
)
|
|
|
|
|
|
@mock_iam()
|
|
def test_create_policy_with_tags():
|
|
conn = boto3.client("iam", region_name="us-east-1")
|
|
conn.create_policy(
|
|
PolicyName="TestCreatePolicyWithTags1",
|
|
PolicyDocument=MOCK_POLICY,
|
|
Tags=[
|
|
{"Key": "somekey", "Value": "somevalue"},
|
|
{"Key": "someotherkey", "Value": "someothervalue"},
|
|
],
|
|
Description="testing",
|
|
)
|
|
|
|
# Get policy:
|
|
policy = conn.get_policy(
|
|
PolicyArn=f"arn:aws:iam::{ACCOUNT_ID}:policy/TestCreatePolicyWithTags1"
|
|
)["Policy"]
|
|
assert len(policy["Tags"]) == 2
|
|
assert policy["Tags"][0]["Key"] == "somekey"
|
|
assert policy["Tags"][0]["Value"] == "somevalue"
|
|
assert policy["Tags"][1]["Key"] == "someotherkey"
|
|
assert policy["Tags"][1]["Value"] == "someothervalue"
|
|
assert policy["Description"] == "testing"
|
|
|
|
|
|
@mock_iam()
|
|
def test_create_policy_with_empty_tag_value():
|
|
conn = boto3.client("iam", region_name="us-east-1")
|
|
|
|
# Empty is good:
|
|
conn.create_policy(
|
|
PolicyName="TestCreatePolicyWithTags2",
|
|
PolicyDocument=MOCK_POLICY,
|
|
Tags=[{"Key": "somekey", "Value": ""}],
|
|
)
|
|
tags = conn.list_policy_tags(
|
|
PolicyArn=f"arn:aws:iam::{ACCOUNT_ID}:policy/TestCreatePolicyWithTags2"
|
|
)
|
|
assert len(tags["Tags"]) == 1
|
|
assert tags["Tags"][0]["Key"] == "somekey"
|
|
assert tags["Tags"][0]["Value"] == ""
|
|
|
|
|
|
@mock_iam()
|
|
def test_create_policy_with_too_many_tags():
|
|
conn = boto3.client("iam", region_name="us-east-1")
|
|
|
|
# With more than 50 tags:
|
|
with pytest.raises(ClientError) as ce:
|
|
too_many_tags = list(
|
|
map(lambda x: {"Key": str(x), "Value": str(x)}, range(0, 51))
|
|
)
|
|
conn.create_policy(
|
|
PolicyName="TestCreatePolicyWithTags3",
|
|
PolicyDocument=MOCK_POLICY,
|
|
Tags=too_many_tags,
|
|
)
|
|
assert (
|
|
"failed to satisfy constraint: Member must have length less than or equal to 50."
|
|
in ce.value.response["Error"]["Message"]
|
|
)
|
|
|
|
|
|
@mock_iam()
|
|
def test_create_policy_with_duplicate_tag():
|
|
conn = boto3.client("iam", region_name="us-east-1")
|
|
|
|
# With a duplicate tag:
|
|
with pytest.raises(ClientError) as ce:
|
|
conn.create_policy(
|
|
PolicyName="TestCreatePolicyWithTags3",
|
|
PolicyDocument=MOCK_POLICY,
|
|
Tags=[{"Key": "0", "Value": ""}, {"Key": "0", "Value": ""}],
|
|
)
|
|
assert (
|
|
"Duplicate tag keys found. Please note that Tag keys are case insensitive."
|
|
in ce.value.response["Error"]["Message"]
|
|
)
|
|
|
|
|
|
@mock_iam()
|
|
def test_create_policy_with_duplicate_tag_different_casing():
|
|
conn = boto3.client("iam", region_name="us-east-1")
|
|
|
|
# Duplicate tag with different casing:
|
|
with pytest.raises(ClientError) as ce:
|
|
conn.create_policy(
|
|
PolicyName="TestCreatePolicyWithTags3",
|
|
PolicyDocument=MOCK_POLICY,
|
|
Tags=[{"Key": "a", "Value": ""}, {"Key": "A", "Value": ""}],
|
|
)
|
|
assert (
|
|
"Duplicate tag keys found. Please note that Tag keys are case insensitive."
|
|
in ce.value.response["Error"]["Message"]
|
|
)
|
|
|
|
|
|
@mock_iam()
|
|
def test_create_policy_with_tag_containing_large_key():
|
|
conn = boto3.client("iam", region_name="us-east-1")
|
|
|
|
# With a really big key:
|
|
with pytest.raises(ClientError) as ce:
|
|
conn.create_policy(
|
|
PolicyName="TestCreatePolicyWithTags3",
|
|
PolicyDocument=MOCK_POLICY,
|
|
Tags=[{"Key": "0" * 129, "Value": ""}],
|
|
)
|
|
assert (
|
|
"Member must have length less than or equal to 128."
|
|
in ce.value.response["Error"]["Message"]
|
|
)
|
|
|
|
|
|
@mock_iam()
|
|
def test_create_policy_with_tag_containing_large_value():
|
|
conn = boto3.client("iam", region_name="us-east-1")
|
|
|
|
# With a really big value:
|
|
with pytest.raises(ClientError) as ce:
|
|
conn.create_policy(
|
|
PolicyName="TestCreatePolicyWithTags3",
|
|
PolicyDocument=MOCK_POLICY,
|
|
Tags=[{"Key": "0", "Value": "0" * 257}],
|
|
)
|
|
assert (
|
|
"Member must have length less than or equal to 256."
|
|
in ce.value.response["Error"]["Message"]
|
|
)
|
|
|
|
|
|
@mock_iam()
|
|
def test_create_policy_with_tag_containing_invalid_character():
|
|
conn = boto3.client("iam", region_name="us-east-1")
|
|
|
|
# With an invalid character:
|
|
with pytest.raises(ClientError) as ce:
|
|
conn.create_policy(
|
|
PolicyName="TestCreatePolicyWithTags3",
|
|
PolicyDocument=MOCK_POLICY,
|
|
Tags=[{"Key": "NOWAY!", "Value": ""}],
|
|
)
|
|
assert (
|
|
"Member must satisfy regular expression pattern: [\\p{L}\\p{Z}\\p{N}_.:/=+\\-@]+"
|
|
in ce.value.response["Error"]["Message"]
|
|
)
|
|
|
|
|
|
@mock_iam()
|
|
def test_create_policy_with_no_tags():
|
|
"""Tests both the tag_policy and get_policy_tags capability"""
|
|
conn = boto3.client("iam", region_name="us-east-1")
|
|
conn.create_policy(PolicyName="TestTagPolicy", PolicyDocument=MOCK_POLICY)
|
|
|
|
# Get without tags:
|
|
policy = conn.get_policy(
|
|
PolicyArn=f"arn:aws:iam::{ACCOUNT_ID}:policy/TestTagPolicy"
|
|
)["Policy"]
|
|
assert not policy.get("Tags")
|
|
|
|
|
|
@mock_iam()
|
|
def test_get_policy_with_tags():
|
|
conn = boto3.client("iam", region_name="us-east-1")
|
|
conn.create_policy(PolicyName="TestTagPolicy", PolicyDocument=MOCK_POLICY)
|
|
conn.tag_policy(
|
|
PolicyArn=f"arn:aws:iam::{ACCOUNT_ID}:policy/TestTagPolicy",
|
|
Tags=[
|
|
{"Key": "somekey", "Value": "somevalue"},
|
|
{"Key": "someotherkey", "Value": "someothervalue"},
|
|
],
|
|
)
|
|
|
|
# Get policy:
|
|
policy = conn.get_policy(
|
|
PolicyArn=f"arn:aws:iam::{ACCOUNT_ID}:policy/TestTagPolicy"
|
|
)["Policy"]
|
|
assert len(policy["Tags"]) == 2
|
|
assert policy["Tags"][0]["Key"] == "somekey"
|
|
assert policy["Tags"][0]["Value"] == "somevalue"
|
|
assert policy["Tags"][1]["Key"] == "someotherkey"
|
|
assert policy["Tags"][1]["Value"] == "someothervalue"
|
|
|
|
|
|
@mock_iam()
|
|
def test_list_policy_tags():
|
|
conn = boto3.client("iam", region_name="us-east-1")
|
|
conn.create_policy(PolicyName="TestTagPolicy", PolicyDocument=MOCK_POLICY)
|
|
conn.tag_policy(
|
|
PolicyArn=f"arn:aws:iam::{ACCOUNT_ID}:policy/TestTagPolicy",
|
|
Tags=[
|
|
{"Key": "somekey", "Value": "somevalue"},
|
|
{"Key": "someotherkey", "Value": "someothervalue"},
|
|
],
|
|
)
|
|
|
|
# List_policy_tags:
|
|
tags = conn.list_policy_tags(
|
|
PolicyArn=f"arn:aws:iam::{ACCOUNT_ID}:policy/TestTagPolicy"
|
|
)
|
|
assert len(tags["Tags"]) == 2
|
|
assert tags["Tags"][0]["Key"] == "somekey"
|
|
assert tags["Tags"][0]["Value"] == "somevalue"
|
|
assert tags["Tags"][1]["Key"] == "someotherkey"
|
|
assert tags["Tags"][1]["Value"] == "someothervalue"
|
|
assert not tags["IsTruncated"]
|
|
assert not tags.get("Marker")
|
|
|
|
|
|
@mock_iam()
|
|
def test_list_policy_tags_pagination():
|
|
conn = boto3.client("iam", region_name="us-east-1")
|
|
conn.create_policy(PolicyName="TestTagPolicy", PolicyDocument=MOCK_POLICY)
|
|
conn.tag_policy(
|
|
PolicyArn=f"arn:aws:iam::{ACCOUNT_ID}:policy/TestTagPolicy",
|
|
Tags=[
|
|
{"Key": "somekey", "Value": "somevalue"},
|
|
{"Key": "someotherkey", "Value": "someothervalue"},
|
|
],
|
|
)
|
|
|
|
# Test pagination:
|
|
tags = conn.list_policy_tags(
|
|
PolicyArn=f"arn:aws:iam::{ACCOUNT_ID}:policy/TestTagPolicy",
|
|
MaxItems=1,
|
|
)
|
|
assert len(tags["Tags"]) == 1
|
|
assert tags["IsTruncated"]
|
|
assert tags["Tags"][0]["Key"] == "somekey"
|
|
assert tags["Tags"][0]["Value"] == "somevalue"
|
|
assert tags["Marker"] == "1"
|
|
|
|
tags = conn.list_policy_tags(
|
|
PolicyArn=f"arn:aws:iam::{ACCOUNT_ID}:policy/TestTagPolicy",
|
|
Marker=tags["Marker"],
|
|
)
|
|
assert len(tags["Tags"]) == 1
|
|
assert tags["Tags"][0]["Key"] == "someotherkey"
|
|
assert tags["Tags"][0]["Value"] == "someothervalue"
|
|
assert not tags["IsTruncated"]
|
|
assert not tags.get("Marker")
|
|
|
|
|
|
@mock_iam()
|
|
def test_updating_existing_tag():
|
|
conn = boto3.client("iam", region_name="us-east-1")
|
|
conn.create_policy(PolicyName="TestTagPolicy", PolicyDocument=MOCK_POLICY)
|
|
conn.tag_policy(
|
|
PolicyArn=f"arn:aws:iam::{ACCOUNT_ID}:policy/TestTagPolicy",
|
|
Tags=[
|
|
{"Key": "somekey", "Value": "somevalue"},
|
|
{"Key": "someotherkey", "Value": "someothervalue"},
|
|
],
|
|
)
|
|
|
|
# Test updating an existing tag:
|
|
conn.tag_policy(
|
|
PolicyArn=f"arn:aws:iam::{ACCOUNT_ID}:policy/TestTagPolicy",
|
|
Tags=[{"Key": "somekey", "Value": "somenewvalue"}],
|
|
)
|
|
tags = conn.list_policy_tags(
|
|
PolicyArn=f"arn:aws:iam::{ACCOUNT_ID}:policy/TestTagPolicy"
|
|
)
|
|
assert len(tags["Tags"]) == 2
|
|
assert tags["Tags"][0]["Key"] == "somekey"
|
|
assert tags["Tags"][0]["Value"] == "somenewvalue"
|
|
|
|
|
|
@mock_iam()
|
|
def test_updating_existing_tag_with_empty_value():
|
|
conn = boto3.client("iam", region_name="us-east-1")
|
|
conn.create_policy(PolicyName="TestTagPolicy", PolicyDocument=MOCK_POLICY)
|
|
conn.tag_policy(
|
|
PolicyArn=f"arn:aws:iam::{ACCOUNT_ID}:policy/TestTagPolicy",
|
|
Tags=[
|
|
{"Key": "somekey", "Value": "somevalue"},
|
|
{"Key": "someotherkey", "Value": "someothervalue"},
|
|
],
|
|
)
|
|
|
|
# Empty is good:
|
|
conn.tag_policy(
|
|
PolicyArn=f"arn:aws:iam::{ACCOUNT_ID}:policy/TestTagPolicy",
|
|
Tags=[{"Key": "somekey", "Value": ""}],
|
|
)
|
|
tags = conn.list_policy_tags(
|
|
PolicyArn=f"arn:aws:iam::{ACCOUNT_ID}:policy/TestTagPolicy"
|
|
)
|
|
assert len(tags["Tags"]) == 2
|
|
assert tags["Tags"][0]["Key"] == "somekey"
|
|
assert tags["Tags"][0]["Value"] == ""
|
|
|
|
|
|
@mock_iam()
|
|
def test_updating_existing_tagged_policy_with_too_many_tags():
|
|
conn = boto3.client("iam", region_name="us-east-1")
|
|
conn.create_policy(PolicyName="TestTagPolicy", PolicyDocument=MOCK_POLICY)
|
|
conn.tag_policy(
|
|
PolicyArn=f"arn:aws:iam::{ACCOUNT_ID}:policy/TestTagPolicy",
|
|
Tags=[
|
|
{"Key": "somekey", "Value": "somevalue"},
|
|
{"Key": "someotherkey", "Value": "someothervalue"},
|
|
],
|
|
)
|
|
|
|
# With more than 50 tags:
|
|
with pytest.raises(ClientError) as ce:
|
|
too_many_tags = list(
|
|
map(lambda x: {"Key": str(x), "Value": str(x)}, range(0, 51))
|
|
)
|
|
conn.tag_policy(
|
|
PolicyArn=f"arn:aws:iam::{ACCOUNT_ID}:policy/TestTagPolicy",
|
|
Tags=too_many_tags,
|
|
)
|
|
assert (
|
|
"failed to satisfy constraint: Member must have length less than or equal to 50."
|
|
in ce.value.response["Error"]["Message"]
|
|
)
|
|
|
|
|
|
@mock_iam()
|
|
def test_updating_existing_tagged_policy_with_duplicate_tag():
|
|
conn = boto3.client("iam", region_name="us-east-1")
|
|
conn.create_policy(PolicyName="TestTagPolicy", PolicyDocument=MOCK_POLICY)
|
|
conn.tag_policy(
|
|
PolicyArn=f"arn:aws:iam::{ACCOUNT_ID}:policy/TestTagPolicy",
|
|
Tags=[
|
|
{"Key": "somekey", "Value": "somevalue"},
|
|
{"Key": "someotherkey", "Value": "someothervalue"},
|
|
],
|
|
)
|
|
|
|
# With a duplicate tag:
|
|
with pytest.raises(ClientError) as ce:
|
|
conn.tag_policy(
|
|
PolicyArn=f"arn:aws:iam::{ACCOUNT_ID}:policy/TestTagPolicy",
|
|
Tags=[{"Key": "0", "Value": ""}, {"Key": "0", "Value": ""}],
|
|
)
|
|
assert (
|
|
"Duplicate tag keys found. Please note that Tag keys are case insensitive."
|
|
in ce.value.response["Error"]["Message"]
|
|
)
|
|
|
|
|
|
@mock_iam()
|
|
def test_updating_existing_tagged_policy_with_duplicate_tag_different_casing():
|
|
conn = boto3.client("iam", region_name="us-east-1")
|
|
conn.create_policy(PolicyName="TestTagPolicy", PolicyDocument=MOCK_POLICY)
|
|
conn.tag_policy(
|
|
PolicyArn=f"arn:aws:iam::{ACCOUNT_ID}:policy/TestTagPolicy",
|
|
Tags=[
|
|
{"Key": "somekey", "Value": "somevalue"},
|
|
{"Key": "someotherkey", "Value": "someothervalue"},
|
|
],
|
|
)
|
|
|
|
# Duplicate tag with different casing:
|
|
with pytest.raises(ClientError) as ce:
|
|
conn.tag_policy(
|
|
PolicyArn=f"arn:aws:iam::{ACCOUNT_ID}:policy/TestTagPolicy",
|
|
Tags=[{"Key": "a", "Value": ""}, {"Key": "A", "Value": ""}],
|
|
)
|
|
assert (
|
|
"Duplicate tag keys found. Please note that Tag keys are case insensitive."
|
|
in ce.value.response["Error"]["Message"]
|
|
)
|
|
|
|
|
|
@mock_iam()
|
|
def test_updating_existing_tagged_policy_with_large_key():
|
|
conn = boto3.client("iam", region_name="us-east-1")
|
|
conn.create_policy(PolicyName="TestTagPolicy", PolicyDocument=MOCK_POLICY)
|
|
conn.tag_policy(
|
|
PolicyArn=f"arn:aws:iam::{ACCOUNT_ID}:policy/TestTagPolicy",
|
|
Tags=[
|
|
{"Key": "somekey", "Value": "somevalue"},
|
|
{"Key": "someotherkey", "Value": "someothervalue"},
|
|
],
|
|
)
|
|
|
|
# With a really big key:
|
|
with pytest.raises(ClientError) as ce:
|
|
conn.tag_policy(
|
|
PolicyArn=f"arn:aws:iam::{ACCOUNT_ID}:policy/TestTagPolicy",
|
|
Tags=[{"Key": "0" * 129, "Value": ""}],
|
|
)
|
|
assert (
|
|
"Member must have length less than or equal to 128."
|
|
in ce.value.response["Error"]["Message"]
|
|
)
|
|
|
|
|
|
@mock_iam()
|
|
def test_updating_existing_tagged_policy_with_large_value():
|
|
conn = boto3.client("iam", region_name="us-east-1")
|
|
conn.create_policy(PolicyName="TestTagPolicy", PolicyDocument=MOCK_POLICY)
|
|
conn.tag_policy(
|
|
PolicyArn=f"arn:aws:iam::{ACCOUNT_ID}:policy/TestTagPolicy",
|
|
Tags=[
|
|
{"Key": "somekey", "Value": "somevalue"},
|
|
{"Key": "someotherkey", "Value": "someothervalue"},
|
|
],
|
|
)
|
|
|
|
# With a really big value:
|
|
with pytest.raises(ClientError) as ce:
|
|
conn.tag_policy(
|
|
PolicyArn=f"arn:aws:iam::{ACCOUNT_ID}:policy/TestTagPolicy",
|
|
Tags=[{"Key": "0", "Value": "0" * 257}],
|
|
)
|
|
assert (
|
|
"Member must have length less than or equal to 256."
|
|
in ce.value.response["Error"]["Message"]
|
|
)
|
|
|
|
|
|
@mock_iam()
|
|
def test_updating_existing_tagged_policy_with_invalid_character():
|
|
conn = boto3.client("iam", region_name="us-east-1")
|
|
conn.create_policy(PolicyName="TestTagPolicy", PolicyDocument=MOCK_POLICY)
|
|
conn.tag_policy(
|
|
PolicyArn=f"arn:aws:iam::{ACCOUNT_ID}:policy/TestTagPolicy",
|
|
Tags=[
|
|
{"Key": "somekey", "Value": "somevalue"},
|
|
{"Key": "someotherkey", "Value": "someothervalue"},
|
|
],
|
|
)
|
|
|
|
# With an invalid character:
|
|
with pytest.raises(ClientError) as ce:
|
|
conn.tag_policy(
|
|
PolicyArn=f"arn:aws:iam::{ACCOUNT_ID}:policy/TestTagPolicy",
|
|
Tags=[{"Key": "NOWAY!", "Value": ""}],
|
|
)
|
|
assert (
|
|
"Member must satisfy regular expression pattern: [\\p{L}\\p{Z}\\p{N}_.:/=+\\-@]+"
|
|
in ce.value.response["Error"]["Message"]
|
|
)
|
|
|
|
|
|
@mock_iam()
|
|
def test_tag_non_existant_policy():
|
|
conn = boto3.client("iam", region_name="us-east-1")
|
|
|
|
# With a policy that doesn't exist:
|
|
with pytest.raises(ClientError):
|
|
conn.tag_policy(
|
|
PolicyArn=f"arn:aws:iam::{ACCOUNT_ID}:policy/NotAPolicy",
|
|
Tags=[{"Key": "some", "Value": "value"}],
|
|
)
|
|
|
|
|
|
@mock_iam
|
|
def test_untag_policy():
|
|
conn = boto3.client("iam", region_name="us-east-1")
|
|
conn.create_policy(PolicyName="TestUnTagPolicy", PolicyDocument=MOCK_POLICY)
|
|
|
|
# With proper tag values:
|
|
conn.tag_policy(
|
|
PolicyArn=f"arn:aws:iam::{ACCOUNT_ID}:policy/TestUnTagPolicy",
|
|
Tags=[
|
|
{"Key": "somekey", "Value": "somevalue"},
|
|
{"Key": "someotherkey", "Value": "someothervalue"},
|
|
],
|
|
)
|
|
|
|
# Remove them:
|
|
conn.untag_policy(
|
|
PolicyArn=f"arn:aws:iam::{ACCOUNT_ID}:policy/TestUnTagPolicy",
|
|
TagKeys=["somekey"],
|
|
)
|
|
tags = conn.list_policy_tags(
|
|
PolicyArn=f"arn:aws:iam::{ACCOUNT_ID}:policy/TestUnTagPolicy"
|
|
)
|
|
assert len(tags["Tags"]) == 1
|
|
assert tags["Tags"][0]["Key"] == "someotherkey"
|
|
assert tags["Tags"][0]["Value"] == "someothervalue"
|
|
|
|
# And again:
|
|
conn.untag_policy(
|
|
PolicyArn=f"arn:aws:iam::{ACCOUNT_ID}:policy/TestUnTagPolicy",
|
|
TagKeys=["someotherkey"],
|
|
)
|
|
tags = conn.list_policy_tags(
|
|
PolicyArn=f"arn:aws:iam::{ACCOUNT_ID}:policy/TestUnTagPolicy"
|
|
)
|
|
assert not tags["Tags"]
|
|
|
|
# Test removing tags with invalid values:
|
|
# With more than 50 tags:
|
|
with pytest.raises(ClientError) as ce:
|
|
conn.untag_policy(
|
|
PolicyArn=f"arn:aws:iam::{ACCOUNT_ID}:policy/TestUnTagPolicy",
|
|
TagKeys=[str(x) for x in range(0, 51)],
|
|
)
|
|
assert (
|
|
"failed to satisfy constraint: Member must have length less than or equal to 50."
|
|
in ce.value.response["Error"]["Message"]
|
|
)
|
|
assert "tagKeys" in ce.value.response["Error"]["Message"]
|
|
|
|
# With a really big key:
|
|
with pytest.raises(ClientError) as ce:
|
|
conn.untag_policy(
|
|
PolicyArn=f"arn:aws:iam::{ACCOUNT_ID}:policy/TestUnTagPolicy",
|
|
TagKeys=["0" * 129],
|
|
)
|
|
assert (
|
|
"Member must have length less than or equal to 128."
|
|
in ce.value.response["Error"]["Message"]
|
|
)
|
|
assert "tagKeys" in ce.value.response["Error"]["Message"]
|
|
|
|
# With an invalid character:
|
|
with pytest.raises(ClientError) as ce:
|
|
conn.untag_policy(
|
|
PolicyArn=f"arn:aws:iam::{ACCOUNT_ID}:policy/TestUnTagPolicy",
|
|
TagKeys=["NOWAY!"],
|
|
)
|
|
assert (
|
|
"Member must satisfy regular expression pattern: [\\p{L}\\p{Z}\\p{N}_.:/=+\\-@]+"
|
|
in ce.value.response["Error"]["Message"]
|
|
)
|
|
assert "tagKeys" in ce.value.response["Error"]["Message"]
|
|
|
|
# With a policy that doesn't exist:
|
|
with pytest.raises(ClientError):
|
|
conn.untag_policy(
|
|
PolicyArn=f"arn:aws:iam::{ACCOUNT_ID}:policy/NotAPolicy",
|
|
TagKeys=["somevalue"],
|
|
)
|
|
|
|
|
|
@mock_iam
|
|
def test_create_user_boto():
|
|
conn = boto3.client("iam", region_name="us-east-1")
|
|
u = conn.create_user(UserName="my-user")["User"]
|
|
assert u["Path"] == "/"
|
|
assert u["UserName"] == "my-user"
|
|
assert "UserId" in u
|
|
assert u["Arn"] == f"arn:aws:iam::{ACCOUNT_ID}:user/my-user"
|
|
assert isinstance(u["CreateDate"], datetime)
|
|
|
|
with pytest.raises(ClientError) as ex:
|
|
conn.create_user(UserName="my-user")
|
|
err = ex.value.response["Error"]
|
|
assert err["Code"] == "EntityAlreadyExists"
|
|
assert err["Message"] == "User my-user already exists"
|
|
|
|
|
|
@mock_iam
|
|
def test_get_user():
|
|
conn = boto3.client("iam", region_name="us-east-1")
|
|
with pytest.raises(ClientError) as ex:
|
|
conn.get_user(UserName="my-user")
|
|
err = ex.value.response["Error"]
|
|
assert err["Code"] == "NoSuchEntity"
|
|
assert err["Message"] == "The user with name my-user cannot be found."
|
|
|
|
conn.create_user(UserName="my-user")
|
|
|
|
u = conn.get_user(UserName="my-user")["User"]
|
|
assert u["Path"] == "/"
|
|
assert u["UserName"] == "my-user"
|
|
assert "UserId" in u
|
|
assert u["Arn"] == f"arn:aws:iam::{ACCOUNT_ID}:user/my-user"
|
|
assert isinstance(u["CreateDate"], datetime)
|
|
|
|
|
|
@mock_iam()
|
|
def test_update_user():
|
|
conn = boto3.client("iam", region_name="us-east-1")
|
|
with pytest.raises(conn.exceptions.NoSuchEntityException):
|
|
conn.update_user(UserName="my-user")
|
|
conn.create_user(UserName="my-user")
|
|
conn.update_user(UserName="my-user", NewPath="/new-path/", NewUserName="new-user")
|
|
response = conn.get_user(UserName="new-user")
|
|
assert response["User"]["Path"] == "/new-path/"
|
|
with pytest.raises(conn.exceptions.NoSuchEntityException):
|
|
conn.get_user(UserName="my-user")
|
|
|
|
|
|
@mock_iam
|
|
def test_get_current_user():
|
|
"""If no user is specific, IAM returns the current user"""
|
|
conn = boto3.client("iam", region_name="us-east-1")
|
|
user = conn.get_user()["User"]
|
|
assert user["UserName"] == "default_user"
|
|
|
|
|
|
@mock_iam()
|
|
def test_list_users():
|
|
path_prefix = "/"
|
|
max_items = 10
|
|
conn = boto3.client("iam", region_name="us-east-1")
|
|
conn.create_user(UserName="my-user")
|
|
response = conn.list_users(PathPrefix=path_prefix, MaxItems=max_items)
|
|
user = response["Users"][0]
|
|
assert user["UserName"] == "my-user"
|
|
assert user["Path"] == "/"
|
|
assert user["Arn"] == f"arn:aws:iam::{ACCOUNT_ID}:user/my-user"
|
|
assert response["IsTruncated"] is False
|
|
|
|
conn.create_user(UserName="my-user-1", Path="myUser")
|
|
response = conn.list_users(PathPrefix="my")
|
|
user = response["Users"][0]
|
|
assert user["UserName"] == "my-user-1"
|
|
assert user["Path"] == "myUser"
|
|
|
|
|
|
@mock_iam()
|
|
def test_user_policies():
|
|
policy_name = "UserManagedPolicy"
|
|
user_name = "my-user"
|
|
conn = boto3.client("iam", region_name="us-east-1")
|
|
conn.create_user(UserName=user_name)
|
|
conn.put_user_policy(
|
|
UserName=user_name, PolicyName=policy_name, PolicyDocument=MOCK_POLICY
|
|
)
|
|
|
|
policy_doc = conn.get_user_policy(UserName=user_name, PolicyName=policy_name)
|
|
assert policy_doc["PolicyDocument"] == json.loads(MOCK_POLICY)
|
|
|
|
policies = conn.list_user_policies(UserName=user_name)
|
|
assert len(policies["PolicyNames"]) == 1
|
|
assert policies["PolicyNames"][0] == policy_name
|
|
|
|
conn.delete_user_policy(UserName=user_name, PolicyName=policy_name)
|
|
|
|
policies = conn.list_user_policies(UserName=user_name)
|
|
assert len(policies["PolicyNames"]) == 0
|
|
|
|
|
|
@mock_iam
|
|
def test_create_login_profile_with_unknown_user():
|
|
conn = boto3.client("iam", region_name="us-east-1")
|
|
with pytest.raises(ClientError) as ex:
|
|
conn.create_login_profile(UserName="my-user", Password="my-pass")
|
|
err = ex.value.response["Error"]
|
|
assert err["Code"] == "NoSuchEntity"
|
|
assert err["Message"] == "The user with name my-user cannot be found."
|
|
|
|
|
|
@mock_iam
|
|
def test_delete_login_profile_with_unknown_user():
|
|
conn = boto3.client("iam", region_name="us-east-1")
|
|
with pytest.raises(ClientError) as ex:
|
|
conn.delete_login_profile(UserName="my-user")
|
|
err = ex.value.response["Error"]
|
|
assert err["Code"] == "NoSuchEntity"
|
|
assert err["Message"] == "The user with name my-user cannot be found."
|
|
|
|
|
|
@mock_iam
|
|
def test_delete_nonexistent_login_profile():
|
|
conn = boto3.client("iam", region_name="us-east-1")
|
|
conn.create_user(UserName="my-user")
|
|
with pytest.raises(ClientError) as ex:
|
|
conn.delete_login_profile(UserName="my-user")
|
|
err = ex.value.response["Error"]
|
|
assert err["Code"] == "NoSuchEntity"
|
|
assert err["Message"] == "Login profile for my-user not found"
|
|
|
|
|
|
@mock_iam
|
|
def test_delete_login_profile():
|
|
conn = boto3.client("iam", region_name="us-east-1")
|
|
conn.create_user(UserName="my-user")
|
|
conn.create_login_profile(UserName="my-user", Password="my-pass")
|
|
conn.delete_login_profile(UserName="my-user")
|
|
|
|
with pytest.raises(ClientError):
|
|
conn.get_login_profile(UserName="my-user")
|
|
|
|
|
|
@mock_iam
|
|
def test_create_access_key():
|
|
conn = boto3.client("iam", region_name="us-east-1")
|
|
with pytest.raises(ClientError):
|
|
conn.create_access_key(UserName="my-user")
|
|
conn.create_user(UserName="my-user")
|
|
access_key = conn.create_access_key(UserName="my-user")["AccessKey"]
|
|
assert 0 <= (utcnow() - access_key["CreateDate"].replace(tzinfo=None)).seconds < 10
|
|
assert len(access_key["AccessKeyId"]) == 20
|
|
assert len(access_key["SecretAccessKey"]) == 40
|
|
assert access_key["AccessKeyId"].startswith("AKIA")
|
|
conn = boto3.client(
|
|
"iam",
|
|
region_name="us-east-1",
|
|
aws_access_key_id=access_key["AccessKeyId"],
|
|
aws_secret_access_key=access_key["SecretAccessKey"],
|
|
)
|
|
access_key = conn.create_access_key()["AccessKey"]
|
|
assert 0 <= (utcnow() - access_key["CreateDate"].replace(tzinfo=None)).seconds < 10
|
|
assert len(access_key["AccessKeyId"]) == 20
|
|
assert len(access_key["SecretAccessKey"]) == 40
|
|
assert access_key["AccessKeyId"].startswith("AKIA")
|
|
|
|
|
|
@mock_iam
|
|
def test_limit_access_key_per_user():
|
|
conn = boto3.client("iam", region_name=DEFAULT_REGION_NAME)
|
|
user_name = "test-user"
|
|
conn.create_user(UserName=user_name)
|
|
|
|
conn.create_access_key(UserName=user_name)
|
|
conn.create_access_key(UserName=user_name)
|
|
with pytest.raises(ClientError) as ex:
|
|
conn.create_access_key(UserName=user_name)
|
|
|
|
err = ex.value.response["Error"]
|
|
assert err["Code"] == "LimitExceeded"
|
|
assert err["Message"] == "Cannot exceed quota for AccessKeysPerUser: 2"
|
|
|
|
|
|
@mock_iam
|
|
def test_list_access_keys():
|
|
conn = boto3.client("iam", region_name="us-east-1")
|
|
conn.create_user(UserName="my-user")
|
|
response = conn.list_access_keys(UserName="my-user")
|
|
assert response["AccessKeyMetadata"] == []
|
|
access_key = conn.create_access_key(UserName="my-user")["AccessKey"]
|
|
response = conn.list_access_keys(UserName="my-user")
|
|
assert sorted(response["AccessKeyMetadata"][0].keys()) == sorted(
|
|
["Status", "CreateDate", "UserName", "AccessKeyId"]
|
|
)
|
|
conn = boto3.client(
|
|
"iam",
|
|
region_name="us-east-1",
|
|
aws_access_key_id=access_key["AccessKeyId"],
|
|
aws_secret_access_key=access_key["SecretAccessKey"],
|
|
)
|
|
response = conn.list_access_keys()
|
|
assert sorted(response["AccessKeyMetadata"][0].keys()) == sorted(
|
|
["Status", "CreateDate", "UserName", "AccessKeyId"]
|
|
)
|
|
|
|
|
|
@mock_iam
|
|
def test_delete_access_key():
|
|
conn = boto3.client("iam", region_name="us-east-1")
|
|
conn.create_user(UserName="my-user")
|
|
key = conn.create_access_key(UserName="my-user")["AccessKey"]
|
|
conn.delete_access_key(AccessKeyId=key["AccessKeyId"], UserName="my-user")
|
|
key = conn.create_access_key(UserName="my-user")["AccessKey"]
|
|
conn.delete_access_key(AccessKeyId=key["AccessKeyId"])
|
|
|
|
|
|
@mock_iam()
|
|
def test_mfa_devices():
|
|
# Test enable device
|
|
conn = boto3.client("iam", region_name="us-east-1")
|
|
conn.create_user(UserName="my-user")
|
|
conn.enable_mfa_device(
|
|
UserName="my-user",
|
|
SerialNumber="123456789",
|
|
AuthenticationCode1="234567",
|
|
AuthenticationCode2="987654",
|
|
)
|
|
|
|
# Test list mfa devices
|
|
response = conn.list_mfa_devices(UserName="my-user")
|
|
device = response["MFADevices"][0]
|
|
assert device["SerialNumber"] == "123456789"
|
|
|
|
# Test deactivate mfa device
|
|
conn.deactivate_mfa_device(UserName="my-user", SerialNumber="123456789")
|
|
response = conn.list_mfa_devices(UserName="my-user")
|
|
assert len(response["MFADevices"]) == 0
|
|
|
|
|
|
@mock_iam
|
|
def test_create_virtual_mfa_device():
|
|
client = boto3.client("iam", region_name="us-east-1")
|
|
response = client.create_virtual_mfa_device(VirtualMFADeviceName="test-device")
|
|
device = response["VirtualMFADevice"]
|
|
|
|
assert device["SerialNumber"] == f"arn:aws:iam::{ACCOUNT_ID}:mfa/test-device"
|
|
device["Base32StringSeed"].decode("ascii")
|
|
assert device["QRCodePNG"] != ""
|
|
|
|
response = client.create_virtual_mfa_device(
|
|
Path="/", VirtualMFADeviceName="test-device-2"
|
|
)
|
|
device = response["VirtualMFADevice"]
|
|
|
|
assert device["SerialNumber"] == f"arn:aws:iam::{ACCOUNT_ID}:mfa/test-device-2"
|
|
device["Base32StringSeed"].decode("ascii")
|
|
assert device["QRCodePNG"] != ""
|
|
|
|
response = client.create_virtual_mfa_device(
|
|
Path="/test/", VirtualMFADeviceName="test-device"
|
|
)
|
|
device = response["VirtualMFADevice"]
|
|
|
|
assert device["SerialNumber"] == f"arn:aws:iam::{ACCOUNT_ID}:mfa/test/test-device"
|
|
device["Base32StringSeed"].decode("ascii")
|
|
assert device["QRCodePNG"] != ""
|
|
assert isinstance(device["QRCodePNG"], bytes)
|
|
|
|
|
|
@mock_iam
|
|
def test_create_virtual_mfa_device_errors():
|
|
client = boto3.client("iam", region_name="us-east-1")
|
|
client.create_virtual_mfa_device(VirtualMFADeviceName="test-device")
|
|
|
|
with pytest.raises(ClientError) as exc:
|
|
client.create_virtual_mfa_device(VirtualMFADeviceName="test-device")
|
|
err = exc.value.response["Error"]
|
|
assert (
|
|
err["Message"] == "MFADevice entity at the same path and name already exists."
|
|
)
|
|
|
|
with pytest.raises(ClientError) as exc:
|
|
client.create_virtual_mfa_device(
|
|
Path="test", VirtualMFADeviceName="test-device"
|
|
)
|
|
err = exc.value.response["Error"]
|
|
assert (
|
|
err["Message"]
|
|
== "The specified value for path is invalid. It must begin and end with / and contain only alphanumeric characters and/or / characters."
|
|
)
|
|
|
|
with pytest.raises(ClientError) as exc:
|
|
client.create_virtual_mfa_device(
|
|
Path="/test//test/", VirtualMFADeviceName="test-device"
|
|
)
|
|
err = exc.value.response["Error"]
|
|
assert (
|
|
err["Message"]
|
|
== "The specified value for path is invalid. It must begin and end with / and contain only alphanumeric characters and/or / characters."
|
|
)
|
|
|
|
too_long_path = f"/{('b' * 511)}/"
|
|
with pytest.raises(ClientError) as exc:
|
|
client.create_virtual_mfa_device(
|
|
Path=too_long_path, VirtualMFADeviceName="test-device"
|
|
)
|
|
err = exc.value.response["Error"]
|
|
assert (
|
|
err["Message"]
|
|
== '1 validation error detected: Value "{}" at "path" failed to satisfy constraint: Member must have length less than or equal to 512'
|
|
)
|
|
|
|
|
|
@mock_iam
|
|
def test_delete_virtual_mfa_device():
|
|
client = boto3.client("iam", region_name="us-east-1")
|
|
response = client.create_virtual_mfa_device(VirtualMFADeviceName="test-device")
|
|
serial_number = response["VirtualMFADevice"]["SerialNumber"]
|
|
|
|
client.delete_virtual_mfa_device(SerialNumber=serial_number)
|
|
|
|
response = client.list_virtual_mfa_devices()
|
|
|
|
assert len(response["VirtualMFADevices"]) == 0
|
|
assert response["IsTruncated"] is False
|
|
|
|
|
|
@mock_iam
|
|
def test_delete_virtual_mfa_device_errors():
|
|
client = boto3.client("iam", region_name="us-east-1")
|
|
|
|
serial_number = f"arn:aws:iam::{ACCOUNT_ID}:mfa/not-existing"
|
|
with pytest.raises(ClientError) as exc:
|
|
client.delete_virtual_mfa_device(SerialNumber=serial_number)
|
|
err = exc.value.response["Error"]
|
|
assert (
|
|
err["Message"]
|
|
== f"VirtualMFADevice with serial number {serial_number} doesn't exist."
|
|
)
|
|
|
|
|
|
@mock_iam
|
|
def test_list_virtual_mfa_devices():
|
|
client = boto3.client("iam", region_name="us-east-1")
|
|
response = client.create_virtual_mfa_device(VirtualMFADeviceName="test-device")
|
|
serial_number_1 = response["VirtualMFADevice"]["SerialNumber"]
|
|
|
|
response = client.create_virtual_mfa_device(
|
|
Path="/test/", VirtualMFADeviceName="test-device"
|
|
)
|
|
serial_number_2 = response["VirtualMFADevice"]["SerialNumber"]
|
|
|
|
response = client.list_virtual_mfa_devices()
|
|
|
|
assert response["VirtualMFADevices"] == [
|
|
{"SerialNumber": serial_number_1},
|
|
{"SerialNumber": serial_number_2},
|
|
]
|
|
assert response["IsTruncated"] is False
|
|
|
|
response = client.list_virtual_mfa_devices(AssignmentStatus="Assigned")
|
|
|
|
assert len(response["VirtualMFADevices"]) == 0
|
|
assert response["IsTruncated"] is False
|
|
|
|
response = client.list_virtual_mfa_devices(AssignmentStatus="Unassigned")
|
|
|
|
assert response["VirtualMFADevices"] == [
|
|
{"SerialNumber": serial_number_1},
|
|
{"SerialNumber": serial_number_2},
|
|
]
|
|
assert response["IsTruncated"] is False
|
|
|
|
response = client.list_virtual_mfa_devices(AssignmentStatus="Any", MaxItems=1)
|
|
|
|
assert response["VirtualMFADevices"] == [{"SerialNumber": serial_number_1}]
|
|
assert response["IsTruncated"] is True
|
|
assert response["Marker"] == "1"
|
|
|
|
response = client.list_virtual_mfa_devices(
|
|
AssignmentStatus="Any", Marker=response["Marker"]
|
|
)
|
|
|
|
assert response["VirtualMFADevices"] == [{"SerialNumber": serial_number_2}]
|
|
assert response["IsTruncated"] is False
|
|
|
|
|
|
@mock_iam
|
|
def test_list_virtual_mfa_devices_errors():
|
|
client = boto3.client("iam", region_name="us-east-1")
|
|
client.create_virtual_mfa_device(VirtualMFADeviceName="test-device")
|
|
|
|
with pytest.raises(ClientError) as exc:
|
|
client.list_virtual_mfa_devices(Marker="100")
|
|
err = exc.value.response["Error"]
|
|
assert err["Message"] == "Invalid Marker."
|
|
|
|
|
|
@mock_iam
|
|
def test_enable_virtual_mfa_device():
|
|
client = boto3.client("iam", region_name="us-east-1")
|
|
response = client.create_virtual_mfa_device(VirtualMFADeviceName="test-device")
|
|
serial_number = response["VirtualMFADevice"]["SerialNumber"]
|
|
tags = [{"Key": "key", "Value": "value"}]
|
|
|
|
client.create_user(UserName="test-user", Tags=tags)
|
|
client.enable_mfa_device(
|
|
UserName="test-user",
|
|
SerialNumber=serial_number,
|
|
AuthenticationCode1="234567",
|
|
AuthenticationCode2="987654",
|
|
)
|
|
|
|
response = client.list_virtual_mfa_devices(AssignmentStatus="Unassigned")
|
|
|
|
assert len(response["VirtualMFADevices"]) == 0
|
|
assert response["IsTruncated"] is False
|
|
|
|
response = client.list_virtual_mfa_devices(AssignmentStatus="Assigned")
|
|
|
|
device = response["VirtualMFADevices"][0]
|
|
assert device["SerialNumber"] == serial_number
|
|
assert device["User"]["Path"] == "/"
|
|
assert device["User"]["UserName"] == "test-user"
|
|
assert device["User"]["Arn"] == f"arn:aws:iam::{ACCOUNT_ID}:user/test-user"
|
|
assert isinstance(device["User"]["CreateDate"], datetime)
|
|
assert device["User"]["Tags"] == tags
|
|
assert isinstance(device["EnableDate"], datetime)
|
|
assert response["IsTruncated"] is False
|
|
|
|
client.deactivate_mfa_device(UserName="test-user", SerialNumber=serial_number)
|
|
|
|
response = client.list_virtual_mfa_devices(AssignmentStatus="Assigned")
|
|
|
|
assert len(response["VirtualMFADevices"]) == 0
|
|
assert response["IsTruncated"] is False
|
|
|
|
response = client.list_virtual_mfa_devices(AssignmentStatus="Unassigned")
|
|
|
|
assert response["VirtualMFADevices"] == [{"SerialNumber": serial_number}]
|
|
assert response["IsTruncated"] is False
|
|
|
|
|
|
@mock_iam()
|
|
def test_delete_user():
|
|
conn = boto3.client("iam", region_name="us-east-1")
|
|
with pytest.raises(conn.exceptions.NoSuchEntityException):
|
|
conn.delete_user(UserName="my-user")
|
|
|
|
# Test deletion failure with a managed policy
|
|
conn.create_user(UserName="my-user")
|
|
response = conn.create_policy(
|
|
PolicyName="my-managed-policy", PolicyDocument=MOCK_POLICY
|
|
)
|
|
conn.attach_user_policy(PolicyArn=response["Policy"]["Arn"], UserName="my-user")
|
|
with pytest.raises(conn.exceptions.DeleteConflictException):
|
|
conn.delete_user(UserName="my-user")
|
|
conn.detach_user_policy(PolicyArn=response["Policy"]["Arn"], UserName="my-user")
|
|
conn.delete_policy(PolicyArn=response["Policy"]["Arn"])
|
|
conn.delete_user(UserName="my-user")
|
|
with pytest.raises(conn.exceptions.NoSuchEntityException):
|
|
conn.get_user(UserName="my-user")
|
|
|
|
# Test deletion failure with an inline policy
|
|
conn.create_user(UserName="my-user")
|
|
conn.put_user_policy(
|
|
UserName="my-user", PolicyName="my-user-policy", PolicyDocument=MOCK_POLICY
|
|
)
|
|
with pytest.raises(conn.exceptions.DeleteConflictException):
|
|
conn.delete_user(UserName="my-user")
|
|
conn.delete_user_policy(UserName="my-user", PolicyName="my-user-policy")
|
|
conn.delete_user(UserName="my-user")
|
|
with pytest.raises(conn.exceptions.NoSuchEntityException):
|
|
conn.get_user(UserName="my-user")
|
|
|
|
# Test deletion with no conflicts
|
|
conn.create_user(UserName="my-user")
|
|
conn.delete_user(UserName="my-user")
|
|
with pytest.raises(conn.exceptions.NoSuchEntityException):
|
|
conn.get_user(UserName="my-user")
|
|
|
|
|
|
@mock_iam
|
|
def test_generate_credential_report():
|
|
conn = boto3.client("iam", region_name="us-east-1")
|
|
result = conn.generate_credential_report()
|
|
assert result["State"] == "STARTED"
|
|
result = conn.generate_credential_report()
|
|
assert result["State"] == "COMPLETE"
|
|
|
|
|
|
@mock_iam
|
|
def test_get_credential_report():
|
|
conn = boto3.client("iam", region_name="us-east-1")
|
|
conn.create_user(UserName="my-user")
|
|
with pytest.raises(ClientError):
|
|
conn.get_credential_report()
|
|
result = conn.generate_credential_report()
|
|
while result["State"] != "COMPLETE":
|
|
result = conn.generate_credential_report()
|
|
result = conn.get_credential_report()
|
|
report = result["Content"].decode("utf-8")
|
|
assert "my-user" in report
|
|
|
|
|
|
@mock_iam
|
|
def test_get_credential_report_content():
|
|
conn = boto3.client("iam", region_name="us-east-1")
|
|
username = "my-user"
|
|
conn.create_user(UserName=username)
|
|
conn.create_login_profile(UserName=username, Password="123")
|
|
key1 = conn.create_access_key(UserName=username)["AccessKey"]
|
|
conn.update_access_key(
|
|
UserName=username, AccessKeyId=key1["AccessKeyId"], Status="Inactive"
|
|
)
|
|
key1 = conn.create_access_key(UserName=username)["AccessKey"]
|
|
timestamp = utcnow()
|
|
if not settings.TEST_SERVER_MODE:
|
|
iam_backend = get_backend("iam")[ACCOUNT_ID]["global"]
|
|
iam_backend.users[username].access_keys[1].last_used = timestamp
|
|
iam_backend.users[username].password_last_used = timestamp
|
|
with pytest.raises(ClientError):
|
|
conn.get_credential_report()
|
|
result = conn.generate_credential_report()
|
|
while result["State"] != "COMPLETE":
|
|
result = conn.generate_credential_report()
|
|
result = conn.get_credential_report()
|
|
report = result["Content"].decode("utf-8")
|
|
header = report.split("\n")[0]
|
|
assert (
|
|
header
|
|
== "user,arn,user_creation_time,password_enabled,password_last_used,password_last_changed,password_next_rotation,mfa_active,access_key_1_active,access_key_1_last_rotated,access_key_1_last_used_date,access_key_1_last_used_region,access_key_1_last_used_service,access_key_2_active,access_key_2_last_rotated,access_key_2_last_used_date,access_key_2_last_used_region,access_key_2_last_used_service,cert_1_active,cert_1_last_rotated,cert_2_active,cert_2_last_rotated"
|
|
)
|
|
report_dict = csv.DictReader(report.split("\n"))
|
|
user = next(report_dict)
|
|
assert user["user"] == "my-user"
|
|
assert user["access_key_1_active"] == "false"
|
|
assert timestamp.strftime("%Y-%m-%d") in user["access_key_1_last_rotated"]
|
|
assert user["access_key_1_last_used_date"] == "N/A"
|
|
assert user["access_key_2_active"] == "true"
|
|
if not settings.TEST_SERVER_MODE:
|
|
assert timestamp.strftime("%Y-%m-%d") in user["access_key_2_last_used_date"]
|
|
assert timestamp.strftime("%Y-%m-%d") in user["password_last_used"]
|
|
else:
|
|
assert user["access_key_2_last_used_date"] == "N/A"
|
|
assert user["password_last_used"] == "no_information"
|
|
|
|
|
|
@mock_iam
|
|
def test_get_access_key_last_used_when_used():
|
|
iam = boto3.resource("iam", region_name="us-east-1")
|
|
client = iam.meta.client
|
|
username = "test-user"
|
|
iam.create_user(UserName=username)
|
|
with pytest.raises(ClientError):
|
|
client.get_access_key_last_used(AccessKeyId="non-existent-key-id")
|
|
create_key_response = client.create_access_key(UserName=username)["AccessKey"]
|
|
|
|
access_key_client = boto3.client(
|
|
"iam",
|
|
region_name="us-east-1",
|
|
aws_access_key_id=create_key_response["AccessKeyId"],
|
|
aws_secret_access_key=create_key_response["SecretAccessKey"],
|
|
)
|
|
access_key_client.list_users()
|
|
|
|
resp = client.get_access_key_last_used(
|
|
AccessKeyId=create_key_response["AccessKeyId"]
|
|
)
|
|
assert "LastUsedDate" in resp["AccessKeyLastUsed"]
|
|
assert resp["AccessKeyLastUsed"]["ServiceName"] == "iam"
|
|
assert resp["AccessKeyLastUsed"]["Region"] == "us-east-1"
|
|
|
|
|
|
@mock_iam
|
|
def test_managed_policy():
|
|
conn = boto3.client("iam", region_name="us-west-1")
|
|
|
|
conn.create_policy(
|
|
PolicyName="UserManagedPolicy",
|
|
PolicyDocument=MOCK_POLICY,
|
|
Path="/mypolicy/",
|
|
Description="my user managed policy",
|
|
)
|
|
|
|
marker = "0"
|
|
aws_policies = []
|
|
while marker is not None:
|
|
response = conn.list_policies(Scope="AWS", Marker=marker)
|
|
for policy in response["Policies"]:
|
|
aws_policies.append(policy)
|
|
marker = response.get("Marker")
|
|
aws_managed_policies = iam_backends[ACCOUNT_ID]["global"].aws_managed_policies
|
|
assert set(p.name for p in aws_managed_policies) == set(
|
|
p["PolicyName"] for p in aws_policies
|
|
)
|
|
|
|
user_policies = conn.list_policies(Scope="Local")["Policies"]
|
|
assert set(["UserManagedPolicy"]) == set(p["PolicyName"] for p in user_policies)
|
|
|
|
marker = "0"
|
|
all_policies = []
|
|
while marker is not None:
|
|
response = conn.list_policies(Marker=marker)
|
|
for policy in response["Policies"]:
|
|
all_policies.append(policy)
|
|
marker = response.get("Marker")
|
|
assert set(p["PolicyName"] for p in aws_policies + user_policies) == set(
|
|
p["PolicyName"] for p in all_policies
|
|
)
|
|
|
|
role_name = "my-new-role"
|
|
conn.create_role(
|
|
RoleName=role_name, AssumeRolePolicyDocument="test policy", Path="my-path"
|
|
)
|
|
for policy_name in [
|
|
"AmazonElasticMapReduceRole",
|
|
"AWSControlTowerServiceRolePolicy",
|
|
]:
|
|
policy_arn = "arn:aws:iam::aws:policy/service-role/" + policy_name
|
|
conn.attach_role_policy(PolicyArn=policy_arn, RoleName=role_name)
|
|
|
|
rows = conn.list_policies(OnlyAttached=True)["Policies"]
|
|
assert len(rows) == 2
|
|
for x in rows:
|
|
assert x["AttachmentCount"] > 0
|
|
|
|
resp = conn.list_attached_role_policies(RoleName=role_name)
|
|
assert len(resp["AttachedPolicies"]) == 2
|
|
|
|
conn.detach_role_policy(
|
|
PolicyArn="arn:aws:iam::aws:policy/service-role/AmazonElasticMapReduceRole",
|
|
RoleName=role_name,
|
|
)
|
|
rows = conn.list_policies(OnlyAttached=True)["Policies"]
|
|
assert "AWSControlTowerServiceRolePolicy" in [r["PolicyName"] for r in rows]
|
|
assert "AmazonElasticMapReduceRole" not in [r["PolicyName"] for r in rows]
|
|
for x in rows:
|
|
assert x["AttachmentCount"] > 0
|
|
|
|
policies = conn.list_attached_role_policies(RoleName=role_name)["AttachedPolicies"]
|
|
assert "AWSControlTowerServiceRolePolicy" in [p["PolicyName"] for p in policies]
|
|
assert "AmazonElasticMapReduceRole" not in [p["PolicyName"] for p in policies]
|
|
|
|
with pytest.raises(ClientError) as ex:
|
|
conn.detach_role_policy(
|
|
PolicyArn="arn:aws:iam::aws:policy/service-role/AmazonElasticMapReduceRole",
|
|
RoleName=role_name,
|
|
)
|
|
err = ex.value.response["Error"]
|
|
assert err["Code"] == "NoSuchEntity"
|
|
assert (
|
|
err["Message"]
|
|
== "Policy arn:aws:iam::aws:policy/service-role/AmazonElasticMapReduceRole was not found."
|
|
)
|
|
|
|
with pytest.raises(ClientError) as ex:
|
|
conn.detach_role_policy(
|
|
PolicyArn="arn:aws:iam::aws:policy/Nonexistent", RoleName=role_name
|
|
)
|
|
err = ex.value.response["Error"]
|
|
assert err["Code"] == "NoSuchEntity"
|
|
assert err["Message"] == "Policy arn:aws:iam::aws:policy/Nonexistent was not found."
|
|
|
|
|
|
@mock_iam
|
|
def test_create_login_profile__duplicate():
|
|
conn = boto3.client("iam", region_name="us-east-1")
|
|
|
|
conn.create_user(UserName="my-user")
|
|
conn.create_login_profile(UserName="my-user", Password="Password")
|
|
|
|
with pytest.raises(ClientError) as exc:
|
|
conn.create_login_profile(UserName="my-user", Password="my-pass")
|
|
err = exc.value.response["Error"]
|
|
assert err["Code"] == "User my-user already has password"
|
|
assert err["Message"] is None
|
|
|
|
|
|
@mock_iam()
|
|
def test_attach_detach_user_policy():
|
|
iam = boto3.resource("iam", region_name="us-east-1")
|
|
client = boto3.client("iam", region_name="us-east-1")
|
|
|
|
user = iam.create_user(UserName="test-user")
|
|
|
|
policy_name = "UserAttachedPolicy"
|
|
policy = iam.create_policy(
|
|
PolicyName=policy_name,
|
|
PolicyDocument=MOCK_POLICY,
|
|
Path="/mypolicy/",
|
|
Description="my user attached policy",
|
|
)
|
|
|
|
# try a non-existent policy
|
|
non_existent_policy_arn = f"arn:aws:iam::{ACCOUNT_ID}:policy/not-existent"
|
|
with pytest.raises(ClientError) as exc:
|
|
client.attach_user_policy(UserName=user.name, PolicyArn=non_existent_policy_arn)
|
|
err = exc.value.response["Error"]
|
|
assert err["Code"] == "NoSuchEntity"
|
|
assert (
|
|
err["Message"]
|
|
== f"Policy {non_existent_policy_arn} does not exist or is not attachable."
|
|
)
|
|
|
|
client.attach_user_policy(UserName=user.name, PolicyArn=policy.arn)
|
|
|
|
resp = client.list_attached_user_policies(UserName=user.name)
|
|
assert len(resp["AttachedPolicies"]) == 1
|
|
attached_policy = resp["AttachedPolicies"][0]
|
|
assert attached_policy["PolicyArn"] == policy.arn
|
|
assert attached_policy["PolicyName"] == policy_name
|
|
|
|
client.detach_user_policy(UserName=user.name, PolicyArn=policy.arn)
|
|
|
|
resp = client.list_attached_user_policies(UserName=user.name)
|
|
assert len(resp["AttachedPolicies"]) == 0
|
|
|
|
|
|
@mock_iam()
|
|
def test_attach_detach_role_policy():
|
|
iam = boto3.resource("iam", region_name="us-east-1")
|
|
client = boto3.client("iam", region_name="us-east-1")
|
|
|
|
role = iam.create_role(RoleName="test-role", AssumeRolePolicyDocument="{}")
|
|
|
|
policy_name = "RoleAttachedPolicy"
|
|
policy = iam.create_policy(
|
|
PolicyName=policy_name,
|
|
PolicyDocument=MOCK_POLICY,
|
|
Path="/mypolicy/",
|
|
Description="my role attached policy",
|
|
)
|
|
|
|
# try a non-existent policy
|
|
non_existent_policy_arn = f"arn:aws:iam::{ACCOUNT_ID}:policy/not-existent"
|
|
with pytest.raises(ClientError) as exc:
|
|
client.attach_role_policy(RoleName=role.name, PolicyArn=non_existent_policy_arn)
|
|
err = exc.value.response["Error"]
|
|
assert err["Code"] == "NoSuchEntity"
|
|
assert (
|
|
err["Message"]
|
|
== f"Policy {non_existent_policy_arn} does not exist or is not attachable."
|
|
)
|
|
|
|
client.attach_role_policy(RoleName=role.name, PolicyArn=policy.arn)
|
|
|
|
resp = client.list_attached_role_policies(RoleName=role.name)
|
|
assert len(resp["AttachedPolicies"]) == 1
|
|
attached_policy = resp["AttachedPolicies"][0]
|
|
assert attached_policy["PolicyArn"] == policy.arn
|
|
assert attached_policy["PolicyName"] == policy_name
|
|
|
|
client.detach_role_policy(RoleName=role.name, PolicyArn=policy.arn)
|
|
|
|
resp = client.list_attached_role_policies(RoleName=role.name)
|
|
assert len(resp["AttachedPolicies"]) == 0
|
|
|
|
|
|
@mock_iam()
|
|
def test_only_detach_user_policy():
|
|
iam = boto3.resource("iam", region_name="us-east-1")
|
|
client = boto3.client("iam", region_name="us-east-1")
|
|
|
|
user = iam.create_user(UserName="test-user")
|
|
|
|
policy_name = "FreePolicy"
|
|
policy = iam.create_policy(
|
|
PolicyName=policy_name,
|
|
PolicyDocument=MOCK_POLICY,
|
|
Path="/mypolicy/",
|
|
Description="free floating policy",
|
|
)
|
|
|
|
resp = client.list_attached_user_policies(UserName=user.name)
|
|
assert len(resp["AttachedPolicies"]) == 0
|
|
|
|
with pytest.raises(ClientError) as exc:
|
|
client.detach_user_policy(UserName=user.name, PolicyArn=policy.arn)
|
|
err = exc.value.response["Error"]
|
|
assert err["Code"] == "NoSuchEntity"
|
|
assert err["Message"] == f"Policy {policy.arn} was not found."
|
|
|
|
|
|
@mock_iam()
|
|
def test_only_detach_group_policy():
|
|
iam = boto3.resource("iam", region_name="us-east-1")
|
|
client = boto3.client("iam", region_name="us-east-1")
|
|
|
|
group = iam.create_group(GroupName="test-group")
|
|
|
|
policy_name = "FreePolicy"
|
|
policy = iam.create_policy(
|
|
PolicyName=policy_name,
|
|
PolicyDocument=MOCK_POLICY,
|
|
Path="/mypolicy/",
|
|
Description="free floating policy",
|
|
)
|
|
|
|
resp = client.list_attached_group_policies(GroupName=group.name)
|
|
assert len(resp["AttachedPolicies"]) == 0
|
|
|
|
with pytest.raises(ClientError) as exc:
|
|
client.detach_group_policy(GroupName=group.name, PolicyArn=policy.arn)
|
|
err = exc.value.response["Error"]
|
|
assert err["Code"] == "NoSuchEntity"
|
|
assert err["Message"] == f"Policy {policy.arn} was not found."
|
|
|
|
|
|
@mock_iam()
|
|
def test_only_detach_role_policy():
|
|
iam = boto3.resource("iam", region_name="us-east-1")
|
|
client = boto3.client("iam", region_name="us-east-1")
|
|
|
|
role = iam.create_role(RoleName="test-role", AssumeRolePolicyDocument="{}")
|
|
|
|
policy_name = "FreePolicy"
|
|
policy = iam.create_policy(
|
|
PolicyName=policy_name,
|
|
PolicyDocument=MOCK_POLICY,
|
|
Path="/mypolicy/",
|
|
Description="free floating policy",
|
|
)
|
|
|
|
resp = client.list_attached_role_policies(RoleName=role.name)
|
|
assert len(resp["AttachedPolicies"]) == 0
|
|
|
|
with pytest.raises(ClientError) as exc:
|
|
client.detach_role_policy(RoleName=role.name, PolicyArn=policy.arn)
|
|
err = exc.value.response["Error"]
|
|
assert err["Code"] == "NoSuchEntity"
|
|
assert err["Message"] == f"Policy {policy.arn} was not found."
|
|
|
|
|
|
@mock_iam
|
|
def test_update_access_key():
|
|
iam = boto3.resource("iam", region_name="us-east-1")
|
|
client = iam.meta.client
|
|
username = "test-user"
|
|
iam.create_user(UserName=username)
|
|
with pytest.raises(ClientError):
|
|
client.update_access_key(
|
|
UserName=username, AccessKeyId="non-existent-key", Status="Inactive"
|
|
)
|
|
key = client.create_access_key(UserName=username)["AccessKey"]
|
|
client.update_access_key(
|
|
UserName=username, AccessKeyId=key["AccessKeyId"], Status="Inactive"
|
|
)
|
|
resp = client.list_access_keys(UserName=username)
|
|
assert resp["AccessKeyMetadata"][0]["Status"] == "Inactive"
|
|
client.update_access_key(AccessKeyId=key["AccessKeyId"], Status="Active")
|
|
resp = client.list_access_keys(UserName=username)
|
|
assert resp["AccessKeyMetadata"][0]["Status"] == "Active"
|
|
|
|
|
|
@mock_iam
|
|
def test_get_access_key_last_used_when_unused():
|
|
iam = boto3.resource("iam", region_name="us-east-1")
|
|
client = iam.meta.client
|
|
username = "test-user"
|
|
iam.create_user(UserName=username)
|
|
with pytest.raises(ClientError):
|
|
client.get_access_key_last_used(AccessKeyId="non-existent-key-id")
|
|
create_key_response = client.create_access_key(UserName=username)["AccessKey"]
|
|
resp = client.get_access_key_last_used(
|
|
AccessKeyId=create_key_response["AccessKeyId"]
|
|
)
|
|
assert "LastUsedDate" not in resp["AccessKeyLastUsed"]
|
|
assert resp["UserName"] == create_key_response["UserName"]
|
|
|
|
|
|
@mock_iam
|
|
def test_upload_ssh_public_key():
|
|
iam = boto3.resource("iam", region_name="us-east-1")
|
|
client = iam.meta.client
|
|
username = "test-user"
|
|
iam.create_user(UserName=username)
|
|
public_key = MOCK_CERT
|
|
|
|
resp = client.upload_ssh_public_key(UserName=username, SSHPublicKeyBody=public_key)
|
|
pubkey = resp["SSHPublicKey"]
|
|
assert pubkey["SSHPublicKeyBody"] == public_key
|
|
assert pubkey["UserName"] == username
|
|
assert len(pubkey["SSHPublicKeyId"]) == 20
|
|
assert pubkey["SSHPublicKeyId"].startswith("APKA")
|
|
assert "Fingerprint" in pubkey
|
|
assert pubkey["Status"] == "Active"
|
|
assert 0 <= ((utcnow() - pubkey["UploadDate"].replace(tzinfo=None)).seconds) < 10
|
|
|
|
|
|
@mock_iam
|
|
def test_get_ssh_public_key():
|
|
iam = boto3.resource("iam", region_name="us-east-1")
|
|
client = iam.meta.client
|
|
username = "test-user"
|
|
iam.create_user(UserName=username)
|
|
public_key = MOCK_CERT
|
|
|
|
with pytest.raises(ClientError):
|
|
client.get_ssh_public_key(
|
|
UserName=username, SSHPublicKeyId="xxnon-existent-keyxx", Encoding="SSH"
|
|
)
|
|
|
|
resp = client.upload_ssh_public_key(UserName=username, SSHPublicKeyBody=public_key)
|
|
ssh_public_key_id = resp["SSHPublicKey"]["SSHPublicKeyId"]
|
|
|
|
resp = client.get_ssh_public_key(
|
|
UserName=username, SSHPublicKeyId=ssh_public_key_id, Encoding="SSH"
|
|
)
|
|
assert resp["SSHPublicKey"]["SSHPublicKeyBody"] == public_key
|
|
|
|
|
|
@mock_iam
|
|
def test_list_ssh_public_keys():
|
|
iam = boto3.resource("iam", region_name="us-east-1")
|
|
client = iam.meta.client
|
|
username = "test-user"
|
|
iam.create_user(UserName=username)
|
|
public_key = MOCK_CERT
|
|
|
|
resp = client.list_ssh_public_keys(UserName=username)
|
|
assert len(resp["SSHPublicKeys"]) == 0
|
|
|
|
resp = client.upload_ssh_public_key(UserName=username, SSHPublicKeyBody=public_key)
|
|
ssh_public_key_id = resp["SSHPublicKey"]["SSHPublicKeyId"]
|
|
|
|
resp = client.list_ssh_public_keys(UserName=username)
|
|
assert len(resp["SSHPublicKeys"]) == 1
|
|
assert resp["SSHPublicKeys"][0]["SSHPublicKeyId"] == ssh_public_key_id
|
|
|
|
|
|
@mock_iam
|
|
def test_update_ssh_public_key():
|
|
iam = boto3.resource("iam", region_name="us-east-1")
|
|
client = iam.meta.client
|
|
username = "test-user"
|
|
iam.create_user(UserName=username)
|
|
public_key = MOCK_CERT
|
|
|
|
with pytest.raises(ClientError):
|
|
client.update_ssh_public_key(
|
|
UserName=username, SSHPublicKeyId="xxnon-existent-keyxx", Status="Inactive"
|
|
)
|
|
|
|
resp = client.upload_ssh_public_key(UserName=username, SSHPublicKeyBody=public_key)
|
|
ssh_public_key_id = resp["SSHPublicKey"]["SSHPublicKeyId"]
|
|
assert resp["SSHPublicKey"]["Status"] == "Active"
|
|
|
|
resp = client.update_ssh_public_key(
|
|
UserName=username, SSHPublicKeyId=ssh_public_key_id, Status="Inactive"
|
|
)
|
|
|
|
resp = client.get_ssh_public_key(
|
|
UserName=username, SSHPublicKeyId=ssh_public_key_id, Encoding="SSH"
|
|
)
|
|
assert resp["SSHPublicKey"]["Status"] == "Inactive"
|
|
|
|
|
|
@mock_iam
|
|
def test_delete_ssh_public_key():
|
|
iam = boto3.resource("iam", region_name="us-east-1")
|
|
client = iam.meta.client
|
|
username = "test-user"
|
|
iam.create_user(UserName=username)
|
|
public_key = MOCK_CERT
|
|
|
|
with pytest.raises(ClientError):
|
|
client.delete_ssh_public_key(
|
|
UserName=username, SSHPublicKeyId="xxnon-existent-keyxx"
|
|
)
|
|
|
|
resp = client.upload_ssh_public_key(UserName=username, SSHPublicKeyBody=public_key)
|
|
ssh_public_key_id = resp["SSHPublicKey"]["SSHPublicKeyId"]
|
|
|
|
resp = client.list_ssh_public_keys(UserName=username)
|
|
assert len(resp["SSHPublicKeys"]) == 1
|
|
|
|
resp = client.delete_ssh_public_key(
|
|
UserName=username, SSHPublicKeyId=ssh_public_key_id
|
|
)
|
|
|
|
resp = client.list_ssh_public_keys(UserName=username)
|
|
assert len(resp["SSHPublicKeys"]) == 0
|
|
|
|
|
|
@mock_iam
|
|
def test_get_account_authorization_details():
|
|
test_policy = json.dumps(
|
|
{
|
|
"Version": "2012-10-17",
|
|
"Statement": [
|
|
{"Action": "s3:ListBucket", "Resource": "*", "Effect": "Allow"}
|
|
],
|
|
}
|
|
)
|
|
|
|
conn = boto3.client("iam", region_name="us-east-1")
|
|
boundary = f"arn:aws:iam::{ACCOUNT_ID}:policy/boundary"
|
|
conn.create_role(
|
|
RoleName="my-role",
|
|
AssumeRolePolicyDocument="some policy",
|
|
Path="/my-path/",
|
|
Description="testing",
|
|
PermissionsBoundary=boundary,
|
|
)
|
|
conn.create_user(Path="/", UserName="testUser")
|
|
conn.create_group(Path="/", GroupName="testGroup")
|
|
conn.create_policy(
|
|
PolicyName="testPolicy",
|
|
Path="/",
|
|
PolicyDocument=test_policy,
|
|
Description="Test Policy",
|
|
)
|
|
|
|
# Attach things to the user and group:
|
|
conn.put_user_policy(
|
|
UserName="testUser", PolicyName="testPolicy", PolicyDocument=test_policy
|
|
)
|
|
conn.put_group_policy(
|
|
GroupName="testGroup", PolicyName="testPolicy", PolicyDocument=test_policy
|
|
)
|
|
|
|
conn.attach_user_policy(
|
|
UserName="testUser",
|
|
PolicyArn=f"arn:aws:iam::{ACCOUNT_ID}:policy/testPolicy",
|
|
)
|
|
conn.attach_group_policy(
|
|
GroupName="testGroup",
|
|
PolicyArn=f"arn:aws:iam::{ACCOUNT_ID}:policy/testPolicy",
|
|
)
|
|
|
|
conn.add_user_to_group(UserName="testUser", GroupName="testGroup")
|
|
|
|
# Add things to the role:
|
|
conn.create_instance_profile(InstanceProfileName="ipn")
|
|
conn.add_role_to_instance_profile(InstanceProfileName="ipn", RoleName="my-role")
|
|
conn.tag_role(
|
|
RoleName="my-role",
|
|
Tags=[
|
|
{"Key": "somekey", "Value": "somevalue"},
|
|
{"Key": "someotherkey", "Value": "someothervalue"},
|
|
],
|
|
)
|
|
conn.put_role_policy(
|
|
RoleName="my-role", PolicyName="test-policy", PolicyDocument=test_policy
|
|
)
|
|
conn.attach_role_policy(
|
|
RoleName="my-role",
|
|
PolicyArn=f"arn:aws:iam::{ACCOUNT_ID}:policy/testPolicy",
|
|
)
|
|
# add tags to the user
|
|
conn.tag_user(
|
|
UserName="testUser",
|
|
Tags=[
|
|
{"Key": "somekey", "Value": "somevalue"},
|
|
{"Key": "someotherkey", "Value": "someothervalue"},
|
|
],
|
|
)
|
|
|
|
result = conn.get_account_authorization_details(Filter=["Role"])
|
|
assert len(result["RoleDetailList"]) == 1
|
|
assert len(result["UserDetailList"]) == 0
|
|
assert len(result["GroupDetailList"]) == 0
|
|
assert len(result["Policies"]) == 0
|
|
assert len(result["RoleDetailList"][0]["InstanceProfileList"]) == 1
|
|
assert (
|
|
result["RoleDetailList"][0]["InstanceProfileList"][0]["Roles"][0]["Description"]
|
|
== "testing"
|
|
)
|
|
assert result["RoleDetailList"][0]["InstanceProfileList"][0]["Roles"][0][
|
|
"PermissionsBoundary"
|
|
] == {
|
|
"PermissionsBoundaryType": "PermissionsBoundaryPolicy",
|
|
"PermissionsBoundaryArn": f"arn:aws:iam::{ACCOUNT_ID}:policy/boundary",
|
|
}
|
|
assert len(result["RoleDetailList"][0]["Tags"]) == 2
|
|
assert len(result["RoleDetailList"][0]["RolePolicyList"]) == 1
|
|
assert len(result["RoleDetailList"][0]["AttachedManagedPolicies"]) == 1
|
|
assert (
|
|
result["RoleDetailList"][0]["AttachedManagedPolicies"][0]["PolicyName"]
|
|
== "testPolicy"
|
|
)
|
|
assert (
|
|
result["RoleDetailList"][0]["AttachedManagedPolicies"][0]["PolicyArn"]
|
|
== f"arn:aws:iam::{ACCOUNT_ID}:policy/testPolicy"
|
|
)
|
|
assert result["RoleDetailList"][0]["RolePolicyList"][0][
|
|
"PolicyDocument"
|
|
] == json.loads(test_policy)
|
|
|
|
result = conn.get_account_authorization_details(Filter=["User"])
|
|
assert len(result["RoleDetailList"]) == 0
|
|
assert len(result["UserDetailList"]) == 1
|
|
assert len(result["UserDetailList"][0]["GroupList"]) == 1
|
|
assert len(result["UserDetailList"][0]["UserPolicyList"]) == 1
|
|
assert len(result["UserDetailList"][0]["AttachedManagedPolicies"]) == 1
|
|
assert len(result["UserDetailList"][0]["Tags"]) == 2
|
|
assert len(result["GroupDetailList"]) == 0
|
|
assert len(result["Policies"]) == 0
|
|
assert (
|
|
result["UserDetailList"][0]["AttachedManagedPolicies"][0]["PolicyName"]
|
|
== "testPolicy"
|
|
)
|
|
assert (
|
|
result["UserDetailList"][0]["AttachedManagedPolicies"][0]["PolicyArn"]
|
|
== f"arn:aws:iam::{ACCOUNT_ID}:policy/testPolicy"
|
|
)
|
|
assert result["UserDetailList"][0]["UserPolicyList"][0][
|
|
"PolicyDocument"
|
|
] == json.loads(test_policy)
|
|
|
|
result = conn.get_account_authorization_details(Filter=["Group"])
|
|
assert len(result["RoleDetailList"]) == 0
|
|
assert len(result["UserDetailList"]) == 0
|
|
assert len(result["GroupDetailList"]) == 1
|
|
assert len(result["GroupDetailList"][0]["GroupPolicyList"]) == 1
|
|
assert len(result["GroupDetailList"][0]["AttachedManagedPolicies"]) == 1
|
|
assert len(result["Policies"]) == 0
|
|
assert (
|
|
result["GroupDetailList"][0]["AttachedManagedPolicies"][0]["PolicyName"]
|
|
== "testPolicy"
|
|
)
|
|
assert (
|
|
result["GroupDetailList"][0]["AttachedManagedPolicies"][0]["PolicyArn"]
|
|
== f"arn:aws:iam::{ACCOUNT_ID}:policy/testPolicy"
|
|
)
|
|
assert result["GroupDetailList"][0]["GroupPolicyList"][0][
|
|
"PolicyDocument"
|
|
] == json.loads(test_policy)
|
|
|
|
result = conn.get_account_authorization_details(Filter=["LocalManagedPolicy"])
|
|
assert len(result["RoleDetailList"]) == 0
|
|
assert len(result["UserDetailList"]) == 0
|
|
assert len(result["GroupDetailList"]) == 0
|
|
assert len(result["Policies"]) == 1
|
|
assert len(result["Policies"][0]["PolicyVersionList"]) == 1
|
|
|
|
# Check for greater than 1 since this should always be greater than one but might change.
|
|
# See iam/aws_managed_policies.py
|
|
result = conn.get_account_authorization_details(Filter=["AWSManagedPolicy"])
|
|
assert len(result["RoleDetailList"]) == 0
|
|
assert len(result["UserDetailList"]) == 0
|
|
assert len(result["GroupDetailList"]) == 0
|
|
assert len(result["Policies"]) > 1
|
|
|
|
result = conn.get_account_authorization_details()
|
|
assert len(result["RoleDetailList"]) == 1
|
|
assert len(result["UserDetailList"]) == 1
|
|
assert len(result["GroupDetailList"]) == 1
|
|
assert len(result["Policies"]) > 1
|
|
|
|
|
|
@mock_iam
|
|
def test_signing_certs():
|
|
client = boto3.client("iam", region_name="us-east-1")
|
|
|
|
# Create the IAM user first:
|
|
client.create_user(UserName="testing")
|
|
|
|
# Upload the cert:
|
|
resp = client.upload_signing_certificate(
|
|
UserName="testing", CertificateBody=MOCK_CERT
|
|
)["Certificate"]
|
|
cert_id = resp["CertificateId"]
|
|
|
|
assert resp["UserName"] == "testing"
|
|
assert resp["Status"] == "Active"
|
|
assert resp["CertificateBody"] == MOCK_CERT
|
|
assert resp["CertificateId"]
|
|
|
|
# Upload a the cert with an invalid body:
|
|
with pytest.raises(ClientError) as ce:
|
|
client.upload_signing_certificate(
|
|
UserName="testing", CertificateBody="notacert"
|
|
)
|
|
assert ce.value.response["Error"]["Code"] == "MalformedCertificate"
|
|
|
|
# Upload with an invalid user:
|
|
with pytest.raises(ClientError):
|
|
client.upload_signing_certificate(
|
|
UserName="notauser", CertificateBody=MOCK_CERT
|
|
)
|
|
|
|
# Update:
|
|
client.update_signing_certificate(
|
|
UserName="testing", CertificateId=cert_id, Status="Inactive"
|
|
)
|
|
|
|
with pytest.raises(ClientError):
|
|
client.update_signing_certificate(
|
|
UserName="notauser", CertificateId=cert_id, Status="Inactive"
|
|
)
|
|
|
|
fake_id_name = "x" * 32
|
|
with pytest.raises(ClientError) as ce:
|
|
client.update_signing_certificate(
|
|
UserName="testing", CertificateId=fake_id_name, Status="Inactive"
|
|
)
|
|
|
|
assert (
|
|
ce.value.response["Error"]["Message"]
|
|
== f"The Certificate with id {fake_id_name} cannot be found."
|
|
)
|
|
|
|
# List the certs:
|
|
resp = client.list_signing_certificates(UserName="testing")["Certificates"]
|
|
assert len(resp) == 1
|
|
assert resp[0]["CertificateBody"] == MOCK_CERT
|
|
assert resp[0]["Status"] == "Inactive" # Changed with the update call above.
|
|
|
|
with pytest.raises(ClientError):
|
|
client.list_signing_certificates(UserName="notauser")
|
|
|
|
# Delete:
|
|
client.delete_signing_certificate(UserName="testing", CertificateId=cert_id)
|
|
|
|
with pytest.raises(ClientError):
|
|
client.delete_signing_certificate(UserName="notauser", CertificateId=cert_id)
|
|
|
|
|
|
@mock_iam()
|
|
def test_create_saml_provider():
|
|
conn = boto3.client("iam", region_name="us-east-1")
|
|
response = conn.create_saml_provider(
|
|
Name="TestSAMLProvider", SAMLMetadataDocument="a" * 1024
|
|
)
|
|
assert (
|
|
response["SAMLProviderArn"]
|
|
== f"arn:aws:iam::{ACCOUNT_ID}:saml-provider/TestSAMLProvider"
|
|
)
|
|
|
|
|
|
@mock_iam()
|
|
def test_get_saml_provider():
|
|
conn = boto3.client("iam", region_name="us-east-1")
|
|
saml_provider_create = conn.create_saml_provider(
|
|
Name="TestSAMLProvider", SAMLMetadataDocument="a" * 1024
|
|
)
|
|
response = conn.get_saml_provider(
|
|
SAMLProviderArn=saml_provider_create["SAMLProviderArn"]
|
|
)
|
|
assert response["SAMLMetadataDocument"] == "a" * 1024
|
|
|
|
|
|
@mock_iam()
|
|
def test_list_saml_providers():
|
|
conn = boto3.client("iam", region_name="us-east-1")
|
|
conn.create_saml_provider(Name="TestSAMLProvider", SAMLMetadataDocument="a" * 1024)
|
|
response = conn.list_saml_providers()
|
|
assert (
|
|
response["SAMLProviderList"][0]["Arn"]
|
|
== f"arn:aws:iam::{ACCOUNT_ID}:saml-provider/TestSAMLProvider"
|
|
)
|
|
|
|
|
|
@mock_iam()
|
|
def test_delete_saml_provider():
|
|
conn = boto3.client("iam", region_name="us-east-1")
|
|
saml_provider_create = conn.create_saml_provider(
|
|
Name="TestSAMLProvider", SAMLMetadataDocument="a" * 1024
|
|
)
|
|
response = conn.list_saml_providers()
|
|
assert len(response["SAMLProviderList"]) == 1
|
|
conn.delete_saml_provider(SAMLProviderArn=saml_provider_create["SAMLProviderArn"])
|
|
response = conn.list_saml_providers()
|
|
assert len(response["SAMLProviderList"]) == 0
|
|
conn.create_user(UserName="testing")
|
|
|
|
cert_id = "123456789012345678901234"
|
|
with pytest.raises(ClientError) as ce:
|
|
conn.delete_signing_certificate(UserName="testing", CertificateId=cert_id)
|
|
|
|
assert (
|
|
ce.value.response["Error"]["Message"]
|
|
== f"The Certificate with id {cert_id} cannot be found."
|
|
)
|
|
|
|
# Verify that it's not in the list:
|
|
resp = conn.list_signing_certificates(UserName="testing")
|
|
assert not resp["Certificates"]
|
|
|
|
|
|
@mock_iam()
|
|
def test_create_role_defaults():
|
|
"""Tests default values"""
|
|
conn = boto3.client("iam", region_name="us-east-1")
|
|
conn.create_role(RoleName="my-role", AssumeRolePolicyDocument="{}")
|
|
|
|
# Get role:
|
|
role = conn.get_role(RoleName="my-role")["Role"]
|
|
assert role["RoleId"].startswith("AROA")
|
|
assert role["MaxSessionDuration"] == 3600
|
|
assert role.get("Description") is None
|
|
|
|
|
|
@mock_iam()
|
|
def test_create_role_with_tags():
|
|
"""Tests both the tag_role and get_role_tags capability"""
|
|
conn = boto3.client("iam", region_name="us-east-1")
|
|
conn.create_role(
|
|
RoleName="my-role",
|
|
AssumeRolePolicyDocument="{}",
|
|
Tags=[
|
|
{"Key": "somekey", "Value": "somevalue"},
|
|
{"Key": "someotherkey", "Value": "someothervalue"},
|
|
],
|
|
Description="testing",
|
|
)
|
|
|
|
# Get role:
|
|
role = conn.get_role(RoleName="my-role")["Role"]
|
|
assert len(role["Tags"]) == 2
|
|
assert role["Tags"][0]["Key"] == "somekey"
|
|
assert role["Tags"][0]["Value"] == "somevalue"
|
|
assert role["Tags"][1]["Key"] == "someotherkey"
|
|
assert role["Tags"][1]["Value"] == "someothervalue"
|
|
assert role["Description"] == "testing"
|
|
|
|
# Empty is good:
|
|
conn.create_role(
|
|
RoleName="my-role2",
|
|
AssumeRolePolicyDocument="{}",
|
|
Tags=[{"Key": "somekey", "Value": ""}],
|
|
)
|
|
tags = conn.list_role_tags(RoleName="my-role2")
|
|
assert len(tags["Tags"]) == 1
|
|
assert tags["Tags"][0]["Key"] == "somekey"
|
|
assert tags["Tags"][0]["Value"] == ""
|
|
|
|
# Test creating tags with invalid values:
|
|
# With more than 50 tags:
|
|
with pytest.raises(ClientError) as ce:
|
|
too_many_tags = list(
|
|
map(lambda x: {"Key": str(x), "Value": str(x)}, range(0, 51))
|
|
)
|
|
conn.create_role(
|
|
RoleName="my-role3", AssumeRolePolicyDocument="{}", Tags=too_many_tags
|
|
)
|
|
assert (
|
|
"failed to satisfy constraint: Member must have length less than or equal to 50."
|
|
in ce.value.response["Error"]["Message"]
|
|
)
|
|
|
|
# With a duplicate tag:
|
|
with pytest.raises(ClientError) as ce:
|
|
conn.create_role(
|
|
RoleName="my-role3",
|
|
AssumeRolePolicyDocument="{}",
|
|
Tags=[{"Key": "0", "Value": ""}, {"Key": "0", "Value": ""}],
|
|
)
|
|
assert (
|
|
"Duplicate tag keys found. Please note that Tag keys are case insensitive."
|
|
in ce.value.response["Error"]["Message"]
|
|
)
|
|
|
|
# Duplicate tag with different casing:
|
|
with pytest.raises(ClientError) as ce:
|
|
conn.create_role(
|
|
RoleName="my-role3",
|
|
AssumeRolePolicyDocument="{}",
|
|
Tags=[{"Key": "a", "Value": ""}, {"Key": "A", "Value": ""}],
|
|
)
|
|
assert (
|
|
"Duplicate tag keys found. Please note that Tag keys are case insensitive."
|
|
in ce.value.response["Error"]["Message"]
|
|
)
|
|
|
|
# With a really big key:
|
|
with pytest.raises(ClientError) as ce:
|
|
conn.create_role(
|
|
RoleName="my-role3",
|
|
AssumeRolePolicyDocument="{}",
|
|
Tags=[{"Key": "0" * 129, "Value": ""}],
|
|
)
|
|
assert (
|
|
"Member must have length less than or equal to 128."
|
|
in ce.value.response["Error"]["Message"]
|
|
)
|
|
|
|
# With a really big value:
|
|
with pytest.raises(ClientError) as ce:
|
|
conn.create_role(
|
|
RoleName="my-role3",
|
|
AssumeRolePolicyDocument="{}",
|
|
Tags=[{"Key": "0", "Value": "0" * 257}],
|
|
)
|
|
assert (
|
|
"Member must have length less than or equal to 256."
|
|
in ce.value.response["Error"]["Message"]
|
|
)
|
|
|
|
# With an invalid character:
|
|
with pytest.raises(ClientError) as ce:
|
|
conn.create_role(
|
|
RoleName="my-role3",
|
|
AssumeRolePolicyDocument="{}",
|
|
Tags=[{"Key": "NOWAY!", "Value": ""}],
|
|
)
|
|
assert (
|
|
"Member must satisfy regular expression pattern: [\\p{L}\\p{Z}\\p{N}_.:/=+\\-@]+"
|
|
in ce.value.response["Error"]["Message"]
|
|
)
|
|
|
|
|
|
@mock_iam()
|
|
def test_tag_role():
|
|
"""Tests both the tag_role and get_role_tags capability"""
|
|
conn = boto3.client("iam", region_name="us-east-1")
|
|
conn.create_role(RoleName="my-role", AssumeRolePolicyDocument="{}")
|
|
|
|
# Get without tags:
|
|
role = conn.get_role(RoleName="my-role")["Role"]
|
|
assert not role.get("Tags")
|
|
|
|
# With proper tag values:
|
|
conn.tag_role(
|
|
RoleName="my-role",
|
|
Tags=[
|
|
{"Key": "somekey", "Value": "somevalue"},
|
|
{"Key": "someotherkey", "Value": "someothervalue"},
|
|
],
|
|
)
|
|
|
|
# Get role:
|
|
role = conn.get_role(RoleName="my-role")["Role"]
|
|
assert len(role["Tags"]) == 2
|
|
assert role["Tags"][0]["Key"] == "somekey"
|
|
assert role["Tags"][0]["Value"] == "somevalue"
|
|
assert role["Tags"][1]["Key"] == "someotherkey"
|
|
assert role["Tags"][1]["Value"] == "someothervalue"
|
|
|
|
# Same -- but for list_role_tags:
|
|
tags = conn.list_role_tags(RoleName="my-role")
|
|
assert len(tags["Tags"]) == 2
|
|
assert role["Tags"][0]["Key"] == "somekey"
|
|
assert role["Tags"][0]["Value"] == "somevalue"
|
|
assert role["Tags"][1]["Key"] == "someotherkey"
|
|
assert role["Tags"][1]["Value"] == "someothervalue"
|
|
assert not tags["IsTruncated"]
|
|
assert not tags.get("Marker")
|
|
|
|
# Test pagination:
|
|
tags = conn.list_role_tags(RoleName="my-role", MaxItems=1)
|
|
assert len(tags["Tags"]) == 1
|
|
assert tags["IsTruncated"]
|
|
assert tags["Tags"][0]["Key"] == "somekey"
|
|
assert tags["Tags"][0]["Value"] == "somevalue"
|
|
assert tags["Marker"] == "1"
|
|
|
|
tags = conn.list_role_tags(RoleName="my-role", Marker=tags["Marker"])
|
|
assert len(tags["Tags"]) == 1
|
|
assert tags["Tags"][0]["Key"] == "someotherkey"
|
|
assert tags["Tags"][0]["Value"] == "someothervalue"
|
|
assert not tags["IsTruncated"]
|
|
assert not tags.get("Marker")
|
|
|
|
# Test updating an existing tag:
|
|
conn.tag_role(
|
|
RoleName="my-role", Tags=[{"Key": "somekey", "Value": "somenewvalue"}]
|
|
)
|
|
tags = conn.list_role_tags(RoleName="my-role")
|
|
assert len(tags["Tags"]) == 2
|
|
assert tags["Tags"][0]["Key"] == "somekey"
|
|
assert tags["Tags"][0]["Value"] == "somenewvalue"
|
|
|
|
# Empty is good:
|
|
conn.tag_role(RoleName="my-role", Tags=[{"Key": "somekey", "Value": ""}])
|
|
tags = conn.list_role_tags(RoleName="my-role")
|
|
assert len(tags["Tags"]) == 2
|
|
assert tags["Tags"][0]["Key"] == "somekey"
|
|
assert tags["Tags"][0]["Value"] == ""
|
|
|
|
# Test creating tags with invalid values:
|
|
# With more than 50 tags:
|
|
with pytest.raises(ClientError) as ce:
|
|
too_many_tags = list(
|
|
map(lambda x: {"Key": str(x), "Value": str(x)}, range(0, 51))
|
|
)
|
|
conn.tag_role(RoleName="my-role", Tags=too_many_tags)
|
|
assert (
|
|
"failed to satisfy constraint: Member must have length less than or equal to 50."
|
|
in ce.value.response["Error"]["Message"]
|
|
)
|
|
|
|
# With a duplicate tag:
|
|
with pytest.raises(ClientError) as ce:
|
|
conn.tag_role(
|
|
RoleName="my-role",
|
|
Tags=[{"Key": "0", "Value": ""}, {"Key": "0", "Value": ""}],
|
|
)
|
|
assert (
|
|
"Duplicate tag keys found. Please note that Tag keys are case insensitive."
|
|
in ce.value.response["Error"]["Message"]
|
|
)
|
|
|
|
# Duplicate tag with different casing:
|
|
with pytest.raises(ClientError) as ce:
|
|
conn.tag_role(
|
|
RoleName="my-role",
|
|
Tags=[{"Key": "a", "Value": ""}, {"Key": "A", "Value": ""}],
|
|
)
|
|
assert (
|
|
"Duplicate tag keys found. Please note that Tag keys are case insensitive."
|
|
in ce.value.response["Error"]["Message"]
|
|
)
|
|
|
|
# With a really big key:
|
|
with pytest.raises(ClientError) as ce:
|
|
conn.tag_role(RoleName="my-role", Tags=[{"Key": "0" * 129, "Value": ""}])
|
|
assert (
|
|
"Member must have length less than or equal to 128."
|
|
in ce.value.response["Error"]["Message"]
|
|
)
|
|
|
|
# With a really big value:
|
|
with pytest.raises(ClientError) as ce:
|
|
conn.tag_role(RoleName="my-role", Tags=[{"Key": "0", "Value": "0" * 257}])
|
|
assert (
|
|
"Member must have length less than or equal to 256."
|
|
in ce.value.response["Error"]["Message"]
|
|
)
|
|
|
|
# With an invalid character:
|
|
with pytest.raises(ClientError) as ce:
|
|
conn.tag_role(RoleName="my-role", Tags=[{"Key": "NOWAY!", "Value": ""}])
|
|
assert (
|
|
"Member must satisfy regular expression pattern: [\\p{L}\\p{Z}\\p{N}_.:/=+\\-@]+"
|
|
in ce.value.response["Error"]["Message"]
|
|
)
|
|
|
|
# With a role that doesn't exist:
|
|
with pytest.raises(ClientError):
|
|
conn.tag_role(RoleName="notarole", Tags=[{"Key": "some", "Value": "value"}])
|
|
|
|
|
|
@mock_iam
|
|
def test_untag_role():
|
|
conn = boto3.client("iam", region_name="us-east-1")
|
|
conn.create_role(RoleName="my-role", AssumeRolePolicyDocument="{}")
|
|
|
|
# With proper tag values:
|
|
conn.tag_role(
|
|
RoleName="my-role",
|
|
Tags=[
|
|
{"Key": "somekey", "Value": "somevalue"},
|
|
{"Key": "someotherkey", "Value": "someothervalue"},
|
|
],
|
|
)
|
|
|
|
# Remove them:
|
|
conn.untag_role(RoleName="my-role", TagKeys=["somekey"])
|
|
tags = conn.list_role_tags(RoleName="my-role")
|
|
assert len(tags["Tags"]) == 1
|
|
assert tags["Tags"][0]["Key"] == "someotherkey"
|
|
assert tags["Tags"][0]["Value"] == "someothervalue"
|
|
|
|
# And again:
|
|
conn.untag_role(RoleName="my-role", TagKeys=["someotherkey"])
|
|
tags = conn.list_role_tags(RoleName="my-role")
|
|
assert not tags["Tags"]
|
|
|
|
# Test removing tags with invalid values:
|
|
# With more than 50 tags:
|
|
with pytest.raises(ClientError) as ce:
|
|
conn.untag_role(RoleName="my-role", TagKeys=[str(x) for x in range(0, 51)])
|
|
assert (
|
|
"failed to satisfy constraint: Member must have length less than or equal to 50."
|
|
in ce.value.response["Error"]["Message"]
|
|
)
|
|
assert "tagKeys" in ce.value.response["Error"]["Message"]
|
|
|
|
# With a really big key:
|
|
with pytest.raises(ClientError) as ce:
|
|
conn.untag_role(RoleName="my-role", TagKeys=["0" * 129])
|
|
assert (
|
|
"Member must have length less than or equal to 128."
|
|
in ce.value.response["Error"]["Message"]
|
|
)
|
|
assert "tagKeys" in ce.value.response["Error"]["Message"]
|
|
|
|
# With an invalid character:
|
|
with pytest.raises(ClientError) as ce:
|
|
conn.untag_role(RoleName="my-role", TagKeys=["NOWAY!"])
|
|
assert (
|
|
"Member must satisfy regular expression pattern: [\\p{L}\\p{Z}\\p{N}_.:/=+\\-@]+"
|
|
in ce.value.response["Error"]["Message"]
|
|
)
|
|
assert "tagKeys" in ce.value.response["Error"]["Message"]
|
|
|
|
# With a role that doesn't exist:
|
|
with pytest.raises(ClientError):
|
|
conn.untag_role(RoleName="notarole", TagKeys=["somevalue"])
|
|
|
|
|
|
@mock_iam()
|
|
def test_update_role_description():
|
|
conn = boto3.client("iam", region_name="us-east-1")
|
|
|
|
with pytest.raises(ClientError):
|
|
conn.delete_role(RoleName="my-role")
|
|
|
|
conn.create_role(
|
|
RoleName="my-role", AssumeRolePolicyDocument="some policy", Path="/my-path/"
|
|
)
|
|
response = conn.update_role_description(RoleName="my-role", Description="test")
|
|
|
|
assert response["Role"]["RoleName"] == "my-role"
|
|
|
|
|
|
@mock_iam()
|
|
def test_update_role():
|
|
conn = boto3.client("iam", region_name="us-east-1")
|
|
|
|
with pytest.raises(ClientError):
|
|
conn.delete_role(RoleName="my-role")
|
|
|
|
conn.create_role(
|
|
RoleName="my-role", AssumeRolePolicyDocument="some policy", Path="/my-path/"
|
|
)
|
|
response = conn.update_role(RoleName="my-role", Description="test")
|
|
assert len(response.keys()) == 1
|
|
|
|
|
|
@mock_iam()
|
|
def test_update_role_defaults():
|
|
conn = boto3.client("iam", region_name="us-east-1")
|
|
|
|
with pytest.raises(ClientError):
|
|
conn.delete_role(RoleName="my-role")
|
|
|
|
conn.create_role(
|
|
RoleName="my-role",
|
|
AssumeRolePolicyDocument="some policy",
|
|
Description="test",
|
|
Path="/my-path/",
|
|
)
|
|
response = conn.update_role(RoleName="my-role")
|
|
assert len(response.keys()) == 1
|
|
|
|
role = conn.get_role(RoleName="my-role")["Role"]
|
|
|
|
assert role["MaxSessionDuration"] == 3600
|
|
assert role.get("Description") is None
|
|
|
|
|
|
@mock_iam()
|
|
def test_list_entities_for_policy():
|
|
test_policy = json.dumps(
|
|
{
|
|
"Version": "2012-10-17",
|
|
"Statement": [
|
|
{"Action": "s3:ListBucket", "Resource": "*", "Effect": "Allow"}
|
|
],
|
|
}
|
|
)
|
|
|
|
conn = boto3.client("iam", region_name="us-east-1")
|
|
conn.create_role(
|
|
RoleName="my-role", AssumeRolePolicyDocument="some policy", Path="/my-path/"
|
|
)
|
|
conn.create_user(Path="/", UserName="testUser")
|
|
conn.create_group(Path="/", GroupName="testGroup")
|
|
conn.create_policy(
|
|
PolicyName="testPolicy",
|
|
Path="/",
|
|
PolicyDocument=test_policy,
|
|
Description="Test Policy",
|
|
)
|
|
|
|
# Attach things to the user and group:
|
|
conn.put_user_policy(
|
|
UserName="testUser", PolicyName="testPolicy", PolicyDocument=test_policy
|
|
)
|
|
conn.put_group_policy(
|
|
GroupName="testGroup", PolicyName="testPolicy", PolicyDocument=test_policy
|
|
)
|
|
|
|
conn.attach_user_policy(
|
|
UserName="testUser",
|
|
PolicyArn=f"arn:aws:iam::{ACCOUNT_ID}:policy/testPolicy",
|
|
)
|
|
conn.attach_group_policy(
|
|
GroupName="testGroup",
|
|
PolicyArn=f"arn:aws:iam::{ACCOUNT_ID}:policy/testPolicy",
|
|
)
|
|
|
|
conn.add_user_to_group(UserName="testUser", GroupName="testGroup")
|
|
|
|
# Add things to the role:
|
|
conn.create_instance_profile(InstanceProfileName="ipn")
|
|
conn.add_role_to_instance_profile(InstanceProfileName="ipn", RoleName="my-role")
|
|
conn.tag_role(
|
|
RoleName="my-role",
|
|
Tags=[
|
|
{"Key": "somekey", "Value": "somevalue"},
|
|
{"Key": "someotherkey", "Value": "someothervalue"},
|
|
],
|
|
)
|
|
conn.put_role_policy(
|
|
RoleName="my-role", PolicyName="test-policy", PolicyDocument=test_policy
|
|
)
|
|
conn.attach_role_policy(
|
|
RoleName="my-role",
|
|
PolicyArn=f"arn:aws:iam::{ACCOUNT_ID}:policy/testPolicy",
|
|
)
|
|
|
|
response = conn.list_entities_for_policy(
|
|
PolicyArn=f"arn:aws:iam::{ACCOUNT_ID}:policy/testPolicy",
|
|
EntityFilter="Role",
|
|
)
|
|
assert response["PolicyRoles"][0]["RoleName"] == "my-role"
|
|
assert "RoleId" in response["PolicyRoles"][0]
|
|
assert response["PolicyGroups"] == []
|
|
assert response["PolicyUsers"] == []
|
|
|
|
response = conn.list_entities_for_policy(
|
|
PolicyArn=f"arn:aws:iam::{ACCOUNT_ID}:policy/testPolicy",
|
|
EntityFilter="User",
|
|
)
|
|
assert response["PolicyUsers"][0]["UserName"] == "testUser"
|
|
assert "UserId" in response["PolicyUsers"][0]
|
|
assert response["PolicyGroups"] == []
|
|
assert response["PolicyRoles"] == []
|
|
|
|
response = conn.list_entities_for_policy(
|
|
PolicyArn=f"arn:aws:iam::{ACCOUNT_ID}:policy/testPolicy",
|
|
EntityFilter="Group",
|
|
)
|
|
assert response["PolicyGroups"][0]["GroupName"] == "testGroup"
|
|
assert "GroupId" in response["PolicyGroups"][0]
|
|
assert response["PolicyRoles"] == []
|
|
assert response["PolicyUsers"] == []
|
|
|
|
response = conn.list_entities_for_policy(
|
|
PolicyArn=f"arn:aws:iam::{ACCOUNT_ID}:policy/testPolicy",
|
|
EntityFilter="LocalManagedPolicy",
|
|
)
|
|
assert response["PolicyGroups"][0]["GroupName"] == "testGroup"
|
|
assert response["PolicyUsers"][0]["UserName"] == "testUser"
|
|
assert response["PolicyRoles"][0]["RoleName"] == "my-role"
|
|
|
|
assert "GroupId" in response["PolicyGroups"][0]
|
|
assert "UserId" in response["PolicyUsers"][0]
|
|
assert "RoleId" in response["PolicyRoles"][0]
|
|
|
|
# Return everything when no entity is specified
|
|
response = conn.list_entities_for_policy(
|
|
PolicyArn=f"arn:aws:iam::{ACCOUNT_ID}:policy/testPolicy"
|
|
)
|
|
assert response["PolicyGroups"][0]["GroupName"] == "testGroup"
|
|
assert response["PolicyUsers"][0]["UserName"] == "testUser"
|
|
assert response["PolicyRoles"][0]["RoleName"] == "my-role"
|
|
|
|
assert "GroupId" in response["PolicyGroups"][0]
|
|
assert "UserId" in response["PolicyUsers"][0]
|
|
assert "RoleId" in response["PolicyRoles"][0]
|
|
|
|
|
|
@mock_iam()
|
|
def test_create_role_no_path():
|
|
conn = boto3.client("iam", region_name="us-east-1")
|
|
resp = conn.create_role(
|
|
RoleName="my-role", AssumeRolePolicyDocument="some policy", Description="test"
|
|
)
|
|
assert resp["Role"].get("Arn") == f"arn:aws:iam::{ACCOUNT_ID}:role/my-role"
|
|
assert "PermissionsBoundary" not in resp["Role"]
|
|
assert resp["Role"]["Description"] == "test"
|
|
|
|
|
|
@mock_iam()
|
|
def test_create_role_with_permissions_boundary():
|
|
conn = boto3.client("iam", region_name="us-east-1")
|
|
boundary = f"arn:aws:iam::{ACCOUNT_ID}:policy/boundary"
|
|
resp = conn.create_role(
|
|
RoleName="my-role",
|
|
AssumeRolePolicyDocument="some policy",
|
|
Description="test",
|
|
PermissionsBoundary=boundary,
|
|
)
|
|
expected = {
|
|
"PermissionsBoundaryType": "PermissionsBoundaryPolicy",
|
|
"PermissionsBoundaryArn": boundary,
|
|
}
|
|
assert resp["Role"].get("PermissionsBoundary") == expected
|
|
assert resp["Role"]["Description"] == "test"
|
|
|
|
conn.delete_role_permissions_boundary(RoleName="my-role")
|
|
assert "PermissionsBoundary" not in conn.list_roles()["Roles"][0]
|
|
|
|
conn.put_role_permissions_boundary(RoleName="my-role", PermissionsBoundary=boundary)
|
|
assert resp["Role"].get("PermissionsBoundary") == expected
|
|
|
|
invalid_boundary_arn = "arn:aws:iam::123456789:not_a_boundary"
|
|
|
|
with pytest.raises(ClientError):
|
|
conn.put_role_permissions_boundary(
|
|
RoleName="my-role", PermissionsBoundary=invalid_boundary_arn
|
|
)
|
|
|
|
with pytest.raises(ClientError):
|
|
conn.create_role(
|
|
RoleName="bad-boundary",
|
|
AssumeRolePolicyDocument="some policy",
|
|
Description="test",
|
|
PermissionsBoundary=invalid_boundary_arn,
|
|
)
|
|
|
|
# Ensure the PermissionsBoundary is included in role listing as well
|
|
assert conn.list_roles()["Roles"][0].get("PermissionsBoundary") == expected
|
|
|
|
|
|
@mock_iam
|
|
def test_create_role_with_same_name_should_fail():
|
|
iam = boto3.client("iam", region_name="us-east-1")
|
|
test_role_name = str(uuid4())
|
|
iam.create_role(
|
|
RoleName=test_role_name, AssumeRolePolicyDocument="policy", Description="test"
|
|
)
|
|
# Create the role again, and verify that it fails
|
|
with pytest.raises(ClientError) as err:
|
|
iam.create_role(
|
|
RoleName=test_role_name,
|
|
AssumeRolePolicyDocument="policy",
|
|
Description="test",
|
|
)
|
|
assert err.value.response["Error"]["Code"] == "EntityAlreadyExists"
|
|
assert (
|
|
err.value.response["Error"]["Message"]
|
|
== f"Role with name {test_role_name} already exists."
|
|
)
|
|
|
|
|
|
@mock_iam
|
|
def test_create_policy_with_same_name_should_fail():
|
|
iam = boto3.client("iam", region_name="us-east-1")
|
|
test_policy_name = str(uuid4())
|
|
iam.create_policy(PolicyName=test_policy_name, PolicyDocument=MOCK_POLICY)
|
|
# Create the role again, and verify that it fails
|
|
with pytest.raises(ClientError) as err:
|
|
iam.create_policy(PolicyName=test_policy_name, PolicyDocument=MOCK_POLICY)
|
|
assert err.value.response["Error"]["Code"] == "EntityAlreadyExists"
|
|
assert (
|
|
err.value.response["Error"]["Message"]
|
|
== f"A policy called {test_policy_name} already exists. Duplicate names are not allowed."
|
|
)
|
|
|
|
|
|
@mock_iam
|
|
def test_update_account_password_policy():
|
|
client = boto3.client("iam", region_name="us-east-1")
|
|
|
|
client.update_account_password_policy()
|
|
|
|
response = client.get_account_password_policy()
|
|
assert response["PasswordPolicy"] == {
|
|
"AllowUsersToChangePassword": False,
|
|
"ExpirePasswords": False,
|
|
"MinimumPasswordLength": 6,
|
|
"RequireLowercaseCharacters": False,
|
|
"RequireNumbers": False,
|
|
"RequireSymbols": False,
|
|
"RequireUppercaseCharacters": False,
|
|
"HardExpiry": False,
|
|
}
|
|
|
|
|
|
@mock_iam
|
|
def test_update_account_password_policy_errors():
|
|
client = boto3.client("iam", region_name="us-east-1")
|
|
|
|
with pytest.raises(ClientError) as exc:
|
|
client.update_account_password_policy(
|
|
MaxPasswordAge=1096, MinimumPasswordLength=129, PasswordReusePrevention=25
|
|
)
|
|
err = exc.value.response["Error"]
|
|
assert (
|
|
err["Message"]
|
|
== '3 validation errors detected: Value "129" at "minimumPasswordLength" failed to satisfy constraint: Member must have value less than or equal to 128; Value "25" at "passwordReusePrevention" failed to satisfy constraint: Member must have value less than or equal to 24; Value "1096" at "maxPasswordAge" failed to satisfy constraint: Member must have value less than or equal to 1095'
|
|
)
|
|
|
|
|
|
@mock_iam
|
|
def test_get_account_password_policy():
|
|
client = boto3.client("iam", region_name="us-east-1")
|
|
client.update_account_password_policy(
|
|
AllowUsersToChangePassword=True,
|
|
HardExpiry=True,
|
|
MaxPasswordAge=60,
|
|
MinimumPasswordLength=10,
|
|
PasswordReusePrevention=3,
|
|
RequireLowercaseCharacters=True,
|
|
RequireNumbers=True,
|
|
RequireSymbols=True,
|
|
RequireUppercaseCharacters=True,
|
|
)
|
|
|
|
response = client.get_account_password_policy()
|
|
|
|
assert response["PasswordPolicy"] == {
|
|
"AllowUsersToChangePassword": True,
|
|
"ExpirePasswords": True,
|
|
"HardExpiry": True,
|
|
"MaxPasswordAge": 60,
|
|
"MinimumPasswordLength": 10,
|
|
"PasswordReusePrevention": 3,
|
|
"RequireLowercaseCharacters": True,
|
|
"RequireNumbers": True,
|
|
"RequireSymbols": True,
|
|
"RequireUppercaseCharacters": True,
|
|
}
|
|
|
|
|
|
@mock_iam
|
|
def test_get_account_password_policy_errors():
|
|
client = boto3.client("iam", region_name="us-east-1")
|
|
|
|
with pytest.raises(ClientError) as exc:
|
|
client.get_account_password_policy()
|
|
err = exc.value.response["Error"]
|
|
assert (
|
|
err["Message"]
|
|
== f"The Password Policy with domain name {ACCOUNT_ID} cannot be found."
|
|
)
|
|
|
|
|
|
@mock_iam
|
|
def test_delete_account_password_policy():
|
|
client = boto3.client("iam", region_name="us-east-1")
|
|
client.update_account_password_policy()
|
|
|
|
response = client.get_account_password_policy()
|
|
|
|
assert isinstance(response["PasswordPolicy"], dict)
|
|
|
|
client.delete_account_password_policy()
|
|
|
|
with pytest.raises(ClientError) as exc:
|
|
client.get_account_password_policy()
|
|
err = exc.value.response["Error"]
|
|
assert (
|
|
err["Message"]
|
|
== f"The Password Policy with domain name {ACCOUNT_ID} cannot be found."
|
|
)
|
|
|
|
|
|
@mock_iam
|
|
def test_get_account_summary():
|
|
client = boto3.client("iam", region_name="us-east-1")
|
|
iam = boto3.resource("iam", region_name="us-east-1")
|
|
|
|
account_summary = iam.AccountSummary()
|
|
|
|
assert account_summary.summary_map == {
|
|
"GroupPolicySizeQuota": 5120,
|
|
"InstanceProfilesQuota": 1000,
|
|
"Policies": 0,
|
|
"GroupsPerUserQuota": 10,
|
|
"InstanceProfiles": 0,
|
|
"AttachedPoliciesPerUserQuota": 10,
|
|
"Users": 0,
|
|
"PoliciesQuota": 1500,
|
|
"Providers": 0,
|
|
"AccountMFAEnabled": 0,
|
|
"AccessKeysPerUserQuota": 2,
|
|
"AssumeRolePolicySizeQuota": 2048,
|
|
"PolicyVersionsInUseQuota": 10000,
|
|
"GlobalEndpointTokenVersion": 1,
|
|
"VersionsPerPolicyQuota": 5,
|
|
"AttachedPoliciesPerGroupQuota": 10,
|
|
"PolicySizeQuota": 6144,
|
|
"Groups": 0,
|
|
"AccountSigningCertificatesPresent": 0,
|
|
"UsersQuota": 5000,
|
|
"ServerCertificatesQuota": 20,
|
|
"MFADevices": 0,
|
|
"UserPolicySizeQuota": 2048,
|
|
"PolicyVersionsInUse": 0,
|
|
"ServerCertificates": 0,
|
|
"Roles": 0,
|
|
"RolesQuota": 1000,
|
|
"SigningCertificatesPerUserQuota": 2,
|
|
"MFADevicesInUse": 0,
|
|
"RolePolicySizeQuota": 10240,
|
|
"AttachedPoliciesPerRoleQuota": 10,
|
|
"AccountAccessKeysPresent": 0,
|
|
"GroupsQuota": 300,
|
|
}
|
|
|
|
client.create_instance_profile(InstanceProfileName="test-profile")
|
|
client.create_open_id_connect_provider(Url="https://example.com", ThumbprintList=[])
|
|
response_policy = client.create_policy(
|
|
PolicyName="test-policy", PolicyDocument=MOCK_POLICY
|
|
)
|
|
client.create_role(RoleName="test-role", AssumeRolePolicyDocument="test policy")
|
|
client.attach_role_policy(
|
|
RoleName="test-role", PolicyArn=response_policy["Policy"]["Arn"]
|
|
)
|
|
client.create_saml_provider(
|
|
Name="TestSAMLProvider", SAMLMetadataDocument="a" * 1024
|
|
)
|
|
client.create_group(GroupName="test-group")
|
|
client.attach_group_policy(
|
|
GroupName="test-group", PolicyArn=response_policy["Policy"]["Arn"]
|
|
)
|
|
client.create_user(UserName="test-user")
|
|
client.attach_user_policy(
|
|
UserName="test-user", PolicyArn=response_policy["Policy"]["Arn"]
|
|
)
|
|
client.enable_mfa_device(
|
|
UserName="test-user",
|
|
SerialNumber="123456789",
|
|
AuthenticationCode1="234567",
|
|
AuthenticationCode2="987654",
|
|
)
|
|
client.create_virtual_mfa_device(VirtualMFADeviceName="test-device")
|
|
client.upload_server_certificate(
|
|
ServerCertificateName="test-cert",
|
|
CertificateBody="cert-body",
|
|
PrivateKey="private-key",
|
|
)
|
|
account_summary.load()
|
|
|
|
assert account_summary.summary_map == {
|
|
"GroupPolicySizeQuota": 5120,
|
|
"InstanceProfilesQuota": 1000,
|
|
"Policies": 1,
|
|
"GroupsPerUserQuota": 10,
|
|
"InstanceProfiles": 1,
|
|
"AttachedPoliciesPerUserQuota": 10,
|
|
"Users": 1,
|
|
"PoliciesQuota": 1500,
|
|
"Providers": 2,
|
|
"AccountMFAEnabled": 0,
|
|
"AccessKeysPerUserQuota": 2,
|
|
"AssumeRolePolicySizeQuota": 2048,
|
|
"PolicyVersionsInUseQuota": 10000,
|
|
"GlobalEndpointTokenVersion": 1,
|
|
"VersionsPerPolicyQuota": 5,
|
|
"AttachedPoliciesPerGroupQuota": 10,
|
|
"PolicySizeQuota": 6144,
|
|
"Groups": 1,
|
|
"AccountSigningCertificatesPresent": 0,
|
|
"UsersQuota": 5000,
|
|
"ServerCertificatesQuota": 20,
|
|
"MFADevices": 1,
|
|
"UserPolicySizeQuota": 2048,
|
|
"PolicyVersionsInUse": 3,
|
|
"ServerCertificates": 1,
|
|
"Roles": 1,
|
|
"RolesQuota": 1000,
|
|
"SigningCertificatesPerUserQuota": 2,
|
|
"MFADevicesInUse": 1,
|
|
"RolePolicySizeQuota": 10240,
|
|
"AttachedPoliciesPerRoleQuota": 10,
|
|
"AccountAccessKeysPresent": 0,
|
|
"GroupsQuota": 300,
|
|
}
|
|
|
|
|
|
@mock_iam()
|
|
def test_list_user_tags():
|
|
"""Tests both setting a tags on a user in create_user and list_user_tags"""
|
|
conn = boto3.client("iam", region_name="us-east-1")
|
|
conn.create_user(UserName="kenny-bania")
|
|
conn.create_user(
|
|
UserName="jackie-chiles", Tags=[{"Key": "Sue-Allen", "Value": "Oh-Henry"}]
|
|
)
|
|
conn.create_user(
|
|
UserName="cosmo",
|
|
Tags=[
|
|
{"Key": "Stan", "Value": "The Caddy"},
|
|
{"Key": "like-a", "Value": "glove"},
|
|
],
|
|
)
|
|
response = conn.list_user_tags(UserName="kenny-bania")
|
|
assert len(response["Tags"]) == 0
|
|
assert response["IsTruncated"] is False
|
|
|
|
response = conn.list_user_tags(UserName="jackie-chiles")
|
|
assert response["Tags"] == [{"Key": "Sue-Allen", "Value": "Oh-Henry"}]
|
|
assert response["IsTruncated"] is False
|
|
|
|
response = conn.list_user_tags(UserName="cosmo")
|
|
assert response["Tags"] == [
|
|
{"Key": "Stan", "Value": "The Caddy"},
|
|
{"Key": "like-a", "Value": "glove"},
|
|
]
|
|
assert response["IsTruncated"] is False
|
|
|
|
|
|
@mock_iam()
|
|
def test_delete_role_with_instance_profiles_present():
|
|
iam = boto3.client("iam", region_name="us-east-1")
|
|
|
|
trust_policy = MOCK_STS_EC2_POLICY_DOCUMENT.strip()
|
|
|
|
iam.create_role(RoleName="Role1", AssumeRolePolicyDocument=trust_policy)
|
|
iam.create_instance_profile(InstanceProfileName="IP1")
|
|
iam.add_role_to_instance_profile(InstanceProfileName="IP1", RoleName="Role1")
|
|
|
|
iam.create_role(RoleName="Role2", AssumeRolePolicyDocument=trust_policy)
|
|
|
|
iam.delete_role(RoleName="Role2")
|
|
|
|
role_names = [role["RoleName"] for role in iam.list_roles()["Roles"]]
|
|
assert "Role1" in role_names
|
|
assert "Role2" not in role_names
|
|
|
|
|
|
@mock_iam
|
|
def test_delete_account_password_policy_errors():
|
|
client = boto3.client("iam", region_name="us-east-1")
|
|
|
|
with pytest.raises(ClientError) as exc:
|
|
client.delete_account_password_policy()
|
|
err = exc.value.response["Error"]
|
|
assert (
|
|
err["Message"] == "The account policy with name PasswordPolicy cannot be found."
|
|
)
|
|
|
|
|
|
@mock_iam
|
|
def test_role_list_config_discovered_resources():
|
|
from moto.iam.config import role_config_query
|
|
|
|
# Without any roles
|
|
assert role_config_query.list_config_service_resources(
|
|
DEFAULT_ACCOUNT_ID, None, None, 100, None
|
|
) == (
|
|
[],
|
|
None,
|
|
)
|
|
|
|
# Make 3 roles
|
|
roles = []
|
|
num_roles = 3
|
|
for ix in range(1, num_roles + 1):
|
|
this_role = role_config_query.backends[DEFAULT_ACCOUNT_ID][
|
|
"global"
|
|
].create_role(
|
|
role_name=f"role{ix}",
|
|
assume_role_policy_document=None,
|
|
path="/",
|
|
permissions_boundary=None,
|
|
description=f"role{ix}",
|
|
tags=[{"Key": "foo", "Value": "bar"}],
|
|
max_session_duration=3600,
|
|
)
|
|
roles.append({"id": this_role.id, "name": this_role.name})
|
|
|
|
assert len(roles) == num_roles
|
|
|
|
result = role_config_query.list_config_service_resources(
|
|
DEFAULT_ACCOUNT_ID, None, None, 100, None
|
|
)[0]
|
|
assert len(result) == num_roles
|
|
|
|
# The roles gets a random ID, so we can't directly test it
|
|
role = result[0]
|
|
assert role["type"] == "AWS::IAM::Role"
|
|
assert role["id"] in list(map(lambda p: p["id"], roles))
|
|
assert role["name"] in list(map(lambda p: p["name"], roles))
|
|
assert role["region"] == "global"
|
|
|
|
# test passing list of resource ids
|
|
resource_ids = role_config_query.list_config_service_resources(
|
|
DEFAULT_ACCOUNT_ID, [roles[0]["id"], roles[1]["id"]], None, 100, None
|
|
)[0]
|
|
assert len(resource_ids) == 2
|
|
|
|
# test passing a single resource name
|
|
resource_name = role_config_query.list_config_service_resources(
|
|
DEFAULT_ACCOUNT_ID, None, roles[0]["name"], 100, None
|
|
)[0]
|
|
assert len(resource_name) == 1
|
|
assert resource_name[0]["id"] == roles[0]["id"]
|
|
assert resource_name[0]["name"] == roles[0]["name"]
|
|
|
|
# test passing a single resource name AND some resource id's
|
|
both_filter_good = role_config_query.list_config_service_resources(
|
|
DEFAULT_ACCOUNT_ID,
|
|
[roles[0]["id"], roles[1]["id"]],
|
|
roles[0]["name"],
|
|
100,
|
|
None,
|
|
)[0]
|
|
assert len(both_filter_good) == 1
|
|
assert both_filter_good[0]["id"] == roles[0]["id"]
|
|
assert both_filter_good[0]["name"] == roles[0]["name"]
|
|
|
|
both_filter_bad = role_config_query.list_config_service_resources(
|
|
DEFAULT_ACCOUNT_ID,
|
|
[roles[0]["id"], roles[1]["id"]],
|
|
roles[2]["name"],
|
|
100,
|
|
None,
|
|
)[0]
|
|
assert len(both_filter_bad) == 0
|
|
|
|
|
|
@mock_iam
|
|
def test_role_config_dict():
|
|
from moto.iam.config import policy_config_query, role_config_query
|
|
from moto.iam.utils import random_policy_id, random_role_id
|
|
|
|
# Without any roles
|
|
assert not role_config_query.get_config_resource(DEFAULT_ACCOUNT_ID, "something")
|
|
assert role_config_query.list_config_service_resources(
|
|
DEFAULT_ACCOUNT_ID, None, None, 100, None
|
|
) == (
|
|
[],
|
|
None,
|
|
)
|
|
|
|
basic_assume_role = {
|
|
"Version": "2012-10-17",
|
|
"Statement": [
|
|
{"Effect": "Allow", "Principal": {"AWS": "*"}, "Action": "sts:AssumeRole"}
|
|
],
|
|
}
|
|
|
|
basic_policy = {
|
|
"Version": "2012-10-17",
|
|
"Statement": [{"Action": ["ec2:*"], "Effect": "Allow", "Resource": "*"}],
|
|
}
|
|
|
|
# Create a policy for use in role permissions boundary
|
|
policy_arn = (
|
|
policy_config_query.backends[DEFAULT_ACCOUNT_ID]["global"]
|
|
.create_policy(
|
|
description="basic_policy",
|
|
path="/",
|
|
policy_document=json.dumps(basic_policy),
|
|
policy_name="basic_policy",
|
|
tags=[],
|
|
)
|
|
.arn
|
|
)
|
|
|
|
policy_id = policy_config_query.list_config_service_resources(
|
|
DEFAULT_ACCOUNT_ID, None, None, 100, None
|
|
)[0][0]["id"]
|
|
assert len(policy_id) == len(random_policy_id())
|
|
|
|
# Create some roles (and grab them repeatedly since they create with random names)
|
|
role_config_query.backends[DEFAULT_ACCOUNT_ID]["global"].create_role(
|
|
role_name="plain_role",
|
|
assume_role_policy_document=None,
|
|
path="/",
|
|
permissions_boundary=None,
|
|
description="plain_role",
|
|
tags=[{"Key": "foo", "Value": "bar"}],
|
|
max_session_duration=3600,
|
|
)
|
|
|
|
plain_role = role_config_query.list_config_service_resources(
|
|
DEFAULT_ACCOUNT_ID, None, None, 100, None
|
|
)[0][0]
|
|
assert plain_role is not None
|
|
assert len(plain_role["id"]) == len(random_role_id(DEFAULT_ACCOUNT_ID))
|
|
|
|
role_config_query.backends[DEFAULT_ACCOUNT_ID]["global"].create_role(
|
|
role_name="assume_role",
|
|
assume_role_policy_document=json.dumps(basic_assume_role),
|
|
path="/",
|
|
permissions_boundary=None,
|
|
description="assume_role",
|
|
tags=[],
|
|
max_session_duration=3600,
|
|
)
|
|
|
|
assume_role = next(
|
|
role
|
|
for role in role_config_query.list_config_service_resources(
|
|
DEFAULT_ACCOUNT_ID, None, None, 100, None
|
|
)[0]
|
|
if role["id"] not in [plain_role["id"]]
|
|
)
|
|
assert assume_role is not None
|
|
assert len(assume_role["id"]) == len(random_role_id(DEFAULT_ACCOUNT_ID))
|
|
assert assume_role["id"] is not plain_role["id"]
|
|
|
|
role_config_query.backends[DEFAULT_ACCOUNT_ID]["global"].create_role(
|
|
role_name="assume_and_permission_boundary_role",
|
|
assume_role_policy_document=json.dumps(basic_assume_role),
|
|
path="/",
|
|
permissions_boundary=policy_arn,
|
|
description="assume_and_permission_boundary_role",
|
|
tags=[],
|
|
max_session_duration=3600,
|
|
)
|
|
|
|
assume_and_permission_boundary_role = next(
|
|
role
|
|
for role in role_config_query.list_config_service_resources(
|
|
DEFAULT_ACCOUNT_ID, None, None, 100, None
|
|
)[0]
|
|
if role["id"] not in [plain_role["id"], assume_role["id"]]
|
|
)
|
|
assert assume_and_permission_boundary_role is not None
|
|
assert len(assume_and_permission_boundary_role["id"]) == len(
|
|
random_role_id(DEFAULT_ACCOUNT_ID)
|
|
)
|
|
assert assume_and_permission_boundary_role["id"] is not plain_role["id"]
|
|
assert assume_and_permission_boundary_role["id"] is not assume_role["id"]
|
|
|
|
role_config_query.backends[DEFAULT_ACCOUNT_ID]["global"].create_role(
|
|
role_name="role_with_attached_policy",
|
|
assume_role_policy_document=json.dumps(basic_assume_role),
|
|
path="/",
|
|
permissions_boundary=None,
|
|
description="role_with_attached_policy",
|
|
tags=[],
|
|
max_session_duration=3600,
|
|
)
|
|
role_config_query.backends[DEFAULT_ACCOUNT_ID]["global"].attach_role_policy(
|
|
policy_arn, "role_with_attached_policy"
|
|
)
|
|
role_with_attached_policy = next(
|
|
role
|
|
for role in role_config_query.list_config_service_resources(
|
|
DEFAULT_ACCOUNT_ID, None, None, 100, None
|
|
)[0]
|
|
if role["id"]
|
|
not in [
|
|
plain_role["id"],
|
|
assume_role["id"],
|
|
assume_and_permission_boundary_role["id"],
|
|
]
|
|
)
|
|
assert role_with_attached_policy is not None
|
|
assert len(role_with_attached_policy["id"]) == len(
|
|
random_role_id(DEFAULT_ACCOUNT_ID)
|
|
)
|
|
assert role_with_attached_policy["id"] is not plain_role["id"]
|
|
assert role_with_attached_policy["id"] is not assume_role["id"]
|
|
assert (
|
|
role_with_attached_policy["id"] is not assume_and_permission_boundary_role["id"]
|
|
)
|
|
|
|
role_config_query.backends[DEFAULT_ACCOUNT_ID]["global"].create_role(
|
|
role_name="role_with_inline_policy",
|
|
assume_role_policy_document=json.dumps(basic_assume_role),
|
|
path="/",
|
|
permissions_boundary=None,
|
|
description="role_with_inline_policy",
|
|
tags=[],
|
|
max_session_duration=3600,
|
|
)
|
|
role_config_query.backends[DEFAULT_ACCOUNT_ID]["global"].put_role_policy(
|
|
"role_with_inline_policy", "inline_policy", json.dumps(basic_policy)
|
|
)
|
|
|
|
role_with_inline_policy = next(
|
|
role
|
|
for role in role_config_query.list_config_service_resources(
|
|
DEFAULT_ACCOUNT_ID, None, None, 100, None
|
|
)[0]
|
|
if role["id"]
|
|
not in [
|
|
plain_role["id"],
|
|
assume_role["id"],
|
|
assume_and_permission_boundary_role["id"],
|
|
role_with_attached_policy["id"],
|
|
]
|
|
)
|
|
assert role_with_inline_policy is not None
|
|
assert len(role_with_inline_policy["id"]) == len(random_role_id(DEFAULT_ACCOUNT_ID))
|
|
assert role_with_inline_policy["id"] is not plain_role["id"]
|
|
assert role_with_inline_policy["id"] is not assume_role["id"]
|
|
assert (
|
|
role_with_inline_policy["id"] is not assume_and_permission_boundary_role["id"]
|
|
)
|
|
assert role_with_inline_policy["id"] is not role_with_attached_policy["id"]
|
|
|
|
# plain role
|
|
plain_role_config = (
|
|
role_config_query.backends[DEFAULT_ACCOUNT_ID]["global"]
|
|
.roles[plain_role["id"]]
|
|
.to_config_dict()
|
|
)
|
|
assert plain_role_config["version"] == "1.3"
|
|
assert plain_role_config["configurationItemStatus"] == "ResourceDiscovered"
|
|
assert plain_role_config["configurationStateId"] is not None
|
|
assert plain_role_config["arn"] == "arn:aws:iam::123456789012:role/plain_role"
|
|
assert plain_role_config["resourceType"] == "AWS::IAM::Role"
|
|
assert plain_role_config["resourceId"] == "plain_role"
|
|
assert plain_role_config["resourceName"] == "plain_role"
|
|
assert plain_role_config["awsRegion"] == "global"
|
|
assert plain_role_config["availabilityZone"] == "Not Applicable"
|
|
assert plain_role_config["resourceCreationTime"] is not None
|
|
assert plain_role_config["tags"] == {"foo": {"Key": "foo", "Value": "bar"}}
|
|
assert plain_role_config["configuration"]["path"] == "/"
|
|
assert plain_role_config["configuration"]["roleName"] == "plain_role"
|
|
assert plain_role_config["configuration"]["roleId"] == plain_role["id"]
|
|
assert plain_role_config["configuration"]["arn"] == plain_role_config["arn"]
|
|
assert plain_role_config["configuration"]["assumeRolePolicyDocument"] is None
|
|
assert plain_role_config["configuration"]["instanceProfileList"] == []
|
|
assert plain_role_config["configuration"]["rolePolicyList"] == []
|
|
assert plain_role_config["configuration"]["attachedManagedPolicies"] == []
|
|
assert plain_role_config["configuration"]["permissionsBoundary"] is None
|
|
assert plain_role_config["configuration"]["tags"] == [
|
|
{"key": "foo", "value": "bar"}
|
|
]
|
|
assert plain_role_config["supplementaryConfiguration"] == {}
|
|
|
|
# assume_role
|
|
assume_role_config = (
|
|
role_config_query.backends[DEFAULT_ACCOUNT_ID]["global"]
|
|
.roles[assume_role["id"]]
|
|
.to_config_dict()
|
|
)
|
|
assert assume_role_config["arn"] == "arn:aws:iam::123456789012:role/assume_role"
|
|
assert assume_role_config["resourceId"] == "assume_role"
|
|
assert assume_role_config["resourceName"] == "assume_role"
|
|
assert assume_role_config["configuration"][
|
|
"assumeRolePolicyDocument"
|
|
] == parse.quote(json.dumps(basic_assume_role))
|
|
|
|
# assume_and_permission_boundary_role
|
|
assume_and_permission_boundary_role_config = (
|
|
role_config_query.backends[DEFAULT_ACCOUNT_ID]["global"]
|
|
.roles[assume_and_permission_boundary_role["id"]]
|
|
.to_config_dict()
|
|
)
|
|
assert (
|
|
assume_and_permission_boundary_role_config["arn"]
|
|
== "arn:aws:iam::123456789012:role/assume_and_permission_boundary_role"
|
|
)
|
|
assert (
|
|
assume_and_permission_boundary_role_config["resourceId"]
|
|
== "assume_and_permission_boundary_role"
|
|
)
|
|
assert (
|
|
assume_and_permission_boundary_role_config["resourceName"]
|
|
== "assume_and_permission_boundary_role"
|
|
)
|
|
assert assume_and_permission_boundary_role_config["configuration"][
|
|
"assumeRolePolicyDocument"
|
|
] == parse.quote(json.dumps(basic_assume_role))
|
|
assert (
|
|
assume_and_permission_boundary_role_config["configuration"][
|
|
"permissionsBoundary"
|
|
]
|
|
== policy_arn
|
|
)
|
|
|
|
# role_with_attached_policy
|
|
role_with_attached_policy_config = (
|
|
role_config_query.backends[DEFAULT_ACCOUNT_ID]["global"]
|
|
.roles[role_with_attached_policy["id"]]
|
|
.to_config_dict()
|
|
)
|
|
assert (
|
|
role_with_attached_policy_config["arn"]
|
|
== "arn:aws:iam::123456789012:role/role_with_attached_policy"
|
|
)
|
|
assert role_with_attached_policy_config["configuration"][
|
|
"attachedManagedPolicies"
|
|
] == [{"policyArn": policy_arn, "policyName": "basic_policy"}]
|
|
|
|
# role_with_inline_policy
|
|
role_with_inline_policy_config = (
|
|
role_config_query.backends[DEFAULT_ACCOUNT_ID]["global"]
|
|
.roles[role_with_inline_policy["id"]]
|
|
.to_config_dict()
|
|
)
|
|
assert (
|
|
role_with_inline_policy_config["arn"]
|
|
== "arn:aws:iam::123456789012:role/role_with_inline_policy"
|
|
)
|
|
assert role_with_inline_policy_config["configuration"]["rolePolicyList"] == [
|
|
{
|
|
"policyName": "inline_policy",
|
|
"policyDocument": parse.quote(json.dumps(basic_policy)),
|
|
}
|
|
]
|
|
|
|
|
|
@mock_iam
|
|
@mock_config
|
|
def test_role_config_client():
|
|
from moto.iam.utils import random_role_id
|
|
|
|
CONFIG_REGIONS = boto3.Session().get_available_regions("config")
|
|
|
|
iam_client = boto3.client("iam", region_name="us-west-2")
|
|
config_client = boto3.client("config", region_name="us-west-2")
|
|
|
|
all_account_aggregation_source = {
|
|
"AccountIds": [ACCOUNT_ID],
|
|
"AllAwsRegions": True,
|
|
}
|
|
|
|
two_region_account_aggregation_source = {
|
|
"AccountIds": [ACCOUNT_ID],
|
|
"AwsRegions": ["us-east-1", "us-west-2"],
|
|
}
|
|
|
|
config_client.put_configuration_aggregator(
|
|
ConfigurationAggregatorName="test_aggregator",
|
|
AccountAggregationSources=[all_account_aggregation_source],
|
|
)
|
|
|
|
config_client.put_configuration_aggregator(
|
|
ConfigurationAggregatorName="test_aggregator_two_regions",
|
|
AccountAggregationSources=[two_region_account_aggregation_source],
|
|
)
|
|
|
|
result = config_client.list_discovered_resources(resourceType="AWS::IAM::Role")
|
|
assert not result["resourceIdentifiers"]
|
|
|
|
# Make 10 policies
|
|
roles = []
|
|
num_roles = 10
|
|
for ix in range(1, num_roles + 1):
|
|
this_policy = iam_client.create_role(
|
|
RoleName=f"role{ix}",
|
|
Path="/",
|
|
Description=f"role{ix}",
|
|
AssumeRolePolicyDocument=json.dumps("{ }"),
|
|
)
|
|
roles.append(
|
|
{
|
|
"id": this_policy["Role"]["RoleId"],
|
|
"name": this_policy["Role"]["RoleName"],
|
|
}
|
|
)
|
|
|
|
assert len(roles) == num_roles
|
|
|
|
# Test non-aggregated query: (everything is getting a random id, so we can't test names by ordering)
|
|
result = config_client.list_discovered_resources(
|
|
resourceType="AWS::IAM::Role", limit=1
|
|
)
|
|
first_result = result["resourceIdentifiers"][0]["resourceId"]
|
|
assert result["resourceIdentifiers"][0]["resourceType"] == "AWS::IAM::Role"
|
|
assert len(first_result) == len(random_role_id(DEFAULT_ACCOUNT_ID))
|
|
|
|
# Test non-aggregated pagination
|
|
assert (
|
|
config_client.list_discovered_resources(
|
|
resourceType="AWS::IAM::Role", limit=1, nextToken=result["nextToken"]
|
|
)["resourceIdentifiers"][0]["resourceId"]
|
|
) != first_result
|
|
|
|
# Test aggregated query - by `Limit=len(CONFIG_REGIONS)`, we should get a single policy duplicated across all regions
|
|
agg_result = config_client.list_aggregate_discovered_resources(
|
|
ResourceType="AWS::IAM::Role",
|
|
ConfigurationAggregatorName="test_aggregator",
|
|
Limit=len(CONFIG_REGIONS),
|
|
)
|
|
assert len(agg_result["ResourceIdentifiers"]) == len(CONFIG_REGIONS)
|
|
|
|
agg_name = None
|
|
agg_id = None
|
|
for resource in agg_result["ResourceIdentifiers"]:
|
|
assert resource["ResourceType"] == "AWS::IAM::Role"
|
|
assert resource["SourceRegion"] in CONFIG_REGIONS
|
|
assert resource["SourceAccountId"] == ACCOUNT_ID
|
|
if agg_id:
|
|
assert resource["ResourceId"] == agg_id
|
|
if agg_name:
|
|
assert resource["ResourceName"] == agg_name
|
|
agg_name = resource["ResourceName"]
|
|
agg_id = resource["ResourceId"]
|
|
|
|
# Test aggregated pagination
|
|
for resource in config_client.list_aggregate_discovered_resources(
|
|
ConfigurationAggregatorName="test_aggregator",
|
|
ResourceType="AWS::IAM::Role",
|
|
NextToken=agg_result["NextToken"],
|
|
)["ResourceIdentifiers"]:
|
|
assert resource["ResourceId"] != agg_id
|
|
|
|
# Test non-aggregated resource name/id filter
|
|
assert (
|
|
config_client.list_discovered_resources(
|
|
resourceType="AWS::IAM::Role", resourceName=roles[1]["name"], limit=1
|
|
)["resourceIdentifiers"][0]["resourceName"]
|
|
== roles[1]["name"]
|
|
)
|
|
|
|
assert (
|
|
config_client.list_discovered_resources(
|
|
resourceType="AWS::IAM::Role", resourceIds=[roles[0]["id"]], limit=1
|
|
)["resourceIdentifiers"][0]["resourceName"]
|
|
== roles[0]["name"]
|
|
)
|
|
|
|
# Test aggregated resource name/id filter
|
|
agg_name_filter = config_client.list_aggregate_discovered_resources(
|
|
ConfigurationAggregatorName="test_aggregator",
|
|
ResourceType="AWS::IAM::Role",
|
|
Filters={"ResourceName": roles[5]["name"]},
|
|
)
|
|
assert len(agg_name_filter["ResourceIdentifiers"]) == len(CONFIG_REGIONS)
|
|
assert agg_name_filter["ResourceIdentifiers"][0]["ResourceId"] == roles[5]["id"]
|
|
|
|
agg_name_filter = config_client.list_aggregate_discovered_resources(
|
|
ConfigurationAggregatorName="test_aggregator_two_regions",
|
|
ResourceType="AWS::IAM::Role",
|
|
Filters={"ResourceName": roles[5]["name"]},
|
|
)
|
|
assert len(agg_name_filter["ResourceIdentifiers"]) == len(
|
|
two_region_account_aggregation_source["AwsRegions"]
|
|
)
|
|
assert agg_name_filter["ResourceIdentifiers"][0]["ResourceId"] == roles[5]["id"]
|
|
|
|
agg_id_filter = config_client.list_aggregate_discovered_resources(
|
|
ConfigurationAggregatorName="test_aggregator",
|
|
ResourceType="AWS::IAM::Role",
|
|
Filters={"ResourceId": roles[4]["id"]},
|
|
)
|
|
|
|
assert len(agg_id_filter["ResourceIdentifiers"]) == len(CONFIG_REGIONS)
|
|
assert agg_id_filter["ResourceIdentifiers"][0]["ResourceName"] == roles[4]["name"]
|
|
|
|
agg_name_filter = config_client.list_aggregate_discovered_resources(
|
|
ConfigurationAggregatorName="test_aggregator_two_regions",
|
|
ResourceType="AWS::IAM::Role",
|
|
Filters={"ResourceId": roles[5]["id"]},
|
|
)
|
|
assert len(agg_name_filter["ResourceIdentifiers"]) == len(
|
|
two_region_account_aggregation_source["AwsRegions"]
|
|
)
|
|
assert agg_name_filter["ResourceIdentifiers"][0]["ResourceName"] == roles[5]["name"]
|
|
|
|
# Test non-aggregated resource name/id filter
|
|
assert (
|
|
config_client.list_discovered_resources(
|
|
resourceType="AWS::IAM::Role", resourceName=roles[1]["name"], limit=1
|
|
)["resourceIdentifiers"][0]["resourceName"]
|
|
== roles[1]["name"]
|
|
)
|
|
assert (
|
|
config_client.list_discovered_resources(
|
|
resourceType="AWS::IAM::Role", resourceIds=[roles[0]["id"]], limit=1
|
|
)["resourceIdentifiers"][0]["resourceName"]
|
|
== roles[0]["name"]
|
|
)
|
|
|
|
# Test aggregated resource name/id filter
|
|
assert (
|
|
config_client.list_aggregate_discovered_resources(
|
|
ConfigurationAggregatorName="test_aggregator",
|
|
ResourceType="AWS::IAM::Role",
|
|
Filters={"ResourceName": roles[5]["name"]},
|
|
Limit=1,
|
|
)["ResourceIdentifiers"][0]["ResourceName"]
|
|
== roles[5]["name"]
|
|
)
|
|
|
|
assert (
|
|
config_client.list_aggregate_discovered_resources(
|
|
ConfigurationAggregatorName="test_aggregator",
|
|
ResourceType="AWS::IAM::Role",
|
|
Filters={"ResourceId": roles[4]["id"]},
|
|
Limit=1,
|
|
)["ResourceIdentifiers"][0]["ResourceName"]
|
|
== roles[4]["name"]
|
|
)
|
|
|
|
# Test name/id filter with pagination
|
|
first_call = config_client.list_discovered_resources(
|
|
resourceType="AWS::IAM::Role",
|
|
resourceIds=[roles[1]["id"], roles[2]["id"]],
|
|
limit=1,
|
|
)
|
|
|
|
assert first_call["nextToken"] in [roles[1]["id"], roles[2]["id"]]
|
|
assert first_call["resourceIdentifiers"][0]["resourceName"] in [
|
|
roles[1]["name"],
|
|
roles[2]["name"],
|
|
]
|
|
second_call = config_client.list_discovered_resources(
|
|
resourceType="AWS::IAM::Role",
|
|
resourceIds=[roles[1]["id"], roles[2]["id"]],
|
|
limit=1,
|
|
nextToken=first_call["nextToken"],
|
|
)
|
|
assert "nextToken" not in second_call
|
|
assert first_call["resourceIdentifiers"][0]["resourceName"] in [
|
|
roles[1]["name"],
|
|
roles[2]["name"],
|
|
]
|
|
assert (
|
|
first_call["resourceIdentifiers"][0]["resourceName"]
|
|
!= second_call["resourceIdentifiers"][0]["resourceName"]
|
|
)
|
|
|
|
# Test non-aggregated batch get
|
|
assert (
|
|
config_client.batch_get_resource_config(
|
|
resourceKeys=[
|
|
{"resourceType": "AWS::IAM::Role", "resourceId": roles[0]["id"]}
|
|
]
|
|
)["baseConfigurationItems"][0]["resourceName"]
|
|
== roles[0]["name"]
|
|
)
|
|
|
|
# Test aggregated batch get
|
|
assert (
|
|
config_client.batch_get_aggregate_resource_config(
|
|
ConfigurationAggregatorName="test_aggregator",
|
|
ResourceIdentifiers=[
|
|
{
|
|
"SourceAccountId": ACCOUNT_ID,
|
|
"SourceRegion": "us-east-1",
|
|
"ResourceId": roles[1]["id"],
|
|
"ResourceType": "AWS::IAM::Role",
|
|
}
|
|
],
|
|
)["BaseConfigurationItems"][0]["resourceName"]
|
|
== roles[1]["name"]
|
|
)
|
|
|
|
|
|
@mock_iam
|
|
def test_policy_list_config_discovered_resources():
|
|
from moto.iam.config import policy_config_query
|
|
|
|
# Without any policies
|
|
assert policy_config_query.list_config_service_resources(
|
|
DEFAULT_ACCOUNT_ID, None, None, 100, None
|
|
) == (
|
|
[],
|
|
None,
|
|
)
|
|
|
|
basic_policy = {
|
|
"Version": "2012-10-17",
|
|
"Statement": [
|
|
{"Action": ["ec2:DeleteKeyPair"], "Effect": "Deny", "Resource": "*"}
|
|
],
|
|
}
|
|
|
|
# Make 3 policies
|
|
policies = []
|
|
num_policies = 3
|
|
for ix in range(1, num_policies + 1):
|
|
this_policy = policy_config_query.backends[DEFAULT_ACCOUNT_ID][
|
|
"global"
|
|
].create_policy(
|
|
description=f"policy{ix}",
|
|
path="",
|
|
policy_document=json.dumps(basic_policy),
|
|
policy_name=f"policy{ix}",
|
|
tags=[],
|
|
)
|
|
policies.append({"id": this_policy.id, "name": this_policy.name})
|
|
|
|
assert len(policies) == num_policies
|
|
|
|
# We expect the backend to have arns as their keys
|
|
for backend_key in list(
|
|
policy_config_query.backends[DEFAULT_ACCOUNT_ID][
|
|
"global"
|
|
].managed_policies.keys()
|
|
):
|
|
assert backend_key.startswith("arn:aws:iam::")
|
|
|
|
result = policy_config_query.list_config_service_resources(
|
|
DEFAULT_ACCOUNT_ID, None, None, 100, None
|
|
)[0]
|
|
assert len(result) == num_policies
|
|
|
|
policy = result[0]
|
|
assert policy["type"] == "AWS::IAM::Policy"
|
|
assert policy["id"] in list(map(lambda p: p["id"], policies))
|
|
assert policy["name"] in list(map(lambda p: p["name"], policies))
|
|
assert policy["region"] == "global"
|
|
|
|
# test passing list of resource ids
|
|
resource_ids = policy_config_query.list_config_service_resources(
|
|
DEFAULT_ACCOUNT_ID, [policies[0]["id"], policies[1]["id"]], None, 100, None
|
|
)[0]
|
|
assert len(resource_ids) == 2
|
|
|
|
# test passing a single resource name
|
|
resource_name = policy_config_query.list_config_service_resources(
|
|
DEFAULT_ACCOUNT_ID, None, policies[0]["name"], 100, None
|
|
)[0]
|
|
assert len(resource_name) == 1
|
|
assert resource_name[0]["id"] == policies[0]["id"]
|
|
assert resource_name[0]["name"] == policies[0]["name"]
|
|
|
|
# test passing a single resource name AND some resource id's
|
|
both_filter_good = policy_config_query.list_config_service_resources(
|
|
DEFAULT_ACCOUNT_ID,
|
|
[policies[0]["id"], policies[1]["id"]],
|
|
policies[0]["name"],
|
|
100,
|
|
None,
|
|
)[0]
|
|
assert len(both_filter_good) == 1
|
|
assert both_filter_good[0]["id"] == policies[0]["id"]
|
|
assert both_filter_good[0]["name"] == policies[0]["name"]
|
|
|
|
both_filter_bad = policy_config_query.list_config_service_resources(
|
|
DEFAULT_ACCOUNT_ID,
|
|
[policies[0]["id"], policies[1]["id"]],
|
|
policies[2]["name"],
|
|
100,
|
|
None,
|
|
)[0]
|
|
assert len(both_filter_bad) == 0
|
|
|
|
|
|
@mock_iam
|
|
def test_policy_config_dict():
|
|
from moto.iam.config import policy_config_query, role_config_query
|
|
from moto.iam.utils import random_policy_id
|
|
|
|
# Without any roles
|
|
assert not policy_config_query.get_config_resource(
|
|
DEFAULT_ACCOUNT_ID, "arn:aws:iam::123456789012:policy/basic_policy"
|
|
)
|
|
assert policy_config_query.list_config_service_resources(
|
|
DEFAULT_ACCOUNT_ID, None, None, 100, None
|
|
) == (
|
|
[],
|
|
None,
|
|
)
|
|
|
|
basic_policy = {
|
|
"Version": "2012-10-17",
|
|
"Statement": [{"Action": ["ec2:*"], "Effect": "Allow", "Resource": "*"}],
|
|
}
|
|
|
|
basic_policy_v2 = {
|
|
"Version": "2012-10-17",
|
|
"Statement": [
|
|
{"Action": ["ec2:*", "s3:*"], "Effect": "Allow", "Resource": "*"}
|
|
],
|
|
}
|
|
|
|
policy_arn = (
|
|
policy_config_query.backends[DEFAULT_ACCOUNT_ID]["global"]
|
|
.create_policy(
|
|
description="basic_policy",
|
|
path="/",
|
|
policy_document=json.dumps(basic_policy),
|
|
policy_name="basic_policy",
|
|
tags=[],
|
|
)
|
|
.arn
|
|
)
|
|
|
|
policy_id = policy_config_query.list_config_service_resources(
|
|
DEFAULT_ACCOUNT_ID, None, None, 100, None
|
|
)[0][0]["id"]
|
|
assert len(policy_id) == len(random_policy_id())
|
|
|
|
assert policy_arn == "arn:aws:iam::123456789012:policy/basic_policy"
|
|
assert (
|
|
policy_config_query.get_config_resource(DEFAULT_ACCOUNT_ID, policy_id)
|
|
is not None
|
|
)
|
|
|
|
# Create a new version
|
|
policy_config_query.backends[DEFAULT_ACCOUNT_ID]["global"].create_policy_version(
|
|
policy_arn, json.dumps(basic_policy_v2), "true"
|
|
)
|
|
|
|
# Create role to trigger attachment
|
|
role_config_query.backends[DEFAULT_ACCOUNT_ID]["global"].create_role(
|
|
role_name="role_with_attached_policy",
|
|
assume_role_policy_document=None,
|
|
path="/",
|
|
permissions_boundary=None,
|
|
description="role_with_attached_policy",
|
|
tags=[],
|
|
max_session_duration=3600,
|
|
)
|
|
role_config_query.backends[DEFAULT_ACCOUNT_ID]["global"].attach_role_policy(
|
|
policy_arn, "role_with_attached_policy"
|
|
)
|
|
|
|
policy = (
|
|
role_config_query.backends[DEFAULT_ACCOUNT_ID]["global"]
|
|
.managed_policies["arn:aws:iam::123456789012:policy/basic_policy"]
|
|
.to_config_dict()
|
|
)
|
|
assert policy["version"] == "1.3"
|
|
assert policy["configurationItemCaptureTime"] is not None
|
|
assert policy["configurationItemStatus"] == "OK"
|
|
assert policy["configurationStateId"] is not None
|
|
assert policy["arn"] == "arn:aws:iam::123456789012:policy/basic_policy"
|
|
assert policy["resourceType"] == "AWS::IAM::Policy"
|
|
assert len(policy["resourceId"]) == len(random_policy_id())
|
|
assert policy["resourceName"] == "basic_policy"
|
|
assert policy["awsRegion"] == "global"
|
|
assert policy["availabilityZone"] == "Not Applicable"
|
|
assert policy["resourceCreationTime"] is not None
|
|
assert policy["configuration"]["policyName"] == policy["resourceName"]
|
|
assert policy["configuration"]["policyId"] == policy["resourceId"]
|
|
assert policy["configuration"]["arn"] == policy["arn"]
|
|
assert policy["configuration"]["path"] == "/"
|
|
assert policy["configuration"]["defaultVersionId"] == "v2"
|
|
assert policy["configuration"]["attachmentCount"] == 1
|
|
assert policy["configuration"]["permissionsBoundaryUsageCount"] == 0
|
|
assert policy["configuration"]["isAttachable"] is True
|
|
assert policy["configuration"]["description"] == "basic_policy"
|
|
assert policy["configuration"]["createDate"] is not None
|
|
assert policy["configuration"]["updateDate"] is not None
|
|
assert policy["configuration"]["policyVersionList"] == [
|
|
{
|
|
"document": str(parse.quote(json.dumps(basic_policy))),
|
|
"versionId": "v1",
|
|
"isDefaultVersion": False,
|
|
"createDate": policy["configuration"]["policyVersionList"][0]["createDate"],
|
|
},
|
|
{
|
|
"document": str(parse.quote(json.dumps(basic_policy_v2))),
|
|
"versionId": "v2",
|
|
"isDefaultVersion": True,
|
|
"createDate": policy["configuration"]["policyVersionList"][1]["createDate"],
|
|
},
|
|
]
|
|
assert policy["supplementaryConfiguration"] == {}
|
|
|
|
|
|
@mock_iam
|
|
@mock_config
|
|
def test_policy_config_client():
|
|
from moto.iam.utils import random_policy_id
|
|
|
|
CONFIG_REGIONS = boto3.Session().get_available_regions("config")
|
|
|
|
basic_policy = {
|
|
"Version": "2012-10-17",
|
|
"Statement": [{"Action": ["ec2:*"], "Effect": "Allow", "Resource": "*"}],
|
|
}
|
|
|
|
iam_client = boto3.client("iam", region_name="us-west-2")
|
|
config_client = boto3.client("config", region_name="us-west-2")
|
|
|
|
all_account_aggregation_source = {
|
|
"AccountIds": [ACCOUNT_ID],
|
|
"AllAwsRegions": True,
|
|
}
|
|
|
|
two_region_account_aggregation_source = {
|
|
"AccountIds": [ACCOUNT_ID],
|
|
"AwsRegions": ["us-east-1", "us-west-2"],
|
|
}
|
|
|
|
config_client.put_configuration_aggregator(
|
|
ConfigurationAggregatorName="test_aggregator",
|
|
AccountAggregationSources=[all_account_aggregation_source],
|
|
)
|
|
|
|
config_client.put_configuration_aggregator(
|
|
ConfigurationAggregatorName="test_aggregator_two_regions",
|
|
AccountAggregationSources=[two_region_account_aggregation_source],
|
|
)
|
|
|
|
result = config_client.list_discovered_resources(resourceType="AWS::IAM::Policy")
|
|
assert not result["resourceIdentifiers"]
|
|
|
|
# Make 10 policies
|
|
policies = []
|
|
num_policies = 10
|
|
for ix in range(1, num_policies + 1):
|
|
this_policy = iam_client.create_policy(
|
|
PolicyName=f"policy{ix}",
|
|
Path="/",
|
|
PolicyDocument=json.dumps(basic_policy),
|
|
Description=f"policy{ix}",
|
|
)
|
|
policies.append(
|
|
{
|
|
"id": this_policy["Policy"]["PolicyId"],
|
|
"name": this_policy["Policy"]["PolicyName"],
|
|
}
|
|
)
|
|
|
|
assert len(policies) == num_policies
|
|
|
|
# Test non-aggregated query: (everything is getting a random id, so we can't test names by ordering)
|
|
result = config_client.list_discovered_resources(
|
|
resourceType="AWS::IAM::Policy", limit=1
|
|
)
|
|
first_result = result["resourceIdentifiers"][0]["resourceId"]
|
|
assert result["resourceIdentifiers"][0]["resourceType"] == "AWS::IAM::Policy"
|
|
assert len(first_result) == len(random_policy_id())
|
|
|
|
# Test non-aggregated pagination
|
|
assert (
|
|
config_client.list_discovered_resources(
|
|
resourceType="AWS::IAM::Policy", limit=1, nextToken=result["nextToken"]
|
|
)["resourceIdentifiers"][0]["resourceId"]
|
|
) != first_result
|
|
|
|
# Test aggregated query - by `Limit=len(CONFIG_REGIONS)`, we should get a single policy duplicated across all regions
|
|
agg_result = config_client.list_aggregate_discovered_resources(
|
|
ResourceType="AWS::IAM::Policy",
|
|
ConfigurationAggregatorName="test_aggregator",
|
|
Limit=len(CONFIG_REGIONS),
|
|
)
|
|
assert len(agg_result["ResourceIdentifiers"]) == len(CONFIG_REGIONS)
|
|
|
|
agg_name = None
|
|
agg_id = None
|
|
for resource in agg_result["ResourceIdentifiers"]:
|
|
assert resource["ResourceType"] == "AWS::IAM::Policy"
|
|
assert resource["SourceRegion"] in CONFIG_REGIONS
|
|
assert resource["SourceAccountId"] == ACCOUNT_ID
|
|
if agg_id:
|
|
assert resource["ResourceId"] == agg_id
|
|
if agg_name:
|
|
assert resource["ResourceName"] == agg_name
|
|
agg_name = resource["ResourceName"]
|
|
agg_id = resource["ResourceId"]
|
|
|
|
# Test aggregated pagination
|
|
for resource in config_client.list_aggregate_discovered_resources(
|
|
ConfigurationAggregatorName="test_aggregator",
|
|
ResourceType="AWS::IAM::Policy",
|
|
Limit=1,
|
|
NextToken=agg_result["NextToken"],
|
|
)["ResourceIdentifiers"]:
|
|
assert resource["ResourceId"] != agg_id
|
|
|
|
# Test non-aggregated resource name/id filter
|
|
assert (
|
|
config_client.list_discovered_resources(
|
|
resourceType="AWS::IAM::Policy", resourceName=policies[1]["name"], limit=1
|
|
)["resourceIdentifiers"][0]["resourceName"]
|
|
== policies[1]["name"]
|
|
)
|
|
|
|
assert (
|
|
config_client.list_discovered_resources(
|
|
resourceType="AWS::IAM::Policy", resourceIds=[policies[0]["id"]], limit=1
|
|
)["resourceIdentifiers"][0]["resourceName"]
|
|
== policies[0]["name"]
|
|
)
|
|
|
|
# Test aggregated resource name/id filter
|
|
agg_name_filter = config_client.list_aggregate_discovered_resources(
|
|
ConfigurationAggregatorName="test_aggregator",
|
|
ResourceType="AWS::IAM::Policy",
|
|
Filters={"ResourceName": policies[5]["name"]},
|
|
)
|
|
assert len(agg_name_filter["ResourceIdentifiers"]) == len(CONFIG_REGIONS)
|
|
assert (
|
|
agg_name_filter["ResourceIdentifiers"][0]["ResourceName"] == policies[5]["name"]
|
|
)
|
|
|
|
agg_name_filter = config_client.list_aggregate_discovered_resources(
|
|
ConfigurationAggregatorName="test_aggregator_two_regions",
|
|
ResourceType="AWS::IAM::Policy",
|
|
Filters={"ResourceName": policies[5]["name"]},
|
|
)
|
|
assert len(agg_name_filter["ResourceIdentifiers"]) == len(
|
|
two_region_account_aggregation_source["AwsRegions"]
|
|
)
|
|
assert agg_name_filter["ResourceIdentifiers"][0]["ResourceId"] == policies[5]["id"]
|
|
|
|
agg_id_filter = config_client.list_aggregate_discovered_resources(
|
|
ConfigurationAggregatorName="test_aggregator",
|
|
ResourceType="AWS::IAM::Policy",
|
|
Filters={"ResourceId": policies[4]["id"]},
|
|
)
|
|
|
|
assert len(agg_id_filter["ResourceIdentifiers"]) == len(CONFIG_REGIONS)
|
|
assert (
|
|
agg_id_filter["ResourceIdentifiers"][0]["ResourceName"] == policies[4]["name"]
|
|
)
|
|
|
|
agg_name_filter = config_client.list_aggregate_discovered_resources(
|
|
ConfigurationAggregatorName="test_aggregator_two_regions",
|
|
ResourceType="AWS::IAM::Policy",
|
|
Filters={"ResourceId": policies[5]["id"]},
|
|
)
|
|
assert len(agg_name_filter["ResourceIdentifiers"]) == len(
|
|
two_region_account_aggregation_source["AwsRegions"]
|
|
)
|
|
assert (
|
|
agg_name_filter["ResourceIdentifiers"][0]["ResourceName"] == policies[5]["name"]
|
|
)
|
|
|
|
# Test name/id filter with pagination
|
|
first_call = config_client.list_discovered_resources(
|
|
resourceType="AWS::IAM::Policy",
|
|
resourceIds=[policies[1]["id"], policies[2]["id"]],
|
|
limit=1,
|
|
)
|
|
|
|
assert first_call["nextToken"] in [policies[1]["id"], policies[2]["id"]]
|
|
assert first_call["resourceIdentifiers"][0]["resourceName"] in [
|
|
policies[1]["name"],
|
|
policies[2]["name"],
|
|
]
|
|
second_call = config_client.list_discovered_resources(
|
|
resourceType="AWS::IAM::Policy",
|
|
resourceIds=[policies[1]["id"], policies[2]["id"]],
|
|
limit=1,
|
|
nextToken=first_call["nextToken"],
|
|
)
|
|
assert "nextToken" not in second_call
|
|
assert first_call["resourceIdentifiers"][0]["resourceName"] in [
|
|
policies[1]["name"],
|
|
policies[2]["name"],
|
|
]
|
|
assert (
|
|
first_call["resourceIdentifiers"][0]["resourceName"]
|
|
!= second_call["resourceIdentifiers"][0]["resourceName"]
|
|
)
|
|
|
|
# Test non-aggregated batch get
|
|
assert (
|
|
config_client.batch_get_resource_config(
|
|
resourceKeys=[
|
|
{"resourceType": "AWS::IAM::Policy", "resourceId": policies[7]["id"]}
|
|
]
|
|
)["baseConfigurationItems"][0]["resourceName"]
|
|
== policies[7]["name"]
|
|
)
|
|
|
|
# Test aggregated batch get
|
|
assert (
|
|
config_client.batch_get_aggregate_resource_config(
|
|
ConfigurationAggregatorName="test_aggregator",
|
|
ResourceIdentifiers=[
|
|
{
|
|
"SourceAccountId": ACCOUNT_ID,
|
|
"SourceRegion": "us-east-2",
|
|
"ResourceId": policies[8]["id"],
|
|
"ResourceType": "AWS::IAM::Policy",
|
|
}
|
|
],
|
|
)["BaseConfigurationItems"][0]["resourceName"]
|
|
== policies[8]["name"]
|
|
)
|
|
|
|
|
|
@mock_iam()
|
|
def test_list_roles_with_more_than_100_roles_no_max_items_defaults_to_100():
|
|
iam = boto3.client("iam", region_name="us-east-1")
|
|
for i in range(150):
|
|
iam.create_role(
|
|
RoleName=f"test_role_{i}", AssumeRolePolicyDocument="some policy"
|
|
)
|
|
response = iam.list_roles()
|
|
roles = response["Roles"]
|
|
|
|
assert response["IsTruncated"] is True
|
|
assert len(roles) == 100
|
|
|
|
|
|
@mock_iam()
|
|
def test_list_roles_max_item_and_marker_values_adhered():
|
|
iam = boto3.client("iam", region_name="us-east-1")
|
|
for i in range(10):
|
|
iam.create_role(
|
|
RoleName=f"test_role_{i}", AssumeRolePolicyDocument="some policy"
|
|
)
|
|
response = iam.list_roles(MaxItems=2)
|
|
roles = response["Roles"]
|
|
|
|
assert response["IsTruncated"] is True
|
|
assert len(roles) == 2
|
|
|
|
response = iam.list_roles(Marker=response["Marker"])
|
|
roles = response["Roles"]
|
|
|
|
assert response["IsTruncated"] is False
|
|
assert len(roles) == 8
|
|
|
|
|
|
@mock_iam()
|
|
def test_list_roles_path_prefix_value_adhered():
|
|
iam = boto3.client("iam", region_name="us-east-1")
|
|
iam.create_role(
|
|
RoleName="test_role_without_path", AssumeRolePolicyDocument="some policy"
|
|
)
|
|
iam.create_role(
|
|
RoleName="test_role_with_path",
|
|
AssumeRolePolicyDocument="some policy",
|
|
Path="/TestPath/",
|
|
)
|
|
|
|
response = iam.list_roles(PathPrefix="/TestPath/")
|
|
roles = response["Roles"]
|
|
|
|
assert len(roles) == 1
|
|
assert roles[0]["RoleName"] == "test_role_with_path"
|
|
|
|
|
|
@mock_iam()
|
|
def test_list_roles_none_found_returns_empty_list():
|
|
iam = boto3.client("iam", region_name="us-east-1")
|
|
|
|
response = iam.list_roles()
|
|
roles = response["Roles"]
|
|
assert len(roles) == 0
|
|
|
|
response = iam.list_roles(PathPrefix="/TestPath")
|
|
roles = response["Roles"]
|
|
assert len(roles) == 0
|
|
|
|
response = iam.list_roles(Marker="10")
|
|
roles = response["Roles"]
|
|
assert len(roles) == 0
|
|
|
|
response = iam.list_roles(MaxItems=10)
|
|
roles = response["Roles"]
|
|
assert len(roles) == 0
|
|
|
|
|
|
@mock_iam()
|
|
def test_list_roles():
|
|
conn = boto3.client("iam", region_name="us-east-1")
|
|
for desc in ["", "desc"]:
|
|
resp = conn.create_role(
|
|
RoleName=f"role_{desc}",
|
|
AssumeRolePolicyDocument="some policy",
|
|
Description=desc,
|
|
)
|
|
assert resp["Role"]["Description"] == desc
|
|
conn.create_role(RoleName="role3", AssumeRolePolicyDocument="sp")
|
|
|
|
# Ensure the Description is included in role listing as well
|
|
all_roles = conn.list_roles()["Roles"]
|
|
|
|
role1 = next(r for r in all_roles if r["RoleName"] == "role_")
|
|
role2 = next(r for r in all_roles if r["RoleName"] == "role_desc")
|
|
role3 = next(r for r in all_roles if r["RoleName"] == "role3")
|
|
assert role1["Description"] == ""
|
|
assert role2["Description"] == "desc"
|
|
assert "Description" not in role3
|
|
|
|
assert all([role["CreateDate"] for role in all_roles])
|
|
assert all([role["MaxSessionDuration"] for role in all_roles])
|
|
|
|
|
|
@mock_iam()
|
|
def test_create_user_with_tags():
|
|
conn = boto3.client("iam", region_name="us-east-1")
|
|
user_name = "test-user"
|
|
tags = [
|
|
{"Key": "somekey", "Value": "somevalue"},
|
|
{"Key": "someotherkey", "Value": "someothervalue"},
|
|
]
|
|
resp = conn.create_user(UserName=user_name, Tags=tags)
|
|
assert resp["User"]["Tags"] == tags
|
|
resp = conn.list_user_tags(UserName=user_name)
|
|
assert resp["Tags"] == tags
|
|
resp = conn.get_user(UserName=user_name)
|
|
assert resp["User"]["Tags"] == tags
|
|
resp = conn.create_user(UserName="test-create-user-no-tags")
|
|
assert "Tags" not in resp["User"]
|
|
|
|
|
|
@mock_iam
|
|
def test_tag_user():
|
|
# given
|
|
client = boto3.client("iam", region_name="eu-central-1")
|
|
name = "test-user"
|
|
tags = sorted(
|
|
[{"Key": "key", "Value": "value"}, {"Key": "key-2", "Value": "value-2"}],
|
|
key=lambda item: item["Key"],
|
|
)
|
|
client.create_user(UserName=name)
|
|
|
|
# when
|
|
client.tag_user(UserName=name, Tags=tags)
|
|
|
|
# then
|
|
response = client.list_user_tags(UserName=name)
|
|
assert sorted(response["Tags"], key=lambda item: item["Key"]) == tags
|
|
|
|
|
|
@mock_iam
|
|
def test_tag_user_error_unknown_user_name():
|
|
# given
|
|
client = boto3.client("iam", region_name="eu-central-1")
|
|
name = "unknown"
|
|
|
|
# when
|
|
with pytest.raises(ClientError) as e:
|
|
client.tag_user(UserName=name, Tags=[{"Key": "key", "Value": "value"}])
|
|
|
|
# then
|
|
ex = e.value
|
|
assert ex.operation_name == "TagUser"
|
|
assert ex.response["ResponseMetadata"]["HTTPStatusCode"] == 404
|
|
assert "NoSuchEntity" in ex.response["Error"]["Code"]
|
|
assert (
|
|
ex.response["Error"]["Message"] == f"The user with name {name} cannot be found."
|
|
)
|
|
|
|
|
|
@mock_iam
|
|
def test_untag_user():
|
|
# given
|
|
client = boto3.client("iam", region_name="eu-central-1")
|
|
name = "test-user"
|
|
client.create_user(
|
|
UserName=name,
|
|
Tags=[{"Key": "key", "Value": "value"}, {"Key": "key-2", "Value": "value"}],
|
|
)
|
|
|
|
# when
|
|
client.untag_user(UserName=name, TagKeys=["key-2"])
|
|
|
|
# then
|
|
response = client.list_user_tags(UserName=name)
|
|
assert response["Tags"] == [{"Key": "key", "Value": "value"}]
|
|
|
|
|
|
@mock_iam
|
|
def test_untag_user_error_unknown_user_name():
|
|
# given
|
|
client = boto3.client("iam", region_name="eu-central-1")
|
|
name = "unknown"
|
|
|
|
# when
|
|
with pytest.raises(ClientError) as e:
|
|
client.untag_user(UserName=name, TagKeys=["key"])
|
|
|
|
# then
|
|
ex = e.value
|
|
assert ex.operation_name == "UntagUser"
|
|
assert ex.response["ResponseMetadata"]["HTTPStatusCode"] == 404
|
|
assert "NoSuchEntity" in ex.response["Error"]["Code"]
|
|
assert (
|
|
ex.response["Error"]["Message"] == f"The user with name {name} cannot be found."
|
|
)
|
|
|
|
|
|
@mock_iam
|
|
@pytest.mark.parametrize(
|
|
"service,cased",
|
|
[
|
|
("autoscaling", "AutoScaling"),
|
|
("elasticbeanstalk", "ElasticBeanstalk"),
|
|
(
|
|
"custom-resource.application-autoscaling",
|
|
"ApplicationAutoScaling_CustomResource",
|
|
),
|
|
("other", "other"),
|
|
],
|
|
)
|
|
def test_create_service_linked_role(service, cased):
|
|
client = boto3.client("iam", region_name="eu-central-1")
|
|
|
|
resp = client.create_service_linked_role(
|
|
AWSServiceName=f"{service}.amazonaws.com", Description="desc"
|
|
)["Role"]
|
|
|
|
assert resp["RoleName"] == f"AWSServiceRoleFor{cased}"
|
|
|
|
|
|
@mock_iam
|
|
def test_create_service_linked_role__with_suffix():
|
|
client = boto3.client("iam", region_name="eu-central-1")
|
|
|
|
resp = client.create_service_linked_role(
|
|
AWSServiceName="autoscaling.amazonaws.com",
|
|
CustomSuffix="suf",
|
|
Description="desc",
|
|
)["Role"]
|
|
|
|
assert resp["RoleName"].endswith("_suf")
|
|
assert resp["Description"] == "desc"
|
|
policy_doc = resp["AssumeRolePolicyDocument"]
|
|
assert policy_doc["Statement"] == [
|
|
{
|
|
"Action": ["sts:AssumeRole"],
|
|
"Effect": "Allow",
|
|
"Principal": {"Service": ["autoscaling.amazonaws.com"]},
|
|
}
|
|
]
|
|
|
|
|
|
@mock_iam
|
|
def test_delete_service_linked_role():
|
|
client = boto3.client("iam", region_name="eu-central-1")
|
|
|
|
role_name = client.create_service_linked_role(
|
|
AWSServiceName="autoscaling.amazonaws.com",
|
|
CustomSuffix="suf",
|
|
Description="desc",
|
|
)["Role"]["RoleName"]
|
|
|
|
# Role exists
|
|
client.get_role(RoleName=role_name)
|
|
|
|
# Delete role
|
|
resp = client.delete_service_linked_role(RoleName=role_name)
|
|
|
|
# Role deletion should be successful
|
|
resp = client.get_service_linked_role_deletion_status(
|
|
DeletionTaskId=resp["DeletionTaskId"]
|
|
)
|
|
assert resp["Status"] == "SUCCEEDED"
|
|
|
|
# Role no longer exists
|
|
with pytest.raises(ClientError) as ex:
|
|
client.get_role(RoleName=role_name)
|
|
err = ex.value.response["Error"]
|
|
assert err["Code"] == "NoSuchEntity"
|
|
assert "not found" in err["Message"]
|