Skip to content

Commit

Permalink
fix(token-service): generated password should contain required charac…
Browse files Browse the repository at this point in the history
…ters
  • Loading branch information
mmalenic committed Jan 16, 2025
1 parent f38855e commit ebdf111
Show file tree
Hide file tree
Showing 3 changed files with 52 additions and 12 deletions.
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
# -*- coding: utf-8 -*-
import random
import secrets
import string
from dataclasses import dataclass
from typing import List

import boto3

Expand All @@ -13,24 +14,49 @@ class ServiceUserDto:
email: str


SPECIAL_PASSWORD_CHAR_SET = "_-!@#%&."


class CognitoTokenService:

def __init__(self, user_pool_id: str, user_pool_app_client_id: str):
self.client = boto3.client('cognito-idp')
self.user_pool_id: str = user_pool_id
self.user_pool_app_client_id: str = user_pool_app_client_id

@staticmethod
def generate_password(length: int = 32) -> str:
self.password_char_sets: List[str] = [
string.ascii_lowercase,
string.ascii_uppercase,
string.digits,
SPECIAL_PASSWORD_CHAR_SET,
]

def generate_password(self, length: int = 32) -> str:
"""
Must meet the Cognito password requirements policy
https://docs.aws.amazon.com/cognito/latest/developerguide/user-pool-settings-policies.html
"""
if length < 8:
raise ValueError('Length must be at least 8 characters or more better')
return ''.join(
random.SystemRandom().choice(string.ascii_letters + string.digits + '_-!@#%&.') for _ in range(length)
)
raise ValueError("Length must be at least 8 characters or more better")

password = ""
while not self.is_password_valid(password):
password = "".join(
secrets.SystemRandom().choice("".join(self.password_char_sets))
for _ in range(length)
)

return password

def is_password_valid(self, password: str) -> bool:
"""
Check if the password meets the Cognito password requirements policy
https://docs.aws.amazon.com/cognito/latest/developerguide/user-pool-settings-policies.html
"""
for char_set in self.password_char_sets:
if not any(c in char_set for c in password):
return False

return True

def list_users(self, **kwargs) -> dict:
if 'UserPoolId' in kwargs.keys():
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
import botocore
from botocore.stub import Stubber

from . import CognitoTokenService, ServiceUserDto
from . import CognitoTokenService, ServiceUserDto, SPECIAL_PASSWORD_CHAR_SET

logger = logging.getLogger()
logger.setLevel(logging.INFO)
Expand All @@ -22,7 +22,7 @@ def setUp(self):
self.jjb_dto = ServiceUserDto(
username='jjb',
email='<EMAIL>',
password=CognitoTokenService.generate_password()
password=self.srv.generate_password()
)

self.mock_client = botocore.session.get_session().create_client('cognito-idp')
Expand All @@ -39,11 +39,25 @@ def test_generate_password(self):
"""
python -m unittest token_service.cognitor.tests.CognitorUnitTest.test_generate_password
"""
passwd = CognitoTokenService.generate_password()
passwd = self.srv.generate_password()
self.assertIsNotNone(passwd)
self.assertEqual(len(passwd), 32)
self.assertTrue(any(c.islower() for c in passwd))
self.assertTrue(any(c.isupper() for c in passwd))
self.assertTrue(any(c.isdigit() for c in passwd))
self.assertTrue(any(c in SPECIAL_PASSWORD_CHAR_SET for c in passwd))
# print(passwd)

def test_is_password_valid(self):
"""
python -m unittest token_service.cognitor.tests.CognitorUnitTest.test_is_password_valid
"""
self.assertTrue(self.srv.is_password_valid("abAB12!@")) # pragma: allowlist secret
self.assertFalse(self.srv.is_password_valid("abAB1234")) # pragma: allowlist secret
self.assertFalse(self.srv.is_password_valid("abAB#&!@")) # pragma: allowlist secret
self.assertFalse(self.srv.is_password_valid("ABCD12!@")) # pragma: allowlist secret
self.assertFalse(self.srv.is_password_valid("abcd12!@")) # pragma: allowlist secret

def test_list_users(self):
"""
python -m unittest token_service.cognitor.tests.CognitorUnitTest.test_list_users
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,7 @@ def create_secret(service_client, arn, token):
logger.info("createSecret: Successfully retrieved secret for %s." % arn)
except service_client.exceptions.ResourceNotFoundException:
# Generate a random password
current_dict['password'] = CognitoTokenService.generate_password()
current_dict['password'] = token_srv.generate_password()
# Put the secret
service_client.put_secret_value(SecretId=arn, ClientRequestToken=token, SecretString=json.dumps(current_dict), VersionStages=['AWSPENDING'])
logger.info("createSecret: Successfully put secret for ARN %s and version %s." % (arn, token))
Expand Down

0 comments on commit ebdf111

Please sign in to comment.