mirror of
https://github.com/openclaw/openclaw.git
synced 2026-07-15 05:56:11 +00:00
* style: apply SwiftFormat 0.62.1 rules Refs #103202 * ci: enforce deterministic Swift lint Refs #103202 * refactor: keep gateway connect lint-clean Refs #103202 * style: keep iOS typography checks warning-free * ci: route MLX Swift changes through pre-push * fix: preserve native i18n extraction after Swift cleanup * refactor: keep rebased Swift surfaces lint-clean * style: format latest Swift additions * chore: refresh native i18n inventory * style: keep generated Swift formatter-clean * fix: preserve node route invalidation callbacks * fix: keep native translation IDs stable * fix: retain native translation identifiers * fix: preserve translations across Swift source moves
92 lines
3.9 KiB
Swift
92 lines
3.9 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 identityURL = tempDir.appendingPathComponent("device.json", isDirectory: false)
|
|
defer { try? FileManager.default.removeItem(at: tempDir) }
|
|
let identity = DeviceIdentityStore.loadOrCreate(fileURL: identityURL)
|
|
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) == "")
|
|
}
|
|
}
|