Commit 7b8ea65
authored
feat: add security.password.enabled for SSO-only deployments (#84)
Follow-up to #83, which noted this gap but did not close it.
## Problem
Enabling Google SSO does not disable password sign-in, and there is no
way to make it exclusive.
- `PasswordlessAuthService.loginWithPassword()` never consults
`security.google.enabled` — no branch, no guard
- `/auth/login` is `permitAll` in `SecurityConfig` unconditionally
- no `security.password.enabled` (or equivalent) exists anywhere
So a deployment that puts every human behind Google Workspace —
domain-allowlisted, `hd`-claim verified — still leaves `/auth/login`
open to every local account. Anyone reasoning "we turned on SSO, so
password login is closed" is wrong, and nothing in the codebase says
otherwise.
This is most acute right after enabling SSO on an install that has seed
or demo accounts: those credentials keep working, internet-facing, with
SSO fully configured.
## Change
Adds `security.password.enabled`, **defaulting to `true`** so existing
installs behave exactly as before.
**`PasswordlessAuthService`**
- Rejects **before** the rate limiter and before any credential
comparison. Nothing needs rate-limiting when the path is closed, and
rejecting early avoids leaking whether an account exists.
- Emits `PASSWORD_LOGIN_FAILURE` with `reason=password_login_disabled`,
so refusals are auditable instead of silent.
- `@PostConstruct` guard logs an **ERROR** when password *and* Google
are both disabled. That combination leaves nobody able to sign in and is
otherwise only discovered at the login screen.
- The class needed `@Slf4j` — it had no logger.
**`SetupController`** — exposes `passwordLoginEnabled` on the public
`/setup/status`, alongside the `googleEnabled` added in #83.
**`Login.jsx`** — hides the password form, drops the now-meaningless
"or" divider, and rewords the subtitle to "Sign in with your work Google
account."
The frontend guard is `passwordLoginEnabled !== false`, deliberately
**not** a truthiness test: `setupStatus` is `null` on first paint and
the field is `undefined` on installs predating this flag. Both must
render the form. Inverting it would strand users on a login page with no
way in whenever the status call was slow or failed.
## Testing
Verified on a live self-host install with
`SECURITY_PASSWORD_ENABLED=false`:
```
/api/setup/status -> "googleEnabled":true,"passwordLoginEnabled":false
POST /api/auth/login -> {"message":"Password sign-in is disabled. Please sign in with Google."}
(real ADMIN account, account_status=ACTIVE — a valid credential is refused, not just a bad one)
/api/auth/google/start -> 302 (unaffected)
startup log -> Password sign-in is DISABLED (security.password.enabled=false); Google SSO only.
security_event -> PASSWORD_LOGIN_FAILURE | {"reason": "password_login_disabled"}
```
Login page renders the Google button alone — no password fields, no
orphaned divider. With the flag unset, the page and the login behaviour
are unchanged.1 parent 8081bb4 commit 7b8ea65
7 files changed
Lines changed: 95 additions & 15 deletions
File tree
- backend/src
- main
- java/com/dbaagent
- controller
- service
- resources
- test/java/com/dbaagent/service
- src
- lib/api
- pages
Lines changed: 13 additions & 2 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
46 | 46 | | |
47 | 47 | | |
48 | 48 | | |
| 49 | + | |
| 50 | + | |
| 51 | + | |
| 52 | + | |
| 53 | + | |
| 54 | + | |
| 55 | + | |
| 56 | + | |
49 | 57 | | |
50 | 58 | | |
51 | 59 | | |
| |||
66 | 74 | | |
67 | 75 | | |
68 | 76 | | |
69 | | - | |
| 77 | + | |
| 78 | + | |
70 | 79 | | |
71 | 80 | | |
72 | 81 | | |
| |||
293 | 302 | | |
294 | 303 | | |
295 | 304 | | |
296 | | - | |
| 305 | + | |
| 306 | + | |
| 307 | + | |
297 | 308 | | |
298 | 309 | | |
299 | 310 | | |
| |||
Lines changed: 46 additions & 0 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
4 | 4 | | |
5 | 5 | | |
6 | 6 | | |
| 7 | + | |
7 | 8 | | |
8 | 9 | | |
| 10 | + | |
9 | 11 | | |
10 | 12 | | |
11 | 13 | | |
| |||
25 | 27 | | |
26 | 28 | | |
27 | 29 | | |
| 30 | + | |
28 | 31 | | |
29 | 32 | | |
30 | 33 | | |
| |||
72 | 75 | | |
73 | 76 | | |
74 | 77 | | |
| 78 | + | |
| 79 | + | |
| 80 | + | |
| 81 | + | |
| 82 | + | |
| 83 | + | |
| 84 | + | |
| 85 | + | |
| 86 | + | |
| 87 | + | |
| 88 | + | |
| 89 | + | |
| 90 | + | |
| 91 | + | |
| 92 | + | |
| 93 | + | |
| 94 | + | |
| 95 | + | |
| 96 | + | |
| 97 | + | |
| 98 | + | |
| 99 | + | |
| 100 | + | |
| 101 | + | |
| 102 | + | |
| 103 | + | |
75 | 104 | | |
76 | 105 | | |
77 | 106 | | |
| |||
87 | 116 | | |
88 | 117 | | |
89 | 118 | | |
| 119 | + | |
| 120 | + | |
| 121 | + | |
| 122 | + | |
| 123 | + | |
| 124 | + | |
| 125 | + | |
| 126 | + | |
| 127 | + | |
| 128 | + | |
| 129 | + | |
| 130 | + | |
| 131 | + | |
| 132 | + | |
| 133 | + | |
| 134 | + | |
| 135 | + | |
90 | 136 | | |
91 | 137 | | |
92 | 138 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
7 | 7 | | |
8 | 8 | | |
9 | 9 | | |
| 10 | + | |
10 | 11 | | |
11 | 12 | | |
12 | 13 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
16 | 16 | | |
17 | 17 | | |
18 | 18 | | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
19 | 22 | | |
20 | 23 | | |
21 | 24 | | |
| |||
Lines changed: 5 additions & 0 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
57 | 57 | | |
58 | 58 | | |
59 | 59 | | |
| 60 | + | |
| 61 | + | |
| 62 | + | |
| 63 | + | |
| 64 | + | |
60 | 65 | | |
61 | 66 | | |
62 | 67 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
3682 | 3682 | | |
3683 | 3683 | | |
3684 | 3684 | | |
3685 | | - | |
| 3685 | + | |
| 3686 | + | |
3686 | 3687 | | |
3687 | 3688 | | |
3688 | 3689 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
98 | 98 | | |
99 | 99 | | |
100 | 100 | | |
| 101 | + | |
| 102 | + | |
| 103 | + | |
| 104 | + | |
| 105 | + | |
| 106 | + | |
101 | 107 | | |
102 | 108 | | |
| 109 | + | |
103 | 110 | | |
104 | 111 | | |
105 | 112 | | |
| |||
148 | 155 | | |
149 | 156 | | |
150 | 157 | | |
| 158 | + | |
151 | 159 | | |
152 | 160 | | |
153 | 161 | | |
| |||
159 | 167 | | |
160 | 168 | | |
161 | 169 | | |
162 | | - | |
163 | | - | |
164 | | - | |
165 | | - | |
166 | | - | |
167 | | - | |
168 | | - | |
169 | | - | |
170 | | - | |
| 170 | + | |
| 171 | + | |
| 172 | + | |
| 173 | + | |
| 174 | + | |
| 175 | + | |
| 176 | + | |
| 177 | + | |
| 178 | + | |
| 179 | + | |
| 180 | + | |
| 181 | + | |
171 | 182 | | |
172 | | - | |
| 183 | + | |
173 | 184 | | |
174 | 185 | | |
175 | 186 | | |
176 | | - | |
| 187 | + | |
177 | 188 | | |
178 | 189 | | |
179 | 190 | | |
| |||
250 | 261 | | |
251 | 262 | | |
252 | 263 | | |
253 | | - | |
| 264 | + | |
| 265 | + | |
| 266 | + | |
254 | 267 | | |
255 | 268 | | |
256 | 269 | | |
| |||
0 commit comments