diff --git a/.gitignore b/.gitignore index 3cdeabcd..c4be777f 100644 --- a/.gitignore +++ b/.gitignore @@ -31,4 +31,7 @@ tests/node_modules tests/screenshot* # notebooks -.ipynb_checkpoints \ No newline at end of file +.ipynb_checkpoints + +/.idea +/log \ No newline at end of file diff --git a/gofr-backend/.gitignore b/gofr-backend/.gitignore index e812639c..afb80b1e 100644 --- a/gofr-backend/.gitignore +++ b/gofr-backend/.gitignore @@ -1,2 +1,3 @@ /node_modules lib/dbArhives +/coverage diff --git a/gofr-backend/__tests__/config.test.js b/gofr-backend/__tests__/config.test.js new file mode 100644 index 00000000..fafb6cc8 --- /dev/null +++ b/gofr-backend/__tests__/config.test.js @@ -0,0 +1,115 @@ +'use strict' + +const nconf = require('nconf') +const path = require('path') +const configFilePath = path.join(__dirname, "./config/default.json") +nconf.file({file: configFilePath}) + +const testConfig = nconf.get('test') + +const jest_setup = require('../jest-setup') +const config = require("../lib/config"); +jest.mock('axios') + +const DEFAULT_URL = `${testConfig.DEFAULT_URL}/` + +describe('Loads nconf plus base config', () => { + const CONFIG_FILE_OUTPUT = { + "keys": {"gofr": "-----BEGIN PUBLIC KEY-----\nMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDdeFrJr76IQ+SYAoAIw8crZKNW\nir2re7Z7Iu+XzeYYop5+36Ux6uEQKSXo7s1xY2ou9nCkVAddZ1qehBo0e2MCtk62\nmQJbBT18fiZ3veQPvb0LC/9aFl64RuOguPrCZC+sbZLegQ6Wwf96UWyqmR49gaHO\nEdXwdFdSVyBGyS7dmwIDAQAB\n-----END PUBLIC KEY-----"}, + "additionalConfig": {"gofr-config": "gofr-config"}, + "mCSD": { + "server": { + "protocal": "http", + "host": "localhost", + "port": "8090", + "basePath": "fhir", + "username": "", + "password": "" + }, + "fakeOrgId": "eac583d2-d1ba-11e8-a8d5-f2801f1b9fd1", + "fakeOrgName": "Taifafeki", + "cacheTime": 1200, + "registryDB": "DEFAULT" + }, + } + + const CONFIG_FULL_OUTPUT = { + "additionalConfig": { + "gofr-config": "gofr-config" + }, "keys": { + "gofr": "-----BEGIN PUBLIC KEY-----\nMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDdeFrJr76IQ+SYAoAIw8crZKNW\nir2re7Z7Iu+XzeYYop5+36Ux6uEQKSXo7s1xY2ou9nCkVAddZ1qehBo0e2MCtk62\nmQJbBT18fiZ3veQPvb0LC/9aFl64RuOguPrCZC+sbZLegQ6Wwf96UWyqmR49gaHO\nEdXwdFdSVyBGyS7dmwIDAQAB\n-----END PUBLIC KEY-----" + }, "mCSD": { + "server": { + "protocal": "http", + "host": "localhost", + "port": "8090", + "basePath": "fhir", + "username": "", + "password": "" + }, + "fakeOrgId": "eac583d2-d1ba-11e8-a8d5-f2801f1b9fd1", + "fakeOrgName": "Taifafeki", + "cacheTime": 1200, + "registryDB": "DEFAULT" + }, + } + + const MOCK_FHIR_OBJECT = { + "resourceType": "Parameters", "id": "gofr-config", "meta": { + "profile": ["http://gofr.org/fhir/StructureDefinition/gofr-parameters-remote-config"] + }, "parameter": [{ + "name": "signature", "valueSignature": { + "type": [{ + "code": "1.2.840.10065.1.12.1.14", "system": "urn:iso-astm:E1762-95:2013" + }], + "when": "2022-09-20T03:44:20.575Z", + "who": { + "reference": "http://gofr.org/fhir/Organization/gofr" + }, + "data": "Td3sLJzy//AlrMy/w+FMYkv1V2zd7vro0+sQiQK32CwXun1B9MYMTLLA214BWsOmnUhi82Dojo3Jn3U2GpPf0BCu47wlln1weYsLA169HDLkG50ch89p3YZ87TyNiNidctCaAHAQ4gz8W+X20szMZeqTkOy/EoEGW0+GuPNGbpw=" + } + }, { + "name": "config", "part": [{ + "name": "site:title", "valueString": "Manage" + }, { + "name": "site:site", "valueString": "Demo" + }, { + "name": "site:logo", "valueString": "iHRIS5Logo.png" + }] + }] + } + + beforeEach(() => { + require('axios').__setFhirResults(DEFAULT_URL + "Parameters/gofr-config", null, MOCK_FHIR_OBJECT) + }) + + test('Check if object contains required properties', () => { + let additionalConfig = config.get('additionalConfig') + let keys = config.get('keys') + let appConfig = config.get('app') + expect(additionalConfig).toHaveProperty('gofr-config') + expect(keys).toHaveProperty('gofr') + expect(appConfig).toHaveProperty('site'); + expect(appConfig.site).toHaveProperty('path'); + expect(appConfig).toHaveProperty('core'); + expect(appConfig.core).toHaveProperty('path'); + }) + + test('loads default config from mock file and mock fhir server', () => { + const nconf = require('../lib/config') + let config = { + additionalConfig: nconf.get('additionalConfig'), keys: nconf.get('keys'), mCSD: nconf.get('mCSD') + } + expect(config).toEqual(CONFIG_FILE_OUTPUT) + }) + + test('loads default config and remote config from mock fhir server', () => { + const nconf = require('../lib/config') + return nconf.loadRemote().then(() => { + let config = { + additionalConfig: nconf.get('additionalConfig'), keys: nconf.get('keys'), mCSD: nconf.get('mCSD') + } + expect(config).toEqual(CONFIG_FULL_OUTPUT) + }) + }) +}) diff --git a/gofr-backend/__tests__/config/default.json b/gofr-backend/__tests__/config/default.json index 17fe04bf..caf7bd5d 100644 --- a/gofr-backend/__tests__/config/default.json +++ b/gofr-backend/__tests__/config/default.json @@ -10,7 +10,7 @@ "idp": "dhis2", "version": "2.2.0", "site": { - "path": "/home/ally/gofr/gofr-backend/lib/gofr-backend-site", + "path": "/home/pc/Documents/Intrahealth/Gofr/updated/gofr/gofr-backend/lib/gofr-backend-site", "routes": { "performance": { "mount": "performance", @@ -19,7 +19,7 @@ } }, "core": { - "path": "/home/ally/gofr/gofr-backend/lib" + "path": "/home/pc/Documents/Intrahealth/Gofr/updated/gofr/gofr-backend/lib" } }, "server": { @@ -88,8 +88,8 @@ "server": { "protocal": "http", "host": "localhost", - "port": "8081", - "basePath": "gofr/fhir", + "port": "8090", + "basePath": "fhir", "username": "", "password": "" }, @@ -163,5 +163,9 @@ "encryption": { "algorithm": "aes-256-ctr", "secret": "b1ea334a-bf5a-11e8-a355-529269fb1459" + }, + "test": { + "DEFAULT_URL": "http://localhost:8090/fhir/DEFAULT", + "PARTITION": "DEFAULT" } } \ No newline at end of file diff --git a/gofr-backend/__tests__/route-config-test.js b/gofr-backend/__tests__/route-config-test.js deleted file mode 100644 index c083f487..00000000 --- a/gofr-backend/__tests__/route-config-test.js +++ /dev/null @@ -1,5 +0,0 @@ -'use strict' - -const jest_setup = require('../jest-setup') -jest.mock('axios') - diff --git a/gofr-backend/__tests__/route.auth.test.js b/gofr-backend/__tests__/route.auth.test.js new file mode 100644 index 00000000..1d1f4c75 --- /dev/null +++ b/gofr-backend/__tests__/route.auth.test.js @@ -0,0 +1,119 @@ +'use strict' +const nconf = require('nconf') +const path = require('path') +const configFilePath = path.join(__dirname, "./config/default.json") +nconf.file({file: configFilePath}) + +const testConfig = nconf.get('test') + +const jest_setup = require('../jest-setup') +jest.mock('axios') + +const DEFAULT_URL = testConfig.DEFAULT_URL + +const request = require("supertest") +const route = require("../lib/routes/auth") + +const express = require('express') +const app = express() +app.use(express.urlencoded({extended: false})) +app.use("/", route) + +describe('Test Auth routes', () => { + describe('Test Public User', () => { + test('Test Public User setup', () => { + const MOCK_PUBLIC_USER = { + "resourceType": "Person", "id": "26e19ebd-65e5-4181-84b6-589bcf3bc44b", "meta": { + "versionId": "1", "profile": ["http://gofr.org/fhir/StructureDefinition/gofr-person-user"] + }, "extension": [{ + "url": "http://gofr.org/fhir/StructureDefinition/gofr-owning-organization", "valueReference": { + "reference": "Organization/54cdcbe3-87e0-421f-b657-8313fce5f418" + } + }, { + "url": "http://gofr.org/fhir/StructureDefinition/gofr-password", "extension": [{ + "url": "hash", + "valueString": "6906687939864f1462d52839f52c8800b9ead0aa67d050649a9eed1d9c9ffbe5d815b4d286ee2c2786d1f9781ba72df087b9a2057e8a96c4fc30e870a5cc587b" + }, { + "url": "salt", "valueString": "be664906fbbe50918d8cadb5ebd22093" + }] + }, { + "url": "http://gofr.org/fhir/StructureDefinition/gofr-assign-role", "valueReference": { + "reference": "Basic/gofr-role-public" + } + }], "name": [{ + "text": "GOFR Public User" + }], "telecom": [{ + "system": "email", "value": "public@gofr.org" + }, { + "system": "phone" + }], "active": true + } + require('axios').__setFhirResults(DEFAULT_URL + "/Person/26e19ebd-65e5-4181-84b6-589bcf3bc44b", null, MOCK_PUBLIC_USER) + return request(app).get(`/`).then((response) => { + expect(response.statusCode).toBe(200) + expect(response.body).toEqual({ok: true}) + }) + }) + }) + + describe('Test Local User', () => { + const MOCK_LOCAL_USER_LOOKUP = { + "resourceType": "Bundle", "type": "searchset", "total": 1, "entry": [{ + "furllUrl": DEFAULT_URL + "Person/e9b41c35-7c85-46df-aeea-a4e8dbf0364e", "resource": { + "resourceType": "Person", "id": "e9b41c35-7c85-46df-aeea-a4e8dbf0364e", "meta": { + "versionId": "1", + "lastUpdated": "2023-11-08T10:59:50.435+00:00", + "source": "#fYi1xDSww6QUe05Y", + "profile": ["http://gofr.org/fhir/StructureDefinition/gofr-person-user"] + }, "extension": [{ + "url": "http://gofr.org/fhir/StructureDefinition/gofr-owning-organization", "valueReference": { + "reference": "Organization/54cdcbe3-87e0-421f-b657-8313fce5f418" + } + }, { + "url": "http://gofr.org/fhir/StructureDefinition/gofr-password", "extension": [{ + "url": "hash", + "valueString": "727c00bcb3d604db9b807155240b97347951e5e89e4c69b823279287694501fcaa683d883f5854a05c2c50c5b31413c6bb4a5949876a42b5c5bd74247e5777fc" + }, { + "url": "salt", "valueString": "be664906fbbe50918d8cadb5ebd22093" + }] + }, { + "url": "http://gofr.org/fhir/StructureDefinition/gofr-assign-role", "valueReference": { + "reference": "Basic/gofr-role-admin" + } + }], "name": [{ + "text": "GOFR Admin" + }], "telecom": [{ + "system": "email", "value": "root@gofr.org" + }, { + "system": "phone" + }], "active": true + } + }] + } + + require('axios').__setFhirResults(DEFAULT_URL + "/Person", {telecom: "email|root@gofr.org"}, MOCK_LOCAL_USER_LOOKUP) + test('test local user login', () => { + return request(app).post("/login").send("username=root@gofr.org&password=gofr").then((response) => { + expect(response.statusCode).toBe(200) + expect(response.body.userObj.resource).toEqual(MOCK_LOCAL_USER_LOOKUP.entry[0].resource) + }) + }) + + test('test invalid password', () => { + return request(app).post("/login").send("username=root@gofr.org&password=wrong").then((response) => { + expect(response.statusCode).toBe(401) + }) + }) + + test('test invalid user', () => { + const MOCK_LOCAL_MISSING = { + "resourceType": "Bundle", "type": "searchset", "total": 0, "entry": [] + } + require('axios').__setFhirResults(DEFAULT_URL + "/Person", {telecom: "email|notfound@gofr.org"}, MOCK_LOCAL_MISSING) + return request(app).post("/login").send("username=notfound@gofr.org&password=wrong").then((response) => { + expect(response.statusCode).toBe(401) + }) + + }) + }) +}) diff --git a/gofr-backend/__tests__/route.fhir.test.js b/gofr-backend/__tests__/route.fhir.test.js new file mode 100644 index 00000000..6143b14a --- /dev/null +++ b/gofr-backend/__tests__/route.fhir.test.js @@ -0,0 +1,355 @@ +'use strict' +const nconf = require('nconf') +const path = require('path') +const configFilePath = path.join(__dirname, "./config/default.json") +nconf.file({file: configFilePath}) + +const testConfig = nconf.get('test') +const jest_setup = require('../jest-setup') +jest.mock('axios') + +const DEFAULT_URL = testConfig.DEFAULT_URL +const user = require('../lib/modules/user') + +const PARTITION = testConfig.PARTITION + +const TEST_USER = user.restoreUser({ + resource: {resourceType: "Person"}, permissions: { + "partitions": [{ + "name": "DEFAULT", "*": { + "*": true + } + }, { + "name": "Malawie9b41c35-7c85-46df-aeea-a4e8dbf0364e", "*": { + "*": true + } + }], "special": { + "*": true + }, "read": { + "StructureDefinition": true, "CodeSystem": true, "ValueSet": true, "DocumentReference": { + "constraint": { + "category.exists(coding.exists(code = 'open'))": true + } + } + } + } +}) +const TEST_USER_FAIL = user.restoreUser({ + resource: {resourceType: "Person"}, permissions: {} + +}) +const route = require('../lib/routes/fhir') + + +const express = require('express') +const app = express() +app.use(express.json()) + +const request = require("supertest") +const axios = require('../__mocks__/axios') + +// Set up middleware to add mocks for anything that would exist in the request like session and user from passport +let appUser = TEST_USER +app.use((req, res, next) => { + req.user = appUser + next() +}) + +app.use('/', route) + +describe('TEST /fhir routes', () => { + const MOCK_PERSON = { + resourceType: "Person", id: "test-person", name: [{use: "official", family: "Tester", given: ["Test", "E."]}] + } + const MOCK_PERSON_FAIL = { + resourceType: "Person", id: "test-fail", name: [{use: "official", family: "Tester", given: ["Test", "E."]}] + } + const NOT_FOUND_OUTCOME = { + "resourceType": "OperationOutcome", "issue": [{ + "severity": "error", "code": "exception", "diagnostics": { + "response": "Not found" + } + }] + } + + const NOTLOGGEDIN_OUTCOME = { + resourceType: "OperationOutcome", issue: [{ + severity: "error", code: "forbidden", diagnostics: "Not logged in" + }] + } + const DENIED_OUTCOME = { + resourceType: "OperationOutcome", issue: [{ + severity: "error", code: "forbidden", diagnostics: "Access Denied" + }] + } + + describe('GET /:partition/:resource/:id route', () => { + + it('Should return Not found', () => { + return request(app).get(`/${PARTITION}/StructureDefinition/mock-test`).then((response) => { + expect(response.body).toEqual(NOT_FOUND_OUTCOME) + }) + }) + + it('Should return Not LoggedIn', () => { + appUser = undefined + return request(app).get(`/${PARTITION}/StructureDefinition/mock-test`).then((response) => { + expect(response.statusCode).toBe(401) + expect(response.body).toEqual(NOTLOGGEDIN_OUTCOME) + }) + }) + + it('Should return access denied ', () => { + appUser = TEST_USER_FAIL + return request(app).get(`/${PARTITION}/StructureDefinition/mock-test`).then((response) => { + expect(response.statusCode).toBe(403) + expect(response.body).toEqual(DENIED_OUTCOME) + }) + }) + + it('test read StructureDefinition instance', () => { + const MOCK_PROFILE = { + resourceType: "StructureDefinition", id: "mock-test" + } + appUser = TEST_USER + axios.__setFhirResults(DEFAULT_URL + "/StructureDefinition/mock-test", null, MOCK_PROFILE) + return request(app).get(`/${PARTITION}/StructureDefinition/mock-test`).then((response) => { + expect(response.body).toEqual(MOCK_PROFILE) + }) + }) + + test('test read Location without user', () => { + appUser = null + return request(app).get(`/${PARTITION}/Location/test-practitioner`).then((response) => { + expect(response.statusCode).toBe(401) + expect(response.body).toEqual(NOTLOGGEDIN_OUTCOME) + }) + }) + + test('test read Person without access', () => { + axios.__setFhirResults(DEFAULT_URL + "/Person/test-fail", null, MOCK_PERSON_FAIL) + appUser = TEST_USER_FAIL + return request(app).get(`/${PARTITION}/Person/test-fail`).then((response) => { + expect(response.statusCode).toBe(403) + expect(response.body).toEqual(DENIED_OUTCOME) + }) + }) + }) + + describe('GET /:partition/:resource/:params route', () => { + test('test search CodeSystem', () => { + const MOCK_CODESYSTEM = { + resourceType: "Bundle", id: "mock-test", test: "test", entry: [] + } + axios.__setFhirResults(DEFAULT_URL + "/CodeSystem", {test: "test"}, MOCK_CODESYSTEM) + + appUser = TEST_USER + return request(app).get(`/${PARTITION}/CodeSystem`).query("test=test").then((response) => { + expect(response.statusCode).toBe(200) + expect(response.body).toEqual(MOCK_CODESYSTEM) + }) + }) + test('test search CodeSystem without user', () => { + appUser = null + return request(app).get(`/${PARTITION}/CodeSystem`).query("test=test").then((response) => { + expect(response.statusCode).toBe(401) + expect(response.body).toEqual(NOTLOGGEDIN_OUTCOME) + }) + }) + test('test search Practitioner without access', () => { + appUser = TEST_USER_FAIL + return request(app).get(`/${PARTITION}/Practitioner`).query("test=test").then((response) => { + expect(response.statusCode).toBe(403) + expect(response.body).toEqual(DENIED_OUTCOME) + }) + + }) + }) + + describe('test GET /:partition/ValueSet/id/$expand', () => { + const MOCK_VALUESET_EXPANSION = { + resourceType: "ValueSet", id: "mock-test" + } + + test('test expand ValueSet instance', () => { + axios.__setFhirResults(DEFAULT_URL + "/ValueSet/mock-test/$expand", null, MOCK_VALUESET_EXPANSION) + appUser = TEST_USER + return request(app).get(`/${PARTITION}/ValueSet/mock-test/$expand`).then((response) => { + expect(response.statusCode).toBe(200) + expect(response.body).toEqual(MOCK_VALUESET_EXPANSION) + }) + }) + + test('test expand ValueSet instance without user', () => { + appUser = null + return request(app).get(`/${PARTITION}/ValueSet/mock-test/$expand`).then((response) => { + expect(response.statusCode).toBe(401) + expect(response.body).toEqual(NOTLOGGEDIN_OUTCOME) + }) + }) + + test('test expand ValueSet without access', () => { + appUser = TEST_USER_FAIL + return request(app).get(`/${PARTITION}/ValueSet/mock-fail/$expand`).then((response) => { + expect(response.statusCode).toBe(403) + expect(response.body).toEqual(DENIED_OUTCOME) + }) + }) + + test('test expand ValueSet with field level access', () => { + axios.__setFhirResults(DEFAULT_URL + "/ValueSet/mock-field/$expand", null, MOCK_VALUESET_EXPANSION) + appUser = null + return request(app).get(`/${PARTITION}/ValueSet/mock-field/$expand`).then((response) => { + expect(response.statusCode).toBe(401) + expect(response.body).toEqual(NOTLOGGEDIN_OUTCOME) + }) + }) + }) + + describe('test POST /:partition/:resource route', () => { + test('test create Person', () => { + axios.__setFhirResults(DEFAULT_URL + "/Person", MOCK_PERSON, MOCK_PERSON) + appUser = TEST_USER + return request(app).post(`/${PARTITION}/Person`).send(MOCK_PERSON).then((response) => { + let output = {...MOCK_PERSON} + output.id = "1" + expect(response.statusCode).toBe(201) + expect(response.body).toEqual(output) + }) + }) + test('test create Person without user', () => { + appUser = null + return request(app).post(`/${PARTITION}/Person`).send(MOCK_PERSON).then((response) => { + expect(response.statusCode).toBe(401) + expect(response.body).toEqual(NOTLOGGEDIN_OUTCOME) + }) + }) + test('test create Person without access', () => { + axios.__setFhirResults(DEFAULT_URL + "/Person", MOCK_PERSON_FAIL, MOCK_PERSON_FAIL) + appUser = TEST_USER_FAIL + return request(app).post(`/${PARTITION}/Person`).send(MOCK_PERSON_FAIL).then((response) => { + expect(response.statusCode).toBe(403) + expect(response.body).toEqual(DENIED_OUTCOME) + }) + }) + }) + + describe('test PUT /:partition/:resource/:id', () => { + + test('test update resource: PUT /Person/test-person', () => { + axios.__setFhirResults(DEFAULT_URL + "/Person/test-person", MOCK_PERSON, MOCK_PERSON) + appUser = TEST_USER + return request(app).put(`/${PARTITION}/Person/test-person`).send(MOCK_PERSON).then((response) => { + expect(response.statusCode).toBe(200) + expect(response.body).toEqual(MOCK_PERSON) + }) + }) + + test('test create Person without user', () => { + appUser = null + return request(app).put(`/${PARTITION}/Person/test-person`).send(MOCK_PERSON).then((response) => { + expect(response.statusCode).toBe(401) + expect(response.body).toEqual(NOTLOGGEDIN_OUTCOME) + }) + }) + + test('test create Person without access', () => { + axios.__setFhirResults(DEFAULT_URL + "/Person/test-fail", MOCK_PERSON_FAIL, MOCK_PERSON_FAIL) + appUser = TEST_USER_FAIL + return request(app).put(`/${PARTITION}/Person/test-fail`).send(MOCK_PERSON_FAIL).then((response) => { + expect(response.statusCode).toBe(403) + expect(response.body).toEqual(DENIED_OUTCOME) + }) + }) + }) + + describe('test GET /:partition/DocumentReference/id/$html', () => { + const MOCK_OPEN_DOCUMENT = { + "resourceType": "DocumentReference", + "id": "page-test", + "meta": {"profile": ["http://ihris.org/fhir/StructureDefinition/ihris-document"]}, + "status": "current", + "docStatus": "final", + "date": "2020-06-07T14:54:00Z", + "category": [{ + "coding": [{ + "code": "open", + "system": "http://ihris.org/fhir/CodeSystem/ihris-document-category", + "display": "Open Access" + }] + }], + "content": [{ + "attachment": { + "contentType": "text/markdown", "title": "Testing", "data": "IyBUZXN0aW5nCg==" + } + }] + } + const MOCK_DOCUMENT = {title: "Testing", html: "

