Skip to content

Commit

Permalink
Add user identities
Browse files Browse the repository at this point in the history
  • Loading branch information
nirinchev committed Oct 5, 2020
1 parent 8c38425 commit 6343f35
Show file tree
Hide file tree
Showing 5 changed files with 174 additions and 39 deletions.
14 changes: 13 additions & 1 deletion Realm/Realm/Handles/SyncUserHandle.cs
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,10 @@ public static extern void call_function(SyncUserHandle handle, AppHandle app,
IntPtr tcs_ptr, out NativeException ex);

[DllImport(InteropConfig.DLL_NAME, EntryPoint = "realm_syncuser_link_credentials", CallingConvention = CallingConvention.Cdecl)]
public static extern void link_credentials(SyncUserHandle hadnle, AppHandle app, Native.Credentials credentials, IntPtr tcs_ptr, out NativeException ex);
public static extern void link_credentials(SyncUserHandle handle, AppHandle app, Native.Credentials credentials, IntPtr tcs_ptr, out NativeException ex);

[DllImport(InteropConfig.DLL_NAME, EntryPoint = "realm_syncuser_get_serialized_identities", CallingConvention = CallingConvention.Cdecl)]
public static extern IntPtr get_identities(SyncUserHandle handle, IntPtr buffer, IntPtr bufsize, out NativeException ex);

#region Api Keys

Expand Down Expand Up @@ -253,6 +256,15 @@ public void LinkCredentials(AppHandle app, Native.Credentials credentials, TaskC
ex.ThrowIfNecessary(tcsHandle);
}

public string GetIdentities()
{
return MarshalHelpers.GetString((IntPtr buffer, IntPtr length, out bool isNull, out NativeException ex) =>
{
isNull = false;
return NativeMethods.get_identities(this, buffer, length, out ex);
});
}

#region Api Keys

public void CreateApiKey(AppHandle app, string name, TaskCompletionSource<UserApiKey[]> tcs)
Expand Down
14 changes: 14 additions & 0 deletions Realm/Realm/Sync/User.cs
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
using System.Linq;
using System.Threading.Tasks;
using MongoDB.Bson;
using MongoDB.Bson.Serialization;
using Realms.Helpers;
using Realms.Native;
using Realms.Sync.Exceptions;
Expand Down Expand Up @@ -115,6 +116,19 @@ public BsonDocument CustomData
}
}

/// <summary>
/// Gets a collection of all identities associated with this user.
/// </summary>
/// <value>The user's identities across different <see cref="Credentials.AuthProvider"/>s.</value>
public UserIdentity[] Identities
{
get
{
var serialized = Handle.GetIdentities();
return BsonSerializer.Deserialize<UserIdentity[]>(serialized);
}
}

/// <summary>
/// Gets a <see cref="ApiKeyApi"/> instance that exposes functionality about managing user API keys.
/// </summary>
Expand Down
62 changes: 62 additions & 0 deletions Realm/Realm/Sync/UserIdentity.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
////////////////////////////////////////////////////////////////////////////
//
// Copyright 2020 Realm Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
////////////////////////////////////////////////////////////////////////////

using MongoDB.Bson.Serialization.Attributes;

namespace Realms.Sync
{
/// <summary>
/// A class containing information about an identity associated with a user.
/// </summary>
[BsonNoId]
public class UserIdentity
{
/// <summary>
/// Gets the unique identifier for this identity.
/// </summary>
/// <value>The identity's Id.</value>
public string Id { get; private set; }

/// <summary>
/// Gets the auth provider defining this identity.
/// </summary>
/// <value>The identity's auth provider.</value>
public Credentials.AuthProvider Provider { get; private set; }

/// <inheritdoc/>
public override bool Equals(object obj) => (obj is UserIdentity id) && id.Id == Id && id.Provider == Provider;

/// <summary>
/// Gets the hash code.
/// </summary>
/// <returns>The hash code.</returns>
public override int GetHashCode()
{
var hashCode = -1285871140;
hashCode = (hashCode * -1521134295) + (Id?.GetHashCode() ?? 0);
hashCode = (hashCode * -1521134295) + Provider.GetHashCode();
return hashCode;
}

/// <summary>
/// Returns a string representation of the value.
/// </summary>
/// <returns>A string representation of the value.</returns>
public override string ToString() => $"UserIdentity: {Id} ({Provider})";
}
}
28 changes: 28 additions & 0 deletions Tests/Realm.Tests/Sync/UserManagementTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,8 @@ public void AppSwitchUser_SwitchesCurrentUser()
public void AppSwitchUser_WhenUserIsCurrent_DoesNothing()
{
var first = GetFakeUser();
Assert.That(DefaultApp.CurrentUser, Is.EqualTo(first));

var second = GetFakeUser();

Assert.That(DefaultApp.CurrentUser, Is.EqualTo(second));
Expand Down Expand Up @@ -194,15 +196,27 @@ public void User_LinkCredentials_AllowsLoginWithNewCredentials()
SyncTestHelpers.RunBaasTestAsync(async () =>
{
var user = await DefaultApp.LogInAsync(Credentials.Anonymous());

Assert.That(user.Identities, Has.Length.EqualTo(1));
Assert.That(user.Identities[0].Provider, Is.EqualTo(Credentials.AuthProvider.Anonymous));
Assert.That(user.Identities[0].Id, Is.Not.Null);

var email = SyncTestHelpers.GetVerifiedUsername();
await DefaultApp.EmailPasswordAuth.RegisterUserAsync(email, SyncTestHelpers.DefaultPassword);
var linkedUser = await user.LinkCredentialsAsync(Credentials.EmailPassword(email, SyncTestHelpers.DefaultPassword));

Assert.That(user.Identities, Has.Length.EqualTo(2));
Assert.That(user.Identities[1].Provider, Is.EqualTo(Credentials.AuthProvider.EmailPassword));
Assert.That(user.Identities[1].Id, Is.Not.Null);

Assert.That(linkedUser.Identities, Has.Length.EqualTo(2));
Assert.That(linkedUser.Id, Is.EqualTo(user.Id));
Assert.That(linkedUser.Identities, Is.EquivalentTo(user.Identities));

var emailPasswordUser = await DefaultApp.LogInAsync(Credentials.EmailPassword(email, SyncTestHelpers.DefaultPassword));

Assert.That(emailPasswordUser.Id, Is.EqualTo(user.Id));
Assert.That(emailPasswordUser.Identities, Is.EquivalentTo(user.Identities));
});
}

