From 3c13155a1ff90b5cae1dfc5a2e5865c02d997516 Mon Sep 17 00:00:00 2001 From: yaswanth-pula-skyflow Date: Tue, 30 May 2023 13:26:45 +0530 Subject: [PATCH 1/2] SK-351 add redaction to detokenize interface. --- src/vault-api/core/Reveal.ts | 9 +++++---- src/vault-api/utils/constants.ts | 4 ++++ src/vault-api/utils/logs.ts | 1 + src/vault-api/utils/validators/index.ts | 6 ++++++ 4 files changed, 16 insertions(+), 4 deletions(-) diff --git a/src/vault-api/core/Reveal.ts b/src/vault-api/core/Reveal.ts index ea417757..86a40b2f 100644 --- a/src/vault-api/core/Reveal.ts +++ b/src/vault-api/core/Reveal.ts @@ -4,7 +4,7 @@ import Client from '../client'; import SkyflowError from '../libs/SkyflowError'; import { - ISkyflowIdRecord, IRevealRecord, IRevealResponseType, IUpdateRecord, IUpdateOptions, + ISkyflowIdRecord, IRevealRecord, IRevealResponseType, IUpdateRecord, IUpdateOptions,RedactionType } from '../utils/common'; import 'core-js/modules/es.promise.all-settled'; interface IApiSuccessResponse { @@ -81,7 +81,7 @@ const getSkyflowIdRecordsFromVault = ( }; const getTokenRecordsFromVault = ( - token: string, + tokenRecord: IRevealRecord, client: Client, authToken: string, ): Promise => { @@ -97,7 +97,8 @@ const getTokenRecordsFromVault = ( { detokenizationParameters: [ { - token, + token : tokenRecord.token, + redaction: (tokenRecord?.redaction ? tokenRecord.redaction : RedactionType.PLAIN_TEXT) }, ], }, @@ -112,7 +113,7 @@ export const fetchRecordsByTokenId = ( const vaultResponseSet: Promise[] = tokenIdRecords.map( (tokenRecord) => new Promise((resolve) => { const apiResponse: any = []; - getTokenRecordsFromVault(tokenRecord.token, client, authToken as string) + getTokenRecordsFromVault(tokenRecord, client, authToken as string) .then( (response: IApiSuccessResponse) => { const fieldsData = formatForPureJsSuccess(response); diff --git a/src/vault-api/utils/constants.ts b/src/vault-api/utils/constants.ts index a66fe178..3283cd0c 100644 --- a/src/vault-api/utils/constants.ts +++ b/src/vault-api/utils/constants.ts @@ -126,6 +126,10 @@ const SKYFLOW_ERROR_CODE = { INVALID_GET_BY_ID_INPUT:{ code: 400, description: logs.errorLogs.INVALID_GET_BY_ID_INPUT, + }, + DETOKENIZE_INVALID_REDACTION_TYPE:{ + code: 400, + description: logs.errorLogs.DETOKENIZE_INVALID_REDACTION_TYPE, } }; diff --git a/src/vault-api/utils/logs.ts b/src/vault-api/utils/logs.ts index c0f7caa0..d9fb427e 100644 --- a/src/vault-api/utils/logs.ts +++ b/src/vault-api/utils/logs.ts @@ -125,6 +125,7 @@ const logs = { INVALID_UPDATE_INPUT: 'Interface: update method - Invalid argument , object with records key is required.', INVALID_RECORDS_UPDATE_INPUT: 'Interface: update method - Invalid records type, records should be an array of objects.', INVALID_GET_BY_ID_INPUT: 'Interface: getById method - columnName or columnValues cannot be passed, use get method instead.', + DETOKENIZE_INVALID_REDACTION_TYPE:'Interface: detokenize method - Invalid redaction type, use Skyflow.RedactionType enum.', }, warnLogs: { GENERATE_BEARER_DEPRECATED: 'This method has been deprecated will be removed in future release, use GenerateBearerToken instead', diff --git a/src/vault-api/utils/validators/index.ts b/src/vault-api/utils/validators/index.ts index d912d3cb..4c06e16c 100644 --- a/src/vault-api/utils/validators/index.ts +++ b/src/vault-api/utils/validators/index.ts @@ -43,6 +43,12 @@ export const validateDetokenizeInput = (detokenizeInput: IDetokenizeInput) => { } if (recordToken === '' || typeof recordToken !== 'string') { throw new SkyflowError(SKYFLOW_ERROR_CODE.INVALID_TOKEN_ID); } + if (Object.prototype.hasOwnProperty.call(record, 'redaction')) { + if (!Object.values(RedactionType).includes(record.redaction as RedactionType)) { + throw new SkyflowError(SKYFLOW_ERROR_CODE.DETOKENIZE_INVALID_REDACTION_TYPE); + } + } + }); }; From 978a7b04b7bb07237547830573fd3ab71b4f23d6 Mon Sep 17 00:00:00 2001 From: yaswanth-pula-skyflow Date: Tue, 30 May 2023 13:27:49 +0530 Subject: [PATCH 2/2] SK-352 add unit test cases for redaction in detokenize. --- test/vault-api/Skyflow.test.js | 308 +++++++++++++++++++++++++++++++++ 1 file changed, 308 insertions(+) diff --git a/test/vault-api/Skyflow.test.js b/test/vault-api/Skyflow.test.js index 9ac53557..ef9f0911 100644 --- a/test/vault-api/Skyflow.test.js +++ b/test/vault-api/Skyflow.test.js @@ -1479,4 +1479,312 @@ describe("Update method",()=>{ }); }); +}); + +describe('skyflow detokenize with redaction', () => { + + let skyflow; + beforeEach(() => { + skyflow = Skyflow.init({ + vaultID: '', + vaultURL: 'https://www.vaulturl.com', + getBearerToken: jest.fn(), + }); + }); + + test('request body should contains plain text redaction when passed plain text', (done) => { + let reqArg; + const clientReq = jest.fn((arg) => { + reqArg = arg; + return Promise.resolve(detokenizeRes) + }); + + const mockClient = { + config: skyflowConfig, + request: clientReq, + metadata: {} + } + + clientModule.mockImplementation(() => { return mockClient }); + skyflow = Skyflow.init({ + vaultID: '', + vaultURL: 'https://www.vaulturl.com', + getBearerToken: () => { + return new Promise((resolve, _) => { + resolve("token") + }) + } + }); + + const response = skyflow.detokenize({ + records: [ + { + token: '123', + redaction: RedactionType.PLAIN_TEXT + } + ] + }); + + response.then(() => { + console.log(reqArg.body); + const tokenRecords = reqArg.body.detokenizationParameters + tokenRecords.forEach((record) => { + expect(record.token).toBe('123'); + expect(record.redaction).toBe('PLAIN_TEXT'); + }) + done(); + }).catch((err) => { + done(err); + }) + + + }); + + test('request body should contains redacted redaction when passed redacted', (done) => { + let reqArg; + const clientReq = jest.fn((arg) => { + reqArg = arg; + return Promise.resolve(detokenizeRes) + }); + + const mockClient = { + config: skyflowConfig, + request: clientReq, + metadata: {} + } + + clientModule.mockImplementation(() => { return mockClient }); + skyflow = Skyflow.init({ + vaultID: '', + vaultURL: 'https://www.vaulturl.com', + getBearerToken: () => { + return new Promise((resolve, _) => { + resolve("token") + }) + } + }); + + const response = skyflow.detokenize({ + records: [ + { + token: '123', + redaction: RedactionType.REDACTED + } + ] + }); + + response.then(() => { + console.log(reqArg.body); + const tokenRecords = reqArg.body.detokenizationParameters + tokenRecords.forEach((record) => { + expect(record.token).toBe('123'); + expect(record.redaction).toBe('REDACTED'); + }) + done(); + }).catch((err) => { + done(err); + }) + + + }); + + test('request body should contains masked redaction when passed masked', (done) => { + let reqArg; + const clientReq = jest.fn((arg) => { + reqArg = arg; + return Promise.resolve(detokenizeRes) + }); + + const mockClient = { + config: skyflowConfig, + request: clientReq, + metadata: {} + } + + clientModule.mockImplementation(() => { return mockClient }); + skyflow = Skyflow.init({ + vaultID: '', + vaultURL: 'https://www.vaulturl.com', + getBearerToken: () => { + return new Promise((resolve, _) => { + resolve("token") + }) + } + }); + + const response = skyflow.detokenize({ + records: [ + { + token: '123', + redaction: RedactionType.MASKED + } + ] + }); + + response.then(() => { + console.log(reqArg.body); + const tokenRecords = reqArg.body.detokenizationParameters + tokenRecords.forEach((record) => { + expect(record.token).toBe('123'); + expect(record.redaction).toBe('MASKED'); + }) + done(); + }).catch((err) => { + done(err); + }) + + + }); + + test('request body should contains default redaction when passed default', (done) => { + let reqArg; + const clientReq = jest.fn((arg) => { + reqArg = arg; + return Promise.resolve(detokenizeRes) + }); + + const mockClient = { + config: skyflowConfig, + request: clientReq, + metadata: {} + } + + clientModule.mockImplementation(() => { return mockClient }); + skyflow = Skyflow.init({ + vaultID: '', + vaultURL: 'https://www.vaulturl.com', + getBearerToken: () => { + return new Promise((resolve, _) => { + resolve("token") + }) + } + }); + + const response = skyflow.detokenize({ + records: [ + { + token: '123', + redaction: RedactionType.DEFAULT + } + ] + }); + + response.then(() => { + console.log(reqArg.body); + const tokenRecords = reqArg.body.detokenizationParameters + tokenRecords.forEach((record) => { + expect(record.token).toBe('123'); + expect(record.redaction).toBe('DEFAULT'); + }) + done(); + }).catch((err) => { + done(err); + }) + + + }); + + test('request body should contains plain text redaction when not passed.', (done) => { + let reqArg; + const clientReq = jest.fn((arg) => { + reqArg = arg; + return Promise.resolve(detokenizeRes) + }); + + const mockClient = { + config: skyflowConfig, + request: clientReq, + metadata: {} + } + + clientModule.mockImplementation(() => { return mockClient }); + skyflow = Skyflow.init({ + vaultID: '', + vaultURL: 'https://www.vaulturl.com', + getBearerToken: () => { + return new Promise((resolve, _) => { + resolve("token") + }) + } + }); + + const response = skyflow.detokenize({ + records: [ + { + token: '123', + } + ] + }); + + response.then(() => { + console.log(reqArg.body); + const tokenRecords = reqArg.body.detokenizationParameters + tokenRecords.forEach((record) => { + expect(record.token).toBe('123'); + expect(record.redaction).toBe('PLAIN_TEXT'); + }) + done(); + }).catch((err) => { + done(err); + }) + + + }); + + test('detokenize should throw error when invalid null redaction value is passed.', (done) => { + const response = skyflow.detokenize({ + records: [ + { + token: '123', + redaction: null + } + ] + }); + + response.then((res) => { + done('should throw error'); + }).catch((err) => { + expect(err.errors[0].description).toBe(SKYFLOW_ERROR_CODE.DETOKENIZE_INVALID_REDACTION_TYPE.description); + done(); + }) + + }); + + test('detokenize should throw error when invalid undefined redaction value is passed.', (done) => { + const response = skyflow.detokenize({ + records: [ + { + token: '123', + redaction: undefined + } + ] + }); + + response.then((res) => { + done('should throw error'); + }).catch((err) => { + expect(err.errors[0].description).toBe(SKYFLOW_ERROR_CODE.DETOKENIZE_INVALID_REDACTION_TYPE.description); + done(); + }) + + }); + + test('detokenize should throw error when invalid type redaction value is passed.', (done) => { + const response = skyflow.detokenize({ + records: [ + { + token: '123', + redaction: '' + } + ] + }); + + response.then((res) => { + done('should throw error'); + }).catch((err) => { + expect(err.errors[0].description).toBe(SKYFLOW_ERROR_CODE.DETOKENIZE_INVALID_REDACTION_TYPE.description); + done(); + }) + + }); }); \ No newline at end of file