Files
RenewCMS/features/registration_status_endpoint.feature

81 lines
4.9 KiB
Gherkin

Feature: The SPA can ask whether registration is currently open, without exposing anything else
As the maintainer of RenewCMS
I want a public, unauthenticated GET endpoint that reports whether registration is open, driven by
the exact same rule CreateUserUseCase already enforces on POST /v1/auth/register, and that reveals
nothing beyond that single boolean
So that the SPA can decide whether to show a registration form before the user submits anything,
without a second, drifting copy of the "is registration closed" rule and without leaking how many
accounts exist or who they belong to
Background:
Given CreateUserUseCase already has a private registrationClosed() bool method that loops over
user.Repository.GetAll() and returns true as soon as any existing user has IsVerified true; this
slice exports that same check as a new method on CreateUserUseCase (e.g. IsRegistrationOpen() bool,
returning the negation) so it can be called from outside the use case, and CreateUser itself keeps
calling it exactly as before
And this slice adds GET /v1/auth/registration, handled from the same handler package as
POST /v1/auth/register (api/handlers/auth), reusing the same CreateUserUseCase instance the
register handler already holds; no new use case, no new domain method beyond the exported check,
and no second loop over user.Repository.GetAll anywhere in the handler
And GET /v1/auth/registration is mounted in api/router.go's NewAuthRouter at the top level, outside
every r.Group: not inside the jwtauth.Verifier/Authenticator group that guards /verify,
DELETE /register and /me, and not inside either middleware.NewRateLimiter group that guards /login
and /password-reset; no jwt cookie is ever read, parsed or validated for this route
And the handler decodes no request body and reads no cookie and no claim; it calls
CreateUserUseCase.IsRegistrationOpen(), sets the response header Cache-Control: no-store, and
responds 200 with a JSON body containing exactly one field, "open", and nothing else: no user
count, no usernames, no emails, no is_verified values
Scenario: Registration is reported open while no verified account exists
Given the user repository is empty
When the client sends GET /v1/auth/registration with no jwt cookie
Then the response status is 200
And the response header Cache-Control is "no-store"
And the response body is exactly {"open": true}
Scenario: Registration is reported open while every existing account is unverified
Given the user repository contains a user "alice" with IsVerified false
When the client sends GET /v1/auth/registration with no jwt cookie
Then the response status is 200
And the response body is exactly {"open": true}
Scenario: Registration is reported closed once a verified account exists
Given the user repository contains a user "alice" with IsVerified true
When the client sends GET /v1/auth/registration with no jwt cookie
Then the response status is 200
And the response header Cache-Control is "no-store"
And the response body is exactly {"open": false}
Scenario: An expired, forged, or otherwise invalid jwt cookie makes no difference to the response
Given the user repository contains a user "alice" with IsVerified true
And the client holds a jwt cookie that is expired, forged, or otherwise invalid and would be
rejected by jwtauth.Authenticator on any protected route
When the client sends GET /v1/auth/registration with that cookie attached
Then the response status is 200
And the response body is exactly {"open": false}, the same as a request with no cookie at all
And the response does not set or clear any cookie
And this proves the route never even attempts to parse or validate the cookie, not merely that a
valid one would be ignored
Scenario: The endpoint reveals nothing beyond the open flag
Given the user repository contains three users, two verified and one unverified
When the client sends GET /v1/auth/registration
Then the response body's only top-level field is "open"
And the response body contains none of: a user count, a username, an email, or an is_verified value
Scenario: The route is never rate-limited, unlike login and password reset
Given the user repository is empty
When the client sends GET /v1/auth/registration 20 times in immediate succession from the same
client
Then every response status is 200
And none of the responses is 429
Scenario: The same rule CreateUser enforces is what answers this endpoint
Given a user.Repository fake containing one user with IsVerified true
And a CreateUserUseCase built directly over that fake, a hasher and a mail.Repository fake
When IsRegistrationOpen is called
Then it returns false
And calling CreateUser on the same use case with a new username/email/password returns
user.ErrRegistrationClosed, asserted with errors.Is