def index():
    # Log user-agent for browser use analysis
    app.logger.info({"user-agent": request.headers.get("User-Agent")})

    args = request.args

    if "code" in args:
        oauth_code = args["code"]
        response = exchange_code_for_session_user(oauth_code)
        if response.status_code != 200:
            app.logger.error({"error": "OAuth failed", "response": response})
            return redirect("/403")

        return redirect("/admin" if has_admin_role() else "/")

    if "details" in session:
        upload_rights = has_upload_rights()
        is_admin_role = has_admin_role()
        return render_template_custom(
            "welcome.html",
            user=session["user"],
            email=session["email"],
            upload_rights=upload_rights,
            is_admin_role=is_admin_role,
        )
    else:
        login_url = (
            f"https://{app.config['cognito_domain']}/oauth2/authorize?"
            f"client_id={app.config['client_id']}&"
            "response_type=code&"
            f"redirect_uri={app.config['redirect_host']}&"
            "scope=profile+email+phone+openid+aws.cognito.signin.user.admin")
        return render_template_custom("login.html",
                                      hide_logout=True,
                                      login_url=login_url)
def admin_user(app):
    done = "None"

    email = ""

    if len(request.args) != 0:
        email = request.args.get("email", "")
        # Remove url encoding for special characters
        email = unquote(email)
        done = request.args.get("done", "None")

    if len(request.form) != 0:
        email = request.form.get("email", "")
        done = request.form.get("done", "None")

    if email != "":
        clear_session(app)
    elif "admin_user_email" in session:
        email = session["admin_user_email"]

    if email != "":
        user = User(email).get_details()

        if user != {}:
            session["admin_user_email"] = user["email"]
            session["admin_user_object"] = user

            user_group = return_users_group(user)

            return render_template_custom("admin/user.html",
                                          user=user,
                                          user_group=user_group,
                                          done=done)

    return redirect("/admin/user/not-found")
def admin_confirm_user(app):
    """
    Render the /admin/user/confirm flask route

    This route posts back to the same page and performs the
    user create/updates against cognito.
    """

    # Get the edited user content from the session
    # or initialise as an empty dictionary
    admin_user_object = session.get("admin_user_object", {})
    task = ""
    new_user = False

    # Redirect to admin home if post data missing
    args = request.form
    if len(args) == 0:
        clear_session(app)
        return redirect("/admin")
    elif "task" in args:
        task = args["task"]

    # Sanitise the email address if user edited
    if task in ["new", "continue-new"]:
        new_user = True
        if "email" in args:
            user = User(args["email"])
            if user.email_address_is_valid():
                admin_user_object["email"] = user.email_address
            else:
                return redirect(url_for("admin_edit_user"))

    # Handle Cognito create and update user logic
    if task in ["confirm-new", "confirm-existing"]:
        is_task_complete = perform_cognito_task(task, admin_user_object)

        target = "/admin/user/error"
        state = "created" if task == "confirm-new" else "updated"
        if is_task_complete:
            target = "/admin/user?done={}&email={}".format(
                state, quote(admin_user_object["email"]))
        # If this fails an error message should have been added to the session
        # by the user.create or user.update methods.
        clear_session(app)
        return redirect(target)

    admin_user_object = parse_edit_form_fields(args, admin_user_object, app)

    session["admin_user_email"] = admin_user_object["email"]
    session["admin_user_object"] = admin_user_object

    return render_template_custom(
        app,
        "admin/confirm-user.html",
        user=admin_user_object,
        new_user=new_user,
        user_group=admin_user_object["group"],
    )
def server_error_403():
    error_message = session.get("error_message", "Access denied")
    app.logger.error(f"Server error: {error_message}")
    del session["error_message"]
    return (
        render_template_custom(
            app, "error.html", hide_logout=True, error=error_message
        ),
        403,
    )
def admin_user_error(app):
    error_message = session.get("error_message", "Something went wrong")
    app.logger.error(f"Server error: {error_message}")
    del session["error_message"]
    return (
        render_template_custom("error.html",
                               hide_logout=True,
                               error=error_message),
        403,
    )
