• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

realm / realm-core / 1675

15 Sep 2023 08:33PM UTC coverage: 91.218% (+0.009%) from 91.209%
1675

push

Evergreen

web-flow
Allow non-embedded links in asymmetric objects (#6981)

* Allow non-embedded links in asymmetric objects

* Update CHANGELOG

* Add test of non-embedded links in asymmetric objects

* Fix lint

96014 of 175988 branches covered (0.0%)

8 of 8 new or added lines in 2 files covered. (100.0%)

145 existing lines in 18 files now uncovered.

233838 of 256352 relevant lines covered (91.22%)

7330937.36 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

87.98
/src/realm/object-store/sync/sync_user.cpp
1
////////////////////////////////////////////////////////////////////////////
2
//
3
// Copyright 2016 Realm Inc.
4
//
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
8
//
9
// http://www.apache.org/licenses/LICENSE-2.0
10
//
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.
16
//
17
////////////////////////////////////////////////////////////////////////////
18

19
#include <realm/object-store/sync/sync_user.hpp>
20

21
#include <realm/object-store/sync/app.hpp>
22
#include <realm/object-store/sync/app_credentials.hpp>
23
#include <realm/object-store/sync/generic_network_transport.hpp>
24
#include <realm/object-store/sync/impl/sync_metadata.hpp>
25
#include <realm/object-store/sync/mongo_client.hpp>
26
#include <realm/object-store/sync/sync_manager.hpp>
27
#include <realm/object-store/sync/sync_session.hpp>
28

29
#include <realm/util/base64.hpp>
30

31
namespace realm {
32

33
static std::string base64_decode(const std::string& in)
34
{
15,762✔
35
    std::string out;
15,762✔
36
    out.resize(util::base64_decoded_size(in.size()));
15,762✔
37
    util::base64_decode(in, &out[0], out.size());
15,762✔
38
    return out;
15,762✔
39
}
15,762✔
40

41
static std::vector<std::string> split_token(const std::string& jwt)
42
{
15,766✔
43
    constexpr static char delimiter = '.';
15,766✔
44

7,803✔
45
    std::vector<std::string> parts;
15,766✔
46
    size_t pos = 0, start_from = 0;
15,766✔
47

7,803✔
48
    while ((pos = jwt.find(delimiter, start_from)) != std::string::npos) {
47,290✔
49
        parts.push_back(jwt.substr(start_from, pos - start_from));
31,524✔
50
        start_from = pos + 1;
31,524✔
51
    }
31,524✔
52

7,803✔
53
    parts.push_back(jwt.substr(start_from));
15,766✔
54

7,803✔
55
    if (parts.size() != 3) {
15,766✔
56
        throw app::AppError(ErrorCodes::BadToken, "jwt missing parts");
4✔
57
    }
4✔
58

7,801✔
59
    return parts;
15,762✔
60
}
15,762✔
61

62
RealmJWT::RealmJWT(const std::string& token)
63
    : token(token)
64
{
15,766✔
65
    auto parts = split_token(this->token);
15,766✔
66

7,803✔
67
    auto json_str = base64_decode(parts[1]);
15,766✔
68
    auto json = static_cast<bson::BsonDocument>(bson::parse(json_str));
15,766✔
69

7,803✔
70
    this->expires_at = static_cast<int64_t>(json["exp"]);
15,766✔
71
    this->issued_at = static_cast<int64_t>(json["iat"]);
15,766✔
72

7,803✔
73
    if (json.find("user_data") != json.end()) {
15,766✔
74
        this->user_data = static_cast<bson::BsonDocument>(json["user_data"]);
13,768✔
75
    }
13,768✔
76
}
15,766✔
77

78
SyncUserIdentity::SyncUserIdentity(const std::string& id, const std::string& provider_type)
79
    : id(id)
80
    , provider_type(provider_type)
81
{
1,052✔
82
}
1,052✔
83

84
SyncUserContextFactory SyncUser::s_binding_context_factory;
85
std::mutex SyncUser::s_binding_context_factory_mutex;
86

87
SyncUser::SyncUser(std::string refresh_token, const std::string identity, const std::string provider_type,
88
                   std::string access_token, SyncUser::State state, const std::string device_id,
89
                   SyncManager* sync_manager)
90
    : m_provider_type(provider_type)
91
    , m_identity(std::move(identity))
92
    , m_refresh_token(RealmJWT(std::move(refresh_token)))
93
    , m_access_token(RealmJWT(std::move(access_token)))
94
    , m_device_id(device_id)
95
    , m_sync_manager(sync_manager)
96
{
4,518✔
97
    m_state.store(state);
4,518✔
98
    {
4,518✔
99
        std::lock_guard<std::mutex> lock(s_binding_context_factory_mutex);
4,518✔
100
        if (s_binding_context_factory) {
4,518✔
101
            m_binding_context = s_binding_context_factory();
×
102
        }
×
103
    }
4,518✔
104

2,221✔
105
    bool updated = m_sync_manager->perform_metadata_update([&](const auto& manager) NO_THREAD_SAFETY_ANALYSIS {
4,498✔
106
        auto metadata = manager.get_or_make_user_metadata(m_identity, m_provider_type);
4,478✔
107
        metadata->set_state_and_tokens(state, m_access_token.token, m_refresh_token.token);
4,478✔
108
        metadata->set_device_id(m_device_id);
4,478✔
109
        m_local_identity = metadata->local_uuid();
4,478✔
110
        this->m_user_profile = metadata->profile();
4,478✔
111
    });
4,478✔
112
    if (!updated)
4,518✔
113
        m_local_identity = m_identity;
38✔
114
}
4,518✔
115

116
SyncUser::~SyncUser() {}
4,516✔
117

118
std::shared_ptr<SyncManager> SyncUser::sync_manager() const
119
{
2,618✔
120
    util::CheckedLockGuard lk(m_mutex);
2,618✔
121
    if (m_state == State::Removed) {
2,618✔
122
        throw std::logic_error(util::format(
6✔
123
            "Cannot start a sync session for user '%1' because this user has been removed.", identity()));
6✔
124
    }
6✔
125
    REALM_ASSERT(m_sync_manager);
2,612✔
126
    return m_sync_manager->shared_from_this();
2,612✔
127
}
2,612✔
128

129
void SyncUser::detach_from_sync_manager()
130
{
4,516✔
131
    util::CheckedLockGuard lk(m_mutex);
4,516✔
132
    REALM_ASSERT(m_sync_manager);
4,516✔
133
    m_state = SyncUser::State::Removed;
4,516✔
134
    m_sync_manager = nullptr;
4,516✔
135
}
4,516✔
136

137
std::vector<std::shared_ptr<SyncSession>> SyncUser::all_sessions()
138
{
20✔
139
    util::CheckedLockGuard lock(m_mutex);
20✔
140
    std::vector<std::shared_ptr<SyncSession>> sessions;
20✔
141
    if (m_state == State::Removed) {
20✔
142
        return sessions;
×
143
    }
×
144
    for (auto it = m_sessions.begin(); it != m_sessions.end();) {
42✔
145
        if (auto ptr_to_session = it->second.lock()) {
22✔
146
            sessions.emplace_back(std::move(ptr_to_session));
22✔
147
            it++;
22✔
148
            continue;
22✔
149
        }
22✔
150
        // This session is bad, destroy it.
151
        it = m_sessions.erase(it);
×
152
    }
×
153
    return sessions;
20✔
154
}
20✔
155

156
std::shared_ptr<SyncSession> SyncUser::session_for_on_disk_path(const std::string& path)
157
{
1,534✔
158
    util::CheckedLockGuard lock(m_mutex);
1,534✔
159
    if (m_state == State::Removed) {
1,534✔
160
        return nullptr;
×
161
    }
×
162
    auto it = m_sessions.find(path);
1,534✔
163
    if (it == m_sessions.end()) {
1,534✔
164
        return nullptr;
×
165
    }
×
166
    auto locked = it->second.lock();
1,534✔
167
    if (!locked) {
1,534✔
168
        // Remove the session from the map, because it has fatally errored out or the entry is invalid.
169
        m_sessions.erase(it);
×
170
    }
×
171
    return locked;
1,534✔
172
}
1,534✔
173

174
void SyncUser::update_state_and_tokens(SyncUser::State state, const std::string& access_token,
175
                                       const std::string& refresh_token)
176
{
3,336✔
177
    std::vector<std::shared_ptr<SyncSession>> sessions_to_revive;
3,336✔
178
    {
3,336✔
179
        util::CheckedLockGuard lock1(m_mutex);
3,336✔
180
        util::CheckedLockGuard lock2(m_tokens_mutex);
3,336✔
181
        m_state = state;
3,336✔
182
        m_access_token = access_token.empty() ? RealmJWT{} : RealmJWT(access_token);
3,335✔
183
        m_refresh_token = refresh_token.empty() ? RealmJWT{} : RealmJWT(refresh_token);
3,335✔
184
        switch (m_state) {
3,336✔
185
            case State::Removed:
✔
186
                // Call set_state() rather than update_state_and_tokens to remove a user.
187
                REALM_UNREACHABLE();
×
188
            case State::LoggedIn:
3,334✔
189
                sessions_to_revive = revive_sessions();
3,334✔
190
                break;
3,334✔
191
            case State::LoggedOut: {
2✔
192
                REALM_ASSERT(m_access_token == RealmJWT{});
2✔
193
                REALM_ASSERT(m_refresh_token == RealmJWT{});
2✔
194
                break;
2✔
195
            }
3,336✔
196
        }
3,336✔
197

1,666✔
198
        m_sync_manager->perform_metadata_update([&, state = m_state.load()](const auto& manager) {
3,336✔
199
            auto metadata = manager.get_or_make_user_metadata(m_identity, m_provider_type);
3,332✔
200
            metadata->set_state_and_tokens(state, access_token, refresh_token);
3,332✔
201
        });
3,332✔
202
    }
3,336✔
203
    // (Re)activate all pending sessions.
1,666✔
204
    // Note that we do this after releasing the lock, since the session may
1,666✔
205
    // need to access protected User state in the process of binding itself.
1,666✔
206
    for (auto& session : sessions_to_revive) {
1,672✔
207
        session->revive_if_needed();
12✔
208
    }
12✔
209

1,666✔
210
    emit_change_to_subscribers(*this);
3,336✔
211
}
3,336✔
212

213
std::vector<std::shared_ptr<SyncSession>> SyncUser::revive_sessions()
214
{
3,334✔
215
    std::vector<std::shared_ptr<SyncSession>> sessions_to_revive;
3,334✔
216
    sessions_to_revive.reserve(m_waiting_sessions.size());
3,334✔
217
    for (auto& [path, weak_session] : m_waiting_sessions) {
1,673✔
218
        if (auto ptr = weak_session.lock()) {
16✔
219
            m_sessions[path] = ptr;
12✔
220
            sessions_to_revive.emplace_back(std::move(ptr));
12✔
221
        }
12✔
222
    }
16✔
223
    m_waiting_sessions.clear();
3,334✔
224
    return sessions_to_revive;
3,334✔
225
}
3,334✔
226

227
void SyncUser::update_refresh_token(std::string&& token)
228
{
4✔
229
    std::vector<std::shared_ptr<SyncSession>> sessions_to_revive;
4✔
230
    {
4✔
231
        util::CheckedLockGuard lock(m_mutex);
4✔
232
        util::CheckedLockGuard lock2(m_tokens_mutex);
4✔
233
        switch (m_state) {
4✔
234
            case State::Removed:
✔
235
                return;
×
236
            case State::LoggedIn:
4✔
237
                m_refresh_token = RealmJWT(std::move(token));
4✔
238
                break;
4✔
239
            case State::LoggedOut: {
✔
240
                m_refresh_token = RealmJWT(std::move(token));
×
241
                m_state = State::LoggedIn;
×
242
                sessions_to_revive = revive_sessions();
×
243
                break;
×
244
            }
4✔
245
        }
4✔
246

2✔
247
        m_sync_manager->perform_metadata_update([&, raw_refresh_token = m_refresh_token.token](const auto& manager) {
4✔
248
            auto metadata = manager.get_or_make_user_metadata(m_identity, m_provider_type);
4✔
249
            metadata->set_refresh_token(raw_refresh_token);
4✔
250
        });
4✔
251
    }
4✔
252
    // (Re)activate all pending sessions.
2✔
253
    // Note that we do this after releasing the lock, since the session may
2✔
254
    // need to access protected User state in the process of binding itself.
2✔
255
    for (auto& session : sessions_to_revive) {
2✔
256
        session->revive_if_needed();
×
257
    }
×
258

2✔
259
    emit_change_to_subscribers(*this);
4✔
260
}
4✔
261

262
void SyncUser::update_access_token(std::string&& token)
263
{
60✔
264
    std::vector<std::shared_ptr<SyncSession>> sessions_to_revive;
60✔
265
    {
60✔
266
        util::CheckedLockGuard lock(m_mutex);
60✔
267
        util::CheckedLockGuard lock2(m_tokens_mutex);
60✔
268
        switch (m_state) {
60✔
UNCOV
269
            case State::Removed:
✔
UNCOV
270
                return;
×
271
            case State::LoggedIn:
60✔
272
                m_access_token = RealmJWT(std::move(token));
60✔
273
                break;
60✔
274
            case State::LoggedOut: {
✔
275
                m_access_token = RealmJWT(std::move(token));
×
276
                m_state = State::LoggedIn;
×
277
                sessions_to_revive = revive_sessions();
×
278
                break;
×
279
            }
58✔
280
        }
58✔
281

29✔
282
        m_sync_manager->perform_metadata_update([&, raw_access_token = m_access_token.token](const auto& manager) {
58✔
283
            auto metadata = manager.get_or_make_user_metadata(m_identity, m_provider_type);
57✔
284
            metadata->set_access_token(raw_access_token);
57✔
285
        });
57✔
286
    }
58✔
287

29✔
288
    // (Re)activate all pending sessions.
29✔
289
    // Note that we do this after releasing the lock, since the session may
29✔
290
    // need to access protected User state in the process of binding itself.
29✔
291
    for (auto& session : sessions_to_revive) {
29✔
292
        session->revive_if_needed();
×
293
    }
×
294

29✔
295
    emit_change_to_subscribers(*this);
58✔
296
}
58✔
297

298
std::vector<SyncUserIdentity> SyncUser::identities() const
299
{
16✔
300
    util::CheckedLockGuard lock(m_mutex);
16✔
301
    return m_user_identities;
16✔
302
}
16✔
303

304

305
void SyncUser::update_identities(std::vector<SyncUserIdentity> identities)
306
{
988✔
307
    util::CheckedLockGuard lock(m_mutex);
988✔
308
    if (m_state != SyncUser::State::LoggedIn) {
988✔
309
        return;
2✔
310
    }
2✔
311

482✔
312
    m_user_identities = identities;
986✔
313

482✔
314
    m_sync_manager->perform_metadata_update([&](const auto& manager) {
985✔
315
        auto metadata = manager.get_or_make_user_metadata(m_identity, m_provider_type);
984✔
316
        metadata->set_identities(identities);
984✔
317
    });
984✔
318
}
986✔
319

320
void SyncUser::log_out()
321
{
93✔
322
    // We'll extend the lifetime of SyncManager while holding m_mutex so that we know it's safe to call methods on it
46✔
323
    // after we've been marked as logged out.
46✔
324
    std::shared_ptr<SyncManager> sync_manager_shared;
93✔
325
    {
93✔
326
        util::CheckedLockGuard lock(m_mutex);
93✔
327
        {
93✔
328
            util::CheckedLockGuard lock2(m_tokens_mutex);
93✔
329
            if (m_state != State::LoggedIn) {
93✔
330
                return;
×
331
            }
×
332
            m_state = State::LoggedOut;
93✔
333
            m_access_token = RealmJWT{};
93✔
334
            m_refresh_token = RealmJWT{};
93✔
335
        }
93✔
336

46✔
337
        if (this->m_provider_type == app::IdentityProviderAnonymous) {
93✔
338
            // An Anonymous user can not log back in.
12✔
339
            // Mark the user as 'dead' in the persisted metadata Realm.
12✔
340
            m_state = State::Removed;
24✔
341
            m_sync_manager->perform_metadata_update([&](const auto& manager) {
24✔
342
                auto metadata = manager.get_or_make_user_metadata(m_identity, m_provider_type, false);
24✔
343
                if (metadata)
24✔
344
                    metadata->remove();
24✔
345
            });
24✔
346
        }
24✔
347
        else {
69✔
348
            m_sync_manager->perform_metadata_update([&](const auto& manager) {
65✔
349
                auto metadata = manager.get_or_make_user_metadata(m_identity, m_provider_type);
61✔
350
                metadata->set_state_and_tokens(State::LoggedOut, "", "");
61✔
351
            });
61✔
352
        }
69✔
353
        sync_manager_shared = m_sync_manager->shared_from_this();
93✔
354
        // Move all active sessions into the waiting sessions pool. If the user is
46✔
355
        // logged back in, they will automatically be reactivated.
46✔
356
        for (auto& [path, weak_session] : m_sessions) {
64✔
357
            if (auto ptr = weak_session.lock()) {
35✔
358
                ptr->force_close();
33✔
359
                m_waiting_sessions[path] = std::move(ptr);
33✔
360
            }
33✔
361
        }
35✔
362
        m_sessions.clear();
93✔
363
    }
93✔
364

46✔
365
    sync_manager_shared->log_out_user(m_identity);
93✔
366
    emit_change_to_subscribers(*this);
93✔
367
}
93✔
368

369
bool SyncUser::is_logged_in() const
370
{
1,673✔
371
    util::CheckedLockGuard lock(m_mutex);
1,673✔
372
    util::CheckedLockGuard lock2(m_tokens_mutex);
1,673✔
373
    return do_is_logged_in();
1,673✔
374
}
1,673✔
375

376
bool SyncUser::do_is_logged_in() const
377
{
3,352✔
378
    return !m_access_token.token.empty() && !m_refresh_token.token.empty() && state() == State::LoggedIn;
3,352✔
379
}
3,352✔
380

381
void SyncUser::invalidate()
382
{
×
383
    set_state(SyncUser::State::Removed);
×
384
}
×
385

386
std::string SyncUser::refresh_token() const
387
{
226✔
388
    util::CheckedLockGuard lock(m_tokens_mutex);
226✔
389
    return m_refresh_token.token;
226✔
390
}
226✔
391

392
std::string SyncUser::access_token() const
393
{
3,227✔
394
    util::CheckedLockGuard lock(m_tokens_mutex);
3,227✔
395
    return m_access_token.token;
3,227✔
396
}
3,227✔
397

398
std::string SyncUser::device_id() const
399
{
42✔
400
    util::CheckedLockGuard lock(m_mutex);
42✔
401
    return m_device_id;
42✔
402
}
42✔
403

404
bool SyncUser::has_device_id() const
405
{
40✔
406
    util::CheckedLockGuard lock(m_mutex);
40✔
407
    return !m_device_id.empty() && m_device_id != "000000000000000000000000";
40✔
408
}
40✔
409

410
SyncUser::State SyncUser::state() const NO_THREAD_SAFETY_ANALYSIS
411
{
10,510✔
412
    return m_state;
10,510✔
413
}
10,510✔
414

415
void SyncUser::set_state(SyncUser::State state)
416
{
999✔
417
    util::CheckedLockGuard lock(m_mutex);
999✔
418
    m_state = state;
999✔
419

488✔
420
    REALM_ASSERT(m_sync_manager);
999✔
421
    m_sync_manager->perform_metadata_update([&](const auto& manager) {
998✔
422
        auto metadata = manager.get_or_make_user_metadata(m_identity, m_provider_type);
997✔
423
        metadata->set_state(state);
997✔
424
    });
997✔
425
}
999✔
426

427
SyncUserProfile SyncUser::user_profile() const
428
{
74✔
429
    util::CheckedLockGuard lock(m_mutex);
74✔
430
    return m_user_profile;
74✔
431
}
74✔
432

433
util::Optional<bson::BsonDocument> SyncUser::custom_data() const
434
{
4✔
435
    util::CheckedLockGuard lock(m_tokens_mutex);
4✔
436
    return m_access_token.user_data;
4✔
437
}
4✔
438

439
void SyncUser::update_user_profile(const SyncUserProfile& profile)
440
{
972✔
441
    util::CheckedLockGuard lock(m_mutex);
972✔
442
    if (m_state != SyncUser::State::LoggedIn) {
972✔
443
        return;
2✔
444
    }
2✔
445

474✔
446
    m_user_profile = profile;
970✔
447

474✔
448
    m_sync_manager->perform_metadata_update([&](const auto& manager) {
969✔
449
        auto metadata = manager.get_or_make_user_metadata(m_identity, m_provider_type);
968✔
450
        metadata->set_user_profile(profile);
968✔
451
    });
968✔
452
}
970✔
453

454
void SyncUser::register_session(std::shared_ptr<SyncSession> session)
455
{
1,345✔
456
    const std::string& path = session->path();
1,345✔
457
    util::CheckedUniqueLock lock(m_mutex);
1,345✔
458
    switch (m_state) {
1,345✔
459
        case State::LoggedIn:
1,341✔
460
            m_sessions[path] = session;
1,341✔
461
            break;
1,341✔
462
        case State::LoggedOut:
4✔
463
            m_waiting_sessions[path] = session;
4✔
464
            break;
4✔
465
        case State::Removed:
✔
466
            break;
×
467
    }
1,345✔
468
}
1,345✔
469

470
app::MongoClient SyncUser::mongo_client(const std::string& service_name)
471
{
57✔
472
    util::CheckedLockGuard lk(m_mutex);
57✔
473
    REALM_ASSERT(m_state == SyncUser::State::LoggedIn);
57✔
474
    return app::MongoClient(shared_from_this(), m_sync_manager->app().lock(), service_name);
57✔
475
}
57✔
476

477
void SyncUser::set_binding_context_factory(SyncUserContextFactory factory)
478
{
×
479
    std::lock_guard<std::mutex> lock(s_binding_context_factory_mutex);
×
480
    s_binding_context_factory = std::move(factory);
×
481
}
×
482

483
void SyncUser::refresh_custom_data(util::UniqueFunction<void(util::Optional<app::AppError>)> completion_block)
484
    REQUIRES(!m_mutex)
485
{
28✔
486
    refresh_custom_data(false, std::move(completion_block));
28✔
487
}
28✔
488

489
void SyncUser::refresh_custom_data(bool update_location,
490
                                   util::UniqueFunction<void(util::Optional<app::AppError>)> completion_block)
491
{
52✔
492
    std::shared_ptr<app::App> app;
52✔
493
    std::shared_ptr<SyncUser> user;
52✔
494
    {
52✔
495
        util::CheckedLockGuard lk(m_mutex);
52✔
496
        if (m_state != SyncUser::State::Removed) {
52✔
497
            user = shared_from_this();
50✔
498
        }
50✔
499
        if (m_sync_manager) {
52✔
500
            app = m_sync_manager->app().lock();
52✔
501
        }
52✔
502
    }
52✔
503
    if (!user) {
52✔
504
        completion_block(app::AppError(
2✔
505
            ErrorCodes::ClientUserNotFound,
2✔
506
            util::format("Cannot initiate a refresh on user '%1' because the user has been removed", m_identity)));
2✔
507
    }
2✔
508
    else if (!app) {
50✔
509
        completion_block(app::AppError(
×
510
            ErrorCodes::ClientAppDeallocated,
×
511
            util::format("Cannot initiate a refresh on user '%1' because the app has been deallocated", m_identity)));
×
512
    }
×
513
    else {
50✔
514
        std::weak_ptr<SyncUser> weak_user = user->weak_from_this();
50✔
515
        app->refresh_custom_data(user, update_location,
50✔
516
                                 [completion_block = std::move(completion_block), weak_user](auto error) {
50✔
517
                                     if (auto strong = weak_user.lock()) {
50✔
518
                                         strong->emit_change_to_subscribers(*strong);
50✔
519
                                     }
50✔
520
                                     completion_block(error);
50✔
521
                                 });
50✔
522
    }
50✔
523
}
52✔
524

525
bool SyncUser::access_token_refresh_required() const
526
{
1,679✔
527
    using namespace std::chrono;
1,679✔
528
    constexpr size_t buffer_seconds = 5; // arbitrary
1,679✔
529
    util::CheckedLockGuard lock(m_tokens_mutex);
1,679✔
530
    const auto now = duration_cast<seconds>(system_clock::now().time_since_epoch()).count() +
1,679✔
531
                     m_seconds_to_adjust_time_for_testing.load(std::memory_order_relaxed);
1,679✔
532
    const auto threshold = now - buffer_seconds;
1,679✔
533
    return do_is_logged_in() && m_access_token.expires_at < static_cast<int64_t>(threshold);
1,679✔
534
}
1,679✔
535

536
} // namespace realm
537

538
namespace std {
539
size_t hash<realm::SyncUserIdentity>::operator()(const realm::SyncUserIdentity& k) const
540
{
×
541
    return ((hash<string>()(k.id) ^ (hash<string>()(k.provider_type) << 1)) >> 1);
×
542
}
×
543
} // namespace std
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc