From a4a1bd54510d67aa5af5b9434ab0c8d007c6e7a5 Mon Sep 17 00:00:00 2001 From: Sindre Stephansen Date: Tue, 17 Mar 2020 14:36:28 +0100 Subject: [PATCH 1/2] Add a render helper that adds required globals --- src/app/views/app.py | 13 +------------ src/app/views/apply.py | 10 ++++------ src/app/views/index.py | 12 +++++------- src/app/views/login.py | 8 +++----- src/app/views/logout.py | 3 --- src/app/views/new_project.py | 8 +++----- src/app/views/open_projects.py | 7 ++----- src/app/views/project.py | 10 ++++------ src/app/views/register.py | 8 +++----- src/app/views/utils.py | 13 +++++++++++++ 10 files changed, 38 insertions(+), 54 deletions(-) diff --git a/src/app/views/app.py b/src/app/views/app.py index 1de48ac..8baa2b2 100755 --- a/src/app/views/app.py +++ b/src/app/views/app.py @@ -1,6 +1,5 @@ import os import web -from views.utils import get_nav_bar, csrf_field from views.login import Login from views.logout import Logout from views.register import Register @@ -13,7 +12,7 @@ from views.apply import Apply # Connect to smtp server, enables web.sendmail() try: smtp_server = os.getenv("smtp_server") + ":25" - web.config.smtp_server = smtp_server + web.config.smtp_server = smtp_server except: smtp_server = "molde.idi.ntnu.no:25" web.config.smtp_server = smtp_server @@ -39,9 +38,6 @@ urls = ( # Initialize application using the web py framework app = web.application(urls, globals()) -# Get html templates -render = web.template.render('templates/') - # Set session timeout web.config.session_parameters['timeout'] = 15000000 @@ -52,16 +48,9 @@ if web.config.get('_session') is None: else: session = web.config._session -# Add session to global variables -render._add_global(session, 'session') - -# Add CSRF field to global variables -web.template.Template.globals['csrf_field'] = csrf_field - # Make the session available cross modules through webctx def session_hook(): web.ctx.session = session - web.template.Template.globals['session'] = session app.add_processor(web.loadhook(session_hook)) diff --git a/src/app/views/apply.py b/src/app/views/apply.py index 30f716c..bef050f 100755 --- a/src/app/views/apply.py +++ b/src/app/views/apply.py @@ -1,11 +1,9 @@ import web import models.project from models.user import get_user_name_by_id -from views.utils import get_nav_bar, get_element_count, csrf_protected +from views.utils import get_nav_bar, get_element_count, csrf_protected, get_render from views.forms import get_apply_form, get_apply_permissions_form -# Get html templates -render = web.template.render('templates/') class Apply: @@ -30,8 +28,7 @@ class Apply: apply_permissions_form = get_apply_permissions_form() applicants = [[session.userid, session.username]] permissions = [["TRUE", "TRUE", "TRUE"]] - render = web.template.render('templates/', globals={"get_apply_permissions_form":get_apply_permissions_form, 'session':session}) - + render = get_render(globals={"get_apply_permissions_form": get_apply_permissions_form}) return render.apply(nav, apply_form, get_apply_permissions_form, project, applicants, permissions) @csrf_protected @@ -49,7 +46,8 @@ class Apply: apply_permission_form = get_apply_permissions_form() # Prepare globals - render = web.template.render('templates/', globals={"get_apply_permissions_form":get_apply_permissions_form, 'session':session}) + render = get_render(globals={"get_apply_permissions_form": get_apply_permissions_form}) + if data.projectid: project = models.project.get_project_by_id(data.projectid) diff --git a/src/app/views/index.py b/src/app/views/index.py index d91b32e..c3b0eca 100755 --- a/src/app/views/index.py +++ b/src/app/views/index.py @@ -1,17 +1,15 @@ import web import models.project -from views.utils import get_nav_bar +from views.utils import get_nav_bar, get_render -# Get html templates -render = web.template.render('templates/') class Index: - + def GET(self): - """ + """ Get main page using the projects URL input variable to determine which projects to show. - + :return: index page """ session = web.ctx.session @@ -31,4 +29,4 @@ class Index: project_bulk_one = models.project.get_projects_by_status_and_owner(str(session.userid), "finished") project_bulk_two = models.project.get_projects_by_participant_and_status(str(session.userid), "finished") - return render.index(nav, project_bulk_one, project_bulk_two, data.projects, categories) + return get_render().index(nav, project_bulk_one, project_bulk_two, data.projects, categories) diff --git a/src/app/views/login.py b/src/app/views/login.py index bf431c7..605ea08 100755 --- a/src/app/views/login.py +++ b/src/app/views/login.py @@ -1,6 +1,6 @@ import web from views.forms import login_form -from views.utils import get_nav_bar, csrf_protected +from views.utils import get_nav_bar, csrf_protected, get_render import models.session import models.user import logging @@ -11,9 +11,6 @@ import time logger = logging.getLogger(__name__) -# Get html templates -render = web.template.render('templates/') - # The remember cookie should be valid for a week remember_timeout = 3600*24*7 @@ -39,7 +36,7 @@ class Login(): # Log the user in if the rememberme cookie is set and valid self.check_rememberme() - return render.login(nav, login_form, "") + return get_render().login(nav, login_form, "") @csrf_protected def POST(self): @@ -50,6 +47,7 @@ class Login(): session = web.ctx.session nav = get_nav_bar(session) data = web.input(username="", password="", remember=False) + render = get_render() # Validate login credential with database query user = models.user.get_user(data.username) diff --git a/src/app/views/logout.py b/src/app/views/logout.py index 56fafc3..f313b4f 100755 --- a/src/app/views/logout.py +++ b/src/app/views/logout.py @@ -2,9 +2,6 @@ import web from views.utils import get_nav_bar import models.session -# Get html templates -render = web.template.render('templates/') - class Logout: diff --git a/src/app/views/new_project.py b/src/app/views/new_project.py index e1b32f9..891909b 100755 --- a/src/app/views/new_project.py +++ b/src/app/views/new_project.py @@ -1,13 +1,10 @@ import web from web import form from views.forms import get_task_form_elements, get_project_form_elements, get_user_form_elements, project_buttons -from views.utils import get_nav_bar, get_element_count, csrf_protected +from views.utils import get_nav_bar, get_element_count, csrf_protected, get_render import models.project import models.user -# Get html templates -render = web.template.render('templates/') - class New_project: def GET(self): @@ -24,7 +21,7 @@ class New_project: task_form_elements = get_task_form_elements() user_form_elements = get_user_form_elements() project_form = form.Form(*(project_form_elements + task_form_elements + user_form_elements)) - return render.new_project(nav, project_form, project_buttons, "") + return get_render().new_project(nav, project_form, project_buttons, "") @csrf_protected def POST(self): @@ -35,6 +32,7 @@ class New_project: """ session = web.ctx.session nav = get_nav_bar(session) + render = get_render() # Try the different URL input parameters to determine how to generate the form data = web.input(add_user=None, remove_user=None, diff --git a/src/app/views/open_projects.py b/src/app/views/open_projects.py index 332e3b9..fff29dd 100755 --- a/src/app/views/open_projects.py +++ b/src/app/views/open_projects.py @@ -1,10 +1,7 @@ import web -from views.utils import get_nav_bar +from views.utils import get_nav_bar, get_render from models.project import get_categories, get_projects_by_status_and_category -# Get html templates -render = web.template.render('templates/') - class Open_projects: def GET(self): @@ -20,4 +17,4 @@ class Open_projects: open_projects = get_projects_by_status_and_category(data.categoryid, "open") nav = get_nav_bar(session) categories = get_categories() - return render.open_projects(nav, categories, open_projects) + return get_render().open_projects(nav, categories, open_projects) diff --git a/src/app/views/project.py b/src/app/views/project.py index 83a42ce..d1bf7ff 100755 --- a/src/app/views/project.py +++ b/src/app/views/project.py @@ -1,13 +1,10 @@ import web import models.project -from views.utils import get_nav_bar, csrf_protected +from views.utils import get_nav_bar, csrf_protected, get_render from views.forms import project_form import os from time import sleep -# Get html templates -render = web.template.render('templates/') - class Project: @@ -38,8 +35,9 @@ class Project: else: project = [[]] tasks = [[]] - render = web.template.render('templates/', globals={'get_task_files':models.project.get_task_files, 'session':session}) - return render.project(nav, project_form, project, tasks,permissions, categories) + + render = get_render(globals={'get_task_files': models.project.get_task_files}) + return render.project(nav, project_form, project, tasks, permissions, categories) @csrf_protected def POST(self): diff --git a/src/app/views/register.py b/src/app/views/register.py index c4108a0..a7bef39 100755 --- a/src/app/views/register.py +++ b/src/app/views/register.py @@ -1,6 +1,6 @@ import web from views.forms import register_form -from views.utils import get_nav_bar, csrf_protected, password_weakness +from views.utils import get_nav_bar, csrf_protected, password_weakness, get_render import models.register import models.user import logging @@ -9,9 +9,6 @@ import re logger = logging.getLogger(__name__) -# Get html templates -render = web.template.render('templates/') - class Register: @@ -23,7 +20,7 @@ class Register: """ session = web.ctx.session nav = get_nav_bar(session) - return render.register(nav, register_form, "") + return get_render().register(nav, register_form, "") @csrf_protected def POST(self): @@ -35,6 +32,7 @@ class Register: session = web.ctx.session nav = get_nav_bar(session) data = web.input() + render = get_render() register = register_form() if not register.validates(): diff --git a/src/app/views/utils.py b/src/app/views/utils.py index 9b0650b..4955ac7 100755 --- a/src/app/views/utils.py +++ b/src/app/views/utils.py @@ -2,6 +2,19 @@ import web from uuid import uuid4 +def get_render(path='templates/', globals={}, **kwargs): + default_globals = { + 'session': web.config._session, + 'csrf_field': csrf_field, + } + + return web.template.render( + path, + globals={**default_globals, **globals}, + **kwargs, + ) + + def get_nav_bar(session): """ Generates the page nav bar From 442f6e14709bc4124201b5acc6ffce8dc201a1da Mon Sep 17 00:00:00 2001 From: Sindre Stephansen Date: Tue, 17 Mar 2020 14:37:26 +0100 Subject: [PATCH 2/2] Prevent account enumeration when creating a project Fixes #9 --- src/app/models/project.py | 5 ++- src/app/views/new_project.py | 60 +++++++++++++++++------------------- 2 files changed, 31 insertions(+), 34 deletions(-) diff --git a/src/app/models/project.py b/src/app/models/project.py index 9fbd8c1..12b9f0d 100755 --- a/src/app/models/project.py +++ b/src/app/models/project.py @@ -127,6 +127,8 @@ def get_user_permissions(userid, projectid): try: cursor.execute(query, (projectid, userid)) permissions = cursor.fetchall() + if len(permissions): + return permissions[0] except mysql.connector.Error as err: logger.error("Failed executing query: %s", err) cursor.fetchall() @@ -135,9 +137,6 @@ def get_user_permissions(userid, projectid): cursor.close() db.close() - if len(permissions): - return permissions[0] - return [0, 0, 0] diff --git a/src/app/views/new_project.py b/src/app/views/new_project.py index 891909b..14f0d3c 100755 --- a/src/app/views/new_project.py +++ b/src/app/views/new_project.py @@ -70,44 +70,42 @@ class New_project: if len(data.user_name_0): status = "in progress" - # Validate the input user names - for i in range(0, user_count): - if len(data["user_name_"+str(i)]) and not models.user.get_user_id_by_name(data["user_name_"+str(i)]): - return render.new_project(nav, project_form, project_buttons, "Invalid user: " + data["user_name_"+str(i)]) - # Save the project to the database projectid = models.project.set_project(data.category_name, str(session.userid), - data.project_title, data.project_description, status) + data.project_title, data.project_description, status) # Save the tasks in the database for i in range(0, task_count): - models.project.set_task(str(projectid), (data["task_title_" + str(i)]), - (data["task_description_" + str(i)]), (data["budget_" + str(i)])) + models.project.set_task(str(projectid), + (data["task_title_" + str(i)]), + (data["task_description_" + str(i)]), + (data["budget_" + str(i)])) - # Save the users in the database given that the input field is not empty + # Validate the input user names. If the user doesn't exist we silently ignore it for i in range(0, user_count): - if len(data["user_name_"+str(i)]): - userid = models.user.get_user_id_by_name(data["user_name_"+str(i)]) - read, write, modify = "FALSE", "FALSE", "FALSE" - try: - data["read_permission_"+str(i)] - read = "TRUE" - except Exception as e: - read = "FALSE" - pass - try: - data["write_permission_"+str(i)] - write = "TRUE" - except Exception as e: - write = "FALSE" - pass - try: - data["modify_permission_"+str(i)] - modify = "TRUE" - except Exception as e: - modify = "FALSE" - pass - models.project.set_projects_user(str(projectid), str(userid), read, write, modify) + username = data["user_name_"+str(i)] + if len(username): + userid = models.user.get_user_id_by_name(username) + if userid: + # Save the users in the database given that the input field is not empty + read, write, modify = 0, 0, 0 + try: + data["read_permission_"+str(i)] + read = 1 + except Exception as e: + pass + try: + data["write_permission_"+str(i)] + write = 1 + except Exception as e: + pass + try: + data["modify_permission_"+str(i)] + modify = 1 + except Exception as e: + pass + models.project.set_projects_user(projectid, userid, read, write, modify) + raise web.seeother('/?projects=my') def compose_form(self, data, operation):