Working on user login and related updates
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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() ###
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user