diff --git a/ios/Runner.xcodeproj/project.pbxproj b/ios/Runner.xcodeproj/project.pbxproj index fe8d93bb0..bf1d15d48 100644 --- a/ios/Runner.xcodeproj/project.pbxproj +++ b/ios/Runner.xcodeproj/project.pbxproj @@ -14,6 +14,15 @@ 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; }; 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; }; CDFECA8C54311B749F044831 /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 5810EDDA99BEFEACD742F507 /* Pods_Runner.framework */; }; + F522E87F2C0EE23400DDA35B /* AuthenticationSSOTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = F522E87E2C0EE23400DDA35B /* AuthenticationSSOTests.swift */; }; + F522E8812C0EE3F200DDA35B /* AuthenticationSSO.swift in Sources */ = {isa = PBXBuildFile; fileRef = F5E7D8832B3877050009BB8A /* AuthenticationSSO.swift */; }; + F522E8822C0EE48700DDA35B /* AuthenticationType.swift in Sources */ = {isa = PBXBuildFile; fileRef = F53D1E872B2E4B3B00051FD0 /* AuthenticationType.swift */; }; + F522E8832C0EE48C00DDA35B /* Authentication.swift in Sources */ = {isa = PBXBuildFile; fileRef = F5E7D87F2B3876DE0009BB8A /* Authentication.swift */; }; + F522E8862C0EE8B600DDA35B /* CoreUtils.swift in Sources */ = {isa = PBXBuildFile; fileRef = F522E8852C0EE8B600DDA35B /* CoreUtils.swift */; }; + F522E8872C0EE8B600DDA35B /* CoreUtils.swift in Sources */ = {isa = PBXBuildFile; fileRef = F522E8852C0EE8B600DDA35B /* CoreUtils.swift */; }; + F522E8882C0EE8B600DDA35B /* CoreUtils.swift in Sources */ = {isa = PBXBuildFile; fileRef = F522E8852C0EE8B600DDA35B /* CoreUtils.swift */; }; + F522E88A2C0F117900DDA35B /* IntegerExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = F522E8892C0F117900DDA35B /* IntegerExtensions.swift */; }; + F522E88B2C0F117900DDA35B /* IntegerExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = F522E8892C0F117900DDA35B /* IntegerExtensions.swift */; }; F52F993027FD6EB900346091 /* ShareViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = F52F992F27FD6EB900346091 /* ShareViewController.swift */; }; F52F993327FD6EB900346091 /* MainInterface.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = F52F993127FD6EB900346091 /* MainInterface.storyboard */; }; F52F993727FD6EB900346091 /* TeamMailShareExtension.appex in Embed App Extensions */ = {isa = PBXBuildFile; fileRef = F52F992D27FD6EB900346091 /* TeamMailShareExtension.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; }; @@ -128,6 +137,9 @@ 97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; B2EAFF659572E6B9F5AFAAF8 /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"; sourceTree = ""; }; + F522E87E2C0EE23400DDA35B /* AuthenticationSSOTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AuthenticationSSOTests.swift; sourceTree = ""; }; + F522E8852C0EE8B600DDA35B /* CoreUtils.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CoreUtils.swift; sourceTree = ""; }; + F522E8892C0F117900DDA35B /* IntegerExtensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IntegerExtensions.swift; sourceTree = ""; }; F52F992D27FD6EB900346091 /* TeamMailShareExtension.appex */ = {isa = PBXFileReference; explicitFileType = "wrapper.app-extension"; includeInIndex = 0; path = TeamMailShareExtension.appex; sourceTree = BUILT_PRODUCTS_DIR; }; F52F992F27FD6EB900346091 /* ShareViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ShareViewController.swift; sourceTree = ""; }; F52F993227FD6EB900346091 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/MainInterface.storyboard; sourceTree = ""; }; @@ -273,6 +285,14 @@ path = Runner; sourceTree = ""; }; + F522E8842C0EE89D00DDA35B /* Utils */ = { + isa = PBXGroup; + children = ( + F522E8852C0EE8B600DDA35B /* CoreUtils.swift */, + ); + path = Utils; + sourceTree = ""; + }; F52F992E27FD6EB900346091 /* TeamMailShareExtension */ = { isa = PBXGroup; children = ( @@ -353,6 +373,7 @@ isa = PBXGroup; children = ( F55114CE2BFBACFA00E41E93 /* DateConversionTests.swift */, + F522E87E2C0EE23400DDA35B /* AuthenticationSSOTests.swift */, ); path = TwakeMailTests; sourceTree = ""; @@ -425,6 +446,7 @@ children = ( F5E7D8782B38763B0009BB8A /* DateExtensions.swift */, F5E7D87B2B38764F0009BB8A /* StringExtensions.swift */, + F522E8892C0F117900DDA35B /* IntegerExtensions.swift */, ); path = Extensions; sourceTree = ""; @@ -448,6 +470,7 @@ F5EFC0802B32965100829056 /* TwakeCore */ = { isa = PBXGroup; children = ( + F522E8842C0EE89D00DDA35B /* Utils */, F5E7D8772B38761B0009BB8A /* Extensions */, F5BBBF4F2B2EEC1B007930E1 /* Exceptions */, F53D1E642B2DE84E00051FD0 /* Network */, @@ -725,10 +748,15 @@ files = ( F5E7D87C2B38764F0009BB8A /* StringExtensions.swift in Sources */, 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */, + F522E88A2C0F117900DDA35B /* IntegerExtensions.swift in Sources */, F5E7D8792B38763B0009BB8A /* DateExtensions.swift in Sources */, 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */, + F522E8862C0EE8B600DDA35B /* CoreUtils.swift in Sources */, + F522E8812C0EE3F200DDA35B /* AuthenticationSSO.swift in Sources */, + F522E8822C0EE48700DDA35B /* AuthenticationType.swift in Sources */, F5EFC07D2B328B9F00829056 /* TwakeLogger.swift in Sources */, F5E7D8742B3578F90009BB8A /* JmapConstants.swift in Sources */, + F522E8832C0EE48C00DDA35B /* Authentication.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -746,6 +774,8 @@ buildActionMask = 2147483647; files = ( F55114CF2BFBACFA00E41E93 /* DateConversionTests.swift in Sources */, + F522E8882C0EE8B600DDA35B /* CoreUtils.swift in Sources */, + F522E87F2C0EE23400DDA35B /* AuthenticationSSOTests.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -757,6 +787,8 @@ F53D1E7F2B2E3C2600051FD0 /* JmapConstants.swift in Sources */, F5E7D8822B3876F60009BB8A /* AuthenticationCredential.swift in Sources */, F5D4EA032B2ABF090090DDFC /* NotificationService.swift in Sources */, + F522E88B2C0F117900DDA35B /* IntegerExtensions.swift in Sources */, + F522E8872C0EE8B600DDA35B /* CoreUtils.swift in Sources */, F53D1E8A2B2E4BB700051FD0 /* TokenOidc.swift in Sources */, F53D1E862B2E401B00051FD0 /* JmapRequestGenerator.swift in Sources */, F5E7D87A2B38763B0009BB8A /* DateExtensions.swift in Sources */, diff --git a/ios/TwakeCore/Extensions/DateExtensions.swift b/ios/TwakeCore/Extensions/DateExtensions.swift index b610c1bcd..9ee091784 100644 --- a/ios/TwakeCore/Extensions/DateExtensions.swift +++ b/ios/TwakeCore/Extensions/DateExtensions.swift @@ -4,4 +4,11 @@ extension Date { func isBefore(_ otherDate: Date) -> Bool { return self < otherDate } + + func convertDateToISO8601String() -> String { + let dateFormatter = DateFormatter() + dateFormatter.dateFormat = CoreUtils.ISO8601_DATE_FORMAT + dateFormatter.locale = Locale(identifier: CoreUtils.EN_US_POSIX_LOCALE) + return dateFormatter.string(from: self) + } } diff --git a/ios/TwakeCore/Extensions/IntegerExtensions.swift b/ios/TwakeCore/Extensions/IntegerExtensions.swift new file mode 100644 index 000000000..1efc2d464 --- /dev/null +++ b/ios/TwakeCore/Extensions/IntegerExtensions.swift @@ -0,0 +1,11 @@ +import Foundation + +extension Int { + func convertMillisecondsToDate() -> Date { + return Date(timeIntervalSince1970: TimeInterval(self) / 1000) + } + + func convertMillisecondsToISO8601String() -> String { + return convertMillisecondsToDate().convertDateToISO8601String() + } +} diff --git a/ios/TwakeCore/Extensions/StringExtensions.swift b/ios/TwakeCore/Extensions/StringExtensions.swift index 73f960718..4f1fff70f 100644 --- a/ios/TwakeCore/Extensions/StringExtensions.swift +++ b/ios/TwakeCore/Extensions/StringExtensions.swift @@ -3,8 +3,8 @@ import Foundation extension String { func convertISO8601StringToDate() -> Date? { let dateFormatter = DateFormatter() - dateFormatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ss.SSS" - dateFormatter.locale = Locale(identifier: "en_US_POSIX") + dateFormatter.dateFormat = CoreUtils.ISO8601_DATE_FORMAT + dateFormatter.locale = Locale(identifier: CoreUtils.EN_US_POSIX_LOCALE) return dateFormatter.date(from: self) } diff --git a/ios/TwakeCore/Network/Interceptor/AuthenticationInterceptor.swift b/ios/TwakeCore/Network/Interceptor/AuthenticationInterceptor.swift index 5e17bfef4..c6cebaeae 100644 --- a/ios/TwakeCore/Network/Interceptor/AuthenticationInterceptor.swift +++ b/ios/TwakeCore/Network/Interceptor/AuthenticationInterceptor.swift @@ -69,7 +69,7 @@ class AuthenticationInterceptor: RequestInterceptor { private func validateToRefreshToken(response: HTTPURLResponse, authenticationSSO: AuthenticationSSO) -> Bool { return response.statusCode == 401 && !authenticationSSO.refreshToken.isEmpty && - authenticationSSO.isExpiredTime() + authenticationSSO.isExpiredTime(currentDate: CoreUtils.shared.getCurrentDate()) } // MARK: - Handle refresh token to get new token diff --git a/ios/TwakeCore/Network/Model/AuthenticationSSO.swift b/ios/TwakeCore/Network/Model/AuthenticationSSO.swift index d965d0188..a41794043 100644 --- a/ios/TwakeCore/Network/Model/AuthenticationSSO.swift +++ b/ios/TwakeCore/Network/Model/AuthenticationSSO.swift @@ -18,23 +18,15 @@ struct AuthenticationSSO: Authentication { return "Bearer \(accessToken)" } - func isExpiredTime() -> Bool { + func isExpiredTime(currentDate: Date) -> Bool { guard let expireTime else { return false } - - if #available(iOSApplicationExtension 15, *) { - guard let expireDate = expireTime.convertISO8601StringToDate(), - expireDate.isBefore(Date.now) else { - return true - } + + if let expireDate = expireTime.convertISO8601StringToDate() { + return expireDate.isBefore(currentDate) } else { - guard let expireDate = expireTime.convertISO8601StringToDate(), - expireDate.isBefore(Date()) else { - return true - } + return false } - - return false } } diff --git a/ios/TwakeCore/Utils/CoreUtils.swift b/ios/TwakeCore/Utils/CoreUtils.swift new file mode 100644 index 000000000..f0d1bde46 --- /dev/null +++ b/ios/TwakeCore/Utils/CoreUtils.swift @@ -0,0 +1,17 @@ +import Foundation + +class CoreUtils { + static let shared: CoreUtils = CoreUtils() + + static let ISO8601_DATE_FORMAT = "yyyy-MM-dd'T'HH:mm:ss.SSS" + static let EN_US_POSIX_LOCALE = "en_US_POSIX" + + func getCurrentDate() -> Date { + if #available(iOS 15, *) { + return Date.now + } else { + return Date() + } + } +} + diff --git a/ios/TwakeMailTests/AuthenticationSSOTests.swift b/ios/TwakeMailTests/AuthenticationSSOTests.swift new file mode 100644 index 000000000..1c0127cd7 --- /dev/null +++ b/ios/TwakeMailTests/AuthenticationSSOTests.swift @@ -0,0 +1,47 @@ +import XCTest + +@testable import Runner + +final class AuthenticationSSOTests: XCTestCase { + + func testIsExpiredTimeMethodWithValidExpireTime() { + let currentDate = CoreUtils.shared.getCurrentDate() + let expireDate = currentDate.addingTimeInterval(3600) + + let authenticationSSO = AuthenticationSSO( + type: AuthenticationType.oidc, + accessToken: "abcxyz", + refreshToken: "abcxyz", + expireTime: expireDate.convertDateToISO8601String() + ) + + XCTAssertFalse(authenticationSSO.isExpiredTime(currentDate: currentDate)) + } + + func testIsExpiredTimeMethodWithExpireTime() { + let currentDate = CoreUtils.shared.getCurrentDate() + let expireDate = currentDate.addingTimeInterval(-3600) + + let authenticationSSO = AuthenticationSSO( + type: AuthenticationType.oidc, + accessToken: "abcxyz", + refreshToken: "abcxyz", + expireTime: expireDate.convertDateToISO8601String() + ) + + XCTAssertTrue(authenticationSSO.isExpiredTime(currentDate: currentDate)) + } + + func testIsExpiredTimeMethodNilExpireTime() { + let currentDate = CoreUtils.shared.getCurrentDate() + + let authenticationSSO = AuthenticationSSO( + type: AuthenticationType.oidc, + accessToken: "abcxyz", + refreshToken: "abcxyz", + expireTime: nil + ) + + XCTAssertFalse(authenticationSSO.isExpiredTime(currentDate: currentDate)) + } +} diff --git a/ios/TwakeMailTests/DateConversionTests.swift b/ios/TwakeMailTests/DateConversionTests.swift index d51f0f8ce..51c4c58b5 100644 --- a/ios/TwakeMailTests/DateConversionTests.swift +++ b/ios/TwakeMailTests/DateConversionTests.swift @@ -22,4 +22,24 @@ class DateConversionTests: XCTestCase { let date = invalidDateString.convertISO8601StringToDate() XCTAssertNil(date, "The conversion should return nil for an invalid date string.") } + + func testConvertValidDateToISO8601String() { + let validDate = Calendar.current.date( + from: DateComponents( + year: 2024, + month: 5, + day: 20, + hour: 22, + minute: 54, + second: 57, + nanosecond: 958000000 + ) + ) + + + let expectedDateString = "2024-05-20T22:54:57.958" + let validDateString = validDate!.convertDateToISO8601String() + + XCTAssertEqual(validDateString, expectedDateString, "Converted date string does not match expected date string") + } }