From 23d05a0ad45f2918996e4c77c9dd567fe9bed11c Mon Sep 17 00:00:00 2001 From: Rory Doak <138574807+RODO94@users.noreply.github.com> Date: Mon, 28 Oct 2024 16:36:49 +0000 Subject: [PATCH] feat: add demoUser role to Hasura with permissions (#3852) --- hasura.planx.uk/metadata/tables.yaml | 338 ++++++++++++++++-- .../down.sql | 1 + .../up.sql | 1 + hasura.planx.uk/tests/analytics.test.js | 15 + hasura.planx.uk/tests/blpu_codes.test.js | 15 + .../tests/bops_applications.test.js | 15 + .../tests/email_applications.test.js | 15 + hasura.planx.uk/tests/feedback.test.js | 65 ++-- hasura.planx.uk/tests/feedback_status.test.js | 193 +++++----- .../tests/feedback_type_enum.test.js | 193 +++++----- .../tests/flow_document_templates.test.js | 15 + .../tests/flow_status_history.test.js | 15 + hasura.planx.uk/tests/flows.test.js | 52 +++ hasura.planx.uk/tests/global_settings.test.js | 15 + hasura.planx.uk/tests/lowcal_sessions.test.js | 274 ++++++++++---- .../tests/payment_requests.test.js | 46 ++- hasura.planx.uk/tests/payment_status.test.js | 17 +- .../planning_constraints_requests.test.js | 27 +- .../tests/reconciliation_requests.test.js | 15 + hasura.planx.uk/tests/sessions.test.js | 18 +- .../tests/team_integrations.test.js | 15 + hasura.planx.uk/tests/team_members.test.js | 17 +- hasura.planx.uk/tests/team_themes.test.js | 15 + hasura.planx.uk/tests/teams.test.js | 15 + .../tests/uniform_applications.test.js | 17 +- hasura.planx.uk/tests/users.test.js | 15 + hasura.planx.uk/tests/utils.js | 64 ++-- 27 files changed, 1138 insertions(+), 365 deletions(-) create mode 100644 hasura.planx.uk/migrations/1729675271585_insert_into_public_user_roles/down.sql create mode 100644 hasura.planx.uk/migrations/1729675271585_insert_into_public_user_roles/up.sql diff --git a/hasura.planx.uk/metadata/tables.yaml b/hasura.planx.uk/metadata/tables.yaml index 78a98cbf2f..2a737e6cc7 100644 --- a/hasura.planx.uk/metadata/tables.yaml +++ b/hasura.planx.uk/metadata/tables.yaml @@ -157,7 +157,7 @@ definition: enable_manual: false insert: - columns: '*' + columns: "*" retry_conf: interval_sec: 30 num_retries: 1 @@ -171,7 +171,7 @@ query_params: type: bops-submission template_engine: Kriti - url: '{{$base_url}}/webhooks/hasura/send-slack-notification' + url: "{{$base_url}}/webhooks/hasura/send-slack-notification" version: 2 - table: name: document_template @@ -225,7 +225,7 @@ definition: enable_manual: false insert: - columns: '*' + columns: "*" retry_conf: interval_sec: 30 num_retries: 1 @@ -239,7 +239,7 @@ query_params: type: email-submission template_engine: Kriti - url: '{{$base_url}}/webhooks/hasura/send-slack-notification' + url: "{{$base_url}}/webhooks/hasura/send-slack-notification" version: 2 - table: name: feedback @@ -301,6 +301,36 @@ name: teams schema: public select_permissions: + - role: demoUser + permission: + columns: + - feedback_id + - device + - node_data + - address + - feedback_type + - help_definition + - help_sources + - help_text + - intersecting_constraints + - node_id + - node_text + - node_title + - node_type + - project_type + - service_slug + - status + - team_slug + - uprn + - user_comment + - user_context + - created_at + filter: + team: + flows: + creator_id: + _eq: x-hasura-user-id + comment: "" - role: platformAdmin permission: columns: @@ -451,10 +481,28 @@ forward_client_headers: false headers: - name: authorization - value: '{{HASURA_PLANX_API_KEY}}' + value: "{{HASURA_PLANX_API_KEY}}" timeout: 10 - url: '{{HASURA_PLANX_API_URL}}/webhooks/hasura/validate-input/jsonb/clean-html' + url: "{{HASURA_PLANX_API_URL}}/webhooks/hasura/validate-input/jsonb/clean-html" type: http + - role: demoUser + permission: + check: + team_id: + _eq: 32 + columns: + - copied_from + - created_at + - creator_id + - data + - id + - name + - settings + - slug + - team_id + - updated_at + - version + comment: They can only insert into Demo team [id = 32] - role: platformAdmin permission: check: {} @@ -475,9 +523,9 @@ forward_client_headers: false headers: - name: authorization - value: '{{HASURA_PLANX_API_KEY}}' + value: "{{HASURA_PLANX_API_KEY}}" timeout: 10 - url: '{{HASURA_PLANX_API_URL}}/webhooks/hasura/validate-input/jsonb/clean-html' + url: "{{HASURA_PLANX_API_URL}}/webhooks/hasura/validate-input/jsonb/clean-html" type: http - role: teamEditor permission: @@ -506,9 +554,9 @@ forward_client_headers: false headers: - name: authorization - value: '{{HASURA_PLANX_API_KEY}}' + value: "{{HASURA_PLANX_API_KEY}}" timeout: 10 - url: '{{HASURA_PLANX_API_URL}}/webhooks/hasura/validate-input/jsonb/clean-html' + url: "{{HASURA_PLANX_API_URL}}/webhooks/hasura/validate-input/jsonb/clean-html" type: http select_permissions: - role: api @@ -530,6 +578,37 @@ - data_merged filter: {} allow_aggregations: true + - role: demoUser + permission: + columns: + - created_at + - creator_id + - data + - id + - name + - settings + - slug + - status + - team_id + - updated_at + - version + computed_fields: + - data_merged + filter: + _or: + - _and: + - creator_id: + _eq: x-hasura-user-id + - team: + id: + _eq: 32 + - team: + id: + _in: + - 1 + - 29 + - 30 + comment: "For the demo user, we want to ensure they can only see their own flows, and flows from the Open Digital Planning [id = 30], Open Systems Lab [id = 1], and Templates [id = 29] team " - role: platformAdmin permission: columns: @@ -567,6 +646,7 @@ - data_merged filter: {} allow_aggregations: true + comment: "" - role: teamEditor permission: columns: @@ -609,10 +689,32 @@ forward_client_headers: false headers: - name: authorization - value: '{{HASURA_PLANX_API_KEY}}' + value: "{{HASURA_PLANX_API_KEY}}" timeout: 10 - url: '{{HASURA_PLANX_API_URL}}/webhooks/hasura/validate-input/jsonb/clean-html' + url: "{{HASURA_PLANX_API_URL}}/webhooks/hasura/validate-input/jsonb/clean-html" type: http + - role: demoUser + permission: + columns: + - copied_from + - created_at + - creator_id + - data + - id + - name + - settings + - slug + - team_id + - updated_at + - version + filter: + _and: + - team_id: + _eq: 32 + - creator_id: + _eq: x-hasura-user-id + check: null + comment: "" - role: platformAdmin permission: columns: @@ -629,9 +731,9 @@ forward_client_headers: false headers: - name: authorization - value: '{{HASURA_PLANX_API_KEY}}' + value: "{{HASURA_PLANX_API_KEY}}" timeout: 10 - url: '{{HASURA_PLANX_API_URL}}/webhooks/hasura/validate-input/jsonb/clean-html' + url: "{{HASURA_PLANX_API_URL}}/webhooks/hasura/validate-input/jsonb/clean-html" type: http - role: teamEditor permission: @@ -656,11 +758,20 @@ forward_client_headers: false headers: - name: authorization - value: '{{HASURA_PLANX_API_KEY}}' + value: "{{HASURA_PLANX_API_KEY}}" timeout: 10 - url: '{{HASURA_PLANX_API_URL}}/webhooks/hasura/validate-input/jsonb/clean-html' + url: "{{HASURA_PLANX_API_URL}}/webhooks/hasura/validate-input/jsonb/clean-html" type: http delete_permissions: + - role: demoUser + permission: + filter: + _and: + - team_id: + _eq: 32 + - creator_id: + _eq: x-hasura-user-id + comment: "" - role: platformAdmin permission: filter: {} @@ -689,11 +800,16 @@ forward_client_headers: false headers: - name: authorization - value: '{{HASURA_PLANX_API_KEY}}' + value: "{{HASURA_PLANX_API_KEY}}" timeout: 10 - url: '{{HASURA_PLANX_API_URL}}/webhooks/hasura/validate-input/jsonb/clean-html' + url: "{{HASURA_PLANX_API_URL}}/webhooks/hasura/validate-input/jsonb/clean-html" type: http select_permissions: + - role: demoUser + permission: + columns: + - footer_content + filter: {} - role: platformAdmin permission: columns: @@ -724,9 +840,9 @@ forward_client_headers: false headers: - name: authorization - value: '{{HASURA_PLANX_API_KEY}}' + value: "{{HASURA_PLANX_API_KEY}}" timeout: 10 - url: '{{HASURA_PLANX_API_URL}}/webhooks/hasura/validate-input/jsonb/clean-html' + url: "{{HASURA_PLANX_API_URL}}/webhooks/hasura/validate-input/jsonb/clean-html" type: http - table: name: lowcal_sessions @@ -873,7 +989,7 @@ method: POST query_params: {} template_engine: Kriti - url: '{{$base_url}}/send-email/confirmation' + url: "{{$base_url}}/send-email/confirmation" version: 2 - name: setup_lowcal_expiry_events definition: @@ -903,7 +1019,7 @@ method: POST query_params: {} template_engine: Kriti - url: '{{$base_url}}/webhooks/hasura/create-expiry-event' + url: "{{$base_url}}/webhooks/hasura/create-expiry-event" version: 2 - name: setup_lowcal_reminder_events definition: @@ -933,7 +1049,7 @@ method: POST query_params: {} template_engine: Kriti - url: '{{$base_url}}/webhooks/hasura/create-reminder-event' + url: "{{$base_url}}/webhooks/hasura/create-reminder-event" version: 2 - table: name: operations @@ -946,6 +1062,14 @@ using: foreign_key_constraint_on: flow_id insert_permissions: + - role: demoUser + permission: + check: + flow: + creator_id: + _eq: x-hausra-user-id + columns: [] + comment: "" - role: platformAdmin permission: check: {} @@ -977,6 +1101,18 @@ - updated_at - flow_id select_permissions: + - role: demoUser + permission: + columns: + - id + - actor_id + - version + - data + - created_at + - updated_at + - flow_id + filter: {} + comment: "" - role: platformAdmin permission: columns: @@ -1000,6 +1136,15 @@ - updated_at filter: {} update_permissions: + - role: demoUser + permission: + columns: [] + filter: + flow: + creator_id: + _eq: x-hausra-user-id + check: null + comment: "" - role: platformAdmin permission: columns: @@ -1110,7 +1255,7 @@ definition: enable_manual: false insert: - columns: '*' + columns: "*" retry_conf: interval_sec: 10 num_retries: 3 @@ -1132,13 +1277,13 @@ method: POST query_params: {} template_engine: Kriti - url: '{{$base_url}}/webhooks/hasura/create-payment-expiry-events' + url: "{{$base_url}}/webhooks/hasura/create-payment-expiry-events" version: 2 - name: setup_payment_invitation_events definition: enable_manual: false insert: - columns: '*' + columns: "*" retry_conf: interval_sec: 10 num_retries: 3 @@ -1160,13 +1305,13 @@ method: POST query_params: {} template_engine: Kriti - url: '{{$base_url}}/webhooks/hasura/create-payment-invitation-events' + url: "{{$base_url}}/webhooks/hasura/create-payment-invitation-events" version: 2 - name: setup_payment_reminder_events definition: enable_manual: false insert: - columns: '*' + columns: "*" retry_conf: interval_sec: 10 num_retries: 3 @@ -1188,7 +1333,7 @@ method: POST query_params: {} template_engine: Kriti - url: '{{$base_url}}/webhooks/hasura/create-payment-reminder-events' + url: "{{$base_url}}/webhooks/hasura/create-payment-reminder-events" version: 2 - name: setup_payment_send_events definition: @@ -1217,7 +1362,7 @@ method: POST query_params: {} template_engine: Kriti - url: '{{$base_url}}/webhooks/hasura/create-payment-send-events' + url: "{{$base_url}}/webhooks/hasura/create-payment-send-events" version: 2 - table: name: payment_status @@ -1304,6 +1449,25 @@ - created_at - flow_id - data + - role: demoUser + permission: + check: + _and: + - flow: + creator_id: + _eq: x-hasura-user-id + - flow: + team: + id: + _eq: 32 + columns: + - id + - publisher_id + - summary + - created_at + - flow_id + - data + comment: A demoUser can only insert a published flow for their own flows and for flows inside the Demo team [id = 32] - role: platformAdmin permission: check: {} @@ -1344,6 +1508,17 @@ - data filter: {} allow_aggregations: true + - role: demoUser + permission: + columns: + - created_at + - data + - flow_id + - id + - publisher_id + - summary + filter: {} + allow_aggregations: true - role: platformAdmin permission: columns: @@ -1449,12 +1624,12 @@ definition: enable_manual: false insert: - columns: '*' + columns: "*" retry_conf: interval_sec: 30 num_retries: 1 timeout_sec: 60 - webhook: '{{HASURA_PLANX_API_URL}}' + webhook: "{{HASURA_PLANX_API_URL}}" headers: - name: authorization value_from_env: HASURA_PLANX_API_KEY @@ -1463,7 +1638,7 @@ query_params: type: s3-submission template_engine: Kriti - url: '{{$base_url}}/webhooks/hasura/send-slack-notification' + url: "{{$base_url}}/webhooks/hasura/send-slack-notification" version: 2 - table: name: sessions @@ -1532,6 +1707,19 @@ name: submission_services_log schema: public select_permissions: + - role: demoUser + permission: + columns: + - retry + - response + - event_id + - event_type + - status + - created_at + - flow_id + - session_id + filter: {} + comment: "" - role: platformAdmin permission: columns: @@ -1562,6 +1750,38 @@ name: submission_services_summary schema: public select_permissions: + - role: demoUser + permission: + columns: + - number_times_resumed + - sent_to_bops + - sent_to_email + - sent_to_s3_power_automate + - sent_to_uniform + - user_clicked_save + - user_invited_to_pay + - session_length_days + - bops_applications + - email_applications + - payment_requests + - payment_status + - s3_applications + - uniform_applications + - allow_list_answers + - application_declaration_connection + - draw_boundary_action + - find_property_action + - property_constraints_planning + - property_type + - proposal_project_type + - user_role + - service_slug + - session_id + - team_slug + - created_at + - submitted_at + filter: {} + comment: 'For future, if this moves outside of the Flow to somewhere like Team, we should update "demoUser" to only see submission data related to only their flows. ' - role: platformAdmin permission: columns: @@ -1672,6 +1892,14 @@ - staging_govpay_secret - power_automate_webhook_url filter: {} + - role: demoUser + permission: + columns: + - has_planning_data + - id + - team_id + filter: {} + comment: "" - role: platformAdmin permission: columns: @@ -1929,6 +2157,18 @@ - team_id filter: {} comment: "" + - role: demoUser + permission: + columns: + - action_colour + - favicon + - id + - link_colour + - logo + - primary_colour + - team_id + filter: {} + comment: "" - role: platformAdmin permission: columns: @@ -2057,6 +2297,23 @@ - slug - updated_at filter: {} + - role: demoUser + permission: + columns: + - created_at + - domain + - id + - name + - slug + - updated_at + filter: + id: + _in: + - 1 + - 29 + - 30 + - 32 + comment: "For the demo user, we want to ensure they can only see their own team [id = 32], and the teams: Open Digital Planning [id = 30], Open Systems Lab [id = 1], and Templates [id = 29] team " - role: platformAdmin permission: columns: @@ -2173,7 +2430,7 @@ definition: enable_manual: false insert: - columns: '*' + columns: "*" retry_conf: interval_sec: 30 num_retries: 1 @@ -2187,7 +2444,7 @@ query_params: type: uniform-submission template_engine: Kriti - url: '{{$base_url}}/webhooks/hasura/send-slack-notification' + url: "{{$base_url}}/webhooks/hasura/send-slack-notification" version: 2 - table: name: user_roles @@ -2243,6 +2500,17 @@ - last_name - updated_at filter: {} + - role: demoUser + permission: + columns: + - created_at + - email + - first_name + - id + - is_platform_admin + - last_name + - updated_at + filter: {} - role: platformAdmin permission: columns: diff --git a/hasura.planx.uk/migrations/1729675271585_insert_into_public_user_roles/down.sql b/hasura.planx.uk/migrations/1729675271585_insert_into_public_user_roles/down.sql new file mode 100644 index 0000000000..6ab981936e --- /dev/null +++ b/hasura.planx.uk/migrations/1729675271585_insert_into_public_user_roles/down.sql @@ -0,0 +1 @@ +DELETE FROM "public"."user_roles" WHERE "value" = 'demoUser'; diff --git a/hasura.planx.uk/migrations/1729675271585_insert_into_public_user_roles/up.sql b/hasura.planx.uk/migrations/1729675271585_insert_into_public_user_roles/up.sql new file mode 100644 index 0000000000..bacb81581c --- /dev/null +++ b/hasura.planx.uk/migrations/1729675271585_insert_into_public_user_roles/up.sql @@ -0,0 +1 @@ +INSERT INTO "public"."user_roles"("value") VALUES (E'demoUser'); diff --git a/hasura.planx.uk/tests/analytics.test.js b/hasura.planx.uk/tests/analytics.test.js index 63b69a6d26..12120fa4e8 100644 --- a/hasura.planx.uk/tests/analytics.test.js +++ b/hasura.planx.uk/tests/analytics.test.js @@ -76,6 +76,21 @@ describe("analytics and analytics_logs", () => { }); }); + describe("demoUser", () => { + let i; + beforeAll(async () => { + i = await introspectAs("demoUser"); + }); + + test("cannot query analytics_logs", () => { + expect(i.queries).not.toContain("analytics_logs"); + }); + + test("cannot create, update, or delete analytics_logs", () => { + expect(i).toHaveNoMutationsFor("analytics_logs"); + }); + }); + describe("api", () => { beforeAll(async () => { i = await introspectAs("api"); diff --git a/hasura.planx.uk/tests/blpu_codes.test.js b/hasura.planx.uk/tests/blpu_codes.test.js index 7614433e41..32cb4e0c2c 100644 --- a/hasura.planx.uk/tests/blpu_codes.test.js +++ b/hasura.planx.uk/tests/blpu_codes.test.js @@ -59,6 +59,21 @@ describe("blpu_codes", () => { }); }); + describe("demoUser", () => { + let i; + beforeAll(async () => { + i = await introspectAs("demoUser"); + }); + + test("can query blpu_codes", () => { + expect(i.queries).not.toContain("blpu_codes"); + }); + + test("cannot create, update, or delete blpu_codes", () => { + expect(i).toHaveNoMutationsFor("blpu_codes"); + }); + }); + describe("api", () => { let i; beforeAll(async () => { diff --git a/hasura.planx.uk/tests/bops_applications.test.js b/hasura.planx.uk/tests/bops_applications.test.js index 91001b1262..7ef50991a2 100644 --- a/hasura.planx.uk/tests/bops_applications.test.js +++ b/hasura.planx.uk/tests/bops_applications.test.js @@ -60,6 +60,21 @@ describe("bops_applications", () => { }); }); + describe("demoUser", () => { + let i; + beforeAll(async () => { + i = await introspectAs("demoUser"); + }); + + test("cannot query bops applications", () => { + expect(i.queries).not.toContain("bops_applications"); + }); + + test("cannot create, update, or delete bops applications", () => { + expect(i).toHaveNoMutationsFor("bops_applications"); + }); + }); + describe("api", () => { let i; beforeAll(async () => { diff --git a/hasura.planx.uk/tests/email_applications.test.js b/hasura.planx.uk/tests/email_applications.test.js index 11db01eed5..df0a92416d 100644 --- a/hasura.planx.uk/tests/email_applications.test.js +++ b/hasura.planx.uk/tests/email_applications.test.js @@ -60,6 +60,21 @@ describe("email_applications", () => { expect(i).toHaveNoMutationsFor("email_applications"); }); }); + + describe("demoUser", () => { + let i; + beforeAll(async () => { + i = await introspectAs("demoUser"); + }); + + test("cannot query email_applications", () => { + expect(i.queries).not.toContain("email_applications"); + }); + + test("cannot create, update, or delete email_applications", () => { + expect(i).toHaveNoMutationsFor("email_applications"); + }); + }); describe("api", () => { let i; diff --git a/hasura.planx.uk/tests/feedback.test.js b/hasura.planx.uk/tests/feedback.test.js index 07d5e4b4da..4d650b1e17 100644 --- a/hasura.planx.uk/tests/feedback.test.js +++ b/hasura.planx.uk/tests/feedback.test.js @@ -8,21 +8,21 @@ describe("feedback", () => { }); test("cannot query feedback", () => { - expect(i.queries).not.toContain("feedback"); - }); - - test("cannot update feedback", () => { - expect(i.mutations).not.toContain("update_feedback"); - expect(i.mutations).not.toContain("update_feedback_by_pk"); - }); - - test("cannot delete feedback", async () => { - expect(i.mutations).not.toContain("delete_feedback"); - }); - - test("can insert feedback", async () => { - expect(i.mutations).toContain("insert_feedback"); - }); + expect(i.queries).not.toContain("feedback"); + }); + + test("cannot update feedback", () => { + expect(i.mutations).not.toContain("update_feedback"); + expect(i.mutations).not.toContain("update_feedback_by_pk"); + }); + + test("cannot delete feedback", async () => { + expect(i.mutations).not.toContain("delete_feedback"); + }); + + test("can insert feedback", async () => { + expect(i.mutations).toContain("insert_feedback"); + }); }); describe("admin", () => { @@ -32,13 +32,13 @@ describe("feedback", () => { }); test("has full access to query and mutate feedback", () => { - expect(i.mutations).toContain("insert_feedback"); - expect(i.mutations).toContain("insert_feedback_one"); - expect(i.mutations).toContain("update_feedback"); - expect(i.mutations).toContain("update_feedback_by_pk"); - expect(i.mutations).toContain("update_feedback_many"); - expect(i.mutations).toContain("delete_feedback"); - expect(i.mutations).toContain("delete_feedback_by_pk"); + expect(i.mutations).toContain("insert_feedback"); + expect(i.mutations).toContain("insert_feedback_one"); + expect(i.mutations).toContain("update_feedback"); + expect(i.mutations).toContain("update_feedback_by_pk"); + expect(i.mutations).toContain("update_feedback_many"); + expect(i.mutations).toContain("delete_feedback"); + expect(i.mutations).toContain("delete_feedback_by_pk"); }); }); @@ -50,11 +50,10 @@ describe("feedback", () => { test("cannot query feedback", () => { expect(i.queries).not.toContain("feedback"); - }); test("cannot mutate feedback", async () => { - expect(i).toHaveNoMutationsFor("feedback") + expect(i).toHaveNoMutationsFor("feedback"); }); }); @@ -66,11 +65,25 @@ describe("feedback", () => { test("cannot query feedback", () => { expect(i.queries).not.toContain("feedback"); + }); + + test("cannot mutate feedback", async () => { + expect(i).toHaveNoMutationsFor("feedback"); + }); + }); + + describe("demoUser", () => { + let i; + beforeAll(async () => { + i = await introspectAs("demoUser"); + }); + test("cannot query feedback", () => { + expect(i.queries).not.toContain("feedback"); }); test("cannot mutate feedback", async () => { - expect(i).toHaveNoMutationsFor("feedback") + expect(i).toHaveNoMutationsFor("feedback"); }); }); @@ -92,7 +105,7 @@ describe("feedback", () => { test("can delete feedback", async () => { expect(i.mutations).toContain("delete_feedback"); }); - + test("cannot insert feedback", async () => { expect(i.mutations).not.toContain("insert_feedback"); }); diff --git a/hasura.planx.uk/tests/feedback_status.test.js b/hasura.planx.uk/tests/feedback_status.test.js index d3483584d2..85b66dce8c 100644 --- a/hasura.planx.uk/tests/feedback_status.test.js +++ b/hasura.planx.uk/tests/feedback_status.test.js @@ -1,92 +1,107 @@ const { introspectAs } = require("./utils"); describe("feedback_status_enum", () => { - describe("public", () => { - let i; - beforeAll(async () => { - i = await introspectAs("public"); - }); - - test("cannot INSERT records", () => { - expect(i.mutations).not.toContain("insert_feedback_status_enum"); - }); - - test("cannot QUERY records", () => { - expect(i.queries).not.toContain("feedback_status_enum"); - }); - - test("cannot DELETE records", () => { - expect(i.mutations).not.toContain("delete_feedback_status_enum"); - }); - - test("cannot UPDATE records", () => { - expect(i.mutations).not.toContain("update_feedback_status_enum"); - }); - }); - - describe("admin", () => { - let i; - beforeAll(async () => { - i = await introspectAs("admin"); - }); - - test("has full access to query and mutate feedback_status_enum", async () => { - expect(i.queries).toContain("feedback_status_enum"); - expect(i.mutations).toContain("insert_feedback_status_enum"); - expect(i.mutations).toContain("delete_feedback_status_enum"); - }); - }); - - describe("platformAdmin", () => { - let i; - beforeAll(async () => { - i = await introspectAs("platformAdmin"); - }); - - test("cannot query feedback_status_enum", () => { - expect(i.queries).not.toContain("feedback_status_enum"); - }); - - test("cannot create, update, or delete feedback_status_enum", () => { - expect(i).toHaveNoMutationsFor("feedback_status_enum"); - }); - }); - - describe("teamEditor", () => { - let i; - beforeAll(async () => { - i = await introspectAs("teamEditor"); - }); - - test("cannot query feedback_status_enum", () => { - expect(i.queries).not.toContain("feedback_status_enum"); - }); - - test("cannot create, update, or delete feedback_status_enum", () => { - expect(i).toHaveNoMutationsFor("feedback_status_enum"); - }); - }); - - describe("api", () => { - let i; - beforeAll(async () => { - i = await introspectAs("api"); - }); - - test("cannot INSERT records", () => { - expect(i.mutations).not.toContain("insert_feedback_status_enum"); - }); - - test("cannot QUERY records", () => { - expect(i.queries).not.toContain("feedback_status_enum"); - }); - - test("cannot DELETE records", () => { - expect(i.mutations).not.toContain("delete_feedback_status_enum"); - }); - - test("cannot UPDATE records", () => { - expect(i.mutations).not.toContain("update_feedback_status_enum"); - }); - }); - }); + describe("public", () => { + let i; + beforeAll(async () => { + i = await introspectAs("public"); + }); + + test("cannot INSERT records", () => { + expect(i.mutations).not.toContain("insert_feedback_status_enum"); + }); + + test("cannot QUERY records", () => { + expect(i.queries).not.toContain("feedback_status_enum"); + }); + + test("cannot DELETE records", () => { + expect(i.mutations).not.toContain("delete_feedback_status_enum"); + }); + + test("cannot UPDATE records", () => { + expect(i.mutations).not.toContain("update_feedback_status_enum"); + }); + }); + + describe("admin", () => { + let i; + beforeAll(async () => { + i = await introspectAs("admin"); + }); + + test("has full access to query and mutate feedback_status_enum", async () => { + expect(i.queries).toContain("feedback_status_enum"); + expect(i.mutations).toContain("insert_feedback_status_enum"); + expect(i.mutations).toContain("delete_feedback_status_enum"); + }); + }); + + describe("platformAdmin", () => { + let i; + beforeAll(async () => { + i = await introspectAs("platformAdmin"); + }); + + test("cannot query feedback_status_enum", () => { + expect(i.queries).not.toContain("feedback_status_enum"); + }); + + test("cannot create, update, or delete feedback_status_enum", () => { + expect(i).toHaveNoMutationsFor("feedback_status_enum"); + }); + }); + + describe("teamEditor", () => { + let i; + beforeAll(async () => { + i = await introspectAs("teamEditor"); + }); + + test("cannot query feedback_status_enum", () => { + expect(i.queries).not.toContain("feedback_status_enum"); + }); + + test("cannot create, update, or delete feedback_status_enum", () => { + expect(i).toHaveNoMutationsFor("feedback_status_enum"); + }); + }); + + describe("demoUser", () => { + let i; + beforeAll(async () => { + i = await introspectAs("demoUser"); + }); + + test("cannot query feedback_status_enum", () => { + expect(i.queries).not.toContain("feedback_status_enum"); + }); + + test("cannot create, update, or delete feedback_status_enum", () => { + expect(i).toHaveNoMutationsFor("feedback_status_enum"); + }); + }); + + describe("api", () => { + let i; + beforeAll(async () => { + i = await introspectAs("api"); + }); + + test("cannot INSERT records", () => { + expect(i.mutations).not.toContain("insert_feedback_status_enum"); + }); + + test("cannot QUERY records", () => { + expect(i.queries).not.toContain("feedback_status_enum"); + }); + + test("cannot DELETE records", () => { + expect(i.mutations).not.toContain("delete_feedback_status_enum"); + }); + + test("cannot UPDATE records", () => { + expect(i.mutations).not.toContain("update_feedback_status_enum"); + }); + }); +}); diff --git a/hasura.planx.uk/tests/feedback_type_enum.test.js b/hasura.planx.uk/tests/feedback_type_enum.test.js index b79d487bfe..4048dbec27 100644 --- a/hasura.planx.uk/tests/feedback_type_enum.test.js +++ b/hasura.planx.uk/tests/feedback_type_enum.test.js @@ -1,92 +1,107 @@ const { introspectAs } = require("./utils"); describe("feedback_type_enum", () => { - describe("public", () => { - let i; - beforeAll(async () => { - i = await introspectAs("public"); - }); - - test("cannot INSERT records", () => { - expect(i.mutations).not.toContain("insert_feedback_type_enum"); - }); - - test("cannot QUERY records", () => { - expect(i.queries).not.toContain("feedback_type_enum"); - }); - - test("cannot DELETE records", () => { - expect(i.mutations).not.toContain("delete_feedback_type_enum"); - }); - - test("cannot UPDATE records", () => { - expect(i.mutations).not.toContain("update_feedback_type_enum"); - }); - }); - - describe("admin", () => { - let i; - beforeAll(async () => { - i = await introspectAs("admin"); - }); - - test("has full access to query and mutate feedback_type_enum", async () => { - expect(i.queries).toContain("feedback_type_enum"); - expect(i.mutations).toContain("insert_feedback_type_enum"); - expect(i.mutations).toContain("delete_feedback_type_enum"); - }); - }); - - describe("platformAdmin", () => { - let i; - beforeAll(async () => { - i = await introspectAs("platformAdmin"); - }); - - test("cannot query feedback_type_enum", () => { - expect(i.queries).not.toContain("feedback_type_enum"); - }); - - test("cannot create, update, or delete feedback_type_enum", () => { - expect(i).toHaveNoMutationsFor("feedback_type_enum"); - }); - }); - - describe("teamEditor", () => { - let i; - beforeAll(async () => { - i = await introspectAs("teamEditor"); - }); - - test("cannot query feedback_type_enum", () => { - expect(i.queries).not.toContain("feedback_type_enum"); - }); - - test("cannot create, update, or delete feedback_type_enum", () => { - expect(i).toHaveNoMutationsFor("feedback_type_enum"); - }); - }); - - describe("api", () => { - let i; - beforeAll(async () => { - i = await introspectAs("api"); - }); - - test("cannot INSERT records", () => { - expect(i.mutations).not.toContain("insert_feedback_type_enum"); - }); - - test("cannot QUERY records", () => { - expect(i.queries).not.toContain("feedback_type_enum"); - }); - - test("cannot DELETE records", () => { - expect(i.mutations).not.toContain("delete_feedback_type_enum"); - }); - - test("cannot UPDATE records", () => { - expect(i.mutations).not.toContain("update_feedback_type_enum"); - }); - }); - }); + describe("public", () => { + let i; + beforeAll(async () => { + i = await introspectAs("public"); + }); + + test("cannot INSERT records", () => { + expect(i.mutations).not.toContain("insert_feedback_type_enum"); + }); + + test("cannot QUERY records", () => { + expect(i.queries).not.toContain("feedback_type_enum"); + }); + + test("cannot DELETE records", () => { + expect(i.mutations).not.toContain("delete_feedback_type_enum"); + }); + + test("cannot UPDATE records", () => { + expect(i.mutations).not.toContain("update_feedback_type_enum"); + }); + }); + + describe("admin", () => { + let i; + beforeAll(async () => { + i = await introspectAs("admin"); + }); + + test("has full access to query and mutate feedback_type_enum", async () => { + expect(i.queries).toContain("feedback_type_enum"); + expect(i.mutations).toContain("insert_feedback_type_enum"); + expect(i.mutations).toContain("delete_feedback_type_enum"); + }); + }); + + describe("platformAdmin", () => { + let i; + beforeAll(async () => { + i = await introspectAs("platformAdmin"); + }); + + test("cannot query feedback_type_enum", () => { + expect(i.queries).not.toContain("feedback_type_enum"); + }); + + test("cannot create, update, or delete feedback_type_enum", () => { + expect(i).toHaveNoMutationsFor("feedback_type_enum"); + }); + }); + + describe("teamEditor", () => { + let i; + beforeAll(async () => { + i = await introspectAs("teamEditor"); + }); + + test("cannot query feedback_type_enum", () => { + expect(i.queries).not.toContain("feedback_type_enum"); + }); + + test("cannot create, update, or delete feedback_type_enum", () => { + expect(i).toHaveNoMutationsFor("feedback_type_enum"); + }); + }); + + describe("demoUser", () => { + let i; + beforeAll(async () => { + i = await introspectAs("demoUser"); + }); + + test("cannot query feedback_type_enum", () => { + expect(i.queries).not.toContain("feedback_type_enum"); + }); + + test("cannot create, update, or delete feedback_type_enum", () => { + expect(i).toHaveNoMutationsFor("feedback_type_enum"); + }); + }); + + describe("api", () => { + let i; + beforeAll(async () => { + i = await introspectAs("api"); + }); + + test("cannot INSERT records", () => { + expect(i.mutations).not.toContain("insert_feedback_type_enum"); + }); + + test("cannot QUERY records", () => { + expect(i.queries).not.toContain("feedback_type_enum"); + }); + + test("cannot DELETE records", () => { + expect(i.mutations).not.toContain("delete_feedback_type_enum"); + }); + + test("cannot UPDATE records", () => { + expect(i.mutations).not.toContain("update_feedback_type_enum"); + }); + }); +}); diff --git a/hasura.planx.uk/tests/flow_document_templates.test.js b/hasura.planx.uk/tests/flow_document_templates.test.js index 75057b2f86..c77c0db4cf 100644 --- a/hasura.planx.uk/tests/flow_document_templates.test.js +++ b/hasura.planx.uk/tests/flow_document_templates.test.js @@ -64,6 +64,21 @@ describe("flow_document_templates", () => { expect(i).toHaveNoMutationsFor("flow_document_templates"); }); }); + + describe("demoUser", () => { + let i; + beforeAll(async () => { + i = await introspectAs("demoUser"); + }); + + test("cannot query flow_document_templates", () => { + expect(i.queries).not.toContain("flow_document_templates"); + }); + + test("cannot create, update, or delete flow_document_templates", () => { + expect(i).toHaveNoMutationsFor("flow_document_templates"); + }); + }); describe("api", () => { beforeAll(async () => { diff --git a/hasura.planx.uk/tests/flow_status_history.test.js b/hasura.planx.uk/tests/flow_status_history.test.js index cee2e236d4..b5893844b0 100644 --- a/hasura.planx.uk/tests/flow_status_history.test.js +++ b/hasura.planx.uk/tests/flow_status_history.test.js @@ -61,6 +61,21 @@ describe("flows status history", () => { }); }); + describe("demoUser", () => { + let i; + beforeAll(async () => { + i = await introspectAs("demoUser"); + }); + + test("cannot query flow_status_history", () => { + expect(i.queries).not.toContain("flow_status_history"); + }); + + test("cannot create, update, or delete flows or their associated operations", () => { + expect(i).toHaveNoMutationsFor("flow_status_history"); + }); + }); + describe("api", () => { let i; beforeAll(async () => { diff --git a/hasura.planx.uk/tests/flows.test.js b/hasura.planx.uk/tests/flows.test.js index 2338e7ea27..d857e0c04d 100644 --- a/hasura.planx.uk/tests/flows.test.js +++ b/hasura.planx.uk/tests/flows.test.js @@ -140,6 +140,58 @@ describe("flows and operations", () => { expect(i.mutations).not.toContain("update_published_flows"); }); }); + + describe("demoUser", () => { + let i; + beforeAll(async () => { + i = await introspectAs("demoUser"); + }); + + test("can query flows and their associated operations", () => { + expect(i.queries).toContain("flows"); + expect(i.queries).toContain("operations"); + }); + + test("can update flows and their associated operations", () => { + expect(i.mutations).toContain("update_flows_by_pk"); + expect(i.mutations).toContain("update_flows"); + expect(i.mutations).toContain("update_operations_by_pk"); + expect(i.mutations).toContain("update_operations"); + }); + + test("can create flows and their associated operations", () => { + expect(i.mutations).toContain("insert_flows_one"); + expect(i.mutations).toContain("insert_flows"); + expect(i.mutations).toContain("insert_operations_one"); + expect(i.mutations).toContain("insert_operations"); + }); + + test("can delete flows", () => { + expect(i.mutations).toContain("delete_flows_by_pk"); + expect(i.mutations).toContain("delete_flows"); + }); + + test("cannot delete operations", () => { + expect(i.mutations).not.toContain("delete_operations_by_pk"); + expect(i.mutations).not.toContain("delete_operations"); + }); + + test("can query published flows", () => { + expect(i.queries).toContain("published_flows"); + }); + + test("can create published_flows", () => { + expect(i.mutations).toContain("insert_published_flows_one"); + expect(i.mutations).toContain("insert_published_flows"); + }); + + test("cannot update or delete published_flows", () => { + expect(i.mutations).not.toContain("delete_published_flows_by_pk"); + expect(i.mutations).not.toContain("delete_published_flows"); + expect(i.mutations).not.toContain("update_published_flows_by_pk"); + expect(i.mutations).not.toContain("update_published_flows"); + }); + }); describe("api", () => { let i; diff --git a/hasura.planx.uk/tests/global_settings.test.js b/hasura.planx.uk/tests/global_settings.test.js index c7b3d9be95..a75a7a6629 100644 --- a/hasura.planx.uk/tests/global_settings.test.js +++ b/hasura.planx.uk/tests/global_settings.test.js @@ -64,6 +64,21 @@ describe("global_settings", () => { }); }); + describe("demoUser", () => { + let i; + beforeAll(async () => { + i = await introspectAs("demoUser"); + }); + + test("can query global_settings view", () => { + expect(i.queries).toContain("global_settings"); + }); + + test("cannot create, update, or delete global_settings", () => { + expect(i).toHaveNoMutationsFor("global_settings"); + }); + }); + describe("api", () => { let i; beforeAll(async () => { diff --git a/hasura.planx.uk/tests/lowcal_sessions.test.js b/hasura.planx.uk/tests/lowcal_sessions.test.js index aa0149ff88..8e6e2d6da7 100644 --- a/hasura.planx.uk/tests/lowcal_sessions.test.js +++ b/hasura.planx.uk/tests/lowcal_sessions.test.js @@ -1,8 +1,7 @@ const { introspectAs, gqlAdmin, gqlPublic } = require("./utils"); -const { v4: uuidV4 } = require('uuid'); +const { v4: uuidV4 } = require("uuid"); const assert = require("assert"); - describe("lowcal_sessions", () => { describe("public role introspection", () => { let i; @@ -23,15 +22,9 @@ describe("lowcal_sessions", () => { describe("public role queries and mutations", () => { let ids; const flowId = uuidV4(); - const [ - alice1, - alice2, - bob1, - bob2, - mallory1, - robert1, - anon1 - ] = [...Array(7)].map(() => uuidV4()); + const [alice1, alice2, bob1, bob2, mallory1, robert1, anon1] = [ + ...Array(7), + ].map(() => uuidV4()); const insertSession = ` mutation InsertLowcalSession( @@ -123,12 +116,12 @@ describe("lowcal_sessions", () => { } } } - ` + `; let res = await gqlAdmin(query); ids = res.data.insert_lowcal_sessions.returning.map((row) => row.id); assert.strictEqual(ids.length, 5); }); - + afterAll(async () => { const res = await gqlAdmin(` mutation { @@ -137,20 +130,23 @@ describe("lowcal_sessions", () => { } } `); - assert.strictEqual(res.data.delete_lowcal_sessions.affected_rows, ids.length); + assert.strictEqual( + res.data.delete_lowcal_sessions.affected_rows, + ids.length + ); }); describe("INSERT without permission", () => { test("Anonymous users can insert a session with an empty email", async () => { const headers = { "x-hasura-lowcal-session-id": anon1, - "x-hasura-lowcal-email": "" + "x-hasura-lowcal-email": "", }; const payload = { email: "", sessionId: anon1, - data: { x: 1 } - } + data: { x: 1 }, + }; const res = await gqlPublic(insertSession, payload, headers); ids.push(res.data.insert_lowcal_sessions_one.id); // add the email to ids for teardown expect(res).not.toHaveProperty("errors"); @@ -162,89 +158,133 @@ describe("lowcal_sessions", () => { test("Alice cannot insert a session with an email that doesn't match headers", async () => { const headers = { "x-hasura-lowcal-session-id": alice2, - "x-hasura-lowcal-email": "helloalice@opensystemslab.io" + "x-hasura-lowcal-email": "helloalice@opensystemslab.io", }; const payload = { email: "alice@opensystemslab.io", // not the same as in header sessionId: alice2, - data: { x: 1 } - } + data: { x: 1 }, + }; const res = await gqlPublic(insertSession, payload, headers); expect(res).toHaveProperty("errors"); - expect(res.errors[0].message).toContain('check constraint of an insert/update permission has failed'); + expect(res.errors[0].message).toContain( + "check constraint of an insert/update permission has failed" + ); }); - }) + }); describe("UPDATE without permission", () => { test("cannot update without 'x-hasura-lowcal-session-id' header", async () => { - const res = await gqlPublic(updateByPK, { sessionId: alice1, data: { x: 1 } }, { "x-hasura-lowcal-email": "alice@opensystemslab.io" }); + const res = await gqlPublic( + updateByPK, + { sessionId: alice1, data: { x: 1 } }, + { "x-hasura-lowcal-email": "alice@opensystemslab.io" } + ); expect(res).toHaveProperty("errors"); - expect(res.errors[0].message).toContain('missing session variable: "x-hasura-lowcal-session-id"'); + expect(res.errors[0].message).toContain( + 'missing session variable: "x-hasura-lowcal-session-id"' + ); }); - + test("cannot update without 'x-hasura-lowcal-email' header", async () => { - const res = await gqlPublic(updateByPK, { sessionId: alice1, data: { x: 1 } }, { "x-hasura-lowcal-session-id": uuidV4() }); + const res = await gqlPublic( + updateByPK, + { sessionId: alice1, data: { x: 1 } }, + { "x-hasura-lowcal-session-id": uuidV4() } + ); expect(res).toHaveProperty("errors"); - expect(res.errors[0].message).toContain('missing session variable: "x-hasura-lowcal-email"'); + expect(res.errors[0].message).toContain( + 'missing session variable: "x-hasura-lowcal-email"' + ); }); test("'x-hasura-lowcal-session-id' header must have value", async () => { - const res = await gqlPublic(updateByPK, { sessionId: alice1, data: { x: 1 } }, { "x-hasura-lowcal-session-id": null, "x-hasura-lowcal-email": "alice@opensystemslab.io"}); + const res = await gqlPublic( + updateByPK, + { sessionId: alice1, data: { x: 1 } }, + { + "x-hasura-lowcal-session-id": null, + "x-hasura-lowcal-email": "alice@opensystemslab.io", + } + ); expect(res).toHaveProperty("errors"); - expect(res.errors[0].message).toContain("invalid input syntax for type uuid: \"null\""); + expect(res.errors[0].message).toContain( + 'invalid input syntax for type uuid: "null"' + ); }); test("Alice cannot update her own session with invalid sessionId", async () => { const headers = { "x-hasura-lowcal-session-id": uuidV4(), - "x-hasura-lowcal-email": "alice@opensystemslab.io" + "x-hasura-lowcal-email": "alice@opensystemslab.io", }; - const res = await gqlPublic(updateByPK, { sessionId: alice1, data: { x: 1 } }, headers); + const res = await gqlPublic( + updateByPK, + { sessionId: alice1, data: { x: 1 } }, + headers + ); expect(res.data.update_lowcal_sessions_by_pk).toBeNull(); }); test("Alice cannot update her own session with invalid email", async () => { const headers = { "x-hasura-lowcal-session-id": alice1, - "x-hasura-lowcal-email": "not-alice@opensystemslab.io" + "x-hasura-lowcal-email": "not-alice@opensystemslab.io", }; - const res = await gqlPublic(updateByPK, { sessionId: alice1, data: { x: 1 } }, headers); + const res = await gqlPublic( + updateByPK, + { sessionId: alice1, data: { x: 1 } }, + headers + ); expect(res.data.update_lowcal_sessions_by_pk).toBeNull(); }); test("Alice cannot update her own session with a missing email", async () => { const headers = { "x-hasura-lowcal-session-id": alice1, - "x-hasura-lowcal-email": null + "x-hasura-lowcal-email": null, }; - const res = await gqlPublic(updateByPK, { sessionId: alice1, data: { x: 1 } }, headers); + const res = await gqlPublic( + updateByPK, + { sessionId: alice1, data: { x: 1 } }, + headers + ); expect(res.data.update_lowcal_sessions_by_pk).toBeNull(); }); test("Alice cannot update her own existing session with an empty email ", async () => { const headers = { "x-hasura-lowcal-session-id": alice1, - "x-hasura-lowcal-email": "" + "x-hasura-lowcal-email": "", }; - const res = await gqlPublic(updateByPK, { sessionId: alice1, data: { x: 1 } }, headers); + const res = await gqlPublic( + updateByPK, + { sessionId: alice1, data: { x: 1 } }, + headers + ); expect(res.data.update_lowcal_sessions_by_pk).toBeNull(); }); test("Mallory cannot update Alice's session", async () => { const headers = { "x-hasura-lowcal-session-id": uuidV4(), - "x-hasura-lowcal-email": "random@opensystemslab.io" + "x-hasura-lowcal-email": "random@opensystemslab.io", }; - const res = await gqlPublic(updateByPK, { sessionId: alice1, data: { x: 1 } }, headers); + const res = await gqlPublic( + updateByPK, + { sessionId: alice1, data: { x: 1 } }, + headers + ); expect(res.data.update_lowcal_sessions_by_pk).toBeNull(); }); test("Mallory cannot update multiple sessions which do not belong to them", async () => { const headers = { "x-hasura-lowcal-session-id": uuidV4(), - "x-hasura-lowcal-email": "random@opensystemslab.io" + "x-hasura-lowcal-email": "random@opensystemslab.io", }; - const res = await gqlPublic(` + const res = await gqlPublic( + ` mutation UpdateMultipleSessionsWithoutWhereClause { update_lowcal_sessions(where: {}, _set: { data: "{ x: 1 }" }) { returning { @@ -252,16 +292,20 @@ describe("lowcal_sessions", () => { } } } - `, null, headers); + `, + null, + headers + ); expect(res.data.update_lowcal_sessions.returning).toHaveLength(0); }); test("Bob cannot update multiple sessions which do belong to them", async () => { const headers = { "x-hasura-lowcal-session-id": bob1, - "x-hasura-lowcal-email": "bob@opensystemslab.io" + "x-hasura-lowcal-email": "bob@opensystemslab.io", }; - const res = await gqlPublic(` + const res = await gqlPublic( + ` mutation UpdateMultipleSessionsWithoutWhereClause { update_lowcal_sessions(where: {}, _set: { data: "{ x: 1 }" }) { returning { @@ -269,7 +313,10 @@ describe("lowcal_sessions", () => { } } } - `, null, headers); + `, + null, + headers + ); expect(res.data.update_lowcal_sessions.returning).toHaveLength(1); expect(res.data.update_lowcal_sessions.returning[0].id).toEqual(bob1); }); @@ -277,46 +324,75 @@ describe("lowcal_sessions", () => { test("Anonymous users can upsert their own session with an empty email", async () => { const headers = { "x-hasura-lowcal-session-id": anon1, - "x-hasura-lowcal-email": "" + "x-hasura-lowcal-email": "", }; // initial insert (upsert) - const res1 = await gqlPublic(updateByPK, { sessionId: anon1, data: { x: 1 } }, headers); + const res1 = await gqlPublic( + updateByPK, + { sessionId: anon1, data: { x: 1 } }, + headers + ); expect(res1.data.update_lowcal_sessions_by_pk).not.toBeNull(); expect(res1.data.update_lowcal_sessions_by_pk.id).toEqual(anon1); - expect(res1.data.update_lowcal_sessions_by_pk.data).toHaveProperty("x", 1); + expect(res1.data.update_lowcal_sessions_by_pk.data).toHaveProperty( + "x", + 1 + ); // update 1 - const res2 = await gqlPublic(updateByPK, { sessionId: anon1, data: { y: 2 } }, headers); + const res2 = await gqlPublic( + updateByPK, + { sessionId: anon1, data: { y: 2 } }, + headers + ); expect(res2.data.update_lowcal_sessions_by_pk).not.toBeNull(); expect(res2.data.update_lowcal_sessions_by_pk.id).toEqual(anon1); - expect(res2.data.update_lowcal_sessions_by_pk.data).toHaveProperty("y", 2); + expect(res2.data.update_lowcal_sessions_by_pk.data).toHaveProperty( + "y", + 2 + ); // update 2 - const res3 = await gqlPublic(updateByPK, { sessionId: anon1, data: {} }, headers); + const res3 = await gqlPublic( + updateByPK, + { sessionId: anon1, data: {} }, + headers + ); expect(res3.data.update_lowcal_sessions_by_pk).not.toBeNull(); expect(res3.data.update_lowcal_sessions_by_pk.data).toEqual({}); }); }); - + describe("UPDATE with permission", () => { test("Alice can update her session", async () => { const headers = { "x-hasura-lowcal-session-id": alice1, - "x-hasura-lowcal-email": "alice@opensystemslab.io" + "x-hasura-lowcal-email": "alice@opensystemslab.io", }; - const res = await gqlPublic(updateByPK, { sessionId: alice1, data: { x: 1 } }, headers); + const res = await gqlPublic( + updateByPK, + { sessionId: alice1, data: { x: 1 } }, + headers + ); expect(res.data.update_lowcal_sessions_by_pk).not.toBeNull(); expect(res.data.update_lowcal_sessions_by_pk.id).toEqual(alice1); - expect(res.data.update_lowcal_sessions_by_pk.data).toHaveProperty("x", 1); + expect(res.data.update_lowcal_sessions_by_pk.data).toHaveProperty( + "x", + 1 + ); }); test("Alice cannot update her session with an empty email", async () => { const headers = { "x-hasura-lowcal-session-id": alice1, - "x-hasura-lowcal-email": "" + "x-hasura-lowcal-email": "", }; - const res = await gqlPublic(updateByPK, { sessionId: alice1, data: { x: 1 } }, headers); + const res = await gqlPublic( + updateByPK, + { sessionId: alice1, data: { x: 1 } }, + headers + ); expect(res).not.toHaveProperty("errors"); expect(res.data.update_lowcal_sessions_by_pk).toBeNull(); }); @@ -324,9 +400,13 @@ describe("lowcal_sessions", () => { test("Robert cannot update his read-only session", async () => { const headers = { "x-hasura-lowcal-session-id": robert1, - "x-hasura-lowcal-email": "robert@opensystemslab.io" + "x-hasura-lowcal-email": "robert@opensystemslab.io", }; - const res = await gqlPublic(updateByPK, { sessionId: robert1, data: { x: 1 } }, headers); + const res = await gqlPublic( + updateByPK, + { sessionId: robert1, data: { x: 1 } }, + headers + ); expect(res).not.toHaveProperty("errors"); expect(res.data.update_lowcal_sessions_by_pk).toBeNull(); }); @@ -336,19 +416,27 @@ describe("lowcal_sessions", () => { test("cannot select without 'x-hasura-lowcal-session-id' header", async () => { const res = await gqlPublic(selectByPK, { sessionId: alice1 }); expect(res).toHaveProperty("errors"); - expect(res.errors[0].message).toContain('missing session variable: "x-hasura-lowcal-session-id"'); + expect(res.errors[0].message).toContain( + 'missing session variable: "x-hasura-lowcal-session-id"' + ); }); test("cannot select without 'x-hasura-lowcal-email' header", async () => { - const res = await gqlPublic(selectByPK, { sessionId: alice1 }, { "x-hasura-lowcal-session-id": uuidV4() }); + const res = await gqlPublic( + selectByPK, + { sessionId: alice1 }, + { "x-hasura-lowcal-session-id": uuidV4() } + ); expect(res).toHaveProperty("errors"); - expect(res.errors[0].message).toContain('missing session variable: "x-hasura-lowcal-email"'); + expect(res.errors[0].message).toContain( + 'missing session variable: "x-hasura-lowcal-email"' + ); }); test("Mallory cannot select Alice's session", async () => { const headers = { "x-hasura-lowcal-session-id": uuidV4(), - "x-hasura-lowcal-email": "random@opensystemslab.io" + "x-hasura-lowcal-email": "random@opensystemslab.io", }; const res = await gqlPublic(selectByPK, { sessionId: alice1 }, headers); expect(res.data.lowcal_sessions_by_pk).toBeNull(); @@ -357,43 +445,51 @@ describe("lowcal_sessions", () => { test("Mallory cannot select all sessions", async () => { const headers = { "x-hasura-lowcal-session-id": uuidV4(), - "x-hasura-lowcal-email": "random@opensystemslab.io" + "x-hasura-lowcal-email": "random@opensystemslab.io", }; - const res = await gqlPublic(` + const res = await gqlPublic( + ` query SelectAllLowcalSessions { lowcal_sessions { id } } - `, null, headers); + `, + null, + headers + ); expect(res.data.lowcal_sessions).toHaveLength(0); }); test("Bob cannot select multiple sessions which belong to him", async () => { const headers = { "x-hasura-lowcal-session-id": bob1, - "x-hasura-lowcal-email": "bob@opensystemslab.io" + "x-hasura-lowcal-email": "bob@opensystemslab.io", }; - const res = await gqlPublic(` + const res = await gqlPublic( + ` query SelectAllLowcalSessions { lowcal_sessions { id } } - `, null, headers); + `, + null, + headers + ); expect(res.data.lowcal_sessions).toHaveLength(1); - expect(res.data.lowcal_sessions[0].id).toEqual(bob1) + expect(res.data.lowcal_sessions[0].id).toEqual(bob1); }); - }); describe("SELECT with permission", () => { test("Alice can select her session", async () => { const headers = { "x-hasura-lowcal-session-id": alice1, - "x-hasura-lowcal-email": "alice@opensystemslab.io" + "x-hasura-lowcal-email": "alice@opensystemslab.io", }; - const res = await gqlPublic(` + const res = await gqlPublic( + ` query SelectAllLowcalSessions { lowcal_sessions { created_at @@ -403,17 +499,21 @@ describe("lowcal_sessions", () => { updated_at } } - `, null, headers); + `, + null, + headers + ); expect(res.data.lowcal_sessions).toHaveLength(1); - expect(res.data.lowcal_sessions[0].id).toEqual(alice1) + expect(res.data.lowcal_sessions[0].id).toEqual(alice1); }); test("Anonymous users cannot select their own session", async () => { const headers = { "x-hasura-lowcal-session-id": anon1, - "x-hasura-lowcal-email": "" + "x-hasura-lowcal-email": "", }; - const res = await gqlPublic(` + const res = await gqlPublic( + ` query SelectAllLowcalSessions { lowcal_sessions { created_at @@ -423,7 +523,10 @@ describe("lowcal_sessions", () => { updated_at } } - `, null, headers); + `, + null, + headers + ); expect(res.data.lowcal_sessions).toHaveLength(0); }); }); @@ -459,6 +562,21 @@ describe("lowcal_sessions", () => { }); }); + describe("demoUser", () => { + let i; + beforeAll(async () => { + i = await introspectAs("demoUser"); + }); + + test("cannot query lowcal_sessions", () => { + expect(i.queries).not.toContain("lowcal_sessions"); + }); + + test("cannot create, update, or delete lowcal_sessions", () => { + expect(i).toHaveNoMutationsFor("lowcal_sessions"); + }); + }); + describe("api", () => { let i; beforeAll(async () => { diff --git a/hasura.planx.uk/tests/payment_requests.test.js b/hasura.planx.uk/tests/payment_requests.test.js index 5580187ae3..14f26aac38 100644 --- a/hasura.planx.uk/tests/payment_requests.test.js +++ b/hasura.planx.uk/tests/payment_requests.test.js @@ -1,5 +1,5 @@ const { introspectAs, gqlAdmin, gqlPublic } = require("./utils"); -const { v4: uuidV4 } = require('uuid'); +const { v4: uuidV4 } = require("uuid"); const assert = require("assert"); describe("payment_requests", () => { @@ -22,8 +22,8 @@ describe("payment_requests", () => { }); describe("public query", () => { - const sessionIds = [uuidV4(), uuidV4()] - const paymentRequestIds = [uuidV4(), uuidV4()] + const sessionIds = [uuidV4(), uuidV4()]; + const paymentRequestIds = [uuidV4(), uuidV4()]; beforeAll(async () => { await insertSessions(sessionIds); @@ -32,23 +32,25 @@ describe("payment_requests", () => { afterAll(async () => { await deleteSessions(sessionIds); - }) + }); test("can QUERY records", () => { expect(i.queries).toContain("payment_requests"); expect(i.queries).toContain("payment_requests_by_pk"); }); - test("requires x-hasura-payment-request-id to query", async() => { + test("requires x-hasura-payment-request-id to query", async () => { const query = ` query GetAllPaymentRequests { payment_requests { id } } - ` + `; const publicRes = await gqlPublic(query); - expect(publicRes.errors[0].message).toMatch(/missing session variable: "x-hasura-payment-request-id"/) + expect(publicRes.errors[0].message).toMatch( + /missing session variable: "x-hasura-payment-request-id"/ + ); }); test("can only access records with a known id", async () => { @@ -58,7 +60,7 @@ describe("payment_requests", () => { id } } - ` + `; const headers = { "x-hasura-payment-request-id": paymentRequestIds[0], }; @@ -75,7 +77,7 @@ describe("payment_requests", () => { paid_at } } - ` + `; const publicRes = await gqlPublic(query); expect(publicRes).toHaveProperty("errors"); @@ -128,6 +130,21 @@ describe("payment_requests", () => { }); }); + describe("demoUser", () => { + let i; + beforeAll(async () => { + i = await introspectAs("demoUser"); + }); + + test("cannot query payment_requests", () => { + expect(i.queries).not.toContain("payment_requests"); + }); + + test("cannot create, update, or delete payment_requests", () => { + expect(i).toHaveNoMutationsFor("payment_requests"); + }); + }); + describe("api", () => { let i; beforeAll(async () => { @@ -205,7 +222,7 @@ const insertPaymentRequests = async (sessionIds, paymentRequestIds) => { } } } - ` + `; const res = await gqlAdmin(query); ids = res.data.insert_payment_requests.returning.map((row) => row.id); assert.strictEqual(ids.length, 2); @@ -214,10 +231,15 @@ const insertPaymentRequests = async (sessionIds, paymentRequestIds) => { const deleteSessions = async (sessionIds) => { const res = await gqlAdmin(` mutation { - delete_lowcal_sessions(where: {id: {_in: ${JSON.stringify(sessionIds)}}}) { + delete_lowcal_sessions(where: {id: {_in: ${JSON.stringify( + sessionIds + )}}}) { affected_rows } } `); - assert.strictEqual(res.data.delete_lowcal_sessions.affected_rows, sessionIds.length); + assert.strictEqual( + res.data.delete_lowcal_sessions.affected_rows, + sessionIds.length + ); }; diff --git a/hasura.planx.uk/tests/payment_status.test.js b/hasura.planx.uk/tests/payment_status.test.js index 6bcc81d3a0..57072df591 100644 --- a/hasura.planx.uk/tests/payment_status.test.js +++ b/hasura.planx.uk/tests/payment_status.test.js @@ -67,6 +67,21 @@ describe("payment_status", () => { }); }); + describe("demoUser", () => { + let i; + beforeAll(async () => { + i = await introspectAs("demoUser"); + }); + + test("cannot query payment_status", () => { + expect(i.queries).not.toContain("payment_status"); + }); + + test("cannot create, update, or delete payment_status", () => { + expect(i).toHaveNoMutationsFor("payment_status"); + }); + }); + describe("api", () => { let i; beforeAll(async () => { @@ -84,6 +99,6 @@ describe("payment_status", () => { test("cannot delete or update payment_status", () => { expect(i.mutations).not.toContain("update_payment_status"); expect(i.mutations).not.toContain("delete_payment_status"); - }) + }); }); }); diff --git a/hasura.planx.uk/tests/planning_constraints_requests.test.js b/hasura.planx.uk/tests/planning_constraints_requests.test.js index 8f0c2627ff..e9a68d0c1c 100644 --- a/hasura.planx.uk/tests/planning_constraints_requests.test.js +++ b/hasura.planx.uk/tests/planning_constraints_requests.test.js @@ -25,7 +25,9 @@ describe("planning_constraints_requests", () => { test("has full access to query and mutate planning constraints requests", () => { expect(i.queries).toContain("planning_constraints_requests"); expect(i.mutations).toContain("insert_planning_constraints_requests"); - expect(i.mutations).toContain("update_planning_constraints_requests_by_pk"); + expect(i.mutations).toContain( + "update_planning_constraints_requests_by_pk" + ); expect(i.mutations).toContain("delete_planning_constraints_requests"); }); }); @@ -60,6 +62,21 @@ describe("planning_constraints_requests", () => { }); }); + describe("demoUser", () => { + let i; + beforeAll(async () => { + i = await introspectAs("demoUser"); + }); + + test("cannot query planning_constraints_requests", () => { + expect(i.queries).not.toContain("planning_constraints_requests"); + }); + + test("cannot create, update, or delete planning_constraints_requests", () => { + expect(i).toHaveNoMutationsFor("planning_constraints_requests"); + }); + }); + describe("api", () => { let i; beforeAll(async () => { @@ -68,15 +85,17 @@ describe("planning_constraints_requests", () => { test("can query planning_constraints_requests", () => { expect(i.queries).toContain("planning_constraints_requests"); - }) + }); test("can insert planning_constraints_requests", () => { expect(i.mutations).toContain("insert_planning_constraints_requests"); }); test("cannot update or delete planning_constriants_requests", () => { - expect(i.mutations).not.toContain("update_planning_constraints_requests_by_pk"); + expect(i.mutations).not.toContain( + "update_planning_constraints_requests_by_pk" + ); expect(i.mutations).not.toContain("delete_planning_constraints_requests"); - }) + }); }); }); diff --git a/hasura.planx.uk/tests/reconciliation_requests.test.js b/hasura.planx.uk/tests/reconciliation_requests.test.js index 9e8f5812d6..91a43de011 100644 --- a/hasura.planx.uk/tests/reconciliation_requests.test.js +++ b/hasura.planx.uk/tests/reconciliation_requests.test.js @@ -64,6 +64,21 @@ describe("reconciliation_requests", () => { }); }); + describe("demoUser", () => { + let i; + beforeAll(async () => { + i = await introspectAs("demoUser"); + }); + + test("cannot query reconciliation_requests", () => { + expect(i.queries).not.toContain("reconciliation_requests"); + }); + + test("cannot create, update, or delete reconciliation_requests", () => { + expect(i).toHaveNoMutationsFor("reconciliation_requests"); + }); + }); + describe("api", () => { beforeAll(async () => { i = await introspectAs("api"); diff --git a/hasura.planx.uk/tests/sessions.test.js b/hasura.planx.uk/tests/sessions.test.js index 54c7a615a2..c77482f746 100644 --- a/hasura.planx.uk/tests/sessions.test.js +++ b/hasura.planx.uk/tests/sessions.test.js @@ -564,7 +564,7 @@ describe("sessions", () => { headers ); expect(res.data.sessions).toHaveLength(1); - const session = res.data.sessions[0] + const session = res.data.sessions[0]; expect(session.id).toEqual(alice1); expect(session).toHaveProperty(["created_at"]); expect(session).toHaveProperty(["breadcrumbs"]); @@ -629,6 +629,21 @@ describe("sessions", () => { }); }); + describe("demoUser", () => { + let i; + beforeAll(async () => { + i = await introspectAs("demoUser"); + }); + + test("cannot query sessions", () => { + expect(i.queries).not.toContain("sessions"); + }); + + test("cannot create, update, or delete sessions", () => { + expect(i).toHaveNoMutationsFor("sessions"); + }); + }); + describe("api", () => { let i; beforeAll(async () => { @@ -645,5 +660,4 @@ describe("sessions", () => { expect(i.mutations).not.toContain("delete_sessions"); }); }); - }); diff --git a/hasura.planx.uk/tests/team_integrations.test.js b/hasura.planx.uk/tests/team_integrations.test.js index aab8de9680..d141493096 100644 --- a/hasura.planx.uk/tests/team_integrations.test.js +++ b/hasura.planx.uk/tests/team_integrations.test.js @@ -61,6 +61,21 @@ describe("team_integrations", () => { }); }); + describe("demoUser", () => { + let i; + beforeAll(async () => { + i = await introspectAs("demoUser"); + }); + + test("can query team_integrations", () => { + expect(i.queries).toContain("team_integrations"); + }); + + test("cannot create, update, or delete team_integrations", () => { + expect(i).toHaveNoMutationsFor("team_integrations"); + }); + }); + describe("api", () => { let i; beforeAll(async () => { diff --git a/hasura.planx.uk/tests/team_members.test.js b/hasura.planx.uk/tests/team_members.test.js index 6ed7853b1e..f46b3609dd 100644 --- a/hasura.planx.uk/tests/team_members.test.js +++ b/hasura.planx.uk/tests/team_members.test.js @@ -48,7 +48,7 @@ describe("team_members", () => { beforeAll(async () => { i = await introspectAs("teamEditor"); }); - + // Row-level permissions tested in e2e/tests/api-driven // teamEditors can only query their own record test("can query teams", () => { @@ -60,6 +60,21 @@ describe("team_members", () => { }); }); + describe("demoUser", () => { + let i; + beforeAll(async () => { + i = await introspectAs("demoUser"); + }); + + test("cannot query teams", () => { + expect(i.queries).not.toContain("team_members"); + }); + + test("cannot create, update, or delete team_members", () => { + expect(i).toHaveNoMutationsFor("team_members"); + }); + }); + describe("api", () => { let i; beforeAll(async () => { diff --git a/hasura.planx.uk/tests/team_themes.test.js b/hasura.planx.uk/tests/team_themes.test.js index 6e22bff88a..386c6cba0c 100644 --- a/hasura.planx.uk/tests/team_themes.test.js +++ b/hasura.planx.uk/tests/team_themes.test.js @@ -82,6 +82,21 @@ describe("team_themes", () => { }); }); + describe("demoUser", () => { + let i; + beforeAll(async () => { + i = await introspectAs("demoUser"); + }); + + test("can query team_themes", () => { + expect(i.queries).toContain("team_themes"); + }); + + test("cannot create, update, or delete team_themes", () => { + expect(i).toHaveNoMutationsFor("team_themes"); + }); + }); + describe("api", () => { let i; beforeAll(async () => { diff --git a/hasura.planx.uk/tests/teams.test.js b/hasura.planx.uk/tests/teams.test.js index 85865cb2dc..d20267eb0d 100644 --- a/hasura.planx.uk/tests/teams.test.js +++ b/hasura.planx.uk/tests/teams.test.js @@ -72,6 +72,21 @@ describe("teams", () => { }); }); + describe("demoUser", () => { + let i; + beforeAll(async () => { + i = await introspectAs("demoUser"); + }); + + test("can query teams", () => { + expect(i.queries).toContain("teams"); + }); + + test("cannot create, update, or delete teams", () => { + expect(i).toHaveNoMutationsFor("teams"); + }); + }); + describe("api", () => { let i; beforeAll(async () => { diff --git a/hasura.planx.uk/tests/uniform_applications.test.js b/hasura.planx.uk/tests/uniform_applications.test.js index 92f851a377..ca8947bd2f 100644 --- a/hasura.planx.uk/tests/uniform_applications.test.js +++ b/hasura.planx.uk/tests/uniform_applications.test.js @@ -60,6 +60,21 @@ describe("uniform_applications", () => { }); }); + describe("demoUser", () => { + let i; + beforeAll(async () => { + i = await introspectAs("demoUser"); + }); + + test("cannot query uniform applications", () => { + expect(i.queries).not.toContain("uniform_applications"); + }); + + test("cannot create, update, or delete uniform applications", () => { + expect(i).toHaveNoMutationsFor("uniform_applications"); + }); + }); + describe("api", () => { let i; beforeAll(async () => { @@ -74,6 +89,6 @@ describe("uniform_applications", () => { test("cannot delete uniform applications", () => { expect(i.mutations).not.toContain("delete_uniform_applications"); - }) + }); }); }); diff --git a/hasura.planx.uk/tests/users.test.js b/hasura.planx.uk/tests/users.test.js index 80466806d2..15ac9f0b68 100644 --- a/hasura.planx.uk/tests/users.test.js +++ b/hasura.planx.uk/tests/users.test.js @@ -67,6 +67,21 @@ describe("users", () => { }); }); + describe("demoUser", () => { + let i; + beforeAll(async () => { + i = await introspectAs("demoUser"); + }); + + test("can query users", async () => { + expect(i.queries).toContain("users"); + }); + + test("cannot create, update, or delete users", async () => { + expect(i).toHaveNoMutationsFor("users"); + }); + }); + describe("api", () => { let i; beforeAll(async () => { diff --git a/hasura.planx.uk/tests/utils.js b/hasura.planx.uk/tests/utils.js index f0e5a4cdf0..c9b2adb302 100644 --- a/hasura.planx.uk/tests/utils.js +++ b/hasura.planx.uk/tests/utils.js @@ -2,13 +2,16 @@ const fetch = require("isomorphic-fetch"); const jsonwebtoken = require("jsonwebtoken"); async function gqlAdmin(query, variables = {}) { - const res = await fetch(`http://${process.env.HASURA_HOST}:${process.env.HASURA_PORT}/v1/graphql`, { - method: "POST", - headers: { - "X-Hasura-Admin-Secret": process.env.HASURA_ADMIN_SECRET, - }, - body: JSON.stringify({ query, variables }), - }); + const res = await fetch( + `http://${process.env.HASURA_HOST}:${process.env.HASURA_PORT}/v1/graphql`, + { + method: "POST", + headers: { + "X-Hasura-Admin-Secret": process.env.HASURA_ADMIN_SECRET, + }, + body: JSON.stringify({ query, variables }), + } + ); const json = await res.json(); if (json.errors && json.errors[0].message.includes("x-hasura-admin-secret")) { throw Error("Invalid HASURA_SECRET"); @@ -17,41 +20,47 @@ async function gqlAdmin(query, variables = {}) { } async function gqlPublic(query, variables = {}, headers = {}) { - const res = await fetch(`http://${process.env.HASURA_HOST}:${process.env.HASURA_PORT}/v1/graphql`, { - method: "POST", - headers: headers, - body: JSON.stringify({ query: query, variables }), - }); + const res = await fetch( + `http://${process.env.HASURA_HOST}:${process.env.HASURA_PORT}/v1/graphql`, + { + method: "POST", + headers: headers, + body: JSON.stringify({ query: query, variables }), + } + ); return await res.json(); } /** * Get a role-based connection to Hasura - * @param {string} role - * @param {number} userId + * @param {string} role + * @param {number} userId * @returns A GQL client which authenticates to Hasura with the given role and userId */ function gqlWithRole(role, userId) { - const jwt = buildJWTForRole(role, userId) + const jwt = buildJWTForRole(role, userId); const gql = async (query, variables = {}, headers = {}) => { - const res = await fetch(`http://${process.env.HASURA_HOST}:${process.env.HASURA_PORT}/v1/graphql`, { - method: "POST", - headers: { - ...headers, - Authorization: `Bearer ${jwt}`, - }, - body: JSON.stringify({ query: query, variables }), - }); + const res = await fetch( + `http://${process.env.HASURA_HOST}:${process.env.HASURA_PORT}/v1/graphql`, + { + method: "POST", + headers: { + ...headers, + Authorization: `Bearer ${jwt}`, + }, + body: JSON.stringify({ query: query, variables }), + } + ); return await res.json(); - } + }; return gql; } /** - * @param {string} role - * @param {number} userId + * @param {string} role + * @param {number} userId * @returns {string} */ function buildJWTForRole(role, userId = 1) { @@ -81,10 +90,11 @@ const introspectAs = async (role, userId = undefined) => { const gql = { admin: gqlAdmin, public: gqlPublic, + demoUser: gqlWithRole("demoUser", userId), platformAdmin: gqlWithRole("platformAdmin", userId), teamEditor: gqlWithRole("teamEditor", userId), api: gqlWithRole("api"), - }[role] + }[role]; const INTROSPECTION_QUERY = ` query IntrospectionQuery { __schema {