Skip to content

Commit

Permalink
FS-4805 - Adding authentication to FAB
Browse files Browse the repository at this point in the history
  • Loading branch information
wjrm500 committed Dec 7, 2024
1 parent d2c6660 commit e5cc1e4
Show file tree
Hide file tree
Showing 13 changed files with 180 additions and 9 deletions.
17 changes: 17 additions & 0 deletions app/blueprints/dev/routes.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,9 @@
from flask import redirect
from flask import request
from flask import url_for
from fsd_utils.authentication.decorators import SupportedApp
from fsd_utils.authentication.decorators import check_internal_user
from fsd_utils.authentication.decorators import login_required

from app.blueprints.self_serve.data.data_access import clear_all_responses
from app.blueprints.self_serve.data.data_access import get_all_components
Expand All @@ -22,6 +25,8 @@


@dev_bp.route("/responses")
@login_required(return_app=SupportedApp.FUND_APPLICATION_BUILDER)
@check_internal_user
def view_responses():
"""
Retrieves all responses received from a 'Save per page' callback when a form is in preview mode.
Expand All @@ -32,6 +37,8 @@ def view_responses():


@dev_bp.route("/responses/clear")
@login_required(return_app=SupportedApp.FUND_APPLICATION_BUILDER)
@check_internal_user
def clear_responses():
"""
Clears all responses received from a 'Save per page' callback when a form is in preview mode.
Expand All @@ -41,6 +48,8 @@ def clear_responses():


@dev_bp.route("/save", methods=["PUT"])
@login_required(return_app=SupportedApp.FUND_APPLICATION_BUILDER)
@check_internal_user
def save_per_page():
"""
Mock version of the 'save per page' route in application store - used to save and enable viewing of save
Expand All @@ -66,24 +75,32 @@ def save_per_page():


@dev_bp.route("/forms")
@login_required(return_app=SupportedApp.FUND_APPLICATION_BUILDER)
@check_internal_user
def view_forms():
forms = get_saved_forms()
return forms


@dev_bp.route("/pages")
@login_required(return_app=SupportedApp.FUND_APPLICATION_BUILDER)
@check_internal_user
def view_pages():
forms = get_all_pages()
return forms


@dev_bp.route("/sections")
@login_required(return_app=SupportedApp.FUND_APPLICATION_BUILDER)
@check_internal_user
def view_sections():
forms = get_all_sections()
return forms


@dev_bp.route("/questions")
@login_required(return_app=SupportedApp.FUND_APPLICATION_BUILDER)
@check_internal_user
def view_questions():
forms = get_all_components()
return forms
53 changes: 52 additions & 1 deletion app/blueprints/fund_builder/routes.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,16 @@
from flask import Response
from flask import after_this_request
from flask import flash
from flask import g
from flask import redirect
from flask import render_template
from flask import request
from flask import send_file
from flask import url_for
from fsd_utils.authentication.decorators import SupportedApp
from fsd_utils.authentication.decorators import check_internal_user
from fsd_utils.authentication.decorators import login_requested
from fsd_utils.authentication.decorators import login_required

from app.all_questions.metadata_utils import generate_print_data_for_sections
from app.blueprints.fund_builder.forms.fund import FundForm
Expand Down Expand Up @@ -46,7 +51,9 @@
from app.db.queries.round import get_round_by_id
from app.db.queries.round import update_round
from app.export_config.generate_all_questions import print_html
from app.export_config.generate_assessment_config import generate_assessment_config_for_round
from app.export_config.generate_assessment_config import (
generate_assessment_config_for_round,
)
from app.export_config.generate_form import build_form_json
from app.export_config.generate_fund_round_config import generate_config_for_round
from app.export_config.generate_fund_round_form_jsons import (
Expand All @@ -66,12 +73,34 @@
)


@build_fund_bp.route("/healthcheck")
def healthcheck():
return "OK", 200


@build_fund_bp.route("/")
@login_requested
def index():
if not g.is_authenticated:
return redirect(url_for("build_fund_bp.login"))
return redirect(url_for("build_fund_bp.dashboard"))


@build_fund_bp.route("/login", methods=["GET"])
def login():
return render_template("login.html")


@build_fund_bp.route("/dashboard", methods=["GET"])
@login_required(return_app=SupportedApp.FUND_APPLICATION_BUILDER)
@check_internal_user
def dashboard():
return render_template("index.html")


@build_fund_bp.route("/fund/round/<round_id>/section", methods=["GET", "POST"])
@login_required(return_app=SupportedApp.FUND_APPLICATION_BUILDER)
@check_internal_user
def section(round_id):
round_obj = get_round_by_id(round_id)
fund_obj = get_fund_by_id(round_obj.fund_id)
Expand Down Expand Up @@ -134,6 +163,8 @@ def section(round_id):


@build_fund_bp.route("/fund/round/<round_id>/section/<section_id>/forms", methods=["POST", "GET"])
@login_required(return_app=SupportedApp.FUND_APPLICATION_BUILDER)
@check_internal_user
def configure_forms_in_section(round_id, section_id):
if request.method == "GET":
if request.args.get("action") == "remove":
Expand Down Expand Up @@ -162,6 +193,8 @@ def all_funds_as_govuk_select_items(all_funds: list) -> list:


@build_fund_bp.route("/fund/view", methods=["GET", "POST"])
@login_required(return_app=SupportedApp.FUND_APPLICATION_BUILDER)
@check_internal_user
def view_fund():
"""
Renders a template providing a drop down list of funds. If a fund is selected, renders its config info
Expand All @@ -185,6 +218,8 @@ def view_fund():


