Fix most glaring bugs and vulnerabilities
This commit is contained in:
@@ -11,6 +11,25 @@ exports.listComments = async (req, res) => {
|
||||
filters.ctxId = null
|
||||
}
|
||||
|
||||
if (filters.ctxType === 'entry_dict_int') {
|
||||
const { dictionary_id: dictionaryId } = await Entry.fetch(filters.ctxId)
|
||||
const isEditor = req.user.hasAnyDictionaryRole(dictionaryId)
|
||||
const isPortalAdmin = req.user.hasRole('portal admin')
|
||||
const isDictionariesAdmin = req.user.hasRole('dictionaries admin')
|
||||
if (!(isEditor || isPortalAdmin || isDictionariesAdmin)) {
|
||||
return res.status(400).end()
|
||||
}
|
||||
} else if (filters.ctxType === 'entry_consult_int') {
|
||||
const isConsultantForEntry = req.user.isEditorOfConsultancyEntry(
|
||||
filters.ctxId
|
||||
)
|
||||
const isPortalAdmin = req.user.hasRole('portal admin')
|
||||
const isConsultancyAdmin = req.user.hasRole('consultancy admin')
|
||||
if (!(isConsultantForEntry || isPortalAdmin || isConsultancyAdmin)) {
|
||||
return res.status(400).end()
|
||||
}
|
||||
}
|
||||
|
||||
const {
|
||||
pages_total: numberOfAllPages,
|
||||
comments,
|
||||
@@ -38,20 +57,55 @@ exports.createComment = async (req, res) => {
|
||||
res.send({ comments, pagesTotal })
|
||||
}
|
||||
|
||||
exports.seedComments = async (req, res) => {
|
||||
const { commentCount } = req.params
|
||||
await Comment.seed(commentCount)
|
||||
res.send(`${commentCount} new comments generated`)
|
||||
}
|
||||
|
||||
exports.clearComments = async (req, res) => {
|
||||
await Comment.clear()
|
||||
res.send('All comments cleared')
|
||||
}
|
||||
|
||||
exports.updateStatus = async (req, res) => {
|
||||
const commentId = req.body.params.id
|
||||
const commentStatus = req.body.params.status
|
||||
|
||||
const { ctxType, ctxId } = await Comment.fetchContextById(commentId)
|
||||
|
||||
let canUpdateStatus = false
|
||||
switch (ctxType) {
|
||||
case 'portal':
|
||||
if (req.user.hasRole('portal admin')) canUpdateStatus = true
|
||||
break
|
||||
|
||||
case 'dictionary':
|
||||
if (
|
||||
req.user.hasRole('portal admin') ||
|
||||
req.user.hasRole('dictionaries admin')
|
||||
) {
|
||||
canUpdateStatus = true
|
||||
}
|
||||
break
|
||||
|
||||
case 'consultancy':
|
||||
if (
|
||||
req.user.hasRole('portal admin') ||
|
||||
req.user.hasRole('consultancy admin')
|
||||
) {
|
||||
canUpdateStatus = true
|
||||
}
|
||||
break
|
||||
|
||||
case 'entry_dict_ext': {
|
||||
const { dictionary_id: dictionaryId } = await Entry.fetch(ctxId)
|
||||
|
||||
if (
|
||||
req.user.hasRole('portal admin') ||
|
||||
req.user.hasRole('dictionaries admin') ||
|
||||
req.user.hasDictionaryRole(dictionaryId, 'administration')
|
||||
) {
|
||||
canUpdateStatus = true
|
||||
}
|
||||
break
|
||||
}
|
||||
|
||||
default:
|
||||
throw Error('Invalid context type')
|
||||
}
|
||||
|
||||
if (!canUpdateStatus) return res.status(400).end()
|
||||
|
||||
await Comment.updateStatus(commentId, commentStatus)
|
||||
res.send('Visibility changed')
|
||||
}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
const user = require('../../../middleware/user')
|
||||
const ConsultancyEntry = require('../../../models/consultancy-entry')
|
||||
const Domain = require('../../../models/domain')
|
||||
const User = require('../../../models/user')
|
||||
@@ -16,20 +17,6 @@ const generateQuery = require('../../../models/helpers/search/generate-query')
|
||||
|
||||
const consultancy = {}
|
||||
|
||||
consultancy.listEntries = async (req, res) => {
|
||||
const consultancyEntryList = await ConsultancyEntry.fetchAll()
|
||||
const data = {}
|
||||
data.consEntryList = consultancyEntryList
|
||||
res.send(data)
|
||||
}
|
||||
|
||||
consultancy.listNewEntries = async (req, res) => {
|
||||
const consultancyNewEntryList = await ConsultancyEntry.fetchAllNew()
|
||||
const data = {}
|
||||
data.consultancyNewEntryList = consultancyNewEntryList
|
||||
res.send(data)
|
||||
}
|
||||
|
||||
consultancy.sendPaginationData = async (req, res) => {
|
||||
let requestType = req.query.type
|
||||
const isAdminPage = req.query.isAdmin === 'true'
|
||||
@@ -325,9 +312,12 @@ consultancy.publish = async (req, res) => {
|
||||
const author = await User.fetchUser(entry.authorId)
|
||||
const portalName = await getInstanceSetting(`portal_name_${author.language}`)
|
||||
const renderAsync = promisify(req.app.render.bind(req.app))
|
||||
const emailHtml = await renderAsync('email/consultancy-publish-notify', {
|
||||
portalName
|
||||
})
|
||||
const emailHtml = await renderAsync(
|
||||
`email/consultancy-publish-notify_${author.language}`,
|
||||
{
|
||||
portalName
|
||||
}
|
||||
)
|
||||
await email.send({
|
||||
to: author.email,
|
||||
subject: i18next.t('Objava terminološkega vprašanja', {
|
||||
@@ -342,29 +332,38 @@ consultancy.publish = async (req, res) => {
|
||||
res.send()
|
||||
}
|
||||
|
||||
consultancy.updateQuestion = async (req, res) => {
|
||||
const { id, questionTitle, domain: domainId, question, answer } = req.body
|
||||
consultancy.updateQuestion = [
|
||||
(req, res, next) => {
|
||||
const { id } = req.body
|
||||
|
||||
if (!id) return res.status(400).send({})
|
||||
if (!id) return res.status(400).send({})
|
||||
|
||||
if (questionTitle === '' || question === '' || answer === '') {
|
||||
return res
|
||||
.status(422)
|
||||
.send(req.t('Polja naslov, vprašanje in mnenje so obvezna!'))
|
||||
req.entryId = id
|
||||
next()
|
||||
},
|
||||
user.canConsultEntry,
|
||||
async (req, res) => {
|
||||
const { id, questionTitle, domain: domainId, question, answer } = req.body
|
||||
|
||||
if (questionTitle === '' || question === '' || answer === '') {
|
||||
return res
|
||||
.status(422)
|
||||
.send(req.t('Polja naslov, vprašanje in mnenje so obvezna!'))
|
||||
}
|
||||
|
||||
const entry = await ConsultancyEntry.fetchById(id)
|
||||
entry.domainPrimaryId = domainId > 0 ? domainId : null
|
||||
entry.question = question
|
||||
entry.answer = answer // helper.removeHtmlTags(answer).trim()
|
||||
entry.title = questionTitle
|
||||
|
||||
// TODO Luka: Miha, update only fields that were updated.
|
||||
await ConsultancyEntry.updateQuestion(entry)
|
||||
await ConsultancyEntry.indexIntoSearchEngine(id, true)
|
||||
|
||||
res.send({})
|
||||
}
|
||||
|
||||
const entry = await ConsultancyEntry.fetchById(id)
|
||||
entry.domainPrimaryId = domainId > 0 ? domainId : null
|
||||
entry.question = question
|
||||
entry.answer = answer // helper.removeHtmlTags(answer).trim()
|
||||
entry.title = questionTitle
|
||||
|
||||
// TODO Luka: Miha, update only fields that were updated.
|
||||
await ConsultancyEntry.updateQuestion(entry)
|
||||
await ConsultancyEntry.indexIntoSearchEngine(id, true)
|
||||
|
||||
res.send({})
|
||||
}
|
||||
]
|
||||
|
||||
consultancy.insertNonModerator = async (req, res) => {
|
||||
const questionId = req.body.question_id
|
||||
|
||||
@@ -1,15 +0,0 @@
|
||||
const DemoPaginacija = require('../../../models/demo-paginacija')
|
||||
const { DEFAULT_HITS_PER_PAGE } = require('../../../config/settings')
|
||||
|
||||
exports.list = async (req, res) => {
|
||||
const resultsPerPage = req.user?.hitsPerPage || DEFAULT_HITS_PER_PAGE
|
||||
|
||||
const page = +req.query.p > 0 ? +req.query.p : 1
|
||||
|
||||
const { pages_total: numberOfAllPages, results } = await DemoPaginacija.fetch(
|
||||
resultsPerPage,
|
||||
page
|
||||
)
|
||||
|
||||
res.send({ page, numberOfAllPages, results })
|
||||
}
|
||||
@@ -1,4 +1,5 @@
|
||||
const { rm } = require('fs/promises')
|
||||
const user = require('../../../middleware/user')
|
||||
const Dictionary = require('../../../models/dictionary')
|
||||
const Entry = require('../../../models/entry')
|
||||
const Extraction = require('../../../models/extraction')
|
||||
@@ -69,7 +70,11 @@ dictionary.deleteEntry = async (req, res) => {
|
||||
await Promise.all([
|
||||
Dictionary.updateMetadataAfterModifyingEntries(dictionaryId),
|
||||
deleteEntryFromIndex(entryId, true),
|
||||
minEntriesRequirementCheckAndAct.onDelete(dictionaryId, req.app)
|
||||
minEntriesRequirementCheckAndAct.onDelete(
|
||||
dictionaryId,
|
||||
req.app,
|
||||
req.determinedLanguage
|
||||
)
|
||||
])
|
||||
|
||||
res.end()
|
||||
@@ -84,7 +89,11 @@ dictionary.deleteAllEntries = async (req, res) => {
|
||||
await Promise.all([
|
||||
Dictionary.updateMetadataAfterModifyingEntries(dictionaryId),
|
||||
deleteDictionaryEntriesFromIndex(dictionaryId),
|
||||
minEntriesRequirementCheckAndAct.onDelete(dictionaryId, req.app)
|
||||
minEntriesRequirementCheckAndAct.onDelete(
|
||||
dictionaryId,
|
||||
req.app,
|
||||
req.determinedLanguage
|
||||
)
|
||||
])
|
||||
|
||||
res.end()
|
||||
@@ -124,12 +133,19 @@ dictionary.delete = async (req, res) => {
|
||||
res.end()
|
||||
}
|
||||
|
||||
dictionary.updateDomainLabels = async (req, res) => {
|
||||
const { dictionaryId, payload } = req.body.params
|
||||
dictionary.updateDomainLabels = [
|
||||
(req, res, next) => {
|
||||
req.dictionaryId = req.body.params.dictionaryId
|
||||
next()
|
||||
},
|
||||
user.canAdministrateDictionary,
|
||||
async (req, res) => {
|
||||
const { dictionaryId, payload } = req.body.params
|
||||
|
||||
await Dictionary.updateDomainLabel(dictionaryId, payload)
|
||||
res.end()
|
||||
}
|
||||
await Dictionary.updateDomainLabel(dictionaryId, payload)
|
||||
res.end()
|
||||
}
|
||||
]
|
||||
|
||||
dictionary.renovateSecondaryDomains = async (req, res) => {
|
||||
const data = req.body.params.payload
|
||||
@@ -152,7 +168,11 @@ dictionary.listDictionaries = async (req, res) => {
|
||||
const page = +req.query.p > 0 ? +req.query.p : 1
|
||||
|
||||
const { pages_total: numberOfAllPages, results } =
|
||||
await Dictionary.fetchAllAdminDictionaries(resultsPerPage, page)
|
||||
await Dictionary.fetchAllAdminDictionaries(
|
||||
req.determinedLanguage,
|
||||
resultsPerPage,
|
||||
page
|
||||
)
|
||||
|
||||
res.send({ page, numberOfAllPages, results })
|
||||
}
|
||||
@@ -222,15 +242,6 @@ dictionary.listSecondaryDomainData = async (req, res) => {
|
||||
})
|
||||
}
|
||||
|
||||
dictionary.listSecondaryDomains = async (req, res) => {
|
||||
const resultsPerPage = req.user?.hitsPerPage || DEFAULT_HITS_PER_PAGE
|
||||
const page = +req.query.p > 0 ? +req.query.p : 1
|
||||
const { pages_total: numberOfAllPages, results } =
|
||||
await Dictionary.fetchAllSecondaryDomains(resultsPerPage, page)
|
||||
|
||||
res.send({ page, numberOfAllPages, results })
|
||||
}
|
||||
|
||||
dictionary.showImportFromFileForm = async (req, res) => {
|
||||
const resultsPerPage = req.user?.hitsPerPage || DEFAULT_HITS_PER_PAGE
|
||||
const { dictionaryId } = req.params
|
||||
@@ -252,7 +263,7 @@ dictionary.showExportToFileForm = async (req, res) => {
|
||||
}
|
||||
|
||||
dictionary.importFromExtraction = async (req, res) => {
|
||||
const { id: dictionaryId, extractionId } = req.params
|
||||
const { dictionaryId, extractionId } = req.params
|
||||
const { from, to } = req.body
|
||||
const fromIndex = +from > 1 ? Math.floor(from) - 1 : 0
|
||||
const toIndex = Number.isInteger(+(to === '' ? undefined : to))
|
||||
@@ -279,7 +290,7 @@ dictionary.importFromExtraction = async (req, res) => {
|
||||
}
|
||||
|
||||
dictionary.exportBegin = async (req, res) => {
|
||||
const dictionaryId = req.params.id
|
||||
const { dictionaryId } = req.params
|
||||
const exportParams = {
|
||||
isValidFilter:
|
||||
req.body.isValidFilter === 'on' ? undefined : req.body.isValidFilter,
|
||||
|
||||
@@ -166,7 +166,7 @@ extraction.ossSearch = [
|
||||
|
||||
extraction.ossConfirmParams = async (req, res) => {
|
||||
const { id: extractionId } = req.params
|
||||
const { ossParams } = await Extraction.fetch(extractionId)
|
||||
const { ossParams } = req.extractionData
|
||||
if (ossParams.status !== 'valid') throw Error('OSS params not valid')
|
||||
await Extraction.updateOssParams(extractionId, {
|
||||
params: ossParams.params,
|
||||
@@ -177,7 +177,7 @@ extraction.ossConfirmParams = async (req, res) => {
|
||||
|
||||
extraction.begin = async (req, res) => {
|
||||
const extractionId = req.params.id
|
||||
const extraction = await Extraction.fetch(extractionId)
|
||||
const extraction = req.extractionData
|
||||
const canBegin = await checkIfcanBegin(extraction)
|
||||
if (!canBegin) throw Error('Extraction does not qualify to be ran')
|
||||
|
||||
@@ -268,11 +268,6 @@ extraction.termCandidatesExport = async (req, res) => {
|
||||
}
|
||||
}
|
||||
|
||||
extraction.listFinishedForUser = async (req, res) => {
|
||||
const extractions = await Extraction.fetchFinishedForUser(req.user.id)
|
||||
res.send(extractions)
|
||||
}
|
||||
|
||||
extraction.listTermCandidates = async (req, res) => {
|
||||
const extractionId = req.params.id
|
||||
const termCandidatesJson = await Extraction.fetchTermCandidatesJson(
|
||||
|
||||
@@ -178,7 +178,8 @@ exports.listFilteredDictionaries = async (req, res) => {
|
||||
hitsPerPage,
|
||||
page,
|
||||
orderAttribute,
|
||||
orderIndex
|
||||
orderIndex,
|
||||
req.determinedLanguage
|
||||
)
|
||||
|
||||
dictionaries = dictionaries.map(e => {
|
||||
@@ -246,6 +247,9 @@ exports.showModalFilterResults = async (req, res) => {
|
||||
|
||||
const aggregationRaw = await searchEntryIndex(aggregateQuery)
|
||||
|
||||
const aggregation = await prepareAggregation(aggregationRaw)
|
||||
const aggregation = await prepareAggregation(
|
||||
aggregationRaw,
|
||||
req.determinedLanguage
|
||||
)
|
||||
res.send(prepareSeachFilterData(aggregation, filters))
|
||||
}
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
const debug = require('debug')('termPortal:controllers/api/v1/system')
|
||||
const Eurotermbank = require('../../../models/system/eurotermbank')
|
||||
// const debug = require('debug')('termPortal:controllers/api/v1/system')
|
||||
|
||||
exports.handleCspReports = (req, res) => {
|
||||
debug(req.body)
|
||||
res.sendStatus(200)
|
||||
// eslint-disable-next-line no-console
|
||||
console.error(req.body)
|
||||
res.end()
|
||||
}
|
||||
|
||||
exports.syncWithEurotermbank = async (req, res) => {
|
||||
|
||||
@@ -1,5 +1,14 @@
|
||||
const { randomBytes } = require('crypto')
|
||||
const { promisify } = require('util')
|
||||
const RandomBytesAsync = promisify(randomBytes)
|
||||
const User = require('../../../models/user')
|
||||
const { DEFAULT_HITS_PER_PAGE } = require('../../../config/settings')
|
||||
const email = require('../../../models/email')
|
||||
const { logout: logoutUser } = require('../../../middleware/user')
|
||||
const { origin } = require('../../../config/keys')
|
||||
const {
|
||||
DEFAULT_HITS_PER_PAGE,
|
||||
CHANGE_EMAIL_TOKEN_VALID_DAYS
|
||||
} = require('../../../config/settings')
|
||||
|
||||
const users = {}
|
||||
users.listUsers = async (req, res) => {
|
||||
@@ -23,13 +32,103 @@ users.updateHitsPerPage = async (req, res) => {
|
||||
res.status(200).send()
|
||||
}
|
||||
|
||||
users.updateFristNameAndSurname = async (req, res) => {
|
||||
const firstname = req.body.name
|
||||
const surname = req.body.surname
|
||||
users.updateBasicData = async (req, res) => {
|
||||
const { firstName, lastName, email: newEmail } = req.body
|
||||
|
||||
await User.updateFirstNameAndLastName(req.user.userName, firstname, surname)
|
||||
// TODO Validation (valid email format, ...).
|
||||
|
||||
res.status(200).send()
|
||||
const oldEmail = await User.updateFirstNameAndLastName(
|
||||
req.user.id,
|
||||
firstName,
|
||||
lastName
|
||||
)
|
||||
|
||||
if (newEmail === oldEmail) return res.send()
|
||||
|
||||
if (await User.isEmailAlreadyTaken(newEmail)) {
|
||||
req.flash('info', req.t('Elektronski naslov uporablja že drug uporabnik.'))
|
||||
return res.send()
|
||||
}
|
||||
|
||||
const changeEmailToken = (await RandomBytesAsync(32)).toString('hex')
|
||||
await User.saveChangeEmailToken(req.user.id, changeEmailToken, newEmail)
|
||||
let changeEmailLink = new URL('/sprememba-elektronskega-naslova', origin)
|
||||
changeEmailLink.searchParams.set('token', changeEmailToken)
|
||||
changeEmailLink = changeEmailLink.href
|
||||
const renderAsync = promisify(req.app.render.bind(req.app))
|
||||
const emailHtml = await renderAsync(
|
||||
`email/user-change-email-token_${req.language}`,
|
||||
{
|
||||
username: req.user.userName,
|
||||
changeEmailLink
|
||||
}
|
||||
)
|
||||
await email.send({
|
||||
to: newEmail,
|
||||
subject: req.t('Sprememba elektronskega naslova'),
|
||||
html: emailHtml
|
||||
})
|
||||
|
||||
const message =
|
||||
req.t(
|
||||
'Na vaš elektronski naslov smo vam poslali sporočilo s povezavo, s katero boste potrdili menjavo elektronskega naslova. Povezava za potrditev je veljavna '
|
||||
) +
|
||||
`${CHANGE_EMAIL_TOKEN_VALID_DAYS} ` +
|
||||
req.t('dni.')
|
||||
|
||||
req.flash('info', message)
|
||||
res.send()
|
||||
}
|
||||
|
||||
users.updatePassword = async (req, res) => {
|
||||
const { passwordOld, passwordNew, passwordNewRepeat } = req.body
|
||||
|
||||
// TODO Validation (mirror front end validation, ...).
|
||||
if (passwordNew !== passwordNewRepeat) {
|
||||
const err = Error(req.t('Gesli se ne ujemata'))
|
||||
err.status = 403
|
||||
err.displayInProd = true
|
||||
|
||||
throw err
|
||||
}
|
||||
|
||||
await User.changePassword(req.user.id, passwordOld, passwordNew, req.t)
|
||||
|
||||
// TODO Invalidate or log out all session for this user. More details in deleteCurrent method TODO.
|
||||
|
||||
const renderAsync = promisify(req.app.render.bind(req.app))
|
||||
const emailHtml = await renderAsync(
|
||||
`email/user-change-password_${req.language}`,
|
||||
{
|
||||
username: req.user.userName
|
||||
}
|
||||
)
|
||||
await email.send({
|
||||
to: req.user.email,
|
||||
subject: req.t('Sprememba gesla'),
|
||||
html: emailHtml
|
||||
})
|
||||
|
||||
req.flash('info', 'Geslo je bilo spremenjeno.')
|
||||
res.send()
|
||||
}
|
||||
|
||||
users.deleteCurrent = [
|
||||
async (req, res, next) => {
|
||||
await User.closeAccount(req.user.id)
|
||||
next()
|
||||
},
|
||||
logoutUser,
|
||||
(req, res) => {
|
||||
// TODO Invalidate or log out all session for this user. Current workaround is in passport.deserializeUser.
|
||||
// You can probably do it in 1 of 3 ways:
|
||||
// 1. Brute force; loop through all sessions (using session store's all or ids methods),
|
||||
// look up their values and remote the ones with user's id
|
||||
// 2. Include user Id as part of session key; something similar to https://github.com/tj/connect-redis/issues/210#issuecomment-1336545115
|
||||
// 3. Create some kind of inverse index, mapping user id to his/hers sessions (also mentioned in issue linked above)
|
||||
req.flash('info', req.t('Vaš uporabniški račun je bil uspešno izbrisan.'))
|
||||
res.send()
|
||||
}
|
||||
]
|
||||
|
||||
module.exports = users
|
||||
|
||||
Reference in New Issue
Block a user