diff --git a/src/exceptions/index.js b/src/exceptions/index.js index 6c0d95e..7b75265 100644 --- a/src/exceptions/index.js +++ b/src/exceptions/index.js @@ -37,15 +37,21 @@ export class FeatureUnavailableError extends Error { } export function responseException(res, err, code, feature = undefined) { + if (process.env.SWITCHER_API_LOGGER == 'true' && feature) { + Logger.info(`Feature [${feature}]`, { log: Switcher.getLogger(feature) }); + } + + responseExceptionSilent(res, err, code, err.message); +} + +export function responseExceptionSilent(res, err, code, message) { if (process.env.SWITCHER_API_LOGGER == 'true') { Logger.httpError(err.constructor.name, err.code, err.message, err); - if (feature) { - Logger.info(`Feature [${feature}]`, { log: Switcher.getLogger(feature) }); - } } if (err.code) { - return res.status(err.code).send({ error: err.message }); + return res.status(err.code).send({ error: message }); } - res.status(code).send({ error: err.message }); + + res.status(code).send({ error: message }); } \ No newline at end of file diff --git a/src/middleware/auth.js b/src/middleware/auth.js index 2b78607..5c419d1 100644 --- a/src/middleware/auth.js +++ b/src/middleware/auth.js @@ -5,26 +5,27 @@ import { getComponentById } from '../services/component'; import Admin from '../models/admin'; import Component from '../models/component'; import { getRateLimit } from '../external/switcher-api-facade'; +import { responseExceptionSilent } from '../exceptions'; export async function auth(req, res, next) { try { - const token = req.header('Authorization').replace('Bearer ', ''); + const token = req.header('Authorization')?.replace('Bearer ', ''); const decoded = jwt.verify(token, process.env.JWT_SECRET); const admin = await getAdminById(decoded._id); - if (!admin || !admin.active) { - throw new Error(); + if (!admin?.active) { + throw new Error('User not active'); } if (admin.token !== Admin.extractTokenPart(token)) { - throw new Error(); + throw new Error(`Invalid token for ${admin.email}`); } req.token = token; req.admin = admin; next(); - } catch (e) { - res.status(401).send({ error: 'Please authenticate.' }); + } catch (err) { + responseExceptionSilent(res, err, 401, 'Please authenticate.'); } } @@ -35,21 +36,21 @@ export async function authRefreshToken(req, res, next) { const decodedRefreshToken = jwt.verify(refreshToken, process.env.JWT_SECRET); if (decodedRefreshToken.subject !== Admin.extractTokenPart(token)) { - throw new Error(); + throw new Error('Refresh code does not match'); } const decoded = await jwt.decode(token); const admin = await getAdmin({ _id: decoded._id, token: decodedRefreshToken.subject }); - if (!admin || !admin.active) { - throw new Error(); + if (!admin?.active) { + throw new Error('User not active'); } const newTokenPair = await admin.generateAuthToken(); res.jwt = newTokenPair; next(); - } catch (e) { - res.status(401).send({ error: 'Unable to refresh token.' }); + } catch (err) { + responseExceptionSilent(res, err, 401, 'Unable to refresh token.'); } } @@ -60,7 +61,7 @@ export async function appAuth(req, res, next) { const component = await getComponentById(decoded.component); if (component?.apihash.substring(50, component.apihash.length - 1) !== decoded.vc) { - throw new Error(); + throw new Error('Invalid API token'); } req.token = token; @@ -70,8 +71,8 @@ export async function appAuth(req, res, next) { req.environment = decoded.environment; req.rate_limit = decoded.rate_limit; next(); - } catch (e) { - res.status(401).send({ error: 'Invalid API token.' }); + } catch (err) { + responseExceptionSilent(res, err, 401, 'Invalid API token.'); } } @@ -80,8 +81,8 @@ export async function slackAuth(req, res, next) { const token = req.header('Authorization').replace('Bearer ', ''); jwt.verify(token, process.env.SWITCHER_SLACK_JWT_SECRET); next(); - } catch (e) { - res.status(401).send({ error: 'Invalid API token.' }); + } catch (err) { + responseExceptionSilent(res, err, 401, 'Invalid API token.'); } } @@ -99,10 +100,6 @@ export async function appGenerateCredentials(req, res, next) { const key = req.header('switcher-api-key'); const { component, domain } = await Component.findByCredentials(req.body.domain, req.body.component, key); - if (!component) { - throw new Error(); - } - const rate_limit = await getRateLimit(key, component); const token = await component.generateAuthToken(req.body.environment, rate_limit); @@ -111,7 +108,7 @@ export async function appGenerateCredentials(req, res, next) { req.environment = req.body.environment; req.rate_limit = rate_limit; next(); - } catch (e) { - res.status(401).send({ error: 'Invalid token request' }); + } catch (err) { + responseExceptionSilent(res, err, 401, 'Invalid token request.'); } } \ No newline at end of file diff --git a/src/services/metric.js b/src/services/metric.js index e53a3d1..9391710 100644 --- a/src/services/metric.js +++ b/src/services/metric.js @@ -46,11 +46,11 @@ export async function getStatistics(req) { let result = {}; const options = String(req.query.statistics); await Promise.all([ - options.match(/(switchers)|(all)/) ? + RegExp(/(switchers)|(all)/).exec(options) ? aggregateSwitchers(switcher.aggregate, dateGroupPattern, result) : Promise.resolve(), - options.match(/(components)|(all)/) ? + RegExp(/(components)|(all)/).exec(options) ? aggregateComponents(components.aggregate, dateGroupPattern, result) : Promise.resolve(), - options.match(/(reasons)|(all)/) ? + RegExp(/(reasons)|(all)/).exec(options) ? aggreagateReasons(reasons.aggregate, result) : Promise.resolve() ]); diff --git a/tests/admin.test.js b/tests/admin.test.js index db85ce7..082a9ab 100644 --- a/tests/admin.test.js +++ b/tests/admin.test.js @@ -140,6 +140,32 @@ describe('Testing Admin insertion', () => { }).expect(200); }); + test('ADMIN_SUITE - Should NOT access routes - Admin not active', async () => { + // given + let admin = await Admin.findById(signedupUser).exec(); + let response = await request(app) + .post('/admin/login') + .send({ + email: admin.email, + password: '12312312312' + }).expect(200); + + // admin not active + admin.active = false; + await admin.save(); + const token = response.body.jwt.token; + + // test + await request(app) + .get('/admin/me') + .set('Authorization', `Bearer ${token}`) + .send().expect(401); + + // teardown - restore admin + admin.active = true; + await admin.save(); + }); + test('ADMIN_SUITE - Should NOT confirm access to a new Admin - Invalid access code', async () => { await request(app) .post('/admin/signup/authorization?code=INVALID_CODE') diff --git a/tests/metric.test.js b/tests/metric.test.js index 70395cc..4ac86dc 100644 --- a/tests/metric.test.js +++ b/tests/metric.test.js @@ -54,6 +54,19 @@ describe('Fetch overall statistics', () => { expect(response.body?.reasons).toEqual(undefined); }); + test('METRIC_SUITE - Should return only reason statistics from a given Domain', async () => { + const response = await request(app) + .get(`/metric/statistics?domainid=${domainId}&statistics=reasons`) + .set('Authorization', `Bearer ${adminMasterAccountToken}`) + .send().expect(200); + + // Response validation + expect(response.body).not.toBeNull(); + expect(response.body?.reasons.length > 0).toEqual(true); + expect(response.body?.switchers).toEqual(undefined); + expect(response.body?.components).toEqual(undefined); + }); + test('METRIC_SUITE - Should return statistics filtered by Switcher KEY', async () => { const response = await request(app) .get(`/metric/statistics?domainid=${domainId}&key=KEY_1&statistics=all`)