fix(profiles): gate picker management affordances on server authorization - #220
fix(profiles): gate picker management affordances on server authorization#220Daicaa wants to merge 1 commit into
Conversation
…tion The Who's watching picker showed Add Profile and Manage Profiles to every account, but the server only authorizes profile management when the acting profile is the household primary or the account is admin. At the picker the phone app keeps the acting profile across Switch Profile while TV clears it, so on TV and at first login only the admin arm can hold — every other account got the server's raw 403 after already filling in the create form. Gate both pickers on a shared canManageProfilesFromPicker(user, activeProfile) predicate mirroring the server rule. The add tile stays for an empty grid: the server exempts first-profile bootstrap and the picker renders that state itself. The grant is resolved with the grid under the same identity-scope capture, dropped on scope changes, and revoking it also exits manage mode and closes any pending delete dialog.
📝 WalkthroughWalkthroughProfile picker management now uses a shared authorization rule on Android and Android TV. ViewModels resolve the current user, update management state during profile loading, clear protected state when access changes, and gate management and add-profile controls. ChangesProfile picker authorization
Estimated code review effort: 3 (Moderate) | ~25 minutes Sequence Diagram(s)sequenceDiagram
participant ProfileSelectionScreen
participant ProfileSelectionViewModel
participant AuthRepository
participant ProfileRepository
ProfileSelectionScreen->>ProfileSelectionViewModel: request profile load
ProfileSelectionViewModel->>AuthRepository: resolve current user
ProfileSelectionViewModel->>ProfileRepository: load profiles and active profile
ProfileSelectionViewModel->>ProfileSelectionScreen: expose management and add permissions
Possibly related PRs
Suggested reviewers: 🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (1)
shared/src/commonTest/kotlin/org/siloserver/silo/model/auth/AdminPermissionsTest.kt (1)
63-96: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low valueUse
camelCasetest function names.The new Kotlin test function identifiers use backticked prose names instead of
camelCase.
shared/src/commonTest/kotlin/org/siloserver/silo/model/auth/AdminPermissionsTest.kt#L63-L96: rename the added test functions tocamelCase.androidApp/src/androidUnitTest/kotlin/org/siloserver/silo/android/ui/screens/profiles/ProfileSelectionManagementGatingTest.kt#L48-L181: rename the added test functions tocamelCase.androidTvApp/src/androidUnitTest/kotlin/org/siloserver/silo/tv/ui/screens/profiles/TvProfileSelectionManagementGatingTest.kt#L48-L160: rename the added test functions tocamelCase.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@shared/src/commonTest/kotlin/org/siloserver/silo/model/auth/AdminPermissionsTest.kt` around lines 63 - 96, Rename the added backticked test functions to camelCase identifiers in shared/src/commonTest/kotlin/org/siloserver/silo/model/auth/AdminPermissionsTest.kt lines 63-96, androidApp/src/androidUnitTest/kotlin/org/siloserver/silo/android/ui/screens/profiles/ProfileSelectionManagementGatingTest.kt lines 48-181, and androidTvApp/src/androidUnitTest/kotlin/org/siloserver/silo/tv/ui/screens/profiles/TvProfileSelectionManagementGatingTest.kt lines 48-160; preserve each test’s behavior and coverage.Source: Coding guidelines
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In
`@androidApp/src/androidMain/kotlin/org/siloserver/silo/android/ui/screens/profiles/ProfileSelectionViewModel.kt`:
- Line 108: Clear canManageProfiles, isManageMode, and deleteDialogProfile at
the start of the load flow in ProfileSelectionViewModel before
currentUserProvider() runs. Apply the equivalent reset in
TvProfileSelectionViewModel, clearing canManageProfiles, isManageMode, and
deleteCandidate when loading starts.
---
Nitpick comments:
In
`@shared/src/commonTest/kotlin/org/siloserver/silo/model/auth/AdminPermissionsTest.kt`:
- Around line 63-96: Rename the added backticked test functions to camelCase
identifiers in
shared/src/commonTest/kotlin/org/siloserver/silo/model/auth/AdminPermissionsTest.kt
lines 63-96,
androidApp/src/androidUnitTest/kotlin/org/siloserver/silo/android/ui/screens/profiles/ProfileSelectionManagementGatingTest.kt
lines 48-181, and
androidTvApp/src/androidUnitTest/kotlin/org/siloserver/silo/tv/ui/screens/profiles/TvProfileSelectionManagementGatingTest.kt
lines 48-160; preserve each test’s behavior and coverage.
🪄 Autofix
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro Plus
Run ID: e4a44c73-1a97-43b9-9431-646413219d41
📒 Files selected for processing (10)
androidApp/src/androidMain/kotlin/org/siloserver/silo/android/di/AndroidModule.ktandroidApp/src/androidMain/kotlin/org/siloserver/silo/android/ui/screens/profiles/ProfileSelectionScreen.ktandroidApp/src/androidMain/kotlin/org/siloserver/silo/android/ui/screens/profiles/ProfileSelectionViewModel.ktandroidApp/src/androidUnitTest/kotlin/org/siloserver/silo/android/ui/screens/profiles/ProfileSelectionManagementGatingTest.ktandroidTvApp/src/androidMain/kotlin/org/siloserver/silo/tv/di/AndroidTvModule.ktandroidTvApp/src/androidMain/kotlin/org/siloserver/silo/tv/ui/screens/profiles/TvProfileSelectionScreen.ktandroidTvApp/src/androidMain/kotlin/org/siloserver/silo/tv/ui/screens/profiles/TvProfileSelectionViewModel.ktandroidTvApp/src/androidUnitTest/kotlin/org/siloserver/silo/tv/ui/screens/profiles/TvProfileSelectionManagementGatingTest.ktshared/src/commonMain/kotlin/org/siloserver/silo/model/auth/AdminPermissions.ktshared/src/commonTest/kotlin/org/siloserver/silo/model/auth/AdminPermissionsTest.kt
Problem
Part of #219
The "Who's watching?" picker shows Add Profile and Manage Profiles to every
account, but the server only authorizes profile management for an active
primary profile or an admin account. At the picker no profile is active, so
for every non-admin account these affordances can only end in a 403 — shown
as the server's raw error string after the user has already filled in the
create form.
Approach
canManageProfilesFromPicker(user, activeProfile) = role == admin || activeProfile.isPrimarybesideisActingAdmin, mirroringBOTH arms of the server rule. This matters because the two platforms reach
the picker differently: phone keeps the acting profile across "Switch
Profile" (a non-admin owner acting as primary IS authorized there — and the
phone picker is the app's only management surface), while TV clears it, so
on TV and at first login only the admin arm can hold. Fails closed when
neither input resolves.
authorized; the Add tile stays for an EMPTY grid regardless of role — the
server exempts first-profile bootstrap, and the picker renders the empty
state itself.
AuthRepository.getCurrentUser()through a constructorseam mirroring AdminEntryViewModel's pattern; the grant is resolved together
with the grid under the same identity-scope capture, dropped on scope
changes/logout, and re-evaluated on every resume. Revoking it also exits
manage mode and closes any pending delete-confirmation dialog.
decision left to the maintainers (see issue).
fetch before the profile list; a transient /me failure hides an admin's
affordances until the next resume (fail closed, self-heals).
Testing
Built and unit-tested for real (JDK 21 + SDK platform 36); excerpts:
No emulator/device screenshots included — my build host has no KVM, so an
emulator run wasn't practical. Happy to add screenshots if wanted; the change
is limited to hiding two affordances behind the new predicate.
AI Disclosure
design; I reviewed the diff and ran the builds/tests myself)
draft found a BLOCKING design bug: the original predicate was admin-only,
assuming no acting profile exists at the picker — but the phone app keeps
the acting profile across Switch Profile, so the gate would have removed
server-authorized profile management from every non-admin household owner
(and the phone picker is the only management surface). Fixed by modeling
both server arms. The review also caught the delete-confirmation dialog
surviving a permission downgrade, and manage-mode reset asymmetries between
the phone/TV reset paths — all fixed, each with a regression test. Verified
sound by the review: first-profile bootstrap, identity-scope/race handling,
TV grid arithmetic, existing-caller compatibility, and that the tests catch
an inverted predicate.
Checklist
has no lint config beyond compiler warnings — none new).
Summary by CodeRabbit
New Features
Tests