def records(): """Load test data fixture.""" import uuid from invenio_records.api import Record from invenio_pidstore.models import PersistentIdentifier, PIDStatus create_test_user() indexer = RecordIndexer() # Record 1 - Live record with db.session.begin_nested(): rec_uuid = uuid.uuid4() pid1 = PersistentIdentifier.create( 'recid', '1', object_type='rec', object_uuid=rec_uuid, status=PIDStatus.REGISTERED) Record.create({ 'title': 'Registered', 'description': 'This is an awesome description', 'control_number': '1', 'access_right': 'restricted', 'access_conditions': 'fuu', 'owners': [1, 2], 'recid': 1 }, id_=rec_uuid) indexer.index_by_id(pid1.object_uuid) db.session.commit() sleep(3)
def test_view_list_sessions(app): """Test view list sessions.""" with app.test_request_context(): user1 = create_test_user(email="*****@*****.**") user2 = create_test_user(email="*****@*****.**") with app.test_client() as client: client.post( url_for_security("login"), data=dict( email=user1.email, password=user1.password_plaintext, ), ) with app.test_client() as client: client.post( url_for_security("login"), data=dict( email=user2.email, password=user2.password_plaintext, ), ) # get the list of user 2 sessions url = url_for("invenio_accounts.security") res = client.get(url) assert res.status_code == 200 # check session for user 1 is not in the list sessions_1 = SessionActivity.query.filter_by(user_id=user1.id).all() assert len(sessions_1) == 1 assert sessions_1[0].sid_s not in res.data.decode("utf-8") # check session for user 2 is in the list sessions_2 = SessionActivity.query.filter_by(user_id=user2.id).all() assert len(sessions_2) == 1 assert sessions_2[0].sid_s in res.data.decode("utf-8") # test user 2 to delete user 1 session url = url_for("invenio_accounts.revoke_session") res = client.post(url, data={"sid_s": sessions_1[0].sid_s}) assert res.status_code == 302 assert ( SessionActivity.query.filter_by( user_id=user1.id, sid_s=sessions_1[0].sid_s ).count() == 1 ) # test user 2 to delete user 1 session url = url_for("invenio_accounts.revoke_session") res = client.post(url, data={"sid_s": sessions_2[0].sid_s}) assert res.status_code == 302 assert ( SessionActivity.query.filter_by( user_id=user1.id, sid_s=sessions_2[0].sid_s ).count() == 0 )
def users(db, app): """Create users.""" # create users admin = create_test_user(email='*****@*****.**', password='******', active=True) librarian = create_test_user(email='*****@*****.**', password='******', active=True) patron = create_test_user(email='*****@*****.**', password='******', active=True) with db.session.begin_nested(): datastore = app.extensions['security'].datastore # Give role to admin admin_role = Role(name='admin') db.session.add( ActionRoles(action=superuser_access.value, role=admin_role)) datastore.add_role_to_user(admin, admin_role) # Give role to librarian librarian_role = Role(name='librarian') db.session.add( ActionRoles(action=authenticated_user.value, role=librarian_role)) datastore.add_role_to_user(librarian, librarian_role) db.session.commit() return {'admin': admin, 'librarian': librarian, 'user': patron}
def test_create_test_user(app): """Test for testutils.py:create_test_user('*****@*****.**'). Demonstrates basic usage and context requirements. """ ds = app.extensions['security'].datastore email = '*****@*****.**' password = '******' with app.app_context(): user = testutils.create_test_user(email, password) assert user.password_plaintext == password # Will fail if the app is configured to not "encrypt" the passwords. assert user.password != password # Verify that user exists in app's datastore user_ds = ds.find_user(email=email) assert user_ds assert user_ds.password == user.password with pytest.raises(Exception): # Catch-all "Exception" because it's raised by the datastore, # and the actual exception type will probably vary depending on # which datastore we're running the tests with. testutils.create_test_user(email, password)
def test_confirm_email_view(api): from flask_security.confirmable import generate_confirmation_token app = api with app.app_context(): normal_user = create_test_user(email='*****@*****.**') confirmed_user = create_test_user(email='*****@*****.**', confirmed_at=datetime.datetime.now()) db.session.commit() # Generate token token = generate_confirmation_token(normal_user) confirmed_token = generate_confirmation_token(confirmed_user) with app.test_client() as client: url = url_for('invenio_accounts_rest_auth.confirm_email') # Invalid token res = client.post(url, data=dict(token='foo')) payload = get_json(res) assert 'invalid confirmation token' in \ payload['message'][0].lower() # Already confirmed user res = client.post(url, data=dict(token=confirmed_token)) payload = get_json(res) assert 'email has already been confirmed' in \ payload['message'].lower() # Valid confirm user assert normal_user.confirmed_at is None res = client.post(url, data=dict(token=token)) payload = get_json(res) assert 'your email has been confirmed' in \ payload['message'].lower() assert normal_user.confirmed_at
def records(): """Load test data fixture.""" import uuid from invenio_records.api import Record from invenio_pidstore.models import PersistentIdentifier, PIDStatus create_test_user() indexer = RecordIndexer() # Record 1 - Live record with db.session.begin_nested(): rec_uuid = uuid.uuid4() pid1 = PersistentIdentifier.create('recid', '1', object_type='rec', object_uuid=rec_uuid, status=PIDStatus.REGISTERED) Record.create( { 'title': 'Registered', 'description': 'This is an awesome description', 'control_number': '1', 'access_right': 'restricted', 'access_conditions': 'fuu', 'owners': [1, 2], 'recid': 1 }, id_=rec_uuid) indexer.index_by_id(pid1.object_uuid) db.session.commit() sleep(3)
def test_create_test_user(app): """Test for testutils.py:create_test_user(). Demonstrates basic usage and context requirements. """ ds = app.extensions['security'].datastore email = '*****@*****.**' password = '******' with app.app_context(): user = testutils.create_test_user(email, password) assert user.password_plaintext == password # Will fail if the app is configured to not "encrypt" the passwords. assert user.password != password # Verify that user exists in app's datastore user_ds = ds.find_user(email=email) assert user_ds assert user_ds.password == user.password with pytest.raises(Exception): # Catch-all "Exception" because it's raised by the datastore, # and the actual exception type will probably vary depending on # which datastore we're running the tests with. testutils.create_test_user(email, password)
def test_login_view(api): app = api with app.app_context(): normal_user = create_test_user(email="*****@*****.**") create_test_user(email="*****@*****.**", active=False) create_test_user(email="*****@*****.**", password=None) db.session.commit() with app.test_client() as client: url = url_for("invenio_accounts_rest_auth.login") # Missing fields res = client.post(url) assert_error_resp( res, ( ("password", "required"), ("email", "required"), ), ) # Invalid fields res = client.post(url, data=dict(email="invalid-email", password=None)) assert_error_resp( res, ( ("password", "required field"), ("email", "not a valid email"), ), ) # Invalid credentials res = client.post(url, data=dict(email="*****@*****.**", password="******")) assert_error_resp(res, (("email", "user does not exist"), )) # No-password user res = client.post(url, data=dict(email="*****@*****.**", password="******")) assert_error_resp(res, (("password", "no password is set"), )) # Disabled account res = client.post(url, data=dict(email="*****@*****.**", password="******")) assert_error_resp(res, ((None, "account is disabled"), )) # Successful login _login_user(client, normal_user) # User info view res = client.get(url_for("invenio_accounts_rest_auth.user_info")) payload = get_json(res) assert res.status_code == 200 assert payload["id"] == normal_user.id
def test_login_view(api): app = api with app.app_context(): normal_user = create_test_user(email='*****@*****.**') create_test_user(email='*****@*****.**', active=False) create_test_user(email='*****@*****.**', password=None) db.session.commit() with app.test_client() as client: url = url_for('invenio_accounts_rest_auth.login') # Missing fields res = client.post(url) assert_error_resp(res, ( ('password', 'required'), ('email', 'required'), )) # Invalid fields res = client.post(url, data=dict( email='invalid-email', password=None)) assert_error_resp(res, ( ('password', 'required field'), ('email', 'not a valid email'), )) # Invalid credentials res = client.post(url, data=dict( email='*****@*****.**', password='******')) assert_error_resp(res, ( ('email', 'user does not exist'), )) # No-password user res = client.post(url, data=dict( email='*****@*****.**', password='******')) assert_error_resp(res, ( ('password', 'no password is set'), )) # Disabled account res = client.post(url, data=dict( email='*****@*****.**', password='******')) assert_error_resp(res, ( (None, 'account is disabled'), )) # Successful login _login_user(client, normal_user) # User info view res = client.get(url_for('invenio_accounts_rest_auth.user_info')) payload = get_json(res) assert res.status_code == 200 assert payload['id'] == normal_user.id
def users(app, db): """Create users.""" user1 = create_test_user(email='*****@*****.**', password='******') user2 = create_test_user(email='*****@*****.**', password='******') db.session.commit() return [ {'email': user1.email, 'id': user1.id}, {'email': user2.email, 'id': user2.id}, ]
def test_clean_session_table(task_app): """Test clean session table.""" # set session lifetime task_app.permanent_session_lifetime = timedelta(seconds=20) # protected page @task_app.route("/test", methods=["GET"]) @login_required def test(): return "test" with task_app.test_request_context(): user1 = create_test_user(email="*****@*****.**") user2 = create_test_user(email="*****@*****.**") with task_app.test_client() as client: client.post( url_for_security("login"), data=dict( email=user1.email, password=user1.password_plaintext, ), ) assert len(SessionActivity.query.all()) == 1 sleep(15) with task_app.test_client() as client: client.post( url_for_security("login"), data=dict( email=user2.email, password=user2.password_plaintext, ), ) assert len(SessionActivity.query.all()) == 2 sleep(10) clean_session_table.s().apply() assert len(SessionActivity.query.all()) == 1 protected_url = url_for("test") res = client.get(protected_url) assert res.status_code == 200 sleep(15) clean_session_table.s().apply() assert len(SessionActivity.query.all()) == 0 res = client.get(protected_url) # check if the user is really logout assert res.status_code == 302
def test_registration_view(api): app = api with app.app_context(): create_test_user(email="*****@*****.**") db.session.commit() with app.test_client() as client: url = url_for("invenio_accounts_rest_auth.register") # Missing fields res = client.post(url) assert_error_resp( res, ( ("password", "required"), ("email", "required"), ), ) # Invalid fields res = client.post(url, data=dict(email="invalid-email", password="******")) assert_error_resp( res, ( ("password", "length"), ("email", "not a valid email"), ), ) # Existing user res = client.post(url, data=dict(email="*****@*****.**", password="******")) assert_error_resp( res, (("email", "[email protected] is already associated"), )) # Successful registration res = client.post(url, data=dict(email="*****@*****.**", password="******")) payload = get_json(res) assert res.status_code == 200 assert payload["id"] == 2 assert payload["email"] == "*****@*****.**" session_cookie = next(c for c in client.cookie_jar if c.name == "session") assert session_cookie is not None assert session_cookie.value res = client.get(url_for("invenio_accounts_rest_auth.user_info")) assert res.status_code == 200
def users(app): """Create users.""" user1 = create_test_user(email='*****@*****.**', password='******') user2 = create_test_user(email='*****@*****.**', password='******') return [ {'email': user1.email, 'id': user1.id, 'password': user1.password_plaintext, 'obj': user1}, {'email': user2.email, 'id': user2.id, 'password': user2.password_plaintext, 'obj': user2}, ]
def users(app, db): """Create users.""" user1 = create_test_user(email='*****@*****.**', password='******') user2 = create_test_user(email='*****@*****.**', password='******') user1.allowed_token = Token.create_personal(name='allowed_token', user_id=user1.id, scopes=[]).access_token user2.allowed_token = Token.create_personal(name='allowed_token', user_id=user2.id, scopes=[]).access_token return {'authorized': user1, 'unauthorized': user2}
def users(app, db): """Create users.""" user1 = create_test_user(email='*****@*****.**', password='******') user2 = create_test_user(email='*****@*****.**', password='******') user_admin = create_test_user(email='*****@*****.**', password='******') with db.session.begin_nested(): # set admin permissions db.session.add(ActionUsers(action=action_admin_access.value, user=user_admin)) db.session.commit() return [{'email': user1.email, 'id': user1.id}, {'email': user2.email, 'id': user2.id}, {'email': user_admin.email, 'id': user_admin.id}]
def test_view_list_sessions(app, app_i18n): """Test view list sessions.""" with app.test_request_context(): user1 = create_test_user(email='*****@*****.**') user2 = create_test_user(email='*****@*****.**') with app.test_client() as client: client.post(url_for_security('login'), data=dict( email=user1.email, password=user1.password_plaintext, )) with app.test_client() as client: client.post(url_for_security('login'), data=dict( email=user2.email, password=user2.password_plaintext, )) # get the list of user 2 sessions url = url_for('invenio_accounts.security') res = client.get(url) assert res.status_code == 200 # check session for user 1 is not in the list sessions_1 = SessionActivity.query.filter_by( user_id=user1.id).all() assert len(sessions_1) == 1 assert sessions_1[0].sid_s not in res.data.decode('utf-8') # check session for user 2 is in the list sessions_2 = SessionActivity.query.filter_by( user_id=user2.id).all() assert len(sessions_2) == 1 assert sessions_2[0].sid_s in res.data.decode('utf-8') # test user 2 to delete user 1 session url = url_for('invenio_accounts.revoke_session') res = client.post(url, data={'sid_s': sessions_1[0].sid_s}) assert res.status_code == 302 assert SessionActivity.query.filter_by( user_id=user1.id, sid_s=sessions_1[0].sid_s).count() == 1 # test user 2 to delete user 1 session url = url_for('invenio_accounts.revoke_session') res = client.post(url, data={'sid_s': sessions_2[0].sid_s}) assert res.status_code == 302 assert SessionActivity.query.filter_by( user_id=user1.id, sid_s=sessions_2[0].sid_s).count() == 0
def test_user_login(app): """Test users' high-level login process.""" with app.app_context(): user = create_test_user('*****@*****.**') with app.test_client() as client: login_user_via_view(client, user.email, user.password_plaintext) assert client_authenticated(client)
def test_repeated_login_session_population(app): """Verify session population on repeated login. Check that the number of SessionActivity entries match the number of sessions in the kv-store, when logging in with one user. """ with app.app_context(): user = testutils.create_test_user('*****@*****.**') query = db.session.query(SessionActivity) assert query.count() == len(app.kvsession_store.keys()) with app.test_client() as client: # After logging in, there should be one session in the kv-store and # one SessionActivity testutils.login_user_via_view(client, user=user) assert testutils.client_authenticated(client) query = db.session.query(SessionActivity) assert query.count() == 1 assert query.count() == len(app.kvsession_store.keys()) # Sessions are not deleted upon logout client.get(flask_security.url_for_security('logout')) assert len(app.kvsession_store.keys()) == 1 query = db.session.query(SessionActivity) assert query.count() == len(app.kvsession_store.keys()) # After logging out and back in, the number of sessions correspond # to the number of SessionActivity entries. testutils.login_user_via_view(client, user=user) query = db.session.query(SessionActivity) assert query.count() == len(app.kvsession_store.keys())
def test_create_sessions_for_user(app): """Test testutils.py:create_sessions_for_user.""" InvenioAccounts(app) app.register_blueprint(blueprint) user = testutils.create_test_user() assert len(user.active_sessions) == 0 res = testutils.create_sessions_for_user(user=user) assert len(user.active_sessions) == 1 assert res['user'] == user # Cookie is retrievable from the client. sid_s = user.active_sessions[0].sid_s client_one = res['clients'][0] cookie = testutils.get_cookie_from_client(client_one) assert sid_s == testutils.get_sid_s_from_cookie(cookie) # The client is still authenticated with client_one as client: assert testutils.client_authenticated(client) # Repeated calls create new sessions. res = testutils.create_sessions_for_user(app=app, user=user) assert len(user.active_sessions) == 2 assert len(res['clients']) == 1 # No user argument (fails b/c `create_test_user` has static default values) # res = testutils.create_sessions_for_user(app=app) # user_two = res['user'] # assert not user_two == user # assert len(user_two.active_sessions) == 1 n = 3 res = testutils.create_sessions_for_user(user=user, n=n) assert len(user.active_sessions) == 2+n assert len(res['clients']) == n
def test_create_sessions_for_user(app): """Test testutils.py:create_sessions_for_user.""" InvenioAccounts(app) app.register_blueprint(blueprint) user = testutils.create_test_user() assert len(user.active_sessions) == 0 res = testutils.create_sessions_for_user(user=user) assert len(user.active_sessions) == 1 assert res['user'] == user # Cookie is retrievable from the client. sid_s = user.active_sessions[0].sid_s client_one = res['clients'][0] cookie = testutils.get_cookie_from_client(client_one) assert sid_s == testutils.get_sid_s_from_cookie(cookie) # The client is still authenticated with client_one as client: assert testutils.client_authenticated(client) # Repeated calls create new sessions. res = testutils.create_sessions_for_user(app=app, user=user) assert len(user.active_sessions) == 2 assert len(res['clients']) == 1 # No user argument (fails b/c `create_test_user` has static default values) # res = testutils.create_sessions_for_user(app=app) # user_two = res['user'] # assert not user_two == user # assert len(user_two.active_sessions) == 1 n = 3 res = testutils.create_sessions_for_user(user=user, n=n) assert len(user.active_sessions) == 2 + n assert len(res['clients']) == n
def test_repeated_login_session_population(app): """Verify session population on repeated login.""" with app.app_context(): user = testutils.create_test_user('*****@*****.**') query = db.session.query(SessionActivity) assert query.count() == len(app.kvsession_store.keys()) with app.test_client() as client: # After logging in, there should be one session in the kv-store and # one SessionActivity testutils.login_user_via_view(client, user=user) assert testutils.client_authenticated(client) query = db.session.query(SessionActivity) assert query.count() == 1 assert len(app.kvsession_store.keys()) == 1 first_login_session_id = app.kvsession_store.keys()[0] # Sessions are not deleted upon logout client.get(flask_security.url_for_security('logout')) assert len(app.kvsession_store.keys()) == 1 query = db.session.query(SessionActivity) assert query.count() == 1 assert len(app.kvsession_store.keys()) == 1 # Session id doesn't change after logout assert first_login_session_id == app.kvsession_store.keys()[0] # After logging out and back in, the inital session id in the # sessionstore should be updated, and there should be two # SessionActivity entries (the old one and the regenerated). testutils.login_user_via_view(client, user=user) query = db.session.query(SessionActivity) assert query.count() == 2 assert len(app.kvsession_store.keys()) == 1 assert first_login_session_id != app.kvsession_store.keys()[0]
def test_sessionstore_default_ttl_secs(app): """Test the `default_ttl_secs` field for simplekv sessionstore backends using the TimeToLive-mixin (http://pythonhosted.org/simplekv/index.html#simplekv.TimeToLiveMixin)""" ttl_seconds = 1 ttl_delta = datetime.timedelta(0, ttl_seconds) sessionstore = RedisStore(redis.StrictRedis()) sessionstore.default_ttl_secs = ttl_seconds ext = InvenioAccounts(app, sessionstore=sessionstore) app.register_blueprint(blueprint) # Verify that the backend supports ttl assert ext.sessionstore.ttl_support user = testutils.create_test_user() with app.test_client() as client: testutils.login_user_via_view(client, user=user) sid = testutils.unserialize_session(flask.session.sid_s) while not sid.has_expired(ttl_delta): pass # When we get here the session should have expired. # But the client is still authenticated. assert testutils.client_authenticated(client)
def test_session_ttl(app): """Test actual/working session expiration/TTL settings.""" ttl_seconds = 1 # Set ttl to "0 days, 1 seconds" ttl_delta = datetime.timedelta(0, ttl_seconds) ext = InvenioAccounts(app) app.register_blueprint(blueprint) assert ext.sessionstore.ttl_support # _THIS_ is what flask_kvsession uses to determine default ttl # sets default ttl to `ttl_seconds` seconds app.config['PERMANENT_SESSION_LIFETIME'] = ttl_delta assert app.permanent_session_lifetime.total_seconds() == ttl_seconds user = testutils.create_test_user() with app.test_client() as client: testutils.login_user_via_view(client, user=user) assert len(testutils.get_kvsession_keys()) == 1 sid = testutils.unserialize_session(flask.session.sid_s) testutils.let_session_expire() assert sid.has_expired(ttl_delta) assert not testutils.client_authenticated(client) # Expired sessions are automagically removed from the sessionstore # Although not _instantly_. while len(testutils.get_kvsession_keys()) > 0: pass assert len(testutils.get_kvsession_keys()) == 0
def test_sessionstore_default_ttl_secs(app): """Test the `default_ttl_secs` field for simplekv sessionstore. See http://simplekv.readthedocs.io/index.html#simplekv.TimeToLiveMixin. """ if type(app.kvsession_store) is not RedisStore: pytest.skip('TTL support needed, this test requires Redis.') ttl_seconds = 3 ttl_delta = datetime.timedelta(0, ttl_seconds) sessionstore = app.kvsession_store sessionstore.default_ttl_secs = ttl_seconds # Verify that the backend supports ttl assert sessionstore.ttl_support app.kvsession_store = sessionstore with app.app_context(): user = testutils.create_test_user('*****@*****.**') with app.test_client() as client: testutils.login_user_via_view(client, user=user) sid = testutils.unserialize_session(session.sid_s) while not sid.has_expired(ttl_delta): pass # When we get here the session should have expired. # But the client is still authenticated. assert testutils.client_authenticated(client)
def test_repeated_login_session_population(app): """Verify that the number of SessionActivity entries match the number of sessions in the kv-store, when logging in with one user.""" InvenioAccounts(app) app.register_blueprint(blueprint) user = testutils.create_test_user() query = _datastore.db.session.query(SessionActivity) assert query.count() == len(testutils.get_kvsession_keys()) with app.test_client() as client: # After logging in, there should be one session in the kv-store and # one SessionActivity testutils.login_user_via_view(client, user=user) assert testutils.client_authenticated(client) query = _datastore.db.session.query(SessionActivity) assert query.count() == 1 assert query.count() == len(testutils.get_kvsession_keys()) # Sessions are not deleted upon logout client.get(flask_security.url_for_security('logout')) assert len(testutils.get_kvsession_keys()) == 1 query = _datastore.db.session.query(SessionActivity) assert query.count() == len(testutils.get_kvsession_keys()) # After logging out and back in, the number of sessions correspond to # the number of SessionActivity entries. testutils.login_user_via_view(client, user=user) query = _datastore.db.session.query(SessionActivity) assert query.count() == len(testutils.get_kvsession_keys())
def test_session_deletion(app): """Test that a user/client is no longer authenticated when its session is deleted via `delete_session`.""" InvenioAccounts(app) app.register_blueprint(blueprint) user = testutils.create_test_user() with app.test_client() as client: testutils.login_user_via_view(client, user=user) assert testutils.client_authenticated(client) assert len(user.active_sessions) == 1 saved_sid_s = flask.session.sid_s delete_session(saved_sid_s) # The user now has no active sessions assert len(testutils.get_kvsession_keys()) == 0 assert len(user.active_sessions) == 0 query = _datastore.db.session.query(SessionActivity) assert query.count() == 0 # After deleting the session, the client is not authenticated assert not testutils.client_authenticated(client) # A new session is created in the kv-sessionstore, but its # sid_s is different and the user is not authenticated with it. assert len(testutils.get_kvsession_keys()) == 1 assert not flask.session.sid_s == saved_sid_s assert not testutils.client_authenticated(client)
def test_sip_model(db): """Test the SIP model.""" user1 = create_test_user('*****@*****.**') # Valid agent JSON agent1 = {'email': '*****@*****.**', 'ip_address': '1.1.1.1'} # Invalid agent JSON agent2 = { 'email': ['should', 'not', 'be', 'a', 'list'], 'ip_address': {'definitely': 'not', 'a': 'dict'}, } # Agent JSON with wrong schema agent3 = { 'email': '*****@*****.**', 'ip_address': '1.1.1.1', '$schema': 'http://incorrect/agent/schema.json', } sip1 = SIP.create(user_id=user1.id, agent=agent1) assert sip1.user == user1 SIP.create() SIP.create(user_id=user1.id, agent=agent1) assert SIP.query.count() == 3 pytest.raises(ValidationError, SIP.create, agent=agent2) pytest.raises(SIPUserDoesNotExist, SIP.create, user_id=5) pytest.raises(JSONSchemaNotFound, SIP.create, agent=agent3) db.session.commit()
def test_session_deletion(app): """Test if user is not authenticated when session is deleted.""" with app.app_context(): user = testutils.create_test_user('*****@*****.**') with app.test_client() as client: testutils.login_user_via_view(client, user=user) assert testutils.client_authenticated(client) assert len(user.active_sessions) == 1 saved_sid_s = session.sid_s delete_session(saved_sid_s) db.session.commit() # The user now has no active sessions assert len(app.kvsession_store.keys()) == 0 assert len(user.active_sessions) == 0 query = db.session.query(SessionActivity) assert query.count() == 0 # After deleting the session, the client is not authenticated assert not testutils.client_authenticated(client) # A new session is created in the kv-sessionstore, but its # sid_s is different and the user is not authenticated with it. assert len(app.kvsession_store.keys()) == 1 assert not session.sid_s == saved_sid_s assert not testutils.client_authenticated(client)
def test_session_ttl(app): """Test actual/working session expiration/TTL settings.""" if type(app.kvsession_store) is not RedisStore: pytest.skip('TTL support needed, this test requires Redis.') ttl_seconds = 3 # Set ttl to "0 days, 1 seconds" ttl_delta = datetime.timedelta(0, ttl_seconds) assert app.kvsession_store.ttl_support # _THIS_ is what flask_kvsession uses to determine default ttl # sets default ttl to `ttl_seconds` seconds app.config['PERMANENT_SESSION_LIFETIME'] = ttl_delta assert app.permanent_session_lifetime.total_seconds() == ttl_seconds with app.app_context(): user = testutils.create_test_user('*****@*****.**') with app.test_client() as client: testutils.login_user_via_view(client, user=user) assert len(app.kvsession_store.keys()) == 1 sid = testutils.unserialize_session(session.sid_s) time.sleep(ttl_seconds + 1) assert sid.has_expired(ttl_delta) assert not testutils.client_authenticated(client) # Expired sessions are automagically removed from the sessionstore # Although not _instantly_. while len(app.kvsession_store.keys()) > 0: pass assert len(app.kvsession_store.keys()) == 0
def admin_user(db): """Permission for admin users.""" perms = [ location_update_all, bucket_read_all, bucket_read_versions_all, bucket_update_all, bucket_listmultiparts_all, object_read_all, object_read_version_all, object_delete_all, object_delete_version_all, multipart_read_all, multipart_delete_all, bucket_read_all, object_read_all, bucket_update_all, object_delete_all, multipart_read_all, object_read_all, ] admin = Role(name='admin') for perm in perms: db.session.add(ActionRoles.allow(perm, role=admin)) admin_user = create_test_user(email='*****@*****.**', password='******', active=True) admin.users.append(admin_user) db.session.commit() yield admin_user
def test_repeated_login_session_population(app): """Verify session population on repeated login.""" with app.app_context(): user = testutils.create_test_user('*****@*****.**') query = db.session.query(SessionActivity) assert query.count() == len(app.kvsession_store.keys()) with app.test_client() as client: # After logging in, there should be one session in the kv-store and # one SessionActivity testutils.login_user_via_view(client, user=user) assert testutils.client_authenticated(client) query = db.session.query(SessionActivity) assert query.count() == 1 assert len(app.kvsession_store.keys()) == 1 first_login_session_id = app.kvsession_store.keys()[0] # SessionActivity is deleted upon logout client.get(flask_security.url_for_security('logout')) query = db.session.query(SessionActivity) assert query.count() == 0 # Session id changes after logout assert len(app.kvsession_store.keys()) == 1 assert first_login_session_id != app.kvsession_store.keys()[0] # After logging out and back in, the should be one # SessionActivity entry. testutils.login_user_via_view(client, user=user) query = db.session.query(SessionActivity) assert query.count() == 1 assert len(app.kvsession_store.keys()) == 1 assert first_login_session_id != app.kvsession_store.keys()[0]
def test_load_permissions_on_identity_loaded(app): """Check that the needs are loaded properly in the user Identity.""" InvenioAccess(app) with app.test_request_context(): identity_changed.send(current_app._get_current_object(), identity=AnonymousIdentity()) assert g.identity.provides == {any_user} with app.test_request_context(): user = testutils.create_test_user('*****@*****.**') login_user(user) assert g.identity.provides == { any_user, authenticated_user, UserNeed(user.id) } logout_user() # FIXME: The user is still authenticatd when the identity loader # is called during logout. We could filter on AnonymousIdentity, but # This would be inconsistent as the UserNeed(user.id) is still there. # This will pass even if it is unexpected: # assert g.identity.provides == { # any_user, authenticated_user, UserNeed(user.id) # } # Forcing the identity to reload again cleans the mess. In practice # this won't be needed as the identity is reloaded between requests. identity_changed.send(current_app._get_current_object(), identity=AnonymousIdentity()) assert g.identity.provides == {any_user}
def test_Archive(db): """Test the Archive model class.""" assert Archive.query.count() == 0 # we create an SIP, it will automatically create an Archive via signals user = create_test_user('*****@*****.**') sip = SIP.create(True, user_id=user.id, agent={'test': 'test'}) db.session.commit() assert Archive.query.count() == 1 ark = Archive.get_from_sip(sip.id) assert ark.sip.user.id == sip.user.id assert ark.status == ArchiveStatus.NEW assert ark.accession_id is None assert ark.archivematica_id is None # let's change the object ark.status = ArchiveStatus.REGISTERED ark.accession_id = '08' ark.archivematica_id = sip.id db.session.commit() ark = Archive.get_from_accession_id('08') assert Archive.query.count() == 1 assert ark.status == ArchiveStatus.REGISTERED assert ark.archivematica_id == sip.id # we try to get a non existing record assert Archive.get_from_sip(uuid.uuid4()) is None
def test_change_password_view(api): app = api with app.app_context(): normal_user = create_test_user(email='*****@*****.**') db.session.commit() with app.test_client() as client: url = url_for('invenio_accounts_rest_auth.change_password') # Not authorized user res = client.post(url, data=dict(password='******', new_password='******')) assert_error_resp( res, ((None, 'server could not verify that you are authorized'), ), expected_status_code=401) # Logged in user _login_user(client, normal_user) # Same password res = client.post(url, data=dict(password='******', new_password='******')) assert_error_resp( res, (('password', 'new password must be different'), )) # Valid password res = client.post(url, data=dict(password='******', new_password='******')) payload = get_json(res) assert 'successfully changed your password' in payload['message'] # Log in using new password _login_user(client, normal_user, password='******')
def test_session_ttl(app): """Test actual/working session expiration/TTL settings.""" if type(app.kvsession_store) is not RedisStore: pytest.skip('TTL support needed, this test requires Redis.') ttl_seconds = 1 # Set ttl to "0 days, 1 seconds" ttl_delta = datetime.timedelta(0, ttl_seconds) assert app.kvsession_store.ttl_support # _THIS_ is what flask_kvsession uses to determine default ttl # sets default ttl to `ttl_seconds` seconds app.config['PERMANENT_SESSION_LIFETIME'] = ttl_delta assert app.permanent_session_lifetime.total_seconds() == ttl_seconds with app.app_context(): user = testutils.create_test_user() with app.test_client() as client: testutils.login_user_via_view(client, user=user) assert len(app.kvsession_store.keys()) == 1 sid = testutils.unserialize_session(flask.session.sid_s) time.sleep(ttl_seconds + 1) assert sid.has_expired(ttl_delta) assert not testutils.client_authenticated(client) # Expired sessions are automagically removed from the sessionstore # Although not _instantly_. while len(app.kvsession_store.keys()) > 0: pass assert len(app.kvsession_store.keys()) == 0
def test_session_deletion(app): """Test that a user/client is no longer authenticated when its session is deleted via `delete_session`.""" with app.app_context(): user = testutils.create_test_user() with app.test_client() as client: testutils.login_user_via_view(client, user=user) assert testutils.client_authenticated(client) assert len(user.active_sessions) == 1 saved_sid_s = flask.session.sid_s delete_session(saved_sid_s) db.session.commit() # The user now has no active sessions assert len(app.kvsession_store.keys()) == 0 assert len(user.active_sessions) == 0 query = db.session.query(SessionActivity) assert query.count() == 0 # After deleting the session, the client is not authenticated assert not testutils.client_authenticated(client) # A new session is created in the kv-sessionstore, but its # sid_s is different and the user is not authenticated with it. assert len(app.kvsession_store.keys()) == 1 assert not flask.session.sid_s == saved_sid_s assert not testutils.client_authenticated(client)
def test_reset_password_view(api): from flask_security.recoverable import generate_reset_password_token app = api with app.app_context(): normal_user = create_test_user(email='*****@*****.**') # Generate token token = generate_reset_password_token(normal_user) db.session.commit() with app.test_client() as client: url = url_for('invenio_accounts_rest_auth.reset_password') res = client.post(url, data=dict(password='******', token=token)) payload = get_json(res) assert res.status_code == 200 assert 'You successfully reset your password' in payload['message'] # Login using new password res = client.post(url, data=dict(email='*****@*****.**', password='******')) payload = get_json(res) assert res.status_code == 200 assert payload['id'] == normal_user.id assert payload['email'] == normal_user.email session_cookie = next(c for c in client.cookie_jar if c.name == 'session') assert session_cookie is not None assert session_cookie.value
def test_repeated_login_session_population(app): """Verify session population on repeated login. Check that the number of SessionActivity entries match the number of sessions in the kv-store, when logging in with one user. """ with app.app_context(): user = testutils.create_test_user() query = db.session.query(SessionActivity) assert query.count() == len(app.kvsession_store.keys()) with app.test_client() as client: # After logging in, there should be one session in the kv-store and # one SessionActivity testutils.login_user_via_view(client, user=user) assert testutils.client_authenticated(client) query = db.session.query(SessionActivity) assert query.count() == 1 assert query.count() == len(app.kvsession_store.keys()) # Sessions are not deleted upon logout client.get(flask_security.url_for_security('logout')) assert len(app.kvsession_store.keys()) == 1 query = db.session.query(SessionActivity) assert query.count() == len(app.kvsession_store.keys()) # After logging out and back in, the number of sessions correspond # to the number of SessionActivity entries. testutils.login_user_via_view(client, user=user) query = db.session.query(SessionActivity) assert query.count() == len(app.kvsession_store.keys())
def test_model_featured_community(app): """Test the featured community model and actions.""" with app.app_context(): # Create a user and two communities t1 = datetime.now() user1 = create_test_user() comm1 = Community(id='comm1', id_user=user1.id) comm2 = Community(id='comm2', id_user=user1.id) db.session.add(comm1) db.session.add(comm2) db.session.commit() # Create two community featurings starting at different times fc1 = FeaturedCommunity(id_community=comm1.id, start_date=t1 + timedelta(days=1)) fc2 = FeaturedCommunity(id_community=comm2.id, start_date=t1 + timedelta(days=3)) db.session.add(fc1) db.session.add(fc2) # Check the featured community at three different points in time assert FeaturedCommunity.get_featured_or_none(start_date=t1) is None assert FeaturedCommunity.get_featured_or_none( start_date=t1 + timedelta(days=2)) is comm1 assert FeaturedCommunity.get_featured_or_none( start_date=t1 + timedelta(days=4)) is comm2
def test_sessionstore_default_ttl_secs(app): """Test the `default_ttl_secs` field for simplekv sessionstore. See http://pythonhosted.org/simplekv/index.html#simplekv.TimeToLiveMixin. """ if type(app.kvsession_store) is not RedisStore: pytest.skip('TTL support needed, this test requires Redis.') ttl_seconds = 1 ttl_delta = datetime.timedelta(0, ttl_seconds) sessionstore = app.kvsession_store sessionstore.default_ttl_secs = ttl_seconds # Verify that the backend supports ttl assert sessionstore.ttl_support app.kvsession_store = sessionstore with app.app_context(): user = testutils.create_test_user() with app.test_client() as client: testutils.login_user_via_view(client, user=user) sid = testutils.unserialize_session(flask.session.sid_s) while not sid.has_expired(ttl_delta): pass # When we get here the session should have expired. # But the client is still authenticated. assert testutils.client_authenticated(client)
def test_create_test_user_defaults(app): """Test the default values for testutils.py:create_test_user.""" with app.app_context(): user = testutils.create_test_user() with app.test_client() as client: testutils.login_user_via_view(client, user.email, user.password_plaintext) assert testutils.client_authenticated(client)
def test_registration_view(api): app = api with app.app_context(): create_test_user(email='*****@*****.**') db.session.commit() with app.test_client() as client: url = url_for('invenio_accounts_rest_auth.register') # Missing fields res = client.post(url) assert_error_resp(res, ( ('password', 'required'), ('email', 'required'), )) # Invalid fields res = client.post(url, data=dict(email='invalid-email', password='******')) assert_error_resp(res, ( ('password', 'length'), ('email', 'not a valid email'), )) # Existing user res = client.post(url, data=dict(email='*****@*****.**', password='******')) assert_error_resp( res, (('email', '[email protected] is already associated'), )) # Successful registration res = client.post(url, data=dict(email='*****@*****.**', password='******')) payload = get_json(res) assert res.status_code == 200 assert payload['id'] == 2 assert payload['email'] == '*****@*****.**' session_cookie = next(c for c in client.cookie_jar if c.name == 'session') assert session_cookie is not None assert session_cookie.value res = client.get(url_for('invenio_accounts_rest_auth.user_info')) assert res.status_code == 200
def test_clean_session_table(task_app): """Test clean session table.""" # set session lifetime task_app.permanent_session_lifetime = timedelta(seconds=20) # protected page @task_app.route('/test', methods=['GET']) @login_required def test(): return 'test' with task_app.test_request_context(): user1 = create_test_user(email='*****@*****.**') user2 = create_test_user(email='*****@*****.**') with task_app.test_client() as client: client.post(url_for_security('login'), data=dict( email=user1.email, password=user1.password_plaintext, )) assert len(SessionActivity.query.all()) == 1 sleep(15) with task_app.test_client() as client: client.post(url_for_security('login'), data=dict( email=user2.email, password=user2.password_plaintext, )) assert len(SessionActivity.query.all()) == 2 sleep(10) clean_session_table.s().apply() assert len(SessionActivity.query.all()) == 1 protected_url = url_for('test') res = client.get(protected_url) assert res.status_code == 200 sleep(15) clean_session_table.s().apply() assert len(SessionActivity.query.all()) == 0 res = client.get(protected_url) # check if the user is really logout assert res.status_code == 302
def permissions(db, bucket): """Permission for users.""" users = { None: None, } for user in [ 'auth', 'location', 'bucket', 'objects', 'objects-read-version']: users[user] = create_test_user( email='{0}@invenio-software.org'.format(user), password='******', active=True ) location_perms = [ location_update_all, bucket_read_all, bucket_read_versions_all, bucket_update_all, bucket_listmultiparts_all, object_read_all, object_read_version_all, object_delete_all, object_delete_version_all, multipart_read_all, multipart_delete_all, ] bucket_perms = [ bucket_read_all, object_read_all, bucket_update_all, object_delete_all, multipart_read_all, ] objects_perms = [ object_read_all, ] for perm in location_perms: db.session.add(ActionUsers( action=perm.value, user=users['location'])) for perm in bucket_perms: db.session.add(ActionUsers( action=perm.value, argument=str(bucket.id), user=users['bucket'])) for perm in objects_perms: db.session.add(ActionUsers( action=perm.value, argument=str(bucket.id), user=users['objects'])) db.session.commit() yield users
def users(app, db): """Demo users.""" sender = create_test_user( email='*****@*****.**', password='******', confirmed_at=datetime.utcnow(), ) receiver = create_test_user( email='*****@*****.**', password='******', ) users = { "sender": dict(id=sender.id), "receiver": dict(id=receiver.id), } return users
def test_session_activity_model(app): """Test SessionActivity model.""" ext = InvenioAccounts(app) app.register_blueprint(blueprint) # SessionActivity table is in the datastore database. datastore = app.extensions['invenio-accounts'].datastore inspector = inspect(datastore.db.engine) assert 'accounts_user_session_activity' in inspector.get_table_names() user = testutils.create_test_user() # Create a new SessionActivity object, put it in the datastore. session_activity = SessionActivity(user_id=user.get_id(), sid_s="teststring") database = datastore.db # the `created` field is magicked in via the Timestamp mixin class assert not session_activity.created assert not session_activity.id database.session.add(session_activity) # Commit it to the books. database.session.commit() assert session_activity.created assert session_activity.id assert len(user.active_sessions) == 1 # Now how does this look on the user object? assert session_activity == user.active_sessions[0] session_two = SessionActivity(user_id=user.get_id(), sid_s="testring_2") database.session.add(session_two) # Commit it to the books. database.session.commit() assert len(user.active_sessions) == 2 # Check #columns in table queried = database.session.query(SessionActivity) assert queried.count() == 2 active_sessions = queried.all() assert session_activity.sid_s in [x.sid_s for x in active_sessions] assert session_two in queried.filter( SessionActivity.sid_s == session_two.sid_s) assert queried.count() == 2 # `.filter` doesn't change the query # Test session deletion session_to_delete = user.active_sessions[0] database.session.delete(session_to_delete) assert len(user.active_sessions) == 2 # Not yet updated. assert queried.count() == 1 # Deletion is visible on `user` once database session is commited. database.session.commit() assert len(user.active_sessions) == 1 assert user.active_sessions[0].sid_s != session_to_delete.sid_s
def test_model_init(app): """Test basic model initialization and actions.""" with app.app_context(): # Init the User and the Community user1 = create_test_user() comm1 = Community(id='comm1', id_user=user1.id) db.session.add(comm1) db.session.commit() communities_key = app.config["COMMUNITIES_RECORD_KEY"] # Create a record and accept it into the community by creating an # InclusionRequest and then calling the accept action rec1 = Record.create({'title': 'Foobar'}) InclusionRequest.create(community=comm1, record=rec1) assert InclusionRequest.query.count() == 1 comm1.accept_record(rec1) assert 'comm1' in rec1[communities_key] assert InclusionRequest.query.count() == 0 # Likewise, reject a record from the community rec2 = Record.create({'title': 'Bazbar'}) InclusionRequest.create(community=comm1, record=rec2) assert InclusionRequest.query.count() == 1 comm1.reject_record(rec2) assert communities_key not in rec2 # dict key should not be created assert InclusionRequest.query.count() == 0 # Add record to another community comm2 = Community(id='comm2', id_user=user1.id) db.session.add(comm2) db.session.commit() InclusionRequest.create(community=comm2, record=rec1) comm2.accept_record(rec1) assert communities_key in rec1 assert len(rec1[communities_key]) == 2 assert comm1.id in rec1[communities_key] assert comm2.id in rec1[communities_key] # Accept/reject a record to/from a community without inclusion request rec3 = Record.create({'title': 'Spam'}) pytest.raises(InclusionRequestMissingError, comm1.accept_record, rec3) pytest.raises(InclusionRequestMissingError, comm1.reject_record, rec3) # Create two inclusion requests comm3 = Community(id='comm3', id_user=user1.id) db.session.add(comm3) db.session.commit() InclusionRequest.create(community=comm3, record=rec1) pytest.raises(InclusionRequestExistsError, InclusionRequest.create, community=comm3, record=rec1) # Try to accept a record to a community twice (should raise) # (comm1 is already in rec1) pytest.raises(InclusionRequestObsoleteError, InclusionRequest.create, community=comm1, record=rec1)
def test_login_user_via_session(app): """Test the login-via-view function/hack.""" email = '*****@*****.**' password = '******' with app.app_context(): user = testutils.create_test_user(email, password) with app.test_client() as client: assert not testutils.client_authenticated(client) testutils.login_user_via_session(client, email=user.email) assert testutils.client_authenticated(client)
def test_RecordSIP_create(db, mocker): """Test create method from the API class RecordSIP.""" # we setup a file storage tmppath = tempfile.mkdtemp() db.session.add(Location(name='default', uri=tmppath, default=True)) # setup metadata mtype = SIPMetadataType(title='JSON Test', name='json-test', format='json', schema='url://to/schema') db.session.add(mtype) db.session.commit() # first we create a record recid = uuid.uuid4() pid = PersistentIdentifier.create( 'recid', '1337', object_type='rec', object_uuid=recid, status=PIDStatus.REGISTERED) mocker.patch('invenio_records.api.RecordBase.validate', return_value=True, autospec=True) record = Record.create( {'title': 'record test', '$schema': 'url://to/schema'}, recid) # we add a file to the record bucket = Bucket.create() content = b'Test file\n' RecordsBuckets.create(record=record.model, bucket=bucket) record.files['test.txt'] = BytesIO(content) db.session.commit() # Let's create a SIP user = create_test_user('*****@*****.**') agent = {'email': '*****@*****.**', 'ip_address': '1.1.1.1'} rsip = RecordSIP.create(pid, record, True, user_id=user.id, agent=agent) db.session.commit() # test! assert RecordSIP_.query.count() == 1 assert SIP_.query.count() == 1 assert SIPFile.query.count() == 1 assert SIPMetadata.query.count() == 1 assert len(rsip.sip.files) == 1 assert len(rsip.sip.metadata) == 1 metadata = rsip.sip.metadata[0] assert metadata.type.format == 'json' assert '"title": "record test"' in metadata.content assert rsip.sip.archivable is True # we try with no files rsip = RecordSIP.create(pid, record, True, create_sip_files=False, user_id=user.id, agent=agent) assert SIPFile.query.count() == 1 assert SIPMetadata.query.count() == 2 assert len(rsip.sip.files) == 0 assert len(rsip.sip.metadata) == 1 # finalization rmtree(tmppath)
def create_user_with_access(username, action): user = _datastore.find_user(email=username) if not user: user = create_test_user(email=username, password='******') db_.session.add(ActionUsers.allow( ActionNeed(action), user=user)) db_.session.commit() return user
def test_communities_load(db, communities_dump, featured_dump, logos_dir): """Load the communities JSON dump.""" # Create a single user (with id=1) to meet the community's FK constraint. create_test_user('*****@*****.**', password='******') # Initialize communities bucket (requires a default Location) loc = Location(name='local', uri='file:///tmp', default=True) db.session.add(loc) db.session.commit() initialize_communities_bucket() # Load the communities for data in communities_dump: load_community.delay(data, logos_dir) # Check if community was loaded correctly assert Community.query.count() == 1 c = Community.query.first() assert c.title == 'Invenio Community' assert c.id == 'invenio' # Check object metadata assert ObjectVersion.query.count() == 1 # Check if the logo was created assert ObjectVersion.query.first().key == 'invenio/logo.jpg' # Open the original logo from test data and check if checksums match with open(join(logos_dir, 'invenio.jpg'), 'rb') as fp: logo_checksum = hashlib.md5(fp.read()).hexdigest() assert ObjectVersion.query.first().file.checksum == \ 'md5:{0}'.format(logo_checksum) # Load the community featurings for data in featured_dump: load_featured.delay(data) # Make sure they are loaded correctly assert FeaturedCommunity.query.count() == 1 fc = FeaturedCommunity.query.first() assert fc.community.id == c.id
def create_user_with_role(username, rolename): _datastore = LocalProxy( lambda: current_app.extensions['security'].datastore) user, role = _datastore._prepare_role_modify_args(username, rolename) if not user: user = create_test_user(email=username, password='******') if not role: role = _datastore.create_role(name=rolename) _datastore.add_role_to_user(user, role) return user
def test_login_user_via_view(app): """Test the login-via-view function/hack.""" InvenioAccounts(app) app.register_blueprint(blueprint) email = '*****@*****.**' password = '******' with app.app_context(): user = testutils.create_test_user(email, password) with app.test_client() as client: assert not testutils.client_authenticated(client) testutils.login_user_via_view(client, user.email, user.password_plaintext) assert testutils.client_authenticated(client)