Skip to content

Commit

Permalink
WIP: Keychain implementation for sharepoint
Browse files Browse the repository at this point in the history
  • Loading branch information
iammajid committed Jan 8, 2025
1 parent 2f3c070 commit 8a197db
Show file tree
Hide file tree
Showing 4 changed files with 116 additions and 5 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,7 @@ class CreateNewVaultCoordinator: AccountListing, CloudChoosing, DefaultShowEditA
return
}
do {
try MicrosoftGraphDriveManager.shared.saveDriveToKeychain(drive, for: account.accountUID)
let credential = MicrosoftGraphCredential.createForSharePoint(with: account.accountUID)
let provider = try MicrosoftGraphCloudProvider(credential: credential, driveIdentifier: drive.identifier)
startFolderChooser(with: provider, account: account.cloudProviderAccount)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ class CryptomatorKeychain: CryptomatorKeychainType {
static let bundleId = CryptomatorConstants.mainAppBundleId
static let box = CryptomatorKeychain(service: "box.auth")
static let pCloud = CryptomatorKeychain(service: "pCloud.auth")
static let microsoftGraph = CryptomatorKeychain(service: "microsoftGraph.auth")
static let s3 = CryptomatorKeychain(service: "s3.auth")
static let webDAV = CryptomatorKeychain(service: "webDAV.auth")
static let localFileSystem = CryptomatorKeychain(service: "localFileSystem.auth")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
// Copyright © 2020 Skymatic GmbH. All rights reserved.
//

import Combine
import CryptomatorCloudAccessCore
import Foundation
import PCloudSDKSwift
Expand All @@ -30,11 +31,13 @@ public class CloudProviderDBManager: CloudProviderManager, CloudProviderUpdating
static var cachedProvider = [CachedProvider]()
public static let shared = CloudProviderDBManager(accountManager: CloudProviderAccountDBManager.shared)
let accountManager: CloudProviderAccountDBManager
let driveManager: MicrosoftGraphDriveManaging

private let maxPageSizeForFileProvider = 500

init(accountManager: CloudProviderAccountDBManager) {
init(accountManager: CloudProviderAccountDBManager, driveManager: MicrosoftGraphDriveManaging = MicrosoftGraphDriveManager.shared) {
self.accountManager = accountManager
self.driveManager = driveManager
}

public func getProvider(with accountUID: String) throws -> CloudProvider {
Expand Down Expand Up @@ -88,8 +91,12 @@ public class CloudProviderDBManager: CloudProviderManager, CloudProviderUpdating
let credential = try getS3Credential(for: accountUID)
provider = try S3CloudProvider(credential: credential)
case .sharePoint:
let credential = MicrosoftGraphCredential.createForSharePoint(with: accountUID)
provider = try MicrosoftGraphCloudProvider(credential: credential, maxPageSize: .max)
let allDrives = try driveManager.getDrivesFromKeychain(for: accountUID)
guard let drive = allDrives.first else {
throw CloudProviderError.itemNotFound
}
let (credential, driveID) = try getSharePointCredentialAndDriveIdentifier(for: accountUID, driveID: drive.identifier)
provider = try MicrosoftGraphCloudProvider(credential: credential, driveIdentifier: driveID, maxPageSize: .max)
case .webDAV:
let credential = try getWebDAVCredential(for: accountUID)
let client = WebDAVClient(credential: credential)
Expand Down Expand Up @@ -142,8 +149,12 @@ public class CloudProviderDBManager: CloudProviderManager, CloudProviderUpdating
let credential = try getS3Credential(for: accountUID)
provider = try S3CloudProvider.withBackgroundSession(credential: credential, sharedContainerIdentifier: CryptomatorConstants.appGroupName)
case .sharePoint:
let credential = MicrosoftGraphCredential.createForSharePoint(with: accountUID)
provider = try MicrosoftGraphCloudProvider.withBackgroundSession(credential: credential, maxPageSize: maxPageSizeForFileProvider, sessionIdentifier: sessionIdentifier)
let allDrives = try driveManager.getDrivesFromKeychain(for: accountUID)
guard let drive = allDrives.first else {
throw CloudProviderError.itemNotFound
}
let (credential, driveID) = try getSharePointCredentialAndDriveIdentifier(for: accountUID, driveID: drive.identifier)
provider = try MicrosoftGraphCloudProvider.withBackgroundSession(credential: credential, driveIdentifier: driveID, maxPageSize: maxPageSizeForFileProvider, sessionIdentifier: sessionIdentifier)
case .webDAV:
let credential = try getWebDAVCredential(for: accountUID)
let client = WebDAVClient.withBackgroundSession(credential: credential, sessionIdentifier: sessionIdentifier, sharedContainerIdentifier: CryptomatorConstants.appGroupName)
Expand All @@ -159,6 +170,14 @@ public class CloudProviderDBManager: CloudProviderManager, CloudProviderUpdating
return provider
}

private func getSharePointCredentialAndDriveIdentifier(for accountUID: String, driveID: String) throws -> (MicrosoftGraphCredential, String) {
guard let drive = try driveManager.getDriveFromKeychain(for: accountUID, driveID: driveID) else {
throw CloudProviderError.itemNotFound
}
let credential = MicrosoftGraphCredential(identifier: drive.identifier, scopes: MicrosoftGraphScopes.sharePoint)
return (credential, drive.identifier)
}

private func getS3Credential(for accountUID: String) throws -> S3Credential {
guard let credential = S3CredentialManager.shared.getCredential(with: accountUID) else {
throw CloudProviderAccountError.accountNotFoundError
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
//
// MicrosoftGraphDriveManager.swift
// CryptomatorCommon
//
// Created by Majid Achhoud on 07.01.25.
//

import Foundation
import CryptomatorCloudAccessCore
import Combine

public enum MicrosoftGraphDriveManagerError: Error {
case driveDuplicate(existingIdentifier: String)
}

public protocol MicrosoftGraphDriveManaging {
var didUpdate: AnyPublisher<Void, Never> { get }

func getDrivesFromKeychain(for accountUID: String) throws -> [MicrosoftGraphDrive]
func saveDriveToKeychain(_ drive: MicrosoftGraphDrive, for accountUID: String) throws
func removeDriveFromKeychain(with accountUID: String, driveIdentifier: String) throws
func getDriveFromKeychain(for accountUID: String, driveID: String) throws -> MicrosoftGraphDrive?
}

public struct MicrosoftGraphDriveManager: MicrosoftGraphDriveManaging {
public static let shared = MicrosoftGraphDriveManager(keychain: CryptomatorKeychain.microsoftGraph)

public var didUpdate: AnyPublisher<Void, Never> {
didUpdatePublisher.eraseToAnyPublisher()
}

private let keychain: CryptomatorKeychainType
private let didUpdatePublisher = PassthroughSubject<Void, Never>()

public func getDrivesFromKeychain(for accountUID: String) throws -> [MicrosoftGraphDrive] {
return try keychain.getDriveIdentifiers(for: accountUID) ?? []
}

public func saveDriveToKeychain(_ drive: MicrosoftGraphDrive, for accountUID: String) throws {
var allDrives = try getDrivesFromKeychain(for: accountUID)

if allDrives.contains(where: { $0.identifier == drive.identifier }) {
throw MicrosoftGraphDriveManagerError.driveDuplicate(existingIdentifier: drive.identifier)
}

allDrives.append(drive)
try keychain.setDriveIdentifiers(allDrives, for: accountUID)
didUpdatePublisher.send(())
}

public func removeDriveFromKeychain(with accountUID: String, driveIdentifier: String) throws {
var allDrives = try getDrivesFromKeychain(for: accountUID)
allDrives.removeAll { $0.identifier == driveIdentifier }
try keychain.setDriveIdentifiers(allDrives, for: accountUID)
didUpdatePublisher.send(())
}

public func getDriveFromKeychain(for accountUID: String, driveID: String) throws -> MicrosoftGraphDrive? {
let allDrives = try getDrivesFromKeychain(for: accountUID)
return allDrives.first(where: { $0.identifier == driveID })
}
}

extension CryptomatorKeychainType {
func getDriveIdentifiers(for accountUID: String) throws -> [MicrosoftGraphDrive]? {
let driveKey = "driveIdentifiers_\(accountUID)"
guard let data = getAsData(driveKey) else {
return nil
}
do {
let jsonDecoder = JSONDecoder()
let drives = try jsonDecoder.decode([MicrosoftGraphDrive].self, from: data)
return drives
} catch {
return nil
}
}

func setDriveIdentifiers(_ drives: [MicrosoftGraphDrive], for accountUID: String) throws {
let driveKey = "driveIdentifiers_\(accountUID)"
let jsonEncoder = JSONEncoder()
let encodedDrives = try jsonEncoder.encode(drives)
try set(driveKey, value: encodedDrives)
}

func getDrive(by driveID: String, for accountUID: String) throws -> MicrosoftGraphDrive? {
let allDrives = try getDriveIdentifiers(for: accountUID) ?? []
return allDrives.first(where: { $0.identifier == driveID })
}
}

0 comments on commit 8a197db

Please sign in to comment.