-
Notifications
You must be signed in to change notification settings - Fork 9
fix(admin): hide administration without cached permission evidence #487
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
6 commits
Select commit
Hold shift + click to select a range
4eb5190
fix(admin): cache permissions and hide unauthorized administration
veryCrunchy 6b25bcb
chore(website): refresh marketing captures
obiente-automations[bot] f9b3816
fix(admin): stop hidden polling and preserve permission recovery
veryCrunchy 2a8658d
chore(website): refresh marketing captures
obiente-automations[bot] 471cfd7
fix(admin): pause hidden polling and retain settings selection
veryCrunchy a168863
chore(website): refresh marketing captures
obiente-automations[bot] File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,7 @@ | ||
| category: fix | ||
| issue: 187 | ||
| pull: none | ||
| platforms: android, desktop | ||
| user-facing: yes | ||
|
|
||
| Hide Administration and Server apps unless the signed-in account has freshly verified access. Cache permission checks for five minutes within the session, and prevent stale responses or restored navigation from exposing admin controls. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
131 changes: 131 additions & 0 deletions
131
ui/src/commonMain/kotlin/dev/obiente/nextcloudnative/app/AdminAppsScreen.kt
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,131 @@ | ||
| package dev.obiente.nextcloudnative.app | ||
|
|
||
| import androidx.compose.foundation.layout.Column | ||
| import androidx.compose.foundation.layout.fillMaxSize | ||
| import androidx.compose.foundation.layout.safeDrawingPadding | ||
| import androidx.compose.material3.AlertDialog | ||
| import androidx.compose.material3.Button | ||
| import androidx.compose.material3.ButtonDefaults | ||
| import androidx.compose.material3.MaterialTheme | ||
| import androidx.compose.material3.Text | ||
| import androidx.compose.material3.TextButton | ||
| import androidx.compose.runtime.Composable | ||
| import androidx.compose.runtime.getValue | ||
| import androidx.compose.runtime.mutableStateOf | ||
| import androidx.compose.runtime.remember | ||
| import androidx.compose.runtime.setValue | ||
| import androidx.compose.ui.Modifier | ||
|
|
||
| internal sealed interface AdminAppsContent { | ||
| data object Checking : AdminAppsContent | ||
| data class Catalog(val catalog: NativeAppCatalog) : AdminAppsContent | ||
| enum class Failure(val message: String) : AdminAppsContent { | ||
| Forbidden("This account does not have permission to manage server apps. You can return to Settings."), | ||
| Unavailable("Administrator access could not be verified. Try again, or return to Settings."), | ||
| InvalidResponse("The server returned an unexpected response while checking administrator access. Try again, or return to Settings."), | ||
| Expired("Administrator access needs to be checked again before server apps can be shown."), | ||
| } | ||
| } | ||
|
|
||
| internal fun adminAppsContent(state: AdministrationAccessState, canAdminister: Boolean): AdminAppsContent { | ||
| if (state.checking) return AdminAppsContent.Checking | ||
| return when (val result = state.result) { | ||
| null -> AdminAppsContent.Checking | ||
| is NativeAppCatalogResult.Available -> if (canAdminister && state.canAdminister) { | ||
| AdminAppsContent.Catalog(result.catalog) | ||
| } else { | ||
| AdminAppsContent.Failure.Expired | ||
| } | ||
| NativeAppCatalogResult.Forbidden -> AdminAppsContent.Failure.Forbidden | ||
| NativeAppCatalogResult.Unavailable -> AdminAppsContent.Failure.Unavailable | ||
| is NativeAppCatalogResult.InvalidResponse -> AdminAppsContent.Failure.InvalidResponse | ||
| } | ||
| } | ||
|
|
||
| @Composable | ||
| internal fun AdminAppsScreen( | ||
| access: AdministrationAccessController, | ||
| onContinueInBrowser: () -> Unit, | ||
| serverInfo: NextcloudServerInfo?, | ||
| onOpenApp: (NextcloudAppEntry) -> Unit, | ||
| onBack: () -> Unit, | ||
| ) { | ||
| var search by remember { mutableStateOf("") } | ||
| var catalogFilter by remember { mutableStateOf(NativeAppCatalogFilter.All) } | ||
| var pendingLifecycleAction by remember(access.state.result) { | ||
| mutableStateOf<Pair<NativeManagedApp, NativeAppLifecycleAction>?>(null) | ||
| } | ||
| val content = adminAppsContent(access.state, access.canAdminister) | ||
|
|
||
| Column(modifier = Modifier.fillMaxSize().safeDrawingPadding()) { | ||
| ScreenHeader( | ||
| title = "Server apps", | ||
| subtitle = "Administrator app management", | ||
| onBack = onBack, | ||
| ) | ||
| when (content) { | ||
| AdminAppsContent.Checking -> LoadingMessage("Checking administrator access...") | ||
| is AdminAppsContent.Catalog -> NativeAppCatalogSurface( | ||
| catalog = content.catalog, | ||
| query = search, | ||
| filter = catalogFilter, | ||
| onQueryChanged = { search = it }, | ||
| onFilterChanged = { catalogFilter = it }, | ||
| onOpenInstalledApp = { managed -> | ||
| serverInfo?.apps?.firstOrNull { app -> app.id == managed.id }?.let(onOpenApp) | ||
| }, | ||
| onLifecycleAction = { app, action -> | ||
| if (access.canAdminister) pendingLifecycleAction = app to action | ||
| }, | ||
| ) | ||
| is AdminAppsContent.Failure -> ErrorMessage( | ||
| content.message, | ||
| onRetry = access.refresh, | ||
| ) | ||
| } | ||
| } | ||
|
|
||
| pendingLifecycleAction?.takeIf { content is AdminAppsContent.Catalog }?.let { (app, action) -> | ||
| AlertDialog( | ||
| onDismissRequest = { pendingLifecycleAction = null }, | ||
| title = { Text("${action.uiLabel()} ${app.name}?") }, | ||
| text = { | ||
| Text( | ||
| when (action) { | ||
| NativeAppLifecycleAction.InstallAndEnable -> | ||
| "This downloads server-side code and enables the app for users." | ||
| NativeAppLifecycleAction.Enable -> | ||
| "This activates the app and may add navigation, jobs, and integrations for users." | ||
| NativeAppLifecycleAction.Disable -> | ||
| "This makes the app and its integrations unavailable until an administrator enables it again." | ||
| NativeAppLifecycleAction.Update -> | ||
| "The server may enter maintenance mode while the app package is updated. Do not interrupt it." | ||
| NativeAppLifecycleAction.Uninstall -> | ||
| "This removes the app package. App data retention depends on the app and is not guaranteed." | ||
| } + "\n\nNextcloud requires administrator password confirmation. Continue in the authenticated server administration page.", | ||
| ) | ||
| }, | ||
| dismissButton = { | ||
| TextButton(onClick = { pendingLifecycleAction = null }) { Text("Cancel") } | ||
| }, | ||
| confirmButton = { | ||
| Button( | ||
| onClick = { | ||
| pendingLifecycleAction = null | ||
| if (access.canAdminister) { | ||
| onContinueInBrowser() | ||
| access.refresh() | ||
| } | ||
| }, | ||
| colors = if (action == NativeAppLifecycleAction.Uninstall) { | ||
| ButtonDefaults.buttonColors(containerColor = MaterialTheme.colorScheme.error) | ||
| } else { | ||
| ButtonDefaults.buttonColors() | ||
| }, | ||
| ) { | ||
| Text("Continue in browser") | ||
| } | ||
| }, | ||
| ) | ||
| } | ||
| } |
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
On a two-pane Settings layout, every five-minute revalidation temporarily makes
canAdministerfalse because the repository clears its prior result while checking. Filtering Administration here then resolves the saved selection to Account, and the existingLaunchedEffectat lines 268-271 writes that fallback intoselectedSectionName; after a successful check, the administrator is therefore left on Account instead of returning to the section they were using. The same overwrite loses a restored Administration selection during the initial post-recreation check, so retain the requested selection while permission is merely unknown/checking.AGENTS.md reference: AGENTS.md:L457-L459
Useful? React with 👍 / 👎.