Skip to content

Commit

Permalink
Merge branch 'release/0.6.1'
Browse files Browse the repository at this point in the history
  • Loading branch information
mblomdahl committed Nov 23, 2017
2 parents 229aa2d + b4aa5df commit 9842fdb
Show file tree
Hide file tree
Showing 32 changed files with 958 additions and 205 deletions.
10 changes: 8 additions & 2 deletions README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -175,13 +175,19 @@ Technology choices:
DB Models
---------

.. image:: https://user-images.githubusercontent.com/786326/32836181-43de9248-ca09-11e7-99c5-7b877272aef4.png
:alt: screen shot 2017-11-15 at 1 30 31 pm
.. image:: https://user-images.githubusercontent.com/786326/33126887-7b6c746e-cf86-11e7-9176-1d500739adf7.png
:alt: screen shot 2017-11-22 at 12 24 37 pm


Changelog
=========

v. 0.6.1
--------

* Under-the-hood traceability updates (`#78 <https://github.com/libris/xl_auth/issues/78>`_)


v. 0.6.0
--------

Expand Down

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "xl_auth",
"version": "0.6.0",
"version": "0.6.1",
"author": "National Library of Sweden",
"license": "Apache-2.0",
"description": "OAuth2 authorization for LibrisXL, replacing BibDB counterpart",
Expand Down
10 changes: 8 additions & 2 deletions tests/end2end/test_collection_editing.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,10 @@

def test_superuser_can_edit_existing_collection(superuser, collection, testapp):
"""Edit an existing collection."""
# Check expected premises
collection_creator = collection.created_by
initial_modified_by = collection.modified_by
assert collection_creator != superuser and initial_modified_by != superuser
# Goes to homepage
res = testapp.get('/')
# Fills out login form
Expand All @@ -39,6 +43,9 @@ def test_superuser_can_edit_existing_collection(superuser, collection, testapp):
edited_collection = Collection.query.filter(Collection.code == collection.code).first()
assert edited_collection.friendly_name == form['friendly_name'].value
assert edited_collection.category == form['category'].value
# 'modified_by' is updated to reflect change, with 'created_by' intact
assert edited_collection.created_by == collection_creator
assert edited_collection.modified_by == superuser
# The edited collection is listed under existing collections
collection_id = 'collection-{0}'.format(form['code'].value)
collection_anchor_xpath = "//a[@class='anchor' and @id='{0}']".format(collection_id)
Expand All @@ -53,8 +60,7 @@ def test_superuser_can_edit_existing_collection(superuser, collection, testapp):
_(form['category'].value.capitalize())))) == 1
else:
assert len(res.lxml.xpath("//td[contains(., '{0}')]/ancestor::tr/td[contains(., '{1}')]"
.format(form['code'].value,
_('No category')))) == 1
.format(form['code'].value, _('No category')))) == 1


def test_superuser_sees_error_message_if_code_is_changed(superuser, collection, testapp):
Expand Down
11 changes: 7 additions & 4 deletions tests/end2end/test_collection_registering.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,9 +39,13 @@ def test_superuser_can_register_new_collection(superuser, testapp):
assert res.status_code == 200
# A new collection was created
assert len(Collection.query.all()) == old_count + 1
registered_collection = Collection.query.filter(Collection.code == form['code'].value).first()
# Keeping track of who created what
assert registered_collection.created_by == superuser
assert registered_collection.modified_by == superuser
# The new collection is listed under existing collections
collection_id = 'collection-{0}'.format(form['code'].value)
collection_anchor_xpath = "//a[@class='anchor' and @id='{0}']".format(collection_id)
collection_anchor_xpath = "//a[@class='anchor' and @id='{0}']".format(
'collection-' + registered_collection.code)
assert len(res.lxml.xpath(collection_anchor_xpath)) == 1

assert len(res.lxml.xpath("//td[contains(., '{0}')]".format(form['code'].value))) == 1
Expand All @@ -52,8 +56,7 @@ def test_superuser_can_register_new_collection(superuser, testapp):
_(form['category'].value.capitalize())))) == 1
else:
assert len(res.lxml.xpath("//td[contains(., '{0}')]/ancestor::tr/td[contains(., '{1}')]"
.format(form['code'].value,
_('No category')))) == 1
.format(form['code'].value, _('No category')))) == 1


