Skip to content

Commit

Permalink
Merge pull request #3476 from GSA-TTS/main
Browse files Browse the repository at this point in the history
  • Loading branch information
jadudm authored Mar 5, 2024
2 parents cfd4a8a + 8f0fa0f commit 0bbc05a
Show file tree
Hide file tree
Showing 27 changed files with 811 additions and 79 deletions.
105 changes: 101 additions & 4 deletions backend/cypress/support/dissemination-table.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,10 @@

const API_GOV_JWT = Cypress.env('API_GOV_JWT') || '';
const API_GOV_KEY = Cypress.env('API_GOV_KEY') || '';
const API_GOV_USER_ID_ADMIN = Cypress.env('API_GOV_USER_ID_ADMIN');
const API_GOV_URL = Cypress.env('API_GOV_URL');
const API_VERSION = Cypress.env('API_VERSION');
const ADMIN_API_VERSION = Cypress.env('ADMIN_API_VERSION');

const requestOptions = {
method: 'GET',
Expand All @@ -15,7 +18,51 @@ const requestOptions = {
},
}

export function testReportIdNotFound(reportId) {
function grantTribalAccess(email, user_id) {
// use admin user to grant tribal access to user
cy.request({
method: 'POST',
url: `${API_GOV_URL}/rpc/add_tribal_api_key_access`,
headers: {
Authorization: `Bearer ${API_GOV_JWT}`,
'X-Api-Key': API_GOV_KEY,
'X-Api-User-Id': API_GOV_USER_ID_ADMIN,
'Content-Profile': ADMIN_API_VERSION,
'Content-Type': 'application/json',
'Prefer': 'params=single-object',
},
body: {
"email": `${email}`,
"key_id": `${user_id}`,
}
}).should((response) => {
expect(response.body).to.equal(true);
});
}

function revokeTribalAccess(email, user_id) {
// use admin user to revoke tribal access to user
cy.request({
method: 'POST',
url: `${API_GOV_URL}/rpc/remove_tribal_api_key_access`,
headers: {
Authorization: `Bearer ${API_GOV_JWT}`,
'X-Api-Key': API_GOV_KEY,
'X-Api-User-Id': API_GOV_USER_ID_ADMIN,
'Content-Profile': ADMIN_API_VERSION,
'Content-Type': 'application/json',
'Prefer': 'params=single-object',
},
body: {
"email": `${email}`,
"key_id": `${user_id}`,
}
}).should((response) => {
expect(response.body).to.equal(true);
});
}

export function testReportIdNotFoundWithoutTribalAccess(reportId) {
cy.request({
...requestOptions,
qs: {report_id: `eq.${reportId}`},
Expand All @@ -24,13 +71,63 @@ export function testReportIdNotFound(reportId) {
});
}

export function testReportIdFound(reportId) {
export function testReportIdFoundWithoutTribalAccess(reportId) {
cy.request({
...requestOptions,
qs: {report_id: `eq.${reportId}`},
}).should((response) => {
expect(response.body).to.have.length(1);
const hasAgency = !!(response.body[0]?.cognizant_agency || response.body[0]?.oversight_agency);
expect(hasAgency).to.be.true;
const hasAgency = response.body[0]?.cognizant_agency || response.body[0]?.oversight_agency;
expect(Boolean(hasAgency)).to.be.true;
});
}

export function testReportIdFoundWithTribalAccess(reportId) {
const tribal_access_email = `${crypto.randomUUID()}@example.com`;
const tribal_access_user_id = crypto.randomUUID();

grantTribalAccess(tribal_access_email, tribal_access_user_id);

// try to pull the tribal, non-public data from the API using the (now) privileged user
cy.request({
method: 'GET',
url: `${API_GOV_URL}/general`,
headers: {
Authorization: `Bearer ${API_GOV_JWT}`,
'X-Api-Key': API_GOV_KEY,
'X-Api-User-Id': tribal_access_user_id,
'Accept-Profile': API_VERSION
},
qs: {report_id: `eq.${reportId}`},
}).should((response) => {
expect(response.body).to.have.length(1);
const hasAgency = response.body[0]?.cognizant_agency || response.body[0]?.oversight_agency;
expect(Boolean(hasAgency)).to.be.true;
});

revokeTribalAccess(tribal_access_email, tribal_access_user_id);
}

export function testReportIdNotFoundWithTribalAccess(reportId) {
const tribal_access_email = `${crypto.randomUUID()}@example.com`;
const tribal_access_user_id = crypto.randomUUID();

grantTribalAccess(tribal_access_email, tribal_access_user_id);

// try to pull the tribal, non-public data from the API using the (now) privileged user
cy.request({
method: 'GET',
url: `${API_GOV_URL}/general`,
headers: {
Authorization: `Bearer ${API_GOV_JWT}`,
'X-Api-Key': API_GOV_KEY,
'X-Api-User-Id': tribal_access_user_id,
'Accept-Profile': API_VERSION
},
qs: {report_id: `eq.${reportId}`},
}).should((response) => {
expect(response.body).to.have.length(0);
});

revokeTribalAccess(tribal_access_email, tribal_access_user_id);
}
8 changes: 5 additions & 3 deletions backend/cypress/support/full-submission.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { testAuditInformationForm } from './audit-info-form.js';
import { testPdfAuditReport } from './report-pdf.js';
import { testAuditorCertification } from './auditor-certification.js';
import { testAuditeeCertification } from './auditee-certification.js';
import { testReportIdFound, testReportIdNotFound } from './dissemination-table.js';
import { testReportIdFoundWithTribalAccess, testReportIdFoundWithoutTribalAccess, testReportIdNotFoundWithoutTribalAccess } from './dissemination-table.js';
import { testTribalAuditPublic, testTribalAuditPrivate } from './tribal-audit-form.js';
import { testInitializeAudit } from './initialize-audit.js';
import {
Expand Down Expand Up @@ -131,9 +131,11 @@ export function testFullSubmission(isTribal, isPublic) {

// The Report should not be in the dissemination table
if (isPublic) {
testReportIdFound(reportId);
testReportIdFoundWithoutTribalAccess(reportId);
testReportIdFoundWithTribalAccess(reportId);
} else {
testReportIdNotFound(reportId);
testReportIdNotFoundWithoutTribalAccess(reportId);
testReportIdFoundWithTribalAccess(reportId);
}
});

Expand Down
5 changes: 3 additions & 2 deletions backend/cypress/support/initialize-audit.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { testValidAccess } from './check-access.js';
import { testValidEligibility } from './check-eligibility.js';
import { testValidAuditeeInfo } from './auditee-info.js';
import { testValidGeneralInfo } from './general-info.js';
import { testReportIdNotFound } from './dissemination-table.js';
import { testReportIdNotFoundWithTribalAccess, testReportIdNotFoundWithoutTribalAccess } from './dissemination-table.js';

export function testInitializeAudit(isTribal=false) {
// Check the terms and conditions link and click "Accept and start..."
Expand All @@ -22,7 +22,8 @@ export function testInitializeAudit(isTribal=false) {
// Report should not yet be in the dissemination table
cy.url().then(url => {
const reportId = url.split('/').pop();
testReportIdNotFound(reportId);
testReportIdNotFoundWithTribalAccess(reportId);
testReportIdNotFoundWithoutTribalAccess(reportId);
});

// Fill out the general info form
Expand Down
File renamed without changes.
40 changes: 40 additions & 0 deletions backend/dissemination/api/api_v1_1_0/create_functions.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
-- WARNING
-- Under PostgreSQL 12, the functions below work.
-- Under PostgreSQL 14, these will break.
--
-- Note the differences:
--
-- raise info 'Works under PostgreSQL 12';
-- raise info 'request.header.x-magic %', (SELECT current_setting('request.header.x-magic', true));
-- raise info 'request.jwt.claim.expires %', (SELECT current_setting('request.jwt.claim.expires', true));
-- raise info 'Works under PostgreSQL 14';
-- raise info 'request.headers::json->>x-magic %', (SELECT current_setting('request.headers', true)::json->>'x-magic');
-- raise info 'request.jwt.claims::json->expires %', (SELECT current_setting('request.jwt.claims', true)::json->>'expires');
--
-- To quote the work of Dav Pilkey, "remember this now."

-- We don't grant tribal access (yet)
create or replace function api_v1_1_0_functions.has_tribal_data_access()
returns boolean
as $has_tribal_data_access$
DECLARE
uuid_header UUID;
key_exists boolean;
BEGIN

SELECT admin_api_v1_1_0_functions.get_api_key_uuid() INTO uuid_header;
SELECT
CASE WHEN EXISTS (
SELECT key_id
FROM public.dissemination_TribalApiAccessKeyIds taaki
WHERE taaki.key_id = uuid_header::TEXT)
THEN 1::BOOLEAN
ELSE 0::BOOLEAN
END
INTO key_exists;
RETURN key_exists;
END;
$has_tribal_data_access$ LANGUAGE plpgsql;


NOTIFY pgrst, 'reload schema';
49 changes: 49 additions & 0 deletions backend/dissemination/api/api_v1_1_0/create_schema.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
begin;

do
$$
begin
DROP SCHEMA IF EXISTS api_v1_1_0 CASCADE;
DROP SCHEMA IF EXISTS api_v1_1_0_functions CASCADE;

if not exists (select schema_name from information_schema.schemata where schema_name = 'api_v1_1_0') then
create schema api_v1_1_0;
create schema api_v1_1_0_functions;

grant usage on schema api_v1_1_0_functions to api_fac_gov;

-- Grant access to tables and views
alter default privileges
in schema api_v1_1_0
grant select
-- this includes views
on tables
to api_fac_gov;

-- Grant access to sequences, if we have them
grant usage on schema api_v1_1_0 to api_fac_gov;
grant select, usage on all sequences in schema api_v1_1_0 to api_fac_gov;
alter default privileges
in schema api_v1_1_0
grant select, usage
on sequences
to api_fac_gov;
end if;
end
$$
;

-- This is the description
COMMENT ON SCHEMA api_v1_1_0 IS
'The FAC dissemation API version 1.0.3.'
;

-- https://postgrest.org/en/stable/references/api/openapi.html
-- This is the title
COMMENT ON SCHEMA api_v1_1_0 IS 'A RESTful API that serves data from the SF-SAC.';

commit;

notify pgrst,
'reload schema';

Loading

0 comments on commit 0bbc05a

Please sign in to comment.