Skip to content

Commit

Permalink
Merge branch 'master' into 2566-add-email-signatures-support
Browse files Browse the repository at this point in the history
  • Loading branch information
ioanmo226 authored Nov 5, 2024
2 parents 03945ce + 1482265 commit 1a51c91
Show file tree
Hide file tree
Showing 19 changed files with 206 additions and 36 deletions.
2 changes: 1 addition & 1 deletion .semaphore/semaphore.yml
Original file line number Diff line number Diff line change
Expand Up @@ -114,4 +114,4 @@ after_pipeline:
jobs:
- name: Publish Results
commands:
- test-results gen-pipeline-report
- test-results gen-pipeline-report
6 changes: 5 additions & 1 deletion FlowCrypt.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -420,6 +420,7 @@
D2FD0F692453245E00259FF0 /* Either.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2FD0F682453245E00259FF0 /* Either.swift */; };
D2FF6966243115EC007182F0 /* SetupImapViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2FF6965243115EC007182F0 /* SetupImapViewController.swift */; };
D2FF6968243115F9007182F0 /* SetupImapViewDecorator.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2FF6967243115F9007182F0 /* SetupImapViewDecorator.swift */; };
D741F9B22CA5661C00E1CAFF /* SecurityWarningNode.swift in Sources */ = {isa = PBXBuildFile; fileRef = D741F9B12CA5661400E1CAFF /* SecurityWarningNode.swift */; };
F191F621272511790053833E /* BlurViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = F191F620272511790053833E /* BlurViewController.swift */; };
F8678DCC2722143300BB1710 /* GmailService+draft.swift in Sources */ = {isa = PBXBuildFile; fileRef = F8678DCB2722143300BB1710 /* GmailService+draft.swift */; };
F8A72FA12729F82800E4BCAB /* DraftGatewayMock.swift in Sources */ = {isa = PBXBuildFile; fileRef = F8A72FA02729F82800E4BCAB /* DraftGatewayMock.swift */; };
Expand Down Expand Up @@ -886,6 +887,7 @@
D2FD0F682453245E00259FF0 /* Either.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Either.swift; sourceTree = "<group>"; };
D2FF6965243115EC007182F0 /* SetupImapViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SetupImapViewController.swift; sourceTree = "<group>"; };
D2FF6967243115F9007182F0 /* SetupImapViewDecorator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SetupImapViewDecorator.swift; sourceTree = "<group>"; };
D741F9B12CA5661400E1CAFF /* SecurityWarningNode.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SecurityWarningNode.swift; sourceTree = "<group>"; };
E26D5E20275AA417007B8802 /* BundleExtensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BundleExtensions.swift; sourceTree = "<group>"; };
F191F620272511790053833E /* BlurViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BlurViewController.swift; sourceTree = "<group>"; };
F8678DCB2722143300BB1710 /* GmailService+draft.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "GmailService+draft.swift"; sourceTree = "<group>"; };
Expand Down Expand Up @@ -2212,6 +2214,7 @@
D2A1D3D123FD9A9E00D626D6 /* Cell Nodes */ = {
isa = PBXGroup;
children = (
D741F9B12CA5661400E1CAFF /* SecurityWarningNode.swift */,
9F8D5E61236B04E300186E43 /* CellNode.swift */,
9F1797652368EE50002BF770 /* SetupTitleNode.swift */,
5A39F433239EC61C001F4607 /* TitleCellNode.swift */,
Expand Down Expand Up @@ -2904,6 +2907,7 @@
D2CDC3D72404704D002B045F /* RecipientEmailsCellNode.swift in Sources */,
5165ABCC27B526D100CCC379 /* RecipientEmailTextFieldNode.swift in Sources */,
51C56BE82901867D00610D12 /* ENSideMenu.swift in Sources */,
D741F9B22CA5661C00E1CAFF /* SecurityWarningNode.swift in Sources */,
955475FB2B0650AC00F52076 /* WebNode.swift in Sources */,
D2717752242567EB00BDA9A9 /* KeyTextCellNode.swift in Sources */,
511D07E12769FBBA0050417B /* MessageActionCellNode.swift in Sources */,
Expand Down Expand Up @@ -3883,7 +3887,7 @@
repositoryURL = "https://github.com/google/GTMAppAuth";
requirement = {
kind = upToNextMajorVersion;
minimumVersion = 2.0.0;
minimumVersion = 4.0.0;
};
};
51A1A12629070CDF007F1188 /* XCRemoteSwiftPackageReference "Texture" */ = {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,8 +42,8 @@
"kind" : "remoteSourceControl",
"location" : "https://github.com/google/GoogleSignIn-iOS",
"state" : {
"revision" : "7932d33686c1dc4d7df7a919aae47361d1cdfda4",
"version" : "7.0.0"
"revision" : "a7965d134c5d3567026c523e0a8a583f73b62b0d",
"version" : "7.1.0"
}
},
{
Expand All @@ -60,8 +60,8 @@
"kind" : "remoteSourceControl",
"location" : "https://github.com/google/GTMAppAuth.git",
"state" : {
"revision" : "cee3c709307912d040bd1e06ca919875a92339c6",
"version" : "2.0.0"
"revision" : "5d7d66f647400952b1758b230e019b07c0b4b22a",
"version" : "4.1.1"
}
},
{
Expand Down
2 changes: 1 addition & 1 deletion FlowCrypt/Controllers/Compose/ComposeViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,7 @@ final class ComposeViewController: TableNodeViewController {
self.googleAuthManager = GoogleAuthManager(
appDelegateGoogleSessionContainer: UIApplication.shared.delegate as? AppDelegate
)
self.contactsProvider = GoogleContactsProvider(authorization: self.googleAuthManager.authorization(for: appContext.user.email))
self.contactsProvider = try GoogleContactsProvider(authorization: self.googleAuthManager.authorization(for: appContext.user.email))

let draftsApiClient = try appContext.getRequiredMailProvider().draftsApiClient

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ extension ComposeViewController {
for: .gmailLogin(self),
appContext: appContext
)
contactsProvider.authorization = googleAuthManager.authorization(for: appContext.user.email)
contactsProvider.authorization = try googleAuthManager.authorization(for: appContext.user.email)
shouldEvaluateRecipientInput = true
reload(sections: [.contacts])
} catch {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,8 @@ extension ThreadDetailsViewController: ASTableDelegate, ASTableDataSource {
let processedMessage = input[section - 1].processedMessage
let attachmentsCount = processedMessage?.attachments.count ?? 0
let pubkeysCount = processedMessage?.keyDetails.count ?? 0
return Parts.allCases.count + attachmentsCount + pubkeysCount
let securityWarningBlockCount = processedMessage?.message.isSuspicious == true ? 1 : 0
return Parts.allCases.count + attachmentsCount + pubkeysCount + securityWarningBlockCount
}

// swiftlint:disable cyclomatic_complexity function_body_length
Expand Down Expand Up @@ -65,10 +66,16 @@ extension ThreadDetailsViewController: ASTableDelegate, ASTableDataSource {
}
}

if message.rawMessage.isSuspicious, row == 1 {
return SecurityWarningNode()
}

guard message.isExpanded, let processedMessage = message.processedMessage
else { return self.dividerNode(indexPath: indexPath) }

guard row > 1 else {
let securityWarningBlockCount = message.rawMessage.isSuspicious == true ? 1 : 0

guard row > 1 + securityWarningBlockCount else {
if processedMessage.text.isHTMLString {
return ThreadDetailWebNode(
input: .init(message: processedMessage.text, quote: processedMessage.quote, index: messageIndex)
Expand All @@ -85,7 +92,7 @@ extension ThreadDetailsViewController: ASTableDelegate, ASTableDataSource {
}

let keyCount = processedMessage.keyDetails.count
let keyIndex = row - 2
let keyIndex = row - 2 - securityWarningBlockCount
if keyIndex < keyCount {
let keyDetails = processedMessage.keyDetails[keyIndex]
let node = PublicKeyDetailNode(
Expand All @@ -97,7 +104,7 @@ extension ThreadDetailsViewController: ASTableDelegate, ASTableDataSource {
return node
}

let attachmentIndex = row - 2 - keyCount
let attachmentIndex = row - 2 - keyCount - securityWarningBlockCount
if let attachment = processedMessage.attachments[safe: attachmentIndex] {
return AttachmentNode(
input: .init(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import Foundation
import GTMAppAuth

protocol ContactsProviderType {
var authorization: GTMAppAuthFetcherAuthorization? { get set }
var authorization: GTMAppAuth.AuthSession? { get set }
var isContactsScopeEnabled: Bool { get }
func searchContacts(query: String) async throws -> [Recipient]
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ enum ContactsProviderError: Error {
}

class GoogleContactsProvider: ContactsProviderType {
var authorization: GTMAppAuthFetcherAuthorization? {
var authorization: GTMAppAuth.AuthSession? {
didSet {
guard isContactsScopeEnabled else { return }
runWarmupQuery()
Expand Down Expand Up @@ -68,7 +68,7 @@ class GoogleContactsProvider: ContactsProviderType {
}
}

init(authorization: GTMAppAuthFetcherAuthorization?) {
init(authorization: GTMAppAuth.AuthSession?) {
self.authorization = authorization
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ class GmailService: MailServiceProvider {
self?.progressHandler?(progress)
}

guard let authorization = googleAuthManager.authorization(for: currentUserEmail) else {
guard let authorization = try? googleAuthManager.authorization(for: currentUserEmail) else {
logger.logWarning("authorization for current user is nil")
return service
}
Expand Down Expand Up @@ -60,5 +60,6 @@ extension String {
static let bcc = "bcc"
static let replyTo = "reply-to"
static let inReplyTo = "in-reply-to"
static let receivedSPF = "received-spf"
static let identifier = "message-id"
}
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ extension Message {
var replyTo: String?
var inReplyTo: String?
var rfc822MsgId: String?
var isSuspicious = false

for messageHeader in messageHeaders.compactMap({ $0 }) {
guard let name = messageHeader.name?.lowercased(),
Expand All @@ -58,6 +59,7 @@ extension Message {
case .bcc: bcc = value
case .replyTo: replyTo = value
case .inReplyTo: inReplyTo = value
case .receivedSPF: isSuspicious = value.contains("softfail")
case .identifier: rfc822MsgId = value
default: break
}
Expand All @@ -81,7 +83,8 @@ extension Message {
cc: cc,
bcc: bcc,
replyTo: replyTo,
inReplyTo: inReplyTo
inReplyTo: inReplyTo,
isSuspicious: isSuspicious
)
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ struct Message: Hashable {
let body: MessageBody
let inReplyTo: String?
let replyToMsgId: String?
let isSuspicious: Bool
private(set) var labels: [MessageLabel]

var isRead: Bool {
Expand Down Expand Up @@ -83,7 +84,8 @@ struct Message: Hashable {
bcc: String? = nil,
replyTo: String? = nil,
inReplyTo: String? = nil,
replyToMsgId: String? = nil
replyToMsgId: String? = nil,
isSuspicious: Bool = false
) {
self.identifier = identifier
self.date = date
Expand All @@ -104,6 +106,7 @@ struct Message: Hashable {
self.replyTo = Self.parseRecipients(replyTo)
self.inReplyTo = inReplyTo
self.replyToMsgId = replyToMsgId
self.isSuspicious = isSuspicious
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ enum GoogleAuthManagerError: Error, CustomStringConvertible {
}

protocol GoogleAuthManagerType {
func authorization(for email: String?) -> GTMAppAuthFetcherAuthorization?
func authorization(for email: String?) throws -> GTMAppAuth.AuthSession?
}

// this is here so that we don't have to include AppDelegate in test target
Expand All @@ -64,16 +64,17 @@ final class GoogleAuthManager: NSObject, GoogleAuthManagerType {

lazy var logger = Logger.nested(in: Self.self, with: .userAppStart)

private func idToken(for email: String?) -> String? {
return authorization(for: email)?.authState.lastTokenResponse?.idToken
private func idToken(for email: String?) throws -> String? {
return try authorization(for: email)?.authState.lastTokenResponse?.idToken
}

func authorization(for email: String?) -> GTMAppAuthFetcherAuthorization? {
func authorization(for email: String?) throws -> GTMAppAuth.AuthSession? {
guard let email else {
return nil
}
let keychainStore = GTMAppAuth.KeychainStore(itemName: Constants.index + email)
// get authorization from keychain
return GTMAppAuthFetcherAuthorization(fromKeychainForName: Constants.index + email)
return try keychainStore.retrieveAuthSession()
}

private var authorizationConfiguration: OIDServiceConfiguration {
Expand All @@ -83,7 +84,7 @@ final class GoogleAuthManager: NSObject, GoogleAuthManagerType {
tokenEndpoint: URL(string: "\(GeneralConstants.Mock.backendUrl)/token")!
)
} else {
return GTMAppAuthFetcherAuthorization.configurationForGoogle()
return GTMAppAuth.AuthSession.configurationForGoogle()
}
}
}
Expand Down Expand Up @@ -129,7 +130,8 @@ extension GoogleAuthManager {
func signOut(user email: String) {
DispatchQueue.main.async {
self.appDelegateGoogleSessionContainer?.googleAuthSession = nil
GTMAppAuthFetcherAuthorization.removeFromKeychain(forName: Constants.index + email)
let keychainStore = GTMAppAuth.KeychainStore(itemName: Constants.index + email)
try? keychainStore.removeAuthSession()
}
}

Expand Down Expand Up @@ -163,7 +165,7 @@ extension GoogleAuthManager {
scopes: [GoogleScope],
userEmail: String?
) async throws -> SessionType {
let authorization = GTMAppAuthFetcherAuthorization(authState: authState)
let authorization = GTMAppAuth.AuthSession(authState: authState)

guard let email = authorization.userEmail else {
throw GoogleAuthManagerError.inconsistentState("Missing email")
Expand All @@ -178,7 +180,7 @@ extension GoogleAuthManager {
email: authorization.userEmail
)
}
saveAuth(state: authState, for: email)
try saveAuth(state: authState, for: email)
guard let token = authState.lastTokenResponse?.accessToken else {
throw GoogleAuthManagerError.inconsistentState("Missing token")
}
Expand Down Expand Up @@ -210,14 +212,16 @@ extension GoogleAuthManager {
}

// save auth session to keychain
private func saveAuth(state: OIDAuthState, for email: String) {
private func saveAuth(state: OIDAuthState, for email: String) throws {
state.stateChangeDelegate = self
let authorization = GTMAppAuthFetcherAuthorization(authState: state)
GTMAppAuthFetcherAuthorization.save(authorization, toKeychainForName: Constants.index + email)
let authorization = GTMAppAuth.AuthSession(authState: state)
let keychainStore = GTMAppAuth.KeychainStore(itemName: Constants.index + email)

try keychainStore.save(authSession: authorization)
}

private func fetchGoogleUser(
with authorization: GTMAppAuthFetcherAuthorization
with authorization: GTMAppAuth.AuthSession
) async throws -> GTLROauth2_Userinfo {
return try await withCheckedThrowingContinuation { continuation in
let query = GTLROauth2Query_UserinfoGet.query()
Expand Down Expand Up @@ -249,7 +253,7 @@ extension GoogleAuthManager {
// MARK: - Tokens
extension GoogleAuthManager {
func getCachedOrRefreshedIdToken(minExpiryDuration: Double = 0, email: String?) async throws -> String {
guard let idToken = idToken(for: email) else { throw (IdTokenError.missingToken) }
guard let idToken = try idToken(for: email) else { throw (IdTokenError.missingToken) }

let decodedToken = try decode(idToken: idToken)

Expand Down Expand Up @@ -282,7 +286,7 @@ extension GoogleAuthManager {

private func performTokenRefresh(email: String?) async throws -> (accessToken: String, idToken: String) {
return try await withCheckedThrowingContinuation { continuation in
let authorization = authorization(for: email)
let authorization = try? authorization(for: email)
authorization?.authState.setNeedsTokenRefresh()
authorization?.authState.performAction { accessToken, idToken, error in
guard let accessToken, let idToken else {
Expand All @@ -299,10 +303,10 @@ extension GoogleAuthManager {
// MARK: - OIDAuthStateChangeDelegate
extension GoogleAuthManager: OIDAuthStateChangeDelegate {
func didChange(_ state: OIDAuthState) {
let authorization = GTMAppAuthFetcherAuthorization(authState: state)
let authorization = GTMAppAuth.AuthSession(authState: state)
guard let email = authorization.userEmail else {
return
}
saveAuth(state: state, for: email)
try? saveAuth(state: state, for: email)
}
}
3 changes: 3 additions & 0 deletions FlowCrypt/Resources/en.lproj/Localizable.strings
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,9 @@
"message_mark_read_error" = "Could not mark message as read: %@";
"message_reply_all" = "Reply all";
"message_not_found_in_folder" = "Message not found in folder: ";
"message_security_warning_subject" = "Potentially suspicious message";
"message_security_warning_message" = "It wasn't properly verified by the sender, so its authenticity can't be confirmed.
Be careful - avoid clicking links and downloading attachments, or sharing personal info.";

// Passphrase Anti BruteForce Protection
"passphrase_anti_brute_force_protection_hint" = "To protect you and your data, the next attempt will only be possible after the timer below finishes. Please wait until then before trying again.";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ class GmailServiceTest: XCTestCase {

// MARK: - Mock
class GoogleAuthManagerMock: GoogleAuthManagerType {
func authorization(for email: String?) -> GTMAppAuthFetcherAuthorization? {
func authorization(for email: String?) -> GTMAppAuth.AuthSession? {
return nil
}

Expand Down
Loading

0 comments on commit 1a51c91

Please sign in to comment.