]> git.apps.os.sepia.ceph.com Git - s3-tests.git/commitdiff
iam: add tests for User apis
authorCasey Bodley <cbodley@redhat.com>
Tue, 19 Dec 2023 15:26:19 +0000 (10:26 -0500)
committerCasey Bodley <cbodley@redhat.com>
Tue, 16 Apr 2024 15:24:49 +0000 (11:24 -0400)
adds test cases for the following iam actions:
* CreateUser
* GetUser
* UpdateUser
* DeleteUser
* ListUsers

verified to pass against aws when an account root user's credentials are
provided in the [iam] section of s3tests.conf

Signed-off-by: Casey Bodley <cbodley@redhat.com>
(cherry picked from commit d8becad96a24f260b50d06f7397a615c1ab52ed2)

pytest.ini
s3tests_boto3/functional/test_iam.py

index 824adeca31f0c7a8d4aace8ffe47b80c49b024e4..dcaa0f7a55587945a7556bd05f552c943e50e153 100644 (file)
@@ -16,7 +16,9 @@ markers =
     fails_on_rgw
     fails_on_s3
     fails_with_subdomain
+    iam_account
     iam_tenant
+    iam_user
     lifecycle
     lifecycle_expiration
     lifecycle_transition
index 195ce80f2394c35868332b485fb76edf96c1db1d..8213627aca8742ef5d74186e8a4d624ee62464af 100644 (file)
@@ -1,4 +1,5 @@
 import json
+import datetime
 
 from botocore.exceptions import ClientError
 import pytest
