@nio-cli/cli 0.3.6 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.env.example +68 -5
- package/Dockerfile.gateway +13 -5
- package/dist/adapters/lang/vendor.js +5 -10
- package/dist/adapters/lang/vendor.js.map +1 -1
- package/dist/adapters/pg/auth-event-repository.js +30 -0
- package/dist/adapters/pg/auth-event-repository.js.map +1 -0
- package/dist/adapters/pg/client.js +22 -4
- package/dist/adapters/pg/client.js.map +1 -1
- package/dist/adapters/pg/login-ip-repository.js +26 -0
- package/dist/adapters/pg/login-ip-repository.js.map +1 -0
- package/dist/adapters/pg/user-repository.js +47 -18
- package/dist/adapters/pg/user-repository.js.map +1 -1
- package/dist/adapters/sms/http-generic.js +21 -0
- package/dist/adapters/sms/http-generic.js.map +1 -1
- package/dist/brand.js +1 -1
- package/dist/brand.js.map +1 -1
- package/dist/cli/commands/auth.js +25 -18
- package/dist/cli/commands/auth.js.map +1 -1
- package/dist/cli/commands/docker.js +4 -4
- package/dist/cli/commands/docker.js.map +1 -1
- package/dist/cli/commands/security.js +76 -5
- package/dist/cli/commands/security.js.map +1 -1
- package/dist/cli/commands/sync.js +5 -1
- package/dist/cli/commands/sync.js.map +1 -1
- package/dist/gateway/config.js +23 -1
- package/dist/gateway/config.js.map +1 -1
- package/dist/gateway/edge-filter.js +20 -3
- package/dist/gateway/edge-filter.js.map +1 -1
- package/dist/gateway/index.js +145 -22
- package/dist/gateway/index.js.map +1 -1
- package/dist/gateway/middleware/auth.js +15 -3
- package/dist/gateway/middleware/auth.js.map +1 -1
- package/dist/gateway/services/login.js +47 -11
- package/dist/gateway/services/login.js.map +1 -1
- package/dist/gateway/services/register.js +20 -0
- package/dist/gateway/services/register.js.map +1 -0
- package/dist/gateway/services/security.js +64 -11
- package/dist/gateway/services/security.js.map +1 -1
- package/dist/gateway/throttle.js +57 -0
- package/dist/gateway/throttle.js.map +1 -0
- package/dist/lib/auth/backup-codes.js +4 -4
- package/dist/lib/auth/backup-codes.js.map +1 -1
- package/dist/lib/auth/breach-check.js +58 -0
- package/dist/lib/auth/breach-check.js.map +1 -0
- package/dist/lib/auth/gateway-client.js +17 -2
- package/dist/lib/auth/gateway-client.js.map +1 -1
- package/dist/lib/auth/gateway-process.js +17 -2
- package/dist/lib/auth/gateway-process.js.map +1 -1
- package/dist/lib/auth/gateway-token.js +2 -2
- package/dist/lib/auth/gateway-token.js.map +1 -1
- package/dist/lib/auth/nio-config.js +29 -7
- package/dist/lib/auth/nio-config.js.map +1 -1
- package/dist/lib/auth/otp.js +3 -3
- package/dist/lib/auth/otp.js.map +1 -1
- package/dist/lib/auth/password.js +33 -10
- package/dist/lib/auth/password.js.map +1 -1
- package/dist/lib/auth/secrets.js +96 -0
- package/dist/lib/auth/secrets.js.map +1 -0
- package/dist/lib/auth/session-store.js +2 -2
- package/dist/lib/auth/session-store.js.map +1 -1
- package/dist/lib/clients/client-install.js +22 -4
- package/dist/lib/clients/client-install.js.map +1 -1
- package/dist/lib/fetch-zipball.js +59 -0
- package/dist/lib/fetch-zipball.js.map +1 -0
- package/dist/lib/load-env.js +17 -5
- package/dist/lib/load-env.js.map +1 -1
- package/dist/lib/open-url.js +5 -2
- package/dist/lib/open-url.js.map +1 -1
- package/dist/lib/proc.js +27 -0
- package/dist/lib/proc.js.map +1 -1
- package/dist/lib/skills/skills-cache.js +25 -12
- package/dist/lib/skills/skills-cache.js.map +1 -1
- package/dist/lib/version-check.js +1 -11
- package/dist/lib/version-check.js.map +1 -1
- package/dist/mcp-server.js +5 -2
- package/dist/mcp-server.js.map +1 -1
- package/dist/tui/launch.js +6 -1
- package/dist/tui/launch.js.map +1 -1
- package/dist/version.js +17 -6
- package/dist/version.js.map +1 -1
- package/docker/docker-compose.yml +56 -26
- package/docker/kong.yml +10 -0
- package/package.json +20 -11
package/.env.example
CHANGED
|
@@ -1,14 +1,73 @@
|
|
|
1
|
-
# Database connection string
|
|
2
|
-
|
|
1
|
+
# Database connection string
|
|
2
|
+
# CLI/MCP: role `nio_cli` (migration 0008) — SELECT em user_cli/auth_sessions,
|
|
3
|
+
# escreve só em sessions/dependency_events/log_session/session_activity.
|
|
4
|
+
NIO_DATABASE_URL=postgres://nio_cli_user:senha@localhost:5432/nio_cli
|
|
3
5
|
|
|
4
|
-
#
|
|
6
|
+
# Gateway: role `nio_gateway` — escreve em user_cli/auth_sessions/login_challenges/
|
|
7
|
+
# auth_events/login_ip_events. Só o processo nio-gateway lê esta var; se ausente,
|
|
8
|
+
# ele cai em NIO_DATABASE_URL (dev com um único role).
|
|
9
|
+
# NIO_GATEWAY_DATABASE_URL=postgres://nio_gw_user:senha@localhost:5432/nio_cli
|
|
10
|
+
|
|
11
|
+
# TLS — descomente e ponha "true" quando o banco tiver TLS (não use com Postgres
|
|
12
|
+
# local sem SSL). Quando ligado, o certificado do servidor É VERIFICADO.
|
|
5
13
|
# NIO_DATABASE_SSL=true
|
|
14
|
+
# NIO_DATABASE_CA=/etc/ssl/nio/db-ca.pem
|
|
15
|
+
# Último recurso — desliga a verificação de cert (expõe a MITM). Prefira NIO_DATABASE_CA:
|
|
16
|
+
# NIO_DATABASE_SSL_INSECURE=1
|
|
6
17
|
|
|
7
18
|
# Gateway — JWT (login v2). Sem prefixo NIO_ de propósito: segredo da equipe,
|
|
8
|
-
# mesmo valor em toda máquina que roda o nio-gateway.
|
|
19
|
+
# mesmo valor em toda máquina que roda o nio-gateway.
|
|
20
|
+
# Mínimo 32 caracteres aleatórios. Gere com: `nio config setup` ou
|
|
21
|
+
# node -e "console.log(require('crypto').randomBytes(32).toString('base64url'))"
|
|
9
22
|
JWT_SECRET=
|
|
10
23
|
# JWT_EXPIRES_IN=12h
|
|
11
24
|
|
|
25
|
+
# ── Endurecimento de cripto (ADR 0011) — tudo opcional, defaults = comportamento atual ──
|
|
26
|
+
#
|
|
27
|
+
# Pepper de senha: segredo de servidor combinado à senha antes do argon2. Um dump
|
|
28
|
+
# do banco SEM ele não dá pra brute-forçar offline. MESMO valor em todo nó do
|
|
29
|
+
# gateway. PERDER = reset de senha forçado dos usuários peppered — FAÇA BACKUP.
|
|
30
|
+
# Formato: id:segredo[,id:segredo] — o de maior id é o atual (rotação: adiciona o
|
|
31
|
+
# novo, o re-hash on login migra, depois remove o velho). Gere: openssl rand -base64 32
|
|
32
|
+
# NIO_PEPPERS=1:troque-por-um-segredo-forte-de-32-ou-mais-chars
|
|
33
|
+
#
|
|
34
|
+
# Chave do HMAC do OTP — separa do JWT_SECRET. Default = JWT_SECRET.
|
|
35
|
+
# OTP_HMAC_SECRET=
|
|
36
|
+
#
|
|
37
|
+
# Rotação de JWT sem derrubar sessão: JWT_SECRETS = kid:segredo[,kid:segredo].
|
|
38
|
+
# A ÚLTIMA entrada assina os tokens novos; QUALQUER uma verifica. Token antigo
|
|
39
|
+
# (sem kid) verifica contra JWT_SECRET. Fluxo: adiciona o kid novo no fim →
|
|
40
|
+
# deploy → espera 12h (TTL do JWT) → remove o kid velho. Ausente = usa JWT_SECRET.
|
|
41
|
+
# JWT_SECRETS=2026a:segredo-antigo-de-32+-chars,2026b:segredo-novo-de-32+-chars
|
|
42
|
+
#
|
|
43
|
+
# Params do argon2id — defaults = piso OWASP. Calibre na máquina do gateway com
|
|
44
|
+
# node scripts/argon2-calibrate.mjs (ou `bun run argon2:calibrate`)
|
|
45
|
+
# mire ~250-500 ms no hash. Subir só re-hasheia no próximo login (não é breaking).
|
|
46
|
+
# NIO_ARGON2_MEMORY_MIB=19
|
|
47
|
+
# NIO_ARGON2_TIME=2
|
|
48
|
+
# NIO_ARGON2_PARALLELISM=1
|
|
49
|
+
#
|
|
50
|
+
# Auditoria de IP de login (ADR 0011 §F) — o gateway registra de quais IPs cada
|
|
51
|
+
# usuário loga (visível em `nio security status`; retenção 90 d). SÓ registro,
|
|
52
|
+
# não bloqueia. Ligue NIO_TRUST_PROXY=1 pra usar o X-Forwarded-For **apenas** se
|
|
53
|
+
# o Kong for a ÚNICA entrada do gateway (senão o IP é forjável).
|
|
54
|
+
# NIO_TRUST_PROXY=1
|
|
55
|
+
|
|
56
|
+
# Trilha de auth persistente (ADR 0012) — `auth_events` guarda todo evento de auth
|
|
57
|
+
# (login OK/falho, 2FA, mudanças de 2FA, logout). Retenção default 180 d.
|
|
58
|
+
# NIO_AUTH_EVENTS_RETENTION_DAYS=180
|
|
59
|
+
# No modo HOST (sem container), o stdout/stderr do gateway — incl. a trilha —
|
|
60
|
+
# vai pra ~/.nio/gateway.log (chmod 600).
|
|
61
|
+
#
|
|
62
|
+
# Teto de sessões (dispositivos) ativas por usuário (SP-5). Login além disso
|
|
63
|
+
# revoga as mais antigas. `nio logout --all` encerra todas de uma vez.
|
|
64
|
+
# NIO_MAX_SESSIONS_PER_USER=5
|
|
65
|
+
#
|
|
66
|
+
# Checagem de senha vazada (SP-7): lista local (sempre) + Have I Been Pwned
|
|
67
|
+
# (k-anonymity — só o prefixo do SHA-1 sai; falha de rede = ignora).
|
|
68
|
+
# NIO_HIBP_DISABLE=1 # desliga o HIBP (ambiente sem saída pra internet)
|
|
69
|
+
# NIO_HIBP_TIMEOUT_MS=1500
|
|
70
|
+
|
|
12
71
|
# Bind do nio-gateway. Default 127.0.0.1 (loopback). Ponha 0.0.0.0 quando o Kong
|
|
13
72
|
# roda em container e alcança o gateway via host.docker.internal.
|
|
14
73
|
# NIO_GATEWAY_HOST=0.0.0.0
|
|
@@ -24,12 +83,16 @@ JWT_SECRET=
|
|
|
24
83
|
# SMS_AUTH_HEADER=X-API-TOKEN: seu-token # linha "Nome: valor"
|
|
25
84
|
# SMS_BODY_TEMPLATE={"to":"{to}","message":"{text}"} # JSON com {to}/{text}/{from}
|
|
26
85
|
# SMS_FROM=NIO # opcional, se o template usar {from}
|
|
86
|
+
#
|
|
87
|
+
# DEV: se SMS_ENDPOINT_URL for loopback (127.0.0.1/localhost — o mock
|
|
88
|
+
# `bun run dev:sms-echo`), o gateway entra em "modo echo": NENHUM SMS real sai.
|
|
89
|
+
# A CLI avisa e mostra o código direto; `nio security status` mostra o backend.
|
|
27
90
|
|
|
28
91
|
# Client de IA (nio ai) — Headroom, proxy de compressão em container (ADR 0007).
|
|
29
92
|
# NIO_HEADROOM_PORT=8787 # porta do proxy no host
|
|
30
93
|
# NIO_HEADROOM_URL=http://127.0.0.1:8787/v1 # override total (o provider do OpenCode aponta pra cá)
|
|
31
94
|
# NIO_HEADROOM_OPENAI_TARGET=https://opencode.ai/zen/v1 # upstream OpenAI-compatível (default: OpenCode Zen)
|
|
32
95
|
|
|
33
|
-
# Debug
|
|
96
|
+
# Debug
|
|
34
97
|
# NIO_DEBUG=1 # log verboso [nio:debug] + stack trace completo nos erros
|
|
35
98
|
# NIO_NO_ANIM=1 # logo Matrix sempre estático (sem a animação de chuva)
|
package/Dockerfile.gateway
CHANGED
|
@@ -1,12 +1,20 @@
|
|
|
1
|
-
|
|
2
|
-
#
|
|
1
|
+
# Build reprodutível (auditoria M-2 + I-3a): um lockfile só (`bun.lock`), imagens
|
|
2
|
+
# pinadas por digest. Multi-stage — o Bun resolve as deps com o lockfile
|
|
3
|
+
# congelado; o runtime continua sendo Node (o alvo do projeto).
|
|
4
|
+
|
|
5
|
+
# --- deps: instala node_modules de runtime a partir do bun.lock ---
|
|
6
|
+
FROM oven/bun:1-slim@sha256:cb3bbbb08e13a4a2ff400f24c7a2a1d5efa83f6ef8544d52d95a519631e2fc61 AS deps
|
|
7
|
+
WORKDIR /app
|
|
8
|
+
COPY package.json bun.lock ./
|
|
9
|
+
RUN bun install --frozen-lockfile --production
|
|
10
|
+
|
|
11
|
+
# --- runtime: Node + curl (healthcheck do compose) ---
|
|
12
|
+
FROM node:20-slim@sha256:2cf067cfed83d5ea958367df9f966191a942351a2df77d6f0193e162b5febfc0
|
|
3
13
|
RUN apt-get update && apt-get install -y --no-install-recommends curl \
|
|
4
14
|
&& rm -rf /var/lib/apt/lists/*
|
|
5
15
|
WORKDIR /app
|
|
6
|
-
|
|
7
|
-
# npm install (não 'ci') porque o repo usa bun.lock, sem package-lock.json.
|
|
16
|
+
COPY --from=deps /app/node_modules ./node_modules
|
|
8
17
|
COPY package.json ./
|
|
9
18
|
COPY dist ./dist
|
|
10
|
-
RUN npm install --omit=dev --no-audit --no-fund
|
|
11
19
|
EXPOSE 3000
|
|
12
20
|
CMD ["node", "dist/gateway/index.js"]
|
|
@@ -3,6 +3,7 @@ import { tmpdir } from 'node:os';
|
|
|
3
3
|
import { join } from 'node:path';
|
|
4
4
|
import AdmZip from 'adm-zip';
|
|
5
5
|
import { homePath } from '../../brand.js';
|
|
6
|
+
import { codeloadZipUrl, fetchZipball, rejectSymlinks } from '../../lib/fetch-zipball.js';
|
|
6
7
|
import { LANG_REPOS } from './repos.js';
|
|
7
8
|
const DEFAULT_TIMEOUT_MS = 30_000;
|
|
8
9
|
async function downloadRepo(spec, baseDir, opts) {
|
|
@@ -11,32 +12,26 @@ async function downloadRepo(spec, baseDir, opts) {
|
|
|
11
12
|
if (!opts.force && existsSync(dest)) {
|
|
12
13
|
return { ...base, status: 'cached' };
|
|
13
14
|
}
|
|
14
|
-
const url =
|
|
15
|
+
const url = codeloadZipUrl(spec.repo, spec.ref);
|
|
15
16
|
const staging = join(tmpdir(), `nio-lang-${spec.dir}-${process.pid}-${Date.now()}`);
|
|
16
17
|
const timeoutMs = opts.timeoutMs ?? DEFAULT_TIMEOUT_MS;
|
|
17
|
-
const ac = new AbortController();
|
|
18
|
-
const timer = setTimeout(() => ac.abort(), timeoutMs);
|
|
19
18
|
try {
|
|
20
|
-
const
|
|
21
|
-
if (!res.ok)
|
|
22
|
-
throw new Error(`HTTP ${res.status} ao baixar ${url}`);
|
|
23
|
-
const buf = Buffer.from(await res.arrayBuffer());
|
|
19
|
+
const buf = await fetchZipball(url, { timeoutMs });
|
|
24
20
|
rmSync(staging, { recursive: true, force: true });
|
|
25
21
|
mkdirSync(staging, { recursive: true });
|
|
26
22
|
new AdmZip(buf).extractAllTo(staging, true);
|
|
27
23
|
const dirs = readdirSync(staging, { withFileTypes: true }).filter((e) => e.isDirectory());
|
|
28
24
|
const root = dirs.length === 1 ? join(staging, dirs[0].name) : staging;
|
|
25
|
+
rejectSymlinks(root);
|
|
29
26
|
rmSync(dest, { recursive: true, force: true });
|
|
30
27
|
mkdirSync(dest, { recursive: true });
|
|
31
28
|
cpSync(root, dest, { recursive: true });
|
|
32
29
|
return { ...base, status: 'fetched' };
|
|
33
30
|
}
|
|
34
31
|
catch (err) {
|
|
35
|
-
|
|
36
|
-
return { ...base, status: existsSync(dest) ? 'cached' : 'failed', error: msg };
|
|
32
|
+
return { ...base, status: existsSync(dest) ? 'cached' : 'failed', error: err.message };
|
|
37
33
|
}
|
|
38
34
|
finally {
|
|
39
|
-
clearTimeout(timer);
|
|
40
35
|
rmSync(staging, { recursive: true, force: true });
|
|
41
36
|
}
|
|
42
37
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"vendor.js","sourceRoot":"","sources":["../../../src/adapters/lang/vendor.ts"],"names":[],"mappings":"AAMA,OAAO,EAAE,UAAU,EAAE,SAAS,EAAE,MAAM,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,SAAS,CAAC;AAC7E,OAAO,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC;AACjC,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,MAAM,MAAM,SAAS,CAAC;AAC7B,OAAO,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAC1C,OAAO,EAAE,UAAU,EAAiB,MAAM,YAAY,CAAC;AAEvD,MAAM,kBAAkB,GAAG,MAAM,CAAC;AAWlC,KAAK,UAAU,YAAY,CACzB,IAAc,EACd,OAAe,EACf,IAA6C;IAE7C,MAAM,IAAI,GAAG,IAAI,CAAC,OAAO,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC;IACrC,MAAM,IAAI,GAAG,EAAE,GAAG,EAAE,IAAI,CAAC,GAAG,EAAE,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE,GAAG,EAAE,IAAI,CAAC,GAAG,EAAE,CAAC;IAE/D,IAAI,CAAC,IAAI,CAAC,KAAK,IAAI,UAAU,CAAC,IAAI,CAAC,EAAE,CAAC;QACpC,OAAO,EAAE,GAAG,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,CAAC;IACvC,CAAC;IAED,MAAM,GAAG,GAAG
|
|
1
|
+
{"version":3,"file":"vendor.js","sourceRoot":"","sources":["../../../src/adapters/lang/vendor.ts"],"names":[],"mappings":"AAMA,OAAO,EAAE,UAAU,EAAE,SAAS,EAAE,MAAM,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,SAAS,CAAC;AAC7E,OAAO,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC;AACjC,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,MAAM,MAAM,SAAS,CAAC;AAC7B,OAAO,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAC1C,OAAO,EAAE,cAAc,EAAE,YAAY,EAAE,cAAc,EAAE,MAAM,4BAA4B,CAAC;AAC1F,OAAO,EAAE,UAAU,EAAiB,MAAM,YAAY,CAAC;AAEvD,MAAM,kBAAkB,GAAG,MAAM,CAAC;AAWlC,KAAK,UAAU,YAAY,CACzB,IAAc,EACd,OAAe,EACf,IAA6C;IAE7C,MAAM,IAAI,GAAG,IAAI,CAAC,OAAO,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC;IACrC,MAAM,IAAI,GAAG,EAAE,GAAG,EAAE,IAAI,CAAC,GAAG,EAAE,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE,GAAG,EAAE,IAAI,CAAC,GAAG,EAAE,CAAC;IAE/D,IAAI,CAAC,IAAI,CAAC,KAAK,IAAI,UAAU,CAAC,IAAI,CAAC,EAAE,CAAC;QACpC,OAAO,EAAE,GAAG,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,CAAC;IACvC,CAAC;IAED,MAAM,GAAG,GAAG,cAAc,CAAC,IAAI,CAAC,IAAI,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC;IAChD,MAAM,OAAO,GAAG,IAAI,CAAC,MAAM,EAAE,EAAE,YAAY,IAAI,CAAC,GAAG,IAAI,OAAO,CAAC,GAAG,IAAI,IAAI,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC;IACpF,MAAM,SAAS,GAAG,IAAI,CAAC,SAAS,IAAI,kBAAkB,CAAC;IACvD,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,MAAM,YAAY,CAAC,GAAG,EAAE,EAAE,SAAS,EAAE,CAAC,CAAC;QAGnD,MAAM,CAAC,OAAO,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;QAClD,SAAS,CAAC,OAAO,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;QACxC,IAAI,MAAM,CAAC,GAAG,CAAC,CAAC,YAAY,CAAC,OAAO,EAAE,IAAI,CAAC,CAAC;QAC5C,MAAM,IAAI,GAAG,WAAW,CAAC,OAAO,EAAE,EAAE,aAAa,EAAE,IAAI,EAAE,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,WAAW,EAAE,CAAC,CAAC;QAC1F,MAAM,IAAI,GAAG,IAAI,CAAC,MAAM,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,OAAO,EAAE,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC;QACvE,cAAc,CAAC,IAAI,CAAC,CAAC;QAErB,MAAM,CAAC,IAAI,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;QAC/C,SAAS,CAAC,IAAI,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;QACrC,MAAM,CAAC,IAAI,EAAE,IAAI,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;QAExC,OAAO,EAAE,GAAG,IAAI,EAAE,MAAM,EAAE,SAAS,EAAE,CAAC;IACxC,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,OAAO,EAAE,GAAG,IAAI,EAAE,MAAM,EAAE,UAAU,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,QAAQ,EAAE,KAAK,EAAG,GAAa,CAAC,OAAO,EAAE,CAAC;IACpG,CAAC;YAAS,CAAC;QACT,MAAM,CAAC,OAAO,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;IACpD,CAAC;AACH,CAAC;AAMD,MAAM,CAAC,KAAK,UAAU,aAAa,CACjC,OAA8D,EAAE;IAEhE,MAAM,OAAO,GAAG,IAAI,CAAC,GAAG,IAAI,QAAQ,CAAC,MAAM,CAAC,CAAC;IAC7C,SAAS,CAAC,OAAO,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IACxC,MAAM,OAAO,GAAqB,EAAE,CAAC;IACrC,KAAK,MAAM,IAAI,IAAI,MAAM,CAAC,MAAM,CAAC,UAAU,CAAC,EAAE,CAAC;QAC7C,OAAO,CAAC,IAAI,CAAC,MAAM,YAAY,CAAC,IAAI,EAAE,OAAO,EAAE,IAAI,CAAC,CAAC,CAAC;IACxD,CAAC;IACD,OAAO,OAAO,CAAC;AACjB,CAAC"}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { query } from './client.js';
|
|
2
|
+
const NAME_MAX = 128;
|
|
3
|
+
const FAILURE_EVENTS = ['password_fail', '2fa_fail', '2fa_expired'];
|
|
4
|
+
export function createAuthEventRepository() {
|
|
5
|
+
return {
|
|
6
|
+
async record(input) {
|
|
7
|
+
await query(`INSERT INTO auth_events (event, name, user_id, ip, trace_id, detail)
|
|
8
|
+
VALUES ($1, $2, $3, $4, $5, $6)`, [
|
|
9
|
+
input.event,
|
|
10
|
+
input.name ? input.name.slice(0, NAME_MAX) : null,
|
|
11
|
+
input.userId ?? null,
|
|
12
|
+
input.ip ?? null,
|
|
13
|
+
input.traceId ?? null,
|
|
14
|
+
input.detail ? JSON.stringify(input.detail) : null,
|
|
15
|
+
]);
|
|
16
|
+
},
|
|
17
|
+
async recentFailures({ userId, name, limit = 5 }) {
|
|
18
|
+
const res = await query(`SELECT at, event, host(ip) AS ip FROM auth_events
|
|
19
|
+
WHERE event = ANY($1)
|
|
20
|
+
AND ( ($2::bigint IS NOT NULL AND user_id = $2) OR ($3::text IS NOT NULL AND name = $3) )
|
|
21
|
+
ORDER BY at DESC LIMIT $4`, [FAILURE_EVENTS, userId ?? null, name ?? null, limit]);
|
|
22
|
+
return res.rows.map((r) => ({ at: r.at, event: r.event, ip: r.ip }));
|
|
23
|
+
},
|
|
24
|
+
async pruneOlderThan(days) {
|
|
25
|
+
const res = await query(`DELETE FROM auth_events WHERE at < NOW() - ($1 || ' days')::interval`, [days]);
|
|
26
|
+
return res.rowCount ?? 0;
|
|
27
|
+
},
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
//# sourceMappingURL=auth-event-repository.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"auth-event-repository.js","sourceRoot":"","sources":["../../../src/adapters/pg/auth-event-repository.ts"],"names":[],"mappings":"AAUA,OAAO,EAAE,KAAK,EAAE,MAAM,aAAa,CAAC;AAGpC,MAAM,QAAQ,GAAG,GAAG,CAAC;AAGrB,MAAM,cAAc,GAAG,CAAC,eAAe,EAAE,UAAU,EAAE,aAAa,CAAC,CAAC;AAEpE,MAAM,UAAU,yBAAyB;IACvC,OAAO;QACL,KAAK,CAAC,MAAM,CAAC,KAAqB;YAChC,MAAM,KAAK,CACT;yCACiC,EACjC;gBACE,KAAK,CAAC,KAAK;gBACX,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,QAAQ,CAAC,CAAC,CAAC,CAAC,IAAI;gBACjD,KAAK,CAAC,MAAM,IAAI,IAAI;gBACpB,KAAK,CAAC,EAAE,IAAI,IAAI;gBAChB,KAAK,CAAC,OAAO,IAAI,IAAI;gBACrB,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,IAAI;aACnD,CACF,CAAC;QACJ,CAAC;QAED,KAAK,CAAC,cAAc,CAAC,EAAE,MAAM,EAAE,IAAI,EAAE,KAAK,GAAG,CAAC,EAAE;YAE9C,MAAM,GAAG,GAAG,MAAM,KAAK,CACrB;;;mCAG2B,EAC3B,CAAC,cAAc,EAAE,MAAM,IAAI,IAAI,EAAE,IAAI,IAAI,IAAI,EAAE,KAAK,CAAC,CACtD,CAAC;YACF,OAAO,GAAG,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,EAAe,EAAE,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC,EAAE,EAAE,KAAK,EAAE,CAAC,CAAC,KAAK,EAAE,EAAE,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC;QACpF,CAAC;QAED,KAAK,CAAC,cAAc,CAAC,IAAI;YACvB,MAAM,GAAG,GAAG,MAAM,KAAK,CACrB,sEAAsE,EACtE,CAAC,IAAI,CAAC,CACP,CAAC;YACF,OAAO,GAAG,CAAC,QAAQ,IAAI,CAAC,CAAC;QAC3B,CAAC;KACF,CAAC;AACJ,CAAC"}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { readFileSync } from 'node:fs';
|
|
1
2
|
import { Pool } from 'pg';
|
|
2
3
|
function readDatabaseUrl() {
|
|
3
4
|
const url = process.env.NIO_DATABASE_URL?.trim();
|
|
@@ -10,11 +11,28 @@ function readDatabaseUrl() {
|
|
|
10
11
|
}
|
|
11
12
|
return url;
|
|
12
13
|
}
|
|
13
|
-
function
|
|
14
|
-
const
|
|
15
|
-
|
|
14
|
+
function envFlag(name) {
|
|
15
|
+
const v = process.env[name]?.trim().toLowerCase();
|
|
16
|
+
return v === 'true' || v === '1';
|
|
17
|
+
}
|
|
18
|
+
export function readSslOption() {
|
|
19
|
+
if (!envFlag('NIO_DATABASE_SSL'))
|
|
20
|
+
return undefined;
|
|
21
|
+
if (envFlag('NIO_DATABASE_SSL_INSECURE')) {
|
|
22
|
+
console.error('[pg] AVISO: NIO_DATABASE_SSL_INSECURE=1 — TLS sem verificação de certificado. ' +
|
|
23
|
+
'A conexão com o banco fica vulnerável a MITM. Use NIO_DATABASE_CA em vez disso.');
|
|
16
24
|
return { rejectUnauthorized: false };
|
|
17
|
-
|
|
25
|
+
}
|
|
26
|
+
const caPath = process.env.NIO_DATABASE_CA?.trim();
|
|
27
|
+
if (caPath) {
|
|
28
|
+
try {
|
|
29
|
+
return { rejectUnauthorized: true, ca: readFileSync(caPath, 'utf8') };
|
|
30
|
+
}
|
|
31
|
+
catch (err) {
|
|
32
|
+
throw new Error(`NIO_DATABASE_CA não pôde ser lido ("${caPath}"): ${err.message}`);
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
return { rejectUnauthorized: true };
|
|
18
36
|
}
|
|
19
37
|
let pool = null;
|
|
20
38
|
export function getPool() {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"client.js","sourceRoot":"","sources":["../../../src/adapters/pg/client.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"client.js","sourceRoot":"","sources":["../../../src/adapters/pg/client.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { query } from './client.js';
|
|
2
|
+
const map = (r) => ({
|
|
3
|
+
ip: r.ip,
|
|
4
|
+
firstSeen: r.first_seen,
|
|
5
|
+
lastSeen: r.last_seen,
|
|
6
|
+
count: Number(r.count),
|
|
7
|
+
});
|
|
8
|
+
export function createLoginIpRepository() {
|
|
9
|
+
return {
|
|
10
|
+
async record(userId, ip) {
|
|
11
|
+
await query(`INSERT INTO login_ip_events (user_id, ip) VALUES ($1, $2)
|
|
12
|
+
ON CONFLICT (user_id, ip)
|
|
13
|
+
DO UPDATE SET last_seen = NOW(), count = login_ip_events.count + 1`, [userId, ip]);
|
|
14
|
+
},
|
|
15
|
+
async recent(userId, limit = 10) {
|
|
16
|
+
const res = await query(`SELECT host(ip) AS ip, first_seen, last_seen, count FROM login_ip_events
|
|
17
|
+
WHERE user_id = $1 ORDER BY last_seen DESC LIMIT $2`, [userId, limit]);
|
|
18
|
+
return res.rows.map(map);
|
|
19
|
+
},
|
|
20
|
+
async pruneOlderThan(days) {
|
|
21
|
+
const res = await query(`DELETE FROM login_ip_events WHERE last_seen < NOW() - ($1 || ' days')::interval`, [days]);
|
|
22
|
+
return res.rowCount ?? 0;
|
|
23
|
+
},
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
//# sourceMappingURL=login-ip-repository.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"login-ip-repository.js","sourceRoot":"","sources":["../../../src/adapters/pg/login-ip-repository.ts"],"names":[],"mappings":"AAMA,OAAO,EAAE,KAAK,EAAE,MAAM,aAAa,CAAC;AASpC,MAAM,GAAG,GAAG,CAAC,CAAM,EAAgB,EAAE,CAAC,CAAC;IACrC,EAAE,EAAE,CAAC,CAAC,EAAE;IACR,SAAS,EAAE,CAAC,CAAC,UAAU;IACvB,QAAQ,EAAE,CAAC,CAAC,SAAS;IACrB,KAAK,EAAE,MAAM,CAAC,CAAC,CAAC,KAAK,CAAC;CACvB,CAAC,CAAC;AAEH,MAAM,UAAU,uBAAuB;IACrC,OAAO;QACL,KAAK,CAAC,MAAM,CAAC,MAAM,EAAE,EAAE;YACrB,MAAM,KAAK,CACT;;4EAEoE,EACpE,CAAC,MAAM,EAAE,EAAE,CAAC,CACb,CAAC;QACJ,CAAC;QAED,KAAK,CAAC,MAAM,CAAC,MAAM,EAAE,KAAK,GAAG,EAAE;YAC7B,MAAM,GAAG,GAAG,MAAM,KAAK,CACrB;6DACqD,EACrD,CAAC,MAAM,EAAE,KAAK,CAAC,CAChB,CAAC;YACF,OAAO,GAAG,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;QAC3B,CAAC;QAED,KAAK,CAAC,cAAc,CAAC,IAAI;YACvB,MAAM,GAAG,GAAG,MAAM,KAAK,CACrB,iFAAiF,EACjF,CAAC,IAAI,CAAC,CACP,CAAC;YACF,OAAO,GAAG,CAAC,QAAQ,IAAI,CAAC,CAAC;QAC3B,CAAC;KACF,CAAC;AACJ,CAAC"}
|
|
@@ -1,5 +1,10 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { randomBytes } from 'node:crypto';
|
|
2
|
+
import { hashPassword, needsRehash, verifyPassword, MIN_PASSWORD_LENGTH } from '../../lib/auth/password.js';
|
|
2
3
|
import { query } from './client.js';
|
|
4
|
+
let decoyHashPromise = null;
|
|
5
|
+
function decoyHash() {
|
|
6
|
+
return (decoyHashPromise ??= hashPassword(`decoy-${randomBytes(16).toString('hex')}`).then((h) => h.phc));
|
|
7
|
+
}
|
|
3
8
|
function parseIps(raw) {
|
|
4
9
|
if (!raw)
|
|
5
10
|
return [];
|
|
@@ -23,48 +28,72 @@ export function mapUserRow(row) {
|
|
|
23
28
|
timestampLastSession: row.timestamp_last_session,
|
|
24
29
|
};
|
|
25
30
|
}
|
|
26
|
-
const
|
|
27
|
-
'auth_2, phone,
|
|
31
|
+
const PUBLIC_COLS = 'id, name, password_pepper_id, timestamp_creation, timestamp_password_change, ' +
|
|
32
|
+
'auth_2, phone, backup_pepper_id, timestamp_last_session, ips_using';
|
|
33
|
+
const FULL_COLS = `${PUBLIC_COLS}, password, backup_codes`;
|
|
28
34
|
export function createUserRepository() {
|
|
29
35
|
return {
|
|
30
36
|
async findByName(name) {
|
|
31
|
-
const res = await query(`SELECT ${
|
|
37
|
+
const res = await query(`SELECT ${PUBLIC_COLS} FROM user_cli WHERE name = $1`, [name]);
|
|
32
38
|
const row = res.rows[0];
|
|
33
39
|
return row ? mapUserRow(row) : null;
|
|
34
40
|
},
|
|
35
41
|
async findById(id) {
|
|
36
|
-
const res = await query(`SELECT ${
|
|
42
|
+
const res = await query(`SELECT ${PUBLIC_COLS} FROM user_cli WHERE id = $1`, [id]);
|
|
37
43
|
const row = res.rows[0];
|
|
38
44
|
return row ? mapUserRow(row) : null;
|
|
39
45
|
},
|
|
40
46
|
async create(input) {
|
|
41
|
-
|
|
42
|
-
|
|
47
|
+
if (input.password.length < MIN_PASSWORD_LENGTH) {
|
|
48
|
+
throw new Error(`Senha muito curta (mínimo ${MIN_PASSWORD_LENGTH} caracteres).`);
|
|
49
|
+
}
|
|
50
|
+
const { phc, pepperId } = await hashPassword(input.password);
|
|
51
|
+
const res = await query(`INSERT INTO user_cli (name, password, password_pepper_id) VALUES ($1, $2, $3) RETURNING ${PUBLIC_COLS}`, [input.name, phc, pepperId]);
|
|
43
52
|
return mapUserRow(res.rows[0]);
|
|
44
53
|
},
|
|
45
54
|
async verifyCredentials(name, password) {
|
|
46
|
-
const res = await query(`SELECT ${
|
|
55
|
+
const res = await query(`SELECT ${FULL_COLS} FROM user_cli WHERE name = $1`, [name]);
|
|
47
56
|
const row = res.rows[0];
|
|
48
|
-
if (!row)
|
|
57
|
+
if (!row) {
|
|
58
|
+
await verifyPassword(await decoyHash(), password, 0);
|
|
59
|
+
return null;
|
|
60
|
+
}
|
|
61
|
+
const ok = await verifyPassword(row.password, password, row.password_pepper_id);
|
|
62
|
+
if (!ok)
|
|
49
63
|
return null;
|
|
50
|
-
|
|
51
|
-
|
|
64
|
+
if (needsRehash(row.password, row.password_pepper_id)) {
|
|
65
|
+
try {
|
|
66
|
+
const fresh = await hashPassword(password);
|
|
67
|
+
await query('UPDATE user_cli SET password = $2, password_pepper_id = $3, timestamp_password_change = NOW() WHERE id = $1', [Number(row.id), fresh.phc, fresh.pepperId]);
|
|
68
|
+
}
|
|
69
|
+
catch {
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
return mapUserRow(row);
|
|
52
73
|
},
|
|
53
74
|
async touchLastSession(userId) {
|
|
54
75
|
await query('UPDATE user_cli SET timestamp_last_session = NOW() WHERE id = $1', [userId]);
|
|
55
76
|
},
|
|
56
|
-
async
|
|
57
|
-
await query('UPDATE user_cli SET
|
|
77
|
+
async updatePasswordHash(userId, phc, pepperId) {
|
|
78
|
+
await query('UPDATE user_cli SET password = $2, password_pepper_id = $3, timestamp_password_change = NOW() WHERE id = $1', [userId, phc, pepperId]);
|
|
79
|
+
},
|
|
80
|
+
async enable2fa(userId, phone, backupCodeHashes, backupPepperId) {
|
|
81
|
+
await query('UPDATE user_cli SET auth_2 = TRUE, phone = $2, backup_codes = $3, backup_pepper_id = $4 WHERE id = $1', [userId, phone, backupCodeHashes, backupPepperId]);
|
|
58
82
|
},
|
|
59
83
|
async disable2fa(userId) {
|
|
60
|
-
await query('UPDATE user_cli SET auth_2 = FALSE, phone = NULL, backup_codes = NULL WHERE id = $1', [userId]);
|
|
84
|
+
await query('UPDATE user_cli SET auth_2 = FALSE, phone = NULL, backup_codes = NULL, backup_pepper_id = 0 WHERE id = $1', [userId]);
|
|
61
85
|
},
|
|
62
|
-
async updateBackupCodes(userId, joined) {
|
|
63
|
-
await query('UPDATE user_cli SET backup_codes = $2 WHERE id = $1', [
|
|
86
|
+
async updateBackupCodes(userId, joined, pepperId) {
|
|
87
|
+
await query('UPDATE user_cli SET backup_codes = $2, backup_pepper_id = $3 WHERE id = $1', [
|
|
88
|
+
userId,
|
|
89
|
+
joined,
|
|
90
|
+
pepperId,
|
|
91
|
+
]);
|
|
64
92
|
},
|
|
65
93
|
async getBackupCodes(userId) {
|
|
66
|
-
const res = await query('SELECT backup_codes FROM user_cli WHERE id = $1', [userId]);
|
|
67
|
-
|
|
94
|
+
const res = await query('SELECT backup_codes, backup_pepper_id FROM user_cli WHERE id = $1', [userId]);
|
|
95
|
+
const row = res.rows[0];
|
|
96
|
+
return { codes: row?.backup_codes ?? null, pepperId: row?.backup_pepper_id ?? 0 };
|
|
68
97
|
},
|
|
69
98
|
};
|
|
70
99
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"user-repository.js","sourceRoot":"","sources":["../../../src/adapters/pg/user-repository.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"user-repository.js","sourceRoot":"","sources":["../../../src/adapters/pg/user-repository.ts"],"names":[],"mappings":"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"}
|
|
@@ -21,6 +21,27 @@ function readEnv() {
|
|
|
21
21
|
from: process.env.SMS_FROM?.trim(),
|
|
22
22
|
};
|
|
23
23
|
}
|
|
24
|
+
const LOOPBACK_HOSTS = new Set(['localhost', '127.0.0.1', '::1', '[::1]', '0.0.0.0']);
|
|
25
|
+
export function smsMode(env = readEnv()) {
|
|
26
|
+
if (!env.url || !env.bodyTemplate)
|
|
27
|
+
return 'unconfigured';
|
|
28
|
+
try {
|
|
29
|
+
return LOOPBACK_HOSTS.has(new URL(env.url).hostname) ? 'echo' : 'provider';
|
|
30
|
+
}
|
|
31
|
+
catch {
|
|
32
|
+
return 'provider';
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
export function smsProviderHost(env = readEnv()) {
|
|
36
|
+
if (!env.url)
|
|
37
|
+
return null;
|
|
38
|
+
try {
|
|
39
|
+
return new URL(env.url).hostname;
|
|
40
|
+
}
|
|
41
|
+
catch {
|
|
42
|
+
return null;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
24
45
|
export function createHttpSmsSender(env = readEnv()) {
|
|
25
46
|
return {
|
|
26
47
|
async send(to, text) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"http-generic.js","sourceRoot":"","sources":["../../../src/adapters/sms/http-generic.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"http-generic.js","sourceRoot":"","sources":["../../../src/adapters/sms/http-generic.ts"],"names":[],"mappings":"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"}
|
package/dist/brand.js
CHANGED
|
@@ -15,7 +15,7 @@ export const brand = {
|
|
|
15
15
|
envPrefix: 'NIO',
|
|
16
16
|
patPrefix: 'nio_',
|
|
17
17
|
skillsRepo: 'hugoreiis12-png/NIO-SKILLS-',
|
|
18
|
-
skillsRef: '
|
|
18
|
+
skillsRef: '11980256cff5c261740e0abd0c549da9b1da3d95',
|
|
19
19
|
skillsPackage: '@nio-cli/skills',
|
|
20
20
|
toolPrefix: 'nos_',
|
|
21
21
|
cliToolPrefix: 'nio_',
|
package/dist/brand.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"brand.js","sourceRoot":"","sources":["../src/brand.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAClC,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAYjC,MAAM,CAAC,MAAM,KAAK,GAAG;IAGnB,IAAI,EAAE,KAAK;IAEX,WAAW,EAAE,KAAK;IAElB,eAAe,EAAE,KAAK;IAGtB,SAAS,EAAE,iBAAiB;IAE5B,UAAU,EAAE,UAAU;IAEtB,WAAW,EAAE,cAAc;IAE3B,UAAU,EAAE,SAAS;IAErB,YAAY,EAAE,KAAK;IAOnB,MAAM,EAAE,EAAE;IAIV,WAAW,EAAE,MAAM;IAEnB,iBAAiB,EAAE,UAAU;IAI7B,SAAS,EAAE,KAAK;IAMhB,SAAS,EAAE,MAAM;IAIjB,UAAU,EAAE,6BAA6B;
|
|
1
|
+
{"version":3,"file":"brand.js","sourceRoot":"","sources":["../src/brand.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAClC,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAYjC,MAAM,CAAC,MAAM,KAAK,GAAG;IAGnB,IAAI,EAAE,KAAK;IAEX,WAAW,EAAE,KAAK;IAElB,eAAe,EAAE,KAAK;IAGtB,SAAS,EAAE,iBAAiB;IAE5B,UAAU,EAAE,UAAU;IAEtB,WAAW,EAAE,cAAc;IAE3B,UAAU,EAAE,SAAS;IAErB,YAAY,EAAE,KAAK;IAOnB,MAAM,EAAE,EAAE;IAIV,WAAW,EAAE,MAAM;IAEnB,iBAAiB,EAAE,UAAU;IAI7B,SAAS,EAAE,KAAK;IAMhB,SAAS,EAAE,MAAM;IAIjB,UAAU,EAAE,6BAA6B;IAWzC,SAAS,EAAE,0CAA0C;IAErD,aAAa,EAAE,iBAAiB;IAIhC,UAAU,EAAE,MAAM;IAElB,aAAa,EAAE,MAAM;CACb,CAAC;AASX,MAAM,CAAC,MAAM,QAAQ,GAAG,IAAI,MAAM,CAAC,IAAI,KAAK,CAAC,SAAS,eAAe,CAAC,CAAC;AAGvE,MAAM,UAAU,QAAQ,CAAC,GAAG,KAAe;IACzC,OAAO,IAAI,CAAC,OAAO,EAAE,EAAE,KAAK,CAAC,WAAW,EAAE,GAAG,KAAK,CAAC,CAAC;AACtD,CAAC;AAGD,MAAM,UAAU,OAAO,CAAC,IAAY;IAClC,OAAO,GAAG,KAAK,CAAC,SAAS,IAAI,IAAI,EAAE,CAAC;AACtC,CAAC;AAGD,MAAM,UAAU,GAAG,CAAC,IAAY;IAC9B,OAAO,OAAO,CAAC,GAAG,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC;AACpC,CAAC"}
|
|
@@ -3,16 +3,22 @@ import { brand } from "../../brand.js";
|
|
|
3
3
|
import { animateMatrixLogo } from "../../matrix-logo.js";
|
|
4
4
|
import { startSpinner } from "../../lib/spinner.js";
|
|
5
5
|
import { c, sym } from "../../lib/colors.js";
|
|
6
|
-
import {
|
|
7
|
-
import { gatewayLogin, gatewayLogout, gatewayVerify2fa, } from "../../lib/auth/gateway-client.js";
|
|
6
|
+
import { MIN_PASSWORD_LENGTH } from "../../lib/auth/password.js";
|
|
7
|
+
import { gatewayLogin, gatewayLogout, gatewayLogoutAll, gatewayRegister, gatewayVerify2fa, } from "../../lib/auth/gateway-client.js";
|
|
8
8
|
import { loadSession, saveSession, clearSession } from "../../lib/auth/session-store.js";
|
|
9
9
|
import { ensureConfig } from "../../lib/auth/nio-config.js";
|
|
10
10
|
import { ensureGatewayRunning } from "../../lib/auth/gateway-process.js";
|
|
11
11
|
import { continueChain } from "../flows/onboarding.js";
|
|
12
12
|
import { box, cmd } from "../../lib/colors.js";
|
|
13
13
|
import { authCopy } from "../copy.js";
|
|
14
|
-
async function resolveSecondFactor(challengeId, phoneHint) {
|
|
15
|
-
|
|
14
|
+
async function resolveSecondFactor(challengeId, phoneHint, sms) {
|
|
15
|
+
if (sms?.smsMode === "echo") {
|
|
16
|
+
console.log(` ${c.yellow(sym.warn)} modo echo (dev) — nenhum SMS real foi enviado` +
|
|
17
|
+
(sms.devCode ? `. Código: ${c.bold(sms.devCode)}` : " (veja ~/.nio/sms-echo-last.json)"));
|
|
18
|
+
}
|
|
19
|
+
else {
|
|
20
|
+
console.log(` ${c.dim(`código enviado por SMS para ${phoneHint}`)}`);
|
|
21
|
+
}
|
|
16
22
|
let type = "otp";
|
|
17
23
|
for (let attempt = 0; attempt < 4; attempt++) {
|
|
18
24
|
const code = (await input({
|
|
@@ -32,7 +38,6 @@ async function resolveSecondFactor(challengeId, phoneHint) {
|
|
|
32
38
|
}
|
|
33
39
|
return null;
|
|
34
40
|
}
|
|
35
|
-
const MIN_PASSWORD_LENGTH = 8;
|
|
36
41
|
async function requireGateway() {
|
|
37
42
|
const result = await ensureGatewayRunning();
|
|
38
43
|
if (result.ok)
|
|
@@ -60,7 +65,10 @@ export async function runLogin() {
|
|
|
60
65
|
session = result;
|
|
61
66
|
}
|
|
62
67
|
else {
|
|
63
|
-
const s = await resolveSecondFactor(result.challengeId, result.phoneHint
|
|
68
|
+
const s = await resolveSecondFactor(result.challengeId, result.phoneHint, {
|
|
69
|
+
smsMode: result.smsMode,
|
|
70
|
+
devCode: result.devCode,
|
|
71
|
+
});
|
|
64
72
|
if (!s) {
|
|
65
73
|
console.error(`${c.red(sym.err)} 2º fator não concluído.`);
|
|
66
74
|
process.exit(1);
|
|
@@ -87,6 +95,7 @@ export async function runLogin() {
|
|
|
87
95
|
}
|
|
88
96
|
export async function runRegister() {
|
|
89
97
|
await ensureConfig({ interactive: true });
|
|
98
|
+
await requireGateway();
|
|
90
99
|
const name = await input({
|
|
91
100
|
message: authCopy.register.namePrompt,
|
|
92
101
|
validate: (v) => v.trim().length > 0 || authCopy.register.nameInvalid,
|
|
@@ -96,17 +105,11 @@ export async function runRegister() {
|
|
|
96
105
|
mask: "*",
|
|
97
106
|
validate: (v) => v.length >= MIN_PASSWORD_LENGTH || authCopy.register.passwordInvalid,
|
|
98
107
|
});
|
|
99
|
-
const repo = createUserRepository();
|
|
100
108
|
const spinner = startSpinner("Criando usuário...");
|
|
101
109
|
try {
|
|
102
|
-
const
|
|
103
|
-
if (existing) {
|
|
104
|
-
spinner.fail(`Usuário "${name.trim()}" já existe.`);
|
|
105
|
-
process.exit(1);
|
|
106
|
-
}
|
|
107
|
-
const user = await repo.create({ name: name.trim(), password: pass });
|
|
110
|
+
const user = await gatewayRegister(name.trim(), pass);
|
|
108
111
|
spinner.stop();
|
|
109
|
-
console.log(`${c.green(sym.ok)} Usuário criado: ${user.name} (id ${user.
|
|
112
|
+
console.log(`${c.green(sym.ok)} Usuário criado: ${user.name} (id ${user.userId})`);
|
|
110
113
|
}
|
|
111
114
|
catch (err) {
|
|
112
115
|
spinner.fail(`Falha ao criar usuário: ${err.message}`);
|
|
@@ -118,7 +121,7 @@ export async function runRegister() {
|
|
|
118
121
|
function registerRegisterCommand(program) {
|
|
119
122
|
program
|
|
120
123
|
.command("register")
|
|
121
|
-
.description("Cria um novo usuário
|
|
124
|
+
.description("Cria um novo usuário via nio-gateway e já entra (login)")
|
|
122
125
|
.action(async () => {
|
|
123
126
|
await runRegister();
|
|
124
127
|
await continueChain({ from: "command" });
|
|
@@ -137,17 +140,21 @@ function registerLogoutCommand(program) {
|
|
|
137
140
|
program
|
|
138
141
|
.command("logout")
|
|
139
142
|
.description("Encerra a sessão local e revoga a auth_session no banco")
|
|
140
|
-
.
|
|
143
|
+
.option("--all", "Revoga TODAS as sessões do usuário (todos os dispositivos)")
|
|
144
|
+
.action(async (opts) => {
|
|
141
145
|
const session = await loadSession();
|
|
142
146
|
if (session) {
|
|
143
147
|
try {
|
|
144
|
-
|
|
148
|
+
if (opts.all)
|
|
149
|
+
await gatewayLogoutAll(session.token);
|
|
150
|
+
else
|
|
151
|
+
await gatewayLogout(session.sessionId, session.token);
|
|
145
152
|
}
|
|
146
153
|
catch {
|
|
147
154
|
}
|
|
148
155
|
}
|
|
149
156
|
await clearSession();
|
|
150
|
-
console.log("Sessão removida.");
|
|
157
|
+
console.log(opts.all ? "Todas as sessões foram encerradas." : "Sessão removida.");
|
|
151
158
|
});
|
|
152
159
|
}
|
|
153
160
|
function registerWhoamiCommand(program) {
|