diff --git a/backend/src/routes/user/user.service.ts b/backend/src/routes/user/user.service.ts index 08017f7..77365c0 100644 --- a/backend/src/routes/user/user.service.ts +++ b/backend/src/routes/user/user.service.ts @@ -155,6 +155,16 @@ export class UserService { async updateUser(id: string, dto: UpdateUserDto, currentUser: Users): Promise { const user = await this.findOne(id); + // Le super administrateur conserve une identité figée. + if ( + user.role === RoleType.SUPER_ADMIN && + (dto.nom !== undefined || dto.prenom !== undefined) + ) { + throw new ForbiddenException( + 'Le nom et le prénom du super administrateur ne peuvent pas être modifiés', + ); + } + // Interdire changement de rôle si pas super admin if (dto.role && currentUser.role !== RoleType.SUPER_ADMIN) { throw new ForbiddenException('Accès réservé aux super admins'); @@ -251,6 +261,12 @@ export class UserService { if (currentUser.role !== RoleType.SUPER_ADMIN) { throw new ForbiddenException('Accès réservé aux super admins'); } + const user = await this.findOne(id); + if (user.role === RoleType.SUPER_ADMIN) { + throw new ForbiddenException( + 'Le super administrateur ne peut pas être supprimé', + ); + } const result = await this.usersRepository.delete(id); if (result.affected === 0) { throw new NotFoundException('Utilisateur introuvable'); diff --git a/frontend/lib/screens/administrateurs/creation/gestionnaires_create.dart b/frontend/lib/screens/administrateurs/creation/gestionnaires_create.dart index 9417eae..9a79fbb 100644 --- a/frontend/lib/screens/administrateurs/creation/gestionnaires_create.dart +++ b/frontend/lib/screens/administrateurs/creation/gestionnaires_create.dart @@ -39,6 +39,10 @@ class _AdminUserFormDialogState extends State { List _relais = []; String? _selectedRelaisId; bool get _isEditMode => widget.initialUser != null; + bool get _isSuperAdminTarget => + widget.initialUser?.role.toLowerCase() == 'super_admin'; + bool get _isLockedAdminIdentity => + _isEditMode && widget.adminMode && _isSuperAdminTarget; @override void initState() { @@ -212,10 +216,12 @@ class _AdminUserFormDialogState extends State { if (_isEditMode) { if (widget.adminMode) { + final lockedNom = _toTitleCase(widget.initialUser!.nom ?? ''); + final lockedPrenom = _toTitleCase(widget.initialUser!.prenom ?? ''); await UserService.updateAdministrateur( adminId: widget.initialUser!.id, - nom: normalizedNom, - prenom: normalizedPrenom, + nom: _isLockedAdminIdentity ? lockedNom : normalizedNom, + prenom: _isLockedAdminIdentity ? lockedPrenom : normalizedPrenom, email: _emailController.text.trim(), telephone: normalizedPhone.isEmpty ? _normalizePhone(widget.initialUser!.telephone ?? '') @@ -308,6 +314,7 @@ class _AdminUserFormDialogState extends State { Future _delete() async { if (widget.readOnly) return; + if (_isSuperAdminTarget) return; if (!_isEditMode || _isSubmitting) return; final confirmed = await showDialog( @@ -430,11 +437,12 @@ class _AdminUserFormDialogState extends State { child: const Text('Fermer'), ), ] else if (_isEditMode) ...[ - OutlinedButton( - onPressed: _isSubmitting ? null : _delete, - style: OutlinedButton.styleFrom(foregroundColor: Colors.red.shade700), - child: const Text('Supprimer'), - ), + if (!_isSuperAdminTarget) + OutlinedButton( + onPressed: _isSubmitting ? null : _delete, + style: OutlinedButton.styleFrom(foregroundColor: Colors.red.shade700), + child: const Text('Supprimer'), + ), FilledButton.icon( onPressed: _isSubmitting ? null : _submit, icon: _isSubmitting @@ -471,26 +479,30 @@ class _AdminUserFormDialogState extends State { Widget _buildNomField() { return TextFormField( controller: _nomController, - readOnly: widget.readOnly, + readOnly: widget.readOnly || _isLockedAdminIdentity, textCapitalization: TextCapitalization.words, decoration: const InputDecoration( labelText: 'Nom', border: OutlineInputBorder(), ), - validator: widget.readOnly ? null : (v) => _required(v, 'Nom'), + validator: (widget.readOnly || _isLockedAdminIdentity) + ? null + : (v) => _required(v, 'Nom'), ); } Widget _buildPrenomField() { return TextFormField( controller: _prenomController, - readOnly: widget.readOnly, + readOnly: widget.readOnly || _isLockedAdminIdentity, textCapitalization: TextCapitalization.words, decoration: const InputDecoration( labelText: 'Prénom', border: OutlineInputBorder(), ), - validator: widget.readOnly ? null : (v) => _required(v, 'Prénom'), + validator: (widget.readOnly || _isLockedAdminIdentity) + ? null + : (v) => _required(v, 'Prénom'), ); }