@@ -10,6 +11,9 @@ from . import (
     setup_teardown,
     get_alt_client,
     get_iam_client,
+    get_iam_root_client,
+    make_iam_name,
+    get_iam_path_prefix,
     get_new_bucket,
     get_iam_s3client,
     get_alt_iam_client,
@@ -862,3 +866,219 @@ def test_verify_allow_iam_actions():
     response = iam_client_alt.delete_user_policy(PolicyName='AllowAccessPolicy',
                                           UserName=get_alt_user_id())
     assert response['ResponseMetadata']['HTTPStatusCode'] == 200
+
+
+def nuke_user(client, name):
+    # delete access keys, user policies, etc
+    client.delete_user(UserName=name)
+
+def nuke_users(client, **kwargs):
+    p = client.get_paginator('list_users')
+    for response in p.paginate(**kwargs):
+        for user in response['Users']:
+            try:
+                nuke_user(client, user['UserName'])
+            except:
+                pass
+
+# fixture for iam account root user
+@pytest.fixture
+def iam_root(configfile):
+    client = get_iam_root_client()
+    try:
+        arn = client.get_user()['User']['Arn']
+        if not arn.endswith(':root'):
+            pytest.skip('[iam root] user does not have :root arn')
+    except ClientError as e:
+        pytest.skip('[iam root] user does not belong to an account')
+
+    yield client
+    nuke_users(client, PathPrefix=get_iam_path_prefix())
+
+
+# IAM User apis
+@pytest.mark.iam_account
+@pytest.mark.iam_user
+def test_account_user_create(iam_root):
+    path = get_iam_path_prefix()
+    name1 = make_iam_name('U1')
+    response = iam_root.create_user(UserName=name1, Path=path)
+    user = response['User']
+    assert user['Path'] == path
+    assert user['UserName'] == name1
+    assert len(user['UserId'])
+    assert user['Arn'].startswith('arn:aws:iam:')
+    assert user['Arn'].endswith(f':user{path}{name1}')
+    assert user['CreateDate'] > datetime.datetime(1970, 1, 1, tzinfo=datetime.timezone.utc)
+
+    path2 = get_iam_path_prefix() + 'foo/'
+    with pytest.raises(iam_root.exceptions.EntityAlreadyExistsException):
+        iam_root.create_user(UserName=name1, Path=path2)
+
+    name2 = make_iam_name('U2')
+    response = iam_root.create_user(UserName=name2, Path=path2)
+    user = response['User']
+    assert user['Path'] == path2
+    assert user['UserName'] == name2
+
+@pytest.mark.iam_account
+@pytest.mark.iam_user
+def test_account_user_case_insensitive_name(iam_root):
+    path = get_iam_path_prefix()
+    name_upper = make_iam_name('U1')
+    name_lower = make_iam_name('u1')
+    response = iam_root.create_user(UserName=name_upper, Path=path)
+    user = response['User']
+
+    # name is case-insensitive, so 'u1' should also conflict
+    with pytest.raises(iam_root.exceptions.EntityAlreadyExistsException):
+        iam_root.create_user(UserName=name_lower)
+
+    # search for 'u1' should return the same 'U1' user
+    response = iam_root.get_user(UserName=name_lower)
+    assert user == response['User']
+
+    # delete for 'u1' should delete the same 'U1' user
+    iam_root.delete_user(UserName=name_lower)
+
+    with pytest.raises(iam_root.exceptions.NoSuchEntityException):
+        iam_root.get_user(UserName=name_lower)
+
+@pytest.mark.iam_account
+@pytest.mark.iam_user
+def test_account_user_delete(iam_root):
+    path = get_iam_path_prefix()
+    name = make_iam_name('U1')
+    with pytest.raises(iam_root.exceptions.NoSuchEntityException):
+        iam_root.delete_user(UserName=name)
+
+    response = iam_root.create_user(UserName=name, Path=path)
+    uid = response['User']['UserId']
+    create_date = response['User']['CreateDate']
+
+    iam_root.delete_user(UserName=name)
+
+    response = iam_root.create_user(UserName=name, Path=path)
+    assert uid != response['User']['UserId']
+    assert create_date <= response['User']['CreateDate']
+
+def user_list_names(client, **kwargs):
+    p = client.get_paginator('list_users')
+    usernames = []
+    for response in p.paginate(**kwargs):
+        usernames += [u['UserName'] for u in response['Users']]
+    return usernames
+
+@pytest.mark.iam_account
+@pytest.mark.iam_user
+def test_account_user_list(iam_root):
+    path = get_iam_path_prefix()
+    response = iam_root.list_users(PathPrefix=path)
+    assert len(response['Users']) == 0
+    assert response['IsTruncated'] == False
+
+    name1 = make_iam_name('aa')
+    name2 = make_iam_name('Ab')
+    name3 = make_iam_name('ac')
+    name4 = make_iam_name('Ad')
+
+    # sort order is independent of CreateDate, Path, and UserName capitalization
+    iam_root.create_user(UserName=name4, Path=path+'w/')
+    iam_root.create_user(UserName=name3, Path=path+'x/')
+    iam_root.create_user(UserName=name2, Path=path+'y/')
+    iam_root.create_user(UserName=name1, Path=path+'z/')
+
+    assert [name1, name2, name3, name4] == \
+            user_list_names(iam_root, PathPrefix=path)
+    assert [name1, name2, name3, name4] == \
+            user_list_names(iam_root, PathPrefix=path, PaginationConfig={'PageSize': 1})
+
+@pytest.mark.iam_account
+@pytest.mark.iam_user
+def test_account_user_list_path_prefix(iam_root):
+    path = get_iam_path_prefix()
+    response = iam_root.list_users(PathPrefix=path)
+    assert len(response['Users']) == 0
+    assert response['IsTruncated'] == False
+
+    name1 = make_iam_name('a')
+    name2 = make_iam_name('b')
+    name3 = make_iam_name('c')
+    name4 = make_iam_name('d')
+
+    iam_root.create_user(UserName=name1, Path=path)
+    iam_root.create_user(UserName=name2, Path=path)
+    iam_root.create_user(UserName=name3, Path=path+'a/')
+    iam_root.create_user(UserName=name4, Path=path+'a/x/')
+
+    assert [name1, name2, name3, name4] == \
+            user_list_names(iam_root, PathPrefix=path)
+    assert [name1, name2, name3, name4] == \
+            user_list_names(iam_root, PathPrefix=path,
+                            PaginationConfig={'PageSize': 1})
+    assert [name3, name4] == \
+            user_list_names(iam_root, PathPrefix=path+'a')
+    assert [name3, name4] == \
+            user_list_names(iam_root, PathPrefix=path+'a',
+                            PaginationConfig={'PageSize': 1})
+    assert [name4] == \
+            user_list_names(iam_root, PathPrefix=path+'a/x')
+    assert [name4] == \
+            user_list_names(iam_root, PathPrefix=path+'a/x',
+                            PaginationConfig={'PageSize': 1})
+    assert [] == user_list_names(iam_root, PathPrefix=path+'a/x/d')
+
+@pytest.mark.iam_account
+@pytest.mark.iam_user
+def test_account_user_update_name(iam_root):
+    path = get_iam_path_prefix()
+    name1 = make_iam_name('a')
+    new_name1 = make_iam_name('z')
+    name2 = make_iam_name('b')
+    with pytest.raises(iam_root.exceptions.NoSuchEntityException):
+        iam_root.update_user(UserName=name1, NewUserName=new_name1)
+
+    iam_root.create_user(UserName=name1, Path=path)
+    iam_root.create_user(UserName=name2, Path=path+'m/')
+    assert [name1, name2] == user_list_names(iam_root, PathPrefix=path)
+
+    response = iam_root.get_user(UserName=name1)
+    assert name1 == response['User']['UserName']
+    uid = response['User']['UserId']
+
+    iam_root.update_user(UserName=name1, NewUserName=new_name1)
+
+    with pytest.raises(iam_root.exceptions.NoSuchEntityException):
+        iam_root.get_user(UserName=name1)
+
+    response = iam_root.get_user(UserName=new_name1)
+    assert new_name1 == response['User']['UserName']
+    assert uid == response['User']['UserId']
+    assert response['User']['Arn'].endswith(f':user{path}{new_name1}')
+
+    assert [name2, new_name1] == user_list_names(iam_root, PathPrefix=path)
+
+@pytest.mark.iam_account
+@pytest.mark.iam_user
+def test_account_user_update_path(iam_root):
+    path = get_iam_path_prefix()
+    name1 = make_iam_name('a')
+    name2 = make_iam_name('b')
+    iam_root.create_user(UserName=name1, Path=path)
+    iam_root.create_user(UserName=name2, Path=path+'m/')
+    assert [name1, name2] == user_list_names(iam_root, PathPrefix=path)
+
+    response = iam_root.get_user(UserName=name1)
+    assert name1 == response['User']['UserName']
+    assert path == response['User']['Path']
+    uid = response['User']['UserId']
+
+    iam_root.update_user(UserName=name1, NewPath=path+'z/')
+
+    response = iam_root.get_user(UserName=name1)
+    assert name1 == response['User']['UserName']
+    assert f'{path}z/' == response['User']['Path']
+    assert uid == response['User']['UserId']
+    assert response['User']['Arn'].endswith(f':user{path}z/{name1}')
+
+    assert [name1, name2] == user_list_names(iam_root, PathPrefix=path)