Skip to content

Commit

Permalink
Streamlined API to be more consistent between different cloud services
Browse files Browse the repository at this point in the history
  • Loading branch information
tobihagemann committed Apr 18, 2024
1 parent 1b974b7 commit c25cb21
Show file tree
Hide file tree
Showing 14 changed files with 102 additions and 77 deletions.
4 changes: 4 additions & 0 deletions CryptomatorCloudAccess.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,7 @@
748BD4CC24B4D3820001CA8C /* Date+RFC822.swift in Sources */ = {isa = PBXBuildFile; fileRef = 748BD4CB24B4D3820001CA8C /* Date+RFC822.swift */; };
7494505F24BC5C3300149816 /* PropfindResponseParserTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7494505E24BC5C3300149816 /* PropfindResponseParserTests.swift */; };
7494B06D249CEAC500430610 /* VaultFormat7ShorteningProviderDecoratorTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7494B06C249CEAC500430610 /* VaultFormat7ShorteningProviderDecoratorTests.swift */; };
749F59A52BD1211500AB299A /* PCloudSetup.swift in Sources */ = {isa = PBXBuildFile; fileRef = 749F59A42BD1211500AB299A /* PCloudSetup.swift */; };
74B321E42923F979008E7C01 /* HTTPDebugLogger.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74B321E32923F979008E7C01 /* HTTPDebugLogger.swift */; };
74C0FB2729B209B6008EF811 /* S3Authenticator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74C0FB2629B209B6008EF811 /* S3Authenticator.swift */; };
74C596E824F022AF00FFD17E /* CloudPath.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74C596E724F022AF00FFD17E /* CloudPath.swift */; };
Expand Down Expand Up @@ -342,6 +343,7 @@
748BD4CB24B4D3820001CA8C /* Date+RFC822.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Date+RFC822.swift"; sourceTree = "<group>"; };
7494505E24BC5C3300149816 /* PropfindResponseParserTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PropfindResponseParserTests.swift; sourceTree = "<group>"; };
7494B06C249CEAC500430610 /* VaultFormat7ShorteningProviderDecoratorTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VaultFormat7ShorteningProviderDecoratorTests.swift; sourceTree = "<group>"; };
749F59A42BD1211500AB299A /* PCloudSetup.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PCloudSetup.swift; sourceTree = "<group>"; };
74B321E32923F979008E7C01 /* HTTPDebugLogger.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HTTPDebugLogger.swift; sourceTree = "<group>"; };
74C0FB2629B209B6008EF811 /* S3Authenticator.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = S3Authenticator.swift; sourceTree = "<group>"; };
74C596E724F022AF00FFD17E /* CloudPath.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CloudPath.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -827,6 +829,7 @@
746F091A27BD029C003FCD9F /* PCloudError.swift */,
746F091827BD0077003FCD9F /* PCloudIdentifierCache.swift */,
746F091627BD006D003FCD9F /* PCloudItem.swift */,
749F59A42BD1211500AB299A /* PCloudSetup.swift */,
74073D1827C9406000A86C9A /* Task+Promises.swift */,
);
path = PCloud;
Expand Down Expand Up @@ -1258,6 +1261,7 @@
4A0785362859FA820015DAE1 /* AWSEndpoint+CustomRegionName.swift in Sources */,
4A765CA32878596000794440 /* DirectoryContentCache.swift in Sources */,
746F091927BD0077003FCD9F /* PCloudIdentifierCache.swift in Sources */,
749F59A52BD1211500AB299A /* PCloudSetup.swift in Sources */,
4AC75F9028607B20002731FE /* CustomAWSEndpointRegionNameStorage.swift in Sources */,
4A05900E2451A145008831F9 /* CloudItemList.swift in Sources */,
9EE62A0D247D54760089DAF7 /* CloudProvider+Convenience.swift in Sources */,
Expand Down
60 changes: 38 additions & 22 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ The API is implemented once for each cloud. It also forms the foundation for dec
You can use [Swift Package Manager](https://swift.org/package-manager/ "Swift Package Manager").

```swift
.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", .upToNextMinor(from: "1.10.0"))
```

## Usage
Expand Down Expand Up @@ -151,17 +151,24 @@ GoogleDriveAuthenticator.authenticate(credential: credential, from: viewControll
You can then use the credential to create a Google Drive provider:

```swift
let useBackgroundSession = ... // optional: only needed if you want to create a `GoogleDriveProvider` with a background `URLSession`, defaults to `false`
let provider = GoogleDriveCloudProvider(credential: credential, useBackgroundSession: useBackgroundSession)
let provider = GoogleDriveCloudProvider(credential: credential)
```

Or create a Google Drive provider using a background URLSession:

```swift
let sessionIdentifier = ...
let provider = GoogleDriveCloudProvider.withBackgroundSession(credential: credential, sessionIdentifier: sessionIdentifier)
```

### OneDrive

Set up the `Info.plist` and your app delegate as described in [MSAL](https://github.com/AzureAD/microsoft-authentication-library-for-objc). In addition, the following constants must be set once, e.g. in your app delegate:

```swift
OneDriveSetup.sharedContainerIdentifier = ... // optional: only needed if you want to create a `OneDriveProvider` with a background `URLSession` in an app extension
OneDriveSetup.clientApplication = ... // your `MSALPublicClientApplication`
let clientApplication = ... // your `MSALPublicClientApplication`
let sharedContainerIdentifier = ... // optional: only needed if you want to create a `OneDriveProvider` with a background `URLSession` in an app extension
OneDriveSetup.constants = OneDriveSetup(clientApplication: clientApplication, sharedContainerIdentifier: sharedContainerIdentifier)
```

Begin the authentication flow:
Expand All @@ -179,12 +186,26 @@ OneDriveAuthenticator.authenticate(from: viewController).then { credential in
You can then use the credential to create a OneDrive provider:

```swift
let useBackgroundSession = ... // optional: only needed if you want to create a `OneDriveProvider` with a background `URLSession`, defaults to `false`
let provider = OneDriveCloudProvider(credential: credential, useBackgroundSession: useBackgroundSession)
let provider = OneDriveCloudProvider(credential: credential)
```

Or create a OneDrive provider using a background URLSession:

```swift
let sessionIdentifier = ...
let provider = OneDriveCloudProvider.withBackgroundSession(credential: credential, sessionIdentifier: sessionIdentifier)
```

### pCloud

The following constants must be set once, e.g. in your app delegate:

```swift
let appKey = ... // your pCloud app key
let sharedContainerIdentifier = ... // optional: only needed if you want to create a `OneDriveProvider` with a background `URLSession` in an app extension
PCloudSetup.constants = PCloudSetup(appKey: appKey, sharedContainerIdentifier: sharedContainerIdentifier)
```

Begin the authentication flow:

```swift
Expand All @@ -197,20 +218,18 @@ PCloudAuthenticator.authenticate(from: viewController).then { credential in
}
```

You can then use the credential to create a pCloud provider.

Create a pCloud provider with a pCloud client:
You can then use the credential to create a pCloud provider with a pCloud client:

```swift
let client = PCloud.createClient(with: credential.user)
let provider = PCloudCloudProvider(client: client)
```

Create a pCloud provider with a pCloud client using a background URLSession:
Or create a pCloud provider with a pCloud client using a background URLSession:

```swift
let sharedContainerIdentifier = ... // optional: only needed if you want to create a `PCloudCloudProvider` in an app extension
let client = PCloud.createBackgroundClient(with: credential.user, sharedContainerIdentifier: sharedContainerIdentifier)
let sessionIdentifier = ...
let client = PCloud.createBackgroundClient(with: credential.user, sessionIdentifier: sessionIdentifier)
let provider = PCloudCloudProvider(client: client)
```

Expand All @@ -228,15 +247,13 @@ let identifier = ... // optional: you might want to give this credential an iden
let credential = S3Credential(accessKey: accessKey, secretKey: secretKey, url: url, bucket: bucket, region: region, identifier: identifier)
```

You can then use the credential to create a S3 provider.

Create a S3 provider with a S3 credential:
You can then use the credential to create a S3 provider:

```swift
let provider = try S3Provider(credential: credential)
```

Create a S3 provider using a background URLSession:
Or create a S3 provider using a background URLSession:

```swift
let sharedContainerIdentifier = ... // optional: only needed if you want to create a `S3CloudProvider` in an app extension
Expand Down Expand Up @@ -267,20 +284,19 @@ let identifier = ... // optional: you might want to give this credential an iden
let credential = WebDAVCredential(baseURL: baseURL, username: username, password: password, allowedCertificate: allowedCertificate, identifier: identifier)
```

You can then use the credential to create a WebDAV provider.

Create a WebDAV provider with a WebDAV client:
You can then use the credential to create a WebDAV provider with a WebDAV client:

```swift
let client = WebDAVClient(credential: credential)
let provider = WebDAVProvider(with: client)
```

Create a WebDAV provider with a WebDAV client using a background URLSession:
Or create a WebDAV provider with a WebDAV client using a background URLSession:

```swift
let sessionIdentifier = ...
let sharedContainerIdentifier = ... // optional: only needed if you want to create a `WebDAVProvider` in an app extension
let client = WebDAVClient.withBackgroundSession(credential: credential, sharedContainerIdentifier: sharedContainerIdentifier)
let client = WebDAVClient.withBackgroundSession(credential: credential, sessionIdentifier: sessionIdentifier, sharedContainerIdentifier: sharedContainerIdentifier)
let provider = WebDAVProvider(with: client)
```

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,9 @@ import Foundation
public class GoogleDriveSetup {
public static var constants: GoogleDriveSetup!

public let clientId: String
public let redirectURL: URL
public let sharedContainerIdentifier: String?
let clientId: String
let redirectURL: URL
let sharedContainerIdentifier: String?

public init(clientId: String, redirectURL: URL, sharedContainerIdentifier: String?) {
self.clientId = clientId
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ public enum OneDriveAuthenticator {
let webviewParameters = MSALWebviewParameters(authPresentationViewController: viewController)
let interactiveParameters = MSALInteractiveTokenParameters(scopes: OneDriveCredential.scopes, webviewParameters: webviewParameters)
interactiveParameters.promptType = .login
return OneDriveSetup.clientApplication.acquireToken(with: interactiveParameters).then { result -> OneDriveCredential in
return OneDriveSetup.constants.clientApplication.acquireToken(with: interactiveParameters).then { result -> OneDriveCredential in
guard let identifier = result.account.identifier else {
throw OneDriveAuthenticatorError.missingAccountIdentifier
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ public class OneDriveCloudProvider: CloudProvider {

public static func withBackgroundSession(credential: OneDriveCredential, maxPageSize: Int = .max, sessionIdentifier: String) throws -> OneDriveCloudProvider {
let configuration = URLSessionConfiguration.background(withIdentifier: sessionIdentifier)
configuration.sharedContainerIdentifier = OneDriveSetup.sharedContainerIdentifier
configuration.sharedContainerIdentifier = OneDriveSetup.constants.sharedContainerIdentifier
return try OneDriveCloudProvider(credential: credential, maxPageSize: maxPageSize, urlSessionConfiguration: configuration)
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,8 @@ public class OneDriveCredential {
private let clientApplication: MSALPublicClientApplication

public convenience init(with identifier: String) throws {
let authProvider = OneDriveAuthenticationProvider(identifier: identifier, clientApplication: OneDriveSetup.clientApplication, scopes: OneDriveCredential.scopes)
try self.init(with: identifier, authProvider: authProvider, clientApplication: OneDriveSetup.clientApplication)
let authProvider = OneDriveAuthenticationProvider(identifier: identifier, clientApplication: OneDriveSetup.constants.clientApplication, scopes: OneDriveCredential.scopes)
try self.init(with: identifier, authProvider: authProvider, clientApplication: OneDriveSetup.constants.clientApplication)
}

init(with identifier: String, authProvider: MSAuthenticationProvider, clientApplication: MSALPublicClientApplication) throws {
Expand Down
13 changes: 10 additions & 3 deletions Sources/CryptomatorCloudAccess/OneDrive/OneDriveSetup.swift
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,14 @@
import Foundation
import MSAL

public enum OneDriveSetup {
public static var clientApplication: MSALPublicClientApplication!
public static var sharedContainerIdentifier: String?
public class OneDriveSetup {
public static var constants: OneDriveSetup!

let clientApplication: MSALPublicClientApplication
let sharedContainerIdentifier: String?

public init(clientApplication: MSALPublicClientApplication, sharedContainerIdentifier: String?) {
self.clientApplication = clientApplication
self.sharedContainerIdentifier = sharedContainerIdentifier
}
}
14 changes: 4 additions & 10 deletions Sources/CryptomatorCloudAccess/PCloud/PCloudAuthenticator.swift
Original file line number Diff line number Diff line change
Expand Up @@ -20,29 +20,23 @@ public enum PCloudAuthenticatorError: Error {
}

public class PCloudAuthenticator {
private let appKey: String

public init(appKey: String) {
self.appKey = appKey
}

public func authenticate(from viewController: UIViewController) -> Promise<PCloudCredential> {
public static func authenticate(from viewController: UIViewController) -> Promise<PCloudCredential> {
let promise: Promise<OAuth.Result>
if #available(iOS 13, *) {
guard let window = viewController.view.window else {
assertionFailure("Cannot present from a view controller that is not part of the view hierarchy.")
return Promise(PCloudAuthenticatorError.missingWindow)
}
promise = OAuth.performAuthorizationFlow(with: window, appKey: appKey)
promise = OAuth.performAuthorizationFlow(with: window, appKey: PCloudSetup.constants.appKey)
} else {
promise = OAuth.performAuthorizationFlow(with: WebViewControllerPresenterMobile(presentingViewController: viewController), appKey: appKey)
promise = OAuth.performAuthorizationFlow(with: WebViewControllerPresenterMobile(presentingViewController: viewController), appKey: PCloudSetup.constants.appKey)
}
return promise.then { result in
return try self.completeAuthorizationFlow(result: result)
}
}

private func completeAuthorizationFlow(result: OAuth.Result) throws -> PCloudCredential {
private static func completeAuthorizationFlow(result: OAuth.Result) throws -> PCloudCredential {
switch result {
case let .success(user):
return PCloudCredential(user: user)
Expand Down
8 changes: 4 additions & 4 deletions Sources/CryptomatorCloudAccess/PCloud/PCloudCredential.swift
Original file line number Diff line number Diff line change
Expand Up @@ -41,15 +41,15 @@ extension PCloud {
- Parameter sharedContainerIdentifier: To create a URL session for use by an app extension, set this property to a valid identifier for a container shared between the app extension and its containing app.
- Returns: An instance of a `PCloudClient` ready to take requests.
*/
public static func createBackgroundClient(with user: OAuth.User, sessionIdentifier: String, sharedContainerIdentifier: String? = nil) -> PCloudClient {
return createBackgroundClient(withAccessToken: user.token, apiHostName: user.httpAPIHostName, sessionIdentifier: sessionIdentifier, sharedContainerIdentifier: sharedContainerIdentifier)
public static func createBackgroundClient(with user: OAuth.User, sessionIdentifier: String) -> PCloudClient {
return createBackgroundClient(withAccessToken: user.token, apiHostName: user.httpAPIHostName, sessionIdentifier: sessionIdentifier)
}

private static func createBackgroundClient(withAccessToken accessToken: String, apiHostName: String, sessionIdentifier: String, sharedContainerIdentifier: String?) -> PCloudClient {
private static func createBackgroundClient(withAccessToken accessToken: String, apiHostName: String, sessionIdentifier: String) -> PCloudClient {
let authenticator = OAuthAccessTokenBasedAuthenticator(accessToken: accessToken)
let eventHub = URLSessionEventHub()
let configuration = URLSessionConfiguration.background(withIdentifier: sessionIdentifier)
configuration.sharedContainerIdentifier = sharedContainerIdentifier
configuration.sharedContainerIdentifier = PCloudSetup.constants.sharedContainerIdentifier
let session = URLSession(configuration: configuration, delegate: eventHub, delegateQueue: nil)
let foregroundSession = URLSession(configuration: .default, delegate: eventHub, delegateQueue: nil)

Expand Down
21 changes: 21 additions & 0 deletions Sources/CryptomatorCloudAccess/PCloud/PCloudSetup.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
//
// PCloudSetup.swift
// CryptomatorCloudAccess
//
// Created by Tobias Hagemann on 18.04.24.
// Copyright © 2024 Skymatic GmbH. All rights reserved.
//

import Foundation

public class PCloudSetup {
public static var constants: PCloudSetup!

let appKey: String
let sharedContainerIdentifier: String?

public init(appKey: String, sharedContainerIdentifier: String?) {
self.appKey = appKey
self.sharedContainerIdentifier = sharedContainerIdentifier
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ class VaultFormat6S3IntegrationTests: CloudAccessIntegrationTest {
}

// swiftlint:disable:next force_try
private static let cloudProvider = try! S3CloudProvider(credential: .mock)
private static let cloudProvider = try! S3CloudProvider(credential: IntegrationTestSecrets.s3Credential)
private static let vaultPath = CloudPath("/iOS-IntegrationTests-VaultFormat6")

override class func setUp() {
Expand Down Expand Up @@ -58,7 +58,7 @@ class VaultFormat6S3IntegrationTests: CloudAccessIntegrationTest {
}

override func createLimitedCloudProvider() throws -> CloudProvider {
let limitedDelegate = try S3CloudProvider(credential: .mock, maxPageSize: maxPageSizeForLimitedCloudProvider)
let limitedDelegate = try S3CloudProvider(credential: IntegrationTestSecrets.s3Credential, maxPageSize: maxPageSizeForLimitedCloudProvider)
let setUpPromise = DecoratorFactory.createFromExistingVaultFormat6(delegate: limitedDelegate, vaultPath: VaultFormat6S3IntegrationTests.vaultPath, password: "IntegrationTest").then { decorator in
self.provider = decorator
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ class VaultFormat7S3IntegrationTests: CloudAccessIntegrationTest {
}

// swiftlint:disable:next force_try
private static let cloudProvider = try! S3CloudProvider(credential: .mock)
private static let cloudProvider = try! S3CloudProvider(credential: IntegrationTestSecrets.s3Credential)
private static let vaultPath = CloudPath("/iOS-IntegrationTests-VaultFormat7")

override class func setUp() {
Expand Down Expand Up @@ -58,7 +58,7 @@ class VaultFormat7S3IntegrationTests: CloudAccessIntegrationTest {
}

override func createLimitedCloudProvider() throws -> CloudProvider {
let limitedDelegate = try S3CloudProvider(credential: .mock, maxPageSize: maxPageSizeForLimitedCloudProvider)
let limitedDelegate = try S3CloudProvider(credential: IntegrationTestSecrets.s3Credential, maxPageSize: maxPageSizeForLimitedCloudProvider)
let setUpPromise = DecoratorFactory.createFromExistingVaultFormat7(delegate: limitedDelegate, vaultPath: VaultFormat7S3IntegrationTests.vaultPath, password: "IntegrationTest").then { decorator in
self.provider = decorator
}
Expand Down
Loading

0 comments on commit c25cb21

Please sign in to comment.