An org without the app must not accrue achievements either

FixSecurityService
Shipped
September 9, 2026 at 5:23 AM UTC
Author
Kamo
Commit
8c3b971

The award gate asked for VIEW_ACHIEVEMENTS and stopped there. Rights are not entitlement-filtered, so that right stays granted — and keeps answering true — in an organization whose plan never included Gamification. Five orgs with no org_features row for the app had members accumulating badges behind a nav that correctly hid every way of seeing them: navRegistry asks `organization.isGamification && checkRight(...)`, and the awarding code asked neither half of that. Both halves now, in the two places that can answer them: * AppAvailabilityInterceptor gains /api/security/achievements -> GAMIFICATION, which covers every request a browser makes. Deliberately not **************** that seam carries the cluster secret and no session, so there is no org for the gate to resolve. * GamificationEntitlement covers what a path prefix cannot — four of the five award seams are not member HTTP requests at all. Modelled on MediaService's GamesEntitlement, routed through isAppEffectivelyEnabled, which is the one right answer: applied model, org_features row and subscription matrix together. Any one read alone gives a different and wrong answer. The right is checked before the app: one indexed lookup ahead of a model and subscription resolution, on a path whose caller is sometimes the endpoint that keeps members signed in. Registration is now derived from PREFIX_MAP rather than typed out a second time in WebMvcConfig. Two lists were required and neither could fail loudly — a prefix in the map alone is never routed through the interceptor, and a path registered alone resolves no ServiceType. Both read as a working gate in source. Registering each prefix bare as well as with /** closes a related hole: /** does not match the collection endpoint, so GET /api/security/leads had never once run the gate's matcher. AppAvailabilityGateWiringTest reads the patterns back off a real InterceptorRegistry, so it fails on drift rather than comparing the map to something derived from it.

All changes

Like what you see shipping?

Every one of these updates lands in your workspace automatically. Start free and watch it grow week after week.

Start Free ForeverView Pricing