Compare commits

..

296 Commits

Author SHA1 Message Date
Andrew Shumway
98f3abeaac Add quotes to values in one off 2024-07-23 16:10:22 -06:00
Andrew Shumway
8a3bdd181d Pull main 2024-07-10 08:36:49 -06:00
Carlo Costino
3a2ba3a8c0 Merge pull request #1700 from GSA/update-python-deps-7-5-24
Update Python Dependencies - 7/5/2024
2024-07-05 15:44:12 -04:00
Andrew Shumway
a81dc83133 pull main 2024-07-05 09:07:08 -06:00
Carlo Costino
c80ec7e5fa Update Python Dependencies - 7/5/2024
This changeset updates several Python dependencies that Dependabot had flagged for updating.  It includes a few others that we are getting ahead of, as well.

Signed-off-by: Carlo Costino <carlo.costino@gsa.gov>
2024-07-05 08:36:28 -04:00
Carlo Costino
ffd935c4b6 Merge pull request #1629 from GSA/1256-bug-tour-steps-needs-to-be-adjusted-for-the-new-preview-page
made changes/fixes to tour flow
2024-07-03 14:37:07 -04:00
Carlo Costino
28efb2716b Merge pull request #1618 from GSA/1612-add-in-new-endpoint-for-weekly-by-user_id
1612 add in new endpoint for week stats by user
2024-07-03 14:21:49 -04:00
Carlo Costino
bd3c6de002 Merge pull request #1691 from GSA/update-deps-7-2-24
Update Python dependencies - 7/2/2024
2024-07-03 11:12:22 -04:00
Beverly Nguyen
b8a4303a97 Merge branch 'main' into 1256-bug-tour-steps-needs-to-be-adjusted-for-the-new-preview-page 2024-07-02 16:02:29 -07:00
Beverly Nguyen
bd3e8e15c8 revised e2e 2024-07-02 15:55:10 -07:00
Carlo Costino
cc6f60353f Update Python dependencies - 7/2/2024
This changeset updates several Python dependencies that Dependabot had flagged for updates.

Signed-off-by: Carlo Costino <carlo.costino@gsa.gov>
2024-07-02 17:36:12 -04:00
Carlo Costino
b96a2dc935 Merge pull request #1686 from GSA/1460-update-the-lodash-js-library
Removed package that seems to be unused that caused a security concern
2024-07-02 16:57:28 -04:00
alexjanousekGSA
feb879be53 Removed package that seems to be unused that caused a security concern 2024-07-02 10:49:09 -06:00
Carlo Costino
256626ca4f Merge pull request #1683 from GSA/debug_wash
more debug for user who can't send all messages
2024-07-02 11:37:07 -04:00
Kenneth Kehl
0c097c6592 more debug 2024-07-01 10:53:21 -07:00
Carlo Costino
068af5fccf Merge pull request #1680 from GSA/update-pr-template
Update pull request template
2024-07-01 11:45:00 -04:00
Carlo Costino
003d257589 Update pull request template
This changeset updates our pull request template to be much more streamlined and shifts most of the information to our documentation.  The PR template now links to the docs for folks who are new and unfamiliar with what we require in our pull requests so that the template itself just has the headings and quick outlines to get started more easily and quickly.

