Userpool UsernameAttributes (#4262)
This commit is contained in:
parent
766f9ffc0d
commit
46131e0340
@ -18,7 +18,14 @@ from .exceptions import (
|
|||||||
UserNotConfirmedException,
|
UserNotConfirmedException,
|
||||||
InvalidParameterException,
|
InvalidParameterException,
|
||||||
)
|
)
|
||||||
from .utils import create_id, check_secret_hash, PAGINATION_MODEL
|
from .utils import (
|
||||||
|
create_id,
|
||||||
|
check_secret_hash,
|
||||||
|
validate_username_format,
|
||||||
|
flatten_attrs,
|
||||||
|
expand_attrs,
|
||||||
|
PAGINATION_MODEL,
|
||||||
|
)
|
||||||
from moto.utilities.paginator import paginate
|
from moto.utilities.paginator import paginate
|
||||||
|
|
||||||
UserStatus = {
|
UserStatus = {
|
||||||
@ -82,6 +89,20 @@ class CognitoIdpUserPool(BaseModel):
|
|||||||
|
|
||||||
return user_pool_json
|
return user_pool_json
|
||||||
|
|
||||||
|
def _get_user(self, username):
|
||||||
|
"""Find a user within a user pool by Username or any UsernameAttributes
|
||||||
|
(`email` or `phone_number` or both)"""
|
||||||
|
if self.extended_config.get("UsernameAttributes"):
|
||||||
|
attribute_types = self.extended_config["UsernameAttributes"]
|
||||||
|
for user in self.users.values():
|
||||||
|
if username in [
|
||||||
|
flatten_attrs(user.attributes).get(attribute_type)
|
||||||
|
for attribute_type in attribute_types
|
||||||
|
]:
|
||||||
|
return user
|
||||||
|
|
||||||
|
return self.users.get(username)
|
||||||
|
|
||||||
def create_jwt(
|
def create_jwt(
|
||||||
self, client_id, username, token_use, expires_in=60 * 60, extra_data={}
|
self, client_id, username, token_use, expires_in=60 * 60, extra_data={}
|
||||||
):
|
):
|
||||||
@ -90,12 +111,12 @@ class CognitoIdpUserPool(BaseModel):
|
|||||||
"iss": "https://cognito-idp.{}.amazonaws.com/{}".format(
|
"iss": "https://cognito-idp.{}.amazonaws.com/{}".format(
|
||||||
self.region, self.id
|
self.region, self.id
|
||||||
),
|
),
|
||||||
"sub": self.users[username].id,
|
"sub": self._get_user(username).id,
|
||||||
"aud": client_id,
|
"aud": client_id,
|
||||||
"token_use": token_use,
|
"token_use": token_use,
|
||||||
"auth_time": now,
|
"auth_time": now,
|
||||||
"exp": now + expires_in,
|
"exp": now + expires_in,
|
||||||
"email": self.users[username].username,
|
"email": flatten_attrs(self._get_user(username).attributes).get("email"),
|
||||||
}
|
}
|
||||||
payload.update(extra_data)
|
payload.update(extra_data)
|
||||||
headers = {"kid": "dummy"} # KID as present in jwks-public.json
|
headers = {"kid": "dummy"} # KID as present in jwks-public.json
|
||||||
@ -140,7 +161,7 @@ class CognitoIdpUserPool(BaseModel):
|
|||||||
attribute = list(
|
attribute = list(
|
||||||
filter(
|
filter(
|
||||||
lambda f: f["Name"] == readable_field,
|
lambda f: f["Name"] == readable_field,
|
||||||
self.users.get(username).attributes,
|
self._get_user(username).attributes,
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
if len(attribute) > 0:
|
if len(attribute) > 0:
|
||||||
@ -261,12 +282,14 @@ class CognitoIdpUser(BaseModel):
|
|||||||
def __init__(self, user_pool_id, username, password, status, attributes):
|
def __init__(self, user_pool_id, username, password, status, attributes):
|
||||||
self.id = str(uuid.uuid4())
|
self.id = str(uuid.uuid4())
|
||||||
self.user_pool_id = user_pool_id
|
self.user_pool_id = user_pool_id
|
||||||
self.username = username
|
# Username is None when users sign up with an email or phone_number,
|
||||||
|
# and should be given the value of the internal id generate (sub)
|
||||||
|
self.username = username if username else self.id
|
||||||
self.password = password
|
self.password = password
|
||||||
self.status = status
|
self.status = status
|
||||||
self.enabled = True
|
self.enabled = True
|
||||||
self.attributes = attributes
|
self.attributes = attributes
|
||||||
self.attribute_lookup = self._flatten_attributes(attributes)
|
self.attribute_lookup = flatten_attrs(attributes)
|
||||||
self.create_date = datetime.datetime.utcnow()
|
self.create_date = datetime.datetime.utcnow()
|
||||||
self.last_modified_date = datetime.datetime.utcnow()
|
self.last_modified_date = datetime.datetime.utcnow()
|
||||||
self.sms_mfa_enabled = False
|
self.sms_mfa_enabled = False
|
||||||
@ -277,6 +300,8 @@ class CognitoIdpUser(BaseModel):
|
|||||||
# Note that these links are bidirectional.
|
# Note that these links are bidirectional.
|
||||||
self.groups = set()
|
self.groups = set()
|
||||||
|
|
||||||
|
self.update_attributes([{"Name": "sub", "Value": self.id}])
|
||||||
|
|
||||||
def _base_json(self):
|
def _base_json(self):
|
||||||
return {
|
return {
|
||||||
"UserPoolId": self.user_pool_id,
|
"UserPoolId": self.user_pool_id,
|
||||||
@ -306,15 +331,9 @@ class CognitoIdpUser(BaseModel):
|
|||||||
|
|
||||||
return user_json
|
return user_json
|
||||||
|
|
||||||
def _flatten_attributes(self, attributes):
|
|
||||||
return {attr["Name"]: attr["Value"] for attr in attributes}
|
|
||||||
|
|
||||||
def update_attributes(self, new_attributes):
|
def update_attributes(self, new_attributes):
|
||||||
def expand_attrs(attrs):
|
flat_attributes = flatten_attrs(self.attributes)
|
||||||
return [{"Name": k, "Value": v} for k, v in attrs.items()]
|
flat_attributes.update(flatten_attrs(new_attributes))
|
||||||
|
|
||||||
flat_attributes = self._flatten_attributes(self.attributes)
|
|
||||||
flat_attributes.update(self._flatten_attributes(new_attributes))
|
|
||||||
self.attribute_lookup = flat_attributes
|
self.attribute_lookup = flat_attributes
|
||||||
self.attributes = expand_attrs(flat_attributes)
|
self.attributes = expand_attrs(flat_attributes)
|
||||||
|
|
||||||
@ -630,11 +649,52 @@ class CognitoIdpBackend(BaseBackend):
|
|||||||
raise ResourceNotFoundError(user_pool_id)
|
raise ResourceNotFoundError(user_pool_id)
|
||||||
|
|
||||||
if message_action and message_action == "RESEND":
|
if message_action and message_action == "RESEND":
|
||||||
if username not in user_pool.users:
|
if not user_pool._get_user(username):
|
||||||
raise UserNotFoundError(username)
|
raise UserNotFoundError(username)
|
||||||
elif username in user_pool.users:
|
elif user_pool._get_user(username):
|
||||||
raise UsernameExistsException(username)
|
raise UsernameExistsException(username)
|
||||||
|
|
||||||
|
# UsernameAttributes are attributes (either `email` or `phone_number`
|
||||||
|
# or both) than can be used in the place of a unique username. If the
|
||||||
|
# user provides an email or phone number when signing up, the user pool
|
||||||
|
# performs the following steps:
|
||||||
|
# 1. populates the correct field (email, phone_number) with the value
|
||||||
|
# supplied for Username
|
||||||
|
# 2. generates a persistent GUID for the user that will be returned as
|
||||||
|
# the value of `Username` in the `get-user` and `list-users`
|
||||||
|
# operations, as well as the value of `sub` in `IdToken` and
|
||||||
|
# `AccessToken`
|
||||||
|
#
|
||||||
|
# ref: https://docs.aws.amazon.com/cognito/latest/developerguide/user-pool-settings-attributes.html#user-pool-settings-aliases-settings
|
||||||
|
if user_pool.extended_config.get("UsernameAttributes"):
|
||||||
|
username_attributes = user_pool.extended_config["UsernameAttributes"]
|
||||||
|
# attribute_type should be one of `email`, `phone_number` or both
|
||||||
|
for attribute_type in username_attributes:
|
||||||
|
# check if provided username matches one of the attribute types in
|
||||||
|
# `UsernameAttributes`
|
||||||
|
if attribute_type in username_attributes and validate_username_format(
|
||||||
|
username, _format=attribute_type
|
||||||
|
):
|
||||||
|
# insert provided username into new user's attributes under the
|
||||||
|
# correct key
|
||||||
|
flattened_attrs = flatten_attrs(attributes or {})
|
||||||
|
flattened_attrs.update({attribute_type: username})
|
||||||
|
attributes = expand_attrs(flattened_attrs)
|
||||||
|
# set username to None so that it will be default to the internal GUID
|
||||||
|
# when them user gets created
|
||||||
|
username = None
|
||||||
|
# once the username has been validated against a username attribute
|
||||||
|
# type, there is no need to attempt validation against the other
|
||||||
|
# type(s)
|
||||||
|
break
|
||||||
|
|
||||||
|
# The provided username has not matched the required format for any
|
||||||
|
# of the possible attributes
|
||||||
|
if username is not None:
|
||||||
|
raise InvalidParameterException(
|
||||||
|
"Username should be either an email or a phone number."
|
||||||
|
)
|
||||||
|
|
||||||
user = CognitoIdpUser(
|
user = CognitoIdpUser(
|
||||||
user_pool_id,
|
user_pool_id,
|
||||||
username,
|
username,
|
||||||
@ -651,16 +711,16 @@ class CognitoIdpBackend(BaseBackend):
|
|||||||
if not user_pool:
|
if not user_pool:
|
||||||
raise ResourceNotFoundError(user_pool_id)
|
raise ResourceNotFoundError(user_pool_id)
|
||||||
|
|
||||||
if username not in user_pool.users:
|
user = user_pool._get_user(username)
|
||||||
|
if not user:
|
||||||
raise UserNotFoundError(username)
|
raise UserNotFoundError(username)
|
||||||
|
return user
|
||||||
return user_pool.users[username]
|
|
||||||
|
|
||||||
def get_user(self, access_token):
|
def get_user(self, access_token):
|
||||||
for user_pool in self.user_pools.values():
|
for user_pool in self.user_pools.values():
|
||||||
if access_token in user_pool.access_tokens:
|
if access_token in user_pool.access_tokens:
|
||||||
_, username = user_pool.access_tokens[access_token]
|
_, username = user_pool.access_tokens[access_token]
|
||||||
user = user_pool.users.get(username)
|
user = user_pool._get_user(username)
|
||||||
if (
|
if (
|
||||||
not user
|
not user
|
||||||
or not user.enabled
|
or not user.enabled
|
||||||
@ -691,14 +751,15 @@ class CognitoIdpBackend(BaseBackend):
|
|||||||
if not user_pool:
|
if not user_pool:
|
||||||
raise ResourceNotFoundError(user_pool_id)
|
raise ResourceNotFoundError(user_pool_id)
|
||||||
|
|
||||||
if username not in user_pool.users:
|
user = user_pool._get_user(username)
|
||||||
|
if not user:
|
||||||
raise UserNotFoundError(username)
|
raise UserNotFoundError(username)
|
||||||
|
|
||||||
user = user_pool.users[username]
|
|
||||||
for group in user.groups:
|
for group in user.groups:
|
||||||
group.users.remove(user)
|
group.users.remove(user)
|
||||||
|
|
||||||
del user_pool.users[username]
|
# use internal username
|
||||||
|
del user_pool.users[user.username]
|
||||||
|
|
||||||
def _log_user_in(self, user_pool, client, username):
|
def _log_user_in(self, user_pool, client, username):
|
||||||
refresh_token = user_pool.create_refresh_token(client.id, username)
|
refresh_token = user_pool.create_refresh_token(client.id, username)
|
||||||
@ -727,7 +788,7 @@ class CognitoIdpBackend(BaseBackend):
|
|||||||
if auth_flow in ("ADMIN_USER_PASSWORD_AUTH", "ADMIN_NO_SRP_AUTH"):
|
if auth_flow in ("ADMIN_USER_PASSWORD_AUTH", "ADMIN_NO_SRP_AUTH"):
|
||||||
username = auth_parameters.get("USERNAME")
|
username = auth_parameters.get("USERNAME")
|
||||||
password = auth_parameters.get("PASSWORD")
|
password = auth_parameters.get("PASSWORD")
|
||||||
user = user_pool.users.get(username)
|
user = user_pool._get_user(username)
|
||||||
if not user:
|
if not user:
|
||||||
raise UserNotFoundError(username)
|
raise UserNotFoundError(username)
|
||||||
|
|
||||||
@ -783,7 +844,7 @@ class CognitoIdpBackend(BaseBackend):
|
|||||||
if challenge_name == "NEW_PASSWORD_REQUIRED":
|
if challenge_name == "NEW_PASSWORD_REQUIRED":
|
||||||
username = challenge_responses.get("USERNAME")
|
username = challenge_responses.get("USERNAME")
|
||||||
new_password = challenge_responses.get("NEW_PASSWORD")
|
new_password = challenge_responses.get("NEW_PASSWORD")
|
||||||
user = user_pool.users.get(username)
|
user = user_pool._get_user(username)
|
||||||
if not user:
|
if not user:
|
||||||
raise UserNotFoundError(username)
|
raise UserNotFoundError(username)
|
||||||
|
|
||||||
@ -794,7 +855,7 @@ class CognitoIdpBackend(BaseBackend):
|
|||||||
return self._log_user_in(user_pool, client, username)
|
return self._log_user_in(user_pool, client, username)
|
||||||
elif challenge_name == "PASSWORD_VERIFIER":
|
elif challenge_name == "PASSWORD_VERIFIER":
|
||||||
username = challenge_responses.get("USERNAME")
|
username = challenge_responses.get("USERNAME")
|
||||||
user = user_pool.users.get(username)
|
user = user_pool._get_user(username)
|
||||||
if not user:
|
if not user:
|
||||||
raise UserNotFoundError(username)
|
raise UserNotFoundError(username)
|
||||||
|
|
||||||
@ -830,7 +891,7 @@ class CognitoIdpBackend(BaseBackend):
|
|||||||
return self._log_user_in(user_pool, client, username)
|
return self._log_user_in(user_pool, client, username)
|
||||||
elif challenge_name == "SOFTWARE_TOKEN_MFA":
|
elif challenge_name == "SOFTWARE_TOKEN_MFA":
|
||||||
username = challenge_responses.get("USERNAME")
|
username = challenge_responses.get("USERNAME")
|
||||||
user = user_pool.users.get(username)
|
user = user_pool._get_user(username)
|
||||||
if not user:
|
if not user:
|
||||||
raise UserNotFoundError(username)
|
raise UserNotFoundError(username)
|
||||||
|
|
||||||
@ -853,8 +914,8 @@ class CognitoIdpBackend(BaseBackend):
|
|||||||
|
|
||||||
def confirm_forgot_password(self, client_id, username, password):
|
def confirm_forgot_password(self, client_id, username, password):
|
||||||
for user_pool in self.user_pools.values():
|
for user_pool in self.user_pools.values():
|
||||||
if client_id in user_pool.clients and username in user_pool.users:
|
if client_id in user_pool.clients and user_pool._get_user(username):
|
||||||
user_pool.users[username].password = password
|
user_pool._get_user(username).password = password
|
||||||
break
|
break
|
||||||
else:
|
else:
|
||||||
raise ResourceNotFoundError(client_id)
|
raise ResourceNotFoundError(client_id)
|
||||||
@ -863,7 +924,7 @@ class CognitoIdpBackend(BaseBackend):
|
|||||||
for user_pool in self.user_pools.values():
|
for user_pool in self.user_pools.values():
|
||||||
if access_token in user_pool.access_tokens:
|
if access_token in user_pool.access_tokens:
|
||||||
_, username = user_pool.access_tokens[access_token]
|
_, username = user_pool.access_tokens[access_token]
|
||||||
user = user_pool.users.get(username)
|
user = user_pool._get_user(username)
|
||||||
if not user:
|
if not user:
|
||||||
raise UserNotFoundError(username)
|
raise UserNotFoundError(username)
|
||||||
|
|
||||||
@ -886,10 +947,10 @@ class CognitoIdpBackend(BaseBackend):
|
|||||||
if not user_pool:
|
if not user_pool:
|
||||||
raise ResourceNotFoundError(user_pool_id)
|
raise ResourceNotFoundError(user_pool_id)
|
||||||
|
|
||||||
if username not in user_pool.users:
|
user = user_pool._get_user(username)
|
||||||
|
if not user:
|
||||||
raise UserNotFoundError(username)
|
raise UserNotFoundError(username)
|
||||||
|
|
||||||
user = user_pool.users[username]
|
|
||||||
user.update_attributes(attributes)
|
user.update_attributes(attributes)
|
||||||
|
|
||||||
def admin_user_global_sign_out(self, user_pool_id, username):
|
def admin_user_global_sign_out(self, user_pool_id, username):
|
||||||
@ -897,7 +958,8 @@ class CognitoIdpBackend(BaseBackend):
|
|||||||
if not user_pool:
|
if not user_pool:
|
||||||
raise ResourceNotFoundError(user_pool_id)
|
raise ResourceNotFoundError(user_pool_id)
|
||||||
|
|
||||||
if username not in user_pool.users:
|
user = user_pool._get_user(username)
|
||||||
|
if not user:
|
||||||
raise UserNotFoundError(username)
|
raise UserNotFoundError(username)
|
||||||
|
|
||||||
for token, token_tuple in list(user_pool.refresh_tokens.items()):
|
for token, token_tuple in list(user_pool.refresh_tokens.items()):
|
||||||
@ -926,9 +988,50 @@ class CognitoIdpBackend(BaseBackend):
|
|||||||
user_pool = p
|
user_pool = p
|
||||||
if user_pool is None:
|
if user_pool is None:
|
||||||
raise ResourceNotFoundError(client_id)
|
raise ResourceNotFoundError(client_id)
|
||||||
elif username in user_pool.users:
|
elif user_pool._get_user(username):
|
||||||
raise UsernameExistsException(username)
|
raise UsernameExistsException(username)
|
||||||
|
|
||||||
|
# UsernameAttributes are attributes (either `email` or `phone_number`
|
||||||
|
# or both) than can be used in the place of a unique username. If the
|
||||||
|
# user provides an email or phone number when signing up, the user pool
|
||||||
|
# performs the following steps:
|
||||||
|
# 1. populates the correct field (email, phone_number) with the value
|
||||||
|
# supplied for Username
|
||||||
|
# 2. generates a persistent GUID for the user that will be returned as
|
||||||
|
# the value of `Username` in the `get-user` and `list-users`
|
||||||
|
# operations, as well as the value of `sub` in `IdToken` and
|
||||||
|
# `AccessToken`
|
||||||
|
#
|
||||||
|
# ref: https://docs.aws.amazon.com/cognito/latest/developerguide/user-pool-settings-attributes.html#user-pool-settings-aliases-settings
|
||||||
|
if user_pool.extended_config.get("UsernameAttributes"):
|
||||||
|
username_attributes = user_pool.extended_config["UsernameAttributes"]
|
||||||
|
# attribute_type should be one of `email`, `phone_number` or both
|
||||||
|
for attribute_type in username_attributes:
|
||||||
|
# check if provided username matches one of the attribute types in
|
||||||
|
# `UsernameAttributes`
|
||||||
|
if attribute_type in username_attributes and validate_username_format(
|
||||||
|
username, _format=attribute_type
|
||||||
|
):
|
||||||
|
# insert provided username into new user's attributes under the
|
||||||
|
# correct key
|
||||||
|
flattened_attrs = flatten_attrs(attributes or {})
|
||||||
|
flattened_attrs.update({attribute_type: username})
|
||||||
|
attributes = expand_attrs(flattened_attrs)
|
||||||
|
# set username to None so that it will be default to the internal GUID
|
||||||
|
# when them user gets created
|
||||||
|
username = None
|
||||||
|
# once the username has been validated against a username attribute
|
||||||
|
# type, there is no need to attempt validation against the other
|
||||||
|
# type(s)
|
||||||
|
break
|
||||||
|
|
||||||
|
# The provided username has not matched the required format for any
|
||||||
|
# of the possible attributes
|
||||||
|
if username is not None:
|
||||||
|
raise InvalidParameterException(
|
||||||
|
"Username should be either an email or a phone number."
|
||||||
|
)
|
||||||
|
|
||||||
user = CognitoIdpUser(
|
user = CognitoIdpUser(
|
||||||
user_pool_id=user_pool.id,
|
user_pool_id=user_pool.id,
|
||||||
username=username,
|
username=username,
|
||||||
@ -947,10 +1050,10 @@ class CognitoIdpBackend(BaseBackend):
|
|||||||
if user_pool is None:
|
if user_pool is None:
|
||||||
raise ResourceNotFoundError(client_id)
|
raise ResourceNotFoundError(client_id)
|
||||||
|
|
||||||
if username not in user_pool.users:
|
user = user_pool._get_user(username)
|
||||||
|
if not user:
|
||||||
raise UserNotFoundError(username)
|
raise UserNotFoundError(username)
|
||||||
|
|
||||||
user = user_pool.users[username]
|
|
||||||
user.status = UserStatus["CONFIRMED"]
|
user.status = UserStatus["CONFIRMED"]
|
||||||
return ""
|
return ""
|
||||||
|
|
||||||
@ -976,7 +1079,7 @@ class CognitoIdpBackend(BaseBackend):
|
|||||||
):
|
):
|
||||||
raise NotAuthorizedError(secret_hash)
|
raise NotAuthorizedError(secret_hash)
|
||||||
|
|
||||||
user = user_pool.users.get(username)
|
user = user_pool._get_user(username)
|
||||||
if not user:
|
if not user:
|
||||||
raise UserNotFoundError(username)
|
raise UserNotFoundError(username)
|
||||||
|
|
||||||
@ -1001,7 +1104,7 @@ class CognitoIdpBackend(BaseBackend):
|
|||||||
username = auth_parameters.get("USERNAME")
|
username = auth_parameters.get("USERNAME")
|
||||||
password = auth_parameters.get("PASSWORD")
|
password = auth_parameters.get("PASSWORD")
|
||||||
|
|
||||||
user = user_pool.users.get(username)
|
user = user_pool._get_user(username)
|
||||||
|
|
||||||
if not user:
|
if not user:
|
||||||
raise UserNotFoundError(username)
|
raise UserNotFoundError(username)
|
||||||
@ -1069,7 +1172,7 @@ class CognitoIdpBackend(BaseBackend):
|
|||||||
for user_pool in self.user_pools.values():
|
for user_pool in self.user_pools.values():
|
||||||
if access_token in user_pool.access_tokens:
|
if access_token in user_pool.access_tokens:
|
||||||
_, username = user_pool.access_tokens[access_token]
|
_, username = user_pool.access_tokens[access_token]
|
||||||
user = user_pool.users.get(username)
|
user = user_pool._get_user(username)
|
||||||
if not user:
|
if not user:
|
||||||
raise UserNotFoundError(username)
|
raise UserNotFoundError(username)
|
||||||
|
|
||||||
@ -1081,7 +1184,7 @@ class CognitoIdpBackend(BaseBackend):
|
|||||||
for user_pool in self.user_pools.values():
|
for user_pool in self.user_pools.values():
|
||||||
if access_token in user_pool.access_tokens:
|
if access_token in user_pool.access_tokens:
|
||||||
_, username = user_pool.access_tokens[access_token]
|
_, username = user_pool.access_tokens[access_token]
|
||||||
user = user_pool.users.get(username)
|
user = user_pool._get_user(username)
|
||||||
if not user:
|
if not user:
|
||||||
raise UserNotFoundError(username)
|
raise UserNotFoundError(username)
|
||||||
|
|
||||||
@ -1097,7 +1200,7 @@ class CognitoIdpBackend(BaseBackend):
|
|||||||
for user_pool in self.user_pools.values():
|
for user_pool in self.user_pools.values():
|
||||||
if access_token in user_pool.access_tokens:
|
if access_token in user_pool.access_tokens:
|
||||||
_, username = user_pool.access_tokens[access_token]
|
_, username = user_pool.access_tokens[access_token]
|
||||||
user = user_pool.users.get(username)
|
user = user_pool._get_user(username)
|
||||||
if not user:
|
if not user:
|
||||||
raise UserNotFoundError(username)
|
raise UserNotFoundError(username)
|
||||||
|
|
||||||
|
@ -3,6 +3,12 @@ import string
|
|||||||
import hashlib
|
import hashlib
|
||||||
import hmac
|
import hmac
|
||||||
import base64
|
import base64
|
||||||
|
import re
|
||||||
|
|
||||||
|
FORMATS = {
|
||||||
|
"email": r"\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Z|a-z]{2,}\b",
|
||||||
|
"phone_number": r"\+\d{,15}",
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
PAGINATION_MODEL = {
|
PAGINATION_MODEL = {
|
||||||
@ -45,3 +51,18 @@ def check_secret_hash(app_client_secret, app_client_id, username, secret_hash):
|
|||||||
new_digest = hmac.new(key, msg, hashlib.sha256).digest()
|
new_digest = hmac.new(key, msg, hashlib.sha256).digest()
|
||||||
SECRET_HASH = base64.b64encode(new_digest).decode()
|
SECRET_HASH = base64.b64encode(new_digest).decode()
|
||||||
return SECRET_HASH == secret_hash
|
return SECRET_HASH == secret_hash
|
||||||
|
|
||||||
|
|
||||||
|
def validate_username_format(username, _format="email"):
|
||||||
|
# if the value of the `_format` param other than `email` or `phone_number`,
|
||||||
|
# the default value for the regex will match nothing and the
|
||||||
|
# method will return None
|
||||||
|
return re.fullmatch(FORMATS.get(_format, r"a^"), username)
|
||||||
|
|
||||||
|
|
||||||
|
def flatten_attrs(attrs):
|
||||||
|
return {attr["Name"]: attr["Value"] for attr in attrs}
|
||||||
|
|
||||||
|
|
||||||
|
def expand_attrs(attrs):
|
||||||
|
return [{"Name": k, "Value": v} for k, v in attrs.items()]
|
||||||
|
@ -913,6 +913,25 @@ def test_admin_add_user_to_group():
|
|||||||
list(result.keys()).should.equal(["ResponseMetadata"]) # No response expected
|
list(result.keys()).should.equal(["ResponseMetadata"]) # No response expected
|
||||||
|
|
||||||
|
|
||||||
|
@mock_cognitoidp
|
||||||
|
def test_admin_add_user_to_group_with_username_attributes():
|
||||||
|
conn = boto3.client("cognito-idp", "us-west-2")
|
||||||
|
|
||||||
|
user_pool_id = conn.create_user_pool(
|
||||||
|
PoolName=str(uuid.uuid4()), UsernameAttributes=["email"]
|
||||||
|
)["UserPool"]["Id"]
|
||||||
|
group_name = str(uuid.uuid4())
|
||||||
|
conn.create_group(GroupName=group_name, UserPoolId=user_pool_id)
|
||||||
|
|
||||||
|
username = "test@example.com"
|
||||||
|
conn.admin_create_user(UserPoolId=user_pool_id, Username=username)
|
||||||
|
|
||||||
|
result = conn.admin_add_user_to_group(
|
||||||
|
UserPoolId=user_pool_id, Username=username, GroupName=group_name
|
||||||
|
)
|
||||||
|
list(result.keys()).should.equal(["ResponseMetadata"]) # No response expected
|
||||||
|
|
||||||
|
|
||||||
@mock_cognitoidp
|
@mock_cognitoidp
|
||||||
def test_admin_add_user_to_group_again_is_noop():
|
def test_admin_add_user_to_group_again_is_noop():
|
||||||
conn = boto3.client("cognito-idp", "us-west-2")
|
conn = boto3.client("cognito-idp", "us-west-2")
|
||||||
@ -930,6 +949,7 @@ def test_admin_add_user_to_group_again_is_noop():
|
|||||||
conn.admin_add_user_to_group(
|
conn.admin_add_user_to_group(
|
||||||
UserPoolId=user_pool_id, Username=username, GroupName=group_name
|
UserPoolId=user_pool_id, Username=username, GroupName=group_name
|
||||||
)
|
)
|
||||||
|
# should there be an assertion here?
|
||||||
|
|
||||||
|
|
||||||
@mock_cognitoidp
|
@mock_cognitoidp
|
||||||
@ -1001,6 +1021,29 @@ def test_admin_list_groups_for_user():
|
|||||||
result["Groups"][0]["GroupName"].should.equal(group_name)
|
result["Groups"][0]["GroupName"].should.equal(group_name)
|
||||||
|
|
||||||
|
|
||||||
|
@mock_cognitoidp
|
||||||
|
def test_admin_list_groups_for_user_with_username_attribute():
|
||||||
|
conn = boto3.client("cognito-idp", "us-west-2")
|
||||||
|
|
||||||
|
user_pool_id = conn.create_user_pool(
|
||||||
|
PoolName=str(uuid.uuid4()), UsernameAttributes=["email"]
|
||||||
|
)["UserPool"]["Id"]
|
||||||
|
group_name = str(uuid.uuid4())
|
||||||
|
conn.create_group(GroupName=group_name, UserPoolId=user_pool_id)
|
||||||
|
|
||||||
|
username = "test@example.com"
|
||||||
|
conn.admin_create_user(UserPoolId=user_pool_id, Username=username)
|
||||||
|
|
||||||
|
conn.admin_add_user_to_group(
|
||||||
|
UserPoolId=user_pool_id, Username=username, GroupName=group_name
|
||||||
|
)
|
||||||
|
|
||||||
|
result = conn.admin_list_groups_for_user(Username=username, UserPoolId=user_pool_id)
|
||||||
|
|
||||||
|
result["Groups"].should.have.length_of(1)
|
||||||
|
result["Groups"][0]["GroupName"].should.equal(group_name)
|
||||||
|
|
||||||
|
|
||||||
@mock_cognitoidp
|
@mock_cognitoidp
|
||||||
def test_admin_list_groups_for_user_ignores_deleted_group():
|
def test_admin_list_groups_for_user_ignores_deleted_group():
|
||||||
conn = boto3.client("cognito-idp", "us-west-2")
|
conn = boto3.client("cognito-idp", "us-west-2")
|
||||||
@ -1055,6 +1098,35 @@ def test_admin_remove_user_from_group():
|
|||||||
].should.have.length_of(0)
|
].should.have.length_of(0)
|
||||||
|
|
||||||
|
|
||||||
|
@mock_cognitoidp
|
||||||
|
def test_admin_remove_user_from_group_with_username_attributes():
|
||||||
|
conn = boto3.client("cognito-idp", "us-west-2")
|
||||||
|
|
||||||
|
user_pool_id = conn.create_user_pool(
|
||||||
|
PoolName=str(uuid.uuid4()), UsernameAttributes=["email"]
|
||||||
|
)["UserPool"]["Id"]
|
||||||
|
group_name = str(uuid.uuid4())
|
||||||
|
conn.create_group(GroupName=group_name, UserPoolId=user_pool_id)
|
||||||
|
|
||||||
|
username = "test@example.com"
|
||||||
|
conn.admin_create_user(UserPoolId=user_pool_id, Username=username)
|
||||||
|
|
||||||
|
conn.admin_add_user_to_group(
|
||||||
|
UserPoolId=user_pool_id, Username=username, GroupName=group_name
|
||||||
|
)
|
||||||
|
|
||||||
|
result = conn.admin_remove_user_from_group(
|
||||||
|
UserPoolId=user_pool_id, Username=username, GroupName=group_name
|
||||||
|
)
|
||||||
|
list(result.keys()).should.equal(["ResponseMetadata"]) # No response expected
|
||||||
|
conn.list_users_in_group(UserPoolId=user_pool_id, GroupName=group_name)[
|
||||||
|
"Users"
|
||||||
|
].should.have.length_of(0)
|
||||||
|
conn.admin_list_groups_for_user(Username=username, UserPoolId=user_pool_id)[
|
||||||
|
"Groups"
|
||||||
|
].should.have.length_of(0)
|
||||||
|
|
||||||
|
|
||||||
@mock_cognitoidp
|
@mock_cognitoidp
|
||||||
def test_admin_remove_user_from_group_again_is_noop():
|
def test_admin_remove_user_from_group_again_is_noop():
|
||||||
conn = boto3.client("cognito-idp", "us-west-2")
|
conn = boto3.client("cognito-idp", "us-west-2")
|
||||||
@ -1089,7 +1161,7 @@ def test_admin_create_user():
|
|||||||
|
|
||||||
result["User"]["Username"].should.equal(username)
|
result["User"]["Username"].should.equal(username)
|
||||||
result["User"]["UserStatus"].should.equal("FORCE_CHANGE_PASSWORD")
|
result["User"]["UserStatus"].should.equal("FORCE_CHANGE_PASSWORD")
|
||||||
result["User"]["Attributes"].should.have.length_of(1)
|
result["User"]["Attributes"].should.have.length_of(2)
|
||||||
|
|
||||||
def _verify_attribute(name, v):
|
def _verify_attribute(name, v):
|
||||||
attr = [a for a in result["User"]["Attributes"] if a["Name"] == name]
|
attr = [a for a in result["User"]["Attributes"] if a["Name"] == name]
|
||||||
@ -1100,6 +1172,84 @@ def test_admin_create_user():
|
|||||||
result["User"]["Enabled"].should.equal(True)
|
result["User"]["Enabled"].should.equal(True)
|
||||||
|
|
||||||
|
|
||||||
|
@mock_cognitoidp
|
||||||
|
def test_admin_create_user_with_username_attributes():
|
||||||
|
conn = boto3.client("cognito-idp", "us-west-2")
|
||||||
|
|
||||||
|
username = "test@example.com"
|
||||||
|
value = str(uuid.uuid4())
|
||||||
|
user_pool_id = conn.create_user_pool(
|
||||||
|
PoolName=str(uuid.uuid4()), UsernameAttributes=["email"]
|
||||||
|
)["UserPool"]["Id"]
|
||||||
|
result = conn.admin_create_user(
|
||||||
|
UserPoolId=user_pool_id,
|
||||||
|
Username=username,
|
||||||
|
UserAttributes=[{"Name": "thing", "Value": value}],
|
||||||
|
)
|
||||||
|
|
||||||
|
result["User"]["Username"].should_not.equal(username)
|
||||||
|
result["User"]["UserStatus"].should.equal("FORCE_CHANGE_PASSWORD")
|
||||||
|
result["User"]["Attributes"].should.have.length_of(3)
|
||||||
|
|
||||||
|
def _verify_attribute(name, v):
|
||||||
|
attr = [a for a in result["User"]["Attributes"] if a["Name"] == name]
|
||||||
|
attr.should.have.length_of(1)
|
||||||
|
attr[0]["Value"].should.equal(v)
|
||||||
|
|
||||||
|
_verify_attribute("thing", value)
|
||||||
|
_verify_attribute("email", username)
|
||||||
|
result["User"]["Enabled"].should.equal(True)
|
||||||
|
|
||||||
|
|
||||||
|
@mock_cognitoidp
|
||||||
|
def test_admin_create_user_with_incorrect_username_attribute_type_fails():
|
||||||
|
conn = boto3.client("cognito-idp", "us-west-2")
|
||||||
|
|
||||||
|
value = str(uuid.uuid4())
|
||||||
|
user_pool_id = conn.create_user_pool(
|
||||||
|
PoolName=str(uuid.uuid4()), UsernameAttributes=["email"]
|
||||||
|
)["UserPool"]["Id"]
|
||||||
|
|
||||||
|
with pytest.raises(ClientError) as ex:
|
||||||
|
username = str(uuid.uuid4())
|
||||||
|
conn.admin_create_user(
|
||||||
|
UserPoolId=user_pool_id,
|
||||||
|
Username=username,
|
||||||
|
UserAttributes=[{"Name": "thing", "Value": value}],
|
||||||
|
)
|
||||||
|
err = ex.value.response["Error"]
|
||||||
|
err["Code"].should.equal("InvalidParameterException")
|
||||||
|
err["Message"].should.equal("Username should be either an email or a phone number.")
|
||||||
|
|
||||||
|
|
||||||
|
@mock_cognitoidp
|
||||||
|
def test_admin_create_user_with_existing_username_attribute_fails():
|
||||||
|
conn = boto3.client("cognito-idp", "us-west-2")
|
||||||
|
|
||||||
|
value = str(uuid.uuid4())
|
||||||
|
user_pool_id = conn.create_user_pool(
|
||||||
|
PoolName=str(uuid.uuid4()), UsernameAttributes=["email"]
|
||||||
|
)["UserPool"]["Id"]
|
||||||
|
|
||||||
|
username = "test@example.com"
|
||||||
|
conn.admin_create_user(
|
||||||
|
UserPoolId=user_pool_id,
|
||||||
|
Username=username,
|
||||||
|
UserAttributes=[{"Name": "thing", "Value": value}],
|
||||||
|
)
|
||||||
|
|
||||||
|
with pytest.raises(ClientError) as ex:
|
||||||
|
username = "test@example.com"
|
||||||
|
conn.admin_create_user(
|
||||||
|
UserPoolId=user_pool_id,
|
||||||
|
Username=username,
|
||||||
|
UserAttributes=[{"Name": "thing", "Value": value}],
|
||||||
|
)
|
||||||
|
err = ex.value.response["Error"]
|
||||||
|
err["Code"].should.equal("UsernameExistsException")
|
||||||
|
err["Message"].should.equal("test@example.com")
|
||||||
|
|
||||||
|
|
||||||
@mock_cognitoidp
|
@mock_cognitoidp
|
||||||
def test_admin_create_existing_user():
|
def test_admin_create_existing_user():
|
||||||
conn = boto3.client("cognito-idp", "us-west-2")
|
conn = boto3.client("cognito-idp", "us-west-2")
|
||||||
@ -1190,7 +1340,60 @@ def test_admin_get_user():
|
|||||||
|
|
||||||
result = conn.admin_get_user(UserPoolId=user_pool_id, Username=username)
|
result = conn.admin_get_user(UserPoolId=user_pool_id, Username=username)
|
||||||
result["Username"].should.equal(username)
|
result["Username"].should.equal(username)
|
||||||
result["UserAttributes"].should.have.length_of(1)
|
result["UserAttributes"].should.have.length_of(2)
|
||||||
|
|
||||||
|
|
||||||
|
@mock_cognitoidp
|
||||||
|
def test_admin_get_user_with_username_attributes():
|
||||||
|
conn = boto3.client("cognito-idp", "us-west-2")
|
||||||
|
|
||||||
|
username = "test@example.com"
|
||||||
|
value = str(uuid.uuid4())
|
||||||
|
user_pool_id = conn.create_user_pool(
|
||||||
|
PoolName=str(uuid.uuid4()), UsernameAttributes=["email", "phone_number"]
|
||||||
|
)["UserPool"]["Id"]
|
||||||
|
conn.admin_create_user(
|
||||||
|
UserPoolId=user_pool_id,
|
||||||
|
Username=username,
|
||||||
|
UserAttributes=[
|
||||||
|
{"Name": "thing", "Value": value},
|
||||||
|
{"Name": "phone_number", "Value": "+123456789"},
|
||||||
|
],
|
||||||
|
)
|
||||||
|
# verify user can be queried by email
|
||||||
|
result = conn.admin_get_user(UserPoolId=user_pool_id, Username=username)
|
||||||
|
result["Username"].should_not.equal(username)
|
||||||
|
result["UserAttributes"].should.have.length_of(4)
|
||||||
|
|
||||||
|
def _verify_attribute(name, v):
|
||||||
|
attr = [a for a in result["UserAttributes"] if a["Name"] == name]
|
||||||
|
attr.should.have.length_of(1)
|
||||||
|
attr[0]["Value"].should.equal(v)
|
||||||
|
|
||||||
|
_verify_attribute("phone_number", "+123456789")
|
||||||
|
_verify_attribute("email", "test@example.com")
|
||||||
|
|
||||||
|
# verify user can be queried by phone number
|
||||||
|
result = conn.admin_get_user(UserPoolId=user_pool_id, Username="+123456789")
|
||||||
|
|
||||||
|
result["Username"].should_not.equal(username)
|
||||||
|
result["UserAttributes"].should.have.length_of(4)
|
||||||
|
_verify_attribute("phone_number", "+123456789")
|
||||||
|
_verify_attribute("email", "test@example.com")
|
||||||
|
|
||||||
|
# verify that the generate user sub is a valid UUID v4
|
||||||
|
[user_sub] = [
|
||||||
|
attr["Value"] for attr in result["UserAttributes"] if attr["Name"] == "sub"
|
||||||
|
]
|
||||||
|
uuid.UUID(user_sub)
|
||||||
|
|
||||||
|
# verify user should be queried by user sub
|
||||||
|
result = conn.admin_get_user(UserPoolId=user_pool_id, Username=user_sub)
|
||||||
|
|
||||||
|
result["Username"].should_not.equal(username)
|
||||||
|
result["UserAttributes"].should.have.length_of(4)
|
||||||
|
_verify_attribute("phone_number", "+123456789")
|
||||||
|
_verify_attribute("email", "test@example.com")
|
||||||
|
|
||||||
|
|
||||||
@mock_cognitoidp
|
@mock_cognitoidp
|
||||||
@ -1209,13 +1412,29 @@ def test_admin_get_missing_user():
|
|||||||
caught.should.be.true
|
caught.should.be.true
|
||||||
|
|
||||||
|
|
||||||
|
@mock_cognitoidp
|
||||||
|
def test_admin_get_missing_user_with_username_attributes():
|
||||||
|
conn = boto3.client("cognito-idp", "us-west-2")
|
||||||
|
|
||||||
|
username = "test@example.com"
|
||||||
|
user_pool_id = conn.create_user_pool(
|
||||||
|
PoolName=str(uuid.uuid4()), UsernameAttributes=["email"]
|
||||||
|
)["UserPool"]["Id"]
|
||||||
|
|
||||||
|
with pytest.raises(ClientError) as ex:
|
||||||
|
conn.admin_get_user(UserPoolId=user_pool_id, Username=username)
|
||||||
|
|
||||||
|
err = ex.value.response["Error"]
|
||||||
|
err["Code"].should.equal("UserNotFoundException")
|
||||||
|
|
||||||
|
|
||||||
@mock_cognitoidp
|
@mock_cognitoidp
|
||||||
def test_get_user():
|
def test_get_user():
|
||||||
conn = boto3.client("cognito-idp", "us-west-2")
|
conn = boto3.client("cognito-idp", "us-west-2")
|
||||||
outputs = authentication_flow(conn, "ADMIN_NO_SRP_AUTH")
|
outputs = authentication_flow(conn, "ADMIN_NO_SRP_AUTH")
|
||||||
result = conn.get_user(AccessToken=outputs["access_token"])
|
result = conn.get_user(AccessToken=outputs["access_token"])
|
||||||
result["Username"].should.equal(outputs["username"])
|
result["Username"].should.equal(outputs["username"])
|
||||||
result["UserAttributes"].should.have.length_of(1)
|
result["UserAttributes"].should.have.length_of(2)
|
||||||
|
|
||||||
def _verify_attribute(name, v):
|
def _verify_attribute(name, v):
|
||||||
attr = [a for a in result["UserAttributes"] if a["Name"] == name]
|
attr = [a for a in result["UserAttributes"] if a["Name"] == name]
|
||||||
@ -1317,6 +1536,50 @@ def test_list_users_invalid_attributes():
|
|||||||
assert err["Message"].should.equal("Invalid search attribute: custom:foo")
|
assert err["Message"].should.equal("Invalid search attribute: custom:foo")
|
||||||
|
|
||||||
|
|
||||||
|
@mock_cognitoidp
|
||||||
|
def test_list_users_with_username_attributes():
|
||||||
|
conn = boto3.client("cognito-idp", "us-west-2")
|
||||||
|
|
||||||
|
username = "test@example.com"
|
||||||
|
user_pool_id = conn.create_user_pool(
|
||||||
|
PoolName=str(uuid.uuid4()), UsernameAttributes=["email"]
|
||||||
|
)["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_not.equal(username)
|
||||||
|
|
||||||
|
def _verify_attribute(name, v):
|
||||||
|
attr = [a for a in result["Users"][0]["Attributes"] if a["Name"] == name]
|
||||||
|
attr.should.have.length_of(1)
|
||||||
|
attr[0]["Value"].should.equal(v)
|
||||||
|
|
||||||
|
_verify_attribute("email", username)
|
||||||
|
|
||||||
|
username_bis = "test2@uexample.com"
|
||||||
|
conn.admin_create_user(
|
||||||
|
UserPoolId=user_pool_id,
|
||||||
|
Username=username_bis,
|
||||||
|
UserAttributes=[{"Name": "phone_number", "Value": "+33666666666"}],
|
||||||
|
)
|
||||||
|
result = conn.list_users(
|
||||||
|
UserPoolId=user_pool_id, Filter='phone_number="+33666666666"'
|
||||||
|
)
|
||||||
|
result["Users"].should.have.length_of(1)
|
||||||
|
result["Users"][0]["Username"].should_not.equal(username_bis)
|
||||||
|
uuid.UUID(result["Users"][0]["Username"])
|
||||||
|
|
||||||
|
_verify_attribute("email", username_bis)
|
||||||
|
|
||||||
|
# checking Filter with space
|
||||||
|
result = conn.list_users(
|
||||||
|
UserPoolId=user_pool_id, Filter='phone_number = "+33666666666"'
|
||||||
|
)
|
||||||
|
result["Users"].should.have.length_of(1)
|
||||||
|
result["Users"][0]["Username"].should_not.equal(username_bis)
|
||||||
|
_verify_attribute("email", username_bis)
|
||||||
|
|
||||||
|
|
||||||
@mock_cognitoidp
|
@mock_cognitoidp
|
||||||
def test_list_users_inherent_attributes():
|
def test_list_users_inherent_attributes():
|
||||||
conn = boto3.client("cognito-idp", "us-west-2")
|
conn = boto3.client("cognito-idp", "us-west-2")
|
||||||
@ -1442,6 +1705,24 @@ def test_admin_disable_user():
|
|||||||
].should.equal(False)
|
].should.equal(False)
|
||||||
|
|
||||||
|
|
||||||
|
@mock_cognitoidp
|
||||||
|
def test_admin_disable_user_with_username_attributes():
|
||||||
|
conn = boto3.client("cognito-idp", "us-west-2")
|
||||||
|
|
||||||
|
username = "test@example.com"
|
||||||
|
user_pool_id = conn.create_user_pool(
|
||||||
|
PoolName=str(uuid.uuid4()), UsernameAttributes=["email"]
|
||||||
|
)["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
|
@mock_cognitoidp
|
||||||
def test_admin_enable_user():
|
def test_admin_enable_user():
|
||||||
conn = boto3.client("cognito-idp", "us-west-2")
|
conn = boto3.client("cognito-idp", "us-west-2")
|
||||||
@ -1459,6 +1740,25 @@ def test_admin_enable_user():
|
|||||||
].should.equal(True)
|
].should.equal(True)
|
||||||
|
|
||||||
|
|
||||||
|
@mock_cognitoidp
|
||||||
|
def test_admin_enable_user_with_username_attributes():
|
||||||
|
conn = boto3.client("cognito-idp", "us-west-2")
|
||||||
|
|
||||||
|
username = "test@example.com"
|
||||||
|
user_pool_id = conn.create_user_pool(
|
||||||
|
PoolName=str(uuid.uuid4()), UsernameAttributes=["email"]
|
||||||
|
)["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
|
@mock_cognitoidp
|
||||||
def test_admin_delete_user():
|
def test_admin_delete_user():
|
||||||
conn = boto3.client("cognito-idp", "us-west-2")
|
conn = boto3.client("cognito-idp", "us-west-2")
|
||||||
@ -1477,6 +1777,24 @@ def test_admin_delete_user():
|
|||||||
caught.should.be.true
|
caught.should.be.true
|
||||||
|
|
||||||
|
|
||||||
|
@mock_cognitoidp
|
||||||
|
def test_admin_delete_user_with_username_attributes():
|
||||||
|
conn = boto3.client("cognito-idp", "us-west-2")
|
||||||
|
|
||||||
|
username = "test@example.com"
|
||||||
|
user_pool_id = conn.create_user_pool(
|
||||||
|
PoolName=str(uuid.uuid4()), UsernameAttributes=["email"]
|
||||||
|
)["UserPool"]["Id"]
|
||||||
|
conn.admin_create_user(UserPoolId=user_pool_id, Username=username)
|
||||||
|
conn.admin_delete_user(UserPoolId=user_pool_id, Username=username)
|
||||||
|
|
||||||
|
with pytest.raises(ClientError) as ex:
|
||||||
|
conn.admin_get_user(UserPoolId=user_pool_id, Username=username)
|
||||||
|
|
||||||
|
err = ex.value.response["Error"]
|
||||||
|
err["Code"].should.equal("UserNotFoundException")
|
||||||
|
|
||||||
|
|
||||||
def authentication_flow(conn, auth_flow):
|
def authentication_flow(conn, auth_flow):
|
||||||
username = str(uuid.uuid4())
|
username = str(uuid.uuid4())
|
||||||
temporary_password = str(uuid.uuid4())
|
temporary_password = str(uuid.uuid4())
|
||||||
@ -1927,6 +2245,34 @@ def test_sign_up():
|
|||||||
result["UserSub"].should_not.be.none
|
result["UserSub"].should_not.be.none
|
||||||
|
|
||||||
|
|
||||||
|
@mock_cognitoidp
|
||||||
|
def test_sign_up_with_username_attributes():
|
||||||
|
conn = boto3.client("cognito-idp", "us-west-2")
|
||||||
|
user_pool_id = conn.create_user_pool(
|
||||||
|
PoolName=str(uuid.uuid4()), UsernameAttributes=["email", "phone_number"]
|
||||||
|
)["UserPool"]["Id"]
|
||||||
|
client_id = conn.create_user_pool_client(
|
||||||
|
UserPoolId=user_pool_id, ClientName=str(uuid.uuid4()),
|
||||||
|
)["UserPoolClient"]["ClientId"]
|
||||||
|
username = str(uuid.uuid4())
|
||||||
|
password = str(uuid.uuid4())
|
||||||
|
with pytest.raises(ClientError) as err:
|
||||||
|
# Attempt to add user again
|
||||||
|
result = conn.sign_up(ClientId=client_id, Username=username, Password=password)
|
||||||
|
err.value.response["Error"]["Code"].should.equal("InvalidParameterException")
|
||||||
|
|
||||||
|
username = "test@example.com"
|
||||||
|
result = conn.sign_up(ClientId=client_id, Username=username, Password=password)
|
||||||
|
|
||||||
|
result["UserConfirmed"].should.be.false
|
||||||
|
result["UserSub"].should_not.be.none
|
||||||
|
username = "+123456789"
|
||||||
|
result = conn.sign_up(ClientId=client_id, Username=username, Password=password)
|
||||||
|
|
||||||
|
result["UserConfirmed"].should.be.false
|
||||||
|
result["UserSub"].should_not.be.none
|
||||||
|
|
||||||
|
|
||||||
@mock_cognitoidp
|
@mock_cognitoidp
|
||||||
def test_sign_up_existing_user():
|
def test_sign_up_existing_user():
|
||||||
conn = boto3.client("cognito-idp", "us-west-2")
|
conn = boto3.client("cognito-idp", "us-west-2")
|
||||||
@ -1966,6 +2312,27 @@ def test_confirm_sign_up():
|
|||||||
result["UserStatus"].should.equal("CONFIRMED")
|
result["UserStatus"].should.equal("CONFIRMED")
|
||||||
|
|
||||||
|
|
||||||
|
@mock_cognitoidp
|
||||||
|
def test_confirm_sign_up_with_username_attributes():
|
||||||
|
conn = boto3.client("cognito-idp", "us-west-2")
|
||||||
|
username = "test@example.com"
|
||||||
|
password = str(uuid.uuid4())
|
||||||
|
user_pool_id = conn.create_user_pool(
|
||||||
|
PoolName=str(uuid.uuid4()), UsernameAttributes=["email"]
|
||||||
|
)["UserPool"]["Id"]
|
||||||
|
client_id = conn.create_user_pool_client(
|
||||||
|
UserPoolId=user_pool_id, ClientName=str(uuid.uuid4()), GenerateSecret=True,
|
||||||
|
)["UserPoolClient"]["ClientId"]
|
||||||
|
conn.sign_up(ClientId=client_id, Username=username, Password=password)
|
||||||
|
|
||||||
|
conn.confirm_sign_up(
|
||||||
|
ClientId=client_id, Username=username, ConfirmationCode="123456",
|
||||||
|
)
|
||||||
|
|
||||||
|
result = conn.admin_get_user(UserPoolId=user_pool_id, Username=username)
|
||||||
|
result["UserStatus"].should.equal("CONFIRMED")
|
||||||
|
|
||||||
|
|
||||||
@mock_cognitoidp
|
@mock_cognitoidp
|
||||||
def test_initiate_auth_USER_SRP_AUTH():
|
def test_initiate_auth_USER_SRP_AUTH():
|
||||||
conn = boto3.client("cognito-idp", "us-west-2")
|
conn = boto3.client("cognito-idp", "us-west-2")
|
||||||
@ -2001,6 +2368,43 @@ def test_initiate_auth_USER_SRP_AUTH():
|
|||||||
result["ChallengeName"].should.equal("PASSWORD_VERIFIER")
|
result["ChallengeName"].should.equal("PASSWORD_VERIFIER")
|
||||||
|
|
||||||
|
|
||||||
|
@mock_cognitoidp
|
||||||
|
def test_initiate_auth_USER_SRP_AUTH_with_username_attributes():
|
||||||
|
conn = boto3.client("cognito-idp", "us-west-2")
|
||||||
|
username = "test@example.com"
|
||||||
|
password = str(uuid.uuid4())
|
||||||
|
user_pool_id = conn.create_user_pool(
|
||||||
|
PoolName=str(uuid.uuid4()), UsernameAttributes=["email"]
|
||||||
|
)["UserPool"]["Id"]
|
||||||
|
client_id = conn.create_user_pool_client(
|
||||||
|
UserPoolId=user_pool_id, ClientName=str(uuid.uuid4()), GenerateSecret=True,
|
||||||
|
)["UserPoolClient"]["ClientId"]
|
||||||
|
conn.sign_up(ClientId=client_id, Username=username, Password=password)
|
||||||
|
client_secret = conn.describe_user_pool_client(
|
||||||
|
UserPoolId=user_pool_id, ClientId=client_id,
|
||||||
|
)["UserPoolClient"]["ClientSecret"]
|
||||||
|
conn.confirm_sign_up(
|
||||||
|
ClientId=client_id, Username=username, ConfirmationCode="123456",
|
||||||
|
)
|
||||||
|
|
||||||
|
key = bytes(str(client_secret).encode("latin-1"))
|
||||||
|
msg = bytes(str(username + client_id).encode("latin-1"))
|
||||||
|
new_digest = hmac.new(key, msg, hashlib.sha256).digest()
|
||||||
|
secret_hash = base64.b64encode(new_digest).decode()
|
||||||
|
|
||||||
|
result = conn.initiate_auth(
|
||||||
|
ClientId=client_id,
|
||||||
|
AuthFlow="USER_SRP_AUTH",
|
||||||
|
AuthParameters={
|
||||||
|
"USERNAME": username,
|
||||||
|
"SRP_A": uuid.uuid4().hex,
|
||||||
|
"SECRET_HASH": secret_hash,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
result["ChallengeName"].should.equal("PASSWORD_VERIFIER")
|
||||||
|
|
||||||
|
|
||||||
@mock_cognitoidp
|
@mock_cognitoidp
|
||||||
def test_initiate_auth_REFRESH_TOKEN():
|
def test_initiate_auth_REFRESH_TOKEN():
|
||||||
conn = boto3.client("cognito-idp", "us-west-2")
|
conn = boto3.client("cognito-idp", "us-west-2")
|
||||||
@ -2260,7 +2664,7 @@ def test_admin_set_user_password():
|
|||||||
)
|
)
|
||||||
result = conn.admin_get_user(UserPoolId=user_pool_id, Username=username)
|
result = conn.admin_get_user(UserPoolId=user_pool_id, Username=username)
|
||||||
result["Username"].should.equal(username)
|
result["Username"].should.equal(username)
|
||||||
result["UserAttributes"].should.have.length_of(1)
|
result["UserAttributes"].should.have.length_of(2)
|
||||||
|
|
||||||
def _verify_attribute(name, v):
|
def _verify_attribute(name, v):
|
||||||
attr = [a for a in result["UserAttributes"] if a["Name"] == name]
|
attr = [a for a in result["UserAttributes"] if a["Name"] == name]
|
||||||
|
Loading…
x
Reference in New Issue
Block a user