-
Notifications
You must be signed in to change notification settings - Fork 5
fix(android): reject self-provider picker roots #446
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
base: fix/durable-upload-scheduling-recovery-stack
Are you sure you want to change the base?
Changes from all commits
e8c0717
65b0aff
7bcfee5
af7d7bc
75da6a5
b5c8ccb
32925b6
6d34a92
8a45c14
472976e
ef91456
8e3b47b
4fb89d4
2fa9a3d
70a4c3e
bd11db7
923cf9f
7f93ecb
606098f
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -30,6 +30,33 @@ internal suspend fun <Result> withAndroidAccountRemovalLease( | |
| action = action, | ||
| ) | ||
|
|
||
| internal suspend fun <Result> withPreparedAndroidAccountRemovalLease( | ||
| accountIdentity: String, | ||
| guard: AndroidAccountOperationGuard = ANDROID_ACCOUNT_OPERATION_GUARD, | ||
| prepare: suspend () -> Unit, | ||
| revalidate: suspend () -> Unit, | ||
| action: suspend () -> Result, | ||
| ): Result { | ||
| prepare() | ||
| return withAndroidAccountRemovalLease(accountIdentity, guard) { | ||
| revalidate() | ||
| action() | ||
| } | ||
| } | ||
|
|
||
| internal suspend fun <Result> withUnavailableAndroidAccountRemovalLease( | ||
| accountIdentity: String, | ||
| guard: AndroidAccountOperationGuard = ANDROID_ACCOUNT_OPERATION_GUARD, | ||
| preflight: suspend () -> Unit, | ||
| action: suspend () -> Result, | ||
| ): Result = withPreparedAndroidAccountRemovalLease( | ||
| accountIdentity = accountIdentity, | ||
| guard = guard, | ||
| prepare = preflight, | ||
| revalidate = preflight, | ||
| action = action, | ||
| ) | ||
|
|
||
| internal suspend fun revokeAndroidSessionAfterRemovalPreflight( | ||
| preflight: suspend () -> Unit, | ||
| revoke: suspend () -> Unit, | ||
|
|
@@ -64,11 +91,55 @@ internal suspend fun revokeAndroidSessionAfterRemovalPreflight( | |
| internal suspend fun revokeAndroidSessionWithAccountLease( | ||
| accountIdentity: String, | ||
| guard: AndroidAccountOperationGuard = ANDROID_ACCOUNT_OPERATION_GUARD, | ||
| preflight: suspend () -> Unit, | ||
| prepare: suspend () -> Unit, | ||
| revalidate: suspend () -> Unit, | ||
| revoke: suspend () -> Unit, | ||
| removeLocalAccount: suspend () -> Unit, | ||
| ) = withAndroidAccountRemovalLease(accountIdentity, guard) { | ||
| revokeAndroidSessionAfterRemovalPreflight(preflight, revoke, removeLocalAccount) | ||
| ) = withPreparedAndroidAccountRemovalLease(accountIdentity, guard, prepare, revalidate) { | ||
| revokeAndroidSessionAfterRemovalPreflight({}, revoke, removeLocalAccount) | ||
| } | ||
|
|
||
| internal class AndroidAccountRemovalLeaseCoordinator( | ||
| context: Context, | ||
| private val guard: AndroidAccountOperationGuard = ANDROID_ACCOUNT_OPERATION_GUARD, | ||
| ) { | ||
| private val appContext = context.applicationContext | ||
|
|
||
| suspend fun <Result> withLease( | ||
| session: NextcloudSession, | ||
| action: suspend () -> Result, | ||
| ): Result = withPreparedAndroidAccountRemovalLease( | ||
| accountIdentity = NextcloudDocumentIds.accountKey(session), | ||
| guard = guard, | ||
| prepare = { prepareAndroidAccountRemoval(appContext, session) }, | ||
| revalidate = { preflightAndroidAccountRemoval(appContext, session) }, | ||
| action = action, | ||
|
Comment on lines
+114
to
+116
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
If a folder-sync run starts after AGENTS.md reference: AGENTS.md:L428-L430 Useful? React with 👍 / 👎. |
||
| ) | ||
|
|
||
| // Missing credentials cannot safely repair legacy self-provider downloads before removal. | ||
| // Commit first; durable owned-state cleanup remains fail-closed and can resume after re-add. | ||
|
Comment on lines
+119
to
+120
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
When an unavailable account is removed with a legacy self-provider download pending, the promised recovery after re-add cannot run: AGENTS.md reference: AGENTS.md:L428-L430 Useful? React with 👍 / 👎. |
||
| suspend fun <Result> withUnavailableLease( | ||
| session: NextcloudSession, | ||
| action: suspend () -> Result, | ||
| ): Result = withUnavailableAndroidAccountRemovalLease( | ||
| accountIdentity = NextcloudDocumentIds.accountKey(session), | ||
| guard = guard, | ||
| preflight = { preflightAndroidAccountRemoval(appContext, session) }, | ||
| action = action, | ||
| ) | ||
|
|
||
| suspend fun revoke( | ||
| session: NextcloudSession, | ||
| revoke: suspend () -> Unit, | ||
| removeLocalAccount: suspend () -> Unit, | ||
| ) = revokeAndroidSessionWithAccountLease( | ||
| accountIdentity = NextcloudDocumentIds.accountKey(session), | ||
| guard = guard, | ||
| prepare = { prepareAndroidAccountRemoval(appContext, session) }, | ||
| revalidate = { preflightAndroidAccountRemoval(appContext, session) }, | ||
| revoke = revoke, | ||
| removeLocalAccount = removeLocalAccount, | ||
| ) | ||
| } | ||
|
|
||
| internal enum class AndroidAccountDocumentGrantScope(val pathSegment: String) { | ||
|
|
@@ -88,6 +159,11 @@ internal suspend fun preflightAndroidAccountRemoval(context: Context, session: N | |
|
|
||
| internal suspend fun prepareAndroidAccountRemoval(context: Context, session: NextcloudSession) { | ||
| preflightAndroidAccountRemoval(context, session) | ||
| reconcileAndroidFileSyncAccountDownloadsBeforeCredentialRemoval( | ||
| context, | ||
| NextcloudDocumentIds.accountKey(session), | ||
| session, | ||
| ) | ||
|
veryCrunchy marked this conversation as resolved.
veryCrunchy marked this conversation as resolved.
|
||
| ANDROID_FILE_RANGE_SESSION_COORDINATOR.quiesce(NextcloudDocumentIds.accountKey(session)) | ||
| } | ||
|
|
||
|
|
||
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.
When ordinary account removal races a newly opened seekable DocumentsProvider file,
prepare()now runs before the exclusive removal lease.AndroidFileRangeSessionCoordinator.quiesce()snapshots existing registrations, whileopenTrackedAndroidFileRangeSession()holds the account guard only until it registers the range session; an open that registers after that snapshot can release the guard before removal callstryWithAccount. The quiesce pass then misses (and removes the registry entry for) this session, allowing credential deletion to proceed while the descriptor can continue issuing authenticated range requests with the retained app password. Keep range-session admission fenced through quiescing and credential removal, or re-quiesce under the final lease.Useful? React with 👍 / 👎.