Signed-off-by: Carlo Costino <carlo.costino@gsa.gov>
2024-06-28 17:23:07 -04:00
Carlo Costino
889d7ae4eb Merge pull request #1625 from GSA/notify-admin-1495
Upgrade Node.js to the latest stable release
2024-06-28 12:26:07 -04:00
Carlo Costino
1f42dbdd63 Merge branch 'main' into notify-admin-1495 2024-06-28 11:01:13 -04:00
Carlo Costino
b9b2495af1 Updated several other Python dependencies
Signed-off-by: Carlo Costino <carlo.costino@gsa.gov>
2024-06-28 10:57:50 -04:00
Carlo Costino
137e2a5683 Updated the flake8 dependency and poetry.lock file
Signed-off-by: Carlo Costino <carlo.costino@gsa.gov>
2024-06-28 10:49:00 -04:00
Carlo Costino
cc59404a8a Merge pull request #1670 from GSA/remove-old-redis
Removed old redis from terraform.
2024-06-27 11:23:59 -04:00
Cliff Hill
f051883a92 Removed old redis from terraform.
Signed-off-by: Cliff Hill <clifford.hill@gsa.gov>
2024-06-27 10:56:57 -04:00
Andrew Shumway
cc24604aa4 Pull main 2024-06-27 08:47:01 -06:00
Carlo Costino
8350620c76 Merge pull request #1667 from GSA/jskinne3-restore-redis-v70
Restore Redis instance at v7.0
2024-06-26 22:19:17 -04:00
John Skiles Skinner
e897800cb5 Restore v7.0 Redis at the correct plan name in Prod 2024-06-26 19:11:55 -07:00
Carlo Costino
6eeeaa6066 Merge pull request #1665 from GSA/jskinne3-update-deploy-paths
Update deploy paths
2024-06-26 21:55:01 -04:00
John Skiles Skinner
ea1dc14ab3 Add /** to paths in three deploy YAML files 2024-06-26 18:47:21 -07:00
Carlo Costino
89365cd405 Merge pull request #1663 from GSA/jskinne3-update-redis-plan-name
Temp switch manifest to old Redis name, delete new Redis block
2024-06-26 21:19:58 -04:00
John Skiles Skinner
3e258d3910 Temp remove new Redis, revert to old in Manifest 2024-06-26 18:13:13 -07:00
Beverly Nguyen
4fe3e54dd0 removed blank statements 2024-06-26 17:04:19 -07:00
Beverly Nguyen
abb011fcb6 removed print statements 2024-06-26 17:00:00 -07:00
Beverly Nguyen
83d3b1684f removed commented preview button 2024-06-26 16:59:12 -07:00
Beverly Nguyen
6e5ca94643 commented out get_by_text_ preview 2024-06-26 16:52:23 -07:00
Carlo Costino
bce751750d Merge pull request #1660 from GSA/jskinne3-yaml-indentation
Adjust indentation in deploy.yml
2024-06-26 18:50:44 -04:00
John Skiles Skinner
ece3bd201d Adjust indentation in deploy.yml 2024-06-26 15:44:01 -07:00
Carlo Costino
269ff8d2a2 Merge pull request #1659 from GSA/jskinne3-poke-terraform
Added null_resource technique previously used in API repo
2024-06-26 18:22:57 -04:00
John Skiles Skinner
789ca4cb1b Added null_resource technique previously used in API repo 2024-06-26 15:16:15 -07:00
Carlo Costino
c204a31ccc Merge pull request #1658 from GSA/jskinne3-manifest-update-redis-name
Update name of Redis to include v70
2024-06-26 18:04:23 -04:00
John Skiles Skinner
41d1a3e1b5 Update name of Redis to include v70 2024-06-26 14:51:20 -07:00
Carlo Costino
8f8e06db6f Merge pull request #1656 from GSA/readme-update
Updated readme to include instructions for adding login pem to .env file
2024-06-26 15:53:52 -04:00
Carlo Costino
54578b58ea Merge pull request #1605 from GSA/add-report-options
Add report options
2024-06-26 15:38:32 -04:00
alexjanousekGSA
18da813fe6 Updated readme 2024-06-26 11:08:20 -06:00
Andrew Shumway
e3a85aab0c Pull main 2024-06-26 08:34:47 -06:00
Andrew Shumway
f218c01244 Remove commented out code 2024-06-26 08:34:05 -06:00
Carlo Costino
d59960df44 Merge pull request #1650 from GSA/jskinne3-redis-upgrade-step-1
Redis upgrade step 1
2024-06-25 23:55:14 -04:00
John Skiles Skinner
ab48aea438 Merge branch 'main' into jskinne3-redis-upgrade-step-1 2024-06-25 20:46:27 -07:00
Carlo Costino
0974fdceb4 Merge pull request #1649 from GSA/jskinne3-get-sandbox-working
Version upgrades
2024-06-25 22:51:21 -04:00
John Skiles Skinner
f591ef21fc Upgrade tj-actions from v41 to v44 2024-06-25 15:19:49 -07:00
John Skiles Skinner
73b42b4abf Same new resource in Demo and Production envs 2024-06-25 15:05:58 -07:00
John Skiles Skinner
302a0f51f6 Redis v7 in sandbox and staging env main.tf 2024-06-25 14:59:12 -07:00
John Skiles Skinner
382c1910c9 Missed one 2024-06-25 14:25:32 -07:00
John Skiles Skinner
6095471166 Terraform minimum version upgrade 2024-06-25 14:24:31 -07:00
John Skiles Skinner
624e161292 Upgrade cloudfoundry to v0.53.1 to match api app 2024-06-25 14:18:14 -07:00
Andrew Shumway
7cfa00197e Pull main 2024-06-25 09:05:52 -06:00
Andrew Shumway
9685a4bd24 Remove code that shouldn't be in 2024-06-25 09:04:59 -06:00
Carlo Costino
b05678a095 Merge pull request #1609 from GSA/notify-admin-1456
Need magic PII-free debugging method for Admin
2024-06-25 09:52:12 -04:00
Beverly Nguyen
e72af9671b removed print 2024-06-24 16:54:31 -07:00
Beverly Nguyen
44baaf665a fixed testing 2024-06-24 15:33:48 -07:00
Beverly Nguyen
5028054034 Merge branch 'main' into 1256-bug-tour-steps-needs-to-be-adjusted-for-the-new-preview-page 2024-06-24 14:25:00 -07:00
Carlo Costino
a9de6be102 Merge pull request #1645 from GSA/increase-prod-memory
Increased production memory to 2 GB
2024-06-24 12:45:56 -04:00
Andrew Shumway
7027cae3de pull main 2024-06-24 10:45:33 -06:00
Andrew Shumway
a9e4df0591 Todays data is only todays 2024-06-24 10:44:19 -06:00
Carlo Costino
a887f3f527 Increased production memory to 2 GB
This changeset increases our memory for the API app and its corresponding workers to 2 GB each.

Signed-off-by: Carlo Costino <carlo.costino@gsa.gov>
2024-06-24 12:07:28 -04:00
Carlo Costino
9c3c2ad9f8 Merge pull request #1643 from GSA/notify-admin-1642
fix gunicorn config
2024-06-24 10:26:00 -04:00
Kenneth Kehl
81dd764a54 fix gunicorn config 2024-06-21 09:22:20 -07:00
Kenneth Kehl
82aa564eb2 merge from main 2024-06-21 07:47:49 -07:00
Beverly Nguyen
fba6187fd9 Merge branch 'main' into 1612-add-in-new-endpoint-for-weekly-by-user_id 2024-06-20 19:37:43 -07:00
Beverly Nguyen
4b5c77f6c4 fixing flake8 error 2024-06-20 16:14:07 -07:00
Carlo Costino
144337e617 Merge pull request #1639 from GSA/pin_python_to_3_12_2
Pin Python to 3.13.3
2024-06-20 18:40:24 -04:00
Carlo Costino
31cd55725f Update runtime.txt to reference Python 3.12.3
Signed-off-by: Carlo Costino <carlo.costino@gsa.gov>
2024-06-20 18:28:26 -04:00
Carlo Costino
96af45d1f9 Merge branch 'main' into pin_python_to_3_12_2 2024-06-20 18:27:49 -04:00
Carlo Costino
bf38e121c6 Merge pull request #1636 from GSA/fix_raw_set
fix raw set
2024-06-20 17:39:26 -04:00
Kenneth Kehl
3b6ccc031a fix format? 2024-06-20 13:16:45 -07:00
Kenneth Kehl
f890635f24 merge from main 2024-06-20 12:57:15 -07:00
Kenneth Kehl
87c3efd312 switch to 3.12.3 2024-06-20 12:37:11 -07:00
Carlo Costino
4f93757a1a Merge pull request #1633 from GSA/fix-python-runtime
Fix runtime.txt for cloud.gov and Python
2024-06-20 14:51:13 -04:00
Kenneth Kehl
7b5fdd41df fix raw set 2024-06-20 11:50:51 -07:00
Carlo Costino
27befe0806 Fix runtime.txt for cloud.gov and Python
This changeset updates the runtime.txt file to match the recent Python version pinning that we had to do to fix a dependency issue.

Signed-off-by: Carlo Costino <carlo.costino@gsa.gov>
2024-06-20 14:32:19 -04:00
Carlo Costino
f0b95f3c81 Merge pull request #1599 from GSA/admin-1587_Logging_stuff
admin-1587: Adding some logging that hopefully might shed some light on things.
2024-06-20 14:20:17 -04:00
Carlo Costino
ac670ad60a Merge pull request #1632 from GSA/pin_python_to_3_12_2
pin python to 3.12.2
2024-06-20 14:08:56 -04:00
Kenneth Kehl
5429af16d4 pin python to 3.12.2 2024-06-20 10:45:22 -07:00
Beverly Nguyen
5e98ec396e made changes/fixes to tour flow 2024-06-19 13:59:39 -07:00
Beverly Nguyen
637f3d4b42 added fetch stats user to js 2024-06-19 10:03:12 -07:00
Kenneth Kehl
b126518940 update readme 2024-06-19 08:40:11 -07:00
Kenneth Kehl
ecbceb3b63 upgrade chokidar and mkdirp 2024-06-19 08:32:17 -07:00
Kenneth Kehl
aa02e65c1f revert upgrade of gulp 2024-06-18 11:25:05 -07:00
Kenneth Kehl
8f985f2429 update gulp to 5.0.0 2024-06-18 11:17:01 -07:00
Carlo Costino
e68aa60b42 Merge pull request #1624 from GSA/fix-npm-steps
Fix npm steps in make bootstrap
2024-06-18 14:09:44 -04:00
Beverly Nguyen
d54cfdd6c0 added utc time 2024-06-18 10:06:57 -07:00
Cliff Hill
56abd154c1 Now not revealing env var lengths.
Signed-off-by: Cliff Hill <clifford.hill@gsa.gov>
2024-06-18 12:57:22 -04:00
Cliff Hill
708af19697 Fixes.
Signed-off-by: Cliff Hill <clifford.hill@gsa.gov>
2024-06-18 12:57:22 -04:00
Cliff Hill
79bf8d1225 Better exception handling.
Signed-off-by: Cliff Hill <clifford.hill@gsa.gov>
2024-06-18 12:57:22 -04:00
Cliff Hill
f13898442c Reversing poetry.lock changes.
Signed-off-by: Cliff Hill <clifford.hill@gsa.gov>
2024-06-18 12:57:22 -04:00
Cliff Hill
cdf4a40591 Stuff was done.
Signed-off-by: Cliff Hill <clifford.hill@gsa.gov>
2024-06-18 12:57:22 -04:00
Cliff Hill
fbc51a2c0c Changest made to fix things.
Signed-off-by: Cliff Hill <clifford.hill@gsa.gov>
2024-06-18 12:57:21 -04:00
Cliff Hill
1a385c6346 More cleanup stuff.
Signed-off-by: Cliff Hill <clifford.hill@gsa.gov>
2024-06-18 12:57:21 -04:00
Cliff Hill
530206e33a Cleaning up the logging, gave some additional notes, removed hashing.
Signed-off-by: Cliff Hill <clifford.hill@gsa.gov>
2024-06-18 12:57:21 -04:00
Cliff Hill
9e94b5a242 saabling CSRF in dev because it breaks all the tests.
Signed-off-by: Cliff Hill <clifford.hill@gsa.gov>
2024-06-18 12:57:21 -04:00
Cliff Hill
ec9a36e0dd Activating CSRF for staging/demo/dev environments.
Signed-off-by: Cliff Hill <clifford.hill@gsa.gov>
2024-06-18 12:57:21 -04:00
Cliff Hill
f539cad4b4 Added some logging up front when the app is initialized to see if
DANGEROUS_SALT and SECRET_KEY are both available and have data in them.

Signed-off-by: Cliff Hill <clifford.hill@gsa.gov>
2024-06-18 12:57:21 -04:00
Beverly Nguyen
e26324638a Merge branch 'main' into 1612-add-in-new-endpoint-for-weekly-by-user_id 2024-06-18 09:50:50 -07:00
Kenneth Kehl
7f1f97a60b move to 22 2024-06-18 09:16:25 -07:00
Kenneth Kehl
9469b6a138 upgrade to 20 2024-06-18 08:53:25 -07:00
Kenneth Kehl
388cb1796a move to node 18.20.3 2024-06-18 07:59:07 -07:00
Kenneth Kehl
f000fa0ff3 merge from main 2024-06-18 07:50:35 -07:00
Kenneth Kehl
b1624c25dc more updated 2024-06-18 07:49:20 -07:00
Carlo Costino
b26c3a78b8 Fix npm steps in make bootstrap
This changeset fixes the order of steps in the make bootstrap command so npm does not fail.

Signed-off-by: Carlo Costino <carlo.costino@gsa.gov>
2024-06-18 10:21:23 -04:00
Carlo Costino
17bafd74e3 Merge pull request #1619 from GSA/notify-admin-1495
initial
2024-06-17 23:58:16 -04:00
Carlo Costino
59b0325124 Adjust npm install line and update Python dependencies
The npm install line needs to be run within the context of the nvm-managed Node.js and should happen after that environment is initialized but before the build step.  This also adds a couple more Python dependency updates that Dependabot flagged as well.

Signed-off-by: Carlo Costino <carlo.costino@gsa.gov>
2024-06-17 23:46:59 -04:00
Beverly Nguyen
6ae2f5b3f1 fixed lint in js 2024-06-17 17:46:43 -07:00
Beverly Nguyen
30a3c9ce73 merge main 2024-06-17 17:39:57 -07:00
Beverly Nguyen
71901f4912 comment out mychart 2024-06-17 17:34:37 -07:00
Beverly Nguyen
a1f2deda8f added test_fetch_daily_stats_by_user test 2024-06-17 17:32:24 -07:00
Kenneth Kehl
daa75c56d9 edit package lock 2024-06-17 16:04:29 -07:00
Kenneth Kehl
3674dd4e4d edit package lock 2024-06-17 15:08:31 -07:00
Kenneth Kehl
9817f9e434 revert gulp 2024-06-17 14:41:31 -07:00
Kenneth Kehl
00ab6f5122 hmm 2024-06-17 14:36:06 -07:00
Kenneth Kehl
5544400212 fix vulnerability 2024-06-17 14:27:59 -07:00
Kenneth Kehl
c491b4152c initial 2024-06-17 13:55:25 -07:00
Beverly Nguyen
fc7500932b added daily stats by user to js 2024-06-17 12:39:08 -07:00
Beverly Nguyen
3594694de2 added fetch by user 2024-06-17 12:36:21 -07:00
Kenneth Kehl
7e533731ed fix flake 8 2024-06-17 11:40:13 -07:00
Kenneth Kehl
16ad4a6c0c revamp PII masking 2024-06-17 11:13:03 -07:00
Carlo Costino
e426449a38 Merge pull request #1611 from GSA/notify-admin-740
update gunicorn config
2024-06-14 17:12:23 -04:00
Carlo Costino
ddaec17034 Merge pull request #1600 from GSA/notify-admin-1457
fix set and get in redis_client
2024-06-14 16:49:25 -04:00
Kenneth Kehl
c3ae47e7f9 merge from main 2024-06-14 12:50:15 -07:00
Steven Reilly
0a56941bb9 Merge pull request #1607 from GSA/1546-remove-effective-april-16-login-landing-page
Remove "Effective April 16" Login Landing Page
2024-06-14 15:28:13 -04:00
Steven Reilly
60256c0cba Merge pull request #1617 from GSA/update-deps-6-14-24
Updated dependencies - 6/14/2024
2024-06-14 10:36:27 -04:00
Carlo Costino
79df8288df Updated dependencies - 6/14/2024
This changeset updates Python dependencies that Dependabot has flagged in addition to several others that were due for updates.  It also reformats a test file via black.

Signed-off-by: Carlo Costino <carlo.costino@gsa.gov>
2024-06-14 10:22:01 -04:00
Beverly Nguyen
b5623ca96b updated css 2024-06-13 12:10:22 -07:00
Kenneth Kehl
4e8e1acb5b fix email masking algorithm 2024-06-13 10:17:47 -07:00
Andrew Shumway
ea8cfbdadb poetry lock 2024-06-13 10:43:46 -06:00
Beverly Nguyen
0fa6e59a49 fetch notification stats by user 2024-06-12 14:32:25 -07:00
Kenneth Kehl
51a9dfdfd2 update gunicorn config 2024-06-12 13:42:04 -07:00
Kenneth Kehl
f6f99b6124 fix bug where only exception is passed to logger 2024-06-12 10:20:06 -07:00
Andrew Shumway
d1ed7e1d0c Pull main 2024-06-12 09:29:37 -06:00
Beverly Nguyen
d028beda4f Removed comments 2024-06-11 16:29:31 -07:00
Beverly Nguyen
6be4f878e4 fixed end2end 2024-06-11 16:22:38 -07:00
Beverly Nguyen
5147072621 fixed e2e test 2024-06-11 15:46:36 -07:00
Beverly Nguyen
c8fb1b2f91 fixed import errors 2024-06-11 15:28:46 -07:00
Beverly Nguyen
53c938ebc6 fixed css 2024-06-11 15:12:05 -07:00
Beverly Nguyen
7b8db70552 fixed testing 2024-06-11 14:01:40 -07:00
Beverly Nguyen
cfa31af652 fixed testing 2024-06-11 13:38:26 -07:00
Kenneth Kehl
096658ab75 Need magic PII-free debugging method for Admin 2024-06-11 13:24:05 -07:00
Beverly Nguyen
89bd835cd8 update testing 2024-06-11 12:35:59 -07:00
Beverly Nguyen
8407f5605b add back signin.html and removed login banner 2024-06-11 11:06:13 -07:00
Beverly Nguyen
ed0f5a7b9e removed old login announcement page and added new sign in button 2024-06-10 16:05:59 -07:00
Andrew Shumway
35e27ff25a Merge main 2024-06-10 10:44:09 -06:00
Andrew Shumway
a401562735 Add report options 1/3/5-day 2024-06-10 10:15:22 -06:00
Carlo Costino
749935e94d Merge pull request #1595 from GSA/notify-admin-1588
Add debug for admin #1588
2024-06-08 00:44:55 -04:00
Carlo Costino
9f9614d292 Merge pull request #1596 from GSA/1305-delivery-status-content-update
Delivery status content updates
2024-06-08 00:33:34 -04:00
Kenneth Kehl
da96edbfcd fix set and get in redis_client 2024-06-07 09:07:42 -07:00
Kenneth Kehl
63bd660403 code review feedback 2024-06-07 08:03:43 -07:00
Carlo Costino
4321e31377 Merge pull request #1558 from GSA/socketIO
Installing socketIO and connecting new dashboard API endpoints
2024-06-06 17:44:58 -04:00
Beverly Nguyen
33f8f4c000 fix import sort 2024-06-06 10:50:24 -07:00
Jonathan Bobel
934f8927c6 Updated content, removed test no longer needed 2024-06-06 13:50:08 -04:00
Kenneth Kehl
83f5b6f0ab add comment 2024-06-06 10:43:41 -07:00
Beverly Nguyen
c0b2c42473 removed imported unused style issue 2024-06-06 10:42:49 -07:00
Beverly Nguyen
e413f8c640 added sampleChartDashboard.js 2024-06-06 10:05:36 -07:00
Beverly Nguyen
f98cff8d40 fixed linting issues 2024-06-06 10:01:58 -07:00
Kenneth Kehl
adc89a8326 merge from main 2024-06-06 09:35:07 -07:00
Kenneth Kehl
82690d6e3d add debug for user issue 2024-06-06 09:34:49 -07:00
Beverly Nguyen
ae7a14f3f2 fixed js chart referenced 2024-06-06 01:10:09 -07:00
Beverly Nguyen
61253963f7 added socketiotestclent 2024-06-06 00:48:08 -07:00
Beverly Nguyen
9d3dc047ea added chartjs to path in gulp 2024-06-05 15:03:19 -07:00
Beverly Nguyen
e5a9eed626 installing socketio 2024-06-05 14:56:22 -07:00
Carlo Costino
834c6df7dc Merge pull request #1593 from GSA/update-deps-6-5-24
Update Python dependencies - 6/5/2024
2024-06-05 17:03:20 -04:00
Carlo Costino
43d67a4821 Update Python dependencies - 6/5/2024
This changeset updates several Python dependencies flagged by Dependabot.

Signed-off-by: Carlo Costino <carlo.costino@gsa.gov>
2024-06-05 16:55:17 -04:00
Beverly Nguyen
fed2aff43c cleaned up code 2024-06-04 16:52:32 -07:00
Beverly Nguyen
0a4cf290ac added flask-socketio 2024-06-04 16:33:55 -07:00
Beverly Nguyen
fec6c2ff72 removed comments 2024-06-04 16:27:35 -07:00
Beverly Nguyen
7a9d260717 fix testing 2024-06-03 15:34:43 -07:00
Andrew Shumway
f9169fcbcb poetry lock 2024-06-03 15:55:37 -06:00
Carlo Costino
44a3c9a13b Merge pull request #1585 from GSA/update-deps-6-3-24
Update Python dependencies - 6/3/24
2024-06-03 17:31:23 -04:00
Beverly Nguyen
8554df58f8 cleared up style checks 2024-06-03 13:51:59 -07:00
Beverly Nguyen
d3bee61742 cleared up style checks 2024-06-03 13:45:36 -07:00
Beverly Nguyen
89f19bb63d cleared up style checks 2024-06-03 13:35:30 -07:00
Beverly Nguyen
d12b798bba removed sending status 2024-06-03 13:17:31 -07:00
Beverly Nguyen
2e41b752f5 Remove .ds.baseline from repository 2024-06-03 12:50:05 -07:00
Beverly Nguyen
72e82c818b added socket.io.min.js 2024-06-03 11:39:02 -07:00
Beverly Nguyen
4ebd5734d6 removed socket.js 2024-06-03 11:32:56 -07:00
Beverly Nguyen
8152985f9b Merge branch 'main' into socketIO 2024-06-03 09:52:41 -07:00
Carlo Costino
f0fcb8f21d Update Python dependencies - 6/3/24
This changeset updates several Python dependencies to stay on top of Dependabot alerts.

Signed-off-by: Carlo Costino <carlo.costino@gsa.gov>
2024-06-03 11:03:29 -04:00
Carlo Costino
37546dd7d4 Merge pull request #1582 from GSA/invalidate-cancelled-service-invites
Update expired and cancelled service invite handling
2024-06-03 10:54:33 -04:00
Carlo Costino
f6cf493266 Update expired and cancelled service invite handling
This changeset adds a bit of extra handling for expired and cancelled service invites so that users can no longer accept them and are provided with more detailed error messages.

Signed-off-by: Carlo Costino <carlo.costino@gsa.gov>
2024-05-31 17:20:08 -04:00
Carlo Costino
5fa2e77a71 Merge pull request #1570 from GSA/notify-admin-1041
use moto to mock s3
2024-05-31 16:12:06 -04:00
Carlo Costino
7bc8e64f36 Merge pull request #1555 from GSA/notify-admin-1545b
Admin: Use Vulture to identify potentially unused code
2024-05-31 15:57:25 -04:00
Kenneth Kehl
29dbe45cbe code review feedback 2024-05-31 11:27:39 -07:00
Kenneth Kehl
5f92f9a95d merge from main 2024-05-31 09:54:52 -07:00
Carlo Costino
42912bbbc4 Merge pull request #1581 from GSA/update-deps-05-31-24
Update dependencies and fix E2E test
2024-05-31 12:48:09 -04:00
Kenneth Kehl
2414b09c0f fix flake8 2024-05-31 09:47:19 -07:00
Kenneth Kehl
2f366ff65c code review feedback 2024-05-31 09:41:36 -07:00
Carlo Costino
8c85d99449 Update dependencies and fix E2E test
This changeset updates a couple of dependencies flagged by Dependabot and fixes an end-to-end test that needed to be updated with the one-off send filename changes.

Signed-off-by: Carlo Costino <carlo.costino@gsa.gov>
2024-05-31 12:09:22 -04:00
Kenneth Kehl
a834166f41 merge from main 2024-05-31 08:56:11 -07:00
Andrew Shumway
17b21b2382 Pull main 2024-05-31 09:27:42 -06:00
Carlo Costino
fc29871b57 Merge pull request #1560 from GSA/terrazoon-patch-1
Look into enabling Dependabot Code Scanning for our API and admin repos
2024-05-31 10:39:58 -04:00
Carlo Costino
56d21ecbcd Merge pull request #1554 from GSA/500-pricing-content-updates
500 - Updating "Pricing" page content
2024-05-31 10:34:11 -04:00
Carlo Costino
8a6637075e Merge pull request #1552 from GSA/notify-admin-1545
change vulture rules in Makefile
2024-05-31 10:32:56 -04:00
Carlo Costino
2fdc8e7e64 Merge pull request #1530 from GSA/notify-admin-1481
add check for government email addresses
2024-05-31 10:30:31 -04:00
Carlo Costino
67523fb535 Merge pull request #1502 from GSA/1405-bug-long-complicated-csv-name
Updated filename string into db for a notification and UI display
2024-05-31 10:29:16 -04:00
Carlo Costino
4bf6af4e45 Merge pull request #1480 from GSA/1464-switch-to-the-new-withnav_template
Switch to the new withnav_template
2024-05-31 10:28:01 -04:00
Beverly Nguyen
6c44982a5e removed socket.js 2024-05-30 21:20:09 -07:00
Beverly Nguyen
01321aeddc connecting api data to socket and setting up chartjs to use the data 2024-05-30 21:17:10 -07:00
Beverly Nguyen
d5934855a0 Merge branch '1513-provide-notification-stats-data-within-7-days-month-and-year' into socketIO 2024-05-30 14:51:23 -07:00
Beverly Nguyen
108e889ac5 added example of fetch 2024-05-30 14:48:25 -07:00
Kenneth Kehl
2332744cdf merge from main 2024-05-30 08:32:19 -07:00
Kenneth Kehl
a4fc04a659 test that S3 not getting hit in tests 2024-05-30 08:25:00 -07:00
Kenneth Kehl
df032ba76d remove mock_aws where its not useful 2024-05-30 07:58:30 -07:00
Carlo Costino
5508796779 Merge pull request #1577 from GSA/update-deps-05-29-24
Update admin repo dependencies
2024-05-30 09:05:53 -04:00
Carlo Costino
43b99a5905 Update admin repo dependencies
This changeset updates several Python dependencies that Dependabot flagged for updates due to end-to-end tests still failing in Dependabot PRs.

Signed-off-by: Carlo Costino <carlo.costino@gsa.gov>
2024-05-29 19:40:02 -04:00
Kenneth Kehl
e694b9a5f6 clean up mock_s3_download 2024-05-29 14:49:06 -07:00
Kenneth Kehl
9368f6a496 remove some fixtures 2024-05-29 14:18:08 -07:00
Beverly Nguyen
758c1cd8a0 added new api for month, by year, previous 7 day stats 2024-05-29 12:13:09 -07:00
Andrew Shumway
8e6b3fa39e Poetry lock 2024-05-29 10:15:11 -06:00
Andrew Shumway
91bb6d254f Pull main 2024-05-29 10:09:02 -06:00
Carlo Costino
502b65aaeb Merge pull request #1569 from GSA/update-admin-deps
Update Python dependencies
2024-05-28 21:16:47 -04:00
Carlo Costino
548b7cd3a2 Updated boto3 and botocore again
Signed-off-by: Carlo Costino <carlo.costino@gsa.gov>
2024-05-28 17:52:29 -04:00
Kenneth Kehl
17fec1c99e use moto to mock s3 2024-05-28 11:27:57 -07:00
Carlo Costino
1c1918ba52 Update Python dependencies
This changeset updates a few Python dependencies the week of May 28th to help keep our project up-to-date.

Signed-off-by: Carlo Costino <carlo.costino@gsa.gov>
2024-05-28 12:11:53 -04:00
Kenneth Kehl
be4b1d558d merge from main 2024-05-24 08:59:03 -07:00
Kenneth Kehl
f939687283 merge 2024-05-24 08:58:47 -07:00
Kenneth Kehl
6494f44c43 merge from main 2024-05-24 08:23:21 -07:00
Steven Reilly
184d181e15 Merge pull request #1553 from GSA/update-pre-commit-hooks
Update pre-commit hooks
2024-05-24 10:54:18 -04:00
Carlo Costino
408b00c697 Removed duplicate pre-commit hook
Signed-off-by: Carlo Costino <carlo.costino@gsa.gov>
2024-05-24 10:36:32 -04:00
Carlo Costino
fe836f2d3d Updated several other dependencies
This includes requests to take care of a Dependabot security alert.

Signed-off-by: Carlo Costino <carlo.costino@gsa.gov>
2024-05-24 10:22:59 -04:00
Kenneth Kehl
626f4a1ac0 Create codeql.yml 2024-05-21 07:47:12 -07:00
Kenneth Kehl
16dc4512dd fix si.edu 2024-05-21 07:38:23 -07:00
Beverly Nguyen
ad68c26450 installing socketIO and testing the WebSocket connection 2024-05-20 16:21:38 -07:00
Kenneth Kehl
622255e5a6 merges 2024-05-20 13:19:14 -07:00
Kenneth Kehl
7a1ab19583 merge 2024-05-20 12:11:09 -07:00
Kenneth Kehl
9ba5e3b917 code review feedback 2024-05-20 12:09:49 -07:00
Kenneth Kehl
c790921141 Update app/main/views/sign_in.py
Co-authored-by: Carlo Costino <ccostino@users.noreply.github.com>
2024-05-20 11:24:39 -07:00
Kenneth Kehl
309c168f52 revert back to 100% confidence level for vulture and add a comment 2024-05-20 11:18:31 -07:00
Kenneth Kehl
9916632187 set confidence back to 100% here. The 60% confidence is handled in a different PR 2024-05-20 10:55:26 -07:00
Kenneth Kehl
d045ecfa21 remove easy targets 2024-05-20 10:45:34 -07:00
Carlo Costino
210e8b5a33 This changeset updates our pre-commit hooks to help check for things in our code before we commit anything, such as code format and structure as well as sensitive information and credentials.
Signed-off-by: Carlo Costino <carlo.costino@gsa.gov>
2024-05-20 12:04:25 -04:00
Jonathan Bobel
2c18786486 500 - Updating "Pricing" page content 2024-05-20 11:51:09 -04:00
Kenneth Kehl
5bc652db9e change vulture rules in Makefile 2024-05-20 07:13:19 -07:00
Steven Reilly
8e0724effa Merge pull request #1548 from GSA/fix-utils-deps
Add missing dependencies
2024-05-20 09:34:51 -04:00
Beverly Nguyen
d739324d57 moved flash_message 2024-05-17 15:17:25 -07:00
Carlo Costino
3caf1e1631 Merge pull request #1539 from GSA/testing-chart.js
Installing chart.js
2024-05-17 18:16:28 -04:00
Carlo Costino
6ce5856f3b Add missing dependencies
The merging of notifications_utils to this repo does not deploy because of missing dependencies.  This changeset adds them back in directly.

Signed-off-by: Carlo Costino <carlo.costino@gsa.gov>
2024-05-17 18:13:09 -04:00
Beverly Nguyen
868edd3c28 Merge branch 'main' into 1464-switch-to-the-new-withnav_template 2024-05-17 15:12:36 -07:00
Beverly Nguyen
f91756d2c3 moved script to base.html 2024-05-17 15:07:05 -07:00
Carlo Costino
1b6741d0e5 Merge pull request #1543 from GSA/notify-admin-1396
Exception Investigation: app.notify_client:InviteTokenError
2024-05-17 17:43:06 -04:00
Carlo Costino
fe9e25ba6f Merge pull request #1542 from GSA/add-utils
Localize notification_utils to the admin
2024-05-17 17:26:58 -04:00
Carlo Costino
aa175aa4be Removing empty test file.
Signed-off-by: Carlo Costino <carlo.costino@gsa.gov>
2024-05-17 17:15:33 -04:00
Carlo Costino
6f59cff0c4 Merge pull request #1547 from GSA/admin-1534
[ADMIN-1534] API Research - Allowance Count Down
2024-05-17 16:25:37 -04:00
Kenneth Kehl
33d402f9b9 noqa the last one 2024-05-17 12:36:42 -07:00
Beverly Nguyen
90d3dde6b5 Merge branch 'main' into testing-chart.js 2024-05-17 11:26:45 -07:00
Beverly Nguyen
ff1b60cc89 Merge branch 'main' into 1464-switch-to-the-new-withnav_template 2024-05-17 11:20:55 -07:00
Beverly Nguyen
68168a85a1 Merge branch 'main' into testing-chart.js 2024-05-17 11:14:14 -07:00
Carlo Costino
3a42aafad2 Merge pull request #1470 from GSA/1463-convert-basehtml-template-to-become-the-new-main-template
Switch to the new base.html template
2024-05-17 14:11:04 -04:00
Kenneth Kehl
ec6bdee427 almost done 2024-05-17 11:05:22 -07:00
Kenneth Kehl
1b172553f5 more fixes 2024-05-17 10:25:09 -07:00
Cliff Hill
a0e17816e6 Added documentation of findings.
Signed-off-by: Cliff Hill <clifford.hill@gsa.gov>
2024-05-17 12:41:30 -04:00
Kenneth Kehl
7302e4c811 more fixes 2024-05-17 08:52:23 -07:00
Kenneth Kehl
dcfc78d805 some fixes 2024-05-16 15:13:45 -07:00
Kenneth Kehl
e660541e9c Exception Investigation: app.notify_client:InviteTokenError 2024-05-16 09:11:11 -07:00
Carlo Costino
9a83ba7475 Localize notification_utils to the admin
This changeset pulls in all of the notification_utils code directly into the admin and removes it as an external dependency. We are doing this to cut down on operational maintenance of the project and will begin removing parts of it no longer needed for the admin.

Signed-off-by: Carlo Costino <carlo.costino@gsa.gov>
2024-05-16 10:37:37 -04:00
Beverly Nguyen
7ff0cec147 installing chart.js via npm 2024-05-15 15:43:49 -07:00
Beverly Nguyen
d4a46ae552 installing chart.js via npm 2024-05-15 15:40:15 -07:00
Beverly Nguyen
6855a6ebe4 installing chart.js via npm 2024-05-15 15:35:39 -07:00
Beverly Nguyen
5546de66c4 testing chart plain vanilla 2024-05-15 11:39:03 -07:00
Carlo Costino
6bab682edd Merge pull request #1538 from GSA/973-update-config-for-cloud-gov
Updated config files to split the cloud foundry and public admin routes
2024-05-15 09:58:09 -04:00
Anastasia Gradova
e91723fc26 Updated config files to split the cloud foundry
route and the public admin route
2024-05-14 18:45:50 -06:00
Carlo Costino
b3a401a4c7 Merge pull request #1537 from GSA/update-deps-05-14-24
Update dependencies (05/14/2024)
2024-05-14 15:53:24 -04:00
Carlo Costino
d484c3636b Update dependencies (05/14/2024)
This changeset updates several dependencies flagged by Dependabot that we cannot merge directly due to the E2E test issue.

Signed-off-by: Carlo Costino <carlo.costino@gsa.gov>
2024-05-14 15:42:08 -04:00
Kenneth Kehl
d47f9933d2 add check for government email addresses 2024-05-13 13:39:51 -07:00
Carlo Costino
b5c7a71215 Merge pull request #1523 from GSA/notify-admin-1501
Users are still asked to complete their profile when attempting to accept an invite for someone else
2024-05-10 17:28:43 -04:00
Andrew Shumway
8a6802be8f pull main 2024-05-10 10:42:32 -06:00
Kenneth Kehl
e973c11d3b write new tests 2024-05-10 07:52:07 -07:00
Kenneth Kehl
c578175e53 write new tests 2024-05-10 07:51:32 -07:00
Kenneth Kehl
11beff2541 get it working 2024-05-09 14:04:30 -07:00
Kenneth Kehl
7f72599401 notify-admin-1501 2024-05-09 13:08:34 -07:00
Andrew Shumway
5506da56e2 poetry lock 2024-05-08 09:05:27 -06:00
Andrew Shumway
cdd71fa07d pull down main 2024-05-08 09:03:24 -06:00
Beverly Nguyen
93d7ca4052 added pending and requested status to monthly stats dict 2024-05-06 17:23:38 -07:00
Beverly Nguyen
065f1b7c15 removed comments on base.html 2024-05-03 10:58:06 -07:00
Anastasia Gradova
5f531f1f78 Updated filename string into db and UI display 2024-05-02 12:53:27 -06:00
Andrew Shumway
7108c5f1c8 pull main down 2024-04-30 10:06:49 -06:00
Andrew Shumway
65899f6171 poetry lock 2024-04-26 10:15:44 -06:00
Andrew Shumway
bd633ddc98 Merge branch 'main' of https://github.com/GSA/notifications-admin 2024-04-25 15:27:31 -06:00
Beverly Nguyen
8366a4c772 fix testing 2024-04-24 16:12:54 -07:00
Beverly Nguyen
f734e45242 added comments 2024-04-24 13:56:07 -07:00
Beverly Nguyen
04f1f6244c fixed file name 2024-04-24 13:53:28 -07:00
Beverly Nguyen
3b82e136e0 switched to withnav_template for org_template, settings template 2024-04-24 13:43:17 -07:00
Beverly Nguyen
bc945c9420 replaced content_tempalte and withoutnav_template with base.html 2024-04-24 12:31:00 -07:00
Andrew Shumway
ff1956ce70 Merge branch 'main' of https://github.com/GSA/notifications-admin 2024-04-24 09:29:58 -06:00
Beverly Nguyen
2c0a4f16f2 fixed testing 2024-04-23 16:23:31 -07:00
Beverly Nguyen
d47fbb8eb7 switched base.html in 2024-04-23 14:58:44 -07:00
Andrew Shumway
98f12980ad poetry lock 2024-04-22 10:48:46 -06:00
Andrew Shumway
2d3dbbc532 Merge branch 'main' of https://github.com/GSA/notifications-admin 2024-04-22 10:44:17 -06:00
Andrew Shumway
fcf2e31911 Merge branch 'main' of https://github.com/GSA/notifications-admin 2024-04-17 10:42:18 -06:00
Andrew Shumway
2454a6e94b poetry lock 2024-04-15 10:38:27 -06:00
264 changed files with 54378 additions and 3381 deletions

706
.ds.baseline Normal file
View File

@@ -0,0 +1,706 @@
{
"version": "1.5.0",
"plugins_used": [
{
"name": "ArtifactoryDetector"
},
{
"name": "AWSKeyDetector"
},
{
"name": "AzureStorageKeyDetector"
},
{
"name": "Base64HighEntropyString",
"limit": 4.5
},
{
"name": "BasicAuthDetector"
},
{
"name": "CloudantDetector"
},
{
"name": "DiscordBotTokenDetector"
},
{
"name": "GitHubTokenDetector"
},
{
"name": "GitLabTokenDetector"
},
{
"name": "HexHighEntropyString",
"limit": 3.0
},
{
"name": "IbmCloudIamDetector"
},
{
"name": "IbmCosHmacDetector"
},
{
"name": "IPPublicDetector"
},
{
"name": "JwtTokenDetector"
},
{
"name": "KeywordDetector",
"keyword_exclude": ""
},
{
"name": "MailchimpDetector"
},
{
"name": "NpmDetector"
},
{
"name": "OpenAIDetector"
},
{
"name": "PrivateKeyDetector"
},
{
"name": "PypiTokenDetector"
},
{
"name": "SendGridDetector"
},
{
"name": "SlackDetector"
},
{
"name": "SoftlayerDetector"
},
{
"name": "SquareOAuthDetector"
},
{
"name": "StripeDetector"
},
{
"name": "TelegramBotTokenDetector"
},
{
"name": "TwilioKeyDetector"
}
],
"filters_used": [
{
"path": "detect_secrets.filters.allowlist.is_line_allowlisted"
},
{
"path": "detect_secrets.filters.common.is_baseline_file",
"filename": ".ds.baseline"
},
{
"path": "detect_secrets.filters.common.is_ignored_due_to_verification_policies",
"min_level": 2
},
{
"path": "detect_secrets.filters.heuristic.is_indirect_reference"
},
{
"path": "detect_secrets.filters.heuristic.is_likely_id_string"
},
{
"path": "detect_secrets.filters.heuristic.is_lock_file"
},
{
"path": "detect_secrets.filters.heuristic.is_not_alphanumeric_string"
},
{
"path": "detect_secrets.filters.heuristic.is_potential_uuid"
},
{
"path": "detect_secrets.filters.heuristic.is_prefixed_with_dollar_sign"
},
{
"path": "detect_secrets.filters.heuristic.is_sequential_string"
},
{
"path": "detect_secrets.filters.heuristic.is_swagger_file"
},
{
"path": "detect_secrets.filters.heuristic.is_templated_secret"
}
],
"results": {
".github/workflows/checks.yml": [
{
"type": "Secret Keyword",
"filename": ".github/workflows/checks.yml",
"hashed_secret": "5baa61e4c9b93f3f0682250b6cf8331b7ee68fd8",
"is_verified": false,
"line_number": 61,
"is_secret": false
},
{
"type": "Basic Auth Credentials",
"filename": ".github/workflows/checks.yml",
"hashed_secret": "5baa61e4c9b93f3f0682250b6cf8331b7ee68fd8",
"is_verified": false,
"line_number": 95,
"is_secret": false
}
],
"app/assets/js/uswds.min.js": [
{
"type": "Secret Keyword",
"filename": "app/assets/js/uswds.min.js",
"hashed_secret": "372ea08cab33e71c02c651dbc83a474d32c676ea",
"is_verified": false,
"line_number": 85,
"is_secret": false
},
{
"type": "Secret Keyword",
"filename": "app/assets/js/uswds.min.js",
"hashed_secret": "53e07a32bf191d6917ee6fd863f0b52632a86798",
"is_verified": false,
"line_number": 85,
"is_secret": false
}
],
"app/config.py": [
{
"type": "Secret Keyword",
"filename": "app/config.py",
"hashed_secret": "577a4c667e4af8682ca431857214b3a920883efc",
"is_verified": false,
"line_number": 117,
"is_secret": false
}
],
"app/main/_commonly_used_passwords.py": [
{
"type": "Hex High Entropy String",
"filename": "app/main/_commonly_used_passwords.py",
"hashed_secret": "82e19fa12aab7cfc718a002fc82c0f074bf070e7",
"is_verified": false,
"line_number": 123,
"is_secret": false
},
{
"type": "Hex High Entropy String",
"filename": "app/main/_commonly_used_passwords.py",
"hashed_secret": "a172ffc990129fe6f68b50f6037c54a1894ee3fd",
"is_verified": false,
"line_number": 240,
"is_secret": false
},
{
"type": "Hex High Entropy String",
"filename": "app/main/_commonly_used_passwords.py",
"hashed_secret": "4de69ee6b12b7fc91070873b71ba6e2929b90619",
"is_verified": false,
"line_number": 244,
"is_secret": false
},
{
"type": "Hex High Entropy String",
"filename": "app/main/_commonly_used_passwords.py",
"hashed_secret": "370194ff6e0f93a7432e16cc9badd9427e8b4e13",
"is_verified": false,
"line_number": 284,
"is_secret": false
},
{
"type": "Hex High Entropy String",
"filename": "app/main/_commonly_used_passwords.py",
"hashed_secret": "3dd635a808ddb6dd4b6731f7c409d53dd4b14df2",
"is_verified": false,
"line_number": 356,
"is_secret": false
},
{
"type": "Hex High Entropy String",
"filename": "app/main/_commonly_used_passwords.py",
"hashed_secret": "67a74306b06d0c01624fe0d0249a570f4d093747",
"is_verified": false,
"line_number": 374,
"is_secret": false
},
{
"type": "Hex High Entropy String",
"filename": "app/main/_commonly_used_passwords.py",
"hashed_secret": "61d6504733ca7757e259c644acd085c4dd471019",
"is_verified": false,
"line_number": 910,
"is_secret": false
},
{
"type": "Hex High Entropy String",
"filename": "app/main/_commonly_used_passwords.py",
"hashed_secret": "4ea872dfd7eefbde0036da7f0780826353dc7477",
"is_verified": false,
"line_number": 940,
"is_secret": false
},
{
"type": "Hex High Entropy String",
"filename": "app/main/_commonly_used_passwords.py",
"hashed_secret": "b214f706bb602c1cc2adc5c6165e73622305f4bb",
"is_verified": false,
"line_number": 1010,
"is_secret": false
},
{
"type": "Hex High Entropy String",
"filename": "app/main/_commonly_used_passwords.py",
"hashed_secret": "5cbabd43e49a1fedbbc3b86311aa6c8fe446abf9",
"is_verified": false,
"line_number": 1195,
"is_secret": false
},
{
"type": "Hex High Entropy String",
"filename": "app/main/_commonly_used_passwords.py",
"hashed_secret": "18ad10fd4a67f21fc07b1aa5046b410f6b2bedf1",
"is_verified": false,
"line_number": 1213,
"is_secret": false
},
{
"type": "Hex High Entropy String",
"filename": "app/main/_commonly_used_passwords.py",
"hashed_secret": "10470c3b4b1fed12c3baac014be15fac67c6e815",
"is_verified": false,
"line_number": 1263,
"is_secret": false
},
{
"type": "Hex High Entropy String",
"filename": "app/main/_commonly_used_passwords.py",
"hashed_secret": "65e1946c8f102eca8ba0af291f7c5e807516d94c",
"is_verified": false,
"line_number": 1346,
"is_secret": false
},
{
"type": "Hex High Entropy String",
"filename": "app/main/_commonly_used_passwords.py",
"hashed_secret": "0075df0a74c07ee295c98238c018401c9a80183b",
"is_verified": false,
"line_number": 1397,
"is_secret": false
},
{
"type": "Hex High Entropy String",
"filename": "app/main/_commonly_used_passwords.py",
"hashed_secret": "ca0023d7b345802fbc227b902cb9c57a3e02195f",
"is_verified": false,
"line_number": 1442,
"is_secret": false
},
{
"type": "Hex High Entropy String",
"filename": "app/main/_commonly_used_passwords.py",
"hashed_secret": "c8c6ca2e11c2dfd2a40914585b5944bffea15c8c",
"is_verified": false,
"line_number": 1555,
"is_secret": false
},
{
"type": "Hex High Entropy String",
"filename": "app/main/_commonly_used_passwords.py",
"hashed_secret": "b85b97a99eab8c809570c61d6404c1e49bdefbb4",
"is_verified": false,
"line_number": 1596,
"is_secret": false
},
{
"type": "Hex High Entropy String",
"filename": "app/main/_commonly_used_passwords.py",
"hashed_secret": "dec7dd342a499dfd4d283d872ccf598d8a7b6039",
"is_verified": false,
"line_number": 1789,
"is_secret": false
},
{
"type": "Hex High Entropy String",
"filename": "app/main/_commonly_used_passwords.py",
"hashed_secret": "2dc5053699a351121bf839c446bd4a878dda5735",
"is_verified": false,
"line_number": 1939,
"is_secret": false
},
{
"type": "Hex High Entropy String",
"filename": "app/main/_commonly_used_passwords.py",
"hashed_secret": "e5d54f0ac13abbdaa94b696c2469148b96dd11ab",
"is_verified": false,
"line_number": 2242,
"is_secret": false
},
{
"type": "Hex High Entropy String",
"filename": "app/main/_commonly_used_passwords.py",
"hashed_secret": "6059f42e2bbae78141e8a9e6286755ee691d5ce0",
"is_verified": false,
"line_number": 2305,
"is_secret": false
},
{
"type": "Hex High Entropy String",
"filename": "app/main/_commonly_used_passwords.py",
"hashed_secret": "fe703d258c7ef5f50b71e06565a65aa07194907f",
"is_verified": false,
"line_number": 2348,
"is_secret": false
},
{
"type": "Hex High Entropy String",
"filename": "app/main/_commonly_used_passwords.py",
"hashed_secret": "c229b68e1c3ffd9874838b5cb5354a0ee1367ddc",
"is_verified": false,
"line_number": 2349,
"is_secret": false
},
{
"type": "Hex High Entropy String",
"filename": "app/main/_commonly_used_passwords.py",
"hashed_secret": "756de479126e911b6f3400ae686d663d9d26b509",
"is_verified": false,
"line_number": 2920,
"is_secret": false
},
{
"type": "Hex High Entropy String",
"filename": "app/main/_commonly_used_passwords.py",
"hashed_secret": "6b174322afcdb440ee9cc3cc11eb16f9a00dec04",
"is_verified": false,
"line_number": 2975,
"is_secret": false
},
{
"type": "Hex High Entropy String",
"filename": "app/main/_commonly_used_passwords.py",
"hashed_secret": "9860783bfb510cbb2bf34471ec0b84a7ea587695",
"is_verified": false,
"line_number": 3359,
"is_secret": false
},
{
"type": "Hex High Entropy String",
"filename": "app/main/_commonly_used_passwords.py",
"hashed_secret": "b227cbd22eaa96019ebfc4aff35ad2add2a47439",
"is_verified": false,
"line_number": 3590,
"is_secret": false
},
{
"type": "Hex High Entropy String",
"filename": "app/main/_commonly_used_passwords.py",
"hashed_secret": "381d48209aecab8834eb495c5b5406100da07882",
"is_verified": false,
"line_number": 3811,
"is_secret": false
},
{
"type": "Hex High Entropy String",
"filename": "app/main/_commonly_used_passwords.py",
"hashed_secret": "508b38590a90d32990aadd7350d160b795c3ab41",
"is_verified": false,
"line_number": 3850,
"is_secret": false
}
],
"app/main/views/sign_in.py": [
{
"type": "Private Key",
"filename": "app/main/views/sign_in.py",
"hashed_secret": "1348b145fa1a555461c1b790a2f66614781091e9",
"is_verified": false,
"line_number": 27,
"is_secret": false
}
],
"app/templates/new/components/head.html": [
{
"type": "Base64 High Entropy String",
"filename": "app/templates/new/components/head.html",
"hashed_secret": "ee5048791fc7ff45a1545e24f85bec3317371327",
"is_verified": false,
"line_number": 34,
"is_secret": false
}
],
"app/templates/old/admin_template.html": [
{
"type": "Base64 High Entropy String",
"filename": "app/templates/old/admin_template.html",
"hashed_secret": "ee5048791fc7ff45a1545e24f85bec3317371327",
"is_verified": false,
"line_number": 18,
"is_secret": false
}
],
"deploy-config/sandbox.yml": [
{
"type": "Secret Keyword",
"filename": "deploy-config/sandbox.yml",
"hashed_secret": "113151dd10316fcb0d5507b6215d78e2f3fe9e54",
"is_verified": false,
"line_number": 8,
"is_secret": false
}
],
"pytest.ini": [
{
"type": "Secret Keyword",
"filename": "pytest.ini",
"hashed_secret": "577a4c667e4af8682ca431857214b3a920883efc",
"is_verified": false,
"line_number": 7,
"is_secret": false
},
{
"type": "Base64 High Entropy String",
"filename": "pytest.ini",
"hashed_secret": "d347784b1ab6074a65cda7bc42f1561bed85493f",
"is_verified": false,
"line_number": 7,
"is_secret": false
},
{
"type": "Base64 High Entropy String",
"filename": "pytest.ini",
"hashed_secret": "ed1754d5cc82c8fd83205ebfb8c43fe4e88415a4",
"is_verified": false,
"line_number": 9,
"is_secret": false
},
{
"type": "Secret Keyword",
"filename": "pytest.ini",
"hashed_secret": "a94a8fe5ccb19ba61c4c0873d391e987982fbbd3",
"is_verified": false,
"line_number": 11,
"is_secret": false
}
],
"tests/__init__.py": [
{
"type": "Secret Keyword",
"filename": "tests/__init__.py",
"hashed_secret": "f8377c90fcfd699f0ddbdcb30c2c9183d2d933ea",
"is_verified": false,
"line_number": 388,
"is_secret": false
}
],
"tests/app/main/forms/test_register_user_form.py": [
{
"type": "Secret Keyword",
"filename": "tests/app/main/forms/test_register_user_form.py",
"hashed_secret": "8c6c978dc8e08771c7dea1ea2370fdf2446e5ba5",
"is_verified": false,
"line_number": 38,
"is_secret": false
}
],
"tests/app/main/test_errorhandlers.py": [
{
"type": "Base64 High Entropy String",
"filename": "tests/app/main/test_errorhandlers.py",
"hashed_secret": "005fa73b3f2be8f0d71d361c1f0a9d787cd09b4e",
"is_verified": false,
"line_number": 33,
"is_secret": false
}
],
"tests/app/main/test_request_header.py": [
{
"type": "Secret Keyword",
"filename": "tests/app/main/test_request_header.py",
"hashed_secret": "6866ef97a972ba3a2c6ff8bb2812981054770162",
"is_verified": false,
"line_number": 21,
"is_secret": false
}
],
"tests/app/main/views/organizations/test_organization_invites.py": [
{
"type": "Secret Keyword",
"filename": "tests/app/main/views/organizations/test_organization_invites.py",
"hashed_secret": "bdbb156d25d02fd7792865824201dda1c60f4473",
"is_verified": false,
"line_number": 265,
"is_secret": false
},
{
"type": "Secret Keyword",
"filename": "tests/app/main/views/organizations/test_organization_invites.py",
"hashed_secret": "5baa61e4c9b93f3f0682250b6cf8331b7ee68fd8",
"is_verified": false,
"line_number": 273,
"is_secret": false
}
],
"tests/app/main/views/test_accept_invite.py": [
{
"type": "Secret Keyword",
"filename": "tests/app/main/views/test_accept_invite.py",
"hashed_secret": "07f0a6c13923fc3b5f0c57ffa2d29b715eb80d71",
"is_verified": false,
"line_number": 607,
"is_secret": false
}
],
"tests/app/main/views/test_new_password.py": [
{
"type": "Secret Keyword",
"filename": "tests/app/main/views/test_new_password.py",
"hashed_secret": "a41d5c3bbcd0b39c627b9cbf4897c6d25efa694f",
"is_verified": false,
"line_number": 89,
"is_secret": false
}
],
"tests/app/main/views/test_register.py": [
{
"type": "Secret Keyword",
"filename": "tests/app/main/views/test_register.py",
"hashed_secret": "bdbb156d25d02fd7792865824201dda1c60f4473",
"is_verified": false,
"line_number": 116,
"is_secret": false
},
{
"type": "Secret Keyword",
"filename": "tests/app/main/views/test_register.py",
"hashed_secret": "5baa61e4c9b93f3f0682250b6cf8331b7ee68fd8",
"is_verified": false,
"line_number": 192,
"is_secret": false
},
{
"type": "Secret Keyword",
"filename": "tests/app/main/views/test_register.py",
"hashed_secret": "bb5b7caa27d005d38039e3797c3ddb9bcd22c3c8",
"is_verified": false,
"line_number": 260,
"is_secret": false
}
],
"tests/app/main/views/test_sign_in.py": [
{
"type": "Secret Keyword",
"filename": "tests/app/main/views/test_sign_in.py",
"hashed_secret": "8b8b69116ee882b5e987e330f55db81aba0636f9",
"is_verified": false,
"line_number": 90,
"is_secret": false
}
],
"tests/app/main/views/test_two_factor.py": [
{
"type": "Secret Keyword",
"filename": "tests/app/main/views/test_two_factor.py",
"hashed_secret": "dc66ad927c29e31c6c374231f57a4684b0687bfe",
"is_verified": false,
"line_number": 267,
"is_secret": false
}
],
"tests/app/main/views/test_user_profile.py": [
{
"type": "Secret Keyword",
"filename": "tests/app/main/views/test_user_profile.py",
"hashed_secret": "8072d7aad32964ec43fbcb699c75dc38890792f7",
"is_verified": false,
"line_number": 350,
"is_secret": false
},
{
"type": "Secret Keyword",
"filename": "tests/app/main/views/test_user_profile.py",
"hashed_secret": "4c9dbb972da179e4f66f023eaa5fb9451d835030",
"is_verified": false,
"line_number": 351,
"is_secret": false
}
],
"tests/app/main/views/test_verify.py": [
{
"type": "Secret Keyword",
"filename": "tests/app/main/views/test_verify.py",
"hashed_secret": "faafcfa63e128929409bf310b7ea5a415f2331ce",
"is_verified": false,
"line_number": 160,
"is_secret": false
}
],
"tests/app/notify_client/test_user_client.py": [
{
"type": "Secret Keyword",
"filename": "tests/app/notify_client/test_user_client.py",
"hashed_secret": "f2c57870308dc87f432e5912d4de6f8e322721ba",
"is_verified": false,
"line_number": 55,
"is_secret": false
}
],
"tests/app/test_cloudfoundry_config.py": [
{
"type": "Secret Keyword",
"filename": "tests/app/test_cloudfoundry_config.py",
"hashed_secret": "5e44dae2de8b6e57c797b968035265c9f2cd2b3e",
"is_verified": false,
"line_number": 12,
"is_secret": false
},
{
"type": "Secret Keyword",
"filename": "tests/app/test_cloudfoundry_config.py",
"hashed_secret": "e5e178db7317356946d13e5d2da037d39ac61c71",
"is_verified": false,
"line_number": 27,
"is_secret": false
}
],
"tests/conftest.py": [
{
"type": "Secret Keyword",
"filename": "tests/conftest.py",
"hashed_secret": "f8377c90fcfd699f0ddbdcb30c2c9183d2d933ea",
"is_verified": false,
"line_number": 3266,
"is_secret": false
}
],
"tests/notifications_utils/clients/antivirus/test_antivirus_client.py": [
{
"type": "Secret Keyword",
"filename": "tests/notifications_utils/clients/antivirus/test_antivirus_client.py",
"hashed_secret": "932b25270abe1301c22c709a19082dff07d469ff",
"is_verified": false,
"line_number": 16,
"is_secret": false
}
],
"tests/notifications_utils/clients/encryption/test_encryption_client.py": [
{
"type": "Secret Keyword",
"filename": "tests/notifications_utils/clients/encryption/test_encryption_client.py",
"hashed_secret": "f1e923a9667de11be6a210849a8651c1bfd81605",
"is_verified": false,
"line_number": 13,
"is_secret": false
}
],
"tests/notifications_utils/clients/zendesk/test_zendesk_client.py": [
{
"type": "Secret Keyword",
"filename": "tests/notifications_utils/clients/zendesk/test_zendesk_client.py",
"hashed_secret": "913a73b565c8e2c8ed94497580f619397709b8b6",
"is_verified": false,
"line_number": 16,
"is_secret": false
}
]
},
"generated_at": "2024-06-11T22:26:18Z"
}

View File

@@ -9,10 +9,10 @@ runs:
sudo apt-get update \
&& sudo apt-get install -y --no-install-recommends \
libcurl4-openssl-dev
- name: Set up Python 3.12
- name: Set up Python 3.12.3
uses: actions/setup-python@v4
with:
python-version: "3.12"
python-version: "3.12.3"
- name: Install poetry
shell: bash
run: pip install poetry

View File

@@ -1,79 +1,22 @@
<!--
Please follow the instructions found in this pull request template so that we
have all of the relevant details needed for our work.
At the minimum, please be sure to fill in all sections found below and also do
the following:
- Provide an appropriate and descriptive title for the pull request
- Link the pull request to its corresponding issue (must be done after creating
the pull request itself)
- Assign yourself as the author
- Attach the appropriate labels to it
- Set it to be on the Notify.gov project board
- Select one or more reviewers from the team or mark the pull request as a draft
depending on its current state
- If the pull request is a draft, please be sure to add reviewers once it is
ready for review and mark it ready for review
For each section, please delete the instructions/sample text (that includes this
text, though it is wrapped in an HTML comment just in case) and put in your own
information. Thank you!
Not sure what you should include or write in a pull request? Please read the
[pull request documentation in our docs!](https://github.com/GSA/notifications-api/blob/main/docs/all.md#pull-requests)
-->
*A note to PR reviewers: it may be helpful to review our
[code review documentation](https://github.com/GSA/notifications-api/blob/main/docs/all.md#code-reviews)
to know what to keep in mind while reviewing pull requests.*
*A note to PR reviewers: it may be helpful to review our [code review documentation](https://github.com/GSA/notifications-api/blob/main/docs/all.md#code-reviews) to know what to keep in mind while reviewing pull requests.*
## Description
Please enter a clear description about your proposed changes and what the
expected outcome(s) is/are from there. If there are complex implementation
details within the changes, this is a great place to explain those details using
plain language.
This should include:
- Links to issues that this PR addresses
- Screenshots or screen captures of any visible changes, especially for UI work
- Dependency changes
If there are any caveats, known issues, follow-up items, etc., make a quick note
of them here as well, though more details are probably warranted in the issue
itself in this case.
Please enter a detailed description here.
## TODO (optional)
If you're opening a draft PR, it might be helpful to list any outstanding work,
especially if you're asking folks to take a look before it's ready for full
review. In this case, create a small checklist with the outstanding items:
- [ ] TODO item 1
- [ ] TODO item 2
- [ ] TODO item ...
* [ ] TODO item 1
* [ ] TODO item 2
* [ ] TODO item ...
## Security Considerations
Please think about the security compliance aspect of your changes and what the
potential impacts might be.
**NOTE: Please be mindful of sharing sensitive information here! If you're not
sure of what to write, please ask the team first before writing anything here.**
Relevant details could include (and are not limited to) the following:
- Handling secrets/credential management (or specifically calling out that there
is nothing to handle)
- Any adjustments to the flow of data in and out the system, or even within it
- Connecting or disconnecting any external services to the application
- Handling of any sensitive information, such as PII
- Handling of information within log statements or other application monitoring
services/hooks
- The inclusion of a new external dependency or the removal of an existing one
- ... (anything else relevant from a security compliance perspective)
There are some cases where there are no security considerations to be had, e.g.,
updating our documentation with publicly available information. In those cases
it is fine to simply put something like this:
- None; this is a documentation update with publicly available information.
* Consideration 1
* Consideration 2
* Consideration ...

95
.github/workflows/codeql.yml vendored Normal file
View 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}}"

View File

@@ -18,11 +18,11 @@ jobs:
- name: Check for changes to Terraform
id: changed-terraform-files
uses: tj-actions/changed-files@v41.0.0
uses: tj-actions/changed-files@v44
with:
files: |
terraform/demo
terraform/shared
terraform/demo/**
terraform/shared/**
.github/workflows/deploy-demo.yml
- name: Terraform init
if: steps.changed-terraform-files.outputs.any_changed == 'true'
@@ -88,7 +88,7 @@ jobs:
- name: Check for changes to egress config
id: changed-egress-config
uses: tj-actions/changed-files@v41.0.0
uses: tj-actions/changed-files@v44
with:
files: |
deploy-config/egress_proxy/notify-admin-demo.*.acl

View File

@@ -18,11 +18,11 @@ jobs:
- name: Check for changes to Terraform
id: changed-terraform-files
uses: tj-actions/changed-files@v41.0.0
uses: tj-actions/changed-files@v44
with:
files: |
terraform/production
terraform/shared
terraform/production/**
terraform/shared/**
.github/workflows/deploy-prod.yml
- name: Terraform init
if: steps.changed-terraform-files.outputs.any_changed == 'true'
@@ -88,7 +88,7 @@ jobs:
- name: Check for changes to egress config
id: changed-egress-config
uses: tj-actions/changed-files@v41.0.0
uses: tj-actions/changed-files@v44
with:
files: |
deploy-config/egress_proxy/notify-admin-production.*.acl

View File

@@ -17,96 +17,96 @@ jobs:
environment: staging
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 2
- uses: actions/checkout@v4
with:
fetch-depth: 2
- name: Check for changes to Terraform
id: changed-terraform-files
uses: tj-actions/changed-files@v41.0.0
with:
files: |
terraform/staging
terraform/shared
.github/workflows/deploy.yml
- name: Terraform init
if: steps.changed-terraform-files.outputs.any_changed == 'true'
working-directory: terraform/staging
env:
AWS_ACCESS_KEY_ID: ${{ secrets.TERRAFORM_STATE_ACCESS_KEY }}
AWS_SECRET_ACCESS_KEY: ${{ secrets.TERRAFORM_STATE_SECRET_ACCESS_KEY }}
run: terraform init
- name: Terraform apply
if: steps.changed-terraform-files.outputs.any_changed == 'true'
working-directory: terraform/staging
env:
AWS_ACCESS_KEY_ID: ${{ secrets.TERRAFORM_STATE_ACCESS_KEY }}
AWS_SECRET_ACCESS_KEY: ${{ secrets.TERRAFORM_STATE_SECRET_ACCESS_KEY }}
TF_VAR_cf_user: ${{ secrets.CLOUDGOV_USERNAME }}
TF_VAR_cf_password: ${{ secrets.CLOUDGOV_PASSWORD }}
run: terraform apply -auto-approve -input=false
- name: Check for changes to Terraform
id: changed-terraform-files
uses: tj-actions/changed-files@v44
with:
files: |
terraform/staging/**
terraform/shared/**
.github/workflows/deploy.yml
- name: Terraform init
if: steps.changed-terraform-files.outputs.any_changed == 'true'
working-directory: terraform/staging
env:
AWS_ACCESS_KEY_ID: ${{ secrets.TERRAFORM_STATE_ACCESS_KEY }}
AWS_SECRET_ACCESS_KEY: ${{ secrets.TERRAFORM_STATE_SECRET_ACCESS_KEY }}
run: terraform init
- name: Terraform apply
if: steps.changed-terraform-files.outputs.any_changed == 'true'
working-directory: terraform/staging
env:
AWS_ACCESS_KEY_ID: ${{ secrets.TERRAFORM_STATE_ACCESS_KEY }}
AWS_SECRET_ACCESS_KEY: ${{ secrets.TERRAFORM_STATE_SECRET_ACCESS_KEY }}
TF_VAR_cf_user: ${{ secrets.CLOUDGOV_USERNAME }}
TF_VAR_cf_password: ${{ secrets.CLOUDGOV_PASSWORD }}
run: terraform apply -auto-approve -input=false
- uses: ./.github/actions/setup-project
- uses: ./.github/actions/setup-project
- name: Create requirements.txt
run: poetry export --without-hashes --format=requirements.txt > requirements.txt
- name: Create requirements.txt
run: poetry export --without-hashes --format=requirements.txt > requirements.txt
- name: Deploy to cloud.gov
uses: 18f/cg-deploy-action@main
env:
DANGEROUS_SALT: ${{ secrets.DANGEROUS_SALT }}
SECRET_KEY: ${{ secrets.SECRET_KEY }}
ADMIN_CLIENT_SECRET: ${{ secrets.ADMIN_CLIENT_SECRET }}
NEW_RELIC_LICENSE_KEY: ${{ secrets.NEW_RELIC_LICENSE_KEY }}
NR_BROWSER_KEY: ${{ secrets.NR_BROWSER_KEY }}
COMMIT_HASH: ${{ github.sha }}
LOGIN_PEM: ${{ secrets.LOGIN_PEM }}
LOGIN_DOT_GOV_CLIENT_ID: "urn:gov:gsa:openidconnect.profiles:sp:sso:gsa:notify-gov"
LOGIN_DOT_GOV_USER_INFO_URL: "https://secure.login.gov/api/openid_connect/userinfo"
LOGIN_DOT_GOV_ACCESS_TOKEN_URL: "https://secure.login.gov/api/openid_connect/token"
LOGIN_DOT_GOV_LOGOUT_URL: "https://secure.login.gov/openid_connect/logout?client_id=urn:gov:gsa:openidconnect.profiles:sp:sso:gsa:notify-gov&post_logout_redirect_uri=https://notify-staging.app.cloud.gov/sign-out"
LOGIN_DOT_GOV_BASE_LOGOUT_URL: "https://secure.login.gov/openid_connect/logout?"
LOGIN_DOT_GOV_SIGNOUT_REDIRECT: "https://notify-staging.app.cloud.gov/sign-out"
LOGIN_DOT_GOV_INITIAL_SIGNIN_URL: "https://secure.login.gov/openid_connect/authorize?acr_values=http%3A%2F%2Fidmanagement.gov%2Fns%2Fassurance%2Fial%2F1&client_id=urn:gov:gsa:openidconnect.profiles:sp:sso:gsa:notify-gov&nonce=NONCE&prompt=select_account&redirect_uri=https://notify-staging.app.cloud.gov/sign-in&response_type=code&scope=openid+email&state=STATEE"
with:
cf_username: ${{ secrets.CLOUDGOV_USERNAME }}
cf_password: ${{ secrets.CLOUDGOV_PASSWORD }}
cf_org: gsa-tts-benefits-studio
cf_space: notify-staging
push_arguments: >-
--vars-file deploy-config/staging.yml
--var DANGEROUS_SALT="$DANGEROUS_SALT"
--var SECRET_KEY="$SECRET_KEY"
--var ADMIN_CLIENT_USERNAME="notify-admin"
--var ADMIN_CLIENT_SECRET="$ADMIN_CLIENT_SECRET"
--var NEW_RELIC_LICENSE_KEY="$NEW_RELIC_LICENSE_KEY"
--var NR_BROWSER_KEY="$NR_BROWSER_KEY"
--var COMMIT_HASH="$COMMIT_HASH"
--var LOGIN_PEM="$LOGIN_PEM"
--var LOGIN_DOT_GOV_CLIENT_ID="$LOGIN_DOT_GOV_CLIENT_ID"
--var LOGIN_DOT_GOV_USER_INFO_URL="$LOGIN_DOT_GOV_USER_INFO_URL"
--var LOGIN_DOT_GOV_ACCESS_TOKEN_URL="$LOGIN_DOT_GOV_ACCESS_TOKEN_URL"
--var LOGIN_DOT_GOV_LOGOUT_URL="$LOGIN_DOT_GOV_LOGOUT_URL"
--var LOGIN_DOT_GOV_BASE_LOGOUT_URL="$LOGIN_DOT_GOV_BASE_LOGOUT_URL"
--var LOGIN_DOT_GOV_SIGNOUT_REDIRECT="$LOGIN_DOT_GOV_SIGNOUT_REDIRECT"
--var LOGIN_DOT_GOV_INITIAL_SIGNIN_URL="$LOGIN_DOT_GOV_INITIAL_SIGNIN_URL"
- name: Deploy to cloud.gov
uses: 18f/cg-deploy-action@main
env:
DANGEROUS_SALT: ${{ secrets.DANGEROUS_SALT }}
SECRET_KEY: ${{ secrets.SECRET_KEY }}
ADMIN_CLIENT_SECRET: ${{ secrets.ADMIN_CLIENT_SECRET }}
NEW_RELIC_LICENSE_KEY: ${{ secrets.NEW_RELIC_LICENSE_KEY }}
NR_BROWSER_KEY: ${{ secrets.NR_BROWSER_KEY }}
COMMIT_HASH: ${{ github.sha }}
LOGIN_PEM: ${{ secrets.LOGIN_PEM }}
LOGIN_DOT_GOV_CLIENT_ID: "urn:gov:gsa:openidconnect.profiles:sp:sso:gsa:notify-gov"
LOGIN_DOT_GOV_USER_INFO_URL: "https://secure.login.gov/api/openid_connect/userinfo"
LOGIN_DOT_GOV_ACCESS_TOKEN_URL: "https://secure.login.gov/api/openid_connect/token"
LOGIN_DOT_GOV_LOGOUT_URL: "https://secure.login.gov/openid_connect/logout?client_id=urn:gov:gsa:openidconnect.profiles:sp:sso:gsa:notify-gov&post_logout_redirect_uri=https://notify-staging.app.cloud.gov/sign-out"
LOGIN_DOT_GOV_BASE_LOGOUT_URL: "https://secure.login.gov/openid_connect/logout?"
LOGIN_DOT_GOV_SIGNOUT_REDIRECT: "https://notify-staging.app.cloud.gov/sign-out"
LOGIN_DOT_GOV_INITIAL_SIGNIN_URL: "https://secure.login.gov/openid_connect/authorize?acr_values=http%3A%2F%2Fidmanagement.gov%2Fns%2Fassurance%2Fial%2F1&client_id=urn:gov:gsa:openidconnect.profiles:sp:sso:gsa:notify-gov&nonce=NONCE&prompt=select_account&redirect_uri=https://notify-staging.app.cloud.gov/sign-in&response_type=code&scope=openid+email&state=STATEE"
with:
cf_username: ${{ secrets.CLOUDGOV_USERNAME }}
cf_password: ${{ secrets.CLOUDGOV_PASSWORD }}
cf_org: gsa-tts-benefits-studio
cf_space: notify-staging
push_arguments: >-
--vars-file deploy-config/staging.yml
--var DANGEROUS_SALT="$DANGEROUS_SALT"
--var SECRET_KEY="$SECRET_KEY"
--var ADMIN_CLIENT_USERNAME="notify-admin"
--var ADMIN_CLIENT_SECRET="$ADMIN_CLIENT_SECRET"
--var NEW_RELIC_LICENSE_KEY="$NEW_RELIC_LICENSE_KEY"
--var NR_BROWSER_KEY="$NR_BROWSER_KEY"
--var COMMIT_HASH="$COMMIT_HASH"
--var LOGIN_PEM="$LOGIN_PEM"
--var LOGIN_DOT_GOV_CLIENT_ID="$LOGIN_DOT_GOV_CLIENT_ID"
--var LOGIN_DOT_GOV_USER_INFO_URL="$LOGIN_DOT_GOV_USER_INFO_URL"
--var LOGIN_DOT_GOV_ACCESS_TOKEN_URL="$LOGIN_DOT_GOV_ACCESS_TOKEN_URL"
--var LOGIN_DOT_GOV_LOGOUT_URL="$LOGIN_DOT_GOV_LOGOUT_URL"
--var LOGIN_DOT_GOV_BASE_LOGOUT_URL="$LOGIN_DOT_GOV_BASE_LOGOUT_URL"
--var LOGIN_DOT_GOV_SIGNOUT_REDIRECT="$LOGIN_DOT_GOV_SIGNOUT_REDIRECT"
--var LOGIN_DOT_GOV_INITIAL_SIGNIN_URL="$LOGIN_DOT_GOV_INITIAL_SIGNIN_URL"
- name: Check for changes to egress config
id: changed-egress-config
uses: tj-actions/changed-files@v41.0.0
with:
files: |
deploy-config/egress_proxy/notify-admin-staging.*.acl
.github/actions/deploy-proxy/action.yml
.github/workflows/deploy.yml
- name: Deploy egress proxy
if: steps.changed-egress-config.outputs.any_changed == 'true'
uses: ./.github/actions/deploy-proxy
with:
cf_space: notify-staging
app: notify-admin-staging
- name: Check for changes to egress config
id: changed-egress-config
uses: tj-actions/changed-files@v44
with:
files: |
deploy-config/egress_proxy/notify-admin-staging.*.acl
.github/actions/deploy-proxy/action.yml
.github/workflows/deploy.yml
- name: Deploy egress proxy
if: steps.changed-egress-config.outputs.any_changed == 'true'
uses: ./.github/actions/deploy-proxy
with:
cf_space: notify-staging
app: notify-admin-staging
bail:
runs-on: ubuntu-latest

2
.nvmrc
View File

@@ -1 +1 @@
16.15.1
22.3.0

View File

@@ -2,7 +2,7 @@
# See https://pre-commit.com/hooks.html for more hooks
repos:
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v3.2.0
rev: v4.6.0
hooks:
- id: trailing-whitespace
- id: end-of-file-fixer
@@ -11,3 +11,14 @@ repos:
- id: debug-statements
- id: check-merge-conflict
- id: check-toml
- id: check-ast
- id: fix-byte-order-marker
- id: detect-aws-credentials
args: [--allow-missing-credentials]
- id: detect-private-key
- id: mixed-line-ending
- repo: https://github.com/Yelp/detect-secrets
rev: v1.5.0
hooks:
- id: detect-secrets
args: ['--baseline', '.ds.baseline']

View File

@@ -21,7 +21,8 @@ bootstrap: generate-version-file ## Set up everything to run the app
poetry install --sync --no-root
poetry run playwright install --with-deps
poetry run pre-commit install
source $(NVMSH) --no-use && nvm install && npm ci --no-audit
source $(NVMSH) --no-use && nvm install && npm install
source $(NVMSH) && npm ci --no-audit
source $(NVMSH) && npm run build
.PHONY: watch-frontend
@@ -40,6 +41,10 @@ run-flask-bare: ## Run flask without invoking poetry so we can override ENV var
npm-audit: ## Check for vulnerabilities in NPM packages
source $(NVMSH) && npm run audit
.PHONY: npm-audit-fix
npm-audit-fix: ## Fix vulnerabilities that do not require attentino (according to npm)
source $(NVMSH) && npm audit fix
.PHONY: help
help:
@cat $(MAKEFILE_LIST) | grep -E '^[a-zA-Z_-]+:.*?## .*$$' | sort | awk 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-30s\033[0m %s\n", $$1, $$2}'
@@ -71,13 +76,14 @@ too-complex:
.PHONY: py-test
py-test: export NEW_RELIC_ENVIRONMENT=test
py-test: ## Run python unit tests
poetry run coverage run --omit=*/notifications_utils/* -m pytest --maxfail=10 --ignore=tests/end_to_end tests/
poetry run coverage run -m pytest --maxfail=10 --ignore=tests/end_to_end tests/
poetry run coverage report --fail-under=96
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
@@ -101,13 +107,6 @@ py-lock: ## Syncs dependencies and updates lock file without performing recursiv
poetry lock --no-update
poetry install --sync
.PHONY: update-utils
update-utils: ## Forces Poetry to pull the latest changes from the notifications-utils repo; requires that you commit the changes to poetry.lock!
poetry update notifications-utils
@echo
@echo !!! PLEASE MAKE SURE TO COMMIT AND PUSH THE UPDATED poetry.lock FILE !!!
@echo
.PHONY: freeze-requirements
freeze-requirements: ## create static requirements.txt
poetry export --without-hashes --format=requirements.txt > requirements.txt

View File

@@ -19,7 +19,6 @@ UI's backend and is required for most things to function. Set that up first!
Our other repositories are:
- [notifications-admin](https://github.com/GSA/notifications-admin)
- [notifications-utils](https://github.com/GSA/notifications-utils)
- [us-notify-compliance](https://github.com/GSA/us-notify-compliance/)
- [notify-python-demo](https://github.com/GSA/notify-python-demo)
@@ -41,7 +40,7 @@ You will need the following items:
[Follow the instructions here to set up the Notify.gov API.](https://github.com/GSA/notifications-api#before-you-start)
The Notify.gov API is required in order for the Notify.gov Admin UI to run, and
it will also take care of many of the steps that are listed here. The sections
it will also take care of many of the steps that are listed here. The sections
that are a repeat from the API setup are flagged with an **[API Step]** label
in front of them.
@@ -49,7 +48,7 @@ in front of them.
This project is set up to work with
[nvm (Node Version Manager)](https://github.com/nvm-sh/nvm#installing-and-updating)
for managing and using Node.js (version 16.15.1) and the `npm` package manager.
for managing and using Node.js (version 22.3.0) and the `npm` package manager.
These instructions will walk you through how to set your machine up with all of
the required tools for this project.
@@ -84,11 +83,13 @@ Your system `$PATH` environment variable is likely set in one of these
locations:
For BASH shells:
- `~/.bashrc`
- `~/.bash_profile`
- `~/.profile`
For ZSH shells:
- `~/.zshrc`
- `~/.zprofile`
@@ -98,7 +99,7 @@ environments.
Which file you need to modify depends on whether or not you are running an
interactive shell or a login shell
(see [this Stack Overflow post](https://stackoverflow.com/questions/18186929/what-are-the-differences-between-a-login-shell-and-interactive-shell)
for an explanation of the differences). If you're still not sure, please ask
for an explanation of the differences). If you're still not sure, please ask
the team for help!
Once you determine which file you'll need to modify, add these lines before any
@@ -159,7 +160,7 @@ _NOTE: This project currently uses the latest `1.4.x release of Terraform._
#### [API Step] Python Installation
Now we're going to install a tool to help us manage Python versions and
virtual environments on our system. First, we'll install
virtual environments on our system. First, we'll install
[pyenv](https://github.com/pyenv/pyenv) and one of its plugins,
[pyenv-virtualenv](https://github.com/pyenv/pyenv-virtualenv), with Homebrew:
@@ -286,7 +287,7 @@ we'll use `3.12` in our example here since we recently upgraded to this version:
pyenv install 3.12
```
Next, delete the virtual environment you previously had set up. If you followed
Next, delete the virtual environment you previously had set up. If you followed
the instructions above with the first-time set up, you can do this with `pyenv`:
```sh
@@ -307,6 +308,20 @@ you'll be set with an upgraded version of Python.
_If you're not sure about the details of your current virtual environment, you can run `poetry env info` to get more information. If you've been using `pyenv` for everything, you can also see all available virtual environments with `pyenv virtualenvs`._
#### Updating the .env file for Login.gov
To configure the application for Login.gov, you will need to update the following environment variables in the .env file:
```
COMMIT_HASH=”--------”
```
Reach out to someone on the team to get the most recent Login.gov key.
```
LOGIN_PEM="INSERT_LOGIN_GOV_KEY_HERE"
```
#### Updating the .env file for E2E tests
With the newly created `.env` file in place, you'll need to make one more
@@ -354,7 +369,7 @@ API is running as well!
## Creating a 'First User' in the database
After you have completed all setup steps, you will be unable to log in, because there
will not be a user in the database to link to the login.gov account you are using. So
will not be a user in the database to link to the login.gov account you are using. So
you will need to create that user in your database using the 'create-test-user' command.
Open two terminals pointing to the api project and then run these commands in the
@@ -373,8 +388,6 @@ is the same one you are using in login.gov and make sure your phone number is in
If for any reason in the course of development it is necessary for your to delete your db
via the `dropdb` command, you will need to repeat these steps when you recreate your db.
## Git Hooks
We're using [`pre-commit`](https://pre-commit.com/) to manage hooks in order to
@@ -420,23 +433,6 @@ In either situation, once you are finished and have verified the dependency
changes are working, please be sure to commit both the `pyproject.toml` and
`poetry.lock` files.
### Keeping the notification-utils Dependency Up-to-Date
The `notifications-utils` dependency references the other repository we have at
https://github.com/GSA/notifications-utils - this dependency requires a bit of
extra legwork to ensure it stays up-to-date.
Whenever a PR is merged in the `notifications-utils` repository, we need to make
sure the changes are pulled in here and committed to this repository as well.
You can do this by going through these steps:
- Make sure your local `main` branch is up-to-date
- Create a new branch to work in
- Run `make update-utils`
- Commit the updated `poetry.lock` file and push the changes
- Make a new PR with the change
- Have the PR get reviewed and merged
## Known Installation Issues
### Python Installation Errors

View File

@@ -18,17 +18,12 @@ from flask import (
)
from flask.globals import request_ctx
from flask_login import LoginManager, current_user
from flask_socketio import SocketIO
from flask_talisman import Talisman
from flask_wtf import CSRFProtect
from flask_wtf.csrf import CSRFError
from itsdangerous import BadSignature
from notifications_python_client.errors import HTTPError
from notifications_utils import logging, request_helper
from notifications_utils.formatters import (
formatted_list,
get_lines_with_normalised_whitespace,
)
from notifications_utils.recipients import format_phone_number_human_readable
from werkzeug.exceptions import HTTPException as WerkzeugHTTPException
from werkzeug.exceptions import abort
from werkzeug.local import LocalProxy
@@ -36,7 +31,7 @@ from werkzeug.local import LocalProxy
from app import proxy_fix
from app.asset_fingerprinter import asset_fingerprinter
from app.config import configs
from app.extensions import redis_client, zendesk_client
from app.extensions import redis_client
from app.formatters import (
convert_markdown_template,
convert_to_boolean,
@@ -114,10 +109,17 @@ from app.notify_client.upload_api_client import upload_api_client
from app.notify_client.user_api_client import user_api_client
from app.url_converters import SimpleDateTypeConverter, TemplateTypeConverter
from app.utils.govuk_frontend_jinja.flask_ext import init_govuk_frontend
from notifications_utils import logging, request_helper
from notifications_utils.formatters import (
formatted_list,
get_lines_with_normalised_whitespace,
)
from notifications_utils.recipients import format_phone_number_human_readable
login_manager = LoginManager()
csrf = CSRFProtect()
talisman = Talisman()
socketio = SocketIO()
# The current service attached to the request stack.
@@ -175,6 +177,7 @@ def create_app(application):
init_govuk_frontend(application)
init_jinja(application)
socketio.init_app(application)
for client in (
csrf,
@@ -202,7 +205,6 @@ def create_app(application):
user_api_client,
# External API clients
redis_client,
zendesk_client,
):
client.init_app(application)
@@ -231,6 +233,24 @@ def create_app(application):
)
logging.init_app(application)
# Hopefully will help identify if there is a race condition causing the CSRF errors
# that we have occasionally seen in our environments.
for key in ("SECRET_KEY", "DANGEROUS_SALT"):
try:
value = application.config[key]
except KeyError:
application.logger.error(f"Env Var {key} doesn't exist.")
else:
try:
data_len = len(value.strip())
except (TypeError, AttributeError):
application.logger.error(f"Env Var {key} invalid type: {type(value)}")
else:
if data_len:
application.logger.info(f"Env Var {key} is a non-zero length.")
else:
application.logger.error(f"Env Var {key} is empty.")
login_manager.login_view = "main.sign_in"
login_manager.login_message_category = "default"
login_manager.session_protection = None

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 292.1 39.47"><path fill="#fff" d="M7.44 6.89h17.2v22.06H7.44z"/><path fill="#112e51" d="M53.45 5.28h-6.16v27.31h17.87v-5.38H53.45V5.28zm30.76-.19c-8.35 0-14.67 5.89-14.67 13.85s6.32 13.93 14.67 13.93 14.67-6 14.67-13.93S92.56 5.09 84.21 5.09zm.08 22.39a8.49 8.49 0 0 1-8.43-8.54 8.4 8.4 0 0 1 8.43-8.47 8.29 8.29 0 0 1 8.27 8.47 8.36 8.36 0 0 1-8.27 8.54zm35.36-17.05a11.43 11.43 0 0 1 7.49 3l3.47-4.41a17.21 17.21 0 0 0-11-4c-8.51 0-14.9 6-14.9 13.85s6.29 13.99 14.58 13.99a20.21 20.21 0 0 0 11.08-3.55V17.57h-10.11v4.82H125V26a12.42 12.42 0 0 1-5.34 1.48 8.52 8.52 0 1 1 0-17zm19.61-5.15h6.17v27.31h-6.17zm34.53 17.56L160.52 5.28h-5.61v27.31h5.89V15.07l13.22 17.52h5.62V5.28h-5.85v17.56zm17.47 2.85a3.39 3.39 0 0 0-3.47 3.56 3.47 3.47 0 1 0 6.94 0 3.36 3.36 0 0 0-3.47-3.56zm23.15-15.26a11.43 11.43 0 0 1 7.49 3l3.47-4.41a17.21 17.21 0 0 0-11-4c-8.51 0-14.9 6-14.9 13.85s6.28 13.93 14.55 13.93a20.21 20.21 0 0 0 11.08-3.55V17.57H215v4.82h4.77V26a12.42 12.42 0 0 1-5.34 1.48 8.52 8.52 0 1 1 0-17zm32.23-5.34C238.29 5.09 232 11 232 18.94s6.32 13.93 14.67 13.93 14.67-6 14.67-13.93S255 5.09 246.64 5.09zm.08 22.39a8.49 8.49 0 0 1-8.43-8.54 8.4 8.4 0 0 1 8.43-8.47 8.29 8.29 0 0 1 8.28 8.47 8.36 8.36 0 0 1-8.28 8.54zm39.02-22.2-7.49 20.6-7.45-20.6h-6.63l10.73 27.31h6.36L292.1 5.28h-6.36z"/><path fill="#e21d3e" d="m11.38 25.34 1.45-8.28a4.39 4.39 0 0 1 2.71-7.86V0H4.34A4.34 4.34 0 0 0 0 4.34v21.28a2.62 2.62 0 0 0 .26 1.16c.94 1.9 4.65 8 15.27 12.69V25.7a24.3 24.3 0 0 1-4.15-.36z"/><path fill="#b51e23" d="M26.72 0H15.53v9.19A4.39 4.39 0 0 1 18.28 17l1.45 8.32a24.29 24.29 0 0 1-4.2.36v13.79C26.14 34.8 29.86 28.68 30.8 26.79a2.62 2.62 0 0 0 .26-1.16V4.34A4.34 4.34 0 0 0 26.72 0z"/></svg>

After

Width:  |  Height:  |  Size: 1.7 KiB

View File

@@ -0,0 +1,67 @@
(function (window) {
function initializeChartAndSocket() {
var ctx = document.getElementById('myChart');
if (!ctx) {
return;
}
var myBarChart = new Chart(ctx.getContext('2d'), {
type: 'bar',
data: {
labels: [],
datasets: [
{
label: 'Delivered',
data: [],
backgroundColor: '#0076d6',
stack: 'Stack 0'
},
]
},
options: {
scales: {
y: {
beginAtZero: true
}
}
}
});
var socket = io();
socket.on('connect', function() {
socket.emit('fetch_daily_stats_by_user');
});
socket.on('daily_stats_by_user_update', function(data) {
// console.log('Data received:', data);
var labels = [];
var deliveredData = [];
for (var date in data) {
labels.push(date);
deliveredData.push(data[date].sms.delivered);
}
myBarChart.data.labels = labels;
myBarChart.data.datasets[0].data = deliveredData;
myBarChart.update();
});
socket.on('error', function(data) {
// console.log('Error:', data);
});
var sevenDaysButton = document.getElementById('sevenDaysButton');
if (sevenDaysButton) {
sevenDaysButton.addEventListener('click', function() {
socket.emit('fetch_daily_stats_by_user');
// console.log('clicked');
});
}
}
document.addEventListener('DOMContentLoaded', initializeChartAndSocket);
})(window);

View File

@@ -66,6 +66,10 @@ h2.sms-message-header {
margin-bottom: 0.5rem;
}
.usa-prose >*+ h2.message-header {
margin-top: 1em;
}
h2.recipient-list {
margin-bottom: 0.5rem;
}

View File

@@ -157,6 +157,18 @@ td.table-empty-message {
}
}
.usa-button img {
margin-left: .5rem;
height: 1rem;
}
.usa-button.login-button.login-button--primary,.login-button.login-button--primary:hover{
color:#112e51;background-color:#fff;
border:1px solid #767676;
display: inline-flex;
justify-content: center;
}
.user-list-edit-link:active:before,
.user-list-edit-link:focus:before {
box-shadow: none;

View File

@@ -2,9 +2,9 @@ import json
from os import getenv
import newrelic.agent
from notifications_utils import DAILY_MESSAGE_LIMIT
from app.cloudfoundry_config import cloud_config
from notifications_utils import DAILY_MESSAGE_LIMIT
class Config(object):
@@ -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")
@@ -53,7 +53,13 @@ class Config(object):
PERMANENT_SESSION_LIFETIME = 1800 # 30 Minutes
SEND_FILE_MAX_AGE_DEFAULT = 365 * 24 * 60 * 60 # 1 year
REPLY_TO_EMAIL_ADDRESS_VALIDATION_TIMEOUT = 45
ACTIVITY_STATS_LIMIT_DAYS = 7
ACTIVITY_STATS_LIMIT_DAYS = {
"today": 0,
"one_day": 1,
"three_day": 3,
"five_day": 5,
"seven_day": 7,
}
SESSION_COOKIE_HTTPONLY = True
SESSION_COOKIE_NAME = "notify_admin_session"
SESSION_COOKIE_SECURE = True

View File

@@ -1,5 +1,3 @@
from notifications_utils.clients.redis.redis_client import RedisClient
from notifications_utils.clients.zendesk.zendesk_client import ZendeskClient
zendesk_client = ZendeskClient()
redis_client = RedisClient()

View File

@@ -16,15 +16,15 @@ from bs4 import BeautifulSoup
from flask import render_template_string, url_for
from flask.helpers import get_root_path
from markupsafe import Markup
from app.utils.csv import get_user_preferred_timezone
from app.utils.time import parse_naive_dt
from notifications_utils.field import Field
from notifications_utils.formatters import make_quotes_smart
from notifications_utils.formatters import nl2br as utils_nl2br
from notifications_utils.recipients import InvalidPhoneError, validate_phone_number
from notifications_utils.take import Take
from app.utils.csv import get_user_preferred_timezone
from app.utils.time import parse_naive_dt
def apply_html_class(tags, html_file):
new_html = html_file

View File

@@ -11,9 +11,6 @@ from flask_wtf.file import FileAllowed
from flask_wtf.file import FileField as FileField_wtf
from flask_wtf.file import FileSize
from markupsafe import Markup
from notifications_utils.formatters import strip_all_whitespace
from notifications_utils.insensitive_dict import InsensitiveDict
from notifications_utils.recipients import InvalidPhoneError, validate_phone_number
from werkzeug.utils import cached_property
from wtforms import (
BooleanField,
@@ -66,6 +63,9 @@ from app.models.organization import Organization
from app.utils import merge_jsonlike
from app.utils.csv import get_user_preferred_timezone
from app.utils.user_permissions import all_ui_permissions, permission_options
from notifications_utils.formatters import strip_all_whitespace
from notifications_utils.insensitive_dict import InsensitiveDict
from notifications_utils.recipients import InvalidPhoneError, validate_phone_number
def get_time_value_and_label(future_time):

View File

@@ -1,15 +1,15 @@
import re
from abc import ABC, abstractmethod
from notifications_utils.field import Field
from notifications_utils.formatters import formatted_list
from notifications_utils.recipients import InvalidEmailError, validate_email_address
from notifications_utils.sanitise_text import SanitiseSMS
from wtforms import ValidationError
from app.main._commonly_used_passwords import commonly_used_passwords
from app.models.spreadsheet import Spreadsheet
from app.utils.user import is_gov_user
from notifications_utils.field import Field
from notifications_utils.formatters import formatted_list
from notifications_utils.recipients import InvalidEmailError, validate_email_address
from notifications_utils.sanitise_text import SanitiseSMS
class CommonlyUsedPassword:

View File

@@ -1,14 +1,14 @@
from flask import jsonify, redirect, render_template, session, url_for
from flask_login import current_user
from notifications_python_client.errors import HTTPError
from notifications_utils.recipients import format_phone_number_human_readable
from notifications_utils.template import SMSPreviewTemplate
from app import current_service, notification_api_client, service_api_client
from app.main import main
from app.main.forms import SearchByNameForm
from app.models.template_list import TemplateList
from app.utils.user import user_has_permissions
from notifications_utils.recipients import format_phone_number_human_readable
from notifications_utils.template import SMSPreviewTemplate
@main.route("/services/<uuid:service_id>/conversation/<uuid:notification_id>")

View File

@@ -6,7 +6,7 @@ from itertools import groupby
from flask import Response, abort, jsonify, render_template, request, session, url_for
from flask_login import current_user
from notifications_utils.recipients import format_phone_number_human_readable
from flask_socketio import emit
from werkzeug.utils import redirect
from app import (
@@ -15,6 +15,7 @@ from app import (
job_api_client,
notification_api_client,
service_api_client,
socketio,
template_statistics_client,
)
from app.formatters import format_date_numeric, format_datetime_numeric, get_time_left
@@ -30,6 +31,39 @@ from app.utils.csv import Spreadsheet
from app.utils.pagination import generate_next_dict, generate_previous_dict
from app.utils.time import get_current_financial_year
from app.utils.user import user_has_permissions
from notifications_utils.recipients import format_phone_number_human_readable
@socketio.on("fetch_daily_stats")
def handle_fetch_daily_stats():
service_id = session.get('service_id')
if service_id:
date_range = get_stats_date_range()
daily_stats = service_api_client.get_service_notification_statistics_by_day(
service_id, start_date=date_range["start_date"], days=date_range["days"]
)
emit("daily_stats_update", daily_stats)
else:
emit("error", {"error": "No service_id provided"})
@socketio.on("fetch_daily_stats_by_user")
def handle_fetch_daily_stats_by_user():
service_id = session.get("service_id")
user_id = session.get("user_id")
if service_id and user_id:
date_range = get_stats_date_range()
daily_stats_by_user = (
service_api_client.get_user_service_notification_statistics_by_day(
service_id,
user_id,
start_date=date_range["start_date"],
days=date_range["days"],
)
)
emit("daily_stats_by_user_update", daily_stats_by_user)
else:
emit("error", {"error": "No service_id or user_id provided"})
@main.route("/services/<uuid:service_id>/dashboard")
@@ -434,6 +468,24 @@ def get_months_for_financial_year(year, time_format="%B"):
return [month.strftime(time_format) for month in (get_months_for_year(1, 13, year))]
def get_current_month_for_financial_year(year):
current_month = datetime.now().month
return current_month
def get_stats_date_range():
current_financial_year = get_current_financial_year()
current_month = get_current_month_for_financial_year(current_financial_year)
start_date = datetime.now().strftime("%Y-%m-%d")
days = 7
return {
"current_financial_year": current_financial_year,
"current_month": current_month,
"start_date": start_date,
"days": days,
}
def get_months_for_year(start, end, year):
return [datetime(year, month, 1) for month in range(start, end)]

View File

@@ -1,6 +1,6 @@
import os
from flask import abort, redirect, render_template, request, url_for
from flask import abort, current_app, redirect, render_template, request, url_for
from flask_login import current_user
from app import status_api_client
@@ -9,20 +9,28 @@ from app.main import main
from app.main.views.pricing import CURRENT_SMS_RATE
from app.main.views.sub_navigation_dictionaries import features_nav, using_notify_nav
from app.utils.user import user_is_logged_in
login_dot_gov_url = os.getenv("LOGIN_DOT_GOV_INITIAL_SIGNIN_URL")
from notifications_utils.url_safe_token import generate_token
@main.route("/")
def index():
if current_user and current_user.is_authenticated:
return redirect(url_for("main.choose_account"))
token = generate_token(
str(request.remote_addr),
current_app.config["SECRET_KEY"],
current_app.config["DANGEROUS_SALT"],
)
url = os.getenv("LOGIN_DOT_GOV_INITIAL_SIGNIN_URL")
# handle unit tests
if url is not None:
url = url.replace("NONCE", token)
url = url.replace("STATE", token)
return render_template(
"views/signedout.html",
sms_rate=CURRENT_SMS_RATE,
counts=status_api_client.get_count_of_live_services_and_organizations(),
login_dot_gov_url=login_dot_gov_url,
initial_signin_url=url,
)

View File

@@ -15,7 +15,6 @@ from flask import (
)
from flask_login import current_user
from markupsafe import Markup
from notifications_utils.template import EmailPreviewTemplate, SMSBodyPreviewTemplate
from app import (
current_service,
@@ -35,6 +34,7 @@ from app.utils.pagination import (
get_page_from_request,
)
from app.utils.user import user_has_permissions
from notifications_utils.template import EmailPreviewTemplate, SMSBodyPreviewTemplate
@main.route("/services/<uuid:service_id>/jobs")
@@ -143,11 +143,40 @@ def view_notifications(service_id, message_type=None):
True: ["reference"],
False: [],
}.get(bool(current_service.api_keys)),
download_link=url_for(
download_link_one_day=url_for(
".download_notifications_csv",
service_id=current_service.id,
message_type=message_type,
status=request.args.get("status"),
number_of_days="one_day",
),
download_link_today=url_for(
".download_notifications_csv",
service_id=current_service.id,
message_type=message_type,
status=request.args.get("status"),
number_of_days="today",
),
download_link_three_day=url_for(
".download_notifications_csv",
service_id=current_service.id,
message_type=message_type,
status=request.args.get("status"),
number_of_days="three_day",
),
download_link_five_day=url_for(
".download_notifications_csv",
service_id=current_service.id,
message_type=message_type,
status=request.args.get("status"),
number_of_days="five_day",
),
download_link_seven_day=url_for(
".download_notifications_csv",
service_id=current_service.id,
message_type=message_type,
status=request.args.get("status"),
number_of_days="seven_day",
),
)
@@ -183,10 +212,9 @@ def get_notifications(service_id, message_type, status_override=None): # noqa
filter_args["status"] = set_status_filters(filter_args)
service_data_retention_days = None
search_term = request.form.get("to", "")
if message_type is not None:
service_data_retention_days = current_service.get_days_of_retention(
message_type
message_type, number_of_days="seven_day"
)
if request.path.endswith("csv") and current_user.has_permissions("view_activity"):
@@ -212,7 +240,6 @@ def get_notifications(service_id, message_type, status_override=None): # noqa
)
url_args = {"message_type": message_type, "status": request.args.get("status")}
prev_page = None
if "links" in notifications and notifications["links"].get("prev", None):
prev_page = generate_previous_dict(
"main.view_notifications", service_id, page, url_args=url_args
@@ -233,7 +260,6 @@ def get_notifications(service_id, message_type, status_override=None): # noqa
)
else:
download_link = None
return {
"service_data_retention_days": service_data_retention_days,
"counts": render_template(
@@ -362,6 +388,7 @@ def get_job_partials(job):
filter_args = parse_filter_args(request.args)
filter_args["status"] = set_status_filters(filter_args)
notifications = job.get_notifications(status=filter_args["status"])
number_of_days = "seven_day"
counts = render_template(
"partials/count.html",
counts=_get_job_counts(job),
@@ -371,7 +398,7 @@ def get_job_partials(job):
),
)
service_data_retention_days = current_service.get_days_of_retention(
job.template_type
job.template_type, number_of_days
)
if request.referrer is not None:

View File

@@ -10,12 +10,12 @@ from flask import (
url_for,
)
from itsdangerous import SignatureExpired
from notifications_utils.url_safe_token import check_token
from app.main import main
from app.main.forms import NewPasswordForm
from app.models.user import User
from app.utils.login import log_in_user
from notifications_utils.url_safe_token import check_token
@main.route("/new-password/<path:token>", methods=["GET", "POST"])

View File

@@ -137,9 +137,9 @@ def get_all_personalisation_from_notification(notification):
def download_notifications_csv(service_id):
filter_args = parse_filter_args(request.args)
filter_args["status"] = set_status_filters(filter_args)
number_of_days = request.args["number_of_days"]
service_data_retention_days = current_service.get_days_of_retention(
filter_args.get("message_type")[0]
filter_args.get("message_type")[0], number_of_days
)
file_time = datetime.now().strftime("%Y-%m-%d %I:%M:%S %p")
file_time = f"{file_time} {get_user_preferred_timezone()}"

View File

@@ -1,11 +1,11 @@
from flask import current_app, render_template
from flask_login import current_user
from notifications_utils.international_billing_rates import INTERNATIONAL_BILLING_RATES
from app.main import main
from app.main.forms import SearchByNameForm
from app.main.views.sub_navigation_dictionaries import using_notify_nav
from app.utils.user import user_is_logged_in
from notifications_utils.international_billing_rates import INTERNATIONAL_BILLING_RATES
CURRENT_SMS_RATE = "1.72"

View File

@@ -15,7 +15,7 @@ from flask import (
)
from flask_login import current_user
from app import user_api_client
from app import redis_client, user_api_client
from app.main import main
from app.main.forms import (
RegisterUserForm,
@@ -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"])
@@ -113,74 +114,94 @@ def registration_continue():
raise Exception("Unexpected routing in registration_continue")
def get_invite_data_from_redis(state):
invite_data = json.loads(redis_client.get(f"invitedata-{state}"))
user_email = redis_client.get(f"user_email-{state}").decode("utf8")
user_uuid = redis_client.get(f"user_uuid-{state}").decode("utf8")
invited_user_email_address = redis_client.get(
f"invited_user_email_address-{state}"
).decode("utf8")
return invite_data, user_email, user_uuid, invited_user_email_address
def put_invite_data_in_redis(
state, invite_data, user_email, user_uuid, invited_user_email_address
):
ttl = 60 * 15 # 15 minutes
redis_client.set(f"invitedata-{state}", json.dumps(invite_data), ex=ttl)
redis_client.set(f"user_email-{state}", user_email, ex=ttl)
redis_client.set(f"user_uuid-{state}", user_uuid, ex=ttl)
redis_client.set(
f"invited_user_email_address-{state}",
invited_user_email_address,
ex=ttl,
)
def check_invited_user_email_address_matches_expected(
user_email, invited_user_email_address
):
if user_email.lower() != invited_user_email_address.lower():
debug_msg("invited user email did not match expected email, abort(403)")
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
def set_up_your_profile():
form = SetupUserProfileForm()
debug_msg(f"Enter set_up_your_profile with request.args {request.args}")
code = request.args.get("code")
state = request.args.get("state")
login_gov_error = request.args.get("error")
if form.validate_on_submit():
# start login.gov
code = request.args.get("code")
state = request.args.get("state")
login_gov_error = request.args.get("error")
if code and state:
return _handle_login_dot_gov_invite(code, state, form)
elif login_gov_error:
current_app.logger.error(f"login.gov error: {login_gov_error}")
raise Exception(f"Could not login with login.gov {login_gov_error}")
# end login.gov
if redis_client.get(f"invitedata-{state}") is None:
access_token = sign_in._get_access_token(code, state)
debug_msg("Got the access token for login.gov")
user_email, user_uuid = sign_in._get_user_email_and_uuid(access_token)
debug_msg(
f"Got the user_email {user_email} and user_uuid {user_uuid} from login.gov"
)
invite_data = state.encode("utf8")
invite_data = base64.b64decode(invite_data)
invite_data = json.loads(invite_data)
debug_msg(f"final state {invite_data}")
invited_user_id = invite_data["invited_user_id"]
invited_user_email_address = get_invited_user_email_address(invited_user_id)
debug_msg(f"email address from the invite_date is {invited_user_email_address}")
check_invited_user_email_address_matches_expected(
user_email, invited_user_email_address
)
return render_template("views/set-up-your-profile.html", form=form)
def get_invited_user_email_address(invited_user_id):
# InvitedUser is an unhashable type and hard to mock in tests
# so this convenience method is a workaround for that
invited_user = InvitedUser.by_id(invited_user_id)
return invited_user.email_address
def invited_user_accept_invite(invited_user_id):
# InvitedUser is an unhashable type and hard to mock in tests
# so this convenience method is a workaround for that
invited_user = InvitedUser.by_id(invited_user_id)
invited_user.accept_invite()
def debug_msg(msg):
current_app.logger.debug(hilite(msg))
def _handle_login_dot_gov_invite(code, state, form):
debug_msg(f"enter _handle_login_dot_gov_invite with code {code} state {state}")
access_token = sign_in._get_access_token(code, state)
debug_msg("Got the access token for login.gov")
user_email, user_uuid = sign_in._get_user_email_and_uuid(access_token)
debug_msg(
f"Got the user_email {user_email} and user_uuid {user_uuid} from login.gov"
)
debug_msg(f"raw state {state}")
invite_data = state.encode("utf8")
debug_msg(f"utf8 encoded state {invite_data}")
invite_data = base64.b64decode(invite_data)
debug_msg(f"b64 decoded state {invite_data}")
invite_data = json.loads(invite_data)
debug_msg(f"final state {invite_data}")
invited_user_id = invite_data["invited_user_id"]
invited_user_email_address = get_invited_user_email_address(invited_user_id)
debug_msg(f"email address from the invite_date is {invited_user_email_address}")
if user_email.lower() != invited_user_email_address.lower():
debug_msg("invited user email did not match expected email, abort(403)")
flash("You cannot accept an invite for another person.")
session.pop("invited_user_id", None)
abort(403)
else:
invited_user_accept_invite(invited_user_id)
debug_msg(
f"invited user {invited_user_email_address} to service {invite_data['service_id']}"
f"accepted invite user {invited_user_email_address} to service {invite_data['service_id']}"
)
debug_msg("accepted invite")
# We need to avoid taking a second trip through the login.gov code because we cannot pull the
# access token twice. So once we retrieve these values, let's park them in redis for 15 minutes
put_invite_data_in_redis(
state, invite_data, user_email, user_uuid, invited_user_email_address
)
form = SetupUserProfileForm()
if (
form.validate_on_submit()
and redis_client.get(f"invitedata-{state}") is not None
):
invite_data, user_email, user_uuid, invited_user_email_address = (
get_invite_data_from_redis(state)
)
# create or update the user
user = user_api_client.get_user_by_uuid_or_email(user_uuid, user_email)
if user is None:
user = User.register(
@@ -191,6 +212,9 @@ def _handle_login_dot_gov_invite(code, state, form):
auth_type="sms_auth",
)
debug_msg(f"registered user {form.name.data} with email {user_email}")
else:
user.update(mobile_number=form.mobile_number.data, name=form.name.data)
debug_msg(f"updated user {form.name.data}")
# activate the user
user = user_api_client.get_user_by_uuid_or_email(user_uuid, user_email)
@@ -207,3 +231,43 @@ def _handle_login_dot_gov_invite(code, state, form):
f"Added user {usr.email_address} to service {invite_data['service_id']}"
)
return redirect(url_for("main.show_accounts_or_dashboard"))
elif login_gov_error:
current_app.logger.error(f"login.gov error: {login_gov_error}")
abort(403)
# we take two trips through this method, but should only hit this
# line on the first trip. On the second trip, we should get redirected
# to the accounts page because we have successfully registered.
return render_template("views/set-up-your-profile.html", form=form)
def get_invited_user_email_address(invited_user_id):
# InvitedUser is an unhashable type and hard to mock in tests
# so this convenience method is a workaround for that
invited_user = InvitedUser.by_id(invited_user_id)
return invited_user.email_address
def invited_user_accept_invite(invited_user_id):
invited_user = InvitedUser.by_id(invited_user_id)
if invited_user.status == "expired":
current_app.logger.error("User invitation has expired")
flash(
"Your invitation has expired; please contact the person who invited you for additional help."
)
abort(401)
if invited_user.status == "cancelled":
current_app.logger.error("User invitation has been cancelled")
flash(
"Your invitation is no longer valid; please contact the person who invited you for additional help."
)
abort(401)
invited_user.accept_invite()
def debug_msg(msg):
current_app.logger.debug(hilite(msg))

View File

@@ -1,16 +1,22 @@
from pipes import quote
import time
import uuid
from string import ascii_uppercase
from zipfile import BadZipFile
from flask import abort, flash, redirect, render_template, request, session, url_for
from flask import (
abort,
current_app,
flash,
redirect,
render_template,
request,
session,
url_for,
)
from flask_login import current_user
from markupsafe import Markup
from notifications_python_client.errors import HTTPError
from notifications_utils import SMS_CHAR_COUNT_LIMIT
from notifications_utils.insensitive_dict import InsensitiveDict
from notifications_utils.recipients import RecipientCSV, first_column_headings
from notifications_utils.sanitise_text import SanitiseASCII
from xlrd.biffh import XLRDError
from xlrd.xldate import XLDateError
@@ -35,10 +41,19 @@ from app.s3_client.s3_csv_client import (
s3upload,
set_metadata_on_csv_upload,
)
from app.utils import PermanentRedirect, should_skip_template_page, unicode_truncate
from app.utils import (
PermanentRedirect,
hilite,
should_skip_template_page,
unicode_truncate,
)
from app.utils.csv import Spreadsheet, get_errors_for_csv
from app.utils.templates import get_template
from app.utils.user import user_has_permissions
from notifications_utils import SMS_CHAR_COUNT_LIMIT
from notifications_utils.insensitive_dict import InsensitiveDict
from notifications_utils.recipients import RecipientCSV, first_column_headings
from notifications_utils.sanitise_text import SanitiseASCII
def get_example_csv_fields(column_headers, use_example_as_example, submitted_fields):
@@ -79,6 +94,7 @@ def get_example_csv_rows(template, use_example_as_example=True, submitted_fields
)
@user_has_permissions("send_messages", restrict_admin_usage=True)
def send_messages(service_id, template_id):
print(hilite("top of send_messages"))
notification_count = service_api_client.get_notification_count(service_id)
remaining_messages = current_service.message_limit - notification_count
@@ -471,6 +487,7 @@ def send_one_off_step(service_id, template_id, step_index):
def _check_messages(service_id, template_id, upload_id, preview_row, **kwargs):
print(hilite("top of _check_messages"))
try:
# The happy path is that the job doesnt already exist, so the
# API will return a 404 and the client will raise HTTPError.
@@ -606,6 +623,7 @@ def _check_messages(service_id, template_id, upload_id, preview_row, **kwargs):
@user_has_permissions("send_messages", restrict_admin_usage=True)
def check_messages(service_id, template_id, upload_id, row_index=2):
data = _check_messages(service_id, template_id, upload_id, row_index)
print(hilite(f"data after checking messages: \n {data}"))
data["allowed_file_extensions"] = Spreadsheet.ALLOWED_FILE_EXTENSIONS
if (
@@ -942,15 +960,31 @@ def send_notification(service_id, template_id):
values = []
for k, v in session["placeholders"].items():
keys.append(k)
values.append(v)
print(hilite(f"value: {v} type: {type(v)}"))
value_quotes = '"' + v + '"'
print(hilite(f"value with quotes: {value_quotes}"))
values.append(value_quotes)
data = ",".join(keys)
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)
# To debug messages that the user reports have not been sent, we log
# the csv filename and the job id. The user will give us the file name,
# so we can search on that to obtain the job id, which we can use elsewhere
# on the API side to find out what happens to the message.
current_app.logger.info(
hilite(
f"One-off file: {filename} job_id: {upload_id} s3 location: service-{service_id}-notify/{upload_id}.csv"
)
)
form = CsvUploadForm()
form.file.data = my_data
form.file.name = filename
@@ -1000,7 +1034,12 @@ def send_notification(service_id, template_id):
job_id=upload_id,
)
)
total = notifications["total"]
current_app.logger.info(
hilite(
f"job_id: {upload_id} has notifications: {total} and attempts: {attempts}"
)
)
return redirect(
url_for(
".view_job",

View File

@@ -4,9 +4,17 @@ 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 notifications_utils.url_safe_token import generate_token
from app import login_manager, user_api_client
from app.main import main
@@ -16,6 +24,8 @@ 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
def _reformat_keystring(orig):
@@ -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)

View File

@@ -32,7 +32,7 @@ def using_notify_nav():
"link": "main.trial_mode_new",
},
{
"name": "Pricing",
"name": "Tracking usage",
"link": "main.pricing",
},
{

View File

@@ -4,7 +4,6 @@ from flask import abort, flash, jsonify, redirect, render_template, request, url
from flask_login import current_user
from markupsafe import Markup
from notifications_python_client.errors import HTTPError
from notifications_utils import SMS_CHAR_COUNT_LIMIT
from app import (
current_service,
@@ -30,6 +29,7 @@ from app.models.template_list import TemplateList, TemplateLists
from app.utils import NOTIFICATION_TYPES, should_skip_template_page
from app.utils.templates import get_template
from app.utils.user import user_has_permissions
from notifications_utils import SMS_CHAR_COUNT_LIMIT
form_objects = {
"email": EmailTemplateForm,

View File

@@ -195,10 +195,10 @@ def check_tour_notification(service_id, template_id):
)
return render_template(
"views/notifications/check.html",
"views/notifications/preview.html",
template=template,
back_link=back_link,
help="2",
help="3",
)

View File

@@ -3,7 +3,6 @@ import json
from flask import current_app, redirect, render_template, request, session, url_for
from flask_login import current_user
from itsdangerous import SignatureExpired
from notifications_utils.url_safe_token import check_token
from app import user_api_client
from app.main import main
@@ -15,6 +14,7 @@ from app.utils.login import (
redirect_to_sign_in,
redirect_when_logged_in,
)
from notifications_utils.url_safe_token import check_token
@main.route("/two-factor-email-sent", methods=["GET"])

View File

@@ -11,7 +11,6 @@ from flask import (
url_for,
)
from flask_login import current_user
from notifications_utils.url_safe_token import check_token
from app import user_api_client
from app.event_handlers import (
@@ -31,6 +30,7 @@ from app.main.forms import (
)
from app.models.user import User
from app.utils.user import user_is_gov_user, user_is_logged_in
from notifications_utils.url_safe_token import check_token
NEW_EMAIL = "new-email"
NEW_MOBILE = "new-mob"

View File

@@ -2,13 +2,13 @@ import json
from flask import abort, current_app, flash, redirect, render_template, session, url_for
from itsdangerous import SignatureExpired
from notifications_utils.url_safe_token import check_token
from app import user_api_client
from app.main import main
from app.main.forms import TwoFactorForm
from app.models.user import User
from app.utils.login import redirect_to_sign_in
from notifications_utils.url_safe_token import check_token
@main.route("/verify", methods=["GET", "POST"])
@@ -66,7 +66,7 @@ def activate_user(user_id):
user = User.from_id(user_id)
# TODO add org invites back in the new way
# organization_id = redis_client.raw_get(
# organization_id = redis_client.get(
# f"organization-invite-{user.email_address}"
# )
# user_api_client.add_user_to_organization(

View File

@@ -1,6 +1,7 @@
from abc import abstractmethod
from flask import abort
from notifications_utils.serialised_model import (
SerialisedModel,
SerialisedModelCollection,

View File

@@ -1,10 +1,9 @@
from abc import ABC, abstractmethod
from notifications_utils.formatters import formatted_list
from app.formatters import format_thousands
from app.models import ModelList
from app.notify_client.service_api_client import service_api_client
from notifications_utils.formatters import formatted_list
class Event(ABC):

View File

@@ -1,5 +1,4 @@
from flask import abort, current_app
from notifications_utils.serialised_model import SerialisedModelCollection
from werkzeug.utils import cached_property
from app.models import JSONModel, SortByNameMixin
@@ -15,6 +14,7 @@ from app.notify_client.organizations_api_client import organizations_client
from app.notify_client.service_api_client import service_api_client
from app.notify_client.template_folder_api_client import template_folder_api_client
from app.utils import get_default_sms_sender
from notifications_utils.serialised_model import SerialisedModelCollection
class Service(JSONModel, SortByNameMixin):
@@ -390,7 +390,7 @@ class Service(JSONModel, SortByNameMixin):
def get_data_retention_item(self, id):
return next((dr for dr in self.data_retention if dr["id"] == id), None)
def get_days_of_retention(self, notification_type):
def get_days_of_retention(self, notification_type, number_of_days):
return next(
(
dr
@@ -398,7 +398,10 @@ class Service(JSONModel, SortByNameMixin):
if dr["notification_type"] == notification_type
),
{},
).get("days_of_retention", current_app.config["ACTIVITY_STATS_LIMIT_DAYS"])
).get(
"days_of_retention",
current_app.config["ACTIVITY_STATS_LIMIT_DAYS"].get(number_of_days),
)
@cached_property
def organization(self):

View File

@@ -2,9 +2,9 @@ from flask import abort, has_request_context, request
from flask_login import current_user
from notifications_python_client import __version__
from notifications_python_client.base import BaseAPIClient
from notifications_utils.clients.redis import RequestCache
from app.extensions import redis_client
from notifications_utils.clients.redis import RequestCache
cache = RequestCache(redis_client)

View File

@@ -1,4 +1,4 @@
from datetime import datetime
from datetime import datetime, timezone
from app.extensions import redis_client
from app.notify_client import NotifyAdminAPIClient, _attach_current_user, cache
@@ -43,6 +43,28 @@ class ServiceAPIClient(NotifyAdminAPIClient):
params={"limit_days": limit_days},
)["data"]
def get_service_notification_statistics_by_day(
self, service_id, start_date=None, days=None
):
if start_date is None:
start_date = datetime.now().strftime("%Y-%m-%d")
return self.get(
"/service/{0}/statistics/{1}/{2}".format(service_id, start_date, days),
)["data"]
def get_user_service_notification_statistics_by_day(
self, service_id, user_id, start_date=None, days=None
):
if start_date is None:
start_date = datetime.now(timezone.utc).strftime("%Y-%m-%d")
return self.get(
"/service/{0}/statistics/user/{1}/{2}/{3}".format(
service_id, user_id, start_date, days
),
)["data"]
def get_services(self, params_dict=None):
"""
Retrieve a list of services.

View File

@@ -1,3 +1,5 @@
import os
import botocore
from boto3 import Session
from botocore.config import Config
@@ -29,6 +31,17 @@ def get_s3_object(
)
s3 = session.resource("s3", config=AWS_CLIENT_CONFIG)
obj = s3.Object(bucket_name, filename)
# This 'proves' that use of moto in the relevant tests in test_send.py
# mocks everything related to S3. What you will see in the logs is:
# Exception: CREATED AT <MagicMock name='resource().Bucket().creation_date' id='4665562448'>
#
# raise Exception(f"CREATED AT {_s3.Bucket(bucket_name).creation_date}")
if os.getenv("NOTIFY_ENVIRONMENT") == "test":
teststr = str(s3.Bucket(bucket_name).creation_date).lower()
if "magicmock" not in teststr:
raise Exception(
"Test is not mocked, use @mock_aws or the relevant mocker.patch to avoid accessing S3"
)
return obj

View File

@@ -1,7 +1,6 @@
import uuid
from flask import current_app
from notifications_utils.s3 import s3upload as utils_s3upload
from app.s3_client import (
get_s3_contents,
@@ -9,6 +8,7 @@ from app.s3_client import (
get_s3_object,
set_s3_metadata,
)
from notifications_utils.s3 import s3upload as utils_s3upload
FILE_LOCATION_STRUCTURE = "service-{}-notify/{}.csv"
@@ -28,6 +28,7 @@ def get_csv_upload(service_id, upload_id):
def s3upload(service_id, filedata):
upload_id = str(uuid.uuid4())
bucket_name, file_location, access_key, secret_key, region = get_csv_location(
service_id, upload_id

View File

@@ -2,9 +2,9 @@ import uuid
from boto3 import Session
from flask import current_app
from notifications_utils.s3 import s3upload as utils_s3upload
from app.s3_client import get_s3_object
from notifications_utils.s3 import s3upload as utils_s3upload
TEMP_TAG = "temp-{user_id}_"
EMAIL_LOGO_LOCATION_STRUCTURE = "{temp}{unique_id}-{filename}"

View File

@@ -1,10 +1,17 @@
{% from "../components/banner.html" import banner %}
{% from "../components/components/skip-link/macro.njk" import usaSkipLink -%}
{% from "components/banner.html" import banner %}
{% from "components/components/skip-link/macro.njk" import usaSkipLink -%}
{% from "components/sub-navigation.html" import sub_navigation %}
<!DOCTYPE html>
<html lang="{{ htmlLang | default('en') }}" class="{{ htmlClasses }}">
{% include "new/components/head.html" %}
<head>
<title>
{% block pageTitle %}
{% block per_page_title %}{% endblock %} Notify.gov
{% endblock %}
</title>
{% include "new/components/head.html" %}
</head>
<body class="usa-template__body {{ bodyClasses }}">
<script nonce="{{ csp_nonce() }}">document.body.className = ((document.body.className) ? document.body.className + ' js-enabled' : 'js-enabled');</script>
{% block bodyStart %}
@@ -23,11 +30,10 @@
}) }}
{% endblock %}
{% block header %}
{% if current_user.is_authenticated %}
{% include 'new/components/usa_banner.html' %}
{% include 'new/components/header.html' %}
{% endif %}
{% include 'new/components/usa_banner.html' %}
{% include 'new/components/header.html' %}
{% endblock %}
{% block main %}
@@ -36,18 +42,17 @@
{% block backLink %}{% endblock %}
{% endblock %}
{% block mainClasses %}
<!-- notes set mainClasses = "margin-top-5 padding-bottom-5" where withoutnav_template was used and maybe templates that are using content_template -->
{% set mainClasses = "margin-top-5 padding-bottom-5" %}
<main class="{{ mainClasses }}" id="main-content" role="main">
{% endblock %}
{% block content %}
{% block flash_messages %}
<!-- flash_message.html was from the withoutnav_template and is only included on child templates that was using withoutnav_template. Now, we can add in flash_message blocks and include 'flash_messages.html' to child templates that was using withoutnav_template. This will help to eliminate the use of a whole other parent template. -->
{% include 'new/components/flash_messages.html' %}
{% endblock %}
{% block maincolumn_content %}
{% block fromContentTemplatetwoColumnGrid %}
<div class="grid-row">
{% if navigation_links %}
<div class="tablet:grid-col-2">
<div class="tablet:grid-col-2 margin-bottom-4">
{{ sub_navigation(navigation_links) }}
</div>
<div class="tablet:grid-col-10 padding-left-4 usa-prose site-prose">
@@ -57,8 +62,6 @@
{% block content_column_content %}{% endblock %}
</div>
</div>
<!-- content_column_content block is from the content_template.html. We do not need this template. We can consolidate and move it to the base.html template. We can call on this block where child templates were using the content_template.html -->
{% endblock %}
{% endblock %}
{% endblock %}
</main>

View File

@@ -1,4 +1,4 @@
{% extends "withoutnav_template.html" %}
{% extends "base.html" %}
{% block per_page_title %}Youre not authorised to see this page{% endblock %}
{% block maincolumn_content %}
<div class="grid-row">

View File

@@ -1,4 +1,4 @@
{% extends "withoutnav_template.html" %}
{% extends "base.html" %}
{% block per_page_title %}Youre not allowed to see this page{% endblock %}
{% block maincolumn_content %}
<div class="grid-row">

View File

@@ -1,4 +1,4 @@
{% extends "withoutnav_template.html" %}
{% extends "base.html" %}
{% block per_page_title %}Page not found{% endblock %}
{% block maincolumn_content %}
<div class="grid-row">

View File

@@ -1,4 +1,4 @@
{% extends "withoutnav_template.html" %}
{% extends "base.html" %}
{% block per_page_title %}Page not found{% endblock %}
{% block maincolumn_content %}
<div class="grid-row">

View File

@@ -1,4 +1,4 @@
{% extends "withoutnav_template.html" %}
{% extends "base.html" %}
{% block per_page_title %}Sorry, theres a problem with the service{% endblock %}
{% block maincolumn_content %}
<div class="grid-row">

View File

@@ -1,6 +1,4 @@
<head>
<meta charset="utf-8" />
<title>{% block pageTitle %}Notify.gov{% endblock %}</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="description" content="Reach people where they are with a text message platform built just for government">
<meta name="theme-color" media="(prefers-color-scheme: light)" content="f0f0f0" />
@@ -12,7 +10,7 @@
{# Ensure that older IE versions always render with the correct rendering engine #}
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
{% block headIcons %}
<link rel="shortcut icon" href="{{ assetPath | default('/assets') }}/images/favicon.ico" />
<link rel="icon" type="image/png" sizes="32x32" href="{{ assetPath | default('/assets') }}/images/favicon-32x32.png" />
<link rel="icon" type="image/png" sizes="16x16" href="{{ assetPath | default('/assets') }}/images/favicon-16x16.png" />
@@ -22,25 +20,18 @@
<meta name="msapplication-TileColor" content="#da532c">
<link href="{{ assetPath | default('/assets') }}/images/notify-dark-favicon.png" rel="icon" media="(prefers-color-scheme: dark)">
<meta name="theme-color" content="#ffffff">
{% endblock %}
<link rel="stylesheet" media="screen" href="{{ asset_url('css/styles.css') }}" />
{% block extra_stylesheets %}{% endblock %}
{% block meta_format_detection %}
<meta name="format-detection" content="telephone=no">
{% endblock %}
{% block og_image %}
<meta property="og:title" content="Notify.gov">
<meta property="og:description" content="Reach people where they are with a text message platform built just for government">
<meta property="og:image" content="/static/images/notify-og-image.png">
{% endblock %}
{# google #}
<script type="text/javascript" src="{{ asset_url('js/gtm_head.js') }}"></script>
<meta name="google-site-verification" content="niWnSqImOWz6mVQTYqNb5tFK8HaKSB4b3ED4Z9gtUQ0" />
{% if g.hide_from_search_engines %}
<meta name="robots" content="noindex" />
{% endif %}
{% block head %}{% endblock %}
</head>

View File

@@ -1,25 +1,28 @@
{# setting navigation and secondarynavigation #}
{% set navigation = [
{"href": url_for("main.show_accounts_or_dashboard"), "text": "Current service", "active": header_navigation.is_selected('accounts-or-dashboard')},
{"href": url_for('main.get_started'), "text": "Using Notify", "active": header_navigation.is_selected('using_notify')},
{"href": url_for('main.features'), "text": "Features", "active": header_navigation.is_selected('features')},
{"href": url_for('main.support'), "text": "Contact us", "active": header_navigation.is_selected('support')}
] %}
{% if current_user.platform_admin %}
{% set navigation = navigation + [{"href": url_for('main.platform_admin_splash_page'), "text": "Platform admin", "active": header_navigation.is_selected('platform-admin')}] %}
{% else %}
{% set navigation = navigation + [{"href": url_for('main.user_profile'), "text": "User profile", "active": header_navigation.is_selected('user-profile')}] %}
{% endif %}
{% if current_service %}
{% set secondaryNavigation = [
{"href": url_for('main.service_settings', service_id=current_service.id), "text": "Settings", "active": secondary_navigation.is_selected('settings')},
{"href": url_for('main.sign_out'), "text": "Sign out"}
{% if current_user.is_authenticated %}
{% set navigation = [
{"href": url_for("main.show_accounts_or_dashboard"), "text": "Current service", "active": header_navigation.is_selected('accounts-or-dashboard')},
{"href": url_for('main.get_started'), "text": "Using Notify", "active": header_navigation.is_selected('using_notify')},
{"href": url_for('main.features'), "text": "Features", "active": header_navigation.is_selected('features')},
{"href": url_for('main.support'), "text": "Contact us", "active": header_navigation.is_selected('support')}
] %}
{% else %}
{% set secondaryNavigation = [{"href": url_for('main.sign_out'), "text": "Sign out"}] %}
{% if current_user.platform_admin %}
{% set navigation = navigation + [{"href": url_for('main.platform_admin_splash_page'), "text": "Platform admin", "active": header_navigation.is_selected('platform-admin')}] %}
{% else %}
{% set navigation = navigation + [{"href": url_for('main.user_profile'), "text": "User profile", "active": header_navigation.is_selected('user-profile')}] %}
{% endif %}
{% if current_service %}
{% set secondaryNavigation = [
{"href": url_for('main.service_settings', service_id=current_service.id), "text": "Settings", "active": secondary_navigation.is_selected('settings')},
{"href": url_for('main.sign_out'), "text": "Sign out"}
] %}
{% else %}
{% set secondaryNavigation = [{"href": url_for('main.sign_out'), "text": "Sign out"}] %}
{% endif %}
{% endif %}
{# usa header #}
<header class="usa-header usa-header--extended">
<div class="usa-nav-container">

View File

@@ -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 %}

View File

@@ -6,10 +6,9 @@ This document serves as a glossary for the templates directory structure of the
## Directory Structure
- `/templates`
- `base.html`: The main base template from which all other templates inherit. This template is a combination of `main_template`, `admin_template`, `withoutnav_template`, `settings_templates`, and `content_template`.
- `base.html`: The main base template from which all other templates inherit. This template is a combination of `main_template`, `admin_template`, `withoutnav_template`, and `content_template`.
- **/layouts**: Contains shared layouts used across the site. Simply put, it defines the overall structure or skeleton of the application (less frequently revised).
- `withnav_template.html`: A variation of the base layout that includes a sidebar.
- `org_template.html`: A variaton of the withnav_template
- `withnav_template.html`: A variation of org_template and settings_template that includes a sidebar.
- **/components**: Houses reusable UI components that can be included in multiple templates and can be tailored with different content or links depending on the context.(more frequently revised or customized)
- `head.html`: Template for the site's <head>, included in `base.html`.
- `header.html`: Template for the site's header, included in `base.html`.
@@ -23,20 +22,15 @@ This document serves as a glossary for the templates directory structure of the
### Best Practices
- Use **inheritance** `{% extends %}` to build on base layouts.
- Employ **components** `{% include %}` for reusable UI elements to keep the code DRY and facilitate easier updates.
### Observation Notes
- The macro-options.json files in the header and footer component act as structural guides. They aren't directly used as data passed to the usaFooter function/macro. Instead, these files outline the expected properties and provide a description of their purpose. The `usaFooter` macro component is currently only invoked in the `admin_template`, which will eventually serve as the `base.html` template. This will simplify the approach when we change the footer macros to componenets by eliminating the need to dynamically pass this data from the base.html template. This is also true for `usaHeader` macro component.
- Employ **components** `{% include %}` for reusable UI elements.
### Old Layout Templates We Don't Need
- withoutnav_template.html Delete
- main_template.html Delete
- withoutnav_template.html Delete, it is now apart of base.html
- main_template.html Delete, it is now apart of base.html
- settings_templates.html `withnav_template` can be used to replace `settings_template`.
- settings_nav.html (move to /components/ directory)
- main_nav.html (move to /components/ directory)
- service_navigation.html (move to /components/ directory)
- org_template, could be under it's own directory called /layout/organization
- org_template, Delete, it is now apart of withnav_template.html
- org_nav.html (move to /components/ directory)
- content_template.html Delete
- content_template.html Delete, it is now apart of base.html

