mirror of
https://github.com/openclaw/openclaw.git
synced 2026-08-16 23:52:40 -06:00
98410d986e
* refactor: store device identities in SQLite * style: satisfy Swift identity lint limits * test: harden device identity migration fixtures * fix: harden SQLite device identity recovery * chore: remove stale identity helper * refactor: isolate device identity repair * chore: remove stale migration import * test: validate migrated identity key types * fix: harden device identity repair * style: format device identity assertion * fix: derive repaired identity key from PEM * style: remove redundant PEM conversions * fix: align native identity store with schema v4 * fix(state): satisfy SQLite identity CI gates * fix(state): coordinate native identity migration * test(doctor): include native identity claim path * fix(infra): preserve coordinator release error * fix(state): clear identity validation gates * refactor(apple): remove identity test-only APIs * refactor(apple): remove dead identity error type * test(apple): use canonical identity test seam * test(macos): isolate gateway readiness identity
95 lines
4.0 KiB
Swift
95 lines
4.0 KiB
Swift
import CryptoKit
|
|
import Foundation
|
|
import Testing
|
|
@testable import OpenClawKit
|
|
|
|
@Suite("DeviceAuthPayload")
|
|
struct DeviceAuthPayloadTests {
|
|
@Test
|
|
func `builds Swift connect compatibility payload with v2 canonical fields`() {
|
|
let signedAtMs: Int64 = 1_800_000_000_000
|
|
let payload = GatewayDeviceAuthPayload.buildConnectCompatibilityPayload(
|
|
fields: .init(
|
|
deviceId: "dev-1",
|
|
client: .init(id: "openclaw-macos", mode: "ui"),
|
|
role: "operator",
|
|
scopes: ["operator.admin", "operator.read"],
|
|
signedAtMs: signedAtMs,
|
|
token: "tok-123",
|
|
nonce: "nonce-abc"))
|
|
#expect(
|
|
payload
|
|
== "v2|dev-1|openclaw-macos|ui|operator|operator.admin,operator.read|1800000000000|tok-123|nonce-abc")
|
|
}
|
|
|
|
@Test
|
|
func `builds canonical v3 payload vector`() {
|
|
let signedAtMs: Int64 = 1_800_000_000_000
|
|
let payload = GatewayDeviceAuthPayload.buildV3(
|
|
fields: .init(
|
|
deviceId: "dev-1",
|
|
client: .init(id: "openclaw-macos", mode: "ui"),
|
|
role: "operator",
|
|
scopes: ["operator.admin", "operator.read"],
|
|
signedAtMs: signedAtMs,
|
|
token: "tok-123",
|
|
nonce: "nonce-abc"),
|
|
platform: " IOS ",
|
|
deviceFamily: " iPhone ")
|
|
#expect(
|
|
payload
|
|
==
|
|
"v3|dev-1|openclaw-macos|ui|operator|operator.admin,operator.read|1800000000000|tok-123|nonce-abc|ios|iphone")
|
|
}
|
|
|
|
@Test
|
|
func `signed device dictionary preserves 64-bit timestamp`() throws {
|
|
let tempDir = FileManager.default.temporaryDirectory
|
|
.appendingPathComponent(UUID().uuidString, isDirectory: true)
|
|
let databaseURL = tempDir.appendingPathComponent("openclaw.sqlite", isDirectory: false)
|
|
defer { try? FileManager.default.removeItem(at: tempDir) }
|
|
let identity = try DeviceIdentitySQLiteStore.loadOrCreate(
|
|
databaseURL: databaseURL,
|
|
destinationStateDirURL: tempDir,
|
|
profile: .primary)
|
|
let signedAtMs: Int64 = 1_800_000_000_000
|
|
let payload = GatewayDeviceAuthPayload.buildV3(
|
|
fields: .init(
|
|
deviceId: identity.deviceId,
|
|
client: .init(id: "openclaw-watchos", mode: "node"),
|
|
role: "node",
|
|
scopes: [],
|
|
signedAtMs: signedAtMs,
|
|
token: "device-token",
|
|
nonce: "nonce-abc"),
|
|
platform: "watchOS",
|
|
deviceFamily: "Apple Watch")
|
|
|
|
let device = try #require(GatewayDeviceAuthPayload.signedDeviceDictionary(
|
|
payload: payload,
|
|
identity: identity,
|
|
signedAtMs: signedAtMs,
|
|
nonce: "nonce-abc"))
|
|
let signature = try #require(device["signature"]?.value as? String)
|
|
let signatureBase64 = signature
|
|
.replacingOccurrences(of: "-", with: "+")
|
|
.replacingOccurrences(of: "_", with: "/")
|
|
let signaturePadding = String(repeating: "=", count: (4 - signatureBase64.count % 4) % 4)
|
|
let signatureData = try #require(Data(base64Encoded: signatureBase64 + signaturePadding))
|
|
let publicKeyData = try #require(Data(base64Encoded: identity.publicKey))
|
|
let publicKey = try Curve25519.Signing.PublicKey(rawRepresentation: publicKeyData)
|
|
let data = try JSONEncoder().encode(device)
|
|
let object = try #require(JSONSerialization.jsonObject(with: data) as? [String: Any])
|
|
|
|
#expect(publicKey.isValidSignature(signatureData, for: Data(payload.utf8)))
|
|
#expect((object["signedAt"] as? NSNumber)?.int64Value == signedAtMs)
|
|
}
|
|
|
|
@Test
|
|
func `normalizes metadata with ASCII-only lowercase`() {
|
|
#expect(GatewayDeviceAuthPayload.normalizeMetadataField(" İOS ") == "İos")
|
|
#expect(GatewayDeviceAuthPayload.normalizeMetadataField(" MAC ") == "mac")
|
|
#expect(GatewayDeviceAuthPayload.normalizeMetadataField(nil) == "")
|
|
}
|
|
}
|