getNodesOfText, createNode, and bulkCreateNodes now require
the session user, look up the target text, and respond 403
unless the user owns the text or is an admin. paves the way
for moving these endpoints out of the admin-only group.
open POST /api/texts and node create endpoints to any
authenticated user; expose new /texts and /texts/{id} pages
plus admin-only GET /api/texts/all. ViewController gains
userTexts and userText methods. seed gives Tanach to the
regular user and adds a second non-admin user.
TextRepository gains findByUser; JsonTextRepository and the
fake implement filtering by stored userId. TextController
splits the list endpoint into getMyTexts (own) and
getAllTexts (admin), and getText now requires the session
user, returning 403 to non-owners while admins bypass.
drop UserRepository dependency; controller now passes the
authenticated User directly via CreateTextRequest, eliminating
a redundant repository lookup.
Forcing every call site to be explicit about admin status and
password eliminates a class of bugs where an unintended
isAdmin=false or empty passwordHash could silently slip through.
The CreateUserTest case that asserted the isAdmin default is
dropped since the default no longer exists.
Replace direct password_hash and password_verify calls with the
injected PasswordHasher so the bcrypt cost can be substituted out
in tests. Production wiring is handled by the container's autowiring
of BcryptPasswordHasher.
This commit alone breaks the test suite because the existing tests
construct these use cases without the new dependency; the next
commit restores green by introducing FakePasswordHasher.
Introduce an injectable abstraction over password_hash and
password_verify so callers can be swapped for a fast fake in tests
without paying bcrypt's CPU cost. The bcrypt implementation is a
direct passthrough using PASSWORD_DEFAULT, matching the prior inline
behavior, so existing stored hashes continue to verify.
Wired into the DI container alongside the other auth primitives
(Clock, TokenGenerator). No callers reference it yet, so production
behavior is unchanged.