66// Copyright © 2022 Leanplum. All rights reserved.
77
88import Foundation
9+ // Use @_implementationOnly to *not* expose CleverTapSDK to the Leanplum-Swift header
10+ @_implementationOnly import CleverTapSDK
911
1012/**
1113 * Identity mapping between Leanplum userId and deviceId and CleverTap Identity and CTID.
1214 *
1315 * Mappings:
1416 * - anonymous: <CTID=deviceId, Identity=null>
15- * - non-anonymous to <CTID=deviceId_userId, Identity=userId>
17+ * - non-anonymous to <CTID=deviceId_userIdHash, Identity=userId>
18+ * - if invalid deviceId <deviceId=deviceIdHash>
19+ *
20+ * UserId Hash is generated using the first 10 chars of the userId SHA256 string (hex).
21+ * If the deviceId does _not_ pass validation _or_ is _longer_ than 50 characters,
22+ * the first 32 chars of the deviceId SHA256 string (hex) are used as deviceIdHash.
1623 *
1724 * - Note: On login of anonymous user, a merge should happen. CleverTap SDK allows merges
1825 * only when the CTID remains the same, meaning that the merged profile would get the anonymous
19- * profile's CTID: <CTID=deviceId, Identity=userId >.
26+ * profile's CTID: <CTID=deviceId, Identity=userIdHash >.
2027 * In order to keep track which is that userId, it is saved into `anonymousLoginUserId`.
2128 * For this userId, the CTID is always set to deviceId.
2229 * Leanplum UserId can be set through Leanplum.start and Leanplum.setUserId
@@ -29,6 +36,9 @@ class IdentityManager {
2936 static let Identity = " Identity "
3037 static let AnonymousLoginUserIdKey = " __leanplum_anonymous_login_user_id "
3138 static let IdentityStateKey = " __leanplum_identity_state "
39+
40+ static let DeviceIdLengthLimit = 50
41+ static let IdentityHashLength = 10
3242 }
3343
3444 enum IdentityState : String {
@@ -58,7 +68,12 @@ class IdentityManager {
5868
5969 func setUserId( _ userId: String ) {
6070 if ( state == IdentityState . anonymous ( ) ) {
61- anonymousLoginUserId = userId
71+ if let hash = Utilities . sha256_40 ( string: userId) {
72+ anonymousLoginUserId = hash
73+ } else {
74+ Log . error ( " [Wrapper] Failed to generate SHA256 for userId: \( userId) " )
75+ anonymousLoginUserId = userIdHash
76+ }
6277 Log . debug ( " [Wrapper] Anonymous user on device \( deviceId) will be merged to \( userId) " )
6378 state = IdentityState . identified ( )
6479 }
@@ -76,19 +91,51 @@ class IdentityManager {
7691 func identifyNonAnonymous( ) {
7792 if let state = state,
7893 state == IdentityState . anonymous ( ) {
79- anonymousLoginUserId = userId
94+ anonymousLoginUserId = userIdHash
8095 }
8196 state = IdentityState . identified ( )
8297 }
8398
84- var cleverTapID : String {
85- if userId != anonymousLoginUserId ,
86- userId != deviceId {
87- return " \( deviceId ) _ \( userId) "
99+ var userIdHash : String {
100+ guard let hash = Utilities . sha256_40 ( string : userId ) else {
101+ Log . error ( " [Wrapper] Failed to generate SHA256 for userId: \( userId ) " )
102+ return userId
88103 }
89-
104+
105+ return hash
106+ }
107+
108+ var isValidCleverTapID : Bool {
109+ // Only the deviceId could be invalid, since the userIdHash should always be valid,
110+ // but we still validate the whole CTID to be safe
111+ CleverTap . isValidCleverTapId ( originalCleverTapID) &&
112+ deviceId. count <= Constants . DeviceIdLengthLimit
113+ }
114+
115+ var originalCleverTapID : String {
116+ if shouldAppendUserId {
117+ return " \( deviceId) _ \( userIdHash) "
118+ }
119+
90120 return deviceId
91121 }
122+
123+ var cleverTapID : String {
124+ if isValidCleverTapID {
125+ return originalCleverTapID
126+ }
127+
128+ guard let ctDevice = Utilities . sha256_128 ( string: deviceId) else {
129+ Log . error ( " [Wrapper] Failed to generate SHA256 for deviceId: \( deviceId) " )
130+ return originalCleverTapID
131+ }
132+
133+ if shouldAppendUserId {
134+ return " \( ctDevice) _ \( userIdHash) "
135+ }
136+
137+ return ctDevice
138+ }
92139
93140 var profile : [ AnyHashable : Any ] {
94141 [ Constants . Identity: userId]
@@ -97,4 +144,8 @@ class IdentityManager {
97144 var isAnonymous : Bool {
98145 userId == deviceId
99146 }
147+
148+ var shouldAppendUserId : Bool {
149+ userIdHash != anonymousLoginUserId && userId != deviceId
150+ }
100151}
0 commit comments