parameters: (cls, new_user)
return_statement: return core
docstring: Create a new user.
  
          @param: new_user is a parameters/user.py.NewUser object
          # 1.check username regex
          # 2.check username is not in reserved_name table
          # 3.check recovery email validity (TODO : check if email is not within
          #   the current Caliopen's instance)
          # 4.check username availability
          # 5.add username to user cassa user_name table (to block the
          #   availability)
          # 6.check password strength (and regex?)
          # then
          #      create user and linked contact
docstring_summary: Create a new user.
docstring_tokens: Create a new user .
function: def create(cls, new_user):
          """Create a new user.
  
          @param: new_user is a parameters/user.py.NewUser object
          # 1.check username regex
          # 2.check username is not in reserved_name table
          # 3.check recovery email validity (TODO : check if email is not within
          #   the current Caliopen's instance)
          # 4.check username availability
          # 5.add username to user cassa user_name table (to block the
          #   availability)
          # 6.check password strength (and regex?)
          # then
          #      create user and linked contact
          """
  
          def rollback_username_storage(username):
              UserName.get(username).delete()
  
          # 0. check for user email white list and max number of users
          cls._check_whitelistes(new_user)
          cls._check_max_users()
  
          # 1.
          try:
              validators.is_valid_username(new_user.name)
          except SyntaxError:
              raise ValueError("Malformed username")
  
          # 2.
          try:
              ReservedName.get(new_user.name)
              raise ValueError('Reserved user name')
          except NotFound:
              pass
  
          user_id = uuid.uuid4()
          # 3.
          if not new_user.recovery_email:
              raise ValueError("Missing recovery email")
  
          try:
              cls.validate_recovery_email(new_user.recovery_email)
          except Exception as exc:
              log.info("recovery email failed validation : {}".format(exc))
              raise ValueError(exc)
  
          # 4. & 5.
          if User.is_username_available(new_user.name.lower()):
              # save username immediately to prevent concurrent creation
              UserName.create(name=new_user.name.lower(), user_id=user_id)
              # NB : need to rollback this username creation if the below
              #      User creation failed for any reason
          else:
              raise ValueError("Username already exist")
  
          # 6.
          try:
              user_inputs = [new_user.name.encode("utf-8"),
                             new_user.recovery_email.encode("utf-8")]
              # TODO: add contact inputs if any
              password_strength = zxcvbn(new_user.password,
                                         user_inputs=user_inputs)
              privacy_features = {"password_strength":
                                      str(password_strength["score"])}
              passwd = new_user.password.encode('utf-8')
              new_user.password = bcrypt.hashpw(passwd, bcrypt.gensalt())
          except Exception as exc:
              log.exception(exc)
              rollback_username_storage(new_user.name)
              raise exc
  
          try:
              new_user.validate()  # schematic model validation
          except Exception as exc:
              rollback_username_storage(new_user.name)
              log.info("schematics validation error: {}".format(exc))
              raise ValueError("new user malformed")
  
          try:
              recovery = new_user.recovery_email
              if hasattr(new_user, "contact"):
                  family_name = new_user.contact.family_name
                  given_name = new_user.contact.given_name
              else:
                  family_name = ""
                  given_name = ""
  
              # XXX PI compute
              pi = PIModel()
              pi.technic = 0
              pi.comportment = 0
              pi.context = 0
              pi.version = 0
              shard_id = allocate_user_shard(user_id)
  
              core = super(User, cls).create(user_id=user_id,
                                             name=new_user.name,
                                             password=new_user.password,
                                             recovery_email=recovery,
                                             params=new_user.params,
                                             date_insert=datetime.datetime.now(
                                                 tz=pytz.utc),
                                             privacy_features=privacy_features,
                                             pi=pi,
                                             family_name=family_name,
                                             given_name=given_name,
                                             shard_id=shard_id)
          except Exception as exc:
              log.info(exc)
              rollback_username_storage(new_user.name)
              raise exc
  
          # **** operations below do not raise fatal error and rollback **** #
          # Setup index
          setup_index(core)
          # Setup others entities related to user
          setup_system_tags(core)
          setup_settings(core, new_user.settings)
  
          UserRecoveryEmail.create(recovery_email=recovery, user_id=user_id)
          # Add a default local identity on a default configured domain
          default_domain = Configuration('global').get('default_domain')
          default_local_id = '{}@{}'.format(core.name, default_domain)
          if not core.add_local_identity(default_local_id):
              log.warn('Impossible to create default local identity {}'.
                       format(default_local_id))
  
          # save and index linked contact
          if hasattr(new_user, "contact"):
              #  add local email to contact
              local_mail = NewEmail()
              local_mail.address = default_local_id
              new_user.contact.emails.append(local_mail)
  
              #  create default contact for user
              contact = CoreContact.create(core, new_user.contact)
  
              core.model.contact_id = contact.contact_id
  
              log.info("contact id {} for new user {}".format(contact.contact_id,
                                                              core.user_id))
          else:
              log.error(
                  "missing contact in new_user params for user {}. "
                  "Can't create related tables".format(core.user_id))
  
          core.save()
          return core
