- PermissionSet reads the stored JSON tolerantly and writes an object
of integers; FrontendPermission is the catalogue row
- MergedPermissions, HasPermission and UserHasPermission port PHP
getMergedPermissions and Winter's hasPermission: groups merge with
most positive wins, the user's own values override, held means
exactly 1, wildcards as in Winter
- User gains Permissions (never serialized) and declares the avatar
attachment relation the user API already writes
- the list opens the preview, which shows one status hint (banned,
deactivated, not activated, suspended) and the IP address fields
- bulk actions activate, deactivate, restore, ban and unban; record
actions activate, unban and unsuspend, offered while they apply
- delete (form button and bulk) removes the user for good with the
throttle rows, group memberships and attachments in one transaction;
blobs go after the commit
- a privileged-group member is still refused, a bulk delete as a whole
- admin harness: boots the plugin with the admin API mounted and
mints backend administrators with chosen permissions
- TestAdminUsersTracer and TestAdminPrivilegedMember
- permissions (the four Winter codes and manage_privileged_groups),
the user navigation item and the embedded admin YAML
- Users controller: list with deactivated users, row states, the
groups, created date and activated filters, and the form's name,
surname and email
- a member of a group in golem15.user.privileged_groups (default
admin) keeps their email, password and row unless the administrator
holds golem15.users.manage_privileged_groups; the refusal is a 403
that saves nothing
- users.permissions and users.last_seen as additive columns
- golem15_user_frontend_permissions (code, label, tab, comment)
- User gains LastSeen, read-only CreatedAt and UpdatedAt and the
filterByGroup list scope; none of them is serialized
- migration tests roll back by position past the three new steps
- RegisterUser: nil handle, validation refusals that write nothing, the
activation and IP options, the configured bcrypt cost, input untouched
- FireRegisterEvent: payload without password keys, listener error
returned, nil app and nil fields
- IssueToken: issuer from the request URL and X-Forwarded-Proto, nil
user, nil request and a missing JWT secret refused
- APIArray: listener merge, listener error, nil user
- RegisterEvent.Payload: a copy of the register input without password and
password_confirmation (Winter's golem15.user.register data array)
- RegisterUser, RegisterOptions, FireRegisterEvent, IssueToken and APIArray:
the steps POST /register runs, now called by Register itself; its status
codes and bodies are unchanged
- README documents the exports and Payload
- UserGroupCodes and HasGroupCode against Postgres: id order, code-less
groups skipped, membership by code, a missing handle and a failing query
reported as errors
- mail locale resolution, hook registration (a failing hook stops the
chain), the code lifetimes and throttle settings carried on the context,
constant-time code comparison, JWT secret loading and the principal
lookup without an app or a gorm handle
- user_groups (seeded with the guest and registered groups) and the
users_groups pivot, folded from the PHP v1.1.1 and v2.8.0 migrations
into migration 202610020001_create_user_groups
- models.UserGroup and a User.Groups relation that is never serialized
- classes.UserGroupCodes and classes.HasGroupCode for server-side checks
such as the site-admin group code admin
- additive only: no route, payload or existing column changes; the user
payload still answers "groups":[]
GOWORK=off go mod tidy records the framework's transitive tidwall/*
requirements as indirect and adds the missing go.sum lines. No direct
requirement changes.
Prefix replacement of the old application-nested module path in go.mod
and every import. No identifier, string, table, migration ID, config key
or comment changes.
- go mod tidy in the app and plugin modules for River v0.47.0
- allow-list summer_jobs and the River v7 tables in the schema diff
- expect summer_migrations_summercms_conga among the history tables
- TestPhase8RedOAuthClientCommand drives the not-implemented command
stub's create path and fails with PHASE8_RED:oauth-command until the
real create/update/list behavior lands (D-19)
- oauth_client.go stub declares the exact PHP-parity flag/argument
contract (name, --redirect-uri=*, --scope=*, --auth-method,
--client-id, --list) so the RED test exercises the real command shape
- user/console_test.go's emailArg gains Flags(string) []string: bonfire's
Input interface (summercms.go) grew this method for D-19's repeatable
flags, and emailArg must satisfy it to keep building
Unit tests now expect Winter 500 HTML when the target user is already
active, and the inventory assertions match 169/22/147.
Co-authored-by: Cursor <cursoragent@cursor.com>
Match PHP payloads, already-activated Winter 500 HTML, and direct-DB
seeding so the corpus can flip those routes to ported without onboarding.
Co-authored-by: Cursor <cursoragent@cursor.com>
Register through logout is one in-process flow, and token mint accepts read, write, ai, and the two-scope combination.
Co-authored-by: Cursor <cursoragent@cursor.com>
Reset and activation codes expire on a TTL, and a missing issued-at still accepts a cutover code. Forgot-password always returns the same 200 body.
Co-authored-by: Cursor <cursoragent@cursor.com>
Registration follows activate_mode and hides a disabled or throttled cause in production. Fonoteka adds organisation fields through GetApiArrayEvent.
Co-authored-by: Cursor <cursoragent@cursor.com>
Wire the user API session loop to the JWT blacklist and a pre-password throttle gate so a logged-out token cannot be fetched again.
Co-authored-by: Cursor <cursoragent@cursor.com>
- Verify inv_ tokens against ApiToken hash, expiry, revocation, last-used
- InvScope writes PHP TokenScope 401/403 bodies and gates on HasScope
- Register jwt and inv_token on the shared bouncer.Registry at plugin Boot
- Move models, classes, controllers, middleware, and updates into subpackages
- Add self-registering models/updates/classes registries so later plans add files
- Keep the genres route and shipped migration SQL unchanged
golem15.user registers jwt.auth, golem15.fonoteka mounts GET /_fonoteka/api/v1/genres
through that guard, and a smoke test reads the 15 migrated rows with a test-only token.
Co-authored-by: Cursor <cursoragent@cursor.com>
Add compiled golem15.user and golem15.fonoteka plugins, squash schema
to the genre slice tables, and seed the 15 canonical genres on ICU pl-PL.
Co-authored-by: Cursor <cursoragent@cursor.com>