diff --git a/api/database/auracle_db_create.sql b/api/database/auracle_db_create.sql
index acbfada..15b31e8 100644
--- a/api/database/auracle_db_create.sql
+++ b/api/database/auracle_db_create.sql
@@ -24,6 +24,7 @@ CREATE TABLE IF NOT EXISTS `user` (
`password` VARCHAR(255) NOT NULL,
`role_id` INT UNSIGNED NOT NULL DEFAULT 1,
`verified` BOOLEAN DEFAULT false,
+ `verification_token` VARCHAR(255),
`banned` BOOLEAN DEFAULT false,
PRIMARY KEY(`id`),
FOREIGN KEY(`role_id`) REFERENCES role(`id`)
diff --git a/api/index.js b/api/index.js
index 90449b1..1165be8 100644
--- a/api/index.js
+++ b/api/index.js
@@ -33,13 +33,13 @@ app.use(helmet());
// Server
app.listen(port, () => console.log(`App listening on port ${port}`));
-// Auth guard
+// TEMP Auth guard
const authguard = (req, res, next) => {
- if (req.headers.auracle_key !== process.env.API_KEY_PUBLIC) {
- return res.status(401).send('The API key is either missing or incorrect.');
- } else {
+ // if (req.headers.auracle_key !== process.env.API_KEY_PUBLIC) {
+ // return res.status(401).send('The API key is either missing or incorrect.');
+ // } else {
next();
- }
+ // }
}
// Routing
diff --git a/api/package-lock.json b/api/package-lock.json
index ab9c5b9..2bdbe7b 100644
--- a/api/package-lock.json
+++ b/api/package-lock.json
@@ -856,6 +856,25 @@
"which": "^1.2.14"
}
},
+ "handlebars": {
+ "version": "4.7.6",
+ "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.7.6.tgz",
+ "integrity": "sha512-1f2BACcBfiwAfStCKZNrUCgqNZkGsAT7UM3kkYtXuLo0KnaVfjKOyf7PRzB6++aK9STyT1Pd2ZCPe3EGOXleXA==",
+ "requires": {
+ "minimist": "^1.2.5",
+ "neo-async": "^2.6.0",
+ "source-map": "^0.6.1",
+ "uglify-js": "^3.1.4",
+ "wordwrap": "^1.0.0"
+ },
+ "dependencies": {
+ "source-map": {
+ "version": "0.6.1",
+ "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
+ "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g=="
+ }
+ }
+ },
"has-unicode": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz",
@@ -1564,6 +1583,11 @@
"resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.2.tgz",
"integrity": "sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw=="
},
+ "neo-async": {
+ "version": "2.6.2",
+ "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz",
+ "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw=="
+ },
"nocache": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/nocache/-/nocache-2.1.0.tgz",
@@ -1591,6 +1615,11 @@
"tar": "^4.4.2"
}
},
+ "nodemailer": {
+ "version": "6.4.17",
+ "resolved": "https://registry.npmjs.org/nodemailer/-/nodemailer-6.4.17.tgz",
+ "integrity": "sha512-89ps+SBGpo0D4Bi5ZrxcrCiRFaMmkCt+gItMXQGzEtZVR3uAD3QAQIDoxTWnx3ky0Dwwy/dhFrQ+6NNGXpw/qQ=="
+ },
"nopt": {
"version": "4.0.3",
"resolved": "https://registry.npmjs.org/nopt/-/nopt-4.0.3.tgz",
@@ -2303,6 +2332,12 @@
"mime-types": "~2.1.24"
}
},
+ "uglify-js": {
+ "version": "3.12.3",
+ "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.12.3.tgz",
+ "integrity": "sha512-feZzR+kIcSVuLi3s/0x0b2Tx4Iokwqt+8PJM7yRHKuldg4MLdam4TCFeICv+lgDtuYiCtdmrtIP+uN9LWvDasw==",
+ "optional": true
+ },
"unc-path-regex": {
"version": "0.1.2",
"resolved": "https://registry.npmjs.org/unc-path-regex/-/unc-path-regex-0.1.2.tgz",
@@ -2414,6 +2449,11 @@
"string-width": "^1.0.2 || 2"
}
},
+ "wordwrap": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz",
+ "integrity": "sha1-J1hIEIkUVqQXHI0CJkQa3pDLyus="
+ },
"wrappy": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
diff --git a/api/package.json b/api/package.json
index 244fba0..f62208a 100644
--- a/api/package.json
+++ b/api/package.json
@@ -32,12 +32,14 @@
"cors": "^2.8.5",
"dotenv": "^8.2.0",
"express": "^4.17.1",
+ "handlebars": "^4.7.6",
"helmet": "^3.22.0",
"jsonschema": "^1.2.6",
"jsonwebtoken": "^8.5.1",
"knex": "^0.21.1",
"morgan": "^1.10.0",
"mysql": "^2.18.1",
+ "nodemailer": "^6.4.17",
"uuid": "^8.2.0"
}
}
diff --git a/api/repositories/user-repository.js b/api/repositories/user-repository.js
index e414f98..dd5d3a3 100644
--- a/api/repositories/user-repository.js
+++ b/api/repositories/user-repository.js
@@ -7,6 +7,9 @@ const model = require('../models/user-model')
const bcrypt = require('bcrypt')
const { v4: uuidv4 } = require('uuid')
+// Mailing methods
+const mails = require('../smtp/mails')
+
// Model validation
const Validator = require('jsonschema').Validator
const v = new Validator()
@@ -92,7 +95,9 @@ class UserRepository {
})
} else {
let hash = await bcrypt.hash(u.password, 10)
+
let uuid = uuidv4()
+ let verification_token = uuidv4();
this.checkIfEmailAvailable(u.mail)
.then(() => {
@@ -102,6 +107,7 @@ class UserRepository {
'name': u.name,
'mail': u.mail,
'password': hash,
+ 'verification_token': verification_token,
})
.save(null, {
transacting: t
@@ -114,6 +120,17 @@ class UserRepository {
return this.getOneByUUID(uuid, false)
})
.then(newUser => {
+
+ // Send a mail to the new user's email with a link to verification url
+ mails.sendRegistrationMail({
+ user: {
+ name: newUser.name,
+ mail: newUser.mail,
+ token: verification_token,
+ }
+ })
+
+ // Then resolves the api call
resolve({
"message": `Compte utilisateur #${newUser.id} créé avec succès.`,
"code": 201,
@@ -134,6 +151,37 @@ class UserRepository {
})
}
+ verifyUser(token) {
+ return new Promise((resolve, reject) => {
+ model.forge()
+ .where({ 'verification_token' : token })
+ .fetch()
+ .then(v => {
+ bookshelf.transaction(t => {
+ return v.save({
+ 'verification_token': null,
+ 'verified': 1,
+ }, {
+ method: 'update',
+ transacting: t
+ })
+ })
+ .then(v => {
+ resolve({
+ "message": "Insérez ici une future redirection vers le client.",
+ "code": 202,
+ })
+ })
+ })
+ .catch(() => {
+ reject({
+ "message": "Le lien de vérification ne semble pas correct.",
+ "code": 404
+ })
+ })
+ });
+ }
+
// Log user with an email address and a password
logUser(mail, password) {
return new Promise((resolve, reject) => {
@@ -145,23 +193,23 @@ class UserRepository {
delete fetchedUser.password
if (match) {
- if (fetchedUser.banned) {
- reject({
- "message": `L'utilisateur #${fetchedUser.name} a été banni, la connexion est impossible.`,
- "code": 403
- })
- } else if (!fetchedUser.verified) {
- reject({
- "message": `L'utilisateur #${fetchedUser.name} n'as pas été vérifié, le compte doit être activé avant la connexion.`,
- "code": 401
- })
- } else {
- resolve({
- "message": `L'utilisateur #${fetchedUser.name} s'est connecté.`,
- "code": 200,
- "user": fetchedUser,
- })
- }
+ if (fetchedUser.banned) {
+ reject({
+ "message": `L'utilisateur #${fetchedUser.name} a été banni, la connexion est impossible.`,
+ "code": 403
+ })
+ } else if (!fetchedUser.verified) {
+ reject({
+ "message": `L'utilisateur #${fetchedUser.name} n'as pas été vérifié, le compte doit être activé avant la connexion.`,
+ "code": 401
+ })
+ } else {
+ resolve({
+ "message": `L'utilisateur #${fetchedUser.name} s'est connecté.`,
+ "code": 200,
+ "user": fetchedUser,
+ })
+ }
} else {
reject({
"message": "Les informations de connexions sont erronées.",
diff --git a/api/routes/users.js b/api/routes/users.js
index 8e0ac19..175fbea 100644
--- a/api/routes/users.js
+++ b/api/routes/users.js
@@ -85,29 +85,6 @@ router.get('/available/:mail/', async (req, res) => {
})
-// LOG A USER ------------------
-const logUser = (mail, password) => {
- return Users.logUser(mail, password)
- .catch(err => {
- throw err
- })
-}
-router.post('/login', async (req, res) => {
- logUser(req.body.mail, req.body.password)
- .then(v => {
- res.setHeader('Content-Type', 'application/json;charset=utf-8')
- res.end(JSON.stringify(v))
- })
- .catch(err => {
- res.status(err.code).send(JSON.stringify(
- {
- "error": err.message,
- "code": err.code
- })
- )
- })
-})
-
// CREATE ONE ------------------
const addUser = (u) => {
return Users.addOne(u)
@@ -131,4 +108,51 @@ router.post('/', async (req, res) => {
})
})
+
+// VERIFY A USER
+const verifyUser = (token) => {
+ return Users.verifyUser(token)
+ .catch(err => {
+ throw err;
+ })
+}
+router.get('/verification/:token', async (req, res) => {
+ verifyUser(req.params.token)
+ .then(v => {
+ res.setHeader('Content-Type', 'application/json;charset=utf-8')
+ res.send(JSON.stringify(v))
+ })
+ .catch(err => {
+ res.status(err.code).send(JSON.stringify(
+ {
+ "error": err.message,
+ "code": err.code
+ })
+ )
+ })
+});
+
+// LOG A USER ------------------
+const logUser = (mail, password) => {
+ return Users.logUser(mail, password)
+ .catch(err => {
+ throw err
+ })
+}
+router.post('/login', async (req, res) => {
+ logUser(req.body.mail, req.body.password)
+ .then(v => {
+ res.setHeader('Content-Type', 'application/json;charset=utf-8')
+ res.end(JSON.stringify(v))
+ })
+ .catch(err => {
+ res.status(err.code).send(JSON.stringify(
+ {
+ "error": err.message,
+ "code": err.code
+ })
+ )
+ })
+})
+
module.exports = router
\ No newline at end of file
diff --git a/api/smtp/config.js b/api/smtp/config.js
new file mode 100644
index 0000000..520c05c
--- /dev/null
+++ b/api/smtp/config.js
@@ -0,0 +1,14 @@
+const nodemailer = require('nodemailer');
+
+const transport = nodemailer.createTransport({
+ host: "smtp.mailtrap.io",
+ port: 2525,
+ auth: {
+ user: process.env.SMTP_USER,
+ pass: process.env.SMTP_PASS,
+ }
+});
+
+module.exports = {
+ transport,
+};
\ No newline at end of file
diff --git a/api/smtp/mails.js b/api/smtp/mails.js
new file mode 100644
index 0000000..115ab76
--- /dev/null
+++ b/api/smtp/mails.js
@@ -0,0 +1,61 @@
+const smtp = require('./config');
+const fs = require('fs');
+const handlebars = require('handlebars');
+
+// Sender address for mail service
+const sender = 'tymos@auracle.io';
+
+// Fetches a HTML template file for parsing
+const getTemplateFile = (path) => {
+ return new Promise((resolve, reject) => {
+ fs.readFile(path, {encoding: 'utf-8'}, (err, html) => {
+ if (err) {
+ reject(err);
+ } else {
+ resolve(html);
+ }
+ })
+ })
+};
+
+/**
+ * SEND REGISTRATION MAIL FUNCTION
+ * @param {Object} data
+ * - user
+ * - name
+ * - mail
+ * - token
+ */
+const sendRegistrationMail = (data) => {
+ getTemplateFile(__dirname + '/templates/template-sign-up.html')
+ .then(html => {
+ let template = handlebars.compile(html);
+ let template_parsed = template(data);
+
+ let message = {
+ from: sender,
+ to: data.user.mail,
+ subject: 'Inscription Auracle.io',
+ html: template_parsed,
+ };
+
+ smtp.transport.sendMail(message, (err, info) => {
+ if (err) {
+ throw err;
+ } else {
+ console.log(info);
+ }
+ });
+ })
+ .catch(err => {
+ console.log(err);
+ });
+}
+
+const sendBanEmail = (date) => {
+ return null;
+}
+
+module.exports = {
+ sendRegistrationMail,
+}
\ No newline at end of file
diff --git a/api/smtp/templates/template-sign-up.html b/api/smtp/templates/template-sign-up.html
new file mode 100644
index 0000000..e11c838
--- /dev/null
+++ b/api/smtp/templates/template-sign-up.html
@@ -0,0 +1,102 @@
+
+
+
+ Template Email Auracle Inscription
+
+
+
+
+
+
+
+ Bienvenue sur Auracle !
+
+
+ Votre compte Auracle a bien été enregistré et est rattaché à cette adresse mail.
+ Cependant, afin de garantir la sécurité de vos données, votre compte doit être vérifié avant votre première connexion. Vous pouvez suivre ce lien afin de confirmer votre inscription.
+ Si vous rencontrez des problèmes, n'hésitez pas à envoyer un mail à support@auracle.io
+ Merci de votre soutien !
+ Bien sincèrement, Izàc Tymos.
+
+
+
+
+
+
\ No newline at end of file
diff --git a/client/src/pages/user/register-page.vue b/client/src/pages/user/register-page.vue
index 5fd97bb..f363b1c 100644
--- a/client/src/pages/user/register-page.vue
+++ b/client/src/pages/user/register-page.vue
@@ -11,7 +11,7 @@
v-model="name"
:class="{
'is-invalid': errors.name,
- 'is-valid': !errors.name }"
+ 'is-valid': submitted && !errors.name }"
type="text"
id="username"
class="form-control"
@@ -38,7 +38,7 @@
v-model="email"
:class="{
'is-invalid': errors.email,
- 'is-valid': !errors.email }"
+ 'is-valid': submitted && !errors.email }"
type="email"
id="email"
class="form-control"
@@ -64,7 +64,7 @@