81 lines
4.9 KiB
Gherkin
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
|