def shib_sp_login(): """The request from shibboleth sp. :return: confirm page when relation is empty """ try: if not current_app.config['SHIB_ACCOUNTS_LOGIN_ENABLED']: return url_for_security('login') shib_session_id = request.form.get('SHIB_ATTR_SESSION_ID', None) if shib_session_id is None or len(shib_session_id) == 0: return url_for_security('login') shib_attr, error = parse_attributes() if error: return url_for_security('login') datastore = RedisStore( redis.StrictRedis.from_url(current_app.config['CACHE_REDIS_URL'])) ttl_sec = int(current_app.config['SHIB_ACCOUNTS_LOGIN_CACHE_TTL']) datastore.put(config.SHIB_CACHE_PREFIX + shib_session_id, bytes(json.dumps(shib_attr), encoding='utf-8'), ttl_secs=ttl_sec) shib_user = ShibUser(shib_attr) rst = shib_user.get_relation_info() """ check the relation of shibboleth user with weko account""" next_url = 'weko_accounts.shib_auto_login' if rst is None: """relation is not existed, cache shibboleth info to redis.""" next_url = 'weko_accounts.shib_login' query_string = { 'SHIB_ATTR_SESSION_ID': shib_session_id, '_method': 'GET' } return url_for(next_url, **query_string) except BaseException: current_app.logger.error('Unexpected error: ', sys.exc_info()[0]) return url_for_security('login')
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 shib_login(): """Get shibboleth user login page. :return: confirm user page when relation is empty """ try: shib_session_id = request.args.get('SHIB_ATTR_SESSION_ID', None) if shib_session_id is None or len(shib_session_id) == 0: return redirect(url_for_security('login')) datastore = RedisStore( redis.StrictRedis.from_url(current_app.config['CACHE_REDIS_URL'])) cache_key = config.SHIB_CACHE_PREFIX + shib_session_id if not datastore.redis.exists(cache_key): return redirect(url_for_security('login')) cache_val = datastore.get(cache_key) if cache_val is None: datastore.delete(cache_key) return redirect(url_for_security('login')) cache_val = json.loads(str(cache_val, encoding='utf-8')) session['shib_session_id'] = shib_session_id csrf_random = generate_random_str(length=64) session['csrf_random'] = csrf_random return render_template(config.WEKO_ACCOUNTS_CONFIRM_USER_TEMPLATE, csrf_random=csrf_random, email=cache_val['shib_mail'] if len(cache_val['shib_mail']) > 0 else '') except BaseException: current_app.logger.error('Unexpected error: ', sys.exc_info()[0]) return abort(400)
def shib_auto_login(): """Create new account and auto login when shibboleth user first login. :return: next url """ try: is_auto_bind = False shib_session_id = request.args.get('SHIB_ATTR_SESSION_ID', None) if shib_session_id is None: shib_session_id = session['shib_session_id'] is_auto_bind = True if shib_session_id is None or len(shib_session_id) == 0: return redirect(url_for_security('login')) datastore = RedisStore( redis.StrictRedis.from_url(current_app.config['CACHE_REDIS_URL'])) cache_key = config.SHIB_CACHE_PREFIX + shib_session_id if not datastore.redis.exists(cache_key): return redirect(url_for_security('login')) cache_val = datastore.get(cache_key) if cache_val is None: datastore.delete(cache_key) return redirect(url_for_security('login')) cache_val = json.loads(str(cache_val, encoding='utf-8')) shib_user = ShibUser(cache_val) if not is_auto_bind: shib_user.get_relation_info() else: shib_user.new_relation_info() if shib_user.shib_user is not None: shib_user.shib_user_login() datastore.delete(cache_key) return redirect(session['next'] if 'next' in session else '/') except BaseException: current_app.logger.error('Unexpected error: ', sys.exc_info()[0]) return abort(400)
def test_read_metadata(client, superuser, admin, moderator, submitter, user, document_with_file): """Test read files permissions.""" users = [superuser, admin, moderator, submitter, user, None] url_files = url_for('invenio_records_files.doc_bucket_api', pid_value=document_with_file.get('pid')) for u, status in zip(users, [200, 200, 200, 200, 200, 200]): if u: login_user_via_session(client, email=u['email']) else: client.get(url_for_security('logout')) res = client.get(url_files) assert res.status_code == status # Masked document document_with_file['masked'] = 'masked_for_all' document_with_file.commit() for u, status in zip(users, [200, 200, 200, 404, 404, 404]): if u: login_user_via_session(client, email=u['email']) else: client.get(url_for_security('logout')) res = client.get(url_files) assert res.status_code == status
def test_login(self): zero = User.query.count() self.assertEqual(0, zero) self.client.post(url_for_security('login'), data={ 'email': '*****@*****.**', 'password': '******' }) self.assertTrue(current_user.is_anonymous) self.client.post(url_for_security('register'), data={ 'email': '*****@*****.**', 'password': '******', 'password_confirm': 'test123' }) cnt = User.query.count() self.assertEqual(1, cnt) self.client.get(url_for_security('logout')) with self.client: it = self.client.post(url_for_security('login'), data={ 'email': '*****@*****.**', 'password': '******' }) t = current_user self.assertIsNotNone(t) self.assertEqual('*****@*****.**', t.email)
def test_client_authenticated(app): """Test for testutils.py:client_authenticated(client). We want to verify that it doesn't return True when the client isn't authenticated/logged in.""" ext = InvenioAccounts(app) # Required for the test app/templates app.register_blueprint(blueprint) email = '*****@*****.**' password = '******' with app.app_context(): change_password_url = url_for_security('change_password') login_url = url_for_security('login') with app.test_client() as client: # At this point we should not be authenticated/logged in as a user assert flask_login.current_user.is_anonymous assert not testutils.client_authenticated(client, test_url=change_password_url) # Test HTTP status code of view when not logged in. response = client.get(change_password_url) assert response.status_code == 302 assert change_password_url not in response.location assert login_url in response.location # Once more, following redirects. response = client.get(change_password_url, follow_redirects=True) assert response.status_code == 200 assert response.location is None # Create a user manually directly in the datastore ext.datastore.create_user(email=email, password=encrypt_password(password)) db.session.commit() # Manual login via view response = client.post(login_url, data={ 'email': email, 'password': password }, environ_base={'REMOTE_ADDR': '127.0.0.1'}) # Client gets redirected after logging in assert response.status_code == 302 assert testutils.client_authenticated(client) assert flask_login.current_user.is_authenticated # `is_authenticated` returns True as long as the user object # isn't anonymous, i.e. it's an actual user. response = client.get(change_password_url) assert response.status_code == 200 response = client.get(change_password_url, follow_redirects=True) assert response.status_code == 200
def test_unique_user_username(self): data = dict(username='******', email='*****@*****.**', password='******', password_confirm='password') self.client.post(url_for_security('register'), data=data) response = self.client.post(url_for_security('register'), data=data) self.assertIn('Username already taken', response.data.decode(response.charset))
def test_client_authenticated(app): """Test for testutils.py:client_authenticated(client). We want to verify that it doesn't return True when the client isn't authenticated/logged in. """ ds = app.extensions["security"].datastore email = "*****@*****.**" password = "******" with app.app_context(): change_password_url = url_for_security("change_password") login_url = url_for_security("login") with app.test_client() as client: # At this point we should not be authenticated/logged in as a user # assert flask_login.current_user.is_anonymous assert not testutils.client_authenticated( client, test_url=change_password_url) # Test HTTP status code of view when not logged in. response = client.get(change_password_url) assert response.status_code == 302 assert change_password_url not in response.location assert login_url in response.location # Once more, following redirects. response = client.get(change_password_url, follow_redirects=True) assert response.status_code == 200 assert response.location is None # Create a user manually directly in the datastore ds.create_user(email=email, password=hash_password(password)) db.session.commit() # Manual login via view response = client.post( login_url, data={ "email": email, "password": password }, environ_base={"REMOTE_ADDR": "127.0.0.1"}, ) # Client gets redirected after logging in assert response.status_code == 302 assert testutils.client_authenticated(client) assert flask_login.current_user.is_authenticated # `is_authenticated` returns True as long as the user object # isn't anonymous, i.e. it's an actual user. response = client.get(change_password_url) assert response.status_code == 200 response = client.get(change_password_url, follow_redirects=True) assert response.status_code == 200
def test_client_authenticated(app): """Test for testutils.py:client_authenticated(client). We want to verify that it doesn't return True when the client isn't authenticated/logged in.""" ext = InvenioAccounts(app) # Required for the test app/templates app.register_blueprint(blueprint) email = '*****@*****.**' password = '******' with app.app_context(): change_password_url = url_for_security('change_password') login_url = url_for_security('login') with app.test_client() as client: # At this point we should not be authenticated/logged in as a user assert flask_login.current_user.is_anonymous assert not testutils.client_authenticated( client, test_url=change_password_url) # Test HTTP status code of view when not logged in. response = client.get(change_password_url) assert response.status_code == 302 assert change_password_url not in response.location assert login_url in response.location # Once more, following redirects. response = client.get(change_password_url, follow_redirects=True) assert response.status_code == 200 assert response.location is None # Create a user manually directly in the datastore ext.datastore.create_user(email=email, password=encrypt_password(password)) db.session.commit() # Manual login via view response = client.post(login_url, data={'email': email, 'password': password}, environ_base={'REMOTE_ADDR': '127.0.0.1'}) # Client gets redirected after logging in assert response.status_code == 302 assert testutils.client_authenticated(client) assert flask_login.current_user.is_authenticated # `is_authenticated` returns True as long as the user object # isn't anonymous, i.e. it's an actual user. response = client.get(change_password_url) assert response.status_code == 200 response = client.get(change_password_url, follow_redirects=True) assert response.status_code == 200
def test_unique_user_username(self): data = dict( username="******", email="*****@*****.**", password="******", password_confirm="password", ) self.client.post(url_for_security("register"), data=data) response = self.client.post(url_for_security("register"), data=data) self.assertIn("Username already taken", response.data.decode(response.charset))
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_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_by_reference_validation(api, users, location, es, data, fields_with_errors, fields_without_errors): with api.test_request_context(), api.test_client() as client: client.post( url_for_security("login"), data={ "email": users[0]["email"], "password": "******" }, ) response = client.post( "/sword/service-document", data=json.dumps(data), headers={ "Content-Disposition": "attachment; by-reference=true", "Content-Type": "application/ld+json", }, ) assert response.status_code == HTTPStatus.BAD_REQUEST assert response.is_json assert response.json["@type"] == "ValidationFailed" # The fields with errors are the superset of the ones we expected; i.e. it shouldn't accept data we # in this test know is wrong print(response.json) assert set(response.json["errors"]) >= set(fields_with_errors) # We know these fields to be good assert not (set(response.json["errors"]) & set(fields_without_errors))
def _create_new_record_version(app): """Create a new version of a record existing before the upgrade.""" from invenio_accounts.models import User from flask_security import url_for_security from flask_security.utils import hash_password from flask import url_for from invenio_oauth2server.models import Token user = User.query.filter(User.email=='*****@*****.**').one() user.password = hash_password('123') token = Token.create_personal( 'other_token', user.id, scopes=[] ) headers = [ ('Authorization', 'Bearer {}'.format(token.access_token)), ('Content-type', 'application/json') ] db.session.commit() with app.test_request_context(): login_url = url_for_security('login') with app.test_client() as client: res = client.post(login_url, data={ 'email': '*****@*****.**', 'password': '******' }) assert res.status_code == 302 url = url_for('b2share_records_rest.b2rec_list', version_of='1033083fedf4408fb5611f23527a926d') res = client.post(url, headers=headers) assert res.status_code == 201
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_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_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_change_thumbnail(self): file = (io.BytesIO(b"abcdef"), 'test.jpg') api_utils.post(url_for_security('login'), data={ 'email': '*****@*****.**', 'password': '******' }, headers={'Authentication-Token': self.token}) bookmark = BookMark(url='dummy.url', img='dummy.png') past_img = bookmark.img self.current_user.bookmarks.append(bookmark) db.session.add(self.current_user) db.session.commit() data = {'img': file, 'id': BookMark.query.first().id} res = self.client.patch(url_for('library.change_thumbnail'), data=data, headers={ 'Authentication-Token': self.token, 'Content-Type': 'multipart/form-data' }) self.assertNotEqual(past_img, bookmark.img) path = Path(app.config['STORAGE_PATH'] + '/' + bookmark.img) self.assertTrue(path.is_file()) self.assertStatus(res, 204)
def login(test_client, email='*****@*****.**', password='******'): return test_client.post( url_for_security('login'), data={ 'email': email, 'password': password, })
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 send_welcome_email(user_record, user): """Send this email when user is created from backend. :param user_record: User record. :param user: User account. """ user_record = user_record.replace_refs() code = user_record['organisation'].get('code', '') plain_platform_name = 'SONAR' pname = platform_name(user_record['organisation']) if pname: plain_platform_name = pname token = generate_reset_password_token(user) reset_link = url_for_security( 'reset_password', token=token, next=f'/{code}', _external=True ) send_email( [user_record['email']], f'{_("Welcome to")} {plain_platform_name}', 'users/email/welcome', { 'user': user_record, 'reset_link': reset_link, 'platform_name': plain_platform_name } )
def login_user_via_view(client, email=None, password=None, user=None, login_url=None): r"""Attempt to log the given user in via the 'login' view on the client. :param client: client to send the request from. :param email: email of user account to log in with. :param password: password of user account to log in with. :param user: If present, ``user.email`` and ``user.password_plaintext`` \ take precedence over the `email` and `password` parameters. :type user: :class:`invenio_accounts.models.User` (with the addition of \ a ``password_plaintext`` field) :param login_url: URL to post login details to. Defaults to the current \ application's login URL. :returns: The response object from the POST to the login form. """ if user is not None: email = user.email password = user.password_plaintext return client.post(login_url or url_for_security('login'), data={ 'email': email, 'password': password }, environ_base={'REMOTE_ADDR': '127.0.0.1'})
def test_deposit_empty(api, users, location): with api.test_request_context(), api.test_client() as client: client.post( url_for_security("login"), data={ "email": users[0]["email"], "password": "******" }, ) response = client.post("/sword/service-document") assert response.status_code == HTTPStatus.CREATED match = re.match("^http://localhost/sword/deposit/([^/]+)$", response.headers["Location"]) assert match is not None pid_value = match.group(1) _, record = pid_resolver.resolve(pid_value) assert dict(record) == { "metadata": {}, "$schema": "http://localhost/schemas/deposits/deposit-v1.0.0.json", "_deposit": { "id": pid_value, "status": "published", "owners": [users[0]["id"]], "created_by": users[0]["id"], }, "_bucket": record.bucket_id, } # POSTing with no metadata, by-reference, or request body should result in no files being created. assert ObjectVersion.query.filter_by(bucket=record.bucket).count() == 0
def _create_new_record_version(app): """Create a new version of a record existing before the upgrade.""" from invenio_accounts.models import User from flask_security import url_for_security from flask_security.utils import hash_password from flask import url_for from invenio_oauth2server.models import Token user = User.query.filter(User.email == '*****@*****.**').one() user.password = hash_password('123') token = Token.create_personal('other_token', user.id, scopes=[]) headers = [('Authorization', 'Bearer {}'.format(token.access_token)), ('Content-type', 'application/json')] db.session.commit() with app.test_request_context(): login_url = url_for_security('login') with app.test_client() as client: res = client.post(login_url, data={ 'email': '*****@*****.**', 'password': '******' }) assert res.status_code == 302 url = url_for('b2share_records_rest.b2rec_list', version_of='1033083fedf4408fb5611f23527a926d') res = client.post(url, headers=headers) assert res.status_code == 201
def test_put_metadata_document(api, users, location, es): with api.test_request_context(), api.test_client() as client: client.post( url_for_security("login"), data={ "email": users[0]["email"], "password": "******" }, ) record = SWORDDeposit.create({}) record.commit() db.session.commit() response = client.put( "/sword/deposit/{}/metadata".format(record.pid.pid_value), headers={ "Metadata-Format": "http://purl.org/net/sword/3.0/types/Metadata", "Content-Type": "application/ld+json", }, data=json.dumps({}), ) assert response.status_code == HTTPStatus.NO_CONTENT record = SWORDDeposit.get_record(record.id) assert (record["swordMetadataSourceFormat"] == "http://purl.org/net/sword/3.0/types/Metadata") assert any("http://purl.org/net/sword/3.0/terms/formattedMetadata" in link["rel"] and link["metadataFormat"] == "http://purl.org/net/sword/3.0/types/Metadata" for link in record.get_status_as_jsonld()["links"])
def resend_invitation(user_id): """ For the purpose of re-sending a lost "activation" email, this endpoint allows an admin to re-send that message. This is a convenience option in the admin's dashboard (on the individual user's profile) but it is no different than the reset password flow. We simply generate a reset password token and send the email using our "invite" custom template. Something to note: You can instruct your users who might have misplaced their "activation" email to just use the password reset link from the login page. It's the same flow and they can request a new password with their email address. """ user = Users.query.get(user_id) try: link = url_for_security('reset_password', token=generate_reset_password_token(user), _external=True) subject = 'Activate your account for the Health Tracker' if Config.ORG: subject = f'Activate your account for the {Config.ORG} Health Tracker' send_mail(subject, user.email, 'invite_new_user', reset_link=link) flash('User invitation email was sent.', category='success') except Exception as e: app.logger.debug(e) db.session.rollback() flash('User invitation email was not sent, there was an error', category='error') return redirect(url_for('single_user', id=user_id))
def test_get_metadata_document(api, users, location, es): with api.test_request_context(), api.test_client() as client: client.post( url_for_security("login"), data={ "email": users[0]["email"], "password": "******" }, ) record = SWORDDeposit.create({}) record.set_metadata({"dc:title": "Deposit title"}, SWORDMetadata) record.commit() db.session.commit() response = client.get("/sword/deposit/{}".format(record.pid.pid_value)) assert response.status_code == HTTPStatus.OK response = client.get("/sword/deposit/{}/metadata".format( record.pid.pid_value)) assert response.status_code == HTTPStatus.OK assert response.is_json assert response.json == { "@id": "http://localhost/sword/deposit/{}".format(record.pid.pid_value), "@context": "https://swordapp.github.io/swordv3/swordv3.jsonld", "dc:title": "Deposit title", }
def test_webhook_post_no_token(app, tester_id, receiver): ds = app.extensions["security"].datastore with app.test_request_context(): user = ds.get_user(tester_id) with app.test_client() as client: # Manual login via view response = client.post( url_for_security("login"), data={"email": user.email, "password": user.password}, environ_base={"REMOTE_ADDR": "127.0.0.1"}, ) assert response.status_code == 302 assert user.get_id() == current_user.get_id() payload = dict(somekey="somevalue") response = make_request( None, client.post, "invenio_webhooks.event_list", urlargs=dict(receiver_id="test-receiver"), data=payload, code=202, ) make_request( None, client.get, "invenio_webhooks.event_item", urlargs=dict(receiver_id=response.headers["X-Hub-Event"], event_id=response.headers["X-Hub-Delivery"]), data=payload, code=202, )
def test_file_get(fixtures_path, location, es, api, users): with api.test_request_context(), api.test_client() as client: client.post( url_for_security("login"), data={ "email": users[0]["email"], "password": "******" }, ) bagit_record = create_bagit_record(fixtures_path) status_document = bagit_record.get_status_as_jsonld() assert len(status_document["links"]) > 0 expected_content = [] for file in bagit_record.files: with file.obj.file.storage().open() as f: expected_content.append(f.read()) expected_content.sort() actual_content = [] for link in status_document["links"]: response = client.get(link["@id"]) assert response.status_code == HTTPStatus.OK actual_content.append(response.data) actual_content.sort() assert expected_content == actual_content
def before_request(): #flask sessions expire once you close the browser unless you have a permanent session session.permanent = True #By default in Flask, permanent_session_lifetime is set to 31 days. app.permanent_session_lifetime = timedelta(minutes=30) max_login_retry = 5 ip = request.headers['X-Real-IP'] or request.headers['Remote_Addr'] if (not getattr(g.identity, 'user', None)) and request.path == url_for_security('login'): if request.method == 'POST' and request.form['password']: login_retry = session.get('login_retry', 0) + 1 session['login_retry'] = login_retry #使用redis来限制ip登录限制 key = session_ip_prefix + ip with redis.pipeline() as pipe: pipe.incr(key).expire(key, 2 * 3600).execute() #print(type(redis.get(key))) ip_retry_count = int(redis.get(key)) if login_retry > max_login_retry or ip_retry_count > max_login_retry: return render_template('hintInfo.html', msg='登录次数超出限制,请2小时后重试') log.info(ip + " enter into " + request.path or '' + " for " + request.endpoint or '' + " of http method:" + request.method)
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_file_put(api, deposit, files, users): """PUT a deposit file.""" with api.test_request_context(): with api.test_client() as client: old_file_id = files[0].file_id old_filename = files[0].key new_filename = '{0}-new-name'.format(old_filename) # test rename file (without login) res = client.put(url_for('invenio_deposit_rest.depid_file', pid_value=deposit['_deposit']['id'], key=old_filename), data=json.dumps({'filename': new_filename})) assert res.status_code == 401 # login res = client.post(url_for_security('login'), data=dict(email=users[0]['email'], password="******")) # test rename file res = client.put(url_for('invenio_deposit_rest.depid_file', pid_value=deposit['_deposit']['id'], key=old_filename), data=json.dumps({'filename': new_filename})) deposit_id = deposit.id db.session.expunge(deposit.model) deposit = Deposit.get_record(deposit_id) files = list(deposit.files) assert res.status_code == 200 assert new_filename == files[0].key assert old_file_id == files[0].file_id data = json.loads(res.data.decode('utf-8')) obj = files[0] assert data['filename'] == obj.key assert data['checksum'] == obj.file.checksum assert data['id'] == str(obj.file.id)
def test_read_content(client, superuser, admin, moderator, submitter, user, user_without_org, document_with_file): """Test read documents permissions.""" file_name = 'test1.pdf' users = [ superuser, admin, moderator, submitter, user, user_without_org, None ] url_file_content = url_for('invenio_records_files.doc_object_api', pid_value=document_with_file.get('pid'), key=file_name) open_access_code = "coar:c_abf2" document_with_file.files[file_name]['access'] = open_access_code document_with_file.commit() for u, status in zip(users, [200, 200, 200, 200, 200, 200, 200]): if u: login_user_via_session(client, email=u['email']) else: client.get(url_for_security('logout')) res = client.get(url_file_content) assert res.status_code == status # Masked document document_with_file['masked'] = 'masked_for_all' document_with_file.commit() for u, status in zip(users, [200, 200, 200, 404, 404, 404, 404]): if u: login_user_via_session(client, email=u['email']) else: client.get(url_for_security('logout')) res = client.get(url_file_content) assert res.status_code == status del document_with_file['masked'] # restricted files restricted_code = "coar:c_16ec" document_with_file.files[file_name]['access'] = restricted_code document_with_file.files[file_name][ 'restricted_outside_organisation'] = True document_with_file.commit() for u, status in zip(users, [200, 200, 200, 200, 200, 404, 404]): if u: login_user_via_session(client, email=u['email']) else: client.get(url_for_security('logout')) res = client.get(url_file_content) assert res.status_code == status
def test_no_log_in_message_for_logged_in_users(app): """Test the password reset form for logged in users. Password reset form should not show log in or sign up messages for logged in users. """ with app.app_context(): forgot_password_url = url_for_security("forgot_password") with app.test_client() as client: log_in_message = _("Log in").encode("utf-8") sign_up_message = _("Sign up").encode("utf-8") resp = client.get(forgot_password_url) assert resp.status_code == 200 assert log_in_message in resp.data assert sign_up_message in resp.data test_email = "*****@*****.**" test_password = "******" resp = client.post( url_for_security("register"), data=dict( email=test_email, password=test_password, ), environ_base={"REMOTE_ADDR": "127.0.0.1"}, ) resp = client.post( url_for_security("login"), data=dict( email=test_email, password=test_password, ), ) resp = client.get(forgot_password_url, follow_redirects=True) if resp.status_code == 200: # This behavior will be phased out in future Flask-Security # release as per mattupstate/flask-security#291 assert log_in_message not in resp.data assert sign_up_message not in resp.data else: # Future Flask-Security will redirect to post login view when # authenticated user requests password reset page. assert resp.data == client.get(app.config["SECURITY_POST_LOGIN_VIEW"]).data
def test_file_delete(api, deposit, files, users): """Test delete file.""" with api.test_request_context(): # the user is the owner with api.test_client() as client: deposit_id = deposit.id res = client.delete(url_for( 'invenio_deposit_rest.depid_file', pid_value=deposit['_deposit']['id'], key=files[0].key )) assert res.status_code == 401 db.session.expunge(deposit.model) deposit = Deposit.get_record(deposit_id) assert files[0].key in deposit.files assert deposit.files[files[0].key] is not None # login res = client.post(url_for_security('login'), data=dict( email=users[0]['email'], password="******" )) # delete resource res = client.delete(url_for( 'invenio_deposit_rest.depid_file', pid_value=deposit['_deposit']['id'], key=files[0].key )) assert res.status_code == 204 assert res.data == b'' db.session.expunge(deposit.model) deposit = Deposit.get_record(deposit_id) assert files[0].key not in deposit.files # the user is NOT the owner with api.test_client() as client: # login res = client.post(url_for_security('login'), data=dict( email=users[1]['email'], password="******" )) # delete resource res = client.delete(url_for( 'invenio_deposit_rest.depid_file', pid_value=deposit['_deposit']['id'], key=files[0].key )) assert res.status_code == 403
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 login_user_via_view(client, email, password, login_url=None): """Attempt to log the given user in via the 'login' view on the client. Returns the response object. """ return client.post(login_url or url_for_security('login'), data={'email': email, 'password': password}, environ_base={'REMOTE_ADDR': '127.0.0.1'})
def test_view(app): """Test view.""" with app.app_context(): login_url = url_for_security('login') with app.test_client() as client: res = client.get(login_url) assert res.status_code == 200
def _handle_view(self, name, **kwargs): if not self.is_accessible(): if current_user.is_authenticated: # send 403 permission denied return self.inaccessible_callback(name, **kwargs) else: # redirect to login return redirect(url_for_security('login', next=request.path))
def test_file_get(api, deposit, files, users): """Test get file.""" with api.test_request_context(): # the user is the owner with api.test_client() as client: # get resource without login res = client.get(url_for( 'invenio_deposit_rest.depid_file', pid_value=deposit['_deposit']['id'], key=files[0].key )) assert res.status_code == 401 # login res = client.post(url_for_security('login'), data=dict( email=users[0]['email'], password="******" )) # get resource res = client.get(url_for( 'invenio_deposit_rest.depid_file', pid_value=deposit['_deposit']['id'], key=files[0].key )) assert res.status_code == 200 data = json.loads(res.data.decode('utf-8')) obj = files[0] assert data['filename'] == obj.key assert data['checksum'] == obj.file.checksum assert data['id'] == str(obj.file.id) # the user is NOT the owner with api.test_client() as client: # login res = client.post(url_for_security('login'), data=dict( email=users[1]['email'], password="******" )) # get resource res = client.get(url_for( 'invenio_deposit_rest.depid_file', pid_value=deposit['_deposit']['id'], key=files[0].key )) assert res.status_code == 403
def client_authenticated(client, test_url=None): """Attempt to get the change password page with the given client object. Returns True if the client can get the change password page without getting redirected and flask_login's `current_user` object isn't anonymous. """ response = client.get(test_url or url_for_security('change_password')) return (response.status_code == 200 and not flask_login.current_user.is_anonymous)
def test_no_log_in_message_for_logged_in_users(app): """Test the password reset form for logged in users. Password reset form should not show log in or sign up messages for logged in users. """ InvenioAccounts(app) app.register_blueprint(blueprint) with app.app_context(): forgot_password_url = url_for_security('forgot_password') with app.test_client() as client: log_in_message = _('Log In').encode('utf-8') sign_up_message = _('Sign Up').encode('utf-8') resp = client.get(forgot_password_url) assert resp.status_code == 200 assert log_in_message in resp.data assert sign_up_message in resp.data test_email = '*****@*****.**' test_password = '******' resp = client.post(url_for_security('register'), data=dict( email=test_email, password=test_password, ), environ_base={'REMOTE_ADDR': '127.0.0.1'}) resp = client.post(url_for_security('login'), data=dict( email=test_email, password=test_password, )) resp = client.get(forgot_password_url, follow_redirects=True) if resp.status_code == 200: # This behavior will be phased out in future Flask-Security # release as per mattupstate/flask-security#291 assert log_in_message not in resp.data assert sign_up_message not in resp.data else: # Future Flask-Security will redirect to post login view when # authenticated user requests password reset page. assert resp.data == client.get( app.config['SECURITY_POST_LOGIN_VIEW']).data
def _handle_view(self, name, **kwargs): next_url = '/admin' if self.url: next_url = self.url if not current_user.is_authenticated: return redirect(url_for_security('login', next=next_url)) if not self.is_accessible(): return self.inaccessible_callback(name, **kwargs)
def test_view(app): """Test view.""" InvenioAccounts(app) app.register_blueprint(blueprint) with app.app_context(): login_url = url_for_security("login") with app.test_client() as client: res = client.get(login_url) assert res.status_code == 200
def login_user(app): """Login a user.""" with app.test_request_context(): login_url = url_for_security('login') def login(user_info, client): res = client.post(login_url, data={ 'email': user_info.email, 'password': user_info.password}) assert res.status_code == 302 return login
def test_files_get(api, deposit, files, users): """Test rest files get.""" with api.test_request_context(): # the user is the owner with api.test_client() as client: # get resources without login res = client.get( url_for('invenio_deposit_rest.depid_files', pid_value=deposit['_deposit']['id'])) assert res.status_code == 401 # login res = client.post(url_for_security('login'), data=dict( email=users[0]['email'], password="******" )) # get resources res = client.get( url_for('invenio_deposit_rest.depid_files', pid_value=deposit['_deposit']['id']), headers=[('Content-Type', 'application/json'), ('Accept', 'application/json')] ) assert res.status_code == 200 data = json.loads(res.data.decode('utf-8')) assert data[0]['checksum'] == files[0].file.checksum assert data[0]['filename'] == files[0].key assert data[0]['filesize'] == files[0].file.size # the user is NOT the owner with api.test_client() as client: # login res = client.post(url_for_security('login'), data=dict( email=users[1]['email'], password="******" )) # get resources res = client.get( url_for('invenio_deposit_rest.depid_files', pid_value=deposit['_deposit']['id'])) assert res.status_code == 403
def openid_login(provider): """Return OAuth2 login view for the given provider. :param provider: OAuth2 provider. """ # get parser for provider parser = eval(str.format('{0}_parser', provider.lower())) code = request.args.get('code') oauth_kwargs = current_app.config[str.format('OAUTH_{0}', provider.upper())] c = Client(**oauth_kwargs) # get request token c.request_token(parser=parser, redirect_uri=current_app.config['KINORSI_SERVER_HOST'], grant_type='authorization_code', code=code) if hasattr(c, 'error') and c.error != 0: current_app.logger.info(c.error_description) return redirect(url_for_security('login')) else: session[u'access_token'] = c.access_token session[u'refresh_token'] = c.refresh_token session[u'expires_in'] = c.expires_in # get open id res = c.request("/oauth2.0/me", parser=parser) res['oauth_consumer_key'] = res['client_id'] # get nickname. user_info = c.request('/user/get_user_info?' + urllib.urlencode(res), method='GET', parser=parser) # 看看是不是已经在数据库中了,没有就写一个 security = current_app.extensions['security'] datastore = security.datastore user = datastore.find_user(openid=res['openid'], provider=provider.lower()) if user is None: user = datastore.create_user(openid=res['openid'], provider=provider.lower(), nickname=user_info['nickname'], avatar=user_info['figureurl_qq_1']) datastore.commit() else: pass #print 'user :'******'is here' login_user(user) next_url = get_url(request.args.get('next')) or get_url(request.form.get('next')) \ or current_app.extensions['security'].post_login_view or '' # 如果用户没有绑定,可以让用户尝试进行首次的帐号绑定。如果不绑也可以在以后再绑 # 2014-12-5 先去掉绑定功能。不然似乎有点复杂过头了。 if user.bind_username is None and user.bind_email is None and (user.bind_remind is None or user.bind_remind ): form_class = _security.login_form form = form_class() form.next.data = next_url return render_template('security/bind_user.html', bind_form=form) return redirect(next_url)
def client_authenticated(client, test_url=None): r"""Attempt to access the change password page with the given client. :param test_url: URL to attempt to get. Defaults to the current \ application's "change password" page. :returns: True if the client can get the test_url without getting \ redirected and ``flask_login.current_user`` is not anonymous \ after requesting the page. """ response = client.get(test_url or url_for_security('change_password')) return (response.status_code == 200 and not flask_login.current_user.is_anonymous)
def test_edit_deposit_users(app, db, es, users, location, deposit, json_headers, user_info, status): """Test edit deposit by the owner.""" deposit_id = deposit['_deposit']['id'] with app.test_request_context(): with app.test_client() as client: if user_info: # login res = client.post(url_for_security('login'), data=user_info) res = client.put( url_for('invenio_deposit_rest.dep_item', pid_value=deposit_id), data=json.dumps({"title": "bar"}), headers=json_headers ) assert res.status_code == status
def test_files_get_without_files(api, deposit, users): """Test rest files get a deposit without files.""" with api.test_request_context(): with api.test_client() as client: # login res = client.post(url_for_security('login'), data=dict( email=users[0]['email'], password="******" )) # get resources res = client.get( url_for('invenio_deposit_rest.depid_files', pid_value=deposit['_deposit']['id'])) assert res.status_code == 200 data = json.loads(res.data.decode('utf-8')) assert data == []
def test_file_put_not_found_file_not_exist(api, deposit, files, users): """Test put file and file doesn't exist.""" with api.test_request_context(): with api.test_client() as client: # login res = client.post(url_for_security('login'), data=dict( email=users[0]['email'], password="******" )) res = client.put(url_for( 'invenio_deposit_rest.depid_file', pid_value=deposit['_deposit']['id'], key='not_found'), data=json.dumps({'filename': 'foobar'}) ) assert res.status_code == 404
def test_file_get_not_found(api, deposit, users): """Test get file.""" with api.test_request_context(): with api.test_client() as client: # login res = client.post(url_for_security('login'), data=dict( email=users[0]['email'], password="******" )) # get resource res = client.get(url_for( 'invenio_deposit_rest.depid_file', pid_value=deposit['_deposit']['id'], key='not_found' )) assert res.status_code == 404
def test_login_remember_me_disabled(app, users): """Test login remember me is disabled.""" email = users[0]['email'] password = users[0]['password'] _security.login_form = LoginForm with app.test_client() as client: res = client.post( url_for_security('login'), data={'email': email, 'password': password, 'remember': True}, environ_base={'REMOTE_ADDR': '127.0.0.1'}) # check the remember_me cookie is not there name = '{0}='.format(COOKIE_NAME) assert all([ name not in val for val in res.headers.values()]) # check the session cookie is still there assert any([ 'session=' in val for val in res.headers.values()])
def test_headers_info(app, users): """Test if session and user id is set response header.""" u = users[0] url = url_for_security('change_password') with app.app_context(): with app.test_client() as client: response = client.get(url) # Not logged in, so only session id available assert not testutils.client_authenticated(client) assert 'X-Session-ID' in response.headers assert 'X-User-ID' not in response.headers # Login testutils.login_user_via_session(client, email=u['email']) response = client.get(url) cookie = requests.utils.dict_from_cookiejar(client.cookie_jar) assert response.headers['X-Session-ID'] == \ cookie['session'].split('.')[0] assert int(response.headers['X-User-ID']) == u['id']
def test_forgot_password_token(app, users, sleep, expired): """Test expiration of token for password reset.""" token = generate_reset_password_token(users[0]['obj']) reset_link = url_for_security('reset_password', token=token) with app.test_client() as client: res = client.get(reset_link, follow_redirects=True) time.sleep(sleep) if expired: app.config['SECURITY_MSG_PASSWORD_RESET_EXPIRED'][0] % { 'within': app.config['SECURITY_RESET_PASSWORD_WITHIN'], 'email': users[0]['email'] } in res.get_data(as_text=True) else: assert ( '<button type="submit" class="btn btn-primary btn-lg ' 'btn-block">Reset Password</button>' ) in res.get_data(as_text=True)
def test_publish_merge_conflict(app, db, es, users, location, deposit, json_headers, fake_schemas): """Test publish with merge conflicts.""" with app.test_request_context(): with app.test_client() as client: user_info = dict(email=users[0].email, password='******') # login res = client.post(url_for_security('login'), data=user_info) # create a deposit deposit = Deposit.create({"metadata": { "title": "title-1", }}) deposit.commit() db.session.commit() # publish deposit.publish() db.session.commit() # edit deposit = deposit.edit() db.session.commit() # simulate a externally modification rid, record = deposit.fetch_published() rev_id = record.revision_id record.update({'metadata': { "title": "title-2.1", }}) record.commit() db.session.commit() assert rev_id != record.revision_id # edit again and check the merging deposit.update({"metadata": { "title": "title-2.2", }}) deposit.commit() current_search.flush_and_refresh('_all') deposit_id = deposit.pid.pid_value res = client.post( url_for('invenio_deposit_rest.depid_actions', pid_value=deposit_id, action='publish'), ) assert res.status_code == 409