Testing

\n
"} + const MOCK_RESTRICTED_DOCUMENT = { + "resourceType": "DocumentReference", + "id": "page-test", + "meta": {"profile": ["http://ihris.org/fhir/StructureDefinition/ihris-document"]}, + "status": "current", + "docStatus": "final", + "date": "2020-06-07T14:54:00Z", + "category": [{ + "coding": [{ + "code": "restricted", + "system": "http://ihris.org/fhir/CodeSystem/ihris-document-category", + "display": "Open Access" + }] + }], + "content": [{ + "attachment": { + "contentType": "text/markdown", "title": "Testing", "data": "IyBUZXN0aW5nCg==" + } + }] + } + + test('test open DocumentReference to HTML instance for open user', () => { + axios.__setFhirResults(DEFAULT_URL + "/DocumentReference/page-test", null, MOCK_OPEN_DOCUMENT) + + appUser = TEST_USER + return request(app).get(`/${PARTITION}/DocumentReference/page-test/$html`).then((response) => { + expect(response.statusCode).toBe(200) + expect(response.body).toEqual(MOCK_DOCUMENT) + }) + }) + + test('test restricted DocumentReference to HTML instance for open user', () => { + axios.__setFhirResults(DEFAULT_URL + "/DocumentReference/page-test", null, MOCK_RESTRICTED_DOCUMENT) + appUser = TEST_USER_FAIL + return request(app).get(`/${PARTITION}/DocumentReference/page-test/$html`).then((response) => { + expect(response.statusCode).toBe(403) + expect(response.body).toEqual(DENIED_OUTCOME) + }) + }) + }) + + describe('test GET /:partition/$short-name', () => { + const MOCK_CODE_LOOKUP = { + "resourceType": "Parameters", "parameter": [{ + "name": "display", "valueString": "Test" + }] + } + const MOCK_STANDARD_RESOURCE = { + "resourceType": "Location", "id": "test", "name": "Test Location" + } + + test('Lookup a codesystem value that exists', () => { + require('axios').__setFhirResults(DEFAULT_URL + "/CodeSystem/$lookup?system=test-system&code=test", null, MOCK_CODE_LOOKUP) + appUser = TEST_USER + return request(app).get(`/${PARTITION}/$short-name?system=test-system&code=test`).then((response) => { + expect(response.body).toEqual({display: "Test"}) + }) + }) + + test('resource standard lookup', () => { + require('axios').__setFhirResults(DEFAULT_URL + "/Location/test", null, MOCK_STANDARD_RESOURCE) + appUser = TEST_USER + return request(app).get(`/${PARTITION}/$short-name?reference=Location/test`).then((response) => { + expect(response.body).toEqual({display: "Test Location"}) + }) + }) + }) +}) diff --git a/gofr-backend/jest.config.js b/gofr-backend/jest.config.js index 482613e6..f1d1ae7d 100644 --- a/gofr-backend/jest.config.js +++ b/gofr-backend/jest.config.js @@ -148,7 +148,8 @@ module.exports = { // "/node_modules/" // ], testPathIgnorePatterns: [ - "resources/" + "resources/", + "__tests__/config/operationOutcomes.js", ], // The regexp pattern or array of patterns that Jest uses to detect test files diff --git a/gofr-backend/lib/routes/fhir.js b/gofr-backend/lib/routes/fhir.js index ddd707fb..75f8ab35 100644 --- a/gofr-backend/lib/routes/fhir.js +++ b/gofr-backend/lib/routes/fhir.js @@ -20,6 +20,9 @@ router.get('/:partition/:resource/:id?', (req, res, next) => { if (req.params.resource.startsWith('$') || (req.params.id && req.params.id.startsWith('$'))) { return next(); } + if ( !req.user ) { + return res.status(401).json( outcomes.NOTLOGGEDIN) + } let allowed = false; if (req.params.id) { allowed = req.user.hasPermissionByName('read', req.params.resource, req.params.id, req.params.partition); @@ -49,7 +52,7 @@ router.get('/:partition/:resource/:id?', (req, res, next) => { /* for custom responses */ logger.error(err.message); const outcome = { ...outcomes.ERROR }; - outcome.issue[0].diagnostics = err.message; + outcome.issue[0].diagnostics = err; return res.status(500).json(outcome); }); } else { @@ -82,13 +85,16 @@ router.get('/:partition/:resource/:id?', (req, res, next) => { }).catch((err) => { logger.error(err.message); const outcome = { ...outcomes.ERROR }; - outcome.issue[0].diagnostics = err.message; + outcome.issue[0].diagnostics = err; return res.status(500).json(outcome); }); } }); router.post('/:partition/:resource', (req, res) => { + if ( !req.user ) { + return res.status(401).json( outcomes.NOTLOGGEDIN) + } const allowed = req.user.hasPermissionByObject('write', req.body, req.params.partition); let resource; if (allowed === true) { @@ -100,7 +106,7 @@ router.post('/:partition/:resource', (req, res) => { } fhirAxios.create(resource, req.params.partition).then((output) => { fhirAudit.create(req.user, req.ip, `${output.resourceType}/${output.id - }${output.meta.versionId ? `/_history/${output.meta.versionId}` : ''}`, true); + }${output?.meta?.versionId ? `/_history/${output.meta.versionId}` : ''}`, true); return res.status(201).json(output); }).catch((err) => { fhirAudit.create(req.user, req.ip, null, false, { resource, err }); @@ -190,6 +196,9 @@ router.patch('/:partition/CodeSystem/:id/:code', (req, res) => { }); router.put('/:partition/:resource/:id', (req, res) => { + if ( !req.user ) { + return res.status(401).json( outcomes.NOTLOGGEDIN) + } const update = req.body; const allowed = req.user.hasPermissionByObject('write', update, req.params.partition); if (!allowed) { @@ -204,7 +213,7 @@ router.put('/:partition/:resource/:id', (req, res) => { } fhirAxios.update(update, req.params.partition).then((resource) => { fhirAudit.update(req.user, req.ip, `${resource.resourceType}/${resource.id - }${resource.meta.versionId ? `/_history/${resource.meta.versionId}` : ''}`, true); + }${resource?.meta?.versionId ? `/_history/${resource.meta.versionId}` : ''}`, true); res.status(200).json(resource); }).catch((err) => { /* return response from FHIR server */ @@ -218,6 +227,9 @@ router.put('/:partition/:resource/:id', (req, res) => { }); router.get('/:partition/ValueSet/:id/\\$expand', (req, res) => { + if ( !req.user ) { + return res.status(401).json( outcomes.NOTLOGGEDIN) + } const allowed = req.user.hasPermissionByName('read', 'ValueSet', req.params.id); if (!allowed) { return res.status(403).json(outcomes.DENIED); @@ -260,6 +272,9 @@ router.get('/:partition/CodeSystem/\\$lookup', (req, res) => { }); router.get('/:partition/DocumentReference/:id/\\$html', (req, res) => { + if ( !req.user ) { + return res.status(401).json( outcomes.NOTLOGGEDIN) + } const allowed = req.user.hasPermissionByName('read', 'DocumentReference', req.params.id, req.params.partition); if (!allowed) { return res.status(403).json(outcomes.DENIED); diff --git a/gofr-backend/lib/routes/passportAuth.js b/gofr-backend/lib/routes/passportAuth.js index 12c1ed48..ce04b13a 100644 --- a/gofr-backend/lib/routes/passportAuth.js +++ b/gofr-backend/lib/routes/passportAuth.js @@ -111,7 +111,7 @@ router.get('/', async (req, res) => { userObj: req.user, }); } - return res.status(200).send(); + return res.status(200).send({ok: true}); }, passport.authenticate('custom-loggedout', {}), (req, res) => { if (req.user) { @@ -134,6 +134,7 @@ router.get('/google/callback', passport.authenticate('google', { failureRedirect router.post('/login', passport.authenticate('local', {}), (req, res) => { res.status(200).json({ + oK: true, userObj: req.user, }); }); diff --git a/gofr-backend/package.json b/gofr-backend/package.json index 4142c99d..9c9846e8 100644 --- a/gofr-backend/package.json +++ b/gofr-backend/package.json @@ -49,6 +49,7 @@ "ihrissmartrequire": "^1.0.1", "is-empty": "^1.2.0", "is-json": "^2.0.1", + "jest": "^29.7.0", "jsdom": "^16.5.3", "json-as-xlsx": "^2.4.2", "json-merger": "^1.1.7",