def change_user_email(token: str) -> None: try: jwt_payload = decode_jwt_token(token) except ( ExpiredSignatureError, InvalidSignatureError, DecodeError, InvalidTokenError, ) as error: raise InvalidTokenError() from error if not {"exp", "new_email", "current_email"} <= set(jwt_payload): raise InvalidTokenError() new_email = sanitize_email(jwt_payload["new_email"]) if find_user_by_email(new_email): return current_user = find_user_by_email(jwt_payload["current_email"]) if not current_user: return current_user.email = new_email sessions = UserSession.query.filter_by(userId=current_user.id) repository.delete(*sessions) repository.save(current_user) logger.info("User has changed their email", extra={"user": current_user.id}) return
def create_or_update_users(rows: Iterable[dict]) -> list[User]: # The purpose of this function is to recreate test users on # staging after the staging database is reset. It's not meant to # be used anywhere else, and certainly not on production. if settings.IS_PROD: raise ValueError( "This function is not supposed to be run on production") users = [] for row in rows: user = find_user_by_email(row["Mail"]) birthdate = datetime.strptime(row["Date de naissance"], "%Y-%m-%d") if user: user.dateOfBirth = birthdate user.setPassword(settings.STAGING_TEST_USER_PASSWORD) else: user = users_api.create_account( email=sanitize_email(row["Mail"]), password=settings.STAGING_TEST_USER_PASSWORD, birthdate=birthdate, is_email_validated=True, send_activation_mail=False, remote_updates=False, ) deposit = payments_api.create_deposit(user, "import_users (csv)") repository.save(deposit) user.add_beneficiary_role() user.lastName = row["Nom"] user.firstName = row["Prénom"] user.publicName = f"{user.firstName} {user.lastName}" user.phoneNumber = row["Téléphone"] user.departementCode = row["Département"] user.postalCode = row["Code postal"] repository.save(user) users.append(user) logger.info("Created or updated user=%s from CSV import", user.id) admin = find_user_by_email("*****@*****.**") if not admin: admin = users_api.create_account( email="*****@*****.**", password=settings.STAGING_TEST_USER_PASSWORD, birthdate=datetime(1946, 12, 24), is_email_validated=True, send_activation_mail=False, remote_updates=False, ) admin.setPassword(settings.STAGING_TEST_USER_PASSWORD) admin.remove_beneficiary_role() admin.add_admin_role() admin.firstName = "Jeanne" admin.lastName = "Admin" admin.publicName = f"{user.firstName} {user.lastName}" repository.save(admin) logger.info("Created or updated admin user=%s", admin.id) return users
def post_for_password_token(body: ResetPasswordBodyModel) -> None: check_webapp_recaptcha_token( body.token, original_action="resetPassword", minimal_score=settings.RECAPTCHA_RESET_PASSWORD_MINIMAL_SCORE, ) user = find_user_by_email(body.email) if not user or not user.isActive: # Here we also return a 204 to prevent attacker from discovering which email exists in db return generate_reset_token(user) repository.save(user) is_not_pro_user = user.isBeneficiary if is_not_pro_user: try: send_reset_password_email_to_user(user) except MailServiceException as mail_service_exception: app.logger.exception( "[send_reset_password_email_to_user] " "Mail service failure", mail_service_exception) else: try: send_reset_password_email_to_pro(user) except MailServiceException as mail_service_exception: app.logger.exception( "[send_reset_password_email_to_pro] " "Mail service failure", mail_service_exception)
def create_account( email: str, password: str, birthdate: date, has_allowed_recommendations: bool = False, is_email_validated: bool = False, send_activation_mail: bool = True, ) -> User: if find_user_by_email(email): raise exceptions.UserAlreadyExistsException() user = User( email=format_email(email), dateOfBirth=datetime.combine(birthdate, datetime.min.time()), isEmailValidated=is_email_validated, departementCode="007", publicName=VOID_PUBLIC_NAME, # Required because model validation requires 3+ chars hasSeenTutorials=False, firstName=VOID_FIRST_NAME, hasAllowedRecommendations=has_allowed_recommendations, ) age = user.calculate_age() if not age or age < constants.ACCOUNT_CREATION_MINIMUM_AGE: raise exceptions.UnderAgeUserException() user.setPassword(password) repository.save(user) if not is_email_validated and send_activation_mail: request_email_confirmation(user) return user
def unsubscribe_user(): source_ip = ipaddress.IPv4Address( request.headers.get("X-Forwarded-For", "0.0.0.0")) if source_ip not in SENDINBLUE_IP_RANGE and settings.IS_DEV is False: raise ApiErrors( {"IP": "Source IP address is not whitelisted"}, status_code=401, ) user_email = request.json.get("email") if not user_email: raise ApiErrors( {"email": "Email missing in request payload"}, status_code=400, ) user_to_unsubscribe = find_user_by_email(user_email) if user_to_unsubscribe is None: raise ApiErrors({"User": "******" % user_email}, status_code=400) user_to_unsubscribe.notificationSubscriptions = ({ **user_to_unsubscribe.notificationSubscriptions, "marketing_email": False } if user_to_unsubscribe.notificationSubscriptions is not None else { "marketing_email": False }) repository.save(user_to_unsubscribe)
def post_for_password_token(body: ResetPasswordBodyModel) -> None: try: check_webapp_recaptcha_token( body.token, original_action="resetPassword", minimal_score=settings.RECAPTCHA_RESET_PASSWORD_MINIMAL_SCORE, ) except ReCaptchaException: raise ApiErrors({"token": "The given token is invalid"}) user = find_user_by_email(body.email) if not user or not user.isActive: # Here we also return a 204 to prevent attacker from discovering which email exists in db return if user.is_beneficiary: send_email = user_emails.send_reset_password_email_to_user else: send_email = send_reset_password_email_to_pro try: send_email(user) except MailServiceException as mail_service_exception: logger.exception("Could not send reset password email", extra={ "user": user.id, "exc": str(mail_service_exception) })
def refresh() -> authentication.RefreshResponse: email = get_jwt_identity() user = find_user_by_email(email) if not user: raise ApiErrors({"email": "unknown"}, status_code=401) users_api.update_last_connection_date(user) return authentication.RefreshResponse(access_token=users_api.create_user_access_token(user))
def execute(self, application_id: int) -> None: beneficiary_pre_subscription = get_application_by_id(application_id) user = find_user_by_email(beneficiary_pre_subscription.email) try: validate(beneficiary_pre_subscription, preexisting_account=user) except CantRegisterBeneficiary as cant_register_beneficiary_exception: self.beneficiary_repository.reject( beneficiary_pre_subscription, detail=str(cant_register_beneficiary_exception)) send_rejection_email_to_beneficiary_pre_subscription( beneficiary_pre_subscription=beneficiary_pre_subscription, beneficiary_is_eligible=isinstance( cant_register_beneficiary_exception, BeneficiaryIsADuplicate), send_email=send_raw_email, ) else: if not user: beneficiary = self.beneficiary_repository.save( beneficiary_pre_subscription) else: beneficiary = users_api.activate_beneficiary( user, beneficiary_pre_subscription.deposit_source) users_api.attach_beneficiary_import_details( user, beneficiary_pre_subscription) send_activation_email(user=beneficiary, send_email=send_raw_email)
def execute(self, application_id: int) -> None: beneficiary_pre_subscription = get_application_by_id(application_id) user = find_user_by_email(beneficiary_pre_subscription.email) try: validate(beneficiary_pre_subscription, preexisting_account=user) except CantRegisterBeneficiary as cant_register_beneficiary_exception: self.beneficiary_repository.reject( beneficiary_pre_subscription, detail=str(cant_register_beneficiary_exception)) send_rejection_email_to_beneficiary_pre_subscription( beneficiary_pre_subscription=beneficiary_pre_subscription, beneficiary_is_eligible=isinstance( cant_register_beneficiary_exception, BeneficiaryIsADuplicate), ) else: beneficiary = self.beneficiary_repository.save( beneficiary_pre_subscription, user=user) if user is None: send_activation_email(user=beneficiary) else: send_accepted_as_beneficiary_email(user=beneficiary)
def import_new_offerer_from_csv(row: dict) -> None: # We can't process a row without a postal code if not row["Postal Code"] and not row["code_postal"]: logger.warning("Unable to import this line %s - %s", row[""], row["Company ID"]) return existing_pro = find_user_by_email(row["Email"]) if existing_pro: pro = existing_pro else: pro_model = create_user_model_from_csv(row) pro = create_pro_user(pro_model) pro.validationToken = None create_reset_password_token(pro, token_life_time=timedelta(days=90)) existing_offerer = find_offerer_by_siren(row["SIREN"]) if existing_offerer: offerer = existing_offerer else: offerer = create_offerer_from_csv(row) create_digital_venue(offerer) offerer.grant_access(pro) repository.save(pro) if row["SIRET"]: venue = create_venue_from_csv(row, offerer) try: repository.save(venue) except ApiErrors: logger.warning("Unable to save this venue %s - %s", row[""], row["Company ID"]) else: logger.warning("Unable to import this venue %s - %s", row[""], row["Company ID"])
def create_account( email: str, password: str, birthdate: date, has_allowed_recommendations: bool = False, is_email_validated: bool = False, send_activation_mail: bool = True, ) -> User: if find_user_by_email(email): raise exceptions.UserAlreadyExistsException() user = User( email=format_email(email), dateOfBirth=birthdate, isEmailValidated=is_email_validated, departementCode="007", publicName=" ", # Required because model validation requires 3+ chars hasSeenTutorials=False, firstName="", hasAllowedRecommendations=has_allowed_recommendations, ) user.setPassword(password) repository.save(user) if not is_email_validated and send_activation_mail: request_email_confirmation(user) return user
def retrieve_authenticated_user(*args, **kwargs): # type: ignore email = get_jwt_identity() user = find_user_by_email(email) if user is None or not user.isActive: logger.error("Authenticated user with email %s not found or inactive", email) raise ForbiddenError({"email": ["Utilisateur introuvable"]}) return route_function(user, *args, **kwargs)
def request_password_reset(body: RequestPasswordResetRequest) -> None: user = find_user_by_email(body.email) try: users_api.request_password_reset(user) except users_exceptions.EmailNotSent: raise ApiErrors( {"email": ["L'email n'a pas pu être envoyé"]}, status_code=400, )
def send_user_emails_for_email_change(user: User, new_email: str) -> None: user_with_new_email = find_user_by_email(new_email) if user_with_new_email: return send_information_email_change_email(user) link_for_email_change = _build_link_for_email_change(user.email, new_email) send_confirmation_email_change_email(user, new_email, link_for_email_change) return
def post_change_password(): json = request.get_json() validate_change_password_request(json) user = find_user_by_email(current_user.email) new_password = json["newPassword"] new_confirmation_password = json["newConfirmationPassword"] old_password = json["oldPassword"] check_password_validity(new_password, new_confirmation_password, old_password, user) user.setPassword(new_password) repository.save(user) return "", 204
def get_user_profile() -> serializers.UserProfileResponse: identifier = get_jwt_identity() user = find_user_by_email(identifier) if user is None: app.logger.error("Authenticated user with email %s not found", identifier) raise ApiErrors({"email": ["Utilisateur introuvable"]}) return serializers.UserProfileResponse( first_name=user.firstName if user.firstName != VOID_FIRST_NAME else None, email=user.email, is_beneficiary=user.isBeneficiary, )
def request_password_reset(body: RequestPasswordResetRequest) -> None: if feature_queries.is_active(FeatureToggle.ENABLE_NATIVE_APP_RECAPTCHA): try: api_recaptcha.check_native_app_recaptcha_token(body.token) except api_recaptcha.ReCaptchaException: raise ApiErrors({"token": "The given token is not invalid"}) user = find_user_by_email(body.email) try: users_api.request_password_reset(user) except users_exceptions.EmailNotSent: raise ApiErrors( {"email": ["L'email n'a pas pu être envoyé"]}, status_code=400, )
def verify_identity_document_informations(image_storage_path: str) -> None: email, image = _get_identity_document_informations(image_storage_path) valid, code = ask_for_identity_document_verification(email, image) if not valid: old_user_emails.send_document_verification_error_email(email, code) fraud_api.handle_document_validation_error(email, code) user = find_user_by_email(email) if user: message_function = { "invalid-age": subscription_messages.on_idcheck_invalid_age, "invalid-document": subscription_messages.on_idcheck_invalid_document, "invalid-document-date": subscription_messages.on_idcheck_invalid_document_date, "unread-document": subscription_messages.on_id_check_unread_document, "unread-mrz-document": subscription_messages.on_idcheck_unread_mrz, }.get(code, lambda x: None) message_function(user) delete_object(image_storage_path)
def create_account(body: serializers.AccountRequest) -> None: if settings.NATIVE_ACCOUNT_CREATION_REQUIRES_RECAPTCHA: try: check_recaptcha_token_is_valid(body.token, "submit", settings.RECAPTCHA_RESET_PASSWORD_MINIMAL_SCORE) except ReCaptchaException: raise ApiErrors({"token": "The given token is not invalid"}) try: api.create_account( email=body.email, password=body.password, birthdate=body.birthdate, has_allowed_recommendations=body.has_allowed_recommendations, is_email_validated=False, ) except UserAlreadyExistsException: user = find_user_by_email(body.email) api.request_password_reset(user)
def resend_email_validation( body: serializers.ResendEmailValidationRequest) -> None: user = find_user_by_email(body.email) if not user or not user.isActive: return try: if user.isEmailValidated: api.request_password_reset(user) else: api.request_email_confirmation(user) except exceptions.EmailNotSent: raise ApiErrors( { "code": "EMAIL_NOT_SENT", "general": ["L'email n'a pas pu être envoyé"] }, status_code=400, )
def create_industrial_offer_validation_config() -> None: logger.info("create_industrial_offer_validation_config") super_admin = find_user_by_email("*****@*****.**") previous_config_yaml = """ minimum_score: 0.6 rules: - name: "Check offer name" factor: 0 conditions: - model: "Offer" attribute: "name" condition: operator: "not in" comparated: - "REJECTED" - "PENDING" - "DRAFT" - name: "Check max price" factor: 0.7 conditions: - model: "Offer" attribute: "max_price" condition: operator: ">" comparated: 100 """ config_yaml = """ minimum_score: 0.6 rules: - name: "Check offer name" factor: 0 conditions: - model: "Offer" attribute: "name" condition: operator: "contains" comparated: - "REJECTED" - "PENDING" """ import_offer_validation_config(previous_config_yaml, super_admin) import_offer_validation_config(config_yaml, super_admin) logger.info("created 2 offer validation config")
def retrieve_authenticated_user(*args, **kwargs): # type: ignore email = get_jwt_identity() user = find_user_by_email(email) if user is None or not user.isActive: logger.info( "Authenticated user with email %s not found or inactive", email) raise ForbiddenError({"email": ["Utilisateur introuvable"]}) # push the user to the current context - similar to flask-login ctx = _request_ctx_stack.top ctx.user = user # the user is set in sentry in before_request, way before we do the # token auth so it needs to be also set here. sentry_sdk.set_user({"id": user.id}) sentry_sdk.set_tag("device.id", request.headers.get("device-id", None)) return route_function(user, *args, **kwargs)
def create_account(body: serializers.AccountRequest) -> None: if feature_queries.is_active(FeatureToggle.ENABLE_NATIVE_APP_RECAPTCHA): try: api_recaptcha.check_native_app_recaptcha_token(body.token) except api_recaptcha.ReCaptchaException: raise ApiErrors({"token": "The given token is not invalid"}) try: api.create_account( email=body.email, password=body.password, birthdate=body.birthdate, has_allowed_recommendations=body.has_allowed_recommendations, is_email_validated=False, ) except exceptions.UserAlreadyExistsException: user = find_user_by_email(body.email) api.request_password_reset(user) except exceptions.UnderAgeUserException: raise ApiErrors({"dateOfBirth": "The birthdate is invalid"})
def create_account( email: str, password: str, birthdate: date, marketing_email_subscription: bool = False, is_email_validated: bool = False, send_activation_mail: bool = True, remote_updates: bool = True, postal_code: str = None, phone_number: str = None, apps_flyer_user_id: str = None, apps_flyer_platform: str = None, ) -> User: email = sanitize_email(email) if find_user_by_email(email): raise exceptions.UserAlreadyExistsException() departement_code = PostalCode(postal_code).get_departement_code() if postal_code else None user = User( email=email, dateOfBirth=datetime.combine(birthdate, datetime.min.time()), isEmailValidated=is_email_validated, publicName=VOID_PUBLIC_NAME, # Required because model validation requires 3+ chars hasSeenTutorials=False, notificationSubscriptions=asdict(NotificationSubscriptions(marketing_email=marketing_email_subscription)), postalCode=postal_code, departementCode=departement_code, phoneNumber=phone_number, lastConnectionDate=datetime.now(), ) if not user.age or user.age < constants.ACCOUNT_CREATION_MINIMUM_AGE: raise exceptions.UnderAgeUserException() return initialize_account( user, password, apps_flyer_user_id, apps_flyer_platform, send_activation_mail, remote_updates )
def create_account(body: serializers.AccountRequest) -> None: if FeatureToggle.ENABLE_NATIVE_APP_RECAPTCHA.is_active(): try: api_recaptcha.check_native_app_recaptcha_token(body.token) except api_recaptcha.ReCaptchaException: raise ApiErrors({"token": "The given token is not invalid"}) try: api.create_account( email=body.email, password=body.password, birthdate=body.birthdate, marketing_email_subscription=body.marketing_email_subscription, is_email_validated=False, postal_code=body.postal_code, apps_flyer_user_id=body.apps_flyer_user_id, apps_flyer_platform=body.apps_flyer_platform, ) except exceptions.UserAlreadyExistsException: user = find_user_by_email(body.email) if not user.isEmailValidated: try: api.initialize_account( user, body.password, apps_flyer_user_id=body.apps_flyer_user_id, apps_flyer_platform=body.apps_flyer_platform, ) except exceptions.EmailNotSent: raise ApiErrors({"email": ["L'email n'a pas pu être envoyé"]}) else: try: api.request_password_reset(user) except exceptions.EmailNotSent: raise ApiErrors({"email": ["L'email n'a pas pu être envoyé"]}) except exceptions.UnderAgeUserException: raise ApiErrors({"dateOfBirth": "The birthdate is invalid"})
def process_parsing_error(exception: DMSParsingError, procedure_id: int, application_id: int) -> None: logger.info( "[BATCH][REMOTE IMPORT BENEFICIARIES] Invalid values (%r) detected in Application %s in procedure %s", exception.errors, application_id, procedure_id, ) user = find_user_by_email(exception.user_email) user_emails.send_dms_wrong_values_emails( exception.user_email, exception.errors.get("postal_code"), exception.errors.get("id_piece_number") ) if user: subscription_messages.on_dms_application_parsing_errors(user, list(exception.errors.keys())) errors = ",".join([f"'{key}' ({value})" for key, value in sorted(exception.errors.items())]) error_detail = f"Erreur dans les données soumises dans le dossier DMS : {errors}" # keep a compatibility with BeneficiaryImport table save_beneficiary_import_with_status( ImportStatus.ERROR, application_id, source=BeneficiaryImportSources.demarches_simplifiees, source_id=procedure_id, detail=error_detail, user=user, )
def _check_email_is_not_taken(beneficiary_pre_subscription: BeneficiaryPreSubscription) -> None: email = beneficiary_pre_subscription.email if find_user_by_email(email): raise BeneficiaryIsADuplicate(f"Email {email} is already taken.")
def get_profile(): user = find_user_by_email(current_user.email) return jsonify(as_dict(user, includes=USER_INCLUDES)), 200
def refresh() -> authentication.RefreshResponse: email = get_jwt_identity() user = find_user_by_email(email) return authentication.RefreshResponse( access_token=create_user_access_token(user))
def execute( self, application_id: int, run_fraud_detection: bool = True, ignore_id_piece_number_field: bool = False, fraud_detection_ko: bool = False, ) -> None: try: jouve_content = jouve_backend.get_application_content( application_id, ignore_id_piece_number_field=ignore_id_piece_number_field) beneficiary_pre_subscription = jouve_backend.get_subscription_from_content( jouve_content) except jouve_backend.ApiJouveException as api_jouve_exception: logger.error( api_jouve_exception.message, extra={ "route": api_jouve_exception.route, "statusCode": api_jouve_exception.status_code, "applicationId": application_id, }, ) return except jouve_backend.JouveContentValidationError as exc: logger.error( "Validation error when parsing Jouve content: %s", exc.message, extra={ "application_id": application_id, "validation_errors": exc.errors }, ) return preexisting_account = find_user_by_email( beneficiary_pre_subscription.email) if not preexisting_account: save_beneficiary_import_with_status( ImportStatus.ERROR, application_id, source=BeneficiaryImportSources.demarches_simplifiees, source_id=jouve_backend.DEFAULT_JOUVE_SOURCE_ID, detail= f"Aucun utilisateur trouvé pour l'email {beneficiary_pre_subscription.email}", ) return try: on_jouve_result(preexisting_account, jouve_content) except Exception as exc: # pylint: disable=broad-except logger.exception("Error on jouve result: %s", exc) try: validate( beneficiary_pre_subscription, preexisting_account=preexisting_account, ignore_id_piece_number_field=ignore_id_piece_number_field, ) if fraud_detection_ko: raise FraudDetected( "Forced by 'fraud_detection_ko' script argument") if run_fraud_detection: validate_fraud(beneficiary_pre_subscription) except SuspiciousFraudDetected: send_fraud_suspicion_email(beneficiary_pre_subscription) subscription_messages.create_message_jouve_manual_review( preexisting_account, application_id=application_id) except FraudDetected as cant_register_beneficiary_exception: # detail column cannot contain more than 255 characters detail = f"Fraud controls triggered: {cant_register_beneficiary_exception}"[: 255] self.beneficiary_repository.reject( beneficiary_pre_subscription, detail=detail, user=preexisting_account, ) except BeneficiaryIsADuplicate as exception: exception_reason = str(exception) logger.info( "User is a duplicate : cannot register user from application", extra={ "applicationId": application_id, "reason": exception_reason, }, ) subscription_messages.on_duplicate_user(preexisting_account) self.beneficiary_repository.reject(beneficiary_pre_subscription, detail=exception_reason, user=preexisting_account) old_user_emails.send_rejection_email_to_beneficiary_pre_subscription( beneficiary_pre_subscription=beneficiary_pre_subscription, beneficiary_is_eligible=True) except SubscriptionJourneyOnHold as exc: logger.warning("User subscription is on hold", extra={ "applicationId": application_id, "reason": str(exc) }) except CantRegisterBeneficiary as cant_register_beneficiary_exception: exception_reason = str(cant_register_beneficiary_exception) logger.warning( "Couldn't register user from application", extra={ "applicationId": application_id, "reason": exception_reason, }, ) self.beneficiary_repository.reject(beneficiary_pre_subscription, detail=exception_reason, user=preexisting_account) old_user_emails.send_rejection_email_to_beneficiary_pre_subscription( beneficiary_pre_subscription=beneficiary_pre_subscription, beneficiary_is_eligible=False) else: user = self.beneficiary_repository.save( beneficiary_pre_subscription, user=preexisting_account) logger.info("User registered from application", extra={ "applicationId": application_id, "userId": user.id })