-
Benachrichtigungen
You must be signed in to change notification settings - Fork 16
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #22 from cul-it/accounts-0.2
Pre-release merge for accounts-0.2
- Loading branch information
Showing
12 changed files
with
239 additions
and
1,533 deletions.
There are no files selected for viewing
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -15,7 +15,7 @@ | |
|
||
from werkzeug import MultiDict, ImmutableMultiDict | ||
from werkzeug.exceptions import BadRequest, InternalServerError | ||
from flask import url_for | ||
from flask import url_for, Markup | ||
|
||
from wtforms import StringField, PasswordField, SelectField, \ | ||
SelectMultipleField, BooleanField, Form, HiddenField | ||
|
@@ -88,6 +88,16 @@ def login(method: str, form_data: MultiDict, ip: str, | |
data.update({'error': 'Invalid username or password.'}) | ||
return data, status.HTTP_400_BAD_REQUEST, {} | ||
|
||
if not userdata.verified: | ||
data.update({ | ||
'error': Markup( | ||
'Your account has not yet been verified. Please contact ' | ||
'<a href="mailto:[email protected]">[email protected]</a> if ' | ||
'you believe this to be in error.' | ||
) | ||
}) | ||
return data, status.HTTP_400_BAD_REQUEST, {} | ||
|
||
# Create a session in the distributed session store. | ||
try: | ||
session = sessions.create(auths, ip, ip, track, user=userdata) | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -119,7 +119,8 @@ def test_post_great(self, mock_legacy, mock_sessions, mock_users): | |
user = domain.User( | ||
user_id=42, | ||
username='foouser', | ||
email='[email protected]' | ||
email='[email protected]', | ||
verified=True | ||
) | ||
auths = domain.Authorizations( | ||
classic=6, | ||
|
@@ -156,3 +157,54 @@ def test_post_great(self, mock_legacy, mock_sessions, mock_users): | |
"Session cookie is returned") | ||
self.assertEqual(data['cookies']['classic_cookie'], (c_cookie, None), | ||
"Classic session cookie is returned") | ||
|
||
@mock.patch('accounts.controllers.authentication.users') | ||
@mock.patch('accounts.controllers.authentication.sessions') | ||
@mock.patch('accounts.controllers.authentication.legacy') | ||
def test_post_not_verified(self, mock_legacy, mock_sessions, mock_users): | ||
"""Form data are valid and check out.""" | ||
mock_users.exceptions.AuthenticationFailed = \ | ||
users.exceptions.AuthenticationFailed | ||
mock_sessions.exceptions.SessionCreationFailed = \ | ||
sessions.exceptions.SessionCreationFailed | ||
mock_legacy.exceptions.SessionCreationFailed = \ | ||
legacy.exceptions.SessionCreationFailed | ||
form_data = MultiDict({'username': 'foouser', 'password': 'bazpass'}) | ||
ip = '123.45.67.89' | ||
next_page = '/foo' | ||
start_time = datetime.now(tz=EASTERN) | ||
user = domain.User( | ||
user_id=42, | ||
username='foouser', | ||
email='[email protected]', | ||
verified=False | ||
) | ||
auths = domain.Authorizations( | ||
classic=6, | ||
scopes=['public:read', 'submission:create'] | ||
) | ||
mock_users.authenticate.return_value = user, auths | ||
c_session = domain.Session( | ||
session_id='barsession', | ||
user=user, | ||
start_time=start_time, | ||
authorizations=auths | ||
) | ||
c_cookie = 'bardata' | ||
mock_legacy.create.return_value = c_session | ||
mock_legacy.generate_cookie.return_value = c_cookie | ||
session = domain.Session( | ||
session_id='foosession', | ||
user=user, | ||
start_time=start_time, | ||
authorizations=domain.Authorizations( | ||
scopes=['public:read', 'submission:create'] | ||
) | ||
) | ||
cookie = 'foodata' | ||
mock_sessions.create.return_value = session | ||
mock_sessions.generate_cookie.return_value = cookie | ||
|
||
data, status_code, header = login('POST', form_data, ip, next_page) | ||
self.assertEqual(status_code, status.HTTP_400_BAD_REQUEST, | ||
"Bad request error is returned") |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -447,3 +447,133 @@ def test_login_logout(self): | |
'0', | ||
'Classic session cookie is expired' | ||
) | ||
|
||
|
||
class TestLogoutLegacySubmitCookie(TestCase): | ||
"""The legacy system has a submission session cookie that must be unset.""" | ||
|
||
@classmethod | ||
def setUpClass(self): | ||
"""Spin up redis.""" | ||
# self.redis = subprocess.run( | ||
# "docker run -d -p 7000:7000 -p 7001:7001 -p 7002:7002 -p 7003:7003" | ||
# " -p 7004:7004 -p 7005:7005 -p 7006:7006 -e \"IP=0.0.0.0\"" | ||
# " --hostname=server grokzen/redis-cluster:4.0.9", | ||
# stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=True | ||
# ) | ||
# time.sleep(10) # In case it takes a moment to start. | ||
# if self.redis.returncode > 0: | ||
# raise RuntimeError('Could not start redis. Is Docker running?') | ||
# | ||
# self.container = self.redis.stdout.decode('ascii').strip() | ||
self.secret = 'bazsecret' | ||
self.db = 'db.sqlite' | ||
try: | ||
self.app = create_web_app() | ||
self.app.config['CLASSIC_COOKIE_NAME'] = 'foo_tapir_session' | ||
self.app.config['SESSION_COOKIE_NAME'] = 'baz_session' | ||
self.app.config['SESSION_COOKIE_SECURE'] = '0' | ||
self.app.config['JWT_SECRET'] = self.secret | ||
self.app.config['CLASSIC_DATABASE_URI'] = f'sqlite:///{self.db}' | ||
self.app.config['REDIS_HOST'] = 'localhost' | ||
self.app.config['REDIS_PORT'] = '7000' | ||
|
||
with self.app.app_context(): | ||
from accounts.services import legacy, users | ||
legacy.create_all() | ||
users.create_all() | ||
|
||
with users.transaction() as session: | ||
# We have a good old-fashioned user. | ||
db_user = users.models.DBUser( | ||
user_id=1, | ||
first_name='first', | ||
last_name='last', | ||
suffix_name='iv', | ||
email='[email protected]', | ||
policy_class=2, | ||
flag_edit_users=1, | ||
flag_email_verified=1, | ||
flag_edit_system=0, | ||
flag_approved=1, | ||
flag_deleted=0, | ||
flag_banned=0, | ||
tracking_cookie='foocookie', | ||
) | ||
db_nick = users.models.DBUserNickname( | ||
nick_id=1, | ||
nickname='foouser', | ||
user_id=1, | ||
user_seq=1, | ||
flag_valid=1, | ||
role=0, | ||
policy=0, | ||
flag_primary=1 | ||
) | ||
salt = b'fdoo' | ||
password = b'thepassword' | ||
hashed = hashlib.sha1(salt + b'-' + password).digest() | ||
encrypted = b64encode(salt + hashed) | ||
db_password = users.models.DBUserPassword( | ||
user_id=1, | ||
password_storage=2, | ||
password_enc=encrypted | ||
) | ||
session.add(db_user) | ||
session.add(db_password) | ||
session.add(db_nick) | ||
|
||
except Exception as e: | ||
stop_container(self.container) | ||
raise | ||
|
||
@classmethod | ||
def tearDownClass(self): | ||
"""Tear down redis and the test DB.""" | ||
# stop_container(self.container) | ||
os.remove(self.db) | ||
|
||
def test_logout_clears_legacy_submit_cookie(self): | ||
"""When the user logs out, the legacy submit cookie is unset.""" | ||
client = self.app.test_client() | ||
# client.set_cookie('localhost') | ||
form_data = {'username': 'foouser', 'password': 'thepassword'} | ||
|
||
# Werkzeug should keep the cookies around for the next request. | ||
response = client.post('/login', data=form_data) | ||
cookies = _parse_cookies(response.headers.getlist('Set-Cookie')) | ||
|
||
client.set_cookie('', 'submit_session', '12345678') | ||
self.assertIn(self.app.config['SESSION_COOKIE_NAME'], cookies, | ||
"Sets cookie for authn session.") | ||
self.assertIn(self.app.config['CLASSIC_COOKIE_NAME'], cookies, | ||
"Sets cookie for classic sessions.") | ||
|
||
response = client.get('/logout') | ||
logout_cookies = _parse_cookies(response.headers.getlist('Set-Cookie')) | ||
|
||
self.assertEqual( | ||
logout_cookies[self.app.config['SESSION_COOKIE_NAME']]['value'], | ||
'', | ||
'Session cookie is unset' | ||
) | ||
self.assertEqual( | ||
logout_cookies[self.app.config['SESSION_COOKIE_NAME']]['Max-Age'], | ||
'0', | ||
'Session cookie is expired' | ||
) | ||
self.assertEqual( | ||
logout_cookies[self.app.config['CLASSIC_COOKIE_NAME']]['value'], | ||
'', | ||
'Classic cookie is unset' | ||
) | ||
self.assertEqual( | ||
logout_cookies[self.app.config['CLASSIC_COOKIE_NAME']]['Max-Age'], | ||
'0', | ||
'Classic session cookie is expired' | ||
) | ||
self.assertEqual( | ||
logout_cookies['submit_session']['Max-Age'], | ||
'0', | ||
'Legacy submission cookie is expired' | ||
) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters