diff --git a/app.js b/app.js deleted file mode 100644 index b5aac0bc6362999b0cb402096628af41d12a362c..0000000000000000000000000000000000000000 --- a/app.js +++ /dev/null @@ -1,82 +0,0 @@ -const express = require('express'); -const http = require('http'); -const path = require('path'); -const passport = require('passport'); -const morgan = require('morgan'); -const cookieParser = require('cookie-parser'); -const bodyParser = require('body-parser'); -const session = require('express-session'); -const errorhandler = require('errorhandler'); -const flash = require('express-flash-2'); -const fileUpload = require('express-fileupload'); -const helmet = require('helmet'); -const compression = require('compression'); -const methodOverride = require('method-override'); - -var env = process.env.NODE_ENV || 'testing'; -const config = require('./config/config')[env]; -const lang = 'DE'; - -var app = express(); - -app.set('port', config.app.port); -app.set('views', __dirname + '/views'); -app.set('view engine', 'pug'); - -// enable files upload -app.use(fileUpload({ - createParentPath: true, - limits: { - fileSize: 1000000 // 1 MB max. file size - } -})); - -app.use(methodOverride('_method')); -app.use(helmet()); -app.use(compression()); -app.use(morgan('combined')); -app.use(cookieParser()); -app.use(bodyParser.json()); -app.use(bodyParser.urlencoded({extended: false})); -app.use(express.static(path.join(__dirname, 'public'))); -app.use((req, res, next) => { - next(); -}); - -app.use(session( - { - resave: true, - saveUninitialized: true, - secret: config.app.sessionSecret - } -)); -app.use(flash()); -app.use(passport.initialize()); -app.use(passport.session()); - -// caching disabled for every route -// NOTE: Works in Firefox and Opera. Does not work in Edge -app.use(function(req, res, next) { - res.set('Cache-Control', 'no-cache, private, no-store, must-revalidate, max-stale=0, post-check=0, pre-check=0'); - next(); -}); - -require('./routes/account')(app, config, passport, lang); -require('./routes/public')(app, config, lang); - -// Handle 404 -app.use(function (req, res) { - res.status(404).render(lang+'/404') -}) - -// Handle 500 - any server error -app.use(function (err, req, res, next) { - console.error(err.stack) - res.status(500).render(lang+'/500', { - error: err - }) -}) - -app.listen(app.get('port'), function () { - console.log('Express server listening on port ' + app.get('port')); -}); \ No newline at end of file diff --git a/app.ts b/app.ts new file mode 100644 index 0000000000000000000000000000000000000000..89a8a359589fd6220aeab4f5ec4e14e0c742df46 --- /dev/null +++ b/app.ts @@ -0,0 +1,93 @@ +import express from 'express'; +import path from 'path'; +import passport from 'passport'; +import morgan from 'morgan'; +import cookieParser from 'cookie-parser'; +import bodyParser from 'body-parser'; +import session from 'express-session'; +import flash from 'express-flash-2'; +import fileUpload from 'express-fileupload'; +import helmet from 'helmet'; +import compression from 'compression'; +import methodOverride from 'method-override'; +import dotenv from 'dotenv' + +dotenv.config(); + +var env = process.env.NODE_ENV || 'testing'; +const config = require('./config/config')[env]; +const lang = 'DE'; + +var app = express(); +app.set('port', config.app.port); +app.set('views', path.join( __dirname + '/views')); +app.set('view engine', 'pug'); + +// enable files upload +app.use(fileUpload({ + createParentPath: true, + limits: { + fileSize: 1000000 // 1 MB max. file size + } +})); +app.use(methodOverride('_method')); +app.use( + helmet.contentSecurityPolicy({ + useDefaults: true, + directives: { + "font-src": ["'self'", "https://use.fontawesome.com"], + "img-src": ["'self'", "https://transfer.hft-stuttgart.de"], + "script-src": ["'self'", "https://code.jquery.com/jquery-3.3.1.min.js", "https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.7/umd/popper.min.js", + "https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/js/bootstrap.min.js", "https://unpkg.com/bootstrap-show-password@1.2.1/dist/bootstrap-show-password.min.js"], + "style-src": ["'self'", "https://use.fontawesome.com/releases/v5.8.2/css/all.css"], + "frame-src": ["'self'"] + }, + reportOnly: true, + }) +); + +app.use(compression()); +app.use(morgan('combined')); +app.use(cookieParser(config.app.sessionSecret)); +app.use(bodyParser.json()); +app.use(bodyParser.urlencoded({extended: false})); +app.use(express.static(path.join(__dirname, 'public'))); +app.use((req, res, next) => { + next(); +}); + +app.use(session({ + resave: true, + saveUninitialized: true, + secret: config.app.sessionSecret +})); +app.use(flash()); +app.use(passport.initialize()); +app.use(passport.session()); + +// caching disabled for every route +// NOTE: Works in Firefox and Opera. Does not work in Edge +app.use(function(req, res, next) { + res.set('Cache-Control', 'no-cache, private, no-store, must-revalidate, max-stale=0, post-check=0, pre-check=0'); + next(); +}); + +require('./routes/public')(app, config, lang); +require('./routes/account')(app, config, passport, lang); + +// Handle 404 +app.use(function (req:any, res:any) { + res.status(404).render(lang+'/404') +}) + +// Handle 500 - any server error +app.use(function (err:any, req:any, res:any, next:any) { + console.error(err.stack) + res.status(500).render(lang+'/500', { + error: err + }) +}) + +app.listen(app.get('port'), function () { + console.log('Express server listening on port ' + app.get('port')); +}); \ No newline at end of file diff --git a/classes/project.js b/classes/project.ts similarity index 63% rename from classes/project.js rename to classes/project.ts index 85dafab3830e16ea3db1c50802a4a3a38e2988ed..045e86a73cad5333ba58bf85f9be8aa19fa619ea 100644 --- a/classes/project.js +++ b/classes/project.ts @@ -1,9 +1,16 @@ class Project { - constructor(ownerGitlabId, id, name, desc, logo, path) { + ownerGitlabId:number + name:string + desc:string + id?:number + logo?:string + path?:string + + constructor(ownerGitlabId:number, name:string, desc:string, id?:number, logo?:string, path?:string) { this.ownerGitlabId = ownerGitlabId - this.id = id this.name = name this.desc = desc + this.id = id this.logo = logo this.path = path } @@ -28,24 +35,24 @@ class Project { return this.path } // setter - setOwnerGitlabId(newOwnerGitlabId){ + setOwnerGitlabId(newOwnerGitlabId:number){ this.ownerGitlabId = newOwnerGitlabId } - setId(newId) { + setId(newId:number) { this.id = newId } - setName(newName) { + setName(newName:string) { this.name = newName } - setDesc(newDesc) { + setDesc(newDesc:string) { this.desc = newDesc } - setLogo(newLogoUrl) { + setLogo(newLogoUrl:string) { this.logo = newLogoUrl } - setPath(newPath) { + setPath(newPath:string) { this.path = newPath } } -module.exports = Project \ No newline at end of file +export = Project \ No newline at end of file diff --git a/classes/repo.js b/classes/repo.js deleted file mode 100644 index c99ac66f6e4ada393426c40df94c2a25335d96d1..0000000000000000000000000000000000000000 --- a/classes/repo.js +++ /dev/null @@ -1,9 +0,0 @@ -const Project = require("./project"); - -class Repo extends Project { - constructor(ownerGitlabId, id, name, desc, logo, path) { - super(ownerGitlabId, id, name, desc, logo, path) - } -} - -module.exports = Repo \ No newline at end of file diff --git a/classes/repo.ts b/classes/repo.ts new file mode 100644 index 0000000000000000000000000000000000000000..b41a8fe9ad343c385383d8ede7f1f82a57f6162f --- /dev/null +++ b/classes/repo.ts @@ -0,0 +1,9 @@ +import Project from "./project" + +class Repo extends Project { + constructor(ownerGitlabId:number, name:string, desc:string, id?:number, logo?:string, path?:string) { + super(ownerGitlabId, name, desc, id, logo, path) + } +} + +export = Repo \ No newline at end of file diff --git a/classes/user.js b/classes/user.js deleted file mode 100644 index 94f07edd3f8c382b22504f0e356f84506011d662..0000000000000000000000000000000000000000 --- a/classes/user.js +++ /dev/null @@ -1,83 +0,0 @@ -class User { - constructor(id, email, salutation, title, firstName, lastName, industry, organisation, speciality, is_m4lab_idp, gitlabUserId, verificationStatus) { - this.id = id - this.email = email - this.salutation = salutation - this.title = title - this.firstName = firstName - this.lastName = lastName - this.industry = industry - this.organisation = organisation - this.speciality = speciality - this.is_m4lab_idp = is_m4lab_idp // 1 or 0 - this.gitlabUserId = gitlabUserId - this.verificationStatus = verificationStatus - } - - // getter - getId() { - return this.id - } - getEmail() { - return this.email - } - getFullName() { - return this.firstName+' '+this.lastName - } - getIdpStatus() { - return this.is_m4lab_idp - } - getGitlabUserId() { - return this.gitlabUserId - } - getVerificationStatus() { - return this.verificationStatus - } - // setter - setEmail(email) { - this.email = email - } - setSalutation(salutation) { - this.salutation = salutation - } - setTitle(title) { - this.title = title - } - setFirstName(firstName) { - this.firstName = firstName - } - setLastName(lastName) { - this.lastName = lastName - } - setIndustry(industry) { - this.industry = industry - } - setOrganisation(organisation) { - this.organisation = organisation - } - setSpeciality(speciality) { - this.speciality = speciality - } - setM4lab_idp(m4lab_idp) { - this.m4lab_idp = m4lab_idp - } - setGitlabUserId(newGitlabUserId) { - this.gitlabUserId = newGitlabUserId - } - setVerificationStatus(verificationStatus) { - this.verificationStatus = verificationStatus - } - - updateProfile(newSalutation, newTitle, newFirstname, newLastname, newEmail, newOrganisation, newIndustry, newSpeciality) { - this.salutation = newSalutation - this.title = newTitle - this.firstName = newFirstname - this.lastName = newLastname - this.email = newEmail - this.organisation = newOrganisation - this.industry = newIndustry - this.speciality = newSpeciality - } -} - -module.exports = User \ No newline at end of file diff --git a/classes/user.ts b/classes/user.ts new file mode 100644 index 0000000000000000000000000000000000000000..3b26a694b4a95dd98109f976189561ff8a08a31d --- /dev/null +++ b/classes/user.ts @@ -0,0 +1,97 @@ +class User { + id:number + email:string + salutation:string // should be enum + title:string // should be enum + firstName:string + lastName:string + industry:string + organisation:string + speciality:string + is_m4lab_idp: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) { + this.id = id + this.email = email + this.salutation = salutation + this.title = title + this.firstName = firstName + this.lastName = lastName + this.industry = industry + this.organisation = organisation + this.speciality = speciality + this.is_m4lab_idp = is_m4lab_idp + this.verificationStatus = verificationStatus + this.gitlabUserId = gitlabUserId + } + + // getter + getId() { + return this.id + } + getEmail() { + return this.email + } + getFullName() { + return this.firstName+' '+this.lastName + } + getIdpStatus() { + return this.is_m4lab_idp + } + getVerificationStatus() { + return this.verificationStatus + } + getGitlabUserId() { + return this.gitlabUserId + } + // setter + setEmail(email:string) { + this.email = email + } + setSalutation(salutation:string) { + this.salutation = salutation + } + setTitle(title:string) { + this.title = title + } + setFirstName(firstName:string) { + this.firstName = firstName + } + setLastName(lastName:string) { + this.lastName = lastName + } + setIndustry(industry:string) { + this.industry = industry + } + setOrganisation(organisation:string) { + this.organisation = organisation + } + setSpeciality(speciality:string) { + this.speciality = speciality + } + setM4lab_idp(m4lab_idp:number) { + this.is_m4lab_idp = m4lab_idp + } + setVerificationStatus(verificationStatus:number) { + this.verificationStatus = verificationStatus + } + setGitlabUserId(newGitlabUserId:number) { + this.gitlabUserId = newGitlabUserId + } + + updateProfile(newSalutation:string, newTitle:string, newFirstname:string, newLastname:string, newEmail:string, newOrganisation:string, newIndustry:string, newSpeciality:string) { + this.salutation = newSalutation + this.title = newTitle + this.firstName = newFirstname + this.lastName = newLastname + this.email = newEmail + this.organisation = newOrganisation + this.industry = newIndustry + this.speciality = newSpeciality + } +} + +export = User \ No newline at end of file diff --git a/classes/website.js b/classes/website.js deleted file mode 100644 index 1d05c40059221e15d8fa04fc43abee51d7e2190f..0000000000000000000000000000000000000000 --- a/classes/website.js +++ /dev/null @@ -1,9 +0,0 @@ -const Project = require("./project"); - -class Website extends Project { - constructor(ownerGitlabId, id, name, desc, logo, path) { - super(ownerGitlabId, id, name, desc, logo, path) - } -} - -module.exports = Website \ No newline at end of file diff --git a/classes/website.ts b/classes/website.ts new file mode 100644 index 0000000000000000000000000000000000000000..bd64140dde3774db0aeaba750bd07691ac348059 --- /dev/null +++ b/classes/website.ts @@ -0,0 +1,9 @@ +import Project from "./project" + +class Website extends Project { + constructor(ownerGitlabId:number, name:string, desc:string, id?:number, logo?:string, path?:string) { + super(ownerGitlabId, name, desc, id, logo, path) + } +} + +export = Website \ No newline at end of file diff --git a/config/config.js b/config/config.ts similarity index 94% rename from config/config.js rename to config/config.ts index e4bb457ce3bd4b6f30a784e4ec7d2dc685fba4b4..47558a5d53d2a00b17098074510779b5bb44cb05 100644 --- a/config/config.js +++ b/config/config.ts @@ -1,4 +1,4 @@ -module.exports = { +export = { development: { app: { name: 'User Account Management', @@ -28,7 +28,7 @@ module.exports = { host: 'mailhost', // hostname secureConnection: false, // TLS requires secureConnection to be false port: 587, // port for secure SMTP - TLS: true, // sets requireTLS + TLS: true, authUser: 'mailuser', authPass: 'mailpass', tlsCiphers: 'SSLv3', @@ -67,7 +67,7 @@ module.exports = { host: 'mailhost', // hostname secureConnection: false, // TLS requires secureConnection to be false port: 587, // port for secure SMTP - TLS: true, // sets requireTLS + TLS: true, authUser: 'mailuser', authPass: 'mailpass', tlsCiphers: 'SSLv3', @@ -77,4 +77,4 @@ module.exports = { token_readWriteProjects: 'token-goes-here' } } -} +} \ No newline at end of file diff --git a/config/const.js b/config/const.ts similarity index 98% rename from config/const.js rename to config/const.ts index dcbb821a4bf191c8a5ddd0dd10e9e0abab0a1c56..302d017701af834d7cf3f73f3b66c2e0a0d2f584 100644 --- a/config/const.js +++ b/config/const.ts @@ -1,4 +1,4 @@ -module.exports = { +export = { mailSignature: 'Mit den besten Grüßen,<br/>das Transferportal-Team der HFT Stuttgart<br/><br/>' + 'Transferportal der Hochschule für Technik Stuttgart<br/>' + diff --git a/config/dbconn.js b/config/dbconn.ts similarity index 91% rename from config/dbconn.js rename to config/dbconn.ts index 670032958e099606b5558a39b94cc6492eb155e4..d918ce7c2e911d3a997c92df8d313f334f8024ca 100644 --- a/config/dbconn.js +++ b/config/dbconn.ts @@ -1,6 +1,6 @@ -const mysql = require('mysql') +import mysql from 'mysql' -var env = process.env.NODE_ENV || 'testing'; +var env = process.env.NODE_ENV || 'testing' const config = require('./config')[env] // ==== USER ACOOUNT DB CONNECTION ==== @@ -14,7 +14,7 @@ var userConnection = mysql.createConnection({ }) userConnection.connect(function(err) { - if (err) throw err; + if (err) throw err }) userConnection.query('USE '+config.database.dbUser) @@ -52,7 +52,7 @@ var projectConnection = mysql.createConnection({ }) projectConnection.connect(function(err) { - if (err) throw err; + if (err) throw err }) projectConnection.query('USE '+config.database.dbProject) @@ -61,4 +61,4 @@ var connection = { project: projectConnection } -module.exports = connection \ No newline at end of file +export = connection \ No newline at end of file diff --git a/config/dbconn2.js b/config/dbconn2.ts similarity index 91% rename from config/dbconn2.js rename to config/dbconn2.ts index abbd0a012666e0d1f86b03512759cd438cd2d8fe..c13c40f11c2c3994791d75c866c5cfc6d210e1d7 100644 --- a/config/dbconn2.js +++ b/config/dbconn2.ts @@ -1,6 +1,6 @@ -const mysql = require('mysql2') +import mysql from 'mysql2' -var env = process.env.NODE_ENV || 'testing'; +var env = process.env.NODE_ENV || 'testing' const config = require('./config')[env] // ==== USER ACOOUNT DB CONNECTION ==== @@ -14,7 +14,7 @@ var userConnection = mysql.createConnection({ }) userConnection.connect(function(err) { - if (err) throw err; + if (err) throw err }) userConnection.query('USE '+config.database.dbUser) @@ -52,7 +52,7 @@ var projectConnection = mysql.createConnection({ }) projectConnection.connect(function(err) { - if (err) throw err; + if (err) throw err }) projectConnection.query('USE '+config.database.dbProject) @@ -61,4 +61,4 @@ var connection = { project: projectConnection } -module.exports = connection \ No newline at end of file +export = connection \ No newline at end of file diff --git a/config/mailer.js b/config/mailer.js deleted file mode 100644 index d7c6faf0983428c4e15944cf5d85fa68ee5c158f..0000000000000000000000000000000000000000 --- a/config/mailer.js +++ /dev/null @@ -1,39 +0,0 @@ -const nodemailer = require('nodemailer'); -const nodemailerNTLMAuth = require('nodemailer-ntlm-auth'); - - -var env = process.env.NODE_ENV || 'testing'; -const config = require('./config')[env] - -var smtpTransport = nodemailer.createTransport({ - host: config.mailer.host, - secure: config.mailer.secureConnection, - port: config.mailer.port, - requireTLS: config.mailer.TLS, - auth: { - type: 'custom', - method: 'NTLM', - user: config.mailer.authUser, - pass: config.mailer.authPass, - options: { - domain: 'ad' - } - }, - customAuth:{ - NTLM: nodemailerNTLMAuth - } -}); - -var mailOptions = { - to: "", - from: config.mailer.from, - subject: "", - text: "" -}; - -var mailer = { - transport: smtpTransport, - options: mailOptions -} - -module.exports = mailer diff --git a/config/mailer.ts b/config/mailer.ts new file mode 100644 index 0000000000000000000000000000000000000000..18f25191d2a4abb7e30f6810404e0968a1660a4c --- /dev/null +++ b/config/mailer.ts @@ -0,0 +1,40 @@ +const nodemailer = require('nodemailer') +const nodemailerNTLMAuth = require('nodemailer-ntlm-auth') + +var env = process.env.NODE_ENV || 'testing' +const config = require('./config')[env] + +var smtpTransporter = nodemailer.createTransport({ + host: config.mailer.host, + secure: config.mailer.secureConnection, + port: config.mailer.port, + requireTLS: config.mailer.TLS, + auth: { + type: 'custom', + method: 'NTLM', + user: config.mailer.authUser, + pass: config.mailer.authPass, + options: { + domain: 'ad' + } + }, + customAuth:{ + NTLM: nodemailerNTLMAuth + } +}); + +var mailOptions:any = { + to: "", + cc: "", + from: config.mailer.from, + subject: "", + text: "", + html: "" +} + +var mailer:any = { + transporter: smtpTransporter, + options: mailOptions +} + +export = mailer \ No newline at end of file diff --git a/functions/gitlab.js b/functions/gitlab.ts similarity index 58% rename from functions/gitlab.js rename to functions/gitlab.ts index 62ff36508f80a0747538386a7838e4892c5a1706..5924afd12edb9abca93fef917957dada166078f4 100644 --- a/functions/gitlab.js +++ b/functions/gitlab.ts @@ -1,13 +1,12 @@ +import axios from 'axios' +import fs from 'fs' +import formData from 'form-data' + var env = process.env.NODE_ENV || 'testing' const config = require('../config/config')[env] -const axios = require('axios') -const fs = require('fs') -var formData = require('form-data') var gitlab = { - // todo: GraphQL currentUser - - getUserByEmail: async function(email) { + getUserByEmail: async function(email:string) { return axios({ method: 'get', url: 'https://transfer.hft-stuttgart.de/gitlab/api/v4/users?search='+email, @@ -15,9 +14,12 @@ var gitlab = { 'Authorization': 'Bearer '+config.gitlab.token_readWriteProjects} }) .then(res => res.data[0]) - .catch(err => console.error(err)) + .catch(function(err){ + console.error(err) + return null + }) }, - createNewPages: async function(newPagesData, newLogoFile, template) { + createNewPages: async function(newPagesData:any, newLogoFile:string, template:any) { let data = new formData() data.append('avatar', fs.createReadStream(newLogoFile)) @@ -32,16 +34,15 @@ var gitlab = { }, data: data }) - .then(res => res = { - error: false, - data: res.data - }) - .catch(err => res = { - error: true, - data: err.response.data + .then(res => res.data) + .catch(function(err) { + console.error("ERR Status: "+err.response.status) + console.error("ERR Name: "+err.response.data.message.name) + console.error("ERR Path: "+err.response.data.message.path) + return err.response }) }, - updateProject: async function(updatedProjectData, newLogoFile){ + updateProject: async function(updatedProjectData:any, newLogoFile:string){ let data = new formData() if (newLogoFile) { data.append('avatar', fs.createReadStream(newLogoFile)) @@ -57,16 +58,16 @@ var gitlab = { }, data : data }) - .then(res => res = { - error: false, - data: res.data - }) - .catch(err => res = { - error: true, - data: err.response.data + //.then(res => res.data[0]) + .then(res => res.data) + .catch(function(err){ + console.error("ERR Status: "+err.response.status) + console.error("ERR Name: "+err.response.data.message.name) + console.error("ERR Path: "+err.response.data.message.path) + return err.response }) }, - deleteProjectById: function(projectId){ + deleteProjectById: function(projectId:number){ // https://docs.gitlab.com/ee/api/projects.html#delete-project return axios({ method: 'delete', @@ -75,16 +76,15 @@ var gitlab = { 'Authorization': 'Bearer '+config.gitlab.token_readWriteProjects } }) - .then(res => res = { - error: false, - data: res.data - }) - .catch(err => res = { - error: true, - data: err.response.data + .then(res => true) + .catch(function(err) { + console.error("ERR Status: "+err.response.status) + console.error("ERR Name: "+err.response.data.message.name) + console.error("ERR Path: "+err.response.data.message.path) + return false }) }, - getUserProjects: async function(gitlabUserId) { + getUserProjects: async function(gitlabUserId:number) { return axios({ method: 'get', url: 'https://transfer.hft-stuttgart.de/gitlab/api/v4/users/'+gitlabUserId+'/projects?owned=true&visibility=public', @@ -93,9 +93,12 @@ var gitlab = { } }) .then(res => res.data) - .catch(err => console.error(err)) + .catch(function(err) { + console.error(err) + return null + }) }, - getProjectById: async function(projectId) { + getProjectById: async function(projectId:number) { return axios({ method: 'get', url: 'https://transfer.hft-stuttgart.de/gitlab/api/v4/projects/'+projectId, @@ -104,45 +107,22 @@ var gitlab = { } }) .then(res => res.data) - .catch(err => console.error(err.response.status)) + .catch(function(err) { + console.error(err) + return null + }) }, - getProjectPipelineLatestStatus: async function(projectId) { + getProjectPipelineLatestStatus: async function(projectId:number) { return axios({ method: 'get', url: 'https://transfer.hft-stuttgart.de/gitlab/api/v4/projects/'+projectId+'/pipelines' }) .then(res => res.data[0].status) - .catch(err => console.error(err)) - }, - // - // test GraphQL - getGraphqlTest: function(callback) { - axios({ - url: 'https://gitlab.com/api/graphql', - method: 'get', - headers: { - 'Content-Type': 'application/json', - 'Authorization': 'Bearer '+config.gitlab.token_readWriteProjects - }, - data: { - query: `{ - currentUser { - id - username - } - }` - /* query: `{ - projects { - nodes { - id - } - } - }` */ - } - }).then((result) => { - console.log(JSON.stringify(result.data)) - }); + .catch(function(err) { + console.error(err) + return null + }) } } -module.exports = gitlab \ No newline at end of file +export = gitlab \ No newline at end of file diff --git a/functions/helpers.js b/functions/helpers.ts similarity index 69% rename from functions/helpers.js rename to functions/helpers.ts index 4645aec75363bf4110ce33f65b8615dc60902f60..1c3451fc00883bf4307ba324dc98f75a7d76b80f 100644 --- a/functions/helpers.js +++ b/functions/helpers.ts @@ -1,5 +1,5 @@ var helpers = { - stringToArray: function (input){ + stringToArray: function (input:string){ if(input != null){ return input.split(','); }else{ @@ -8,4 +8,4 @@ var helpers = { } }; -module.exports = helpers; \ No newline at end of file +export = helpers; \ No newline at end of file diff --git a/functions/methods.js b/functions/methods.ts similarity index 56% rename from functions/methods.js rename to functions/methods.ts index 0037a7e143bab55cee0d9d48b71263818af86b5a..16143b851a743a4f5e023c4b50913dd695cbcc2b 100644 --- a/functions/methods.js +++ b/functions/methods.ts @@ -1,57 +1,55 @@ -const dbconn_OBSOLETE = require('../config/dbconn') // DO NOT USE THIS FOR NEW FUNCTIONS -const dbconn = require('../config/dbconn2') +import dbconn_OBSOLETE = require('../config/dbconn') // DO NOT USE THIS FOR NEW FUNCTIONS +import dbconn = require('../config/dbconn2') var methods = { // ===================== user db ===================== - registerNewUser: function(data, callback) { - dbconn_OBSOLETE.user.beginTransaction(function(err) { // START TRANSACTION - if (err) { - throw err - } + registerNewUser: function(data:any, callback:any) { + dbconn_OBSOLETE.user.beginTransaction(function(err:any) { // START TRANSACTION + if (err) { throw err } // insert profile - dbconn_OBSOLETE.user.query('INSERT INTO user SET ?', data.profile, function (err, results, fields) { + dbconn_OBSOLETE.user.query('INSERT INTO user SET ?', data.profile, function (err:any, results:any, fields:any) { if (err) { return dbconn_OBSOLETE.user.rollback(function() { throw err }); } - var newUserId = results.insertId + let newUserId:number = results.insertId // set password - var credentialData = { + var credentialData:any = { user_id: newUserId, password: data.password } - dbconn_OBSOLETE.user.query('INSERT INTO credential SET ?', credentialData, function (err, results, fields) { + dbconn_OBSOLETE.user.query('INSERT INTO credential SET ?', credentialData, function (err:any, results:any, fields:any) { if (err) { return dbconn_OBSOLETE.user.rollback(function() { throw err }); } // set default user-project-role - var projectRoleData = { + var projectRoleData:any = { project_id: 1, //M4_LAB role_id: 2, // USER user_id: newUserId } - dbconn_OBSOLETE.user.query('INSERT INTO user_project_role SET ?', projectRoleData, function (err, results, fields) { + dbconn_OBSOLETE.user.query('INSERT INTO user_project_role SET ?', projectRoleData, function (err:any, results:any, fields:any) { if (err) { return dbconn_OBSOLETE.user.rollback(function() { throw err }); } // MLAB-129: INSERT verification token - let verificationData = { + let verificationData:any = { user_id: newUserId, token: data.verificationToken } - dbconn_OBSOLETE.user.query('INSERT INTO verification SET ?', verificationData, function (err, results, fields) { + dbconn_OBSOLETE.user.query('INSERT INTO verification SET ?', verificationData, function (err:any, results:any, fields:any) { if (err) { return dbconn_OBSOLETE.user.rollback(function() { throw err }); } // COMMIT - dbconn_OBSOLETE.user.commit(function(err) { + dbconn_OBSOLETE.user.commit(function(err:any) { if (err) { return dbconn_OBSOLETE.user.rollback(function() { throw err @@ -65,9 +63,9 @@ var methods = { callback(err) }) }, - getUserByEmail: async function(email) { + getUserByEmail: async function(email:any) { try { - let rows = await dbconn.user.promise().query('SELECT id, verificationStatus, salutation, title, firstname, lastname, industry, organisation, speciality, m4lab_idp FROM user WHERE email = "' +email+'"') + let rows:any = await dbconn.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] } @@ -77,9 +75,9 @@ var methods = { } return null }, - getUserEmailById: async function(userId) { + getUserEmailById: async function(userId:number) { try { - let rows = await dbconn.user.promise().query('SELECT email FROM user WHERE id = ' +userId) + let rows:any = await dbconn.user.promise().query('SELECT email FROM user WHERE id = ' +userId) if (rows[0][0]) { return rows[0][0].email } @@ -89,9 +87,9 @@ var methods = { } return null }, - checkUserEmail: async function(email) { + checkUserEmail: async function(email:any) { try { - let rows = await dbconn.user.promise().query('SELECT id, email FROM user WHERE email = "' +email+'"') + let rows:any = await dbconn.user.promise().query('SELECT id, email FROM user WHERE email = "' +email+'"') if (rows[0][0]) { return rows[0][0] } @@ -101,9 +99,9 @@ var methods = { } return null }, - getUserByToken: async function(token) { + getUserByToken: async function(token:any) { try { - let rows = 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 = "' + let 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 = "' +token+'" and resetPasswordExpires > '+Date.now()) if (rows[0][0]) { return rows[0][0] @@ -114,47 +112,38 @@ var methods = { } return null }, - updateUserById: function(userData, callback) { - dbconn_OBSOLETE.user.query('UPDATE user SET ? WHERE id = ' +userData.id, userData, function (err, rows, fields) { - if (err) throw err - callback(err) - }) - }, - updateCredential: function(data, callback) { - dbconn_OBSOLETE.user.query('UPDATE credential SET ? WHERE user_id = ' +data.user_id, data, function (err, rows, fields) { - if (err) throw err - callback(err) - }) + updateUserById: async function(userId:number, userData:any) { + try { + let result:any = await dbconn.user.promise().query('UPDATE user SET ? WHERE id = ' +userId, userData) + return result + } catch (err) { + console.error(err) + } + return null }, - getUserIdByEmail_OBSOLETE: function(email, callback) { - let userId - dbconn_OBSOLETE.user.query('SELECT id FROM user WHERE email = "' +email+'"', function (err, rows, fields) { - if (err) { - throw err - } - else { - if ( rows.length > 0) { - userId = rows[0].id - } - } - callback(userId, err) - }); + updateCredential: async function(data:any) { + try { + let result:any = await dbconn.user.promise().query('UPDATE credential SET ? WHERE user_id = ' +data.user_id, data) + return result + } catch (err) { + console.error(err) + } + return null }, - getUserProjectRole_OBSOLETE: function(userId, callback) { - dbconn_OBSOLETE.user.query('SELECT project_id, role_id FROM user_project_role WHERE user_id = "' +userId+'"', function (err, rows, fields) { + addUserProjectRole_OBSOLETE: function(data:any, callback:any) { + /* + dbconn_OBSOLETE.user.query('INSERT INTO user_project_role SET ?', data, function (err:any, results:any, fields:any){ if (err) throw err - callback(rows, err) - }); - }, - addUserProjectRole: function(data, callback) { - dbconn_OBSOLETE.user.query('INSERT INTO user_project_role SET ?', data, function (err, results, fields){ + callback(err) + }) */ + dbconn.user.query('INSERT INTO user_project_role SET ?', data, function (err:any){ if (err) throw err callback(err) }) }, - getVerificationTokenByUserId: async function(userId) { + getVerificationTokenByUserId: async function(userId:number) { try { - let rows = await dbconn.user.promise().query('SELECT token FROM verification WHERE user_id = "' +userId+'"') + let rows:any = await dbconn.user.promise().query('SELECT token FROM verification WHERE user_id = "' +userId+'"') if (rows[0][0]) { return rows[0][0].token } @@ -164,9 +153,9 @@ var methods = { } return null }, - getUserIdByVerificationToken: async function(token) { + getUserIdByVerificationToken: async function(token:any) { try { - let rows = await dbconn.user.promise().query('SELECT user_id FROM verification WHERE token = "' +token+'"') + let rows:any = await dbconn.user.promise().query('SELECT user_id FROM verification WHERE token = "' +token+'"') if (rows[0][0]) { return rows[0][0].user_id } @@ -178,21 +167,21 @@ var methods = { } return null }, - verifyUserAccount: function(userData, callback) { - dbconn_OBSOLETE.user.beginTransaction(function(err) { // START TRANSACTION + verifyUserAccount: function(userData:any, callback:any) { + dbconn_OBSOLETE.user.beginTransaction(function(err:any) { // START TRANSACTION if (err) { throw err } // update user status - dbconn_OBSOLETE.user.query('UPDATE user SET ? WHERE id =' +userData.id, userData, function (err, rows, fields) { + dbconn_OBSOLETE.user.query('UPDATE user SET ? WHERE id =' +userData.id, userData, function (err:any, rows:any, fields:any) { if (err) { return dbconn_OBSOLETE.user.rollback(function() { throw err }) } // delete verification token - dbconn_OBSOLETE.user.query('DELETE FROM verification WHERE user_id = '+userData.id, function (err, rows, fields) { + dbconn_OBSOLETE.user.query('DELETE FROM verification WHERE user_id = '+userData.id, function (err:any, rows:any, fields:any) { if (err) { return dbconn_OBSOLETE.user.rollback(function() { throw err }) } // COMMIT - dbconn_OBSOLETE.user.commit(function(err) { + dbconn_OBSOLETE.user.commit(function(err:any) { if (err) { return dbconn_OBSOLETE.user.rollback(function() { throw err }) } @@ -203,9 +192,9 @@ var methods = { }) }, /* ===== GitLab ===== */ - getGitlabId: async function(userId) { + getGitlabId: async function(userId:number) { try { - let rows = 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') + let 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') if (rows[0][0]) { return rows[0][0].gitlab_userId } else { @@ -217,12 +206,12 @@ var methods = { return err } }, - addGitlabUser: function(data, callback){ - dbconn_OBSOLETE.user.query('INSERT INTO user_gitlab SET ?', data, function (err) { + addGitlabUser: function(data:any, callback:any){ + dbconn.user.query('INSERT INTO user_gitlab SET ?', data, function (err:any) { if (err) throw err callback(err) }) } }; -module.exports = methods; \ No newline at end of file +export = methods \ No newline at end of file diff --git a/public/js/jquery-ui/i18n/datepicker-de.js b/public/js/jquery-ui/i18n/datepicker-de.js deleted file mode 100644 index e2e61d27b6b4bf33c0fd59e9ebf2d307d0337af3..0000000000000000000000000000000000000000 --- a/public/js/jquery-ui/i18n/datepicker-de.js +++ /dev/null @@ -1,37 +0,0 @@ -/* German initialisation for the jQuery UI date picker plugin. */ -/* Written by Milian Wolff (mail@milianw.de). */ -( function( factory ) { - if ( typeof define === "function" && define.amd ) { - - // AMD. Register as an anonymous module. - define( [ "../widgets/datepicker" ], factory ); - } else { - - // Browser globals - factory( jQuery.datepicker ); - } -}( function( datepicker ) { - -datepicker.regional.de = { - closeText: "Schließen", - prevText: "<Zurück", - nextText: "Vor>", - currentText: "Heute", - monthNames: [ "Januar","Februar","März","April","Mai","Juni", - "Juli","August","September","Oktober","November","Dezember" ], - monthNamesShort: [ "Jan","Feb","Mär","Apr","Mai","Jun", - "Jul","Aug","Sep","Okt","Nov","Dez" ], - dayNames: [ "Sonntag","Montag","Dienstag","Mittwoch","Donnerstag","Freitag","Samstag" ], - dayNamesShort: [ "So","Mo","Di","Mi","Do","Fr","Sa" ], - dayNamesMin: [ "So","Mo","Di","Mi","Do","Fr","Sa" ], - weekHeader: "KW", - dateFormat: "dd.mm.yy", - firstDay: 1, - isRTL: false, - showMonthAfterYear: false, - yearSuffix: "" }; -datepicker.setDefaults( datepicker.regional.de ); - -return datepicker.regional.de; - -} ) ); \ No newline at end of file diff --git a/routes/account.js b/routes/account.js deleted file mode 100644 index 0f3b2fc7fb77929caa72ef75797173fcecfa3a4d..0000000000000000000000000000000000000000 --- a/routes/account.js +++ /dev/null @@ -1,568 +0,0 @@ -const fs = require('fs') -const SamlStrategy = require('passport-saml').Strategy -const dbconn = require('../config/dbconn') -const methods = require('../functions/methods') -const gitlab = require('../functions/gitlab') -const constants = require('../config/const') -// pwd encryption -const bcrypt = require('bcryptjs'); -const saltRounds = 10; -const salt = 64; // salt length -// forgot pwd -const async = require('async') -const crypto = require('crypto') -const mailer = require('../config/mailer') -const logoDir = 'public/upload/' -const defaultLogo = 'public/default/logo.png' -const tpGitlabURL = 'https://transfer.hft-stuttgart.de/gitlab/' -const tpGitlabPagesURL = 'https://transfer.hft-stuttgart.de/pages/' - -const portalUser = require('../classes/user') -const projectInformation = require('../classes/website') -const projectRepo = require('../classes/repo') - -module.exports = function (app, config, passport, lang) { - - // =========== PASSPORT ======= - passport.serializeUser(function (user, done) { - done(null, user); - }); - - passport.deserializeUser(function (user, done) { - done(null, user); - }); - - var samlStrategy = new SamlStrategy({ - // URL that goes from the Identity Provider -> Service Provider - callbackUrl: config.passport.saml.path, - // Base address to call logout requests - logoutUrl: config.passport.saml.logoutUrl, - - entryPoint: config.passport.saml.entryPoint, - issuer: config.passport.saml.issuer, - identifierFormat: null, - - // Service Provider private key - decryptionPvk: fs.readFileSync(__dirname + '/cert/key.pem', 'utf8'), - // Service Provider Certificate - privateCert: fs.readFileSync(__dirname + '/cert/key.pem', 'utf8'), - // Identity Provider's public key - cert: fs.readFileSync(__dirname + '/cert/cert_idp.pem', 'utf8'), - - validateInResponseTo: false, - disableRequestedAuthnContext: true - }, - function (profile, done) { - return done(null, { - id: profile.nameID, - idFormat: profile.nameIDFormat, - email: profile.email, - firstName: profile.givenName, - lastName: profile.sn - }); - }); - - passport.use(samlStrategy); - - // ============= SAML ============== - app.post(config.passport.saml.path, - passport.authenticate(config.passport.strategy, - { - failureRedirect: '/account/', - failureFlash: true - }), - function (req, res) { - res.redirect('/account/'); - } - ); - - // to generate Service Provider's XML metadata - app.get('/saml/metadata', - function(req, res) { - res.type('application/xml'); - var spMetadata = samlStrategy.generateServiceProviderMetadata(fs.readFileSync(__dirname + '/cert/cert.pem', 'utf8')); - res.status(200).send(spMetadata); - } - ); - - // ======== APP ROUTES - ACCOUNT ==================== - - async function getLoggedInUserData(email) { - let user = await methods.getUserByEmail(email) - if (!user) { - console.log('no user found') - return null - } else { - let loggedInUser = new portalUser( - user.id, email, user.salutation, user.title, user.firstname, user.lastname, user.industry, user.organisation, user.speciality, user.m4lab_idp, null, user.verificationStatus - ) - - let userGitlabId = await methods.getGitlabId(loggedInUser.id) - if (userGitlabId) { - loggedInUser.setGitlabUserId(userGitlabId) - } - return loggedInUser - } - } - - app.get('/', async function (req, res) { - if ( !req.isAuthenticated() ) { - res.redirect('/login') - } else { - let loggedInUser = await getLoggedInUserData(req.user.email) - - res.render(lang+'/account/home', { - user: loggedInUser - }); - } - }); - - app.get('/login', - passport.authenticate(config.passport.strategy, { - successRedirect: '/', - failureRedirect: '/login' - }) - ) - - app.get('/logout', function (req, res) { - if (req.user == null) { - return res.redirect('/'); - } - - req.user.nameID = req.user.id; - req.user.nameIDFormat = req.user.idFormat; - return samlStrategy.logout(req, function(err, uri) { - req.logout(); - - if ( req.session ) { - req.session.destroy((err) => { - if(err) { - return console.log(err); - } - }); - } - - return res.redirect(uri); - }); - }); - - app.get('/profile', async function (req, res) { - if ( !req.isAuthenticated() ) { - res.redirect('/login') - } else { - let loggedInUser = await getLoggedInUserData(req.user.email) - if(loggedInUser.getVerificationStatus() != 1) { - res.redirect('/account/') - } else { - res.render(lang+'/account/profile', { - user: loggedInUser - }) - } - } - }) - - app.get('/services', async function(req, res){ - if( !req.isAuthenticated() ) { - res.redirect('/login') - } else { - let loggedInUser = await getLoggedInUserData(req.user.email) - if(loggedInUser.getVerificationStatus() != 1) { // unverified users - res.redirect('/account/') - } else { - let gitlabReposArr = [] - let gitlabPagesArr = [] - - if(loggedInUser.getGitlabUserId()) { // for users who have activated their gitlab account - let userProjects = await gitlab.getUserProjects(loggedInUser.getGitlabUserId()) - if (!userProjects) { - console.error("something went wrong") - res.status(500).render(lang+'/500', { error: "something went wrong" }) - } - - for (project in userProjects) { - if (userProjects[project].tag_list.includes('website')) { - let page = { - projectInformation: new projectInformation(loggedInUser.getGitlabUserId(), userProjects[project].id, userProjects[project].name, - userProjects[project].description, userProjects[project].avatar_url, userProjects[project].path_with_namespace), - pipelineStatus: await gitlab.getProjectPipelineLatestStatus(userProjects[project].id) - } - gitlabPagesArr.push(page) - } else { - let repo = new projectRepo(loggedInUser.getGitlabUserId(), userProjects[project].id, userProjects[project].name, - userProjects[project].description, userProjects[project].avatar_url, userProjects[project].path_with_namespace) - gitlabReposArr.push(repo) - } - } - - res.render(lang+'/account/services', { - user: loggedInUser, - gitlabRepos: gitlabReposArr, - gitlabPages: gitlabPagesArr - }) - } else { // for users who have not activated their gitlab account yet - let gitlabUser = await gitlab.getUserByEmail(loggedInUser.getEmail()) - if (!gitlabUser) { - res.render(lang+'/account/services', { - user: loggedInUser, - gitlabRepos: null, - gitlabPages: null - }) - } else { - let gitlabActivationData = { - user_id: loggedInUser.getId(), - gitlab_userId: gitlabUser.id} - // RS: update to await? - methods.addGitlabUser(gitlabActivationData, function(err){ - if(err) { - res.status(500).render(lang+'/500', { error: err }) - } else { - res.redirect('/account/services') - } - }) - } - } - } - } - }) - - app.get('/security', async function (req, res) { - if ( !req.isAuthenticated() ) { - res.redirect('/login') - } else { - let loggedInUser = await getLoggedInUserData(req.user.email) - if(loggedInUser.getVerificationStatus() == 1 && loggedInUser.getIdpStatus() == 1) { - res.render(lang+'/account/security', { - user: loggedInUser - }) - } else { - res.redirect('/account/') - } - } - }) - - app.post('/updateProfile', async function (req, res) { - var userData = { - 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, - } - - if ( !req.isAuthenticated() ) { - res.redirect('/login') - } else { - let loggedInUser = await getLoggedInUserData(req.user.email) - if (userData.email) { - dbconn.user.query('UPDATE user SET ? WHERE email = "' +userData.email+'"', userData, function (err, rows, fields) { - if (err) { - res.flash('error', "Failed") - } - else { - loggedInUser.updateProfile(userData.salutation, userData.title, userData.firstname, userData.lastname, userData.email, - userData.organisation, userData.industry, userData.speciality) - res.flash('success', 'Ihr Benutzerprofil wurde aktualisiert!') - } - res.redirect('/account/profile'); - }) - } - } - }); - - app.post('/changePwd', async function (req, res) { - if( !req.isAuthenticated() ) { - res.redirect('/login') - } else { - let loggedInUser = await getLoggedInUserData(req.user.email) - - var currPwd = req.body.inputCurrPwd - var newPwd = req.body.inputNewPwd - var retypePwd = req.body.inputConfirm - - // update - get userId from loggedInUser - dbconn.user.query('SELECT password FROM credential WHERE user_id='+loggedInUser.getId(), function (err, rows, fields) { - if (err) { - console.error(err) - res.status(500).render(lang+'/500', { error: err }) - } - var userPwd = rows[0].password - - // check if the password is correct - bcrypt.compare(currPwd, userPwd, function(err, isMatch) { - if (err) { - console.error(err) - res.status(500).render(lang+'/500', { error: err }) - } else if (!isMatch) { - res.flash('error', "Das Passwort ist leider falsch. Bitte überprüfen Sie Ihre Eingabe.") - res.redirect('/account/security') - } else { - 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 { - // update password - bcrypt.genSalt(saltRounds, function(err, salt) { - bcrypt.hash(newPwd, salt, function(err, hash) { - var credentialData = { - password: hash, - user_id: userId - } - methods.updateCredential(credentialData, function(err){ - if (err) { - res.flash('error', "Datenbankfehler: Passwort kann nicht geändert werden.") - throw err - } else { - res.flash('success', "Passwort aktualisiert!") - mailer.options.to = req.user.email - mailer.options.subject = constants.updatePasswordMailSubject - mailer.options.html = constants.updatePasswordMailContent+'<div>'+constants.mailSignature+'</div>' - mailer.transport.sendMail(mailer.options, function(err) { - if (err) { console.log(err) } - }); - } - res.redirect('/account/security') - }) - }); - }); - } - } - }) - }) - } - }); - - app.get('/resendVerificationEmail', async function(req, res){ - if (!req.isAuthenticated) { - res.redirect('/login') - } else { - let loggedInUser = await getLoggedInUserData(req.user.email) - if (!loggedInUser) { - res.redirect('/login') - } else { - let token = await methods.getVerificationTokenByUserId(loggedInUser.id) - if (!token) { - res.send(false) - } else { - // send email - var emailSubject = "Bitte bestätigen Sie Ihr M4_LAB Benutzerkonto" - var 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 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 + - '</div>'; - mailer.options.to = loggedInUser.email; - mailer.options.subject = emailSubject; - mailer.options.html = emailContent; - mailer.transport.sendMail(mailer.options, function(err) { - if (err) { - console.log('cannot send email') - throw err - } - }) - res.send(true) - } - } - } - }) - - // ============= NEW GITLAB PAGES =========================== - - app.get('/newInformation', async function(req, res){ - if ( !req.isAuthenticated() ) { - res.redirect('/login') - } else { - let loggedInUser = await getLoggedInUserData(req.user.email) - let gitlabUser = await gitlab.getUserByEmail(loggedInUser.getEmail()) - if (!gitlabUser) { // no user found - res.redirect('/account/services') - } else { - res.render(lang+'/account/newInformation', { - user: loggedInUser, - gitlabUsername: gitlabUser.username - }) - } - } - }) - app.post('/newInformation', async function(req, res) { - if( !req.isAuthenticated() ) { - res.redirect('/login') - } else { - let loggedInUser = await getLoggedInUserData(req.user.email) - - if (!req.body.name && !req.body.description) { - res.flash('error', 'Bitte geben Sie die benötigten Daten ein') - res.redirect('/account/newInformation') - } else { - let projectName = req.body.name.toLowerCase().replace(/\s/g, '-') - let projectDesc = req.body.description - let projectTemplate = req.body.template - let newInformation = new projectInformation(loggedInUser.getGitlabUserId(), null, projectName, projectDesc, null, null) - let newLogoFile = defaultLogo - - if (req.files) { newLogoFile = req.files.logo } - - async.waterfall([ - function(callback){ // upload logo - if (!req.files) { - callback(null, newLogoFile) - } else { - newLogoFile.mv(logoDir + newLogoFile.name, function(err) { - newLogoFile = logoDir+newLogoFile.name - callback(err, newLogoFile) - }) - } - }, - async function(newLogoFile){ // create a new GitLab Page - let newPages = await gitlab.createNewPages(newInformation, newLogoFile, projectTemplate) - if (newPages.error) { - 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. ") - } - res.redirect('/account/newInformation') - } else { - let newPagesData = newPages.data - - res.flash("success", "Ihre Webseite wurde erstellt, aber noch nicht veröffentlicht. Um Ihre Webseite endgültig zu veröffentlichen, "+ - "schließen Sie die Einrichtung gemäß unten stehender Anleitung ab.") - /* res.flash("success", "Your website will be published AFTER you complete your website by following the provided guideline below."+ - "\r\n Your website URL: "+tpGitlabPagesURL+newPagesData.path_with_namespace+"/home/") */ - res.redirect('/account/updateInformation?id='+newPagesData.id) - } - } - ], function (err) { - if(err) console.log(err) - // remove logo - if (req.files) { - fs.unlink(newLogoFile, (err) => { - if(err) console.log(err) - }) - } - }) - } - } - }) - - app.get('/updateInformation', async function(req, res){ - if( !req.isAuthenticated() ) { - res.redirect('/login') - } else { - let loggedInUser = await getLoggedInUserData(req.user.email) - - if(!req.query.id) { - res.redirect('/account/services') - } else { - let project = await gitlab.getProjectById(req.query.id) - if (!project) { - console.log(" ========= Error or no project found") - res.redirect('/account/services') - } 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()) { - console.log(" ========= Access denied: Not your project") - res.redirect('/account/services') - } else { - let curInformation = new projectInformation(loggedInUser.getGitlabUserId(), req.query.id, project.name, project.description, - project.avatar_url, project.path_with_namespace) - - res.render(lang+'/account/updateInformation', { - user: loggedInUser, - information: curInformation - }) - } - } - } - }) - // update a website - app.post('/updateInformation', async function(req, res){ - if( !req.isAuthenticated() ) { - res.redirect('/login') - } else { - let loggedInUser = await getLoggedInUserData(req.user.email) - - if (!req.body.name && !req.body.description) { - res.flash('error', 'Bitte geben Sie die benötigten Daten ein') - res.redirect('/account/updateInformation') - } else { - let projectName = req.body.name.toLowerCase().replace(/\s/g, '-') - let projectDesc = req.body.description - let updatedInformation = new projectInformation(loggedInUser.getGitlabUserId(), req.query.id, projectName, projectDesc, null, null) - let newLogoFile - - async.waterfall([ - function(callback){ // upload logo - if(!req.files) { - callback(null, newLogoFile) - } else { - newLogoFile = req.files.logo - newLogoFile.mv(logoDir + newLogoFile.name, function(err) { - newLogoFile = logoDir + newLogoFile.name - callback(err, newLogoFile) - }) - } - }, - async function(newLogoFile, callback){ // update gitlab page - let updatedPages = await gitlab.updateProject(updatedInformation, newLogoFile) - let pagesData = updatedPages.data - if (updatedPages.error) { - if(pagesData.message.name == "has already been taken") { - res.flash("error", "Der Projektname ist bereits vergeben, bitte wählen Sie einen anderen Namen.") - } else { - res.flash("error", "Ein Fehler ist aufgetreten. Bitte versuchen Sie es erneut. ") - } - } else { - updatedInformation.setLogo(pagesData.avatar_url) - updatedInformation.setPath(pagesData.path) - res.flash("success", "Ihre Website wurde aktualisiert") - } - res.redirect('/account/updateInformation?id='+updatedInformation.getId()) - } - ], function (err) { - if(err) console.log(err) - if(newLogoFile){ // remove logo - fs.unlink(newLogoFile, (err) => { - if(err) console.log(err) - }) - } - }) - } - } - }) - - app.delete('/deleteProject', async function(req, res){ - console.log("delete project") - - if( !req.isAuthenticated() ) { - res.redirect('/login') - } else { - let loggedInUser = await getLoggedInUserData(req.user.email) - let projectId = req.body.id - - if (projectId) { - // check if the owner is valid - let project = await gitlab.getProjectById(projectId) - if (!project) { - 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()) { - console.log(" ========= Access denied: Not your project") - } else { - // delete project - let project = await gitlab.deleteProjectById(projectId) - if (project.error) { - res.flash("error", "Project cannot be deleted. Please try again.") - } - } - } - res.redirect('/account/services') - } - }) - -} \ No newline at end of file diff --git a/routes/account.ts b/routes/account.ts new file mode 100644 index 0000000000000000000000000000000000000000..da1b2fb9e4907cfb424dc8edd6ae085be6beba37 --- /dev/null +++ b/routes/account.ts @@ -0,0 +1,596 @@ +import fs from 'fs' +import async from 'async' +import bcrypt from 'bcryptjs' +import * as passportSaml from 'passport-saml' +import dbconn from '../config/dbconn' +import methods from '../functions/methods' +import gitlab from '../functions/gitlab' +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' + +const SamlStrategy = passportSaml.Strategy +const saltRounds = 10; +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) { + + // =========== PASSPORT ======= + passport.serializeUser(function (user:any, done:any) { + done(null, user); + }); + + passport.deserializeUser(function (user:any, done:any) { + done(null, user); + }); + + var samlStrategy = new SamlStrategy({ + // URL that goes from the Identity Provider -> Service Provider + callbackUrl: config.passport.saml.path, + // Base address to call logout requests + logoutUrl: config.passport.saml.logoutUrl, + + entryPoint: config.passport.saml.entryPoint, + issuer: config.passport.saml.issuer, + identifierFormat: undefined, + + // Service Provider private key + decryptionPvk: fs.readFileSync(__dirname + '/cert/key.pem', 'utf8'), + // Service Provider Certificate + privateCert: fs.readFileSync(__dirname + '/cert/key.pem', 'utf8'), + // Identity Provider's public key + cert: fs.readFileSync(__dirname + '/cert/cert_idp.pem', 'utf8'), + + validateInResponseTo: false, + disableRequestedAuthnContext: true + }, + function (profile:any, done:any) { + return done(null, { + id: profile.nameID, + idFormat: profile.nameIDFormat, + email: profile.email, + firstName: profile.givenName, + lastName: profile.sn + }); + }); + + passport.use(samlStrategy); + + // ============= SAML ============== + app.post(config.passport.saml.path, + passport.authenticate(config.passport.strategy, + { + failureRedirect: '/account/', + failureFlash: true + }), + function (req:any, res:any) { + res.redirect('/account/'); + } + ); + + // to generate Service Provider's XML metadata + app.get('/saml/metadata', + function(req:any, res:any) { + res.type('application/xml'); + var spMetadata = samlStrategy.generateServiceProviderMetadata(fs.readFileSync(__dirname + '/cert/cert.pem', 'utf8')); + res.status(200).send(spMetadata); + } + ); + + // ======== APP ROUTES - ACCOUNT ==================== + + async function getLoggedInUserData(email:string) { + let user = await methods.getUserByEmail(email) + if (!user) { + console.log('no user found') + return null + } else { + let loggedInUser = new portalUser( + user.id, email, user.salutation, user.title, user.firstname, user.lastname, user.industry, user.organisation, user.speciality, user.m4lab_idp, user.verificationStatus + ) + + let userGitlabId = await methods.getGitlabId(loggedInUser.id) + if (userGitlabId) { + loggedInUser.setGitlabUserId(userGitlabId) + } + return loggedInUser + } + } + + app.get('/', async function (req:any, res:any) { + if ( !req.isAuthenticated() ) { + res.redirect('/login') + } else { + let loggedInUser = await getLoggedInUserData(req.user.email) + + res.render(lang+'/account/home', { + user: loggedInUser + }); + } + }); + + app.get('/login', + passport.authenticate(config.passport.strategy, { + successRedirect: '/', + failureRedirect: '/login' + }) + ) + + app.get('/logout', function (req:any, res:any) { + if (req.user == null) { + return res.redirect('/'); + } + + req.user.nameID = req.user.id; + req.user.nameIDFormat = req.user.idFormat; + return samlStrategy.logout(req, function(err:any, uri:any) { + req.logout(); + + if ( req.session ) { + req.session.destroy((err:any) => { + if(err) { + return console.log(err); + } + }); + } + + return res.redirect(uri); + }); + }); + + app.get('/profile', async function (req:any, res:any) { + if ( !req.isAuthenticated() ) { + res.redirect('/login') + } else { + let loggedInUser = await getLoggedInUserData(req.user.email) + if (!loggedInUser) { // null user + res.redirect('/account/') + } else { + if(loggedInUser.getVerificationStatus() != 1) { + res.redirect('/account/') + } else { + res.render(lang+'/account/profile', { + user: loggedInUser + }) + } + } + + } + }) + + app.get('/services', async function(req:any, res:any){ + if( !req.isAuthenticated() ) { + res.redirect('/login') + } else { + let loggedInUser = await getLoggedInUserData(req.user.email) + if (!loggedInUser) { // null user + res.redirect('/account/') + } else { + if(loggedInUser.getVerificationStatus() != 1) { // unverified users + res.redirect('/account/') + } else { + let gitlabReposArr = [] + let gitlabPagesArr = [] + + if(loggedInUser.getGitlabUserId()) { // for users who have activated their gitlab account + let userProjects = await gitlab.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')) { + let 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 gitlab.getProjectPipelineLatestStatus(userProjects[project].id) + } + gitlabPagesArr.push(page) + } else { + let 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, + gitlabPages: gitlabPagesArr + }) + } else { // for users who have not activated their gitlab account yet + let gitlabUser = await gitlab.getUserByEmail(loggedInUser.getEmail()) + if (!gitlabUser) { + res.render(lang+'/account/services', { + user: loggedInUser, + gitlabRepos: null, + gitlabPages: null + }) + } else { + let gitlabActivationData = { + user_id: loggedInUser.getId(), + gitlab_userId: gitlabUser.id} + + methods.addGitlabUser(gitlabActivationData, function(err:any){ + if(err) { + res.status(500).render(lang+'/500', { error: err }) + } else { + res.redirect('/account/services') + } + }) + } + } + } + } + } + }) + + app.get('/security', async function (req:any, res:any) { + if ( !req.isAuthenticated() ) { + res.redirect('/login') + } else { + let loggedInUser = await getLoggedInUserData(req.user.email) + if (!loggedInUser) { // null user + res.redirect('/account/') + } else { + if(loggedInUser.getVerificationStatus() == 1 && loggedInUser.getIdpStatus() == 1) { + res.render(lang+'/account/security', { + user: loggedInUser + }) + } else { + res.redirect('/account/') + } + } + } + }) + + app.post('/updateProfile', async function (req:any, res:any) { + if ( !req.isAuthenticated() ) { + res.redirect('/login') + } else { + let loggedInUser = await getLoggedInUserData(req.user.email) + if (!loggedInUser) { // null user + res.redirect('/account/') + } else { + let userData = { + 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, + } + let result = await methods.updateUserById(loggedInUser.getId(), userData) + if (!result) { + res.flash('error', "Failed") + } else { + loggedInUser.updateProfile(userData.salutation, userData.title, userData.firstname, userData.lastname, userData.email, + userData.organisation, userData.industry, userData.speciality) + res.flash('success', 'Ihr Benutzerprofil wurde aktualisiert!') + } + res.redirect('/account/profile') + } + + } + }); + + app.post('/account/changePwd', async function (req:any, res:any) { + if( !req.isAuthenticated() ) { + res.redirect('/login') + } else { + let loggedInUser = await getLoggedInUserData(req.user.email) + + if (!loggedInUser) { // null user + res.redirect('/account/') + } else { + let currPwd = req.body.inputCurrPwd + let newPwd = req.body.inputNewPwd + let retypePwd = req.body.inputConfirm + + dbconn.user.query('SELECT password FROM credential WHERE user_id='+loggedInUser.getId(), function (err, rows, fields) { + if (err) { + console.error(err) + res.status(500).render(lang+'/500', { error: err }) + } + var userPwd = rows[0].password + + // check if the password is correct + bcrypt.compare(currPwd, userPwd, function(err, isMatch) { + if (err) { + console.error(err) + res.status(500).render(lang+'/500', { error: err }) + } else if (!isMatch) { + res.flash('error', "Das Passwort ist leider falsch. Bitte überprüfen Sie Ihre Eingabe.") + res.redirect('/account/security') + } else { + 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 { + // update password + bcrypt.genSalt(saltRounds, function(err, salt) { + bcrypt.hash(newPwd, salt, async function(err, hash) { + var credentialData = { + password: hash, + user_id: loggedInUser!.getId() + } + + let result = await methods.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 notifiaction email + mailer.options.to = loggedInUser!.getEmail() + 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('/account/security') + + }); + }); + } + } + }) + }) + } + } + }); + + app.get('/resendVerificationEmail', async function(req:any, res:any){ + if (!req.isAuthenticated) { + res.redirect('/login') + } else { + let loggedInUser = await getLoggedInUserData(req.user.email) + if (!loggedInUser) { + res.redirect('/login') + } else { + let token = await methods.getVerificationTokenByUserId(loggedInUser.id) + if (!token) { + res.send(false) + } else { + // send email + var emailSubject = "Bitte bestätigen Sie Ihr M4_LAB Benutzerkonto" + var 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 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 + + '</div>'; + mailer.options.to = loggedInUser.email; + mailer.options.subject = emailSubject; + mailer.options.html = emailContent; + mailer.transport.sendMail(mailer.options, function(err:any) { + if (err) { + console.log('cannot send email') + throw err + } + }) + res.send(true) + } + } + } + }) + + // ============= NEW GITLAB PAGES =========================== + + app.get('/newInformation', async function(req:any, res:any){ + if ( !req.isAuthenticated() ) { + res.redirect('/login') + } else { + let loggedInUser = await getLoggedInUserData(req.user.email) + if (!loggedInUser) { + res.redirect('/login') + } else { + let gitlabUser = await gitlab.getUserByEmail(loggedInUser.getEmail()) + if (!gitlabUser) { // no user found + res.redirect('/account/services') + } else { + res.render(lang+'/account/newInformation', { + user: loggedInUser, + gitlabUsername: gitlabUser.username + }) + } + } + } + }) + app.post('/newInformation', async function(req:any, res:any) { + if( !req.isAuthenticated() ) { + res.redirect('/login') + } else { + let loggedInUser = await getLoggedInUserData(req.user.email) + if (!loggedInUser) { + res.redirect('/login') + } else { + if (!req.body.name && !req.body.description) { + res.flash('error', 'Bitte geben Sie die benötigten Daten ein') + res.redirect('/account/newInformation') + } else { + let projectName = req.body.name.toLowerCase().replace(/\s/g, '-') + let projectDesc = req.body.description + let projectTemplate = req.body.template + let newInformation = new projectInformation(loggedInUser.getGitlabUserId()!, projectName, projectDesc) + let newLogoFile = defaultLogo + + if (req.files) { newLogoFile = req.files.logo } + + async.waterfall([ + function(callback:any){ // upload logo + if (!req.files) { + callback(null, newLogoFile) + } else { + newLogoFile.mv(logoDir + newLogoFile.name, function(err:any) { + newLogoFile = logoDir+newLogoFile.name + callback(err, newLogoFile) + }) + } + }, + async function(newLogoFile:any){ // create a new GitLab Page + let newPages = await gitlab.createNewPages(newInformation, newLogoFile, projectTemplate) + if (newPages.status) { + 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. ") + } + res.redirect('/account/newInformation') + } else { + res.flash("success", "Ihre Webseite wurde erstellt, aber noch nicht veröffentlicht. Um Ihre Webseite endgültig zu veröffentlichen, "+ + "schließen Sie die Einrichtung gemäß unten stehender Anleitung ab.") + res.redirect('/account/updateInformation?id='+newPages.id) + } + } + ], function (err) { + if(err) console.log(err) + // remove logo + if (req.files) { + fs.unlink(newLogoFile, (err) => { + if(err) console.log(err) + }) + } + }) + } + } + } + }) + + app.get('/updateInformation', async function(req:any, res:any){ + if( !req.isAuthenticated() ) { + res.redirect('/login') + } else { + let loggedInUser = await getLoggedInUserData(req.user.email) + + if (!loggedInUser) { + res.redirect('/login') + } else { + if(!req.query.id) { + res.redirect('/account/services') + } else { + let project = await gitlab.getProjectById(req.query.id) + if (!project) { + console.log(" ========= Error or no project found") + res.redirect('/account/services') + } 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()) { + console.log(" ========= Access denied: Not your project") + res.redirect('/account/services') + } else { + let curInformation = new projectInformation(loggedInUser.getGitlabUserId()!, project.name, project.description, + req.query.id, project.avatar_url, project.path_with_namespace) + + res.render(lang+'/account/updateInformation', { + user: loggedInUser, + information: curInformation + }) + } + } + } + } + }) + // update a website + app.post('/updateInformation', async function(req:any, res:any){ + if( !req.isAuthenticated() ) { + res.redirect('/login') + } else { + let loggedInUser = await getLoggedInUserData(req.user.email) + + if (!loggedInUser) { + res.redirect('/login') + } else { + if (!req.body.name && !req.body.description) { + res.flash('error', 'Bitte geben Sie die benötigten Daten ein') + res.redirect('/account/updateInformation') + } else { + let projectName = req.body.name.toLowerCase().replace(/\s/g, '-') + let projectDesc = req.body.description + let updatedInformation = new projectInformation(loggedInUser.getGitlabUserId()!, projectName, projectDesc, req.query.id) + let newLogoFile:any + + async.waterfall([ + function(callback:any){ // upload logo + if(!req.files) { + callback(null, newLogoFile) + } else { + newLogoFile = req.files.logo + newLogoFile.mv(logoDir + newLogoFile.name, function(err:any) { + newLogoFile = logoDir + newLogoFile.name + callback(err, newLogoFile) + }) + } + }, + async function(newLogoFile:any){ // update gitlab page + let updatedPages = await gitlab.updateProject(updatedInformation, newLogoFile) + + if (updatedPages.status) { + if(updatedPages.data.message.name == "has already been taken") { + res.flash("error", "Der Projektname '"+projectName+"' ist bereits vergeben, bitte wählen Sie einen anderen Namen.") + } else { + res.flash("error", "Ein Fehler ist aufgetreten. Bitte versuchen Sie es erneut. ") + } + } else { + updatedInformation.setLogo(updatedPages.avatar_url) + updatedInformation.setPath(updatedPages.path) + res.flash("success", "Ihre Website wurde aktualisiert") + } + + res.redirect('/account/updateInformation?id='+updatedInformation.getId()) + } + ], function (err) { + if(err) console.log(err) + if(newLogoFile){ // remove logo + fs.unlink(newLogoFile, (err) => { + if(err) console.log(err) + }) + } + }) + } + } + } + }) + + app.delete('/deleteProject', async function(req:any, res:any){ + if( !req.isAuthenticated() ) { + res.redirect('/login') + } else { + let loggedInUser = await getLoggedInUserData(req.user.email) + if (!loggedInUser) { + res.redirect('/login') + } else { + let projectId = req.body.id + + if (projectId) { + // check if the owner is valid + let project = await gitlab.getProjectById(projectId) + if (!project) { + 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()) { + console.log(" ========= Access denied: Not your project") + } else { + let isDeleted = await gitlab.deleteProjectById(projectId) + if (!isDeleted) { + res.flash("error", "Project cannot be deleted. Please try again.") + } + } + } + res.redirect('/account/services') + } + } + }) + +} \ No newline at end of file diff --git a/routes/public.js b/routes/public.js deleted file mode 100644 index e6cd132aafd32c7f576bf6c5cf43b368cc232aca..0000000000000000000000000000000000000000 --- a/routes/public.js +++ /dev/null @@ -1,296 +0,0 @@ -const methods = require('../functions/methods') -const async = require('async') -const mailer = require('../config/mailer') -const constants = require('../config/const') -// pwd encryption -const crypto = require('crypto') -const bcrypt = require('bcryptjs') -const saltRounds = 10 -const salt = 64 - -module.exports = function (app, config, lang) { - - // ================== NEW USERS REGISTRATION ====================== - - app.get('/registration', function(req, res) { - res.render(lang+'/account/registration') - }) - app.post('/registration', function(req, res) { - // user data - var curDate = new Date() - var userData = { - 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) - } - - var userEmail = userData.email - var pos = userEmail.indexOf('@') - var emailLength = userEmail.length - var emailDomain = userEmail.slice(pos, emailLength); - - if ( emailDomain.toLowerCase() == "@hft-stuttgart.de") { - res.flash('error', "Fehlgeschlagen: HFT-Account") - res.redirect('/account/registration') - } else { - let token - async.waterfall([ - function(done) { - crypto.randomBytes(20, function(err, buf) { - token = buf.toString('hex'); - done(err, token); - }); - }, - // encrypt password - function(token, done) { - bcrypt.genSalt(saltRounds, function(err, salt) { - bcrypt.hash(req.body.inputPassword, salt, function(err, hash) { - var newAccount = { - profile: userData, - password: hash, - verificationToken: token - } - done(err, newAccount) - }); - }); - }, - // save data - function(newAccount, err) { - methods.registerNewUser(newAccount, function(err){ - if (err) { - res.flash('error', "Fehlgeschlagen") - } - else { - // send email - var emailSubject = "Bitte bestätigen Sie Ihr M4_LAB Benutzerkonto" - var 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='+config.app.host+'/verifyAccount?token='+token+'>diesen Link</a> ' + - '<br/><br/>' + - 'Ohne Bestätigung Ihres Kontos müssen wir Ihr Konto leider nach 7 Tagen löschen.</p><br/>' + constants.mailSignature + - '</div>'; - mailer.options.to = req.body.inputEmail; - mailer.options.subject = emailSubject; - mailer.options.html = emailContent; - mailer.transport.sendMail(mailer.options, function(err) { - if (err) { - console.error('cannot send email') - 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') - }) - } - ]) - } - }) - - // =================== USERS VERIFICATION ========================= - - app.get("/verifyAccount", async function(req, res){ - let userId = await methods.getUserIdByVerificationToken(req.query.token) - if (!userId) { - // no user found - res.render(lang+'/account/verification', { - status: null - }) - } else { - // a user found, verify the account - let userData = { - id: userId, - verificationStatus: 1 - } - methods.verifyUserAccount(userData, async function(err){ - if (err) { - console.log("Error: "+err) - res.render(lang+'/account/verification', { - status: false - }); - } else { - // send welcome email after successful account verification - let userEmail = await methods.getUserEmailById(userId) - if (!userEmail) { - res.render(lang+'/account/verification', { - status: false - }) - } else { - // send email - var emailSubject = "Herzlich willkommen" - var 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.transport.sendMail(mailer.options, function(err) { - if (err) { - console.log('cannot send email') - throw err - } - }) - - res.render(lang+'/account/verification', { - status: true - }) - } - } - }) - } - }) - - // ==================== FORGOT PASSWORD =========================== - - app.get('/forgotPwd', function (req, res) { - res.render(lang+'/account/forgotPwd', { - user: req.user - }) - }) - app.post('/forgotPwd', function(req, res) { - let emailAddress = req.body.inputEmail - async.waterfall([ - function(done) { - crypto.randomBytes(20, function(err, buf) { - var token = buf.toString('hex') - done(err, token) - }) - }, - async function(token) { - let user = await methods.checkUserEmail(emailAddress) - if (!user) { - console.log('no user found') - } else { - var emailSubject = "Ihre Passwort-Anfrage an das Transferportal der HFT Stuttgart"; - var 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: '+config.app.host+'/reset/' + token + '<br/>' + - 'Dieser Link ist aus Sicherheitsgründen nur für 1 Stunde gültig.<br/></p>' + constants.mailSignature + '</div>' - - var credentialData = { - user_id: user.id, - resetPasswordToken: token, - resetPasswordExpires: Date.now() + 3600000 // 1 hour - } - methods.updateCredential(credentialData, function(err) { - if (err) { console.error(err) } - }) - - // send email - mailer.options.to = emailAddress - mailer.options.subject = emailSubject - mailer.options.html = emailContent - mailer.transport.sendMail(mailer.options, function(err) { - if (err) { console.error(err) } - }) - } - } - ], function(err) { - 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 ' + emailAddress + ' versendet.') - } - res.redirect('/account/forgotPwd') - }) - }) - - // reset - app.get('/reset/:token', async function(req, res) { - let user = await methods.getUserByToken(req.params.token) - if (!user) { - res.flash('error', 'Der Schlüssel zum zurücksetzen des Passworts ist ungültig oder abgelaufen.') - res.redirect('/account/forgotPwd') - } else { - res.render(lang+'/account/reset') - } - }) - app.post('/reset/:token', async function(req, res) { - var newPwd = req.body.inputNewPwd - - let user = await methods.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, function(err, hash) { - var credentialData = { - password: hash, - user_id: user.user_id - } - // update password - methods.updateCredential(credentialData, function(err){ - if (err) { - res.flash('error', "Datenbankfehler: Passwort kann nicht geändert werden.") - throw err - } else { - res.flash('success', "Passwort aktualisiert!") - - // send notifiaction email - mailer.options.to = user.email - mailer.options.subject = constants.updatePasswordMailSubject - mailer.options.html = constants.updatePasswordMailContent+'<div>'+constants.mailSignature+'</div>' - mailer.transport.sendMail(mailer.options, function(err) { - if (err) { console.log(err) } - }) - - res.redirect('/login') - } - }) - }); - }); - } - - }) - - // ======================= CONTACT FORM =========================== - - app.get('/contact', function (req, res) { - res.render(lang+'/account/contact', { - user: req.user - }) - }) - app.post('/contact', function(req, res, next) { - //methods.currentDate(); - let emailAddress = req.body.inputEmail; - let supportAddress = "support-transfer@hft-stuttgart.de"; - let inputName = req.body.name; - let inputContent = req.body.message; - let emailContent = "Es wurde eine Anfrage an das Transferportal gestellt: \n\n NAME: " + inputName + "\n NACHRICHT:\n "+ inputContent; - let emailSubject = "Ihre Anfrage an das Transferportal"; - async.waterfall([ - function(done) { - // send email - mailer.options.to = supportAddress; - mailer.options.cc = emailAddress; - mailer.options.subject = emailSubject; - mailer.options.text = emailContent; - mailer.transport.sendMail(mailer.options, function(err) { - done(err, 'done'); - }); - } - ], function(err) { - if (err) { - console.error(err) - res.flash('error', 'Ein Fehler ist aufgetreten. Bitte versuchen Sie es erneut.'); - } - else { - res.flash('success', 'Vielen Dank für Ihre Anfrage. Wir melden uns baldmöglichst bei Ihnen. Eine Kopie Ihrer Anfrage wurde an ' + emailAddress + ' versandt.'); - } - res.redirect('/account/contact') - }) - }) - -} \ No newline at end of file diff --git a/routes/public.ts b/routes/public.ts new file mode 100644 index 0000000000000000000000000000000000000000..a5c7bd132bd78b93e6f0c70777e69a80cc1c08d7 --- /dev/null +++ b/routes/public.ts @@ -0,0 +1,292 @@ +import async from 'async' +import bcrypt from 'bcryptjs' +import methods from '../functions/methods' +import mailer from '../config/mailer' +import constants from '../config/const' + +const saltRounds:number = 10 +const salt:number = 64 + +export = function (app:any, config:any, lang:string) { + + // ================== NEW USERS REGISTRATION ====================== + app.get('/registration', function(req:any, res:any) { + res.render(lang+'/account/registration') + }) + app.post('/registration', function(req:any, res:any) { + // user data + var curDate:Date = new Date() + var 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) + } + + var userEmail:any = userData.email + var pos:number = userEmail.indexOf('@') + var emailLength:number = userEmail.length + var 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 = ''; + let 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) { + var newAccount:any = { + profile: userData, + password: hash, + verificationToken: token + } + done(err, newAccount) + }); + }); + }, + // save data + function(newAccount:any, err:any) { + methods.registerNewUser(newAccount, function(err:any){ + if (err) { + res.flash('error', "Fehlgeschlagen") + } + else { + // send email + var emailSubject = "Bitte bestätigen Sie Ihr M4_LAB Benutzerkonto" + var 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='+config.app.host+'/verifyAccount?token='+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/>' + 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] '+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') + }) + } + ]) + } + }) + + // =================== USERS VERIFICATION ========================= + + app.get("/verifyAccount", async function(req:any, res:any){ + let userId:number = await methods.getUserIdByVerificationToken(req.query.token) + if (!userId) { + // no user found + res.render(lang+'/account/verification', { + status: null + }) + } else { + // a user found, verify the account + let userData:any = { + id: userId, + verificationStatus: 1 + } + methods.verifyUserAccount(userData, async function(err:any){ + if (err) { + console.log("Error: "+err) + res.render(lang+'/account/verification', { + status: false + }); + } else { + // send welcome email after successful account verification + let userEmail:string = await methods.getUserEmailById(userId) + if (!userEmail) { + res.render(lang+'/account/verification', { + status: false + }) + } else { + // send email + var emailSubject = "Herzlich willkommen" + var 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 + }) + } + } + }) + } + }) + + // ==================== FORGOT PASSWORD =========================== + + app.get('/forgotPwd', function (req:any, res:any) { + res.render(lang+'/account/forgotPwd', { + user: req.user + }) + }) + app.post('/forgotPwd', function(req:any, res:any) { + let emailAddress = req.body.inputEmail + async.waterfall([ + async function(done:any) { + let user = await methods.checkUserEmail(emailAddress) + if (!user) { + console.log('No user found: '+emailAddress) + } else { + // generate token + let token:string = ''; + let randomChars:string = 'abcdefghijklmnopqrstuvwxyz0123456789'; + for ( let i = 0; i<40; i++ ) { + token += randomChars.charAt(Math.floor(Math.random() * randomChars.length)); + } + + var emailSubject = "Ihre Passwort-Anfrage an das Transferportal der HFT Stuttgart"; + var 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: '+config.app.host+'/reset/' + token + '<br/>' + + 'Dieser Link ist aus Sicherheitsgründen nur für 1 Stunde gültig.<br/></p>' + constants.mailSignature + '</div>' + + var credentialData = { + user_id: user.id, + resetPasswordToken: token, + resetPasswordExpires: Date.now() + 3600000 // 1 hour + } + let result = await methods.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 ' + emailAddress + ' versendet.') + } + res.redirect('/account/forgotPwd') + }) + }) + + // reset + app.get('/reset/:token', async function(req:any, res:any) { + let user = await methods.getUserByToken(req.params.token) + if (!user) { + res.flash('error', 'Der Schlüssel zum zurücksetzen des Passworts ist ungültig oder abgelaufen.') + res.redirect('/account/forgotPwd') + } else { + res.render(lang+'/account/reset') + } + }) + app.post('/reset/:token', async function(req:any, res:any) { + var newPwd = req.body.inputNewPwd + + var user = await methods.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) { + var credentialData = { + password: hash, + user_id: user.user_id + } + // update password + let result = await methods.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 notifiaction 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') + }); + }); + } + + }) + + // ======================= CONTACT FORM =========================== + app.get('/contact', function (req:any, res:any) { + res.render(lang+'/account/contact', { + user: req.user + }) + }) + app.post('/contact', function(req:any, res:any, next:any) { + //methods.currentDate(); + let emailAddress = req.body.inputEmail; + let supportAddress = "support-transfer@hft-stuttgart.de"; + let inputName = req.body.name; + let inputContent = req.body.message; + let emailContent = "Es wurde eine Anfrage an das Transferportal gestellt: \n\n NAME: " + inputName + "\n NACHRICHT:\n "+ inputContent; + let emailSubject = "Ihre Anfrage an das Transferportal"; + async.waterfall([ + function(done:any) { + // send email + mailer.options.to = supportAddress; + mailer.options.cc = emailAddress; + mailer.options.subject = emailSubject; + mailer.options.text = emailContent; + mailer.transporter.sendMail(mailer.options, function(err:any) { + done(err, 'done'); + }); + } + ], function(err:any) { + if (err) { + console.error(err) + res.flash('error', 'Ein Fehler ist aufgetreten. Bitte versuchen Sie es erneut.'); + } + else { + res.flash('success', 'Vielen Dank für Ihre Anfrage. Wir melden uns baldmöglichst bei Ihnen. Eine Kopie Ihrer Anfrage wurde an ' + emailAddress + ' versandt.'); + } + res.redirect('/account/contact') + }) + }) + +} \ No newline at end of file diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000000000000000000000000000000000000..1519fee76e91ea94764d0ac22eec6f766f63686e --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,11 @@ +{ + "compilerOptions": { + "target": "es6", + "module": "commonjs", + "rootDir": "./", + "outDir": "./built", + "esModuleInterop": true, + "strict": true, + "allowJs": true + } +} \ No newline at end of file