11"""
22User-related Brood operations
33"""
4+ import base64
5+ import json
46import logging
57import re
8+ import sys
69import uuid
710from datetime import datetime , timedelta
811from random import randint
912from typing import Any , Callable , Dict , List , Optional , Set , Tuple , cast
1013
11- import stripe # type: ignore
1214from passlib .context import CryptContext
1315from sendgrid import SendGridAPIClient
1416from sendgrid .helpers .mail import Mail
1517from sqlalchemy import and_ , func , or_
18+ from sqlalchemy .exc import IntegrityError
1619from sqlalchemy .orm import Query
17- from sqlalchemy .orm .base import PASSIVE_OFF
1820from sqlalchemy .orm .exc import MultipleResultsFound
1921from sqlalchemy .orm .session import Session
22+ from web3login .auth import MoonstreamRegistration , to_checksum_address , verify
23+ from web3login .exceptions import MoonstreamVerificationError
2024
2125from . import data , exceptions , subscriptions
2226from .models import (
@@ -411,43 +415,80 @@ def password_confirm(
411415def create_user (
412416 session : Session ,
413417 username : str ,
414- email : str ,
415- password : str ,
418+ email : Optional [str ] = None ,
419+ password : Optional [str ] = None ,
420+ signature : Optional [str ] = None ,
416421 autogenerated_user : bool = False ,
417422 first_name : Optional [str ] = None ,
418423 last_name : Optional [str ] = None ,
419424 application_id : Optional [uuid .UUID ] = None ,
425+ is_verify : bool = False ,
420426) -> User :
421427 """
422- Creates a new user in the given database session and
423- returns the created user object.
428+ Creates a new user in the given database session and returns
429+ the created user object.
424430
425- According with autogenerated_user var it create bugout user for Slack/Github installation or
426- normal user.
431+ According with autogenerated_user var it create bugout user for
432+ Slack/Github installation or normal user.
427433
428434 Sessions are expected to be sqlalchemy Session objects:
429435 https://docs.sqlalchemy.org/en/13/orm/session_api.html#sqlalchemy.orm.session.Session
430436 """
437+ # Check username correctness
431438 assert username != ""
432- assert email != ""
433- assert password != ""
434-
435- # Username and email should be stored as lowercase strings in the database.
436439 username = username .lower ()
437- normalized_email = normalize_email (email )
438-
439440 verify_username (username )
440- verify_password_strength (password )
441441
442- password_context = get_password_context ()
443- password_hash = password_context .hash (password )
442+ is_creation_allowed = False
443+
444+ # Regular user with password and email creation
445+ normalized_email = None
446+ password_hash = None
447+ if email is not None and password is not None :
448+ assert email != ""
449+ assert password != ""
450+
451+ # Email should be stored as lowercase strings in the database
452+ normalized_email = normalize_email (email )
453+ verify_password_strength (password )
454+ password_context = get_password_context ()
455+ password_hash = password_context .hash (password )
456+
457+ is_creation_allowed = True
458+
459+ # Web3 user with blockchain address creation
460+ web3_address = None
461+ if signature is not None :
462+ payload_json = base64 .decodebytes (signature .encode ()).decode ("utf-8" )
463+ payload = json .loads (payload_json )
464+ if not isinstance (MoonstreamRegistration , MoonstreamRegistration ):
465+ # Mypy hell
466+ raise Exception ()
467+ verified = verify (authorization_payload = payload , schema = MoonstreamRegistration )
468+ if not verified :
469+ logger .info ("Moonstream registration verification error" )
470+ raise MoonstreamVerificationError ()
471+ web3_address = payload .get ("address" )
472+ if web3_address is None :
473+ logger .error (
474+ f"Web3 address in payload could not be None for user with username: { username } "
475+ )
476+ raise Exception ()
477+ web3_address = to_checksum_address (web3_address )
478+
479+ is_creation_allowed = True
480+
481+ if not is_creation_allowed :
482+ logger .info ("Signature or email with password should be specified" )
483+ raise UserInvalidParameters ()
444484
445485 user_object = User (
446486 username = username ,
447487 email = email ,
448488 normalized_email = normalized_email ,
449489 password_hash = password_hash ,
450- verified = True if autogenerated_user else False ,
490+ web3_address = web3_address ,
491+ verified = True if autogenerated_user else is_verify ,
451492 autogenerated = True if autogenerated_user else False ,
452493 first_name = first_name ,
453494 last_name = last_name ,
@@ -460,9 +501,16 @@ def create_user(
460501 session .add (user_object )
461502 session .add (user_group_limit )
462503 session .commit ()
463- except Exception as e :
464- logger .error (e )
465- raise UserAlreadyExists ("This user already exists" )
504+ except IntegrityError :
505+ logger .info (
506+ f"User already exists with username: { username } , email: { email } , address: { web3_address if signature is not None else None } "
507+ )
508+ raise UserAlreadyExists ()
509+ except Exception :
510+ logger .error (
511+ f"Unable to add user with username: { username } , email: { email } , address: { web3_address if signature is not None else None } "
512+ )
513+ raise Exception ()
466514
467515 return user_object
468516
@@ -511,7 +559,7 @@ def get_user(
511559 user_id : Optional [uuid .UUID ] = None ,
512560 application_id : Optional [uuid .UUID ] = None ,
513561 web3_address : Optional [uuid .UUID ] = None ,
514- ) -> data . UserResponse :
562+ ) -> User :
515563 """
516564 Get a user by username, email, user_id or web3_address.
517565 If more than one of those fields is provided, will look for a user having ALL the given parameters.
@@ -635,9 +683,7 @@ def get_user_with_groups(
635683 )
636684
637685
638- def get_current_user_by_token (
639- session : Session , token : uuid .UUID
640- ) -> Tuple [bool , data .UserResponse ]:
686+ def get_current_user_by_token (session : Session , token : uuid .UUID ) -> Tuple [bool , User ]:
641687 """
642688 Get user by its active token for authentication middleware.
643689 """
@@ -656,19 +702,7 @@ def get_current_user_by_token(
656702 is_token_active = objects [0 ][0 ]
657703 user = objects [0 ][1 ]
658704
659- return is_token_active , data .UserResponse (
660- user_id = user .id ,
661- username = user .username ,
662- first_name = user .first_name ,
663- last_name = user .last_name ,
664- email = user .email ,
665- normalized_email = user .normalized_email ,
666- verified = user .verified ,
667- created_at = user .created_at ,
668- updated_at = user .updated_at ,
669- autogenerated = user .autogenerated ,
670- application_id = user .application_id ,
671- )
705+ return is_token_active , user
672706
673707
674708def get_current_user_with_groups_by_token (
@@ -859,8 +893,8 @@ def send_welcome_email(user: User, application_id: Optional[uuid.UUID] = None) -
859893 logger .info (
860894 f"Welcome email successfully submitted to Sendgrid for user with id={ user_id } "
861895 )
862- except Exception as e :
863- logger .error (f"Error sending welcome email { e } " )
896+ except Exception :
897+ logger .error (f"Error sending welcome email to { user . email } " )
864898 pass
865899
866900
0 commit comments