diff --git a/IMPLEMENTATION_COVERAGE.md b/IMPLEMENTATION_COVERAGE.md index 0c05e8b22..db77f06e4 100644 --- a/IMPLEMENTATION_COVERAGE.md +++ b/IMPLEMENTATION_COVERAGE.md @@ -1664,7 +1664,7 @@ ## cognito-idp
-37% implemented +38% implemented - [ ] add_custom_attributes - [X] admin_add_user_to_group @@ -1700,7 +1700,7 @@ - [ ] confirm_sign_up - [X] create_group - [X] create_identity_provider -- [ ] create_resource_server +- [X] create_resource_server - [ ] create_user_import_job - [X] create_user_pool - [X] create_user_pool_client diff --git a/moto/cognitoidp/exceptions.py b/moto/cognitoidp/exceptions.py index e52b7c49f..c9b6368ca 100644 --- a/moto/cognitoidp/exceptions.py +++ b/moto/cognitoidp/exceptions.py @@ -2,6 +2,7 @@ from __future__ import unicode_literals import json from werkzeug.exceptions import BadRequest +from moto.core.exceptions import JsonRESTError class ResourceNotFoundError(BadRequest): @@ -42,3 +43,11 @@ class NotAuthorizedError(BadRequest): self.description = json.dumps( {"message": message, "__type": "NotAuthorizedException"} ) + + +class InvalidParameterException(JsonRESTError): + def __init__(self, msg=None): + self.code = 400 + super(InvalidParameterException, self).__init__( + "InvalidParameterException", msg or "A parameter is specified incorrectly." + ) diff --git a/moto/cognitoidp/models.py b/moto/cognitoidp/models.py index c93563c2a..4d3280272 100644 --- a/moto/cognitoidp/models.py +++ b/moto/cognitoidp/models.py @@ -21,6 +21,7 @@ from .exceptions import ( ResourceNotFoundError, UserNotFoundError, UsernameExistsException, + InvalidParameterException, ) UserStatus = { @@ -83,6 +84,7 @@ class CognitoIdpUserPool(BaseModel): self.identity_providers = OrderedDict() self.groups = OrderedDict() self.users = OrderedDict() + self.resource_servers = OrderedDict() self.refresh_tokens = {} self.access_tokens = {} self.id_tokens = {} @@ -337,6 +339,27 @@ class CognitoIdpUser(BaseModel): self.attributes = expand_attrs(flat_attributes) +class CognitoResourceServer(BaseModel): + def __init__(self, user_pool_id, identifier, name, scopes): + + self.user_pool_id = user_pool_id + self.identifier = identifier + self.name = name + self.scopes = scopes + + def to_json(self): + res = { + "UserPoolId": self.user_pool_id, + "Identifier": self.identifier, + "Name": self.name, + } + + if len(self.scopes) != 0: + res.update({"Scopes": self.scopes}) + + return res + + class CognitoIdpBackend(BaseBackend): def __init__(self, region): super(CognitoIdpBackend, self).__init__() @@ -768,6 +791,20 @@ class CognitoIdpBackend(BaseBackend): user = user_pool.users[username] user.update_attributes(attributes) + def create_resource_server(self, user_pool_id, identifier, name, scopes): + user_pool = self.user_pools.get(user_pool_id) + if not user_pool: + raise ResourceNotFoundError(user_pool_id) + + if identifier in user_pool.resource_servers: + raise InvalidParameterException( + "%s already exists in user pool %s." % (identifier, user_pool_id) + ) + + resource_server = CognitoResourceServer(user_pool_id, identifier, name, scopes) + user_pool.resource_servers[identifier] = resource_server + return resource_server + cognitoidp_backends = {} for region in Session().get_available_regions("cognito-idp"): diff --git a/moto/cognitoidp/responses.py b/moto/cognitoidp/responses.py index 1c945b23e..972ba883a 100644 --- a/moto/cognitoidp/responses.py +++ b/moto/cognitoidp/responses.py @@ -379,6 +379,17 @@ class CognitoIdpResponse(BaseResponse): ) return "" + # Resource Server + def create_resource_server(self): + user_pool_id = self._get_param("UserPoolId") + identifier = self._get_param("Identifier") + name = self._get_param("Name") + scopes = self._get_param("Scopes") + resource_server = cognitoidp_backends[self.region].create_resource_server( + user_pool_id, identifier, name, scopes + ) + return json.dumps({"ResourceServer": resource_server.to_json()}) + class CognitoIdpJsonWebKeyResponse(BaseResponse): def __init__(self): diff --git a/tests/test_cognitoidp/test_cognitoidp.py b/tests/test_cognitoidp/test_cognitoidp.py index d76587d1b..e05f4b457 100644 --- a/tests/test_cognitoidp/test_cognitoidp.py +++ b/tests/test_cognitoidp/test_cognitoidp.py @@ -1398,6 +1398,44 @@ def test_admin_update_user_attributes(): val.should.equal("Jane") +@mock_cognitoidp +def test_resource_server(): + + client = boto3.client("cognito-idp", "us-west-2") + name = str(uuid.uuid4()) + value = str(uuid.uuid4()) + res = client.create_user_pool(PoolName=name) + + user_pool_id = res["UserPool"]["Id"] + identifier = "http://localhost.localdomain" + name = "local server" + scopes = [ + {"ScopeName": "app:write", "ScopeDescription": "write scope"}, + {"ScopeName": "app:read", "ScopeDescription": "read scope"}, + ] + + res = client.create_resource_server( + UserPoolId=user_pool_id, Identifier=identifier, Name=name, Scopes=scopes + ) + + res["ResourceServer"]["UserPoolId"].should.equal(user_pool_id) + res["ResourceServer"]["Identifier"].should.equal(identifier) + res["ResourceServer"]["Name"].should.equal(name) + res["ResourceServer"]["Scopes"].should.equal(scopes) + + with assert_raises(ClientError) as ex: + client.create_resource_server( + UserPoolId=user_pool_id, Identifier=identifier, Name=name, Scopes=scopes + ) + + ex.exception.operation_name.should.equal("CreateResourceServer") + ex.exception.response["Error"]["Code"].should.equal("InvalidParameterException") + ex.exception.response["Error"]["Message"].should.equal( + "%s already exists in user pool %s." % (identifier, user_pool_id) + ) + ex.exception.response["ResponseMetadata"]["HTTPStatusCode"].should.equal(400) + + # Test will retrieve public key from cognito.amazonaws.com/.well-known/jwks.json, # which isnt mocked in ServerMode if not settings.TEST_SERVER_MODE: