Registration works again — and the /app panel is shut. Those were one bug.
1245 tests pass (7 added). Verified live end-to-end.
Versioning note. Continues the v1 line. The release before v1.0.0 was v13.0.0, which tracked the Laravel major, and Packagist still resolves that as highest — so
composer create-projectgets v13.0.0 unless you pin1.0.3. Thev1tag now points here.
The coupling
CreateNewUser attached every registrant to Team::first() — the merchant's own team, seeded by DefaultTeamSeeder and owned by the admin — then assigned a staff role. The method is named assignOrCreateTeam and its docblock promises "or create a personal team"; neither branch ever existed.
Meanwhile canAccessPanel ended return true; // TODO: Check panel and role, so a user with zero roles and zero teams was waved into /app — Products, Orders, Invoices, Customers.
It only failed to bite because registration threw first: RolesSeeder stopped creating staff in cf711cb without touching the caller, and on an unseeded database Team::first() returned null and fatally errored a line earlier. Fixing registration alone would have opened the door.
What changed
- Registration produces a shopper: a user, no team, no role. Team membership gates
/app, and teams are granted throughTeamPolicy::create(which needs thecreate_storepermission) — handing one out at signup bypassed that gate entirely. canAccessPanelasks the right question per panel:admin→super_admin,app→ team membership. It also now reads$thisrather thanauth()->user(), which authorized the session user instead of the one passed in.- Login and register default to the storefront, using
intended()so a shopper interrupted mid-checkout returns there. Shoppers lose nothing:/orders,/wishlist,/invoices,/payment_methodsare unaffected.
The test that hid it
FortifyActionsTest::makeTeam() created the team and the staff role — with a comment saying so. That is exactly why CI stayed green while every real registration threw. It's gone; those tests still pass.
Known issues
ArticleResource/CollectionResourcehave no policy andstrictAuthorizationis off, so Filament allows: a team member can CRUD their own tenant's articles and collections (price included) without a permission check. No longer reachable by a stranger — that takes a team. Needs policies and seeded permissions together, because the Shield set has noarticle_*/collection_*entries at all.- SVG stored XSS is latent — product upload accepts
image/svg+xmlonto a public disk. Unreachable today (nopublic/storagesymlink); live the momentstorage:linkruns. /user/profilebreaks if social login is enabled —// use HasConnectedAccounts;is commented out inUser, soconnectedAccountsis null and Socialstream calls->map()on it.Password::default()is min 8, no complexity, no breach check.- The seeder sets
inventory_count = 0on every product, so the demo storefront ships sold-out. RoleBasedRedirectis dead code inherited from a property-management app (its six targets all 404), andconfig('permission.teams')isfalseso roles are global despite code reading as team-scoped.
Full changelog: v1.0.2...v1.0.3