Merge origin/master (TURN/coturn + BizGaze-only login) into feature tree
Resolved conflicts in routes.js and share.html: kept the dev tree's superset (ALLOW_LOCAL_LOGIN dev escape, avatar sync, richer login errors) which already includes the incoming production BizGaze-only behavior; took the more descriptive incoming comments. Restored 5 untracked modules (chat, calls, directory, reminders, webhooks) that were missing from disk — required by routes/signaling. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
+18
-8
@@ -124,7 +124,8 @@ route('POST', '/api/mfa/enable', async (req, res) => {
|
||||
// Provision (or refresh) a local user from a successful BizGaze identity check.
|
||||
// The local row exists so sessions, audit, and team-scoped data work; BizGaze stays
|
||||
// the source of truth for credentials (the local password is random + unused).
|
||||
// Emails that must always be admins regardless of what BizGaze returns (lockout safety net).
|
||||
// Emails that must always be admins regardless of what BizGaze returns (safety net so an
|
||||
// admin can't be locked out of the report if BizGaze doesn't flag them isAdmin). Optional.
|
||||
const ADMIN_EMAILS = (process.env.ADMIN_EMAILS || '').split(',').map((s) => s.trim().toLowerCase()).filter(Boolean);
|
||||
function provisionFromBizgaze(email, bz) {
|
||||
const role = (bz.isAdmin || ADMIN_EMAILS.includes(String(email).toLowerCase())) ? 'admin' : 'technician';
|
||||
@@ -144,8 +145,10 @@ function provisionFromBizgaze(email, bz) {
|
||||
return R.users.byId(existing.id);
|
||||
}
|
||||
|
||||
// Login: validates locally first (seeded/bootstrap accounts), then against BizGaze
|
||||
// (the identity provider) when BIZGAZE_LOGIN_URL is configured. Sets a session cookie.
|
||||
// Login: when BizGaze (BIZGAZE_LOGIN_URL) is configured it is the ONLY authority — the
|
||||
// credentials are verified against BizGaze and the user is provisioned/synced locally
|
||||
// (local passwords are not accepted). Without it (dev/tests) the local password is
|
||||
// checked. Sets a session cookie.
|
||||
route('POST', '/api/login', async (req, res) => {
|
||||
const { email, password, remember } = await readBody(req);
|
||||
if (!email || !password) return json(res, 400, { error: 'email and password required' });
|
||||
@@ -154,7 +157,8 @@ route('POST', '/api/login', async (req, res) => {
|
||||
|
||||
// Production: when BizGaze is the IdP, verify ONLY against BizGaze (no local-password
|
||||
// fallback) so stale in-app accounts can't shadow a BizGaze login and everyone lands in
|
||||
// the same tenant. Local accounts stay usable for dev/testing via ALLOW_LOCAL_LOGIN=1.
|
||||
// the same tenant (admins then see all sessions). Local accounts stay usable for
|
||||
// dev/testing via ALLOW_LOCAL_LOGIN=1.
|
||||
const bizgazeOnly = BZ.isEnabled() && process.env.ALLOW_LOCAL_LOGIN !== '1';
|
||||
let u = null, bzMsg = null;
|
||||
if (bizgazeOnly) {
|
||||
@@ -164,6 +168,8 @@ route('POST', '/api/login', async (req, res) => {
|
||||
u = provisionFromBizgaze(email, bz);
|
||||
if (u && u.active === 0) return json(res, 403, { error: 'This account has been deactivated' });
|
||||
} else {
|
||||
// Local/dev/tests, or ALLOW_LOCAL_LOGIN=1: verify the local password, then fall back
|
||||
// to BizGaze if a local password isn't set/correct (so SSO users can still sign in).
|
||||
u = (existing && A.verifyPassword(password, existing.pw_salt, existing.pw_hash)) ? existing : null;
|
||||
if (!u) {
|
||||
const bz = await BZ.validateLogin(email, password);
|
||||
@@ -234,8 +240,11 @@ route('GET', '/api/setup-state', async (req, res) => {
|
||||
});
|
||||
|
||||
// ICE servers for WebRTC. Always includes a public STUN; adds our TURN relay if
|
||||
// configured. TURN_SECRET (coturn use-auth-secret) -> time-limited HMAC credentials
|
||||
// (no permanent password exposed); otherwise static TURN_USERNAME + TURN_CREDENTIAL.
|
||||
// configured. Two credential modes:
|
||||
// - Shared secret (recommended, coturn `use-auth-secret`): set TURN_SECRET and we mint
|
||||
// time-limited credentials per request (no permanent password is ever handed out, so
|
||||
// outsiders can't reuse your relay). Optional TURN_TTL seconds (default 24h).
|
||||
// - Static: set TURN_USERNAME + TURN_CREDENTIAL for a fixed long-term credential.
|
||||
route('GET', '/api/ice', async (req, res) => {
|
||||
const iceServers = [{ urls: 'stun:stun.l.google.com:19302' }];
|
||||
if (process.env.TURN_URLS) {
|
||||
@@ -244,7 +253,7 @@ route('GET', '/api/ice', async (req, res) => {
|
||||
let credential = process.env.TURN_CREDENTIAL || '';
|
||||
if (process.env.TURN_SECRET) {
|
||||
const ttl = parseInt(process.env.TURN_TTL || '86400', 10);
|
||||
username = String(Math.floor(Date.now() / 1000) + ttl);
|
||||
username = String(Math.floor(Date.now() / 1000) + ttl); // coturn expects "<expiry>"
|
||||
credential = require('crypto').createHmac('sha1', process.env.TURN_SECRET).update(username).digest('base64');
|
||||
}
|
||||
iceServers.push({ urls, username, credential });
|
||||
@@ -299,7 +308,8 @@ route('POST', '/api/users', async (req, res) => {
|
||||
if (!u) return json(res, 401, { error: 'unauthorized' });
|
||||
if (u.role !== 'admin') return json(res, 403, { error: 'only admins can add agents' });
|
||||
// With BizGaze as the sole IdP, logins are created in BizGaze, not here (creating local
|
||||
// accounts is what previously shadowed BizGaze and split tenants). Allowed in dev.
|
||||
// accounts is what previously shadowed BizGaze and split tenants). Allowed in dev via
|
||||
// ALLOW_LOCAL_LOGIN=1.
|
||||
if (BZ.isEnabled() && process.env.ALLOW_LOCAL_LOGIN !== '1') return json(res, 400, { error: 'Logins are managed in BizGaze. Add the user there; they appear here on first sign-in.' });
|
||||
const { email, password, name, role } = await readBody(req);
|
||||
if (!email || !password) return json(res, 400, { error: 'email and temporary password required' });
|
||||
|
||||
Reference in New Issue
Block a user