@build_fund_bp.route("/fund/round/<round_id>/application_config")
@login_required(return_app=SupportedApp.FUND_APPLICATION_BUILDER)
@check_internal_user
def build_application(round_id):
"""
Renders a template displaying application configuration info for the chosen round
Expand All @@ -200,6 +235,8 @@ def build_application(round_id):


@build_fund_bp.route("/fund/<fund_id>/round/<round_id>/clone")
@login_required(return_app=SupportedApp.FUND_APPLICATION_BUILDER)
@check_internal_user
def clone_round(round_id, fund_id):
cloned = clone_single_round(
round_id=round_id, new_fund_id=fund_id, new_short_name=f"R-C{randint(0, 999)}" # nosec B311
Expand All @@ -211,6 +248,8 @@ def clone_round(round_id, fund_id):

@build_fund_bp.route("/fund", methods=["GET", "POST"])
@build_fund_bp.route("/fund/<fund_id>", methods=["GET", "POST"])
@login_required(return_app=SupportedApp.FUND_APPLICATION_BUILDER)
@check_internal_user
def fund(fund_id=None):
"""
Renders a template to allow a user to add or update a fund, when saved validates the form data and saves to DB
Expand Down Expand Up @@ -276,6 +315,8 @@ def fund(fund_id=None):

@build_fund_bp.route("/round", methods=["GET", "POST"])
@build_fund_bp.route("/round/<round_id>", methods=["GET", "POST"])
@login_required(return_app=SupportedApp.FUND_APPLICATION_BUILDER)
@check_internal_user
def round(round_id=None):
"""
Renders a template to select a fund and add or update a round to that fund. If saved, validates the round form data
Expand Down Expand Up @@ -531,6 +572,8 @@ def create_new_round(form):


@build_fund_bp.route("/preview/<form_id>", methods=["GET"])
@login_required(return_app=SupportedApp.FUND_APPLICATION_BUILDER)
@check_internal_user
def preview_form(form_id):
"""
Generates the form json for a chosen form, does not persist this, but publishes it to the form runner using the
Expand All @@ -552,6 +595,8 @@ def preview_form(form_id):


@build_fund_bp.route("/download/<form_id>", methods=["GET"])
@login_required(return_app=SupportedApp.FUND_APPLICATION_BUILDER)
@check_internal_user
def download_form_json(form_id):
"""
Generates form json for the selected form and returns it as a file download
Expand All @@ -567,6 +612,8 @@ def download_form_json(form_id):


@build_fund_bp.route("/fund/round/<round_id>/all_questions", methods=["GET"])
@login_required(return_app=SupportedApp.FUND_APPLICATION_BUILDER)
@check_internal_user
def view_all_questions(round_id):
"""
Generates the form data for all sections in the selected round, then uses that to generate the 'All Questions'
Expand Down Expand Up @@ -595,6 +642,8 @@ def view_all_questions(round_id):