Expand All @@ -227,6 +241,16 @@ public void User_LinkCredentials_MultipleTimes_AllowsLoginWithAllCredentials()

var functionUser = await DefaultApp.LogInAsync(Credentials.Function(new { realmCustomAuthFuncUserId = functionId }));
Assert.That(functionUser.Id, Is.EqualTo(user.Id));

Assert.That(user.Identities, Has.Length.EqualTo(3));
Assert.That(user.Identities[0].Provider, Is.EqualTo(Credentials.AuthProvider.Anonymous));
Assert.That(user.Identities[0].Id, Is.Not.Null);

Assert.That(user.Identities[1].Provider, Is.EqualTo(Credentials.AuthProvider.EmailPassword));
Assert.That(user.Identities[1].Id, Is.Not.Null);

Assert.That(user.Identities[2].Provider, Is.EqualTo(Credentials.AuthProvider.Function));
Assert.That(user.Identities[2].Id, Is.EqualTo(functionId));
});
}

Expand All @@ -246,6 +270,10 @@ public void User_LinkCredentials_MultipleTimesSameCredentials_IsNoOp()

var functionUser = await DefaultApp.LogInAsync(Credentials.Function(new { realmCustomAuthFuncUserId = functionId }));
Assert.That(functionUser.Id, Is.EqualTo(user.Id));

Assert.That(user.Identities, Has.Length.EqualTo(2));
Assert.That(user.Identities[1].Id, Is.EqualTo(functionId));
Assert.That(user.Identities[1].Provider, Is.EqualTo(Credentials.AuthProvider.Function));
});
}

Expand Down
95 changes: 57 additions & 38 deletions wrappers/src/sync_user_cs.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,54 @@ namespace realm {

invoke_api_key_callback(tcs_ptr, api_keys, err);
}

inline AuthProvider to_auth_provider(const std::string& provider) {
if (provider == IdentityProviderAnonymous) {
return AuthProvider::ANONYMOUS;
}

if (provider == IdentityProviderFacebook) {
return AuthProvider::FACEBOOK;
}

if (provider == IdentityProviderGoogle) {
return AuthProvider::GOOGLE;
}

if (provider == IdentityProviderApple) {
return AuthProvider::APPLE;
}

if (provider == IdentityProviderCustom) {
return AuthProvider::CUSTOM;
}

if (provider == IdentityProviderUsernamePassword) {
return AuthProvider::USERNAME_PASSWORD;
}

if (provider == IdentityProviderFunction) {
return AuthProvider::FUNCTION;
}

if (provider == IdentityProviderUserAPIKey) {
return AuthProvider::USER_API_KEY;
}

if (provider == IdentityProviderServerAPIKey) {
return AuthProvider::SERVER_API_KEY;
}

return (AuthProvider)999;
}
}

void to_json(nlohmann::json& j, const SyncUserIdentity& i)
{
j = nlohmann::json{
{ "Id", i.id },
{ "Provider", to_auth_provider(i.provider_type)}
};
}
}

Expand Down Expand Up @@ -161,44 +209,7 @@ extern "C" {
REALM_EXPORT AuthProvider realm_syncuser_get_auth_provider(SharedSyncUser& user, NativeException::Marshallable& ex)
{
return handle_errors(ex, [&] {
auto provider = user->provider_type();
if (provider == IdentityProviderAnonymous) {
return AuthProvider::ANONYMOUS;
}

if (provider == IdentityProviderFacebook) {
return AuthProvider::FACEBOOK;
}

if (provider == IdentityProviderGoogle) {
return AuthProvider::GOOGLE;
}

if (provider == IdentityProviderApple) {
return AuthProvider::APPLE;
}

if (provider == IdentityProviderCustom) {
return AuthProvider::CUSTOM;
}

if (provider == IdentityProviderUsernamePassword) {
return AuthProvider::USERNAME_PASSWORD;
}

if (provider == IdentityProviderFunction) {
return AuthProvider::FUNCTION;
}

if (provider == IdentityProviderUserAPIKey) {
return AuthProvider::USER_API_KEY;
}

if (provider == IdentityProviderServerAPIKey) {
return AuthProvider::SERVER_API_KEY;
}

return (AuthProvider)999;
return to_auth_provider(user->provider_type());
});
}

Expand Down Expand Up @@ -280,6 +291,14 @@ extern "C" {
});
}

REALM_EXPORT size_t realm_syncuser_get_serialized_identities(SharedSyncUser& user, uint16_t* string_buffer, size_t buffer_size, NativeException::Marshallable& ex)
{
return handle_errors(ex, [&]() -> size_t {
nlohmann::json j = user->identities();
return stringdata_to_csharpstringbuffer(j.dump(), string_buffer, buffer_size);
});
}

REALM_EXPORT SharedApp* realm_syncuser_get_app(SharedSyncUser& user, NativeException::Marshallable& ex)
{
return handle_errors(ex, [&] {
Expand Down

0 comments on commit 6343f35

Please sign in to comment.