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/impl/sync_metadata.hpp"
21 #include "object_schema.hpp"
22 #include "object_store.hpp"
23 #include "property.hpp"
24 #include "results.hpp"
26 #include "util/uuid.hpp"
27 #if REALM_PLATFORM_APPLE
28 #include "impl/apple/keychain_helper.hpp"
31 #include <realm/descriptor.hpp>
32 #include <realm/table.hpp>
35 static const char * const c_sync_userMetadata = "UserMetadata";
36 static const char * const c_sync_marked_for_removal = "marked_for_removal";
37 static const char * const c_sync_identity = "identity";
38 static const char * const c_sync_local_uuid = "local_uuid";
39 static const char * const c_sync_auth_server_url = "auth_server_url";
40 static const char * const c_sync_user_token = "user_token";
41 static const char * const c_sync_user_is_admin = "user_is_admin";
43 static const char * const c_sync_fileActionMetadata = "FileActionMetadata";
44 static const char * const c_sync_original_name = "original_name";
45 static const char * const c_sync_new_name = "new_name";
46 static const char * const c_sync_action = "action";
47 static const char * const c_sync_url = "url";
49 static const char * const c_sync_clientMetadata = "ClientMetadata";
50 static const char * const c_sync_uuid = "uuid";
52 realm::Schema make_schema()
54 using namespace realm;
56 {c_sync_userMetadata, {
57 {c_sync_identity, PropertyType::String},
58 {c_sync_local_uuid, PropertyType::String},
59 {c_sync_marked_for_removal, PropertyType::Bool},
60 {c_sync_user_token, PropertyType::String|PropertyType::Nullable},
61 {c_sync_auth_server_url, PropertyType::String},
62 {c_sync_user_is_admin, PropertyType::Bool},
64 {c_sync_fileActionMetadata, {
65 {c_sync_original_name, PropertyType::String, Property::IsPrimary{true}},
66 {c_sync_new_name, PropertyType::String|PropertyType::Nullable},
67 {c_sync_action, PropertyType::Int},
68 {c_sync_url, PropertyType::String},
69 {c_sync_identity, PropertyType::String},
71 {c_sync_clientMetadata, {
72 {c_sync_uuid, PropertyType::String},
77 } // anonymous namespace
81 // MARK: - Sync metadata manager
83 SyncMetadataManager::SyncMetadataManager(std::string path,
85 util::Optional<std::vector<char>> encryption_key)
87 constexpr uint64_t SCHEMA_VERSION = 2;
91 config.schema = make_schema();
92 config.schema_version = SCHEMA_VERSION;
93 config.schema_mode = SchemaMode::Automatic;
94 #if REALM_PLATFORM_APPLE
95 if (should_encrypt && !encryption_key) {
96 encryption_key = keychain::metadata_realm_encryption_key(File::exists(path));
100 if (!encryption_key) {
101 throw std::invalid_argument("Metadata Realm encryption was specified, but no encryption key was provided.");
103 config.encryption_key = std::move(*encryption_key);
106 config.migration_function = [](SharedRealm old_realm, SharedRealm realm, Schema&) {
107 if (old_realm->schema_version() < 2) {
108 TableRef old_table = ObjectStore::table_for_object_type(old_realm->read_group(), c_sync_userMetadata);
109 TableRef table = ObjectStore::table_for_object_type(realm->read_group(), c_sync_userMetadata);
111 // Get all the SyncUserMetadata objects.
112 Results results(old_realm, *old_table);
115 size_t old_idx_identity = old_table->get_column_index(c_sync_identity);
116 size_t old_idx_url = old_table->get_column_index(c_sync_auth_server_url);
117 size_t idx_local_uuid = table->get_column_index(c_sync_local_uuid);
118 size_t idx_url = table->get_column_index(c_sync_auth_server_url);
120 for (size_t i = 0; i < results.size(); i++) {
121 RowExpr entry = results.get(i);
122 // Set the UUID equal to the user identity for existing users.
123 auto identity = entry.get_string(old_idx_identity);
124 table->set_string(idx_local_uuid, entry.get_index(), identity);
125 // Migrate the auth server URLs to a non-nullable property.
126 auto url = entry.get_string(old_idx_url);
127 table->set_string(idx_url, entry.get_index(), url.is_null() ? "" : url);
132 SharedRealm realm = Realm::get_shared_realm(config);
134 // Get data about the (hardcoded) schemas
135 auto object_schema = realm->schema().find(c_sync_userMetadata);
137 object_schema->persisted_properties[0].table_column,
138 object_schema->persisted_properties[1].table_column,
139 object_schema->persisted_properties[2].table_column,
140 object_schema->persisted_properties[3].table_column,
141 object_schema->persisted_properties[4].table_column,
142 object_schema->persisted_properties[5].table_column,
145 object_schema = realm->schema().find(c_sync_fileActionMetadata);
146 m_file_action_schema = {
147 object_schema->persisted_properties[0].table_column,
148 object_schema->persisted_properties[1].table_column,
149 object_schema->persisted_properties[2].table_column,
150 object_schema->persisted_properties[3].table_column,
151 object_schema->persisted_properties[4].table_column,
154 object_schema = realm->schema().find(c_sync_clientMetadata);
156 object_schema->persisted_properties[0].table_column,
159 m_metadata_config = std::move(config);
162 SyncUserMetadataResults SyncMetadataManager::all_unmarked_users() const
164 return get_users(false);
167 SyncUserMetadataResults SyncMetadataManager::all_users_marked_for_removal() const
169 return get_users(true);
172 SyncUserMetadataResults SyncMetadataManager::get_users(bool marked) const
174 SharedRealm realm = Realm::get_shared_realm(m_metadata_config);
176 TableRef table = ObjectStore::table_for_object_type(realm->read_group(), c_sync_userMetadata);
177 Query query = table->where().equal(m_user_schema.idx_marked_for_removal, marked);
179 Results results(realm, std::move(query));
180 return SyncUserMetadataResults(std::move(results), std::move(realm), m_user_schema);
183 SyncFileActionMetadataResults SyncMetadataManager::all_pending_actions() const
185 SharedRealm realm = Realm::get_shared_realm(m_metadata_config);
186 TableRef table = ObjectStore::table_for_object_type(realm->read_group(), c_sync_fileActionMetadata);
187 Results results(realm, table->where());
188 return SyncFileActionMetadataResults(std::move(results), std::move(realm), m_file_action_schema);
191 bool SyncMetadataManager::delete_metadata_action(const std::string& original_name) const
193 auto shared_realm = Realm::get_shared_realm(m_metadata_config);
195 // Retrieve the row for this object.
196 TableRef table = ObjectStore::table_for_object_type(shared_realm->read_group(), c_sync_fileActionMetadata);
197 shared_realm->begin_transaction();
198 size_t row_idx = table->find_first_string(m_file_action_schema.idx_original_name, original_name);
199 if (row_idx == not_found) {
200 shared_realm->cancel_transaction();
203 table->move_last_over(row_idx);
204 shared_realm->commit_transaction();
208 util::Optional<SyncUserMetadata> SyncMetadataManager::get_or_make_user_metadata(const std::string& identity,
209 const std::string& url,
210 bool make_if_absent) const
212 auto realm = Realm::get_shared_realm(m_metadata_config);
213 auto& schema = m_user_schema;
215 // Retrieve or create the row for this object.
216 TableRef table = ObjectStore::table_for_object_type(realm->read_group(), c_sync_userMetadata);
217 Query query = table->where().equal(schema.idx_identity, identity).equal(schema.idx_auth_server_url, url);
218 Results results(realm, std::move(query));
219 REALM_ASSERT_DEBUG(results.size() < 2);
220 auto row = results.first();
226 realm->begin_transaction();
227 // Check the results again.
228 row = results.first();
230 auto row = table->get(table->add_empty_row());
231 std::string uuid = util::uuid_string();
232 row.set_string(schema.idx_identity, identity);
233 row.set_string(schema.idx_auth_server_url, url);
234 row.set_string(schema.idx_local_uuid, uuid);
235 row.set_bool(schema.idx_user_is_admin, false);
236 row.set_bool(schema.idx_marked_for_removal, false);
237 realm->commit_transaction();
238 return SyncUserMetadata(schema, std::move(realm), std::move(row));
240 // Someone beat us to adding this user.
241 if (row->get_bool(schema.idx_marked_for_removal)) {
242 // User is dead. Revive or return none.
243 if (make_if_absent) {
244 row->set_bool(schema.idx_marked_for_removal, false);
245 realm->commit_transaction();
247 realm->cancel_transaction();
251 // User is alive, nothing else to do.
252 realm->cancel_transaction();
254 return SyncUserMetadata(schema, std::move(realm), std::move(*row));
258 // Got an existing user.
259 if (row->get_bool(schema.idx_marked_for_removal)) {
260 // User is dead. Revive or return none.
261 if (make_if_absent) {
262 realm->begin_transaction();
263 row->set_bool(schema.idx_marked_for_removal, false);
264 realm->commit_transaction();
269 return SyncUserMetadata(schema, std::move(realm), std::move(*row));
272 SyncFileActionMetadata SyncMetadataManager::make_file_action_metadata(const std::string &original_name,
273 const std::string &url,
274 const std::string &local_uuid,
275 SyncFileActionMetadata::Action action,
276 util::Optional<std::string> new_name) const
278 size_t raw_action = static_cast<size_t>(action);
281 auto realm = Realm::get_shared_realm(m_metadata_config);
282 auto& schema = m_file_action_schema;
284 // Retrieve or create the row for this object.
285 TableRef table = ObjectStore::table_for_object_type(realm->read_group(), c_sync_fileActionMetadata);
286 realm->begin_transaction();
287 size_t row_idx = table->find_first_string(schema.idx_original_name, original_name);
288 if (row_idx == not_found) {
289 row_idx = table->add_empty_row();
290 table->set_string(schema.idx_original_name, row_idx, original_name);
292 table->set_string(schema.idx_new_name, row_idx, new_name);
293 table->set_int(schema.idx_action, row_idx, raw_action);
294 table->set_string(schema.idx_url, row_idx, url);
295 table->set_string(schema.idx_user_identity, row_idx, local_uuid);
296 realm->commit_transaction();
297 return SyncFileActionMetadata(schema, std::move(realm), table->get(row_idx));
300 util::Optional<SyncFileActionMetadata> SyncMetadataManager::get_file_action_metadata(const std::string& original_name) const
302 auto realm = Realm::get_shared_realm(m_metadata_config);
303 auto schema = m_file_action_schema;
304 TableRef table = ObjectStore::table_for_object_type(realm->read_group(), c_sync_fileActionMetadata);
305 size_t row_idx = table->find_first_string(schema.idx_original_name, original_name);
306 if (row_idx == not_found)
309 return SyncFileActionMetadata(std::move(schema), std::move(realm), table->get(row_idx));
312 std::string SyncMetadataManager::client_uuid() const
314 auto realm = Realm::get_shared_realm(m_metadata_config);
315 TableRef table = ObjectStore::table_for_object_type(realm->read_group(), c_sync_clientMetadata);
316 if (table->is_empty()) {
317 realm->begin_transaction();
318 if (table->is_empty()) {
319 size_t idx = table->add_empty_row();
320 REALM_ASSERT_DEBUG(idx == 0);
321 auto uuid = uuid_string();
322 table->set_string(m_client_schema.idx_uuid, idx, uuid);
323 realm->commit_transaction();
326 realm->cancel_transaction();
329 return table->get_string(m_client_schema.idx_uuid, 0);
332 // MARK: - Sync user metadata
334 SyncUserMetadata::SyncUserMetadata(Schema schema, SharedRealm realm, RowExpr row)
335 : m_realm(std::move(realm))
336 , m_schema(std::move(schema))
340 std::string SyncUserMetadata::identity() const
342 REALM_ASSERT(m_realm);
343 m_realm->verify_thread();
344 return m_row.get_string(m_schema.idx_identity);
347 std::string SyncUserMetadata::local_uuid() const
349 REALM_ASSERT(m_realm);
350 m_realm->verify_thread();
351 return m_row.get_string(m_schema.idx_local_uuid);
354 util::Optional<std::string> SyncUserMetadata::user_token() const
356 REALM_ASSERT(m_realm);
357 m_realm->verify_thread();
358 StringData result = m_row.get_string(m_schema.idx_user_token);
359 return result.is_null() ? util::none : util::make_optional(std::string(result));
362 std::string SyncUserMetadata::auth_server_url() const
364 REALM_ASSERT(m_realm);
365 m_realm->verify_thread();
366 return m_row.get_string(m_schema.idx_auth_server_url);
369 bool SyncUserMetadata::is_admin() const
371 REALM_ASSERT(m_realm);
372 m_realm->verify_thread();
373 return m_row.get_bool(m_schema.idx_user_is_admin);
376 void SyncUserMetadata::set_user_token(util::Optional<std::string> user_token)
381 REALM_ASSERT_DEBUG(m_realm);
382 m_realm->verify_thread();
383 m_realm->begin_transaction();
384 m_row.set_string(m_schema.idx_user_token, *user_token);
385 m_realm->commit_transaction();
388 void SyncUserMetadata::set_is_admin(bool is_admin)
393 REALM_ASSERT_DEBUG(m_realm);
394 m_realm->verify_thread();
395 m_realm->begin_transaction();
396 m_row.set_bool(m_schema.idx_user_is_admin, is_admin);
397 m_realm->commit_transaction();
400 void SyncUserMetadata::mark_for_removal()
405 m_realm->verify_thread();
406 m_realm->begin_transaction();
407 m_row.set_bool(m_schema.idx_marked_for_removal, true);
408 m_realm->commit_transaction();
411 void SyncUserMetadata::remove()
414 m_realm->begin_transaction();
415 TableRef table = ObjectStore::table_for_object_type(m_realm->read_group(), c_sync_userMetadata);
416 table->move_last_over(m_row.get_index());
417 m_realm->commit_transaction();
421 // MARK: - File action metadata
423 SyncFileActionMetadata::SyncFileActionMetadata(Schema schema, SharedRealm realm, RowExpr row)
424 : m_realm(std::move(realm))
425 , m_schema(std::move(schema))
429 std::string SyncFileActionMetadata::original_name() const
431 REALM_ASSERT(m_realm);
432 m_realm->verify_thread();
433 return m_row.get_string(m_schema.idx_original_name);
436 util::Optional<std::string> SyncFileActionMetadata::new_name() const
438 REALM_ASSERT(m_realm);
439 m_realm->verify_thread();
440 StringData result = m_row.get_string(m_schema.idx_new_name);
441 return result.is_null() ? util::none : util::make_optional(std::string(result));
444 std::string SyncFileActionMetadata::user_local_uuid() const
446 REALM_ASSERT(m_realm);
447 m_realm->verify_thread();
448 return m_row.get_string(m_schema.idx_user_identity);
451 SyncFileActionMetadata::Action SyncFileActionMetadata::action() const
453 REALM_ASSERT(m_realm);
454 m_realm->verify_thread();
455 return static_cast<SyncFileActionMetadata::Action>(m_row.get_int(m_schema.idx_action));
458 std::string SyncFileActionMetadata::url() const
460 REALM_ASSERT(m_realm);
461 m_realm->verify_thread();
462 return m_row.get_string(m_schema.idx_url);
465 void SyncFileActionMetadata::remove()
467 REALM_ASSERT(m_realm);
468 m_realm->verify_thread();
469 m_realm->begin_transaction();
470 TableRef table = ObjectStore::table_for_object_type(m_realm->read_group(), c_sync_fileActionMetadata);
471 table->move_last_over(m_row.get_index());
472 m_realm->commit_transaction();