Skip to content

Commit 0d21faa

Browse files
authored
Merge pull request #6837 from realm/tg/user-provider
Fix handling of users with multiple identities
2 parents 52216f8 + 64bf842 commit 0d21faa

34 files changed

+1199
-1224
lines changed

CHANGELOG.md

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,24 @@
55

66
### Fixed
77
* <How do the end-user experience this issue? what was the impact?> ([#????](https://github.com/realm/realm-core/issues/????), since v?.?.?)
8-
* None.
8+
* Logging into a single user using multiple auth providers created a separate SyncUser per auth provider. This mostly worked, but had some quirks:
9+
- Sync sessions would not necessarily be associated with the specific SyncUser used to create them. As a result, querying a user for its sessions could give incorrect results, and logging one user out could close the wrong sessions.
10+
- Existing local synchronized Realm files created using version of Realm from August - November 2020 would sometimes not be opened correctly and would instead be redownloaded.
11+
- Removing one of the SyncUsers would delete all local Realm files for all SyncUsers for that user.
12+
- Deleting the server-side user via one of the SyncUsers left the other SyncUsers in an invalid state.
13+
- A SyncUser which was originally created via anonymous login and then linked to an identity would still be treated as an anonymous users and removed entirely on logout.
14+
([PR #6837](https://github.com/realm/realm-core/pull/6837), since v10.0.0)
15+
* Reading existing logged-in users on app startup from the sync metadata Realm performed three no-op writes per user on the metadata Realm ([PR #6837](https://github.com/realm/realm-core/pull/6837), since v10.0.0).
16+
* If a user was logged out while an access token refresh was in progress, the refresh completing would mark the user as logged in again and the user would be in an inconsistent state ([PR #6837](https://github.com/realm/realm-core/pull/6837), since v10.0.0).
917

1018
### Breaking changes
11-
* None.
19+
* SyncUser::provider_type() and realm_user_get_auth_provider() have been removed. Users don't have provider types; identities do. `SyncUser::is_anonymous()` is a more correct version of checking if the provider type is anonymous ([PR #6837](https://github.com/realm/realm-core/pull/6837)).
20+
* SyncUser no longer has a `local_identity()`. `identity()` has been guaranteed to be unique per App ever since v10 ([PR #6837](https://github.com/realm/realm-core/pull/6837)).
21+
* SyncUser no longer overrides operator==. Pointer equality should be used to compare sync users ([PR #6837](https://github.com/realm/realm-core/pull/6837)).
1222

1323
### Compatibility
1424
* Fileformat: Generates files with format v23. Reads and automatically upgrade from fileformat v5.
25+
* The metadata Realm used to store sync users has had its schema version bumped. It is automatically migrated to the new version on first open. Downgrading to older version of Realm after upgrading will discard stored user tokens and require logging back in.
1526

1627
-----------
1728

src/realm.h

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3232,13 +3232,9 @@ RLM_API realm_user_state_e realm_user_get_state(const realm_user_t* user) RLM_AP
32323232
RLM_API bool realm_user_get_all_identities(const realm_user_t* user, realm_user_identity_t* out_identities,
32333233
size_t capacity, size_t* out_n);
32343234

3235-
RLM_API const char* realm_user_get_local_identity(const realm_user_t*) RLM_API_NOEXCEPT;
3236-
32373235
// returned pointer must be manually released with realm_free()
32383236
RLM_API char* realm_user_get_device_id(const realm_user_t*) RLM_API_NOEXCEPT;
32393237

3240-
RLM_API realm_auth_provider_e realm_user_get_auth_provider(const realm_user_t*) RLM_API_NOEXCEPT;
3241-
32423238
/**
32433239
* Log out the user and mark it as logged out.
32443240
*

src/realm/collection.hpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ struct CollectionIterator;
1919
/// Collections are bound to particular properties of an object. In a
2020
/// collection's public interface, the implementation must take care to keep the
2121
/// object consistent with the persisted state, mindful of the fact that the
22-
/// state may have changed as a consquence of modifications from other instances
22+
/// state may have changed as a consequence of modifications from other instances
2323
/// referencing the same persisted state.
2424
class CollectionBase {
2525
public:

src/realm/object-store/c_api/app.cpp

Lines changed: 0 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -642,11 +642,6 @@ RLM_API bool realm_user_get_all_identities(const realm_user_t* user, realm_user_
642642
});
643643
}
644644

645-
RLM_API const char* realm_user_get_local_identity(const realm_user_t* user) noexcept
646-
{
647-
return (*user)->local_identity().c_str();
648-
}
649-
650645
RLM_API char* realm_user_get_device_id(const realm_user_t* user) noexcept
651646
{
652647
if ((*user)->has_device_id()) {
@@ -656,11 +651,6 @@ RLM_API char* realm_user_get_device_id(const realm_user_t* user) noexcept
656651
return nullptr;
657652
}
658653

659-
RLM_API realm_auth_provider_e realm_user_get_auth_provider(const realm_user_t* user) noexcept
660-
{
661-
return realm_auth_provider_e(enum_from_provider_type((*user)->provider_type()));
662-
}
663-
664654
RLM_API bool realm_user_log_out(realm_user_t* user)
665655
{
666656
return wrap_err([&] {

src/realm/object-store/c_api/types.hpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -661,7 +661,7 @@ struct realm_user : realm::c_api::WrapC, std::shared_ptr<realm::SyncUser> {
661661
bool equals(const WrapC& other) const noexcept final
662662
{
663663
if (auto ptr = dynamic_cast<const realm_user*>(&other)) {
664-
return *get() == *(ptr->get());
664+
return get() == ptr->get();
665665
}
666666
return false;
667667
}

src/realm/object-store/impl/realm_coordinator.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -183,7 +183,7 @@ void RealmCoordinator::set_config(const Realm::Config& config)
183183
if (config.sync_config) {
184184
auto old_user = m_config.sync_config->user;
185185
auto new_user = config.sync_config->user;
186-
if (old_user && new_user && *old_user != *new_user) {
186+
if (old_user != new_user) {
187187
throw LogicError(
188188
ErrorCodes::MismatchedConfig,
189189
util::format("Realm at path '%1' already opened with different sync user.", config.path));

src/realm/object-store/sync/app.cpp

Lines changed: 5 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -595,9 +595,8 @@ void App::get_profile(const std::shared_ptr<SyncUser>& sync_user,
595595
SyncUserIdentity(get<std::string>(doc, "id"), get<std::string>(doc, "provider_type")));
596596
}
597597

598-
sync_user->update_identities(identities);
599-
sync_user->update_user_profile(SyncUserProfile(get<BsonDocument>(profile_json, "data")));
600-
sync_user->set_state(SyncUser::State::LoggedIn);
598+
sync_user->update_user_profile(std::move(identities),
599+
SyncUserProfile(get<BsonDocument>(profile_json, "data")));
601600
self->m_sync_manager->set_current_user(sync_user->identity());
602601
self->emit_change_to_subscribers(*self);
603602
}
@@ -644,7 +643,7 @@ void App::log_in_with_credentials(
644643
// is already an anonymous session active, reuse it
645644
if (credentials.provider() == AuthProvider::ANONYMOUS) {
646645
for (auto&& user : m_sync_manager->all_users()) {
647-
if (user->provider_type() == credentials.provider_as_string() && user->is_logged_in()) {
646+
if (user->is_anonymous()) {
648647
completion(switch_user(user), util::none);
649648
return;
650649
}
@@ -686,8 +685,7 @@ void App::log_in_with_credentials(
686685
else {
687686
sync_user = self->m_sync_manager->get_user(
688687
get<std::string>(json, "user_id"), get<std::string>(json, "refresh_token"),
689-
get<std::string>(json, "access_token"), credentials.provider_as_string(),
690-
get<std::string>(json, "device_id"));
688+
get<std::string>(json, "access_token"), get<std::string>(json, "device_id"));
691689
}
692690
}
693691
catch (const AppError& e) {
@@ -758,11 +756,7 @@ std::shared_ptr<SyncUser> App::switch_user(const std::shared_ptr<SyncUser>& user
758756
if (!user || user->state() != SyncUser::State::LoggedIn) {
759757
throw AppError(ErrorCodes::ClientUserNotLoggedIn, "User is no longer valid or is logged out");
760758
}
761-
762-
auto users = m_sync_manager->all_users();
763-
auto it = std::find(users.begin(), users.end(), user);
764-
765-
if (it == users.end()) {
759+
if (!verify_user_present(user)) {
766760
throw AppError(ErrorCodes::ClientUserNotFound, "User does not exist");
767761
}
768762

src/realm/object-store/sync/impl/sync_file.cpp

Lines changed: 14 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -295,11 +295,11 @@ bool SyncFileManager::copy_realm_file(const std::string& old_path, const std::st
295295
return true;
296296
}
297297

298-
bool SyncFileManager::remove_realm(const std::string& user_identity, const std::string& local_identity,
298+
bool SyncFileManager::remove_realm(const std::string& user_identity,
299+
const std::vector<std::string>& legacy_user_identities,
299300
const std::string& raw_realm_path, const std::string& partition) const
300301
{
301-
util::Optional<std::string> existing =
302-
get_existing_realm_file_path(user_identity, local_identity, raw_realm_path, partition);
302+
auto existing = get_existing_realm_file_path(user_identity, legacy_user_identities, raw_realm_path, partition);
303303
if (existing) {
304304
return remove_realm(*existing);
305305
}
@@ -327,10 +327,10 @@ static bool try_file_remove(const std::string& path) noexcept
327327
}
328328
}
329329

330-
util::Optional<std::string> SyncFileManager::get_existing_realm_file_path(const std::string& user_identity,
331-
const std::string& local_user_identity,
332-
const std::string& realm_file_name,
333-
const std::string& partition) const
330+
util::Optional<std::string>
331+
SyncFileManager::get_existing_realm_file_path(const std::string& user_identity,
332+
const std::vector<std::string>& legacy_user_identities,
333+
const std::string& realm_file_name, const std::string& partition) const
334334
{
335335
std::string preferred_name = preferred_realm_path_without_suffix(user_identity, realm_file_name);
336336
if (try_file_exists(preferred_name)) {
@@ -365,14 +365,14 @@ util::Optional<std::string> SyncFileManager::get_existing_realm_file_path(const
365365
}
366366
}
367367

368-
if (!local_user_identity.empty()) {
368+
for (auto& legacy_identity : legacy_user_identities) {
369369
// retain support for legacy paths
370-
std::string old_path = legacy_realm_file_path(local_user_identity, realm_file_name);
370+
std::string old_path = legacy_realm_file_path(legacy_identity, realm_file_name);
371371
if (try_file_exists(old_path)) {
372372
return old_path;
373373
}
374374
// retain support for legacy local identity paths
375-
std::string old_local_identity_path = legacy_local_identity_path(local_user_identity, partition);
375+
std::string old_local_identity_path = legacy_local_identity_path(legacy_identity, partition);
376376
if (try_file_exists(old_local_identity_path)) {
377377
return old_local_identity_path;
378378
}
@@ -381,11 +381,12 @@ util::Optional<std::string> SyncFileManager::get_existing_realm_file_path(const
381381
return util::none;
382382
}
383383

384-
std::string SyncFileManager::realm_file_path(const std::string& user_identity, const std::string& local_user_identity,
384+
std::string SyncFileManager::realm_file_path(const std::string& user_identity,
385+
const std::vector<std::string>& legacy_user_identities,
385386
const std::string& realm_file_name, const std::string& partition) const
386387
{
387-
util::Optional<std::string> existing_path =
388-
get_existing_realm_file_path(user_identity, local_user_identity, realm_file_name, partition);
388+
auto existing_path =
389+
get_existing_realm_file_path(user_identity, legacy_user_identities, realm_file_name, partition);
389390
if (existing_path) {
390391
return *existing_path;
391392
}

src/realm/object-store/sync/impl/sync_file.hpp

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -69,15 +69,16 @@ class SyncFileManager {
6969
static bool try_file_exists(const std::string& path) noexcept;
7070

7171
util::Optional<std::string> get_existing_realm_file_path(const std::string& user_identity,
72-
const std::string& local_user_identity,
72+
const std::vector<std::string>& legacy_user_identities,
7373
const std::string& realm_file_name,
7474
const std::string& partition) const;
7575
/// Return the path for a given Realm, creating the user directory if it does not already exist.
76-
std::string realm_file_path(const std::string& user_identity, const std::string& local_user_identity,
76+
std::string realm_file_path(const std::string& user_identity,
77+
const std::vector<std::string>& legacy_user_identities,
7778
const std::string& realm_file_name, const std::string& partition) const;
7879

7980
/// Remove the Realm at a given path for a given user. Returns `true` if the remove operation fully succeeds.
80-
bool remove_realm(const std::string& user_identity, const std::string& local_identity,
81+
bool remove_realm(const std::string& user_identity, const std::vector<std::string>& legacy_user_identities,
8182
const std::string& realm_file_name, const std::string& partition) const;
8283

8384
/// Remove the Realm whose primary Realm file is located at `absolute_path`. Returns `true` if the remove

0 commit comments

Comments
 (0)