def admin_edit_user(app):
    args = request.values
    new_user = False
    user_custom_paths = []

    task = ""
    if "task" in args:
        task = args["task"]

    if task == "new":
        clear_session(app)

        session["admin_user_email"] = ""
        session["admin_user_object"] = {}

        # only admin-full can create a new user
        if not user_has_a_valid_role(["admin-full"]):
            return redirect("/403")

    admin_user_email = session["admin_user_email"]
    admin_user_object = session["admin_user_object"]

    # If the user doesn't exist, allow editing of email/username
    # and don't pre-set user account type options.
    if admin_user_email == "" or User(admin_user_email).get_details() == {}:
        new_user = True
        is_local_authority_user = False
        is_other_user = False
    else:
        # If you are editing an existing user:
        # - check that all the granted paths are valid for the user's type.
        # - and remove any paths where the account type doesn't match
        if "custom:paths" not in admin_user_object:
            admin_user_object["custom:paths"] = ""
        is_local_authority_user = admin_user_object["custom:is_la"] == "1"
        is_other_user = not is_local_authority_user
        admin_user_object = remove_invalid_user_paths(admin_user_object)
        user_custom_paths = admin_user_object["custom:paths"].split(";")

    return render_template_custom(
        app,
        "admin/edit-user.html",
        user=admin_user_object,
        new_user=new_user,
        user_custom_paths=user_custom_paths,
        local_authority=value_paths_by_type("local_authority"),
        is_la=is_local_authority_user,
        other=value_paths_by_type("other"),
        is_other=is_other_user,
        allowed_domains=(User(admin_user_email).allowed_domains()
                         if new_user else []),
        available_groups=user_groups(),
    )
def files():
    files = get_files(app.config["bucket_name"], session)

    # TODO sorting

    return render_template_custom(
        "files.html",
        user=session["user"],
        email=session["email"],
        files=collect_files_by_date(files),
        is_la=return_attribute(session, "custom:is_la"),
    )
def upload():
    user_upload_paths = user_custom_paths(is_upload=True, session=session)
    preupload = True
    file_path_to_upload = ""
    presigned_object = ""
    upload_history = []

    file_extensions = {"csv": {"ext": "csv", "display": "CSV"}}

    if request.method == "POST":
        form_fields = request.form
        task = form_fields.get("task", None)

        if task == "preupload":
            preupload = False

            validated_form = upload_form_validate(form_fields,
                                                  user_upload_paths,
                                                  file_extensions)

            if validated_form["valid"]:
                file_path_to_upload = generate_upload_file_path(
                    validated_form["fields"])

                # generate a S3 presigned_object PutObjct based
                # on s3 key in file_path_to_upload
                presigned_object = create_presigned_post(file_path_to_upload)
                if presigned_object is None:
                    return redirect("/upload?error=True")
            else:
                return redirect("/upload?error=True")

    else:
        upload_history = get_upload_history(config.get("bucket_name"), session)
        app.logger.debug({"uploads": upload_history})

    return render_template_custom(
        "upload.html",
        user=session["user"],
        email=session["email"],
        is_la=return_attribute(session, "custom:is_la"),
        presigned_object=presigned_object,
        preupload=preupload,
        filepathtoupload=file_path_to_upload,
        file_extensions=list(file_extensions.values()) if preupload else {},
        upload_keys=user_upload_paths if preupload else [],
        upload_history=collect_files_by_date(upload_history),
    )
def admin_delete_user(app):

    args = request.values

    task = ""
    if "task" in args:
        task = args["task"]

    admin_user_object = session["admin_user_object"]

    if task == "do-delete-user":
        email = admin_user_object["email"]
        clear_session(app)
        User(email).delete()
        return redirect("/admin?done=deleted")

    return render_template_custom(
        "admin/confirm-delete.html",
        email=quote(admin_user_object["email"]),
        user_email=admin_user_object["email"],
    )
def admin_reinvite_user(app):
    args = request.values

    task = ""
    if "task" in args:
        task = args["task"]

    admin_user_object = session["admin_user_object"]

    if task == "do-reinvite-user":
        email = admin_user_object["email"]
        User(email).reinvite()
        clear_session(app)
        session["admin_user_email"] = email
        return redirect("/admin/user?done=reinvited")

    return render_template_custom(
        app,
        "admin/confirm-reinvite.html",
        email=quote(admin_user_object["email"]),
        user_email=admin_user_object["email"],
    )
def server_error_400(error):
    app.logger.error(f"Server error: {request.url}")
    return render_template_custom(app, "error.html", hide_logout=True, error=error), 400
def admin_list_users(app):
    return render_template_custom(app, "admin/list-user.html")
def admin_user_not_found(app):
    return render_template_custom(app, "error.html", error="User not found")
def admin_main(app):
    clear_session(app)
    return render_template_custom(app,
                                  "admin/index.html",
                                  can_create_users=user_has_a_valid_role(
                                      ["admin-full"]))