Implement some of cognito-idp
This commit is contained in:
parent
cb364eedc6
commit
e111cd1ff9
@ -2,5 +2,6 @@ include README.md LICENSE AUTHORS.md
|
||||
include requirements.txt requirements-dev.txt tox.ini
|
||||
include moto/ec2/resources/instance_types.json
|
||||
include moto/ec2/resources/amis.json
|
||||
include moto/cognitoidp/resources/*.json
|
||||
recursive-include moto/templates *
|
||||
recursive-include tests *
|
||||
recursive-include tests *
|
||||
|
@ -72,6 +72,8 @@ It gets even better! Moto isn't just for Python code and it isn't just for S3. L
|
||||
|------------------------------------------------------------------------------|
|
||||
| Cognito Identity | @mock_cognitoidentity| basic endpoints done |
|
||||
|------------------------------------------------------------------------------|
|
||||
| Cognito Identity Provider | @mock_cognitoidp| basic endpoints done |
|
||||
|------------------------------------------------------------------------------|
|
||||
| Data Pipeline | @mock_datapipeline| basic endpoints done |
|
||||
|------------------------------------------------------------------------------|
|
||||
| DynamoDB | @mock_dynamodb | core endpoints done |
|
||||
|
@ -12,6 +12,7 @@ from .awslambda import mock_lambda, mock_lambda_deprecated # flake8: noqa
|
||||
from .cloudformation import mock_cloudformation, mock_cloudformation_deprecated # flake8: noqa
|
||||
from .cloudwatch import mock_cloudwatch, mock_cloudwatch_deprecated # flake8: noqa
|
||||
from .cognitoidentity import mock_cognitoidentity, mock_cognitoidentity_deprecated # flake8: noqa
|
||||
from .cognitoidp import mock_cognitoidp, mock_cognitoidp_deprecated # flake8: noqa
|
||||
from .datapipeline import mock_datapipeline, mock_datapipeline_deprecated # flake8: noqa
|
||||
from .dynamodb import mock_dynamodb, mock_dynamodb_deprecated # flake8: noqa
|
||||
from .dynamodb2 import mock_dynamodb2, mock_dynamodb2_deprecated # flake8: noqa
|
||||
|
@ -7,6 +7,7 @@ from moto.awslambda import lambda_backends
|
||||
from moto.cloudformation import cloudformation_backends
|
||||
from moto.cloudwatch import cloudwatch_backends
|
||||
from moto.cognitoidentity import cognitoidentity_backends
|
||||
from moto.cognitoidp import cognitoidp_backends
|
||||
from moto.core import moto_api_backends
|
||||
from moto.datapipeline import datapipeline_backends
|
||||
from moto.dynamodb import dynamodb_backends
|
||||
@ -51,6 +52,7 @@ BACKENDS = {
|
||||
'cloudformation': cloudformation_backends,
|
||||
'cloudwatch': cloudwatch_backends,
|
||||
'cognito-identity': cognitoidentity_backends,
|
||||
'cognito-idp': cognitoidp_backends,
|
||||
'datapipeline': datapipeline_backends,
|
||||
'dynamodb': dynamodb_backends,
|
||||
'dynamodb2': dynamodb_backends2,
|
||||
|
6
moto/cognitoidp/__init__.py
Normal file
6
moto/cognitoidp/__init__.py
Normal file
@ -0,0 +1,6 @@
|
||||
from __future__ import unicode_literals
|
||||
from .models import cognitoidp_backends
|
||||
from ..core.models import base_decorator, deprecated_base_decorator
|
||||
|
||||
mock_cognitoidp = base_decorator(cognitoidp_backends)
|
||||
mock_cognitoidp_deprecated = deprecated_base_decorator(cognitoidp_backends)
|
34
moto/cognitoidp/exceptions.py
Normal file
34
moto/cognitoidp/exceptions.py
Normal file
@ -0,0 +1,34 @@
|
||||
from __future__ import unicode_literals
|
||||
|
||||
import json
|
||||
from werkzeug.exceptions import BadRequest
|
||||
|
||||
|
||||
class ResourceNotFoundError(BadRequest):
|
||||
|
||||
def __init__(self, message):
|
||||
super(ResourceNotFoundError, self).__init__()
|
||||
self.description = json.dumps({
|
||||
"message": message,
|
||||
'__type': 'ResourceNotFoundException',
|
||||
})
|
||||
|
||||
|
||||
class UserNotFoundError(BadRequest):
|
||||
|
||||
def __init__(self, message):
|
||||
super(UserNotFoundError, self).__init__()
|
||||
self.description = json.dumps({
|
||||
"message": message,
|
||||
'__type': 'UserNotFoundException',
|
||||
})
|
||||
|
||||
|
||||
class NotAuthorizedError(BadRequest):
|
||||
|
||||
def __init__(self, message):
|
||||
super(NotAuthorizedError, self).__init__()
|
||||
self.description = json.dumps({
|
||||
"message": message,
|
||||
'__type': 'NotAuthorizedException',
|
||||
})
|
512
moto/cognitoidp/models.py
Normal file
512
moto/cognitoidp/models.py
Normal file
@ -0,0 +1,512 @@
|
||||
from __future__ import unicode_literals
|
||||
|
||||
import datetime
|
||||
import json
|
||||
import os
|
||||
import time
|
||||
import uuid
|
||||
|
||||
import boto.cognito.identity
|
||||
from jose import jws
|
||||
|
||||
from moto.compat import OrderedDict
|
||||
from moto.core import BaseBackend, BaseModel
|
||||
from .exceptions import NotAuthorizedError, ResourceNotFoundError, UserNotFoundError
|
||||
|
||||
|
||||
UserStatus = {
|
||||
"FORCE_CHANGE_PASSWORD": "FORCE_CHANGE_PASSWORD",
|
||||
"CONFIRMED": "CONFIRMED",
|
||||
}
|
||||
|
||||
|
||||
class CognitoIdpUserPool(BaseModel):
|
||||
|
||||
def __init__(self, region, name, extended_config):
|
||||
self.region = region
|
||||
self.id = str(uuid.uuid4())
|
||||
self.name = name
|
||||
self.status = None
|
||||
self.extended_config = extended_config or {}
|
||||
self.creation_date = datetime.datetime.utcnow()
|
||||
self.last_modified_date = datetime.datetime.utcnow()
|
||||
|
||||
self.clients = OrderedDict()
|
||||
self.identity_providers = OrderedDict()
|
||||
self.users = OrderedDict()
|
||||
self.refresh_tokens = {}
|
||||
self.access_tokens = {}
|
||||
self.id_tokens = {}
|
||||
|
||||
with open(os.path.join(os.path.dirname(__file__), "resources/jwks-private.json")) as f:
|
||||
self.json_web_key = json.loads(f.read())
|
||||
|
||||
def _base_json(self):
|
||||
return {
|
||||
"Id": self.id,
|
||||
"Name": self.name,
|
||||
"Status": self.status,
|
||||
"CreationDate": time.mktime(self.creation_date.timetuple()),
|
||||
"LastModifiedDate": time.mktime(self.last_modified_date.timetuple()),
|
||||
}
|
||||
|
||||
def to_json(self, extended=False):
|
||||
user_pool_json = self._base_json()
|
||||
if extended:
|
||||
user_pool_json.update(self.extended_config)
|
||||
else:
|
||||
user_pool_json["LambdaConfig"] = self.extended_config.get("LambdaConfig") or {}
|
||||
|
||||
return user_pool_json
|
||||
|
||||
def create_jwt(self, client_id, username, expires_in=60 * 60, extra_data={}):
|
||||
now = int(time.time())
|
||||
payload = {
|
||||
"iss": "https://cognito-idp.{}.amazonaws.com/{}".format(self.region, self.id),
|
||||
"sub": self.users[username].id,
|
||||
"aud": client_id,
|
||||
"token_use": "id",
|
||||
"auth_time": now,
|
||||
"exp": now + expires_in,
|
||||
}
|
||||
payload.update(extra_data)
|
||||
|
||||
return jws.sign(payload, self.json_web_key, algorithm='RS256'), expires_in
|
||||
|
||||
def create_id_token(self, client_id, username):
|
||||
id_token, expires_in = self.create_jwt(client_id, username)
|
||||
self.id_tokens[id_token] = (client_id, username)
|
||||
return id_token, expires_in
|
||||
|
||||
def create_refresh_token(self, client_id, username):
|
||||
refresh_token = str(uuid.uuid4())
|
||||
self.refresh_tokens[refresh_token] = (client_id, username)
|
||||
return refresh_token
|
||||
|
||||
def create_access_token(self, client_id, username):
|
||||
access_token, expires_in = self.create_jwt(client_id, username)
|
||||
self.access_tokens[access_token] = (client_id, username)
|
||||
return access_token, expires_in
|
||||
|
||||
def create_tokens_from_refresh_token(self, refresh_token):
|
||||
client_id, username = self.refresh_tokens.get(refresh_token)
|
||||
if not username:
|
||||
raise NotAuthorizedError(refresh_token)
|
||||
|
||||
access_token, expires_in = self.create_access_token(client_id, username)
|
||||
id_token, _ = self.create_id_token(client_id, username)
|
||||
return access_token, id_token, expires_in
|
||||
|
||||
|
||||
class CognitoIdpUserPoolDomain(BaseModel):
|
||||
|
||||
def __init__(self, user_pool_id, domain):
|
||||
self.user_pool_id = user_pool_id
|
||||
self.domain = domain
|
||||
|
||||
def to_json(self):
|
||||
return {
|
||||
"UserPoolId": self.user_pool_id,
|
||||
"AWSAccountId": str(uuid.uuid4()),
|
||||
"CloudFrontDistribution": None,
|
||||
"Domain": self.domain,
|
||||
"S3Bucket": None,
|
||||
"Status": "ACTIVE",
|
||||
"Version": None,
|
||||
}
|
||||
|
||||
|
||||
class CognitoIdpUserPoolClient(BaseModel):
|
||||
|
||||
def __init__(self, user_pool_id, extended_config):
|
||||
self.user_pool_id = user_pool_id
|
||||
self.id = str(uuid.uuid4())
|
||||
self.secret = str(uuid.uuid4())
|
||||
self.extended_config = extended_config or {}
|
||||
|
||||
def _base_json(self):
|
||||
return {
|
||||
"ClientId": self.id,
|
||||
"ClientName": self.extended_config.get("ClientName"),
|
||||
"UserPoolId": self.user_pool_id,
|
||||
}
|
||||
|
||||
def to_json(self, extended=False):
|
||||
user_pool_client_json = self._base_json()
|
||||
if extended:
|
||||
user_pool_client_json.update(self.extended_config)
|
||||
|
||||
return user_pool_client_json
|
||||
|
||||
|
||||
class CognitoIdpIdentityProvider(BaseModel):
|
||||
|
||||
def __init__(self, name, extended_config):
|
||||
self.name = name
|
||||
self.extended_config = extended_config or {}
|
||||
self.creation_date = datetime.datetime.utcnow()
|
||||
self.last_modified_date = datetime.datetime.utcnow()
|
||||
|
||||
def _base_json(self):
|
||||
return {
|
||||
"ProviderName": self.name,
|
||||
"ProviderType": self.extended_config.get("ProviderType"),
|
||||
"CreationDate": time.mktime(self.creation_date.timetuple()),
|
||||
"LastModifiedDate": time.mktime(self.last_modified_date.timetuple()),
|
||||
}
|
||||
|
||||
def to_json(self, extended=False):
|
||||
identity_provider_json = self._base_json()
|
||||
if extended:
|
||||
identity_provider_json.update(self.extended_config)
|
||||
|
||||
return identity_provider_json
|
||||
|
||||
|
||||
class CognitoIdpUser(BaseModel):
|
||||
|
||||
def __init__(self, user_pool_id, username, password, status, attributes):
|
||||
self.id = str(uuid.uuid4())
|
||||
self.user_pool_id = user_pool_id
|
||||
self.username = username
|
||||
self.password = password
|
||||
self.status = status
|
||||
self.enabled = True
|
||||
self.attributes = attributes
|
||||
self.create_date = datetime.datetime.utcnow()
|
||||
self.last_modified_date = datetime.datetime.utcnow()
|
||||
|
||||
def _base_json(self):
|
||||
return {
|
||||
"UserPoolId": self.user_pool_id,
|
||||
"Username": self.username,
|
||||
"UserStatus": self.status,
|
||||
"UserCreateDate": time.mktime(self.create_date.timetuple()),
|
||||
"UserLastModifiedDate": time.mktime(self.last_modified_date.timetuple()),
|
||||
}
|
||||
|
||||
# list_users brings back "Attributes" while admin_get_user brings back "UserAttributes".
|
||||
def to_json(self, extended=False, attributes_key="Attributes"):
|
||||
user_json = self._base_json()
|
||||
if extended:
|
||||
user_json.update(
|
||||
{
|
||||
"Enabled": self.enabled,
|
||||
attributes_key: self.attributes,
|
||||
"MFAOptions": []
|
||||
}
|
||||
)
|
||||
|
||||
return user_json
|
||||
|
||||
|
||||
class CognitoIdpBackend(BaseBackend):
|
||||
|
||||
def __init__(self, region):
|
||||
super(CognitoIdpBackend, self).__init__()
|
||||
self.region = region
|
||||
self.user_pools = OrderedDict()
|
||||
self.user_pool_domains = OrderedDict()
|
||||
self.sessions = {}
|
||||
|
||||
def reset(self):
|
||||
region = self.region
|
||||
self.__dict__ = {}
|
||||
self.__init__(region)
|
||||
|
||||
# User pool
|
||||
def create_user_pool(self, name, extended_config):
|
||||
user_pool = CognitoIdpUserPool(self.region, name, extended_config)
|
||||
self.user_pools[user_pool.id] = user_pool
|
||||
return user_pool
|
||||
|
||||
def list_user_pools(self):
|
||||
return self.user_pools.values()
|
||||
|
||||
def describe_user_pool(self, user_pool_id):
|
||||
user_pool = self.user_pools.get(user_pool_id)
|
||||
if not user_pool:
|
||||
raise ResourceNotFoundError(user_pool_id)
|
||||
|
||||
return user_pool
|
||||
|
||||
def delete_user_pool(self, user_pool_id):
|
||||
if user_pool_id not in self.user_pools:
|
||||
raise ResourceNotFoundError(user_pool_id)
|
||||
|
||||
del self.user_pools[user_pool_id]
|
||||
|
||||
# User pool domain
|
||||
def create_user_pool_domain(self, user_pool_id, domain):
|
||||
if user_pool_id not in self.user_pools:
|
||||
raise ResourceNotFoundError(user_pool_id)
|
||||
|
||||
user_pool_domain = CognitoIdpUserPoolDomain(user_pool_id, domain)
|
||||
self.user_pool_domains[domain] = user_pool_domain
|
||||
return user_pool_domain
|
||||
|
||||
def describe_user_pool_domain(self, domain):
|
||||
if domain not in self.user_pool_domains:
|
||||
return None
|
||||
|
||||
return self.user_pool_domains[domain]
|
||||
|
||||
def delete_user_pool_domain(self, domain):
|
||||
if domain not in self.user_pool_domains:
|
||||
raise ResourceNotFoundError(domain)
|
||||
|
||||
del self.user_pool_domains[domain]
|
||||
|
||||
# User pool client
|
||||
def create_user_pool_client(self, user_pool_id, extended_config):
|
||||
user_pool = self.user_pools.get(user_pool_id)
|
||||
if not user_pool:
|
||||
raise ResourceNotFoundError(user_pool_id)
|
||||
|
||||
user_pool_client = CognitoIdpUserPoolClient(user_pool_id, extended_config)
|
||||
user_pool.clients[user_pool_client.id] = user_pool_client
|
||||
return user_pool_client
|
||||
|
||||
def list_user_pool_clients(self, user_pool_id):
|
||||
user_pool = self.user_pools.get(user_pool_id)
|
||||
if not user_pool:
|
||||
raise ResourceNotFoundError(user_pool_id)
|
||||
|
||||
return user_pool.clients.values()
|
||||
|
||||
def describe_user_pool_client(self, user_pool_id, client_id):
|
||||
user_pool = self.user_pools.get(user_pool_id)
|
||||
if not user_pool:
|
||||
raise ResourceNotFoundError(user_pool_id)
|
||||
|
||||
client = user_pool.clients.get(client_id)
|
||||
if not client:
|
||||
raise ResourceNotFoundError(client_id)
|
||||
|
||||
return client
|
||||
|
||||
def update_user_pool_client(self, user_pool_id, client_id, extended_config):
|
||||
user_pool = self.user_pools.get(user_pool_id)
|
||||
if not user_pool:
|
||||
raise ResourceNotFoundError(user_pool_id)
|
||||
|
||||
client = user_pool.clients.get(client_id)
|
||||
if not client:
|
||||
raise ResourceNotFoundError(client_id)
|
||||
|
||||
client.extended_config.update(extended_config)
|
||||
return client
|
||||
|
||||
def delete_user_pool_client(self, user_pool_id, client_id):
|
||||
user_pool = self.user_pools.get(user_pool_id)
|
||||
if not user_pool:
|
||||
raise ResourceNotFoundError(user_pool_id)
|
||||
|
||||
if client_id not in user_pool.clients:
|
||||
raise ResourceNotFoundError(client_id)
|
||||
|
||||
del user_pool.clients[client_id]
|
||||
|
||||
# Identity provider
|
||||
def create_identity_provider(self, user_pool_id, name, extended_config):
|
||||
user_pool = self.user_pools.get(user_pool_id)
|
||||
if not user_pool:
|
||||
raise ResourceNotFoundError(user_pool_id)
|
||||
|
||||
identity_provider = CognitoIdpIdentityProvider(name, extended_config)
|
||||
user_pool.identity_providers[name] = identity_provider
|
||||
return identity_provider
|
||||
|
||||
def list_identity_providers(self, user_pool_id):
|
||||
user_pool = self.user_pools.get(user_pool_id)
|
||||
if not user_pool:
|
||||
raise ResourceNotFoundError(user_pool_id)
|
||||
|
||||
return user_pool.identity_providers.values()
|
||||
|
||||
def describe_identity_provider(self, user_pool_id, name):
|
||||
user_pool = self.user_pools.get(user_pool_id)
|
||||
if not user_pool:
|
||||
raise ResourceNotFoundError(user_pool_id)
|
||||
|
||||
identity_provider = user_pool.identity_providers.get(name)
|
||||
if not identity_provider:
|
||||
raise ResourceNotFoundError(name)
|
||||
|
||||
return identity_provider
|
||||
|
||||
def delete_identity_provider(self, user_pool_id, name):
|
||||
user_pool = self.user_pools.get(user_pool_id)
|
||||
if not user_pool:
|
||||
raise ResourceNotFoundError(user_pool_id)
|
||||
|
||||
if name not in user_pool.identity_providers:
|
||||
raise ResourceNotFoundError(name)
|
||||
|
||||
del user_pool.identity_providers[name]
|
||||
|
||||
# User
|
||||
def admin_create_user(self, user_pool_id, username, temporary_password, attributes):
|
||||
user_pool = self.user_pools.get(user_pool_id)
|
||||
if not user_pool:
|
||||
raise ResourceNotFoundError(user_pool_id)
|
||||
|
||||
user = CognitoIdpUser(user_pool_id, username, temporary_password, UserStatus["FORCE_CHANGE_PASSWORD"], attributes)
|
||||
user_pool.users[user.username] = user
|
||||
return user
|
||||
|
||||
def admin_get_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)
|
||||
|
||||
return user_pool.users[username]
|
||||
|
||||
def list_users(self, user_pool_id):
|
||||
user_pool = self.user_pools.get(user_pool_id)
|
||||
if not user_pool:
|
||||
raise ResourceNotFoundError(user_pool_id)
|
||||
|
||||
return user_pool.users.values()
|
||||
|
||||
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)
|
||||
|
||||
del user_pool.users[username]
|
||||
|
||||
def _log_user_in(self, user_pool, client, username):
|
||||
refresh_token = user_pool.create_refresh_token(client.id, username)
|
||||
access_token, id_token, expires_in = user_pool.create_tokens_from_refresh_token(refresh_token)
|
||||
|
||||
return {
|
||||
"AuthenticationResult": {
|
||||
"IdToken": id_token,
|
||||
"AccessToken": access_token,
|
||||
"RefreshToken": refresh_token,
|
||||
"ExpiresIn": expires_in,
|
||||
}
|
||||
}
|
||||
|
||||
def admin_initiate_auth(self, user_pool_id, client_id, auth_flow, auth_parameters):
|
||||
user_pool = self.user_pools.get(user_pool_id)
|
||||
if not user_pool:
|
||||
raise ResourceNotFoundError(user_pool_id)
|
||||
|
||||
client = user_pool.clients.get(client_id)
|
||||
if not client:
|
||||
raise ResourceNotFoundError(client_id)
|
||||
|
||||
if auth_flow == "ADMIN_NO_SRP_AUTH":
|
||||
username = auth_parameters.get("USERNAME")
|
||||
password = auth_parameters.get("PASSWORD")
|
||||
user = user_pool.users.get(username)
|
||||
if not user:
|
||||
raise UserNotFoundError(username)
|
||||
|
||||
if user.password != password:
|
||||
raise NotAuthorizedError(username)
|
||||
|
||||
if user.status == UserStatus["FORCE_CHANGE_PASSWORD"]:
|
||||
session = str(uuid.uuid4())
|
||||
self.sessions[session] = user_pool
|
||||
|
||||
return {
|
||||
"ChallengeName": "NEW_PASSWORD_REQUIRED",
|
||||
"ChallengeParameters": {},
|
||||
"Session": session,
|
||||
}
|
||||
|
||||
return self._log_user_in(user_pool, client, username)
|
||||
elif auth_flow == "REFRESH_TOKEN":
|
||||
refresh_token = auth_parameters.get("REFRESH_TOKEN")
|
||||
id_token, access_token, expires_in = user_pool.create_tokens_from_refresh_token(refresh_token)
|
||||
|
||||
return {
|
||||
"AuthenticationResult": {
|
||||
"IdToken": id_token,
|
||||
"AccessToken": access_token,
|
||||
"ExpiresIn": expires_in,
|
||||
}
|
||||
}
|
||||
else:
|
||||
return {}
|
||||
|
||||
def respond_to_auth_challenge(self, session, client_id, challenge_name, challenge_responses):
|
||||
user_pool = self.sessions.get(session)
|
||||
if not user_pool:
|
||||
raise ResourceNotFoundError(session)
|
||||
|
||||
client = user_pool.clients.get(client_id)
|
||||
if not client:
|
||||
raise ResourceNotFoundError(client_id)
|
||||
|
||||
if challenge_name == "NEW_PASSWORD_REQUIRED":
|
||||
username = challenge_responses.get("USERNAME")
|
||||
new_password = challenge_responses.get("NEW_PASSWORD")
|
||||
user = user_pool.users.get(username)
|
||||
if not user:
|
||||
raise UserNotFoundError(username)
|
||||
|
||||
user.password = new_password
|
||||
user.status = UserStatus["CONFIRMED"]
|
||||
del self.sessions[session]
|
||||
|
||||
return self._log_user_in(user_pool, client, username)
|
||||
else:
|
||||
return {}
|
||||
|
||||
def confirm_forgot_password(self, client_id, username, password):
|
||||
for user_pool in self.user_pools.values():
|
||||
if client_id in user_pool.clients and username in user_pool.users:
|
||||
user_pool.users[username].password = password
|
||||
break
|
||||
else:
|
||||
raise ResourceNotFoundError(client_id)
|
||||
|
||||
def change_password(self, access_token, previous_password, proposed_password):
|
||||
for user_pool in self.user_pools.values():
|
||||
if access_token in user_pool.access_tokens:
|
||||
_, username = user_pool.access_tokens[access_token]
|
||||
user = user_pool.users.get(username)
|
||||
if not user:
|
||||
raise UserNotFoundError(username)
|
||||
|
||||
if user.password != previous_password:
|
||||
raise NotAuthorizedError(username)
|
||||
|
||||
user.password = proposed_password
|
||||
if user.status == UserStatus["FORCE_CHANGE_PASSWORD"]:
|
||||
user.status = UserStatus["CONFIRMED"]
|
||||
|
||||
break
|
||||
else:
|
||||
raise NotAuthorizedError(access_token)
|
||||
|
||||
|
||||
cognitoidp_backends = {}
|
||||
for region in boto.cognito.identity.regions():
|
||||
cognitoidp_backends[region.name] = CognitoIdpBackend(region.name)
|
||||
|
||||
|
||||
# Hack to help moto-server process requests on localhost, where the region isn't
|
||||
# specified in the host header. Some endpoints (change password, confirm forgot
|
||||
# password) have no authorization header from which to extract the region.
|
||||
def find_region_by_value(key, value):
|
||||
for region in cognitoidp_backends:
|
||||
backend = cognitoidp_backends[region]
|
||||
for user_pool in backend.user_pools.values():
|
||||
if key == "client_id" and value in user_pool.clients:
|
||||
return region
|
||||
|
||||
if key == "access_token" and value in user_pool.access_tokens:
|
||||
return region
|
||||
|
||||
return cognitoidp_backends.keys()[0]
|
9
moto/cognitoidp/resources/jwks-private.json
Normal file
9
moto/cognitoidp/resources/jwks-private.json
Normal file
@ -0,0 +1,9 @@
|
||||
{
|
||||
"alg": "RS256",
|
||||
"d": "DrrLT2qMERN0Id-bNglOe6SVkUNF3MTIzrH-TVkMZhsHk8kyqiqt-8JbLQMh2gOgTIjpu93b2_UREGA0BGdWs34hv0v7Gx8uIngCY6e6XO8LDemOo-2VHZHl5Ew-lrRYhwq12c_c4mfavAdMzXHODrpXSnqLnbFK88S-3fu6Da4czc4Svo4v8MkGZk_fcTml3Y1jIFHxbbTWka37j4NLpAzdfvX--J086m-LbZ8CJL_lGMKbAKsWURMmzCFL9ZFH9JdzX79KeDOH0GrzGwS_cOsZHsCamF_CWrtG4asPt-SHyn_k0X4JJJgAWVA674VCqorMAPDVYIzKJOUMImmsEQ",
|
||||
"e": "AQAB",
|
||||
"kid": "dummy",
|
||||
"kty": "RSA",
|
||||
"n": "j1pT3xKbswmMySvCefmiD3mfDaRFpZ9Y3Jl4fF0hMaCRVAt_e0yR7BeueDfqmj_NhVSO0WB5ao5e8V-9RFQOtK8SrqKl3i01-CyWYPICwybaGKhbJJR0S_6cZ8n5kscF1MjpIlsJcCzm-yKgTc3Mxk6KtrLoNgRvMwGLeHUXPkhS9YHfDKRe864iMFOK4df69brIYEICG2VLduh0hXYa0i-J3drwm7vxNdX7pVpCDu34qJtYoWq6CXt3Tzfi3YfWp8cFjGNbaDa3WnCd2IXpp0TFsFS-cEsw5rJjSl5OllJGeZKBtLeyVTy9PYwnk7MW43WSYeYstbk9NluX4H8Iuw",
|
||||
"use": "sig"
|
||||
}
|
12
moto/cognitoidp/resources/jwks-public.json
Normal file
12
moto/cognitoidp/resources/jwks-public.json
Normal file
@ -0,0 +1,12 @@
|
||||
{
|
||||
"keys": [
|
||||
{
|
||||
"alg": "RS256",
|
||||
"e": "AQAB",
|
||||
"kid": "dummy",
|
||||
"kty": "RSA",
|
||||
"n": "j1pT3xKbswmMySvCefmiD3mfDaRFpZ9Y3Jl4fF0hMaCRVAt_e0yR7BeueDfqmj_NhVSO0WB5ao5e8V-9RFQOtK8SrqKl3i01-CyWYPICwybaGKhbJJR0S_6cZ8n5kscF1MjpIlsJcCzm-yKgTc3Mxk6KtrLoNgRvMwGLeHUXPkhS9YHfDKRe864iMFOK4df69brIYEICG2VLduh0hXYa0i-J3drwm7vxNdX7pVpCDu34qJtYoWq6CXt3Tzfi3YfWp8cFjGNbaDa3WnCd2IXpp0TFsFS-cEsw5rJjSl5OllJGeZKBtLeyVTy9PYwnk7MW43WSYeYstbk9NluX4H8Iuw",
|
||||
"use": "sig"
|
||||
}
|
||||
]
|
||||
}
|
235
moto/cognitoidp/responses.py
Normal file
235
moto/cognitoidp/responses.py
Normal file
@ -0,0 +1,235 @@
|
||||
from __future__ import unicode_literals
|
||||
|
||||
import json
|
||||
import os
|
||||
|
||||
from moto.core.responses import BaseResponse
|
||||
from .models import cognitoidp_backends, find_region_by_value
|
||||
|
||||
|
||||
class CognitoIdpResponse(BaseResponse):
|
||||
|
||||
@property
|
||||
def parameters(self):
|
||||
return json.loads(self.body)
|
||||
|
||||
# User pool
|
||||
def create_user_pool(self):
|
||||
name = self.parameters.pop("PoolName")
|
||||
user_pool = cognitoidp_backends[self.region].create_user_pool(name, self.parameters)
|
||||
return json.dumps({
|
||||
"UserPool": user_pool.to_json(extended=True)
|
||||
})
|
||||
|
||||
def list_user_pools(self):
|
||||
user_pools = cognitoidp_backends[self.region].list_user_pools()
|
||||
return json.dumps({
|
||||
"UserPools": [user_pool.to_json() for user_pool in user_pools]
|
||||
})
|
||||
|
||||
def describe_user_pool(self):
|
||||
user_pool_id = self._get_param("UserPoolId")
|
||||
user_pool = cognitoidp_backends[self.region].describe_user_pool(user_pool_id)
|
||||
return json.dumps({
|
||||
"UserPool": user_pool.to_json(extended=True)
|
||||
})
|
||||
|
||||
def delete_user_pool(self):
|
||||
user_pool_id = self._get_param("UserPoolId")
|
||||
cognitoidp_backends[self.region].delete_user_pool(user_pool_id)
|
||||
return ""
|
||||
|
||||
# User pool domain
|
||||
def create_user_pool_domain(self):
|
||||
domain = self._get_param("Domain")
|
||||
user_pool_id = self._get_param("UserPoolId")
|
||||
cognitoidp_backends[self.region].create_user_pool_domain(user_pool_id, domain)
|
||||
return ""
|
||||
|
||||
def describe_user_pool_domain(self):
|
||||
domain = self._get_param("Domain")
|
||||
user_pool_domain = cognitoidp_backends[self.region].describe_user_pool_domain(domain)
|
||||
domain_description = {}
|
||||
if user_pool_domain:
|
||||
domain_description = user_pool_domain.to_json()
|
||||
|
||||
return json.dumps({
|
||||
"DomainDescription": domain_description
|
||||
})
|
||||
|
||||
def delete_user_pool_domain(self):
|
||||
domain = self._get_param("Domain")
|
||||
cognitoidp_backends[self.region].delete_user_pool_domain(domain)
|
||||
return ""
|
||||
|
||||
# User pool client
|
||||
def create_user_pool_client(self):
|
||||
user_pool_id = self.parameters.pop("UserPoolId")
|
||||
user_pool_client = cognitoidp_backends[self.region].create_user_pool_client(user_pool_id, self.parameters)
|
||||
return json.dumps({
|
||||
"UserPoolClient": user_pool_client.to_json(extended=True)
|
||||
})
|
||||
|
||||
def list_user_pool_clients(self):
|
||||
user_pool_id = self._get_param("UserPoolId")
|
||||
user_pool_clients = cognitoidp_backends[self.region].list_user_pool_clients(user_pool_id)
|
||||
return json.dumps({
|
||||
"UserPoolClients": [user_pool_client.to_json() for user_pool_client in user_pool_clients]
|
||||
})
|
||||
|
||||
def describe_user_pool_client(self):
|
||||
user_pool_id = self._get_param("UserPoolId")
|
||||
client_id = self._get_param("ClientId")
|
||||
user_pool_client = cognitoidp_backends[self.region].describe_user_pool_client(user_pool_id, client_id)
|
||||
return json.dumps({
|
||||
"UserPoolClient": user_pool_client.to_json(extended=True)
|
||||
})
|
||||
|
||||
def update_user_pool_client(self):
|
||||
user_pool_id = self.parameters.pop("UserPoolId")
|
||||
client_id = self.parameters.pop("ClientId")
|
||||
user_pool_client = cognitoidp_backends[self.region].update_user_pool_client(user_pool_id, client_id, self.parameters)
|
||||
return json.dumps({
|
||||
"UserPoolClient": user_pool_client.to_json(extended=True)
|
||||
})
|
||||
|
||||
def delete_user_pool_client(self):
|
||||
user_pool_id = self._get_param("UserPoolId")
|
||||
client_id = self._get_param("ClientId")
|
||||
cognitoidp_backends[self.region].delete_user_pool_client(user_pool_id, client_id)
|
||||
return ""
|
||||
|
||||
# Identity provider
|
||||
def create_identity_provider(self):
|
||||
user_pool_id = self._get_param("UserPoolId")
|
||||
name = self.parameters.pop("ProviderName")
|
||||
identity_provider = cognitoidp_backends[self.region].create_identity_provider(user_pool_id, name, self.parameters)
|
||||
return json.dumps({
|
||||
"IdentityProvider": identity_provider.to_json(extended=True)
|
||||
})
|
||||
|
||||
def list_identity_providers(self):
|
||||
user_pool_id = self._get_param("UserPoolId")
|
||||
identity_providers = cognitoidp_backends[self.region].list_identity_providers(user_pool_id)
|
||||
return json.dumps({
|
||||
"Providers": [identity_provider.to_json() for identity_provider in identity_providers]
|
||||
})
|
||||
|
||||
def describe_identity_provider(self):
|
||||
user_pool_id = self._get_param("UserPoolId")
|
||||
name = self._get_param("ProviderName")
|
||||
identity_provider = cognitoidp_backends[self.region].describe_identity_provider(user_pool_id, name)
|
||||
return json.dumps({
|
||||
"IdentityProvider": identity_provider.to_json(extended=True)
|
||||
})
|
||||
|
||||
def delete_identity_provider(self):
|
||||
user_pool_id = self._get_param("UserPoolId")
|
||||
name = self._get_param("ProviderName")
|
||||
cognitoidp_backends[self.region].delete_identity_provider(user_pool_id, name)
|
||||
return ""
|
||||
|
||||
# User
|
||||
def admin_create_user(self):
|
||||
user_pool_id = self._get_param("UserPoolId")
|
||||
username = self._get_param("Username")
|
||||
temporary_password = self._get_param("TemporaryPassword")
|
||||
user = cognitoidp_backends[self.region].admin_create_user(
|
||||
user_pool_id,
|
||||
username,
|
||||
temporary_password,
|
||||
self._get_param("UserAttributes", [])
|
||||
)
|
||||
|
||||
return json.dumps({
|
||||
"User": user.to_json(extended=True)
|
||||
})
|
||||
|
||||
def admin_get_user(self):
|
||||
user_pool_id = self._get_param("UserPoolId")
|
||||
username = self._get_param("Username")
|
||||
user = cognitoidp_backends[self.region].admin_get_user(user_pool_id, username)
|
||||
return json.dumps(
|
||||
user.to_json(extended=True, attributes_key="UserAttributes")
|
||||
)
|
||||
|
||||
def list_users(self):
|
||||
user_pool_id = self._get_param("UserPoolId")
|
||||
users = cognitoidp_backends[self.region].list_users(user_pool_id)
|
||||
return json.dumps({
|
||||
"Users": [user.to_json(extended=True) for user in users]
|
||||
})
|
||||
|
||||
def admin_delete_user(self):
|
||||
user_pool_id = self._get_param("UserPoolId")
|
||||
username = self._get_param("Username")
|
||||
cognitoidp_backends[self.region].admin_delete_user(user_pool_id, username)
|
||||
return ""
|
||||
|
||||
def admin_initiate_auth(self):
|
||||
user_pool_id = self._get_param("UserPoolId")
|
||||
client_id = self._get_param("ClientId")
|
||||
auth_flow = self._get_param("AuthFlow")
|
||||
auth_parameters = self._get_param("AuthParameters")
|
||||
|
||||
auth_result = cognitoidp_backends[self.region].admin_initiate_auth(
|
||||
user_pool_id,
|
||||
client_id,
|
||||
auth_flow,
|
||||
auth_parameters,
|
||||
)
|
||||
|
||||
return json.dumps(auth_result)
|
||||
|
||||
def respond_to_auth_challenge(self):
|
||||
session = self._get_param("Session")
|
||||
client_id = self._get_param("ClientId")
|
||||
challenge_name = self._get_param("ChallengeName")
|
||||
challenge_responses = self._get_param("ChallengeResponses")
|
||||
auth_result = cognitoidp_backends[self.region].respond_to_auth_challenge(
|
||||
session,
|
||||
client_id,
|
||||
challenge_name,
|
||||
challenge_responses,
|
||||
)
|
||||
|
||||
return json.dumps(auth_result)
|
||||
|
||||
def forgot_password(self):
|
||||
return json.dumps({
|
||||
"CodeDeliveryDetails": {
|
||||
"DeliveryMedium": "EMAIL",
|
||||
"Destination": "...",
|
||||
}
|
||||
})
|
||||
|
||||
# This endpoint receives no authorization header, so if moto-server is listening
|
||||
# on localhost (doesn't get a region in the host header), it doesn't know what
|
||||
# region's backend should handle the traffic, and we use `find_region_by_value` to
|
||||
# solve that problem.
|
||||
def confirm_forgot_password(self):
|
||||
client_id = self._get_param("ClientId")
|
||||
username = self._get_param("Username")
|
||||
password = self._get_param("Password")
|
||||
region = find_region_by_value("client_id", client_id)
|
||||
cognitoidp_backends[region].confirm_forgot_password(client_id, username, password)
|
||||
return ""
|
||||
|
||||
# Ditto the comment on confirm_forgot_password.
|
||||
def change_password(self):
|
||||
access_token = self._get_param("AccessToken")
|
||||
previous_password = self._get_param("PreviousPassword")
|
||||
proposed_password = self._get_param("ProposedPassword")
|
||||
region = find_region_by_value("access_token", access_token)
|
||||
cognitoidp_backends[region].change_password(access_token, previous_password, proposed_password)
|
||||
return ""
|
||||
|
||||
|
||||
class CognitoIdpJsonWebKeyResponse(BaseResponse):
|
||||
|
||||
def __init__(self):
|
||||
with open(os.path.join(os.path.dirname(__file__), "resources/jwks-public.json")) as f:
|
||||
self.json_web_key = f.read()
|
||||
|
||||
def serve_json_web_key(self, request, full_url, headers):
|
||||
return 200, {"Content-Type": "application/json"}, self.json_web_key
|
11
moto/cognitoidp/urls.py
Normal file
11
moto/cognitoidp/urls.py
Normal file
@ -0,0 +1,11 @@
|
||||
from __future__ import unicode_literals
|
||||
from .responses import CognitoIdpResponse, CognitoIdpJsonWebKeyResponse
|
||||
|
||||
url_bases = [
|
||||
"https?://cognito-idp.(.+).amazonaws.com",
|
||||
]
|
||||
|
||||
url_paths = {
|
||||
'{0}/$': CognitoIdpResponse.dispatch,
|
||||
'{0}/<user_pool_id>/.well-known/jwks.json$': CognitoIdpJsonWebKeyResponse().serve_json_web_key,
|
||||
}
|
@ -69,8 +69,13 @@ class DomainDispatcherApplication(object):
|
||||
_, _, region, service, _ = environ['HTTP_AUTHORIZATION'].split(",")[0].split()[
|
||||
1].split("/")
|
||||
except (KeyError, ValueError):
|
||||
# Some cognito-idp endpoints (e.g. change password) do not receive an auth header.
|
||||
if environ.get('HTTP_X_AMZ_TARGET', '').startswith('AWSCognitoIdentityProviderService'):
|
||||
service = 'cognito-idp'
|
||||
else:
|
||||
service = 's3'
|
||||
|
||||
region = 'us-east-1'
|
||||
service = 's3'
|
||||
if service == 'dynamodb':
|
||||
dynamo_api_version = environ['HTTP_X_AMZ_TARGET'].split("_")[1].split(".")[0]
|
||||
# If Newer API version, use dynamodb2
|
||||
|
1
setup.py
1
setup.py
@ -19,6 +19,7 @@ install_requires = [
|
||||
"pyaml",
|
||||
"pytz",
|
||||
"python-dateutil<3.0.0,>=2.1",
|
||||
"python-jose<3.0.0",
|
||||
"mock",
|
||||
"docker>=2.5.1",
|
||||
"jsondiff==1.1.1",
|
||||
|
539
tests/test_cognitoidp/test_cognitoidp.py
Normal file
539
tests/test_cognitoidp/test_cognitoidp.py
Normal file
@ -0,0 +1,539 @@
|
||||
from __future__ import unicode_literals
|
||||
|
||||
import boto3
|
||||
import json
|
||||
import os
|
||||
import uuid
|
||||
|
||||
from jose import jws
|
||||
from moto import mock_cognitoidp
|
||||
import sure # noqa
|
||||
|
||||
|
||||
@mock_cognitoidp
|
||||
def test_create_user_pool():
|
||||
conn = boto3.client("cognito-idp", "us-west-2")
|
||||
|
||||
name = str(uuid.uuid4())
|
||||
value = str(uuid.uuid4())
|
||||
result = conn.create_user_pool(
|
||||
PoolName=name,
|
||||
LambdaConfig={
|
||||
"PreSignUp": value
|
||||
}
|
||||
)
|
||||
|
||||
result["UserPool"]["Id"].should_not.be.none
|
||||
result["UserPool"]["Name"].should.equal(name)
|
||||
result["UserPool"]["LambdaConfig"]["PreSignUp"].should.equal(value)
|
||||
|
||||
|
||||
@mock_cognitoidp
|
||||
def test_list_user_pools():
|
||||
conn = boto3.client("cognito-idp", "us-west-2")
|
||||
|
||||
name = str(uuid.uuid4())
|
||||
conn.create_user_pool(PoolName=name)
|
||||
result = conn.list_user_pools(MaxResults=10)
|
||||
result["UserPools"].should.have.length_of(1)
|
||||
result["UserPools"][0]["Name"].should.equal(name)
|
||||
|
||||
|
||||
@mock_cognitoidp
|
||||
def test_describe_user_pool():
|
||||
conn = boto3.client("cognito-idp", "us-west-2")
|
||||
|
||||
name = str(uuid.uuid4())
|
||||
value = str(uuid.uuid4())
|
||||
user_pool_details = conn.create_user_pool(
|
||||
PoolName=name,
|
||||
LambdaConfig={
|
||||
"PreSignUp": value
|
||||
}
|
||||
)
|
||||
|
||||
result = conn.describe_user_pool(UserPoolId=user_pool_details["UserPool"]["Id"])
|
||||
result["UserPool"]["Name"].should.equal(name)
|
||||
result["UserPool"]["LambdaConfig"]["PreSignUp"].should.equal(value)
|
||||
|
||||
|
||||
@mock_cognitoidp
|
||||
def test_delete_user_pool():
|
||||
conn = boto3.client("cognito-idp", "us-west-2")
|
||||
|
||||
user_pool_id = conn.create_user_pool(PoolName=str(uuid.uuid4()))["UserPool"]["Id"]
|
||||
conn.list_user_pools(MaxResults=10)["UserPools"].should.have.length_of(1)
|
||||
conn.delete_user_pool(UserPoolId=user_pool_id)
|
||||
conn.list_user_pools(MaxResults=10)["UserPools"].should.have.length_of(0)
|
||||
|
||||
|
||||
@mock_cognitoidp
|
||||
def test_create_user_pool_domain():
|
||||
conn = boto3.client("cognito-idp", "us-west-2")
|
||||
|
||||
domain = str(uuid.uuid4())
|
||||
user_pool_id = conn.create_user_pool(PoolName=str(uuid.uuid4()))["UserPool"]["Id"]
|
||||
result = conn.create_user_pool_domain(UserPoolId=user_pool_id, Domain=domain)
|
||||
result["ResponseMetadata"]["HTTPStatusCode"].should.equal(200)
|
||||
|
||||
|
||||
@mock_cognitoidp
|
||||
def test_describe_user_pool_domain():
|
||||
conn = boto3.client("cognito-idp", "us-west-2")
|
||||
|
||||
domain = str(uuid.uuid4())
|
||||
user_pool_id = conn.create_user_pool(PoolName=str(uuid.uuid4()))["UserPool"]["Id"]
|
||||
conn.create_user_pool_domain(UserPoolId=user_pool_id, Domain=domain)
|
||||
result = conn.describe_user_pool_domain(Domain=domain)
|
||||
result["DomainDescription"]["Domain"].should.equal(domain)
|
||||
result["DomainDescription"]["UserPoolId"].should.equal(user_pool_id)
|
||||
result["DomainDescription"]["AWSAccountId"].should_not.be.none
|
||||
|
||||
|
||||
@mock_cognitoidp
|
||||
def test_delete_user_pool_domain():
|
||||
conn = boto3.client("cognito-idp", "us-west-2")
|
||||
|
||||
domain = str(uuid.uuid4())
|
||||
user_pool_id = conn.create_user_pool(PoolName=str(uuid.uuid4()))["UserPool"]["Id"]
|
||||
conn.create_user_pool_domain(UserPoolId=user_pool_id, Domain=domain)
|
||||
result = conn.delete_user_pool_domain(UserPoolId=user_pool_id, Domain=domain)
|
||||
result["ResponseMetadata"]["HTTPStatusCode"].should.equal(200)
|
||||
result = conn.describe_user_pool_domain(Domain=domain)
|
||||
# This is a surprising behavior of the real service: describing a missing domain comes
|
||||
# back with status 200 and a DomainDescription of {}
|
||||
result["ResponseMetadata"]["HTTPStatusCode"].should.equal(200)
|
||||
result["DomainDescription"].keys().should.have.length_of(0)
|
||||
|
||||
|
||||
@mock_cognitoidp
|
||||
def test_create_user_pool_client():
|
||||
conn = boto3.client("cognito-idp", "us-west-2")
|
||||
|
||||
client_name = str(uuid.uuid4())
|
||||
value = str(uuid.uuid4())
|
||||
user_pool_id = conn.create_user_pool(PoolName=str(uuid.uuid4()))["UserPool"]["Id"]
|
||||
result = conn.create_user_pool_client(
|
||||
UserPoolId=user_pool_id,
|
||||
ClientName=client_name,
|
||||
CallbackURLs=[value],
|
||||
)
|
||||
|
||||
result["UserPoolClient"]["UserPoolId"].should.equal(user_pool_id)
|
||||
result["UserPoolClient"]["ClientId"].should_not.be.none
|
||||
result["UserPoolClient"]["ClientName"].should.equal(client_name)
|
||||
result["UserPoolClient"]["CallbackURLs"].should.have.length_of(1)
|
||||
result["UserPoolClient"]["CallbackURLs"][0].should.equal(value)
|
||||
|
||||
|
||||
@mock_cognitoidp
|
||||
def test_list_user_pool_clients():
|
||||
conn = boto3.client("cognito-idp", "us-west-2")
|
||||
|
||||
client_name = str(uuid.uuid4())
|
||||
user_pool_id = conn.create_user_pool(PoolName=str(uuid.uuid4()))["UserPool"]["Id"]
|
||||
conn.create_user_pool_client(UserPoolId=user_pool_id, ClientName=client_name)
|
||||
result = conn.list_user_pool_clients(UserPoolId=user_pool_id, MaxResults=10)
|
||||
result["UserPoolClients"].should.have.length_of(1)
|
||||
result["UserPoolClients"][0]["ClientName"].should.equal(client_name)
|
||||
|
||||
|
||||
@mock_cognitoidp
|
||||
def test_describe_user_pool_client():
|
||||
conn = boto3.client("cognito-idp", "us-west-2")
|
||||
|
||||
client_name = str(uuid.uuid4())
|
||||
value = str(uuid.uuid4())
|
||||
user_pool_id = conn.create_user_pool(PoolName=str(uuid.uuid4()))["UserPool"]["Id"]
|
||||
client_details = conn.create_user_pool_client(
|
||||
UserPoolId=user_pool_id,
|
||||
ClientName=client_name,
|
||||
CallbackURLs=[value],
|
||||
)
|
||||
|
||||
result = conn.describe_user_pool_client(
|
||||
UserPoolId=user_pool_id,
|
||||
ClientId=client_details["UserPoolClient"]["ClientId"],
|
||||
)
|
||||
|
||||
result["UserPoolClient"]["ClientName"].should.equal(client_name)
|
||||
result["UserPoolClient"]["CallbackURLs"].should.have.length_of(1)
|
||||
result["UserPoolClient"]["CallbackURLs"][0].should.equal(value)
|
||||
|
||||
|
||||
@mock_cognitoidp
|
||||
def test_update_user_pool_client():
|
||||
conn = boto3.client("cognito-idp", "us-west-2")
|
||||
|
||||
old_client_name = str(uuid.uuid4())
|
||||
new_client_name = str(uuid.uuid4())
|
||||
old_value = str(uuid.uuid4())
|
||||
new_value = str(uuid.uuid4())
|
||||
user_pool_id = conn.create_user_pool(PoolName=str(uuid.uuid4()))["UserPool"]["Id"]
|
||||
client_details = conn.create_user_pool_client(
|
||||
UserPoolId=user_pool_id,
|
||||
ClientName=old_client_name,
|
||||
CallbackURLs=[old_value],
|
||||
)
|
||||
|
||||
result = conn.update_user_pool_client(
|
||||
UserPoolId=user_pool_id,
|
||||
ClientId=client_details["UserPoolClient"]["ClientId"],
|
||||
ClientName=new_client_name,
|
||||
CallbackURLs=[new_value],
|
||||
)
|
||||
|
||||
result["UserPoolClient"]["ClientName"].should.equal(new_client_name)
|
||||
result["UserPoolClient"]["CallbackURLs"].should.have.length_of(1)
|
||||
result["UserPoolClient"]["CallbackURLs"][0].should.equal(new_value)
|
||||
|
||||
|
||||
@mock_cognitoidp
|
||||
def test_delete_user_pool_client():
|
||||
conn = boto3.client("cognito-idp", "us-west-2")
|
||||
|
||||
user_pool_id = conn.create_user_pool(PoolName=str(uuid.uuid4()))["UserPool"]["Id"]
|
||||
client_details = conn.create_user_pool_client(
|
||||
UserPoolId=user_pool_id,
|
||||
ClientName=str(uuid.uuid4()),
|
||||
)
|
||||
|
||||
conn.delete_user_pool_client(
|
||||
UserPoolId=user_pool_id,
|
||||
ClientId=client_details["UserPoolClient"]["ClientId"],
|
||||
)
|
||||
|
||||
caught = False
|
||||
try:
|
||||
conn.describe_user_pool_client(
|
||||
UserPoolId=user_pool_id,
|
||||
ClientId=client_details["UserPoolClient"]["ClientId"],
|
||||
)
|
||||
except conn.exceptions.ResourceNotFoundException:
|
||||
caught = True
|
||||
|
||||
caught.should.be.true
|
||||
|
||||
|
||||
@mock_cognitoidp
|
||||
def test_create_identity_provider():
|
||||
conn = boto3.client("cognito-idp", "us-west-2")
|
||||
|
||||
provider_name = str(uuid.uuid4())
|
||||
provider_type = "Facebook"
|
||||
value = str(uuid.uuid4())
|
||||
user_pool_id = conn.create_user_pool(PoolName=str(uuid.uuid4()))["UserPool"]["Id"]
|
||||
result = conn.create_identity_provider(
|
||||
UserPoolId=user_pool_id,
|
||||
ProviderName=provider_name,
|
||||
ProviderType=provider_type,
|
||||
ProviderDetails={
|
||||
"thing": value
|
||||
},
|
||||
)
|
||||
|
||||
result["IdentityProvider"]["UserPoolId"].should.equal(user_pool_id)
|
||||
result["IdentityProvider"]["ProviderName"].should.equal(provider_name)
|
||||
result["IdentityProvider"]["ProviderType"].should.equal(provider_type)
|
||||
result["IdentityProvider"]["ProviderDetails"]["thing"].should.equal(value)
|
||||
|
||||
|
||||
@mock_cognitoidp
|
||||
def test_list_identity_providers():
|
||||
conn = boto3.client("cognito-idp", "us-west-2")
|
||||
|
||||
provider_name = str(uuid.uuid4())
|
||||
provider_type = "Facebook"
|
||||
user_pool_id = conn.create_user_pool(PoolName=str(uuid.uuid4()))["UserPool"]["Id"]
|
||||
conn.create_identity_provider(
|
||||
UserPoolId=user_pool_id,
|
||||
ProviderName=provider_name,
|
||||
ProviderType=provider_type,
|
||||
ProviderDetails={},
|
||||
)
|
||||
|
||||
result = conn.list_identity_providers(
|
||||
UserPoolId=user_pool_id,
|
||||
MaxResults=10,
|
||||
)
|
||||
|
||||
result["Providers"].should.have.length_of(1)
|
||||
result["Providers"][0]["ProviderName"].should.equal(provider_name)
|
||||
result["Providers"][0]["ProviderType"].should.equal(provider_type)
|
||||
|
||||
|
||||
@mock_cognitoidp
|
||||
def test_describe_identity_providers():
|
||||
conn = boto3.client("cognito-idp", "us-west-2")
|
||||
|
||||
provider_name = str(uuid.uuid4())
|
||||
provider_type = "Facebook"
|
||||
value = str(uuid.uuid4())
|
||||
user_pool_id = conn.create_user_pool(PoolName=str(uuid.uuid4()))["UserPool"]["Id"]
|
||||
conn.create_identity_provider(
|
||||
UserPoolId=user_pool_id,
|
||||
ProviderName=provider_name,
|
||||
ProviderType=provider_type,
|
||||
ProviderDetails={
|
||||
"thing": value
|
||||
},
|
||||
)
|
||||
|
||||
result = conn.describe_identity_provider(
|
||||
UserPoolId=user_pool_id,
|
||||
ProviderName=provider_name,
|
||||
)
|
||||
|
||||
result["IdentityProvider"]["UserPoolId"].should.equal(user_pool_id)
|
||||
result["IdentityProvider"]["ProviderName"].should.equal(provider_name)
|
||||
result["IdentityProvider"]["ProviderType"].should.equal(provider_type)
|
||||
result["IdentityProvider"]["ProviderDetails"]["thing"].should.equal(value)
|
||||
|
||||
|
||||
@mock_cognitoidp
|
||||
def test_delete_identity_providers():
|
||||
conn = boto3.client("cognito-idp", "us-west-2")
|
||||
|
||||
provider_name = str(uuid.uuid4())
|
||||
provider_type = "Facebook"
|
||||
value = str(uuid.uuid4())
|
||||
user_pool_id = conn.create_user_pool(PoolName=str(uuid.uuid4()))["UserPool"]["Id"]
|
||||
conn.create_identity_provider(
|
||||
UserPoolId=user_pool_id,
|
||||
ProviderName=provider_name,
|
||||
ProviderType=provider_type,
|
||||
ProviderDetails={
|
||||
"thing": value
|
||||
},
|
||||
)
|
||||
|
||||
conn.delete_identity_provider(UserPoolId=user_pool_id, ProviderName=provider_name)
|
||||
|
||||
caught = False
|
||||
try:
|
||||
conn.describe_identity_provider(
|
||||
UserPoolId=user_pool_id,
|
||||
ProviderName=provider_name,
|
||||
)
|
||||
except conn.exceptions.ResourceNotFoundException:
|
||||
caught = True
|
||||
|
||||
caught.should.be.true
|
||||
|
||||
|
||||
@mock_cognitoidp
|
||||
def test_admin_create_user():
|
||||
conn = boto3.client("cognito-idp", "us-west-2")
|
||||
|
||||
username = str(uuid.uuid4())
|
||||
value = str(uuid.uuid4())
|
||||
user_pool_id = conn.create_user_pool(PoolName=str(uuid.uuid4()))["UserPool"]["Id"]
|
||||
result = conn.admin_create_user(
|
||||
UserPoolId=user_pool_id,
|
||||
Username=username,
|
||||
UserAttributes=[
|
||||
{"Name": "thing", "Value": value}
|
||||
],
|
||||
)
|
||||
|
||||
result["User"]["Username"].should.equal(username)
|
||||
result["User"]["UserStatus"].should.equal("FORCE_CHANGE_PASSWORD")
|
||||
result["User"]["Attributes"].should.have.length_of(1)
|
||||
result["User"]["Attributes"][0]["Name"].should.equal("thing")
|
||||
result["User"]["Attributes"][0]["Value"].should.equal(value)
|
||||
|
||||
|
||||
@mock_cognitoidp
|
||||
def test_admin_get_user():
|
||||
conn = boto3.client("cognito-idp", "us-west-2")
|
||||
|
||||
username = str(uuid.uuid4())
|
||||
value = 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,
|
||||
UserAttributes=[
|
||||
{"Name": "thing", "Value": value}
|
||||
],
|
||||
)
|
||||
|
||||
result = conn.admin_get_user(UserPoolId=user_pool_id, Username=username)
|
||||
result["Username"].should.equal(username)
|
||||
result["UserAttributes"].should.have.length_of(1)
|
||||
result["UserAttributes"][0]["Name"].should.equal("thing")
|
||||
result["UserAttributes"][0]["Value"].should.equal(value)
|
||||
|
||||
|
||||
@mock_cognitoidp
|
||||
def test_list_users():
|
||||
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.list_users(UserPoolId=user_pool_id)
|
||||
result["Users"].should.have.length_of(1)
|
||||
result["Users"][0]["Username"].should.equal(username)
|
||||
|
||||
|
||||
@mock_cognitoidp
|
||||
def test_admin_delete_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_delete_user(UserPoolId=user_pool_id, Username=username)
|
||||
|
||||
caught = False
|
||||
try:
|
||||
conn.admin_get_user(UserPoolId=user_pool_id, Username=username)
|
||||
except conn.exceptions.ResourceNotFoundException:
|
||||
caught = True
|
||||
|
||||
caught.should.be.true
|
||||
|
||||
|
||||
def authentication_flow(conn):
|
||||
username = str(uuid.uuid4())
|
||||
temporary_password = str(uuid.uuid4())
|
||||
user_pool_id = conn.create_user_pool(PoolName=str(uuid.uuid4()))["UserPool"]["Id"]
|
||||
client_id = conn.create_user_pool_client(
|
||||
UserPoolId=user_pool_id,
|
||||
ClientName=str(uuid.uuid4()),
|
||||
)["UserPoolClient"]["ClientId"]
|
||||
|
||||
conn.admin_create_user(
|
||||
UserPoolId=user_pool_id,
|
||||
Username=username,
|
||||
TemporaryPassword=temporary_password,
|
||||
)
|
||||
|
||||
result = conn.admin_initiate_auth(
|
||||
UserPoolId=user_pool_id,
|
||||
ClientId=client_id,
|
||||
AuthFlow="ADMIN_NO_SRP_AUTH",
|
||||
AuthParameters={
|
||||
"USERNAME": username,
|
||||
"PASSWORD": temporary_password
|
||||
},
|
||||
)
|
||||
|
||||
# A newly created user is forced to set a new password
|
||||
result["ChallengeName"].should.equal("NEW_PASSWORD_REQUIRED")
|
||||
result["Session"].should_not.be.none
|
||||
|
||||
# This sets a new password and logs the user in (creates tokens)
|
||||
new_password = str(uuid.uuid4())
|
||||
result = conn.respond_to_auth_challenge(
|
||||
Session=result["Session"],
|
||||
ClientId=client_id,
|
||||
ChallengeName="NEW_PASSWORD_REQUIRED",
|
||||
ChallengeResponses={
|
||||
"USERNAME": username,
|
||||
"NEW_PASSWORD": new_password
|
||||
}
|
||||
)
|
||||
|
||||
result["AuthenticationResult"]["IdToken"].should_not.be.none
|
||||
result["AuthenticationResult"]["AccessToken"].should_not.be.none
|
||||
|
||||
return {
|
||||
"user_pool_id": user_pool_id,
|
||||
"client_id": client_id,
|
||||
"id_token": result["AuthenticationResult"]["IdToken"],
|
||||
"access_token": result["AuthenticationResult"]["AccessToken"],
|
||||
"username": username,
|
||||
"password": new_password,
|
||||
}
|
||||
|
||||
|
||||
@mock_cognitoidp
|
||||
def test_authentication_flow():
|
||||
conn = boto3.client("cognito-idp", "us-west-2")
|
||||
|
||||
authentication_flow(conn)
|
||||
|
||||
|
||||
@mock_cognitoidp
|
||||
def test_token_legitimacy():
|
||||
conn = boto3.client("cognito-idp", "us-west-2")
|
||||
|
||||
path = "../../moto/cognitoidp/resources/jwks-public.json"
|
||||
with open(os.path.join(os.path.dirname(__file__), path)) as f:
|
||||
json_web_key = json.loads(f.read())["keys"][0]
|
||||
|
||||
outputs = authentication_flow(conn)
|
||||
id_token = outputs["id_token"]
|
||||
access_token = outputs["access_token"]
|
||||
client_id = outputs["client_id"]
|
||||
issuer = "https://cognito-idp.us-west-2.amazonaws.com/{}".format(outputs["user_pool_id"])
|
||||
id_claims = json.loads(jws.verify(id_token, json_web_key, "RS256"))
|
||||
id_claims["iss"].should.equal(issuer)
|
||||
id_claims["aud"].should.equal(client_id)
|
||||
access_claims = json.loads(jws.verify(access_token, json_web_key, "RS256"))
|
||||
access_claims["iss"].should.equal(issuer)
|
||||
access_claims["aud"].should.equal(client_id)
|
||||
|
||||
|
||||
@mock_cognitoidp
|
||||
def test_change_password():
|
||||
conn = boto3.client("cognito-idp", "us-west-2")
|
||||
|
||||
outputs = authentication_flow(conn)
|
||||
|
||||
# Take this opportunity to test change_password, which requires an access token.
|
||||
newer_password = str(uuid.uuid4())
|
||||
conn.change_password(
|
||||
AccessToken=outputs["access_token"],
|
||||
PreviousPassword=outputs["password"],
|
||||
ProposedPassword=newer_password,
|
||||
)
|
||||
|
||||
# Log in again, which should succeed without a challenge because the user is no
|
||||
# longer in the force-new-password state.
|
||||
result = conn.admin_initiate_auth(
|
||||
UserPoolId=outputs["user_pool_id"],
|
||||
ClientId=outputs["client_id"],
|
||||
AuthFlow="ADMIN_NO_SRP_AUTH",
|
||||
AuthParameters={
|
||||
"USERNAME": outputs["username"],
|
||||
"PASSWORD": newer_password,
|
||||
},
|
||||
)
|
||||
|
||||
result["AuthenticationResult"].should_not.be.none
|
||||
|
||||
|
||||
@mock_cognitoidp
|
||||
def test_forgot_password():
|
||||
conn = boto3.client("cognito-idp", "us-west-2")
|
||||
|
||||
result = conn.forgot_password(ClientId=str(uuid.uuid4()), Username=str(uuid.uuid4()))
|
||||
result["CodeDeliveryDetails"].should_not.be.none
|
||||
|
||||
|
||||
@mock_cognitoidp
|
||||
def test_confirm_forgot_password():
|
||||
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"]
|
||||
client_id = conn.create_user_pool_client(
|
||||
UserPoolId=user_pool_id,
|
||||
ClientName=str(uuid.uuid4()),
|
||||
)["UserPoolClient"]["ClientId"]
|
||||
|
||||
conn.admin_create_user(
|
||||
UserPoolId=user_pool_id,
|
||||
Username=username,
|
||||
TemporaryPassword=str(uuid.uuid4()),
|
||||
)
|
||||
|
||||
conn.confirm_forgot_password(
|
||||
ClientId=client_id,
|
||||
Username=username,
|
||||
ConfirmationCode=str(uuid.uuid4()),
|
||||
Password=str(uuid.uuid4()),
|
||||
)
|
Loading…
Reference in New Issue
Block a user