@synoikia/core 0.1.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/LICENSE +21 -0
- package/dist/app.d.ts +79 -0
- package/dist/app.js +155 -0
- package/dist/app.js.map +1 -0
- package/dist/approvals/links.d.ts +23 -0
- package/dist/approvals/links.js +49 -0
- package/dist/approvals/links.js.map +1 -0
- package/dist/approvals/service.d.ts +110 -0
- package/dist/approvals/service.js +182 -0
- package/dist/approvals/service.js.map +1 -0
- package/dist/audit-query.d.ts +52 -0
- package/dist/audit-query.js +116 -0
- package/dist/audit-query.js.map +1 -0
- package/dist/audit.d.ts +5 -0
- package/dist/audit.js +8 -0
- package/dist/audit.js.map +1 -0
- package/dist/auth/mcp-auth.d.ts +50 -0
- package/dist/auth/mcp-auth.js +160 -0
- package/dist/auth/mcp-auth.js.map +1 -0
- package/dist/auth/mcp-tokens.d.ts +57 -0
- package/dist/auth/mcp-tokens.js +143 -0
- package/dist/auth/mcp-tokens.js.map +1 -0
- package/dist/auth/oauth.d.ts +188 -0
- package/dist/auth/oauth.js +470 -0
- package/dist/auth/oauth.js.map +1 -0
- package/dist/auth/oidc.d.ts +110 -0
- package/dist/auth/oidc.js +221 -0
- package/dist/auth/oidc.js.map +1 -0
- package/dist/auth/sessions.d.ts +34 -0
- package/dist/auth/sessions.js +83 -0
- package/dist/auth/sessions.js.map +1 -0
- package/dist/auth/throttle.d.ts +31 -0
- package/dist/auth/throttle.js +52 -0
- package/dist/auth/throttle.js.map +1 -0
- package/dist/auth/tokens.d.ts +7 -0
- package/dist/auth/tokens.js +40 -0
- package/dist/auth/tokens.js.map +1 -0
- package/dist/auth/totp.d.ts +14 -0
- package/dist/auth/totp.js +81 -0
- package/dist/auth/totp.js.map +1 -0
- package/dist/auth/users.d.ts +70 -0
- package/dist/auth/users.js +296 -0
- package/dist/auth/users.js.map +1 -0
- package/dist/catalog/groups.d.ts +102 -0
- package/dist/catalog/groups.js +363 -0
- package/dist/catalog/groups.js.map +1 -0
- package/dist/catalog/registry.d.ts +25 -0
- package/dist/catalog/registry.js +80 -0
- package/dist/catalog/registry.js.map +1 -0
- package/dist/catalog/rules.d.ts +128 -0
- package/dist/catalog/rules.js +178 -0
- package/dist/catalog/rules.js.map +1 -0
- package/dist/catalog/sync.d.ts +29 -0
- package/dist/catalog/sync.js +222 -0
- package/dist/catalog/sync.js.map +1 -0
- package/dist/cli.d.ts +3 -0
- package/dist/cli.js +83 -0
- package/dist/cli.js.map +1 -0
- package/dist/config/env.d.ts +40 -0
- package/dist/config/env.js +71 -0
- package/dist/config/env.js.map +1 -0
- package/dist/crypto/index.d.ts +28 -0
- package/dist/crypto/index.js +88 -0
- package/dist/crypto/index.js.map +1 -0
- package/dist/crypto/rotate.d.ts +15 -0
- package/dist/crypto/rotate.js +61 -0
- package/dist/crypto/rotate.js.map +1 -0
- package/dist/db/index.d.ts +17 -0
- package/dist/db/index.js +29 -0
- package/dist/db/index.js.map +1 -0
- package/dist/db/schema.d.ts +4389 -0
- package/dist/db/schema.js +372 -0
- package/dist/db/schema.js.map +1 -0
- package/dist/endpoints/slug.d.ts +3 -0
- package/dist/endpoints/slug.js +7 -0
- package/dist/endpoints/slug.js.map +1 -0
- package/dist/errors.d.ts +16 -0
- package/dist/errors.js +29 -0
- package/dist/errors.js.map +1 -0
- package/dist/events.d.ts +43 -0
- package/dist/events.js +16 -0
- package/dist/events.js.map +1 -0
- package/dist/gate/access.d.ts +47 -0
- package/dist/gate/access.js +48 -0
- package/dist/gate/access.js.map +1 -0
- package/dist/gate/attestation.d.ts +23 -0
- package/dist/gate/attestation.js +33 -0
- package/dist/gate/attestation.js.map +1 -0
- package/dist/gate/canonical.d.ts +3 -0
- package/dist/gate/canonical.js +11 -0
- package/dist/gate/canonical.js.map +1 -0
- package/dist/gate/match.d.ts +67 -0
- package/dist/gate/match.js +133 -0
- package/dist/gate/match.js.map +1 -0
- package/dist/gate/pipeline.d.ts +61 -0
- package/dist/gate/pipeline.js +284 -0
- package/dist/gate/pipeline.js.map +1 -0
- package/dist/gate/preapproval.d.ts +24 -0
- package/dist/gate/preapproval.js +48 -0
- package/dist/gate/preapproval.js.map +1 -0
- package/dist/gate/rate-limit.d.ts +35 -0
- package/dist/gate/rate-limit.js +82 -0
- package/dist/gate/rate-limit.js.map +1 -0
- package/dist/gate/redact.d.ts +23 -0
- package/dist/gate/redact.js +87 -0
- package/dist/gate/redact.js.map +1 -0
- package/dist/http/admin/auth.d.ts +33 -0
- package/dist/http/admin/auth.js +338 -0
- package/dist/http/admin/auth.js.map +1 -0
- package/dist/http/admin/instances.d.ts +5 -0
- package/dist/http/admin/instances.js +193 -0
- package/dist/http/admin/instances.js.map +1 -0
- package/dist/http/admin/system.d.ts +5 -0
- package/dist/http/admin/system.js +225 -0
- package/dist/http/admin/system.js.map +1 -0
- package/dist/http/admin-app.d.ts +9 -0
- package/dist/http/admin-app.js +51 -0
- package/dist/http/admin-app.js.map +1 -0
- package/dist/http/common.d.ts +32 -0
- package/dist/http/common.js +80 -0
- package/dist/http/common.js.map +1 -0
- package/dist/http/mcp/approval-routes.d.ts +11 -0
- package/dist/http/mcp/approval-routes.js +117 -0
- package/dist/http/mcp/approval-routes.js.map +1 -0
- package/dist/http/mcp/endpoint.d.ts +32 -0
- package/dist/http/mcp/endpoint.js +293 -0
- package/dist/http/mcp/endpoint.js.map +1 -0
- package/dist/http/mcp/oauth-routes.d.ts +29 -0
- package/dist/http/mcp/oauth-routes.js +465 -0
- package/dist/http/mcp/oauth-routes.js.map +1 -0
- package/dist/http/mcp/pages.d.ts +63 -0
- package/dist/http/mcp/pages.js +200 -0
- package/dist/http/mcp/pages.js.map +1 -0
- package/dist/http/mcp-app.d.ts +7 -0
- package/dist/http/mcp-app.js +30 -0
- package/dist/http/mcp-app.js.map +1 -0
- package/dist/instances/connection.d.ts +37 -0
- package/dist/instances/connection.js +109 -0
- package/dist/instances/connection.js.map +1 -0
- package/dist/instances/manager.d.ts +313 -0
- package/dist/instances/manager.js +600 -0
- package/dist/instances/manager.js.map +1 -0
- package/dist/instances/settings.d.ts +42 -0
- package/dist/instances/settings.js +57 -0
- package/dist/instances/settings.js.map +1 -0
- package/dist/lenient.d.ts +16 -0
- package/dist/lenient.js +51 -0
- package/dist/lenient.js.map +1 -0
- package/dist/lock.d.ts +28 -0
- package/dist/lock.js +77 -0
- package/dist/lock.js.map +1 -0
- package/dist/main.d.ts +1 -0
- package/dist/main.js +38 -0
- package/dist/main.js.map +1 -0
- package/dist/maintenance.d.ts +10 -0
- package/dist/maintenance.js +48 -0
- package/dist/maintenance.js.map +1 -0
- package/dist/notify/service.d.ts +149 -0
- package/dist/notify/service.js +358 -0
- package/dist/notify/service.js.map +1 -0
- package/dist/plugins/discovery.d.ts +39 -0
- package/dist/plugins/discovery.js +157 -0
- package/dist/plugins/discovery.js.map +1 -0
- package/dist/plugins/minisign.d.ts +25 -0
- package/dist/plugins/minisign.js +49 -0
- package/dist/plugins/minisign.js.map +1 -0
- package/dist/plugins/process.d.ts +55 -0
- package/dist/plugins/process.js +162 -0
- package/dist/plugins/process.js.map +1 -0
- package/dist/plugins/repos.d.ts +198 -0
- package/dist/plugins/repos.js +612 -0
- package/dist/plugins/repos.js.map +1 -0
- package/dist/plugins/supervisor.d.ts +50 -0
- package/dist/plugins/supervisor.js +125 -0
- package/dist/plugins/supervisor.js.map +1 -0
- package/dist/runtime/index.d.ts +28 -0
- package/dist/runtime/index.js +248 -0
- package/dist/runtime/index.js.map +1 -0
- package/dist/sandbox/index.d.ts +53 -0
- package/dist/sandbox/index.js +197 -0
- package/dist/sandbox/index.js.map +1 -0
- package/dist/server.d.ts +16 -0
- package/dist/server.js +36 -0
- package/dist/server.js.map +1 -0
- package/dist/settings.d.ts +43 -0
- package/dist/settings.js +99 -0
- package/dist/settings.js.map +1 -0
- package/dist/testing/index.d.ts +73 -0
- package/dist/testing/index.js +174 -0
- package/dist/testing/index.js.map +1 -0
- package/dist/version.d.ts +2 -0
- package/dist/version.js +4 -0
- package/dist/version.js.map +1 -0
- package/drizzle/0000_init.sql +334 -0
- package/drizzle/0001_totp_last_step.sql +1 -0
- package/drizzle/0002_access_levels.sql +16 -0
- package/drizzle/0003_drop_exclusion_flags.sql +2 -0
- package/drizzle/0004_grant_instance_ids.sql +1 -0
- package/drizzle/0005_rule_strict_miss.sql +1 -0
- package/drizzle/0006_attestation_waived.sql +1 -0
- package/drizzle/meta/0000_snapshot.json +2318 -0
- package/drizzle/meta/0001_snapshot.json +2325 -0
- package/drizzle/meta/0002_snapshot.json +2348 -0
- package/drizzle/meta/0003_snapshot.json +2332 -0
- package/drizzle/meta/0004_snapshot.json +2339 -0
- package/drizzle/meta/0005_snapshot.json +2346 -0
- package/drizzle/meta/0006_snapshot.json +2354 -0
- package/drizzle/meta/_journal.json +55 -0
- package/package.json +58 -0
|
@@ -0,0 +1,338 @@
|
|
|
1
|
+
import { deleteCookie, getCookie, setCookie } from 'hono/cookie';
|
|
2
|
+
import { z } from 'zod';
|
|
3
|
+
import { writeAudit } from '../../audit.js';
|
|
4
|
+
import { randomToken, safeEqual, signPayload, verifyPayload } from '../../auth/tokens.js';
|
|
5
|
+
import { toPublicUser } from '../../auth/users.js';
|
|
6
|
+
import { ConflictError } from '../../errors.js';
|
|
7
|
+
import { getSettings } from '../../settings.js';
|
|
8
|
+
import { clientIp, isSecure, readJson, requestOrigin } from '../common.js';
|
|
9
|
+
const SESSION_COOKIE = 'syn_admin';
|
|
10
|
+
const SECURE_SESSION_COOKIE = '__Host-syn_admin';
|
|
11
|
+
export const CSRF_COOKIE = 'syn_csrf';
|
|
12
|
+
export const CSRF_HEADER = 'x-csrf-token';
|
|
13
|
+
const MFA_COOKIE = 'syn_mfa';
|
|
14
|
+
const OIDC_COOKIE = 'syn_oidc';
|
|
15
|
+
const MFA_TTL_MS = 5 * 60_000;
|
|
16
|
+
export function sessionLimits(ctx) {
|
|
17
|
+
const s = getSettings(ctx.db, 'security');
|
|
18
|
+
return { idleMs: s.sessionIdleMinutes * 60_000, absoluteMs: s.sessionAbsoluteHours * 3600_000 };
|
|
19
|
+
}
|
|
20
|
+
const readSessionCookie = (c) => getCookie(c, SECURE_SESSION_COOKIE) ?? getCookie(c, SESSION_COOKIE);
|
|
21
|
+
function setSessionCookie(c, ctx, raw) {
|
|
22
|
+
const secure = isSecure(c, ctx.config.TRUST_PROXY);
|
|
23
|
+
const limits = sessionLimits(ctx);
|
|
24
|
+
// `__Host-` requires Secure; plain-HTTP LAN setups fall back to the unprefixed name.
|
|
25
|
+
setCookie(c, secure ? SECURE_SESSION_COOKIE : SESSION_COOKIE, raw, {
|
|
26
|
+
httpOnly: true,
|
|
27
|
+
secure,
|
|
28
|
+
sameSite: 'Strict',
|
|
29
|
+
path: '/',
|
|
30
|
+
maxAge: Math.floor(limits.absoluteMs / 1000),
|
|
31
|
+
});
|
|
32
|
+
setCookie(c, CSRF_COOKIE, randomToken(24), { httpOnly: false, secure, sameSite: 'Strict', path: '/' });
|
|
33
|
+
}
|
|
34
|
+
function clearSessionCookies(c) {
|
|
35
|
+
deleteCookie(c, SESSION_COOKIE, { path: '/' });
|
|
36
|
+
deleteCookie(c, SECURE_SESSION_COOKIE, { path: '/', secure: true });
|
|
37
|
+
}
|
|
38
|
+
export function localLoginEnabled(ctx) {
|
|
39
|
+
return ctx.config.ADMIN_FORCE_LOCAL_LOGIN || !getSettings(ctx.db, 'security').disableLocalLogin;
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* While local login is off (setting, not the env override), single sign-on is the only way in: refuse
|
|
43
|
+
* any change that would leave it off or with no enabled user linked to it (design §6.1).
|
|
44
|
+
*/
|
|
45
|
+
export function assertSsoRemains(ctx, change) {
|
|
46
|
+
if (!getSettings(ctx.db, 'security').disableLocalLogin)
|
|
47
|
+
return;
|
|
48
|
+
if (change.oidcOff || !ctx.users.hasOidcLinkedUser(change.losingUser)) {
|
|
49
|
+
throw new ConflictError('local_login_disabled', 'Local login is disabled, so this would lock everyone out. Re-enable local login first.');
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
/** Local-password users must have TOTP when the admin requires it (OIDC-only users rely on the IdP). */
|
|
53
|
+
export function mustEnrollTotp(ctx, user) {
|
|
54
|
+
return getSettings(ctx.db, 'security').requireTotp && !!user.passwordHash && !user.totpEnabled;
|
|
55
|
+
}
|
|
56
|
+
const UNSAFE = new Set(['POST', 'PUT', 'PATCH', 'DELETE']);
|
|
57
|
+
/** CSRF: double-submit token + Origin check for every state-changing request under /api and /auth. */
|
|
58
|
+
export function csrfGuard(ctx) {
|
|
59
|
+
return async (c, next) => {
|
|
60
|
+
if (!UNSAFE.has(c.req.method))
|
|
61
|
+
return next();
|
|
62
|
+
const origin = c.req.header('origin');
|
|
63
|
+
if (origin) {
|
|
64
|
+
const expected = ctx.config.PUBLIC_ADMIN_URL
|
|
65
|
+
? new URL(ctx.config.PUBLIC_ADMIN_URL).origin
|
|
66
|
+
: requestOrigin(c, ctx.config.TRUST_PROXY);
|
|
67
|
+
if (origin !== expected)
|
|
68
|
+
return c.json({ error: 'bad_origin', message: 'Cross-origin request refused' }, 403);
|
|
69
|
+
}
|
|
70
|
+
const cookie = getCookie(c, CSRF_COOKIE);
|
|
71
|
+
const header = c.req.header(CSRF_HEADER);
|
|
72
|
+
if (!cookie || !header || !safeEqual(cookie, header)) {
|
|
73
|
+
return c.json({ error: 'csrf', message: 'Missing or invalid CSRF token' }, 403);
|
|
74
|
+
}
|
|
75
|
+
return next();
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
const TOTP_ENROLL_PATHS = ['/api/session', '/api/profile', '/api/profile/totp/begin', '/api/profile/totp/confirm'];
|
|
79
|
+
export function requireUser(ctx) {
|
|
80
|
+
return async (c, next) => {
|
|
81
|
+
const raw = readSessionCookie(c);
|
|
82
|
+
const session = ctx.sessions.validate(raw, 'admin', sessionLimits(ctx));
|
|
83
|
+
if (!session || !raw)
|
|
84
|
+
return c.json({ error: 'unauthenticated', message: 'Sign in first' }, 401);
|
|
85
|
+
if (mustEnrollTotp(ctx, session.user) && !TOTP_ENROLL_PATHS.includes(c.req.path)) {
|
|
86
|
+
return c.json({ error: 'totp_enrollment_required', message: 'Set up two-factor authentication first' }, 403);
|
|
87
|
+
}
|
|
88
|
+
c.set('user', session.user);
|
|
89
|
+
c.set('sessionRaw', raw);
|
|
90
|
+
return next();
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
function startSession(c, ctx, user, method) {
|
|
94
|
+
const raw = ctx.sessions.create(user.id, 'admin', sessionLimits(ctx), {
|
|
95
|
+
ip: clientIp(c, ctx.config.TRUST_PROXY),
|
|
96
|
+
userAgent: c.req.header('user-agent'),
|
|
97
|
+
});
|
|
98
|
+
setSessionCookie(c, ctx, raw);
|
|
99
|
+
ctx.users.markLogin(user.id);
|
|
100
|
+
writeAudit(ctx.db, {
|
|
101
|
+
kind: 'auth',
|
|
102
|
+
decision: 'login',
|
|
103
|
+
actorKind: 'user',
|
|
104
|
+
actorId: user.id,
|
|
105
|
+
detail: { username: user.username, method, ip: clientIp(c, ctx.config.TRUST_PROXY) },
|
|
106
|
+
});
|
|
107
|
+
}
|
|
108
|
+
function adminCallbackUrl(c, ctx) {
|
|
109
|
+
const base = ctx.config.PUBLIC_ADMIN_URL ?? requestOrigin(c, ctx.config.TRUST_PROXY);
|
|
110
|
+
return new URL('/auth/oidc/callback', base).toString();
|
|
111
|
+
}
|
|
112
|
+
const safeReturnTo = (v) => (v && v.startsWith('/') && !v.startsWith('//') ? v : '/');
|
|
113
|
+
export function registerAuthRoutes(app, ctx) {
|
|
114
|
+
const LoginBody = z.object({ username: z.string().min(1).max(64), password: z.string().min(1).max(1024) });
|
|
115
|
+
// Public session probe: also hands out the CSRF cookie the SPA echoes back.
|
|
116
|
+
app.get('/api/session', (c) => {
|
|
117
|
+
if (!getCookie(c, CSRF_COOKIE)) {
|
|
118
|
+
setCookie(c, CSRF_COOKIE, randomToken(24), {
|
|
119
|
+
httpOnly: false,
|
|
120
|
+
secure: isSecure(c, ctx.config.TRUST_PROXY),
|
|
121
|
+
sameSite: 'Strict',
|
|
122
|
+
path: '/',
|
|
123
|
+
});
|
|
124
|
+
}
|
|
125
|
+
const oidc = ctx.oidc.getSettings();
|
|
126
|
+
const base = {
|
|
127
|
+
setupRequired: ctx.users.count() === 0,
|
|
128
|
+
localLoginEnabled: localLoginEnabled(ctx),
|
|
129
|
+
oidc: { enabled: !!oidc?.enabled, label: oidc?.label ?? 'SSO' },
|
|
130
|
+
};
|
|
131
|
+
const session = ctx.sessions.validate(readSessionCookie(c), 'admin', sessionLimits(ctx));
|
|
132
|
+
if (!session)
|
|
133
|
+
return c.json({ authenticated: false, ...base });
|
|
134
|
+
return c.json({
|
|
135
|
+
authenticated: true,
|
|
136
|
+
...base,
|
|
137
|
+
user: toPublicUser(session.user),
|
|
138
|
+
mustEnrollTotp: mustEnrollTotp(ctx, session.user),
|
|
139
|
+
});
|
|
140
|
+
});
|
|
141
|
+
app.post('/api/setup', async (c) => {
|
|
142
|
+
const body = await readJson(c, LoginBody);
|
|
143
|
+
const created = await ctx.users.setupFirstUser(body.username, body.password);
|
|
144
|
+
startSession(c, ctx, ctx.users.get(created.id), 'setup');
|
|
145
|
+
return c.json({ status: 'ok', user: created });
|
|
146
|
+
});
|
|
147
|
+
app.post('/auth/login', async (c) => {
|
|
148
|
+
const ip = clientIp(c, ctx.config.TRUST_PROXY);
|
|
149
|
+
if (!ctx.throttle.allowIp(ip))
|
|
150
|
+
return c.json({ error: 'rate_limited', message: 'Too many attempts; try again later' }, 429);
|
|
151
|
+
if (!localLoginEnabled(ctx))
|
|
152
|
+
return c.json({ error: 'local_login_disabled', message: 'Use single sign-on' }, 403);
|
|
153
|
+
const body = await readJson(c, LoginBody);
|
|
154
|
+
const lockedFor = ctx.throttle.lockedFor(body.username);
|
|
155
|
+
if (lockedFor > 0) {
|
|
156
|
+
return c.json({ error: 'locked', message: `Too many failed attempts; try again in ${Math.ceil(lockedFor / 60)} min` }, 429);
|
|
157
|
+
}
|
|
158
|
+
const user = await ctx.users.verifyPassword(body.username, body.password);
|
|
159
|
+
if (!user) {
|
|
160
|
+
const locked = ctx.throttle.fail(body.username);
|
|
161
|
+
writeAudit(ctx.db, {
|
|
162
|
+
kind: 'auth',
|
|
163
|
+
decision: locked ? 'login_locked' : 'login_failed',
|
|
164
|
+
actorKind: 'system',
|
|
165
|
+
detail: { username: body.username, ip },
|
|
166
|
+
});
|
|
167
|
+
if (locked)
|
|
168
|
+
ctx.events.emit('auth.lockout', { username: body.username, ip });
|
|
169
|
+
return c.json({ error: 'invalid_credentials', message: 'Invalid username or password' }, 401);
|
|
170
|
+
}
|
|
171
|
+
if (user.totpEnabled) {
|
|
172
|
+
setCookie(c, MFA_COOKIE, signPayload(ctx.keys.state, { mfaUser: user.id }, MFA_TTL_MS), {
|
|
173
|
+
httpOnly: true,
|
|
174
|
+
secure: isSecure(c, ctx.config.TRUST_PROXY),
|
|
175
|
+
sameSite: 'Strict',
|
|
176
|
+
path: '/auth',
|
|
177
|
+
maxAge: MFA_TTL_MS / 1000,
|
|
178
|
+
});
|
|
179
|
+
return c.json({ status: 'totp_required' });
|
|
180
|
+
}
|
|
181
|
+
ctx.throttle.succeed(body.username);
|
|
182
|
+
startSession(c, ctx, user, 'password');
|
|
183
|
+
return c.json({ status: 'ok', mustEnrollTotp: mustEnrollTotp(ctx, user) });
|
|
184
|
+
});
|
|
185
|
+
app.post('/auth/totp', async (c) => {
|
|
186
|
+
const { code } = await readJson(c, z.object({ code: z.string().min(1).max(32) }));
|
|
187
|
+
const pending = verifyPayload(ctx.keys.state, getCookie(c, MFA_COOKIE));
|
|
188
|
+
if (!pending)
|
|
189
|
+
return c.json({ error: 'mfa_expired', message: 'Sign in again' }, 401);
|
|
190
|
+
const user = ctx.users.get(pending.mfaUser);
|
|
191
|
+
if (ctx.throttle.lockedFor(user.username) > 0)
|
|
192
|
+
return c.json({ error: 'locked', message: 'Too many failed attempts' }, 429);
|
|
193
|
+
const factor = ctx.users.verifySecondFactor(user.id, code);
|
|
194
|
+
if (!factor) {
|
|
195
|
+
if (ctx.throttle.fail(user.username))
|
|
196
|
+
ctx.events.emit('auth.lockout', { username: user.username });
|
|
197
|
+
writeAudit(ctx.db, {
|
|
198
|
+
kind: 'auth',
|
|
199
|
+
decision: 'totp_failed',
|
|
200
|
+
actorKind: 'system',
|
|
201
|
+
detail: { username: user.username },
|
|
202
|
+
});
|
|
203
|
+
return c.json({ error: 'invalid_code', message: 'Invalid code' }, 401);
|
|
204
|
+
}
|
|
205
|
+
deleteCookie(c, MFA_COOKIE, { path: '/auth' });
|
|
206
|
+
ctx.throttle.succeed(user.username);
|
|
207
|
+
startSession(c, ctx, user, factor === 'recovery' ? 'password+recovery_code' : 'password+totp');
|
|
208
|
+
return c.json({ status: 'ok' });
|
|
209
|
+
});
|
|
210
|
+
app.post('/auth/logout', (c) => {
|
|
211
|
+
ctx.sessions.revoke(readSessionCookie(c));
|
|
212
|
+
clearSessionCookies(c);
|
|
213
|
+
return c.json({ status: 'ok' });
|
|
214
|
+
});
|
|
215
|
+
// OIDC: sign in, or link the signed-in user's account (Profile → Link).
|
|
216
|
+
app.get('/auth/oidc/start', async (c) => {
|
|
217
|
+
const { url, stateToken } = await ctx.oidc.begin({
|
|
218
|
+
purpose: 'admin_login',
|
|
219
|
+
redirectUri: adminCallbackUrl(c, ctx),
|
|
220
|
+
returnTo: safeReturnTo(c.req.query('returnTo')),
|
|
221
|
+
});
|
|
222
|
+
setCookie(c, OIDC_COOKIE, stateToken, {
|
|
223
|
+
httpOnly: true,
|
|
224
|
+
secure: isSecure(c, ctx.config.TRUST_PROXY),
|
|
225
|
+
sameSite: 'Lax',
|
|
226
|
+
path: '/auth',
|
|
227
|
+
maxAge: 600,
|
|
228
|
+
});
|
|
229
|
+
return c.redirect(url);
|
|
230
|
+
});
|
|
231
|
+
app.get('/auth/oidc/link', requireUser(ctx), async (c) => {
|
|
232
|
+
const { url, stateToken } = await ctx.oidc.begin({
|
|
233
|
+
purpose: 'admin_link',
|
|
234
|
+
redirectUri: adminCallbackUrl(c, ctx),
|
|
235
|
+
returnTo: '/settings/profile',
|
|
236
|
+
userId: c.get('user').id,
|
|
237
|
+
});
|
|
238
|
+
setCookie(c, OIDC_COOKIE, stateToken, {
|
|
239
|
+
httpOnly: true,
|
|
240
|
+
secure: isSecure(c, ctx.config.TRUST_PROXY),
|
|
241
|
+
sameSite: 'Lax',
|
|
242
|
+
path: '/auth',
|
|
243
|
+
maxAge: 600,
|
|
244
|
+
});
|
|
245
|
+
return c.redirect(url);
|
|
246
|
+
});
|
|
247
|
+
app.get('/auth/oidc/callback', async (c) => {
|
|
248
|
+
const fail = (code) => c.redirect(`/login?error=${encodeURIComponent(code)}`);
|
|
249
|
+
let result;
|
|
250
|
+
try {
|
|
251
|
+
result = await ctx.oidc.complete(new URL(c.req.url, adminCallbackUrl(c, ctx)), getCookie(c, OIDC_COOKIE));
|
|
252
|
+
}
|
|
253
|
+
catch (err) {
|
|
254
|
+
writeAudit(ctx.db, {
|
|
255
|
+
kind: 'auth',
|
|
256
|
+
decision: 'oidc_failed',
|
|
257
|
+
actorKind: 'system',
|
|
258
|
+
detail: { error: err instanceof Error ? err.message : String(err) },
|
|
259
|
+
});
|
|
260
|
+
return fail('oidc_failed');
|
|
261
|
+
}
|
|
262
|
+
finally {
|
|
263
|
+
deleteCookie(c, OIDC_COOKIE, { path: '/auth' });
|
|
264
|
+
}
|
|
265
|
+
const { identity, state } = result;
|
|
266
|
+
if (state.purpose === 'admin_link') {
|
|
267
|
+
// Linking requires the same signed-in user who started it.
|
|
268
|
+
const session = ctx.sessions.validate(readSessionCookie(c), 'admin', sessionLimits(ctx));
|
|
269
|
+
if (!session || session.user.id !== state.userId)
|
|
270
|
+
return fail('session_changed');
|
|
271
|
+
if (!ctx.oidc.isAllowed(identity))
|
|
272
|
+
return c.redirect('/settings/profile?error=oidc_not_allowed');
|
|
273
|
+
try {
|
|
274
|
+
ctx.users.linkOidc(session.user.id, identity.issuer, identity.subject);
|
|
275
|
+
}
|
|
276
|
+
catch (err) {
|
|
277
|
+
if (err instanceof ConflictError)
|
|
278
|
+
return c.redirect('/settings/profile?error=oidc_taken');
|
|
279
|
+
throw err;
|
|
280
|
+
}
|
|
281
|
+
return c.redirect('/settings/profile?linked=1');
|
|
282
|
+
}
|
|
283
|
+
if (state.purpose !== 'admin_login')
|
|
284
|
+
return fail('wrong_flow');
|
|
285
|
+
const user = await ctx.oidc.resolveUser(ctx.users, identity);
|
|
286
|
+
if (!user) {
|
|
287
|
+
writeAudit(ctx.db, {
|
|
288
|
+
kind: 'auth',
|
|
289
|
+
decision: 'oidc_denied',
|
|
290
|
+
actorKind: 'system',
|
|
291
|
+
detail: { issuer: identity.issuer, subject: identity.subject, email: identity.email },
|
|
292
|
+
});
|
|
293
|
+
return fail('oidc_not_allowed');
|
|
294
|
+
}
|
|
295
|
+
startSession(c, ctx, user, 'oidc');
|
|
296
|
+
return c.redirect(safeReturnTo(state.returnTo));
|
|
297
|
+
});
|
|
298
|
+
}
|
|
299
|
+
export function registerProfileRoutes(app, ctx) {
|
|
300
|
+
app.get('/api/profile', (c) => c.json(toPublicUser(ctx.users.get(c.get('user').id))));
|
|
301
|
+
app.post('/api/profile/password', async (c) => {
|
|
302
|
+
const body = await readJson(c, z.object({ currentPassword: z.string().optional(), newPassword: z.string() }));
|
|
303
|
+
const user = c.get('user');
|
|
304
|
+
await ctx.users.setPassword(user.id, body.newPassword, {
|
|
305
|
+
currentPassword: body.currentPassword ?? '',
|
|
306
|
+
actorId: user.id,
|
|
307
|
+
});
|
|
308
|
+
ctx.sessions.revokeUser(user.id, c.get('sessionRaw'));
|
|
309
|
+
ctx.oauth.revokeUserGrants(user.id, 'password_changed', { userId: user.id });
|
|
310
|
+
return c.json({ status: 'ok' });
|
|
311
|
+
});
|
|
312
|
+
app.post('/api/profile/totp/begin', (c) => c.json(ctx.users.beginTotp(c.get('user').id)));
|
|
313
|
+
app.post('/api/profile/totp/confirm', async (c) => {
|
|
314
|
+
const { code } = await readJson(c, z.object({ code: z.string() }));
|
|
315
|
+
return c.json({ recoveryCodes: ctx.users.confirmTotp(c.get('user').id, code) });
|
|
316
|
+
});
|
|
317
|
+
// Turning TOTP off needs a current code, so a hijacked session alone can't remove the second factor.
|
|
318
|
+
app.post('/api/profile/totp/disable', async (c) => {
|
|
319
|
+
const { code } = await readJson(c, z.object({ code: z.string() }));
|
|
320
|
+
const user = c.get('user');
|
|
321
|
+
if (getSettings(ctx.db, 'security').requireTotp && user.passwordHash) {
|
|
322
|
+
throw new ConflictError('totp_required', 'Two-factor authentication is required on this server');
|
|
323
|
+
}
|
|
324
|
+
if (!ctx.users.verifySecondFactor(user.id, code))
|
|
325
|
+
return c.json({ error: 'invalid_code', message: 'Invalid code' }, 401);
|
|
326
|
+
ctx.users.resetTotp(user.id);
|
|
327
|
+
return c.json({ status: 'ok' });
|
|
328
|
+
});
|
|
329
|
+
app.post('/api/profile/oidc/unlink', (c) => {
|
|
330
|
+
const user = c.get('user');
|
|
331
|
+
if (!user.passwordHash)
|
|
332
|
+
throw new ConflictError('last_login_method', 'Set a password before unlinking single sign-on');
|
|
333
|
+
assertSsoRemains(ctx, { losingUser: user.id });
|
|
334
|
+
ctx.users.unlinkOidc(user.id);
|
|
335
|
+
return c.json({ status: 'ok' });
|
|
336
|
+
});
|
|
337
|
+
}
|
|
338
|
+
//# sourceMappingURL=auth.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"auth.js","sourceRoot":"","sources":["../../../src/http/admin/auth.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,YAAY,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AACjE,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,OAAO,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAC;AAE5C,OAAO,EAAE,WAAW,EAAE,SAAS,EAAE,WAAW,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AAC1F,OAAO,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AAEnD,OAAO,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAChD,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAChD,OAAO,EAAE,QAAQ,EAAE,QAAQ,EAAE,QAAQ,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAS3E,MAAM,cAAc,GAAG,WAAW,CAAC;AACnC,MAAM,qBAAqB,GAAG,kBAAkB,CAAC;AACjD,MAAM,CAAC,MAAM,WAAW,GAAG,UAAU,CAAC;AACtC,MAAM,CAAC,MAAM,WAAW,GAAG,cAAc,CAAC;AAC1C,MAAM,UAAU,GAAG,SAAS,CAAC;AAC7B,MAAM,WAAW,GAAG,UAAU,CAAC;AAC/B,MAAM,UAAU,GAAG,CAAC,GAAG,MAAM,CAAC;AAE9B,MAAM,UAAU,aAAa,CAAC,GAAe;IAC3C,MAAM,CAAC,GAAG,WAAW,CAAC,GAAG,CAAC,EAAE,EAAE,UAAU,CAAC,CAAC;IAC1C,OAAO,EAAE,MAAM,EAAE,CAAC,CAAC,kBAAkB,GAAG,MAAM,EAAE,UAAU,EAAE,CAAC,CAAC,oBAAoB,GAAG,QAAQ,EAAE,CAAC;AAClG,CAAC;AAED,MAAM,iBAAiB,GAAG,CAAC,CAAU,EAAE,EAAE,CAAC,SAAS,CAAC,CAAC,EAAE,qBAAqB,CAAC,IAAI,SAAS,CAAC,CAAC,EAAE,cAAc,CAAC,CAAC;AAE9G,SAAS,gBAAgB,CAAC,CAAU,EAAE,GAAe,EAAE,GAAW;IAChE,MAAM,MAAM,GAAG,QAAQ,CAAC,CAAC,EAAE,GAAG,CAAC,MAAM,CAAC,WAAW,CAAC,CAAC;IACnD,MAAM,MAAM,GAAG,aAAa,CAAC,GAAG,CAAC,CAAC;IAClC,qFAAqF;IACrF,SAAS,CAAC,CAAC,EAAE,MAAM,CAAC,CAAC,CAAC,qBAAqB,CAAC,CAAC,CAAC,cAAc,EAAE,GAAG,EAAE;QACjE,QAAQ,EAAE,IAAI;QACd,MAAM;QACN,QAAQ,EAAE,QAAQ;QAClB,IAAI,EAAE,GAAG;QACT,MAAM,EAAE,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,UAAU,GAAG,IAAI,CAAC;KAC7C,CAAC,CAAC;IACH,SAAS,CAAC,CAAC,EAAE,WAAW,EAAE,WAAW,CAAC,EAAE,CAAC,EAAE,EAAE,QAAQ,EAAE,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,QAAQ,EAAE,IAAI,EAAE,GAAG,EAAE,CAAC,CAAC;AACzG,CAAC;AAED,SAAS,mBAAmB,CAAC,CAAU;IACrC,YAAY,CAAC,CAAC,EAAE,cAAc,EAAE,EAAE,IAAI,EAAE,GAAG,EAAE,CAAC,CAAC;IAC/C,YAAY,CAAC,CAAC,EAAE,qBAAqB,EAAE,EAAE,IAAI,EAAE,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC;AACtE,CAAC;AAED,MAAM,UAAU,iBAAiB,CAAC,GAAe;IAC/C,OAAO,GAAG,CAAC,MAAM,CAAC,uBAAuB,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,EAAE,EAAE,UAAU,CAAC,CAAC,iBAAiB,CAAC;AAClG,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,gBAAgB,CAAC,GAAe,EAAE,MAAkD;IAClG,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,EAAE,EAAE,UAAU,CAAC,CAAC,iBAAiB;QAAE,OAAO;IAC/D,IAAI,MAAM,CAAC,OAAO,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,iBAAiB,CAAC,MAAM,CAAC,UAAU,CAAC,EAAE,CAAC;QACtE,MAAM,IAAI,aAAa,CACrB,sBAAsB,EACtB,wFAAwF,CACzF,CAAC;IACJ,CAAC;AACH,CAAC;AAED,wGAAwG;AACxG,MAAM,UAAU,cAAc,CAAC,GAAe,EAAE,IAAa;IAC3D,OAAO,WAAW,CAAC,GAAG,CAAC,EAAE,EAAE,UAAU,CAAC,CAAC,WAAW,IAAI,CAAC,CAAC,IAAI,CAAC,YAAY,IAAI,CAAC,IAAI,CAAC,WAAW,CAAC;AACjG,CAAC;AAED,MAAM,MAAM,GAAG,IAAI,GAAG,CAAC,CAAC,MAAM,EAAE,KAAK,EAAE,OAAO,EAAE,QAAQ,CAAC,CAAC,CAAC;AAE3D,sGAAsG;AACtG,MAAM,UAAU,SAAS,CAAC,GAAe;IACvC,OAAO,KAAK,EAAE,CAAC,EAAE,IAAI,EAAE,EAAE;QACvB,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC,MAAM,CAAC;YAAE,OAAO,IAAI,EAAE,CAAC;QAC7C,MAAM,MAAM,GAAG,CAAC,CAAC,GAAG,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;QACtC,IAAI,MAAM,EAAE,CAAC;YACX,MAAM,QAAQ,GAAG,GAAG,CAAC,MAAM,CAAC,gBAAgB;gBAC1C,CAAC,CAAC,IAAI,GAAG,CAAC,GAAG,CAAC,MAAM,CAAC,gBAAgB,CAAC,CAAC,MAAM;gBAC7C,CAAC,CAAC,aAAa,CAAC,CAAC,EAAE,GAAG,CAAC,MAAM,CAAC,WAAW,CAAC,CAAC;YAC7C,IAAI,MAAM,KAAK,QAAQ;gBAAE,OAAO,CAAC,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,YAAY,EAAE,OAAO,EAAE,8BAA8B,EAAE,EAAE,GAAG,CAAC,CAAC;QAChH,CAAC;QACD,MAAM,MAAM,GAAG,SAAS,CAAC,CAAC,EAAE,WAAW,CAAC,CAAC;QACzC,MAAM,MAAM,GAAG,CAAC,CAAC,GAAG,CAAC,MAAM,CAAC,WAAW,CAAC,CAAC;QACzC,IAAI,CAAC,MAAM,IAAI,CAAC,MAAM,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,MAAM,CAAC,EAAE,CAAC;YACrD,OAAO,CAAC,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,+BAA+B,EAAE,EAAE,GAAG,CAAC,CAAC;QAClF,CAAC;QACD,OAAO,IAAI,EAAE,CAAC;IAChB,CAAC,CAAC;AACJ,CAAC;AAED,MAAM,iBAAiB,GAAG,CAAC,cAAc,EAAE,cAAc,EAAE,yBAAyB,EAAE,2BAA2B,CAAC,CAAC;AAEnH,MAAM,UAAU,WAAW,CAAC,GAAe;IACzC,OAAO,KAAK,EAAE,CAAC,EAAE,IAAI,EAAE,EAAE;QACvB,MAAM,GAAG,GAAG,iBAAiB,CAAC,CAAC,CAAC,CAAC;QACjC,MAAM,OAAO,GAAG,GAAG,CAAC,QAAQ,CAAC,QAAQ,CAAC,GAAG,EAAE,OAAO,EAAE,aAAa,CAAC,GAAG,CAAC,CAAC,CAAC;QACxE,IAAI,CAAC,OAAO,IAAI,CAAC,GAAG;YAAE,OAAO,CAAC,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,iBAAiB,EAAE,OAAO,EAAE,eAAe,EAAE,EAAE,GAAG,CAAC,CAAC;QACjG,IAAI,cAAc,CAAC,GAAG,EAAE,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,iBAAiB,CAAC,QAAQ,CAAC,CAAC,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC;YACjF,OAAO,CAAC,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,0BAA0B,EAAE,OAAO,EAAE,wCAAwC,EAAE,EAAE,GAAG,CAAC,CAAC;QAC/G,CAAC;QACD,CAAC,CAAC,GAAG,CAAC,MAAM,EAAE,OAAO,CAAC,IAAI,CAAC,CAAC;QAC5B,CAAC,CAAC,GAAG,CAAC,YAAY,EAAE,GAAG,CAAC,CAAC;QACzB,OAAO,IAAI,EAAE,CAAC;IAChB,CAAC,CAAC;AACJ,CAAC;AAED,SAAS,YAAY,CAAC,CAAU,EAAE,GAAe,EAAE,IAAa,EAAE,MAAc;IAC9E,MAAM,GAAG,GAAG,GAAG,CAAC,QAAQ,CAAC,MAAM,CAAC,IAAI,CAAC,EAAE,EAAE,OAAO,EAAE,aAAa,CAAC,GAAG,CAAC,EAAE;QACpE,EAAE,EAAE,QAAQ,CAAC,CAAC,EAAE,GAAG,CAAC,MAAM,CAAC,WAAW,CAAC;QACvC,SAAS,EAAE,CAAC,CAAC,GAAG,CAAC,MAAM,CAAC,YAAY,CAAC;KACtC,CAAC,CAAC;IACH,gBAAgB,CAAC,CAAC,EAAE,GAAG,EAAE,GAAG,CAAC,CAAC;IAC9B,GAAG,CAAC,KAAK,CAAC,SAAS,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IAC7B,UAAU,CAAC,GAAG,CAAC,EAAE,EAAE;QACjB,IAAI,EAAE,MAAM;QACZ,QAAQ,EAAE,OAAO;QACjB,SAAS,EAAE,MAAM;QACjB,OAAO,EAAE,IAAI,CAAC,EAAE;QAChB,MAAM,EAAE,EAAE,QAAQ,EAAE,IAAI,CAAC,QAAQ,EAAE,MAAM,EAAE,EAAE,EAAE,QAAQ,CAAC,CAAC,EAAE,GAAG,CAAC,MAAM,CAAC,WAAW,CAAC,EAAE;KACrF,CAAC,CAAC;AACL,CAAC;AAED,SAAS,gBAAgB,CAAC,CAAU,EAAE,GAAe;IACnD,MAAM,IAAI,GAAG,GAAG,CAAC,MAAM,CAAC,gBAAgB,IAAI,aAAa,CAAC,CAAC,EAAE,GAAG,CAAC,MAAM,CAAC,WAAW,CAAC,CAAC;IACrF,OAAO,IAAI,GAAG,CAAC,qBAAqB,EAAE,IAAI,CAAC,CAAC,QAAQ,EAAE,CAAC;AACzD,CAAC;AAED,MAAM,YAAY,GAAG,CAAC,CAAqB,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,UAAU,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC;AAE1G,MAAM,UAAU,kBAAkB,CAAC,GAAmB,EAAE,GAAe;IACrE,MAAM,SAAS,GAAG,CAAC,CAAC,MAAM,CAAC,EAAE,QAAQ,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,EAAE,CAAC,EAAE,QAAQ,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IAE3G,4EAA4E;IAC5E,GAAG,CAAC,GAAG,CAAC,cAAc,EAAE,CAAC,CAAC,EAAE,EAAE;QAC5B,IAAI,CAAC,SAAS,CAAC,CAAC,EAAE,WAAW,CAAC,EAAE,CAAC;YAC/B,SAAS,CAAC,CAAC,EAAE,WAAW,EAAE,WAAW,CAAC,EAAE,CAAC,EAAE;gBACzC,QAAQ,EAAE,KAAK;gBACf,MAAM,EAAE,QAAQ,CAAC,CAAC,EAAE,GAAG,CAAC,MAAM,CAAC,WAAW,CAAC;gBAC3C,QAAQ,EAAE,QAAQ;gBAClB,IAAI,EAAE,GAAG;aACV,CAAC,CAAC;QACL,CAAC;QACD,MAAM,IAAI,GAAG,GAAG,CAAC,IAAI,CAAC,WAAW,EAAE,CAAC;QACpC,MAAM,IAAI,GAAG;YACX,aAAa,EAAE,GAAG,CAAC,KAAK,CAAC,KAAK,EAAE,KAAK,CAAC;YACtC,iBAAiB,EAAE,iBAAiB,CAAC,GAAG,CAAC;YACzC,IAAI,EAAE,EAAE,OAAO,EAAE,CAAC,CAAC,IAAI,EAAE,OAAO,EAAE,KAAK,EAAE,IAAI,EAAE,KAAK,IAAI,KAAK,EAAE;SAChE,CAAC;QACF,MAAM,OAAO,GAAG,GAAG,CAAC,QAAQ,CAAC,QAAQ,CAAC,iBAAiB,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,aAAa,CAAC,GAAG,CAAC,CAAC,CAAC;QACzF,IAAI,CAAC,OAAO;YAAE,OAAO,CAAC,CAAC,IAAI,CAAC,EAAE,aAAa,EAAE,KAAK,EAAE,GAAG,IAAI,EAAE,CAAC,CAAC;QAC/D,OAAO,CAAC,CAAC,IAAI,CAAC;YACZ,aAAa,EAAE,IAAI;YACnB,GAAG,IAAI;YACP,IAAI,EAAE,YAAY,CAAC,OAAO,CAAC,IAAI,CAAC;YAChC,cAAc,EAAE,cAAc,CAAC,GAAG,EAAE,OAAO,CAAC,IAAI,CAAC;SAClD,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IAEH,GAAG,CAAC,IAAI,CAAC,YAAY,EAAE,KAAK,EAAE,CAAC,EAAE,EAAE;QACjC,MAAM,IAAI,GAAG,MAAM,QAAQ,CAAC,CAAC,EAAE,SAAS,CAAC,CAAC;QAC1C,MAAM,OAAO,GAAG,MAAM,GAAG,CAAC,KAAK,CAAC,cAAc,CAAC,IAAI,CAAC,QAAQ,EAAE,IAAI,CAAC,QAAQ,CAAC,CAAC;QAC7E,YAAY,CAAC,CAAC,EAAE,GAAG,EAAE,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,OAAO,CAAC,EAAE,CAAC,EAAE,OAAO,CAAC,CAAC;QACzD,OAAO,CAAC,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,IAAI,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC,CAAC;IACjD,CAAC,CAAC,CAAC;IAEH,GAAG,CAAC,IAAI,CAAC,aAAa,EAAE,KAAK,EAAE,CAAC,EAAE,EAAE;QAClC,MAAM,EAAE,GAAG,QAAQ,CAAC,CAAC,EAAE,GAAG,CAAC,MAAM,CAAC,WAAW,CAAC,CAAC;QAC/C,IAAI,CAAC,GAAG,CAAC,QAAQ,CAAC,OAAO,CAAC,EAAE,CAAC;YAC3B,OAAO,CAAC,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,cAAc,EAAE,OAAO,EAAE,oCAAoC,EAAE,EAAE,GAAG,CAAC,CAAC;QAC/F,IAAI,CAAC,iBAAiB,CAAC,GAAG,CAAC;YAAE,OAAO,CAAC,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,sBAAsB,EAAE,OAAO,EAAE,oBAAoB,EAAE,EAAE,GAAG,CAAC,CAAC;QAClH,MAAM,IAAI,GAAG,MAAM,QAAQ,CAAC,CAAC,EAAE,SAAS,CAAC,CAAC;QAC1C,MAAM,SAAS,GAAG,GAAG,CAAC,QAAQ,CAAC,SAAS,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;QACxD,IAAI,SAAS,GAAG,CAAC,EAAE,CAAC;YAClB,OAAO,CAAC,CAAC,IAAI,CACX,EAAE,KAAK,EAAE,QAAQ,EAAE,OAAO,EAAE,0CAA0C,IAAI,CAAC,IAAI,CAAC,SAAS,GAAG,EAAE,CAAC,MAAM,EAAE,EACvG,GAAG,CACJ,CAAC;QACJ,CAAC;QACD,MAAM,IAAI,GAAG,MAAM,GAAG,CAAC,KAAK,CAAC,cAAc,CAAC,IAAI,CAAC,QAAQ,EAAE,IAAI,CAAC,QAAQ,CAAC,CAAC;QAC1E,IAAI,CAAC,IAAI,EAAE,CAAC;YACV,MAAM,MAAM,GAAG,GAAG,CAAC,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;YAChD,UAAU,CAAC,GAAG,CAAC,EAAE,EAAE;gBACjB,IAAI,EAAE,MAAM;gBACZ,QAAQ,EAAE,MAAM,CAAC,CAAC,CAAC,cAAc,CAAC,CAAC,CAAC,cAAc;gBAClD,SAAS,EAAE,QAAQ;gBACnB,MAAM,EAAE,EAAE,QAAQ,EAAE,IAAI,CAAC,QAAQ,EAAE,EAAE,EAAE;aACxC,CAAC,CAAC;YACH,IAAI,MAAM;gBAAE,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,cAAc,EAAE,EAAE,QAAQ,EAAE,IAAI,CAAC,QAAQ,EAAE,EAAE,EAAE,CAAC,CAAC;YAC7E,OAAO,CAAC,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,qBAAqB,EAAE,OAAO,EAAE,8BAA8B,EAAE,EAAE,GAAG,CAAC,CAAC;QAChG,CAAC;QACD,IAAI,IAAI,CAAC,WAAW,EAAE,CAAC;YACrB,SAAS,CAAC,CAAC,EAAE,UAAU,EAAE,WAAW,CAAC,GAAG,CAAC,IAAI,CAAC,KAAK,EAAE,EAAE,OAAO,EAAE,IAAI,CAAC,EAAE,EAAE,EAAE,UAAU,CAAC,EAAE;gBACtF,QAAQ,EAAE,IAAI;gBACd,MAAM,EAAE,QAAQ,CAAC,CAAC,EAAE,GAAG,CAAC,MAAM,CAAC,WAAW,CAAC;gBAC3C,QAAQ,EAAE,QAAQ;gBAClB,IAAI,EAAE,OAAO;gBACb,MAAM,EAAE,UAAU,GAAG,IAAI;aAC1B,CAAC,CAAC;YACH,OAAO,CAAC,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,eAAe,EAAE,CAAC,CAAC;QAC7C,CAAC;QACD,GAAG,CAAC,QAAQ,CAAC,OAAO,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;QACpC,YAAY,CAAC,CAAC,EAAE,GAAG,EAAE,IAAI,EAAE,UAAU,CAAC,CAAC;QACvC,OAAO,CAAC,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,IAAI,EAAE,cAAc,EAAE,cAAc,CAAC,GAAG,EAAE,IAAI,CAAC,EAAE,CAAC,CAAC;IAC7E,CAAC,CAAC,CAAC;IAEH,GAAG,CAAC,IAAI,CAAC,YAAY,EAAE,KAAK,EAAE,CAAC,EAAE,EAAE;QACjC,MAAM,EAAE,IAAI,EAAE,GAAG,MAAM,QAAQ,CAAC,CAAC,EAAE,CAAC,CAAC,MAAM,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC;QAClF,MAAM,OAAO,GAAG,aAAa,CAAsB,GAAG,CAAC,IAAI,CAAC,KAAK,EAAE,SAAS,CAAC,CAAC,EAAE,UAAU,CAAC,CAAC,CAAC;QAC7F,IAAI,CAAC,OAAO;YAAE,OAAO,CAAC,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,aAAa,EAAE,OAAO,EAAE,eAAe,EAAE,EAAE,GAAG,CAAC,CAAC;QACrF,MAAM,IAAI,GAAG,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC;QAC5C,IAAI,GAAG,CAAC,QAAQ,CAAC,SAAS,CAAC,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC;YAC3C,OAAO,CAAC,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,QAAQ,EAAE,OAAO,EAAE,0BAA0B,EAAE,EAAE,GAAG,CAAC,CAAC;QAC/E,MAAM,MAAM,GAAG,GAAG,CAAC,KAAK,CAAC,kBAAkB,CAAC,IAAI,CAAC,EAAE,EAAE,IAAI,CAAC,CAAC;QAC3D,IAAI,CAAC,MAAM,EAAE,CAAC;YACZ,IAAI,GAAG,CAAC,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC;gBAAE,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,cAAc,EAAE,EAAE,QAAQ,EAAE,IAAI,CAAC,QAAQ,EAAE,CAAC,CAAC;YACnG,UAAU,CAAC,GAAG,CAAC,EAAE,EAAE;gBACjB,IAAI,EAAE,MAAM;gBACZ,QAAQ,EAAE,aAAa;gBACvB,SAAS,EAAE,QAAQ;gBACnB,MAAM,EAAE,EAAE,QAAQ,EAAE,IAAI,CAAC,QAAQ,EAAE;aACpC,CAAC,CAAC;YACH,OAAO,CAAC,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,cAAc,EAAE,OAAO,EAAE,cAAc,EAAE,EAAE,GAAG,CAAC,CAAC;QACzE,CAAC;QACD,YAAY,CAAC,CAAC,EAAE,UAAU,EAAE,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC,CAAC;QAC/C,GAAG,CAAC,QAAQ,CAAC,OAAO,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;QACpC,YAAY,CAAC,CAAC,EAAE,GAAG,EAAE,IAAI,EAAE,MAAM,KAAK,UAAU,CAAC,CAAC,CAAC,wBAAwB,CAAC,CAAC,CAAC,eAAe,CAAC,CAAC;QAC/F,OAAO,CAAC,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC;IAClC,CAAC,CAAC,CAAC;IAEH,GAAG,CAAC,IAAI,CAAC,cAAc,EAAE,CAAC,CAAC,EAAE,EAAE;QAC7B,GAAG,CAAC,QAAQ,CAAC,MAAM,CAAC,iBAAiB,CAAC,CAAC,CAAC,CAAC,CAAC;QAC1C,mBAAmB,CAAC,CAAC,CAAC,CAAC;QACvB,OAAO,CAAC,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC;IAClC,CAAC,CAAC,CAAC;IAEH,wEAAwE;IACxE,GAAG,CAAC,GAAG,CAAC,kBAAkB,EAAE,KAAK,EAAE,CAAC,EAAE,EAAE;QACtC,MAAM,EAAE,GAAG,EAAE,UAAU,EAAE,GAAG,MAAM,GAAG,CAAC,IAAI,CAAC,KAAK,CAAC;YAC/C,OAAO,EAAE,aAAa;YACtB,WAAW,EAAE,gBAAgB,CAAC,CAAC,EAAE,GAAG,CAAC;YACrC,QAAQ,EAAE,YAAY,CAAC,CAAC,CAAC,GAAG,CAAC,KAAK,CAAC,UAAU,CAAC,CAAC;SAChD,CAAC,CAAC;QACH,SAAS,CAAC,CAAC,EAAE,WAAW,EAAE,UAAU,EAAE;YACpC,QAAQ,EAAE,IAAI;YACd,MAAM,EAAE,QAAQ,CAAC,CAAC,EAAE,GAAG,CAAC,MAAM,CAAC,WAAW,CAAC;YAC3C,QAAQ,EAAE,KAAK;YACf,IAAI,EAAE,OAAO;YACb,MAAM,EAAE,GAAG;SACZ,CAAC,CAAC;QACH,OAAO,CAAC,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC;IACzB,CAAC,CAAC,CAAC;IAEH,GAAG,CAAC,GAAG,CAAC,iBAAiB,EAAE,WAAW,CAAC,GAAG,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,EAAE;QACvD,MAAM,EAAE,GAAG,EAAE,UAAU,EAAE,GAAG,MAAM,GAAG,CAAC,IAAI,CAAC,KAAK,CAAC;YAC/C,OAAO,EAAE,YAAY;YACrB,WAAW,EAAE,gBAAgB,CAAC,CAAC,EAAE,GAAG,CAAC;YACrC,QAAQ,EAAE,mBAAmB;YAC7B,MAAM,EAAE,CAAC,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,EAAE;SACzB,CAAC,CAAC;QACH,SAAS,CAAC,CAAC,EAAE,WAAW,EAAE,UAAU,EAAE;YACpC,QAAQ,EAAE,IAAI;YACd,MAAM,EAAE,QAAQ,CAAC,CAAC,EAAE,GAAG,CAAC,MAAM,CAAC,WAAW,CAAC;YAC3C,QAAQ,EAAE,KAAK;YACf,IAAI,EAAE,OAAO;YACb,MAAM,EAAE,GAAG;SACZ,CAAC,CAAC;QACH,OAAO,CAAC,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC;IACzB,CAAC,CAAC,CAAC;IAEH,GAAG,CAAC,GAAG,CAAC,qBAAqB,EAAE,KAAK,EAAE,CAAC,EAAE,EAAE;QACzC,MAAM,IAAI,GAAG,CAAC,IAAY,EAAE,EAAE,CAAC,CAAC,CAAC,QAAQ,CAAC,gBAAgB,kBAAkB,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QACtF,IAAI,MAAM,CAAC;QACX,IAAI,CAAC;YACH,MAAM,GAAG,MAAM,GAAG,CAAC,IAAI,CAAC,QAAQ,CAAC,IAAI,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC,GAAG,EAAE,gBAAgB,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC,EAAE,SAAS,CAAC,CAAC,EAAE,WAAW,CAAC,CAAC,CAAC;QAC5G,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,UAAU,CAAC,GAAG,CAAC,EAAE,EAAE;gBACjB,IAAI,EAAE,MAAM;gBACZ,QAAQ,EAAE,aAAa;gBACvB,SAAS,EAAE,QAAQ;gBACnB,MAAM,EAAE,EAAE,KAAK,EAAE,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE;aACpE,CAAC,CAAC;YACH,OAAO,IAAI,CAAC,aAAa,CAAC,CAAC;QAC7B,CAAC;gBAAS,CAAC;YACT,YAAY,CAAC,CAAC,EAAE,WAAW,EAAE,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC,CAAC;QAClD,CAAC;QACD,MAAM,EAAE,QAAQ,EAAE,KAAK,EAAE,GAAG,MAAM,CAAC;QACnC,IAAI,KAAK,CAAC,OAAO,KAAK,YAAY,EAAE,CAAC;YACnC,2DAA2D;YAC3D,MAAM,OAAO,GAAG,GAAG,CAAC,QAAQ,CAAC,QAAQ,CAAC,iBAAiB,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,aAAa,CAAC,GAAG,CAAC,CAAC,CAAC;YACzF,IAAI,CAAC,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC,EAAE,KAAK,KAAK,CAAC,MAAM;gBAAE,OAAO,IAAI,CAAC,iBAAiB,CAAC,CAAC;YACjF,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,SAAS,CAAC,QAAQ,CAAC;gBAAE,OAAO,CAAC,CAAC,QAAQ,CAAC,0CAA0C,CAAC,CAAC;YACjG,IAAI,CAAC;gBACH,GAAG,CAAC,KAAK,CAAC,QAAQ,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,EAAE,QAAQ,CAAC,MAAM,EAAE,QAAQ,CAAC,OAAO,CAAC,CAAC;YACzE,CAAC;YAAC,OAAO,GAAG,EAAE,CAAC;gBACb,IAAI,GAAG,YAAY,aAAa;oBAAE,OAAO,CAAC,CAAC,QAAQ,CAAC,oCAAoC,CAAC,CAAC;gBAC1F,MAAM,GAAG,CAAC;YACZ,CAAC;YACD,OAAO,CAAC,CAAC,QAAQ,CAAC,4BAA4B,CAAC,CAAC;QAClD,CAAC;QACD,IAAI,KAAK,CAAC,OAAO,KAAK,aAAa;YAAE,OAAO,IAAI,CAAC,YAAY,CAAC,CAAC;QAC/D,MAAM,IAAI,GAAG,MAAM,GAAG,CAAC,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,KAAK,EAAE,QAAQ,CAAC,CAAC;QAC7D,IAAI,CAAC,IAAI,EAAE,CAAC;YACV,UAAU,CAAC,GAAG,CAAC,EAAE,EAAE;gBACjB,IAAI,EAAE,MAAM;gBACZ,QAAQ,EAAE,aAAa;gBACvB,SAAS,EAAE,QAAQ;gBACnB,MAAM,EAAE,EAAE,MAAM,EAAE,QAAQ,CAAC,MAAM,EAAE,OAAO,EAAE,QAAQ,CAAC,OAAO,EAAE,KAAK,EAAE,QAAQ,CAAC,KAAK,EAAE;aACtF,CAAC,CAAC;YACH,OAAO,IAAI,CAAC,kBAAkB,CAAC,CAAC;QAClC,CAAC;QACD,YAAY,CAAC,CAAC,EAAE,GAAG,EAAE,IAAI,EAAE,MAAM,CAAC,CAAC;QACnC,OAAO,CAAC,CAAC,QAAQ,CAAC,YAAY,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC,CAAC;IAClD,CAAC,CAAC,CAAC;AACL,CAAC;AAED,MAAM,UAAU,qBAAqB,CAAC,GAAmB,EAAE,GAAe;IACxE,GAAG,CAAC,GAAG,CAAC,cAAc,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,YAAY,CAAC,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC;IAEtF,GAAG,CAAC,IAAI,CAAC,uBAAuB,EAAE,KAAK,EAAE,CAAC,EAAE,EAAE;QAC5C,MAAM,IAAI,GAAG,MAAM,QAAQ,CAAC,CAAC,EAAE,CAAC,CAAC,MAAM,CAAC,EAAE,eAAe,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE,EAAE,WAAW,EAAE,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC,CAAC;QAC9G,MAAM,IAAI,GAAG,CAAC,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;QAC3B,MAAM,GAAG,CAAC,KAAK,CAAC,WAAW,CAAC,IAAI,CAAC,EAAE,EAAE,IAAI,CAAC,WAAW,EAAE;YACrD,eAAe,EAAE,IAAI,CAAC,eAAe,IAAI,EAAE;YAC3C,OAAO,EAAE,IAAI,CAAC,EAAE;SACjB,CAAC,CAAC;QACH,GAAG,CAAC,QAAQ,CAAC,UAAU,CAAC,IAAI,CAAC,EAAE,EAAE,CAAC,CAAC,GAAG,CAAC,YAAY,CAAC,CAAC,CAAC;QACtD,GAAG,CAAC,KAAK,CAAC,gBAAgB,CAAC,IAAI,CAAC,EAAE,EAAE,kBAAkB,EAAE,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,EAAE,CAAC,CAAC;QAC7E,OAAO,CAAC,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC;IAClC,CAAC,CAAC,CAAC;IAEH,GAAG,CAAC,IAAI,CAAC,yBAAyB,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;IAE1F,GAAG,CAAC,IAAI,CAAC,2BAA2B,EAAE,KAAK,EAAE,CAAC,EAAE,EAAE;QAChD,MAAM,EAAE,IAAI,EAAE,GAAG,MAAM,QAAQ,CAAC,CAAC,EAAE,CAAC,CAAC,MAAM,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC,CAAC;QACnE,OAAO,CAAC,CAAC,IAAI,CAAC,EAAE,aAAa,EAAE,GAAG,CAAC,KAAK,CAAC,WAAW,CAAC,CAAC,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,EAAE,EAAE,IAAI,CAAC,EAAE,CAAC,CAAC;IAClF,CAAC,CAAC,CAAC;IAEH,qGAAqG;IACrG,GAAG,CAAC,IAAI,CAAC,2BAA2B,EAAE,KAAK,EAAE,CAAC,EAAE,EAAE;QAChD,MAAM,EAAE,IAAI,EAAE,GAAG,MAAM,QAAQ,CAAC,CAAC,EAAE,CAAC,CAAC,MAAM,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC,CAAC;QACnE,MAAM,IAAI,GAAG,CAAC,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;QAC3B,IAAI,WAAW,CAAC,GAAG,CAAC,EAAE,EAAE,UAAU,CAAC,CAAC,WAAW,IAAI,IAAI,CAAC,YAAY,EAAE,CAAC;YACrE,MAAM,IAAI,aAAa,CAAC,eAAe,EAAE,sDAAsD,CAAC,CAAC;QACnG,CAAC;QACD,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,kBAAkB,CAAC,IAAI,CAAC,EAAE,EAAE,IAAI,CAAC;YAC9C,OAAO,CAAC,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,cAAc,EAAE,OAAO,EAAE,cAAc,EAAE,EAAE,GAAG,CAAC,CAAC;QACzE,GAAG,CAAC,KAAK,CAAC,SAAS,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QAC7B,OAAO,CAAC,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC;IAClC,CAAC,CAAC,CAAC;IAEH,GAAG,CAAC,IAAI,CAAC,0BAA0B,EAAE,CAAC,CAAC,EAAE,EAAE;QACzC,MAAM,IAAI,GAAG,CAAC,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;QAC3B,IAAI,CAAC,IAAI,CAAC,YAAY;YACpB,MAAM,IAAI,aAAa,CAAC,mBAAmB,EAAE,gDAAgD,CAAC,CAAC;QACjG,gBAAgB,CAAC,GAAG,EAAE,EAAE,UAAU,EAAE,IAAI,CAAC,EAAE,EAAE,CAAC,CAAC;QAC/C,GAAG,CAAC,KAAK,CAAC,UAAU,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QAC9B,OAAO,CAAC,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC;IAClC,CAAC,CAAC,CAAC;AACL,CAAC"}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import type { Hono } from 'hono';
|
|
2
|
+
import type { AppContext } from '../../app.js';
|
|
3
|
+
import type { AdminEnv } from './auth.js';
|
|
4
|
+
/** Instance-scoped Admin API routes (design §8.4). Every mutation is audited by the service layer. */
|
|
5
|
+
export declare function registerInstanceRoutes(app: Hono<AdminEnv>, ctx: AppContext): void;
|
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
import { and, asc, eq } from 'drizzle-orm';
|
|
2
|
+
import { z } from 'zod';
|
|
3
|
+
import { applyBulkLevel, listGroups, mergeGroups, previewBulkLevel, renameGroup, setGroupLevel, updateOperation, accessInput, } from '../../catalog/groups.js';
|
|
4
|
+
import { findRegistryEntries } from '../../catalog/registry.js';
|
|
5
|
+
import { createRule, deleteRule, listRules, updateRule } from '../../catalog/rules.js';
|
|
6
|
+
import { operationGroups, operations } from '../../db/schema.js';
|
|
7
|
+
import { ValidationError } from '../../errors.js';
|
|
8
|
+
import { ACCESS_LEVELS, effectiveAccess } from '../../gate/access.js';
|
|
9
|
+
import { AUTH_MODES } from '../../instances/manager.js';
|
|
10
|
+
import { clientIp, readJson, readOptionalJson } from '../common.js';
|
|
11
|
+
import { createRedactor, GLOBAL_SENSITIVE_KEYS } from '../../gate/redact.js';
|
|
12
|
+
/** Instance-scoped Admin API routes (design §8.4). Every mutation is audited by the service layer. */
|
|
13
|
+
export function registerInstanceRoutes(app, ctx) {
|
|
14
|
+
const actor = (c) => ({ userId: c.get('user').id });
|
|
15
|
+
const exists = (id) => ctx.instances.get(id); // throws 404
|
|
16
|
+
const Connection = z.object({
|
|
17
|
+
config: z.record(z.string(), z.unknown()).default({}),
|
|
18
|
+
secrets: z.record(z.string(), z.string().nullable()).default({}),
|
|
19
|
+
});
|
|
20
|
+
app.get('/api/instances', (c) => c.json(ctx.instances.list()));
|
|
21
|
+
app.post('/api/instances', async (c) => {
|
|
22
|
+
const body = await readJson(c, z.object({
|
|
23
|
+
pluginId: z.string().min(1),
|
|
24
|
+
slug: z.string().min(1).max(63),
|
|
25
|
+
displayName: z.string().max(100).optional(),
|
|
26
|
+
connection: z.record(z.string(), z.unknown()).default({}),
|
|
27
|
+
authMode: z.enum(AUTH_MODES).nullable().optional(),
|
|
28
|
+
settings: z.record(z.string(), z.unknown()).optional(),
|
|
29
|
+
enabled: z.boolean().optional(),
|
|
30
|
+
}));
|
|
31
|
+
return c.json(await ctx.instances.create(body, actor(c)), 201);
|
|
32
|
+
});
|
|
33
|
+
app.get('/api/instances/:id', (c) => c.json(exists(c.req.param('id'))));
|
|
34
|
+
app.patch('/api/instances/:id', async (c) => {
|
|
35
|
+
const body = await readJson(c, z.object({
|
|
36
|
+
displayName: z.string().max(100).optional(),
|
|
37
|
+
slug: z.string().max(63).optional(),
|
|
38
|
+
enabled: z.boolean().optional(),
|
|
39
|
+
authMode: z.enum(AUTH_MODES).nullable().optional(),
|
|
40
|
+
settings: z.record(z.string(), z.unknown()).optional(),
|
|
41
|
+
}));
|
|
42
|
+
return c.json(await ctx.instances.update(c.req.param('id'), body, actor(c)));
|
|
43
|
+
});
|
|
44
|
+
app.delete('/api/instances/:id', async (c) => {
|
|
45
|
+
const { confirm } = await readJson(c, z.object({ confirm: z.string() }));
|
|
46
|
+
await ctx.instances.remove(c.req.param('id'), confirm, actor(c));
|
|
47
|
+
ctx.oauth.forgetInstance(c.req.param('id'));
|
|
48
|
+
return c.body(null, 204);
|
|
49
|
+
});
|
|
50
|
+
app.get('/api/instances/:id/connection', (c) => c.json(ctx.instances.getConnection(c.req.param('id'))));
|
|
51
|
+
app.put('/api/instances/:id/connection', async (c) => {
|
|
52
|
+
const body = await readJson(c, Connection);
|
|
53
|
+
return c.json(await ctx.instances.updateConnection(c.req.param('id'), body, actor(c)));
|
|
54
|
+
});
|
|
55
|
+
// Testing a connection makes the server talk to arbitrary hosts; throttled so it can't be an oracle (TN §4).
|
|
56
|
+
app.post('/api/instances/:id/connection/test', async (c) => {
|
|
57
|
+
if (!ctx.throttle.allowIp(clientIp(c, ctx.config.TRUST_PROXY))) {
|
|
58
|
+
return c.json({ error: 'rate_limited', message: 'Too many attempts; try again later' }, 429);
|
|
59
|
+
}
|
|
60
|
+
const candidate = await readOptionalJson(c, Connection);
|
|
61
|
+
return c.json(await ctx.instances.testConnection(c.req.param('id'), candidate));
|
|
62
|
+
});
|
|
63
|
+
app.post('/api/instances/:id/sync', async (c) => {
|
|
64
|
+
exists(c.req.param('id'));
|
|
65
|
+
return c.json(await ctx.instances.syncNow(c.req.param('id')));
|
|
66
|
+
});
|
|
67
|
+
// ── access groups & operations (design §5.2.1) ──
|
|
68
|
+
app.get('/api/instances/:id/groups', (c) => {
|
|
69
|
+
exists(c.req.param('id'));
|
|
70
|
+
return c.json(listGroups(ctx.db, c.req.param('id')));
|
|
71
|
+
});
|
|
72
|
+
app.patch('/api/instances/:id/groups/:key', async (c) => {
|
|
73
|
+
const id = c.req.param('id');
|
|
74
|
+
exists(id);
|
|
75
|
+
const body = await readJson(c, z.object({
|
|
76
|
+
level: z.string().optional(),
|
|
77
|
+
label: z.string().optional(),
|
|
78
|
+
acknowledge: z.array(z.string()).optional(),
|
|
79
|
+
}));
|
|
80
|
+
if (body.label !== undefined)
|
|
81
|
+
renameGroup(ctx.db, id, c.req.param('key'), body.label, { actor: actor(c) });
|
|
82
|
+
if (body.level !== undefined) {
|
|
83
|
+
setGroupLevel(ctx.db, id, c.req.param('key'), body.level, { acknowledge: body.acknowledge, actor: actor(c) });
|
|
84
|
+
}
|
|
85
|
+
return c.json(listGroups(ctx.db, id).find((g) => g.key === c.req.param('key')) ?? null);
|
|
86
|
+
});
|
|
87
|
+
app.post('/api/instances/:id/groups/merge', async (c) => {
|
|
88
|
+
const id = c.req.param('id');
|
|
89
|
+
exists(id);
|
|
90
|
+
const body = await readJson(c, z.object({ from: z.array(z.string()).min(1), into: z.string().min(1), label: z.string().optional() }));
|
|
91
|
+
return c.json(mergeGroups(ctx.db, id, body, { actor: actor(c) }));
|
|
92
|
+
});
|
|
93
|
+
app.get('/api/instances/:id/groups/bulk-level/preview', (c) => {
|
|
94
|
+
exists(c.req.param('id'));
|
|
95
|
+
return c.json(previewBulkLevel(ctx.db, c.req.param('id'), c.req.query('level') ?? ''));
|
|
96
|
+
});
|
|
97
|
+
app.post('/api/instances/:id/groups/bulk-level', async (c) => {
|
|
98
|
+
exists(c.req.param('id'));
|
|
99
|
+
const body = await readJson(c, z.object({ level: z.string(), confirm: z.string().optional(), acknowledge: z.array(z.string()).optional() }));
|
|
100
|
+
return c.json(applyBulkLevel(ctx.db, c.req.param('id'), body.level, { ...body, actor: actor(c) }));
|
|
101
|
+
});
|
|
102
|
+
app.get('/api/instances/:id/operations', (c) => {
|
|
103
|
+
const id = c.req.param('id');
|
|
104
|
+
exists(id);
|
|
105
|
+
const q = c.req.query();
|
|
106
|
+
const groups = new Map(ctx.db
|
|
107
|
+
.select()
|
|
108
|
+
.from(operationGroups)
|
|
109
|
+
.where(eq(operationGroups.instanceId, id))
|
|
110
|
+
.all()
|
|
111
|
+
.map((g) => [g.id, g]));
|
|
112
|
+
const conditions = [eq(operations.instanceId, id)];
|
|
113
|
+
if (q.stale !== '1')
|
|
114
|
+
conditions.push(eq(operations.stale, false));
|
|
115
|
+
const text = q.q?.toLowerCase();
|
|
116
|
+
const rows = ctx.db
|
|
117
|
+
.select()
|
|
118
|
+
.from(operations)
|
|
119
|
+
.where(and(...conditions))
|
|
120
|
+
.orderBy(asc(operations.key))
|
|
121
|
+
.all()
|
|
122
|
+
.map((op) => {
|
|
123
|
+
const group = groups.get(op.groupId);
|
|
124
|
+
const access = effectiveAccess(accessInput(op), group);
|
|
125
|
+
return {
|
|
126
|
+
...op,
|
|
127
|
+
group: group?.key ?? null,
|
|
128
|
+
/** The level in force: the operation's own, else its group's. */
|
|
129
|
+
level: op.levelOverride ?? group?.level ?? 'none',
|
|
130
|
+
reachable: access.reachable,
|
|
131
|
+
mode: access.reachable ? access.mode : null,
|
|
132
|
+
pendingReview: access.reachable && access.pendingReview === true,
|
|
133
|
+
reason: access.reachable ? null : access.reason,
|
|
134
|
+
};
|
|
135
|
+
})
|
|
136
|
+
.filter((op) => (!q.group || op.group === q.group) && (!q.reason || op.reason === q.reason))
|
|
137
|
+
.filter((op) => (q.needsReview === '1' ? op.needsReview || op.pendingReview : true))
|
|
138
|
+
.filter((op) => !text || op.key.toLowerCase().includes(text) || (op.displayName ?? '').toLowerCase().includes(text));
|
|
139
|
+
return c.json(rows);
|
|
140
|
+
});
|
|
141
|
+
app.patch('/api/instances/:id/operations/:opId', async (c) => {
|
|
142
|
+
const body = await readJson(c, z.object({
|
|
143
|
+
level: z.enum(ACCESS_LEVELS).nullable().optional(),
|
|
144
|
+
acknowledged: z.boolean().optional(),
|
|
145
|
+
classification: z.enum(['read', 'write']).optional(),
|
|
146
|
+
attestationRequired: z.boolean().optional(),
|
|
147
|
+
}));
|
|
148
|
+
updateOperation(ctx.db, c.req.param('id'), c.req.param('opId'), body, { actor: actor(c) });
|
|
149
|
+
return c.json(ctx.db
|
|
150
|
+
.select()
|
|
151
|
+
.from(operations)
|
|
152
|
+
.where(eq(operations.id, c.req.param('opId')))
|
|
153
|
+
.get());
|
|
154
|
+
});
|
|
155
|
+
// ── pickers ──
|
|
156
|
+
app.get('/api/instances/:id/registry', (c) => {
|
|
157
|
+
exists(c.req.param('id'));
|
|
158
|
+
// Mirrored attributes can hold tokens (HA camera `access_token`); the portal gets them redacted too.
|
|
159
|
+
const entries = findRegistryEntries(ctx.db, c.req.param('id'), c.req.query());
|
|
160
|
+
let redact = createRedactor(GLOBAL_SENSITIVE_KEYS);
|
|
161
|
+
try {
|
|
162
|
+
redact = ctx.instances.runtime(c.req.param('id')).redact;
|
|
163
|
+
}
|
|
164
|
+
catch {
|
|
165
|
+
// Plugin not usable right now: the global keys still apply.
|
|
166
|
+
}
|
|
167
|
+
return c.json(redact(entries));
|
|
168
|
+
});
|
|
169
|
+
app.get('/api/instances/:id/options/:source', async (c) => {
|
|
170
|
+
const rt = ctx.instances.runtime(c.req.param('id'));
|
|
171
|
+
if (!/^[a-z0-9][a-z0-9._-]{0,63}$/.test(c.req.param('source')))
|
|
172
|
+
throw new ValidationError('invalid_source', 'Invalid options source');
|
|
173
|
+
return c.json(await rt.plugin().call('optionsFor', { source: c.req.param('source'), query: c.req.query('q') }));
|
|
174
|
+
});
|
|
175
|
+
// ── pre-approval rules ──
|
|
176
|
+
app.get('/api/instances/:id/rules', (c) => {
|
|
177
|
+
exists(c.req.param('id'));
|
|
178
|
+
return c.json(listRules(ctx.db, c.req.param('id')));
|
|
179
|
+
});
|
|
180
|
+
app.post('/api/instances/:id/rules', async (c) => {
|
|
181
|
+
const rt = ctx.instances.runtime(c.req.param('id'));
|
|
182
|
+
return c.json(createRule(ctx.db, rt.manifest, rt.instanceId, await readJson(c, z.unknown()), actor(c)), 201);
|
|
183
|
+
});
|
|
184
|
+
app.patch('/api/instances/:id/rules/:ruleId', async (c) => {
|
|
185
|
+
const rt = ctx.instances.runtime(c.req.param('id'));
|
|
186
|
+
return c.json(updateRule(ctx.db, rt.manifest, rt.instanceId, c.req.param('ruleId'), await readJson(c, z.unknown()), actor(c)));
|
|
187
|
+
});
|
|
188
|
+
app.delete('/api/instances/:id/rules/:ruleId', (c) => {
|
|
189
|
+
deleteRule(ctx.db, c.req.param('id'), c.req.param('ruleId'), actor(c));
|
|
190
|
+
return c.body(null, 204);
|
|
191
|
+
});
|
|
192
|
+
}
|
|
193
|
+
//# sourceMappingURL=instances.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"instances.js","sourceRoot":"","sources":["../../../src/http/admin/instances.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import type { Hono } from 'hono';
|
|
2
|
+
import type { AppContext } from '../../app.js';
|
|
3
|
+
import type { AdminEnv } from './auth.js';
|
|
4
|
+
/** Global Admin API routes (design §8.4): overview, plugins, audit, tokens, users, settings, events. */
|
|
5
|
+
export declare function registerSystemRoutes(app: Hono<AdminEnv>, ctx: AppContext): void;
|