Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 5 additions & 5 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
"name": "switcher-api",
"version": "1.2.8",
"description": "Feature Flag/Toggle API",
"main": "start.js",
"main": "src/start.js",
"type": "module",
"author": {
"name": "Roger Floriano",
Expand Down Expand Up @@ -49,9 +49,9 @@
"helmet": "^7.1.0",
"jsonwebtoken": "^9.0.2",
"moment": "^2.30.1",
"mongodb": "^6.5.0",
"mongoose": "^8.3.2",
"pino": "^8.20.0",
"mongodb": "^6.6.1",
"mongoose": "^8.3.4",
"pino": "^9.0.0",
"pino-pretty": "^11.0.0",
"swagger-ui-express": "^5.0.0",
"switcher-client": "^4.0.3",
Expand All @@ -65,7 +65,7 @@
"node-notifier": "^10.0.1",
"nodemon": "^3.1.0",
"sinon": "^17.0.1",
"supertest": "^6.3.4"
"supertest": "^7.0.0"
},
"overrides": {
"formidable": "^3.5.1"
Expand Down
6 changes: 6 additions & 0 deletions src/middleware/auth.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import basicAuth from 'express-basic-auth';
import jwt from 'jsonwebtoken';
import { getAdmin, getAdminById } from '../services/admin.js';
import { getComponentById } from '../services/component.js';
import { getEnvironmentByName } from '../services/environment.js';
import Admin from '../models/admin.js';
import Component from '../models/component.js';
import { getRateLimit } from '../external/switcher-api-facade.js';
Expand Down Expand Up @@ -114,6 +115,11 @@ export async function appGenerateCredentials(req, res, next) {
try {
const key = req.header('switcher-api-key');
const { component, domain } = await Component.findByCredentials(req.body.domain, req.body.component, key);
const environment = await getEnvironmentByName(component.domain, req.body.environment);

if (!environment) {
throw new Error('Invalid environment');
}

const rate_limit = await getRateLimit(key, component);
const token = await component.generateAuthToken(req.body.environment, rate_limit);
Expand Down
17 changes: 10 additions & 7 deletions src/routers/client-api.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import express from 'express';
import { body, check, query } from 'express-validator';
import { body, check, query, header } from 'express-validator';
import jwt from 'jsonwebtoken';
import { checkConfig, checkConfigComponent, validate } from '../middleware/validators.js';
import { componentAuth, appGenerateCredentials } from '../middleware/auth.js';
Expand Down Expand Up @@ -47,15 +47,13 @@ router.post('/criteria', componentAuth, clientLimiter, [
}
});

router.get('/criteria/snapshot_check/:version', componentAuth, clientLimiter, async (req, res) => {
router.get('/criteria/snapshot_check/:version', componentAuth, clientLimiter, [
check('version', 'Wrong value for domain version').isNumeric()
], validate, async (req, res) => {
try {
const domain = await checkDomain(req.domain);
const version = req.params.version;

if (isNaN(version)) {
return res.status(400).send({ error: 'Wrong value for domain version' });
}

if (domain.lastUpdate > version) {
res.send({ status: false });
} else {
Expand All @@ -78,7 +76,12 @@ router.post('/criteria/switchers_check', componentAuth, clientLimiter, [
}
});

router.post('/criteria/auth', appGenerateCredentials, clientLimiter, async (req, res) => {
router.post('/criteria/auth', [
header('switcher-api-key').isString().withMessage('API Key header is required'),
body('domain').isString().withMessage('Domain is required'),
body('component').isString().withMessage('Component is required'),
body('environment').isString().withMessage('Environment is required')
], validate, appGenerateCredentials, clientLimiter, async (req, res) => {
try {
const { exp } = jwt.decode(req.token);
res.send({ token: req.token, exp });
Expand Down
12 changes: 8 additions & 4 deletions src/services/environment.js
Original file line number Diff line number Diff line change
Expand Up @@ -40,13 +40,17 @@ export async function getEnvironmentById(id) {
}

export async function getEnvironment(where) {
const environment = await getEnvironmentByName(where.domain, where.name);
return response(environment, 'Environment not found');
}

export async function getEnvironmentByName(domain, name) {
const query = Environment.findOne();

if (where.domain) query.where('domain', where.domain);
if (where.name) query.where('name', where.name);
if (domain) query.where('domain', domain);
if (name) query.where('name', name);

let environment = await query.exec();
return response(environment, 'Environment not found');
return query.exec();
}

export async function getEnvironments(where, projection, options) {
Expand Down
26 changes: 10 additions & 16 deletions tests/client-api.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -142,31 +142,25 @@ describe('Testing criteria [GraphQL] ', () => {
expect(JSON.parse(req.text)).toMatchObject(JSON.parse(graphqlUtils.expected101));
});

test('CLIENT_SUITE - Should return success on Flat view resolved by Config Key - Unknown environment, should look to production', async () => {
const response = await request(app)
test('CLIENT_SUITE - Should NOT authenticate invalid component', async () => {
await request(app)
.post('/criteria/auth')
.set('switcher-api-key', `${apiKey}`)
.send({
domain: domainDocument.name,
component: component1.name,
environment: 'UNKNOWN ENVIRONMENT'
}).expect(200);

await request(app)
.post('/graphql')
.set('Authorization', `Bearer ${response.body.token}`)
.send(graphqlUtils.configurationQuery([['key', keyConfig]]))
.expect(200);
component: 'UNKNOWN COMPONENT',
environment: EnvType.DEFAULT
}).expect(401);
});

test('CLIENT_SUITE - Should NOT authenticate invalid component', async () => {
test('CLIENT_SUITE - Should NOT authenticate invalid environment', async () => {
await request(app)
.post('/criteria/auth')
.set('switcher-api-key', `${apiKey}`)
.send({
domain: domainDocument.name,
component: 'UNKNOWN COMPONENT',
environment: EnvType.DEFAULT
component: component1.name,
environment: 'UNKNOWN ENVIRONMENT'
}).expect(401);
});

Expand Down Expand Up @@ -772,8 +766,8 @@ describe('Testing criteria [REST] ', () => {
.set('Authorization', `Bearer ${token}`)
.send();

expect(req.statusCode).toBe(400);
expect(req.body.error).toEqual('Wrong value for domain version');
expect(req.statusCode).toBe(422);
expect(req.body.errors[0].msg).toEqual('Wrong value for domain version');
});

test('CLIENT_SUITE - Should return error when validating snapshot version - Invalid token', async () => {
Expand Down