Working on user login and related updates

This commit is contained in:
Scott Idem
2021-10-06 13:26:03 -04:00
parent d6892c168c
commit 7c919b513c
3 changed files with 110 additions and 68 deletions

View File

@@ -265,7 +265,7 @@ def load_user_obj(
inc_post_comment_list: bool = False,
inc_user_role_list: bool = False,
) -> User_Out_Base|dict|bool:
# log.setLevel(logging.WARNING) # DEBUG, INFO, WARNING, ERROR, EXCEPTION, CRITICAL
log.setLevel(logging.WARNING) # DEBUG, INFO, WARNING, ERROR, EXCEPTION, CRITICAL
log.debug(locals())
if user_id := redis_lookup_id_random(record_id_random=user_id, table_name='user'): pass

View File

@@ -148,27 +148,27 @@ async def get_event_presentation_obj(
return mk_resp(data=None, status_code=404)
if event_presentation_obj := load_event_presentation_obj(
event_presentation_id = event_presentation_id,
enabled = enabled,
# review = review,
# approved = approved,
hidden = hidden,
inc_file_count = inc_file_count,
limit = limit,
inc_address = inc_address,
inc_contact = inc_contact,
inc_event_abstract_list = inc_event_abstract_list,
inc_event_badge = inc_event_badge,
# inc_event_badge_list = inc_event_badge_list,
inc_event_device_list = inc_event_device_list,
inc_event_file_list = inc_event_file_list,
inc_event_person_list = inc_event_person_list,
inc_event_presenter_list = inc_event_presenter_list,
inc_event_registration = inc_event_registration,
# inc_event_registration_list = inc_event_registration_list,
inc_person = inc_person,
inc_user = inc_user,
):
event_presentation_id = event_presentation_id,
enabled = enabled,
# review = review,
# approved = approved,
hidden = hidden,
inc_file_count = inc_file_count,
limit = limit,
inc_address = inc_address,
inc_contact = inc_contact,
inc_event_abstract_list = inc_event_abstract_list,
inc_event_badge = inc_event_badge,
# inc_event_badge_list = inc_event_badge_list,
inc_event_device_list = inc_event_device_list,
inc_event_file_list = inc_event_file_list,
inc_event_person_list = inc_event_person_list,
inc_event_presenter_list = inc_event_presenter_list,
inc_event_registration = inc_event_registration,
# inc_event_registration_list = inc_event_registration_list,
inc_person = inc_person,
inc_user = inc_user,
):
event_presentation_dict = event_presentation_obj.dict(by_alias=by_alias, exclude_unset=exclude_unset)
pass
else:

View File

