From 72bc07f1129f87c89166fec37c953509ebac9948 Mon Sep 17 00:00:00 2001 From: zscholl Date: Wed, 11 Mar 2020 14:54:58 -0500 Subject: [PATCH 01/13] get access key create date for cred report --- .gitignore | 1 + moto/iam/models.py | 6 +++--- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/.gitignore b/.gitignore index fb9bd51de..deb9d9840 100644 --- a/.gitignore +++ b/.gitignore @@ -22,3 +22,4 @@ tests/file.tmp .eggs/ .mypy_cache/ *.tmp +.venv/ \ No newline at end of file diff --git a/moto/iam/models.py b/moto/iam/models.py index 08a1eb36a..dd197c872 100755 --- a/moto/iam/models.py +++ b/moto/iam/models.py @@ -680,14 +680,14 @@ class User(BaseModel): access_key_2_last_rotated = "N/A" elif len(self.access_keys) == 1: access_key_1_active = "true" - access_key_1_last_rotated = date_created.strftime(date_format) + access_key_1_last_rotated = self.access_keys[0].create_date.strftime(date_format) access_key_2_active = "false" access_key_2_last_rotated = "N/A" else: access_key_1_active = "true" - access_key_1_last_rotated = date_created.strftime(date_format) + access_key_1_last_rotated = self.access_keys[0].create_date.strftime(date_format) access_key_2_active = "true" - access_key_2_last_rotated = date_created.strftime(date_format) + access_key_2_last_rotated = self.access_keys[1].create_date.strftime(date_format) return "{0},{1},{2},{3},{4},{5},not_supported,false,{6},{7},{8},{9},false,N/A,false,N/A".format( self.name, From fc5e6ebf512694a78228347b43024e7038cd3431 Mon Sep 17 00:00:00 2001 From: zscholl Date: Wed, 11 Mar 2020 15:00:47 -0500 Subject: [PATCH 02/13] formatting --- moto/iam/models.py | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/moto/iam/models.py b/moto/iam/models.py index dd197c872..dfa6fd36a 100755 --- a/moto/iam/models.py +++ b/moto/iam/models.py @@ -680,14 +680,20 @@ class User(BaseModel): access_key_2_last_rotated = "N/A" elif len(self.access_keys) == 1: access_key_1_active = "true" - access_key_1_last_rotated = self.access_keys[0].create_date.strftime(date_format) + access_key_1_last_rotated = self.access_keys[0].create_date.strftime( + date_format + ) access_key_2_active = "false" access_key_2_last_rotated = "N/A" else: access_key_1_active = "true" - access_key_1_last_rotated = self.access_keys[0].create_date.strftime(date_format) + access_key_1_last_rotated = self.access_keys[0].create_date.strftime( + date_format + ) access_key_2_active = "true" - access_key_2_last_rotated = self.access_keys[1].create_date.strftime(date_format) + access_key_2_last_rotated = self.access_keys[1].create_date.strftime( + date_format + ) return "{0},{1},{2},{3},{4},{5},not_supported,false,{6},{7},{8},{9},false,N/A,false,N/A".format( self.name, From 2f2d6dc3fecb30e7420678d7731ac2bfc9ac336d Mon Sep 17 00:00:00 2001 From: zscholl Date: Wed, 11 Mar 2020 15:07:08 -0500 Subject: [PATCH 03/13] newline --- .gitignore | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index deb9d9840..02e812c5b 100644 --- a/.gitignore +++ b/.gitignore @@ -22,4 +22,4 @@ tests/file.tmp .eggs/ .mypy_cache/ *.tmp -.venv/ \ No newline at end of file +.venv/ From 35fde06381a910331410105be05bf4b28a37eb49 Mon Sep 17 00:00:00 2001 From: zscholl Date: Thu, 12 Mar 2020 13:07:30 -0500 Subject: [PATCH 04/13] update last_used for access keys --- moto/core/utils.py | 2 +- moto/iam/models.py | 23 ++++++++++++++++++++--- moto/iam/responses.py | 5 +++++ 3 files changed, 26 insertions(+), 4 deletions(-) diff --git a/moto/core/utils.py b/moto/core/utils.py index dce9f675c..921f64be2 100644 --- a/moto/core/utils.py +++ b/moto/core/utils.py @@ -187,7 +187,7 @@ def iso_8601_datetime_with_milliseconds(datetime): def iso_8601_datetime_without_milliseconds(datetime): - return datetime.strftime("%Y-%m-%dT%H:%M:%S") + "Z" + return None if datetime is None else datetime.strftime("%Y-%m-%dT%H:%M:%S") + "Z" RFC1123 = "%a, %d %b %Y %H:%M:%S GMT" diff --git a/moto/iam/models.py b/moto/iam/models.py index dfa6fd36a..c84e664c3 100755 --- a/moto/iam/models.py +++ b/moto/iam/models.py @@ -464,7 +464,7 @@ class AccessKey(BaseModel): self.secret_access_key = random_alphanumeric(40) self.status = "Active" self.create_date = datetime.utcnow() - self.last_used = datetime.utcnow() + self.last_used = None @property def created_iso_8601(self): @@ -683,6 +683,11 @@ class User(BaseModel): access_key_1_last_rotated = self.access_keys[0].create_date.strftime( date_format ) + access_key_2_last_rotated = ( + "N/A" + if self.access_key[0].last_used is None + else self.access_key[0].last_used.strftime(date_format) + ) access_key_2_active = "false" access_key_2_last_rotated = "N/A" else: @@ -690,12 +695,22 @@ class User(BaseModel): access_key_1_last_rotated = self.access_keys[0].create_date.strftime( date_format ) + access_key_1_last_used = ( + "N/A" + if self.access_key[0].last_used is None + else self.access_key[0].last_used.strftime(date_format) + ) access_key_2_active = "true" access_key_2_last_rotated = self.access_keys[1].create_date.strftime( date_format ) + access_key_2_last_used = ( + "N/A" + if self.access_key[1].last_used is None + else self.access_key[1].last_used.strftime(date_format) + ) - return "{0},{1},{2},{3},{4},{5},not_supported,false,{6},{7},{8},{9},false,N/A,false,N/A".format( + return "{0},{1},{2},{3},{4},{5},not_supported,false,{6},{7},{8},not_supported,not_supported,{9},{10},{11},false,N/A,false,N/A".format( self.name, self.arn, date_created.strftime(date_format), @@ -704,8 +719,10 @@ class User(BaseModel): date_created.strftime(date_format), access_key_1_active, access_key_1_last_rotated, + access_key_1_last_used, access_key_2_active, access_key_2_last_rotated, + access_key_2_last_used, ) @@ -1805,7 +1822,7 @@ class IAMBackend(BaseBackend): def get_credential_report(self): if not self.credential_report: raise IAMReportNotPresentException("Credential report not present") - report = "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_2_active,access_key_2_last_rotated,cert_1_active,cert_1_last_rotated,cert_2_active,cert_2_last_rotated\n" + report = "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\n" for user in self.users: report += self.users[user].to_csv() return base64.b64encode(report.encode("ascii")).decode("ascii") diff --git a/moto/iam/responses.py b/moto/iam/responses.py index 12501769e..947cccf33 100644 --- a/moto/iam/responses.py +++ b/moto/iam/responses.py @@ -1779,7 +1779,12 @@ GET_ACCESS_KEY_LAST_USED_TEMPLATE = """ {{ user_name }} + {{% if last_used % }} {{ last_used }} + {{% else % }} + N/A + N/A + {{% endif %}} From 54d816f09fbc1ca9e883be02249e66e6c5dbf120 Mon Sep 17 00:00:00 2001 From: zscholl Date: Thu, 12 Mar 2020 13:33:20 -0500 Subject: [PATCH 05/13] fix typo --- moto/iam/models.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/moto/iam/models.py b/moto/iam/models.py index c84e664c3..6256d437a 100755 --- a/moto/iam/models.py +++ b/moto/iam/models.py @@ -697,8 +697,8 @@ class User(BaseModel): ) access_key_1_last_used = ( "N/A" - if self.access_key[0].last_used is None - else self.access_key[0].last_used.strftime(date_format) + if self.access_keys[0].last_used is None + else self.access_keys[0].last_used.strftime(date_format) ) access_key_2_active = "true" access_key_2_last_rotated = self.access_keys[1].create_date.strftime( @@ -706,8 +706,8 @@ class User(BaseModel): ) access_key_2_last_used = ( "N/A" - if self.access_key[1].last_used is None - else self.access_key[1].last_used.strftime(date_format) + if self.access_keys[1].last_used is None + else self.access_keys[1].last_used.strftime(date_format) ) return "{0},{1},{2},{3},{4},{5},not_supported,false,{6},{7},{8},not_supported,not_supported,{9},{10},{11},false,N/A,false,N/A".format( From 9821eff1284109e0135b2486ce55acd418ca899d Mon Sep 17 00:00:00 2001 From: zscholl Date: Thu, 12 Mar 2020 15:06:40 -0500 Subject: [PATCH 06/13] add newline --- moto/iam/models.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/moto/iam/models.py b/moto/iam/models.py index 6256d437a..304a06504 100755 --- a/moto/iam/models.py +++ b/moto/iam/models.py @@ -710,7 +710,7 @@ class User(BaseModel): else self.access_keys[1].last_used.strftime(date_format) ) - return "{0},{1},{2},{3},{4},{5},not_supported,false,{6},{7},{8},not_supported,not_supported,{9},{10},{11},false,N/A,false,N/A".format( + return "{0},{1},{2},{3},{4},{5},not_supported,false,{6},{7},{8},not_supported,not_supported,{9},{10},{11},false,N/A,false,N/A\n".format( self.name, self.arn, date_created.strftime(date_format), From b342a96cb0079de8ce27f5acfaba5c4496211bad Mon Sep 17 00:00:00 2001 From: zscholl Date: Thu, 12 Mar 2020 15:15:14 -0500 Subject: [PATCH 07/13] add fields --- moto/iam/models.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/moto/iam/models.py b/moto/iam/models.py index 304a06504..b92c1f293 100755 --- a/moto/iam/models.py +++ b/moto/iam/models.py @@ -710,7 +710,7 @@ class User(BaseModel): else self.access_keys[1].last_used.strftime(date_format) ) - return "{0},{1},{2},{3},{4},{5},not_supported,false,{6},{7},{8},not_supported,not_supported,{9},{10},{11},false,N/A,false,N/A\n".format( + return "{0},{1},{2},{3},{4},{5},not_supported,false,{6},{7},{8},not_supported,not_supported,{9},{10},{11},not_supported,not_supported,false,N/A,false,N/A\n".format( self.name, self.arn, date_created.strftime(date_format), From 09109f336c14285a3ea805127e994d7851f1d84f Mon Sep 17 00:00:00 2001 From: zscholl Date: Thu, 12 Mar 2020 15:57:54 -0500 Subject: [PATCH 08/13] more fixes --- moto/iam/models.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/moto/iam/models.py b/moto/iam/models.py index b92c1f293..a01b8655a 100755 --- a/moto/iam/models.py +++ b/moto/iam/models.py @@ -676,20 +676,23 @@ class User(BaseModel): if len(self.access_keys) == 0: access_key_1_active = "false" access_key_1_last_rotated = "N/A" + access_key_1_last_used = "N/A" access_key_2_active = "false" access_key_2_last_rotated = "N/A" + access_key_2_last_used = "N/A" elif len(self.access_keys) == 1: access_key_1_active = "true" access_key_1_last_rotated = self.access_keys[0].create_date.strftime( date_format ) - access_key_2_last_rotated = ( + access_key_1_last_used = ( "N/A" - if self.access_key[0].last_used is None - else self.access_key[0].last_used.strftime(date_format) + if self.access_keys[0].last_used is None + else self.access_keys[0].last_used.strftime(date_format) ) access_key_2_active = "false" access_key_2_last_rotated = "N/A" + access_key_2_last_used = "N/A" else: access_key_1_active = "true" access_key_1_last_rotated = self.access_keys[0].create_date.strftime( From 1abff5727581286ac0fec63e96092aed65795e3e Mon Sep 17 00:00:00 2001 From: zscholl Date: Mon, 23 Mar 2020 14:46:46 -0500 Subject: [PATCH 09/13] add status to credential report --- moto/iam/models.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/moto/iam/models.py b/moto/iam/models.py index a01b8655a..6da6c6742 100755 --- a/moto/iam/models.py +++ b/moto/iam/models.py @@ -681,7 +681,7 @@ class User(BaseModel): access_key_2_last_rotated = "N/A" access_key_2_last_used = "N/A" elif len(self.access_keys) == 1: - access_key_1_active = "true" + access_key_1_active = "true" if self.access_keys[0].status == "Active" else "false" access_key_1_last_rotated = self.access_keys[0].create_date.strftime( date_format ) @@ -694,7 +694,7 @@ class User(BaseModel): access_key_2_last_rotated = "N/A" access_key_2_last_used = "N/A" else: - access_key_1_active = "true" + access_key_1_active = "true" if self.access_keys[0].status == "Active" else "false" access_key_1_last_rotated = self.access_keys[0].create_date.strftime( date_format ) @@ -703,7 +703,7 @@ class User(BaseModel): if self.access_keys[0].last_used is None else self.access_keys[0].last_used.strftime(date_format) ) - access_key_2_active = "true" + access_key_2_active = "true" if self.access_keys[1].status == "Active" else "false" access_key_2_last_rotated = self.access_keys[1].create_date.strftime( date_format ) From 48304f81b18fbe75f306c90068796ef5653fddfe Mon Sep 17 00:00:00 2001 From: zscholl Date: Fri, 24 Apr 2020 13:16:13 -0500 Subject: [PATCH 10/13] fix last_used template --- moto/iam/responses.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/moto/iam/responses.py b/moto/iam/responses.py index 947cccf33..086ba508b 100644 --- a/moto/iam/responses.py +++ b/moto/iam/responses.py @@ -1779,12 +1779,12 @@ GET_ACCESS_KEY_LAST_USED_TEMPLATE = """ {{ user_name }} - {{% if last_used % }} + {% if last_used is defined %} {{ last_used }} - {{% else % }} + {% else %} N/A N/A - {{% endif %}} + {% endif %} From 51e7002cbb84c9524372f216115461e2ce5c3f12 Mon Sep 17 00:00:00 2001 From: zscholl Date: Wed, 29 Apr 2020 15:49:14 -0500 Subject: [PATCH 11/13] add tests --- moto/iam/models.py | 12 +++++-- moto/iam/responses.py | 9 +++--- tests/test_iam/test_iam.py | 64 ++++++++++++++++++++++++++++++++++++-- 3 files changed, 74 insertions(+), 11 deletions(-) diff --git a/moto/iam/models.py b/moto/iam/models.py index 6da6c6742..d3907da26 100755 --- a/moto/iam/models.py +++ b/moto/iam/models.py @@ -681,7 +681,9 @@ class User(BaseModel): access_key_2_last_rotated = "N/A" access_key_2_last_used = "N/A" elif len(self.access_keys) == 1: - access_key_1_active = "true" if self.access_keys[0].status == "Active" else "false" + access_key_1_active = ( + "true" if self.access_keys[0].status == "Active" else "false" + ) access_key_1_last_rotated = self.access_keys[0].create_date.strftime( date_format ) @@ -694,7 +696,9 @@ class User(BaseModel): access_key_2_last_rotated = "N/A" access_key_2_last_used = "N/A" else: - access_key_1_active = "true" if self.access_keys[0].status == "Active" else "false" + access_key_1_active = ( + "true" if self.access_keys[0].status == "Active" else "false" + ) access_key_1_last_rotated = self.access_keys[0].create_date.strftime( date_format ) @@ -703,7 +707,9 @@ class User(BaseModel): if self.access_keys[0].last_used is None else self.access_keys[0].last_used.strftime(date_format) ) - access_key_2_active = "true" if self.access_keys[1].status == "Active" else "false" + access_key_2_active = ( + "true" if self.access_keys[1].status == "Active" else "false" + ) access_key_2_last_rotated = self.access_keys[1].create_date.strftime( date_format ) diff --git a/moto/iam/responses.py b/moto/iam/responses.py index 086ba508b..667a6d13b 100644 --- a/moto/iam/responses.py +++ b/moto/iam/responses.py @@ -1779,12 +1779,11 @@ GET_ACCESS_KEY_LAST_USED_TEMPLATE = """ {{ user_name }} - {% if last_used is defined %} - {{ last_used }} - {% else %} - N/A - N/A + {% if last_used %} + {{ last_used }} {% endif %} + N/A + N/A diff --git a/tests/test_iam/test_iam.py b/tests/test_iam/test_iam.py index 995895437..986809bd5 100644 --- a/tests/test_iam/test_iam.py +++ b/tests/test_iam/test_iam.py @@ -4,6 +4,7 @@ import json import boto import boto3 +import csv import os import sure # noqa import sys @@ -12,12 +13,14 @@ from botocore.exceptions import ClientError from dateutil.tz import tzutc from moto import mock_iam, mock_iam_deprecated -from moto.iam.models import aws_managed_policies from moto.core import ACCOUNT_ID +from moto.iam.models import aws_managed_policies +from moto.backends import get_backend from nose.tools import assert_raises, assert_equals from nose.tools import raises from datetime import datetime +from datetime import timezone from tests.helpers import requires_boto_gte from uuid import uuid4 @@ -1215,6 +1218,44 @@ def test_boto3_get_credential_report(): report.should.match(r".*my-user.*") +@mock_iam +def test_boto3_get_credential_report_content(): + conn = boto3.client("iam", region_name="us-east-1") + username = "my-user" + conn.create_user(UserName=username) + 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"] + iam_backend = get_backend("iam")["global"] + timestamp = datetime.now(tz=timezone.utc) + iam_backend.users[username].access_keys[1].last_used = timestamp + with assert_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] + header.should.equal( + "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) + user["user"].should.equal("my-user") + user["access_key_1_active"].should.equal("false") + user["access_key_1_last_rotated"].should.equal( + timestamp.isoformat(timespec="seconds") + ) + user["access_key_1_last_used_date"].should.equal("N/A") + user["access_key_2_active"].should.equal("true") + user["access_key_2_last_used_date"].should.equal( + timestamp.isoformat(timespec="seconds") + ) + + @requires_boto_gte("2.39") @mock_iam_deprecated() def test_managed_policy(): @@ -1382,7 +1423,7 @@ def test_update_access_key(): @mock_iam -def test_get_access_key_last_used(): +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" @@ -1393,11 +1434,28 @@ def test_get_access_key_last_used(): resp = client.get_access_key_last_used( AccessKeyId=create_key_response["AccessKeyId"] ) + resp["AccessKeyLastUsed"].should_not.contain("LastUsedDate") + resp["UserName"].should.equal(create_key_response["UserName"]) + +@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 assert_raises(ClientError): + client.get_access_key_last_used(AccessKeyId="non-existent-key-id") + create_key_response = client.create_access_key(UserName=username)["AccessKey"] + # Set last used date using the IAM backend. Moto currently does not have a mechanism for tracking usage of access keys + iam_backend = get_backend("iam")["global"] + iam_backend.users[username].access_keys[0].last_used = datetime.utcnow() + resp = client.get_access_key_last_used( + AccessKeyId=create_key_response["AccessKeyId"] + ) datetime.strftime( resp["AccessKeyLastUsed"]["LastUsedDate"], "%Y-%m-%d" ).should.equal(datetime.strftime(datetime.utcnow(), "%Y-%m-%d")) - resp["UserName"].should.equal(create_key_response["UserName"]) @mock_iam From 0423be259ada80786ea6b7b1d272470e5edf79b5 Mon Sep 17 00:00:00 2001 From: zscholl Date: Thu, 30 Apr 2020 08:44:45 -0500 Subject: [PATCH 12/13] remove backend logic & py27 incompatible timezone --- tests/test_iam/test_iam.py | 34 ++++------------------------------ 1 file changed, 4 insertions(+), 30 deletions(-) diff --git a/tests/test_iam/test_iam.py b/tests/test_iam/test_iam.py index 986809bd5..5a8ffe709 100644 --- a/tests/test_iam/test_iam.py +++ b/tests/test_iam/test_iam.py @@ -15,12 +15,10 @@ from dateutil.tz import tzutc from moto import mock_iam, mock_iam_deprecated from moto.core import ACCOUNT_ID from moto.iam.models import aws_managed_policies -from moto.backends import get_backend from nose.tools import assert_raises, assert_equals from nose.tools import raises from datetime import datetime -from datetime import timezone from tests.helpers import requires_boto_gte from uuid import uuid4 @@ -1228,9 +1226,7 @@ def test_boto3_get_credential_report_content(): UserName=username, AccessKeyId=key1["AccessKeyId"], Status="Inactive" ) key1 = conn.create_access_key(UserName=username)["AccessKey"] - iam_backend = get_backend("iam")["global"] - timestamp = datetime.now(tz=timezone.utc) - iam_backend.users[username].access_keys[1].last_used = timestamp + timestamp = datetime.utcnow() with assert_raises(ClientError): conn.get_credential_report() result = conn.generate_credential_report() @@ -1246,14 +1242,12 @@ def test_boto3_get_credential_report_content(): user = next(report_dict) user["user"].should.equal("my-user") user["access_key_1_active"].should.equal("false") - user["access_key_1_last_rotated"].should.equal( - timestamp.isoformat(timespec="seconds") + user["access_key_1_last_rotated"].should.match( + timestamp.strftime(datetime.utcnow(), "%Y-%m-%d") ) user["access_key_1_last_used_date"].should.equal("N/A") user["access_key_2_active"].should.equal("true") - user["access_key_2_last_used_date"].should.equal( - timestamp.isoformat(timespec="seconds") - ) + user["access_key_2_last_used_date"].should.equal("N/A") @requires_boto_gte("2.39") @@ -1438,26 +1432,6 @@ def test_get_access_key_last_used_when_unused(): resp["UserName"].should.equal(create_key_response["UserName"]) -@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 assert_raises(ClientError): - client.get_access_key_last_used(AccessKeyId="non-existent-key-id") - create_key_response = client.create_access_key(UserName=username)["AccessKey"] - # Set last used date using the IAM backend. Moto currently does not have a mechanism for tracking usage of access keys - iam_backend = get_backend("iam")["global"] - iam_backend.users[username].access_keys[0].last_used = datetime.utcnow() - resp = client.get_access_key_last_used( - AccessKeyId=create_key_response["AccessKeyId"] - ) - datetime.strftime( - resp["AccessKeyLastUsed"]["LastUsedDate"], "%Y-%m-%d" - ).should.equal(datetime.strftime(datetime.utcnow(), "%Y-%m-%d")) - - @mock_iam def test_upload_ssh_public_key(): iam = boto3.resource("iam", region_name="us-east-1") From 1f1404352e91a6e7f13801f9233a10299dcdece4 Mon Sep 17 00:00:00 2001 From: zscholl Date: Thu, 30 Apr 2020 09:42:22 -0500 Subject: [PATCH 13/13] use conditional TEST_SERVER_MODE for backend tests --- tests/test_iam/test_iam.py | 40 +++++++++++++++++++++++++++++++++----- 1 file changed, 35 insertions(+), 5 deletions(-) diff --git a/tests/test_iam/test_iam.py b/tests/test_iam/test_iam.py index 5a8ffe709..6792d8f52 100644 --- a/tests/test_iam/test_iam.py +++ b/tests/test_iam/test_iam.py @@ -12,9 +12,10 @@ from boto.exception import BotoServerError from botocore.exceptions import ClientError from dateutil.tz import tzutc -from moto import mock_iam, mock_iam_deprecated +from moto import mock_iam, mock_iam_deprecated, settings from moto.core import ACCOUNT_ID from moto.iam.models import aws_managed_policies +from moto.backends import get_backend from nose.tools import assert_raises, assert_equals from nose.tools import raises @@ -1227,6 +1228,9 @@ def test_boto3_get_credential_report_content(): ) key1 = conn.create_access_key(UserName=username)["AccessKey"] timestamp = datetime.utcnow() + if not settings.TEST_SERVER_MODE: + iam_backend = get_backend("iam")["global"] + iam_backend.users[username].access_keys[1].last_used = timestamp with assert_raises(ClientError): conn.get_credential_report() result = conn.generate_credential_report() @@ -1242,12 +1246,38 @@ def test_boto3_get_credential_report_content(): user = next(report_dict) user["user"].should.equal("my-user") user["access_key_1_active"].should.equal("false") - user["access_key_1_last_rotated"].should.match( - timestamp.strftime(datetime.utcnow(), "%Y-%m-%d") - ) + user["access_key_1_last_rotated"].should.match(timestamp.strftime("%Y-%m-%d")) user["access_key_1_last_used_date"].should.equal("N/A") user["access_key_2_active"].should.equal("true") - user["access_key_2_last_used_date"].should.equal("N/A") + if not settings.TEST_SERVER_MODE: + user["access_key_2_last_used_date"].should.match(timestamp.strftime("%Y-%m-%d")) + else: + user["access_key_2_last_used_date"].should.equal("N/A") + + +@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 assert_raises(ClientError): + client.get_access_key_last_used(AccessKeyId="non-existent-key-id") + create_key_response = client.create_access_key(UserName=username)["AccessKey"] + # Set last used date using the IAM backend. Moto currently does not have a mechanism for tracking usage of access keys + if not settings.TEST_SERVER_MODE: + timestamp = datetime.utcnow() + iam_backend = get_backend("iam")["global"] + iam_backend.users[username].access_keys[0].last_used = timestamp + resp = client.get_access_key_last_used( + AccessKeyId=create_key_response["AccessKeyId"] + ) + if not settings.TEST_SERVER_MODE: + datetime.strftime( + resp["AccessKeyLastUsed"]["LastUsedDate"], "%Y-%m-%d" + ).should.equal(timestamp.strftime("%Y-%m-%d")) + else: + resp["AccessKeyLastUsed"].should_not.contain("LastUsedDate") @requires_boto_gte("2.39")