Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added Box support #352

Merged
merged 16 commits into from
Sep 11, 2024
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Cryptomator.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -2466,7 +2466,7 @@
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "if [ -f ./fastlane/scripts/.cloud-access-secrets.sh ]; then\n source ./fastlane/scripts/.cloud-access-secrets.sh \"${CONFIG_NAME}\"\nelse\n echo \"warning: .cloud-access-secrets.sh could not be found, please see README for instructions\"\nfi\n/usr/libexec/PlistBuddy -c \"Add :CFBundleURLTypes:1:CFBundleURLSchemes:0 string db-${DROPBOX_APP_KEY}\" \"${TARGET_BUILD_DIR}/${INFOPLIST_PATH}\"\n/usr/libexec/PlistBuddy -c \"Add :CFBundleURLTypes:1:CFBundleURLSchemes:1 string ${GOOGLE_DRIVE_REDIRECT_URL_SCHEME}\" \"${TARGET_BUILD_DIR}/${INFOPLIST_PATH}\"\n/usr/libexec/PlistBuddy -c \"Add :CFBundleURLTypes:1:CFBundleURLSchemes:2 string ${ONEDRIVE_REDIRECT_URI_SCHEME}\" \"${TARGET_BUILD_DIR}/${INFOPLIST_PATH}\"\n/usr/libexec/PlistBuddy -c \"Add :CFBundleURLTypes:1:CFBundleURLSchemes:3 string ${HUB_REDIRECT_URI_SCHEME}\" \"${TARGET_BUILD_DIR}/${INFOPLIST_PATH}\"\necho \"Updated ${TARGET_BUILD_DIR}/${INFOPLIST_PATH} by adding URL schemes\"\n";
shellScript = "if [ -f ./fastlane/scripts/.cloud-access-secrets.sh ]; then\n source ./fastlane/scripts/.cloud-access-secrets.sh \"${CONFIG_NAME}\"\nelse\n echo \"warning: .cloud-access-secrets.sh could not be found, please see README for instructions\"\nfi\n/usr/libexec/PlistBuddy -c \"Add :CFBundleURLTypes:1:CFBundleURLSchemes:0 string db-${DROPBOX_APP_KEY}\" \"${TARGET_BUILD_DIR}/${INFOPLIST_PATH}\"\n/usr/libexec/PlistBuddy -c \"Add :CFBundleURLTypes:1:CFBundleURLSchemes:1 string ${GOOGLE_DRIVE_REDIRECT_URL_SCHEME}\" \"${TARGET_BUILD_DIR}/${INFOPLIST_PATH}\"\n/usr/libexec/PlistBuddy -c \"Add :CFBundleURLTypes:1:CFBundleURLSchemes:2 string ${ONEDRIVE_REDIRECT_URI_SCHEME}\" \"${TARGET_BUILD_DIR}/${INFOPLIST_PATH}\"\n/usr/libexec/PlistBuddy -c \"Add :CFBundleURLTypes:1:CFBundleURLSchemes:3 string ${HUB_REDIRECT_URI_SCHEME}\" \"${TARGET_BUILD_DIR}/${INFOPLIST_PATH}\"\n/usr/libexec/PlistBuddy -c \"Add :CFBundleURLTypes:1:CFBundleURLSchemes:4 string ${BOX_REDIRECT_URI_SCHEME}\" \"${TARGET_BUILD_DIR}/${INFOPLIST_PATH}\"\necho \"Updated ${TARGET_BUILD_DIR}/${INFOPLIST_PATH} by adding URL schemes\"\n";
iammajid marked this conversation as resolved.
Show resolved Hide resolved
};
742595D72552EE0000A8A008 /* Set Build Number */ = {
isa = PBXShellScriptBuildPhase;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,8 @@
"kind" : "remoteSourceControl",
"location" : "https://github.com/aws-amplify/aws-sdk-ios-spm.git",
"state" : {
"revision" : "59fdc9ca7ff3f5d38e07af27526a527c199b8de6",
"version" : "2.33.7"
"revision" : "8ff8bebfe24271f7b16c5abaeb78daf82bee3a80",
"version" : "2.34.2"
}
},
{
Expand All @@ -54,13 +54,22 @@
"version" : "1.14.0"
}
},
{
"identity" : "box-ios-sdk",
"kind" : "remoteSourceControl",
"location" : "https://github.com/box/box-ios-sdk.git",
"state" : {
"revision" : "daffd86b861a5f5882655bf7a01b891f6b808c1f",
"version" : "5.5.0"
}
},
{
"identity" : "cloud-access-swift",
"kind" : "remoteSourceControl",
"location" : "https://github.com/cryptomator/cloud-access-swift.git",
"state" : {
"revision" : "63fd1cfee9e4d1c0a8d585dd0c7008eb37d2f037",
"version" : "1.9.0"
"branch" : "feature/boxcloud-integration",
"revision" : "b8f691fc35531689ee8122f1e11d0c2d9135ab40"
}
},
{
Expand Down Expand Up @@ -95,17 +104,17 @@
"kind" : "remoteSourceControl",
"location" : "https://github.com/phil1995/dropbox-sdk-obj-c-spm.git",
"state" : {
"revision" : "f0eafe25d26c52377c4a1c08f1dbd77320164994",
"version" : "7.0.0"
"revision" : "87c1fcf96622ab90a956bdf89331ddb4164f4855",
"version" : "7.2.0"
}
},
{
"identity" : "google-api-objectivec-client-for-rest",
"kind" : "remoteSourceControl",
"location" : "https://github.com/google/google-api-objectivec-client-for-rest.git",
"state" : {
"revision" : "40930b2c3add6234b8be1a780c08cf88b6a7a1f7",
"version" : "3.2.0"
"revision" : "bcb0439b37d16d39da6f62139d4009d09e7aef14",
"version" : "3.4.0"
}
},
{
Expand Down Expand Up @@ -149,8 +158,8 @@
"kind" : "remoteSourceControl",
"location" : "https://github.com/AzureAD/microsoft-authentication-library-for-objc.git",
"state" : {
"revision" : "e9ef281b2f281c3ba2d32608138b1431cba5e4df",
"version" : "1.2.20"
"revision" : "71a4d3c476e5cda907f33f155550faa5ad9a0fb2",
"version" : "1.3.2"
}
},
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ class CreateNewVaultCoordinator: AccountListing, CloudChoosing, DefaultShowEditA
}

