diff --git a/src/app.ts b/src/app.ts index b09c7a6031e06391a8012c00273e54a5f1c2babb..e6d5770c86cecf1b08c301dab08272ec2b385a68 100644 --- a/src/app.ts +++ b/src/app.ts @@ -11,13 +11,13 @@ import helmet from 'helmet' import compression from 'compression' import methodOverride from 'method-override' -const env = process.env.NODE_ENV || 'development' +const env = process.env.NODE_ENV ?? 'testing' const config = require('./config/config')[env] const lang = 'DE' const app = express() app.set('port', config.app.port) -app.set('views', path.join(__dirname + '/views')) +app.set('views', path.join(path.join(__dirname, '/views'))) app.set('view engine', 'pug') // enable files upload diff --git a/src/classes/project.ts b/src/classes/project.ts index 325713c1fb3a87ee620f0253dcfa10b987f52b65..e6ad4d8990a07cb50fbe75da5d075cfc89fcecdf 100644 --- a/src/classes/project.ts +++ b/src/classes/project.ts @@ -66,4 +66,4 @@ class Project { } } -export = Project +export { Project } diff --git a/src/classes/repo.ts b/src/classes/repo.ts index c8c6db93581d922608d0666fe386b46b1b5246e7..e35a8fb72e6a71676ad58c0d5546e23814285937 100644 --- a/src/classes/repo.ts +++ b/src/classes/repo.ts @@ -1,4 +1,4 @@ -import Project from './project' +import { Project } from './project' class Repo extends Project { constructor (ownerGitlabId: number, name: string, desc: string, id?: number, logo?: string, path?: string) { @@ -6,4 +6,4 @@ class Repo extends Project { } } -export = Repo +export { Repo } diff --git a/src/classes/user.ts b/src/classes/user.ts index d25196f91f05d1acf4b34b9fa356f73165c3e527..554e248fed55aeafc2cc894b9ecaa1438f9b85ac 100644 --- a/src/classes/user.ts +++ b/src/classes/user.ts @@ -8,12 +8,12 @@ class User { industry: string organisation: string speciality: string - is_m4lab_idp: number // 1 or 0 + isM4labIdp: number // 1 or 0 verificationStatus: number // 1 or 0 - // should be boolean gitlabUserId?: number constructor (id: number, email: string, salutation: string, title: string, firstName: string, lastName: string, industry: string, organisation: string, - speciality: string, is_m4lab_idp: number, verificationStatus: number, gitlabUserId?: number) { + speciality: string, isM4labIdp: number, verificationStatus: number, gitlabUserId?: number) { this.id = id this.email = email this.salutation = salutation @@ -23,7 +23,7 @@ class User { this.industry = industry this.organisation = organisation this.speciality = speciality - this.is_m4lab_idp = is_m4lab_idp + this.isM4labIdp = isM4labIdp this.verificationStatus = verificationStatus this.gitlabUserId = gitlabUserId } @@ -42,7 +42,7 @@ class User { } getIdpStatus () { - return this.is_m4lab_idp + return this.isM4labIdp } getVerificationStatus () { @@ -86,8 +86,8 @@ class User { this.speciality = speciality } - setM4lab_idp (m4lab_idp: number) { - this.is_m4lab_idp = m4lab_idp + setM4lab_idp (m4labIdp: number) { + this.isM4labIdp = m4labIdp } setVerificationStatus (verificationStatus: number) { @@ -110,4 +110,4 @@ class User { } } -export = User +export { User } diff --git a/src/classes/website.ts b/src/classes/website.ts index 0cd6f0026e71658140c2d09af9b4d322450d978e..6a07f9287c31ec80319e63070bfde8e8bec8dd7c 100644 --- a/src/classes/website.ts +++ b/src/classes/website.ts @@ -1,4 +1,4 @@ -import Project from './project' +import { Project } from './project' class Website extends Project { constructor (ownerGitlabId: number, name: string, desc: string, id?: number, logo?: string, path?: string) { @@ -6,4 +6,4 @@ class Website extends Project { } } -export = Website +export { Website } diff --git a/src/config/config.ts b/src/config/config.ts index 71a1454f77a74670c2728984cd4daaa1fc726eb3..623d558a93e263af1d97eb73ee6802e0364a7dbe 100644 --- a/src/config/config.ts +++ b/src/config/config.ts @@ -1,15 +1,15 @@ -export = { +module.exports = { development: { app: { name: 'User Account Management', - port: process.env.PORT || 9989, + port: process.env.PORT ?? 9989, host: 'http://localhost:9989', sessionSecret: 'thisisasecret' }, passport: { strategy: 'saml', saml: { - path: process.env.SAML_PATH || '/saml/SSO', + path: process.env.SAML_PATH ?? '/saml/SSO', entryPoint: process.env.SAML_ENTRY_POINT || 'saml entry point', issuer: 'SAML issuer', logoutUrl: 'SAML logout URL' @@ -41,15 +41,15 @@ export = { testing: { app: { name: 'User Account Management', - port: process.env.PORT || 9989, + port: process.env.PORT ?? 9989, host: 'https://m4lab.hft-stuttgart.de/account', sessionSecret: 'thisisasecret' }, passport: { strategy: 'saml', saml: { - path: process.env.SAML_PATH || '/saml/SSO', - entryPoint: process.env.SAML_ENTRY_POINT || 'saml entry point', + path: process.env.SAML_PATH ?? '/saml/SSO', + entryPoint: process.env.SAML_ENTRY_POINT ?? 'saml entry point', issuer: 'SAML issuer', // testing metadata logoutUrl: 'SAML logout URL' } @@ -78,3 +78,5 @@ export = { } } } + +export {} diff --git a/src/config/const.ts b/src/config/const.ts index b8d625aa42a2a3375945f960fd1039a3e148d2dc..191275b7662068438c4f8c9698bc3ebe3c89770c 100644 --- a/src/config/const.ts +++ b/src/config/const.ts @@ -1,10 +1,10 @@ -export = { +const miscConst = { mailSignature: 'Mit den besten Grüßen,<br/>das Transferportal-Team der HFT Stuttgart<br/><br/>' + 'Transferportal der Hochschule für Technik Stuttgart<br/>' + 'Schellingstr. 24 70174 Stuttgart<br/>' + 'm4lab@hft-stuttgart.de<br/>' + - '<a href="https://transfer.hft-stuttgart.de">https://transfer.hft-stuttgart.de</a><br/>' + + '<a mailConsthref="https://transfer.hft-stuttgart.de">https://transfer.hft-stuttgart.de</a><br/>' + '<a href="http://www.hft-stuttgart.de/Aktuell/"><img border="0" alt="HFT" src="https://m4lab.hft-stuttgart.de/img/signature/hft_logo.png" width="30" height="30"></a> ' + '<a href="http://www.facebook.com/hftstuttgart"><img border="0" alt="Facebook" src="https://m4lab.hft-stuttgart.de/img/signature/fb_bw.png" width="30" height="30"></a> ' + '<a href="https://www.instagram.com/hft_stuttgart/"><img border="0" alt="Instagram" src="https://m4lab.hft-stuttgart.de/img/signature/instagram_bw.png" width="30" height="30"></a> ' + @@ -17,3 +17,5 @@ export = { updatePasswordMailContent: '<div>Lieber Nutzer,<br/><br/>Ihr Passwort wurde erfolgreich geändert.<br/><br/></div>' } + +export { miscConst } diff --git a/src/config/dbconn.ts b/src/config/dbconn.ts index ee10a38313c580c4476af38d050e757e9e9aca46..2b0caa6d3f2952e0710123f594c49ee8e707da08 100644 --- a/src/config/dbconn.ts +++ b/src/config/dbconn.ts @@ -1,6 +1,6 @@ import mysql from 'mysql2' -const env = process.env.NODE_ENV || 'development' +const env = process.env.NODE_ENV ?? 'development' const config = require('./config')[env] // ==== USER ACOOUNT DB CONNECTION ==== @@ -31,9 +31,9 @@ const projectConnection = mysql.createPool({ projectConnection.query('USE ' + config.database.dbProject) -const connection = { +const dbConnection = { user: userConnection, project: projectConnection } -export = connection +export { dbConnection } diff --git a/src/config/mailer.ts b/src/config/mailer.ts index 1c528ae0e9b8979a912f79ce6767c99e77bc4a89..1b88cf93c85d0536d593d15566c07464f896d70b 100644 --- a/src/config/mailer.ts +++ b/src/config/mailer.ts @@ -1,7 +1,7 @@ const nodemailer = require('nodemailer') const nodemailerNTLMAuth = require('nodemailer-ntlm-auth') -const env = process.env.NODE_ENV || 'testing' +const env = process.env.NODE_ENV ?? 'testing' const config = require('./config')[env] const smtpTransporter = nodemailer.createTransport({ @@ -37,4 +37,4 @@ const mailer: any = { options: mailOptions } -export = mailer +export { mailer } diff --git a/src/controller/accountController.ts b/src/controller/accountController.ts deleted file mode 100644 index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..0000000000000000000000000000000000000000 diff --git a/src/controller/dbController.ts b/src/controller/dbController.ts index 2162f10cc21797442ba260032d1d667949410387..12905c073fa58f461be602c190199436264a4f63 100644 --- a/src/controller/dbController.ts +++ b/src/controller/dbController.ts @@ -1,9 +1,9 @@ -import dbconn = require('../config/dbconn') +import { dbConnection } from '../config/dbconn' const dbController = { // ===================== user db ===================== registerNewUser: function (data: any, callback: any) { - dbconn.user.getConnection(function (err: any, thisconn) { + dbConnection.user.getConnection(function (err: any, thisconn) { thisconn.beginTransaction(function (err: any) { // START TRANSACTION if (err) { throw err } // insert profile @@ -66,7 +66,7 @@ const dbController = { }, getUserByEmail: async function (email: any) { try { - const rows: any = await dbconn.user.promise().query('SELECT id, verificationStatus, salutation, title, firstname, lastname, industry, organisation, speciality, m4lab_idp FROM user WHERE email = "' + email + '"') + const rows: any = await dbConnection.user.promise().query('SELECT id, verificationStatus, salutation, title, firstname, lastname, industry, organisation, speciality, m4lab_idp FROM user WHERE email = "' + email + '"') if (rows[0][0]) { return rows[0][0] } else { return null } @@ -77,7 +77,7 @@ const dbController = { }, getUserEmailById: async function (userId: number) { try { - const rows: any = await dbconn.user.promise().query('SELECT email FROM user WHERE id = ' + userId) + const rows: any = await dbConnection.user.promise().query('SELECT email FROM user WHERE id = ' + userId) if (rows[0][0]) { return rows[0][0].email } else { return null } @@ -88,7 +88,7 @@ const dbController = { }, checkUserEmail: async function (email: any) { try { - const rows: any = await dbconn.user.promise().query('SELECT id, email FROM user WHERE email = "' + email + '"') + const rows: any = await dbConnection.user.promise().query('SELECT id, email FROM user WHERE email = "' + email + '"') if (rows[0][0]) { return rows[0][0] } else { return null } @@ -99,7 +99,7 @@ const dbController = { }, getUserByToken: async function (token: any) { try { - const rows: any = await dbconn.user.promise().query('SELECT t1.user_id, t2.email FROM userdb.credential AS t1 INNER JOIN userdb.user AS t2 ON t1.user_id = t2.id AND t1.resetPasswordToken = "' + + const rows: any = await dbConnection.user.promise().query('SELECT t1.user_id, t2.email FROM userdb.credential AS t1 INNER JOIN userdb.user AS t2 ON t1.user_id = t2.id AND t1.resetPasswordToken = "' + token + '" and resetPasswordExpires > ' + Date.now()) if (rows[0][0]) { return rows[0][0] @@ -111,7 +111,7 @@ const dbController = { }, updateUserById: async function (userId: number, userData: any) { try { - const result: any = await dbconn.user.promise().query('UPDATE user SET ? WHERE id = ' + userId, userData) + const result: any = await dbConnection.user.promise().query('UPDATE user SET ? WHERE id = ' + userId, userData) return result } catch (err) { console.error(err) @@ -120,7 +120,7 @@ const dbController = { }, updateCredential: async function (data: any) { try { - const result: any = await dbconn.user.promise().query('UPDATE credential SET ? WHERE user_id = ' + data.user_id, data) + const result: any = await dbConnection.user.promise().query('UPDATE credential SET ? WHERE user_id = ' + data.user_id, data) return result } catch (err) { console.error(err) @@ -128,14 +128,14 @@ const dbController = { return null }, addUserProjectRole_OBSOLETE: function (data: any, callback: any) { - dbconn.user.query('INSERT INTO user_project_role SET ?', data, function (err: any) { + dbConnection.user.query('INSERT INTO user_project_role SET ?', data, function (err: any) { if (err) throw err callback(err) }) }, getVerificationTokenByUserId: async function (userId: number) { try { - const rows: any = await dbconn.user.promise().query('SELECT token FROM verification WHERE user_id = "' + userId + '"') + const rows: any = await dbConnection.user.promise().query('SELECT token FROM verification WHERE user_id = "' + userId + '"') if (rows[0][0]) { return rows[0][0].token } else { return null } @@ -146,7 +146,7 @@ const dbController = { }, getUserIdByVerificationToken: async function (token: any) { try { - const rows: any = await dbconn.user.promise().query('SELECT user_id FROM verification WHERE token = "' + token + '"') + const rows: any = await dbConnection.user.promise().query('SELECT user_id FROM verification WHERE token = "' + token + '"') if (rows[0][0]) { return rows[0][0].user_id } else { @@ -158,7 +158,7 @@ const dbController = { return null }, verifyUserAccount: function (userData: any, callback: any) { - dbconn.user.getConnection(function (err: any, thisconn) { + dbConnection.user.getConnection(function (err: any, thisconn) { thisconn.beginTransaction(function (err: any) { // START TRANSACTION if (err) { throw err } // update user status @@ -186,7 +186,7 @@ const dbController = { /* ===== GitLab ===== */ getGitlabId: async function (userId: number) { try { - const rows: any = await dbconn.user.promise().query('SELECT gu.gitlab_userId FROM user_gitlab gu, user u WHERE u.id = "' + userId + '" and gu.user_id = u.id') + const rows: any = await dbConnection.user.promise().query('SELECT gu.gitlab_userId FROM user_gitlab gu, user u WHERE u.id = "' + userId + '" and gu.user_id = u.id') if (rows[0][0]) { return rows[0][0].gitlab_userId } else { @@ -198,7 +198,7 @@ const dbController = { } }, addGitlabUser: function (data: any, callback: any) { - dbconn.user.query('INSERT INTO user_gitlab SET ?', data, function (err: any) { + dbConnection.user.query('INSERT INTO user_gitlab SET ?', data, function (err: any) { if (err) throw err callback(err) }) diff --git a/src/controller/gitlabController.ts b/src/controller/gitlabController.ts index 647f2a4f0cfd56089ddc25378866bcd51bb54b0c..583a6a5d73d50b0a58c3d6e50c798a7c449c943a 100644 --- a/src/controller/gitlabController.ts +++ b/src/controller/gitlabController.ts @@ -2,7 +2,7 @@ import axios from 'axios' import fs from 'fs' import formData from 'form-data' -const env = process.env.NODE_ENV || 'testing' +const env = process.env.NODE_ENV ?? 'testing' const config = require('../config/config')[env] const gitlabController = { @@ -124,4 +124,4 @@ const gitlabController = { } } -export {gitlabController} +export { gitlabController } diff --git a/src/controller/publicController.ts b/src/controller/publicController.ts index fa52e672eb603bf1228e097c691c2b746dcbc396..0035aee9c5843d61e40b8d459abf17316651753f 100644 --- a/src/controller/publicController.ts +++ b/src/controller/publicController.ts @@ -1,24 +1,26 @@ import async from 'async' -import mailer from '../config/mailer' +import bcrypt from 'bcryptjs' +import { mailer } from '../config/mailer' +import { miscConst } from '../config/const' import { dbController } from './dbController' -const lang = 'DE' +const saltRounds: number = 10 const publicController = { - showRegistrationPage: function (res: any) { + showRegistrationPage: function (res: any, lang: String) { res.render(lang + '/account/registration') }, - showContactPage: function (req: any, res: any) { + showContactPage: function (req: any, res: any, lang: String) { res.render(lang + '/account/contact', { user: req.user }) }, - showForgotPwdPage: function (req: any, res: any) { + showForgotPwdPage: function (req: any, res: any, lang: String) { res.render(lang + '/account/forgotPwd', { user: req.user }) }, - showResetToken: async function (req: any, res: any) { + showResetPassword: async function (req: any, res: any, lang: String) { const user = await dbController.getUserByToken(req.params.token) if (user) { res.render(lang + '/account/reset') @@ -53,6 +55,221 @@ const publicController = { res.redirect('/account/contact') }) }, + registerUser: function (req: any, res: any, config: any) { + // user data + const curDate: Date = new Date() + const userData: any = { + salutation: req.body.inputSalutation, + title: req.body.inputTitle, + firstname: req.body.inputFirstname, + lastname: req.body.inputLastname, + email: req.body.inputEmail, + organisation: req.body.inputOrganisation, + industry: req.body.inputIndustry, + speciality: req.body.inputSpeciality, + createdDate: curDate.toISOString().slice(0, 10) + } + + const userEmail: any = userData.email + const pos: number = userEmail.indexOf('@') + const emailLength: number = userEmail.length + const emailDomain: any = userEmail.slice(pos, emailLength) + + if (emailDomain.toLowerCase() === '@hft-stuttgart.de') { + res.flash('error', 'Fehlgeschlagen: HFT-Account') + res.redirect('/account/registration') + } else { + async.waterfall([ + function (done: any) { + // generate token + let token: string = '' + const randomChars: string = 'abcdefghijklmnopqrstuvwxyz0123456789' + for (let i = 0; i < 40; i++) { + token += randomChars.charAt(Math.floor(Math.random() * randomChars.length)) + } + // encrypt password + bcrypt.genSalt(saltRounds, function (err, salt) { + bcrypt.hash(req.body.inputPassword, salt, function (err: any, hash: any) { + const newAccount: any = { + profile: userData, + password: hash, + verificationToken: token + } + done(err, newAccount) + }) + }) + }, + // save data + function (newAccount: any, err: any) { + dbController.registerNewUser(newAccount, function (err: any) { + if (err) { + res.flash('error', 'Fehlgeschlagen') + } else { + // send email + const emailSubject = 'Bitte bestätigen Sie Ihr M4_LAB Benutzerkonto' + const emailContent = '<div>Lieber Nutzer,<br/><br/>' + + '<p>vielen Dank für Ihre Anmeldung am Transferportal der HFT Stuttgart. <br/>' + + 'Um Ihre Anmeldung zu bestätigen, klicken Sie bitte <a href=' + String(config.app.host) + '/verifyAccount?token=' + String(newAccount.verificationToken) + '>diesen Link</a> ' + + '<br/><br/>' + + 'Ohne Bestätigung Ihres Kontos müssen wir Ihr Konto leider nach 7 Tagen löschen.</p><br/>' + String(miscConst.mailSignature) + + '</div>' + mailer.options.to = req.body.inputEmail + mailer.options.subject = emailSubject + mailer.options.html = emailContent + mailer.transporter.sendMail(mailer.options, function (err: any) { + if (err) { + console.error('Cannot send email. [Error] ' + String(err)) + throw err + } + }) + // user feedback + res.flash('success', 'Vielen Dank für Ihre Registrierung!' + '\r\n\r\n' + + 'Wir haben Ihnen eine E-Mail an Ihre verwendete Adresse gesendet. Diese enthält einen Link zur Bestätigung Ihres Accounts.' + '\r\n' + + 'Wenn Sie die Mail nicht in ihrem Postfach vorfinden, prüfen Sie bitte auch Ihren Spam-Ordner.') + } + res.redirect('/account/registration') + }) + } + ]) + } + }, + verifyAccount: async function (req: any, res: any, lang: String) { + const userId: number = await dbController.getUserIdByVerificationToken(req.query.token) + if (!userId) { + // no user found + res.render(lang + '/account/verification', { + status: null + }) + } else { + // a user found, verify the account + const userData: any = { + id: userId, + verificationStatus: 1 + } + dbController.verifyUserAccount(userData, async function (err: any) { + if (err) { + console.error(err) + res.render(lang + '/account/verification', { + status: false + }) + } else { + // send welcome email after successful account verification + const userEmail: string = await dbController.getUserEmailById(userId) + if (!userEmail) { + res.render(lang + '/account/verification', { + status: false + }) + } else { + // send email + const emailSubject = 'Herzlich willkommen' + const emailContent = '<div>Lieber Nutzer,<br/><br/>' + + '<p>herzlich willkommen beim Transferportal der HFT Stuttgart!<br/>' + + 'Sie können nun alle Dienste des Portals nutzen.<p/><br/>' + miscConst.mailSignature + mailer.options.to = userEmail + mailer.options.subject = emailSubject + mailer.options.html = emailContent + mailer.transporter.sendMail(mailer.options, function (err: any) { + if (err) { + console.log('cannot send email') + throw err + } + }) + + res.render(lang + '/account/verification', { + status: true + }) + } + } + }) + } + }, + resetPassword: async function (req: any, res: any) { + const newPwd = req.body.inputNewPwd + + const user = await dbController.getUserByToken(req.params.token) + if (!user) { + res.flash('error', 'User not found.') + res.redirect('/login') + } else { + // encrypt password + bcrypt.genSalt(saltRounds, function (err, salt) { + bcrypt.hash(newPwd, salt, async function (err: any, hash) { + const credentialData = { + password: hash, + user_id: user.user_id, + resetPasswordToken: null, + resetPasswordExpires: null + } + // update password + const result = await dbController.updateCredential(credentialData) + if (!result) { + console.log('Failed to reset password') + res.flash('error', 'Datenbankfehler: Passwort kann nicht geändert werden.') + } else { + res.flash('success', 'Passwort aktualisiert!') + // send notification email + mailer.options.to = user.email + mailer.options.subject = miscConst.updatePasswordMailSubject + mailer.options.html = miscConst.updatePasswordMailContent + '<div>' + miscConst.mailSignature + '</div>' + mailer.transporter.sendMail(mailer.options, function (err: any) { + if (err) { console.log(err) } + }) + } + res.redirect('/login') + }) + }) + } + }, + generateNewToken: function (req: any, res: any, config: any) { + const emailAddress = req.body.inputEmail + async.waterfall([ + async function (done: any) { + const user = await dbController.checkUserEmail(emailAddress) + if (!user) { + console.log('No user found: ' + String(emailAddress)) + } else { + // generate token + let token: string = '' + const randomChars: string = 'abcdefghijklmnopqrstuvwxyz0123456789' + for (let i = 0; i < 40; i++) { + token += randomChars.charAt(Math.floor(Math.random() * randomChars.length)) + } + + const emailSubject = 'Ihre Passwort-Anfrage an das Transferportal der HFT Stuttgart' + const emailContent = '<div>Lieber Nutzer,<br/><br/>' + + '<p>wir haben Ihre Anfrage zur Erneuerung Ihres Passwortes erhalten. Falls Sie diese Anfrage nicht gesendet haben, ignorieren Sie bitte diese E-Mail.<br/><br/>' + + 'Sie können Ihr Passwort mit dem Klick auf diesen Link ändern: ' + String(config.app.host) + '/reset/' + String(token) + '<br/>' + + 'Dieser Link ist aus Sicherheitsgründen nur für 1 Stunde gültig.<br/></p>' + String(miscConst.mailSignature) + '</div>' + + const credentialData = { + user_id: user.id, + resetPasswordToken: token, + resetPasswordExpires: Date.now() + 3600000 // 1 hour + } + const result = await dbController.updateCredential(credentialData) + if (!result) { + console.log('failed to update credential') + } else { + // send email + mailer.options.to = emailAddress + mailer.options.subject = emailSubject + mailer.options.html = emailContent + mailer.transporter.sendMail(mailer.options, function (err: any) { + if (err) { console.error(err) } + }) + } + } + done(null) + } + ], function (err: any) { + if (err) { + res.flash('error', 'Ein Fehler ist aufgetreten. Bitte versuchen Sie es erneut.') + } else { + res.flash('success', 'Wenn Ihre E-Mail-Adresse registriert ist, wurde eine E-Mail mit dem weiteren Vorgehen an ' + String(emailAddress) + ' versendet.') + } + res.redirect('/account/forgotPwd') + }) + }, checkUserEmail: async function (req: any, res: any) { const user = await dbController.checkUserEmail(req.params.email) if (user) { res.send(false) } else { diff --git a/src/functions/helpers_TBD.ts b/src/functions/helpers_TBD.ts deleted file mode 100644 index 89506026042e8fb2b886a2a974fa3356d7a25c33..0000000000000000000000000000000000000000 --- a/src/functions/helpers_TBD.ts +++ /dev/null @@ -1,11 +0,0 @@ -const helpers = { - stringToArray: function (input: string) { - if (input != null) { - return input.split(',') - } else { - return null - } - } -} - -export = helpers diff --git a/src/public/js/security.js b/src/public/js/security.js index 672131647b9a46fe1661ce9171cb7dda0507f444..5d71fb8a7af81fa5f0f168d8e6df2101bf224dae 100644 --- a/src/public/js/security.js +++ b/src/public/js/security.js @@ -11,7 +11,7 @@ $('#inputNewPwd, #inputConfirm').on('keyup', function () { } // match or not? - if ($('#inputNewPwd').val() == $('#inputConfirm').val()) { + if ($('#inputNewPwd').val() === $('#inputConfirm').val()) { // $('#message').html('Matching').css('color', 'green'); $('#message').html('Übereinstimmend').css('color', 'green') isMatch = true diff --git a/src/routes/account.ts b/src/routes/account.ts index 78a5699a55e6e8c9fe85d0a7caf84db93d86df23..e99095ac1de929df22940be54cffe3e2579c6212 100644 --- a/src/routes/account.ts +++ b/src/routes/account.ts @@ -2,14 +2,14 @@ import fs from 'fs' import async from 'async' import bcrypt from 'bcryptjs' import * as passportSaml from 'passport-saml' -import dbconn from '../config/dbconn' +import { dbConnection } from '../config/dbconn' import { dbController } from '../controller/dbController' import { gitlabController } from '../controller/gitlabController' -import constants from '../config/const' -import mailer from '../config/mailer' -import portalUser from '../classes/user' -import projectInformation from '../classes/website' -import projectRepo from '../classes/repo' +import { miscConst } from '../config/const' +import { mailer } from '../config/mailer' +import { User } from '../classes/user' +import { Website } from '../classes/website' +import { Repo } from '../classes/repo' const SamlStrategy = passportSaml.Strategy const saltRounds = 10 @@ -17,7 +17,7 @@ const salt = 64 // salt length const logoDir = 'public/upload/' const defaultLogo: any = 'public/default/logo.png' -export = function (app: any, config: any, passport: any, lang: string) { +module.exports = function (app: any, config: any, passport: any, lang: string) { // =========== PASSPORT ======= passport.serializeUser(function (user: any, done: any) { done(null, user) @@ -88,7 +88,7 @@ export = function (app: any, config: any, passport: any, lang: string) { console.log('no user found') return null } else { - const loggedInUser = new portalUser( + const loggedInUser = new User( user.id, email, user.salutation, user.title, user.firstname, user.lastname, user.industry, user.organisation, user.speciality, user.m4lab_idp, user.verificationStatus ) @@ -180,27 +180,23 @@ export = function (app: any, config: any, passport: any, lang: string) { if (loggedInUser.getGitlabUserId()) { // for users who have activated their gitlab account const userProjects = await gitlabController.getUserProjects(loggedInUser.getGitlabUserId()!) - if (!userProjects) { - console.error('something went wrong') - res.status(500).render(lang + '/500', { error: 'something went wrong' }) - } - - let project: any - for (project in userProjects) { - if (userProjects[project].tag_list.includes('website')) { - const page = { - projectInformation: new projectInformation(loggedInUser.getGitlabUserId()!, userProjects[project].name, userProjects[project].description, - userProjects[project].id, userProjects[project].avatar_url, userProjects[project].path_with_namespace), - pipelineStatus: await gitlabController.getProjectPipelineLatestStatus(userProjects[project].id) + if (userProjects) { + let project: any + for (project in userProjects) { + if (userProjects[project].tag_list.includes('website')) { + const page = { + projectInformation: new Website(loggedInUser.getGitlabUserId()!, userProjects[project].name, userProjects[project].description, + userProjects[project].id, userProjects[project].avatar_url, userProjects[project].path_with_namespace), + pipelineStatus: await gitlabController.getProjectPipelineLatestStatus(userProjects[project].id) + } + gitlabPagesArr.push(page) + } else { + const repo = new Repo(loggedInUser.getGitlabUserId()!, userProjects[project].name, userProjects[project].description, + userProjects[project].id, userProjects[project].avatar_url, userProjects[project].path_with_namespace) + gitlabReposArr.push(repo) } - gitlabPagesArr.push(page) - } else { - const repo = new projectRepo(loggedInUser.getGitlabUserId()!, userProjects[project].name, userProjects[project].description, - userProjects[project].id, userProjects[project].avatar_url, userProjects[project].path_with_namespace) - gitlabReposArr.push(repo) } } - res.render(lang + '/account/services', { user: loggedInUser, gitlabRepos: gitlabReposArr, @@ -297,7 +293,7 @@ export = function (app: any, config: any, passport: any, lang: string) { const newPwd = req.body.inputNewPwd const retypePwd = req.body.inputConfirm - dbconn.user.query('SELECT password FROM credential WHERE user_id=' + loggedInUser.getId(), function (err: any, rows: any) { + dbConnection.user.query('SELECT password FROM credential WHERE user_id=' + loggedInUser.getId(), function (err: any, rows: any) { if (err) { console.error(err) res.status(500).render(lang + '/500', { error: err }) @@ -313,7 +309,7 @@ export = function (app: any, config: any, passport: any, lang: string) { res.flash('error', 'Das Passwort ist leider falsch. Bitte überprüfen Sie Ihre Eingabe.') res.redirect('/account/security') } else { - if (newPwd != retypePwd) { + if (newPwd !== retypePwd) { res.flash('error', 'Passwörter stimmen nicht überein. Bitte stellen Sie sicher, dass Sie das Passwort beide Male genau gleich eingeben.') res.redirect('/account/security') } else { @@ -333,8 +329,8 @@ export = function (app: any, config: any, passport: any, lang: string) { res.flash('success', 'Passwort aktualisiert!') // send notifiaction email mailer.options.to = loggedInUser.getEmail() - mailer.options.subject = constants.updatePasswordMailSubject - mailer.options.html = constants.updatePasswordMailContent + '<div>' + constants.mailSignature + '</div>' + mailer.options.subject = miscConst.updatePasswordMailSubject + mailer.options.html = miscConst.updatePasswordMailContent + '<div>' + miscConst.mailSignature + '</div>' mailer.transporter.sendMail(mailer.options, function (err: any) { if (err) { console.log(err) } }) @@ -368,7 +364,7 @@ export = function (app: any, config: any, passport: any, lang: string) { '<p>vielen Dank für Ihre Anmeldung am Transferportal der HFT Stuttgart. <br/>' + 'Um Ihre Anmeldung zu bestätigen, klicken Sie bitte diesen Link: ' + config.app.host + '/verifyAccount?token=' + token + '<br/><br/>' + - 'Ohne Bestätigung Ihres Kontos müssen wir Ihr Konto leider nach 7 Tagen löschen.</p><br/>' + constants.mailSignature + + 'Ohne Bestätigung Ihres Kontos müssen wir Ihr Konto leider nach 7 Tagen löschen.</p><br/>' + miscConst.mailSignature + '</div>' mailer.options.to = loggedInUser.email mailer.options.subject = emailSubject @@ -422,7 +418,7 @@ export = function (app: any, config: any, passport: any, lang: string) { const projectName = req.body.name.toLowerCase().replace(/\s/g, '-') const projectDesc = req.body.description const projectTemplate = req.body.template - const newInformation = new projectInformation(loggedInUser.getGitlabUserId()!, projectName, projectDesc) + const newInformation = new Website(loggedInUser.getGitlabUserId()!, projectName, projectDesc) let newLogoFile = defaultLogo if (req.files) { newLogoFile = req.files.logo } @@ -441,7 +437,7 @@ export = function (app: any, config: any, passport: any, lang: string) { async function (newLogoFile: any) { // create a new GitLab Page const newPages = await gitlabController.createNewPages(newInformation, newLogoFile, projectTemplate) if (newPages.status) { - if (newPages.data.message.name == 'has already been taken') { + if (newPages.data.message.name === 'has already been taken') { res.flash('error', "Der Projektname '" + newInformation.getName() + "' ist bereits vergeben, bitte wählen Sie einen anderen Namen.") } else { res.flash('error', 'Ein Fehler ist aufgetreten. Bitte versuchen Sie es erneut. ') @@ -486,11 +482,11 @@ export = function (app: any, config: any, passport: any, lang: string) { } else if (!project.owner) { console.log(' ========= Project cannot be accessed, since it does not have an owner') res.redirect('/account/services') - } else if (project.owner.id != loggedInUser.getGitlabUserId()) { + } else if (project.owner.id !== loggedInUser.getGitlabUserId()) { console.log(' ========= Access denied: Not your project') res.redirect('/account/services') } else { - const curInformation = new projectInformation(loggedInUser.getGitlabUserId()!, project.name, project.description, + const curInformation = new Website(loggedInUser.getGitlabUserId()!, project.name, project.description, req.query.id, project.avatar_url, project.path_with_namespace) res.render(lang + '/account/updateInformation', { @@ -518,7 +514,7 @@ export = function (app: any, config: any, passport: any, lang: string) { } else { const projectName = req.body.name.toLowerCase().replace(/\s/g, '-') const projectDesc = req.body.description - const updatedInformation = new projectInformation(loggedInUser.getGitlabUserId()!, projectName, projectDesc, req.query.id) + const updatedInformation = new Website(loggedInUser.getGitlabUserId()!, projectName, projectDesc, req.query.id) let newLogoFile: any async.waterfall([ @@ -548,7 +544,7 @@ export = function (app: any, config: any, passport: any, lang: string) { res.flash('success', 'Ihre Website wurde aktualisiert') } - res.redirect('/account/updateInformation?id=' + updatedInformation.getId()) + res.redirect('/account/updateInformation?id=' + String(updatedInformation.getId())) } ], function (err) { if (err != null) console.log(err) @@ -580,7 +576,7 @@ export = function (app: any, config: any, passport: any, lang: string) { console.log(' ========= Error or no project found') } else if (!project.owner) { console.log(' ========= Project cannot be accessed, since it does not have an owner') - } else if (project.owner.id != loggedInUser.getGitlabUserId()) { + } else if (project.owner.id !== loggedInUser.getGitlabUserId()) { console.log(' ========= Access denied: Not your project') } else { const isDeleted = await gitlabController.deleteProjectById(projectId) diff --git a/src/routes/public.ts b/src/routes/public.ts index 151540674670e8b89085291f28f902dd65391574..14e5e1bb15d65e375217fe0280a1066fa6dd9958 100644 --- a/src/routes/public.ts +++ b/src/routes/public.ts @@ -1,95 +1,12 @@ -import async from 'async' -import bcrypt from 'bcryptjs' -import { dbController } from '../controller/dbController' -import mailer from '../config/mailer' -import constants from '../config/const' import { publicController } from '../controller/publicController' -const saltRounds: number = 10 -const salt: number = 64 - -export = function (app: any, config: any, lang: string) { +module.exports = function (app: any, config: any, lang: any) { // ================== NEW USERS REGISTRATION ====================== app.get('/registration', function (req: any, res: any) { - publicController.showRegistrationPage(res) + publicController.showRegistrationPage(res, lang) }) app.post('/registration', function (req: any, res: any) { - // user data - const curDate: Date = new Date() - const userData: any = { - salutation: req.body.inputSalutation, - title: req.body.inputTitle, - firstname: req.body.inputFirstname, - lastname: req.body.inputLastname, - email: req.body.inputEmail, - organisation: req.body.inputOrganisation, - industry: req.body.inputIndustry, - speciality: req.body.inputSpeciality, - createdDate: curDate.toISOString().slice(0, 10) - } - - const userEmail: any = userData.email - const pos: number = userEmail.indexOf('@') - const emailLength: number = userEmail.length - const emailDomain: any = userEmail.slice(pos, emailLength) - - if (emailDomain.toLowerCase() == '@hft-stuttgart.de') { - res.flash('error', 'Fehlgeschlagen: HFT-Account') - res.redirect('/account/registration') - } else { - async.waterfall([ - function (done: any) { - // generate token - let token: string = '' - const randomChars: string = 'abcdefghijklmnopqrstuvwxyz0123456789' - for (let i = 0; i < 40; i++) { - token += randomChars.charAt(Math.floor(Math.random() * randomChars.length)) - } - // encrypt password - bcrypt.genSalt(saltRounds, function (err, salt) { - bcrypt.hash(req.body.inputPassword, salt, function (err: any, hash: any) { - const newAccount: any = { - profile: userData, - password: hash, - verificationToken: token - } - done(err, newAccount) - }) - }) - }, - // save data - function (newAccount: any, err: any) { - dbController.registerNewUser(newAccount, function (err: any) { - if (err) { - res.flash('error', 'Fehlgeschlagen') - } else { - // send email - const emailSubject = 'Bitte bestätigen Sie Ihr M4_LAB Benutzerkonto' - const emailContent = '<div>Lieber Nutzer,<br/><br/>' + - '<p>vielen Dank für Ihre Anmeldung am Transferportal der HFT Stuttgart. <br/>' + - 'Um Ihre Anmeldung zu bestätigen, klicken Sie bitte <a href=' + String(config.app.host) + '/verifyAccount?token=' + String(newAccount.verificationToken) + '>diesen Link</a> ' + - '<br/><br/>' + - 'Ohne Bestätigung Ihres Kontos müssen wir Ihr Konto leider nach 7 Tagen löschen.</p><br/>' + String(constants.mailSignature) + - '</div>' - mailer.options.to = req.body.inputEmail - mailer.options.subject = emailSubject - mailer.options.html = emailContent - mailer.transporter.sendMail(mailer.options, function (err: any) { - if (err) { - console.error('Cannot send email. [Error] ' + String(err)) - throw err - } - }) - // user feedback - res.flash('success', 'Vielen Dank für Ihre Registrierung!' + '\r\n\r\n' + - 'Wir haben Ihnen eine E-Mail an Ihre verwendete Adresse gesendet. Diese enthält einen Link zur Bestätigung Ihres Accounts.' + '\r\n' + - 'Wenn Sie die Mail nicht in ihrem Postfach vorfinden, prüfen Sie bitte auch Ihren Spam-Ordner.') - } - res.redirect('/account/registration') - }) - } - ]) - } + publicController.registerUser(req, res, config) }) // to check whether or not an account is already exist app.get('/email/:email', async function (req: any, res: any) { @@ -99,157 +16,29 @@ export = function (app: any, config: any, lang: string) { // =================== USERS VERIFICATION ========================= app.get('/verifyAccount', async function (req: any, res: any) { - const userId: number = await dbController.getUserIdByVerificationToken(req.query.token) - if (!userId) { - // no user found - res.render(lang + '/account/verification', { - status: null - }) - } else { - // a user found, verify the account - const userData: any = { - id: userId, - verificationStatus: 1 - } - dbController.verifyUserAccount(userData, async function (err: any) { - if (err) { - console.error(err) - res.render(lang + '/account/verification', { - status: false - }) - } else { - // send welcome email after successful account verification - const userEmail: string = await dbController.getUserEmailById(userId) - if (!userEmail) { - res.render(lang + '/account/verification', { - status: false - }) - } else { - // send email - const emailSubject = 'Herzlich willkommen' - const emailContent = '<div>Lieber Nutzer,<br/><br/>' + - '<p>herzlich willkommen beim Transferportal der HFT Stuttgart!<br/>' + - 'Sie können nun alle Dienste des Portals nutzen.<p/><br/>' + constants.mailSignature - mailer.options.to = userEmail - mailer.options.subject = emailSubject - mailer.options.html = emailContent - mailer.transporter.sendMail(mailer.options, function (err: any) { - if (err) { - console.log('cannot send email') - throw err - } - }) - - res.render(lang + '/account/verification', { - status: true - }) - } - } - }) - } + publicController.verifyAccount(req, res, lang) }) // ==================== FORGOT PASSWORD =========================== app.get('/forgotPwd', function (req: any, res: any) { - publicController.showForgotPwdPage(req, res) + publicController.showForgotPwdPage(req, res, lang) }) app.post('/forgotPwd', function (req: any, res: any) { - const emailAddress = req.body.inputEmail - async.waterfall([ - async function (done: any) { - const user = await dbController.checkUserEmail(emailAddress) - if (!user) { - console.log('No user found: ' + String(emailAddress)) - } else { - // generate token - let token: string = '' - const randomChars: string = 'abcdefghijklmnopqrstuvwxyz0123456789' - for (let i = 0; i < 40; i++) { - token += randomChars.charAt(Math.floor(Math.random() * randomChars.length)) - } - - const emailSubject = 'Ihre Passwort-Anfrage an das Transferportal der HFT Stuttgart' - const emailContent = '<div>Lieber Nutzer,<br/><br/>' + - '<p>wir haben Ihre Anfrage zur Erneuerung Ihres Passwortes erhalten. Falls Sie diese Anfrage nicht gesendet haben, ignorieren Sie bitte diese E-Mail.<br/><br/>' + - 'Sie können Ihr Passwort mit dem Klick auf diesen Link ändern: ' + String(config.app.host) + '/reset/' + String(token) + '<br/>' + - 'Dieser Link ist aus Sicherheitsgründen nur für 1 Stunde gültig.<br/></p>' + String(constants.mailSignature) + '</div>' - - const credentialData = { - user_id: user.id, - resetPasswordToken: token, - resetPasswordExpires: Date.now() + 3600000 // 1 hour - } - const result = await dbController.updateCredential(credentialData) - if (!result) { - console.log('failed to update credential') - } else { - // send email - mailer.options.to = emailAddress - mailer.options.subject = emailSubject - mailer.options.html = emailContent - mailer.transporter.sendMail(mailer.options, function (err: any) { - if (err) { console.error(err) } - }) - } - } - done(null) - } - ], function (err: any) { - if (err) { - res.flash('error', 'Ein Fehler ist aufgetreten. Bitte versuchen Sie es erneut.') - } else { - res.flash('success', 'Wenn Ihre E-Mail-Adresse registriert ist, wurde eine E-Mail mit dem weiteren Vorgehen an ' + String(emailAddress) + ' versendet.') - } - res.redirect('/account/forgotPwd') - }) + publicController.generateNewToken(req, res, config) }) // reset app.get('/reset/:token', async function (req: any, res: any) { - publicController.showResetToken(req, res) + publicController.showResetPassword(req, res, lang) }) app.post('/reset/:token', async function (req: any, res: any) { - const newPwd = req.body.inputNewPwd - - const user = await dbController.getUserByToken(req.params.token) - if (!user) { - res.flash('error', 'User not found.') - res.redirect('/login') - } else { - // encrypt password - bcrypt.genSalt(saltRounds, function (err, salt) { - bcrypt.hash(newPwd, salt, async function (err: any, hash) { - const credentialData = { - password: hash, - user_id: user.user_id, - resetPasswordToken: null, - resetPasswordExpires: null - } - // update password - const result = await dbController.updateCredential(credentialData) - if (!result) { - console.log('Failed to reset password') - res.flash('error', 'Datenbankfehler: Passwort kann nicht geändert werden.') - } else { - res.flash('success', 'Passwort aktualisiert!') - // send notification email - mailer.options.to = user.email - mailer.options.subject = constants.updatePasswordMailSubject - mailer.options.html = constants.updatePasswordMailContent + '<div>' + constants.mailSignature + '</div>' - mailer.transporter.sendMail(mailer.options, function (err: any) { - if (err) { console.log(err) } - }) - } - res.redirect('/login') - }) - }) - } + publicController.resetPassword(req, res) }) // ======================= CONTACT FORM =========================== app.get('/contact', function (req: any, res: any) { - publicController.showContactPage(req, res) + publicController.showContactPage(req, res, lang) }) app.post('/contact', function (req: any, res: any) { publicController.sendContactMessage(req, res) diff --git a/src/views/DE/account/services.pug b/src/views/DE/account/services.pug index 46c91bc297dc7abeacb21b06db0dd027fc6a8ca5..f03fd1f72e6cb957313dd4ad5ee8da31aebfb910 100644 --- a/src/views/DE/account/services.pug +++ b/src/views/DE/account/services.pug @@ -78,6 +78,9 @@ html(lang="de") div(class="col text-right") button(type="button", class="btn btn-sm btn-success" disabled) Neuer Projektdatensatz table(class="table") + if gitlabRepos.length == 0 + tr + td Currently you have no project codes/data for item in gitlabRepos - let img = item.logo tr