Compare commits

...

3 Commits

6 changed files with 143 additions and 26 deletions

17
.gitignore vendored
View File

@ -1,11 +1,16 @@
# NextJS # Dépendances
node_modules node_modules
# NextJS
.next .next
# Cursor # Environnement
.cursordirectory
# Dev
copy.sh
.env.development .env.development
# IDE et éditeurs
.cursordirectory
.vscode
# Scripts de développement
copy.sh
start.sh start.sh

View File

@ -9,6 +9,8 @@
git_dest: "/opt/Neah-Enkun" git_dest: "/opt/Neah-Enkun"
git_branch: "master" git_branch: "master"
traefik_service_name: "traefik" traefik_service_name: "traefik"
packages:
- jq
docker_packages: docker_packages:
- apt-transport-https - apt-transport-https
- ca-certificates - ca-certificates
@ -33,6 +35,15 @@
delay: 5 delay: 5
tasks: tasks:
- name: Installation de packages supplémentaires
apt:
name: "{{ packages }}"
state: present
register: pkg_status
until: pkg_status is success
retries: 3
delay: 5
- name: Installer les dépendances pour Docker - name: Installer les dépendances pour Docker
apt: apt:
name: "{{ docker_packages }}" name: "{{ docker_packages }}"

View File

@ -33,6 +33,16 @@
- account/view-profile - account/view-profile
student_permissions: student_permissions:
- master-realm/view-users - master-realm/view-users
keycloak_client:
client_id: "front"
client_secret: "Klsbm7hzyXscypXU0wUPPVBrttFPt6Pn"
root_url: "http://neah.local/"
redirect_uris:
- "http://neah.local/*"
- "http://localhost:3000/*"
web_origins:
- "http://neah.local"
- "http://localhost:3000"
tasks: tasks:
- name: Lancer le service Keycloak - name: Lancer le service Keycloak
@ -130,6 +140,69 @@
debug: debug:
msg: "Erreur lors de l'attribution des permissions aux rôles" msg: "Erreur lors de l'attribution des permissions aux rôles"
- name: Configurer le client front
block:
- name: Créer le client
shell: >
docker exec {{ keycloak_container }} {{ keycloak_bin }} create clients -r {{ keycloak_realm }}
-s clientId={{ keycloak_client.client_id }}
-s secret={{ keycloak_client.client_secret }}
-s protocol=openid-connect
-s publicClient=false
-s authorizationServicesEnabled=true
-s serviceAccountsEnabled=true
-s standardFlowEnabled=true
-s implicitFlowEnabled=true
-s directAccessGrantsEnabled=true
-s rootUrl={{ keycloak_client.root_url }}
-s baseUrl={{ keycloak_client.root_url }}
-s 'redirectUris=["{{ keycloak_client.redirect_uris | join('","') }}"]'
-s 'webOrigins=["{{ keycloak_client.web_origins | join('","') }}"]'
register: create_client
until: create_client is success
retries: 3
delay: 5
when: check_config.rc != 0
rescue:
- name: Gérer les erreurs de création du client
debug:
msg: "Erreur lors de la création du client front"
- name: Configurer le Client Scope Profile
block:
- name: Récupérer l'ID du scope profile
shell: |
ID=$(docker exec {{ keycloak_container }} {{ keycloak_bin }} get client-scopes -r {{ keycloak_realm }} --fields id,name --format json | jq -r '.[] | select(.name=="profile") | .id')
echo $ID
register: profile_scope_id
until: profile_scope_id.stdout != ""
retries: 3
delay: 5
- name: Ajouter le Mapper realm roles au scope profile
shell: >
docker exec {{ keycloak_container }} {{ keycloak_bin }} create client-scopes/{{ profile_scope_id.stdout | trim }}/protocol-mappers/models
-r {{ keycloak_realm }}
-s name="realm roles"
-s protocol="openid-connect"
-s protocolMapper="oidc-usermodel-realm-role-mapper"
-s 'config."id.token.claim"=true'
-s 'config."access.token.claim"=true'
-s 'config."userinfo.token.claim"=true'
-s 'config."claim.name"="realm_roles"'
-s 'config."introspection.token.claim"=true'
-s 'config."multivalued"=true'
register: create_mapper
until: create_mapper is success
retries: 3
delay: 5
when: profile_scope_id.stdout != ""
when: check_config.rc != 0
rescue:
- name: Gérer les erreurs de configuration du mapper
debug:
msg: "Erreur lors de la configuration du mapper realm roles"
- name: Supprimer l'administrateur temporaire - name: Supprimer l'administrateur temporaire
block: block:
- name: Récupérer les informations - name: Récupérer les informations

View File

