diff --git a/.gitignore b/.gitignore index d7e2fb92f..a31927462 100644 --- a/.gitignore +++ b/.gitignore @@ -31,6 +31,7 @@ node_modules #custom package-lock.json config.json +config.yml import.csv /src/mailer/templates/promo/ *.pem diff --git a/.yarnclean b/.yarnclean index 0446e553a..2e7df6036 100644 --- a/.yarnclean +++ b/.yarnclean @@ -6,7 +6,7 @@ powered-test # asset directories docs -doc +#doc website # examples diff --git a/app.js b/app.js index 423297e9e..8e070138d 100644 --- a/app.js +++ b/app.js @@ -46,7 +46,7 @@ if (!process.env.FORK) { winston.info('Server Time: ' + new Date()) } -let configFile = path.join(__dirname, '/config.json') +let configFile = path.join(__dirname, '/config.yml') nconf.defaults({ base_dir: __dirname, @@ -60,6 +60,9 @@ if (nconf.get('config')) { configFile = path.resolve(__dirname, nconf.get('config')) } +// Make sure we convert the .json file to .yml +checkForOldConfig() + const configExists = fs.existsSync(configFile) function launchInstallServer () { @@ -75,12 +78,28 @@ if (nconf.get('install') || (!configExists && !isDocker)) { function loadConfig () { nconf.file({ - file: configFile + file: configFile, + format: require('nconf-yaml') }) } +function checkForOldConfig() { + const oldConfigFile = path.join(__dirname, '/config.json') + if (fs.existsSync(oldConfigFile)) { + // Convert config to yaml. + const content = fs.readFileSync(oldConfigFile) + const YAML = require('yaml') + const data = JSON.parse(content) + + fs.writeFileSync(configFile, YAML.stringify(data)) + + // Rename the old config.json to config.json.bk + fs.renameSync(oldConfigFile, path.join(__dirname, '/config.json.bk')) + } +} + function start () { - if (!isDocker) loadConfig() + if (!isDocker)loadConfig() const _db = require('./src/database') diff --git a/kubernetes/configmap.yaml b/kubernetes/configmap.yaml index 2c052649a..2c2433e43 100644 --- a/kubernetes/configmap.yaml +++ b/kubernetes/configmap.yaml @@ -1,13 +1,13 @@ apiVersion: v1 kind: ConfigMap metadata: - name: trudesk-config-json + name: trudesk-config-yml namespace: default data: - config.json: |- + config.yml: |- { "tokens": { "secret": "SECRET_USED_TO_HASH" } - } \ No newline at end of file + } diff --git a/kubernetes/trudesk-deployment.yaml b/kubernetes/trudesk-deployment.yaml index bbf4d5084..66bc3a3a2 100644 --- a/kubernetes/trudesk-deployment.yaml +++ b/kubernetes/trudesk-deployment.yaml @@ -58,10 +58,10 @@ spec: name: trudesk-uploads - mountPath: /usr/src/trudesk/backups name: trudesk-backups - - mountPath: /usr/src/trudesk/config.json - name: trudesk-config-json + - mountPath: /usr/src/trudesk/config.yml + name: trudesk-config-yml readOnly: true - subPath: config.json + subPath: config.yml restartPolicy: Always volumes: - name: trudesk-uploads @@ -70,9 +70,9 @@ spec: - name: trudesk-backups persistentVolumeClaim: claimName: trudesk-backups - - name: trudesk-config-json + - name: trudesk-config-yml configMap: - name: trudesk-config-json + name: trudesk-config-yml status: {} diff --git a/package.json b/package.json index 115c9add7..a4201ff50 100644 --- a/package.json +++ b/package.json @@ -76,6 +76,7 @@ "mongoose-autopopulate": "0.16.0", "mongoose-lean-virtuals": "0.9.0", "nconf": "0.12.0", + "nconf-yaml": "1.0.2", "netmask": "2.0.2", "node-cache": "5.1.2", "node-sass": "7.0.1", @@ -121,7 +122,8 @@ "util": "0.12.4", "velocity-react": "1.4.3", "winston": "3.6.0", - "xss": "1.0.10" + "xss": "1.0.10", + "yaml": "2.1.1" }, "devDependencies": { "@babel/core": "7.17.4", diff --git a/src/cache/cache.js b/src/cache/cache.js index 4a42084bc..b619d05fe 100644 --- a/src/cache/cache.js +++ b/src/cache/cache.js @@ -27,6 +27,10 @@ cache.init = function () { setInterval(spawnCache, 55 * 60 * 1000) } +cache.forceRefresh = function() { + spawnCache() +} + function spawnCache () { const fork = require('child_process').fork @@ -35,6 +39,8 @@ function spawnCache () { env: cache.env }) + cache.fork = n + global.forks.push({ name: 'cache', fork: n }) n.on('message', function (data) { diff --git a/src/cache/index.js b/src/cache/index.js index bf402e82d..29cba02d5 100644 --- a/src/cache/index.js +++ b/src/cache/index.js @@ -27,7 +27,8 @@ global.env = process.env.NODE_ENV || 'production' function loadConfig () { nconf.file({ - file: path.join(__dirname, '/../../config.json') + file: path.join(__dirname, '/../../config.yml'), + format: require('nconf-yaml') }) nconf.defaults({ diff --git a/src/controllers/api/v1/routes.js b/src/controllers/api/v1/routes.js index ed9dc839a..ba2a45f53 100644 --- a/src/controllers/api/v1/routes.js +++ b/src/controllers/api/v1/routes.js @@ -122,6 +122,7 @@ module.exports = function (middleware, router, controllers) { router.delete('/api/v1/groups/:id', apiv1, canUser('groups:delete'), apiCtrl.groups.deleteGroup) // Users + router.put('/api/v1/profile', apiv1, apiCtrl.users.profileUpdate) router.get('/api/v1/users', apiv1, canUser('accounts:view'), apiCtrl.users.getWithLimit) router.post('/api/v1/users/create', apiv1, canUser('accounts:create'), apiCtrl.users.create) router.get('/api/v1/users/notifications', apiv1, apiCtrl.users.getNotifications) diff --git a/src/controllers/api/v1/users.js b/src/controllers/api/v1/users.js index 11f2aae64..3fa2bb168 100644 --- a/src/controllers/api/v1/users.js +++ b/src/controllers/api/v1/users.js @@ -384,6 +384,104 @@ apiUsers.createPublicAccount = function (req, res) { ) } +apiUsers.profileUpdate = function (req, res) { + if (!req.user) return res.status(400).json({ success: false, error: 'Invalid Post Data' }) + const username = req.user.username + if (_.isNull(username) || _.isUndefined(username)) + return res.status(400).json({ success: false, error: 'Invalid Post Data' }) + + const data = req.body + let passwordUpdated = false + + const obj = { + fullname: data.aFullname, + title: data.aTitle, + password: data.aPassword, + passconfirm: data.aPassConfirm, + email: data.aEmail + } + + let passwordComplexityEnabled = true + + async.series( + { + settings: function (done) { + const SettingUtil = require('../../../settings/settingsUtil') + SettingUtil.getSettings(function (err, content) { + if (err) return done(err) + + const settings = content.data.settings + passwordComplexityEnabled = settings.accountsPasswordComplexity.value + + return done() + }) + }, + user: function (done) { + UserSchema.getUserByUsername(username, function (err, user) { + if (err) return done(err) + if (!user) return done('Invalid User Object') + + obj._id = user._id + + if ( + !_.isUndefined(obj.password) && + !_.isEmpty(obj.password) && + !_.isUndefined(obj.passconfirm) && + !_.isEmpty(obj.passconfirm) + ) { + if (obj.password === obj.passconfirm) { + if (passwordComplexityEnabled) { + // check Password Complexity + const passwordComplexity = require('../../../settings/passwordComplexity') + if (!passwordComplexity.validate(obj.password)) return done('Password does not meet requirements') + } + + user.password = obj.password + passwordUpdated = true + } + } + + if (!_.isUndefined(obj.fullname) && obj.fullname.length > 0) user.fullname = obj.fullname + if (!_.isUndefined(obj.email) && obj.email.length > 0) user.email = obj.email + if (!_.isUndefined(obj.title) && obj.title.length > 0) user.title = obj.title + + user.save(function (err, nUser) { + if (err) return done(err) + + nUser.populate('role', function (err, populatedUser) { + if (err) return done(err) + const resUser = stripUserFields(populatedUser) + + return done(null, resUser) + }) + }) + }) + }, + groups: function (done) { + groupSchema.getAllGroupsOfUser(obj._id, done) + } + }, + async function (err, results) { + if (err) { + winston.debug(err) + return res.status(400).json({ success: false, error: err }) + } + + const user = results.user.toJSON() + user.groups = results.groups.map(function (g) { + return { _id: g._id, name: g.name } + }) + + if (passwordUpdated) { + const Session = require('../../../models/session') + await Session.destroy(user._id) + } + + return res.json({ success: true, user: user }) + } + ) +} + /** * @api {put} /api/v1/users/:username Update User * @apiName updateUser diff --git a/src/controllers/install.js b/src/controllers/install.js index 769952b91..3236722f9 100644 --- a/src/controllers/install.js +++ b/src/controllers/install.js @@ -91,8 +91,8 @@ installController.existingdb = function (req, res) { // Write Configfile const fs = require('fs') const chance = new Chance() - const configFile = path.join(__dirname, '../../config.json') - + const configFile = path.join(__dirname, '../../config.yml') + const YAML = require('yaml') const conf = { mongo: { host: host, @@ -107,7 +107,7 @@ installController.existingdb = function (req, res) { } } - fs.writeFile(configFile, JSON.stringify(conf, null, 4), function (err) { + fs.writeFile(configFile, YAML.stringify(conf), function (err) { if (err) { winston.error('FS Error: ' + err.message) return res.status(400).json({ success: false, error: err.message }) @@ -404,8 +404,9 @@ installController.install = function (req, res) { if (process.env.TRUDESK_DOCKER) return next() // Write Configfile const fs = require('fs') - const configFile = path.join(__dirname, '../../config.json') + const configFile = path.join(__dirname, '../../config.yml') const chance = new Chance() + const YAML = require('yaml') const conf = { mongo: { @@ -422,7 +423,7 @@ installController.install = function (req, res) { } } - fs.writeFile(configFile, JSON.stringify(conf, null, 4), function (err) { + fs.writeFile(configFile, YAML.stringify(conf), function (err) { if (err) { winston.error('FS Error: ' + err.message) return next('FS Error: ' + err.message) diff --git a/src/public/js/angularjs/controllers/profile.js b/src/public/js/angularjs/controllers/profile.js index 704ec532c..898c08848 100644 --- a/src/public/js/angularjs/controllers/profile.js +++ b/src/public/js/angularjs/controllers/profile.js @@ -74,7 +74,7 @@ define([ } $http - .put('/api/v1/users/' + data.username, { + .put('/api/v1/profile/', { aId: id, aFullname: data.fullname, aPass: data.password, diff --git a/src/routes/index.js b/src/routes/index.js index 54a84c277..f9b2dd588 100644 --- a/src/routes/index.js +++ b/src/routes/index.js @@ -405,12 +405,9 @@ function mainRoutes (router, middleware, controllers) { }) router.get('/debug/cache/refresh', function (req, res) { - const _ = require('lodash') - - const forkProcess = _.find(global.forks, { name: 'cache' }) - forkProcess.fork.send({ name: 'cache:refresh' }) - - res.send('OK') + const cache = require('../cache/cache.js') + cache.forceRefresh() + return res.send('OK') }) router.get('/debug/restart', function (req, res) { diff --git a/src/settings/defaults.js b/src/settings/defaults.js index 3e1e9cb37..49eb455c1 100644 --- a/src/settings/defaults.js +++ b/src/settings/defaults.js @@ -592,11 +592,11 @@ function elasticSearchConfToDB (callback) { const nconf = require('nconf') const elasticsearch = { enable: nconf.get('elasticsearch:enable') || false, - host: nconf.get('elasticsearch:host'), - port: nconf.get('elasticsearch:port') + host: nconf.get('elasticsearch:host') || "", + port: nconf.get('elasticsearch:port') || 9200 } - nconf.set('elasticsearch', undefined) + nconf.set('elasticsearch', {}) async.parallel( [ diff --git a/yarn.lock b/yarn.lock index 7e10022fe..2cd094dec 100644 --- a/yarn.lock +++ b/yarn.lock @@ -8800,7 +8800,7 @@ js-yaml@4.1.0, js-yaml@^4.1.0: dependencies: argparse "^2.0.1" -js-yaml@^3.13.1, js-yaml@~3.14.0: +js-yaml@^3.13.1, js-yaml@^3.2.3, js-yaml@~3.14.0: version "3.14.1" resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.14.1.tgz#dae812fdb3825fa306609a8717383c50c36a0537" integrity sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g== @@ -10592,6 +10592,13 @@ natural-compare@^1.4.0: resolved "https://registry.yarnpkg.com/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7" integrity sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc= +nconf-yaml@1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/nconf-yaml/-/nconf-yaml-1.0.2.tgz#fea065333cf42b77a5e8060517969799d4156575" + integrity sha1-/qBlMzz0K3el6AYFF5aXmdQVZXU= + dependencies: + js-yaml "^3.2.3" + nconf@0.12.0: version "0.12.0" resolved "https://registry.yarnpkg.com/nconf/-/nconf-0.12.0.tgz#9cf70757aae4d440d43ed53c42f87da18471b8bf" @@ -15977,6 +15984,11 @@ yaml@1.8.3: dependencies: "@babel/runtime" "^7.8.7" +yaml@2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/yaml/-/yaml-2.1.1.tgz#1e06fb4ca46e60d9da07e4f786ea370ed3c3cfec" + integrity sha512-o96x3OPo8GjWeSLF+wOAbrPfhFOGY0W00GNaxCDv+9hkcDJEnev1yh8S7pgHF0ik6zc8sQLuL8hjHjJULZp8bw== + yaml@^1.10.0, yaml@^1.10.2, yaml@^1.7.1: version "1.10.2" resolved "https://registry.yarnpkg.com/yaml/-/yaml-1.10.2.tgz#2301c5ffbf12b467de8da2333a459e29e7920e4b"