Repository: getmoto/moto

Issue:
IAM Operations Require UserName Field, Should be Optional
When calling some IAM functions, Moto requires the `UserName` parameters, even though it is optional (per the [AWS CLI docs](https://docs.aws.amazon.com/cli/latest/reference/iam/index.html)).

Using Moto 1.3.14, calls to all of the following functions return an error when omitting the `UserName` parameter:
[ ] create_access_key
[ ] update_access_key
[ ] list_access_keys
[ ] delete_access_key

To reproduce the issue:

1. Install the `moto[server]` and `awscli-plugin-endpoint` packages via `pip` 
1. Start Moto in standalone server mode
1. Configure the AWS CLI to use Moto's server address as the API endpoint
1. Call the `create-access-key` function from the AWS CLI without a `--user-name` argument. 

```
# aws iam create-access-key

An error occurred (NoSuchEntity) when calling the CreateAccessKey operation: User None not found
```



Discussion:

Resolving patch:
diff --git a/moto/iam/responses.py b/moto/iam/responses.py
--- a/moto/iam/responses.py
+++ b/moto/iam/responses.py
@@ -563,6 +563,10 @@ def delete_user_policy(self):
 
     def create_access_key(self):
         user_name = self._get_param("UserName")
+        if not user_name:
+            access_key_id = self.get_current_user()
+            access_key = iam_backend.get_access_key_last_used(access_key_id)
+            user_name = access_key["user_name"]
 
         key = iam_backend.create_access_key(user_name)
         template = self.response_template(CREATE_ACCESS_KEY_TEMPLATE)
@@ -572,6 +576,10 @@ def update_access_key(self):
         user_name = self._get_param("UserName")
         access_key_id = self._get_param("AccessKeyId")
         status = self._get_param("Status")
+        if not user_name:
+            access_key = iam_backend.get_access_key_last_used(access_key_id)
+            user_name = access_key["user_name"]
+
         iam_backend.update_access_key(user_name, access_key_id, status)
         template = self.response_template(GENERIC_EMPTY_TEMPLATE)
         return template.render(name="UpdateAccessKey")
@@ -587,6 +595,11 @@ def get_access_key_last_used(self):
 
     def list_access_keys(self):
         user_name = self._get_param("UserName")
+        if not user_name:
+            access_key_id = self.get_current_user()
+            access_key = iam_backend.get_access_key_last_used(access_key_id)
+            user_name = access_key["user_name"]
+
         keys = iam_backend.get_all_access_keys(user_name)
         template = self.response_template(LIST_ACCESS_KEYS_TEMPLATE)
         return template.render(user_name=user_name, keys=keys)
@@ -594,6 +607,9 @@ def list_access_keys(self):
     def delete_access_key(self):
         user_name = self._get_param("UserName")
         access_key_id = self._get_param("AccessKeyId")
+        if not user_name:
+            access_key = iam_backend.get_access_key_last_used(access_key_id)
+            user_name = access_key["user_name"]
 
         iam_backend.delete_access_key(access_key_id, user_name)
         template = self.response_template(GENERIC_EMPTY_TEMPLATE)


Test patch:
diff --git a/tests/test_iam/test_iam.py b/tests/test_iam/test_iam.py
--- a/tests/test_iam/test_iam.py
+++ b/tests/test_iam/test_iam.py
@@ -785,7 +785,7 @@ def test_delete_login_profile():
     conn.delete_login_profile("my-user")
 
 
-@mock_iam()
+@mock_iam
 def test_create_access_key():
     conn = boto3.client("iam", region_name="us-east-1")
     with assert_raises(ClientError):
@@ -798,6 +798,19 @@ def test_create_access_key():
     access_key["AccessKeyId"].should.have.length_of(20)
     access_key["SecretAccessKey"].should.have.length_of(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"]
+    (
+        datetime.utcnow() - access_key["CreateDate"].replace(tzinfo=None)
+    ).seconds.should.be.within(0, 10)
+    access_key["AccessKeyId"].should.have.length_of(20)
+    access_key["SecretAccessKey"].should.have.length_of(40)
+    assert access_key["AccessKeyId"].startswith("AKIA")
 
 
 @mock_iam_deprecated()
@@ -825,8 +838,35 @@ def test_get_all_access_keys():
     )
 
 
+@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_equals(
+        response["AccessKeyMetadata"], [],
+    )
+    access_key = conn.create_access_key(UserName="my-user")["AccessKey"]
+    response = conn.list_access_keys(UserName="my-user")
+    assert_equals(
+        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_equals(
+        sorted(response["AccessKeyMetadata"][0].keys()),
+        sorted(["Status", "CreateDate", "UserName", "AccessKeyId"]),
+    )
+
+
 @mock_iam_deprecated()
-def test_delete_access_key():
+def test_delete_access_key_deprecated():
     conn = boto.connect_iam()
     conn.create_user("my-user")
     access_key_id = conn.create_access_key("my-user")["create_access_key_response"][
@@ -835,6 +875,16 @@ def test_delete_access_key():
     conn.delete_access_key(access_key_id, "my-user")
 
 
+@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
@@ -1326,6 +1376,9 @@ def test_update_access_key():
     )
     resp = client.list_access_keys(UserName=username)
     resp["AccessKeyMetadata"][0]["Status"].should.equal("Inactive")
+    client.update_access_key(AccessKeyId=key["AccessKeyId"], Status="Active")
+    resp = client.list_access_keys(UserName=username)
+    resp["AccessKeyMetadata"][0]["Status"].should.equal("Active")
 
 
 @mock_iam