diff --git a/lib/domain/models/QueryAccess.ts b/lib/domain/models/QueryAccess.ts index 7d9e773..97e92e6 100644 --- a/lib/domain/models/QueryAccess.ts +++ b/lib/domain/models/QueryAccess.ts @@ -13,7 +13,7 @@ export class QueryAccessModel { areParamsValid(params: UserCommandParam[]): boolean { return params.every((param) => { - return this.queryAccess[param.name]?.includes(param.value.toString()); + return this.queryAccess[param.name]?.includes(param.value.toString()) || this.queryAccess[param.name]?.toString() === 'any'; }); } } diff --git a/tests/acceptance/application/query_test.ts b/tests/acceptance/application/query_test.ts index 3f8ad4d..0a654d4 100644 --- a/tests/acceptance/application/query_test.ts +++ b/tests/acceptance/application/query_test.ts @@ -1,4 +1,5 @@ import type { UUID } from 'node:crypto'; +import type { UserCommandParam } from '../../../lib/domain/commands/UserCommand.js'; import { createServer, expect, @@ -24,6 +25,8 @@ describe('Acceptance | query', function () { afterEach(async function () { await knexAPI('query_access').delete(); await knexAPI('user-logins').delete(); + await knexAPI('query_param_access').delete(); + await knexAPI('catalog_query_params').delete(); await knexAPI('users').delete(); await knexAPI('catalog_queries').delete(); }); @@ -33,7 +36,7 @@ describe('Acceptance | query', function () { // given const payload = { queryIdddddddd: 'a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a11', - params: [], + params: [], }; // when @@ -72,7 +75,7 @@ describe('Acceptance | query', function () { }); const payload = { queryId, - params: [], + params: [], }; // when @@ -109,7 +112,7 @@ describe('Acceptance | query', function () { }); const payload = { queryId, - params: [], + params: [], }; // when @@ -130,6 +133,319 @@ describe('Acceptance | query', function () { }); }); + context('when user uses params', function () { + context('when user does not have access to param', function () { + it('should return a proper error with status code 422', async function () { + // given + const queryId = '26f6efcc-ce13-4b20-b6ea-5bebae6115af'; + const otherUserId = '26f6efcc-ce13-4b20-b6ea-5bebae6115ae'; + + await knexAPI('users').insert({ + id: otherUserId, + username: 'gigi_lamorosa', + label: 'Gigi l\'amorosa', + hashed_password: 'coucou', + }); + + await knexAPI('catalog_queries').insert({ + id: queryId, + sql_query: `SELECT COUNT(*) as count FROM public.data_ref_academies where nom = {{ academie }}`, + name: 'foo', + }); + await knexAPI('query_access').insert({ + query_id: queryId, + user_id: userId, + }); + await knexAPI('catalog_query_params').insert({ + id: 1, + catalog_query_id: queryId, + name: 'acamdemie', + type: 'string', + mandatory: true, + }); + await knexAPI('query_param_access').insert({ + id: queryId, + user_id: otherUserId, + query_param_id: 1, + value: 'Bordeaux', + }); + + const payload = { + queryId, + params: [{ name: 'academie', value: 'Paris' }], + }; + + // when + const server = await createServer(); + const response = await server.inject({ + method: 'POST', + url: '/query', + payload, + headers: { authorization: headers }, + }); + // then + expect(response.statusCode).to.equal(422); + expect(JSON.parse(response.payload)).to.deep.equal({ + status: 'failure', + messages: ['No access to requested params'], + }); + }); + }); + context('when user has access to param', function () { + it('should return a proper payload response with status code 200', async function () { + // given + const queryId = '26f6efcc-ce13-4b20-b6ea-5bebae6115af'; + + await knexAPI('catalog_queries').insert({ + id: queryId, + sql_query: `SELECT COUNT(*) as count FROM public.data_ref_academies where nom = {{ academie }}`, + name: 'foo', + }); + await knexAPI('query_access').insert({ + query_id: queryId, + user_id: userId, + }); + await knexAPI('catalog_query_params').insert({ + id: 1, + catalog_query_id: queryId, + name: 'academie', + type: 'string', + mandatory: true, + }); + await knexAPI('query_param_access').insert({ + id: queryId, + user_id: userId, + query_param_id: 1, + value: 'Bordeaux', + }); + + const payload = { + queryId, + params: [{ name: 'academie', value: 'Bordeaux' }], + }; + + // when + const server = await createServer(); + const response = await server.inject({ + method: 'POST', + url: '/query', + payload, + headers: { authorization: headers }, + }); + + // then + expect(response.statusCode).to.equal(200); + expect(JSON.parse(response.payload)).to.deep.equal({ + status: 'success', + data: [{ count: 1 }], + messages: [], + }); + }); + }); + context('when user does not have access to value', function () { + it('should return a proper error with status code 422', async function () { + // given + const queryId = '26f6efcc-ce13-4b20-b6ea-5bebae6115af'; + + await knexAPI('catalog_queries').insert({ + id: queryId, + sql_query: `SELECT COUNT(*) as count FROM public.data_ref_academies where nom = {{ academie }}`, + name: 'foo', + }); + await knexAPI('query_access').insert({ + query_id: queryId, + user_id: userId, + }); + await knexAPI('catalog_query_params').insert({ + id: 1, + catalog_query_id: queryId, + name: 'academie', + type: 'string', + mandatory: true, + }); + await knexAPI('query_param_access').insert({ + id: queryId, + user_id: userId, + query_param_id: 1, + value: 'Bordeaux', + }); + + const payload = { + queryId, + params: [{ name: 'academie', value: 'Paris' }], + }; + + // when + const server = await createServer(); + const response = await server.inject({ + method: 'POST', + url: '/query', + payload, + headers: { authorization: headers }, + }); + + // then + expect(response.statusCode).to.equal(422); + expect(JSON.parse(response.payload)).to.deep.equal({ + status: 'failure', + messages: ['No access to requested params'], + }); + }); + }); + + context('when user does not have access to wildcard', function () { + it('should return a proper error with status code 422', async function () { + // given + const queryId = '26f6efcc-ce13-4b20-b6ea-5bebae6115af'; + + await knexAPI('catalog_queries').insert({ + id: queryId, + sql_query: `SELECT COUNT(*) as count FROM public.data_ref_academies where nom = {{ academie }}`, + name: 'foo', + }); + await knexAPI('query_access').insert({ + query_id: queryId, + user_id: userId, + }); + await knexAPI('catalog_query_params').insert({ + id: 1, + catalog_query_id: queryId, + name: 'academie', + type: 'string', + mandatory: true, + }); + await knexAPI('query_param_access').insert({ + id: queryId, + user_id: userId, + query_param_id: 1, + value: 'Bordeaux', + }); + + const payload = { + queryId, + params: [{ name: 'academie', value: 'Paris' }], + }; + + // when + const server = await createServer(); + const response = await server.inject({ + method: 'POST', + url: '/query', + payload, + headers: { authorization: headers }, + }); + + // then + expect(response.statusCode).to.equal(422); + expect(JSON.parse(response.payload)).to.deep.equal({ + status: 'failure', + messages: ['No access to requested params'], + }); + }); + }); + + context('when user has access to wildcard', function () { + it('should return a proper payload response with status code 200 when catalog_params is string', async function () { + // given + const queryId = '26f6efcc-ce13-4b20-b6ea-5bebae6115af'; + + await knexAPI('catalog_queries').insert({ + id: queryId, + sql_query: `SELECT COUNT(*) as count FROM public.data_ref_academies where nom = {{ academie }}`, + name: 'foo', + }); + await knexAPI('query_access').insert({ + query_id: queryId, + user_id: userId, + }); + await knexAPI('catalog_query_params').insert({ + id: 1, + catalog_query_id: queryId, + name: 'academie', + type: 'string', + mandatory: true, + }); + await knexAPI('query_param_access').insert({ + id: queryId, + user_id: userId, + query_param_id: 1, + value: 'any', + }); + + const payload = { + queryId, + params: [{ name: 'academie', value: 'Bordeaux' }], + }; + + // when + const server = await createServer(); + const response = await server.inject({ + method: 'POST', + url: '/query', + payload, + headers: { authorization: headers }, + }); + + // then + expect(response.statusCode).to.equal(200); + expect(JSON.parse(response.payload)).to.deep.equal({ + status: 'success', + data: [{ count: 1 }], + messages: [], + }); + }); + + it('should return a proper payload response with status code 200 when catalog_params is int', async function () { + // given + const queryId = '26f6efcc-ce13-4b20-b6ea-5bebae6115af'; + + await knexAPI('catalog_queries').insert({ + id: queryId, + sql_query: `SELECT COUNT(*) as count FROM public.data_ref_academies where id = {{ academieId }}`, + name: 'foo', + }); + await knexAPI('query_access').insert({ + query_id: queryId, + user_id: userId, + }); + await knexAPI('catalog_query_params').insert({ + id: 1, + catalog_query_id: queryId, + name: 'academieId', + type: 'int', + mandatory: true, + }); + await knexAPI('query_param_access').insert({ + id: queryId, + user_id: userId, + query_param_id: 1, + value: 'any', + }); + + const payload = { + queryId, + params: [{ name: 'academieId', value: 1 }], + }; + + // when + const server = await createServer(); + const response = await server.inject({ + method: 'POST', + url: '/query', + payload, + headers: { authorization: headers }, + }); + + // then + expect(response.statusCode).to.equal(200); + expect(JSON.parse(response.payload)).to.deep.equal({ + status: 'success', + data: [{ count: 1 }], + messages: [], + }); + }); + }); + }); context('when user request response in csv', function () { it('should return a csv response with status code 200', async function () { // given @@ -145,7 +461,7 @@ describe('Acceptance | query', function () { }); const payload = { queryId, - params: [], + params: [], }; // when @@ -177,7 +493,7 @@ describe('Acceptance | query', function () { }); const payload = { queryId, - params: [], + params: [], }; // when diff --git a/tests/integration/infrastructure/repositories/DatamartRepository_test.ts b/tests/integration/infrastructure/repositories/DatamartRepository_test.ts new file mode 100644 index 0000000..d77a768 --- /dev/null +++ b/tests/integration/infrastructure/repositories/DatamartRepository_test.ts @@ -0,0 +1,23 @@ +import { DatamartQueryModel } from '../../../../lib/domain/models/DatamartQuery.js'; +import { datamartRepository } from '../../../../lib/infrastructure/DatamartRepository.js'; +import { expect } from '../../../test-helper.js'; + +describe('Integration | Repository | DatamartRepository', function () { + describe('#find', function () { + it('should execute given DatamartQueryModel', async function () { + // given + const datamartQuery = new DatamartQueryModel({ query: 'SELECT 1=1 as foo;', paramValues: [], paramDefinitions: [] }); + + // when + const { result: stream } = datamartRepository.find(datamartQuery); + + // then + let result; + for await (const chunk of stream) { + result = chunk; + } + + expect(result).to.deep.equal({ foo: true }); + }); + }); +}); diff --git a/tests/unit/domain/models/QueryAccess_test.ts b/tests/unit/domain/models/QueryAccess_test.ts index a752763..5b7b4a3 100644 --- a/tests/unit/domain/models/QueryAccess_test.ts +++ b/tests/unit/domain/models/QueryAccess_test.ts @@ -58,5 +58,22 @@ describe('Unit | Domain | Models | QueryAccess', function () { expect(queryAccessModel.areParamsValid(userCommandParams)).to.be.false; }); }); + + context('when userCommandParams has access to all values', function () { + it('should return true', function () { + // given + const queryAccess: QueryAccess = { + id: ['any'], + }; + + const userCommandParams: UserCommandParam[] = [{ name: 'id', value: '456' }]; + + // when + const queryAccessModel = new QueryAccessModel(queryAccess); + + // then + expect(queryAccessModel.areParamsValid(userCommandParams)).to.be.true; + }); + }); }); });