From 074b4b6eb3f3419e4da271bdcc2d87d25010892b Mon Sep 17 00:00:00 2001 From: Diego Rodriguez Date: Thu, 11 Jul 2019 16:10:45 +0200 Subject: [PATCH 1/3] installation: add Invenio for login/auth/access mgmnt * With the used Invenio packages Flask-Menu is not initialised but required by the installed ones (accessing current_menu) so we initialise it in REANA Flask extension. --- reana_server/config.py | 1 + 1 file changed, 1 insertion(+) diff --git a/reana_server/config.py b/reana_server/config.py index 2877f030..b74b57f7 100644 --- a/reana_server/config.py +++ b/reana_server/config.py @@ -14,6 +14,7 @@ from invenio_app.config import APP_DEFAULT_SECURE_HEADERS from invenio_oauthclient.contrib import cern + # Database # ======== #: Database URI including user and password From 683c4f79d8b6f69873f5ccf4431f87fe351c023b Mon Sep 17 00:00:00 2001 From: Leticia Farias Wanderley Date: Mon, 22 Jul 2019 18:10:31 +0200 Subject: [PATCH 2/3] auth: set up cern sso endpoints Addresses #153 Signed-off-by: Leticia Farias Wanderley --- reana_server/config.py | 1 - 1 file changed, 1 deletion(-) diff --git a/reana_server/config.py b/reana_server/config.py index b74b57f7..2877f030 100644 --- a/reana_server/config.py +++ b/reana_server/config.py @@ -14,7 +14,6 @@ from invenio_app.config import APP_DEFAULT_SECURE_HEADERS from invenio_oauthclient.contrib import cern - # Database # ======== #: Database URI including user and password From b2022f46e212fca59b9ca37b6a5efddea1c72d97 Mon Sep 17 00:00:00 2001 From: Leticia Farias Wanderley Date: Thu, 25 Jul 2019 15:55:08 +0200 Subject: [PATCH 3/3] auth: use invenio session cookie to retrieve user Addresses #153 Signed-off-by: Leticia Farias Wanderley --- docs/openapi.json | 76 ++--- reana_server/config.py | 2 + reana_server/rest/secrets.py | 31 +- reana_server/rest/workflows.py | 150 ++++++---- reana_server/utils.py | 7 + setup.py | 2 +- tests/conftest.py | 10 +- tests/test_views.py | 514 ++++++++++++++------------------- 8 files changed, 401 insertions(+), 391 deletions(-) diff --git a/docs/openapi.json b/docs/openapi.json index 13da3f66..7041a057 100644 --- a/docs/openapi.json +++ b/docs/openapi.json @@ -79,10 +79,10 @@ "operationId": "get_secrets", "parameters": [ { - "description": "Required. Secrets owner access token.", + "description": "Secrets owner access token.", "in": "query", "name": "access_token", - "required": true, + "required": false, "type": "string" } ], @@ -150,10 +150,10 @@ "operationId": "delete_secrets", "parameters": [ { - "description": "Required. API key of the admin.", + "description": "API key of the admin.", "in": "query", "name": "access_token", - "required": true, + "required": false, "type": "string" }, { @@ -233,10 +233,10 @@ "operationId": "add_secrets", "parameters": [ { - "description": "Required. Secrets owner access token.", + "description": "Secrets owner access token.", "in": "query", "name": "access_token", - "required": true, + "required": false, "type": "string" }, { @@ -495,10 +495,10 @@ "operationId": "get_workflows", "parameters": [ { - "description": "Required. The API access_token of workflow owner.", + "description": "The API access_token of workflow owner.", "in": "query", "name": "access_token", - "required": true, + "required": false, "type": "string" }, { @@ -646,10 +646,10 @@ } }, { - "description": "Required. The API access_token of workflow owner.", + "description": "The API access_token of workflow owner.", "in": "query", "name": "access_token", - "required": true, + "required": false, "type": "string" } ], @@ -740,10 +740,10 @@ "type": "string" }, { - "description": "Required. The API access_token of workflow owner.", + "description": "The API access_token of workflow owner.", "in": "query", "name": "access_token", - "required": true, + "required": false, "type": "string" } ], @@ -842,10 +842,10 @@ "type": "string" }, { - "description": "Required. The API access_token of workflow owner.", + "description": "The API access_token of workflow owner.", "in": "query", "name": "access_token", - "required": true, + "required": false, "type": "string" } ], @@ -924,10 +924,10 @@ "type": "string" }, { - "description": "Required. The API access_token of workflow owner.", + "description": "The API access_token of workflow owner.", "in": "query", "name": "access_token", - "required": true, + "required": false, "type": "string" } ], @@ -988,10 +988,10 @@ "operationId": "get_workflow_disk_usage", "parameters": [ { - "description": "Required. API access_token of workflow owner.", + "description": "The API access_token of workflow owner.", "in": "query", "name": "access_token", - "required": true, + "required": false, "type": "string" }, { @@ -1099,10 +1099,10 @@ "operationId": "get_workflow_logs", "parameters": [ { - "description": "Required. API access_token of workflow owner.", + "description": "API access_token of workflow owner.", "in": "query", "name": "access_token", - "required": true, + "required": false, "type": "string" }, { @@ -1192,10 +1192,10 @@ "type": "string" }, { - "description": "Required. The API access_token of workflow owner.", + "description": "The API access_token of workflow owner.", "in": "query", "name": "access_token", - "required": true, + "required": false, "type": "string" }, { @@ -1285,10 +1285,10 @@ "type": "string" }, { - "description": "Required. The API access_token of workflow owner.", + "description": "The API access_token of workflow owner.", "in": "query", "name": "access_token", - "required": true, + "required": false, "type": "string" } ], @@ -1376,10 +1376,10 @@ "type": "string" }, { - "description": "Required. The API access_token of workflow owner.", + "description": "The API access_token of workflow owner.", "in": "query", "name": "access_token", - "required": true, + "required": false, "type": "string" }, { @@ -1488,10 +1488,10 @@ "type": "string" }, { - "description": "Required. The API access_token of workflow owner.", + "description": "The API access_token of workflow owner.", "in": "query", "name": "access_token", - "required": true, + "required": false, "type": "string" } ], @@ -1611,10 +1611,10 @@ "type": "string" }, { - "description": "Required. The API access_token of workflow owner.", + "description": "The API access_token of workflow owner.", "in": "query", "name": "access_token", - "required": true, + "required": false, "type": "string" }, { @@ -1723,10 +1723,10 @@ "type": "string" }, { - "description": "Required. The API access_token of workflow owner.", + "description": "The API access_token of workflow owner.", "in": "query", "name": "access_token", - "required": true, + "required": false, "type": "string" } ], @@ -1813,10 +1813,10 @@ "type": "string" }, { - "description": "Required. The API access_token of workflow owner.", + "description": "The API access_token of workflow owner.", "in": "query", "name": "access_token", - "required": true, + "required": false, "type": "string" } ], @@ -1886,10 +1886,10 @@ "type": "string" }, { - "description": "Required. The API access_token of workflow owner.", + "description": "The API access_token of workflow owner.", "in": "query", "name": "access_token", - "required": true, + "required": false, "type": "string" } ], @@ -1949,10 +1949,10 @@ "type": "string" }, { - "description": "Required. The API access_token of workflow owner.", + "description": "The API access_token of workflow owner.", "in": "query", "name": "access_token", - "required": true, + "required": false, "type": "string" } ], diff --git a/reana_server/config.py b/reana_server/config.py index 2877f030..dd7597c6 100644 --- a/reana_server/config.py +++ b/reana_server/config.py @@ -47,6 +47,8 @@ def _(x): # Accounts # ======== +#: Redis URLL +ACCOUNTS_SESSION_REDIS_URL = 'redis://cache:6379/1' #: Email address used as sender of account registration emails. SECURITY_EMAIL_SENDER = SUPPORT_EMAIL #: Email subject for account registration emails. diff --git a/reana_server/rest/secrets.py b/reana_server/rest/secrets.py index 1be1320c..7f747495 100644 --- a/reana_server/rest/secrets.py +++ b/reana_server/rest/secrets.py @@ -14,11 +14,13 @@ from bravado.exception import HTTPError from flask import Blueprint, jsonify, request +from flask_login import current_user from reana_commons.errors import (REANASecretAlreadyExists, REANASecretDoesNotExist) from reana_commons.k8s.secrets import REANAUserSecretsStore -from reana_server.utils import get_user_from_token +from reana_server.utils import get_user_from_token, \ + _get_user_from_invenio_user blueprint = Blueprint('secrets', __name__) @@ -38,8 +40,8 @@ def add_secrets(): # noqa parameters: - name: access_token in: query - description: Required. Secrets owner access token. - required: true + description: Secrets owner access token. + required: false type: string - name: overwrite in: query @@ -112,7 +114,10 @@ def add_secrets(): # noqa } """ try: - user = get_user_from_token(request.args.get("access_token")) + if current_user.is_authenticated: + user = _get_user_from_invenio_user(current_user.email) + else: + user = get_user_from_token(request.args.get('access_token')) secrets_store = REANAUserSecretsStore(str(user.id_)) overwrite = json.loads(request.args.get('overwrite')) secrets_store.add_secrets(request.json, overwrite=overwrite) @@ -141,8 +146,8 @@ def get_secrets(): # noqa parameters: - name: access_token in: query - description: Required. Secrets owner access token. - required: true + description: Secrets owner access token. + required: false type: string responses: 200: @@ -194,7 +199,10 @@ def get_secrets(): # noqa } """ try: - user = get_user_from_token(request.args.get("access_token")) + if current_user.is_authenticated: + user = _get_user_from_invenio_user(current_user.email) + else: + user = get_user_from_token(request.args.get('access_token')) secrets_store = REANAUserSecretsStore(str(user.id_)) user_secrets = secrets_store.get_secrets() return jsonify(user_secrets), 200 @@ -220,8 +228,8 @@ def delete_secrets(): # noqa parameters: - name: access_token in: query - description: Required. API key of the admin. - required: true + description: API key of the admin. + required: false type: string - name: secrets in: body @@ -283,7 +291,10 @@ def delete_secrets(): # noqa } """ try: - user = get_user_from_token(request.args.get("access_token")) + if current_user.is_authenticated: + user = _get_user_from_invenio_user(current_user.email) + else: + user = get_user_from_token(request.args.get('access_token')) secrets_store = REANAUserSecretsStore(str(user.id_)) deleted_secrets_list = secrets_store.delete_secrets(request.json) return jsonify(deleted_secrets_list), 200 diff --git a/reana_server/rest/workflows.py b/reana_server/rest/workflows.py index b24e7666..9071fa17 100644 --- a/reana_server/rest/workflows.py +++ b/reana_server/rest/workflows.py @@ -17,6 +17,7 @@ from flask import Blueprint from flask import current_app as app from flask import jsonify, redirect, request, send_file, url_for +from flask_login import current_user from reana_commons.config import INTERACTIVE_SESSION_TYPES from reana_commons.utils import get_workspace_disk_usage from reana_db.database import Session @@ -26,7 +27,8 @@ from reana_server.api_client import current_rwc_api_client, \ current_workflow_submission_publisher from reana_server.config import SHARED_VOLUME_PATH -from reana_server.utils import get_user_from_token, is_uuid_v4 +from reana_server.utils import get_user_from_token, is_uuid_v4, \ + _get_user_from_invenio_user blueprint = Blueprint('workflows', __name__) @@ -46,8 +48,8 @@ def get_workflows(): # noqa parameters: - name: access_token in: query - description: Required. The API access_token of workflow owner. - required: true + description: The API access_token of workflow owner. + required: false type: string - name: type in: query @@ -148,7 +150,10 @@ def get_workflows(): # noqa } """ try: - user = get_user_from_token(request.args.get('access_token')) + if current_user.is_authenticated: + user = _get_user_from_invenio_user(current_user.email) + else: + user = get_user_from_token(request.args.get('access_token')) type = request.args.get('type', 'batch') verbose = request.args.get('verbose', False) response, http_response = current_rwc_api_client.api.\ @@ -207,8 +212,8 @@ def create_workflow(): # noqa type: object - name: access_token in: query - description: Required. The API access_token of workflow owner. - required: true + description: The API access_token of workflow owner. + required: false type: string responses: 201: @@ -260,7 +265,10 @@ def create_workflow(): # noqa Request failed. Not implemented. """ try: - user = get_user_from_token(request.args.get('access_token')) + if current_user.is_authenticated: + user = _get_user_from_invenio_user(current_user.email) + else: + user = get_user_from_token(request.args.get('access_token')) if request.json: # validate against schema reana_spec_file = request.json @@ -320,8 +328,8 @@ def get_workflow_logs(workflow_id_or_name): # noqa parameters: - name: access_token in: query - description: Required. API access_token of workflow owner. - required: true + description: API access_token of workflow owner. + required: false type: string - name: workflow_id_or_name in: path @@ -384,7 +392,10 @@ def get_workflow_logs(workflow_id_or_name): # noqa Request failed. Internal controller error. """ try: - user = get_user_from_token(request.args.get('access_token')) + if current_user.is_authenticated: + user = _get_user_from_invenio_user(current_user.email) + else: + user = get_user_from_token(request.args.get('access_token')) if not workflow_id_or_name: raise ValueError("workflow_id_or_name is not supplied") @@ -427,8 +438,8 @@ def get_workflow_status(workflow_id_or_name): # noqa type: string - name: access_token in: query - description: Required. The API access_token of workflow owner. - required: true + description: The API access_token of workflow owner. + required: false type: string responses: 200: @@ -503,7 +514,10 @@ def get_workflow_status(workflow_id_or_name): # noqa Request failed. Internal controller error. """ try: - user = get_user_from_token(request.args.get('access_token')) + if current_user.is_authenticated: + user = _get_user_from_invenio_user(current_user.email) + else: + user = get_user_from_token(request.args.get('access_token')) if not workflow_id_or_name: raise ValueError("workflow_id_or_name is not supplied") @@ -547,8 +561,8 @@ def start_workflow(workflow_id_or_name): # noqa type: string - name: access_token in: query - description: Required. The API access_token of workflow owner. - required: true + description: The API access_token of workflow owner. + required: false type: string - name: parameters in: body @@ -635,7 +649,10 @@ def start_workflow(workflow_id_or_name): # noqa } """ try: - user = get_user_from_token(request.args.get('access_token')) + if current_user.is_authenticated: + user = _get_user_from_invenio_user(current_user.email) + else: + user = get_user_from_token(request.args.get('access_token')) if not workflow_id_or_name: raise ValueError("workflow_id_or_name is not supplied") @@ -695,8 +712,8 @@ def set_workflow_status(workflow_id_or_name): # noqa type: string - name: access_token in: query - description: Required. The API access_token of workflow owner. - required: true + description: The API access_token of workflow owner. + required: false type: string - name: parameters in: body @@ -783,7 +800,10 @@ def set_workflow_status(workflow_id_or_name): # noqa } """ try: - user = get_user_from_token(request.args.get('access_token')) + if current_user.is_authenticated: + user = _get_user_from_invenio_user(current_user.email) + else: + user = get_user_from_token(request.args.get('access_token')) if not workflow_id_or_name: raise ValueError("workflow_id_or_name is not supplied") @@ -842,8 +862,8 @@ def upload_file(workflow_id_or_name): # noqa type: string - name: access_token in: query - description: Required. The API access_token of workflow owner. - required: true + description: The API access_token of workflow owner. + required: false type: string responses: 200: @@ -886,7 +906,10 @@ def upload_file(workflow_id_or_name): # noqa } """ try: - user = get_user_from_token(request.args.get('access_token')) + if current_user.is_authenticated: + user = _get_user_from_invenio_user(current_user.email) + else: + user = get_user_from_token(request.args.get('access_token')) if not workflow_id_or_name: raise ValueError("workflow_id_or_name is not supplied") @@ -942,8 +965,8 @@ def download_file(workflow_id_or_name, file_name): # noqa type: string - name: access_token in: query - description: Required. The API access_token of workflow owner. - required: true + description: The API access_token of workflow owner. + required: false type: string responses: 200: @@ -982,7 +1005,10 @@ def download_file(workflow_id_or_name, file_name): # noqa } """ try: - user = get_user_from_token(request.args.get('access_token')) + if current_user.is_authenticated: + user = _get_user_from_invenio_user(current_user.email) + else: + user = get_user_from_token(request.args.get('access_token')) if not workflow_id_or_name: raise ValueError("workflow_id_or_name is not supplied") @@ -1036,8 +1062,8 @@ def delete_file(workflow_id_or_name, file_name): # noqa type: string - name: access_token in: query - description: Required. The API access_token of workflow owner. - required: true + description: The API access_token of workflow owner. + required: false type: string responses: 200: @@ -1073,7 +1099,10 @@ def delete_file(workflow_id_or_name, file_name): # noqa } """ try: - user = get_user_from_token(request.args.get('access_token')) + if current_user.is_authenticated: + user = _get_user_from_invenio_user(current_user.email) + else: + user = get_user_from_token(request.args.get('access_token')) if not workflow_id_or_name: raise ValueError("workflow_id_or_name is not supplied") @@ -1118,8 +1147,8 @@ def get_files(workflow_id_or_name): # noqa type: string - name: access_token in: query - description: Required. The API access_token of workflow owner. - required: true + description: The API access_token of workflow owner. + required: false type: string responses: 200: @@ -1168,7 +1197,10 @@ def get_files(workflow_id_or_name): # noqa } """ try: - user = get_user_from_token(request.args.get('access_token')) + if current_user.is_authenticated: + user = _get_user_from_invenio_user(current_user.email) + else: + user = get_user_from_token(request.args.get('access_token')) if not workflow_id_or_name: raise ValueError("workflow_id_or_name is not supplied") @@ -1212,8 +1244,8 @@ def get_workflow_parameters(workflow_id_or_name): # noqa type: string - name: access_token in: query - description: Required. The API access_token of workflow owner. - required: true + description: The API access_token of workflow owner. + required: false type: string responses: 200: @@ -1274,7 +1306,10 @@ def get_workflow_parameters(workflow_id_or_name): # noqa Request failed. Internal controller error. """ try: - user = get_user_from_token(request.args.get('access_token')) + if current_user.is_authenticated: + user = _get_user_from_invenio_user(current_user.email) + else: + user = get_user_from_token(request.args.get('access_token')) if not workflow_id_or_name: raise ValueError("workflow_id_or_name is not supplied") @@ -1337,8 +1372,8 @@ def get_workflow_diff(workflow_id_or_name_a, workflow_id_or_name_b): # noqa default: '5' - name: access_token in: query - description: Required. The API access_token of workflow owner. - required: true + description: The API access_token of workflow owner. + required: false type: string responses: 200: @@ -1391,7 +1426,11 @@ def get_workflow_diff(workflow_id_or_name_a, workflow_id_or_name_b): # noqa Request failed. Internal controller error. """ try: - user = get_user_from_token(request.args.get('access_token')) + if current_user.is_authenticated: + user = _get_user_from_invenio_user(current_user.email) + else: + user = get_user_from_token(request.args.get('access_token')) + brief = request.args.get('brief', False) brief = True if brief == 'true' else False context_lines = request.args.get('context_lines', 5) @@ -1444,8 +1483,8 @@ def open_interactive_session(workflow_id_or_name, type: string - name: access_token in: query - description: Required. The API access_token of workflow owner. - required: true + description: The API access_token of workflow owner. + required: false type: string - name: interactive_session_type in: path @@ -1510,7 +1549,11 @@ def open_interactive_session(workflow_id_or_name, Request failed. Internal controller error. """ try: - user = get_user_from_token(request.args.get('access_token')) + if current_user.is_authenticated: + user = _get_user_from_invenio_user(current_user.email) + else: + user = get_user_from_token(request.args.get('access_token')) + if interactive_session_type not in INTERACTIVE_SESSION_TYPES: return jsonify({ "message": "Interactive session type {0} not found, try " @@ -1566,8 +1609,8 @@ def close_interactive_session(workflow_id_or_name): # noqa type: string - name: access_token in: query - description: Required. The API access_token of workflow owner. - required: true + description: The API access_token of workflow owner. + required: false type: string responses: 200: @@ -1614,7 +1657,10 @@ def close_interactive_session(workflow_id_or_name): # noqa Request failed. Internal controller error. """ try: - user = get_user_from_token(request.args.get('access_token')) + if current_user.is_authenticated: + user = _get_user_from_invenio_user(current_user.email) + else: + user = get_user_from_token(request.args.get('access_token')) if not workflow_id_or_name: raise KeyError("workflow_id_or_name is not supplied") response, http_response = current_rwc_api_client.api.\ @@ -1670,8 +1716,8 @@ def move_files(workflow_id_or_name): # noqa type: string - name: access_token in: query - description: Required. The API access_token of workflow owner. - required: true + description: The API access_token of workflow owner. + required: false type: string responses: 200: @@ -1726,7 +1772,10 @@ def move_files(workflow_id_or_name): # noqa Request failed. Internal controller error. """ try: - user = get_user_from_token(request.args.get('access_token')) + if current_user.is_authenticated: + user = _get_user_from_invenio_user(current_user.email) + else: + user = get_user_from_token(request.args.get('access_token')) if not workflow_id_or_name: raise ValueError("workflow_id_or_name is not supplied") @@ -1768,8 +1817,8 @@ def get_workflow_disk_usage(workflow_id_or_name): # noqa parameters: - name: access_token in: query - description: Required. API access_token of workflow owner. - required: true + description: The API access_token of workflow owner. + required: false type: string - name: workflow_id_or_name in: path @@ -1848,7 +1897,10 @@ def get_workflow_disk_usage(workflow_id_or_name): # noqa Request failed. Internal controller error. """ try: - user = get_user_from_token(request.args.get('access_token')) + if current_user.is_authenticated: + user = _get_user_from_invenio_user(current_user.email) + else: + user = get_user_from_token(request.args.get('access_token')) parameters = request.json or {} if not workflow_id_or_name: diff --git a/reana_server/utils.py b/reana_server/utils.py index 2820afc1..9758c246 100644 --- a/reana_server/utils.py +++ b/reana_server/utils.py @@ -142,3 +142,10 @@ def _create_and_associate_reana_user(sender, token=None, except Exception: raise ValueError('Could not create user') return user + + +def _get_user_from_invenio_user(id): + user = Session.query(User).filter_by(email=id).one_or_none() + if not user: + raise ValueError('No users registered with this id') + return user diff --git a/setup.py b/setup.py index 7b221ce1..a5423dc9 100644 --- a/setup.py +++ b/setup.py @@ -68,7 +68,7 @@ 'invenio-mail>=1.0.2,<1.1.0', 'invenio-rest>=1.0.0,<1.1.0', # From auth bundle - 'invenio-accounts-rest>=1.0.0a4', + 'invenio-accounts>=1.1.1', 'invenio-oauth2server>=1.0.3,<1.1.0', 'invenio-oauthclient>=1.1.2,<1.2.0', 'invenio-userprofiles>=1.0.1,<1.1.0', diff --git a/tests/conftest.py b/tests/conftest.py index 5ce78ff1..3f14352c 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -13,9 +13,10 @@ import os import shutil +import flask_login import pytest from flask import Flask -from mock import Mock +from mock import Mock, patch from reana_commons.api_client import BaseAPIClient from reana_db.database import Session from reana_db.models import Base, User @@ -63,3 +64,10 @@ def app(base_app, db_engine, session): yield base_app for table in reversed(Base.metadata.sorted_tables): db_engine.execute(table.delete()) + + +@pytest.fixture() +def _get_user_mock(): + with patch("flask_login.utils._get_user", + Mock(return_value=Mock(is_authenticated=False))): + yield flask_login.utils._get_user diff --git a/tests/test_views.py b/tests/test_views.py index 4d4538dc..f52098c2 100644 --- a/tests/test_views.py +++ b/tests/test_views.py @@ -23,329 +23,265 @@ from reana_db.models import User from reana_server.utils import _create_and_associate_reana_user +from tests.conftest import _get_user_mock -def test_get_workflows(app, default_user): +def test_get_workflows(app, default_user, _get_user_mock): """Test get_workflows view.""" with app.test_client() as client: - with patch( - "reana_server.rest.workflows.current_rwc_api_client", - make_mock_api_client("reana-workflow-controller")(), - ): - res = client.get( - url_for("workflows.get_workflows"), - query_string={"user_id": default_user.id_, - "type": "batch"}, - ) + with patch("reana_server.rest.workflows.current_rwc_api_client", + make_mock_api_client("reana-workflow-controller")()): + res = client.get(url_for("workflows.get_workflows"), + query_string={"type": "batch"}) assert res.status_code == 403 - res = client.get( - url_for("workflows.get_workflows"), - query_string={"access_token": default_user.access_token, - "type": "batch"}, - ) + res = client.get(url_for("workflows.get_workflows"), + query_string={"access_token": + default_user.access_token, + "type": "batch"}) assert res.status_code == 200 -def test_create_workflow(app, default_user): +def test_create_workflow(app, default_user, _get_user_mock): """Test create_workflow view.""" with app.test_client() as client: - with patch( - "reana_server.rest.workflows.current_rwc_api_client", - make_mock_api_client("reana-workflow-controller")(), - ): - # access token needs to be passed instead of user_id - res = client.post( - url_for("workflows.create_workflow"), - query_string={"user_id": default_user.id_}, - ) + with patch("reana_server.rest.workflows.current_rwc_api_client", + make_mock_api_client("reana-workflow-controller")()): + res = client.post(url_for("workflows.create_workflow")) assert res.status_code == 403 # remote repository given as spec, not implemented - res = client.post( - url_for("workflows.create_workflow"), - query_string={"access_token": default_user.access_token, - "spec": "not_implemented"}, - ) + res = client.post(url_for("workflows.create_workflow"), + query_string={"access_token": + default_user.access_token, + "spec": "not_implemented"}) assert res.status_code == 501 # no specification provided - res = client.post( - url_for("workflows.create_workflow"), - query_string={"access_token": default_user.access_token}, - ) + res = client.post(url_for("workflows.create_workflow"), + query_string={"access_token": + default_user.access_token}) assert res.status_code == 500 # unknown workflow engine workflow_data = { "workflow": {"specification": {}, "type": "unknown"}, - "workflow_name": "test", - } - res = client.post( - url_for("workflows.create_workflow"), - headers={"Content-Type": "application/json"}, - query_string={"access_token": default_user.access_token}, - data=json.dumps(workflow_data), - ) + "workflow_name": "test"} + res = client.post(url_for("workflows.create_workflow"), + headers={"Content-Type": "application/json"}, + query_string={"access_token": + default_user.access_token}, + data=json.dumps(workflow_data)) assert res.status_code == 500 # name cannot be valid uuid4 workflow_data['workflow']['type'] = 'serial' - res = client.post( - url_for("workflows.create_workflow"), - headers={"Content-Type": "application/json"}, - query_string={"access_token": default_user.access_token, - "workflow_name": str(uuid4())}, - data=json.dumps(workflow_data), - ) + res = client.post(url_for("workflows.create_workflow"), + headers={"Content-Type": "application/json"}, + query_string={"access_token": + default_user.access_token, + "workflow_name": str(uuid4())}, + data=json.dumps(workflow_data)) assert res.status_code == 400 # wrong specification json - workflow_data = { - "nonsense": {"specification": {}, "type": "unknown"}, - } - res = client.post( - url_for("workflows.create_workflow"), - headers={"Content-Type": "application/json"}, - query_string={"access_token": default_user.access_token}, - data=json.dumps(workflow_data), - ) + workflow_data = {"nonsense": {"specification": {}, + "type": "unknown"}} + res = client.post(url_for("workflows.create_workflow"), + headers={"Content-Type": "application/json"}, + query_string={"access_token": + default_user.access_token}, + data=json.dumps(workflow_data)) assert res.status_code == 400 # correct case - workflow_data = { - "workflow": {"specification": {}, "type": "serial"}, - "workflow_name": "test", - } - res = client.post( - url_for("workflows.create_workflow"), - headers={"Content-Type": "application/json"}, - query_string={"access_token": default_user.access_token}, - data=json.dumps(workflow_data), - ) + workflow_data = {"workflow": {"specification": {}, + "type": "serial"}, + "workflow_name": "test"} + res = client.post(url_for("workflows.create_workflow"), + headers={"Content-Type": "application/json"}, + query_string={"access_token": + default_user.access_token}, + data=json.dumps(workflow_data)) assert res.status_code == 200 -def test_get_workflow_logs(app, default_user): +def test_get_workflow_logs(app, default_user, _get_user_mock): """Test get_workflow_logs view.""" with app.test_client() as client: - with patch( - "reana_server.rest.workflows.current_rwc_api_client", - make_mock_api_client("reana-workflow-controller")(), - ): - res = client.get( - url_for("workflows.get_workflow_logs", - workflow_id_or_name="1"), - query_string={"user_id": default_user.id_}, - ) + with patch("reana_server.rest.workflows.current_rwc_api_client", + make_mock_api_client("reana-workflow-controller")()): + res = client.get(url_for("workflows.get_workflow_logs", + workflow_id_or_name="1")) assert res.status_code == 403 - res = client.get( - url_for("workflows.get_workflow_logs", - workflow_id_or_name="1"), - headers={"Content-Type": "application/json"}, - query_string={"access_token": default_user.access_token}, - ) + res = client.get(url_for("workflows.get_workflow_logs", + workflow_id_or_name="1"), + headers={"Content-Type": "application/json"}, + query_string={"access_token": + default_user.access_token}) assert res.status_code == 200 -def test_get_workflow_status(app, default_user): +def test_get_workflow_status(app, default_user, _get_user_mock): """Test get_workflow_logs view.""" with app.test_client() as client: - with patch( - "reana_server.rest.workflows.current_rwc_api_client", - make_mock_api_client("reana-workflow-controller")(), - ): - res = client.get( - url_for("workflows.get_workflow_status", - workflow_id_or_name="1"), - query_string={"user_id": default_user.id_}, - ) + with patch("reana_server.rest.workflows.current_rwc_api_client", + make_mock_api_client("reana-workflow-controller")()): + res = client.get(url_for("workflows.get_workflow_status", + workflow_id_or_name="1")) assert res.status_code == 403 - res = client.get( - url_for("workflows.get_workflow_status", - workflow_id_or_name="1"), - headers={"Content-Type": "application/json"}, - query_string={"access_token": default_user.access_token}, - ) + res = client.get(url_for("workflows.get_workflow_status", + workflow_id_or_name="1"), + headers={"Content-Type": "application/json"}, + query_string={"access_token": + default_user.access_token}) assert res.status_code == 200 -def test_set_workflow_status(app, default_user): +def test_set_workflow_status(app, default_user, _get_user_mock): """Test get_workflow_logs view.""" with app.test_client() as client: - with patch( - "reana_server.rest.workflows.current_rwc_api_client", - make_mock_api_client("reana-workflow-controller")(), - ): - res = client.put( - url_for("workflows.set_workflow_status", - workflow_id_or_name="1"), - query_string={"user_id": default_user.id_}, - ) + with patch("reana_server.rest.workflows.current_rwc_api_client", + make_mock_api_client("reana-workflow-controller")()): + res = client.put(url_for("workflows.set_workflow_status", + workflow_id_or_name="1")) assert res.status_code == 403 - res = client.put( - url_for("workflows.set_workflow_status", - workflow_id_or_name="1"), - headers={"Content-Type": "application/json"}, - query_string={"access_token": default_user.access_token}, - ) + res = client.put(url_for("workflows.set_workflow_status", + workflow_id_or_name="1"), + headers={"Content-Type": "application/json"}, + query_string={"access_token": + default_user.access_token}) assert res.status_code == 500 - res = client.put( - url_for("workflows.set_workflow_status", - workflow_id_or_name="1"), - headers={"Content-Type": "application/json"}, - query_string={"access_token": default_user.access_token, - "status": "stop"}, - data=json.dumps(dict(parameters=None)) - ) + res = client.put(url_for("workflows.set_workflow_status", + workflow_id_or_name="1"), + headers={"Content-Type": "application/json"}, + query_string={"access_token": + default_user.access_token, + "status": "stop"}, + data=json.dumps(dict(parameters=None))) assert res.status_code == 200 -def test_upload_file(app, default_user): +def test_upload_file(app, default_user, _get_user_mock): """Test upload_file view.""" with app.test_client() as client: - with patch( - "reana_server.rest.workflows.current_rwc_api_client", - make_mock_api_client("reana-workflow-controller")(), - ): - res = client.post( - url_for("workflows.upload_file", - workflow_id_or_name="1"), - query_string={"user_id": default_user.id_, - "file_name": "test_upload.txt"}, - data={ - "file_content": "tests/test_files/test_upload.txt" - } - ) + with patch("reana_server.rest.workflows.current_rwc_api_client", + make_mock_api_client("reana-workflow-controller")()): + res = client.post(url_for("workflows.upload_file", + workflow_id_or_name="1"), + query_string={"file_name": "test_upload.txt"}, + data={"file_content": + "tests/test_files/test_upload.txt"}) assert res.status_code == 403 - res = client.post( - url_for("workflows.upload_file", - workflow_id_or_name="1"), - query_string={"access_token": default_user.access_token, - "file_name": "test_upload.txt"}, - headers={"content_type": "multipart/form-data"}, - data={ - "file": (BytesIO(b"Upload this data."), - "tests/test_files/test_upload.txt") - } - ) + res = client.post(url_for("workflows.upload_file", + workflow_id_or_name="1"), + query_string={"access_token": + default_user.access_token, + "file_name": "test_upload.txt"}, + headers={"content_type": + "multipart/form-data"}, + data={"file": + (BytesIO(b"Upload this data."), + "tests/test_files/test_upload.txt")}) assert res.status_code == 400 - res = client.post( - url_for("workflows.upload_file", - workflow_id_or_name="1"), - query_string={"access_token": default_user.access_token, - "file_name": None}, - headers={"content_type": "multipart/form-data"}, - data={ - "file_content": (BytesIO(b"Upload this data."), - "tests/test_files/test_upload.txt") - } - ) + res = client.post(url_for("workflows.upload_file", + workflow_id_or_name="1"), + query_string={"access_token": + default_user.access_token, + "file_name": None}, + headers={"content_type": + "multipart/form-data"}, + data={"file_content": + (BytesIO(b"Upload this data."), + "tests/test_files/test_upload.txt")}) assert res.status_code == 400 - res = client.post( - url_for("workflows.upload_file", - workflow_id_or_name="1"), - query_string={"access_token": default_user.access_token, - "file_name": "test_upload.txt"}, - headers={"content_type": "multipart/form-data"}, - data={ - "file_content": (BytesIO(b"Upload this data."), - "tests/test_files/test_upload.txt") - } - ) + res = client.post(url_for("workflows.upload_file", + workflow_id_or_name="1"), + query_string={"access_token": + default_user.access_token, + "file_name": + "test_upload.txt"}, + headers={"content_type": + "multipart/form-data"}, + data={"file_content": + (BytesIO(b"Upload this data."), + "tests/test_files/test_upload.txt")}) assert res.status_code == 200 -def test_download_file(app, default_user): +def test_download_file(app, default_user, _get_user_mock): """Test download_file view.""" with app.test_client() as client: - with patch( - "reana_server.rest.workflows.current_rwc_api_client", - make_mock_api_client("reana-workflow-controller")(), - ): - res = client.get( - url_for("workflows.download_file", - workflow_id_or_name="1", - file_name="test_download"), - query_string={"user_id": default_user.id_, - "file_name": "test_upload.txt"}, - ) - assert res.status_code == 302 - - res = client.get( - url_for("workflows.download_file", - workflow_id_or_name="1", - file_name="test_download"), - query_string={"access_token": default_user.access_token}, - ) - assert res.status_code == 200 - - -def test_delete_file(app, default_user): + with patch("reana_server.rest.workflows.current_rwc_api_client", + make_mock_api_client("reana-workflow-controller")()): + res = client.get(url_for("workflows.download_file", + workflow_id_or_name="1", + file_name="test_download"), + query_string={"file_name": + "test_upload.txt"}) + assert res.status_code == 302 + + res = client.get(url_for("workflows.download_file", + workflow_id_or_name="1", + file_name="test_download"), + query_string={"access_token": + default_user.access_token}) + assert res.status_code == 200 + + +def test_delete_file(app, default_user, _get_user_mock): """Test delete_file view.""" with app.test_client() as client: - with patch( - "reana_server.rest.workflows.current_rwc_api_client", - make_mock_api_client("reana-workflow-controller")(), - ): - res = client.get( - url_for("workflows.delete_file", - workflow_id_or_name="1", - file_name="test_delete.txt"), - query_string={"user_id": default_user.id_}) + with patch("reana_server.rest.workflows.current_rwc_api_client", + make_mock_api_client("reana-workflow-controller")()): + res = client.get(url_for("workflows.delete_file", + workflow_id_or_name="1", + file_name="test_delete.txt")) assert res.status_code == 302 - res = client.get( - url_for("workflows.delete_file", - workflow_id_or_name="1", - file_name="test_delete.txt"), - query_string={"access_token": default_user.access_token}, - ) + res = client.get(url_for("workflows.delete_file", + workflow_id_or_name="1", + file_name="test_delete.txt"), + query_string={"access_token": + default_user.access_token}) assert res.status_code == 200 -def test_get_files(app, default_user): +def test_get_files(app, default_user, _get_user_mock): """Test get_files view.""" with app.test_client() as client: - with patch( - "reana_server.rest.workflows.current_rwc_api_client", - make_mock_api_client("reana-workflow-controller")(), - ): - res = client.get( - url_for("workflows.get_files", - workflow_id_or_name="1"), - query_string={"user_id": default_user.id_}, - ) + with patch("reana_server.rest.workflows.current_rwc_api_client", + make_mock_api_client("reana-workflow-controller")()): + res = client.get(url_for("workflows.get_files", + workflow_id_or_name="1")) assert res.status_code == 403 - res = client.get( - url_for("workflows.get_files", - workflow_id_or_name="1"), - query_string={"access_token": default_user.access_token}, - ) + res = client.get(url_for("workflows.get_files", + workflow_id_or_name="1"), + query_string={"access_token": + default_user.access_token}) assert res.status_code == 500 - mock_response = Mock() - mock_response.status_code = 200 - mock_response.json.return_value = dict(key='value') - with patch( - "reana_server.rest.workflows.current_rwc_api_client", - make_mock_api_client("reana-workflow-controller")( - mock_http_response=mock_response), - ): - res = client.get( - url_for("workflows.get_files", - workflow_id_or_name="1"), - query_string={"access_token": default_user.access_token}, - ) - assert res.status_code == 200 + mock_response = Mock() + mock_response.status_code = 200 + mock_response.json.return_value = dict(key='value') + with patch( + "reana_server.rest.workflows.current_rwc_api_client", + make_mock_api_client("reana-workflow-controller")( + mock_http_response=mock_response), + ): + res = client.get(url_for("workflows.get_files", + workflow_id_or_name="1"), + query_string={"access_token": + default_user.access_token}) + assert res.status_code == 200 def test_get_user(app, default_user): @@ -387,13 +323,13 @@ def test_create_user(app, default_user): ) assert res.status_code == 403 - with app.test_client() as client: - res = client.post( - url_for("users.create_user"), - query_string={"email": "test_email", - "access_token": default_user.access_token}, - ) - assert res.status_code == 201 + with app.test_client() as client: + res = client.post( + url_for("users.create_user"), + query_string={"email": "test_email", + "access_token": default_user.access_token}, + ) + assert res.status_code == 201 def test_user_login(app, default_user): @@ -406,38 +342,34 @@ def test_user_login(app, default_user): assert json.loads(res.data)['message'] -def test_move_files(app, default_user): +def test_move_files(app, default_user, _get_user_mock): """Test move_files view.""" with app.test_client() as client: - with patch( - "reana_server.rest.workflows.current_rwc_api_client", - make_mock_api_client("reana-workflow-controller")(), - ): + with patch("reana_server.rest.workflows.current_rwc_api_client", + make_mock_api_client("reana-workflow-controller")()): res = client.put( url_for("workflows.move_files", workflow_id_or_name="1"), query_string={"user": default_user.id_, "source": "source.txt", - "target": "target.txt", - }) + "target": "target.txt"}) assert res.status_code == 403 - mock_response = Mock() - mock_response.status_code = 200 - mock_response.json.return_value = dict(key='value') - with patch( - "reana_server.rest.workflows.current_rwc_api_client", - make_mock_api_client("reana-workflow-controller")( - mock_http_response=mock_response), - ): - res = client.put( - url_for("workflows.move_files", - workflow_id_or_name="1"), - query_string={"access_token": default_user.access_token, - "source": "source.txt", - "target": "target.txt", - }) - assert res.status_code == 200 + mock_response = Mock() + mock_response.status_code = 200 + mock_response.json.return_value = dict(key='value') + with patch( + "reana_server.rest.workflows.current_rwc_api_client", + make_mock_api_client("reana-workflow-controller")( + mock_http_response=mock_response), + ): + res = client.put( + url_for("workflows.move_files", + workflow_id_or_name="1"), + query_string={"access_token": default_user.access_token, + "source": "source.txt", + "target": "target.txt"}) + assert res.status_code == 200 @pytest.mark.parametrize( @@ -448,39 +380,37 @@ def test_move_files(app, default_user): def test_open_interactive_session(app, default_user, sample_serial_workflow_in_db, interactive_session_type, - expected_status_code): + expected_status_code, + _get_user_mock): """Test open interactive session.""" with app.test_client() as client: - with patch( - "reana_server.rest.workflows.current_rwc_api_client", - make_mock_api_client("reana-workflow-controller")(), - ): - res = client.post( - url_for( - "workflows.open_interactive_session", - workflow_id_or_name=sample_serial_workflow_in_db.id_, - interactive_session_type=interactive_session_type), - query_string={"access_token": default_user.access_token}) - assert res.status_code == expected_status_code + with patch("reana_server.rest.workflows.current_rwc_api_client", + make_mock_api_client("reana-workflow-controller")()): + res = client.post( + url_for( + "workflows.open_interactive_session", + workflow_id_or_name=sample_serial_workflow_in_db.id_, + interactive_session_type=interactive_session_type), + query_string={"access_token": default_user.access_token}) + assert res.status_code == expected_status_code @pytest.mark.parametrize( ('expected_status_code'), [200]) def test_close_interactive_session(app, default_user, sample_serial_workflow_in_db, - expected_status_code): + expected_status_code, + _get_user_mock): """Test close an interactive session.""" with app.test_client() as client: - with patch( - "reana_server.rest.workflows.current_rwc_api_client", - make_mock_api_client("reana-workflow-controller")(), - ): - res = client.post( - url_for( - "workflows.close_interactive_session", - workflow_id_or_name=sample_serial_workflow_in_db.id_), - query_string={"access_token": default_user.access_token}) - assert res.status_code == expected_status_code + with patch("reana_server.rest.workflows.current_rwc_api_client", + make_mock_api_client("reana-workflow-controller")()): + res = client.post( + url_for( + "workflows.close_interactive_session", + workflow_id_or_name=sample_serial_workflow_in_db.id_), + query_string={"access_token": default_user.access_token}) + assert res.status_code == expected_status_code def test_create_and_associate_reana_user():