From 7205b4a01b2b5593b9c7915538a61192ea455df7 Mon Sep 17 00:00:00 2001 From: Khanh Ngo Date: Wed, 18 Dec 2019 15:25:20 +0700 Subject: [PATCH] User session improvement - Add session handler on other blueprint's before request - Adjustment in using jTimeout to close warning popup on other tabs when we extend the session --- powerdnsadmin/models/setting.py | 1 + powerdnsadmin/routes/admin.py | 16 +++++++++++++-- powerdnsadmin/routes/api.py | 15 +++++++++++--- powerdnsadmin/routes/dashboard.py | 27 ++++++++++++++++++++++--- powerdnsadmin/routes/domain.py | 27 ++++++++++++++++++++++--- powerdnsadmin/routes/user.py | 28 +++++++++++++++++++++++--- powerdnsadmin/templates/base.html | 33 ++++++++++++++++++++++--------- 7 files changed, 124 insertions(+), 23 deletions(-) diff --git a/powerdnsadmin/models/setting.py b/powerdnsadmin/models/setting.py index 6f44663..376dbbd 100644 --- a/powerdnsadmin/models/setting.py +++ b/powerdnsadmin/models/setting.py @@ -26,6 +26,7 @@ class Setting(db.Model): 'bg_domain_updates': False, 'site_name': 'PowerDNS-Admin', 'session_timeout': 10, + 'warn_session_timeout': True, 'pdns_api_url': '', 'pdns_api_key': '', 'pdns_api_timeout': 30, diff --git a/powerdnsadmin/routes/admin.py b/powerdnsadmin/routes/admin.py index defe591..efcaac5 100644 --- a/powerdnsadmin/routes/admin.py +++ b/powerdnsadmin/routes/admin.py @@ -1,7 +1,8 @@ import json +import datetime import traceback from ast import literal_eval -from flask import Blueprint, render_template, make_response, url_for, current_app, request, redirect, jsonify, abort, flash +from flask import Blueprint, render_template, make_response, url_for, current_app, request, redirect, jsonify, abort, flash, session from flask_login import login_required, current_user from ..decorators import operator_role_required, admin_role_required @@ -23,6 +24,16 @@ admin_bp = Blueprint('admin', url_prefix='/admin') +@admin_bp.before_request +def before_request(): + # Manage session timeout + session.permanent = True + current_app.permanent_session_lifetime = datetime.timedelta( + minutes=int(Setting().get('session_timeout'))) + session.modified = True + + + @admin_bp.route('/pdns', methods=['GET']) @login_required @operator_role_required @@ -489,7 +500,8 @@ def setting_basic(): 'default_domain_table_size', 'auto_ptr', 'record_quick_edit', 'pretty_ipv6_ptr', 'dnssec_admins_only', 'allow_user_create_domain', 'bg_domain_updates', 'site_name', - 'session_timeout', 'ttl_options', 'pdns_api_timeout' + 'session_timeout', 'warn_session_timeout', 'ttl_options', + 'pdns_api_timeout' ] return render_template('admin_setting_basic.html', settings=settings) diff --git a/powerdnsadmin/routes/api.py b/powerdnsadmin/routes/api.py index df5567b..517263c 100644 --- a/powerdnsadmin/routes/api.py +++ b/powerdnsadmin/routes/api.py @@ -1,6 +1,6 @@ import json from urllib.parse import urljoin -from flask import Blueprint, g, request, abort, current_app +from flask import Blueprint, g, request, abort, current_app, make_response, jsonify from flask_login import current_user from ..models.base import db @@ -88,7 +88,16 @@ def handle_request_is_not_json(err): @api_bp.before_request @is_json def before_request(): - pass + # Check site is in maintenance mode + maintenance = Setting().get('maintenance') + if maintenance and current_user.is_authenticated and current_user.role.name not in [ + 'Administrator', 'Operator' + ]: + return make_response( + jsonify({ + "status": False, + "msg": "Site is in maintenance mode" + })) @api_bp.route('/pdnsadmin/zones', methods=['POST']) @@ -281,7 +290,7 @@ def api_get_apikeys(domain_name): if current_user.role.name not in ['Administrator', 'Operator']: if domain_name: msg = "Check if domain {0} exists and \ - is allowed for user." .format(domain_name) + is allowed for user." .format(domain_name) current_app.logger.debug(msg) apikeys = current_user.get_apikeys(domain_name) diff --git a/powerdnsadmin/routes/dashboard.py b/powerdnsadmin/routes/dashboard.py index bae0f1e..e04c6de 100644 --- a/powerdnsadmin/routes/dashboard.py +++ b/powerdnsadmin/routes/dashboard.py @@ -1,9 +1,10 @@ -from flask import Blueprint, render_template, url_for, current_app, request, jsonify, redirect -from flask_login import login_required, current_user +import datetime +from flask import Blueprint, render_template, url_for, current_app, request, jsonify, redirect, g, session +from flask_login import login_required, current_user, login_manager from sqlalchemy import not_ from ..lib.utils import customBoxes -from ..models.user import User +from ..models.user import User, Anonymous from ..models.account import Account from ..models.account_user import AccountUser from ..models.domain import Domain @@ -19,6 +20,26 @@ dashboard_bp = Blueprint('dashboard', url_prefix='/dashboard') +@dashboard_bp.before_request +def before_request(): + # Check if user is anonymous + g.user = current_user + login_manager.anonymous_user = Anonymous + + # Check site is in maintenance mode + maintenance = Setting().get('maintenance') + if maintenance and current_user.is_authenticated and current_user.role.name not in [ + 'Administrator', 'Operator' + ]: + return render_template('maintenance.html') + + # Manage session timeout + session.permanent = True + current_app.permanent_session_lifetime = datetime.timedelta( + minutes=int(Setting().get('session_timeout'))) + session.modified = True + + @dashboard_bp.route('/domains-custom/', methods=['GET']) @login_required def domains_custom(boxId): diff --git a/powerdnsadmin/routes/domain.py b/powerdnsadmin/routes/domain.py index b4962f6..ccf5736 100644 --- a/powerdnsadmin/routes/domain.py +++ b/powerdnsadmin/routes/domain.py @@ -1,15 +1,16 @@ import re import json +import datetime import traceback import dns.name import dns.reversename from distutils.version import StrictVersion -from flask import Blueprint, render_template, make_response, url_for, current_app, request, redirect, abort, jsonify -from flask_login import login_required, current_user +from flask import Blueprint, render_template, make_response, url_for, current_app, request, redirect, abort, jsonify, g, session +from flask_login import login_required, current_user, login_manager from ..lib.utils import pretty_json from ..decorators import can_create_domain, operator_role_required, can_access_domain, can_configure_dnssec -from ..models.user import User +from ..models.user import User, Anonymous from ..models.account import Account from ..models.setting import Setting from ..models.history import History @@ -26,6 +27,26 @@ domain_bp = Blueprint('domain', url_prefix='/domain') +@domain_bp.before_request +def before_request(): + # Check if user is anonymous + g.user = current_user + login_manager.anonymous_user = Anonymous + + # Check site is in maintenance mode + maintenance = Setting().get('maintenance') + if maintenance and current_user.is_authenticated and current_user.role.name not in [ + 'Administrator', 'Operator' + ]: + return render_template('maintenance.html') + + # Manage session timeout + session.permanent = True + current_app.permanent_session_lifetime = datetime.timedelta( + minutes=int(Setting().get('session_timeout'))) + session.modified = True + + @domain_bp.route('/', methods=['GET']) @login_required @can_access_domain diff --git a/powerdnsadmin/routes/user.py b/powerdnsadmin/routes/user.py index 4995aad..b623736 100644 --- a/powerdnsadmin/routes/user.py +++ b/powerdnsadmin/routes/user.py @@ -1,10 +1,12 @@ +import datetime import qrcode as qrc import qrcode.image.svg as qrc_svg from io import BytesIO -from flask import Blueprint, request, render_template, make_response, jsonify, redirect, url_for, session -from flask_login import current_user, login_required +from flask import Blueprint, request, render_template, make_response, jsonify, redirect, url_for, g, session, current_app +from flask_login import current_user, login_required, login_manager -from ..models.user import User +from ..models.user import User, Anonymous +from ..models.setting import Setting user_bp = Blueprint('user', __name__, @@ -12,6 +14,26 @@ user_bp = Blueprint('user', url_prefix='/user') +@user_bp.before_request +def before_request(): + # Check if user is anonymous + g.user = current_user + login_manager.anonymous_user = Anonymous + + # Check site is in maintenance mode + maintenance = Setting().get('maintenance') + if maintenance and current_user.is_authenticated and current_user.role.name not in [ + 'Administrator', 'Operator' + ]: + return render_template('maintenance.html') + + # Manage session timeout + session.permanent = True + current_app.permanent_session_lifetime = datetime.timedelta( + minutes=int(Setting().get('session_timeout'))) + session.modified = True + + @user_bp.route('/profile', methods=['GET', 'POST']) @login_required def profile(): diff --git a/powerdnsadmin/templates/base.html b/powerdnsadmin/templates/base.html index 4542d7a..4d9ad6c 100644 --- a/powerdnsadmin/templates/base.html +++ b/powerdnsadmin/templates/base.html @@ -174,8 +174,18 @@ {% block scripts %} {% assets "js_main" -%} + {% if SETTING.get('warn_session_timeout') %} + {% endif %} {%- endassets %} {% endblock %} {% block extrascripts %}