# noinspection PyUnusedLocal
Expand Down
14 changes: 14 additions & 0 deletions tests/end2end/test_oauth_editing_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,10 @@
def test_superuser_can_edit_existing_client(superuser, client, testapp):
"""Edit an existing client."""
old_count = len(Client.query.all())
# Check expected premises
client_creator = client.created_by
initial_modified_by = client.modified_by
assert client_creator != superuser and initial_modified_by != superuser
# Goes to homepage
res = testapp.get('/')
# Fills out login form
Expand Down Expand Up @@ -40,6 +44,16 @@ def test_superuser_can_edit_existing_client(superuser, client, testapp):
res = form.submit().follow()
assert res.status_code == 200

# The client was edited
edited_client = Client.get_by_id(client.client_id)
assert edited_client.name == form['name'].value
assert edited_client.description == form['description'].value
assert edited_client.redirect_uris == ['http://localhost/']
assert edited_client.default_scopes == ['read', 'write']
# 'modified_by' is updated to reflect change, with 'created_by' intact
assert edited_client.created_by == client_creator
assert edited_client.modified_by == superuser

# No new client was created
assert len(Client.query.all()) == old_count

Expand Down
4 changes: 4 additions & 0 deletions tests/end2end/test_oauth_registering_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,10 @@ def test_superuser_can_register_new_client(superuser, testapp):

# A new client was created
assert len(Client.query.all()) == old_count + 1
registered_client = Client.query.filter(Client.name == form['name'].value).first()
# Keeping track of who created what
assert registered_client.created_by == superuser
assert registered_client.modified_by == superuser

# The new client is listed under existing clients
assert len(res.lxml.xpath("//td[contains(., '{0}')]".format(form['name'].value))) == 1
Expand Down
13 changes: 9 additions & 4 deletions tests/end2end/test_public_logging_in.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,10 +38,13 @@ def test_unauthorized_leads_to_login_which_follows_next_redirect_param_on_succes
assert '/oauth/authorize?param1=value1&param2=value2' in res.location


def test_successful_login_updates_last_login_at(user, testapp):
"""Successful login sets 'last_login_at' to current UTC datetime."""
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.
Expand All @@ -52,10 +55,12 @@ def test_successful_login_updates_last_login_at(user, testapp):
res = form.submit().follow()
assert res.status_code == 200

# Fetch user from database, now with login timestamp.
# 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_sees_alert_on_log_out(user, testapp):
Expand Down
12 changes: 11 additions & 1 deletion tests/end2end/test_public_reset_password.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,16 @@
from ..factories import UserFactory


# noinspection PyUnusedLocal
def test_can_complete_password_reset_flow(db, testapp):
"""Successfully request password reset, use code to change password and activate account."""
inactive_user = UserFactory(is_active=False)
db.session.commit()
assert inactive_user.is_active is False
# Check expected premises.
user_creator = inactive_user.created_by
initial_modified_at = inactive_user.modified_at
initial_modified_by = inactive_user.modified_by
assert initial_modified_by != inactive_user
# Goes to homepage.
res = testapp.get('/')
# Clicks on 'Forgot password'.
Expand All @@ -35,6 +40,7 @@ def test_can_complete_password_reset_flow(db, testapp):
password_reset = PasswordReset.query.filter_by(user=inactive_user).first()
assert password_reset.is_active is True
assert password_reset.expires_at > (datetime.utcnow() + timedelta(seconds=3600))
assert password_reset.user.modified_at == initial_modified_at

