mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-28 05:46:17 +00:00
fix: preserve android legacy auth fallback
This commit is contained in:
@@ -48,13 +48,79 @@ interface DeviceAuthTokenStore {
|
||||
)
|
||||
}
|
||||
|
||||
class DeviceAuthStore(
|
||||
internal interface DeviceAuthStateStore {
|
||||
fun readDeviceAuthToken(
|
||||
deviceId: String,
|
||||
role: String,
|
||||
): OpenClawSQLiteDeviceAuthTokenRow?
|
||||
|
||||
fun readLatestDeviceAuthDeviceId(): String?
|
||||
|
||||
fun upsertDeviceAuthToken(row: OpenClawSQLiteDeviceAuthTokenRow)
|
||||
|
||||
fun deleteDeviceAuthToken(
|
||||
deviceId: String,
|
||||
role: String,
|
||||
)
|
||||
|
||||
fun deleteAllDeviceAuthTokens()
|
||||
}
|
||||
|
||||
private class OpenClawSQLiteDeviceAuthStateStore(
|
||||
private val store: OpenClawSQLiteStateStore,
|
||||
) : DeviceAuthStateStore {
|
||||
override fun readDeviceAuthToken(
|
||||
deviceId: String,
|
||||
role: String,
|
||||
): OpenClawSQLiteDeviceAuthTokenRow? = store.readDeviceAuthToken(deviceId, role)
|
||||
|
||||
override fun readLatestDeviceAuthDeviceId(): String? = store.readLatestDeviceAuthDeviceId()
|
||||
|
||||
override fun upsertDeviceAuthToken(row: OpenClawSQLiteDeviceAuthTokenRow) {
|
||||
store.upsertDeviceAuthToken(row)
|
||||
}
|
||||
|
||||
override fun deleteDeviceAuthToken(
|
||||
deviceId: String,
|
||||
role: String,
|
||||
) {
|
||||
store.deleteDeviceAuthToken(deviceId, role)
|
||||
}
|
||||
|
||||
override fun deleteAllDeviceAuthTokens() {
|
||||
store.deleteAllDeviceAuthTokens()
|
||||
}
|
||||
}
|
||||
|
||||
class DeviceAuthStore private constructor(
|
||||
private val context: Context,
|
||||
private val legacyPrefsOverride: SecurePrefs? = null,
|
||||
private val stateStore: DeviceAuthStateStore,
|
||||
) : DeviceAuthTokenStore {
|
||||
constructor(
|
||||
context: Context,
|
||||
legacyPrefsOverride: SecurePrefs? = null,
|
||||
) : this(
|
||||
context = context,
|
||||
legacyPrefsOverride = legacyPrefsOverride,
|
||||
stateStore = OpenClawSQLiteDeviceAuthStateStore(OpenClawSQLiteStateStore(context)),
|
||||
)
|
||||
|
||||
internal companion object {
|
||||
fun createForTesting(
|
||||
context: Context,
|
||||
legacyPrefsOverride: SecurePrefs? = null,
|
||||
stateStoreOverride: DeviceAuthStateStore,
|
||||
): DeviceAuthStore =
|
||||
DeviceAuthStore(
|
||||
context = context,
|
||||
legacyPrefsOverride = legacyPrefsOverride,
|
||||
stateStore = stateStoreOverride,
|
||||
)
|
||||
}
|
||||
|
||||
private val json = Json { ignoreUnknownKeys = true }
|
||||
private val legacyPrefs by lazy { legacyPrefsOverride ?: SecurePrefs(context) }
|
||||
private val stateStore = OpenClawSQLiteStateStore(context)
|
||||
|
||||
override fun loadEntry(
|
||||
deviceId: String,
|
||||
@@ -150,17 +216,22 @@ class DeviceAuthStore(
|
||||
scopes = normalizeScopes(metadata?.scopes ?: emptyList()),
|
||||
updatedAtMs = metadata?.updatedAtMs?.takeIf { it > 0L } ?: System.currentTimeMillis(),
|
||||
)
|
||||
stateStore.upsertDeviceAuthToken(
|
||||
OpenClawSQLiteDeviceAuthTokenRow(
|
||||
deviceId = normalizedDevice,
|
||||
role = normalizedRole,
|
||||
token = sqliteSecurePrefsTokenMarker,
|
||||
scopesJson = json.encodeToString(entry.scopes),
|
||||
updatedAtMs = entry.updatedAtMs,
|
||||
),
|
||||
)
|
||||
legacyPrefs.putString(tokenKey(normalizedDevice, normalizedRole), entry.token)
|
||||
removeLegacyMetadata(normalizedDevice, normalizedRole)
|
||||
val migrated =
|
||||
runCatching {
|
||||
stateStore.upsertDeviceAuthToken(
|
||||
OpenClawSQLiteDeviceAuthTokenRow(
|
||||
deviceId = normalizedDevice,
|
||||
role = normalizedRole,
|
||||
token = sqliteSecurePrefsTokenMarker,
|
||||
scopesJson = json.encodeToString(entry.scopes),
|
||||
updatedAtMs = entry.updatedAtMs,
|
||||
),
|
||||
)
|
||||
}.isSuccess
|
||||
if (migrated) {
|
||||
legacyPrefs.putString(tokenKey(normalizedDevice, normalizedRole), entry.token)
|
||||
removeLegacyMetadata(normalizedDevice, normalizedRole)
|
||||
}
|
||||
return entry
|
||||
}
|
||||
|
||||
|
||||
@@ -83,6 +83,28 @@ class DeviceAuthStoreTest {
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun loadEntryReturnsLegacySecurePrefsTokenWhenSQLiteMigrationFails() {
|
||||
val app = RuntimeEnvironment.getApplication()
|
||||
val prefs = legacyPrefs(app)
|
||||
val metadata = """{"scopes":["operator.read"],"updatedAtMs":1700000000000}"""
|
||||
prefs.putString("gateway.deviceToken.device-1.operator", " operator-token ")
|
||||
prefs.putString("gateway.deviceTokenMeta.device-1.operator", metadata)
|
||||
|
||||
val entry =
|
||||
DeviceAuthStore.createForTesting(
|
||||
context = app,
|
||||
legacyPrefsOverride = prefs,
|
||||
stateStoreOverride = ThrowingDeviceAuthStateStore(),
|
||||
).loadEntry("device-1", "operator")
|
||||
|
||||
assertEquals("operator-token", entry?.token)
|
||||
assertEquals(listOf("operator.read"), entry?.scopes)
|
||||
assertEquals(1700000000000L, entry?.updatedAtMs)
|
||||
assertEquals(" operator-token ", prefs.getString("gateway.deviceToken.device-1.operator"))
|
||||
assertEquals(metadata, prefs.getString("gateway.deviceTokenMeta.device-1.operator"))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun loadEntryMovesPlaintextSqliteTokenBackToSecurePrefs() {
|
||||
val app = RuntimeEnvironment.getApplication()
|
||||
@@ -132,4 +154,24 @@ class DeviceAuthStoreTest {
|
||||
prefs.edit().clear().commit()
|
||||
return SecurePrefs(context, securePrefsOverride = prefs)
|
||||
}
|
||||
|
||||
private class ThrowingDeviceAuthStateStore : DeviceAuthStateStore {
|
||||
override fun readDeviceAuthToken(
|
||||
deviceId: String,
|
||||
role: String,
|
||||
): OpenClawSQLiteDeviceAuthTokenRow? = null
|
||||
|
||||
override fun readLatestDeviceAuthDeviceId(): String? = null
|
||||
|
||||
override fun upsertDeviceAuthToken(row: OpenClawSQLiteDeviceAuthTokenRow) {
|
||||
error("sqlite unavailable")
|
||||
}
|
||||
|
||||
override fun deleteDeviceAuthToken(
|
||||
deviceId: String,
|
||||
role: String,
|
||||
) = Unit
|
||||
|
||||
override fun deleteAllDeviceAuthTokens() = Unit
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user