diff --git a/backend/models/Campaign.js b/backend/models/Campaign.js index d946c243..06646ceb 100644 --- a/backend/models/Campaign.js +++ b/backend/models/Campaign.js @@ -1,6 +1,8 @@ const mongoose = require("mongoose"); const Schema = mongoose.Schema; +const crypto = require("crypto"); + const CampaignSchema = new Schema({ name: {type: String, required: true}, description: {type: String}, @@ -12,14 +14,7 @@ const CampaignSchema = new Schema({ }); CampaignSchema.statics.generateInvite = function() { - let possible = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTVWXYZ0123456789"; - let cod = ''; - - for (let i = 0; i < 32; i++) { - cod += possible.charAt(Math.floor(Math.random() * possible.length)); - } - - return cod; + return crypto.randomBytes(8).toString('base64url'); } module.exports = mongoose.model('Campaign', CampaignSchema); \ No newline at end of file diff --git a/backend/models/User.js b/backend/models/User.js index 9aeac942..46b5939e 100755 --- a/backend/models/User.js +++ b/backend/models/User.js @@ -9,6 +9,7 @@ const UserSchema = new Schema({ date: { type: Date, default: Date.now}, admin: {type: Boolean, default: false}, image: { type: String }, + setupCode: { type: String }, settings: { type: Object } }); diff --git a/backend/routes/admin.js b/backend/routes/admin.js index 4082883e..015d297f 100644 --- a/backend/routes/admin.js +++ b/backend/routes/admin.js @@ -11,21 +11,6 @@ const User = require("../models/User"); router.post('/register', rateLimitMiddleware, (req, res) => { User.findOne({admin: true}).then((data) => { if(!data) { - let { - name, - username, - email, - password - } = req.body; - - if(!(name && username && email && password)){ - res.json({ - error: true, - msg: "params" - }); - return; - } - User.findOne({username: username}).then((user) => { if(user){ res.json({ diff --git a/backend/routes/user.js b/backend/routes/user.js index e0b21077..fd5d92f9 100755 --- a/backend/routes/user.js +++ b/backend/routes/user.js @@ -7,6 +7,7 @@ const passport = require('passport'); const secret = require('../config/keys').secret; const rateLimitMiddleware = require("../config/rate-limiter"); const { default: jwtDecode } = require('jwt-decode'); +const crypto = require("crypto"); const { isAdmin } = require('../config/middleware'); @@ -14,14 +15,47 @@ const User = require("../models/User"); const upload = require("../config/storage"); +// Admin registers new user router.post('/register', isAdmin, (req, res) => { + let setupCode = crypto.randomBytes(64).toString('base64url'); + + let user = new User({ + admin: false, + name: crypto.randomBytes(16).toString('base64url'), + username: crypto.randomBytes(16).toString('base64url'), + email: crypto.randomBytes(16).toString('base64url'), + setupCode + }); + + user.save().then(user => { + res.json({ + status: "ok", + code: setupCode, + }); + }).catch({status: "err", msg: "internal"}) +}); + +// User gets if setup account exists given the query code +router.get('/setup', (req, res) => { + User.findOne({setupCode: req.query.code}).then(user => { + if(user){ + res.json({status: "ok", code: req.query.code}); + } + res.json({status: "err", msg: "not-exists"}); + }).catch(res.json({status: "err", msg: "internal"})); +}); + +// User posts the parameters of his new account given by admin +router.post('/setup', rateLimitMiddleware, (req, res) => { let { name, username, - email + email, + password } = req.body; + let setupCode = req.query.code; - if(!(name && username && email)){ + if(!(name && username && email && password && setupCode)){ res.json({ error: true, msg: "params" @@ -29,39 +63,36 @@ router.post('/register', isAdmin, (req, res) => { return; } - User.findOne({username: username}).then((user) => { - if(user){ - res.json({ - error: true, - msg: "already-exists" - }); - } else { - User.findOne({email: email}).then((user) => { - if(user){ - res.json({ - error: true, - msg: "already-email" - }); - } else { - var user = new User({ - name: name, - username: username, - email: email, - admin: true - }); + User.findOne({setupCode}).then((user) => { + User.findOne({email: email}).then((sameUser) => { + if(sameUser){ + res.json({ + error: true, + msg: "already-email" + }); + } else { + bcrypt.genSalt(10, (err, salt) => { + bcrypt.hash(user.password, salt, (err, hash) => { + if(err) throw err; + user.password = hash; + user.username = username; + user.email = email; + user.setupCode = undefined; - user.save().then(user => { - res.json({ - status: "ok", - user - }); - }) - } - }).catch((error) => { res.json({ error: true, msg: "Hi ha hagut un error intern, prova-ho més tard" }); return; }); - } - }).catch((error) => { res.json({ error: true, msg: "Hi ha hagut un error intern, prova-ho més tard" }); return; }); + user.save().then(user => { + res.json({ + success: true + }); + return; + }).catch((error) => { res.json({ error: true }); return; }); + }); + }) + } + }).catch((error) => { res.json({ error: true, msg: "internal" }); return; }); + }).catch((error) => { res.json({ error: true, msg: "internal" }); return; }); }); +// Login post router.post('/login', rateLimitMiddleware, (req, res) => { const username = req.body.username; const password = req.body.password; @@ -111,6 +142,7 @@ router.post('/login', rateLimitMiddleware, (req, res) => { }); }); +// Upload avatar post router.post("/upload-avatar", upload.single("image"), passport.authenticate('jwt', {session: false}), (req, res) => { const imageName = req.file.filename; diff --git a/client/src/services/Windows.js b/client/src/services/Windows.js index 88742f1e..28747930 100644 --- a/client/src/services/Windows.js +++ b/client/src/services/Windows.js @@ -31,6 +31,7 @@ import PluginManagementWindow from '@/views/windows/settings/PluginManagementWin import PluginWindow from '../views/windows/settings/PluginWindow.vue'; import FirstRegisterWindow from '../views/windows/FirstRegisterWindow.vue'; import RegisterUserWindow from '../views/windows/settings/RegisterUserWindow.vue'; +import CopyPendingUserWindow from '../views/windows/settings/CopyPendingUserWindow.vue'; let windowMap = { test: ExampleWindow, @@ -39,6 +40,7 @@ let windowMap = { welcome: WelcomeWindow, first_register: FirstRegisterWindow, register_user: RegisterUserWindow, + copy_pending_user_window: CopyPendingUserWindow, edit_profile: EditProfileWindow, settings: SettingsWindow, campaign_list: CampaignListWindow, diff --git a/client/src/views/windows/EditProfileWindow.vue b/client/src/views/windows/EditProfileWindow.vue index dc1948b3..51d8c8ef 100644 --- a/client/src/views/windows/EditProfileWindow.vue +++ b/client/src/views/windows/EditProfileWindow.vue @@ -8,12 +8,14 @@ import Api from '@/services/Api' import BigIconTemplate from '../partials/BigIconTemplate.vue'; import { SetMinSize, SetResizable } from '../../services/Windows'; import { backendUrl } from '../../services/BackendURL'; +import { GetUser } from '../../services/User'; const props = defineProps(['data']); const data = props.data; const userIcon = ref(""); const handle = ref(null); +const isAdmin = ref(false); let id = data.id; console.log(data); @@ -26,6 +28,8 @@ onMounted(() => { SetResizable(id, true); SetMinSize(id, {width: 350, height: 280}); + isAdmin.value = GetUser().admin; + Api().get('/user/retrieve-avatar?username=' + data.user.username).then((response) => { if(response.data.image) userIcon.value = backendUrl + "public/" + response.data.image; else userIcon.value = "public/img/def-avatar.jpg"; @@ -39,7 +43,9 @@ onMounted(() => { - +
+ Admin +
diff --git a/client/src/views/windows/settings/AccountManagementWindow.vue b/client/src/views/windows/settings/AccountManagementWindow.vue index 110e3d69..14f9546e 100644 --- a/client/src/views/windows/settings/AccountManagementWindow.vue +++ b/client/src/views/windows/settings/AccountManagementWindow.vue @@ -1,4 +1,8 @@ + + + + + + + + diff --git a/client/src/views/windows/settings/RegisterUserWindow.vue b/client/src/views/windows/settings/RegisterUserWindow.vue index 8ee68d97..9d16c3c0 100644 --- a/client/src/views/windows/settings/RegisterUserWindow.vue +++ b/client/src/views/windows/settings/RegisterUserWindow.vue @@ -32,18 +32,13 @@ let title = data.title; onMounted(() => { SetupHandle(id, handle); - SetSize(id, {width: 500, height: 430}); + SetSize(id, {width: 400, height: 310}); ResetPosition(id, "center"); }); function register(){ - Api().post('/user/register', - { - name: name.value, - username: username.value, - email: email.value, - }).then((response) => { + Api().post('/user/register').then((response) => { const data = response.data; console.log(data); if(data.error){ @@ -71,19 +66,6 @@ function register(){
-
- - -
-
- - -
- -
- - -
diff --git a/locales/ca.json b/locales/ca.json index 5069d319..91741de7 100644 --- a/locales/ca.json +++ b/locales/ca.json @@ -37,7 +37,8 @@ }, "register-account": { "title": "Create new user account", - "welcome-message": "

Hola

" + "welcome-message": "

Hola

", + "pending-account": "Pending account" }, "main-menu": { "title": "Dragonroll", diff --git a/locales/en-US.json b/locales/en-US.json index 75eabc11..81842a40 100644 --- a/locales/en-US.json +++ b/locales/en-US.json @@ -37,7 +37,8 @@ }, "register-account": { "title": "Create new user account", - "welcome-message": "

Here you can create a new user account. The first time the user logs in will be prompted to set his password


" + "welcome-message": "

Here you can create a new user account.


Once you have registered a new account, you will recieve a link that you must give to the owner of the new account. From there, the owner will be able to setup his new account


", + "pending-account": "Pending account" }, "main-menu": { "title": "Dragonroll", diff --git a/locales/es-ES.json b/locales/es-ES.json index 39856ee5..3503c15c 100644 --- a/locales/es-ES.json +++ b/locales/es-ES.json @@ -37,7 +37,8 @@ }, "register-account": { "title": "Create new user account", - "welcome-message": "

Hola

" + "welcome-message": "

Hola

", + "pending-account": "Pending account" }, "main-menu": { "title": "Dragonroll",