mirror of
https://github.com/ets-cfuhrman-pfe/EvalueTonSavoir.git
synced 2025-08-11 21:23:54 -04:00
Merge pull request #143 from ets-cfuhrman-pfe/modularise-auth-methods
Modularise auth methods
This commit is contained in:
commit
975c88c894
18 changed files with 542 additions and 149 deletions
|
|
@ -23,13 +23,16 @@ import Header from './components/Header/Header';
|
|||
import Footer from './components/Footer/Footer';
|
||||
|
||||
import ApiService from './services/ApiService';
|
||||
import OAuthCallback from './pages/AuthSelection/AuthCallback';
|
||||
|
||||
const handleLogout = () => {
|
||||
ApiService.logout();
|
||||
};
|
||||
|
||||
const isLoggedIn = () => {
|
||||
return ApiService.isLoggedIn();
|
||||
const test = ApiService.isLoggedIn();
|
||||
console.log("App.tsx: " + test);
|
||||
return test;
|
||||
};
|
||||
|
||||
function App() {
|
||||
|
|
@ -71,6 +74,9 @@ function App() {
|
|||
|
||||
{/* Pages authentification sélection */}
|
||||
<Route path="/auth-selection" element={<AuthSelection />} />
|
||||
|
||||
{/* Pages authentification sélection */}
|
||||
<Route path="/oauth/callback" element={<OAuthCallback />} />
|
||||
</Routes>
|
||||
</main>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { useNavigate } from 'react-router-dom';
|
||||
import { Link, useNavigate } from 'react-router-dom';
|
||||
import * as React from 'react';
|
||||
import './header.css';
|
||||
import { Button } from '@mui/material';
|
||||
|
|
@ -32,6 +32,14 @@ const Header: React.FC<HeaderProps> = ({ isLoggedIn, handleLogout }) => {
|
|||
Logout
|
||||
</Button>
|
||||
)}
|
||||
|
||||
{!isLoggedIn() && (
|
||||
<div className="auth-selection-btn">
|
||||
<Link to="/auth-selection">
|
||||
<button className="auth-btn">Connexion</button>
|
||||
</Link>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
|
|
|||
27
client/src/pages/AuthSelection/AuthCallback.tsx
Normal file
27
client/src/pages/AuthSelection/AuthCallback.tsx
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
import { useEffect } from 'react';
|
||||
import { useNavigate, useLocation } from 'react-router-dom';
|
||||
import apiService from '../../services/ApiService';
|
||||
|
||||
const OAuthCallback: React.FC = () => {
|
||||
const navigate = useNavigate();
|
||||
const location = useLocation();
|
||||
|
||||
useEffect(() => {
|
||||
const searchParams = new URLSearchParams(location.search);
|
||||
const user = searchParams.get('user');
|
||||
|
||||
if (user) {
|
||||
// Save user data to localStorage or sessionStorage
|
||||
apiService.saveToken(user);
|
||||
|
||||
// Navigate to the dashboard or another page
|
||||
navigate('/');
|
||||
} else {
|
||||
navigate('/auth-selection');
|
||||
}
|
||||
}, [location, navigate]);
|
||||
|
||||
return <div>Loading...</div>;
|
||||
};
|
||||
|
||||
export default OAuthCallback;
|
||||
|
|
@ -1,9 +1,9 @@
|
|||
import React, { useState, useEffect } from 'react';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
import './authselection.css';
|
||||
import SimpleLogin from './SimpleLogin';
|
||||
|
||||
const AuthSelection: React.FC = () => {
|
||||
const [simpleLoginData, setSimpleLoginData] = useState({ username: '', password: '' });
|
||||
const [authData, setAuthData] = useState<any>(null); // Stocke les données d'auth
|
||||
const navigate = useNavigate();
|
||||
|
||||
|
|
@ -22,17 +22,6 @@ const AuthSelection: React.FC = () => {
|
|||
fetchAuthData(); // Appel de la fonction pour récupérer les données
|
||||
}, []);
|
||||
|
||||
const handleSimpleLoginChange = (e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
const { name, value } = e.target;
|
||||
setSimpleLoginData((prev) => ({ ...prev, [name]: value }));
|
||||
};
|
||||
|
||||
const handleSimpleLoginSubmit = (e: React.FormEvent) => {
|
||||
e.preventDefault();
|
||||
// Logique d'authentification pour Simple Login
|
||||
console.log('Simple Login Data:', simpleLoginData);
|
||||
};
|
||||
|
||||
const handleAuthLogin = (provider: string) => {
|
||||
window.location.href = 'http://localhost:3000/api/auth/' + provider;
|
||||
};
|
||||
|
|
@ -44,25 +33,7 @@ const AuthSelection: React.FC = () => {
|
|||
{/* Formulaire de connexion Simple Login */}
|
||||
{authData && authData['simple-login'] && (
|
||||
<div className="form-container">
|
||||
<form onSubmit={handleSimpleLoginSubmit}>
|
||||
<input
|
||||
type="text"
|
||||
name="username"
|
||||
placeholder="Nom d'utilisateur"
|
||||
value={simpleLoginData.username}
|
||||
onChange={handleSimpleLoginChange}
|
||||
required
|
||||
/>
|
||||
<input
|
||||
type="password"
|
||||
name="password"
|
||||
placeholder="Mot de passe"
|
||||
value={simpleLoginData.password}
|
||||
onChange={handleSimpleLoginChange}
|
||||
required
|
||||
/>
|
||||
<button type="submit">Se connecter</button>
|
||||
</form>
|
||||
<SimpleLogin/>
|
||||
</div>
|
||||
)}
|
||||
|
||||
|
|
|
|||
94
client/src/pages/AuthSelection/SimpleLogin.tsx
Normal file
94
client/src/pages/AuthSelection/SimpleLogin.tsx
Normal file
|
|
@ -0,0 +1,94 @@
|
|||
import { useNavigate, Link } from 'react-router-dom';
|
||||
|
||||
// JoinRoom.tsx
|
||||
import React, { useEffect, useState } from 'react';
|
||||
|
||||
import './simpleLogin.css';
|
||||
import { TextField } from '@mui/material';
|
||||
import LoadingButton from '@mui/lab/LoadingButton';
|
||||
|
||||
import LoginContainer from '../../components/LoginContainer/LoginContainer'
|
||||
import ApiService from '../../services/ApiService';
|
||||
|
||||
const SimpleLogin: React.FC = () => {
|
||||
const navigate = useNavigate();
|
||||
|
||||
const [email, setEmail] = useState('');
|
||||
const [password, setPassword] = useState('');
|
||||
|
||||
const [connectionError, setConnectionError] = useState<string>('');
|
||||
const [isConnecting] = useState<boolean>(false);
|
||||
|
||||
useEffect(() => {
|
||||
return () => {
|
||||
|
||||
};
|
||||
}, []);
|
||||
|
||||
const login = async () => {
|
||||
const result = await ApiService.login(email, password);
|
||||
|
||||
if (result != true) {
|
||||
setConnectionError(result);
|
||||
return;
|
||||
}
|
||||
else {
|
||||
navigate("/")
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
|
||||
return (
|
||||
<LoginContainer
|
||||
title=''
|
||||
error={connectionError}>
|
||||
|
||||
<TextField
|
||||
label="Email"
|
||||
variant="outlined"
|
||||
value={email}
|
||||
onChange={(e) => setEmail(e.target.value)}
|
||||
placeholder="Nom d'utilisateur"
|
||||
sx={{ marginBottom: '1rem' }}
|
||||
fullWidth
|
||||
/>
|
||||
|
||||
<TextField
|
||||
label="Mot de passe"
|
||||
variant="outlined"
|
||||
type="password"
|
||||
value={password}
|
||||
onChange={(e) => setPassword(e.target.value)}
|
||||
placeholder="Nom de la salle"
|
||||
sx={{ marginBottom: '1rem' }}
|
||||
fullWidth
|
||||
/>
|
||||
|
||||
<LoadingButton
|
||||
loading={isConnecting}
|
||||
onClick={login}
|
||||
variant="contained"
|
||||
sx={{ marginBottom: `${connectionError && '2rem'}` }}
|
||||
disabled={!email || !password}
|
||||
>
|
||||
Login
|
||||
</LoadingButton>
|
||||
|
||||
<div className="login-links">
|
||||
|
||||
<Link to="/teacher/resetPassword">
|
||||
Réinitialiser le mot de passe
|
||||
</Link>
|
||||
|
||||
<Link to="/teacher/register">
|
||||
Créer un compte
|
||||
</Link>
|
||||
|
||||
</div>
|
||||
|
||||
</LoginContainer>
|
||||
);
|
||||
};
|
||||
|
||||
export default SimpleLogin;
|
||||
0
client/src/pages/AuthSelection/simpleLogin.css
Normal file
0
client/src/pages/AuthSelection/simpleLogin.css
Normal file
|
|
@ -6,13 +6,6 @@ import { Link } from 'react-router-dom';
|
|||
const Home: React.FC = () => {
|
||||
return (
|
||||
<div className="page">
|
||||
|
||||
<div className="auth-selection-btn">
|
||||
<Link to="/auth-selection">
|
||||
<button className="auth-btn">Connexion</button>
|
||||
</Link>
|
||||
</div>
|
||||
|
||||
<div className="btn-container">
|
||||
|
||||
<Link to="/student/join-room" className="student-btn">
|
||||
|
|
|
|||
|
|
@ -32,7 +32,7 @@ class ApiService {
|
|||
}
|
||||
|
||||
// Helpers
|
||||
private saveToken(token: string): void {
|
||||
public saveToken(token: string): void {
|
||||
const now = new Date();
|
||||
|
||||
const object = {
|
||||
|
|
|
|||
|
|
@ -3,14 +3,18 @@ version: '3'
|
|||
services:
|
||||
|
||||
frontend:
|
||||
image: fuhrmanator/evaluetonsavoir-frontend:latest
|
||||
build:
|
||||
context: ./server
|
||||
dockerfile: Dockerfile
|
||||
container_name: frontend
|
||||
ports:
|
||||
- "5173:5173"
|
||||
restart: always
|
||||
|
||||
backend:
|
||||
image: fuhrmanator/evaluetonsavoir-backend:latest
|
||||
build:
|
||||
context: ./server
|
||||
dockerfile: Dockerfile
|
||||
container_name: backend
|
||||
ports:
|
||||
- "3000:3000"
|
||||
|
|
|
|||
|
|
@ -68,7 +68,6 @@ app.use(session({
|
|||
}));
|
||||
|
||||
authManager = new AuthManager(app)
|
||||
|
||||
app.use(errorHandler)
|
||||
|
||||
// Start server
|
||||
|
|
|
|||
|
|
@ -1,8 +1,19 @@
|
|||
var OAuth2Strategy = require('passport-oauth2')
|
||||
var authUserAssoc = require('../../../models/authUserAssociation')
|
||||
var users = require('../../../models/users')
|
||||
var { hasNestedValue } = require('../../../utils')
|
||||
var jwt = require('../../../middleware/jwtToken')
|
||||
|
||||
class PassportOAuth {
|
||||
constructor(passportjs,auth_name){
|
||||
this.passportjs = passportjs
|
||||
this.auth_name = 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,
|
||||
|
|
@ -18,16 +29,33 @@ class PassportOAuth {
|
|||
});
|
||||
const userInfo = await userInfoResponse.json();
|
||||
|
||||
const user = {
|
||||
id: userInfo.sub,
|
||||
let received_user = {
|
||||
auth_id: userInfo.sub,
|
||||
email: userInfo.email,
|
||||
name: userInfo.name,
|
||||
groups: userInfo.groups ?? [],
|
||||
accessToken: accessToken,
|
||||
refreshToken: refreshToken,
|
||||
expiresIn: params.expires_in
|
||||
roles: []
|
||||
};
|
||||
|
||||
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(self.auth_name._id,received_user.auth_id)
|
||||
|
||||
let user_account = null
|
||||
if(user_association){
|
||||
user_account = await users.getById(user_association.user_id)
|
||||
}
|
||||
else {
|
||||
let user_id = await users.getId(received_user.email)
|
||||
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,
|
||||
|
|
@ -35,7 +63,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);
|
||||
|
|
@ -55,7 +83,14 @@ class PassportOAuth {
|
|||
},
|
||||
(req, res) => {
|
||||
if (req.user) {
|
||||
res.json(req.user)
|
||||
// res.json(req.user)
|
||||
|
||||
//const redirectUrl = `http://your-frontend-url.com/oauth/callback?user=${encodeURIComponent(req.user)}`;
|
||||
//res.redirect(redirectUrl);
|
||||
|
||||
const tokenToSave = jwt.create(req.user.email, req.user._id);
|
||||
res.redirect('/oauth/callback?user=' + tokenToSave);
|
||||
|
||||
console.info(`L'utilisateur '${req.user.name}' vient de se connecter`)
|
||||
} else {
|
||||
res.status(401).json({ error: "L'authentification a échoué" });
|
||||
|
|
|
|||
|
|
@ -1,6 +1,14 @@
|
|||
var OpenIDConnectStrategy = require('passport-openidconnect')
|
||||
var authUserAssoc = require('../../../models/authUserAssociation')
|
||||
var users = require('../../../models/users')
|
||||
var { hasNestedValue } = require('../../../utils')
|
||||
var jwt = require('../../../middleware/jwtToken')
|
||||
|
||||
class PassportOpenIDConnect {
|
||||
constructor(passportjs,auth_name){
|
||||
this.passportjs = passportjs
|
||||
this.auth_name = auth_name
|
||||
}
|
||||
|
||||
async getConfigFromConfigURL(name,provider){
|
||||
try{
|
||||
|
|
@ -15,6 +23,7 @@ class PassportOpenIDConnect {
|
|||
|
||||
const config = await this.getConfigFromConfigURL(name,provider)
|
||||
const cb_url =`${process.env['BACKEND_URL']}${endpoint}/${name}/callback`
|
||||
const self = this
|
||||
|
||||
passport.use(name, new OpenIDConnectStrategy({
|
||||
issuer: config.issuer,
|
||||
|
|
@ -30,15 +39,35 @@ class PassportOpenIDConnect {
|
|||
// patch pour la librairie permet d'obtenir les groupes, PR en cours mais "morte" : https://github.com/jaredhanson/passport-openidconnect/pull/101
|
||||
async function(req, issuer, profile, times, tok, done) {
|
||||
try {
|
||||
const user = {
|
||||
id: profile.id,
|
||||
const received_user = {
|
||||
auth_id: profile.id,
|
||||
email: profile.emails[0].value,
|
||||
name: profile.name.givenName,
|
||||
groups: profile.groups[0].value ?? []
|
||||
roles: []
|
||||
};
|
||||
return done(null, user);
|
||||
} catch (error) {
|
||||
|
||||
if(hasNestedValue(profile,provider.OIDC_ROLE_TEACHER_VALUE)) received_user.roles.push('teacher')
|
||||
if(hasNestedValue(profile,provider.OIDC_ROLE_STUDENT_VALUE)) received_user.roles.push('student')
|
||||
|
||||
const user_association = await authUserAssoc.find_user_association(self.auth_name._id,received_user.auth_id)
|
||||
|
||||
let user_account = null
|
||||
if(user_association){
|
||||
user_account = await users.getById(user_association.user_id)
|
||||
}
|
||||
else {
|
||||
let user_id = await users.getId(received_user.email)
|
||||
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)
|
||||
|
||||
return done(null, user_account);
|
||||
} catch (error) {
|
||||
}
|
||||
}));
|
||||
|
||||
|
|
@ -55,7 +84,11 @@ class PassportOpenIDConnect {
|
|||
},
|
||||
(req, res) => {
|
||||
if (req.user) {
|
||||
res.json(req.user)
|
||||
// res.json(req.user)
|
||||
|
||||
const tokenToSave = jwt.create(req.user.email, req.user._id);
|
||||
res.redirect('/oauth/callback?user=' + tokenToSave);
|
||||
|
||||
console.info(`L'utilisateur '${req.user.name}' vient de se connecter`)
|
||||
} else {
|
||||
res.status(401).json({ error: "L'authentification a échoué" });
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
const fs = require('fs');
|
||||
var passport = require('passport')
|
||||
var authprovider = require('../../models/authProvider')
|
||||
|
||||
class PassportJs{
|
||||
constructor(authmanager,settings){
|
||||
|
|
@ -9,17 +10,20 @@ class PassportJs{
|
|||
this.endpoint = "/api/auth"
|
||||
}
|
||||
|
||||
registerAuth(expressapp){
|
||||
async registerAuth(expressapp){
|
||||
expressapp.use(passport.initialize());
|
||||
expressapp.use(passport.session());
|
||||
|
||||
for(const p of this.providers){
|
||||
for(const [name,provider] of Object.entries(p)){
|
||||
const auth_id = `passportjs_${provider.type}_${name}`
|
||||
|
||||
if(!(provider.type in this.registeredProviders)){
|
||||
this.registerProvider(provider.type)
|
||||
this.registerProvider(provider.type,auth_id)
|
||||
}
|
||||
try{
|
||||
this.registeredProviders[provider.type].register(expressapp,passport,this.endpoint,name,provider)
|
||||
authprovider.create(auth_id)
|
||||
} catch(error){
|
||||
console.error(`La connexion ${name} de type ${provider.type} n'as pu être chargé.`)
|
||||
}
|
||||
|
|
@ -35,17 +39,26 @@ class PassportJs{
|
|||
});
|
||||
}
|
||||
|
||||
registerProvider(providerType){
|
||||
async registerProvider(providerType,auth_id){
|
||||
try{
|
||||
const providerPath = `${process.cwd()}/auth/modules/passport-providers/${providerType}.js`
|
||||
const Provider = require(providerPath);
|
||||
this.registeredProviders[providerType]= new Provider()
|
||||
this.registeredProviders[providerType]= new Provider(this,auth_id)
|
||||
console.info(`Le type de connexion '${providerType}' a été ajouté dans passportjs.`)
|
||||
} catch(error){
|
||||
console.error(`Le type de connexion '${providerType}' n'as pas pu être chargé dans passportjs.`)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
register(userinfos){
|
||||
return this.authmanager.register(userinfos)
|
||||
}
|
||||
|
||||
authenticate(userinfos){
|
||||
return this.authmanager.login(userinfos)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
module.exports = PassportJs;
|
||||
44
server/models/authProvider.js
Normal file
44
server/models/authProvider.js
Normal file
|
|
@ -0,0 +1,44 @@
|
|||
const db = require('../config/db.js')
|
||||
const { ObjectId } = require('mongodb');
|
||||
|
||||
class AuthProvider {
|
||||
constructor(name) {
|
||||
this._id = new ObjectId();
|
||||
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();
|
||||
|
||||
const collection = conn.collection('authprovider');
|
||||
|
||||
const existingauth = await collection.findOne({ name:name });
|
||||
|
||||
if(existingauth){
|
||||
return existingauth._id;
|
||||
}
|
||||
|
||||
const newProvider = {
|
||||
name:name
|
||||
}
|
||||
const result = await collection.insertOne(newProvider);
|
||||
return result.insertedId;
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = new AuthProvider;
|
||||
59
server/models/authUserAssociation.js
Normal file
59
server/models/authUserAssociation.js
Normal file
|
|
@ -0,0 +1,59 @@
|
|||
const authProvider = require('./authProvider.js')
|
||||
const db = require('../config/db.js')
|
||||
const { ObjectId } = require('mongodb');
|
||||
|
||||
|
||||
class AuthUserAssociation {
|
||||
constructor(authProviderId, authId, userId) {
|
||||
this._id = new ObjectId();
|
||||
this.authProvider_id = authProviderId;
|
||||
this.auth_id = authId;
|
||||
this.user_id = userId;
|
||||
this.connected = false;
|
||||
}
|
||||
|
||||
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({ 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;
|
||||
13
server/models/userAuthAssociation.js
Normal file
13
server/models/userAuthAssociation.js
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
const db = require('../config/db.js')
|
||||
const { ObjectId } = require('mongodb');
|
||||
|
||||
|
||||
class AuthUserAssoc {
|
||||
constructor(authProviderId, authId, userId) {
|
||||
this._id = new ObjectId();
|
||||
this.authProvider_id = authProviderId;
|
||||
this.auth_id = authId;
|
||||
this.user_id = userId;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1,14 +1,13 @@
|
|||
//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)
|
||||
return await bcrypt.hash(password, 10);
|
||||
}
|
||||
|
||||
generatePassword() {
|
||||
|
|
@ -16,14 +15,14 @@ class Users {
|
|||
}
|
||||
|
||||
async verify(password, hash) {
|
||||
return await bcrypt.compare(password, hash)
|
||||
return await bcrypt.compare(password, hash);
|
||||
}
|
||||
|
||||
async register(email, password) {
|
||||
await db.connect()
|
||||
await db.connect();
|
||||
const conn = db.getConnection();
|
||||
|
||||
const userCollection = conn.collection('users');
|
||||
const userCollection = conn.collection("users");
|
||||
|
||||
const existingUser = await userCollection.findOne({ email: email });
|
||||
|
||||
|
|
@ -34,23 +33,39 @@ class Users {
|
|||
const newUser = {
|
||||
email: email,
|
||||
password: await this.hashPassword(password),
|
||||
created_at: new Date()
|
||||
created_at: new Date(),
|
||||
};
|
||||
|
||||
await userCollection.insertOne(newUser);
|
||||
let created_user = await userCollection.insertOne(newUser);
|
||||
let user = await this.getById(created_user.insertedId)
|
||||
|
||||
const folderTitle = 'Dossier par Défaut';
|
||||
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;
|
||||
}
|
||||
|
||||
return user;
|
||||
}
|
||||
|
||||
async login(email, password) {
|
||||
await db.connect()
|
||||
await db.connect();
|
||||
const conn = db.getConnection();
|
||||
|
||||
const userCollection = conn.collection('users');
|
||||
const userCollection = conn.collection("users");
|
||||
|
||||
const user = await userCollection.findOne({ email: email });
|
||||
|
||||
|
|
@ -74,25 +89,28 @@ class Users {
|
|||
}
|
||||
|
||||
async changePassword(email, newPassword) {
|
||||
await db.connect()
|
||||
await db.connect();
|
||||
const conn = db.getConnection();
|
||||
|
||||
const userCollection = conn.collection('users');
|
||||
const userCollection = conn.collection("users");
|
||||
|
||||
const hashedPassword = await this.hashPassword(newPassword);
|
||||
|
||||
const result = await userCollection.updateOne({ email }, { $set: { password: hashedPassword } });
|
||||
const result = await userCollection.updateOne(
|
||||
{ email },
|
||||
{ $set: { password: hashedPassword } }
|
||||
);
|
||||
|
||||
if (result.modifiedCount != 1) return null;
|
||||
|
||||
return newPassword
|
||||
return newPassword;
|
||||
}
|
||||
|
||||
async delete(email) {
|
||||
await db.connect()
|
||||
await db.connect();
|
||||
const conn = db.getConnection();
|
||||
|
||||
const userCollection = conn.collection('users');
|
||||
const userCollection = conn.collection("users");
|
||||
|
||||
const result = await userCollection.deleteOne({ email });
|
||||
|
||||
|
|
@ -102,10 +120,10 @@ class Users {
|
|||
}
|
||||
|
||||
async getId(email) {
|
||||
await db.connect()
|
||||
await db.connect();
|
||||
const conn = db.getConnection();
|
||||
|
||||
const userCollection = conn.collection('users');
|
||||
const userCollection = conn.collection("users");
|
||||
|
||||
const user = await userCollection.findOne({ email: email });
|
||||
|
||||
|
|
@ -116,6 +134,47 @@ class Users {
|
|||
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 };
|
||||
delete updatedFields.id;
|
||||
|
||||
const result = await userCollection.updateOne(
|
||||
{ _id: userInfo.id },
|
||||
{ $set: updatedFields }
|
||||
);
|
||||
|
||||
if (result.modifiedCount === 1) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = new Users;
|
||||
module.exports = new Users();
|
||||
|
|
|
|||
35
server/utils.js
Normal file
35
server/utils.js
Normal file
|
|
@ -0,0 +1,35 @@
|
|||
function hasNestedValue(obj, path, delimiter = "_") {
|
||||
const keys = path.split(delimiter);
|
||||
let current = obj;
|
||||
|
||||
for (const key of keys) {
|
||||
while(Array.isArray(current) && current.length == 1 && current[0]){
|
||||
current = current[0]
|
||||
}
|
||||
while(current['value']){
|
||||
current = current.value
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
module.exports = { hasNestedValue};
|
||||
Loading…
Reference in a new issue