def test_get_by_id(superuser): """Get user by ID.""" user = User('*****@*****.**', 'Foo Bar') user.save_as(superuser) retrieved = User.get_by_id(user.id) assert retrieved == user
def test_superuser_can_register_with_password_reset(superuser, testapp): """Register a new user, automatically creating a password reset (for emailing).""" # Goes to homepage res = testapp.get('/') # Fills out login form form = res.forms['loginForm'] form['username'] = superuser.email form['password'] = '******' # Submits res = form.submit().follow() # Navigate to Users res = res.click(_('Users')) # Clicks Create Account button res = res.click(_('New User')) # Fills out the form form = res.forms['registerUserForm'] form['username'] = '******' form['full_name'] = 'End2End' form['send_password_reset_email'].checked = True # Submits res = form.submit() assert res.status_code == 302 assert res.location.endswith(url_for('user.home')) res = res.follow() assert res.status_code == 200 # A new user was created new_user = User.get_by_email('*****@*****.**') assert isinstance(new_user, User) assert new_user.is_active is False assert new_user.created_by == superuser assert new_user.modified_by == superuser # A password reset was created password_resets = PasswordReset.query.filter_by(user=new_user).all() assert len(password_resets) == 1
def create_user(email, full_name, password, admin_email, is_active, is_admin, force): """Create or overwrite user account.""" user = User.get_by_email(email) op_admin = User.get_by_email(admin_email) if force and user: if full_name: user.full_name = full_name user.set_password(password) user.update(is_active=is_active, is_admin=is_admin) user.save_as(op_admin) click.echo('Overwritten account with login {0}:{1}'.format(user.email, password)) else: user = User.create_as(op_admin, email=email, full_name=full_name or email, password=password, is_active=is_active, is_admin=is_admin) click.echo('Created account with login {0}:{1}'.format(user.email, password))
def forget_user(email, dry_run): """Remove all traces of a user from the system.""" user = User.get_by_email(email) if user: if user.is_admin: click.echo('User "{}" is a sysadmin, refusing to delete.'.format(user)) sys.exit(1) if len(User.get_modified_and_created_by_user(user)) > 0: click.echo('User "{}" has created or modified users, refusing to delete.'.format(user)) sys.exit(1) if len(Collection.get_modified_and_created_by_user(user)) > 0: click.echo('User "{}" has created or modified collections, ' 'refusing to delete.'.format(user)) sys.exit(1) if len(Permission.get_modified_and_created_by_user(user)) > 0: click.echo('User "{}" has created or modified permissions, ' 'refusing to delete.'.format(user)) sys.exit(1) if dry_run: tokens = Token.get_all_by_user(user) grants = Grant.get_all_by_user(user) failed_login_attempts = FailedLoginAttempt.get_all_by_user(user) permissions = user.permissions password_resets = user.password_resets click.echo('These tokens would be deleted: {}'.format(tokens)) click.echo('These grants would be deleted: {}'.format(grants)) click.echo('These failed login attempts would be deleted: {}'.format( failed_login_attempts)) click.echo('These permissions would be deleted: {}'.format(permissions)) click.echo('These password_resets would be deleted: {}'.format(password_resets)) else: if click.confirm('Are you sure you want to delete all information ' 'related to user "{}"?'.format(user)): Token.delete_all_by_user(user) Grant.delete_all_by_user(user) Permission.delete_all_by_user(user) PasswordReset.delete_all_by_user(user) FailedLoginAttempt.delete_all_by_user(user) user.delete() else: click.echo('User "{}" not found. Aborting...'.format(email)) sys.exit(1)
def test_check_password(superuser): """Check password.""" user = User.create_as(superuser, email='*****@*****.**', full_name='Foo Bar', password='******') assert user.check_password('fooBarBaz123') is True assert user.check_password('barFooBaz') is False
def test_cataloging_admin_can_register_permission_from_collection_view( user, collection, superuser, testapp): """Register new permission from collection view as cataloging admin.""" PermissionFactory(user=user, collection=collection, cataloging_admin=True).save_as(superuser) old_permission_count = len(Permission.query.all()) # Goes to homepage res = testapp.get('/') # Fills out login form login_form = res.forms['loginForm'] login_form['username'] = user.email login_form['password'] = '******' # Submits res = login_form.submit().follow() # Clicks to View Collection from profile res = res.click( href=url_for('collection.view', collection_code=collection.code)) # Clicks Register New Permission res = res.click(_('New Permission')) # Finds that the intended user doesn't exist res = res.click(_('New User')) # Fills out the user registration form register_user_form = res.forms['registerUserForm'] register_user_form['username'] = '******' register_user_form['full_name'] = 'Registrant' register_user_form['send_password_reset_email'].checked = False res = register_user_form.submit() assert res.status_code == 302 assert url_for('permission.register', collection_id=collection.id) in res.location other_user = User.get_by_email('*****@*****.**') assert other_user is not None # Saves the form to grant 'other_user' permissions on 'collection' res = res.follow(headers={'Referer': res.request.referrer }) # FIXME: Webtest dropping referer. assert res.status_code == 200 register_permission_form = res.forms['registerPermissionForm'] # New user is preset, ``register_permission_form['user_id'] = other_user.id`` is redundant # Defaults are kept, ``register_permission_form['collection_id'] = collection.id`` is redundant register_permission_form['registrant'].checked = True register_permission_form['cataloger'].checked = True # Submits res = register_permission_form.submit() assert res.status_code == 302 assert url_for('collection.view', collection_code=collection.code) in res.location res = res.follow() assert res.status_code == 200 # The permission was created, and number of permissions are 1 more than initially assert _('Added permissions for "%(username)s" on collection "%(code)s".', username=other_user.email, code=collection.code) in res assert len(Permission.query.all()) == old_permission_count + 1 # The new permission is listed on the collection view. assert len(res.lxml.xpath("//td[contains(., '{0}')]".format( user.email))) == 1
def test_superuser_can_administer_existing_user(superuser, user, testapp): """Administer user details for an existing user.""" # Check expected premises. user_creator = user.created_by initial_modified_by = user.modified_by assert user_creator != superuser and initial_modified_by != superuser # Goes to homepage. res = testapp.get('/') # Fills out login form. form = res.forms['loginForm'] form['username'] = superuser.email form['password'] = '******' # Submits. res = form.submit().follow() # Clicks Users button. res = res.click(_('Users')) # Clicks Edit Details button. res = res.click(href='/users/administer/{0}'.format(user.id)) # Fills out the form. form = res.forms['administerForm'] form['username'] = user.email form['full_name'] = 'A new name' form['is_active'].checked = not user.is_active # Submits. res = form.submit() # Redirected back to users' overview. assert res.status_code == 302 assert res.location.endswith(url_for('user.home')) # The user was edited. edited_user = User.get_by_email(user.email) assert edited_user.full_name == form['full_name'].value assert edited_user.is_active == form['is_active'].checked assert edited_user.is_admin == form['is_admin'].checked # 'modified_by' is updated to reflect change, with 'created_by' intact. assert edited_user.created_by == user_creator assert edited_user.modified_by == superuser # Redirect succeeds. res = res.follow() assert res.status_code == 200 # The edited user is listed under existing users. assert len( res.lxml.xpath("//td[contains(., '{0}')]".format( form['username'].value))) == 1 assert len( res.lxml.xpath("//td[contains(., '{0}')]".format( form['full_name'].value))) == 1 is_admin_value = format(form['is_admin'].checked).lower() is_admin_string = _('Yes') if form['is_admin'].checked else _('No') admin_xpath = "//td[@class='bool-value-{0}' and contains(., '{1}')]".format( is_admin_value, is_admin_string) assert len(res.lxml.xpath(admin_xpath)) == 1
def test_modified_at_defaults_to_current_datetime(superuser): """Test modified date.""" user = User('*****@*****.**', 'Wrong Name') user.save_as(superuser) first_modified_at = user.modified_at assert abs((first_modified_at - user.created_at).total_seconds()) < 10 user.full_name = 'Correct Name' user.save_as(user) # Initial 'modified_at' has been overwritten. assert first_modified_at != user.modified_at
def test_update_last_login_does_not_update_modified_at(superuser): """Test modified date.""" user = User('*****@*****.**', 'Hello World') user.save_as(superuser) first_modified_at = user.modified_at # Update 'last_login_at' timestamp. user.update_last_login() # Initial 'modified_at' is still the same. assert first_modified_at == user.modified_at
def test_created_by_and_modified_by_is_updated(superuser): """Test created/modified by.""" user = User(email='*****@*****.**', full_name='Foo Bar') user.save_as(superuser) assert user.created_by_id == superuser.id assert user.created_by == superuser assert user.modified_by_id == superuser.id assert user.modified_by == superuser # User updates something in their profile. user.update_as(user, commit=True, full_name='Bar Foo') assert user.created_by == superuser assert user.modified_by == user
def test_login_attempt_purge(superuser): """Test purging login attempts for username/IP combo.""" user = User('*****@*****.**', 'Foo fooson') user.save_as(superuser) foo_login_attempt = FailedLoginAttempt('*****@*****.**', '127.0.0.1') foo_login_attempt.save() bar_login_attempt = FailedLoginAttempt('bar', '127.0.0.1') bar_login_attempt.save() attempts = FailedLoginAttempt.query.all() assert len(attempts) == 2 user.update_last_login_internal(commit=True, remote_address='127.0.0.1') attempts = FailedLoginAttempt.query.all() assert len(attempts) == 1 assert bar_login_attempt in attempts
def test_cataloging_admin_can_register_not_triggering_password_reset(user, superuser, testapp): """Register a new user as cataloging admin, not creating any password reset.""" PermissionFactory(user=user, cataloging_admin=True).save_as(superuser) assert user.is_cataloging_admin is True # Goes to homepage res = testapp.get('/') # Fills out login form form = res.forms['loginForm'] form['username'] = user.email form['password'] = '******' # Submits res = form.submit().follow() # Gets redirected to profile page assert _('Your Responsibilities as Cataloging Admin') in res # Clicks Create Account button res = res.click(_('New User')) # Fills out the form form = res.forms['registerUserForm'] form['username'] = '******' form['full_name'] = 'End2End' form['send_password_reset_email'].checked = False # Submits res = form.submit() assert res.status_code == 302 # A new user was created new_user = User.get_by_email('*****@*****.**') assert isinstance(new_user, User) assert new_user.is_active is False # Keeping track of who created what assert new_user.created_by == user assert new_user.modified_by == user # A password reset was not created password_reset = PasswordReset.query.filter_by(user=new_user).first() assert password_reset is None # Redirect goes to detail view for the new user assert res.location.endswith(url_for('user.view', user_id=new_user.id)) res = res.follow() assert res.status_code == 200
def test_successful_login_updates_last_login_at_only(user, testapp): """Successful login sets 'last_login_at' to current UTC datetime (but not 'modified_*').""" # Check expected premises. assert user.last_login_at is None initial_modified_at = user.modified_at initial_modified_by = user.modified_by assert initial_modified_by != user # Goes to homepage. res = testapp.get('/') # Fills out login form. form = res.forms['loginForm'] form['username'] = user.email form['password'] = '******' # Submits. res = form.submit().follow() assert res.status_code == 200 # Fetch user from database, now with login timestamp but no modified at/by changes. updated_user = User.get_by_id(user.id) assert isinstance(updated_user.last_login_at, datetime) assert (datetime.utcnow() - updated_user.last_login_at).total_seconds() < 10 assert updated_user.modified_at == initial_modified_at assert updated_user.modified_by == initial_modified_by
def test_user_can_edit_own_details(user, testapp): """Change details for self.""" user_creator = user.created_by assert user_creator != user # Goes to homepage. res = testapp.get('/') # Fills out login form. form = res.forms['loginForm'] form['username'] = user.email form['password'] = '******' # Submits. res = form.submit() assert res.status_code == 302 assert res.location.endswith(url_for('user.profile')) old_name = user.full_name res = res.follow() # Click on 'Edit' button res = res.click(_('Edit')) # Change name form = res.forms['editDetailsForm'] form['full_name'] = 'New Name' res = form.submit() # Redirected back to profile page. assert res.status_code == 302 assert res.location.endswith(url_for('user.profile')) # 'modified_by' is updated to reflect change, with 'created_by' intact. edited_user = User.get_by_email(user.email) assert edited_user.created_by == user_creator assert edited_user.modified_by == user # Redirect succeeds. res = res.follow() assert res.status_code == 200 # Make sure name has been updated assert len(res.lxml.xpath( "//h1[contains(., '{0}')]".format(old_name))) == 0 assert len(res.lxml.xpath("//h1[contains(., 'New Name')]")) == 1
def test_created_at_defaults_to_datetime(superuser): """Test creation date.""" user = User(email='*****@*****.**', full_name='Foo Bar') user.save_as(superuser) assert bool(user.created_at) assert isinstance(user.created_at, datetime)
def test_password_defaults_to_a_random_one(superuser): """Test empty password field is assigned some random password, instead of being set to null.""" user = User(email='*****@*****.**', full_name='Foo Bar') user.save_as(superuser) assert user.password is not None
def test_tos_approved_at_defaults_to_null(superuser): """Test Terms of Service datetime field is not populated automatically.""" user = User(email='*****@*****.**', full_name='Foo Bar') user.save_as(superuser) assert user.tos_approved_at is None
def import_data(verbose, admin_email, wipe_permissions, send_password_resets): """Read data from Voyager dump and BibDB API to create DB entities. Creates: - collections - user accounts for collection managers - permissions between the two """ import requests from flask_babel import gettext from .collection.forms import RegisterForm as CollectionRegisterForm from .collection.models import Collection from .permission.models import Permission from .user.forms import RegisterForm as UserRegisterForm from .user.models import PasswordReset, User def _get_collection_details_from_bibdb(code): raw_bibdb_api_data = json.loads(requests.get( 'https://bibdb.libris.kb.se/api/lib?level=brief&sigel={}' .format(code)).content.decode('utf-8')) if raw_bibdb_api_data['query']['operation'] == 'sigel {}'.format(code): if verbose: print('Fetched details for sigel %r' % code) else: click.echo('.', nl=False) else: if not verbose: click.echo('x', nl=False) raise AssertionError('Lookup failed for sigel %r' % code) bibdb_api_data = None for chunk in raw_bibdb_api_data['libraries']: if chunk['sigel'] == code: if bibdb_api_data is not None: raise AssertionError('Duplicate results for sigel %r' % code) bibdb_api_data = chunk if not bibdb_api_data: raise AssertionError('Zero results for sigel %r' % code) if bibdb_api_data['type'] in {'library', 'bibliography'}: category = bibdb_api_data['type'] else: category = 'uncategorized' if bibdb_api_data['dept']: friendly_name = '%s, %s' % (bibdb_api_data['name'], bibdb_api_data['dept']) else: friendly_name = bibdb_api_data['name'] assert bibdb_api_data['alive'] in {True, False} collection = { 'friendly_name': friendly_name, 'code': bibdb_api_data['sigel'], 'category': category, 'is_active': bibdb_api_data['alive'], 'replaces': bibdb_api_data['sigel_old'], 'replaced_by': bibdb_api_data['sigel_new'] } if bibdb_api_data['date_created']: collection['created_at'] = \ dt.datetime.strptime(bibdb_api_data['date_created'], '%Y-%m-%dT%H:%M:%S') return collection def _get_voyager_data(): raw_voyager_sigels_and_locations = requests.get( 'https://github.com/libris/xl_auth/files/1513982/171129_KB--sigel_locations.txt' ).content.decode('latin-1').splitlines() voyager_sigels_and_collections = dict() voyager_main_sigels, voyager_location_sigels = set(), set() for voyager_row in raw_voyager_sigels_and_locations: voyager_sigel, voyager_location = voyager_row.split(',') assert voyager_sigel and voyager_location voyager_main_sigels.add(voyager_sigel) voyager_location_sigels.add(voyager_location) if voyager_sigel == 'SEK' and voyager_location != 'SEK': continue # Don't add all the collections under SEK super cataloger. if voyager_sigel in voyager_sigels_and_collections: voyager_sigels_and_collections[voyager_sigel].add(voyager_location) else: voyager_sigels_and_collections[voyager_sigel] = {voyager_location} print('voyager_main_sigels:', len(voyager_main_sigels), '/', 'voyager_location_sigels:', len(voyager_location_sigels)) print('(voyager_main_sigels | voyager_location_sigels):', len(voyager_main_sigels | voyager_location_sigels)) return { 'sigel_to_collections': voyager_sigels_and_collections, 'sigels': voyager_main_sigels, 'collections': voyager_location_sigels } def _get_bibdb_cataloging_admins(): raw_bibdb_sigels_and_cataloging_admins = requests.get( 'https://libris.kb.se/libinfo/library_konreg.jsp').content.decode('utf-8').splitlines() registering_bibdb_sigels, bibdb_cataloging_admins = set(), set() bibdb_sigels_and_cataloging_admins = dict() bibdb_cataloging_admins_and_sigels = dict() bibdb_cataloging_admin_emails_and_names = dict() for bibdb_row in raw_bibdb_sigels_and_cataloging_admins: try: bibdb_sigel, cataloging_admin_name, cataloging_admin_email = bibdb_row.split(',') except ValueError as err: print('ValueError: %s / bibdb_row: %r' % (err, bibdb_row)) continue cataloging_admin_email = cataloging_admin_email.lower() bibdb_cataloging_admin_emails_and_names[cataloging_admin_email] = cataloging_admin_name assert bibdb_sigel != '' registering_bibdb_sigels.add(bibdb_sigel) if not cataloging_admin_email: continue bibdb_cataloging_admins.add(cataloging_admin_email) bibdb_sigels_and_cataloging_admins[bibdb_sigel] = cataloging_admin_email if cataloging_admin_email in bibdb_cataloging_admins_and_sigels: bibdb_cataloging_admins_and_sigels[cataloging_admin_email].add(bibdb_sigel) else: bibdb_cataloging_admins_and_sigels[cataloging_admin_email] = {bibdb_sigel} print('registering_bibdb_sigels:', len(registering_bibdb_sigels), '/', 'bibdb_cataloging_admins:', len(bibdb_cataloging_admins)) return { 'sigel_to_cataloging_admins': bibdb_sigels_and_cataloging_admins, 'cataloging_admin_to_sigels': bibdb_cataloging_admins_and_sigels, 'cataloging_admin_emails_to_names': bibdb_cataloging_admin_emails_and_names, 'sigels': registering_bibdb_sigels, 'cataloging_admins': bibdb_cataloging_admins, } def _get_bibdb_sigels_not_in_voyager(bibdb_sigels, voyager_sigels): unknown_sigels = set() for bibdb_sigel in bibdb_sigels: if bibdb_sigel not in voyager_sigels: unknown_sigels.add(bibdb_sigel) return unknown_sigels def _generate_xl_auth_cataloging_admins_and_collections(bibdb_cataloging_admin_to_sigels, voyager_sigel_to_collections): pre_total, post_total = 0, 0 voyager_sigels_unknown_in_bibdb = set() xl_auth_cataloging_admins = dict() xl_auth_collections = dict() # Prepare permissions for cataloging admins. for cataloging_admin, sigels in bibdb_cataloging_admin_to_sigels.items(): pre_total += len(sigels) xl_auth_cataloging_admins[cataloging_admin] = set() for sigel in sigels: # Fetch details if necessary. if sigel not in xl_auth_collections: xl_auth_collections[sigel] = _get_collection_details_from_bibdb(sigel) xl_auth_cataloging_admins[cataloging_admin].add(sigel) if sigel in bibdb_sigels_unknown_in_voyager: continue # Don't attempt resolving sigels that does not exist in Voyager. # Add additional sigels from Voyager sigel-to-"sub-sigel" mapping. for voyager_collection in voyager_sigel_to_collections[sigel]: if voyager_collection not in xl_auth_collections: try: # Fetch details if necessary. xl_auth_collections[voyager_collection] = \ _get_collection_details_from_bibdb(voyager_collection) except AssertionError as err: voyager_sigels_unknown_in_bibdb.add(voyager_collection) if verbose: print(err) continue xl_auth_cataloging_admins[cataloging_admin].add(voyager_collection) post_total += len(xl_auth_cataloging_admins[cataloging_admin]) print('\npre_total:', pre_total, '/ post_total:', post_total) print('voyager_sigels_unknown_in_bibdb:', voyager_sigels_unknown_in_bibdb) resolved_bibdb_refs = set() unresolved_bibdb_refs = set() print('before-replaces-lookups:', len(xl_auth_collections)) for _ in range(10): for _, details in deepcopy(xl_auth_collections).items(): for old_new_ref in {'replaces', 'replaced_by'}: if details[old_new_ref] and details[old_new_ref] not in xl_auth_collections: try: xl_auth_collections[details[old_new_ref]] = \ _get_collection_details_from_bibdb(details[old_new_ref]) resolved_bibdb_refs.add(details[old_new_ref]) except AssertionError as err: unresolved_bibdb_refs.add(details[old_new_ref]) print(err) print('after-replaces-lookups:', len(xl_auth_collections)) print('resolved_bibdb_refs:', resolved_bibdb_refs) print('unresolved_bibdb_refs:', unresolved_bibdb_refs) return { 'collections': xl_auth_collections, 'cataloging_admins': xl_auth_cataloging_admins } def _get_manually_added_permissions(): emails_and_collection_codes = requests.get( 'https://docs.google.com/spreadsheets/d/e/2PACX-1vT2TjS_L9_J5LJztfKWo0UxQD-RCZo3bheFIH' 'Ouz2Gu-aGcd7IrlDzHDmQ2yL726z0BnSc47vasL0l3/pub?gid=0&single=true&output=tsv' ).content.decode('utf-8').splitlines() manual_additions = [] for add_row in emails_and_collection_codes[1:]: add_email, add_code, _ = add_row.split('\t') manual_additions.append((add_email.strip(), add_code.strip())) return manual_additions def _get_manually_deleted_permissions(): emails_and_collection_codes = requests.get( 'https://docs.google.com/spreadsheets/d/e/2PACX-1vT2TjS_L9_J5LJztfKWo0UxQD-RCZo3bheFIH' 'Ouz2Gu-aGcd7IrlDzHDmQ2yL726z0BnSc47vasL0l3/pub?gid=518641812&single=true&output=tsv' ).content.decode('utf-8').splitlines() manual_deletions = [] for del_row in emails_and_collection_codes[1:]: del_email, del_code, _ = del_row.split('\t') manual_deletions.append((del_email.strip(), del_code.strip())) return manual_deletions # Get admin user admin = User.get_by_email(admin_email) # Gather data. voyager = _get_voyager_data() bibdb = _get_bibdb_cataloging_admins() bibdb_sigels_unknown_in_voyager = \ _get_bibdb_sigels_not_in_voyager(bibdb['sigels'], voyager['sigels']) print('bibdb_sigels_unknown_in_voyager:', bibdb_sigels_unknown_in_voyager) # Compile it into xl_auth-compatible model. xl_auth = _generate_xl_auth_cataloging_admins_and_collections( bibdb['cataloging_admin_to_sigels'], voyager['sigel_to_collections']) # Store collections. for collection, details in deepcopy(xl_auth['collections']).items(): with current_app.test_request_context(): collection_form = CollectionRegisterForm(admin, code=details['code'], friendly_name=details['friendly_name']) collection_form.validate() if collection_form.code.errors or collection_form.friendly_name.errors: for code_error in collection_form.code.errors: print('collection %r: %s' % (collection, code_error)) for friendly_name_error in collection_form.friendly_name.errors: print('friendly_name %r: %s' % (details['friendly_name'], friendly_name_error)) del xl_auth['collections'][collection] continue collection = Collection.get_by_code(code=details['code']) if collection: if collection.is_active != details['is_active']: collection.is_active = details['is_active'] collection.save_as(admin) print('corrected collection %r: is_active=%s' % (collection.code, collection.is_active)) else: collection = Collection(**details) if collection.created_at: collection.modified_at = collection.created_at collection.modified_by = collection.created_by = admin collection.save(preserve_modified=True) else: collection.save_as(admin) # Store users. for email, full_name in deepcopy(bibdb['cataloging_admin_emails_to_names']).items(): if email not in bibdb['cataloging_admins']: del bibdb['cataloging_admin_emails_to_names'][email] continue with current_app.test_request_context(): user_form = UserRegisterForm(None, username=email, full_name=full_name) user_form.validate() if gettext('Email already registered') in user_form.username.errors: pass elif user_form.username.errors or user_form.full_name.errors: print('validation failed for %s <%s>' % (full_name, email)) for username_error in user_form.username.errors: print('email %r: %s' % (email, username_error)) for full_name_error in user_form.full_name.errors: print('full_name %r: %s' % (full_name, full_name_error)) del bibdb['cataloging_admin_emails_to_names'][email] continue user = User.get_by_email(email) if not user: user = User(email=email, full_name=full_name, is_active=False) if send_password_resets: # Requires SERVER_NAME and PREFERRED_URL_SCHEME env vars. with current_app.test_request_context(): password_reset = PasswordReset(user) password_reset.send_email(account_registration_from_user=admin) user.save_as(admin) password_reset.save() print('Added inactive user %r (password reset email sent).' % email) else: user.save_as(admin) print('Added inactive user %r (no password reset).' % email) old_permissions = Permission.query.all() current_permissions, new_permissions, removed_permissions = [], [], [] # Store permissions. for email, collections in xl_auth['cataloging_admins'].items(): user = User.get_by_email(email) if not user: continue for code in collections: collection = Collection.get_by_code(code) if not collection: print('Collection %r does not exist' % code) continue permission = Permission.query.filter_by(user_id=user.id, collection_id=collection.id).first() if permission: current_permissions.append(permission) elif collection.is_active: # No creating permissions on inactive collections. if user.email == '*****@*****.**': permission = Permission.create_as(admin, user=user, collection=collection, registrant=True, cataloger=collection.code == 'Utb2', cataloging_admin=False) else: permission = Permission.create_as(admin, user=user, collection=collection, registrant=True, cataloger=True, cataloging_admin=True) new_permissions.append(permission) # Apply manual additions. for email, code in _get_manually_added_permissions(): user = User.get_by_email(email) if not user: print('Cannot add permission manually; user %r does not exist' % email) continue collection = Collection.get_by_code(code) if not collection: print('Cannot add permission manually, collection %r does not exist' % code) continue permission = Permission.query.filter_by(user_id=user.id, collection_id=collection.id).first() if permission: current_permissions.append(permission) if verbose: print('Manual permission for %r on %r already exists.' % (email, code)) else: permission = Permission.create_as(admin, user=user, collection=collection, registrant=True, cataloger=True, cataloging_admin=True) new_permissions.append(permission) if verbose: print('Manually added permissions for %r on %r.' % (email, code)) # Apply manual deletions. for email, code in _get_manually_deleted_permissions(): user = User.get_by_email(email) if not user: print('Cannot delete permission manually; user %r does not exist' % email) continue collection = Collection.get_by_code(code) if not collection: print('Cannot delete permission manually, collection %r does not exist' % code) continue permission = Permission.query.filter_by(user_id=user.id, collection_id=collection.id).first() if permission: permission.delete() removed_permissions.append(permission) if verbose: print('Manually deleted permissions for %r on %r.' % (email, code)) else: current_permissions.append(permission) if verbose: print('Cannot manually deleted permissions for %r on %r; does not exist.' % (email, code)) # Optionally wipe permissions not deduced from controlled sources (BibDB, Voyager, manual), # but only if created by admin account. And also existing permissions on inactive collections. for permission in old_permissions: if not permission.collection.is_active: print('Existing permission for %r on inactive collection %r (deleting=%s).' % (permission.user.email, permission.collection.code, wipe_permissions)) if wipe_permissions: permission.delete() continue elif permission in current_permissions and permission not in removed_permissions: continue else: if permission.created_by != admin: print('Unknown permission for %r on %r, created by %r (deleting=False).' % (permission.user.email, permission.collection.code, permission.created_by.email)) continue print('Permission for %r on %r not found during import (deleting=%s).' % (permission.user.email, permission.collection.code, wipe_permissions)) if wipe_permissions: permission.delete()
def test_password_can_contain_utf8_chars(superuser): """Test that passwords can contain utf-8 characters.""" password = '******' * 40 # test a really long password, just to make sure. user = User(email='*****@*****.**', full_name='Foo Bar', password=password) user.save_as(superuser) assert user.check_password(password)
def test_cataloging_admin_can_edit_permission_from_user_view( user, permission, superuser, testapp): """Edit existing permission from user view as cataloging admin.""" # Make 'user' cataloging admin for 'permission.collection' and 2nd one PermissionFactory(user=user, collection=permission.collection, cataloging_admin=True).save_as(superuser) initial_permission_user_id = permission.user.id old_permission_count = len(Permission.query.all()) # Goes to homepage res = testapp.get('/') # Fills out login form form = res.forms['loginForm'] form['username'] = user.email form['password'] = '******' # Submits res = form.submit().follow() # Clicks to View Collection from profile res = res.click(href=url_for('collection.view', collection_code=permission.collection.code)) # Clicks to View User on collection permissions view res = res.click(href=url_for('user.view', user_id=permission.user_id)) # Clicks Edit on a permission on the user view res = res.click( href=url_for('permission.edit', permission_id=permission.id)) # Finds that the intended user doesn't exist res = res.click(_('New User')) # Fills out the user registration form register_user_form = res.forms['registerUserForm'] register_user_form['username'] = '******' register_user_form['full_name'] = 'Other User' register_user_form['send_password_reset_email'].checked = False res = register_user_form.submit() assert res.status_code == 302 assert url_for('permission.edit', permission_id=permission.id) in res.location other_user = User.get_by_email('*****@*****.**') assert other_user is not None # Saves the form to grant 'other_user' permissions on 'collection' res = res.follow(headers={'Referer': res.request.referrer }) # FIXME: Webtest dropping referer. assert res.status_code == 200 # Fills out the form, by changing to 'other_user'' form = res.forms['editPermissionForm'] # New user is preset, ``form['user_id'] = other_user.id`` is redundant # Defaults are kept, setting ``form['collection_id'] = permission.collection.id`` is redundant # Submits res = form.submit() assert res.status_code == 302 assert url_for('user.view', user_id=initial_permission_user_id) in res.location res = res.follow() assert res.status_code == 200 # The permission was updated, and number of permissions are the same as initial state assert _( 'Updated permissions for "%(username)s" on collection "%(code)s".', username=other_user.email, code=permission.collection.code) in res assert len(Permission.query.all()) == old_permission_count # The edited permission is NOT listed on the view for permissions of initial user. assert len( res.lxml.xpath("//td/a[@href='{0}']".format( url_for('permission.edit', permission_id=permission.id)))) == 0