Skip to content

Commit

Permalink
release v0.11.0
Browse files Browse the repository at this point in the history
  • Loading branch information
kikkomep committed May 26, 2023
2 parents 48a69dc + 45f0b9f commit 1708044
Show file tree
Hide file tree
Showing 19 changed files with 233 additions and 26 deletions.
4 changes: 2 additions & 2 deletions .github/workflows/main.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
Expand Down
2 changes: 1 addition & 1 deletion k8s/Chart.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down
16 changes: 14 additions & 2 deletions lifemonitor/app.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -101,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():
Expand All @@ -110,6 +111,14 @@ 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(
"resp: %s %s %s %s %s %s %s %s %0.3fms",
Expand All @@ -123,6 +132,7 @@ def log_response(response):
request.user_agent,
processing_time
)
# return the response
return response

return app
Expand Down Expand Up @@ -158,3 +168,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
3 changes: 2 additions & 1 deletion lifemonitor/auth/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -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)


Expand Down
30 changes: 23 additions & 7 deletions lifemonitor/auth/controllers.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -162,7 +163,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",))
Expand All @@ -177,7 +178,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
Expand Down Expand Up @@ -212,7 +214,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)
Expand All @@ -228,7 +230,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'),
Expand All @@ -242,7 +243,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
Expand All @@ -254,7 +255,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())


Expand All @@ -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)

Expand All @@ -281,9 +283,10 @@ 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', '/')
next_route = request.args.get('next', '/logout' if back_param else '/')
logger.debug("Next route after logout: %r", next_route)
return redirect(next_route)

Expand Down Expand Up @@ -606,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)
3 changes: 2 additions & 1 deletion lifemonitor/auth/forms.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
4 changes: 2 additions & 2 deletions lifemonitor/auth/oauth2/client/controllers.py
Original file line number Diff line number Diff line change
Expand Up @@ -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


Expand Down Expand Up @@ -261,7 +261,7 @@ def handle_authorize(self, provider: FlaskRemoteApp, token, user_info: OAuthUser
next_url = NextRouteRegistry.pop()
flash(f"Logged with your <b>\"{identity.provider.name}\"</b> 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):
Expand Down
45 changes: 41 additions & 4 deletions lifemonitor/auth/services.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,11 +24,12 @@
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.exceptions import LifeMonitorException
from lifemonitor.lang import messages
from werkzeug.local import LocalProxy

# Config a module level logger
logger = logging.getLogger(__name__)
Expand Down Expand Up @@ -66,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():
Expand Down Expand Up @@ -158,3 +175,23 @@ 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)

# 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)

# if the cookie is present, return the user_id
return {'uid': current_user.id}
2 changes: 2 additions & 0 deletions lifemonitor/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -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):
Expand Down
1 change: 0 additions & 1 deletion lifemonitor/static/api.yaml

This file was deleted.

1 change: 1 addition & 0 deletions lifemonitor/static/specs/api.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@
primary-color="#1f8787"
theme="light"
schema-description-expanded="false"
fetch-credentials="omit"
>
<div
slot="nav-logo"
Expand Down
3 changes: 3 additions & 0 deletions lifemonitor/static/src/config/pkg-plugins.sh
Original file line number Diff line number Diff line change
Expand Up @@ -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"

Expand Down
41 changes: 41 additions & 0 deletions lifemonitor/static/src/js/lifemonitor.js
Original file line number Diff line number Diff line change
Expand Up @@ -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://lifemonitor.eu/legal/privacy-policy.pdf',
policy: 'Cookie Policy',

privacyPolicyLink: 'Privacy Policy',
privacyPolicyHref: 'https://lifemonitor.eu/legal/privacy-policy.pdf',

tosLink: 'Terms of Service',
tosHref: 'https://lifemonitor.eu/legal/terms-of-service.pdf',
},
}
);

}
3 changes: 2 additions & 1 deletion lifemonitor/static/src/package.json
Original file line number Diff line number Diff line change
@@ -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",
Expand Down Expand Up @@ -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",
Expand Down
Loading

0 comments on commit 1708044

Please sign in to comment.