Adds Oauths parsing

Co-authored-by: roesnerb <roesnerb@users.noreply.github.com>
Co-authored-by: MathieuSevignyLavallee <MathieuSevignyLavallee@users.noreply.github.com>
This commit is contained in:
Gabriel Matte 2024-10-01 10:55:48 -04:00
parent f7f03ebeaa
commit 8f7c0a3ac9
6 changed files with 244 additions and 149 deletions

View file

@ -1,21 +1,24 @@
var OAuth2Strategy = require('passport-oauth2') var OAuth2Strategy = require('passport-oauth2')
var authProvider = require('../../../models/authProvider')
var authUserAssoc = require('../../../models/authUserAssociation') var authUserAssoc = require('../../../models/authUserAssociation')
var users = require('../../../models/users') var users = require('../../../models/users')
var {hasNestedValue} = require('../../../utils') var { hasNestedValue } = require('../../../utils')
class PassportOAuth { class PassportOAuth {
constructor(passportjs,auth_id){ constructor(passportjs,auth_name){
this.passportjs = passportjs 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) { register(app, passport,endpoint, name, provider) {
const cb_url =`${process.env['BACKEND_URL']}${endpoint}/${name}/callback` const cb_url =`${process.env['BACKEND_URL']}${endpoint}/${name}/callback`
const self = this
passport.use(name, new OAuth2Strategy({ passport.use(name, new OAuth2Strategy({
authorizationURL: provider.OAUTH_AUTHORIZATION_URL, authorizationURL: provider.OAUTH_AUTHORIZATION_URL,
tokenURL: provider.OAUTH_TOKEN_URL, tokenURL: provider.OAUTH_TOKEN_URL,
@ -32,31 +35,32 @@ class PassportOAuth {
const userInfo = await userInfoResponse.json(); const userInfo = await userInfoResponse.json();
let received_user = { let received_user = {
auth_id: userInfo.sub,
email: userInfo.email, email: userInfo.email,
name: userInfo.name, name: userInfo.name,
roles: [] 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_account = null
let user = await users.getById(user_association.user_id) if(user_association){
user.name = received_user.name user_account = await users.getById(user_association.user_id)
user.email = received_user.email
user.roles = received_user.roles
users.editUser(user)
this.passportjs.authenticate(user)
} }
else { else {
let user_id = await users.getId(userInfo.email) let user_id = await users.getId(userInfo.email)
if(!user_id){ user_account = user_id ? await users.getById(user_id) : await users.register(received_user.email,"")
await users.register(received_user.email,""); await authUserAssoc.link(self.auth_name,received_user.auth_id,user_account._id)
users.editUser
}
} }
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 // Store the tokens in the session
req.session.oauth2Tokens = { req.session.oauth2Tokens = {
accessToken: accessToken, accessToken: accessToken,
@ -64,7 +68,7 @@ class PassportOAuth {
expiresIn: params.expires_in expiresIn: params.expires_in
}; };
return done(null, user); return done(null, user_account);
} catch (error) { } catch (error) {
console.error(`Erreur dans la strategie OAuth2 '${name}' : ${error}`); console.error(`Erreur dans la strategie OAuth2 '${name}' : ${error}`);
return done(error); return done(error);

View file

@ -19,7 +19,7 @@ class PassportJs{
const auth_id = `passportjs_${provider.type}_${name}` const auth_id = `passportjs_${provider.type}_${name}`
if(!(provider.type in this.registeredProviders)){ if(!(provider.type in this.registeredProviders)){
this.registerProvider(provider.typename,auth_id) this.registerProvider(provider.type,auth_id)
} }
try{ try{
this.registeredProviders[provider.type].register(expressapp,passport,this.endpoint,name,provider) this.registeredProviders[provider.type].register(expressapp,passport,this.endpoint,name,provider)
@ -56,7 +56,7 @@ class PassportJs{
} }
authenticate(userinfos){ authenticate(userinfos){
this.authenticate(userinfos) this.authmanager.login(userinfos)
} }
} }

View file

@ -7,6 +7,20 @@ class AuthProvider {
this.name = name; 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) { async create(name) {
await db.connect() await db.connect()
const conn = db.getConnection(); const conn = db.getConnection();

View file

@ -1,3 +1,4 @@
const authProvider = require('./authProvider.js')
const db = require('../config/db.js') const db = require('../config/db.js')
const { ObjectId } = require('mongodb'); const { ObjectId } = require('mongodb');
@ -11,14 +12,48 @@ class AuthUserAssociation {
this.connected = false; this.connected = false;
} }
async find_user_association(authId){ async find_user_association(provider_name,auth_id){
await db.connect() await db.connect()
const conn = db.getConnection(); const conn = db.getConnection();
const collection = conn.collection('authUserAssociation'); 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 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; module.exports = new AuthUserAssociation;

View file

@ -1,151 +1,180 @@
//user //user
const db = require('../config/db.js'); const db = require("../config/db.js");
const bcrypt = require('bcrypt'); const bcrypt = require("bcrypt");
const AppError = require('../middleware/AppError.js'); const AppError = require("../middleware/AppError.js");
const { USER_ALREADY_EXISTS } = require('../constants/errorCodes'); const { USER_ALREADY_EXISTS } = require("../constants/errorCodes");
const Folders = require('../models/folders.js'); const Folders = require("../models/folders.js");
class Users { class Users {
async hashPassword(password) { async hashPassword(password) {
return await bcrypt.hash(password, 10) 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() { const newUser = {
return Math.random().toString(36).slice(-8); 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 user;
return await bcrypt.compare(password, hash) }
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) { const passwordMatch = await this.verify(password, user.password);
await db.connect()
const conn = db.getConnection();
const userCollection = conn.collection('users');
const existingUser = await userCollection.findOne({ email: email }); if (!passwordMatch) {
return false;
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...
} }
async login(email, password) { return user;
await db.connect() }
const conn = db.getConnection();
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) { async changePassword(email, newPassword) {
return false; await db.connect();
} const conn = db.getConnection();
const passwordMatch = await this.verify(password, user.password); const userCollection = conn.collection("users");
if (!passwordMatch) { const hashedPassword = await this.hashPassword(newPassword);
return false;
}
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) { return user._id;
const newPassword = this.generatePassword(); }
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) { return user;
await db.connect() }
const conn = db.getConnection();
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; if (!user) {
return false;
return newPassword
} }
async delete(email) { const updatedFields = { ...userInfo };
await db.connect() delete updatedFields.id;
const conn = db.getConnection();
const userCollection = conn.collection('users'); const result = await userCollection.updateOne(
{ _id: userInfo.id },
{ $set: updatedFields }
);
const result = await userCollection.deleteOne({ email }); if (result.modifiedCount === 1) {
return true;
if (result.deletedCount != 1) return false;
return true;
} }
async getId(email) { return false;
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;
}
} }
module.exports = new Users; module.exports = new Users();

View file

@ -1,15 +1,28 @@
function hasNestedValue(obj, path, delimiter="_") { function hasNestedValue(obj, path, delimiter = "_") {
const keys = path.split(delimiter); const keys = path.split(delimiter);
let current = obj; let current = obj;
for (const key of keys) { for (const key of keys) {
if (current && typeof current === 'object' && key in current) { 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]; current = current[key];
} else { } else {
return false; return false;
} }
} else {
return false;
} }
}
return true;
return true;
} }
module.exports = { hasNestedValue};