View File

@@ -291,10 +291,12 @@
{% block bodyEnd %}
{% block extra_javascripts %}
{% endblock %}
<!--[if gt IE 8]><!-->
<script type="text/javascript" src="{{ asset_url('javascripts/all.js') }}"></script>
<script type="text/javascript" src="{{ asset_url('js/uswds.min.js') }}"></script>
<!--<![endif]-->
{% endblock %}

View File

@@ -1,4 +1,4 @@
{% extends "withoutnav_template.html" %}
{% extends "base.html" %}
{% from "components/sub-navigation.html" import sub_navigation %}
{% from "components/page-header.html" import page_header %}

View File

@@ -1,3 +1,4 @@
{# This template is an old version #}
{% if help %}
{% include 'partials/tour.html' %}
{% else %}

View File

@@ -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>

View File

@@ -1,4 +1,4 @@
{% extends "admin_template.html" %}
{% extends "base.html" %}
{% block per_page_title %}
{% block org_page_title %}{% endblock %} {{ current_org.name }}

View File

@@ -1,3 +1,4 @@
{# This template is an old version #}
{% macro navigation_service_name(service) %}
<div class="font-body-2xl text-bold">
{{ service.name }}

View File

@@ -1,3 +1,4 @@
{# This template is an old version #}
{% if help %}
{% include 'partials/tour.html' %}
{% else %}

View File

@@ -1,3 +1,4 @@
{# This template is an old version #}
{% extends "admin_template.html" %}
{% block per_page_title %}

View 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 %}

View File

@@ -1,4 +1,4 @@
{% extends "admin_template.html" %}
{% extends "base.html" %}
{% set mainClasses = "margin-top-5 padding-bottom-5" %}

View File

@@ -1,4 +1,4 @@
{% extends "content_template.html" %}
{% extends "base.html" %}
{% from "components/service-link.html" import service_link %}
{% from "components/content-metadata.html" import content_metadata %}
@@ -168,7 +168,7 @@
<p>
The following content on the <a class="usa-link" href="https://status.notifications.service.gov.uk/">Notify status page</a> is not compliant with the WCAG version 2.1 AA standard:
</p>
</p>
<ul class="usa-list">
<li>an incorrect heading hierarchy which fails <a class="usa-link" href="https://www.w3.org/TR/WCAG21/#info-and-relationships">success criterion 1.3.1: info and relationships</a></li>

View File

@@ -1,4 +1,4 @@
{% extends "withoutnav_template.html" %}
{% extends "base.html" %}
{% from "components/page-header.html" import page_header %}
{% from "components/page-footer.html" import page_footer %}
{% from "components/form.html" import form_wrapper %}

View File

@@ -1,4 +1,4 @@
{% extends "withoutnav_template.html" %}
{% extends "base.html" %}
{% from "components/page-header.html" import page_header %}
{% from "components/page-footer.html" import page_footer %}
{% from "components/form.html" import form_wrapper %}

View File

@@ -1,4 +1,4 @@
{% extends "withoutnav_template.html" %}
{% extends "base.html" %}
{% block per_page_title %}The invitation you were sent has been cancelled{% endblock %}
{% block maincolumn_content %}
<div class="grid-row">

View File

@@ -1,4 +1,4 @@
{% extends "withoutnav_template.html" %}
{% extends "base.html" %}
{% block per_page_title %}
Check your email

View File

@@ -1,4 +1,4 @@
{% extends "withoutnav_template.html" %}
{% extends "base.html" %}
{% from "components/components/button/macro.njk" import usaButton %}
{% macro service_list(

View File

@@ -22,6 +22,8 @@
Messages sent
</h2>
<!-- <button id="sevenDaysButton">7 Days</button>
<canvas id="myChart"></canvas> -->
{{ ajax_block(partials, updates_url, 'inbox') }}
{{ ajax_block(partials, updates_url, 'totals') }}
@@ -59,7 +61,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>

View File

@@ -1,4 +1,4 @@
{% extends "content_template.html" %}
{% extends "base.html" %}
{% block per_page_title %}
Documentation

View File

@@ -1,4 +1,4 @@
{% extends "withoutnav_template.html" %}
{% extends "base.html" %}
{% from "components/page-header.html" import page_header %}
{% from "components/page-footer.html" import page_footer %}

View File

@@ -1,4 +1,4 @@
{% extends "withoutnav_template.html" %}
{% extends "base.html" %}
{% from "components/page-footer.html" import page_footer %}
{% block per_page_title %}

View File

@@ -1,4 +1,4 @@
{% extends "withoutnav_template.html" %}
{% extends "base.html" %}
{% from "components/components/button/macro.njk" import usaButton %}
{% block per_page_title %}

View File

@@ -1,4 +1,4 @@
{% extends "content_template.html" %}
{% extends "base.html" %}
{% from "components/table.html" import mapping_table, row, text_field, edit_field, field with context %}
{% from "components/sub-navigation.html" import sub_navigation %}
{% from "components/service-link.html" import service_link %}

View File

@@ -1,4 +1,4 @@
{% extends "content_template.html" %}
{% extends "base.html" %}
{% from "components/table.html" import mapping_table, row, text_field, edit_field, field with context %}
{% from "components/service-link.html" import service_link %}

View File

@@ -1,4 +1,4 @@
{% extends "withoutnav_template.html" %}
{% extends "base.html" %}
{% from "components/page-footer.html" import page_footer %}
{% from "components/form.html" import form_wrapper %}

View File

@@ -1,4 +1,4 @@
{% extends "content_template.html" %}
{% extends "base.html" %}
{% from "components/table.html" import mapping_table, row, text_field, edit_field, field with context %}
{% from "components/sub-navigation.html" import sub_navigation %}
{% from "components/components/details/macro.njk" import usaDetails %}

View File

@@ -1,4 +1,4 @@
{% extends "content_template.html" %}
{% extends "base.html" %}
{% from "components/service-link.html" import service_link %}
{% block per_page_title %}

View File

@@ -1,4 +1,4 @@
{% extends "content_template.html" %}
{% extends "base.html" %}
{% from "components/service-link.html" import service_link %}
{% block per_page_title %}

View File

@@ -1,4 +1,4 @@
{% extends "content_template.html" %}
{% extends "base.html" %}
{% from "components/table.html" import mapping_table, row, text_field, edit_field, field with context %}
{% from "components/sub-navigation.html" import sub_navigation %}
{% from "components/service-link.html" import service_link %}

View File

@@ -1,4 +1,4 @@
{% extends "content_template.html" %}
{% extends "base.html" %}
{% block per_page_title %}
Send files by email

View File

@@ -1,5 +1,5 @@
{% from "components/table.html" import mapping_table, row, text_field, edit_field, field with context %}
{% extends "withoutnav_template.html" %}
{% extends "base.html" %}
{% block per_page_title %}
Integration testing

View File

@@ -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">

View File

@@ -1,4 +1,4 @@
{% extends "content_template.html" %}
{% extends "base.html" %}
{% from "components/table.html" import mapping_table, row, text_field %}
{% block per_page_title %}
@@ -9,9 +9,13 @@
<h1 class="font-body-2xl margin-bottom-3">Delivery status</h1>
<p>Notifys real-time dashboard lets you check the status of any message.</p>
<p>For <a class="usa-link" href="{{ url_for('main.security') }}">security</a>, this information is only available for seven days after a message has been sent. You can download a report, including a list of sent messages, for your own records.</p>
<p>This page describes the statuses you'll see when you're signed in to Notify.</p>
<p>Notify starts sending your text messages immediately. Each message is sent to its cell phone carrier, which attempts
delivery to the recipient. This process is often almost instantaneous, but can sometimes take a while if a phone is
unavailable. The carrier will continue to try to deliver the message for up to 72 hours.</p>
<p>The Notify dashboard provides a high-level view of the number of messages Sent, Pending, Delivered, and Failed. The
dashboard data starts to update about five minutes after a message is sent and will continue to update for four hours.</p>
<p>Delivery statuses reflect delivery to a specific <em>cell phone number</em>, not to a <em>person</em>. No status can confirm that a
specific person actually received or read the message.</p>
<!-- <p>If youre using the Notify API, read our <a class="usa-link" href="{{ url_for('.documentation') }}">documentation</a> for a list of API statuses.<p>
@@ -45,7 +49,7 @@
<p>Notify cannot tell you if your users open an email or click on the links in an email. We do not track open rates and click-throughs because there are privacy issues. Tracking emails without asking permission from users could breach General Data Protection Regulations (GDPR).</p> -->
<h2 id="text-message-statuses" class="heading-medium">Text messages</h2>
<div class="bottom-gutter-3-2">
<div class="margin-bottom-8">
{% call mapping_table(
caption='Message statuses text messages',
field_headings=['Status', 'Description'],
@@ -53,12 +57,14 @@
caption_visible=False
) %}
{% for message_status, description in [
('Total', 'The total number of messages that have been sent during the last seven days.'),
('Pending', 'Notify has sent the message to the provider. The provider will try to deliver the message to the recipient for up to 72 hours. Notify is waiting for delivery information.'),
('Scheduled', 'The total number of messages that have been scheduled to be sent at some future time.'),
('Canceled', 'Messages that were created and scheduled, but canceled prior to Sending.'),
('Total', 'The total number of messages that have been sent during the specified time.'),
('Pending', 'Notify has sent the message to the provider. The provider will try to deliver the message to the recipient for up to 72
hours. “Pending” indicates that Notify is waiting for delivery information.'),
('Delivered', 'The message was successfully delivered. Notify cannot tell you if a user has opened or read a message.'),
('Failed', 'The provider could not deliver the message. This can happen if the phone number was wrong or if the network operator
rejects the message. If youre sure that these phone numbers are correct, you should <a class="usa-link" href="/support">contact us</a>. If not, you should remove them from your database. Youll still be charged for text messages that
cannot be delivered.' | safe),
('Failed', 'The message could not be delivered.'),
('Process error / Delivery not attempted', 'If you receive a large number of process errors, please contact the Notify team.'),
] %}
{% call row() %}
{{ text_field(message_status) }}
@@ -67,5 +73,21 @@
{% endfor %}
{% endcall %}
</div>
<h2 class="heading-medium">About carrier statuses</h2>
<p>Sometimes Notify receives more detailed information from the carriers on the status of messages, and these can be found
in the downloadable reports. Not all carriers provide the same level of detail regarding delivery and some delivery
statutes have a slight variation in word choice. Notify includes this information in the reports to provide you as much
detail as possible. Remember, for <a class="usa-link" href="/features/security">security</a> purposes, detailed information is only available for seven days after a
message has been sent.</p>
<h2 class="heading-medium">Opting out</h2>
<p>A text recipient can opt out of receiving text messages from your phone number at any time by responding “STOP” or
“QUIT” or <a class="usa-link" href="https://docs.aws.amazon.com/pinpoint/latest/userguide/channels-sms-limitations-opt-out.html#settings-sms-self-opt-out-terms">one of several other keywords</a>. If they opt out,</p>
<ul>
<li>They may receive a response from their carrier stating that they will no longer receive text messages from that number.</li>
<li>Any subsequent messages sent to that number will not be delivered (unless the recipient opts back in by texting “START”
or “OPT-IN”).</li>
</ul>
<p>Notify.gov does not yet have a way to pull opt-out status and make it available in the UI for agencies to download.</p>
{% endblock %}

View File

@@ -1,4 +1,4 @@
{% extends "withoutnav_template.html" %}
{% extends "base.html" %}
{% from "components/page-footer.html" import page_footer %}
{% from "components/form.html" import form_wrapper %}

View File

@@ -64,10 +64,22 @@
{% if current_user.has_permissions('view_activity') %}
<p class="font-body-sm">
<a href="{{ download_link }}" download="download" class="usa-link">Download this report (<abbr title="Comma separated values">CSV</abbr>)</a>
<a href="{{ download_link_seven_day }}" download="download" class="usa-link">Download all data last 7 days (<abbr title="Comma separated values">CSV</abbr>)</a>
&emsp;
Data available for {{ partials.service_data_retention_days }} days
</p>
<p class="font-body-sm">
<a href="{{ download_link_five_day }}" download="download" class="usa-link">Download all data last 5 days (<abbr title="Comma separated values">CSV</abbr>)</a>
&emsp;
</p>
<p class="font-body-sm">
<a href="{{ download_link_three_day }}" download="download" class="usa-link">Download all data last 3 days (<abbr title="Comma separated values">CSV</abbr>)</a>
&emsp;
</p>
<p class="font-body-sm">
<a href="{{ download_link_today }}" download="download" class="usa-link">Download all data today (<abbr title="Comma separated values">CSV</abbr>)</a>
&emsp;
</p>
{% endif %}
{{ ajax_block(

View File

@@ -5,11 +5,21 @@
{% from "components/components/button/macro.njk" import usaButton %}
{% block service_page_title %}
{{ "Error" if error else "Preview" }}
{% if error %}
{{ "Error" }}
{% elif help %}
{{ "Example text message" }}
{% else %}
{{ "Preview" }}
{% endif %}
{% endblock %}
{% block backLink %}
{{ usaBackLink({ "href": back_link_from_preview }) }}
{% if help %}
{{ usaBackLink({ "href": back_link }) }}
{% else %}
{{ usaBackLink({ "href": back_link_from_preview }) }}
{% endif %}
{% endblock %}
{% block maincolumn_content %}
@@ -40,16 +50,23 @@
{% endcall %}
</div>
{% else %}
{{ page_header('Preview') }}
{{ page_header("Example text message" if help else "Preview") }}
{% endif %}
<div>
<p class="sms-message-scheduler">Scheduled: {{ scheduled_for |format_datetime_scheduled_notification if scheduled_for else 'Now'}}</p>
<p class="sms-message-template">Template: {{template.name}}</p>
<p class="sms-message-sender" >From: {{ template.sender }}</p>
<p class="sms-message-sender" >To: {{ recipient }}</p>
</div>
<h2 id="{{ file_contents_header_id }}">Message</h2>
{% if not help %}
<div>
<p class="sms-message-scheduler">Scheduled: {{ scheduled_for |format_datetime_scheduled_notification if scheduled_for else 'Now'}}</p>
<p class="sms-message-template">Template: {{template.name}}</p>
<p class="sms-message-sender" >From: {{ template.sender }}</p>
<p class="sms-message-sender" >To: {{ recipient }}</p>
</div>
{% endif %}
{% if not help %}
<h2 class="message-header">Message</h2>
{% endif %}
<div class="preview-message"> {{ template|string }}</div>
<div class="js-stick-at-bottom-when-scrolling">

View File

@@ -1,4 +1,4 @@
{% extends "withoutnav_template.html" %}
{% extends "base.html" %}
{% from "components/page-header.html" import page_header %}
{% from "components/page-footer.html" import page_footer %}
{% from "components/form.html" import form_wrapper %}

View File

@@ -1,4 +1,4 @@
{% extends "org_template.html" %}
{% extends "withnav_template.html" %}
{% from "components/page-header.html" import page_header %}
{% block org_page_title %}

View File

@@ -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

View File

@@ -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 %}

View File

@@ -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 %}

View File

@@ -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 %}

Some files were not shown because too many files have changed in this diff Show More