diff --git a/app/main.py b/app/main.py index 0ea4887..853b339 100644 --- a/app/main.py +++ b/app/main.py @@ -18,7 +18,7 @@ from app.lib_general import log, logging from app.log import log # Import the routers here first: -from app.routers import api_crud, api, importing, account, address, archive, archive_content, contact, cont_edu_cert, cont_edu_cert_person, event, event_badge, event_badge_template, event_exhibit, event_file, event_importing, event_location, event_person, event_person_detail, event_person_tracking, event_presentation, event_presenter, event_registration, event_session, flask_cfg, hosted_file, journal, journal_entry, log_client_viewing, lookup, membership_cfg, membership_group, membership_group_person, membership_person, membership_person_profile, membership_type, membership_type_person, order, order_line, order_cart, organization, page, person, post, post_comment, product, site, site_domain, user, user_person, websockets#, e_impexium +from app.routers import api_crud, api, importing, account, address, archive, archive_content, contact, cont_edu_cert, cont_edu_cert_person, event, event_badge, event_badge_template, event_exhibit, event_file, event_importing, event_location, event_person, event_person_detail, event_person_tracking, event_presentation, event_presenter, event_registration, event_session, flask_cfg, hosted_file, journal, journal_entry, log_client_viewing, lookup, membership_cfg, membership_group, membership_group_person, membership_person, membership_person_profile, membership_type, membership_type_person, order, order_line, order_cart, organization, page, person, person_user, post, post_comment, product, site, site_domain, user, websockets#, e_impexium from app.db_sql import db @@ -264,6 +264,11 @@ app.include_router( # prefix='/person', tags=['Person'], ) +app.include_router( + person_user.router, + prefix='/person_user', + tags=['Person User'], +) app.include_router( post.router, # prefix='/post', @@ -294,11 +299,6 @@ app.include_router( # prefix='/user', tags=['User'], ) -app.include_router( - user_person.router, - prefix='/user_person', - tags=['User Person'], -) app.include_router( websockets.router, #prefix='/websocket', diff --git a/app/methods/person_methods.py b/app/methods/person_methods.py index ece186d..6262c88 100644 --- a/app/methods/person_methods.py +++ b/app/methods/person_methods.py @@ -5,7 +5,7 @@ from typing import Dict, List, Optional, Set, Union from pydantic import BaseModel, EmailStr, Field, PrivateAttr, ValidationError, validator from app.db_sql import redis_lookup_id_random, sql_insert, sql_insert_or_update, sql_select, sql_update -from app.lib_general import log, logging +from app.lib_general import log, logging, send_email # from app.methods.address_methods import load_address_obj from app.methods.contact_methods import create_contact_obj, create_update_contact_obj, create_update_contact_obj_v4, load_contact_obj, update_contact_obj @@ -547,8 +547,8 @@ def create_update_person_obj_v4b( # fail_any = fail_any, ): if isinstance(create_user_obj_result, int): - log.info(f'User created. User ID: {user_id}') - log.debug(update_user_obj_result) + log.info(f'User created. User ID: {create_user_obj_result}') + log.debug(create_user_obj_result) user_id = create_user_obj_result # Need to update the person with the new user_id update_person_obj = True @@ -574,7 +574,7 @@ def create_update_person_obj_v4b( else: log.error(f'Person not updated with current User ID. Person ID: {person_id} User ID: {user_id}') if fail_any: return False - log.debug(person_dict_up_result) + log.debug(person_data_up_result) person_outline['user_id'] = user_id else: @@ -1412,3 +1412,83 @@ def update_person_obj( # else: # return False # # ### END ### API Person Methods ### create_update_person_obj_v4b() ### + + +# ### BEGIN ### Person Methods ### email_person_create_url() ### +# This emails the actual one time use account creation URL for a person. +# Updated 2021-12-03 +def email_person_create_url( + account_id: int|str, + person_id: int|str, + root_url: str, + ): + log.setLevel(logging.DEBUG) # DEBUG, INFO, WARNING, ERROR, EXCEPTION, CRITICAL + log.debug(locals()) + + if account_id := redis_lookup_id_random(record_id_random=account_id, table_name='account'): pass + else: return False + + if person_id := redis_lookup_id_random(record_id_random=person_id, table_name='person'): pass + else: return False + + if person_obj := load_person_obj( + person_id = person_id, + ): + log.info('Person object loaded') + else: return False + log.debug(person_obj) + + from app.methods.account_cfg_methods import load_account_cfg_obj + if account_cfg := load_account_cfg_obj( + account_id = account_id, + ): + log.info('Account config loaded') + else: return False + log.debug(account_cfg) + + person_id_random = person_obj.id_random # NOTE: Not person_id_random because of alias + + from_email = account_cfg.default_no_reply_email + from_name = account_cfg.default_no_reply_name + + to_name = person_obj.display_name + to_email = person_obj.email + + bcc_email = account_cfg.confirm_email + bcc_name = account_cfg.confirm_name + + help_tech_email = account_cfg.help_tech_email + help_tech_name = account_cfg.help_tech_name + + account_short_name = account_cfg.account_short_name + + person_create_url = f'{root_url}person/{person_id_random}/create' + # person_create_auth_key_url = f'{root_url}?user_id={user_id_random}&auth_key={new_auth_key}' + + subject = f'{account_short_name}: One Time Use Create Account Link' + # subject = f'{account_short_name}: One Time Use Create Account Link ({new_auth_key})' + + body_html = f""" +
{to_name},
+ +If you did not request this account creation link, please delete this email. It is suggested that you delete this email after the account creation link has been used or if a new link has been requested.
+ +The link below can only be used once. If you would like try again using this method, you must request a new account creation link. If you request multiple links, only the newest link will work.
+ +Click to Finish Account Creation With One Time Use Link
+ +Or copy and paste the link:
+ {person_create_url}
If you have questions about this email or trouble with this one time use link, you can email {help_tech_name} ({help_tech_email}).
+ +Thank you!
+ """ + + if send_email(from_email=from_email, from_name=from_name, to_email=to_email, to_name=to_name, bcc_email=bcc_email, bcc_name=bcc_name, subject=subject, body_text=None, body_html=body_html): + log.info(f'An email with a one time use sign in link was sent to {to_email}.') + return True + else: + log.info(f'An email with a one time use sign in link was not sent to {to_email}.') + return False +# ### END ### User ### email_user_auth_key_url() ### diff --git a/app/routers/person.py b/app/routers/person.py index 5448c80..6a9715c 100644 --- a/app/routers/person.py +++ b/app/routers/person.py @@ -10,7 +10,7 @@ from app.db_sql import sql_insert, sql_update, sql_insert_or_update, sql_select, from app.routers.api_crud import delete_obj_template, get_obj_template, get_obj_li_template, patch_obj_template, post_obj_template # from app.methods.membership_person_methods import load_membership_person_obj -from app.methods.person_methods import create_update_person_obj_v4b, get_person_rec_list, get_person_rec_w_external_id, load_person_obj, update_person_obj +from app.methods.person_methods import create_update_person_obj_v4b, email_person_create_url, get_person_rec_list, get_person_rec_w_external_id, load_person_obj, update_person_obj from app.models.person_models import Person_Base from app.models.response_models import Resp_Body_Base, mk_resp @@ -435,6 +435,43 @@ async def lookup_email( # ### END ### API Person ### lookup_email() ### +# ### BEGIN ### API Person ### email_create_url() ### +# Updated 2021-12-03 +# @router.get('/person/email_create_url', response_model=Resp_Body_Base) +@router.get('/person/{person_id}/email_create_url', response_model=Resp_Body_Base) +async def email_create_url( + person_id: Optional[str] = Query(None, min_length=11, max_length=22), + root_url: Optional[str] = Query(None, min_length=10, max_length=100), # Absolute min = 7 + x_account_id: Optional[str] = Header(..., ), + return_obj: bool = False, + by_alias: bool = True, + exclude_unset: bool = True, + response: Response = Response, + ): + log.setLevel(logging.DEBUG) # DEBUG, INFO, WARNING, ERROR, EXCEPTION, CRITICAL + log.debug(locals()) + + if account_id := redis_lookup_id_random(record_id_random=x_account_id, table_name='account'): pass + else: return mk_resp(data=False, status_code=404, response=response) # Not Found + + if person_id := redis_lookup_id_random(record_id_random=person_id, table_name='person'): pass + else: return mk_resp(data=False, status_code=404, response=response) # Not Found + + if result := email_person_create_url( + account_id = account_id, + person_id = person_id, + root_url = root_url, + ): + log.info('Email with create URL was sent.') + return mk_resp(data=True, response=response) + else: + log.warning('Email with create URL was not sent.') + return mk_resp(data=False, status_code=500, response=response) +# ### END ### API Person ### email_create_url() ### + + + + # ### BEGIN ### API Person ### get_person_obj() ### # Working well as of 2021-06-25. Using as a template for other routes. @router.get('/person/{person_id}', response_model=Resp_Body_Base) diff --git a/app/routers/user_person.py b/app/routers/person_user.py similarity index 100% rename from app/routers/user_person.py rename to app/routers/person_user.py