diff --git a/backend/report_submission/test_views.py b/backend/report_submission/test_views.py
index d7b1c6d3ee..46f8d3fb25 100644
--- a/backend/report_submission/test_views.py
+++ b/backend/report_submission/test_views.py
@@ -385,8 +385,7 @@ def test_step_three_accessandsubmission_submission_fail(self):
data = {}
response = self.client.post(url, data=data)
- self.assertEqual(response.status_code, 302)
- self.assertEqual(response.url, "/report_submission/accessandsubmission/")
+ self.assertEqual(response.status_code, 400)
def test_reportsubmissionredirectview_get_redirects(self):
url = reverse("report_submission:report_submission")
@@ -777,7 +776,7 @@ def test_post_gsa_migration_error(self):
self.assertIn("errors", response.context)
self.assertIn(
- "GSA_MIGRATION not permitted outside of migrations",
+ "Enter a valid email address.",
response.context["errors"],
)
diff --git a/backend/report_submission/views.py b/backend/report_submission/views.py
index 9cb23569d4..fcb2f53859 100644
--- a/backend/report_submission/views.py
+++ b/backend/report_submission/views.py
@@ -114,20 +114,17 @@ def get(self, request):
args["step"] = 3
return render(request, "report_submission/step-3.html", args)
- # render access-submission form
-
- # gather/save step 3 info, redirect to step ...4?
- def post(self, post_request):
- result = api.views.access_and_submission_check(
- post_request.user, post_request.POST
- )
+ def post(self, request):
+ result = api.views.access_and_submission_check(request.user, request.POST)
report_id = result.get("report_id")
if report_id:
return redirect(f"/report_submission/general-information/{report_id}")
else:
- return redirect(reverse("report_submission:accessandsubmission"))
+ return render(
+ request, "report_submission/step-3.html", context=result, status=400
+ )
class GeneralInformationFormView(LoginRequiredMixin, View):
From deb03cb1fb7238672af910df4993e6468a51f989 Mon Sep 17 00:00:00 2001
From: Bobby Novak <176936850+rnovak338@users.noreply.github.com>
Date: Thu, 5 Sep 2024 15:00:45 -0400
Subject: [PATCH 2/2] Convert new Admin API emails to lowercase (#4225)
* Update create_functions.sql
This SQL script either gets or inserts a user for the admin API depending on a few parameters. This proposed change ensures new users have their email reduced to lowercase, and the prior verification transforms both the table and the parameter when checking if the user exists.
* Cleaning edge cases
Converting all email checks and "insertions" to lowercase when doing the following:
- Creating a new admin in the Admin API.
- Retrieving an existing admin in the Admin API.
- Removing Tribal API key access for a specified email.
- Adding read access to the Tribal API for a specified email.
- Removing Tribal access emails.
- Adding Tribal access emails.
* Admin API local testing documentation
Added a first draft of how to start using the admin API for local dev to `/docs/testing.md` .
---
.../api/admin_api_v1_1_0/create_functions.sql | 32 +++++++++----------
docs/testing.md | 28 ++++++++++++++++
2 files changed, 44 insertions(+), 16 deletions(-)
diff --git a/backend/support/api/admin_api_v1_1_0/create_functions.sql b/backend/support/api/admin_api_v1_1_0/create_functions.sql
index 336b2e2ccf..b89b001186 100644
--- a/backend/support/api/admin_api_v1_1_0/create_functions.sql
+++ b/backend/support/api/admin_api_v1_1_0/create_functions.sql
@@ -126,7 +126,7 @@ BEGIN
-- Are they already in the table?
SELECT count(up.email)
FROM public.users_userpermission as up
- WHERE email = params->>'email' INTO already_exists;
+ WHERE LOWER(email) = LOWER(params->>'email') INTO already_exists;
-- If they are, we're going to exit.
IF already_exists <> 0
@@ -146,11 +146,11 @@ BEGIN
-- Can we make the 1 not magic... do a select into.
INSERT INTO public.users_userpermission
(email, permission_id, user_id)
- VALUES (params->>'email', read_tribal_id, null);
+ VALUES (LOWER(params->>'email'), read_tribal_id, null);
- RAISE INFO 'ADMIN_API add_tribal_access_email OK %', params->>'email';
+ RAISE INFO 'ADMIN_API add_tribal_access_email OK %', LOWER(params->>'email');
RETURN admin_api_v1_1_0_functions.log_admin_api_event('tribal-access-email-added',
- json_build_object('email', params->>'email'));
+ json_build_object('email', LOWER(params->>'email')));
END IF;
ELSE
RETURN 0;
@@ -222,7 +222,7 @@ BEGIN
THEN
-- Delete rows where the email address matches
DELETE FROM public.users_userpermission as up
- WHERE up.email = params->>'email';
+ WHERE LOWER(up.email) = LOWER(params->>'email');
-- This is the Postgres way to find out how many rows
-- were affected by a DELETE.
GET DIAGNOSTICS affected_rows = ROW_COUNT;
@@ -230,7 +230,7 @@ BEGIN
IF affected_rows > 0
THEN
RETURN admin_api_v1_1_0_functions.log_admin_api_event('tribal-access-email-removed',
- json_build_object('email', params->>'email'));
+ json_build_object('email', LOWER(params->>'email')));
ELSE
RETURN 0;
END IF;
@@ -297,14 +297,14 @@ BEGIN
SELECT EXISTS (
SELECT 1
FROM public.dissemination_TribalApiAccessKeyIds
- WHERE email = params->>'email'
+ WHERE LOWER(email) = LOWER(params->>'email')
)
INTO user_exists;
-- If the user already exists, it means they have access.
-- For purposes of this function, lets call that "succses", and return true.
IF user_exists THEN
- RAISE INFO 'ADMIN_API add_tribal_api_key_access ALREADY_EXISTS %', params->>'email';
+ RAISE INFO 'ADMIN_API add_tribal_api_key_access ALREADY_EXISTS %', LOWER(params->>'email');
RETURN json_build_object(
'result', 'success',
'message', 'User with this key already exists')::JSON;
@@ -313,8 +313,8 @@ BEGIN
-- If the user does not exist, add a new record
INSERT INTO public.dissemination_TribalApiAccessKeyIds (email, key_id, date_added)
- VALUES (params->>'email', params->>'key_id', CURRENT_TIMESTAMP);
- RAISE INFO 'ADMIN_API add_tribal_api_key_access ACCESS_GRANTED % %', params->>'email', params->>'key_id';
+ VALUES (LOWER(params->>'email'), params->>'key_id', CURRENT_TIMESTAMP);
+ RAISE INFO 'ADMIN_API add_tribal_api_key_access ACCESS_GRANTED % %', LOWER(params->>'email'), params->>'key_id';
RETURN json_build_object(
'result', 'success',
'message', 'User access granted')::JSON;
@@ -328,7 +328,7 @@ BEGIN
END IF;
-- Return false by default.
- RAISE INFO 'ADMIN_API add_tribal_api_key_access WAT %', params->>'email';
+ RAISE INFO 'ADMIN_API add_tribal_api_key_access WAT %', LOWER(params->>'email');
RETURN json_build_object(
'result', 'failure',
'message', 'Unknown error in access addition')::JSON;
@@ -352,20 +352,20 @@ BEGIN
SELECT EXISTS (
SELECT 1
FROM public.dissemination_TribalApiAccessKeyIds
- WHERE email = params->>'email'
+ WHERE LOWER(email) = LOWER(params->>'email')
)
INTO user_exists;
-- If the user exists, remove the record
IF user_exists THEN
DELETE FROM public.dissemination_TribalApiAccessKeyIds
- WHERE email = params->>'email';
- RAISE INFO 'ADMIN_API remove_tribal_api_key_access ACCESS_REMOVED %', params->>'email';
+ WHERE LOWER(email) = LOWER(params->>'email');
+ RAISE INFO 'ADMIN_API remove_tribal_api_key_access ACCESS_REMOVED %', LOWER(params->>'email');
RETURN json_build_object(
'result', 'success',
'message', 'Removed record')::JSON;
ELSE
- RAISE INFO 'ADMIN_API remove_tribal_api_key_access DID_NOT_EXIST %', params->>'email';
+ RAISE INFO 'ADMIN_API remove_tribal_api_key_access DID_NOT_EXIST %', LOWER(params->>'email');
RETURN json_build_object(
'result', 'failure',
'message', 'User did not exist in table')::JSON;
@@ -376,7 +376,7 @@ BEGIN
'result', 'failure',
'message', 'Admin user lacks DELETE permissions')::JSON; -- Return false if the API user doesn't have read permissions
END IF;
- RAISE INFO 'ADMIN_API add_tribal_api_key_access WAT %', params->>'email';
+ RAISE INFO 'ADMIN_API add_tribal_api_key_access WAT %', LOWER(params->>'email');
RETURN json_build_object(
'result', 'failure',
'message', 'Uknown error in access removal')::JSON;
diff --git a/docs/testing.md b/docs/testing.md
index d480e1dd3f..43f56f3391 100644
--- a/docs/testing.md
+++ b/docs/testing.md
@@ -17,6 +17,7 @@ We use [Django's test execution framework](https://docs.djangoproject.com/en/4.0
- [Linting](#linting)
- [End-to-end testing](#end-to-end-testing)
- [Testing behind Login.gov](#testing-behind-logingov)
+- [Admin API testing](#admin-api-testing)
## Packages
- [model_bakery](https://model-bakery.readthedocs.io/en/latest/), to help create data and instances within our tests
@@ -184,3 +185,30 @@ in files in [backend/cypress/e2e/](/backend/cypress/e2e). To run these tests:
Github repository. To use them in a Github Actions workflow, use the [Github
Actions secrets
store](https://docs.github.com/en/actions/security-guides/encrypted-secrets)
+
+# Admin API Testing
+For interfacing the admin API locally, we will need to account for a few things.
+
+## Resources
+
+For communicating with the Postgrest API, you could use one of the following extensions if you are using Visual Studio Code:
+- [REST Client](https://marketplace.visualstudio.com/items?itemName=humao.rest-client)
+- [Postman](https://marketplace.visualstudio.com/items?itemName=Postman.postman-for-vscode)
+
+Though keep in mind you should be able to use whatever API service you prefer to run.
+
+## Checklist
+
+1. When running `docker compose up`, make sure the `web-1` service completes the startup procedures.
+ - E.G., `STARTUP STARTUP_CHECK seed_cog_baseline PASS` should be one of the last startup logs. If this passes, then the API tables are up.
+2. Make sure you prepare your request headers with the following:
+ - `Authorization: Bearer {JWT}` is important for authenticating your requests.
+ - `x-api-user-id: {uuid}` should be populated with a user ID from the `support_administrative_key_uuids` table. You can create your own row in this table for local testing purposes.
+ - `x-api-key: {key}`
+ - `content-profile: {api-version}` is required for POST requests.
+ - `accept-profile: {api-version}` is required for GET requests.
+ - `Prefer: params=single-object` is needed for the API to read your payload. In most if not all cases, it is set to `params=single-object`.
+
+## "Cheat-sheet" for testing
+
+We have a `.rest` file in `/backend/support/api/admin_api_vX_X_X` that contains many of the admin API endpoints, as well the headers/parameters/payload that are needed to run it successfully.