Skip to content

Commit

Permalink
Fixes client auth to include a valid environment (#492)
Browse files Browse the repository at this point in the history
  • Loading branch information
petruki authored May 7, 2024
1 parent 5a5905e commit f167eba
Show file tree
Hide file tree
Showing 5 changed files with 39 additions and 32 deletions.
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

0 comments on commit f167eba

Please sign in to comment.