From ba3b46e1e2938c3718bed36075c8ec54df43362d Mon Sep 17 00:00:00 2001 From: Cesar Varela Date: Tue, 10 Dec 2024 23:13:00 -0300 Subject: [PATCH] Add tests and rules to mutations --- .../playwright/seeds/customData/users.ts | 2 +- site/gatsby-site/server/fields/checklists.ts | 14 +++--- site/gatsby-site/server/generated/graphql.ts | 47 ------------------ site/gatsby-site/server/interfaces.ts | 6 ++- site/gatsby-site/server/rules.ts | 26 +++++++++- .../server/tests/fixtures/checklists.ts | 48 +++++++++++++++---- .../server/tests/mutation-fields.spec.ts | 4 +- 7 files changed, 79 insertions(+), 68 deletions(-) diff --git a/site/gatsby-site/playwright/seeds/customData/users.ts b/site/gatsby-site/playwright/seeds/customData/users.ts index 587312b920..1c7e45155c 100644 --- a/site/gatsby-site/playwright/seeds/customData/users.ts +++ b/site/gatsby-site/playwright/seeds/customData/users.ts @@ -10,7 +10,7 @@ const users: DBUser[] = [ }, { _id: new ObjectId("619b47eb5eed5334edfa3bd9"), - userId: "619b47ea5eed5334edfa3bbc", + userId: "648a4c0eaf3f54bf50f018a3", roles: ["admin"], first_name: "Sean", last_name: "McGregor" diff --git a/site/gatsby-site/server/fields/checklists.ts b/site/gatsby-site/server/fields/checklists.ts index acc2bca7c5..3e7a0bc3e6 100644 --- a/site/gatsby-site/server/fields/checklists.ts +++ b/site/gatsby-site/server/fields/checklists.ts @@ -1,7 +1,8 @@ import { GraphQLFieldConfigMap, GraphQLList } from "graphql"; -import { allow } from "graphql-shield"; +import { allow, or } from "graphql-shield"; import { generateMutationFields, generateQueryFields, getQueryArgs, getQueryResolver } from "../utils"; import { ChecklistType, RisksInputType, RiskType } from "../types/checklist"; +import { isAdmin, isChecklistsOwner, isSubscriber } from "../rules"; const getRiskClassificationsMongoQuery = (tagStrings: any) => { @@ -190,10 +191,9 @@ export const queryFields: GraphQLFieldConfigMap = { } export const mutationFields: GraphQLFieldConfigMap = { - ...generateMutationFields({ collectionName: 'checklists', Type: ChecklistType, generateFields: ['updateOne', 'deleteOne', 'insertOne', 'upsertOne'] }), + ...generateMutationFields({ collectionName: 'checklists', Type: ChecklistType, generateFields: ['deleteOne', 'insertOne', 'upsertOne'] }), } - export const permissions = { Query: { checklist: allow, @@ -201,10 +201,8 @@ export const permissions = { risks: allow, }, Mutation: { - updateOneChecklist: allow, - deleteOneChecklist: allow, - insertOneChecklist: allow, - upsertOneChecklist: allow, + insertOneChecklist: allow, // TODO: anonymous users can create checklists, this may break with the Next Auth implementation + deleteOneChecklist: or(isAdmin, isChecklistsOwner()), + upsertOneChecklist: or(isAdmin, isChecklistsOwner()), }, } - diff --git a/site/gatsby-site/server/generated/graphql.ts b/site/gatsby-site/server/generated/graphql.ts index 115dd6e0d8..00c2ffd026 100644 --- a/site/gatsby-site/server/generated/graphql.ts +++ b/site/gatsby-site/server/generated/graphql.ts @@ -369,20 +369,6 @@ export type ChecklistRiskPrecedent = { title?: Maybe; }; -export type ChecklistSetType = { - _id?: InputMaybe; - about?: InputMaybe; - date_created?: InputMaybe; - date_updated?: InputMaybe; - id?: InputMaybe; - name?: InputMaybe; - owner_id?: InputMaybe; - risks?: InputMaybe>>; - tags_goals?: InputMaybe>>; - tags_methods?: InputMaybe>>; - tags_other?: InputMaybe>>; -}; - export enum ChecklistSortByInput { AboutAsc = 'ABOUT_ASC', AboutDesc = 'ABOUT_DESC', @@ -412,10 +398,6 @@ export type ChecklistSortType = { owner_id?: InputMaybe; }; -export type ChecklistUpdateType = { - set?: InputMaybe; -}; - export type Classification = { __typename?: 'Classification'; _id?: Maybe; @@ -1915,7 +1897,6 @@ export type Mutation = { updateManyNotifications?: Maybe; updateManyQuickadds?: Maybe; updateOneCandidate?: Maybe; - updateOneChecklist?: Maybe; updateOneDuplicate?: Maybe; updateOneEntity?: Maybe; updateOneIncident?: Maybe; @@ -2166,12 +2147,6 @@ export type MutationUpdateOneCandidateArgs = { }; -export type MutationUpdateOneChecklistArgs = { - filter: ChecklistFilterType; - update: ChecklistUpdateType; -}; - - export type MutationUpdateOneDuplicateArgs = { filter: DuplicateFilterType; update: DuplicateUpdateType; @@ -2507,14 +2482,6 @@ export type PrecedentsObjectFilterType = { title?: InputMaybe; }; -export type PrecedentsSetListObjectType = { - description?: InputMaybe; - id?: InputMaybe; - incident_id?: InputMaybe; - tags?: InputMaybe>>; - title?: InputMaybe; -}; - export type PromoteSubmissionToReportInput = { incident_ids: Array>; is_incident_report?: InputMaybe; @@ -3220,20 +3187,6 @@ export type RisksPayloadPrecedentTsne = { y?: Maybe; }; -export type RisksSetListObjectType = { - generated?: InputMaybe; - id?: InputMaybe; - likelihood?: InputMaybe; - precedents?: InputMaybe>>; - risk_notes?: InputMaybe; - risk_status?: InputMaybe; - severity?: InputMaybe; - tag?: InputMaybe; - tags?: InputMaybe>>; - title?: InputMaybe; - touched?: InputMaybe; -}; - export enum SortType { Asc = 'ASC', Desc = 'DESC' diff --git a/site/gatsby-site/server/interfaces.ts b/site/gatsby-site/server/interfaces.ts index ea48abd00f..2a7f3e98c9 100644 --- a/site/gatsby-site/server/interfaces.ts +++ b/site/gatsby-site/server/interfaces.ts @@ -1,5 +1,5 @@ import { MongoClient } from 'mongodb'; -import { Classification, Duplicate, Entity, Incident, Report, Submission, Subscription, User, Notification, History_Report, History_Incident } from './generated/graphql'; +import { Classification, Duplicate, Entity, Incident, Report, Submission, Subscription, User, Notification, History_Report, History_Incident, Checklist } from './generated/graphql'; import { IncomingMessage } from 'http'; export interface Context { @@ -17,7 +17,7 @@ export type DBIncident = Omit - & { "Alleged deployer of AI system": string[], "Alleged developer of AI system": string[], "Alleged harmed or nearly harmed parties": string[], implicated_systems: string[] }; + & { "Alleged deployer of AI system": string[], "Alleged developer of AI system": string[], "Alleged harmed or nearly harmed parties": string[], implicated_systems: string[] }; export type DBEntity = Entity; @@ -51,3 +51,5 @@ export type DBSubscription = Omit & { userId?: string, type: NotificationTypes } + +export type DBChecklist = Checklist; \ No newline at end of file diff --git a/site/gatsby-site/server/rules.ts b/site/gatsby-site/server/rules.ts index dbcd217ef7..35f7a6a8d0 100644 --- a/site/gatsby-site/server/rules.ts +++ b/site/gatsby-site/server/rules.ts @@ -1,11 +1,12 @@ import { rule } from "graphql-shield"; -import { Context, DBSubscription, DBUser } from "./interfaces"; +import { Context, DBChecklist, DBSubscription, DBUser } from "./interfaces"; import { getMongoDbFilter } from "graphql-to-mongodb"; import { SubscriptionType } from "./types/subscription"; import { getSimplifiedType } from "./utils"; import { GraphQLFilter } from "graphql-to-mongodb/lib/src/mongoDbFilter"; import { UserType } from "./types/user"; import config, { Config } from "./config"; +import { ChecklistType } from "./types/checklist"; export const isRole = (role: string) => rule()( async (parent, args, context: Context, info) => { @@ -115,3 +116,26 @@ export const notQueriesAdminData = () => rule()( ) export const isAdmin = isRole('admin'); + +export const isSubscriber = isRole('subscriber'); + +export const isChecklistsOwner = () => rule()( + async (parent, args, context: Context, info) => { + + const collection = context.client.db('aiidprod').collection('checklists'); + const simpleType = getSimplifiedType(ChecklistType); + const filter = getMongoDbFilter(simpleType, args.filter as GraphQLFilter); + const checklists = await collection.find(filter).toArray(); + + const { user } = context; + + const meetsOwnership = checklists.every(c => c.owner_id === user?.id); + + if (!meetsOwnership) { + + return new Error('not authorized'); + } + + return true; + }, +) \ No newline at end of file diff --git a/site/gatsby-site/server/tests/fixtures/checklists.ts b/site/gatsby-site/server/tests/fixtures/checklists.ts index ccc77224aa..243a23edbd 100644 --- a/site/gatsby-site/server/tests/fixtures/checklists.ts +++ b/site/gatsby-site/server/tests/fixtures/checklists.ts @@ -1,10 +1,10 @@ import { ObjectId } from 'bson'; import { Fixture } from "../utils"; -import { Checklist, ChecklistInsertType, ChecklistUpdateType } from "../../generated/graphql"; +import { Checklist, ChecklistInsertType } from "../../generated/graphql"; const checklist1 = { _id: new ObjectId("6537e59e9208f3f75b2db1f7"), - owner_id: "63601cdc29e6840df23ad3e5", + owner_id: "60a7c5b7b4f5b8a6d8f9c7e6", tags_methods: ["GMF:Known AI Technology:Language Modeling"], tags_goals: ["GMF:Known AI Goal:Chatbot"], about: "", @@ -50,7 +50,7 @@ const checklist2 = { } ], tags_other: ["CSETv1:Entertainment Industry:yes"], - id: "849bd303-261f-4abe-8746-77dad5841dbe", + id: "849bd303-261f-4abe-8746-77dad5841daa", name: "Test Checklist 2" } @@ -59,7 +59,7 @@ const subscriber = { first_name: 'Subscriber', last_name: 'One', roles: ['subscriber'], - userId: 'subscriber1', + userId: '60a7c5b7b4f5b8a6d8f9c7e6', } const admin = { @@ -78,7 +78,7 @@ const anonymous = { userId: 'anon', } -const fixture: Fixture = { +const fixture: Fixture = { name: 'checklists', query: ` _id @@ -153,11 +153,43 @@ const fixture: Fixture = { }, testUpdateOne: null, testUpdateMany: null, - testInsertOne: null, + testInsertOne: { + allowed: [subscriber, admin, anonymous], + denied: [], + insert: { + about: "Test Insert", + }, + result: { + _id: expect.any(String), + about: "Test Insert", + } + }, testInsertMany: null, - testDeleteOne: null, + testDeleteOne: { + allowed: [admin, subscriber], + denied: [anonymous], + filter: { _id: { EQ: new ObjectId("6537e59e9208f3f75b2db1f7") } }, + result: { + _id: "6537e59e9208f3f75b2db1f7", + } + }, testDeleteMany: null, - testUpsertOne: null, + testUpsertOne: { + shouldUpdate: { + allowed: [admin, subscriber], + denied: [anonymous], + filter: { id: { EQ: "849bd303-261f-4abe-8746-77dad5841dbe" } }, + update: { name: 'Updated name' }, + result: { name: 'Updated name', _id: '6537e59e9208f3f75b2db1f7' } + }, + shouldInsert: { + allowed: [subscriber, admin, anonymous], + denied: [], + filter: { id: { EQ: "test" } }, + update: { name: 'New checklist', id: "test" }, + result: { name: 'New checklist', id: "test", _id: expect.any(String) } + }, + }, } export default fixture; \ No newline at end of file diff --git a/site/gatsby-site/server/tests/mutation-fields.spec.ts b/site/gatsby-site/server/tests/mutation-fields.spec.ts index 923c7dbb0b..048a12f00b 100644 --- a/site/gatsby-site/server/tests/mutation-fields.spec.ts +++ b/site/gatsby-site/server/tests/mutation-fields.spec.ts @@ -14,6 +14,7 @@ import submissionsFixture from './fixtures/submissions'; import classificationsFixture from './fixtures/classifications'; import subscriptionsFixture from './fixtures/subscriptions'; import duplicatesFixture from './fixtures/duplicates'; +import checklistsFixture from './fixtures/checklists'; const fixtures = [ quickaddsFixture, @@ -23,8 +24,9 @@ const fixtures = [ usersFixture, submissionsFixture, classificationsFixture, - subscriptionsFixture, + subscriptionsFixture, duplicatesFixture, + checklistsFixture, ] fixtures.forEach((collection) => {