Skip to content

feat(account): add operator controls for sign-up and guest login#10949

Open
MichaelUray wants to merge 6 commits into
hcengineering:developfrom
MichaelUray:feat/login-capabilities-disable-signup-guest
Open

feat(account): add operator controls for sign-up and guest login#10949
MichaelUray wants to merge 6 commits into
hcengineering:developfrom
MichaelUray:feat/login-capabilities-disable-signup-guest

Conversation

@MichaelUray

Copy link
Copy Markdown

Summary

Two opt-in operator env vars to gate self-service authentication paths that some deployments deliberately don't expose:

  • DISABLE_SIGNUP=true on the account service — keeps the existing method-registry hiding for signUp / signUpOtp, and adds an early Forbidden guard if those operations are invoked directly.
  • DISABLE_GUEST_LOGIN=true on the account service — loginAsGuest rejects with Forbidden before the existing AccountNotFound lookup.

A new getLoginCapabilities account operation exposes both states (plus an availability check for the read-only-guest DB row) so the login UI can hide the corresponding affordances cleanly instead of letting users click into a backend error.

Why

  • Closed-tenant / SSO-only deployments where account creation flows through an external IdP (Authentik, Google Workspace, etc.) want the local Sign Up tab and the Continue-as-guest button gone — not "click then fail".
  • Today the UI already has a DISABLE_SIGNUP env that hides the Sign Up tab via login metadata. This PR adds the matching server-side enforcement so direct API callers can't bypass it.
  • Guest login previously had no operator-facing availability signal. The login form rendered the action even on deployments where the read-only guest row was not seeded, so a click could only end in AccountNotFound.

Behaviour matrix

Env vars (account service) signUp loginAsGuest UI Sign Up tab UI "Continue as guest"
(none set) works works (if guest row exists) visible visible (if guest row exists)
DISABLE_SIGNUP=true disabled; direct calls return 403 Forbidden unchanged hidden unchanged
DISABLE_GUEST_LOGIN=true unchanged 403 Forbidden unchanged hidden
both both rejected both rejected hidden hidden

Default behaviour (neither env set) is identical to today — single-host / open-signup deployments need no action.

What's added

Server (server/account)

  • signUp, signUpOtp: existing method-registry hiding remains when DISABLE_SIGNUP=true; direct operation calls now return Forbidden before DB access.
  • loginAsGuest: early Forbidden guard when DISABLE_GUEST_LOGIN=true. The pre-existing AccountNotFound path remains for the DB-row-missing case.
  • getLoginCapabilities operation: returns { signUpEnabled, guestLoginAvailable }. guestLoginAvailable is true only when (a) the env var is not set, and (b) the read-only-guest person row exists in global_account.person.
  • 6 new tests covering all gates + capability shape.

Account client (packages/account-client)

  • AccountClient.getLoginCapabilities() method added.

Login UI (plugins/login-resources)

  • LoginForm.svelte fetches capabilities on mount, hides the Sign Up tab + guest button accordingly.
  • Safe fallback: if the fetch fails for any reason, both controls stay visible (= today's behaviour).

Security / compatibility

  • Default behaviour is unchanged when neither env var is set.
  • UI hiding is not the enforcement boundary; the account-service guards reject disabled flows server-side.
  • getLoginCapabilities only exposes minimal unauthenticated booleans needed by the login page.
  • If the capability fetch fails, the UI falls back to today's visible controls to preserve backwards compatibility.

Out of scope

  • The existing hasSignUp / getMethods() registry behaviour is preserved. The new in-operation guard keeps direct callers consistent with the advertised disabled state.
  • Guest-row provisioning (creating the read-only guest person) is deployment policy and not part of this PR.

Tests / verification

  • server/account jest: 180 / 180 pass in operations.test.ts + serviceOperations.test.ts (filtered run; full suite has pre-existing CockroachDB-infra failures in postgres-real.test.ts unrelated to this PR).
  • plugins/login-resources jest: 7 / 7 pass (existing suites, UI changes covered by visual review).
  • TypeScript: clean on account, account-client, login-resources.

DCO

Both commits are Signed-off-by Michael Uray. Maintainer edits are enabled.

…inCapabilities

When DISABLE_SIGNUP=true on the account service, the signUp and signUpOtp
operations throw Forbidden — preventing manual sign-ups even via direct
API calls, not just hiding the UI tab.

When DISABLE_GUEST_LOGIN=true, the loginAsGuest operation throws Forbidden
without touching the database. Combined with the existing
AccountNotFound branch (no guest person row), the guest login is fully
controllable via env or DB state.

A new getLoginCapabilities operation lets the client query both flags so
the UI can hide the corresponding affordances. It reports
signUpEnabled (false iff DISABLE_SIGNUP=true) and guestLoginAvailable
(false iff DISABLE_GUEST_LOGIN=true OR no read-only guest person row
exists). The client RPC stub is added to AccountClient as well.

Signed-off-by: Michael Uray <michael.uray@gmail.com>
Signed-off-by: Michael Uray <michaeluray@users.noreply.github.com>
…lities

LoginForm now queries getLoginCapabilities on mount and uses the result
to gate two UI affordances:

- effectiveSignUpDisabled OR-s the existing signUpDisabled prop with
  !signUpEnabled from the server, so the Sign Up tab disappears when
  the account service reports sign-up disabled (DISABLE_SIGNUP=true).
- The "Continue as guest" button is rendered only when
  guestLoginAvailable is true, which the server reports as false when
  either DISABLE_GUEST_LOGIN=true or the read-only guest person row is
  not present in the DB.

fetchLoginCapabilities returns { signUpEnabled: true,
guestLoginAvailable: true } on any RPC error to keep both controls
visible when talking to older account services without the endpoint.

Signed-off-by: Michael Uray <michael.uray@gmail.com>
Signed-off-by: Michael Uray <michaeluray@users.noreply.github.com>
…in-capabilities

Signed-off-by: Michael Uray <michaeluray@users.noreply.github.com>
Signed-off-by: Michael Uray <michaeluray@users.noreply.github.com>
@huly-github-staging

Copy link
Copy Markdown

Connected to Huly®: UBERF-16600

…ilities-disable-signup-guest

Signed-off-by: Michael Uray <michaeluray@users.noreply.github.com>
…DB probing (L-AUTH-1)

getLoginCapabilities ist unauthenticated und traf pro anonymem Aufruf die
DB (db.person.findOne auf den Guest-Account) -> Existenz-Probing + DB-Last.
Guest-Person-Zustand aendert sich praktisch nie: Ergebnis jetzt mit 60s-TTL
modul-lokal gecacht. resetGuestPersonCache() fuer Tests/Ops.

Signed-off-by: Michael Uray <michaeluray@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant