BarberSync 2.0: Control Plane SaaS e entitlements modulares - #241
Conversation
Codex Review SummaryThis comment shows the latest Codex review activity on this pull request.
Security findingsAdvisory findings (1)ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings. |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: cea9c78571
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| INSERT INTO barber.tenants(id,slug,name,institutional_email) VALUES(@tenant_id,lower(@slug),@tenant_name,lower(@email)) ON CONFLICT DO NOTHING; | ||
| INSERT INTO barber.branches(id,tenant_id,name,code) VALUES(@branch_id,@tenant_id,@branch_name,upper(@branch_code)) ON CONFLICT DO NOTHING; | ||
| INSERT INTO barber.users(id,tenant_id,branch_id,email,password_hash,full_name) | ||
| VALUES(@user_id,@tenant_id,@branch_id,lower(@email),@password_hash,@full_name); | ||
| INSERT INTO barber.user_roles(user_id,role_id) VALUES(@user_id,@superadmin_role),(@user_id,@owner_role); | ||
| INSERT INTO barber.audit_logs(id,tenant_id,branch_id,user_id,operation,entity_name,entity_id,correlation_id,module,action,description) | ||
| VALUES(@audit_id,@tenant_id,@branch_id,@user_id,'FirstAdminCreated','users',@user_id,@correlation_id,'Setup','FirstAdminCreated','Primeiro administrador criado com segurança.'); | ||
| VALUES(@user_id,@tenant_id,@branch_id,lower(@email),@tenant_password_hash,@full_name); | ||
| INSERT INTO barber.user_roles(user_id,role_id) VALUES(@user_id,@owner_role); |
There was a problem hiding this comment.
Provision a base subscription during first-admin setup
On a fresh installation, script_completo.sql backfills the legacy subscription before the documented first-admin endpoint creates this tenant, but this transaction creates no tenant_subscriptions row. Because PostgresModuleEntitlementService.GetEffectiveAccessAsync cross-joins the tenant subscription, the new Owner receives no module claims and all module-gated routes reject the account immediately after setup; create an initial subscription in this transaction.
Useful? React with 👍 / 👎.
| ("/api/scheduling",SaasModuleKeys.Scheduling,"Scheduling.Read"), | ||
| ("/api/service-execution",SaasModuleKeys.ServiceExecution,"ServiceExecution.Read"), |
There was a problem hiding this comment.
Gate the actual operation API prefixes
The middleware gates /api/scheduling and /api/service-execution, but the still-exposed operational controllers use prefixes such as /api/appointments (AppointmentsController.cs:8), /api/service-orders (ServiceOrdersController.cs:8), /api/payments, and /api/cash-registers. A tenant whose plan excludes these modules but whose role retains the corresponding permissions can call those routes directly and bypass module entitlement enforcement, so all equivalent API prefixes must be mapped or annotated.
Useful? React with 👍 / 👎.
| if (currentUser.IsPlatformUser && currentUser.ScopeSessionId.HasValue) | ||
| { | ||
| return; |
There was a problem hiding this comment.
Revalidate scoped sessions before bypassing permissions
This bypass trusts only claims embedded in the scoped JWT and never checks whether the referenced platform_scope_sessions row remains active. After EndAsync marks a scope as Ended, a retained scoped token can therefore continue invoking permission-protected routes not covered by the module middleware—such as /api/appointments—until the JWT expires; validate the exact scope-session ID and its status/expiry before granting the bypass.
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
💡 Codex Security Review
Here are some automated security review suggestions for this pull request.
Reviewed commit: cea9c78571
ℹ️ About Codex security reviews in GitHub
This is an experimental Codex feature. Security reviews are triggered when:
- You comment "@codex security review"
- A regular code review gets triggered (for example, "@codex review" or when a PR is opened), and you’re opted in so security review runs alongside code review
Once complete, Codex will leave suggestions, or a comment if no findings are found.
| await setupLock.ExecuteNonQueryAsync(cancellationToken); | ||
|
|
||
| await using (var activeUsers = new NpgsqlCommand("SELECT EXISTS(SELECT 1 FROM barber.users WHERE is_active AND deleted_at IS NULL)", connection, transaction)) | ||
| await using (var activeUsers = new NpgsqlCommand("SELECT EXISTS(SELECT 1 FROM barber.platform_users WHERE is_active AND deleted_at IS NULL)", connection, transaction)) |
There was a problem hiding this comment.
Security: Preserve completed bootstrap state during upgrades
On upgrades from the parent revision, existing tenant users no longer disable this anonymous endpoint: the migration creates platform_users without backfilling it, so this query returns false until a trusted operator bootstraps again. If the API is reachable in that window, one unauthenticated request with an unused tenant/email creates the caller's platform user and assigns SuperAdmin, which receives every control-plane permission and can scope into existing tenants. Keep prior installations closed and provision the first platform identity through an authenticated migration or one-time deployment secret.
Useful? React with 👍 / 👎.
Implementado
InMemorySaasStoree serviços SaaS de produção dependentes de memóriaValidação
dotnet restore BarberSync.slndotnet build BarberSync.sln --no-restore: aprovadodotnet test BarberSync.sln --no-build: 68/68 aprovados, incluindo integração PostgreSQLScriptsSQL/script_completo.sql: três execuções consecutivas comON_ERROR_STOP=1node --check, contratos de readiness, source integrity e UI contracts: aprovadosObservação ambiental
O readiness Docker completo não pôde avançar porque o daemon do Docker Desktop não estava disponível. Os gates estáticos, build, testes e replay SQL foram executados por alternativas locais reais.