4879769cc7
- Add standalone.enabled config flag to switch between LDAP+SSO and ORM-backed backends without changing the production code path - New ORM models: StandaloneUser (uid, passwordHash, sshPublicKeys, groups) and StandaloneHost (slug, displayName, kind, metadata) - user_file.js and hosts_file.js implement the same interfaces as the LDAP client and accessibleHosts() respectively - models/user_ldap.js and utils/access.js become conditional facades that delegate based on conf.standalone.enabled at require time - Zero changes to ssh_server.js core logic, bridge.js, key_inject.js, tui_picker.js, or any other consumer - Fix ssh_server.js: use ?? instead of || for listenPort (0 is falsy) - Fix ssh_server.js: register session listeners before awaiting audit.create() so client exec/shell requests aren't rejected - Patch StringField.toSequelize() and IntegerField.toSequelize() to pass through primaryKey (the ORM's UUIDField already does this) - 47 tests pass (24 existing + 15 new unit + 3 existing integration + 5 new standalone integration) - Defaults to SQLite; any Sequelize dialect works via conf.orm Co-Authored-By: Claude <noreply@anthropic.com>
66 lines
2.7 KiB
JavaScript
66 lines
2.7 KiB
JavaScript
'use strict';
|
|
|
|
// model-redis backing (same store the sibling apps use). Table is the base
|
|
// class; getRedis() exposes the underlying node-redis client for the counters
|
|
// and sorted-set index in models/metrics.js and models/audit_event.js.
|
|
|
|
const conf = require('@simpleworkjs/conf');
|
|
const { setUpTable } = require('model-redis');
|
|
const { createOidcClient, bootstrapLocalAdmin } = require('@simpleworkjs/oidc-client');
|
|
|
|
const Table = setUpTable(conf.redis);
|
|
|
|
module.exports = Table;
|
|
|
|
// The raw node-redis client (created + connecting inside model-redis) — used
|
|
// for the INCR counters and the sorted-set audit index. model-redis connects
|
|
// it asynchronously; ensure it's open before first use.
|
|
let readyPromise;
|
|
async function getRedis() {
|
|
const client = Table.redisClient;
|
|
if (!readyPromise) {
|
|
readyPromise = (async () => {
|
|
if (!client.isOpen) {
|
|
try { await client.connect(); } catch (_) { /* already connecting */ }
|
|
}
|
|
return client;
|
|
})();
|
|
}
|
|
await readyPromise;
|
|
return client;
|
|
}
|
|
|
|
module.exports.getRedis = getRedis;
|
|
|
|
// Register models (order matters: User before AuthToken's relation resolves).
|
|
require('./user_redis'); // User (redis-backed local + OIDC JIT)
|
|
|
|
// Shared OIDC client (authorization-code + PKCE): session models (Token,
|
|
// AuthToken, OidcState), the Auth service, and the /login /logout /oidc/start
|
|
// /oidc/callback router — all created on this app's Table/redis. jump-host has
|
|
// no Bearer PATs, so checkApiToken is omitted (Auth.checkApiToken is absent).
|
|
const oidcClient = createOidcClient({ Table });
|
|
module.exports.Token = oidcClient.Token;
|
|
module.exports.AuthToken = oidcClient.AuthToken;
|
|
module.exports.OidcState = oidcClient.OidcState;
|
|
module.exports.Auth = oidcClient.Auth;
|
|
module.exports.authRouter = oidcClient.router;
|
|
|
|
require('./audit_event');
|
|
|
|
// Idempotent anti-lockout local admin (was the IIFE in user_redis.js).
|
|
bootstrapLocalAdmin(Table.models.User, { defaultName: 'jumpadmin' });
|
|
|
|
// Standalone mode: initialize @simpleworkjs/orm for local user/host stores.
|
|
// The ORM must be loaded before any code calls user_ldap or access — both of
|
|
// which check conf.standalone.enabled at require time and may delegate to the
|
|
// ORM-backed wrappers. Model registration is synchronous; table sync is async
|
|
// but the first query will implicitly wait (Sequelize.sync is in-flight).
|
|
// Export the promise so integration tests can await it before seeding data.
|
|
let ormReady = Promise.resolve();
|
|
if (conf.standalone && conf.standalone.enabled) {
|
|
const { init } = require('@simpleworkjs/orm');
|
|
const ormConf = conf.orm || { dialect: 'sqlite', storage: './data/standalone.sqlite', logging: false };
|
|
ormReady = init({ conf: { orm: ormConf }, models: [require('./standalone_user'), require('./standalone_host')] });
|
|
}
|
|
module.exports.ormReady = ormReady; |