def sync_user_and_profile(cls, data): """Create or update the rero user with the patron data. :param data - dict representing the patron data """ # start a session to be able to rollback if the data are not valid with db.session.begin_nested(): user = cls._get_user_by_data(data) # need to create the user if not user: birth_date = data.get('birth_date') # sanity check if not birth_date: raise RecordValidationError('birth_date field is required') # the default password is the birth date user = User(email=data.get('email'), password=hash_password(birth_date), profile=dict(), active=True) db.session.add(user) # update all common fields for field in cls.profile_fields: # date field need conversion if field == 'birth_date': setattr(user.profile, field, datetime.strptime(data.get(field), '%Y-%m-%d')) else: setattr(user.profile, field, data.get(field, '')) db.session.merge(user) if not data.get('user_id'): # the fresh created user return user
def user(self): """User account linked to current record. :returns: User account. """ email = self.get('email') user = datastore.find_user(email=email) if not user: # Hash password before storing it. password = hash_password(email) # Create and save new user. user = datastore.create_user(email=email, password=password) datastore.commit() # Send welcome email send_welcome_email(self, user) # Directly confirm user (no account activation by email) confirm_user(user) else: # If user is not active, activate it. if not user.is_active: datastore.activate_user(user) return user
def update(self, data): """User record update. :param data - dictionary representing a user record to update """ from ..patrons.listener import update_from_profile self._validate(data=data) email = data.pop('email', None) roles = data.pop('roles', None) user = self.user with db.session.begin_nested(): if user.profile is None: user.profile = UserProfile(user_id=user.id) profile = user.profile for field in self.profile_fields: if field == 'birth_date': setattr(profile, field, datetime.strptime(data.get(field), '%Y-%m-%d')) else: setattr(profile, field, data.get(field, '')) # change password if data.get('password'): user.password = hash_password(data['password']) if email and email != user.email: user.email = email # remove the email from user data elif not email and user.email: user.email = None db.session.merge(user) db.session.commit() confirm_user(user) update_from_profile('user', self.user.profile) return self
def create(cls, data, **kwargs): """User record creation. :param cls - class object :param data - dictionary representing a user record """ with db.session.begin_nested(): email = data.pop('email', None) roles = data.pop('roles', None) cls._validate(data=data) password = data.pop('password', data.get('birth_date', '123456')) user = BaseUser(password=hash_password(password), profile=data, active=True) db.session.add(user) profile = user.profile for field in cls.profile_fields: value = data.get(field) if value is not None: if field == 'birth_date': value = datetime.strptime(value, '%Y-%m-%d') setattr(profile, field, value) # send the reset password notification for new users if email: user.email = email db.session.merge(user) db.session.commit() if user.email: send_reset_password_instructions(user) confirm_user(user) return cls(user)
def _make_user(role_name, organisation='org', organisation_is_shared=True, access=None): name = role_name if organisation: make_organisation(organisation, is_shared=organisation_is_shared) name = organisation + name email = '{name}@rero.ch'.format(name=name) datastore = app.extensions['security'].datastore user = datastore.find_user(email=email) if user: record = UserRecord.get_user_by_email(email) return record user = datastore.create_user(email=email, password=hash_password('123456'), active=True) datastore.commit() role = datastore.find_role(role_name) if not role: role = Role(name=role_name) role.users.append(user) db.session.add(role) if access: db.session.add(ActionUsers.allow(ActionNeed(access), user=user)) db.session.commit() data = { 'pid': name, 'email': email, 'first_name': name[0].upper() + name[1:], 'last_name': 'Doe', 'role': role_name } if organisation: data['organisation'] = { '$ref': 'https://sonar.ch/api/organisations/{organisation}'.format( organisation=organisation) } record = UserRecord.create(data, dbcommit=True) record.reindex() db.session.commit() return record
def import_users(infile): """Import users.""" click.secho('Importing users from {file}'.format(file=infile)) data = json.load(infile) for user_data in data: try: email = user_data.get('email') # No email found in user's data, account cannot be created if not email: raise ClickException('Email not defined') user = datastore.find_user(email=email) # User already exists, skip account creation if user: raise ClickException( 'User with email {email} already exists'.format( email=email)) password = user_data.get('password', '123456') password = hash_password(password) del user_data['password'] if not user_data.get('roles'): user_data['roles'] = [UserRecord.ROLE_USER] roles = user_data.get('roles', []).copy() for role in roles: if not datastore.find_role(role): datastore.create_role(name=role) datastore.commit() # Create account and activate it datastore.create_user(email=email, password=password, roles=roles) datastore.commit() user = datastore.find_user(email=email) confirm_user(user) datastore.commit() click.secho( 'User {email} with ID #{id} created successfully'.format( email=email, id=user.id), fg='green') # Create user resource user = UserRecord.create(user_data, dbcommit=True) user.reindex() except Exception as error: click.secho( 'User {user} could not be imported: {error}'.format( user=user_data, error=str(error)), fg='red') click.secho('Finished', fg='green')
def user_without_role(app, db): """Create user in database without role.""" datastore = app.extensions['security'].datastore user = datastore.create_user(email='*****@*****.**', password=hash_password('123456'), active=True) db.session.commit() return user
def import_users(infile, verbose): """Import users.""" click.secho('Import users:', fg='green') data = json.load(infile) for patron_data in data: email = patron_data.get('email') if email is None: click.secho('\tUser email not defined!', fg='red') else: # create User password = patron_data.get('password', '123456') del(patron_data['password']) patron = Patron.get_patron_by_email(email) if patron: click.secho('\tUser exist: ' + email, fg='yellow') else: if verbose: click.echo('\tUser: '******'\tUser exist: ' + email, fg='yellow') else: pwd = hash_password(password) datastore.create_user( email=email, password=pwd ) datastore.commit() user = datastore.find_user(email=email) confirm = confirm_user(user) datastore.commit() if not confirm: click.secho( '\tUser not confirmed!' + email, fg='yellow' ) # else: # click.secho('\tUser confirmed!', fg='green') patron = Patron(patron, model=patron.model) if patron_data.get('is_patron', False): patron.add_role(role_name='patrons') if patron_data.get('is_staff', False): patron.add_role(role_name='staff') # TODO: staff role patron.add_role(role_name='cataloguer') patron.reindex()
def _make_user(role_name, organisation='org'): make_organisation(organisation) name = role_name if organisation: name = organisation + name email = '{name}@rero.ch'.format(name=name) datastore = app.extensions['security'].datastore user = datastore.find_user(email=email) if user: record = UserRecord.get_user_by_email(email) return record user = datastore.create_user(email=email, password=hash_password('123456'), active=True) datastore.commit() role = datastore.find_role(role_name) if not role: role = Role(name=role_name) role.users.append(user) db.session.add(role) db.session.add( ActionUsers.allow(ActionNeed( '{role}-access'.format(role=role_name)), user=user)) db.session.commit() record = UserRecord.create( { 'pid': name, 'email': email, 'full_name': name, 'roles': [role_name], 'organisation': { '$ref': 'https://sonar.ch/api/organisations/{organisation}'.format( organisation=organisation) } }, dbcommit=True) record.reindex() db.session.commit() return record
def user(self): """Invenio user of a patron.""" email = self.get('email') user = _datastore.find_user(email=email) if not user: password = hash_password(email) _datastore.create_user(email=email, password=password) _datastore.commit() # send password reset user = _datastore.find_user(email=email) send_reset_password_instructions(user) confirm_user(user) return user
def save_patron(data, record_type, fetcher, minter, record_indexer, record_class, parent_pid): """Save a record into the db and index it. If the user does not exists, it well be created and attached to the patron. """ email = data.get('email') data = clean_patron_fields(data) if email: find_user = datastore.find_user(email=email) if find_user is None: password = hash_password(email) datastore.create_user( email=email, password=password ) datastore.commit() # send password reset user = datastore.find_user(email=email) if user: send_reset_password_instructions(user) confirm_user(user) patron = Patron.get_patron_by_email(email) if patron: patron = Patron(data, model=patron.model) patron.update(data, dbcommit=True, reindex=True) else: patron = Patron.create(data, dbcommit=True, reindex=True) if patron.get('is_patron', False): patron.add_role('patrons') else: patron.remove_role('patrons') if patron.get('is_staff', False): patron.add_role('staff') # TODO: cataloguer role patron.add_role('cataloguer') else: patron.remove_role('cataloguer') # TODO: cataloguer role patron.remove_role('staff') patron.reindex() _next = url_for('invenio_records_ui.ptrn', pid_value=patron.pid) return _next, patron.persistent_identifier
def create_user_from_data(data): """Create a user and set the profile fields from a data. :param data: A dict containing a mix of patron and user data. :returns: The modified dict. """ from .modules.users.api import User data = deepcopy(data) profile_fields = [ 'first_name', 'last_name', 'street', 'postal_code', 'gender', 'city', 'birth_date', 'username', 'home_phone', 'business_phone', 'mobile_phone', 'other_phone', 'keep_history', 'country', 'email' ] user = User.get_by_username(data.get('username')) if not user: with db.session.begin_nested(): # create the user user = BaseUser(password=hash_password( data.get('birth_date', '123456')), profile=dict(), active=True) db.session.add(user) # set the user fields if data.get('email') is not None: user.email = data['email'] profile = user.profile # set the profile for field in profile_fields: value = data.get(field) if value is not None: if field == 'birth_date': value = datetime.strptime(value, '%Y-%m-%d') setattr(profile, field, value) db.session.merge(user) db.session.commit() confirm_user(user) user_id = user.id else: user_id = user.user.id # remove the user fields from the data for field in profile_fields: try: del data[field] except KeyError: pass data['user_id'] = user_id return data
def user_with_profile(db): """Create a simple invenio user with a profile.""" with db.session.begin_nested(): user = User(email='*****@*****.**', password=hash_password('123456'), profile=dict(), active=True) db.session.add(user) profile = user.profile profile.birth_date = datetime(1990, 1, 1) profile.first_name = 'User' profile.last_name = 'With Profile' profile.city = 'Nowhere' profile.username = '******' db.session.merge(user) db.session.commit() return user
def import_users(infile, verbose): """Import users. infile: Json organisation file """ click.secho('Import users:', fg='green') data = json.load(infile) for patron_data in data: email = patron_data.get('email') if email is None: click.secho('\tUser email not defined!', fg='red') else: # create User password = patron_data.get('password', '123456') del(patron_data['password']) patron = Patron.get_patron_by_email(email) if patron: click.secho('\tUser exist: ' + email, fg='yellow') else: if verbose: click.echo('\tUser: '******'\tUser exist: ' + email, fg='yellow') else: pwd = hash_password(password) datastore.create_user( email=email, password=pwd ) datastore.commit() user = datastore.find_user(email=email) confirm_user(user) datastore.commit() patron = Patron.create( patron_data, dbcommit=True, reindex=True ) patron.reindex()
def import_users(infile, verbose, password): """Import users. :param verbose: this function will be verbose. :param password: the password to use for user by default. :param infile: Json user file. """ click.secho('Import users:', fg='green') data = json.load(infile) for patron_data in data: email = patron_data.get('email') if email is None: click.secho('\tUser email not defined!', fg='red') else: # create User password = patron_data.get('password', password) patron_data.pop('password', None) patron = Patron.get_patron_by_email(email) if patron: click.secho('\tUser exist: ' + email, fg='yellow') else: if verbose: click.echo('\tUser: '******'\tUser exist: ' + email, fg='yellow') else: patron = Patron.create(patron_data, dbcommit=False, reindex=False, email_notification=False) patron.reindex() user = patron.user user.password = hash_password(password) user.active = True db.session.merge(user) db.session.commit() confirm_user(user)
def import_users(infile, append, verbose, password, lazy, dont_stop_on_error, debug): """Import users. :param verbose: this function will be verbose. :param password: the password to use for user by default. :param lazy: lazy reads file :param dont_stop_on_error: don't stop on error :param infile: Json user file. """ click.secho('Import users:', fg='green') profile_fields = [ 'first_name', 'last_name', 'street', 'postal_code', 'gender', 'city', 'birth_date', 'username', 'home_phone', 'business_phone', 'mobile_phone', 'other_phone', 'keep_history', 'country', 'email' ] if lazy: # try to lazy read json file (slower, better memory management) data = read_json_record(infile) else: # load everything in memory (faster, bad memory management) data = json.load(infile) pids = [] error_records = [] for count, patron_data in enumerate(data, 1): password = patron_data.get('password', password) username = patron_data['username'] if password: patron_data.pop('password', None) if verbose: user_record = User.get_by_username(username) if not user_record: click.secho('{count: <8} Creating user: {username}'.format( count=count, username=username)) else: user = user_record.user for field in profile_fields: value = patron_data.get(field) if value is not None: if field == 'birth_date': value = datetime.strptime(value, '%Y-%m-%d') setattr(user.profile, field, value) db.session.merge(user) db.session.commit() click.secho('{count: <8} User updated: {username}'.format( count=count, username=username), fg='yellow') try: # patron creation patron = None patron_pid = patron_data.get('pid') if patron_pid: patron = Patron.get_record_by_pid(patron_pid) if not patron: patron = create_patron_from_data(data=patron_data, dbcommit=False, reindex=False) user = patron.user user.password = hash_password(password) user.active = True db.session.merge(user) db.session.commit() confirm_user(user) patron.reindex() pids.append(patron.pid) else: # remove profile fields from patron record for field in profile_fields: patron_data.pop(field, None) patron.update(data=patron_data, dbcommit=True, reindex=True) if verbose: click.secho( '{count: <8} Patron updated: {username}'.format( count=count, username=username), fg='yellow') except Exception as err: error_records.append(patron_data) click.secho('{count: <8} User create error: {err}'.format( count=count, err=err), fg='red') if debug: traceback.print_exc() if not dont_stop_on_error: sys.exit(1) if debug: traceback.print_exc() db.session.rollback() if append: click.secho(f'Append fixtures new identifiers: {len(pids)}') identifier = Patron.provider.identifier try: append_fixtures_new_identifiers(identifier, sorted(pids, key=lambda x: int(x)), PatronProvider.pid_type) except Exception as err: click.secho(f'ERROR append fixtures new identifiers: {err}', fg='red') if error_records: name, ext = os.path.splitext(infile.name) err_file_name = f'{name}_errors{ext}' click.secho(f'Write error file: {err_file_name}') error_file = JsonWriter(err_file_name) for error_record in error_records: error_file.write(error_record)
def import_users(infile, append, verbose, password, lazy, dont_stop_on_error, debug): """Import users. :param verbose: this function will be verbose. :param password: the password to use for user by default. :param lazy: lazy reads file :param dont_stop_on_error: don't stop on error :param infile: Json user file. """ click.secho('Import users:', fg='green') if lazy: # try to lazy read json file (slower, better memory management) data = read_json_record(infile) else: # load everything in memory (faster, bad memory management) data = json.load(infile) pids = [] error_records = [] for count, patron_data in enumerate(data): email = patron_data.get('email') password = patron_data.get('password', password) username = patron_data['username'] if email is None: click.secho( '{count: <8} User {username} do not have email!'.format( count=count, username=username), fg='yellow') if password: patron_data.pop('password', None) # do nothing if the patron alredy exists patron = Patron.get_patron_by_username(username) if patron: click.secho('{count: <8} Patron already exist: {username}'.format( count=count, username=username), fg='yellow') continue if verbose: click.secho('{count: <8} Creating user: {username}'.format( count=count, username=username)) try: profile = UserProfile.get_by_username(username) click.secho( '{count: <8} User already exist: {username}'.format( count=count, username=username), fg='yellow') except NoResultFound: pass try: # patron creation patron = Patron.create( patron_data, # delete_pid=True, dbcommit=False, reindex=False, email_notification=False) user = patron.user user.password = hash_password(password) user.active = True db.session.merge(user) db.session.commit() confirm_user(user) patron.reindex() pids.append(patron.pid) except Exception as err: error_records.append(data) click.secho('{count: <8} User create error: {err}'.format( count=count, err=err), fg='red') if debug: traceback.print_exc() if not dont_stop_on_error: sys.exit(1) if debug: traceback.print_exc() if not dont_stop_on_error: sys.exit(1) if append: click.secho( 'Append fixtures new identifiers: {len}'.format(len=len(pids))) identifier = Patron.provider.identifier try: append_fixtures_new_identifiers(identifier, sorted(pids, key=lambda x: int(x)), PatronProvider.pid_type) except Exception as err: click.secho( "ERROR append fixtures new identifiers: {err}".format(err=err), fg='red') if error_records: name, ext = os.path.splitext(infile.name) err_file_name = '{name}_errors{ext}'.format(name=name, ext=ext) click.secho('Write error file: {name}'.format(name=err_file_name)) with open(err_file_name, 'w') as error_file: error_file.write('[\n') for error_record in error_records: for line in json.dumps(error_record, indent=2).split('\n'): error_file.write(' ' + line + '\n') error_file.write(']')