package main import ( "flag" "fmt" "os" "strings" ) func (t *tool) runWebAuthn(args []string) { if len(args) == 0 { fatalf("webauthn requires a subcommand: list, delete, reset") } switch args[0] { case "list": t.webauthnList(args[1:]) case "delete": t.webauthnDelete(args[1:]) case "reset": t.webauthnReset(args[1:]) default: fatalf("unknown webauthn subcommand %q", args[0]) } } func (t *tool) webauthnList(args []string) { fs := flag.NewFlagSet("webauthn list", flag.ExitOnError) id := fs.String("id", "", "account UUID (required)") _ = fs.Parse(args) if *id == "" { fatalf("webauthn list: --id is required") } a, err := t.db.GetAccountByUUID(*id) if err != nil { fatalf("get account: %v", err) } creds, err := t.db.GetWebAuthnCredentials(a.ID) if err != nil { fatalf("list webauthn credentials: %v", err) } if len(creds) == 0 { fmt.Printf("No WebAuthn credentials for account %s\n", a.Username) return } fmt.Printf("WebAuthn credentials for %s:\n\n", a.Username) fmt.Printf("%-6s %-20s %-12s %-8s %-20s %-20s\n", "ID", "NAME", "DISCOVERABLE", "COUNT", "CREATED", "LAST USED") fmt.Println(strings.Repeat("-", 96)) for _, c := range creds { disc := "no" if c.Discoverable { disc = "yes" } lastUsed := "never" if c.LastUsedAt != nil { lastUsed = c.LastUsedAt.UTC().Format("2006-01-02 15:04:05") } fmt.Printf("%-6d %-20s %-12s %-8d %-20s %-20s\n", c.ID, c.Name, disc, c.SignCount, c.CreatedAt.UTC().Format("2006-01-02 15:04:05"), lastUsed) } } func (t *tool) webauthnDelete(args []string) { fs := flag.NewFlagSet("webauthn delete", flag.ExitOnError) id := fs.String("id", "", "account UUID (required)") credID := fs.Int64("credential-id", 0, "credential DB row ID (required)") _ = fs.Parse(args) if *id == "" || *credID == 0 { fatalf("webauthn delete: --id and --credential-id are required") } a, err := t.db.GetAccountByUUID(*id) if err != nil { fatalf("get account: %v", err) } if err := t.db.DeleteWebAuthnCredential(*credID, a.ID); err != nil { fatalf("delete webauthn credential: %v", err) } if err := t.db.WriteAuditEvent("webauthn_removed", nil, &a.ID, "", fmt.Sprintf(`{"actor":"mciasdb","credential_id":%d}`, *credID)); err != nil { fmt.Fprintf(os.Stderr, "warning: write audit event: %v\n", err) } fmt.Printf("WebAuthn credential %d deleted from account %s\n", *credID, a.Username) } func (t *tool) webauthnReset(args []string) { fs := flag.NewFlagSet("webauthn reset", flag.ExitOnError) id := fs.String("id", "", "account UUID (required)") _ = fs.Parse(args) if *id == "" { fatalf("webauthn reset: --id is required") } a, err := t.db.GetAccountByUUID(*id) if err != nil { fatalf("get account: %v", err) } count, err := t.db.DeleteAllWebAuthnCredentials(a.ID) if err != nil { fatalf("delete all webauthn credentials: %v", err) } if err := t.db.WriteAuditEvent("webauthn_removed", nil, &a.ID, "", fmt.Sprintf(`{"actor":"mciasdb","action":"reset_webauthn","count":%d}`, count)); err != nil { fmt.Fprintf(os.Stderr, "warning: write audit event: %v\n", err) } fmt.Printf("Removed %d WebAuthn credential(s) from account %s\n", count, a.Username) }