@@ -202,14 +202,17 @@ async def user_new_auth_key(
# ### BEGIN ### API User Routers ### user_authenticate() ###
# Authenticate a username and password OR by authorization key
# Authenticate a username and password OR by user ID and authorization key
# An authorization key can only be done once. It will be deleted if found.
# A new key will need to be requested for a particular user each time.
# NOTE: Should this be divided into username/password and user ID/auth key endpoints?
# Updated 2021-10-06
@router.get('/authenticate', response_model=Resp_Body_Base)
async def user_authenticate(
account_id: Optional[Union[int,str]] = None,
username: Optional[str] = Query(None, min_length=2, max_length=50),
password: Optional[str] = Query(None, min_length=6, max_length=50),
user_id: Optional[str] = Query(None, min_length=11, max_length=22),
username: Optional[str] = Query(None, min_length=3, max_length=50),
password: Optional[str] = Query(None, min_length=6, max_length=100),
auth_key: Optional[str] = Query(None, min_length=11, max_length=22),
x_account_id: str = Header(...),
inc_user_role_list: bool = False,
@@ -232,7 +235,7 @@ async def user_authenticate(
user_data['account_id'] = account_id
user_data['username'] = username
sql_select(table_name='user', data=user_data)
# sql_select(table_name='user', data=user_data)
sql = f"""
SELECT `user`.id AS 'user_id', `user`.id_random AS 'user_id_random', `user`.password, `user`.enable, `user`.enable_from, `user`.enable_to
@@ -247,37 +250,63 @@ async def user_authenticate(
if password_hash := user_rec_result.get('password', None):
if verify_secure_hash_string(string=password, string_hash=password_hash):
log.info('The username was found, and the password matched.')
#return mk_resp(data=False, status_message='The username was found, and the password matched.', response=response)
log.info(f'The username was found, and the password matched. Log in allowed if the account is enabled. Account ID: {account_id}, Username: {username}')
# The user account will be checked if it is enabled below.
else:
log.info('The username was found, but the password did not match.')
return mk_resp(data=False, status_message='The username was found, but the password did not match.', response=response)
log.info(f'The username was found, but the password did not match. Not allowed to log in. Account ID: {account_id}, Username: {username}')
return mk_resp(data=False, status_message=f'The username was found, but the password did not match. Not allowed to log in. Account ID: {account_id}, Username: {username}', response=response)
else:
log.error('The password has was not found. This should not happen.')
return mk_resp(data=False, status_message='The password has was not found. This should not happen.', response=response)
log.warning(f'The password hash has was not found. Not allowed to log in. Account ID: {account_id}, Username: {username}')
return mk_resp(data=False, status_code=400, status_message=f'The password hash has was not found. Not allowed to log in. Account ID: {account_id}, Username: {username}', response=response)
else:
log.info(f'A user account was not found with the account and username given. Not allowed to log in. Account ID: {account_id}, Username: {username}')
return mk_resp(data=None, status_code=404, status_message=f'A user account was not found with the account and username given. Not allowed to log in. Account ID: {account_id}, Username: {username}', response=response) # Not Found
elif user_id and auth_key:
# NOTE: Since the user_id (which is required to be unique in the DB table), the account_id is not really needed.
user_data = {}
user_data['user_id_random'] = user_id # user_id_random
user_data['auth_key'] = auth_key
else: return mk_resp(data=None, status_code=404, status_message='The user account was not found', response=response)
elif auth_key:
if user_rec_result := sql_select(table_name='user', field_name='auth_key', field_value=auth_key):
# NOTE: allow_auth_key is hardcoded in the SQL query to check that it is True
sql = f"""
SELECT `user`.id AS 'user_id', `user`.id_random AS 'user_id_random', `user`.password, `user`.enable, `user`.enable_from, `user`.enable_to
FROM `user` AS `user`
WHERE `user`.id_random = :user_id_random
AND `user`.auth_key = :auth_key
AND `user`.allow_auth_key = 1
LIMIT 1
"""
if user_rec_result := sql_select(data=user_data, sql=sql):
log.info(f'The user ID and auth key combination was found. Log in allowed if the account is enabled. User ID: {user_id}')
# The user account will be checked if it is enabled below.
# NOTE: Using the id (not id_random) value to do the SQL UPDATE faster. Probably an insignificant speed difference though.
update_user_data = {}
update_user_data['id'] = user_rec_result.get('id', None)
update_user_data['id'] = user_rec_result.get('user_id', None) # Using ID, not ID Random
update_user_data['auth_key'] = None
if user_rec_update_result := sql_update(table_name='user', data=update_user_data):
log.info('The user record was updated with a NULL auth_key')
log.info(f'The user record was updated with a NULL auth_key. User ID: {user_id}')
else:
log.info('The user record was not updated with a NULL auth_key')
log.error(f'The user record was not updated with a NULL auth_key. User ID: {user_id}')
log.debug(update_user_data)
log.debug(user_rec_update_result)
user_id = user_rec_result.get('id', None) # NOTE: This us looking for "id", not "user_id"
else: return mk_resp(data=None, status_code=404, status_message='A user account with that auth key was not found', response=response)
# user_id = user_rec_result.get('id', None) # NOTE: This is looking for "id", not "user_id"
else:
log.info(f'The user ID and auth key combination was not found. Not allowed to log in. User ID: {user_id}, Auth Key: {auth_key}')
log.debug(user_data)
log.debug(sql)
return mk_resp(data=None, status_code=404, status_message=f'The user ID and auth key combination was not found. Not allowed to log in. User ID: {user_id}, Auth Key: {auth_key}', response=response) # Not Found
else:
return mk_resp(data=None, status_code=400, status_message='One more user account fields was missing or unexpected.', response=response) # Bad Request
log.setLevel(logging.DEBUG) # DEBUG, INFO, WARNING, ERROR, EXCEPTION, CRITICAL
log.debug(user_rec_result)
# Check if the SQL result is as expected and check if the user account is enabled.
if isinstance(user_rec_result, dict):
log.info(f'Checking if the user is enabled. Account ID: {account_id}, Username: {username}')
current_utc_datetime = datetime.datetime.now(datetime.timezone.utc)
log.debug(current_utc_datetime)
@@ -287,36 +316,49 @@ async def user_authenticate(
log.info('The user account is not enabled')
return mk_resp(data=False, status_message='This user account is not enabled', response=response)
#if user_enable_from := user_rec_result.get('enable_from', None).astimezone(pytz.UTC):
if user_enable_from := user_rec_result.get('enable_from', None).replace(tzinfo=datetime.timezone.utc):
log.debug(user_enable_from)
if user_enable_from <= current_utc_datetime:
log.info('Enable from datetime is valid')
else:
log.info('Enable from datetime is in the future. Please wait.')
return mk_resp(data=False, status_message='This account is not yet enabled', response=response)
if user_rec_result.get('enable_from', None):
#if user_enable_from := user_rec_result.get('enable_from', None).astimezone(pytz.UTC):
if user_enable_from := user_rec_result.get('enable_from', None).replace(tzinfo=datetime.timezone.utc):
log.debug(user_enable_from)
if user_enable_from <= current_utc_datetime:
log.info('Enable from datetime is valid')
else:
log.info(f'Enable from datetime is in the future. The user account has not been enabled yet. User Enabled From: {user_enable_from}')
return mk_resp(data=False, status_message=f'This user account is not yet enabled. User Enabled From: {user_enable_from}', response=response)
else:
log.warning('The enable_from datetime was not set. Ignoring this check.')
#if user_enable_to := user_rec_result.get('enable_to', None).astimezone(pytz.UTC):
if user_enable_to := user_rec_result.get('enable_to', None).replace(tzinfo=datetime.timezone.utc):
log.debug(user_enable_to)
if user_enable_to >= current_utc_datetime:
log.info('Enable to datetime is valid')
else:
log.info('Enable to datetime is in the past. Your user account has been disabled.')
return mk_resp(data=False, status_message='This account is not enabled because the expiratation date has passed', response=response)
if user_rec_result.get('enable_to', None):
#if user_enable_to := user_rec_result.get('enable_to', None).astimezone(pytz.UTC):
if user_enable_to := user_rec_result.get('enable_to', None).replace(tzinfo=datetime.timezone.utc):
log.debug(user_enable_to)
if user_enable_to >= current_utc_datetime:
log.info('Enable to datetime is valid')
else:
log.info(f'Enable to datetime is in the past. The user account has been disabled. User Enabled To: {user_enable_to}')
return mk_resp(data=False, status_message=f'This user account is not enabled because the expiratation date has passed. User Enabled To: {user_enable_to}', response=response)
else:
log.warning('The enable_to datetime was not set. Ignoring this check.')
user_obj = load_user_obj(
user_id=user_id,
inc_user_role_list=inc_user_role_list,
inc_contact=inc_contact,
inc_organization=inc_organization,
inc_person=inc_person
).dict(by_alias=by_alias, exclude_unset=exclude_unset)
data = user_obj
return mk_resp(data=user_obj, response=response)
# Try to load the user object
if user_obj_result := load_user_obj(
user_id = user_id,
inc_user_role_list = inc_user_role_list,
inc_contact = inc_contact,
inc_organization = inc_organization,
inc_person = inc_person,
):
log.info(f'The user account was loaded. Account ID: {account_id} Username: {username}')
user_obj_dict = user_obj_result.dict(by_alias=by_alias, exclude_unset=exclude_unset)
return mk_resp(data=user_obj_dict, response=response)
else:
log.warning(f'Something went wrong while trying to load the user account. Account ID: {account_id} Username: {username}')
log.debug(user_obj_result)
return mk_resp(data=False, status_code=500, status_message=f'Something went wrong while trying to load the user account. Account ID: {account_id} Username: {username}', response=response) # Internal Server Error
else:
log.error('SQL result was unexpected. A dict result type was expected. This should not happen.')
return mk_resp(data=False, status_code=500, response=response)
log.error(f'SQL result was unexpected. A dict result type was expected. This should not happen. Account ID: {account_id} Username: {username}')
log.debug(user_rec_result)
return mk_resp(data=False, status_code=500, status_message=f'The database lookup result was unexpected. This should not happen. Account ID: {account_id} Username: {username}', response=response)
# ### END ### API User Routers ### user_authenticate() ###