@ -11,33 +11,37 @@ export const authOptions: NextAuthOptions = {
profile(profileData) { profile(profileData) {
return { return {
id: profileData.sub, id: profileData.sub,
name: profileData.name || profileData.preferred_username, first_name: profileData.given_name,
last_name: profileData.family_name,
username: profileData.preferred_username,
email: profileData.email, email: profileData.email,
image: null, role: profileData.realm_roles,
// Extraction du rôle ou groupe de l'utilisateur depuis Keycloak
role: Array.isArray(profileData.realm_access?.roles)
? profileData.realm_access.roles
: [],
}; };
}, },
}), }),
], ],
callbacks: { callbacks: {
async jwt({ token, account, profile }) { async jwt({ token, account, profile }) {
console.log("Token", token);
console.log("Account", account);
console.log("Profile", profile);
// Au moment de la première connexion, sauvegarde de l'access token et du rôle dans le JWT // Au moment de la première connexion, sauvegarde de l'access token et du rôle dans le JWT
if (account && profile) { if (account && profile) {
token.accessToken = account.access_token; token.accessToken = account.access_token;
token.role = profile.role; token.first_name = profile.given_name;
token.last_name = profile.family_name;
token.username = profile.preferred_username;
token.role = profile.realm_roles;
} }
return token; return token;
}, },
async session({ session, token }) { async session({ session, token }) {
// On injecte l'access token et le rôle dans la session accessible côté client // On injecte l'access token et le rôle dans la session accessible côté client
session.accessToken = token.accessToken as string; session.accessToken = token.accessToken as string;
session.user.role = session.user.first_name = token.first_name as string;
typeof token.role == "string" || Array.isArray(token.role) session.user.last_name = token.last_name as string;
? token.role session.user.username = token.username as string;
: []; session.user.role = token.role as string[];
return session; return session;
}, },
}, },

View File

@ -1,6 +1,6 @@
"use client"; "use client";
import { useState } from "react"; import { useEffect, useState } from "react";
import { import {
Calendar, Calendar,
MessageSquare, MessageSquare,
@ -17,6 +17,8 @@ export function MainNav() {
const [isSidebarOpen, setIsSidebarOpen] = useState(false); const [isSidebarOpen, setIsSidebarOpen] = useState(false);
const { data: session } = useSession(); const { data: session } = useSession();
console.log(session);
return ( return (
<> <>
<div className='flex items-center justify-between p-4 bg-black'> <div className='flex items-center justify-between p-4 bg-black'>
@ -70,13 +72,25 @@ export function MainNav() {
<Bell className='w-6 h-6' /> <Bell className='w-6 h-6' />
</Link> </Link>
</div> </div>
<div className='cursor-pointer text-white/80 hover:text-white'> {session ? (
{session ? ( <div className='flex items-center space-x-4'>
<span>{session.user.name}</span> <div className='cursor-pointer text-white/80 hover:text-white'>
) : ( <span>
{session.user.first_name} {session.user.last_name} -{" "}
{session.user.role.includes("admin") ? "Admin" : ""}
{session.user.role.includes("TEACHERS") ? "Teacher" : ""}
{session.user.role.includes("STUDENTS") ? "Student" : ""}
</span>
</div>
<div className='cursor-pointer text-white/80 hover:text-white'>
<Link href='/api/auth/signout'>Logout</Link>
</div>
</div>
) : (
<div className='cursor-pointer text-white/80 hover:text-white'>
<Link href='/api/auth/signin'>Login</Link> <Link href='/api/auth/signin'>Login</Link>
)} </div>
</div> )}
</div> </div>
<Sidebar isOpen={isSidebarOpen} onClose={() => setIsSidebarOpen(false)} /> <Sidebar isOpen={isSidebarOpen} onClose={() => setIsSidebarOpen(false)} />
</> </>

View File

@ -3,13 +3,20 @@ import NextAuth, { DefaultSession, DefaultUser } from "next-auth";
declare module "next-auth" { declare module "next-auth" {
interface Session { interface Session {
user: { user: {
role?: string[] | string | null; first_name: string;
last_name: string;
email: string;
username: string;
role: string[];
} & DefaultSession["user"]; } & DefaultSession["user"];
accessToken?: string; accessToken?: string;
} }
interface JWT { interface JWT {
accessToken?: string; accessToken?: string;
first_name?: string;
last_name?: string;
username?: string;
role?: string[] | string | null; role?: string[] | string | null;
} }
@ -18,6 +25,9 @@ declare module "next-auth" {
} }
interface Profile { interface Profile {
role?: string[] | string | null; given_name: string;
family_name: string;
preferred_username: string;
realm_roles: string[];
} }
} }