mirror of
https://github.com/HabitRPG/habitica.git
synced 2025-12-17 14:47:53 +01:00
enforce x-client header (#15476)
This commit is contained in:
@@ -1,8 +1,11 @@
|
|||||||
|
import nconf from 'nconf';
|
||||||
|
import requireAgain from 'require-again';
|
||||||
import {
|
import {
|
||||||
generateRes,
|
generateRes,
|
||||||
generateReq,
|
generateReq,
|
||||||
} from '../../../helpers/api-unit.helper';
|
} from '../../../helpers/api-unit.helper';
|
||||||
import { authWithHeaders as authWithHeadersFactory } from '../../../../website/server/middlewares/auth';
|
|
||||||
|
const authPath = '../../../../website/server/middlewares/auth';
|
||||||
|
|
||||||
describe('auth middleware', () => {
|
describe('auth middleware', () => {
|
||||||
let res; let req; let
|
let res; let req; let
|
||||||
@@ -16,6 +19,7 @@ describe('auth middleware', () => {
|
|||||||
|
|
||||||
describe('auth with headers', () => {
|
describe('auth with headers', () => {
|
||||||
it('allows to specify a list of user field that we do not want to load', done => {
|
it('allows to specify a list of user field that we do not want to load', done => {
|
||||||
|
const authWithHeadersFactory = requireAgain(authPath).authWithHeaders;
|
||||||
const authWithHeaders = authWithHeadersFactory({
|
const authWithHeaders = authWithHeadersFactory({
|
||||||
userFieldsToExclude: ['items'],
|
userFieldsToExclude: ['items'],
|
||||||
});
|
});
|
||||||
@@ -35,6 +39,7 @@ describe('auth middleware', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('makes sure some fields are always included', done => {
|
it('makes sure some fields are always included', done => {
|
||||||
|
const authWithHeadersFactory = requireAgain(authPath).authWithHeaders;
|
||||||
const authWithHeaders = authWithHeadersFactory({
|
const authWithHeaders = authWithHeadersFactory({
|
||||||
userFieldsToExclude: [
|
userFieldsToExclude: [
|
||||||
'items', 'auth.timestamps',
|
'items', 'auth.timestamps',
|
||||||
@@ -62,6 +67,7 @@ describe('auth middleware', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('errors with InvalidCredentialsError and code when token is wrong', done => {
|
it('errors with InvalidCredentialsError and code when token is wrong', done => {
|
||||||
|
const authWithHeadersFactory = requireAgain(authPath).authWithHeaders;
|
||||||
const authWithHeaders = authWithHeadersFactory({ userFieldsToExclude: [] });
|
const authWithHeaders = authWithHeadersFactory({ userFieldsToExclude: [] });
|
||||||
|
|
||||||
req.headers['x-api-user'] = user._id;
|
req.headers['x-api-user'] = user._id;
|
||||||
@@ -75,5 +81,41 @@ describe('auth middleware', () => {
|
|||||||
return done();
|
return done();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('when ENFORCE_CLIENT_HEADER is true', () => {
|
||||||
|
let authFactory;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
sandbox.stub(nconf, 'get').withArgs('ENFORCE_CLIENT_HEADER').returns('true');
|
||||||
|
authFactory = requireAgain(authPath).authWithHeaders;
|
||||||
|
});
|
||||||
|
|
||||||
|
it('errors with missingClientHeader when x-client header is not present', done => {
|
||||||
|
const authWithHeaders = authFactory({ userFieldsToExclude: [] });
|
||||||
|
|
||||||
|
req.headers['x-api-user'] = user._id;
|
||||||
|
req.headers['x-api-key'] = user;
|
||||||
|
authWithHeaders(req, res, err => {
|
||||||
|
expect(err).to.exist;
|
||||||
|
expect(err.name).to.equal('BadRequest');
|
||||||
|
expect(err.message).to.equal(res.t('missingClientHeader'));
|
||||||
|
return done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('allows request to pass when x-client header is present', done => {
|
||||||
|
const authWithHeaders = authFactory({ userFieldsToExclude: [] });
|
||||||
|
|
||||||
|
req.headers['x-api-user'] = user._id;
|
||||||
|
req.headers['x-api-key'] = user.apiToken;
|
||||||
|
req.headers['x-client'] = 'habitica-web';
|
||||||
|
|
||||||
|
authWithHeaders(req, res, err => {
|
||||||
|
if (err) return done(err);
|
||||||
|
expect(res.locals.user).to.exist;
|
||||||
|
return done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -109,6 +109,7 @@
|
|||||||
"tweet": "Tweet",
|
"tweet": "Tweet",
|
||||||
"checkOutMobileApps": "Check out our mobile apps!",
|
"checkOutMobileApps": "Check out our mobile apps!",
|
||||||
"missingAuthHeaders": "Missing authentication headers.",
|
"missingAuthHeaders": "Missing authentication headers.",
|
||||||
|
"missingClientHeader": "Missing x-client headers.",
|
||||||
"missingUsernameEmail": "Missing username or email.",
|
"missingUsernameEmail": "Missing username or email.",
|
||||||
"missingEmail": "Missing email.",
|
"missingEmail": "Missing email.",
|
||||||
"missingUsername": "Missing username.",
|
"missingUsername": "Missing username.",
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import url from 'url';
|
|||||||
import {
|
import {
|
||||||
InvalidCredentialsError,
|
InvalidCredentialsError,
|
||||||
NotAuthorized,
|
NotAuthorized,
|
||||||
|
BadRequest,
|
||||||
} from '../libs/errors';
|
} from '../libs/errors';
|
||||||
import {
|
import {
|
||||||
model as User,
|
model as User,
|
||||||
@@ -12,6 +13,8 @@ import gcpStackdriverTracer from '../libs/gcpTraceAgent';
|
|||||||
import common from '../../common';
|
import common from '../../common';
|
||||||
import { getLanguageFromUser } from '../libs/language';
|
import { getLanguageFromUser } from '../libs/language';
|
||||||
|
|
||||||
|
const ENFORCE_CLIENT_HEADER = nconf.get('ENFORCE_CLIENT_HEADER') === 'true';
|
||||||
|
|
||||||
const OFFICIAL_PLATFORMS = ['habitica-web', 'habitica-ios', 'habitica-android'];
|
const OFFICIAL_PLATFORMS = ['habitica-web', 'habitica-ios', 'habitica-android'];
|
||||||
const COMMUNITY_MANAGER_EMAIL = nconf.get('EMAILS_COMMUNITY_MANAGER_EMAIL');
|
const COMMUNITY_MANAGER_EMAIL = nconf.get('EMAILS_COMMUNITY_MANAGER_EMAIL');
|
||||||
const USER_FIELDS_ALWAYS_LOADED = ['_id', '_v', 'notifications', 'preferences', 'auth', 'flags', 'permissions'];
|
const USER_FIELDS_ALWAYS_LOADED = ['_id', '_v', 'notifications', 'preferences', 'auth', 'flags', 'permissions'];
|
||||||
@@ -63,6 +66,10 @@ export function authWithHeaders (options = {}) {
|
|||||||
const client = req.header('x-client');
|
const client = req.header('x-client');
|
||||||
const optional = options.optional || false;
|
const optional = options.optional || false;
|
||||||
|
|
||||||
|
if (ENFORCE_CLIENT_HEADER && !client) {
|
||||||
|
return next(new BadRequest(res.t('missingClientHeader')));
|
||||||
|
}
|
||||||
|
|
||||||
if (!userId || !apiToken) {
|
if (!userId || !apiToken) {
|
||||||
if (optional) return next();
|
if (optional) return next();
|
||||||
return next(new NotAuthorized(res.t('missingAuthHeaders')));
|
return next(new NotAuthorized(res.t('missingAuthHeaders')));
|
||||||
|
|||||||
Reference in New Issue
Block a user