aboutsummaryrefslogtreecommitdiff
path: root/ihatemoney/web.py
diff options
context:
space:
mode:
Diffstat (limited to 'ihatemoney/web.py')
-rw-r--r--ihatemoney/web.py80
1 files changed, 60 insertions, 20 deletions
diff --git a/ihatemoney/web.py b/ihatemoney/web.py
index 82e1591..753fe42 100644
--- a/ihatemoney/web.py
+++ b/ihatemoney/web.py
@@ -34,17 +34,30 @@ main = Blueprint("main", __name__)
login_throttler = LoginThrottler(max_attempts=3, delay=1)
-def requires_admin(f):
+def requires_admin(bypass=None):
"""Require admin permissions for @requires_admin decorated endpoints.
- Has no effect if ADMIN_PASSWORD is empty (default value)
+
+ This has no effect if ADMIN_PASSWORD is empty.
+
+ :param bypass: Used to conditionnaly bypass the admin authentication.
+ It expects a tuple containing the name of an application
+ setting and its expected value.
+ e.g. if you use @require_admin(bypass=("ALLOW_PUBLIC_PROJECT_CREATION", True))
+ Admin authentication will be bypassed when ALLOW_PUBLIC_PROJECT_CREATION is
+ set to True.
"""
- @wraps(f)
- def admin_auth(*args, **kws):
- is_admin = session.get('is_admin')
- if is_admin or not current_app.config['ADMIN_PASSWORD']:
- return f(*args, **kws)
- raise Redirect303(url_for('.admin', goto=request.path))
- return admin_auth
+ def check_admin(f):
+ @wraps(f)
+ def admin_auth(*args, **kws):
+ is_admin_auth_bypassed = False
+ if bypass is not None and current_app.config.get(bypass[0]) == bypass[1]:
+ is_admin_auth_bypassed = True
+ is_admin = session.get('is_admin')
+ if is_admin or is_admin_auth_bypassed:
+ return f(*args, **kws)
+ raise Redirect303(url_for('.admin', goto=request.path))
+ return admin_auth
+ return check_admin
@main.url_defaults
@@ -60,9 +73,23 @@ def add_project_id(endpoint, values):
@main.url_value_preprocessor
+def set_show_admin_dashboard_link(endpoint, values):
+ """Sets the "show_admin_dashboard_link" variable application wide
+ in order to use it in the layout template.
+ """
+
+ g.show_admin_dashboard_link = (
+ current_app.config["ACTIVATE_ADMIN_DASHBOARD"]
+ and current_app.config["ADMIN_PASSWORD"]
+ )
+
+
+@main.url_value_preprocessor
def pull_project(endpoint, values):
"""When a request contains a project_id value, transform it directly
- into a project by checking the credentials are stored in session.
+ into a project by checking the credentials stored in the session.
+
+ With administration credentials, one can access any project.
If not, redirect the user to an authentication form
"""
@@ -76,7 +103,9 @@ def pull_project(endpoint, values):
if not project:
raise Redirect303(url_for(".create_project",
project_id=project_id))
- if project.id in session and session[project.id] == project.password:
+
+ is_admin = session.get('is_admin')
+ if (project.id in session and session[project.id] == project.password) or is_admin:
# add project into kwargs and call the original function
g.project = project
else:
@@ -87,15 +116,20 @@ def pull_project(endpoint, values):
@main.route("/admin", methods=["GET", "POST"])
def admin():
- """Admin authentication"""
+ """Admin authentication.
+
+ When ADMIN_PASSWORD is empty, admin authentication is deactivated.
+ """
form = AdminAuthenticationForm()
goto = request.args.get('goto', url_for('.home'))
+ is_admin_auth_enabled = bool(current_app.config['ADMIN_PASSWORD'])
if request.method == "POST":
client_ip = request.remote_addr
if not login_throttler.is_login_allowed(client_ip):
msg = _("Too many failed login attempts, please retry later.")
form.errors['admin_password'] = [msg]
- return render_template("authenticate.html", form=form, admin_auth=True)
+ return render_template("admin.html", form=form, admin_auth=True,
+ is_admin_auth_enabled=is_admin_auth_enabled)
if form.validate():
# Valid password
if (check_password_hash(current_app.config['ADMIN_PASSWORD'],
@@ -109,7 +143,8 @@ def admin():
msg = _("This admin password is not the right one. Only %(num)d attempts left.",
num=login_throttler.get_remaining_attempts(client_ip))
form.errors['admin_password'] = [msg]
- return render_template("authenticate.html", form=form, admin_auth=True)
+ return render_template("admin.html", form=form, admin_auth=True,
+ is_admin_auth_enabled=is_admin_auth_enabled)
@main.route("/authenticate", methods=["GET", "POST"])
@@ -167,18 +202,17 @@ def authenticate(project_id=None):
def home():
project_form = ProjectForm()
auth_form = AuthenticationForm()
- # If ADMIN_PASSWORD is empty we consider that admin mode is disabled
- is_admin_mode_enabled = bool(current_app.config['ADMIN_PASSWORD'])
is_demo_project_activated = current_app.config['ACTIVATE_DEMO_PROJECT']
+ is_public_project_creation_allowed = current_app.config['ALLOW_PUBLIC_PROJECT_CREATION']
return render_template("home.html", project_form=project_form,
is_demo_project_activated=is_demo_project_activated,
- is_admin_mode_enabled=is_admin_mode_enabled,
+ is_public_project_creation_allowed=is_public_project_creation_allowed,
auth_form=auth_form, session=session)
@main.route("/create", methods=["GET", "POST"])
-@requires_admin
+@requires_admin(bypass=("ALLOW_PUBLIC_PROJECT_CREATION", True))
def create_project():
form = ProjectForm()
if request.method == "GET" and 'project_id' in request.values:
@@ -295,7 +329,7 @@ def delete_project():
g.project.remove_project()
flash(_('Project successfully deleted'))
- return redirect(url_for(".home"))
+ return redirect(request.headers.get('Referer') or url_for('.home'))
@main.route("/exit")
@@ -530,5 +564,11 @@ def statistics():
@main.route("/dashboard")
+@requires_admin()
def dashboard():
- return render_template("dashboard.html", projects=Project.query.all())
+ is_admin_dashboard_activated = current_app.config['ACTIVATE_ADMIN_DASHBOARD']
+ return render_template(
+ "dashboard.html",
+ projects=Project.query.all(),
+ is_admin_dashboard_activated=is_admin_dashboard_activated
+ )