Skip to content

Commit

Permalink
Define Swift Credentials as an enum (#6826)
Browse files Browse the repository at this point in the history
Define Swift `Credentials` as an enum
  • Loading branch information
ejm01 authored Oct 2, 2020
1 parent 3f425d9 commit 9a9178c
Show file tree
Hide file tree
Showing 11 changed files with 126 additions and 81 deletions.
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
83 changes: 28 additions & 55 deletions Realm/ObjectServerTests/SwiftObjectServerTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -600,52 +600,25 @@ class SwiftObjectServerTests: SwiftSyncTestCase {
}
#endif

// MARK: - App Credentials
func testAppCredentialSupport() {
XCTAssertEqual(ObjectiveCSupport.convert(object: Credentials.facebook(accessToken: "accessToken")), RLMCredentials(facebookToken: "accessToken"))

func testEmailPasswordCredential() {
let EmailPasswordCredential = Credentials(email: "email", password: "password")
XCTAssertEqual(EmailPasswordCredential.provider.rawValue, "local-userpass")
}
XCTAssertEqual(ObjectiveCSupport.convert(object: Credentials.google(serverAuthCode: "serverAuthCode")), RLMCredentials(googleAuthCode: "serverAuthCode"))

func testJWTCredentials() {
let jwtCredential = Credentials(jwt: "token")
XCTAssertEqual(jwtCredential.provider.rawValue, "custom-token")
}
XCTAssertEqual(ObjectiveCSupport.convert(object: Credentials.apple(idToken: "idToken")), RLMCredentials(appleToken: "idToken"))

func testAnonymousCredentials() {
let anonymousCredential = Credentials.anonymous()
XCTAssertEqual(anonymousCredential.provider.rawValue, "anon-user")
}
XCTAssertEqual(ObjectiveCSupport.convert(object: Credentials.emailPassword(email: "email", password: "password")), RLMCredentials(email: "email", password: "password"))

func testUserAPIKeyCredentials() {
let userAPIKeyCredential = Credentials(userAPIKey: "apikey")
XCTAssertEqual(userAPIKeyCredential.provider.rawValue, "api-key")
}
XCTAssertEqual(ObjectiveCSupport.convert(object: Credentials.jwt(token: "token")), RLMCredentials(jwt: "token"))

func testServerAPIKeyCredentials() {
let serverAPIKeyCredential = Credentials(serverAPIKey: "apikey")
XCTAssertEqual(serverAPIKeyCredential.provider.rawValue, "api-key")
}
XCTAssertEqual(ObjectiveCSupport.convert(object: Credentials.function(payload: ["dog": ["name": "fido"]])),
RLMCredentials(functionPayload: ["dog": ["name" as NSString: "fido" as NSString] as NSDictionary]))

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")
}
XCTAssertEqual(ObjectiveCSupport.convert(object: Credentials.userAPIKey("key")), RLMCredentials(userAPIKey: "key"))

func testAppleCredentials() {
let appleCredential = Credentials(appleToken: "token")
XCTAssertEqual(appleCredential.provider.rawValue, "oauth2-apple")
}
XCTAssertEqual(ObjectiveCSupport.convert(object: Credentials.serverAPIKey("key")), RLMCredentials(serverAPIKey: "key"))

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

// MARK: - Authentication
Expand All @@ -657,7 +630,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 +715,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 +755,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 +807,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 +819,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 +902,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 +977,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 +1019,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 +1055,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 +1088,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 +2111,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 +2142,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 +2759,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 +2824,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 +2925,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
42 changes: 40 additions & 2 deletions RealmSwift/App.swift
Original file line number Diff line number Diff line change
Expand Up @@ -74,14 +74,52 @@ 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 {
/**
Login to a user for the Realm app.

@param credentials The credentials identifying the user.
@param completion A callback invoked after completion.
*/
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

0 comments on commit 9a9178c

Please sign in to comment.