function_tokens: def create ( cls , new_user ) : def rollback_username_storage ( username ) : UserName . get ( username ) . delete ( ) # 0. check for user email white list and max number of users cls . _check_whitelistes ( new_user ) cls . _check_max_users ( ) # 1. try : validators . is_valid_username ( new_user . name ) except SyntaxError : raise ValueError ( "Malformed username" ) # 2. try : ReservedName . get ( new_user . name ) raise ValueError ( 'Reserved user name' ) except NotFound : pass user_id = uuid . uuid4 ( ) # 3. if not new_user . recovery_email : raise ValueError ( "Missing recovery email" ) try : cls . validate_recovery_email ( new_user . recovery_email ) except Exception as exc : log . info ( "recovery email failed validation : {}" . format ( exc ) ) raise ValueError ( exc ) # 4. & 5. if User . is_username_available ( new_user . name . lower ( ) ) : # save username immediately to prevent concurrent creation UserName . create ( name = new_user . name . lower ( ) , user_id = user_id ) # NB : need to rollback this username creation if the below #      User creation failed for any reason else : raise ValueError ( "Username already exist" ) # 6. try : user_inputs = [ new_user . name . encode ( "utf-8" ) , new_user . recovery_email . encode ( "utf-8" ) ] # TODO: add contact inputs if any password_strength = zxcvbn ( new_user . password , user_inputs = user_inputs ) privacy_features = { "password_strength" : str ( password_strength [ "score" ] ) } passwd = new_user . password . encode ( 'utf-8' ) new_user . password = bcrypt . hashpw ( passwd , bcrypt . gensalt ( ) ) except Exception as exc : log . exception ( exc ) rollback_username_storage ( new_user . name ) raise exc try : new_user . validate ( ) # schematic model validation except Exception as exc : rollback_username_storage ( new_user . name ) log . info ( "schematics validation error: {}" . format ( exc ) ) raise ValueError ( "new user malformed" ) try : recovery = new_user . recovery_email if hasattr ( new_user , "contact" ) : family_name = new_user . contact . family_name given_name = new_user . contact . given_name else : family_name = "" given_name = "" # XXX PI compute pi = PIModel ( ) pi . technic = 0 pi . comportment = 0 pi . context = 0 pi . version = 0 shard_id = allocate_user_shard ( user_id ) core = super ( User , cls ) . create ( user_id = user_id , name = new_user . name , password = new_user . password , recovery_email = recovery , params = new_user . params , date_insert = datetime . datetime . now ( tz = pytz . utc ) , privacy_features = privacy_features , pi = pi , family_name = family_name , given_name = given_name , shard_id = shard_id ) except Exception as exc : log . info ( exc ) rollback_username_storage ( new_user . name ) raise exc # **** operations below do not raise fatal error and rollback **** # # Setup index setup_index ( core ) # Setup others entities related to user setup_system_tags ( core ) setup_settings ( core , new_user . settings ) UserRecoveryEmail . create ( recovery_email = recovery , user_id = user_id ) # Add a default local identity on a default configured domain default_domain = Configuration ( 'global' ) . get ( 'default_domain' ) default_local_id = '{}@{}' . format ( core . name , default_domain ) if not core . add_local_identity ( default_local_id ) : log . warn ( 'Impossible to create default local identity {}' . format ( default_local_id ) ) # save and index linked contact if hasattr ( new_user , "contact" ) : #  add local email to contact local_mail = NewEmail ( ) local_mail . address = default_local_id new_user . contact . emails . append ( local_mail ) #  create default contact for user contact = CoreContact . create ( core , new_user . contact ) core . model . contact_id = contact . contact_id log . info ( "contact id {} for new user {}" . format ( contact . contact_id , core . user_id ) ) else : log . error ( "missing contact in new_user params for user {}. " "Can't create related tables" . format ( core . user_id ) ) core . save ( ) return core