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

Define Swift Credentials as an enum #6826

Merged
merged 19 commits into from
Oct 2, 2020
Merged
Show file tree
Hide file tree
Changes from 16 commits
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
6 changes: 5 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,11 @@ x.y.z Release notes (yyyy-MM-dd)
* <How to hit and notice issue? what was the impact?> ([#????](https://github.com/realm/realm-cocoa/issues/????), since v?.?.?)
* None.

<!-- ### Breaking Changes - ONLY INCLUDE FOR NEW MAJOR version -->
### Breaking Changes
* Define `RealmSwift.Credentials` as an enum instead of a `typealias`. Example usage has changed from `Credentials(googleAuthCode: "token")` to `Credentials.google(serverAuthCode: "serverAuthCode")`, and `Credentials(facebookToken: "token")` to `Credentials.facebook(accessToken: "accessToken")`, etc.
* Remove error parameter and redefine payload in `+ (instancetype)credentialsWithFunctionPayload:(NSDictionary *)payload
error:(NSError **)error;`. It is now defined as `+ (instancetype)credentialsWithFunctionPayload:(NSDictionary<NSString *, id<RLMBSON>> *)payload;`


### Compatibility
* File format: Generates Realms with format v12 (Reads and upgrades all previous formats)
Expand Down
3 changes: 1 addition & 2 deletions Realm/ObjectServerTests/RLMObjectServerTests.mm
Original file line number Diff line number Diff line change
Expand Up @@ -487,8 +487,7 @@ - (void)testAppleCredential {

- (void)testFunctionCredential {
NSError *error;
RLMCredentials *functionCredential = [RLMCredentials credentialsWithFunctionPayload:@{ @"dog" : @{ @"name" : @"fido" } }
error:&error];
RLMCredentials *functionCredential = [RLMCredentials credentialsWithFunctionPayload:@{@"dog": @{@"name": @"fido"}}];
XCTAssertEqualObjects(functionCredential.provider, @"custom-function");
XCTAssertEqualObjects(error, nil);
}
Expand Down
82 changes: 17 additions & 65 deletions Realm/ObjectServerTests/SwiftObjectServerTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -600,54 +600,6 @@ class SwiftObjectServerTests: SwiftSyncTestCase {
}
#endif

// MARK: - App Credentials

func testEmailPasswordCredential() {
let EmailPasswordCredential = Credentials(email: "email", password: "password")
XCTAssertEqual(EmailPasswordCredential.provider.rawValue, "local-userpass")
}

func testJWTCredentials() {
let jwtCredential = Credentials(jwt: "token")
XCTAssertEqual(jwtCredential.provider.rawValue, "custom-token")
}

func testAnonymousCredentials() {
let anonymousCredential = Credentials.anonymous()
XCTAssertEqual(anonymousCredential.provider.rawValue, "anon-user")
}

func testUserAPIKeyCredentials() {
let userAPIKeyCredential = Credentials(userAPIKey: "apikey")
XCTAssertEqual(userAPIKeyCredential.provider.rawValue, "api-key")
}

func testServerAPIKeyCredentials() {
let serverAPIKeyCredential = Credentials(serverAPIKey: "apikey")
XCTAssertEqual(serverAPIKeyCredential.provider.rawValue, "api-key")
}

func testFacebookCredentials() {
let facebookCredential = Credentials(facebookToken: "token")
XCTAssertEqual(facebookCredential.provider.rawValue, "oauth2-facebook")
}

func testGoogleCredentials() {
let googleCredential = Credentials(googleAuthCode: "token")
XCTAssertEqual(googleCredential.provider.rawValue, "oauth2-google")
}

func testAppleCredentials() {
let appleCredential = Credentials(appleToken: "token")
XCTAssertEqual(appleCredential.provider.rawValue, "oauth2-apple")
}

func testFunctionCredentials() {
var error: NSError!
let functionCredential = Credentials.init(functionPayload: ["dog": ["name": "fido"]], error: &error)
XCTAssertEqual(functionCredential.provider.rawValue, "custom-function")
}

// MARK: - Authentication

func testInvalidCredentials() {
Expand All @@ -657,7 +609,7 @@ class SwiftObjectServerTests: SwiftSyncTestCase {
let user = try synchronouslyLogInUser(for: credentials)
XCTAssertEqual(user.state, .loggedIn)

let credentials2 = Credentials(email: email, password: "NOT_A_VALID_PASSWORD")
let credentials2 = Credentials.emailPassword(email: email, password: "NOT_A_VALID_PASSWORD")
let ex = expectation(description: "Should log in the user properly")

self.app.login(credentials: credentials2, completion: { user2, error in
Expand Down Expand Up @@ -742,7 +694,7 @@ class SwiftObjectServerTests: SwiftSyncTestCase {
let loginEx = expectation(description: "Login user")
var syncUser: User?

app.login(credentials: Credentials(email: email, password: password)) { (user, error) in
app.login(credentials: Credentials.emailPassword(email: email, password: password)) { (user, error) in
XCTAssertNil(error)
syncUser = user
loginEx.fulfill()
Expand Down Expand Up @@ -782,15 +734,15 @@ class SwiftObjectServerTests: SwiftSyncTestCase {
var syncUser1: User?
var syncUser2: User?

app.login(credentials: Credentials(email: email1, password: password1)) { (user, error) in
app.login(credentials: Credentials.emailPassword(email: email1, password: password1)) { (user, error) in
XCTAssertNil(error)
syncUser1 = user
login1Ex.fulfill()
}

wait(for: [login1Ex], timeout: 4.0)

app.login(credentials: Credentials(email: email2, password: password2)) { (user, error) in
app.login(credentials: Credentials.emailPassword(email: email2, password: password2)) { (user, error) in
XCTAssertNil(error)
syncUser2 = user
login2Ex.fulfill()
Expand Down Expand Up @@ -834,9 +786,9 @@ class SwiftObjectServerTests: SwiftSyncTestCase {
let loginEx = expectation(description: "Login user")
var syncUser: User?

let credentials = Credentials(email: email, password: password)
let credentials = Credentials.emailPassword(email: email, password: password)

app.login(credentials: Credentials.anonymous()) { (user, error) in
app.login(credentials: Credentials.anonymous) { (user, error) in
XCTAssertNil(error)
syncUser = user
loginEx.fulfill()
Expand All @@ -846,7 +798,7 @@ class SwiftObjectServerTests: SwiftSyncTestCase {

let linkEx = expectation(description: "Link user")

syncUser?.linkUser(with: credentials) { (user, error) in
syncUser?.linkUser(credentials: credentials) { (user, error) in
XCTAssertNil(error)
syncUser = user
linkEx.fulfill()
Expand Down Expand Up @@ -929,7 +881,7 @@ class SwiftObjectServerTests: SwiftSyncTestCase {
wait(for: [registerUserEx], timeout: 4.0)

let loginEx = expectation(description: "Login user")
let credentials = Credentials(email: email, password: password)
let credentials = Credentials.emailPassword(email: email, password: password)

var syncUser: User?
app.login(credentials: credentials) { (user, error) in
Expand Down Expand Up @@ -1004,7 +956,7 @@ class SwiftObjectServerTests: SwiftSyncTestCase {

let loginEx = expectation(description: "Login user")

let credentials = Credentials(email: email, password: password)
let credentials = Credentials.emailPassword(email: email, password: password)
var syncUser: User?
app.login(credentials: credentials) { (user, error) in
syncUser = user
Expand Down Expand Up @@ -1046,7 +998,7 @@ class SwiftObjectServerTests: SwiftSyncTestCase {

let loginExpectation = expectation(description: "Login user")

let credentials = Credentials(email: email, password: password)
let credentials = Credentials.emailPassword(email: email, password: password)
app.login(credentials: credentials) { (_, error) in
XCTAssertNil(error)
loginExpectation.fulfill()
Expand Down Expand Up @@ -1082,7 +1034,7 @@ class SwiftObjectServerTests: SwiftSyncTestCase {
wait(for: [registerUserEx], timeout: 4.0)

let loginEx = expectation(description: "Login user")
let credentials = Credentials(email: email, password: password)
let credentials = Credentials.emailPassword(email: email, password: password)
var syncUser: User?
app.login(credentials: credentials) { (user, error) in
syncUser = user
Expand Down Expand Up @@ -1115,7 +1067,7 @@ class SwiftObjectServerTests: SwiftSyncTestCase {
// MARK: - Mongo Client

func testMongoClient() {
let user = try! synchronouslyLogInUser(for: Credentials.anonymous())
let user = try! synchronouslyLogInUser(for: Credentials.anonymous)
let mongoClient = user.mongoClient("mongodb1")
XCTAssertEqual(mongoClient.name, "mongodb1")
let database = mongoClient.database(named: "test_data")
Expand Down Expand Up @@ -2138,7 +2090,7 @@ class CombineObjectServerTests: SwiftSyncTestCase {
wait(for: [registerUserEx], timeout: 4.0)

let loginEx = expectation(description: "Login user")
app.login(credentials: Credentials(email: email, password: password))
app.login(credentials: Credentials.emailPassword(email: email, password: password))
.sink(receiveCompletion: { result in
if case .failure = result {
XCTFail("Should login")
Expand Down Expand Up @@ -2169,7 +2121,7 @@ class CombineObjectServerTests: SwiftSyncTestCase {
.store(in: &cancellable)
wait(for: [registerUserEx], timeout: 4.0)

let credentials = Credentials(email: email, password: password)
let credentials = Credentials.emailPassword(email: email, password: password)
var syncUser: User!
let loginEx = expectation(description: "Login user")
app.login(credentials: credentials)
Expand Down Expand Up @@ -2786,7 +2738,7 @@ class CombineObjectServerTests: SwiftSyncTestCase {
.store(in: &cancellable)
wait(for: [regEx], timeout: 4.0)

let credentials = Credentials(email: email, password: password)
let credentials = Credentials.emailPassword(email: email, password: password)
var syncUser: User!
let loginEx = expectation(description: "Should login")
app.login(credentials: credentials)
Expand Down Expand Up @@ -2851,7 +2803,7 @@ class CombineObjectServerTests: SwiftSyncTestCase {

let loginEx = expectation(description: "Login user")
var syncUser: User?
app.login(credentials: Credentials(email: email, password: password))
app.login(credentials: Credentials.emailPassword(email: email, password: password))
.sink(receiveCompletion: { _ in },
receiveValue: { (user) in
syncUser = user
Expand Down Expand Up @@ -2952,7 +2904,7 @@ class CombineObjectServerTests: SwiftSyncTestCase {
wait(for: [registerUserEx], timeout: 4.0)

let loginEx = expectation(description: "Login user")
app.login(credentials: Credentials(email: email, password: password))
app.login(credentials: Credentials.emailPassword(email: email, password: password))
.sink(receiveCompletion: { _ in },
receiveValue: { _ in loginEx.fulfill() })
.store(in: &cancellable)
Expand Down
6 changes: 3 additions & 3 deletions Realm/ObjectServerTests/SwiftSyncTestCase.swift
Original file line number Diff line number Diff line change
Expand Up @@ -32,11 +32,11 @@ class SwiftSyncTestCase: RLMSyncTestCase {
func basicCredentials(usernameSuffix: String = "",
file: StaticString = #file,
line: UInt = #line) -> Credentials {
let username = "\(randomString(10))\(usernameSuffix)"
let email = "\(randomString(10))\(usernameSuffix)"
let password = "abcdef"
let credentials = Credentials(email: username, password: password)
let credentials = Credentials.emailPassword(email: email, password: password)
let ex = expectation(description: "Should register in the user properly")
app.emailPasswordAuth.registerUser(email: username, password: password, completion: { error in
app.emailPasswordAuth.registerUser(email: email, password: password, completion: { error in
XCTAssertNil(error)
ex.fulfill()
})
Expand Down
2 changes: 1 addition & 1 deletion Realm/RLMApp.h
Original file line number Diff line number Diff line change
Expand Up @@ -135,7 +135,7 @@ Create a new Realm App configuration.
@param completion A callback invoked after completion.
*/
- (void)loginWithCredential:(RLMCredentials *)credentials
completion:(RLMUserCompletionBlock)completion NS_SWIFT_NAME(login(credentials:completion:));
completion:(RLMUserCompletionBlock)completion NS_REFINED_FOR_SWIFT;

/**
Switches the active user to the specified user.
Expand Down
5 changes: 2 additions & 3 deletions Realm/RLMCredentials.h
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
#import <Realm/RLMSyncUtil.h>

NS_ASSUME_NONNULL_BEGIN
@protocol RLMBSON;

/// A token representing an identity provider's credentials.
typedef NSString *RLMCredentialsToken;
Expand Down Expand Up @@ -79,10 +80,8 @@ extern RLMIdentityProvider const RLMIdentityProviderServerAPIKey;

/**
Construct and return credentials for a MongoDB Realm function using a mongodb document as a json payload.
If the json can not be successfully serialised and error will be produced and the object will be nil.
*/
+ (instancetype)credentialsWithFunctionPayload:(NSDictionary *)payload
error:(NSError **)error;
+ (instancetype)credentialsWithFunctionPayload:(NSDictionary<NSString *, id<RLMBSON>> *)payload;

/**
Construct and return credentials from a user api key.
Expand Down
14 changes: 3 additions & 11 deletions Realm/RLMCredentials.mm
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@

#import "RLMCredentials_Private.hpp"

#import "RLMBSON_Private.hpp"
#import "RLMSyncUtil_Private.h"
#import "RLMUtil.hpp"
#import "util/bson/bson.hpp"
Expand Down Expand Up @@ -56,17 +57,8 @@ + (instancetype)credentialsWithJWT:(NSString *)token {
return [[self alloc] initWithAppCredentials:app::AppCredentials::custom(token.UTF8String)];
}

+ (instancetype)credentialsWithFunctionPayload:(NSDictionary *)payload
error:(NSError **)error {
NSData *jsonData = [NSJSONSerialization dataWithJSONObject:payload
options:NSJSONWritingPrettyPrinted
error:error];
if (!jsonData) {
return nil;
}
NSString *jsonString = [[NSString alloc] initWithData:jsonData encoding:NSUTF8StringEncoding];

return [[self alloc] initWithAppCredentials:app::AppCredentials::function((realm::bson::BsonDocument)realm::bson::parse(jsonString.UTF8String))];
+ (instancetype)credentialsWithFunctionPayload:(NSDictionary<NSString *, id<RLMBSON>> *)payload {
return [[self alloc] initWithAppCredentials:app::AppCredentials::function(static_cast<bson::BsonDocument>(RLMConvertRLMBSONToBson(payload)))];
}

+ (instancetype)credentialsWithUserAPIKey:(NSString *)apiKey {
Expand Down
2 changes: 1 addition & 1 deletion Realm/RLMUser.h
Original file line number Diff line number Diff line change
Expand Up @@ -142,7 +142,7 @@ NS_ASSUME_NONNULL_BEGIN
`RLMUser` object representing the currently logged in user.
*/
- (void)linkUserWithCredentials:(RLMCredentials *)credentials
completion:(RLMOptionalUserBlock)completion;
completion:(RLMOptionalUserBlock)completion NS_REFINED_FOR_SWIFT;

/**
Removes the user
Expand Down
36 changes: 34 additions & 2 deletions RealmSwift/App.swift
Original file line number Diff line number Diff line change
Expand Up @@ -74,14 +74,46 @@ public typealias PushClient = RLMPushClient
/// An object which is used within UserAPIKeyProviderClient
public typealias UserAPIKey = RLMUserAPIKey

/// A `Credentials` represents data that uniquely identifies a Realm Object Server user.
public typealias Credentials = RLMCredentials
/**
`Credentials`is an enum representing supported authentication types for MongoDB Realm.
Example Usage:
```
let credentials = Credentials.JWT(token: myToken)
```
*/
public enum Credentials {
/// Credentials from a Facebook access token.
case facebook(accessToken: String)
/// Credentials from a Google serverAuthCode.
case google(serverAuthCode: String)
/// Credentials from an Apple id token.
case apple(idToken: String)
/// Credentials from an email and password.
case emailPassword(email: String, password: String)
/// Credentials from a JSON Web Token
case jwt(token: String)
/// Credentials for a MongoDB Realm function using a mongodb document as a json payload.
/// If the json can not be successfully serialised and error will be produced and the object will be nil.
case function(payload: Document)
/// Credentials from a user api key.
case userAPIKey(String)
/// Credentials from a sever api key.
case serverAPIKey(String)
/// Represents anonymous credentials
case anonymous
}

/// The `App` has the fundamental set of methods for communicating with a Realm
/// application backend.
/// This interface provides access to login and authentication.
public typealias App = RLMApp

extension App {
public func login(credentials: Credentials, completion: @escaping RLMUserCompletionBlock) {
self.__login(withCredential: ObjectiveCSupport.convert(object: credentials), completion: completion)
}
}

/// Use this delegate to be provided a callback once authentication has succeed or failed
@available(OSX 10.15, watchOS 6.0, iOS 13.0, iOSApplicationExtension 13.0, OSXApplicationExtension 10.15, tvOS 13.0, *)
public typealias ASLoginDelegate = RLMASLoginDelegate
Expand Down
24 changes: 24 additions & 0 deletions RealmSwift/ObjectiveCSupport+Sync.swift
Original file line number Diff line number Diff line change
Expand Up @@ -31,4 +31,28 @@ public extension ObjectiveCSupport {
static func convert(object: RLMSyncConfiguration) -> SyncConfiguration {
return SyncConfiguration(config: object)
}

/// Convert a `Credentials` to a `RLMCredentials`
static func convert(object: Credentials) -> RLMCredentials {
switch object {
case .facebook(let accessToken):
return RLMCredentials(facebookToken: accessToken)
case .google(let serverAuthCode):
return RLMCredentials(googleAuthCode: serverAuthCode)
case .apple(let idToken):
return RLMCredentials(appleToken: idToken)
case .emailPassword(let email,let password):
return RLMCredentials(email: email, password: password)
case .jwt(let token):
return RLMCredentials(jwt: token)
case .function(let payload):
return RLMCredentials(functionPayload: ObjectiveCSupport.convert(object: AnyBSON(payload))! as! [String : RLMBSON])
case .userAPIKey(let APIKey):
return RLMCredentials(userAPIKey: APIKey)
case .serverAPIKey(let serverAPIKey):
return RLMCredentials(serverAPIKey: serverAPIKey)
case .anonymous:
return RLMCredentials.anonymous()
}
}
}
Loading