Skip to content

Commit

Permalink
Merge branch 'master' into 9732-navigation-issue-privacy-page
Browse files Browse the repository at this point in the history
  • Loading branch information
paulpascal authored Jan 27, 2025
2 parents deb3bcf + 2c5a640 commit acca1fe
Show file tree
Hide file tree
Showing 39 changed files with 1,092 additions and 2,251 deletions.
25 changes: 13 additions & 12 deletions api/src/auth.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,13 @@ const dataContext = require('./services/data-context');
const { roles, users } = require('@medic/user-management')(config, db, dataContext);

const contentLengthRegex = /^content-length$/i;
const contentTypeRegex = /^content-type$/i;

const get = (path, headers) => {
const getHeaders = { ...headers };
Object
.keys(getHeaders)
.filter(header => contentLengthRegex.test(header))
.filter(header => contentLengthRegex.test(header) || contentTypeRegex.test(header))
.forEach(header => delete getHeaders[header]);

const url = new URL(path, environment.serverUrlNoAuth);
Expand Down Expand Up @@ -51,7 +52,7 @@ module.exports = {
getUserCtx: req => {
return get('/_session', req.headers)
.catch(err => {
if (err.statusCode === 401) {
if (err.status === 401) {
throw { code: 401, message: 'Not logged in', err: err };
}
throw err;
Expand Down Expand Up @@ -83,7 +84,7 @@ module.exports = {
* @return {Object} {username: username, password: password}
*/
basicAuthCredentials: req => {
const authHeader = req && req.headers && req.headers.authorization;
const authHeader = req?.headers?.authorization;
if (!authHeader || !authHeader.startsWith('Basic ')) {
return false;
}
Expand All @@ -101,16 +102,16 @@ module.exports = {
* @param {Object} Credentials object as created by basicAuthCredentials
*/
validateBasicAuth: ({ username, password }) => {
const authUrl = new URL(environment.serverUrlNoAuth);
authUrl.username = username;
authUrl.password = password;
return request.head({
uri: authUrl.toString(),
resolveWithFullResponse: true
})
return request
.get({
uri: environment.serverUrlNoAuth,
auth: { username, password },
simple: false,
json: false,
})
.then(res => {
if (res.statusCode !== 200) {
return Promise.reject(new Error(`Expected 200 got ${res.statusCode}`));
if (!res.ok) {
return Promise.reject(new Error(`Expected 200 got ${res.status}`));
}
return username;
});
Expand Down
16 changes: 6 additions & 10 deletions api/src/controllers/login.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
const path = require('path');
const request = require('@medic/couch-request');
const _ = require('lodash');
const url = require('node:url');
const auth = require('../auth');
const environment = require('@medic/environment');
Expand Down Expand Up @@ -160,22 +159,19 @@ const render = (page, req, extras = {}) => {
};

const getSessionCookie = res => {
return _.find(
res.headers['set-cookie'],
cookie => cookie.indexOf('AuthSession') === 0
);
return res.headers.getSetCookie().find(cookie => cookie.indexOf('AuthSession') === 0);
};

const createSession = req => {
const user = req.body.user;
const password = req.body.password;

return request.post({
url: new URL('/_session', environment.serverUrlNoAuth).toString(),
json: true,
resolveWithFullResponse: true,
simple: false, // doesn't throw an error on non-200 responses
body: { name: user, password: password },
auth: { user: user, pass: password },
auth: { username: user, password: password },
});
};

Expand Down Expand Up @@ -242,7 +238,7 @@ const getUserCtxRetry = async (options, retry = 10) => {

const createSessionRetry = (req, retry=10) => {
return createSession(req).then(sessionRes => {
if (sessionRes.statusCode === 200) {
if (sessionRes.status === 200) {
return sessionRes;
}

Expand Down Expand Up @@ -303,8 +299,8 @@ const renderLogin = (req) => {
const login = async (req, res) => {
try {
const sessionRes = await createSession(req);
if (sessionRes.statusCode !== 200) {
res.status(sessionRes.statusCode).json({ error: 'Not logged in' });
if (sessionRes.status !== 200) {
res.status(sessionRes.status).json({ error: 'Not logged in' });
} else {
const redirectUrl = await setCookies(req, res, sessionRes);
res.status(302).send(redirectUrl);
Expand Down
11 changes: 6 additions & 5 deletions api/src/db-batch.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,12 @@ const runBatch = (ddoc, view, viewParams, iteratee) => {
});
// using request here instead of PouchDB because
// PouchDB doesn't support startkey_docid: #5319
return request.get({
url: fullUrl,
json: true,
auth: { user: environment.username, pass: environment.password },
})
return request
.get({
url: fullUrl,
json: true,
auth: { username: environment.username, password: environment.password },
})
.then(result => {
logger.info(` Processing doc ${result.offset}`);
let nextPage;
Expand Down
2 changes: 1 addition & 1 deletion api/src/migrations/fix-user-db-security.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ module.exports = {
return p.then(() => {
return userDb.setSecurity(userDb.getDbName(username), username)
.catch(err => {
if (err.statusCode !== 404) {
if (err.status !== 404) {
throw err;
}
// db not found is ok
Expand Down
4 changes: 2 additions & 2 deletions api/src/migrations/restrict-access-to-audit-db.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,8 @@ const addMemberToDb = () => {
pathname: `${environment.db}-audit/_security`,
}),
auth: {
user: environment.username,
pass: environment.password
username: environment.username,
password: environment.password
},
json: true,
body: securityObject
Expand Down
4 changes: 2 additions & 2 deletions api/src/migrations/restrict-access-to-sentinel-db.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,8 @@ const addSecurityToDb = () => {
pathname: `${environment.db}-sentinel/_security`,
}),
auth: {
user: environment.username,
pass: environment.password
username: environment.username,
password: environment.password
},
json: true,
body: securityObject
Expand Down
4 changes: 2 additions & 2 deletions api/src/migrations/restrict-access-to-vault-db.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,8 @@ const addSecurityToDb = () => {
pathname: `${environment.db}-vault/_security`,
}),
auth: {
user: environment.username,
pass: environment.password
username: environment.username,
password: environment.password
},
json: true,
body: securityObject
Expand Down
29 changes: 15 additions & 14 deletions api/src/services/africas-talking.js
Original file line number Diff line number Diff line change
Expand Up @@ -85,20 +85,21 @@ const parseResponseBody = body => {
const sendMessage = (credentials, message) => {
const url = getUrl(credentials.username === 'sandbox');
logger.debug(`Sending message to "${url}"`);
return request.post({
url: url,
simple: false,
form: {
username: credentials.username,
from: credentials.from,
to: message.to,
message: message.content
},
headers: {
apikey: credentials.apiKey,
Accept: 'application/json'
}
})
return request
.post({
url: url,
json: false,
form: {
username: credentials.username,
from: credentials.from,
to: message.to,
message: message.content
},
headers: {
apikey: credentials.apiKey,
Accept: 'application/json'
}
})
.then(body => {
const result = parseResponseBody(body);
if (!result) {
Expand Down
6 changes: 2 additions & 4 deletions api/src/services/rapidpro.js
Original file line number Diff line number Diff line change
Expand Up @@ -50,10 +50,8 @@ const getCredentials = () => {

const getRequestOptions = ({ apiToken, host }={}) => ({
baseUrl: host,
json: true,
headers: {
Authorization: `Token ${apiToken}`,
Accept: 'application/json',
},
});

Expand Down Expand Up @@ -108,7 +106,7 @@ const sendMessage = (credentials, message) => {
})
.catch(err => {
logger.error(`Error thrown when trying to send message: %o`, err);
if (err?.statusCode === 400) {
if (err?.status === 400) {
// source https://rapidpro.io/api/v2/
// 400: The request failed due to invalid parameters.
// Do not retry with the same values, and the body of the response will contain details.
Expand Down Expand Up @@ -168,7 +166,7 @@ const getRemoteStates = (credentials, messages) => {
stateUpdates.push(stateUpdate);
})
.catch(err => {
if (err && err.statusCode === 429) {
if (err && err.status === 429) {
// rate limited, throw error to halt recursive polling
throttled = true;
}
Expand Down
4 changes: 2 additions & 2 deletions api/src/services/user-db.js
Original file line number Diff line number Diff line change
Expand Up @@ -80,8 +80,8 @@ module.exports = {
pathname: `${dbName}/_security`,
}),
auth: {
user: environment.username,
pass: environment.password
username: environment.username,
password: environment.password
},
json: true,
body: {
Expand Down
17 changes: 11 additions & 6 deletions api/tests/mocha/auth.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ describe('Auth', () => {
describe('check', () => {

it('returns error when not logged in', () => {
const get = sinon.stub(request, 'get').rejects({ statusCode: 401 });
const get = sinon.stub(request, 'get').rejects({ status: 401 });
return auth.check({ }).catch(err => {
chai.expect(get.callCount).to.equal(1);
chai.expect(get.args[0][0].url).to.equal('http://abc.com/_session');
Expand Down Expand Up @@ -139,12 +139,11 @@ describe('Auth', () => {
host: 'localhost:5988',
'user-agent': 'curl/8.6.0',
accept: '*/*',
'content-type': 'application/json',
},
}]]);
});

it('should clean content-length headers before forwarding', async () => {
it('should clean content-length and content-type headers before forwarding', async () => {
sinon.stub(request, 'get').resolves({ userCtx: { name: 'theuser', roles: ['userrole'] }});

req.headers['content-length'] = 100;
Expand All @@ -153,6 +152,13 @@ describe('Auth', () => {
req.headers['content-Length'] = 82;
req.headers['CONTENT-LENGTH'] = 240;

req.headers['content-type'] = 'application/json';
req.headers['Content-Type'] = 'image/jpeg';
req.headers['Content-type'] = 'x-www-form-urlencoded';
req.headers['content-Type'] = 'multipart/form-data';
req.headers['CONTENT-TYPE'] = 'text/html';


const result = await auth.getUserCtx(req);
chai.expect(result).to.deep.equal({ name: 'theuser', roles: ['userrole'] });
chai.expect(request.get.args).to.deep.equal([[{
Expand All @@ -162,18 +168,17 @@ describe('Auth', () => {
host: 'localhost:5988',
'user-agent': 'curl/8.6.0',
accept: '*/*',
'content-type': 'application/json',
},
}]]);
});

it('should throw a custom 401 error', async () => {
sinon.stub(request, 'get').rejects({ statusCode: 401, error: 'not logged in' });
sinon.stub(request, 'get').rejects({ status: 401, error: 'not logged in' });

await chai.expect(auth.getUserCtx(req)).to.be.rejected.and.eventually.deep.equal({
code: 401,
message: 'Not logged in',
err: { statusCode: 401, error: 'not logged in' }
err: { status: 401, error: 'not logged in' }
});

chai.expect(request.get.callCount).to.equal(1);
Expand Down
Loading

0 comments on commit acca1fe

Please sign in to comment.