Skip to content
Snippets Groups Projects
Commit 50e8a7d6 authored by Jan Mach's avatar Jan Mach
Browse files

Implemented the feature for redirection back to original page.

After performing certain actions (item create/update/delete, login, locale change) an attempt will be made to SECURELY redirect user back to original page beforefalling back to default redirection target. (Redmine issue: #3734)
parent 0b32b4e4
No related branches found
No related tags found
No related merge requests found
Showing
with 114 additions and 39 deletions
......@@ -243,6 +243,8 @@ def _setup_app_auth(app):
flask_babel.gettext('You have been successfully logged out.'),
hawat.const.HAWAT_FLASH_SUCCESS
)
# Force user to index page.
return flask.redirect(flask.url_for('index'))
return app
......@@ -320,8 +322,8 @@ def _setup_app_babel(app):
hawat.const.HAWAT_FLASH_SUCCESS
)
next = flask.request.args.get('next')
return flask.redirect(next or flask.url_for('index'))
# Redirect user back to original page.
return hawat.forms.flask_redirect(flask.url_for('index'))
@babel.localeselector
def get_locale():
......
......@@ -360,7 +360,7 @@ class HawatItemCreateView(HawatCRUDLView):
gettext('Create action canceled.'),
hawat.const.HAWAT_FLASH_INFO
)
return flask.redirect(self.get_url_cancel())
return form.flask_redirect(self.get_url_cancel())
return None
......@@ -421,7 +421,7 @@ class HawatItemCreateView(HawatCRUDLView):
flask.Markup(self.get_message_success(str(item))),
hawat.const.HAWAT_FLASH_SUCCESS
)
return flask.redirect(self.get_url_success())
return form.flask_redirect(self.get_url_success())
except Exception:
flask.flash(
......@@ -432,7 +432,7 @@ class HawatItemCreateView(HawatCRUDLView):
traceback.TracebackException(*sys.exc_info()),
self.get_message_failure()
)
return flask.redirect(self.get_url_failure())
return form.flask_redirect(self.get_url_failure())
context.update(
action_name = gettext('Create'),
......@@ -500,7 +500,7 @@ class HawatItemUpdateView(HawatCRUDLView):
gettext('Update action canceled.'),
hawat.const.HAWAT_FLASH_INFO
)
return flask.redirect(self.get_url_cancel())
return form.flask_redirect(self.get_url_cancel())
return None
......@@ -559,7 +559,7 @@ class HawatItemUpdateView(HawatCRUDLView):
gettext('No changes detected, no update needed.'),
hawat.const.HAWAT_FLASH_INFO
)
return flask.redirect(self.get_url_success())
return form.flask_redirect(self.get_url_success())
self.dobefore(item)
self.dbsession.commit()
......@@ -569,7 +569,7 @@ class HawatItemUpdateView(HawatCRUDLView):
flask.Markup(self.get_message_success(str(item))),
hawat.const.HAWAT_FLASH_SUCCESS
)
return flask.redirect(self.get_url_success())
return form.flask_redirect(self.get_url_success())
except Exception:
flask.flash(
......@@ -580,7 +580,7 @@ class HawatItemUpdateView(HawatCRUDLView):
traceback.TracebackException(*sys.exc_info()),
self.get_message_failure(item_id)
)
return flask.redirect(self.get_url_failure())
return form.flask_redirect(self.get_url_failure())
context.update(
action_name = gettext('Update'),
......@@ -650,7 +650,7 @@ class HawatItemDeleteView(HawatCRUDLView):
gettext('Delete action canceled.'),
hawat.const.HAWAT_FLASH_INFO
)
return flask.redirect(self.get_url_cancel())
return form.flask_redirect(self.get_url_cancel())
return None
......@@ -701,7 +701,7 @@ class HawatItemDeleteView(HawatCRUDLView):
flask.Markup(self.get_message_success(str(item))),
hawat.const.HAWAT_FLASH_SUCCESS
)
return flask.redirect(self.get_url_success())
return form.flask_redirect(self.get_url_success())
except Exception:
flask.flash(
......@@ -712,7 +712,7 @@ class HawatItemDeleteView(HawatCRUDLView):
traceback.TracebackException(*sys.exc_info()),
self.get_message_failure(item_id)
)
return flask.redirect(self.get_url_failure())
return form.flask_redirect(self.get_url_failure())
context.update(
delete_form = form,
......
......@@ -43,6 +43,7 @@ from flask_babel import gettext, lazy_gettext
#
import hawat.const
import hawat.base
import hawat.forms
from hawat.models.user import GuiUserModel
from hawat.blueprints.auth_dev.forms import LoginForm
......@@ -99,24 +100,23 @@ class LoginView(hawat.base.HawatSimpleView):
)
flask.current_app.logger.info("User '{}' successfully logged in.".format(user.login))
#next = flask.request.args.get('next')
# is_safe_url should check if the url is safe for redirects.
# See http://flask.pocoo.org/snippets/62/ for an example.
#if not is_safe_url(next):
# return flask.abort(400)
#return flask.redirect(next or flask.url_for('index'))
return flask.redirect(flask.url_for('index'))
# Redirect user back to original page.
return hawat.forms.flask_redirect(flask.url_for('index'))
except sqlalchemy.orm.exc.MultipleResultsFound:
flask.current_app.logger.error("Multiple results found for user login '{}'.".format(form.login.data))
return flask.abort(500)
except sqlalchemy.orm.exc.NoResultFound:
flask.flash(
gettext('You have entered wrong login credentials.'),
hawat.const.HAWAT_FLASH_FAILURE
)
context.update(form = form)
context.update(
form = form,
next = hawat.forms.get_redirect_target()
)
return flask.render_template('auth_dev/login.html', **context)
......
......@@ -6,7 +6,7 @@
<div class="col-lg-12">
<div class="well col-md-4 col-md-offset-4">
<form method="POST" action="{{ url_for('auth_dev.login') }}">
<form method="POST" action="{{ url_for('auth_dev.login', next = next) }}">
<fieldset>
<legend><i class="fa fa-lock"></i> {{ hawat_view_title }}</legend>
......
......@@ -43,6 +43,7 @@ from flask_babel import gettext, lazy_gettext
import hawat.const
import hawat.base
import hawat.db
import hawat.forms
from hawat.models.user import GuiUserModel
from hawat.blueprints.auth_env.forms import RegisterUserAccountForm
......@@ -101,9 +102,11 @@ class LoginView(hawat.base.HawatSimpleView):
dbsess = hawat.db.db_get().session
try:
user = dbsess.query(GuiUserModel).filter(GuiUserModel.login == user_login).one()
except sqlalchemy.orm.exc.MultipleResultsFound:
flask.current_app.logger.error("Multiple results found for user login '{}'.".format(user_login))
return flask.abort(500)
except sqlalchemy.orm.exc.NoResultFound:
flask.flash(
gettext('You have entered wrong login credentials.'),
......@@ -132,13 +135,8 @@ class LoginView(hawat.base.HawatSimpleView):
)
flask.current_app.logger.info("User '{}' successfully logged in.".format(user.login))
#next = flask.request.args.get('next')
# is_safe_url should check if the url is safe for redirects.
# See http://flask.pocoo.org/snippets/62/ for an example.
#if not is_safe_url(next):
# return flask.abort(400)
#return flask.redirect(next or flask.url_for('index'))
return flask.redirect(flask.url_for('index'))
# Redirect user back to original page.
return hawat.forms.flask_redirect(flask.url_for('index'))
class RegisterView(hawat.base.HawatCRUDLView):
......
......@@ -21,6 +21,8 @@
<div class="modal-footer">
<form method="POST" action="{{ delete_url }}">
{{ macros_site.form_errors(delete_form.next.errors) }}
{{ delete_form.next }}
{{ macros_site.form_errors(delete_form.csrf_token.errors) }}
{{ delete_form.csrf_token }}
{{ delete_form.cancel(class_='btn btn-default') }}
......
......@@ -23,13 +23,13 @@ from wtforms.ext.sqlalchemy.fields import QuerySelectField
#
# Flask related modules.
#
import flask_wtf
from flask_babel import lazy_gettext
#
# Custom modules.
#
import hawat.db
import hawat.forms
from mentat.datatype.sqldb import GroupModel
from mentat.const import REPORTING_FILTER_BASIC, REPORTING_FILTER_ADVANCED
......@@ -41,7 +41,7 @@ def get_available_groups():
return hawat.db.db_query(GroupModel).order_by(GroupModel.name).all()
class BaseFilterForm(flask_wtf.FlaskForm):
class BaseFilterForm(hawat.forms.BaseItemForm):
"""
Class representing base reporting filter form.
"""
......
......@@ -31,6 +31,8 @@
{{ macros_site.render_form_item_radiobutton(form.enabled) }}
{{ macros_site.form_errors(form.next.errors) }}
{{ form.next }}
{{ macros_site.form_errors(form.csrf_token.errors) }}
{{ form.csrf_token }}
{{ form.cancel(class_='btn btn-default') }}
......
......@@ -24,13 +24,13 @@ from wtforms.ext.sqlalchemy.fields import QuerySelectField, QuerySelectMultipleF
#
# Flask related modules.
#
import flask_wtf
from flask_babel import gettext, lazy_gettext
#
# Custom modules.
#
import hawat.db
import hawat.forms
from mentat.datatype.sqldb import GroupModel, UserModel
......@@ -92,7 +92,7 @@ def get_available_groups(grid = None):
return hawat.db.db_query(GroupModel).order_by(GroupModel.name).all()
class BaseGroupForm(flask_wtf.FlaskForm):
class BaseGroupForm(hawat.forms.BaseItemForm):
"""
Class representing base group form.
"""
......
......@@ -45,6 +45,8 @@
{{ macros_site.form_errors(form.source.errors) }}
{{ form.source }}
{{ macros_site.form_errors(form.next.errors) }}
{{ form.next }}
{{ macros_site.form_errors(form.csrf_token.errors) }}
{{ form.csrf_token }}
{{ form.cancel(class_='btn btn-default') }}
......
......@@ -25,7 +25,6 @@ from wtforms.ext.sqlalchemy.fields import QuerySelectField
#
# Flask related modules.
#
import flask_wtf
from flask_babel import gettext, lazy_gettext
#
......@@ -83,7 +82,7 @@ def check_email_list(form, field):
)
class BaseNetworkForm(flask_wtf.FlaskForm):
class BaseNetworkForm(hawat.forms.BaseItemForm):
"""
Class representing base network record form.
"""
......
......@@ -33,6 +33,8 @@
{{ macros_site.form_errors(form.source.errors) }}
{{ form.source }}
{{ macros_site.form_errors(form.next.errors) }}
{{ form.next }}
{{ macros_site.form_errors(form.csrf_token.errors) }}
{{ form.csrf_token }}
{{ form.cancel(class_='btn btn-default') }}
......
......@@ -24,7 +24,6 @@ from wtforms.ext.sqlalchemy.fields import QuerySelectField
#
# Flask related modules.
#
import flask_wtf
from flask_babel import gettext, lazy_gettext
#
......@@ -64,7 +63,7 @@ def check_email_list(form, field):
)
class BaseSettingsReportingForm(flask_wtf.FlaskForm):
class BaseSettingsReportingForm(hawat.forms.BaseItemForm):
"""
Class representing base reporting settings form.
"""
......
......@@ -119,6 +119,8 @@
<hr>
{{ macros_site.form_errors(form.next.errors) }}
{{ form.next }}
{{ macros_site.form_errors(form.csrf_token.errors) }}
{{ form.csrf_token }}
{{ form.cancel(class_='btn btn-default') }}
......
......@@ -25,13 +25,13 @@ from wtforms.ext.sqlalchemy.fields import QuerySelectMultipleField
#
# Flask related modules.
#
import flask_wtf
from flask_babel import gettext, lazy_gettext
#
# Custom modules.
#
import hawat.db
import hawat.forms
from hawat.models.user import GuiUserModel
from mentat.datatype.sqldb import GroupModel
......@@ -71,7 +71,7 @@ def get_available_groups():
return hawat.db.db_query(GroupModel).order_by(GroupModel.name).all()
class BaseUserAccountForm(flask_wtf.FlaskForm):
class BaseUserAccountForm(hawat.forms.BaseItemForm):
"""
Class representing base user account form.
"""
......
......@@ -53,6 +53,8 @@
<hr>
{{ macros_site.form_errors(form.next.errors) }}
{{ form.next }}
{{ macros_site.form_errors(form.csrf_token.errors) }}
{{ form.csrf_token }}
{{ form.cancel(class_='btn btn-default') }}
......
......@@ -23,15 +23,54 @@ __author__ = "Jan Mach <jan.mach@cesnet.cz>"
__credits__ = "Pavel Kácha <pavel.kacha@cesnet.cz>, Andrea Kropáčová <andrea.kropacova@cesnet.cz>"
import urllib.parse
import wtforms
#
# Flask related modules.
#
import flask
import flask_wtf
from flask_babel import lazy_gettext
def is_safe_url(target):
"""
Check, if the URL is safe enough to be redirected to.
"""
ref_url = urllib.parse.urlparse(flask.request.host_url)
test_url = urllib.parse.urlparse(urllib.parse.urljoin(flask.request.host_url, target))
return test_url.scheme in ('http', 'https') and \
ref_url.netloc == test_url.netloc
def get_redirect_target():
"""
Get redirection target, either from GET request variable, or from referrer header.
"""
for target in flask.request.args.get('next'), flask.request.referrer:
if not target:
continue
if is_safe_url(target):
return target
def flask_redirect(self, url = None):
"""
Perfom HTTP redirection to designated target URL.
"""
target = get_redirect_target()
if target:
flask.flash("TEST1: detect", "info")
return flask.redirect(target)
if url:
flask.flash("TEST2: default", "info")
return flask.redirect(url)
flask.flash("TEST3: index", "info")
return flask.redirect(flask.url_for('index'))
class CommaListField(wtforms.Field):
"""
Custom widget representing list of strings as comma separated list.
......@@ -61,7 +100,33 @@ class CommaListField(wtforms.Field):
yield item
class ItemDeleteForm(flask_wtf.FlaskForm):
class BaseItemForm(flask_wtf.FlaskForm):
"""
Class representing generic item action (create/update/delete) form for Hawat
application.
This form contains support for redirection back to original page.
"""
next = wtforms.HiddenField()
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
# Populate the redirection URL.
if not self.next.data:
self.next.data = get_redirect_target() or ''
def flask_redirect(self, url = None):
"""
Perfom HTTP redirection to designated target URL.
"""
if self.next.data and is_safe_url(self.next.data):
flask.flash("TEST0: form", "info")
return flask.redirect(self.next.data)
return flask_redirect(url)
class ItemDeleteForm(BaseItemForm):
"""
Class representing generic item deletion confirmation form for Hawat application.
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment