Skip to content

feat(auth): gate overleaf behind a service permission - #54

Merged
ExtraToast merged 1 commit into
mainfrom
feat/overleaf-service-permission
Sep 8, 2026
Merged

feat(auth): gate overleaf behind a service permission#54
ExtraToast merged 1 commit into
mainfrom
feat/overleaf-service-permission

Conversation

@ExtraToast

Copy link
Copy Markdown
Contributor

overleaf.jorisjonkers.dev shipped in JorisJonkers-dev/fleet-infra#216 behind the forward-auth middleware, but the host resolved to no ServicePermission — and fromHost returning null makes verify() skip the check entirely:

val requiredPermission = ServicePermission.fromHost(xForwardedHost)
if (requiredPermission != null && !isAuthorizedForService(user.roles, requiredPermission)) {
    return ResponseEntity.status(HttpStatus.FORBIDDEN).build()
}

The practical gate was therefore any authenticated user, not users who were granted it — weaker than every other forward-auth host on the cluster. AuthVerificationControllerTest already asserts that behaviour outright: "unknown subdomain host passes through without permission check".

Why this one has to be enforced here

Overleaf Community Edition has no SSO of its own — SAML, LDAP and OIDC are all Server Pro features. NOTES and HERMES can enforce their grant at the authorization endpoint through their own OIDC client; Overleaf runs no such flow, so this entry is the only per-user gate in front of it.

This is a revocation, not an addition

No existing user holds a SERVICE_OVERLEAF row, and only ROLE_ADMIN bypasses the check. Everyone except admins loses access the moment this deploys. Grant before anyone relies on it:

PUT /api/v1/admin/users/{id}/service-permissions

A corrected comment

The HERMES comment claimed a missing entry meant "every request is denied". It is the reverse — a missing entry allows every authenticated user — and that misreading is exactly what made the overleaf gap easy to miss. Its routes also carry no forward-auth at all, which the comment did not say.

Verification

175/175 tests, including the two new fromHost cases. ktlintCheck and detekt clean.

No migration: user_service_permissions.service is VARCHAR(50) with no check constraint, so a new name needs no schema change. The published OpenAPI spec does not enumerate the permission names, so the contract export is unaffected — confirmed by grepping the spec for existing values rather than assuming.

overleaf.jorisjonkers.dev shipped in fleet-infra#216 behind the
forward-auth middleware, but the host resolved to no ServicePermission --
and fromHost returning null makes verify() skip the check entirely. The
practical gate was therefore "any authenticated user", not "users who
were granted it", which is weaker than every other forward-auth host on
the cluster.

Overleaf Community Edition has no SSO of its own -- SAML, LDAP and OIDC
are all Server Pro features -- so unlike NOTES and HERMES, which enforce
their grant at the authorization endpoint through their own OIDC client,
this entry is the only per-user gate in front of it.

This is a revocation, not an addition. No existing user holds a
SERVICE_OVERLEAF row, and only ROLE_ADMIN bypasses the check, so everyone
except admins loses access the moment it deploys. Grant before anyone
relies on it.

Also corrects the HERMES comment, which claimed a missing entry meant
"every request is denied". It is the reverse -- a missing entry allows
every authenticated user -- and that misreading is exactly what made the
overleaf gap easy to miss.

No migration: user_service_permissions.service is a VARCHAR(50) with no
check constraint, so a new name needs no schema change. The published
OpenAPI spec does not enumerate the permission names either, so the
contract export is unaffected.

Verified: 175/175 tests including the two new fromHost cases, ktlintCheck
and detekt clean.
@ExtraToast ExtraToast added type: feature New user-facing or operator-facing capability. area: auth Authentication, authorization, sessions, or identity. component: security Security, permissions, secrets, or vulnerability handling. labels Sep 8, 2026
@ExtraToast ExtraToast self-assigned this Sep 8, 2026
@ExtraToast
ExtraToast merged commit 33250c9 into main Sep 8, 2026
10 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area: auth Authentication, authorization, sessions, or identity. component: security Security, permissions, secrets, or vulnerability handling. type: feature New user-facing or operator-facing capability.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant