Skip to content

Single sign-on: refuse accounts locked by intruder detection - #4

Merged
jcombs-pointblue merged 1 commit into
mainfrom
fix/sso-intruder-lock
Oct 31, 2026
Merged

jcombs-pointblue merged 1 commit into
mainfrom
fix/sso-intruder-lock

Conversation

@jcombs-pointblue

Copy link
Copy Markdown
Contributor

eDirectory refuses the RFC 4370 proxied-authorization control (-672) for disabled and expired accounts. It accepts it for an account locked by intruder detection. On idm254 (eDirectory 9.3.3), the locked account's own bind returned 53, but a proxied read as it succeeded. So a person locked by intruder detection could still sign in through single sign-on and act in the console.

Change

  • OidcLogin: the service account's search for the person now reads lockedByIntruder and loginIntruderResetTime (it used to ask for no attributes, 1.1). A locked account is refused with the new code locked. The check runs after never-proxy and before no-role.
    • A lock whose loginIntruderResetTime has passed counts as over. eDirectory releases the lock at that time but leaves lockedByIntruder: TRUE until the next login.
    • A reset time that can't be parsed counts as still locked. So does a lock with no reset time.
  • Server.on: when a single sign-on session first reaches another server, it reads the same two attributes on that server, as the service account, before acting as the person there. The answers are 403 locked, 403 no-account if the entry isn't found, or 503 if the read fails.
  • ServiceAccountCheck: the single sign-on check now also checks that the service account can read the two lockout attributes. If it couldn't, the lock check would quietly let everyone through.
  • Sign-in page: added a message for sso-error=locked.
  • Docs: the OidcLogin class comment and docs/DEPLOYING.md §6 now say that locked accounts are refused too.

Tests

  • OidcLoginTest (new): the lock rule. Covers never locked, unlocked, locked with no reset time, a reset time in the future, a reset time in the past, exactly at the reset time, and an unreadable reset time.
  • Phase3Test:
    • Single sign-on refuses lou (locked until 2099) with locked and lets lapsed in (reset time in 2020).
    • New singleSignOnReachesAnotherServerOnlyWhileUnlocked: a second ncpServer whose address points at the fake. It answers 403 locked while the person is locked and 200 once the reset time has passed.
    • alice is no longer locked in the fixture, since she is the single sign-on user. The help desk unlock test now locks her first.
  • mvn -pl console-server -am test: 152 tests, all pass.

Live checks on idm254

  • LivePhase3Check idm254 o=data, extended with a lock and a lapsed lock. All steps pass, including a locked account cannot sign in: /?sso-error=locked and a lock whose reset time has passed is over: /. The service account check now lists uid, lockedByIntruder, loginIntruderResetTime. Its scratch OU and user were deleted (deleted=2).
  • eDirectory's own behaviour after the reset time: I made a scratch OU with intruder lockout on (detectIntruder, lockoutAfterDetection, limit 3, lock reset 60 s) and a scratch user in it. Results:
    • Four bad binds locked the user.
    • A correct bind while locked returned 53 ("login lockout").
    • After the reset time, the entry still showed lockedByIntruder: TRUE with the old reset time.
    • The next correct bind succeeded and cleared the attributes.
    • The OU and user were deleted afterwards; none were left.
  • In a container without lockout (o=data), eDirectory lets a bind through even with lockedByIntruder: TRUE set by hand. The console still refuses that case. eDirectory only sets the attribute where lockout is on, so it should only appear when someone sets it by hand.

Not covered

  • Other-server path not tested live: the Server.on check was tested only with the fake directory. idm254 has one server.
  • Locks during a session: a lock that happens after sign-in isn't noticed by the session's existing connections. It is caught the next time the person signs in or reaches a new server. A password session's bound connection behaves the same way today.

🤖 Generated with Claude Code

eDirectory refuses the proxied-authorization control for disabled and
expired accounts, but accepts it for an account locked by intruder
detection: on the lab (9.3.3) the locked account's own bind returned 53
while a proxied read as it succeeded. So a locked person could sign in
through single sign-on and act in the console.

OidcLogin's search now reads lockedByIntruder and loginIntruderResetTime
and refuses with the new code "locked". A lock whose reset time has
passed counts as over: eDirectory leaves the attribute TRUE after the
reset until the next login (checked on the lab with a real lockout).
A single sign-on session reaching another server (Server.on) checks the
same on that server before acting as the person there. The service
account check covers reading the two attributes, and the live check
LivePhase3Check covers a locked and a lapsed account.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@jcombs-pointblue
jcombs-pointblue merged commit d0ede5b into main Oct 31, 2026
1 check passed
@jcombs-pointblue
jcombs-pointblue deleted the fix/sso-intruder-lock branch October 31, 2026 00:30
jcombs-pointblue added a commit that referenced this pull request Oct 31, 2026
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

# Conflicts:
#	console-server/src/main/java/com/pointblue/console/server/http/Agents.java
#	console-server/src/main/java/com/pointblue/console/server/http/Mcp.java
#	console-server/src/main/java/com/pointblue/console/server/http/Server.java
#	console-server/src/test/java/com/pointblue/console/server/http/AgentsTest.java
#	console-server/src/test/java/com/pointblue/console/server/live/LiveAgentsCheck.java
#	console-web/src/api.ts
#	console-web/src/help/content.ts
#	console-web/src/pages/AgentKeysPage.tsx
#	console-web/src/pages/SettingsPage.tsx
#	docs/API.md
#	docs/MCP.md
#	docs/STATUS.md
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