From 13a071e584743e62d15ea804e3919784d9bc5936 Mon Sep 17 00:00:00 2001 From: Marco Enrico Piras Date: Sat, 6 May 2023 17:39:34 +0200 Subject: [PATCH 01/26] fix(ctrl): redirect after oauth login/logout --- lifemonitor/auth/oauth2/client/controllers.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lifemonitor/auth/oauth2/client/controllers.py b/lifemonitor/auth/oauth2/client/controllers.py index 9ba70b8fb..3734f456d 100644 --- a/lifemonitor/auth/oauth2/client/controllers.py +++ b/lifemonitor/auth/oauth2/client/controllers.py @@ -135,7 +135,7 @@ def logout(name: str): # Determine the right next hop next_url = NextRouteRegistry.pop() return redirect(next_url, code=307) if next_url \ - else RequestHelper.response() or redirect('/', code=302) + else RequestHelper.response() or redirect('/account', code=302) return blueprint @@ -261,7 +261,7 @@ def handle_authorize(self, provider: FlaskRemoteApp, token, user_info: OAuthUser next_url = NextRouteRegistry.pop() flash(f"Logged with your \"{identity.provider.name}\" identity.", category="success") return redirect(next_url, code=307) if next_url \ - else RequestHelper.response() or redirect('/', code=302) + else RequestHelper.response() or redirect('/account', code=302) @staticmethod def validate_identity_token(identity: OAuthIdentity): From ca5d3c5f13c626a48a8dfdaa997e0e13d7e69ec7 Mon Sep 17 00:00:00 2001 From: Marco Enrico Piras Date: Sat, 6 May 2023 18:45:21 +0200 Subject: [PATCH 02/26] fix(ctrl): set back param --- lifemonitor/auth/controllers.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/lifemonitor/auth/controllers.py b/lifemonitor/auth/controllers.py index dfa49a652..121220ea4 100644 --- a/lifemonitor/auth/controllers.py +++ b/lifemonitor/auth/controllers.py @@ -162,7 +162,7 @@ def get_registry_user(user_id): @blueprint.route("/", methods=("GET",)) def index(): - return redirect(url_for('auth.profile', back=request.args.get('back', False))) + return redirect(url_for('auth.profile', back=request.args.get('back', None))) @blueprint.route("/profile", methods=("GET",)) @@ -177,7 +177,8 @@ def profile(form=None, passwordForm=None, currentView=None, logger.debug("Pushing back param to session") else: logger.debug("Getting back param from session") - back_param = back_param or session.get('lm_back_param', False) + back_param = back_param or session.get('lm_back_param', None) + session['lm_back_param'] = back_param logger.debug("detected back param: %s", back_param) from lifemonitor.api.models.registries.forms import RegistrySettingsForm from lifemonitor.integrations.github.forms import GithubSettingsForm @@ -272,6 +273,7 @@ def login(): session.pop('_flashes', None) flash("You have logged in", category="success") return redirect(NextRouteRegistry.pop(url_for("auth.profile"))) + flask.session['lm_back_param'] = flask.request.args.get('back', None) return render_template("auth/login.j2", form=form, providers=get_providers(), is_service_available=is_service_alive) @@ -281,6 +283,7 @@ def login(): def logout(): logout_user() session.pop('_flashes', None) + back_param = session.pop('lm_back_param', None) flash("You have logged out", category="success") NextRouteRegistry.clear() next_route = request.args.get('next', '/') From da636abe50bdfed7b587fe15f0a550e0ba0cf24d Mon Sep 17 00:00:00 2001 From: Marco Enrico Piras Date: Sat, 6 May 2023 18:50:34 +0200 Subject: [PATCH 03/26] fix(ctrl): logout from API profile causes logout from webapp --- lifemonitor/auth/controllers.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lifemonitor/auth/controllers.py b/lifemonitor/auth/controllers.py index 121220ea4..4bb4299e2 100644 --- a/lifemonitor/auth/controllers.py +++ b/lifemonitor/auth/controllers.py @@ -286,7 +286,7 @@ def logout(): back_param = session.pop('lm_back_param', None) flash("You have logged out", category="success") NextRouteRegistry.clear() - next_route = request.args.get('next', '/') + next_route = request.args.get('next', '/logout' if back_param else '/') logger.debug("Next route after logout: %r", next_route) return redirect(next_route) From a06d4af59514c6a1152de1687a4efbe5ab9f6bea Mon Sep 17 00:00:00 2001 From: Marco Enrico Piras Date: Tue, 9 May 2023 11:31:54 +0200 Subject: [PATCH 04/26] fix(ctrl): redirect after sign in --- lifemonitor/auth/controllers.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lifemonitor/auth/controllers.py b/lifemonitor/auth/controllers.py index 4bb4299e2..c5b3f2d7e 100644 --- a/lifemonitor/auth/controllers.py +++ b/lifemonitor/auth/controllers.py @@ -213,7 +213,7 @@ def register(): login_user(user) flash("Account created", category="success") clear_cache() - return redirect(url_for("auth.index")) + return redirect(url_for("auth.profile")) return render_template("auth/register.j2", form=form, action=url_for('auth.register'), providers=get_providers(), is_service_available=is_service_alive) From e5ab7c4340d00c3e44c98a905e61cf8d3a3102c2 Mon Sep 17 00:00:00 2001 From: Marco Enrico Piras Date: Tue, 9 May 2023 11:32:19 +0200 Subject: [PATCH 05/26] refactor(ctrl): remove redundant import --- lifemonitor/auth/controllers.py | 1 - 1 file changed, 1 deletion(-) diff --git a/lifemonitor/auth/controllers.py b/lifemonitor/auth/controllers.py index c5b3f2d7e..8dfb85be7 100644 --- a/lifemonitor/auth/controllers.py +++ b/lifemonitor/auth/controllers.py @@ -229,7 +229,6 @@ def identity_not_found(): form = RegisterForm() user = identity.user # workaround to force clean DB session - from lifemonitor.db import db db.session.rollback() return render_template("auth/identity_not_found.j2", form=form, action=url_for('auth.register_identity') if flask.session.get('sign_in', False) else url_for('auth.register'), From cb97fa4484340f0307a78bb6718a9d3417cc265e Mon Sep 17 00:00:00 2001 From: Marco Enrico Piras Date: Tue, 9 May 2023 11:34:02 +0200 Subject: [PATCH 06/26] fix(ctrl): redirect to sign in page if registration flow is not valid --- lifemonitor/auth/controllers.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lifemonitor/auth/controllers.py b/lifemonitor/auth/controllers.py index 8dfb85be7..7e7cbd046 100644 --- a/lifemonitor/auth/controllers.py +++ b/lifemonitor/auth/controllers.py @@ -242,7 +242,7 @@ def register_identity(): logger.debug("Current provider identity: %r", identity) if not identity: flash("Unable to register the user") - flask.abort(400) + return redirect(url_for("auth.register")) logger.debug("Provider identity on session: %r", identity) logger.debug("User Info: %r", identity.user_info) user = identity.user From ea4fe6c353dc40d1a98b963a229939cc60198b3d Mon Sep 17 00:00:00 2001 From: Marco Enrico Piras Date: Tue, 9 May 2023 11:35:02 +0200 Subject: [PATCH 07/26] fix(ctrl): update action to register a user identity --- lifemonitor/auth/controllers.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lifemonitor/auth/controllers.py b/lifemonitor/auth/controllers.py index 7e7cbd046..b22d3114e 100644 --- a/lifemonitor/auth/controllers.py +++ b/lifemonitor/auth/controllers.py @@ -254,7 +254,7 @@ def register_identity(): flash("Account created", category="success") clear_cache() return redirect(url_for("auth.index")) - return render_template("auth/register.j2", form=form, action=url_for('auth.register'), + return render_template("auth/register.j2", form=form, action=url_for('auth.register_identity'), identity=identity, user=user, providers=get_providers()) From e2a211cd4b78d154e6babf1d16eb12b28cbfb4e2 Mon Sep 17 00:00:00 2001 From: Marco Enrico Piras Date: Tue, 9 May 2023 11:43:57 +0200 Subject: [PATCH 08/26] refactor: log in debug mode --- lifemonitor/auth/forms.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lifemonitor/auth/forms.py b/lifemonitor/auth/forms.py index 18794a9dd..46df3418f 100644 --- a/lifemonitor/auth/forms.py +++ b/lifemonitor/auth/forms.py @@ -84,7 +84,8 @@ def create_user(self, identity=None): db.session.commit() return user except IntegrityError as e: - logger.debug(e) + if logger.isEnabledFor(logging.DEBUG): + logger.exception(e) self.username.errors.append("This username is already taken") db.session.rollback() return None From 00287d658d3e2214b51eed5d6b089eab8c1f6fa6 Mon Sep 17 00:00:00 2001 From: Marco Enrico Piras Date: Thu, 11 May 2023 15:08:04 +0200 Subject: [PATCH 09/26] build(npm): add cookieconsent dep --- lifemonitor/static/src/package.json | 1 + 1 file changed, 1 insertion(+) diff --git a/lifemonitor/static/src/package.json b/lifemonitor/static/src/package.json index 0edf2ae84..8154a9f51 100644 --- a/lifemonitor/static/src/package.json +++ b/lifemonitor/static/src/package.json @@ -54,6 +54,7 @@ "bootstrap-switch": "3.4.0", "bootstrap-switch-button": "1.1.0", "bootstrap4-duallistbox": "^4.0.2", + "cookieconsent": "^3.1.1", "icheck-bootstrap": "^3.0.1", "jquery": "^3.6.0", "jqvmap-novulnerability": "^1.5.1", From 8d6a7c1f4ae4b1820b5f0f55ddddb2d197cd43a4 Mon Sep 17 00:00:00 2001 From: Marco Enrico Piras Date: Thu, 11 May 2023 16:00:52 +0200 Subject: [PATCH 10/26] build(npm): init dependencies --- lifemonitor/static/src/config/pkg-plugins.sh | 3 +++ lifemonitor/templates/base.j2 | 9 +++++++-- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/lifemonitor/static/src/config/pkg-plugins.sh b/lifemonitor/static/src/config/pkg-plugins.sh index 7e87e4cbc..a9f634195 100755 --- a/lifemonitor/static/src/config/pkg-plugins.sh +++ b/lifemonitor/static/src/config/pkg-plugins.sh @@ -10,6 +10,9 @@ mkdir -p ${target_path} # bootstrap cp -r node_modules/bootstrap/dist "${target_path}/bootstrap" +# cookieconsent +cp -r node_modules/cookieconsent/build "${target_path}/cookieconsent" + # jquery cp -r node_modules/jquery/dist "${target_path}/jquery" diff --git a/lifemonitor/templates/base.j2 b/lifemonitor/templates/base.j2 index 1e1695be2..d9fbf0dda 100644 --- a/lifemonitor/templates/base.j2 +++ b/lifemonitor/templates/base.j2 @@ -44,7 +44,10 @@ - + + + + {% endblock stylesheets %} @@ -79,8 +82,10 @@ - + + + {# Enable notifications #} {{ macros.messages() }} From 445628169e711eac082f2212a1bc1764c980bf53 Mon Sep 17 00:00:00 2001 From: Marco Enrico Piras Date: Thu, 11 May 2023 16:02:42 +0200 Subject: [PATCH 11/26] feat: add cookie consent banner --- lifemonitor/static/src/js/lifemonitor.js | 41 ++++++++++++++++++++++++ lifemonitor/templates/base.j2 | 6 ++++ 2 files changed, 47 insertions(+) diff --git a/lifemonitor/static/src/js/lifemonitor.js b/lifemonitor/static/src/js/lifemonitor.js index 446eecdba..12e4d768f 100644 --- a/lifemonitor/static/src/js/lifemonitor.js +++ b/lifemonitor/static/src/js/lifemonitor.js @@ -110,3 +110,44 @@ $(".data-bootstrap-switch").each(function () { // Initialize tooltips $('[data-bs-toggle="tooltip"]').tooltip(); +// cookie consent +function initCookieConsentBanner(domain){ + window.cookieconsent.initialise( + { + cookie: { + domain: domain ?? 'lifemonitor.eu', + }, + position: 'bottom', + theme: 'edgeless', + palette: { + popup: { + background: '#094b4b', + text: '#ffffff', + link: '#ffffff', + }, + button: { + background: '#f9b233', + text: '#000000', + border: 'transparent', + }, + }, + type: 'info', + content: { + message: + 'We use cookies to optimise our website and our service, in accordance with our privacy policy.', + dismiss: 'Got it!', + deny: 'Refuse cookies', + link: 'Learn more', + href: 'https://www.crs4.it/privacy-policy/', + policy: 'Cookie Policy', + + privacyPolicyLink: 'Privacy Policy', + privacyPolicyHref: 'https://www.crs4.it/privacy-policy/', + + tosLink: 'Terms of Service', + tosHref: 'https://www.crs4.it/privacy-policy/', + }, + } + ); + +} diff --git a/lifemonitor/templates/base.j2 b/lifemonitor/templates/base.j2 index d9fbf0dda..3d1d3bef8 100644 --- a/lifemonitor/templates/base.j2 +++ b/lifemonitor/templates/base.j2 @@ -93,6 +93,12 @@ {% endblock javascripts_libraries %} {% block javascripts %} {% endblock javascripts %} + + + {% set domain = config.EXTERNAL_SERVER_URL|domain %} + From b70812ab6555c1b77ccf75cb8c956cb8edf183b4 Mon Sep 17 00:00:00 2001 From: Marco Enrico Piras Date: Thu, 11 May 2023 16:04:32 +0200 Subject: [PATCH 12/26] feat(util): add jinja filter to extract the URL domain --- lifemonitor/app.py | 2 ++ lifemonitor/utils.py | 7 +++++++ 2 files changed, 9 insertions(+) diff --git a/lifemonitor/app.py b/lifemonitor/app.py index edb8ceb13..56368d3e7 100644 --- a/lifemonitor/app.py +++ b/lifemonitor/app.py @@ -158,3 +158,5 @@ def initialize_app(app: Flask, app_context, prom_registry=None, load_jobs: bool init_metrics(app, prom_registry) # register commands commands.register_commands(app) + # register the domain filter with Jinja + app.jinja_env.filters['domain'] = get_domain diff --git a/lifemonitor/utils.py b/lifemonitor/utils.py index 7ec310ba7..ce8d4e0fa 100644 --- a/lifemonitor/utils.py +++ b/lifemonitor/utils.py @@ -612,6 +612,13 @@ def extract_zip(archive_path, target_path=None): raise lm_exceptions.NotValidROCrateException(detail=msg, original_error=str(e)) +def get_domain(value): + try: + return urlparse(value).netloc.split(':')[0] + except Exception: + raise ValueError("Invalid URL: %r" % value) + + def _make_git_credentials_callback(token: str = None): return pygit2.RemoteCallbacks(pygit2.UserPass('x-access-token', token)) if token else None From bd774ef397d14cc72af30e125914f817d52419fe Mon Sep 17 00:00:00 2001 From: Marco Enrico Piras Date: Thu, 11 May 2023 16:09:40 +0200 Subject: [PATCH 13/26] fix: add missing imports --- lifemonitor/app.py | 3 ++- lifemonitor/utils.py | 1 + 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/lifemonitor/app.py b/lifemonitor/app.py index 56368d3e7..72690b357 100644 --- a/lifemonitor/app.py +++ b/lifemonitor/app.py @@ -25,14 +25,15 @@ from flask import Flask, jsonify, redirect, render_template, request, url_for from flask_cors import CORS from flask_migrate import Migrate -from lifemonitor import redis import lifemonitor.config as config +from lifemonitor import redis from lifemonitor.auth.services import current_user from lifemonitor.integrations import init_integrations from lifemonitor.metrics import init_metrics from lifemonitor.routes import register_routes from lifemonitor.tasks import init_task_queues +from lifemonitor.utils import get_domain from . import commands from .cache import init_cache diff --git a/lifemonitor/utils.py b/lifemonitor/utils.py index ce8d4e0fa..ab9235031 100644 --- a/lifemonitor/utils.py +++ b/lifemonitor/utils.py @@ -41,6 +41,7 @@ from importlib import import_module from os.path import basename, dirname, isfile, join from typing import Dict, List, Literal, Optional, Tuple, Type +from urllib.parse import urlparse import flask import networkx as nx From 5d25f2945379d70826274fe1cfe2f25c008636d4 Mon Sep 17 00:00:00 2001 From: Marco Enrico Piras Date: Thu, 11 May 2023 17:36:19 +0200 Subject: [PATCH 14/26] fix: check if server URL is defined --- lifemonitor/templates/base.j2 | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/lifemonitor/templates/base.j2 b/lifemonitor/templates/base.j2 index 3d1d3bef8..cd47ca09f 100644 --- a/lifemonitor/templates/base.j2 +++ b/lifemonitor/templates/base.j2 @@ -95,10 +95,12 @@ {% block javascripts %} {% endblock javascripts %} - {% set domain = config.EXTERNAL_SERVER_URL|domain %} - + {% if config.EXTERNAL_SERVER_URL %} + {% set domain = config.EXTERNAL_SERVER_URL|domain %} + + {% endif %} From c6e5adef3608c8787b62004572a5fa2a4355f0eb Mon Sep 17 00:00:00 2001 From: Marco Enrico Piras Date: Tue, 23 May 2023 15:01:44 +0200 Subject: [PATCH 15/26] feat(auth): add function to authenticate users by http only session cookie --- lifemonitor/auth/services.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/lifemonitor/auth/services.py b/lifemonitor/auth/services.py index 753dd9cd8..2c8f26260 100644 --- a/lifemonitor/auth/services.py +++ b/lifemonitor/auth/services.py @@ -158,3 +158,9 @@ def check_api_key(api_key, required_scopes): login_user(api_key.user) # return the user_id return {'uid': api_key.user.id} + + +def check_cookie(cookie, required_scopes): + logger.debug("Checking the cookie: %r; scopes required: %r", cookie, required_scopes) + logger.debug("Current user: %r", current_user) + return {'uid': current_user.id} From 2999fad5a976a3ec5df05e01fb7ff8132b84c332 Mon Sep 17 00:00:00 2001 From: Marco Enrico Piras Date: Tue, 23 May 2023 15:32:38 +0200 Subject: [PATCH 16/26] feat(specs): add cookie auth --- specs/api.yaml | 42 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) diff --git a/specs/api.yaml b/specs/api.yaml index f70913958..bd24aaa94 100644 --- a/specs/api.yaml +++ b/specs/api.yaml @@ -166,6 +166,7 @@ paths: summary: "Get registry index" description: "Get the index of workflows available on the registry" security: + - cookieAuth: [] - apiKey: ["user.workflow.read"] - RegistryCodeFlow: ["user.workflow.read"] - AuthorizationCodeFlow: ["user.workflow.read"] @@ -195,6 +196,7 @@ paths: summary: "Get registry index workflow" description: "Get information about the specified workflow of the registry index" security: + - cookieAuth: [] - apiKey: ["user.workflow.read"] - RegistryCodeFlow: ["user.workflow.read"] - AuthorizationCodeFlow: ["user.workflow.read"] @@ -226,6 +228,7 @@ paths: description: | Get information about the current (authenticated) user security: + - cookieAuth: [] - apiKey: ["user.profile"] - RegistryCodeFlow: ["user.profile"] - AuthorizationCodeFlow: ["user.profile"] @@ -254,6 +257,7 @@ paths: description: | List all notifications for the current (authenticated) user security: + - cookieAuth: [] - apiKey: ["user.profile"] - RegistryCodeFlow: ["user.profile"] - AuthorizationCodeFlow: ["user.profile"] @@ -281,6 +285,7 @@ paths: description: | Mark as read notifications for the current (authenticated) user security: + - cookieAuth: [] - apiKey: ["user.profile"] - RegistryCodeFlow: ["user.profile"] - AuthorizationCodeFlow: ["user.profile"] @@ -310,6 +315,7 @@ paths: description: | Delete notifications for the current (authenticated) user security: + - cookieAuth: [] - apiKey: ["user.profile"] - RegistryCodeFlow: ["user.profile"] - AuthorizationCodeFlow: ["user.profile"] @@ -344,6 +350,7 @@ paths: description: | Delete notification for the current (authenticated) user security: + - cookieAuth: [] - apiKey: ["user.profile"] - RegistryCodeFlow: ["user.profile"] - AuthorizationCodeFlow: ["user.profile"] @@ -519,6 +526,7 @@ paths: Submit (a new version of) a workflow indexed on the specified registry as the current (authenticated) user security: + - cookieAuth: [] - apiKey: ["user.workflow.write"] - AuthorizationCodeFlow: ["user.workflow.write"] parameters: @@ -615,6 +623,7 @@ paths: description: | List all workflows submitted or subscribed by the current user security: + - cookieAuth: [] - apiKey: ["user.workflow.read"] - RegistryCodeFlow: ["user.workflow.read"] - AuthorizationCodeFlow: ["user.workflow.read"] @@ -640,6 +649,7 @@ paths: description: | Submit (a new version of) a workflow. security: + - cookieAuth: [] - apiKey: ["user.workflow.write"] - AuthorizationCodeFlow: ["user.workflow.write"] requestBody: @@ -673,6 +683,7 @@ paths: description: | List all subscriptions for the current user security: + - cookieAuth: [] - apiKey: ["user.workflow.read"] - RegistryCodeFlow: ["user.workflow.read"] - AuthorizationCodeFlow: ["user.workflow.read"] @@ -695,6 +706,7 @@ paths: description: | List all workflows either public or registered by the current (authenticated) user or registry client. security: + - cookieAuth: [] - apiKey: ["workflow.read"] - RegistryClientCredentials: ["workflow.read"] - RegistryCodeFlow: ["workflow.read"] @@ -777,6 +789,7 @@ paths: operationId: "workflows_get_latest_version_by_id" tags: ["Workflows"] security: + - cookieAuth: [] - apiKey: ["workflow.read"] - AuthorizationCodeFlow: ["workflow.read"] - RegistryClientCredentials: ["workflow.read"] @@ -813,6 +826,7 @@ paths: operationId: "workflows_put" tags: ["Workflows"] security: + - cookieAuth: [] - apiKey: ["workflow.read"] - AuthorizationCodeFlow: ["workflow.read"] - RegistryClientCredentials: ["workflow.read"] @@ -844,6 +858,7 @@ paths: description: "Delete the specified workflow and all its versions" tags: ["Workflows"] security: + - cookieAuth: [] - apiKey: ["workflow.write"] - RegistryClientCredentials: ["workflow.write"] - RegistryCodeFlow: ["workflow.write"] @@ -870,6 +885,7 @@ paths: operationId: "user_workflow_subscribe" tags: ["Users"] security: + - cookieAuth: [] - apiKey: ["workflow.write"] - AuthorizationCodeFlow: ["workflow.write"] - RegistryCodeFlow: ["workflow.write"] @@ -898,6 +914,7 @@ paths: operationId: "user_workflow_subscribe_events" tags: ["Users"] security: + - cookieAuth: [] - apiKey: ["workflow.write"] - AuthorizationCodeFlow: ["workflow.write"] - RegistryCodeFlow: ["workflow.write"] @@ -935,6 +952,7 @@ paths: operationId: "user_workflow_unsubscribe" tags: ["Users"] security: + - cookieAuth: [] - apiKey: ["workflow.write"] - AuthorizationCodeFlow: ["workflow.write"] - RegistryCodeFlow: ["workflow.write"] @@ -960,6 +978,7 @@ paths: description: "List all versions of the specified workflow" tags: ["Workflows"] security: + - cookieAuth: [] - apiKey: ["workflow.read"] - RegistryClientCredentials: ["workflow.read"] - RegistryCodeFlow: ["workflow.read"] @@ -1017,6 +1036,7 @@ paths: operationId: "workflows_get_version_by_id" tags: ["Workflows"] security: + - cookieAuth: [] - apiKey: ["workflow.read"] - AuthorizationCodeFlow: ["workflow.read"] - RegistryClientCredentials: ["workflow.read"] @@ -1049,6 +1069,7 @@ paths: operationId: "workflows_version_put" tags: ["Workflows"] security: + - cookieAuth: [] - apiKey: ["workflow.read"] - AuthorizationCodeFlow: ["workflow.read"] - RegistryClientCredentials: ["workflow.read"] @@ -1084,6 +1105,7 @@ paths: description: "Delete the specified version of the specified workflow" tags: ["Workflows"] security: + - cookieAuth: [] - apiKey: ["workflow.write"] - RegistryClientCredentials: ["workflow.write"] - RegistryCodeFlow: ["workflow.write"] @@ -1111,6 +1133,7 @@ paths: description: "Get the test status for the specified workflow and workflow version" tags: ["Workflows"] security: + - cookieAuth: [] - apiKey: ["workflow.read"] - RegistryClientCredentials: ["workflow.read"] - RegistryCodeFlow: ["workflow.read"] @@ -1143,6 +1166,7 @@ paths: description: "Get the RO-Crate JSON metadata for the specified workflow and workflow version" tags: ["Workflows"] security: + - cookieAuth: [] - apiKey: ["workflow.read"] - RegistryClientCredentials: ["workflow.read"] - RegistryCodeFlow: ["workflow.read"] @@ -1173,6 +1197,7 @@ paths: description: "Get the RO-Crate ZIP archive for the specified workflow and workflow version" tags: ["Workflows"] security: + - cookieAuth: [] - apiKey: ["workflow.read"] - RegistryClientCredentials: ["workflow.read"] - RegistryCodeFlow: ["workflow.read"] @@ -1206,6 +1231,7 @@ paths: operationId: "workflows_get_suites" tags: ["Workflows"] security: + - cookieAuth: [] - apiKey: ["workflow.read"] - RegistryClientCredentials: ["workflow.read"] - RegistryCodeFlow: ["workflow.read"] @@ -1240,6 +1266,7 @@ paths: operationId: "suites_get_by_uuid" tags: ["Test Suites"] security: + - cookieAuth: [] - apiKey: ["workflow.read"] - RegistryClientCredentials: ["workflow.read"] - RegistryCodeFlow: ["workflow.read"] @@ -1272,6 +1299,7 @@ paths: operationId: "suites_put" tags: ["Test Suites"] security: + - cookieAuth: [] - apiKey: ["workflow.write"] - AuthorizationCodeFlow: ["workflow.write"] - RegistryClientCredentials: ["workflow.write"] @@ -1309,6 +1337,7 @@ paths: operationId: "suites_get_status" tags: ["Test Suites"] security: + - cookieAuth: [] - apiKey: ["workflow.read"] - RegistryClientCredentials: ["workflow.read"] - RegistryCodeFlow: ["workflow.read"] @@ -1336,6 +1365,7 @@ paths: operationId: "suites_get_instances" tags: ["Test Suites"] security: + - cookieAuth: [] - apiKey: ["workflow.read"] - RegistryClientCredentials: ["workflow.read"] - RegistryCodeFlow: ["workflow.read"] @@ -1365,6 +1395,7 @@ paths: operationId: "suites_post_instance" tags: ["Test Suites"] security: + - cookieAuth: [] - apiKey: ["workflow.write"] - RegistryClientCredentials: ["workflow.write"] - RegistryCodeFlow: ["workflow.write"] @@ -1419,6 +1450,7 @@ paths: operationId: "instances_get_by_id" tags: ["Test Instances"] security: + - cookieAuth: [] - apiKey: ["workflow.read"] - RegistryClientCredentials: ["workflow.read"] - RegistryCodeFlow: ["workflow.read"] @@ -1449,6 +1481,7 @@ paths: operationId: "instances_put" tags: ["Test Instances"] security: + - cookieAuth: [] - apiKey: ["workflow.write"] - AuthorizationCodeFlow: ["workflow.write"] - RegistryClientCredentials: ["workflow.write"] @@ -1485,6 +1518,7 @@ paths: description: "Delete the specified test instance" tags: ["Test Instances"] security: + - cookieAuth: [] - apiKey: ["workflow.write"] - RegistryClientCredentials: ["workflow.write"] - RegistryCodeFlow: ["workflow.write"] @@ -1511,6 +1545,7 @@ paths: operationId: "instances_get_builds" tags: ["Test Instances"] security: + - cookieAuth: [] - apiKey: ["workflow.read"] - RegistryClientCredentials: ["workflow.read"] - RegistryCodeFlow: ["workflow.read"] @@ -1543,6 +1578,7 @@ paths: operationId: "instances_builds_get_by_id" tags: ["Test Instances"] security: + - cookieAuth: [] - apiKey: ["workflow.read"] - RegistryClientCredentials: ["workflow.read"] - RegistryCodeFlow: ["workflow.read"] @@ -2882,6 +2918,12 @@ components: - status securitySchemes: + cookieAuth: + type: apiKey + in: cookie + name: lifemonitor_session # cookie name + x-apikeyInfoFunc: lifemonitor.auth.services.check_cookie + apiKey: type: apiKey in: header From c62a0cd859327b117ccbeafd8f8036e456fa8ba6 Mon Sep 17 00:00:00 2001 From: Marco Enrico Piras Date: Tue, 23 May 2023 15:34:00 +0200 Subject: [PATCH 17/26] feat(cfg): config cookie name --- lifemonitor/config.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lifemonitor/config.py b/lifemonitor/config.py index 8bde89049..e2469f9f9 100644 --- a/lifemonitor/config.py +++ b/lifemonitor/config.py @@ -128,6 +128,8 @@ class BaseConfig: ENABLE_REGISTRY_INTEGRATION = False # Service Availability Timeout SERVICE_AVAILABILITY_TIMEOUT = 1 + # Cookie Settings + SESSION_COOKIE_NAME = 'lifemonitor_session' class DevelopmentConfig(BaseConfig): From 248b34358db060adc36e6d4134bf4f2ddf3d685f Mon Sep 17 00:00:00 2001 From: Marco Enrico Piras Date: Tue, 23 May 2023 15:37:05 +0200 Subject: [PATCH 18/26] build: bump version number --- k8s/Chart.yaml | 2 +- lifemonitor/static/src/package.json | 2 +- specs/api.yaml | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/k8s/Chart.yaml b/k8s/Chart.yaml index e995e1d2e..aa7fe32d2 100644 --- a/k8s/Chart.yaml +++ b/k8s/Chart.yaml @@ -12,7 +12,7 @@ version: 0.8.0 # This is the version number of the application being deployed. This version number should be # incremented each time you make changes to the application. Versions are not expected to # follow Semantic Versioning. They should reflect the version the application is using. -appVersion: 0.8.0 +appVersion: 0.11.0 # Chart dependencies dependencies: diff --git a/lifemonitor/static/src/package.json b/lifemonitor/static/src/package.json index 8154a9f51..a76b8abab 100644 --- a/lifemonitor/static/src/package.json +++ b/lifemonitor/static/src/package.json @@ -1,7 +1,7 @@ { "name": "lifemonitor", "description": "Workflow Testing Service", - "version": "0.10.0", + "version": "0.11.0", "license": "MIT", "author": "CRS4", "main": "../dist/js/lifemonitor.min.js", diff --git a/specs/api.yaml b/specs/api.yaml index bd24aaa94..7b300bdb4 100644 --- a/specs/api.yaml +++ b/specs/api.yaml @@ -3,7 +3,7 @@ openapi: "3.0.0" info: - version: "0.10.0" + version: "0.11.0" title: "Life Monitor API" description: | *Workflow sustainability service* @@ -18,7 +18,7 @@ info: servers: - url: / description: > - Version 0.10.0 of API. + Version 0.11.0 of API. tags: - name: GitHub Integration From 8b6c3b3967d204ce8cd4b32e610c403d366b8d2d Mon Sep 17 00:00:00 2001 From: Marco Enrico Piras Date: Tue, 23 May 2023 16:03:18 +0200 Subject: [PATCH 19/26] build(ci): update Python to 3.10 on GH CI action --- .github/workflows/main.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/main.yaml b/.github/workflows/main.yaml index 3d4db7260..69ff813f1 100644 --- a/.github/workflows/main.yaml +++ b/.github/workflows/main.yaml @@ -34,7 +34,7 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - python-version: [3.7] + python-version: ["3.10"] steps: # Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it - uses: actions/checkout@v3 @@ -53,7 +53,7 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - python-version: [3.7] + python-version: ["3.10"] steps: # Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it - uses: actions/checkout@v3 From 888b9edcce5bbac3aac7618a37b0281d8ca23d9a Mon Sep 17 00:00:00 2001 From: Marco Enrico Piras Date: Tue, 23 May 2023 16:21:00 +0200 Subject: [PATCH 20/26] refactor(specs): move HTML resources to a dedicated folder --- lifemonitor/app.py | 2 +- lifemonitor/static/api.yaml | 1 - lifemonitor/static/specs/api.yaml | 1 + lifemonitor/static/{ => specs}/apidocs.html | 0 4 files changed, 2 insertions(+), 2 deletions(-) delete mode 120000 lifemonitor/static/api.yaml create mode 120000 lifemonitor/static/specs/api.yaml rename lifemonitor/static/{ => specs}/apidocs.html (100%) diff --git a/lifemonitor/app.py b/lifemonitor/app.py index 72690b357..dd17d85c6 100644 --- a/lifemonitor/app.py +++ b/lifemonitor/app.py @@ -102,7 +102,7 @@ def health(): @app.route("/openapi.html") def openapi(): - return redirect('/static/apidocs.html') + return redirect('/static/specs/apidocs.html', code=302) @app.before_request def set_request_start_time(): diff --git a/lifemonitor/static/api.yaml b/lifemonitor/static/api.yaml deleted file mode 120000 index a97f32b16..000000000 --- a/lifemonitor/static/api.yaml +++ /dev/null @@ -1 +0,0 @@ -../../specs/api.yaml \ No newline at end of file diff --git a/lifemonitor/static/specs/api.yaml b/lifemonitor/static/specs/api.yaml new file mode 120000 index 000000000..500e0b8fa --- /dev/null +++ b/lifemonitor/static/specs/api.yaml @@ -0,0 +1 @@ +../../../specs/api.yaml \ No newline at end of file diff --git a/lifemonitor/static/apidocs.html b/lifemonitor/static/specs/apidocs.html similarity index 100% rename from lifemonitor/static/apidocs.html rename to lifemonitor/static/specs/apidocs.html From 5b1b62e2a2d6d3912716b378d720637aefd0413f Mon Sep 17 00:00:00 2001 From: Marco Enrico Piras Date: Tue, 23 May 2023 16:26:48 +0200 Subject: [PATCH 21/26] build(cfg): disable cookie auth on API Explorer --- lifemonitor/static/specs/apidocs.html | 1 + 1 file changed, 1 insertion(+) diff --git a/lifemonitor/static/specs/apidocs.html b/lifemonitor/static/specs/apidocs.html index 94bce3236..e65f27322 100644 --- a/lifemonitor/static/specs/apidocs.html +++ b/lifemonitor/static/specs/apidocs.html @@ -42,6 +42,7 @@ primary-color="#1f8787" theme="light" schema-description-expanded="false" + fetch-credentials="omit" >
Date: Wed, 24 May 2023 08:16:36 +0200 Subject: [PATCH 22/26] fix: auto logout with token-based authentication --- lifemonitor/app.py | 7 ++++++- lifemonitor/auth/services.py | 16 ++++++++++++++-- 2 files changed, 20 insertions(+), 3 deletions(-) diff --git a/lifemonitor/app.py b/lifemonitor/app.py index dd17d85c6..43b56af63 100644 --- a/lifemonitor/app.py +++ b/lifemonitor/app.py @@ -28,7 +28,7 @@ import lifemonitor.config as config from lifemonitor import redis -from lifemonitor.auth.services import current_user +from lifemonitor.auth.services import current_user, auto_logout from lifemonitor.integrations import init_integrations from lifemonitor.metrics import init_metrics from lifemonitor.routes import register_routes @@ -111,6 +111,7 @@ def set_request_start_time(): @app.after_request def log_response(response): logger = logging.getLogger("response") + # log the request processing_time = (time.time() * 1000.0 - request.start_time * 1000.0) logger.info( "resp: %s %s %s %s %s %s %s %s %0.3fms", @@ -124,6 +125,10 @@ def log_response(response): request.user_agent, processing_time ) + # remove user from the current session when the authentication + # is performed via API key or OAuth2 token + auto_logout() + # return the response return response return app diff --git a/lifemonitor/auth/services.py b/lifemonitor/auth/services.py index 2c8f26260..37823d1da 100644 --- a/lifemonitor/auth/services.py +++ b/lifemonitor/auth/services.py @@ -25,10 +25,12 @@ import flask_login from flask import g, request, url_for +from werkzeug.local import LocalProxy + from lifemonitor.auth.models import Anonymous, ApiKey, User +from lifemonitor.db import db from lifemonitor.exceptions import LifeMonitorException from lifemonitor.lang import messages -from werkzeug.local import LocalProxy # Config a module level logger logger = logging.getLogger(__name__) @@ -163,4 +165,14 @@ def check_api_key(api_key, required_scopes): def check_cookie(cookie, required_scopes): logger.debug("Checking the cookie: %r; scopes required: %r", cookie, required_scopes) logger.debug("Current user: %r", current_user) - return {'uid': current_user.id} +def auto_logout(): + ''' + Auto logout the current authenticated user + if the request contains an ApiKey or an Authorization header + ''' + if request.headers.get('ApiKey', None) or request.headers.get('Authorization', None): + logger.debug("Found an API Key or an Authorization header: auto logout") + logger.debug("Current user: %r", current_user) + if not current_user.is_anonymous: + db.session.flush() + logout_user() From a99f52fed9fdded4bc53b04ce72524db67927adb Mon Sep 17 00:00:00 2001 From: Marco Enrico Piras Date: Wed, 24 May 2023 08:17:24 +0200 Subject: [PATCH 23/26] test: add test for auto logout --- tests/test_users.py | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/tests/test_users.py b/tests/test_users.py index a84644e03..ce37b47a6 100644 --- a/tests/test_users.py +++ b/tests/test_users.py @@ -65,3 +65,23 @@ def test_user1_auth(user1, client_auth_method, user1_auth): assert "ApiKey" in user1_auth else: assert "Bearer" in user1_auth['Authorization'] + + +@pytest.mark.parametrize("client_auth_method", [ + ClientAuthenticationMethod.NOAUTH, + ClientAuthenticationMethod.BASIC, + ClientAuthenticationMethod.API_KEY, + ClientAuthenticationMethod.AUTHORIZATION_CODE +], indirect=True) +def test_user_auto_logout(app_client, user1, client_auth_method, user1_auth): + logger.debug("Auth: %r, %r, %r", user1_auth, client_auth_method, ClientAuthenticationMethod.BASIC.value) + + r1 = app_client.get('/users/current', headers=user1_auth) + if client_auth_method in [ClientAuthenticationMethod.NOAUTH, ClientAuthenticationMethod.BASIC]: + assert r1.status_code == 401, "Expected 401 status code" + else: + assert r1.status_code == 200, "Expected 200 status code" + logger.debug("Response R1: %r", r1.json) + + r2 = app_client.get('/users/current') + assert r2.status_code == 401, "Expected 401 status code" From c17fa9c928a1c80275b718d2f9ab604c625d35a0 Mon Sep 17 00:00:00 2001 From: Marco Enrico Piras Date: Wed, 24 May 2023 09:29:29 +0200 Subject: [PATCH 24/26] style(flake8): fix blanks --- lifemonitor/auth/services.py | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/lifemonitor/auth/services.py b/lifemonitor/auth/services.py index 37823d1da..5cfafcb95 100644 --- a/lifemonitor/auth/services.py +++ b/lifemonitor/auth/services.py @@ -165,9 +165,16 @@ def check_api_key(api_key, required_scopes): def check_cookie(cookie, required_scopes): logger.debug("Checking the cookie: %r; scopes required: %r", cookie, required_scopes) logger.debug("Current user: %r", current_user) + + # if the cookie is present, return the user_id + if cookie: + return {'uid': current_user.id} + return None + + def auto_logout(): ''' - Auto logout the current authenticated user + Auto logout the current authenticated user if the request contains an ApiKey or an Authorization header ''' if request.headers.get('ApiKey', None) or request.headers.get('Authorization', None): From 7c291ef78362735348906f7afcf695eb013fed0d Mon Sep 17 00:00:00 2001 From: Marco Enrico Piras Date: Fri, 26 May 2023 09:38:56 +0200 Subject: [PATCH 25/26] refactor(auth): prevent creating session from API requests --- lifemonitor/app.py | 12 +++++--- lifemonitor/auth/__init__.py | 3 +- lifemonitor/auth/controllers.py | 14 +++++++++ lifemonitor/auth/services.py | 50 ++++++++++++++++++++------------- tests/test_users.py | 15 ++++++++-- 5 files changed, 67 insertions(+), 27 deletions(-) diff --git a/lifemonitor/app.py b/lifemonitor/app.py index 43b56af63..08d220fdb 100644 --- a/lifemonitor/app.py +++ b/lifemonitor/app.py @@ -28,7 +28,7 @@ import lifemonitor.config as config from lifemonitor import redis -from lifemonitor.auth.services import current_user, auto_logout +from lifemonitor.auth.services import current_user from lifemonitor.integrations import init_integrations from lifemonitor.metrics import init_metrics from lifemonitor.routes import register_routes @@ -111,6 +111,13 @@ def set_request_start_time(): @app.after_request def log_response(response): logger = logging.getLogger("response") + # logger.debug("Current user: %s", current_user) + # logger.debug("request: %s %s %s %s %s %s", + # request.remote_addr, request.method, request.path, + # request.scheme, request.full_path, request.referrer, + # ) + # for h in request.headers: + # logger.debug("header: %s %s", h, request.headers.get(h, None)) # log the request processing_time = (time.time() * 1000.0 - request.start_time * 1000.0) logger.info( @@ -125,9 +132,6 @@ def log_response(response): request.user_agent, processing_time ) - # remove user from the current session when the authentication - # is performed via API key or OAuth2 token - auto_logout() # return the response return response diff --git a/lifemonitor/auth/__init__.py b/lifemonitor/auth/__init__.py index 385ed74a3..c6b66cfd1 100644 --- a/lifemonitor/auth/__init__.py +++ b/lifemonitor/auth/__init__.py @@ -22,7 +22,7 @@ import lifemonitor.auth.oauth2 as oauth2 -from .controllers import blueprint as auth_blueprint +from .controllers import blueprint as auth_blueprint, CustomSessionInterface from .models import EventType, Notification, User, UserNotification from .services import (NotAuthorizedException, authorized, current_registry, current_user, login_manager, login_registry, login_user, @@ -37,6 +37,7 @@ def register_api(app, specs_dir): oauth2.client.register_api(app, specs_dir, "auth.merge") oauth2.server.register_api(app, specs_dir) app.register_blueprint(auth_blueprint) + app.session_interface = CustomSessionInterface() login_manager.init_app(app) diff --git a/lifemonitor/auth/controllers.py b/lifemonitor/auth/controllers.py index b22d3114e..9dfef441d 100644 --- a/lifemonitor/auth/controllers.py +++ b/lifemonitor/auth/controllers.py @@ -24,6 +24,7 @@ import flask from flask import (current_app, flash, redirect, render_template, request, session, url_for) +from flask.sessions import SecureCookieSessionInterface from flask_login import login_required, login_user, logout_user from lifemonitor.cache import Timeout, cached, clear_cache @@ -608,3 +609,16 @@ def delete_generic_code_flow_client(): flash("App removed!", category="success") clear_cache() return redirect(url_for('auth.profile', currentView='oauth2ClientsTab')) + + +class CustomSessionInterface(SecureCookieSessionInterface): + """Prevent creating session from API requests.""" + + def save_session(self, *args, **kwargs): + + if flask.g.get('login_via_request'): + logger.debug("Prevent creating session from API requests") + return + # if login_via_request is not set, then create a new session + logger.debug("Saving session") + return super(CustomSessionInterface, self).save_session(*args, **kwargs) diff --git a/lifemonitor/auth/services.py b/lifemonitor/auth/services.py index 5cfafcb95..3ed1cee34 100644 --- a/lifemonitor/auth/services.py +++ b/lifemonitor/auth/services.py @@ -24,11 +24,10 @@ from functools import wraps import flask_login -from flask import g, request, url_for +from flask import current_app, g, request, url_for from werkzeug.local import LocalProxy from lifemonitor.auth.models import Anonymous, ApiKey, User -from lifemonitor.db import db from lifemonitor.exceptions import LifeMonitorException from lifemonitor.lang import messages @@ -68,8 +67,24 @@ def load_user_from_header(_req): return None -def login_user(user): - flask_login.login_user(user) +@flask_login.user_loaded_from_request.connect +def user_loaded_from_request(app, user=None): + logger.debug("User loaded from request: %s", user) + g.login_via_request = True + + +def login_user(user, remember=False, duration=None, force=False, fresh=True): + logger.debug("User logged in: %s", user) + logger.debug("g.get('login_via_request'): %r", g.get('login_via_request')) + + # signal if API key is provided or Token is in the request header + if request.headers.get('ApiKey', None) or request.headers.get('Authorization', None): + flask_login.user_loaded_from_request.send(current_app._get_current_object(), user=user) + logger.debug("g.get('login_via_request'): %r", g.get('login_via_request')) + else: + logger.debug("Not logged in via request") + + flask_login.login_user(user, remember=remember, duration=duration, force=force, fresh=fresh) def logout_user(): @@ -166,20 +181,17 @@ def check_cookie(cookie, required_scopes): logger.debug("Checking the cookie: %r; scopes required: %r", cookie, required_scopes) logger.debug("Current user: %r", current_user) - # if the cookie is present, return the user_id - if cookie: - return {'uid': current_user.id} - return None + # check is an ApiKey is present in the request + if request.headers.get('ApiKey', None): + return check_api_key(request.headers.get('ApiKey', None), required_scopes) + # check is an Authorization header is present in the request + if request.headers.get('Authorization', None): + from lifemonitor.auth.oauth2.server.services import get_token_scopes + auth_header = request.headers.get('Authorization', None) + if auth_header.startswith('Bearer '): + token = auth_header.replace('Bearer ', '') + return get_token_scopes(token) -def auto_logout(): - ''' - Auto logout the current authenticated user - if the request contains an ApiKey or an Authorization header - ''' - if request.headers.get('ApiKey', None) or request.headers.get('Authorization', None): - logger.debug("Found an API Key or an Authorization header: auto logout") - logger.debug("Current user: %r", current_user) - if not current_user.is_anonymous: - db.session.flush() - logout_user() + # if the cookie is present, return the user_id + return {'uid': current_user.id} diff --git a/tests/test_users.py b/tests/test_users.py index ce37b47a6..9900ee08d 100644 --- a/tests/test_users.py +++ b/tests/test_users.py @@ -20,8 +20,10 @@ import logging import pytest +import requests from flask import g +from lifemonitor.utils import get_base_url from .conftest import ClientAuthenticationMethod from .utils import assert_properties_exist @@ -73,15 +75,22 @@ def test_user1_auth(user1, client_auth_method, user1_auth): ClientAuthenticationMethod.API_KEY, ClientAuthenticationMethod.AUTHORIZATION_CODE ], indirect=True) -def test_user_auto_logout(app_client, user1, client_auth_method, user1_auth): +def test_user_auto_logout(user1, client_auth_method, user1_auth): logger.debug("Auth: %r, %r, %r", user1_auth, client_auth_method, ClientAuthenticationMethod.BASIC.value) - r1 = app_client.get('/users/current', headers=user1_auth) + app_client = requests.session() + app_client_url = f'{get_base_url()}/users/current' + logger.debug("client URL: %r", app_client_url) + + r1 = app_client.get(app_client_url, headers=user1_auth) + logger.debug("headers: %r", r1.headers) + logger.debug("response: %r", r1.content) if client_auth_method in [ClientAuthenticationMethod.NOAUTH, ClientAuthenticationMethod.BASIC]: assert r1.status_code == 401, "Expected 401 status code" else: assert r1.status_code == 200, "Expected 200 status code" logger.debug("Response R1: %r", r1.json) - r2 = app_client.get('/users/current') + r2 = app_client.get(app_client_url) + logger.debug("headers: %r", r2.headers) assert r2.status_code == 401, "Expected 401 status code" From 735ca4a40be5e0f5fa889575485790d5dd0f6891 Mon Sep 17 00:00:00 2001 From: Marco Enrico Piras Date: Fri, 26 May 2023 16:58:12 +0200 Subject: [PATCH 26/26] fix: update links to legal docs --- lifemonitor/static/src/js/lifemonitor.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lifemonitor/static/src/js/lifemonitor.js b/lifemonitor/static/src/js/lifemonitor.js index 12e4d768f..e266df178 100644 --- a/lifemonitor/static/src/js/lifemonitor.js +++ b/lifemonitor/static/src/js/lifemonitor.js @@ -138,14 +138,14 @@ function initCookieConsentBanner(domain){ dismiss: 'Got it!', deny: 'Refuse cookies', link: 'Learn more', - href: 'https://www.crs4.it/privacy-policy/', + href: 'https://lifemonitor.eu/legal/privacy-policy.pdf', policy: 'Cookie Policy', privacyPolicyLink: 'Privacy Policy', - privacyPolicyHref: 'https://www.crs4.it/privacy-policy/', + privacyPolicyHref: 'https://lifemonitor.eu/legal/privacy-policy.pdf', tosLink: 'Terms of Service', - tosHref: 'https://www.crs4.it/privacy-policy/', + tosHref: 'https://lifemonitor.eu/legal/terms-of-service.pdf', }, } );