diff --git a/packages/wabe-mongodb/src/index.test.ts b/packages/wabe-mongodb/src/index.test.ts index 5710ca67..0e173455 100644 --- a/packages/wabe-mongodb/src/index.test.ts +++ b/packages/wabe-mongodb/src/index.test.ts @@ -384,6 +384,40 @@ describe('Mongo adapter', () => { expect(res2.length).toBe(1) }) + it('should correctly exclude documents with notContains on array of objects (ACL notContains fix)', async () => { + await mongoAdapter.createObject({ + className: 'Test', + data: { + object: { array: [{ string: 'user1' }] }, + }, + context, + }) + + await mongoAdapter.createObject({ + className: 'Test', + data: { + object: { array: [{ string: 'user2' }] }, + }, + context, + }) + + const res = await mongoAdapter.getObjects({ + className: 'Test', + context, + where: { + object: { + // @ts-expect-error + array: { + notContains: { string: 'user1' }, + }, + }, + }, + }) + + expect(res.length).toBe(1) + expect(res[0]?.object?.array).toEqual([{ string: 'user2' }]) + }) + it('should retry on connection error', async () => { const spyMongoClientConnect = spyOn(mongoAdapter.client, 'connect').mockImplementationOnce( () => { diff --git a/packages/wabe-mongodb/src/index.ts b/packages/wabe-mongodb/src/index.ts index 51c7795a..dbfb48e9 100644 --- a/packages/wabe-mongodb/src/index.ts +++ b/packages/wabe-mongodb/src/index.ts @@ -64,7 +64,10 @@ export const buildMongoWhereQuery = { expect(res2.length).toBe(1) }) + it('should correctly exclude documents with notContains on array of objects (ACL notContains fix)', async () => { + await postgresAdapter.createObject({ + className: 'Test', + data: { + object: { array: [{ string: 'user1' }] }, + }, + context, + }) + + await postgresAdapter.createObject({ + className: 'Test', + data: { + object: { array: [{ string: 'user2' }] }, + }, + context, + }) + + const res = await postgresAdapter.getObjects({ + className: 'Test', + context, + where: { + object: { + // @ts-expect-error + array: { + notContains: { string: 'user1' }, + }, + }, + }, + }) + + expect(res.length).toBe(1) + expect(res[0]?.object?.array).toEqual([{ string: 'user2' }]) + }) + it('should create class', async () => { const client = await postgresAdapter.pool.connect() diff --git a/packages/wabe/src/security.test.ts b/packages/wabe/src/security.test.ts index d261fdb2..b3b232d4 100644 --- a/packages/wabe/src/security.test.ts +++ b/packages/wabe/src/security.test.ts @@ -1902,6 +1902,118 @@ describe('Security tests', () => { await closeTests(wabe) }) + it('should not authorize an user to read an object when explicitly denied in acl.users even if role has access (MongoDB notContains)', async () => { + const setup = await setupTests([ + { + name: 'TestNotContains', + fields: { + name: { type: 'String' }, + }, + permissions: { + read: { + authorizedRoles: ['Client'], + requireAuthentication: true, + }, + create: { + authorizedRoles: ['Client'], + requireAuthentication: true, + }, + }, + }, + ]) + const wabe = setup.wabe + const port = setup.port + const client = getAnonymousClient(port) + const rootClient = getGraphqlClient(port) + + const { userClient, userId } = await createUserAndUpdateRole({ + anonymousClient: client, + port, + roleName: 'Client', + rootClient, + }) + + const clientRole = await rootClient.request(gql` + query getClientRole { + roles(where: { name: { equalTo: "Client" } }) { + edges { + node { + id + } + } + } + } + `) + + const clientRoleId = clientRole.roles.edges[0].node.id + + const objectCreated = await rootClient.request(gql` + mutation createTestNotContains { + createTestNotContains(input: { fields: { name: "secret" } }) { + testNotContains { + id + } + } + } + `) + + const objectId = objectCreated.createTestNotContains.testNotContains.id + + await rootClient.request(gql` + mutation updateACL { + updateTestNotContains(input: { + id: "${objectId}", + fields: { + acl: { + users: [{ + userId: "${userId}", + read: false, + write: false + }], + roles: [{ + roleId: "${clientRoleId}", + read: true, + write: true + }] + } + } + }) { + testNotContains { + id + } + } + } + `) + + await expect( + userClient.request(gql` + query testNotContains { + testNotContains(id: "${objectId}") { + id + name + } + } + `), + ).rejects.toThrow('Object not found') + + const resList = await userClient.request(gql` + query testNotContainses { + testNotContainses { + edges { + node { + id + name + } + } + } + } + `) + + expect(resList.testNotContainses.edges.length).toEqual(0) + + await closeTests(wabe) + }) + it('should not authorize an user to write (delete) an object when the user has not access on write to the object (ACL)', async () => { const setup = await setupTests([ {