Skip to content

Commit 74634c5

Browse files
authored
Backport logout fix to 3.10.x (#7175)
* Prevent users being logged out incorrectly (#7171) Updates API and Webapp to: - Only consider 401s from CouchDB to be valid missing authentication - Throw other _session errors without changes - Malformed _session responses result in a 500 - Don't send 401s for missing userCtx, instead send the actual authentication error or a 500. - Add a custom header to 401 responses that Webapp checks for, and only when matched it would log users out. (prevents man-in-the-middle from logging users out with 401s) #7169 (cherry picked from commit 2486dc5) * Adds GitHub actions workflow to take over CI.
1 parent afb4b53 commit 74634c5

File tree

13 files changed

+510
-86
lines changed

13 files changed

+510
-86
lines changed

.github/workflows/build.yml

Lines changed: 161 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,161 @@
1+
name: Build cht-core and test against node versions
2+
3+
on: [push, pull_request]
4+
5+
env:
6+
COUCH_URL: http://admin:pass@localhost:5984/medic-test
7+
COUCH_NODE_NAME: nonode@nohost
8+
BUILDS_SERVER: _couch/builds_testing
9+
STAGING_SERVER: _couch/builds
10+
UPLOAD_URL: ${{ secrets.MARKET_URL }}
11+
12+
jobs:
13+
build:
14+
name: Compile the app
15+
runs-on: ubuntu-18.04
16+
17+
steps:
18+
- name: Get Docker Hub username
19+
id: get-docker-hub-username
20+
run: echo '::set-output name=dockerhub_username::${{ secrets.DOCKERHUB_USERNAME }}'
21+
- name: Login to Docker Hub
22+
uses: docker/login-action@v1
23+
with:
24+
username: ${{ secrets.DOCKERHUB_USERNAME }}
25+
password: ${{ secrets.DOCKERHUB_TOKEN }}
26+
if: steps.get-docker-hub-username.outputs.dockerhub_username
27+
- name: Set UPLOAD_URL var
28+
run: |
29+
if [[ -z "$UPLOAD_URL" ]]; then
30+
echo "UPLOAD_URL=https://staging.dev.medicmobile.org" >> $GITHUB_ENV
31+
echo "BUILDS_SERVER=_couch/builds_external" >> $GITHUB_ENV
32+
echo "STAGING_SERVER=_couch/builds_external" >> $GITHUB_ENV
33+
fi
34+
- name: Get branch name
35+
uses: nelonoel/branch-name@1ea5c86cb559a8c4e623da7f188496208232e49f
36+
- name: Set Travis Vars
37+
run: |
38+
echo "TRAVIS_BUILD_NUMBER=$GITHUB_RUN_ID" >> $GITHUB_ENV
39+
echo "TRAVIS_BRANCH=$BRANCH_NAME" >> $GITHUB_ENV
40+
- uses: actions/checkout@v2
41+
- name: Use Node.js 12.x
42+
uses: actions/setup-node@v1
43+
with:
44+
node-version: 12.x
45+
- name: Couch Start
46+
run: ./scripts/travis/couch-start
47+
- name: Create logs directory
48+
run: mkdir tests/logs
49+
- name: npm CI
50+
run: npm ci
51+
- name: Grunt Install
52+
run: npm install -g grunt-cli
53+
- name: Configure Couch
54+
run: ./scripts/travis/couch-config
55+
- name: Grunt CI-Compile
56+
run: |
57+
node --stack_size=10000 `which grunt` ci-compile
58+
- name: Publish for testing
59+
run: |
60+
node --stack_size=10000 `which grunt` publish-for-testing
61+
62+
test-e2e:
63+
needs: build
64+
name: e2e Tests for Node version ${{ matrix.node-version }}
65+
runs-on: ubuntu-18.04
66+
67+
strategy:
68+
matrix:
69+
node-version: [8.x, 10.x, 12.x]
70+
71+
steps:
72+
- name: Get Docker Hub username
73+
id: get-docker-hub-username
74+
run: echo '::set-output name=dockerhub_username::${{ secrets.DOCKERHUB_USERNAME }}'
75+
- name: Login to Docker Hub
76+
uses: docker/login-action@v1
77+
with:
78+
username: ${{ secrets.DOCKERHUB_USERNAME }}
79+
password: ${{ secrets.DOCKERHUB_TOKEN }}
80+
if: steps.get-docker-hub-username.outputs.dockerhub_username
81+
- name: Set Travis Vars
82+
run: |
83+
echo "TRAVIS_BUILD_NUMBER=$GITHUB_RUN_ID" >> $GITHUB_ENV
84+
echo "TEST_SUITE=integration" >> $GITHUB_ENV
85+
- name: Set UPLOARD_URL var
86+
run: |
87+
if [[ -z "$UPLOAD_URL" ]]; then
88+
echo "UPLOAD_URL=https://staging.dev.medicmobile.org" >> $GITHUB_ENV
89+
echo "BUILDS_SERVER=_couch/builds_external" >> $GITHUB_ENV
90+
echo "STAGING_SERVER=_couch/builds_external" >> $GITHUB_ENV
91+
fi
92+
- uses: actions/checkout@v2
93+
- name: Use Node.js ${{ matrix.node-version }}
94+
uses: actions/setup-node@v1
95+
with:
96+
node-version: ${{ matrix.node-version }}
97+
- name: Couch Start
98+
run: ./scripts/travis/couch-start
99+
- name: xsltprox install
100+
run: sudo apt-get install xsltproc
101+
- name: Create logs directory
102+
run: mkdir tests/logs
103+
- name: npm CI
104+
run: npm ci
105+
- name: Grunt Install
106+
run: npm install -g grunt-cli
107+
- name: Horti Install
108+
run: npm install -g horticulturalist
109+
- name: Configure Couch
110+
run: ./scripts/travis/couch-config
111+
- name: Echo Vars
112+
run: |
113+
echo "HORTI_BUILDS_SERVER=${UPLOAD_URL}/${BUILDS_SERVER}"
114+
echo "--install=medic:medic:test-${TRAVIS_BUILD_NUMBER}"
115+
echo "COUCH_URL=${COUCH_URL}"
116+
- name: Curl Couch_url
117+
run: curl ${COUCH_URL}
118+
- name: horti setup
119+
run: |
120+
echo "COUCH_URL=$COUCH_URL HORTI_BUILDS_SERVER=$UPLOAD_URL/$BUILDS_SERVER"
121+
COUCH_URL=$COUCH_URL HORTI_BUILDS_SERVER=$UPLOAD_URL/$BUILDS_SERVER horti --local --install=medic:medic:test-$TRAVIS_BUILD_NUMBER > tests/logs/horti.log &
122+
- name: Test it!
123+
run: node --stack_size=10000 `which grunt` ci-e2e
124+
- name: Dump Couch logs
125+
run: docker logs couch > tests/logs/couch.log 2>&1
126+
if: ${{ always() }}
127+
- name: Archive Results
128+
uses: actions/upload-artifact@v2
129+
with:
130+
name: Results Artifact ${{ matrix.node-version }}
131+
path: |
132+
tests/results
133+
tests/logs
134+
if: ${{ failure() }}
135+
136+
publish:
137+
needs: [test-e2e]
138+
name: Publish branch build
139+
runs-on: ubuntu-18.04
140+
141+
steps:
142+
- uses: actions/checkout@v2
143+
- name: Set UPLOAD_URL var
144+
run: |
145+
if [[ -z "$UPLOAD_URL" ]]; then
146+
echo "UPLOAD_URL=https://staging.dev.medicmobile.org" >> $GITHUB_ENV
147+
echo "BUILDS_SERVER=_couch/builds_external" >> $GITHUB_ENV
148+
echo "STAGING_SERVER=_couch/builds_external" >> $GITHUB_ENV
149+
fi
150+
- name: Get branch name
151+
uses: nelonoel/branch-name@1ea5c86cb559a8c4e623da7f188496208232e49f
152+
- name: Set Travis Vars
153+
run: |
154+
echo "TRAVIS_BUILD_NUMBER=$GITHUB_RUN_ID" >> $GITHUB_ENV
155+
echo "TRAVIS_BRANCH=$BRANCH_NAME" >> $GITHUB_ENV
156+
- name: Publish
157+
if: ${{ github.event_name != 'pull_request' }}
158+
run: |
159+
cd scripts/travis
160+
npm ci
161+
node ./publish.js

Gruntfile.js

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ const {
1414
STAGING_SERVER,
1515
BUILDS_SERVER,
1616
TRAVIS_BUILD_NUMBER,
17-
WEBDRIVER_VERSION=85
17+
WEBDRIVER_VERSION=90
1818
} = process.env;
1919

2020
const releaseName = TRAVIS_TAG || TRAVIS_BRANCH || 'local-development';
@@ -521,8 +521,8 @@ module.exports = function(grunt) {
521521
'start-webdriver': {
522522
cmd:
523523
'mkdir -p tests/logs && ' +
524-
'./node_modules/.bin/webdriver-manager update && ' +
525-
'./node_modules/.bin/webdriver-manager start > tests/logs/webdriver.log & ' +
524+
'./node_modules/.bin/webdriver-manager update --versions.chrome 90.0.4430.24 && ' +
525+
'./node_modules/.bin/webdriver-manager start --versions.chrome 90.0.4430.24 &> tests/logs/webdriver.log & ' +
526526
'until nc -z localhost 4444; do sleep 1; done',
527527
},
528528
'check-env-vars':

api/src/auth.js

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -86,14 +86,17 @@ module.exports = {
8686
getUserCtx: req => {
8787
return get('/_session', req.headers)
8888
.catch(err => {
89-
throw { code: 401, message: 'Not logged in', err: err };
89+
if (err.statusCode === 401) {
90+
throw { code: 401, message: 'Not logged in', err: err };
91+
}
92+
throw err;
9093
})
9194
.then(auth => {
9295
if (auth && auth.userCtx && auth.userCtx.name) {
9396
req.headers['X-Medic-User'] = auth.userCtx.name;
9497
return auth.userCtx;
9598
}
96-
throw { code: 401, message: 'Not logged in' };
99+
throw { code: 500, message: 'Failed to authenticate' };
97100
});
98101
},
99102

api/src/middleware/authorization.js

Lines changed: 22 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -31,27 +31,40 @@ module.exports = {
3131
.then(next);
3232
},
3333

34-
// blocks offline users not-authorized requests
35-
offlineUserFirewall: (req, res, next) => {
34+
handleAuthErrors: (req, res, next) => {
35+
if (req.authErr) {
36+
return serverUtils.error(req.authErr, req, res);
37+
}
38+
3639
if (!req.userCtx) {
37-
return serverUtils.notLoggedIn(req, res);
40+
return serverUtils.error('Authentication error', req, res);
41+
}
42+
43+
next();
44+
},
45+
46+
handleAuthErrorsAllowingAuthorized: (req, res, next) => {
47+
if (req.authorized) {
48+
return next();
3849
}
3950

40-
if (!auth.isOnlineOnly(req.userCtx) && !req.authorized) {
51+
return module.exports.handleAuthErrors(req, res, next);
52+
},
53+
54+
// blocks offline users not-authorized requests
55+
offlineUserFirewall: (req, res, next) => {
56+
if (req.userCtx && !auth.isOnlineOnly(req.userCtx) && !req.authorized) {
4157
res.status(FIREWALL_ERROR.code);
4258
return res.json(FIREWALL_ERROR);
4359
}
4460
next();
4561
},
4662

63+
// proxies unauthenticated requests to CouchDB
4764
// proxies online users requests to CouchDB
4865
// saves offline user-settings doc in the request object
4966
onlineUserProxy: (proxy, req, res, next) => {
50-
if (!req.userCtx) {
51-
return serverUtils.notLoggedIn(req, res);
52-
}
53-
54-
if (auth.isOnlineOnly(req.userCtx)) {
67+
if (!req.userCtx || auth.isOnlineOnly(req.userCtx)) {
5568
return proxy.web(req, res);
5669
}
5770

@@ -62,10 +75,6 @@ module.exports = {
6275
// saves offline user-settings doc in the request object
6376
// used for audited endpoints
6477
onlineUserPassThrough:(req, res, next) => {
65-
if (!req.userCtx) {
66-
return serverUtils.notLoggedIn(req, res);
67-
}
68-
6978
if (auth.isOnlineOnly(req.userCtx)) {
7079
return next('route');
7180
}
@@ -80,10 +89,6 @@ module.exports = {
8089
},
8190

8291
getUserSettings: (req, res, next) => {
83-
if (!req.userCtx) {
84-
return serverUtils.notLoggedIn(req, res);
85-
}
86-
8792
if (auth.isOnlineOnly(req.userCtx)) {
8893
return next();
8994
}

0 commit comments

Comments
 (0)