diff --git a/src/models/component.js b/src/models/component.js index 946086f..c5ed22d 100644 --- a/src/models/component.js +++ b/src/models/component.js @@ -1,7 +1,7 @@ import mongoose from 'mongoose'; import moment from 'moment'; import bcryptjs from 'bcryptjs'; -import { randomBytes } from 'crypto'; +import { randomUUID } from 'crypto'; import jwt from 'jsonwebtoken'; import { Config } from './config'; import Domain from './domain'; @@ -55,13 +55,12 @@ componentSchema.options.toJSON = { componentSchema.methods.generateApiKey = async function () { const component = this; - const buffer = randomBytes(32); - const apiKey = Buffer.from(buffer).toString('base64'); + const apiKey = randomUUID(); const hash = await bcryptjs.hash(apiKey, 8); component.apihash = hash; await component.save(); - return Buffer.from(apiKey).toString('base64'); + return apiKey; }; componentSchema.methods.generateAuthToken = async function (environment) { @@ -86,8 +85,16 @@ componentSchema.statics.findByCredentials = async (domainName, componentName, ap throw new Error('Unable to find this Component'); } - let decoded = Buffer.from(apiKey, 'base64').toString('ascii'); - const isMatch = await bcryptjs.compare(decoded, component.apihash); + let isMatch = false; + + // Validate API Key type + if (apiKey.match(/^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i)) { + isMatch = await bcryptjs.compare(apiKey, component.apihash); + } else { + // Must be deprecated by date + let decoded = Buffer.from(apiKey, 'base64').toString('ascii'); + isMatch = await bcryptjs.compare(decoded, component.apihash); + } if (!isMatch) { throw new Error('Unable to find this Component'); diff --git a/tests/client-api.test.js b/tests/client-api.test.js index eaaf0b1..c697733 100644 --- a/tests/client-api.test.js +++ b/tests/client-api.test.js @@ -1,6 +1,5 @@ import mongoose from 'mongoose'; import request from 'supertest'; -import bcryptjs from 'bcryptjs'; import app from '../src/app'; import Domain from '../src/models/domain'; import GroupConfig from '../src/models/group-config'; @@ -548,21 +547,16 @@ describe('Testing criteria [REST] ', () => { }); test('CLIENT_SUITE - Should NOT return success on a entry-based CRITERIA response - Component not registered', async () => { - //given - const component = { + // Given + const component = new Component({ _id: new mongoose.Types.ObjectId(), name: 'Temp Component', description: 'Temporary component', domain: domainId, owner: adminMasterAccountId - }; - - const hashApiKey = await bcryptjs.hash(component._id + component.name, 8); - const hash = await bcryptjs.hash(hashApiKey, 8); - component.apihash = hash; - await new Component(component).save(); - const generatedApiKey = Buffer.from(hashApiKey).toString('base64'); - + }); + + const generatedApiKey = await component.generateApiKey(); const response = await request(app) .post('/criteria/auth') .set('switcher-api-key', `${generatedApiKey}`) @@ -574,7 +568,7 @@ describe('Testing criteria [REST] ', () => { const tempToken = response.body.token; - //test + // Test const req = await request(app) .post(`/criteria?key=${keyConfig}&showReason=true&showStrategy=true`) .set('Authorization', `Bearer ${tempToken}`) diff --git a/tests/component.test.js b/tests/component.test.js index c9805a8..ba912eb 100644 --- a/tests/component.test.js +++ b/tests/component.test.js @@ -44,7 +44,7 @@ describe('Insertion tests', () => { }).expect(201); // DB validation - document created - const component = await Component.findById(response.body.component._id).lean(); + const component = await Component.findById(response.body.component._id).exec(); expect(component).not.toBeNull(); // Response validation @@ -95,8 +95,8 @@ describe('Insertion tests', () => { expect(response.body.apiKey).not.toBeNull(); // DB validation - current Domain token should not be as the same as the generated - const apiKey = Buffer.from(response.body.apiKey, 'base64').toString('ascii'); - const component = await Component.findById(component1Id).lean(); + const apiKey = response.body.apiKey; + const component = await Component.findById(component1Id).exec(); const isMatch = await bcryptjs.compare(apiKey, component.apihash); expect(isMatch).toEqual(true); }); @@ -194,7 +194,7 @@ describe('Updating tests', () => { }).expect(201); // DB validation - document created - let component = await Component.findById(response.body.component._id).lean(); + let component = await Component.findById(response.body.component._id).exec(); expect(component.description).toBe('This is my Web App using this wonderful API'); response = await request(app) @@ -205,7 +205,7 @@ describe('Updating tests', () => { }).expect(200); // DB validation - document updated - component = await Component.findById(response.body._id).lean(); + component = await Component.findById(response.body._id).exec(); expect(component.description).toBe('Wow, this is my updated description'); }); @@ -257,7 +257,7 @@ describe('Deletion tests', () => { component: response.body.component._id }).expect(200); - const configsToRemoveFrom = await Config.find({ components: { $in: [response.body.component._id] } }).lean(); + const configsToRemoveFrom = await Config.find({ components: { $in: [response.body.component._id] } }).exec(); expect(configsToRemoveFrom[0]._id).toEqual(configId1); response = await request(app) @@ -266,7 +266,7 @@ describe('Deletion tests', () => { .send().expect(200); // DB validation - document deleted - const component = await Component.findById(response.body._id).lean(); + const component = await Component.findById(response.body._id).exec(); expect(component).toBeNull(); }); diff --git a/tests/fixtures/db_api.js b/tests/fixtures/db_api.js index a59f2b2..02759cd 100644 --- a/tests/fixtures/db_api.js +++ b/tests/fixtures/db_api.js @@ -1,6 +1,6 @@ import mongoose from 'mongoose'; import bcryptjs from 'bcryptjs'; -import { randomBytes } from 'crypto'; +import { randomUUID } from 'crypto'; import jwt from 'jsonwebtoken'; import Admin from '../../src/models/admin'; import Domain from '../../src/models/domain'; @@ -272,8 +272,7 @@ export const setupDatabase = async () => { await new Permission(permissionAll3).save(); await new Permission(permissionAll4).save(); - const buffer = randomBytes(32); - const apiKey = Buffer.from(buffer).toString('base64'); + const apiKey = randomUUID(); const hash = await bcryptjs.hash(apiKey, 8); component1.apihash = hash; await new Component(component1).save(); diff --git a/tests/fixtures/db_client.js b/tests/fixtures/db_client.js index 2014735..4c8ee0e 100644 --- a/tests/fixtures/db_client.js +++ b/tests/fixtures/db_client.js @@ -1,7 +1,7 @@ import mongoose from 'mongoose'; import jwt from 'jsonwebtoken'; import bcryptjs from 'bcryptjs'; -import { randomBytes } from 'crypto'; +import { randomUUID } from 'crypto'; import Admin from '../../src/models/admin'; import Domain from '../../src/models/domain'; import GroupConfig from '../../src/models/group-config'; @@ -227,10 +227,9 @@ export const setupDatabase = async () => { await new ConfigStrategy(configStrategyTIME_BETWEENDocument).save(); await new ConfigStrategy(configStrategyTIME_GREATDocument).save(); - const buffer = randomBytes(32); - const newApiKey = Buffer.from(buffer).toString('base64'); + const newApiKey = randomUUID(); const hash = await bcryptjs.hash(newApiKey, 8); component1.apihash = hash; await new Component(component1).save(); - apiKey = Buffer.from(newApiKey).toString('base64'); + apiKey = newApiKey; }; \ No newline at end of file diff --git a/tests/fixtures/db_client_payload.js b/tests/fixtures/db_client_payload.js index e3a1898..2e556a4 100644 --- a/tests/fixtures/db_client_payload.js +++ b/tests/fixtures/db_client_payload.js @@ -1,7 +1,7 @@ import mongoose from 'mongoose'; import jwt from 'jsonwebtoken'; import bcryptjs from 'bcryptjs'; -import { randomBytes } from 'crypto'; +import { randomUUID } from 'crypto'; import Admin from '../../src/models/admin'; import Domain from '../../src/models/domain'; import GroupConfig from '../../src/models/group-config'; @@ -116,10 +116,9 @@ export const setupDatabase = async () => { await new Config(configPayloadDocument).save(); await new ConfigStrategy(configStrategyPAYLOAD_HAS_ONEDocument).save(); - const buffer = randomBytes(32); - const newApiKey = Buffer.from(buffer).toString('base64'); + const newApiKey = randomUUID(); const hash = await bcryptjs.hash(newApiKey, 8); component1.apihash = hash; await new Component(component1).save(); - apiKey = Buffer.from(newApiKey).toString('base64'); + apiKey = newApiKey; }; \ No newline at end of file diff --git a/tests/fixtures/db_metrics.js b/tests/fixtures/db_metrics.js index 0967fc6..83c42eb 100644 --- a/tests/fixtures/db_metrics.js +++ b/tests/fixtures/db_metrics.js @@ -1,7 +1,7 @@ import mongoose from 'mongoose'; import jwt from 'jsonwebtoken'; import bcryptjs from 'bcryptjs'; -import { randomBytes } from 'crypto'; +import { randomUUID } from 'crypto'; import { Metric } from '../../src/models/metric'; import Admin from '../../src/models/admin'; import { EnvType } from '../../src/models/environment'; @@ -170,8 +170,7 @@ export const setupDatabase = async () => { await new Metric(entry3).save(); await new Metric(entry4).save(); - const buffer = randomBytes(32); - const newApiKey = Buffer.from(buffer).toString('base64'); + const newApiKey = randomUUID(); const hash = await bcryptjs.hash(newApiKey, 8); component1.apihash = hash; await new Component(component1).save(); diff --git a/tests/model/component.test.js b/tests/model/component.test.js new file mode 100644 index 0000000..af333e9 --- /dev/null +++ b/tests/model/component.test.js @@ -0,0 +1,74 @@ +require('../../src/db/mongoose'); + +import { randomBytes } from 'crypto'; +import bcryptjs from 'bcryptjs'; +import mongoose from 'mongoose'; +import { + setupDatabase, + adminMasterAccountId, + domainId, + domainDocument + } from '../fixtures/db_api'; +import Component from '../../src/models/component'; + +afterAll(async () => { + await new Promise(resolve => setTimeout(resolve, 1000)); + await mongoose.disconnect(); +}); + +describe('(Deprecated) Testing component authentication', () => { + beforeAll(async () => await setupDatabase()); + + /** + * Generates API key using old method + */ + const generateApiKeyDeprecated = async (component) => { + const buffer = randomBytes(32); + const apiKey = Buffer.from(buffer).toString('base64'); + const hash = await bcryptjs.hash(apiKey, 8); + component.apihash = hash; + await component.save(); + + const generatedApiKey = Buffer.from(apiKey).toString('base64'); + return generatedApiKey; + } + + test('COMPONENT_MODEL - Should authenticate component using old API key format', async () => { + // Given + const componentId = new mongoose.Types.ObjectId(); + const component = new Component({ + _id: componentId, + name: 'TestDeprecatedAPIKey', + description: 'Test app with depracated API key', + domain: domainId, + owner: adminMasterAccountId + }); + + // That + const generatedApiKey = await generateApiKeyDeprecated(component); + + // Test + const result = await Component.findByCredentials(domainDocument.name, component.name, generatedApiKey); + expect(result.component).not.toBe(undefined); + }); + + test('COMPONENT_MODEL - Should authenticate component using new API key format', async () => { + // Given + const componentId = new mongoose.Types.ObjectId(); + const component = new Component({ + _id: componentId, + name: 'TestNewAPIKey', + description: 'Test app with New API key', + domain: domainId, + owner: adminMasterAccountId + }); + + // That + const generatedApiKey = await component.generateApiKey(); + + // Test + const result = await Component.findByCredentials(domainDocument.name, component.name, generatedApiKey); + expect(result.component).not.toBe(undefined); + }); + +});