mirror of
https://github.com/GSA/notifications-admin.git
synced 2026-06-19 12:46:20 -04:00
code review feedback
This commit is contained in:
95
.github/workflows/codeql.yml
vendored
Normal file
95
.github/workflows/codeql.yml
vendored
Normal file
@@ -0,0 +1,95 @@
|
||||
# For most projects, this workflow file will not need changing; you simply need
|
||||
# to commit it to your repository.
|
||||
#
|
||||
# You may wish to alter this file to override the set of languages analyzed,
|
||||
# or to provide custom queries or build logic.
|
||||
#
|
||||
# ******** NOTE ********
|
||||
# We have attempted to detect the languages in your repository. Please check
|
||||
# the `language` matrix defined below to confirm you have the correct set of
|
||||
# supported CodeQL languages.
|
||||
#
|
||||
name: "CodeQL"
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ "main", "production" ]
|
||||
pull_request:
|
||||
branches: [ "main", "production" ]
|
||||
schedule:
|
||||
- cron: '18 5 * * 3'
|
||||
|
||||
jobs:
|
||||
analyze:
|
||||
name: Analyze (${{ matrix.language }})
|
||||
# Runner size impacts CodeQL analysis time. To learn more, please see:
|
||||
# - https://gh.io/recommended-hardware-resources-for-running-codeql
|
||||
# - https://gh.io/supported-runners-and-hardware-resources
|
||||
# - https://gh.io/using-larger-runners (GitHub.com only)
|
||||
# Consider using larger runners or machines with greater resources for possible analysis time improvements.
|
||||
runs-on: ${{ (matrix.language == 'swift' && 'macos-latest') || 'ubuntu-latest' }}
|
||||
timeout-minutes: ${{ (matrix.language == 'swift' && 120) || 360 }}
|
||||
permissions:
|
||||
# required for all workflows
|
||||
security-events: write
|
||||
|
||||
# required to fetch internal or private CodeQL packs
|
||||
packages: read
|
||||
|
||||
# only required for workflows in private repositories
|
||||
actions: read
|
||||
contents: read
|
||||
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
include:
|
||||
- language: javascript-typescript
|
||||
build-mode: none
|
||||
- language: python
|
||||
build-mode: none
|
||||
# CodeQL supports the following values keywords for 'language': 'c-cpp', 'csharp', 'go', 'java-kotlin', 'javascript-typescript', 'python', 'ruby', 'swift'
|
||||
# Use `c-cpp` to analyze code written in C, C++ or both
|
||||
# Use 'java-kotlin' to analyze code written in Java, Kotlin or both
|
||||
# Use 'javascript-typescript' to analyze code written in JavaScript, TypeScript or both
|
||||
# To learn more about changing the languages that are analyzed or customizing the build mode for your analysis,
|
||||
# see https://docs.github.com/en/code-security/code-scanning/creating-an-advanced-setup-for-code-scanning/customizing-your-advanced-setup-for-code-scanning.
|
||||
# If you are analyzing a compiled language, you can modify the 'build-mode' for that language to customize how
|
||||
# your codebase is analyzed, see https://docs.github.com/en/code-security/code-scanning/creating-an-advanced-setup-for-code-scanning/codeql-code-scanning-for-compiled-languages
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
|
||||
# Initializes the CodeQL tools for scanning.
|
||||
- name: Initialize CodeQL
|
||||
uses: github/codeql-action/init@v3
|
||||
with:
|
||||
languages: ${{ matrix.language }}
|
||||
build-mode: ${{ matrix.build-mode }}
|
||||
# If you wish to specify custom queries, you can do so here or in a config file.
|
||||
# By default, queries listed here will override any specified in a config file.
|
||||
# Prefix the list here with "+" to use these queries and those in the config file.
|
||||
|
||||
# For more details on CodeQL's query packs, refer to: https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-code-scanning#using-queries-in-ql-packs
|
||||
# queries: security-extended,security-and-quality
|
||||
|
||||
# If the analyze step fails for one of the languages you are analyzing with
|
||||
# "We were unable to automatically build your code", modify the matrix above
|
||||
# to set the build mode to "manual" for that language. Then modify this step
|
||||
# to build your code.
|
||||
# ℹ️ Command-line programs to run using the OS shell.
|
||||
# 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun
|
||||
- if: matrix.build-mode == 'manual'
|
||||
shell: bash
|
||||
run: |
|
||||
echo 'If you are using a "manual" build mode for one or more of the' \
|
||||
'languages you are analyzing, replace this with the commands to build' \
|
||||
'your code, for example:'
|
||||
echo ' make bootstrap'
|
||||
echo ' make release'
|
||||
exit 1
|
||||
|
||||
- name: Perform CodeQL Analysis
|
||||
uses: github/codeql-action/analyze@v3
|
||||
with:
|
||||
category: "/language:${{matrix.language}}"
|
||||
5
Makefile
5
Makefile
@@ -76,8 +76,9 @@ py-test: ## Run python unit tests
|
||||
poetry run coverage html -d .coverage_cache
|
||||
|
||||
.PHONY: dead-code
|
||||
dead-code:
|
||||
poetry run vulture ./app --min-confidence=100
|
||||
dead-code: ## 60% is our aspirational goal, but currently breaks the build
|
||||
poetry run vulture ./app ./notifications_utils --min-confidence=100
|
||||
|
||||
|
||||
.PHONY: e2e-test
|
||||
e2e-test: export NEW_RELIC_ENVIRONMENT=test
|
||||
|
||||
@@ -38,7 +38,7 @@ class Config(object):
|
||||
NR_MONITOR_ON = settings and settings.monitor_mode
|
||||
COMMIT_HASH = getenv("COMMIT_HASH", "--------")[0:7]
|
||||
|
||||
GOVERNMENT_EMAIL_DOMAIN_NAMES = ["gov"]
|
||||
GOVERNMENT_EMAIL_DOMAIN_NAMES = ["gov", "mil", "si.edu"]
|
||||
|
||||
# Logging
|
||||
NOTIFY_LOG_LEVEL = getenv("NOTIFY_LOG_LEVEL", "INFO")
|
||||
|
||||
@@ -26,6 +26,7 @@ from app.main.views import sign_in
|
||||
from app.main.views.verify import activate_user
|
||||
from app.models.user import InvitedOrgUser, InvitedUser, User
|
||||
from app.utils import hide_from_search_engines, hilite
|
||||
from app.utils.user import is_gov_user
|
||||
|
||||
|
||||
@main.route("/register", methods=["GET", "POST"])
|
||||
@@ -147,6 +148,11 @@ def check_invited_user_email_address_matches_expected(
|
||||
flash("You cannot accept an invite for another person.")
|
||||
abort(403)
|
||||
|
||||
if not is_gov_user(user_email):
|
||||
debug_msg("invited user has a non-government email address.")
|
||||
flash("You must use a government email address.")
|
||||
abort(403)
|
||||
|
||||
|
||||
@main.route("/set-up-your-profile", methods=["GET", "POST"])
|
||||
@hide_from_search_engines
|
||||
|
||||
@@ -948,7 +948,9 @@ def send_notification(service_id, template_id):
|
||||
vals = ",".join(values)
|
||||
data = f"{data}\r\n{vals}"
|
||||
|
||||
filename = f"one-off-{current_user.name}-{uuid.uuid4()}.csv"
|
||||
filename = (
|
||||
f"one-off-{uuid.uuid4()}.csv" # {current_user.name} removed from filename
|
||||
)
|
||||
my_data = {"filename": filename, "template_id": template_id, "data": data}
|
||||
upload_id = s3upload(service_id, my_data)
|
||||
form = CsvUploadForm()
|
||||
|
||||
@@ -4,7 +4,16 @@ import uuid
|
||||
|
||||
import jwt
|
||||
import requests
|
||||
from flask import Response, current_app, redirect, render_template, request, url_for
|
||||
from flask import (
|
||||
Response,
|
||||
abort,
|
||||
current_app,
|
||||
flash,
|
||||
redirect,
|
||||
render_template,
|
||||
request,
|
||||
url_for,
|
||||
)
|
||||
from flask_login import current_user
|
||||
|
||||
from app import login_manager, user_api_client
|
||||
@@ -15,6 +24,7 @@ from app.models.user import User
|
||||
from app.utils import hide_from_search_engines
|
||||
from app.utils.login import is_safe_redirect_url
|
||||
from app.utils.time import is_less_than_days_ago
|
||||
from app.utils.user import is_gov_user
|
||||
from notifications_utils.url_safe_token import generate_token
|
||||
|
||||
|
||||
@@ -88,6 +98,12 @@ def _do_login_dot_gov():
|
||||
try:
|
||||
access_token = _get_access_token(code, state)
|
||||
user_email, user_uuid = _get_user_email_and_uuid(access_token)
|
||||
if not is_gov_user(user_email):
|
||||
current_app.logger.error(
|
||||
"invited user has a non-government email address."
|
||||
)
|
||||
flash("You must use a government email address.")
|
||||
abort(403)
|
||||
redirect_url = request.args.get("next")
|
||||
user = user_api_client.get_user_by_uuid_or_email(user_uuid, user_email)
|
||||
|
||||
|
||||
@@ -32,7 +32,7 @@ def using_notify_nav():
|
||||
"link": "main.trial_mode_new",
|
||||
},
|
||||
{
|
||||
"name": "Pricing",
|
||||
"name": "Tracking usage",
|
||||
"link": "main.pricing",
|
||||
},
|
||||
{
|
||||
|
||||
@@ -40,7 +40,7 @@ def get_s3_object(
|
||||
teststr = str(s3.Bucket(bucket_name).creation_date).lower()
|
||||
if "magicmock" not in teststr:
|
||||
raise Exception(
|
||||
f"xxxxxtest not mocked, use @mock_aws creation date is {teststr}"
|
||||
f"Test is not mocked, use @mock_aws or the relevant mocker.patch to avoid accessing S3"
|
||||
)
|
||||
return obj
|
||||
|
||||
|
||||
@@ -47,7 +47,7 @@
|
||||
{% endblock %}
|
||||
{% block content %}
|
||||
{% block flash_messages %}
|
||||
{% include 'flash_messages.html' %}
|
||||
{% include 'new/components/flash_messages.html' %}
|
||||
{% endblock %}
|
||||
{% block maincolumn_content %}
|
||||
<div class="grid-row">
|
||||
|
||||
@@ -1,57 +0,0 @@
|
||||
{% extends "/new/base.html" %}
|
||||
|
||||
{% block per_page_title %}
|
||||
{% block service_page_title %}{% endblock %}{% if current_service.name %} – {{ current_service.name }}{% endif %}
|
||||
{% block org_page_title %}{% endblock %}{% if current_org.name %} – {{ current_org.name }}{% endif %}
|
||||
{% endblock %}
|
||||
|
||||
{% block main %}
|
||||
<div class="grid-container">
|
||||
{% block serviceNavigation %}
|
||||
{% if current_org.name %}
|
||||
{% else %}
|
||||
{% include "new/components/service_navigation.html" %}
|
||||
{% endif %}
|
||||
{% endblock %}
|
||||
{#
|
||||
The withnav_template can serve as a replacement for both settings_template and org_template.html.
|
||||
|
||||
The file service_navigation.html is included only in withnav_template. It's not used in settings_template. That is one out of the two differences between settings template and withnav template. As a result, when other templates extend settings_template, they include the serviceNavigation block but keep it empty. The settings_template.html is specifically used for these pages in the app: manage-users.html, service-settings.html, and user-profile.html.
|
||||
|
||||
In addition, serviceNavigation should be empty on templates that previously extended org_template. For templates that previously extended org_template.html, there's an addition of the orgNavBreadcrumb block.
|
||||
{% block orgNavBreadcrumb %}
|
||||
{% include "/new/components/org_nav_breadcrumb.html" %}
|
||||
{% endblock %}
|
||||
#}
|
||||
{% if current_org.name %}
|
||||
{% block orgNavBreadcrumb %}{% include "/new/components/org_nav_breadcrumb.html" %}{% endblock %}
|
||||
{% endif %}
|
||||
<div class="grid-row margin-top-5">
|
||||
<div class="tablet:grid-col-3">
|
||||
{% block sideNavigation %}
|
||||
{% if org_navigation_links %}
|
||||
{% include "/new/components/org_nav.html" %}
|
||||
{% else %}
|
||||
{% include "/new/components/main_nav.html" %}
|
||||
{% endif %}
|
||||
{#
|
||||
Include settings_nav.html for child templates that previously extended settings_template.
|
||||
|
||||
Include "org_nav.html" for child templates that previously extended org_template html
|
||||
#}
|
||||
{% endblock %}
|
||||
</div>
|
||||
<div class="tablet:grid-col-9 tablet:padding-left-4">
|
||||
{% block beforeContent %}
|
||||
{% block backLink %}{% endblock %}
|
||||
{% endblock %}
|
||||
<main id="main-content" role="main" class="usa-prose site-prose margin-bottom-10">
|
||||
{% block content %}
|
||||
{% include 'flash_messages.html' %}
|
||||
{% block maincolumn_content %}{% endblock %}
|
||||
{% endblock %}
|
||||
</main>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
@@ -1,3 +1,4 @@
|
||||
{# This template is an old version #}
|
||||
{% if help %}
|
||||
{% include 'partials/tour.html' %}
|
||||
{% else %}
|
||||
@@ -1,3 +1,4 @@
|
||||
{# This template is an old version #}
|
||||
<nav class="navigation">
|
||||
<ul>
|
||||
<li><a class="usa-link{{ org_navigation.is_selected('dashboard') }}" href="{{ url_for('.organization_dashboard', org_id=current_org.id) }}">Usage</a></li>
|
||||
@@ -1,3 +1,4 @@
|
||||
{# This template is an old version #}
|
||||
{% macro navigation_service_name(service) %}
|
||||
<div class="font-body-2xl text-bold">
|
||||
{{ service.name }}
|
||||
@@ -1,3 +1,4 @@
|
||||
{# This template is an old version #}
|
||||
{% if help %}
|
||||
{% include 'partials/tour.html' %}
|
||||
{% else %}
|
||||
@@ -1,4 +1,5 @@
|
||||
{% extends "base.html" %}
|
||||
{# This template is an old version #}
|
||||
{% extends "admin_template.html" %}
|
||||
|
||||
{% block per_page_title %}
|
||||
{% block service_page_title %}{% endblock %} – {{ current_service.name }}
|
||||
36
app/templates/old/withnav_template.html
Normal file
36
app/templates/old/withnav_template.html
Normal file
@@ -0,0 +1,36 @@
|
||||
{# This template is an old version #}
|
||||
{% extends "admin_template.html" %}
|
||||
|
||||
{% block per_page_title %}
|
||||
{% block service_page_title %}{% endblock %} – {{ current_service.name }}
|
||||
{% endblock %}
|
||||
|
||||
{% block main %}
|
||||
<div class="grid-container">
|
||||
{% include "service_navigation.html" %}
|
||||
<div class="grid-row margin-top-5">
|
||||
{% if help %}
|
||||
<div class="tablet:grid-col-3">
|
||||
{% else %}
|
||||
<div class="tablet:grid-col-3">
|
||||
{% endif %}
|
||||
{% include "main_nav.html" %}
|
||||
</div>
|
||||
{% if help %}
|
||||
<div class="grid-col-8">
|
||||
{% else %}
|
||||
<div class="tablet:grid-col-9 tablet:padding-left-4">
|
||||
{% endif %}
|
||||
{% block beforeContent %}
|
||||
{% block backLink %}{% endblock %}
|
||||
{% endblock %}
|
||||
<main id="main-content" role="main" class="usa-prose site-prose margin-bottom-10">
|
||||
{% block content %}
|
||||
{% include 'flash_messages.html' %}
|
||||
{% block maincolumn_content %}{% endblock %}
|
||||
{% endblock %}
|
||||
</main>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
@@ -59,7 +59,7 @@
|
||||
{% set notification = job.notifications[0] %}
|
||||
<tr class="table-row" id="{{ job.job_id }}">
|
||||
<td class="table-field file-name">
|
||||
{{ notification.job.original_file_name if notification.job.original_file_name else 'Manually entered number'}}
|
||||
{{ notification.job.original_file_name[:12] if notification.job.original_file_name else 'Manually entered number'}}
|
||||
<br>
|
||||
<a class="usa-link file-list-filename" href="{{ job.view_job_link }}">View Batch</a>
|
||||
</td>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
{% extends "settings_template.html" %}
|
||||
{% extends "withnav_template.html" %}
|
||||
{% from "components/tick-cross.html" import tick_cross %}
|
||||
{% from "components/live-search.html" import live_search %}
|
||||
{% from "components/components/button/macro.njk" import usaButton %}
|
||||
@@ -7,6 +7,12 @@
|
||||
Team members
|
||||
{% endblock %}
|
||||
|
||||
{% block serviceNavigation %}{% endblock %}
|
||||
|
||||
{% block sideNavigation %}
|
||||
{% include "/new/components/settings_nav.html" %}
|
||||
{% endblock %}
|
||||
|
||||
{% block maincolumn_content %}
|
||||
|
||||
<div class="button-flex-header">
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
{% extends "org_template.html" %}
|
||||
{% extends "withnav_template.html" %}
|
||||
{% from "components/page-header.html" import page_header %}
|
||||
|
||||
{% block org_page_title %}
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
{% from "components/big-number.html" import big_number %}
|
||||
{% from "components/live-search.html" import live_search %}
|
||||
{% from "components/pill.html" import pill %}
|
||||
{% extends "org_template.html" %}
|
||||
{% extends "withnav_template.html" %}
|
||||
|
||||
{% block org_page_title %}
|
||||
Usage
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
{% extends "org_template.html" %}
|
||||
{% extends "withnav_template.html" %}
|
||||
{% from "components/page-footer.html" import page_footer %}
|
||||
{% from "components/page-header.html" import page_header %}
|
||||
{% from "components/list-entry.html" import list_entry %}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
{% extends "org_template.html" %}
|
||||
{% extends "withnav_template.html" %}
|
||||
{% from "components/page-header.html" import page_header %}
|
||||
{% from "components/page-footer.html" import page_footer %}
|
||||
{% from "components/form.html" import form_wrapper %}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
{% extends "org_template.html" %}
|
||||
{% extends "withnav_template.html" %}
|
||||
{% from "components/page-header.html" import page_header %}
|
||||
{% from "components/page-footer.html" import page_footer %}
|
||||
{% from "components/form.html" import form_wrapper %}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
{% extends "org_template.html" %}
|
||||
{% extends "withnav_template.html" %}
|
||||
{% from "components/page-header.html" import page_header %}
|
||||
{% from "components/page-footer.html" import page_footer %}
|
||||
{% from "components/form.html" import form_wrapper %}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
{% extends "org_template.html" %}
|
||||
{% extends "withnav_template.html" %}
|
||||
{% from "components/page-footer.html" import page_footer %}
|
||||
{% from "components/page-header.html" import page_header %}
|
||||
{% from "components/form.html" import form_wrapper %}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
{% extends "org_template.html" %}
|
||||
{% extends "withnav_template.html" %}
|
||||
{% from "components/table.html" import mapping_table, optional_text_field, row, text_field, edit_field with context %}
|
||||
|
||||
{% block org_page_title %}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
{% extends "org_template.html" %}
|
||||
{% extends "withnav_template.html" %}
|
||||
{% from "components/live-search.html" import live_search %}
|
||||
|
||||
{% block org_page_title %}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
{% extends "org_template.html" %}
|
||||
{% extends "withnav_template.html" %}
|
||||
{% from "components/table.html" import list_table, row, field, hidden_field_heading %}
|
||||
{% from "components/page-footer.html" import page_footer %}
|
||||
{% from "components/live-search.html" import live_search %}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
{% extends "org_template.html" %}
|
||||
{% extends "withnav_template.html" %}
|
||||
|
||||
{% from "components/page-footer.html" import page_footer %}
|
||||
{% from "components/page-header.html" import page_header %}
|
||||
|
||||
@@ -45,7 +45,7 @@
|
||||
{% block backLink %}{% endblock %}
|
||||
<main id="main-content" role="main">
|
||||
{% block content %}
|
||||
{% include 'flash_messages.html' %}
|
||||
{% include 'new/components/flash_messages.html' %}
|
||||
{% block platform_admin_content %}{% endblock %}
|
||||
{% endblock %}
|
||||
</main>
|
||||
|
||||
@@ -15,7 +15,7 @@ Message parts
|
||||
|
||||
{{ content_metadata(
|
||||
data={
|
||||
"Last updated": "February 5, 2024"
|
||||
"Last updated": "April 10, 2024"
|
||||
}
|
||||
) }}
|
||||
|
||||
@@ -26,13 +26,15 @@ more parts towards the allowance if you:</p>
|
||||
<ul class="list list-bullet">
|
||||
<li>send text messages longer than 160 characters</a></li>
|
||||
<li>use certain <a class="usa-link" href="#symbols">signs and symbols</a></li>
|
||||
<li>use <a class="usa-link" href="#accents">accents and accented letters</a></li>
|
||||
<li>use <a class="usa-link" href="#accents">accents and accented letters, including non-romanized scripts</a></li>
|
||||
</ul>
|
||||
|
||||
<h3 class="font-body-lg" id="long-text-messages">Long text messages</h3>
|
||||
<p>If a text message is longer than 160 characters (including spaces and service name), it counts as more than one message
|
||||
part.</p>
|
||||
|
||||
<h4>Calculation of message parts without special characters</h4>
|
||||
|
||||
<div class="bottom-gutter-3-2">
|
||||
{% call mapping_table(
|
||||
caption='Text message pricing',
|
||||
@@ -72,12 +74,17 @@ and the number of parts you’ll have left.</p>
|
||||
|
||||
<p>Using them can increase the cost of sending text messages.</p>
|
||||
|
||||
<h3 class="font-body-lg" id="accents">Accents and accented characters</h3>
|
||||
<p>Some languages use accented characters.</p>
|
||||
<h3 class="font-body-lg" id="accents">Accented characters and non-romanized scripts</h3>
|
||||
<p>Notify can handle a wide range of different languages and scripts. However, occasionally some phone carriers may
|
||||
struggle to display special characters or non-romanized scripts. (Languages such as Arabic, Chinese, Japanese, Korean,
|
||||
and Russian use non-romanized scripts with different characters.) Best practices encourage communication in the
|
||||
recipient’s preferred language, but we are aware that, rarely, a phone carrier will not be able to handle the message.</p>
|
||||
<p>The following accented characters do not affect the cost of sending text messages: Ä, É, Ö, Ü, à, ä, é, è, ì, ò, ö,
|
||||
ù, ü.</p>
|
||||
<p>Using other accented characters can increase the cost of sending text messages.
|
||||
<p>
|
||||
<p>Using other accented characters or scripts will increase the cost of sending text messages. Even one accented character
|
||||
(with the exception of those noted above), or use of a non-romanized or logographic script will cause the entire message
|
||||
to be calculated as detailed below.<p>
|
||||
<h4>Calculation of message parts with special characters or non-romanized scripts</h4>
|
||||
{% set accentedChars %}
|
||||
<div class="bottom-gutter-3-2">
|
||||
{% call mapping_table(
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
{% extends "settings_template.html" %}
|
||||
{% extends "withnav_template.html" %}
|
||||
{% from "components/banner.html" import banner_wrapper %}
|
||||
{% from "components/table.html" import mapping_table, row, settings_row, text_field, optional_text_field, edit_field, field, boolean_field with context %}
|
||||
|
||||
@@ -6,6 +6,12 @@
|
||||
Settings
|
||||
{% endblock %}
|
||||
|
||||
{% block serviceNavigation %}{% endblock %}
|
||||
|
||||
{% block sideNavigation %}
|
||||
{% include "/new/components/settings_nav.html" %}
|
||||
{% endblock %}
|
||||
|
||||
{% block maincolumn_content %}
|
||||
|
||||
<h1 class="font-body-lg">Settings</h1>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
{% extends "settings_template.html" %}
|
||||
{% extends "withnav_template.html" %}
|
||||
{% from "components/table.html" import list_table, row, field %}
|
||||
{% from "components/table.html" import mapping_table, row, text_field, optional_text_field, edit_field, field, boolean_field with context %}
|
||||
|
||||
@@ -6,6 +6,12 @@
|
||||
User profile
|
||||
{% endblock %}
|
||||
|
||||
{% block serviceNavigation %}{% endblock %}
|
||||
|
||||
{% block sideNavigation %}
|
||||
{% include "/new/components/settings_nav.html" %}
|
||||
{% endblock %}
|
||||
|
||||
{% block maincolumn_content %}
|
||||
|
||||
<h1 class="font-body-2xl margin-bottom-3">User profile</h1>
|
||||
|
||||
@@ -1,31 +1,56 @@
|
||||
{% extends "base.html" %}
|
||||
|
||||
{% block per_page_title %}
|
||||
{% block service_page_title %}{% endblock %} – {{ current_service.name }}
|
||||
{% if current_org.name %}
|
||||
{% block org_page_title %}{% endblock %} – {{ current_org.name }}
|
||||
{% else %}
|
||||
{% block service_page_title %}{% endblock %} – {{ current_service.name }}
|
||||
{% endif %}
|
||||
{% endblock %}
|
||||
|
||||
{% block main %}
|
||||
<div class="grid-container">
|
||||
{% include "service_navigation.html" %}
|
||||
{% block serviceNavigation %}
|
||||
{% if current_org.name %}
|
||||
{% else %}
|
||||
{% include "new/components/service_nav.html" %}
|
||||
{% endif %}
|
||||
{% endblock %}
|
||||
{#
|
||||
The withnav_template can serve as a replacement for both settings_template and org_template.html.
|
||||
|
||||
The file service_navigation.html is included only in withnav_template. It's not used in settings_template. That is one out of the two differences between settings template and withnav template. As a result, when other templates extend settings_template, they include the serviceNavigation block but keep it empty. The settings_template.html is specifically used for these pages in the app: manage-users.html, service-settings.html, and user-profile.html.
|
||||
|
||||
In addition, serviceNavigation should be empty on templates that previously extended org_template. For templates that previously extended org_template.html, there's an addition of the orgNavBreadcrumb block.
|
||||
{% block orgNavBreadcrumb %}
|
||||
{% include "/new/components/org_nav_breadcrumb.html" %}
|
||||
{% endblock %}
|
||||
#}
|
||||
{% if current_org.name %}
|
||||
{% block orgNavBreadcrumb %}{% include "/new/components/org_nav_breadcrumb.html" %}{% endblock %}
|
||||
{% endif %}
|
||||
<div class="grid-row margin-top-5">
|
||||
{% if help %}
|
||||
<div class="tablet:grid-col-3">
|
||||
{% else %}
|
||||
<div class="tablet:grid-col-3">
|
||||
{% endif %}
|
||||
{% include "main_nav.html" %}
|
||||
</div>
|
||||
{% if help %}
|
||||
<div class="grid-col-8">
|
||||
{% else %}
|
||||
<div class="tablet:grid-col-9 tablet:padding-left-4">
|
||||
{% endif %}
|
||||
<div class="tablet:grid-col-3">
|
||||
{% block sideNavigation %}
|
||||
{% if current_org.name %}
|
||||
{% include "/new/components/org_nav.html" %}
|
||||
{% else %}
|
||||
{% include "/new/components/main_nav.html" %}
|
||||
{% endif %}
|
||||
{#
|
||||
Include settings_nav.html for child templates that previously extended settings_template.
|
||||
|
||||
Include "org_nav.html" for child templates that previously extended org_template html
|
||||
#}
|
||||
{% endblock %}
|
||||
</div>
|
||||
<div class="tablet:grid-col-9 tablet:padding-left-4">
|
||||
{% block beforeContent %}
|
||||
{% block backLink %}{% endblock %}
|
||||
{% endblock %}
|
||||
<main id="main-content" role="main" class="usa-prose site-prose margin-bottom-10">
|
||||
{% block content %}
|
||||
{% include 'flash_messages.html' %}
|
||||
{% include 'new/components/flash_messages.html' %}
|
||||
{% block maincolumn_content %}{% endblock %}
|
||||
{% endblock %}
|
||||
</main>
|
||||
|
||||
@@ -4,7 +4,6 @@ from flask import abort, current_app
|
||||
from flask_login import current_user, login_required
|
||||
|
||||
from app import config
|
||||
from app.notify_client.organizations_api_client import organizations_client
|
||||
|
||||
user_is_logged_in = login_required
|
||||
|
||||
@@ -51,7 +50,7 @@ def user_is_platform_admin(f):
|
||||
def is_gov_user(email_address):
|
||||
return _email_address_ends_with(
|
||||
email_address, config.Config.GOVERNMENT_EMAIL_DOMAIN_NAMES
|
||||
) or _email_address_ends_with(email_address, organizations_client.get_domains())
|
||||
) # or _email_address_ends_with(email_address, organizations_client.get_domains())
|
||||
|
||||
|
||||
def _email_address_ends_with(email_address, known_domains):
|
||||
|
||||
@@ -47,7 +47,7 @@ def s3upload(
|
||||
teststr = str(_s3.Bucket(bucket_name).creation_date).lower()
|
||||
if "magicmock" not in teststr:
|
||||
raise Exception(
|
||||
f"xxxxxtest not mocked, use @mock_aws creation date is {teststr}"
|
||||
f"Test is not mocked, use @mock_aws or the relevant mocker.patch to avoid accessing S3"
|
||||
)
|
||||
|
||||
key = _s3.Object(bucket_name, file_location)
|
||||
@@ -102,7 +102,7 @@ def s3download(
|
||||
teststr = str(s3.Bucket(bucket_name).creation_date).lower()
|
||||
if "magicmock" not in teststr:
|
||||
raise Exception(
|
||||
f"xxxxxtest not mocked, use @mock_aws creation date is {teststr}"
|
||||
f"Test is not mocked, use @mock_aws or the relevant mocker.patch to avoid accessing S3"
|
||||
)
|
||||
return key.get()["Body"]
|
||||
except botocore.exceptions.ClientError as error:
|
||||
|
||||
@@ -148,7 +148,7 @@ def test_should_return_200_when_email_is_not_gov_uk(
|
||||
"email_address",
|
||||
[
|
||||
"notfound@example.gsa.gov",
|
||||
"example@lsquo.net",
|
||||
"example@lsquo.si.edu",
|
||||
],
|
||||
)
|
||||
def test_should_add_user_details_to_session(
|
||||
@@ -401,6 +401,26 @@ def test_check_invited_user_email_address_doesnt_match_expected(mocker):
|
||||
mock_abort.assert_called_once_with(403)
|
||||
|
||||
|
||||
def test_check_user_email_address_fails_if_not_government_address(mocker):
|
||||
mock_flash = mocker.patch("app.main.views.register.flash")
|
||||
mock_abort = mocker.patch("app.main.views.register.abort")
|
||||
|
||||
check_invited_user_email_address_matches_expected(
|
||||
"fake@fake.bogus", "Fake@Fake.BOGUS"
|
||||
)
|
||||
mock_flash.assert_called_once_with("You must use a government email address.")
|
||||
mock_abort.assert_called_once_with(403)
|
||||
|
||||
|
||||
def test_check_user_email_address_succeeds_if_government_address(mocker):
|
||||
mock_flash = mocker.patch("app.main.views.register.flash")
|
||||
mock_abort = mocker.patch("app.main.views.register.abort")
|
||||
|
||||
check_invited_user_email_address_matches_expected("fake@fake.mil", "Fake@Fake.MIL")
|
||||
mock_flash.assert_not_called()
|
||||
mock_abort.assert_not_called()
|
||||
|
||||
|
||||
def decode_invite_data(state):
|
||||
state = state.encode("utf8")
|
||||
state = base64.b64decode(state)
|
||||
|
||||
Reference in New Issue
Block a user