Skip to content

Commit 628e6f6

Browse files
authored
Merge pull request #323 from openziti/enroll-to-cert
Fix identity ID race in runEnrollTo by retrying across events
2 parents 764af86 + fcbf21b commit 628e6f6

File tree

1 file changed

+69
-38
lines changed

1 file changed

+69
-38
lines changed

lib/Ziti.swift

Lines changed: 69 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -826,51 +826,82 @@ import CZitiPrivate
826826
}
827827
}, ZitiEvent.EventType.Auth.rawValue | ZitiEvent.EventType.ConfigEvent.rawValue)
828828

829-
// Wait for context to authenticate after enrollment, then read the real identity
829+
// Wait for context to authenticate after enrollment, then read the real identity.
830+
// The identity fetch (ziti_ctrl_current_identity) is async and can complete after
831+
// the first context event. We try on every event, and use a one-shot 3s fallback
832+
// timer for the no-services case where no further events would arrive.
833+
// All callbacks run on the single uv loop thread, so these flags need no locks.
834+
var contextAuthenticated = false
835+
var enrollmentCompleted = false
836+
837+
// Try to read the real identity and finish enrollment. Returns true if successful.
838+
let tryCompleteEnrollment: () -> Bool = {
839+
guard !enrollmentCompleted else { return true }
840+
guard let ztx = ziti.ztx, let czid = ziti_get_identity(ztx), let cId = czid.pointee.id else {
841+
return false
842+
}
843+
enrollmentCompleted = true
844+
let realId = String(cString: cId)
845+
log.info("\(modeLabel) identity id: \(realId)", function:"runEnrollTo()")
846+
847+
if mode == .cert && realId != tempKeychainTag {
848+
let zkc = ZitiKeychain(tag: tempKeychainTag)
849+
if let zErr = zkc.retagPrivateKey(to: realId) {
850+
log.error("\(modeLabel) \(zErr.localizedDescription)", function:"runEnrollTo()")
851+
} else {
852+
log.info("\(modeLabel) re-tagged keychain key to \(realId)", function:"runEnrollTo()")
853+
}
854+
}
855+
let finalId = ZitiIdentity(id: realId,
856+
ztAPIs: ziti.id.ztAPIs ?? [ziti.id.ztAPI],
857+
name: ziti.id.name,
858+
certs: ziti.id.certs,
859+
ca: ziti.id.ca)
860+
if let cName = czid.pointee.name {
861+
finalId.name = String(cString: cName)
862+
}
863+
ziti.shutdown()
864+
enrollCallback(finalId, nil)
865+
return true
866+
}
867+
830868
ziti.registerEventCallback({ event in
831869
guard let event = event, let ziti = event.ziti else { return }
832-
guard let ctxEvent = event.contextEvent else { return }
833-
log.info("\(modeLabel) context event: \(event.type.debug) \(ctxEvent.err ?? "") status=\(ctxEvent.status)", function:"runEnrollTo()")
834-
835-
if ctxEvent.status == 0 && enrollmentConfigReceived {
836-
// Context authenticated - read the real identity ID from the controller
837-
if let ztx = ziti.ztx, let czid = ziti_get_identity(ztx) {
838-
if let cId = czid.pointee.id {
839-
let realId = String(cString: cId)
840-
log.info("\(modeLabel) identity id: \(realId)", function:"runEnrollTo()")
841-
842-
// Re-tag keychain key from temp UUID to real identity ID
843-
if mode == .cert && realId != tempKeychainTag {
844-
let zkc = ZitiKeychain(tag: tempKeychainTag)
845-
if let zErr = zkc.retagPrivateKey(to: realId) {
846-
log.error("\(modeLabel) \(zErr.localizedDescription)", function:"runEnrollTo()")
847-
} else {
848-
log.info("\(modeLabel) re-tagged keychain key to \(realId)", function:"runEnrollTo()")
870+
guard !enrollmentCompleted else { return }
871+
872+
if event.type == .Context, let ctxEvent = event.contextEvent {
873+
log.info("\(modeLabel) context event: \(event.type.debug) \(ctxEvent.err ?? "") status=\(ctxEvent.status)", function:"runEnrollTo()")
874+
if ctxEvent.status == 0 && enrollmentConfigReceived {
875+
contextAuthenticated = true
876+
// Start a one-shot 3s fallback timer in case no further events arrive
877+
// (e.g. identity has no services). If identity is already available the
878+
// tryCompleteEnrollment call below handles it and the timer is a no-op.
879+
ziti.startTimer(3000, 0) { timer in
880+
ziti.endTimer(timer)
881+
if !enrollmentCompleted {
882+
log.info("\(modeLabel) identity fallback timer fired", function:"runEnrollTo()")
883+
if !tryCompleteEnrollment() {
884+
log.warn("\(modeLabel) identity not available after fallback, using temp ID", function:"runEnrollTo()")
885+
enrollmentCompleted = true
886+
ziti.shutdown()
887+
enrollCallback(ziti.id, nil)
849888
}
850889
}
851-
let finalId = ZitiIdentity(id: realId,
852-
ztAPIs: ziti.id.ztAPIs ?? [ziti.id.ztAPI],
853-
name: ziti.id.name,
854-
certs: ziti.id.certs,
855-
ca: ziti.id.ca)
856-
if let cName = czid.pointee.name {
857-
finalId.name = String(cString: cName)
858-
}
859-
ziti.shutdown()
860-
enrollCallback(finalId, nil)
861-
return
862890
}
891+
} else if ctxEvent.status != 0 {
892+
enrollmentCompleted = true
893+
let errMsg = ctxEvent.err ?? "context error (status \(ctxEvent.status))"
894+
log.error("\(modeLabel) context failed: \(errMsg)", function:"runEnrollTo()")
895+
ziti.shutdown()
896+
enrollCallback(nil, ZitiError(errMsg, errorCode: Int(ctxEvent.status)))
897+
return
863898
}
864-
// Fallback: no identity from controller, use what we have
865-
ziti.shutdown()
866-
enrollCallback(ziti.id, nil)
867-
} else if ctxEvent.status != 0 {
868-
let errMsg = ctxEvent.err ?? "context error (status \(ctxEvent.status))"
869-
log.error("\(modeLabel) context failed: \(errMsg)", function:"runEnrollTo()")
870-
ziti.shutdown()
871-
enrollCallback(nil, ZitiError(errMsg, errorCode: Int(ctxEvent.status)))
872899
}
873-
}, ZitiEvent.EventType.Context.rawValue)
900+
901+
// On every event after authentication, try to read the real identity ID.
902+
guard contextAuthenticated else { return }
903+
let _ = tryCompleteEnrollment()
904+
}, ZitiEvent.EventType.Context.rawValue | ZitiEvent.EventType.Service.rawValue)
874905

875906
ziti.runAsync { zErr in
876907
log.info("\(modeLabel) initCallback, error=\(String(describing: zErr))", function:"runEnrollTo()")

0 commit comments

Comments
 (0)