Skip to content

Commit

Permalink
Convert new Admin API emails to lowercase (#4225)
Browse files Browse the repository at this point in the history
* 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` .
  • Loading branch information
rnovak338 committed Sep 5, 2024
1 parent 0aafe98 commit deb03cb
Show file tree
Hide file tree
Showing 2 changed files with 44 additions and 16 deletions.
32 changes: 16 additions & 16 deletions backend/support/api/admin_api_v1_1_0/create_functions.sql
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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;
Expand Down Expand Up @@ -222,15 +222,15 @@ 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;
-- If that is greater than zero, we were successful.
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;
Expand Down Expand Up @@ -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;
Expand All @@ -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;
Expand All @@ -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;
Expand All @@ -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;
Expand All @@ -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;
Expand Down
28 changes: 28 additions & 0 deletions docs/testing.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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.

0 comments on commit deb03cb

Please sign in to comment.