1 ////////////////////////////////////////////////////////////////////////////
3 // Copyright 2016 Realm Inc.
5 // Licensed under the Apache License, Version 2.0 (the "License");
6 // you may not use this file except in compliance with the License.
7 // You may obtain a copy of the License at
9 // http://www.apache.org/licenses/LICENSE-2.0
11 // Unless required by applicable law or agreed to in writing, software
12 // distributed under the License is distributed on an "AS IS" BASIS,
13 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 // See the License for the specific language governing permissions and
15 // limitations under the License.
17 ////////////////////////////////////////////////////////////////////////////
19 #include "sync/sync_manager.hpp"
21 #include "sync/impl/sync_client.hpp"
22 #include "sync/impl/sync_file.hpp"
23 #include "sync/impl/sync_metadata.hpp"
24 #include "sync/sync_session.hpp"
25 #include "sync/sync_user.hpp"
27 using namespace realm;
28 using namespace realm::_impl;
30 constexpr const char SyncManager::c_admin_identity[];
32 SyncManager& SyncManager::shared()
34 // The singleton is heap-allocated in order to fix an issue when running unit tests where tests would crash after
35 // they were done running because the manager was destroyed too early.
36 static SyncManager& manager = *new SyncManager;
40 void SyncManager::configure_file_system(const std::string& base_file_path,
41 MetadataMode metadata_mode,
42 util::Optional<std::vector<char>> custom_encryption_key,
43 bool reset_metadata_on_error)
45 struct UserCreationData {
47 std::string user_token;
48 std::string server_url;
52 std::vector<UserCreationData> users_to_add;
54 std::lock_guard<std::mutex> lock(m_file_system_mutex);
56 // Set up the file manager.
58 REALM_ASSERT(m_file_manager->base_path() == base_file_path);
60 m_file_manager = std::make_unique<SyncFileManager>(base_file_path);
63 // Set up the metadata manager, and perform initial loading/purging work.
64 if (m_metadata_manager) {
67 switch (metadata_mode) {
68 case MetadataMode::NoEncryption:
69 m_metadata_manager = std::make_unique<SyncMetadataManager>(m_file_manager->metadata_path(),
72 case MetadataMode::Encryption:
74 m_metadata_manager = std::make_unique<SyncMetadataManager>(m_file_manager->metadata_path(),
76 custom_encryption_key);
77 } catch (RealmFileException const& ex) {
78 if (reset_metadata_on_error && m_file_manager->remove_metadata_realm()) {
79 m_metadata_manager = std::make_unique<SyncMetadataManager>(m_file_manager->metadata_path(),
81 std::move(custom_encryption_key));
87 case MetadataMode::NoMetadata:
91 REALM_ASSERT(m_metadata_manager);
92 // Perform any necessary file actions.
93 std::vector<SyncFileActionMetadata> completed_actions;
94 SyncFileActionMetadataResults file_actions = m_metadata_manager->all_pending_actions();
95 for (size_t i = 0; i < file_actions.size(); i++) {
96 auto file_action = file_actions.get(i);
97 if (run_file_action(file_action)) {
98 completed_actions.emplace_back(std::move(file_action));
101 for (auto& action : completed_actions) {
104 // Load persisted users into the users map.
105 SyncUserMetadataResults users = m_metadata_manager->all_unmarked_users();
106 for (size_t i = 0; i < users.size(); i++) {
107 // Note that 'admin' style users are not persisted.
108 auto user_data = users.get(i);
109 auto user_token = user_data.user_token();
110 auto identity = user_data.identity();
111 auto server_url = user_data.auth_server_url();
112 bool is_admin = user_data.is_admin();
114 UserCreationData data = {
116 std::move(*user_token),
117 std::move(server_url),
120 users_to_add.emplace_back(std::move(data));
123 // Delete any users marked for death.
124 std::vector<SyncUserMetadata> dead_users;
125 SyncUserMetadataResults users_to_remove = m_metadata_manager->all_users_marked_for_removal();
126 dead_users.reserve(users_to_remove.size());
127 for (size_t i = 0; i < users_to_remove.size(); i++) {
128 auto user = users_to_remove.get(i);
129 // FIXME: delete user data in a different way? (This deletes a logged-out user's data as soon as the app
130 // launches again, which might not be how some apps want to treat their data.)
132 m_file_manager->remove_user_directory(user.local_uuid());
133 dead_users.emplace_back(std::move(user));
134 } catch (util::File::AccessError const&) {
138 for (auto& user : dead_users) {
143 std::lock_guard<std::mutex> lock(m_user_mutex);
144 for (auto& user_data : users_to_add) {
145 auto& identity = user_data.identity;
146 auto& server_url = user_data.server_url;
147 auto user = std::make_shared<SyncUser>(user_data.user_token, identity, server_url);
148 user->set_is_admin(user_data.is_admin);
149 m_users.insert({ {identity, server_url}, std::move(user) });
154 bool SyncManager::immediately_run_file_actions(const std::string& realm_path)
156 if (!m_metadata_manager) {
159 if (auto metadata = m_metadata_manager->get_file_action_metadata(realm_path)) {
160 if (run_file_action(*metadata)) {
168 // Perform a file action. Returns whether or not the file action can be removed.
169 bool SyncManager::run_file_action(const SyncFileActionMetadata& md)
171 switch (md.action()) {
172 case SyncFileActionMetadata::Action::DeleteRealm:
173 // Delete all the files for the given Realm.
174 m_file_manager->remove_realm(md.original_name());
176 case SyncFileActionMetadata::Action::BackUpThenDeleteRealm:
177 // Copy the primary Realm file to the recovery dir, and then delete the Realm.
178 auto new_name = md.new_name();
179 auto original_name = md.original_name();
180 if (!util::File::exists(original_name)) {
181 // The Realm file doesn't exist anymore.
184 if (new_name && !util::File::exists(*new_name) && m_file_manager->copy_realm_file(original_name, *new_name)) {
185 // We successfully copied the Realm file to the recovery directory.
186 m_file_manager->remove_realm(original_name);
194 void SyncManager::reset_for_testing()
196 std::lock_guard<std::mutex> lock(m_file_system_mutex);
197 m_file_manager = nullptr;
198 m_metadata_manager = nullptr;
200 // Destroy all the users.
201 std::lock_guard<std::mutex> lock(m_user_mutex);
203 m_admin_token_users.clear();
206 std::lock_guard<std::mutex> lock(m_mutex);
208 // Stop the client. This will abort any uploads that inactive sessions are waiting for.
210 m_sync_client->stop();
213 std::lock_guard<std::mutex> lock(m_session_mutex);
215 // Callers of `SyncManager::reset_for_testing` should ensure there are no active sessions
216 // prior to calling `reset_for_testing`.
217 auto no_active_sessions = std::none_of(m_sessions.begin(), m_sessions.end(), [](auto& element){
218 return element.second->existing_external_reference();
220 REALM_ASSERT_RELEASE(no_active_sessions);
222 // Destroy any inactive sessions.
223 // FIXME: We shouldn't have any inactive sessions at this point! Sessions are expected to
224 // remain inactive until their final upload completes, at which point they are unregistered
225 // and destroyed. Our call to `sync::Client::stop` above aborts all uploads, so all sessions
226 // should have already been destroyed.
230 // Destroy the client now that we have no remaining sessions.
231 m_sync_client = nullptr;
233 // Reset even more state.
234 // NOTE: these should always match the defaults.
235 m_log_level = util::Logger::Level::info;
236 m_logger_factory = nullptr;
237 m_client_reconnect_mode = ReconnectMode::normal;
238 m_multiplex_sessions = false;
242 void SyncManager::set_log_level(util::Logger::Level level) noexcept
244 std::lock_guard<std::mutex> lock(m_mutex);
248 void SyncManager::set_logger_factory(SyncLoggerFactory& factory) noexcept
250 std::lock_guard<std::mutex> lock(m_mutex);
251 m_logger_factory = &factory;
254 void SyncManager::set_client_should_reconnect_immediately(bool reconnect_immediately)
256 std::lock_guard<std::mutex> lock(m_mutex);
257 m_client_reconnect_mode = reconnect_immediately ? ReconnectMode::immediate : ReconnectMode::normal;
260 bool SyncManager::client_should_reconnect_immediately() const noexcept
262 std::lock_guard<std::mutex> lock(m_mutex);
263 return m_client_reconnect_mode == ReconnectMode::immediate;
266 void SyncManager::reconnect()
268 std::lock_guard<std::mutex> lock(m_session_mutex);
269 for (auto& it : m_sessions) {
270 it.second->handle_reconnect();
274 util::Logger::Level SyncManager::log_level() const noexcept
276 std::lock_guard<std::mutex> lock(m_mutex);
280 bool SyncManager::perform_metadata_update(std::function<void(const SyncMetadataManager&)> update_function) const
282 std::lock_guard<std::mutex> lock(m_file_system_mutex);
283 if (!m_metadata_manager) {
286 update_function(*m_metadata_manager);
290 std::shared_ptr<SyncUser> SyncManager::get_user(const SyncUserIdentifier& identifier, std::string refresh_token)
292 std::lock_guard<std::mutex> lock(m_user_mutex);
293 auto it = m_users.find(identifier);
294 if (it == m_users.end()) {
296 auto new_user = std::make_shared<SyncUser>(std::move(refresh_token),
298 identifier.auth_server_url,
300 SyncUser::TokenType::Normal);
301 m_users.insert({ identifier, new_user });
304 auto user = it->second;
305 if (user->state() == SyncUser::State::Error) {
308 user->update_refresh_token(std::move(refresh_token));
313 std::shared_ptr<SyncUser> SyncManager::get_admin_token_user_from_identity(const std::string& identity,
314 util::Optional<std::string> server_url,
315 const std::string& token)
318 return get_admin_token_user(*server_url, token, identity);
320 std::lock_guard<std::mutex> lock(m_user_mutex);
321 // Look up the user based off the identity.
322 // No server URL, so no migration possible.
323 auto it = m_admin_token_users.find(identity);
324 if (it == m_admin_token_users.end()) {
326 auto new_user = std::make_shared<SyncUser>(token,
328 std::move(server_url),
330 SyncUser::TokenType::Admin);
331 m_admin_token_users.insert({ identity, new_user });
338 std::shared_ptr<SyncUser> SyncManager::get_admin_token_user(const std::string& server_url,
339 const std::string& token,
340 util::Optional<std::string> old_identity)
342 std::shared_ptr<SyncUser> user;
344 std::lock_guard<std::mutex> lock(m_user_mutex);
345 // Look up the user based off the server URL.
346 auto it = m_admin_token_users.find(server_url);
347 if (it != m_admin_token_users.end())
351 user = std::make_shared<SyncUser>(token,
354 c_admin_identity + server_url,
355 SyncUser::TokenType::Admin);
356 m_admin_token_users.insert({ server_url, user });
359 // Try renaming the user's directory to use our new naming standard, if applicable.
360 std::lock_guard<std::mutex> fm_lock(m_file_system_mutex);
362 m_file_manager->try_rename_user_directory(*old_identity, c_admin_identity + server_url);
367 std::vector<std::shared_ptr<SyncUser>> SyncManager::all_logged_in_users() const
369 std::lock_guard<std::mutex> lock(m_user_mutex);
370 std::vector<std::shared_ptr<SyncUser>> users;
371 users.reserve(m_users.size() + m_admin_token_users.size());
372 for (auto& it : m_users) {
373 auto user = it.second;
374 if (user->state() == SyncUser::State::Active) {
375 users.emplace_back(std::move(user));
378 for (auto& it : m_admin_token_users) {
379 users.emplace_back(std::move(it.second));
384 std::shared_ptr<SyncUser> SyncManager::get_current_user() const
386 std::lock_guard<std::mutex> lock(m_user_mutex);
388 auto is_active_user = [](auto& el) { return el.second->state() == SyncUser::State::Active; };
389 auto it = std::find_if(m_users.begin(), m_users.end(), is_active_user);
390 if (it == m_users.end())
393 if (std::find_if(std::next(it), m_users.end(), is_active_user) != m_users.end())
394 throw std::logic_error("Current user is not valid if more that one valid, logged-in user exists.");
399 std::shared_ptr<SyncUser> SyncManager::get_existing_logged_in_user(const SyncUserIdentifier& identifier) const
401 std::lock_guard<std::mutex> lock(m_user_mutex);
402 auto it = m_users.find(identifier);
403 if (it == m_users.end())
406 auto user = it->second;
407 return user->state() == SyncUser::State::Active ? user : nullptr;
410 std::string SyncManager::path_for_realm(const SyncUser& user, const std::string& raw_realm_url) const
412 std::lock_guard<std::mutex> lock(m_file_system_mutex);
413 REALM_ASSERT(m_file_manager);
414 const auto& user_local_identity = user.local_identity();
415 util::Optional<SyncUserIdentifier> user_info;
416 if (user.token_type() == SyncUser::TokenType::Normal)
417 user_info = SyncUserIdentifier{ user.identity(), user.server_url() };
419 return m_file_manager->path(user_local_identity, raw_realm_url, std::move(user_info));
422 std::string SyncManager::recovery_directory_path() const
424 std::lock_guard<std::mutex> lock(m_file_system_mutex);
425 REALM_ASSERT(m_file_manager);
426 return m_file_manager->recovery_directory_path();
429 std::shared_ptr<SyncSession> SyncManager::get_existing_active_session(const std::string& path) const
431 std::lock_guard<std::mutex> lock(m_session_mutex);
432 if (auto session = get_existing_session_locked(path)) {
433 if (auto external_reference = session->existing_external_reference())
434 return external_reference;
439 std::shared_ptr<SyncSession> SyncManager::get_existing_session_locked(const std::string& path) const
441 REALM_ASSERT(!m_session_mutex.try_lock());
442 auto it = m_sessions.find(path);
443 return it == m_sessions.end() ? nullptr : it->second;
446 std::shared_ptr<SyncSession> SyncManager::get_existing_session(const std::string& path) const
448 std::lock_guard<std::mutex> lock(m_session_mutex);
449 if (auto session = get_existing_session_locked(path))
450 return session->external_reference();
455 std::shared_ptr<SyncSession> SyncManager::get_session(const std::string& path, const SyncConfig& sync_config)
457 auto& client = get_sync_client(); // Throws
459 std::lock_guard<std::mutex> lock(m_session_mutex);
460 if (auto session = get_existing_session_locked(path)) {
461 sync_config.user->register_session(session);
462 return session->external_reference();
465 auto shared_session = SyncSession::create(client, path, sync_config);
466 m_sessions[path] = shared_session;
468 // Create the external reference immediately to ensure that the session will become
469 // inactive if an exception is thrown in the following code.
470 auto external_reference = shared_session->external_reference();
472 sync_config.user->register_session(std::move(shared_session));
474 return external_reference;
477 void SyncManager::unregister_session(const std::string& path)
479 std::lock_guard<std::mutex> lock(m_session_mutex);
480 auto it = m_sessions.find(path);
481 REALM_ASSERT(it != m_sessions.end());
483 // If the session has an active external reference, leave it be. This will happen if the session
484 // moves to an inactive state while still externally reference, for instance, as a result of
485 // the session's user being logged out.
486 if (it->second->existing_external_reference())
489 m_sessions.erase(path);
492 void SyncManager::enable_session_multiplexing()
494 std::lock_guard<std::mutex> lock(m_mutex);
496 throw std::logic_error("Cannot enable session multiplexing after creating the sync client");
497 m_multiplex_sessions = true;
500 SyncClient& SyncManager::get_sync_client() const
502 std::lock_guard<std::mutex> lock(m_mutex);
504 m_sync_client = create_sync_client(); // Throws
505 return *m_sync_client;
508 std::unique_ptr<SyncClient> SyncManager::create_sync_client() const
510 REALM_ASSERT(!m_mutex.try_lock());
512 std::unique_ptr<util::Logger> logger;
513 if (m_logger_factory) {
514 logger = m_logger_factory->make_logger(m_log_level); // Throws
517 auto stderr_logger = std::make_unique<util::StderrLogger>(); // Throws
518 stderr_logger->set_level_threshold(m_log_level);
519 logger = std::move(stderr_logger);
521 return std::make_unique<SyncClient>(std::move(logger), m_client_reconnect_mode, m_multiplex_sessions);
524 std::string SyncManager::client_uuid() const
526 REALM_ASSERT(m_metadata_manager);
527 return m_metadata_manager->client_uuid();