diff --git a/IMPLEMENTATION_COVERAGE.md b/IMPLEMENTATION_COVERAGE.md index 17b864dc3..7c68c0e31 100644 --- a/IMPLEMENTATION_COVERAGE.md +++ b/IMPLEMENTATION_COVERAGE.md @@ -835,8 +835,8 @@ - [ ] admin_delete_user - [ ] admin_delete_user_attributes - [ ] admin_disable_provider_for_user -- [ ] admin_disable_user -- [ ] admin_enable_user +- [X] admin_disable_user +- [X] admin_enable_user - [ ] admin_forget_device - [ ] admin_get_device - [ ] admin_get_user diff --git a/moto/apigateway/models.py b/moto/apigateway/models.py index 4094c7a69..db4746a0e 100644 --- a/moto/apigateway/models.py +++ b/moto/apigateway/models.py @@ -606,8 +606,15 @@ class APIGatewayBackend(BaseBackend): self.usage_plans[plan['id']] = plan return plan - def get_usage_plans(self): - return list(self.usage_plans.values()) + def get_usage_plans(self, api_key_id=None): + plans = list(self.usage_plans.values()) + if api_key_id is not None: + plans = [ + plan + for plan in plans + if self.usage_plan_keys.get(plan['id'], {}).get(api_key_id, False) + ] + return plans def get_usage_plan(self, usage_plan_id): return self.usage_plans[usage_plan_id] diff --git a/moto/apigateway/responses.py b/moto/apigateway/responses.py index 7364ae2cb..bc4d262cd 100644 --- a/moto/apigateway/responses.py +++ b/moto/apigateway/responses.py @@ -255,7 +255,8 @@ class APIGatewayResponse(BaseResponse): if self.method == 'POST': usage_plan_response = self.backend.create_usage_plan(json.loads(self.body)) elif self.method == 'GET': - usage_plans_response = self.backend.get_usage_plans() + api_key_id = self.querystring.get("keyId", [None])[0] + usage_plans_response = self.backend.get_usage_plans(api_key_id=api_key_id) return 200, {}, json.dumps({"item": usage_plans_response}) return 200, {}, json.dumps(usage_plan_response) diff --git a/moto/cognitoidp/models.py b/moto/cognitoidp/models.py index 10da0c6ff..476d470b9 100644 --- a/moto/cognitoidp/models.py +++ b/moto/cognitoidp/models.py @@ -383,7 +383,7 @@ class CognitoIdpBackend(BaseBackend): raise ResourceNotFoundError(user_pool_id) if username not in user_pool.users: - raise ResourceNotFoundError(username) + raise UserNotFoundError(username) return user_pool.users[username] @@ -394,13 +394,21 @@ class CognitoIdpBackend(BaseBackend): return user_pool.users.values() + def admin_disable_user(self, user_pool_id, username): + user = self.admin_get_user(user_pool_id, username) + user.enabled = False + + def admin_enable_user(self, user_pool_id, username): + user = self.admin_get_user(user_pool_id, username) + user.enabled = True + def admin_delete_user(self, user_pool_id, username): user_pool = self.user_pools.get(user_pool_id) if not user_pool: raise ResourceNotFoundError(user_pool_id) if username not in user_pool.users: - raise ResourceNotFoundError(username) + raise UserNotFoundError(username) del user_pool.users[username] diff --git a/moto/cognitoidp/responses.py b/moto/cognitoidp/responses.py index e6f20367e..50939786b 100644 --- a/moto/cognitoidp/responses.py +++ b/moto/cognitoidp/responses.py @@ -160,6 +160,18 @@ class CognitoIdpResponse(BaseResponse): "Users": [user.to_json(extended=True) for user in users] }) + def admin_disable_user(self): + user_pool_id = self._get_param("UserPoolId") + username = self._get_param("Username") + cognitoidp_backends[self.region].admin_disable_user(user_pool_id, username) + return "" + + def admin_enable_user(self): + user_pool_id = self._get_param("UserPoolId") + username = self._get_param("Username") + cognitoidp_backends[self.region].admin_enable_user(user_pool_id, username) + return "" + def admin_delete_user(self): user_pool_id = self._get_param("UserPoolId") username = self._get_param("Username") diff --git a/moto/dynamodb2/models.py b/moto/dynamodb2/models.py index 63ad20df6..a54c4f7d0 100644 --- a/moto/dynamodb2/models.py +++ b/moto/dynamodb2/models.py @@ -265,9 +265,9 @@ class Item(BaseModel): self.attrs[attribute_name] = DynamoType({"SS": new_value}) elif isinstance(new_value, dict): self.attrs[attribute_name] = DynamoType({"M": new_value}) - elif update_action['Value'].keys() == ['N']: + elif set(update_action['Value'].keys()) == set(['N']): self.attrs[attribute_name] = DynamoType({"N": new_value}) - elif update_action['Value'].keys() == ['NULL']: + elif set(update_action['Value'].keys()) == set(['NULL']): if attribute_name in self.attrs: del self.attrs[attribute_name] else: diff --git a/moto/iam/models.py b/moto/iam/models.py index 4d884fa2f..4a5240a08 100644 --- a/moto/iam/models.py +++ b/moto/iam/models.py @@ -255,7 +255,15 @@ class Group(BaseModel): @property def arn(self): - return "arn:aws:iam::{0}:group/{1}".format(ACCOUNT_ID, self.path) + if self.path == '/': + return "arn:aws:iam::{0}:group/{1}".format(ACCOUNT_ID, self.name) + + else: + return "arn:aws:iam::{0}:group/{1}/{2}".format(ACCOUNT_ID, self.path, self.name) + + @property + def create_date(self): + return self.created def get_policy(self, policy_name): try: diff --git a/moto/iam/responses.py b/moto/iam/responses.py index 9e8d21396..22558f3f6 100644 --- a/moto/iam/responses.py +++ b/moto/iam/responses.py @@ -285,7 +285,7 @@ class IamResponse(BaseResponse): def create_group(self): group_name = self._get_param('GroupName') - path = self._get_param('Path') + path = self._get_param('Path', '/') group = iam_backend.create_group(group_name, path) template = self.response_template(CREATE_GROUP_TEMPLATE) @@ -1007,6 +1007,7 @@ CREATE_GROUP_TEMPLATE = """ {{ group.name }} {{ group.id }} {{ group.arn }} + {{ group.create_date }} @@ -1021,6 +1022,7 @@ GET_GROUP_TEMPLATE = """ {{ group.name }} {{ group.id }} {{ group.arn }} + {{ group.create_date }} {% for user in group.users %} @@ -1384,10 +1386,6 @@ GET_ACCOUNT_AUTHORIZATION_DETAILS_TEMPLATE = """ {% endfor %} - - EXAMPLEkakv9BCuUNFDtxWSyfzetYwEx2ADc8dnzfvERF5S6YMvXKx41t6gCl/eeaCX3Jo94/ - bKqezEAg8TEVS99EKFLxm3jtbpl25FDWEXAMPLE - {% for group in groups %} diff --git a/tests/test_apigateway/test_apigateway.py b/tests/test_apigateway/test_apigateway.py index 8a2c4370d..5954de8ca 100644 --- a/tests/test_apigateway/test_apigateway.py +++ b/tests/test_apigateway/test_apigateway.py @@ -1084,3 +1084,36 @@ def test_create_usage_plan_key_non_existent_api_key(): # Attempt to create a usage plan key for a API key that doesn't exists payload = {'usagePlanId': usage_plan_id, 'keyId': 'non-existent', 'keyType': 'API_KEY' } client.create_usage_plan_key.when.called_with(**payload).should.throw(ClientError) + + +@mock_apigateway +def test_get_usage_plans_using_key_id(): + region_name = 'us-west-2' + client = boto3.client('apigateway', region_name=region_name) + + # Create 2 Usage Plans + # one will be attached to an API Key, the other will remain unattached + attached_plan = client.create_usage_plan(name='Attached') + unattached_plan = client.create_usage_plan(name='Unattached') + + # Create an API key + # to attach to the usage plan + key_name = 'test-api-key' + response = client.create_api_key(name=key_name) + key_id = response["id"] + + # Create a Usage Plan Key + # Attached the Usage Plan and API Key + key_type = 'API_KEY' + payload = {'usagePlanId': attached_plan['id'], 'keyId': key_id, 'keyType': key_type} + response = client.create_usage_plan_key(**payload) + + # All usage plans should be returned when keyId is not included + all_plans = client.get_usage_plans() + len(all_plans['items']).should.equal(2) + + # Only the usage plan attached to the given api key are included + only_plans_with_key = client.get_usage_plans(keyId=key_id) + len(only_plans_with_key['items']).should.equal(1) + only_plans_with_key['items'][0]['name'].should.equal(attached_plan['name']) + only_plans_with_key['items'][0]['id'].should.equal(attached_plan['id']) diff --git a/tests/test_cognitoidp/test_cognitoidp.py b/tests/test_cognitoidp/test_cognitoidp.py index 56d7c08a8..f72a44762 100644 --- a/tests/test_cognitoidp/test_cognitoidp.py +++ b/tests/test_cognitoidp/test_cognitoidp.py @@ -343,6 +343,7 @@ def test_admin_create_user(): result["User"]["Attributes"].should.have.length_of(1) result["User"]["Attributes"][0]["Name"].should.equal("thing") result["User"]["Attributes"][0]["Value"].should.equal(value) + result["User"]["Enabled"].should.equal(True) @mock_cognitoidp @@ -367,6 +368,22 @@ def test_admin_get_user(): result["UserAttributes"][0]["Value"].should.equal(value) +@mock_cognitoidp +def test_admin_get_missing_user(): + conn = boto3.client("cognito-idp", "us-west-2") + + username = str(uuid.uuid4()) + user_pool_id = conn.create_user_pool(PoolName=str(uuid.uuid4()))["UserPool"]["Id"] + + caught = False + try: + conn.admin_get_user(UserPoolId=user_pool_id, Username=username) + except conn.exceptions.UserNotFoundException: + caught = True + + caught.should.be.true + + @mock_cognitoidp def test_list_users(): conn = boto3.client("cognito-idp", "us-west-2") @@ -379,6 +396,37 @@ def test_list_users(): result["Users"][0]["Username"].should.equal(username) +@mock_cognitoidp +def test_admin_disable_user(): + conn = boto3.client("cognito-idp", "us-west-2") + + username = str(uuid.uuid4()) + user_pool_id = conn.create_user_pool(PoolName=str(uuid.uuid4()))["UserPool"]["Id"] + conn.admin_create_user(UserPoolId=user_pool_id, Username=username) + + result = conn.admin_disable_user(UserPoolId=user_pool_id, Username=username) + list(result.keys()).should.equal(["ResponseMetadata"]) # No response expected + + conn.admin_get_user(UserPoolId=user_pool_id, Username=username) \ + ["Enabled"].should.equal(False) + + +@mock_cognitoidp +def test_admin_enable_user(): + conn = boto3.client("cognito-idp", "us-west-2") + + username = str(uuid.uuid4()) + user_pool_id = conn.create_user_pool(PoolName=str(uuid.uuid4()))["UserPool"]["Id"] + conn.admin_create_user(UserPoolId=user_pool_id, Username=username) + conn.admin_disable_user(UserPoolId=user_pool_id, Username=username) + + result = conn.admin_enable_user(UserPoolId=user_pool_id, Username=username) + list(result.keys()).should.equal(["ResponseMetadata"]) # No response expected + + conn.admin_get_user(UserPoolId=user_pool_id, Username=username) \ + ["Enabled"].should.equal(True) + + @mock_cognitoidp def test_admin_delete_user(): conn = boto3.client("cognito-idp", "us-west-2") @@ -391,7 +439,7 @@ def test_admin_delete_user(): caught = False try: conn.admin_get_user(UserPoolId=user_pool_id, Username=username) - except conn.exceptions.ResourceNotFoundException: + except conn.exceptions.UserNotFoundException: caught = True caught.should.be.true diff --git a/tests/test_iam/test_iam_groups.py b/tests/test_iam/test_iam_groups.py index 49c7987f6..0d4756f75 100644 --- a/tests/test_iam/test_iam_groups.py +++ b/tests/test_iam/test_iam_groups.py @@ -1,4 +1,7 @@ from __future__ import unicode_literals + +from datetime import datetime + import boto import boto3 import sure # noqa @@ -25,6 +28,25 @@ def test_get_group(): conn.get_group('not-group') +@mock_iam() +def test_get_group_current(): + conn = boto3.client('iam', region_name='us-east-1') + conn.create_group(GroupName='my-group') + result = conn.get_group(GroupName='my-group') + + assert result['Group']['Path'] == '/' + assert result['Group']['GroupName'] == 'my-group' + assert isinstance(result['Group']['CreateDate'], datetime) + assert result['Group']['GroupId'] + assert result['Group']['Arn'] == 'arn:aws:iam::123456789012:group/my-group' + assert not result['Users'] + + # Make a group with a different path: + other_group = conn.create_group(GroupName='my-other-group', Path='some/location') + assert other_group['Group']['Path'] == 'some/location' + assert other_group['Group']['Arn'] == 'arn:aws:iam::123456789012:group/some/location/my-other-group' + + @mock_iam_deprecated() def test_get_all_groups(): conn = boto.connect_iam()