# URL sent to user email.
reset_password_url = url_for('public.reset_password', email=password_reset.user.email,
Expand All @@ -54,6 +60,10 @@ def test_can_complete_password_reset_flow(db, testapp):
assert updated_password_reset.is_active is False
assert updated_password_reset.user.check_password('unicorns are real') is True
assert updated_password_reset.user.is_active is True
# 'modified_by' is updated to reflect change, with 'created_by' intact.
assert updated_password_reset.user.created_by == user_creator
assert updated_password_reset.user.modified_at != initial_modified_at
assert updated_password_reset.user.modified_by == updated_password_reset.user


# noinspection PyUnusedLocal
Expand Down
38 changes: 29 additions & 9 deletions tests/end2end/test_user_editing.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,12 @@
from xl_auth.user.models import User


def test_superuser_can_administer_existing_user(superuser, testapp):
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.
Expand All @@ -22,20 +26,23 @@ def test_superuser_can_administer_existing_user(superuser, testapp):
# Clicks Users button.
res = res.click(_('Users'))
# Clicks Edit Details button.
res = res.click(href='/users/administer/{0}'.format(superuser.id))
res = res.click(href='/users/administer/{0}'.format(user.id))
# Fills out the form.
form = res.forms['administerForm']
form['username'] = superuser.email
form['username'] = user.email
form['full_name'] = 'A new name'
form['is_active'].checked = not superuser.is_active
form['is_active'].checked = not user.is_active
# Submits.
res = form.submit().follow()
assert res.status_code == 200
# The user was edited.
edited_user = User.query.filter(User.email == superuser.email).first()
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

# The edited user is listed under existing users.
assert len(res.lxml.xpath("//td[contains(., '{0}')]".format(form['username'].value))) == 1
Expand All @@ -48,8 +55,12 @@ def test_superuser_can_administer_existing_user(superuser, testapp):
assert len(res.lxml.xpath(admin_xpath)) == 1


def test_superuser_can_change_password_for_existing_user(superuser, testapp):
def test_superuser_can_change_password_for_existing_user(superuser, user, testapp):
"""Change password 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.
Expand All @@ -61,20 +72,23 @@ def test_superuser_can_change_password_for_existing_user(superuser, testapp):
# Clicks Users button.
res = res.click(_('Users'))
# Clicks Change Password button.
res = res.click(href='/users/change_password/{0}'.format(superuser.id))
res = res.click(href='/users/change_password/{0}'.format(user.id))
# Fills out the form.
form = res.forms['changePasswordForm']
form['username'] = superuser.email
form['username'] = user.email
form['password'] = 'newSecrets13'
form['confirm'] = 'newSecrets13'
# Submits.
res = form.submit().follow()
assert res.status_code == 200
# The user was edited.
edited_user = User.query.filter(User.email == superuser.email).first()
edited_user = User.query.filter(User.email == user.email).first()
# Verify the new password is considered valid, not the old one.
assert edited_user.check_password('myPrecious') is False
assert edited_user.check_password('newSecrets13') is True
# 'modified_by' is updated to reflect change, with 'created_by' intact
assert edited_user.created_by == user_creator
assert edited_user.modified_by == superuser


def test_superuser_sees_error_message_if_username_is_changed_from_administer(superuser, testapp):
Expand Down Expand Up @@ -197,6 +211,8 @@ def test_user_cannot_administer_other_user(superuser, user, testapp):

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.
Expand All @@ -218,6 +234,10 @@ def test_user_can_edit_own_details(user, testapp):
form = res.forms['editDetailsForm']
form['full_name'] = 'New Name'
res = form.submit().follow()
# '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

# Make sure name has been updated
assert len(res.lxml.xpath("//h1[contains(., '{0} {1}')]".format(_('Welcome'), old_name))) == 0
Expand Down
11 changes: 8 additions & 3 deletions tests/end2end/test_user_registering.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ def test_superuser_can_register_not_triggering_password_reset(superuser, testapp
# Fills out the form
form = res.forms['registerUserForm']
form['username'] = '[email protected]'
form['full_name'] = 'Mr End2End'
form['full_name'] = 'End2End'
form['send_password_reset_email'].checked = False
# Submits
res = form.submit().follow()
Expand All @@ -38,6 +38,9 @@ def test_superuser_can_register_not_triggering_password_reset(superuser, testapp
new_user = User.get_by_email('[email protected]')
assert isinstance(new_user, User)
assert new_user.is_active is False
# Keeping track of who created what
assert new_user.created_by == superuser
assert new_user.modified_by == superuser
# A password reset was not created
password_reset = PasswordReset.query.filter_by(user=new_user).first()
assert password_reset is None
Expand All @@ -61,7 +64,7 @@ def test_superuser_can_register_with_password_reset(superuser, testapp):
# Fills out the form
form = res.forms['registerUserForm']
form['username'] = '[email protected]'
form['full_name'] = 'Mr End2End'
form['full_name'] = 'End2End'
form['send_password_reset_email'].checked = True
# Submits
res = form.submit().follow()
Expand All @@ -70,6 +73,8 @@ def test_superuser_can_register_with_password_reset(superuser, testapp):
new_user = User.get_by_email('[email protected]')
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
Expand Down Expand Up @@ -115,7 +120,7 @@ def test_user_sees_error_message_if_user_already_registered(superuser, user, tes
# Fills out form, but username is already registered.
form = res.forms['registerUserForm']
form['username'] = user.email.upper() # Default would be `[email protected]`, not upper-cased.
form['full_name'] = 'Mr End2End'
form['full_name'] = 'End2End'
# Submits.
res = form.submit()
# Sees error.
Expand Down
Loading

0 comments on commit 9842fdb

Please sign in to comment.