From b9ce30ac65b23ec0b5cc8ec3e5f5e3d997985d4f Mon Sep 17 00:00:00 2001 From: Rochet2 Date: Mon, 3 Jun 2019 14:33:17 +0300 Subject: [PATCH 01/13] Fix requests stuck in pending state --- .../src/routes/elementdetails.js | 16 ++++++++-------- .../oodikone2-backend/src/routes/population.js | 2 +- .../src/middlewares/secret.js | 2 +- services/oodikone2-userservice/index.js | 4 ++-- .../src/middlewares/secret.js | 4 ++-- 5 files changed, 14 insertions(+), 14 deletions(-) diff --git a/services/backend/oodikone2-backend/src/routes/elementdetails.js b/services/backend/oodikone2-backend/src/routes/elementdetails.js index b4545ac281..3bc2c75a7b 100644 --- a/services/backend/oodikone2-backend/src/routes/elementdetails.js +++ b/services/backend/oodikone2-backend/src/routes/elementdetails.js @@ -21,7 +21,7 @@ router.get('/v2/studyprogrammes/:id/mandatory_courses', async (req, res) => { const codes = await MandatoryCourses.byStudyprogramme(req.params.id) res.json(codes) } else { - res.status(422) + res.status(422).end() } }) @@ -30,7 +30,7 @@ router.get('/v2/studyprogrammes/ping', async (req, res) => { const result = await ping() res.json(result) } catch (e) { - res.status(500) + res.status(500).end() } }) @@ -54,7 +54,7 @@ router.get('/v2/studyprogrammes/:id/productivity', async (req, res) => { } return res.json(data) } else { - res.status(422) + res.status(422).end() } }) @@ -118,7 +118,7 @@ router.get('/v2/studyprogrammes/:id/throughput', async (req, res) => { } return res.json({ ...data, thesis: await thesisPromise }) } else { - res.status(422) + res.status(422).end() } }) @@ -162,7 +162,7 @@ router.get('/v2/studyprogrammes/:id/thesis', async (req, res) => { const thesis = await findProgrammeTheses(id) res.json(thesis) } else { - res.status(422) + res.status(422).end() } }) @@ -173,7 +173,7 @@ router.post('/v2/studyprogrammes/:id/thesis', async (req, res) => { const thesis = await createThesisCourse(id, course, thesisType) res.status(201).json(thesis) } else { - res.status(422) + res.status(422).end() } }) @@ -183,8 +183,8 @@ router.delete('/v2/studyprogrammes/:id/thesis/:course', async (req, res) => { const deleted = await deleteThesisCourse(id, course) res.status(204).json(deleted) } else { - res.status(422) + res.status(422).end() } }) -module.exports = router \ No newline at end of file +module.exports = router diff --git a/services/backend/oodikone2-backend/src/routes/population.js b/services/backend/oodikone2-backend/src/routes/population.js index 9a6d6ac4c3..0364145e5d 100644 --- a/services/backend/oodikone2-backend/src/routes/population.js +++ b/services/backend/oodikone2-backend/src/routes/population.js @@ -62,7 +62,7 @@ router.get('/v3/populationstatistics', async (req, res) => { if (result.error) { console.log(result.error) - res.status(400) + res.status(400).end() return } diff --git a/services/oodikone2-usageservice/src/middlewares/secret.js b/services/oodikone2-usageservice/src/middlewares/secret.js index 9d49d71a7c..760ea6446a 100644 --- a/services/oodikone2-usageservice/src/middlewares/secret.js +++ b/services/oodikone2-usageservice/src/middlewares/secret.js @@ -1,5 +1,5 @@ const { SECRET } = require('../conf-usageservice') -const checkSecret = (req, res, next) => req.headers.secret == SECRET ? next() : res.status(403) +const checkSecret = (req, res, next) => req.headers.secret == SECRET ? next() : res.status(403).end() module.exports = checkSecret diff --git a/services/oodikone2-userservice/index.js b/services/oodikone2-userservice/index.js index 7ae2c2319a..437aea3943 100644 --- a/services/oodikone2-userservice/index.js +++ b/services/oodikone2-userservice/index.js @@ -84,7 +84,7 @@ app.post('/superlogin', async (req, res) => { if (token) { res.status(200).json(token) } - res.status(400) + res.status(400).end() }) app.put('/user/:uid', async (req, res) => { @@ -138,7 +138,7 @@ app.get('/access_groups', async (req, res) => { const groups = await AccessGroup.findAll() res.status(200).json(groups) } catch (e) { - res.status(400) + res.status(400).end() } }) diff --git a/services/oodikone2-userservice/src/middlewares/secret.js b/services/oodikone2-userservice/src/middlewares/secret.js index 799528ab51..6c6a9bf8f8 100644 --- a/services/oodikone2-userservice/src/middlewares/secret.js +++ b/services/oodikone2-userservice/src/middlewares/secret.js @@ -1,5 +1,5 @@ -const checkSecret = (req, res, next) => req.headers.secret == process.env.SECRET ? next() : res.status(403) +const checkSecret = (req, res, next) => req.headers.secret == process.env.SECRET ? next() : res.status(403).end() -module.exports = +module.exports = checkSecret From ac29023575420cfada7b39c5a508a91ea326d40b Mon Sep 17 00:00:00 2001 From: Rochet2 Date: Mon, 3 Jun 2019 14:33:41 +0300 Subject: [PATCH 02/13] Fix crash when superlogin used with invalid userid --- services/oodikone2-userservice/src/services/users.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/services/oodikone2-userservice/src/services/users.js b/services/oodikone2-userservice/src/services/users.js index 0423e0849f..97484a06f7 100644 --- a/services/oodikone2-userservice/src/services/users.js +++ b/services/oodikone2-userservice/src/services/users.js @@ -108,7 +108,7 @@ const login = async (uid, full_name, hyGroups, affiliations, mail) => { } const superlogin = async (uid, asUser) => { const user = await byUsername(uid) - if (user.accessgroup.map(r => r.group_code).includes('admin')) { + if (user && user.accessgroup.map(r => r.group_code).includes('admin')) { const token = await generateToken(asUser, uid) return token } From dacc8e9b227ab5481f67c0dd20a148b652a01f94 Mon Sep 17 00:00:00 2001 From: Rochet2 Date: Mon, 3 Jun 2019 14:37:42 +0300 Subject: [PATCH 03/13] err is not convertible to json (circular) --- services/backend/oodikone2-backend/src/routes/login.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/services/backend/oodikone2-backend/src/routes/login.js b/services/backend/oodikone2-backend/src/routes/login.js index 3f03d8f30b..0129e2769d 100644 --- a/services/backend/oodikone2-backend/src/routes/login.js +++ b/services/backend/oodikone2-backend/src/routes/login.js @@ -27,7 +27,7 @@ router.post('/login', async (req, res) => { const mail = req.headers.mail || '' const hyGroups = parseHyGroups(req.headers['hygroupcn']) const affiliations = parseHyGroups(req.headers['edupersonaffiliation']) - + console.log(uid, 'trying to login, referring to userservice.') let { token, isNew } = await userService.login(uid, full_name, hyGroups, affiliations, mail) isNew && sendEmail(uid) @@ -59,8 +59,8 @@ router.post('/superlogin/:uid', async (req, res) => { }).end() } } catch (err) { - console.log(err) - res.status(401).json({ message: 'problem with login', err }) + console.log(err.message) + res.status(401).json({ message: 'problem with login', err: err.message }) } }) From 96df3b559ee547eb15032077e3055f445dc4d75c Mon Sep 17 00:00:00 2001 From: Rochet2 Date: Mon, 3 Jun 2019 15:11:18 +0300 Subject: [PATCH 04/13] Fix superlogin --- services/oodikone2-frontend/src/components/UserPage/index.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/services/oodikone2-frontend/src/components/UserPage/index.jsx b/services/oodikone2-frontend/src/components/UserPage/index.jsx index 8aa8b061a5..c294515663 100644 --- a/services/oodikone2-frontend/src/components/UserPage/index.jsx +++ b/services/oodikone2-frontend/src/components/UserPage/index.jsx @@ -153,7 +153,7 @@ class UserPage extends Component { { this.props.isAdmin && user.is_enabled && ( } + trigger={ ), From 668a4daf51d088a27282d750c8d29c7a57ccce1e Mon Sep 17 00:00:00 2001 From: Rochet2 Date: Mon, 3 Jun 2019 16:44:51 +0300 Subject: [PATCH 06/13] overwrite existing files when unzipping --- scripts.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts.sh b/scripts.sh index a781775ce0..77fbe13c06 100644 --- a/scripts.sh +++ b/scripts.sh @@ -57,7 +57,7 @@ get_anon_oodikone() { } unpack_oodikone_server_backup() { - bunzip2 -d -v ./$BACKUP_DIR/*.bz2 + bunzip2 --force --decompress --verbose ./$BACKUP_DIR/*.bz2 } restore_psql_from_backup () { From e30dde6c5dabf86ba5de7f82809d6a69766c9612 Mon Sep 17 00:00:00 2001 From: Rochet2 Date: Mon, 3 Jun 2019 17:25:12 +0300 Subject: [PATCH 07/13] Fix release slack message --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index b4d533f4a5..f92c36afb7 100644 --- a/.travis.yml +++ b/.travis.yml @@ -57,7 +57,7 @@ jobs: deploy: # deploy is always skipped for PR provider: script skip_cleanup: true - script: "docker-compose -f docker-compose.lateste2e.production.yml push && curl -X POST -H 'Content-type: application/json' --data \"{\\\"text\\\":\\\"${TRAVIS_TAG} release waiting for manual deployment!\\\"}\" ${SLACKBOT_URL}" + script: "docker-compose -f docker-compose.lateste2e.production.yml push && curl -X POST -H 'Content-type: application/json' --data \"{\\\"text\\\":\\\"${TRAVIS_TAG} release started auto deployment!\\\"}\" ${SLACKBOT_URL}" on: tags: true From 5ee619ae99fd9dc8aee5f0c2beba37ca66b9a710 Mon Sep 17 00:00:00 2001 From: Rochet2 Date: Mon, 3 Jun 2019 17:28:45 +0300 Subject: [PATCH 08/13] Fix test clicking button before refresh --- cypress/integration/Users.js | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/cypress/integration/Users.js b/cypress/integration/Users.js index 8ee31654a9..15e8d45e99 100644 --- a/cypress/integration/Users.js +++ b/cypress/integration/Users.js @@ -18,16 +18,6 @@ describe('Users tests', () => { cy.contains("Enable or disable access to Oodikone") }) - it('Admin mocking user shows "mocking as"-button', () => { - cy.contains("mocking").should('not.exist') - cy.contains("tr", "Normaalikäyttäjä").within(($row) => { - cy.contains('button', 'Edit').click() - }) - - cy.get('i.spy').click() - cy.contains("mocking as normk") - }) - it("Admin mocking normal user shows only the mocked user's programmes", () => { cy.contains("mocking").should('not.exist') cy.contains("tr", "Normaalikäyttäjä").within(($row) => { @@ -38,6 +28,7 @@ describe('Users tests', () => { }) cy.get('i.spy').click() + cy.contains("mocking as normk") cy.contains("Study programme").click().siblings().contains("Search by class").click() cy.contains("label", "Study programme") cy.contains("label", "Study programme").siblings().within(($row) => { From f497dc5da06573fc487aa519c34325595ce95665 Mon Sep 17 00:00:00 2001 From: Rochet2 Date: Mon, 3 Jun 2019 18:23:30 +0300 Subject: [PATCH 09/13] Re enable cypress failure recording again --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 0ac6ceecb9..9d6a044dff 100644 --- a/package.json +++ b/package.json @@ -6,7 +6,7 @@ "scripts": { "cypress:open": "CYPRESS_baseUrl=http://localhost:8081 cypress open", "cypress:run": "CYPRESS_baseUrl=http://localhost:8081 cypress run --config video=false -P ./", - "cypress:record": "cypress run --config videoUploadOnPasses=false -P ./", + "cypress:record": "cypress run --config videoUploadOnPasses=false --record -P ./", "concurrently": "concurrently", "test": "./run_all_tests.sh", "test_services": "./run_service_tests.sh" From a40405f8a2c1b3ec16f3672ddacb1d7d3a65321f Mon Sep 17 00:00:00 2001 From: Rochet2 Date: Mon, 3 Jun 2019 18:30:04 +0300 Subject: [PATCH 10/13] Fix mockedby impersonation --- .../backend/oodikone2-backend/src/routes.js | 2 ++ .../oodikone2-backend/src/routes/login.js | 20 ---------------- .../src/routes/superlogin.js | 24 +++++++++++++++++++ 3 files changed, 26 insertions(+), 20 deletions(-) create mode 100644 services/backend/oodikone2-backend/src/routes/superlogin.js diff --git a/services/backend/oodikone2-backend/src/routes.js b/services/backend/oodikone2-backend/src/routes.js index f3294bdf0e..6f416c840a 100644 --- a/services/backend/oodikone2-backend/src/routes.js +++ b/services/backend/oodikone2-backend/src/routes.js @@ -4,6 +4,7 @@ const courses = require('./routes/courses') const students = require('./routes/students') const population = require('./routes/population') const login = require('./routes/login') +const superlogin = require('./routes/superlogin') const language = require('./routes/language') const users = require('./routes/users') const elementdetails = require('./routes/elementdetails') @@ -25,6 +26,7 @@ module.exports = (app, url) => { app.use(url, login) app.use(url, ping) app.use(auth.checkAuth, auth.checkRequiredGroup, auth.checkUserBlacklisting, accessLogger) + app.use(`${url}/superlogin`, auth.roles(['admin']), superlogin) app.use(url, elementdetails) app.use(url, courses) app.use(url, students) diff --git a/services/backend/oodikone2-backend/src/routes/login.js b/services/backend/oodikone2-backend/src/routes/login.js index 0129e2769d..faf1464d39 100644 --- a/services/backend/oodikone2-backend/src/routes/login.js +++ b/services/backend/oodikone2-backend/src/routes/login.js @@ -44,26 +44,6 @@ router.post('/login', async (req, res) => { } }) -router.post('/superlogin/:uid', async (req, res) => { - try { - const asUser = req.params.uid - const uid = req.headers['uid'] - if (req.headers['shib-session-id'] && uid) { - console.log('super') - const token = await userService.superlogin(uid, asUser) - res.status(200).json({ token }) - } else { - res.status(401).json({ - message: `Not enough headers login, uid: - ${req.headers.uid} session-id ${req.headers['shib-session-id']}` - }).end() - } - } catch (err) { - console.log(err.message) - res.status(401).json({ message: 'problem with login', err: err.message }) - } -}) - router.delete('/logout', async (req, res) => { try { const logoutUrl = req.headers.shib_logout_url diff --git a/services/backend/oodikone2-backend/src/routes/superlogin.js b/services/backend/oodikone2-backend/src/routes/superlogin.js new file mode 100644 index 0000000000..574a314cc2 --- /dev/null +++ b/services/backend/oodikone2-backend/src/routes/superlogin.js @@ -0,0 +1,24 @@ +const router = require('express').Router() +const userService = require('../services/userService') + +router.post('/:uid', async (req, res) => { + try { + const asUser = req.params.uid + const uid = req.decodedToken.mockedBy == null ? req.headers['uid'] : req.decodedToken.mockedBy + if (req.headers['shib-session-id'] && uid) { + console.log('super') + const token = await userService.superlogin(uid, asUser) + res.status(200).json({ token }) + } else { + res.status(401).json({ + message: `Not enough headers login, uid: + ${req.headers.uid} session-id ${req.headers['shib-session-id']}` + }).end() + } + } catch (err) { + console.log(err.message) + res.status(401).json({ message: 'problem with login', err: err.message }) + } +}) + +module.exports = router From b82c1dc5979153919ddf4adc927002e8ccb50902 Mon Sep 17 00:00:00 2001 From: Rochet2 Date: Mon, 3 Jun 2019 19:11:39 +0300 Subject: [PATCH 11/13] superlogin now sends token for access control --- .../src/apiConnection/index.js | 19 ++++++------------- 1 file changed, 6 insertions(+), 13 deletions(-) diff --git a/services/oodikone2-frontend/src/apiConnection/index.js b/services/oodikone2-frontend/src/apiConnection/index.js index ac8ee96dc2..74a8d4a6a8 100644 --- a/services/oodikone2-frontend/src/apiConnection/index.js +++ b/services/oodikone2-frontend/src/apiConnection/index.js @@ -72,19 +72,6 @@ export const login = async () => { return response.data.token } -export const superLogin = async (uid) => { - let options = null - if (isDevEnv) { - options = devOptions - } - if (isTestEnv) { - options = testOptions - } - const response = await getAxios().post(`/superlogin/${uid}`, null, options) - console.log(`Setting new token ${response.data.token}`) - await setToken(response.data.token) -} - export const returnToSelf = async () => { const token = await login() await setToken(token) @@ -115,6 +102,12 @@ export const callApi = async (url, method = 'get', data, params, timeout = 0) => } } +export const superLogin = async (uid) => { + const response = await callApi(`/superlogin/${uid}`, 'post') + console.log(`Setting new token ${response.data.token}`) + await setToken(response.data.token) +} + export const callController = (route, prefix, data, method = 'get', query, params) => { const requestSettings = { route, From 5c867fea77e78bd74b61b17abe06713ff099a302 Mon Sep 17 00:00:00 2001 From: Rochet2 Date: Mon, 3 Jun 2019 20:02:43 +0300 Subject: [PATCH 12/13] Save last query and only accept responses related to last query --- services/oodikone2-frontend/src/redux/students.js | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/services/oodikone2-frontend/src/redux/students.js b/services/oodikone2-frontend/src/redux/students.js index edb0180e4f..68f86b2269 100644 --- a/services/oodikone2-frontend/src/redux/students.js +++ b/services/oodikone2-frontend/src/redux/students.js @@ -3,7 +3,7 @@ import { callController } from '../apiConnection' export const findStudents = (searchStr) => { const route = `/students/?searchTerm=${searchStr}` const prefix = 'FIND_STUDENTS_' - return callController(route, prefix) + return callController(route, prefix, undefined, undefined, searchStr) } export const getStudent = (studentNumber) => { @@ -31,6 +31,7 @@ const reducer = (state = { data: [] }, action) => { return { pending: true, selected: state.selected, + lastSearch: action.requestSettings.query, data: state.data } case 'FIND_STUDENTS_FAILURE': @@ -45,7 +46,9 @@ const reducer = (state = { data: [] }, action) => { pending: false, error: false, selected: state.selected, - data: [...state.data.filter(student => student.fetched), ...action.response] + data: state.lastSearch === action.query ? + [...state.data.filter(student => student.fetched), ...action.response] : + state.data } case 'GET_STUDENT_SUCCESS': return { From 35e82b80f0a2139aa28ca2afe50584293e420d4f Mon Sep 17 00:00:00 2001 From: Rochet2 Date: Mon, 3 Jun 2019 21:06:56 +0300 Subject: [PATCH 13/13] Check that nothing has nonzero exit code in travis --- .travis.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.travis.yml b/.travis.yml index f92c36afb7..6899dfd5cc 100644 --- a/.travis.yml +++ b/.travis.yml @@ -34,6 +34,7 @@ jobs: - time npm run test_services - time CYPRESS_baseUrl=http://localhost:1337/staging/ npm run cypress:record - docker ps -a + - if [[ $(docker ps --all | grep -Eo '(Exited|Restarting) \([0-9]+\)' | grep -Eo '[0-9]+' | awk '{ sum += $1 } END { print sum }') != '0' ]]; then echo 'Some process had nonzero exit code'; exit 1; fi deploy: # deploy is always skipped for PR provider: script skip_cleanup: true @@ -54,6 +55,7 @@ jobs: - time npm run test_services - time CYPRESS_baseUrl=http://localhost:1337/ npm run cypress:record - docker ps -a + - if [[ $(docker ps --all | grep -Eo '(Exited|Restarting) \([0-9]+\)' | grep -Eo '[0-9]+' | awk '{ sum += $1 } END { print sum }') != '0' ]]; then echo 'Some process had nonzero exit code'; exit 1; fi deploy: # deploy is always skipped for PR provider: script skip_cleanup: true