|
1 | 1 | import { Logger } from '@nestjs/common' |
2 | 2 | import { ModuleRef } from '@nestjs/core' |
| 3 | +import { Types } from 'mongoose' |
3 | 4 | import { Command, CommandRunner, InquirerService, Question, QuestionSet, SubCommand } from 'nest-commander' |
4 | | -import { AgentsCreateDto } from '~/core/agents/_dto/agents.dto' |
| 5 | +import { AgentsCreateDto, AgentsUpdateDto } from '~/core/agents/_dto/agents.dto' |
5 | 6 | import { AgentsService } from '~/core/agents/agents.service' |
6 | 7 | import { Agents } from './_schemas/agents.schema' |
7 | 8 |
|
| 9 | +function isTotpEnabledForAgentSecurity(security: Record<string, unknown>): boolean { |
| 10 | + const otpKey = `${security.otpKey || ''}`.trim().replace(/\s+/g, '').toUpperCase() |
| 11 | + if (!otpKey) return false |
| 12 | + return /^[A-Z2-7]+=*$/.test(otpKey) && otpKey.length >= 16 |
| 13 | +} |
| 14 | + |
| 15 | +function hasWebAuthnKeyForAgentSecurity(security: Record<string, unknown>): boolean { |
| 16 | + const keys = security.u2fKey |
| 17 | + if (!Array.isArray(keys)) return false |
| 18 | + return keys.some((key) => { |
| 19 | + const entry = key && typeof key === 'object' ? (key as Record<string, unknown>) : {} |
| 20 | + return `${entry.credentialId || ''}`.trim() && `${entry.publicKey || ''}`.trim() |
| 21 | + }) |
| 22 | +} |
| 23 | + |
8 | 24 | /** |
9 | 25 | * Ensemble de questions interactives pour la création d'un agent. |
10 | 26 | * |
@@ -137,6 +153,74 @@ export class AgentsCreateCommand extends CommandRunner { |
137 | 153 | } |
138 | 154 | } |
139 | 155 |
|
| 156 | +/** |
| 157 | + * Supprime le MFA (TOTP + clés FIDO/WebAuthn) d'un agent. |
| 158 | + * |
| 159 | + * @example |
| 160 | + * ```bash |
| 161 | + * yarn console agents clear-mfa admin |
| 162 | + * ``` |
| 163 | + */ |
| 164 | +@SubCommand({ name: 'clear-mfa', arguments: '<username>' }) |
| 165 | +export class AgentsClearMfaCommand extends CommandRunner { |
| 166 | + private readonly logger = new Logger(AgentsClearMfaCommand.name) |
| 167 | + |
| 168 | + public constructor( |
| 169 | + protected moduleRef: ModuleRef, |
| 170 | + private readonly agentsService: AgentsService, |
| 171 | + ) { |
| 172 | + super() |
| 173 | + } |
| 174 | + |
| 175 | + async run(inputs: string[], _options: unknown): Promise<void> { |
| 176 | + const username = `${inputs[0] || ''}`.trim() |
| 177 | + if (!username) { |
| 178 | + console.error('Usage: yarn console agents clear-mfa <username>') |
| 179 | + return |
| 180 | + } |
| 181 | + |
| 182 | + this.logger.log(`Clearing MFA for agent "${username}"...`) |
| 183 | + |
| 184 | + try { |
| 185 | + const agent = (await this.agentsService.findOne<Agents>({ username })) as Agents | null |
| 186 | + if (!agent?._id) { |
| 187 | + console.error(`Agent introuvable: ${username}`) |
| 188 | + return |
| 189 | + } |
| 190 | + |
| 191 | + const currentSecurity = |
| 192 | + agent.security && typeof agent.security === 'object' |
| 193 | + ? typeof (agent.security as { toObject?: () => Record<string, unknown> }).toObject === 'function' |
| 194 | + ? (agent.security as { toObject: () => Record<string, unknown> }).toObject() |
| 195 | + : { ...(agent.security as unknown as Record<string, unknown>) } |
| 196 | + : {} |
| 197 | + |
| 198 | + const hadTotp = isTotpEnabledForAgentSecurity(currentSecurity) |
| 199 | + const hadWebAuthn = hasWebAuthnKeyForAgentSecurity(currentSecurity) |
| 200 | + const fidoKeyCount = Array.isArray(currentSecurity.u2fKey) ? currentSecurity.u2fKey.length : 0 |
| 201 | + |
| 202 | + if (!hadTotp && !hadWebAuthn) { |
| 203 | + console.log(`Aucun MFA actif pour "${username}".`) |
| 204 | + return |
| 205 | + } |
| 206 | + |
| 207 | + await this.agentsService.update(agent._id as Types.ObjectId, { |
| 208 | + security: { |
| 209 | + ...currentSecurity, |
| 210 | + otpKey: '', |
| 211 | + u2fKey: [], |
| 212 | + }, |
| 213 | + } as AgentsUpdateDto) |
| 214 | + |
| 215 | + console.log(`MFA désactivé pour "${username}".`) |
| 216 | + if (hadTotp) console.log('- TOTP supprimé') |
| 217 | + if (hadWebAuthn) console.log(`- ${fidoKeyCount} clé(s) FIDO/WebAuthn supprimée(s)`) |
| 218 | + } catch (error) { |
| 219 | + console.error('Erreur lors de la suppression du MFA', error) |
| 220 | + } |
| 221 | + } |
| 222 | +} |
| 223 | + |
140 | 224 | @SubCommand({ name: 'list' }) |
141 | 225 | export class AgentsListCommand extends CommandRunner { |
142 | 226 | private readonly logger = new Logger(AgentsListCommand.name) |
@@ -176,9 +260,14 @@ export class AgentsListCommand extends CommandRunner { |
176 | 260 | * ```bash |
177 | 261 | * yarn run console agents create |
178 | 262 | * yarn run console agents list |
| 263 | + * yarn console agents clear-mfa <username> |
179 | 264 | * ``` |
180 | 265 | */ |
181 | | -@Command({ name: 'agents', arguments: '<task>', subCommands: [AgentsCreateCommand, AgentsListCommand] }) |
| 266 | +@Command({ |
| 267 | + name: 'agents', |
| 268 | + arguments: '<task>', |
| 269 | + subCommands: [AgentsCreateCommand, AgentsListCommand, AgentsClearMfaCommand], |
| 270 | +}) |
182 | 271 | export class AgentsCommand extends CommandRunner { |
183 | 272 | /** |
184 | 273 | * Constructeur de la commande agents |
|
0 commit comments