diff --git a/auth_config.json b/auth_config.json new file mode 100644 index 0000000..3acc914 --- /dev/null +++ b/auth_config.json @@ -0,0 +1,55 @@ +{ + "auth": { + "passportjs": [ + { + "provider1": { + "type": "oauth", + "OAUTH_AUTHORIZATION_URL": "https://www.testurl.com/oauth2/authorize", + "OAUTH_TOKEN_URL": "https://www.testurl.com/oauth2/token", + "OAUTH_CLIENT_ID": "your_oauth_client_id", + "OAUTH_CLIENT_SECRET": "your_oauth_client_secret", + "OAUTH_CALLBACK_URL": "https://localhost:3000/auth/provider/callback", + "OAUTH_ADD_SCOPE": "scopes", + "OAUTH_ROLE_TEACHER_VALUE": "teacher-claim-value", + "OAUTH_ROLE_STUDENT_VALUE": "student-claim-value" + } + }, + { + "provider2": { + "type": "oidc", + "OIDC_CLIENT_ID": "your_oidc_client_id", + "OIDC_CLIENT_SECRET": "your_oidc_client_secret", + "OIDC_ISSUER_URL": "https://your-issuer.com", + "OIDC_CALLBACK_URL": "http://localhost:3000/auth/oidc/callback" + } + }, + { + "provider3": { + "type": "oauth", + "OAUTH_AUTHORIZATION_URL": "https://www.testurl.com/oauth2/authorize", + "OAUTH_TOKEN_URL": "https://www.testurl.com/oauth2/token", + "OAUTH_CLIENT_ID": "your_oauth_client_id", + "OAUTH_CLIENT_SECRET": "your_oauth_client_secret", + "OAUTH_CALLBACK_URL": "https://localhost:3000/auth/provider/callback", + "OAUTH_ADD_SCOPE": "scopes", + "OAUTH_ROLE_TEACHER_VALUE": "teacher-claim-value", + "OAUTH_ROLE_STUDENT_VALUE": "student-claim-value" + } + }, + { + "provider4": { + "type": "oidc", + "OIDC_CLIENT_ID": "your_oidc_client_id", + "OIDC_CLIENT_SECRET": "your_oidc_client_secret", + "OIDC_ISSUER_URL": "https://your-issuer.com", + "OIDC_CALLBACK_URL": "http://localhost:3000/auth/oidc/callback" + } + } + ], + "simple-login": { + "enabled": true, + "name": "provider3", + "SESSION_SECRET": "your_session_secret" + } + } +} \ No newline at end of file diff --git a/docker-compose.yaml b/docker-compose.yaml index 284a46e..077e2ae 100644 --- a/docker-compose.yaml +++ b/docker-compose.yaml @@ -23,6 +23,8 @@ services: EMAIL_PSW: 'vvml wmfr dkzb vjzb' JWT_SECRET: haQdgd2jp09qb897GeBZyJetC8ECSpbFJe FRONTEND_URL: "http://localhost:5173" + volumes: + - ./auth_config.json:/usr/src/app/serveur/config/auth_config.json depends_on: - mongo restart: always diff --git a/server/__tests__/auth.test.js b/server/__tests__/auth.test.js new file mode 100644 index 0000000..f4c8ea4 --- /dev/null +++ b/server/__tests__/auth.test.js @@ -0,0 +1,103 @@ +const request = require('supertest'); +const AuthConfig = require('../config/auth.js'); + +const mockConfig = { + "auth": { + "passportjs": [ + { + "provider1": { + "type": "oauth", + "OAUTH_AUTHORIZATION_URL": "https://www.testurl.com/oauth2/authorize", + "OAUTH_TOKEN_URL": "https://www.testurl.com/oauth2/token", + "OAUTH_CLIENT_ID": "your_oauth_client_id", + "OAUTH_CLIENT_SECRET": "your_oauth_client_secret", + "OAUTH_CALLBACK_URL": "https://localhost:3000/auth/provider/callback", + "OAUTH_ADD_SCOPE": "scopes", + "OAUTH_ROLE_TEACHER_VALUE": "teacher-claim-value", + "OAUTH_ROLE_STUDENT_VALUE": "student-claim-value" + } + }, + { + "provider2": { + "type": "oidc", + "OIDC_CLIENT_ID": "your_oidc_client_id", + "OIDC_CLIENT_SECRET": "your_oidc_client_secret", + "OIDC_ISSUER_URL": "https://your-issuer.com", + "OIDC_CALLBACK_URL": "http://localhost:3000/auth/oidc/callback" + } + } + ], + "simple-login": { + "enabled": true, + "name": "provider3", + "SESSION_SECRET": "your_session_secret" + } + } +}; + +// Créez une instance de AuthConfig en utilisant la configuration mockée +describe('AuthConfig Class Tests', () => { + let authConfigInstance; + + // Initialisez l'instance avec la configuration mockée + beforeAll(() => { + authConfigInstance = new AuthConfig(); + authConfigInstance.loadConfigTest(mockConfig); // On injecte la configuration mockée + }); + + it('devrait retourner la configuration PassportJS', () => { + const config = authConfigInstance.getPassportJSConfig(); + expect(config).toHaveProperty('provider1'); + expect(config).toHaveProperty('provider2'); + }); + + it('devrait retourner la configuration Simple Login', () => { + const config = authConfigInstance.getSimpleLoginConfig(); + expect(config).toHaveProperty('name', 'provider3'); + expect(config).toHaveProperty('SESSION_SECRET', 'your_session_secret'); + }); + + it('devrait retourner les providers OAuth', () => { + const oauthProviders = authConfigInstance.getOAuthProviders(); + expect(Array.isArray(oauthProviders)).toBe(true); + expect(oauthProviders.length).toBe(1); // Il y a un seul provider OAuth + expect(oauthProviders[0]).toHaveProperty('provider1'); + }); + + it('devrait valider la configuration des providers', () => { + expect(() => authConfigInstance.validateProvidersConfig()).not.toThrow(); + }); + + it('devrait lever une erreur si une configuration manque', () => { + const invalidMockConfig = { + "auth": { + "passportjs": [ + { + "provider1": { + "type": "oauth", + "OAUTH_CLIENT_ID": "your_oauth_client_id" // Il manque des champs nécessaires + } + } + ] + } + }; + + const instanceWithInvalidConfig = new AuthConfig(); + instanceWithInvalidConfig.loadConfigTest(invalidMockConfig); + + // Vérifiez que l'erreur est lancée avec les champs manquants corrects + expect(() => instanceWithInvalidConfig.validateProvidersConfig()).toThrow( + new Error(`Configuration invalide pour les providers suivants : [ + { + "provider": "provider1", + "missingFields": [ + "OAUTH_AUTHORIZATION_URL", + "OAUTH_TOKEN_URL", + "OAUTH_CLIENT_SECRET", + "OAUTH_CALLBACK_URL" + ] + } +]`) + ); + }); +}); \ No newline at end of file diff --git a/server/app.js b/server/app.js index e1ab3a6..27e9541 100644 --- a/server/app.js +++ b/server/app.js @@ -13,6 +13,7 @@ const folderRouter = require('./routers/folders.js'); const quizRouter = require('./routers/quiz.js'); const imagesRouter = require('./routers/images.js') const AuthManager = require('./auth/auth-manager.js') +const authRouter = require('./routers/auth.js') // Setup environement dotenv.config(); @@ -49,6 +50,7 @@ app.use('/api/user', userRouter); app.use('/api/folder', folderRouter); app.use('/api/quiz', quizRouter); app.use('/api/image', imagesRouter); +app.use('/api/auth', authRouter); // Add Auths methods const session = require('express-session'); diff --git a/server/config/auth.js b/server/config/auth.js new file mode 100644 index 0000000..b8b1f34 --- /dev/null +++ b/server/config/auth.js @@ -0,0 +1,184 @@ +const fs = require('fs'); +const path = require('path'); +const pathAuthConfig = './auth_config.json'; + +const configPath = path.join(__dirname, pathAuthConfig); + +class AuthConfig { + + config = null; + + + // Méthode pour lire le fichier de configuration JSON + loadConfig() { + try { + const configData = fs.readFileSync(configPath, 'utf-8'); + this.config = JSON.parse(configData); + } catch (error) { + console.error("Erreur lors de la lecture du fichier de configuration :", error); + return null; + } + } + + // Méthode pour load le fichier de test + loadConfigTest(mockConfig) { + this.config = mockConfig; + } + + // Méthode pour retourner la configuration des fournisseurs PassportJS + getPassportJSConfig() { + if (this.config && this.config.auth && this.config.auth.passportjs) { + const passportConfig = {}; + + this.config.auth.passportjs.forEach(provider => { + const providerName = Object.keys(provider)[0]; + passportConfig[providerName] = provider[providerName]; + }); + + return passportConfig; + } else { + return { error: "Aucune configuration PassportJS disponible." }; + } + } + + // Méthode pour retourner la configuration de Simple Login + getSimpleLoginConfig() { + if (this.config && this.config.auth && this.config.auth["simple-login"]) { + return this.config.auth["simple-login"]; + } else { + return { error: "Aucune configuration Simple Login disponible." }; + } + } + + // Méthode pour retourner tous les providers de type OAuth + getOAuthProviders() { + if (this.config && this.config.auth && this.config.auth.passportjs) { + const oauthProviders = this.config.auth.passportjs.filter(provider => { + const providerName = Object.keys(provider)[0]; + return provider[providerName].type === 'oauth'; + }); + + if (oauthProviders.length > 0) { + return oauthProviders; + } else { + return { error: "Aucun fournisseur OAuth disponible." }; + } + } else { + return { error: "Aucune configuration PassportJS disponible." }; + } + } + + // Méthode pour retourner tous les providers de type OIDC + getOIDCProviders() { + if (this.config && this.config.auth && this.config.auth.passportjs) { + const oidcProviders = this.config.auth.passportjs.filter(provider => { + const providerName = Object.keys(provider)[0]; + return provider[providerName].type === 'oidc'; + }); + + if (oidcProviders.length > 0) { + return oidcProviders; + } else { + return { error: "Aucun fournisseur OIDC disponible." }; + } + } else { + return { error: "Aucune configuration PassportJS disponible." }; + } + } + + // Méthode pour vérifier si tous les providers ont les variables nécessaires + validateProvidersConfig() { + const requiredOAuthFields = [ + 'OAUTH_AUTHORIZATION_URL', 'OAUTH_TOKEN_URL', 'OAUTH_CLIENT_ID', 'OAUTH_CLIENT_SECRET', 'OAUTH_CALLBACK_URL' + ]; + + const requiredOIDCFields = [ + 'OIDC_CLIENT_ID', 'OIDC_CLIENT_SECRET', 'OIDC_ISSUER_URL', 'OIDC_CALLBACK_URL' + ]; + + const missingFieldsReport = []; + + if (this.config && this.config.auth && this.config.auth.passportjs) { + this.config.auth.passportjs.forEach(provider => { + const providerName = Object.keys(provider)[0]; + const providerConfig = provider[providerName]; + + let missingFields = []; + + // Vérification des providers de type OAuth + if (providerConfig.type === 'oauth') { + missingFields = requiredOAuthFields.filter(field => !(field in providerConfig)); + } + // Vérification des providers de type OIDC + else if (providerConfig.type === 'oidc') { + missingFields = requiredOIDCFields.filter(field => !(field in providerConfig)); + } + + // Si des champs manquent, on les ajoute au rapport + if (missingFields.length > 0) { + missingFieldsReport.push({ + provider: providerName, + missingFields: missingFields + }); + } + }); + + // Si des champs manquent, lever une exception + if (missingFieldsReport.length > 0) { + throw new Error(`Configuration invalide pour les providers suivants : ${JSON.stringify(missingFieldsReport, null, 2)}`); + } else { + console.log("Configuration auth_config.json: Tous les providers ont les variables nécessaires.") + return { success: "Tous les providers ont les variables nécessaires." }; + } + } else { + throw new Error("Aucune configuration PassportJS disponible."); + } + } + + // Méthode pour retourner la configuration des fournisseurs PassportJS pour le frontend + getActiveAuth() { + if (this.config && this.config.auth) { + const passportConfig = {}; + + // Gestion des providers PassportJS + if (this.config.auth.passportjs) { + this.config.auth.passportjs.forEach(provider => { + const providerName = Object.keys(provider)[0]; + const providerConfig = provider[providerName]; + + passportConfig[providerName] = {}; + + if (providerConfig.type === 'oauth') { + passportConfig[providerName] = { + type: providerConfig.type, + authorizationUrl: providerConfig.OAUTH_AUTHORIZATION_URL, + callbackUrl: providerConfig.OAUTH_CALLBACK_URL, + }; + } else if (providerConfig.type === 'oidc') { + passportConfig[providerName] = { + type: providerConfig.type, + issuerUrl: providerConfig.OIDC_ISSUER_URL, + callbackUrl: providerConfig.OIDC_CALLBACK_URL + }; + } + }); + } + + // Gestion du Simple Login + if (this.config.auth["simple-login"] && this.config.auth["simple-login"].enabled) { + passportConfig['simple-login'] = { + type: "simple-login", + name: this.config.auth["simple-login"].name + }; + } + + return passportConfig; + } else { + return { error: "Aucune configuration d'authentification disponible." }; + } + } + + +} + +module.exports = AuthConfig; diff --git a/server/controllers/auth.js b/server/controllers/auth.js new file mode 100644 index 0000000..21fa3b1 --- /dev/null +++ b/server/controllers/auth.js @@ -0,0 +1,25 @@ +const AuthConfig = require('../config/auth.js'); + +class authController { + + async getActive(req, res, next) { + try { + + const authC = new AuthConfig(); + authC.loadConfig(); + + const authActive = authC.getActiveAuth(); + + const response = { + authActive + }; + return res.json(response); + } + catch (error) { + return next(error); // Gérer l'erreur + } + } + +} + +module.exports = new authController; \ No newline at end of file diff --git a/server/routers/auth.js b/server/routers/auth.js new file mode 100644 index 0000000..8e8fb4a --- /dev/null +++ b/server/routers/auth.js @@ -0,0 +1,9 @@ +const express = require('express'); +const router = express.Router(); +const jwt = require('../middleware/jwtToken.js'); + +const authController = require('../controllers/auth.js') + +router.get("/getActiveAuth",jwt.authenticate, authController.getActive); + +module.exports = router; \ No newline at end of file