func start() {
let viewModel = ChooseCloudViewModel(clouds: [.localFileSystem(type: .iCloudDrive), .dropbox, .googleDrive, .oneDrive, .pCloud, .webDAV(type: .custom), .s3(type: .custom), .localFileSystem(type: .custom)], headerTitle: LocalizedString.getValue("addVault.createNewVault.chooseCloud.header"))
let viewModel = ChooseCloudViewModel(clouds: [.localFileSystem(type: .iCloudDrive), .dropbox, .googleDrive, .oneDrive, .pCloud, .box, .webDAV(type: .custom), .s3(type: .custom), .localFileSystem(type: .custom)], headerTitle: LocalizedString.getValue("addVault.createNewVault.chooseCloud.header"))
let chooseCloudVC = ChooseCloudViewController(viewModel: viewModel)
chooseCloudVC.title = LocalizedString.getValue("addVault.createNewVault.title")
chooseCloudVC.coordinator = self
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ class OpenExistingVaultCoordinator: AccountListing, CloudChoosing, DefaultShowEd
}

func start() {
let viewModel = ChooseCloudViewModel(clouds: [.localFileSystem(type: .iCloudDrive), .dropbox, .googleDrive, .oneDrive, .pCloud, .webDAV(type: .custom), .s3(type: .custom), .localFileSystem(type: .custom)], headerTitle: LocalizedString.getValue("addVault.openExistingVault.chooseCloud.header"))
let viewModel = ChooseCloudViewModel(clouds: [.localFileSystem(type: .iCloudDrive), .dropbox, .googleDrive, .oneDrive, .pCloud, .box, .webDAV(type: .custom), .s3(type: .custom), .localFileSystem(type: .custom)], headerTitle: LocalizedString.getValue("addVault.openExistingVault.chooseCloud.header"))
let chooseCloudVC = ChooseCloudViewController(viewModel: viewModel)
chooseCloudVC.title = LocalizedString.getValue("addVault.openExistingVault.title")
chooseCloudVC.coordinator = self
Expand Down
3 changes: 3 additions & 0 deletions Cryptomator/AppDelegate.swift
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
// Copyright © 2020 Skymatic GmbH. All rights reserved.
//

import BoxSDK
import CocoaLumberjackSwift
import CryptomatorCloudAccess
import CryptomatorCloudAccessCore
Expand Down Expand Up @@ -41,6 +42,8 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
CloudProviderDBManager.shared.useBackgroundSession = false
DropboxSetup.constants = DropboxSetup(appKey: CloudAccessSecrets.dropboxAppKey, sharedContainerIdentifier: nil, keychainService: CryptomatorConstants.mainAppBundleId, forceForegroundSession: true)
GoogleDriveSetup.constants = GoogleDriveSetup(clientId: CloudAccessSecrets.googleDriveClientId, redirectURL: CloudAccessSecrets.googleDriveRedirectURL!, sharedContainerIdentifier: nil)
BoxSetup.constants = BoxSetup(clientId: CloudAccessSecrets.boxClientId, clientSecret: CloudAccessSecrets.boxClientSecret, sharedContainerIdentifier: nil)

let oneDriveConfiguration = MSALPublicClientApplicationConfig(clientId: CloudAccessSecrets.oneDriveClientId, redirectUri: CloudAccessSecrets.oneDriveRedirectURI, authority: nil)
oneDriveConfiguration.cacheConfig.keychainSharingGroup = CryptomatorConstants.mainAppBundleId
do {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,14 @@ import Foundation
import Promises
import UIKit

class AccountListViewController: ListViewController<AccountCellContent> {
class AccountListViewController: ListViewController<AccountCellContent>, ASWebAuthenticationPresentationContextProviding {
func presentationAnchor(for session: ASWebAuthenticationSession) -> ASPresentationAnchor {
guard let window = UIApplication.shared.windows.first else {
fatalError("No window could be found.")
}
return window
}

weak var coordinator: (Coordinator & AccountListing)?
private let viewModel: AccountListViewModelProtocol

Expand All @@ -28,6 +35,7 @@ class AccountListViewController: ListViewController<AccountCellContent> {
override func viewDidLoad() {
super.viewDidLoad()
title = viewModel.title
updateAddButtonStatus()
}

override func setEditing(_ editing: Bool, animated: Bool) {
Expand Down Expand Up @@ -66,6 +74,7 @@ class AccountListViewController: ListViewController<AccountCellContent> {
self.handleLogout(sender)
}.always {
sender.setSelected(false)
self.updateAddButtonStatus()
}
})
let cancelAction = UIAlertAction(title: LocalizedString.getValue("common.button.cancel"), style: .cancel, handler: { _ in
Expand All @@ -87,10 +96,21 @@ class AccountListViewController: ListViewController<AccountCellContent> {
}

@objc func addNewAccount() {
updateAddButtonStatus()
if viewModel.accountInfos.contains(where: { $0.cloudProviderType == .box }) {
return
}
setEditing(false, animated: true)
coordinator?.showAddAccount(for: viewModel.cloudProviderType, from: self)
}

private func updateAddButtonStatus() {
if viewModel.cloudProviderType == .box {
let hasBoxAccount = viewModel.accountInfos.contains(where: { $0.cloudProviderType == .box })
navigationItem.rightBarButtonItem?.isEnabled = !hasBoxAccount
}
}

// MARK: - UITableViewDelegate

override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
Expand All @@ -111,6 +131,7 @@ class AccountListViewController: ListViewController<AccountCellContent> {
}
do {
try removeRow(at: indexPath)
updateAddButtonStatus()
} catch {
handleError(error)
}
Expand All @@ -130,7 +151,7 @@ class AccountListViewController: ListViewController<AccountCellContent> {

private func supportsEditing(_ cloudProviderType: CloudProviderType) -> Bool {
switch cloudProviderType {
case .dropbox, .googleDrive, .localFileSystem, .oneDrive, .pCloud:
case .dropbox, .googleDrive, .localFileSystem, .oneDrive, .pCloud, .box:
return false
case .s3, .webDAV:
return true
Expand All @@ -140,6 +161,7 @@ class AccountListViewController: ListViewController<AccountCellContent> {

#if DEBUG

import AuthenticationServices
import Combine
import CryptomatorCommonCore
import Promises
Expand Down
43 changes: 33 additions & 10 deletions Cryptomator/Common/CloudAccountList/AccountListViewModel.swift
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ class AccountListViewModel: AccountListViewModelProtocol {

private(set) var accounts = [AccountCellContent]()
private(set) var accountInfos = [AccountInfo]()
private(set) var boxCredentials = [String: BoxCredential]()
tobihagemann marked this conversation as resolved.
Show resolved Hide resolved
var title: String {
cloudProviderType.localizedString()
}
Expand Down Expand Up @@ -66,20 +67,34 @@ class AccountListViewModel: AccountListViewModelProtocol {
}
}

func refreshBoxItems() -> Promise<Void> {
return all(accountInfos
.map { accountInfo in
let tokenStore = BoxTokenStore()
let credential = BoxCredential(tokenStore: tokenStore)
boxCredentials[accountInfo.accountUID] = credential // Workaround: Fixing lifecycle issue with Box credential/client
return credential
}
.map { self.createAccountCellContent(for: $0) }
).then { accounts in
self.accounts = accounts
}
}

func createAccountCellContent(from accountInfo: AccountInfo) throws -> AccountCellContent {
switch cloudProviderType {
case .dropbox:
let credential = DropboxCredential(tokenUID: accountInfo.accountUID)
return createAccountCellContentPlaceholder(for: credential)
return createAccountCellContentPlaceholder()
case .googleDrive:
let credential = GoogleDriveCredential(userID: accountInfo.accountUID)
return try createAccountCellContent(for: credential)
case .oneDrive:
let credential = try OneDriveCredential(with: accountInfo.accountUID)
return try createAccountCellContent(for: credential)
case .pCloud:
let credential = try PCloudCredential(userID: accountInfo.accountUID)
return createAccountCellContentPlaceholder(for: credential)
return createAccountCellContentPlaceholder()
case .box:
return createAccountCellContentPlaceholder()
case .webDAV:
guard let credential = WebDAVCredentialManager.shared.getCredentialFromKeychain(with: accountInfo.accountUID) else {
throw CloudProviderAccountError.accountNotFoundError
Expand All @@ -96,16 +111,16 @@ class AccountListViewModel: AccountListViewModelProtocol {
}
}

private func createAccountCellContentPlaceholder() -> AccountCellContent {
return AccountCellContent(mainLabelText: "(…)", detailLabelText: nil)
}

private func createAccountCellContent(for credential: DropboxCredential) -> Promise<AccountCellContent> {
return credential.getUsername().then { username in
AccountCellContent(mainLabelText: username, detailLabelText: nil)
}
}

private func createAccountCellContentPlaceholder(for credential: DropboxCredential) -> AccountCellContent {
return AccountCellContent(mainLabelText: "(…)", detailLabelText: nil)
}

private func createAccountCellContent(for credential: GoogleDriveCredential) throws -> AccountCellContent {
let username = try credential.getUsername()
return AccountCellContent(mainLabelText: username, detailLabelText: nil)
Expand All @@ -122,8 +137,10 @@ class AccountListViewModel: AccountListViewModelProtocol {
}
}

private func createAccountCellContentPlaceholder(for credential: PCloudCredential) -> AccountCellContent {
return AccountCellContent(mainLabelText: "(…)", detailLabelText: nil)
func createAccountCellContent(for credential: BoxCredential) -> Promise<AccountCellContent> {
return credential.getUsername().then { username in
AccountCellContent(mainLabelText: username, detailLabelText: nil)
}
}

func createAccountCellContent(for credential: WebDAVCredential) -> AccountCellContent {
Expand Down Expand Up @@ -203,6 +220,12 @@ class AccountListViewModel: AccountListViewModelProtocol {
}.catch { error in
self.databaseChangedPublisher.send(.failure(error))
}
} else if self.cloudProviderType == .box {
self.refreshBoxItems().then {
self.databaseChangedPublisher.send(.success(self.accounts))
}.catch { error in
self.databaseChangedPublisher.send(.failure(error))
}
}
})
return databaseChangedPublisher.eraseToAnyPublisher()
Expand Down
15 changes: 15 additions & 0 deletions Cryptomator/Common/CloudAuthenticator.swift
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,15 @@ class CloudAuthenticator {
}
}

func authenticateBox(from viewController: UIViewController) -> Promise<CloudProviderAccount> {
let tokenStore = BoxTokenStore()
return BoxAuthenticator.authenticate(from: viewController, tokenStore: tokenStore).then { _, userId -> CloudProviderAccount in
let account = CloudProviderAccount(accountUID: userId, cloudProviderType: .box)
try self.accountManager.saveNewAccount(account)
return account
}
}

func authenticateWebDAV(from viewController: UIViewController) -> Promise<CloudProviderAccount> {
return WebDAVAuthenticator.authenticate(from: viewController).then { credential -> CloudProviderAccount in
let account = CloudProviderAccount(accountUID: credential.identifier, cloudProviderType: .webDAV(type: .custom))
Expand All @@ -79,6 +88,8 @@ class CloudAuthenticator {

func authenticate(_ cloudProviderType: CloudProviderType, from viewController: UIViewController) -> Promise<CloudProviderAccount> {
switch cloudProviderType {
case .box:
return authenticateBox(from: viewController)
case .dropbox:
return authenticateDropbox(from: viewController)
case .googleDrive:
Expand All @@ -98,6 +109,10 @@ class CloudAuthenticator {

func deauthenticate(account: CloudProviderAccount) throws {
switch account.cloudProviderType {
case .box:
let tokenStore = BoxTokenStore()
let credential = BoxCredential(tokenStore: tokenStore)
credential.deauthenticate()
case .dropbox:
let credential = DropboxCredential(tokenUID: account.accountUID)
credential.deauthenticate()
Expand Down
2 changes: 2 additions & 0 deletions Cryptomator/Common/CloudProviderType+Localization.swift
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ import Foundation
extension CloudProviderType {
func localizedString() -> String {
switch self {
case .box:
return "Box"
case .dropbox:
return "Dropbox"
case .googleDrive:
Expand Down
4 changes: 4 additions & 0 deletions Cryptomator/Common/UIImage+CloudProviderType.swift
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ extension UIImage {
convenience init?(vaultIconFor cloudProviderType: CloudProviderType, state: UIImage.State) {
var assetName: String
switch cloudProviderType {
case .box:
assetName = "dropbox-vault" // TODO: Replace with box-vault when asset is available
case .dropbox:
assetName = "dropbox-vault"
case .googleDrive:
Expand Down Expand Up @@ -51,6 +53,8 @@ extension UIImage {
convenience init?(storageIconFor cloudProviderType: CloudProviderType) {
var assetName: String
switch cloudProviderType {
case .box:
assetName = "dropbox" // TODO: Replace with box when asset is available
case .dropbox:
assetName = "dropbox"
case .googleDrive:
Expand Down
2 changes: 1 addition & 1 deletion Cryptomator/Settings/SettingsCoordinator.swift
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ class SettingsCoordinator: Coordinator {
}

func showCloudServices() {
let viewModel = ChooseCloudViewModel(clouds: [.dropbox, .googleDrive, .oneDrive, .pCloud, .webDAV(type: .custom), .s3(type: .custom)], headerTitle: "")
let viewModel = ChooseCloudViewModel(clouds: [.box, .dropbox, .googleDrive, .oneDrive, .pCloud, .webDAV(type: .custom), .s3(type: .custom)], headerTitle: "")
tobihagemann marked this conversation as resolved.
Show resolved Hide resolved
let chooseCloudVC = ChooseCloudViewController(viewModel: viewModel)
chooseCloudVC.title = LocalizedString.getValue("settings.cloudServices")
chooseCloudVC.coordinator = self
Expand Down
13 changes: 13 additions & 0 deletions Cryptomator/VaultDetail/VaultDetailInfoFooterViewModel.swift
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,11 @@ class VaultDetailInfoFooterViewModel: BindableAttributedTextHeaderFooterViewMode

func getUsername() -> String? {
switch vault.cloudProviderType {
case .box:
let tokenStore = BoxTokenStore()
let credential = BoxCredential(tokenStore: tokenStore)
getUsername(for: credential)
return "(…)"
case .dropbox:
let credential = DropboxCredential(tokenUID: vault.delegateAccountUID)
getUsername(for: credential)
Expand Down Expand Up @@ -88,4 +93,12 @@ class VaultDetailInfoFooterViewModel: BindableAttributedTextHeaderFooterViewMode
self.attributedText.value = attributedText
}
}

func getUsername(for credential: BoxCredential) {
credential.getUsername().then { username in
let loggedInText = self.createLoggedInText(forUsername: username)
let attributedText = self.createAttributedText(loggedInText: loggedInText)
self.attributedText.value = attributedText
}
}
}
2 changes: 1 addition & 1 deletion CryptomatorCommon/Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ let package = Package(
)
],
dependencies: [
.package(url: "https://github.com/cryptomator/cloud-access-swift.git", .upToNextMinor(from: "1.9.0")),
.package(url: "https://github.com/cryptomator/cloud-access-swift.git", branch: "feature/boxcloud-integration"),
.package(url: "https://github.com/CocoaLumberjack/CocoaLumberjack.git", .upToNextMinor(from: "3.8.0")),
.package(url: "https://github.com/PhilLibs/simple-swift-dependencies", .upToNextMajor(from: "0.1.0")),
.package(url: "https://github.com/siteline/SwiftUI-Introspect.git", .upToNextMajor(from: "0.3.0")),
Expand Down
Loading
Loading