diff --git a/server/auth/modules/passport-providers/oauth.js b/server/auth/modules/passport-providers/oauth.js index 9da1095..3545ae9 100644 --- a/server/auth/modules/passport-providers/oauth.js +++ b/server/auth/modules/passport-providers/oauth.js @@ -1,21 +1,24 @@ var OAuth2Strategy = require('passport-oauth2') +var authProvider = require('../../../models/authProvider') var authUserAssoc = require('../../../models/authUserAssociation') var users = require('../../../models/users') -var {hasNestedValue} = require('../../../utils') +var { hasNestedValue } = require('../../../utils') class PassportOAuth { - constructor(passportjs,auth_id){ + constructor(passportjs,auth_name){ this.passportjs = passportjs - this.auth_id = auth_id + this.auth_name = auth_name } - updateUser(userinfos){ - + async getProviderInfo(auth_name){ + return await authProvider.find(auth_name) } register(app, passport,endpoint, name, provider) { const cb_url =`${process.env['BACKEND_URL']}${endpoint}/${name}/callback` + const self = this + passport.use(name, new OAuth2Strategy({ authorizationURL: provider.OAUTH_AUTHORIZATION_URL, tokenURL: provider.OAUTH_TOKEN_URL, @@ -32,31 +35,32 @@ class PassportOAuth { const userInfo = await userInfoResponse.json(); let received_user = { + auth_id: userInfo.sub, email: userInfo.email, name: userInfo.name, roles: [] }; - if(hasNestedValue(userInfo,provider.OIDC_ROLE_TEACHER_VALUE)) received_user.roles.push('teacher') - if(hasNestedValue(userInfo,provider.OIDC_ROLE_STUDENT_VALUE)) received_user.roles.push('student') + + if(hasNestedValue(userInfo,provider.OAUTH_ROLE_TEACHER_VALUE)) received_user.roles.push('teacher') + if(hasNestedValue(userInfo,provider.OAUTH_ROLE_STUDENT_VALUE)) received_user.roles.push('student') - const user_association = await authUserAssoc.find_user_association(userInfo.sub) + const user_association = await authUserAssoc.find_user_association(self.auth_name._id,userInfo.sub) - if(user_linked){ - let user = await users.getById(user_association.user_id) - user.name = received_user.name - user.email = received_user.email - user.roles = received_user.roles - users.editUser(user) - this.passportjs.authenticate(user) + let user_account = null + if(user_association){ + user_account = await users.getById(user_association.user_id) } else { let user_id = await users.getId(userInfo.email) - if(!user_id){ - await users.register(received_user.email,""); - users.editUser - } + user_account = user_id ? await users.getById(user_id) : await users.register(received_user.email,"") + await authUserAssoc.link(self.auth_name,received_user.auth_id,user_account._id) } + user_account.name = received_user.name + user_account.roles = received_user.roles + await users.editUser(user_account) + self.passportjs.authenticate(user_account) + // Store the tokens in the session req.session.oauth2Tokens = { accessToken: accessToken, @@ -64,7 +68,7 @@ class PassportOAuth { expiresIn: params.expires_in }; - return done(null, user); + return done(null, user_account); } catch (error) { console.error(`Erreur dans la strategie OAuth2 '${name}' : ${error}`); return done(error); diff --git a/server/auth/modules/passportjs.js b/server/auth/modules/passportjs.js index 28dca60..d194349 100644 --- a/server/auth/modules/passportjs.js +++ b/server/auth/modules/passportjs.js @@ -19,7 +19,7 @@ class PassportJs{ const auth_id = `passportjs_${provider.type}_${name}` if(!(provider.type in this.registeredProviders)){ - this.registerProvider(provider.typename,auth_id) + this.registerProvider(provider.type,auth_id) } try{ this.registeredProviders[provider.type].register(expressapp,passport,this.endpoint,name,provider) @@ -56,7 +56,7 @@ class PassportJs{ } authenticate(userinfos){ - this.authenticate(userinfos) + this.authmanager.login(userinfos) } } diff --git a/server/models/authProvider.js b/server/models/authProvider.js index 96f76b3..ab92da4 100644 --- a/server/models/authProvider.js +++ b/server/models/authProvider.js @@ -7,6 +7,20 @@ class AuthProvider { this.name = name; } + async getId(name){ + await db.connect() + const conn = db.getConnection(); + + const collection = conn.collection('authprovider'); + + const existingauth = await collection.findOne({ name:name }); + + if(existingauth){ + return existingauth._id + } + return null + } + async create(name) { await db.connect() const conn = db.getConnection(); diff --git a/server/models/authUserAssociation.js b/server/models/authUserAssociation.js index 2d3e346..3c64644 100644 --- a/server/models/authUserAssociation.js +++ b/server/models/authUserAssociation.js @@ -1,3 +1,4 @@ +const authProvider = require('./authProvider.js') const db = require('../config/db.js') const { ObjectId } = require('mongodb'); @@ -11,14 +12,48 @@ class AuthUserAssociation { this.connected = false; } - async find_user_association(authId){ + async find_user_association(provider_name,auth_id){ await db.connect() const conn = db.getConnection(); const collection = conn.collection('authUserAssociation'); + const provider_id = await authProvider.getId(provider_name) - const userAssociation = await collection.findOne({ authId: authId }); + const userAssociation = await collection.findOne({ authProvider_id: provider_id,auth_id,auth_id }); return userAssociation } + + async link(provider_name,auth_id,user_id){ + await db.connect() + const conn = db.getConnection(); + + const collection = conn.collection('authUserAssociation'); + const provider_id = await authProvider.getId(provider_name) + + const userAssociation = await collection.findOne({ authProvider_id: provider_id, user_id: user_id }); + + if(!userAssociation){ + return await collection.insertOne({ + _id:ObjectId, + authProvider_id:provider_id, + auth_id:auth_id, + user_id:user_id, + }) + } + } + + async unlink(provider_name,user_id){ + await db.connect() + const conn = db.getConnection(); + + const collection = conn.collection('authUserAssociation'); + const provider_id = await authProvider.getId(provider_name) + + const userAssociation = await collection.findOne({ authProvider_id: provider_id, user_id: user_id }); + + if(userAssociation){ + return await collection.deleteOne(userAssociation) + } else return null + } } module.exports = new AuthUserAssociation; \ No newline at end of file diff --git a/server/models/users.js b/server/models/users.js index f8584e8..58a1563 100644 --- a/server/models/users.js +++ b/server/models/users.js @@ -1,151 +1,180 @@ //user -const db = require('../config/db.js'); -const bcrypt = require('bcrypt'); -const AppError = require('../middleware/AppError.js'); -const { USER_ALREADY_EXISTS } = require('../constants/errorCodes'); -const Folders = require('../models/folders.js'); +const db = require("../config/db.js"); +const bcrypt = require("bcrypt"); +const AppError = require("../middleware/AppError.js"); +const { USER_ALREADY_EXISTS } = require("../constants/errorCodes"); +const Folders = require("../models/folders.js"); class Users { - async hashPassword(password) { - return await bcrypt.hash(password, 10) + async hashPassword(password) { + return await bcrypt.hash(password, 10); + } + + generatePassword() { + return Math.random().toString(36).slice(-8); + } + + async verify(password, hash) { + return await bcrypt.compare(password, hash); + } + + async register(email, password) { + await db.connect(); + const conn = db.getConnection(); + + const userCollection = conn.collection("users"); + + const existingUser = await userCollection.findOne({ email: email }); + + if (existingUser) { + throw new AppError(USER_ALREADY_EXISTS); } - generatePassword() { - return Math.random().toString(36).slice(-8); + const newUser = { + email: email, + password: await this.hashPassword(password), + created_at: new Date(), + }; + + let created_user = await userCollection.insertOne(newUser); + let user = await this.getById(created_user.insertedId) + + const folderTitle = "Dossier par Défaut"; + const userId = newUser._id.toString(); + await Folders.create(folderTitle, userId); + + // TODO: verif if inserted properly... + return user; + } + + async login(userid) { + await db.connect(); + const conn = db.getConnection(); + + const userCollection = conn.collection("users"); + const user = await userCollection.findOne({ _id: userid }); + + if (!user) { + return false; } - async verify(password, hash) { - return await bcrypt.compare(password, hash) + return user; + } + + async login(email, password) { + await db.connect(); + const conn = db.getConnection(); + + const userCollection = conn.collection("users"); + + const user = await userCollection.findOne({ email: email }); + + if (!user) { + return false; } - async register(email, password) { - await db.connect() - const conn = db.getConnection(); - - const userCollection = conn.collection('users'); + const passwordMatch = await this.verify(password, user.password); - const existingUser = await userCollection.findOne({ email: email }); - - if (existingUser) { - throw new AppError(USER_ALREADY_EXISTS); - } - - const newUser = { - email: email, - password: await this.hashPassword(password), - created_at: new Date(), - }; - - await userCollection.insertOne(newUser); - - const folderTitle = 'Dossier par Défaut'; - const userId = newUser._id.toString(); - await Folders.create(folderTitle, userId); - - // TODO: verif if inserted properly... + if (!passwordMatch) { + return false; } - async login(email, password) { - await db.connect() - const conn = db.getConnection(); + return user; + } - const userCollection = conn.collection('users'); + async resetPassword(email) { + const newPassword = this.generatePassword(); - const user = await userCollection.findOne({ email: email }); + return await this.changePassword(email, newPassword); + } - if (!user) { - return false; - } + async changePassword(email, newPassword) { + await db.connect(); + const conn = db.getConnection(); - const passwordMatch = await this.verify(password, user.password); + const userCollection = conn.collection("users"); - if (!passwordMatch) { - return false; - } + const hashedPassword = await this.hashPassword(newPassword); - return user; + const result = await userCollection.updateOne( + { email }, + { $set: { password: hashedPassword } } + ); + + if (result.modifiedCount != 1) return null; + + return newPassword; + } + + async delete(email) { + await db.connect(); + const conn = db.getConnection(); + + const userCollection = conn.collection("users"); + + const result = await userCollection.deleteOne({ email }); + + if (result.deletedCount != 1) return false; + + return true; + } + + async getId(email) { + await db.connect(); + const conn = db.getConnection(); + + const userCollection = conn.collection("users"); + + const user = await userCollection.findOne({ email: email }); + + if (!user) { + return false; } - async resetPassword(email) { - const newPassword = this.generatePassword(); + return user._id; + } - return await this.changePassword(email, newPassword); + async getById(id) { + await db.connect(); + const conn = db.getConnection(); + + const userCollection = conn.collection("users"); + + const user = await userCollection.findOne({ _id: id }); + + if (!user) { + return false; } - async changePassword(email, newPassword) { - await db.connect() - const conn = db.getConnection(); + return user; + } - const userCollection = conn.collection('users'); + async editUser(userInfo) { + await db.connect(); + const conn = db.getConnection(); - const hashedPassword = await this.hashPassword(newPassword); + const userCollection = conn.collection("users"); - const result = await userCollection.updateOne({ email }, { $set: { password: hashedPassword } }); + const user = await userCollection.findOne({ _id: userInfo.id }); - if (result.modifiedCount != 1) return null; - - return newPassword + if (!user) { + return false; } - async delete(email) { - await db.connect() - const conn = db.getConnection(); + const updatedFields = { ...userInfo }; + delete updatedFields.id; - const userCollection = conn.collection('users'); + const result = await userCollection.updateOne( + { _id: userInfo.id }, + { $set: updatedFields } + ); - const result = await userCollection.deleteOne({ email }); - - if (result.deletedCount != 1) return false; - - return true; + if (result.modifiedCount === 1) { + return true; } - async getId(email) { - await db.connect() - const conn = db.getConnection(); - - const userCollection = conn.collection('users'); - - const user = await userCollection.findOne({ email: email }); - - if (!user) { - return false; - } - - return user._id; - } - - async getById(id){ - await db.connect() - const conn = db.getConnection(); - - const userCollection = conn.collection('users'); - - const user = await userCollection.findOne({ _id: id }); - - if (!user) { - return false; - } - - return user; - } - - async editUser(userInfo){ - await db.connect() - const conn = db.getConnection(); - - const userCollection = conn.collection('users'); - - const user = await userCollection.findOne({ _id: userInfo.id }); - - if (!user) { - return false; - } - - const updatedFields = { ...userInfo }; - - return user; - } + return false; + } } -module.exports = new Users; +module.exports = new Users(); diff --git a/server/utils.js b/server/utils.js index 6192dad..c2429f4 100644 --- a/server/utils.js +++ b/server/utils.js @@ -1,15 +1,28 @@ -function hasNestedValue(obj, path, delimiter="_") { - const keys = path.split(delimiter); - let current = obj; - - for (const key of keys) { - if (current && typeof current === 'object' && key in current) { +function hasNestedValue(obj, path, delimiter = "_") { + const keys = path.split(delimiter); + let current = obj; + + for (const key of keys) { + if (current && typeof current === "object") { + if (Array.isArray(current)) { + const index = current.findIndex(x => x == key) + if (index != -1) { + current = current[index]; + } else { + return false; + } + } else if (key in current) { current = current[key]; } else { return false; } + } else { + return false; } - - return true; + } + + return true; } - \ No newline at end of file + + +module.exports = { hasNestedValue}; \ No newline at end of file