@build_fund_bp.route("/fund/round/<round_id>/all_questions/<form_id>", methods=["GET"])
@login_required(return_app=SupportedApp.FUND_APPLICATION_BUILDER)
@check_internal_user
def view_form_questions(round_id, form_id):
"""
Generates the form data for this form, then uses that to generate the 'All Questions'
Expand Down Expand Up @@ -630,6 +679,8 @@ def create_export_zip(directory_to_zip, zip_file_name, random_post_fix) -> str:


@build_fund_bp.route("/create_export_files/<round_id>", methods=["GET"])
@login_required(return_app=SupportedApp.FUND_APPLICATION_BUILDER)
@check_internal_user
def create_export_files(round_id):
round_short_name = get_round_by_id(round_id).short_name
# Construct the path to the output directory relative to this file's location
Expand Down
21 changes: 21 additions & 0 deletions app/blueprints/fund_builder/templates/login.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
{% extends "base.html" %}

{%- from 'govuk_frontend_jinja/components/button/macro.html' import govukButton -%}

{# Override main width to two thirds #}
{% set mainClasses = "govuk-!-width-two-thirds" %}

{% block content %}
<div class="govuk-width-container">
<h1 class="govuk-heading-l">Sign in to use FAB</h1>
<p class="govuk-body">
Sign in using the Microsoft account for your work email address.
</p>

{{ govukButton({
"text": "Sign in using Microsoft",
"href": config['AUTHENTICATOR_HOST'] + "/sso/login?return_app=fund-application-builder",
"classes": "login-button"
}) }}
</div>
{% endblock content %}
17 changes: 17 additions & 0 deletions app/blueprints/self_serve/routes.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,9 @@
from flask import render_template
from flask import request
from flask import url_for
from fsd_utils.authentication.decorators import SupportedApp
from fsd_utils.authentication.decorators import check_internal_user
from fsd_utils.authentication.decorators import login_required

from app.all_questions.metadata_utils import generate_print_data_for_sections
from app.blueprints.self_serve.data.data_access import get_all_components
Expand Down Expand Up @@ -38,6 +41,8 @@


@self_serve_bp.route("/download_json", methods=["POST"])
@login_required(return_app=SupportedApp.FUND_APPLICATION_BUILDER)
@check_internal_user
def generate_json():
form_json = generate_form_config_from_request()["form_json"]

Expand Down Expand Up @@ -66,6 +71,8 @@ def generate_form_config_from_request():


@self_serve_bp.route("/form_questions", methods=["POST"])
@login_required(return_app=SupportedApp.FUND_APPLICATION_BUILDER)
@check_internal_user
def view_form_questions():
form_config = generate_form_config_from_request()
print_data = generate_print_data_for_sections(
Expand All @@ -82,6 +89,8 @@ def view_form_questions():


@self_serve_bp.route("/section_questions", methods=["POST"])
@login_required(return_app=SupportedApp.FUND_APPLICATION_BUILDER)
@check_internal_user
def view_section_questions():
# form_config = generate_form_config_from_request()
# print_data = generate_print_data_for_sections(
Expand All @@ -103,6 +112,8 @@ def view_section_questions():

# Create routes
@self_serve_bp.route("section", methods=["GET", "POST", "PUT", "DELETE"])
@login_required(return_app=SupportedApp.FUND_APPLICATION_BUILDER)
@check_internal_user
def section():
# TODO: Create frontend routes and connect to middleware
if request.method == "PUT":
Expand Down Expand Up @@ -131,6 +142,8 @@ def section():


@self_serve_bp.route("/form", methods=["GET", "POST", "PUT", "DELETE"])
@login_required(return_app=SupportedApp.FUND_APPLICATION_BUILDER)
@check_internal_user
def form():
# TODO: Create frontend routes and connect to middleware
if request.method == "PUT":
Expand Down Expand Up @@ -169,6 +182,8 @@ def form():


@self_serve_bp.route("/page", methods=["GET", "POST", "PUT", "DELETE"])
@login_required(return_app=SupportedApp.FUND_APPLICATION_BUILDER)
@check_internal_user
def page():
# TODO: Create frontend routes and connect to middleware
if request.method == "PUT":
Expand Down Expand Up @@ -201,6 +216,8 @@ def page():


@self_serve_bp.route("/question", methods=["GET", "PUT", "POST", "DELETE"])
@login_required(return_app=SupportedApp.FUND_APPLICATION_BUILDER)
@check_internal_user
def question():
# TODO: Create frontend routes and connect to middleware
if request.method == "PUT":
Expand Down
7 changes: 7 additions & 0 deletions app/blueprints/templates/routes.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@
from flask import render_template
from flask import request
from flask import url_for
from fsd_utils.authentication.decorators import SupportedApp
from fsd_utils.authentication.decorators import check_internal_user
from fsd_utils.authentication.decorators import login_required
from werkzeug.utils import secure_filename

from app.blueprints.fund_builder.forms.templates import TemplateFormForm
Expand Down Expand Up @@ -54,6 +57,8 @@ def _build_rows(forms: list[Form]) -> list[dict]:


@template_bp.route("/all", methods=["GET", "POST"])
@login_required(return_app=SupportedApp.FUND_APPLICATION_BUILDER)
@check_internal_user
def view_templates():
sections = get_all_template_sections()
forms = get_all_template_forms()
Expand Down Expand Up @@ -92,6 +97,8 @@ def view_templates():


@template_bp.route("/forms/<form_id>", methods=["GET", "POST"])
@login_required(return_app=SupportedApp.FUND_APPLICATION_BUILDER)
@check_internal_user
def edit_form_template(form_id):
template_form = TemplateFormForm()
params = {
Expand Down
5 changes: 5 additions & 0 deletions app/create_app.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
from flask import Flask
from flask import render_template
from flask_assets import Environment
from fsd_utils.logging import logging
from jinja2 import ChoiceLoader
Expand Down Expand Up @@ -62,6 +63,10 @@ def create_app() -> Flask:
)
flask_app.jinja_env.add_extension("jinja2.ext.do")

@flask_app.errorhandler(403)
def forbidden_error(error):
return render_template("403.html"), 403

return flask_app


Expand Down
13 changes: 13 additions & 0 deletions app/templates/403.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
{% extends "base.html" %}

{% block content %}
<div class="govuk-grid-row">
<div class="govuk-grid-column-two-thirds">
<h1 class="govuk-heading-l">You do not have permission to access this page</h1>
<p class="govuk-body">If you believe you should have access, please contact your administrator.</p>
<p class="govuk-body">
<a href="{{ url_for('build_fund_bp.index') }}" class="govuk-link">Return to homepage</a>
</p>
</div>
</div>
{% endblock %}
Loading

0 comments on commit e5cc1e4

Please sign in to comment.