Beispiel #1
0
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
Beispiel #3
0
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))
Beispiel #4
0
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)
Beispiel #5
0
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
Beispiel #7
0
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
Beispiel #8
0
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
Beispiel #9
0
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
Beispiel #10
0
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
Beispiel #11
0
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
Beispiel #14
0
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
Beispiel #15
0
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)
Beispiel #16
0
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
Beispiel #17
0
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
Beispiel #18
0
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()
Beispiel #19
0
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