ESP RFID Webui
Architecture and call graph
Loading...
Searching...
No Matches
AuthSync Class Reference

#include <AuthSync.h>

Public Member Functions

 AuthSync (const String &serverBase)
 ~AuthSync ()
bool begin ()
bool update ()
bool preloadOffline ()
bool isAuthorized (const String &uid)
void dumpMemoryStats () const
void setServerProbeResult (bool ok, unsigned long probeMillis)
uint32_t getCardCount () const
size_t getMemoryUsed () const

Static Public Attributes

static constexpr size_t MAX_SAFE_CARDS = 200000UL
static constexpr size_t MAX_SAFE_BYTES = (MAX_SAFE_CARDS + 7) / 8

Private Member Functions

bool syncFromServer ()
bool getCardAuthFromServer (const String &uid, int &card_id, bool &authorized)
void addKnownAuth (const String &uid, bool allowed)
void saveETagToNVS ()
void loadETagFromNVS ()
bool saveBitsetToFS (size_t bytes)
bool loadBitsetFromFS ()
bool saveAllowDenyToFS () const
bool loadAllowDenyFromFS ()
bool isBitSet (uint32_t id) const
void setBit (uint32_t id) const
void clearBit (uint32_t id) const
bool writeByteAt (size_t idx, uint8_t val) const
bool readByteAt (size_t idx, uint8_t &out) const

Static Private Member Functions

static uint64_t hashUid (const String &s)
static size_t calcBitsetBytes (uint32_t maxId)

Private Attributes

String server_base
uint8_t * authorized_bits = nullptr
uint32_t max_card_id = 0
unsigned long last_sync = 0
unsigned long SYNC_INTERVAL = 60000
unsigned long last_server_probe = 0
bool server_last_ok = false
bool serverPreviouslyUnreachable = false
Preferences prefs_
bool prefsOpen_ = false
std::vector< uint64_t > allowHashes_
std::vector< uint64_t > denyHashes_
String last_etag

Detailed Description

Definition at line 9 of file AuthSync.h.

Constructor & Destructor Documentation

◆ AuthSync()

AuthSync::AuthSync ( const String & serverBase)
explicit

Definition at line 49 of file AuthSync.cpp.

49 : server_base(serverBase) {
50
52}
uint8_t * authorized_bits
Definition AuthSync.h:48
String server_base
Definition AuthSync.h:44
uint8_t authorized_bits_storage[25000]
Definition AuthSync.cpp:22

References authorized_bits, anonymous_namespace{AuthSync.cpp}::authorized_bits_storage, and server_base.

Referenced by setup().

Here is the caller graph for this function:

◆ ~AuthSync()

AuthSync::~AuthSync ( )

Definition at line 54 of file AuthSync.cpp.

54 {
55 // authorized_bits points at static storage — don't free. Reset pointer for safety.
56 authorized_bits = nullptr;
57 if (prefsOpen_) {
58 prefs_.end();
59 prefsOpen_ = false;
60 }
61}
Preferences prefs_
Definition AuthSync.h:69
bool prefsOpen_
Definition AuthSync.h:70

References authorized_bits, prefs_, and prefsOpen_.

Member Function Documentation

◆ addKnownAuth()

void AuthSync::addKnownAuth ( const String & uid,
bool allowed )
private

Definition at line 471 of file AuthSync.cpp.

471 {
472 // Learn a card's authorization status for offline use by
473
474 const uint64_t h = hashUid(uid);
475 // Ensure sorted insert - helper lambda
476 auto insert_sorted = [](std::vector<uint64_t>& vec, uint64_t val){
477 const auto it = std::lower_bound(vec.begin(), vec.end(), val);
478 if (it == vec.end() || *it != val) vec.insert(it, val);
479 };
480
481 if (allowed) {
482 // Remove from deny if present
483 const auto it = std::lower_bound(denyHashes_.begin(), denyHashes_.end(), h);
484 if (it != denyHashes_.end() && *it == h) denyHashes_.erase(it);
485 insert_sorted(allowHashes_, h);
486 } else {
487 const auto it = std::lower_bound(allowHashes_.begin(), allowHashes_.end(), h);
488 if (it != allowHashes_.end() && *it == h) allowHashes_.erase(it);
489 insert_sorted(denyHashes_, h);
490 }
492}
std::vector< uint64_t > allowHashes_
Definition AuthSync.h:71
void saveETagToNVS()
Definition AuthSync.cpp:542
std::vector< uint64_t > denyHashes_
Definition AuthSync.h:72
static uint64_t hashUid(const String &s)
Definition AuthSync.cpp:40

References hashUid(), and saveETagToNVS().

Referenced by isAuthorized().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ begin()

bool AuthSync::begin ( )

Definition at line 120 of file AuthSync.cpp.

120 {
121 if (!prefsOpen_) {
122 prefsOpen_ = prefs_.begin("auth", false);
123 }
124 if (prefsOpen_) {
126 }
127 // Try to load a previously saved bitset snapshot from filesystem
128 if (LittleFS.begin()) {
130 }
131 return syncFromServer();
132}
void loadETagFromNVS()
Definition AuthSync.cpp:556
bool syncFromServer()
Definition AuthSync.cpp:288
bool loadBitsetFromFS()
Definition AuthSync.cpp:595

References loadBitsetFromFS(), loadETagFromNVS(), prefs_, prefsOpen_, and syncFromServer().

Referenced by setup().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ calcBitsetBytes()

size_t AuthSync::calcBitsetBytes ( uint32_t maxId)
staticprivate

Definition at line 64 of file AuthSync.cpp.

64 {
65 // bits = maxId + 1
66 const size_t bits = (size_t)maxId + 1;
67 if (bits == 0) return 0;
68 if (bits > std::numeric_limits<size_t>::max() - 7) return 0;
69 return (bits + 7) / 8;
70}

Referenced by dumpMemoryStats(), getMemoryUsed(), readByteAt(), syncFromServer(), and writeByteAt().

Here is the caller graph for this function:

◆ clearBit()

void AuthSync::clearBit ( uint32_t id) const
private

Definition at line 111 of file AuthSync.cpp.

111 {
112 if (!authorized_bits) return;//buffer is initialized
113 if (id > max_card_id) return;//bounds check
114 const size_t idx = (size_t)id >> 3; //divide by 8
115 const uint8_t bit = id & 7;
116 authorized_bits[idx] &= ~(1u << bit);
117}
uint32_t max_card_id
Definition AuthSync.h:49

References authorized_bits, and max_card_id.

◆ dumpMemoryStats()

void AuthSync::dumpMemoryStats ( ) const

Definition at line 649 of file AuthSync.cpp.

649 {
650 // Print free heap
651 const size_t freeHeap = esp_get_free_heap_size();
652 const size_t largest = heap_caps_get_largest_free_block(MALLOC_CAP_8BIT);
653 Serial.printf("[AuthSync] freeHeap=%u largestFreeBlock=%u\n", static_cast<unsigned>(freeHeap), static_cast<unsigned>(largest));
654
655 // Hash vectors
656 Serial.printf("[AuthSync] allowHashes entries=%u bytes=%u\n", static_cast<unsigned>(allowHashes_.size()), static_cast<unsigned>(allowHashes_.size() * sizeof(uint64_t)));
657 Serial.printf("[AuthSync] denyHashes entries=%u bytes=%u\n", static_cast<unsigned>(denyHashes_.size()), static_cast<unsigned>(denyHashes_.size() * sizeof(uint64_t)));
658
659 // Bitset usage
660 const size_t bitBytes = calcBitsetBytes(max_card_id);
661 Serial.printf("[AuthSync] max_card_id=%u bitset_bytes=%u MAX_SAFE_BYTES=%u\n", max_card_id, static_cast<unsigned>(bitBytes), static_cast<unsigned>(MAX_SAFE_BYTES));
662}
static constexpr size_t MAX_SAFE_BYTES
Definition AuthSync.h:19
static size_t calcBitsetBytes(uint32_t maxId)
Definition AuthSync.cpp:64

References calcBitsetBytes(), max_card_id, and MAX_SAFE_BYTES.

Referenced by setup().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ getCardAuthFromServer()

bool AuthSync::getCardAuthFromServer ( const String & uid,
int & card_id,
bool & authorized )
private

Definition at line 193 of file AuthSync.cpp.

193 {
194 card_id = -1;
195 authorized = false;
196 // Guard: need WiFi and a configured server base
197 if (WiFi.status() != WL_CONNECTED || server_base.length() == 0) return false;
198
199 // Backoff: only apply if we've actually probed before (last_server_probe != 0)
200 if (!server_last_ok && last_server_probe != 0 && (millis() - last_server_probe) < 10000) {
201 return false; // use offline cache immediately
202 }
203
204 // Periodic lightweight server status probe (cached) to avoid expensive lookups when server down
205 if (millis() - last_server_probe > 5000 || last_server_probe == 0) {
206 last_server_probe = millis();
207 HTTPClient ping;
208 // Further reduce probe timeout to minimize per-scan delay when offline.
209 // A very short timeout risks false negatives on a slow network; tune if needed.
210 ping.setTimeout(250); // was 1000ms
211 ping.begin(server_base + "/api/status");
212 const int sc = ping.GET();
213 ping.end();
214 server_last_ok = (sc == 200);
215 if (!server_last_ok) {
216 Serial.println("[AuthSync] Server status probe failed quickly; using offline cache");
217 return false; // return immediately to avoid extra delay this scan
218 }
219 }
220 if (!server_last_ok) return false; // fallback to offline caches
221
222 // Additional quick guard: if probe just failed we already returned
223 HTTPClient http;
224 http.setTimeout(1200); // reduce per-card lookup timeout
225 http.begin(server_base + "/api/cards/" + uid);
226 const int code = http.GET();
227 if (code != 200) {
228 http.end();
229 return false;
230 }
231 String payload = http.getString();
232 http.end();
233
234 JsonDocument doc;
235 const DeserializationError err = deserializeJson(doc, payload);
236 if (err) return false;
237
238 const bool exists = doc["exists"] | false;
239 if (!exists) return false;
240 card_id = doc["card_id"] | -1;
241 authorized = doc["authorized"] | false;
242 return true;
243}
bool server_last_ok
Definition AuthSync.h:54
unsigned long last_server_probe
Definition AuthSync.h:53

References last_server_probe, server_base, and server_last_ok.

Referenced by isAuthorized().

Here is the caller graph for this function:

◆ getCardCount()

uint32_t AuthSync::getCardCount ( ) const
inline

Definition at line 40 of file AuthSync.h.

40{ return max_card_id + 1; }

References max_card_id.

◆ getMemoryUsed()

size_t AuthSync::getMemoryUsed ( ) const
inline

Definition at line 41 of file AuthSync.h.

References calcBitsetBytes(), and max_card_id.

Here is the call graph for this function:

◆ hashUid()

uint64_t AuthSync::hashUid ( const String & s)
staticprivate

Definition at line 40 of file AuthSync.cpp.

40 {
41 return HashUtils::hashUid(s);
42 /* String t = s; // normalize to uppercase, trimmed
43 t.trim();
44 t.toUpperCase();
45 //Normalise hash for whitespace or case differences
46 return fnv1a64(reinterpret_cast<const uint8_t*>(t.c_str()), t.length());
47*/}
uint64_t hashUid(const String &s)
Definition HashUtils.cpp:18

References HashUtils::hashUid().

Referenced by addKnownAuth(), and isAuthorized().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ isAuthorized()

bool AuthSync::isAuthorized ( const String & uid)

Definition at line 158 of file AuthSync.cpp.

158 {
159 // Compute and log hash for debugging/offline cache tracking
160 const uint64_t h = hashUid(uid);
161 Serial.printf("[AuthSync] UID: %s -> Hash: 0x%016llX\n", uid.c_str(), h);
162
163 // Priority 1: Check local cache first (deny takes precedence)
164 const bool denied = std::binary_search(denyHashes_.begin(), denyHashes_.end(), h);
165 if (denied) {
166 Serial.println("[AuthSync] Found in deny cache -> DENIED");
167 return false;
168 }
169 const bool allowed = std::binary_search(allowHashes_.begin(), allowHashes_.end(), h);
170 if (allowed) {
171 Serial.println("[AuthSync] Found in allow cache -> AUTHORIZED");
172 return true;
173 }
174
175 // Priority 2: Unknown card - query server if online
176 Serial.println("[AuthSync] Unknown card; checking server...");
177 if (WiFi.status() == WL_CONNECTED && server_base.length() > 0) {
178 int card_id = -1;
179 bool server_allowed = false;
180 if (getCardAuthFromServer(uid, card_id, server_allowed)) {
181 // Learn the server result for offline use next time
182 addKnownAuth(uid, server_allowed);
183 Serial.printf("[AuthSync] Server says: %s\n", server_allowed ? "AUTHORIZED" : "DENIED");
184 return server_allowed;
185 }
186 }
187
188 // Priority 3: Offline and unknown - deny by default
189 Serial.println("[AuthSync] Offline + unknown -> DENIED by default");
190 return false;
191}
bool getCardAuthFromServer(const String &uid, int &card_id, bool &authorized)
Definition AuthSync.cpp:193
void addKnownAuth(const String &uid, bool allowed)
Definition AuthSync.cpp:471

References addKnownAuth(), getCardAuthFromServer(), hashUid(), and server_base.

Referenced by loop().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ isBitSet()

bool AuthSync::isBitSet ( uint32_t id) const
private

Definition at line 93 of file AuthSync.cpp.

93 {
94 if (!authorized_bits) return false;
95 if (id > max_card_id) return false;
96 const size_t idx = (size_t)id >> 3;
97 const uint8_t bit = id & 7;
98 return ((authorized_bits[idx] >> bit) & 1) != 0;
99}

References authorized_bits, and max_card_id.

◆ loadAllowDenyFromFS()

bool AuthSync::loadAllowDenyFromFS ( )
private

Definition at line 516 of file AuthSync.cpp.

516 {
517 if (!LittleFS.begin()) return false;
518 const char *final = "/allow_deny.bin";
519 if (!LittleFS.exists(final)) return false;
520 File f = LittleFS.open(final, FILE_READ);
521 if (!f) return false;
522 if (f.size() < static_cast<int>(sizeof(uint32_t))*2) { f.close(); return false; }
523 uint32_t an = 0, dn = 0;
524 f.read(reinterpret_cast<uint8_t*>(&an), sizeof(an));
525 f.read(reinterpret_cast<uint8_t*>(&dn), sizeof(dn));
526 // Basic sanity check
527 const size_t expected = sizeof(uint32_t)*2 + (size_t)an * sizeof(uint64_t) + (size_t)dn * sizeof(uint64_t);
528 if ((size_t)f.size() < expected) { f.close(); return false; }
529 allowHashes_.assign(an, 0);
530 denyHashes_.assign(dn, 0);
531 // Read hashes
532 if (an) f.read(reinterpret_cast<uint8_t*>(allowHashes_.data()), an * sizeof(uint64_t));
533 if (dn) f.read(reinterpret_cast<uint8_t*>(denyHashes_.data()), dn * sizeof(uint64_t));
534 f.close();
535 // Ensure sorted
536 std::sort(allowHashes_.begin(), allowHashes_.end());
537 std::sort(denyHashes_.begin(), denyHashes_.end());
538 return true;
539}

Referenced by loadETagFromNVS().

Here is the caller graph for this function:

◆ loadBitsetFromFS()

bool AuthSync::loadBitsetFromFS ( )
private

Definition at line 595 of file AuthSync.cpp.

595 {
596 const char *final = "/bits.bin";
597 if (!LittleFS.exists(final)) return false;
598 File f = LittleFS.open(final, FILE_READ);
599 if (!f) return false;
600 const size_t bytes = f.size();
601 if (bytes == 0 || bytes > MAX_SAFE_BYTES) {
602 f.close();
603 Serial.println("[AuthSync] Bitset file size invalid or too large");
604 return false;
605 }
606 const size_t r = f.read(reinterpret_cast<uint8_t*>(authorized_bits), bytes);
607 f.close();
608 if (r != bytes) {
609 Serial.println("[AuthSync] Failed to read full bitset from file");
610 return false;
611 }
612 if (prefsOpen_) {
613 max_card_id = prefs_.getUInt("max_id", (uint32_t)((bytes * 8) - 1));
614 } else {
615 max_card_id = (uint32_t)((bytes * 8) - 1);
616 }
617 Serial.printf("[AuthSync] Loaded bitset snapshot %u bytes, max_id=%u\n", static_cast<unsigned>(bytes), max_card_id);
618 return true;
619}

References authorized_bits, max_card_id, MAX_SAFE_BYTES, prefs_, and prefsOpen_.

Referenced by begin(), and preloadOffline().

Here is the caller graph for this function:

◆ loadETagFromNVS()

void AuthSync::loadETagFromNVS ( )
private

Definition at line 556 of file AuthSync.cpp.

556 {
557 if (!prefsOpen_) return;
558 // Restore persisted ETag if present
559 if (prefs_.isKey("bitset_etag")) {
560 last_etag = prefs_.getString("bitset_etag", "");
561 } else {
562 last_etag = "";
563 }
564 // Attempt to load allow/deny from LittleFS; if it fails leave vectors empty
566}
bool loadAllowDenyFromFS()
Definition AuthSync.cpp:516
String last_etag
Definition AuthSync.h:74

References last_etag, loadAllowDenyFromFS(), prefs_, and prefsOpen_.

Referenced by begin(), and preloadOffline().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ preloadOffline()

bool AuthSync::preloadOffline ( )

Definition at line 136 of file AuthSync.cpp.

136 {
137 if (!prefsOpen_) {
138 prefsOpen_ = prefs_.begin("auth", false);
139 }
140 if (prefsOpen_) {
142 // Load filesystem snapshot if present
143 if (LittleFS.begin()) {
145 }
146 return true;
147 }
148 return false;
149}

References loadBitsetFromFS(), loadETagFromNVS(), prefs_, and prefsOpen_.

Referenced by setup().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ readByteAt()

bool AuthSync::readByteAt ( size_t idx,
uint8_t & out ) const
private

Definition at line 83 of file AuthSync.cpp.

83 {
84 if (!authorized_bits) return false;
85 const size_t bytes = calcBitsetBytes(max_card_id);
86 if (bytes == 0) return false;
87 if (idx >= bytes) return false;
88 out = authorized_bits[idx];
89 return true;
90}

References authorized_bits, calcBitsetBytes(), and max_card_id.

Here is the call graph for this function:

◆ saveAllowDenyToFS()

bool AuthSync::saveAllowDenyToFS ( ) const
private

Definition at line 494 of file AuthSync.cpp.

494 {
495 if (!LittleFS.begin()) return false;
496 const char *tmp = "/allow_deny.bin.tmp";
497 const char *final = "/allow_deny.bin";
498 File f = LittleFS.open(tmp, FILE_WRITE);
499 if (!f) return false;
500 // Write counts as 32-bit little-endian
501 const uint32_t an = (uint32_t)allowHashes_.size();
502 const uint32_t dn = (uint32_t)denyHashes_.size();
503 f.write(reinterpret_cast<const uint8_t*>(&an), sizeof(an));
504 f.write(reinterpret_cast<const uint8_t*>(&dn), sizeof(dn));
505 if (an) f.write(reinterpret_cast<const uint8_t*>(allowHashes_.data()), an * sizeof(uint64_t));
506 if (dn) f.write(reinterpret_cast<const uint8_t*>(denyHashes_.data()), dn * sizeof(uint64_t));
507 f.close();
508 LittleFS.remove(final);
509 if (!LittleFS.rename(tmp, final)) {
510 LittleFS.remove(tmp);
511 return false;
512 }
513 return true;
514}

Referenced by saveETagToNVS().

Here is the caller graph for this function:

◆ saveBitsetToFS()

bool AuthSync::saveBitsetToFS ( size_t bytes)
private

Definition at line 568 of file AuthSync.cpp.

568 {
569 if (bytes == 0) return false;
570 const char *tmp = "/bits.bin.tmp";
571 const char *final = "/bits.bin";
572 File f = LittleFS.open(tmp, FILE_WRITE);
573 if (!f) {
574 Serial.println("[AuthSync] Failed to open tmp file for bitset");
575 return false;
576 }
577 //removed redundant reinterpret_cast<const uint8_t*> from below
578 const size_t written = f.write((authorized_bits), bytes);
579 f.close();
580 if (written != bytes) {
581 Serial.println("[AuthSync] Failed to write full bitset to tmp file");
582 LittleFS.remove(tmp);
583 return false;
584 }
585 LittleFS.remove(final);
586 if (!LittleFS.rename(tmp, final)) {
587 Serial.println("[AuthSync] Failed to rename bitset tmp file");
588 return false;
589 }
590 if (prefsOpen_) prefs_.putUInt("max_id", max_card_id);
591 Serial.printf("[AuthSync] Saved bitset snapshot %u bytes\n", static_cast<unsigned>(bytes));
592 return true;
593}

References authorized_bits, max_card_id, prefs_, and prefsOpen_.

Referenced by syncFromServer().

Here is the caller graph for this function:

◆ saveETagToNVS()

void AuthSync::saveETagToNVS ( )
private

Definition at line 542 of file AuthSync.cpp.

542 {
543 if (!prefsOpen_) return;
544 // Persist last_etag only
545 if (last_etag.length()) {
546 prefs_.putString("bitset_etag", last_etag);
547 } else {
548 prefs_.remove("bitset_etag");
549 }
550 // Persist allow/deny vectors to LittleFS (best-effort - log on failure, no retry)
551 if (!saveAllowDenyToFS()) {
552 Serial.println("[AuthSync] Warning: failed to persist allow/deny to LittleFS");
553 }
554}
bool saveAllowDenyToFS() const
Definition AuthSync.cpp:494

References last_etag, prefs_, prefsOpen_, and saveAllowDenyToFS().

Referenced by addKnownAuth(), and syncFromServer().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ setBit()

void AuthSync::setBit ( uint32_t id) const
private

Definition at line 102 of file AuthSync.cpp.

102 {
103 if (!authorized_bits) return;//buffer is initialized
104 if (id > max_card_id) return;//bounds check
105 const size_t idx = (size_t)id >> 3;
106 const uint8_t bit = id & 7;
107 authorized_bits[idx] |= 1u << bit;
108}

References authorized_bits, and max_card_id.

◆ setServerProbeResult()

void AuthSync::setServerProbeResult ( bool ok,
unsigned long probeMillis )

Definition at line 669 of file AuthSync.cpp.

669 {
670 server_last_ok = ok;
671 last_server_probe = probeMillis;
672}

References last_server_probe, and server_last_ok.

Referenced by serverCheckTimerCallback().

Here is the caller graph for this function:

◆ syncFromServer()

bool AuthSync::syncFromServer ( )
private

Definition at line 288 of file AuthSync.cpp.

288 {
289 if (WiFi.status() != WL_CONNECTED || server_base.length() == 0)
290 return false;
291 // Backoff: only after a failed probe and not on the very first attempt (last_server_probe != 0)
292 if (!server_last_ok && last_server_probe != 0 && (millis() - last_server_probe) < 10000) {
293 Serial.println("[AuthSync] Backoff active; skipping sync");
294 return false;
295 }
296
297 // Only perform an inline reachability probe on the very first sync attempt.
298 // After the initial probe we rely on the external server-check timer
299 // (NetworkTask) to update `server_last_ok` and `last_server_probe` so
300 // we don't duplicate probes here.
301 if (last_server_probe == 0) {
302 // First-time probe: do a quick reachability check so the initial
303 // sync (called from setup()) can proceed when no external timer has
304 // yet run.
305 last_server_probe = millis();
306 HTTPClient ping;
307 ping.setTimeout(1000); // short probe for initial sync
308 ping.begin(server_base + "/api/status");
309 const int sc = ping.GET();
310 ping.end();
311 server_last_ok = (sc == 200);
312 if (!server_last_ok) {
313 Serial.println("[AuthSync] Sync aborted: initial probe failed (server unreachable)");
314 return false;
315 }
316 } else if (!server_last_ok) {
317 // An external timer (NetworkTask) has already probed and reported the
318 // server as unreachable — skip the sync to avoid redundant network calls.
319 Serial.println("[AuthSync] Sync aborted: server unreachable (cached)");
320 return false;
321 }
322
323 HTTPClient http;
324 http.setTimeout(2000); // shorter sync timeout
325 http.begin(server_base + "/api/sync");
326 // Send If-None-Match header if we have a saved ETag to allow 304 responses
327 if (last_etag.length()) {
328 http.addHeader("If-None-Match", last_etag);
329 }
330 const int code = http.GET();
331
332 if (code == 304) {
333 // Not modified — nothing to do. Update last_sync and return success.
334 last_sync = millis();
335 Serial.println("[AuthSync] Sync: 304 Not Modified — skipping update");
336 http.end();
337 return true;
338 }
339 if (code != 200) {
340 Serial.printf("[AuthSync] Sync failed with code: %d\n", code);
341 http.end();
342 return false;
343 }
344
345 String payload = http.getString();
346 http.end();
347
348 JsonDocument doc;
349 const DeserializationError err = deserializeJson(doc, payload);
350 if (err) {
351 Serial.printf("[AuthSync] JSON parse error: %s\n", err.c_str());
352 return false;
353 }
354
355 // Extract new maximum card ID and bitset hex from server payload
356 const uint32_t new_max = doc["max_id"] | 0;
357 const String hex = doc["bits"].as<String>();
358
359 // Save new ETag header from server (if returned)
360 const String serverEtag = http.header("ETag");
361 if (serverEtag.length()) {
362 last_etag = serverEtag;
363 if (prefsOpen_) prefs_.putString("bitset_etag", last_etag);
364 }
365
366 // Use the static storage; validate size fits
367 const size_t bytes = calcBitsetBytes(new_max);
368 if (bytes == 0 || bytes > MAX_SAFE_BYTES) {
369 Serial.println("[AuthSync] Sync failed: requested bitset too large for static buffer");
370 max_card_id = 0;
371 return false;
372 }
373 // Zero only the required portion
374 std::fill_n(authorized_bits, bytes, 0);
375
376 // Decode the hex bitset payload (two characters per byte) into
377 // the newly allocated buffer using the bounds-checked writer.
378 for (size_t i = 0; i + 1 < hex.length(); i += 2) {
379 String byteStr = hex.substring(i, i + 2);
380 const auto v = static_cast<uint8_t>(strtol(byteStr.c_str(), nullptr, 16));
381 if (!writeByteAt(i / 2, v)) break;
382 }
383
384 // Commit the new bitset and record the time of this successful sync.
385 max_card_id = new_max;
386 last_sync = millis();
387
388 // Persist the bitset snapshot to filesystem for faster boot/offline use
389 if (LittleFS.begin()) {
390 saveBitsetToFS(bytes);
391 }
392
393 // Optionally refresh offline allow/deny UID hash lists when the
394 // server includes arrays of UIDs. These are normalized, hashed,
395 // de-duplicated, and then swapped into the in-memory caches.
396 if (doc["allow"].is<JsonArray>() || doc["allow_uids"].is<JsonArray>() ||
397 doc["deny"].is<JsonArray>() || doc["deny_uids"].is<JsonArray>()) {
398 std::vector<uint64_t> allowNew;
399 std::vector<uint64_t> denyNew;
400// Extract optional allow/deny UID arrays from the sync JSON, normalize + hash
401// each UID into 64-bit values, and append to the new vectors.
402 auto loadArray = [&](const char* key, std::vector<uint64_t>& out){
403 const JsonVariant var = doc[key];
404 if (!var.is<JsonArray>()) return;
405 for (JsonVariant v : var.as<JsonArray>()) {
406 String uid = v.as<const char*>();
407 out.push_back(hashUid(uid));
408 }
409 };
410//std::sort magic incantation
411
412 loadArray("allow", allowNew);
413 loadArray("allow_uids", allowNew);
414 loadArray("deny", denyNew);
415 loadArray("deny_uids", denyNew);
416
417 std::sort(allowNew.begin(), allowNew.end());//
418 allowNew.erase(std::unique(allowNew.begin(), allowNew.end()), allowNew.end());
419 std::sort(denyNew.begin(), denyNew.end());
420 denyNew.erase(std::unique(denyNew.begin(), denyNew.end()), denyNew.end());
421
422 if (!allowNew.empty() || !denyNew.empty()) {
423 allowHashes_.swap(allowNew);
424 denyHashes_.swap(denyNew);
426 //It then saves the new vectors to NVS for persistence across reboots.
427
428 }
429 }
430
431 // Log a compact summary of the sync result for debugging.
432 Serial.printf("[AuthSync] Synced max_id=%u (%u bytes heap)\n", max_card_id, bytes);
433 return true;
434}
bool saveBitsetToFS(size_t bytes)
Definition AuthSync.cpp:568
bool writeByteAt(size_t idx, uint8_t val) const
Definition AuthSync.cpp:73
unsigned long last_sync
Definition AuthSync.h:50

References authorized_bits, calcBitsetBytes(), last_etag, last_server_probe, last_sync, max_card_id, MAX_SAFE_BYTES, prefs_, prefsOpen_, saveBitsetToFS(), saveETagToNVS(), server_base, server_last_ok, and writeByteAt().

Referenced by begin(), and update().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ update()

bool AuthSync::update ( )

Definition at line 151 of file AuthSync.cpp.

151 {
152 if (millis() - last_sync > SYNC_INTERVAL) {
153 return syncFromServer();
154 }
155 return true;
156}
unsigned long SYNC_INTERVAL
Definition AuthSync.h:51

References last_sync, SYNC_INTERVAL, and syncFromServer().

Referenced by NetworkTask().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ writeByteAt()

bool AuthSync::writeByteAt ( size_t idx,
uint8_t val ) const
private

Definition at line 73 of file AuthSync.cpp.

73 {
74 if (!authorized_bits) return false;
75 const size_t bytes = calcBitsetBytes(max_card_id);
76 if (bytes == 0) return false;
77 if (idx >= bytes) return false;
78 authorized_bits[idx] = val;
79 return true;
80}

References authorized_bits, calcBitsetBytes(), and max_card_id.

Referenced by syncFromServer().

Here is the call graph for this function:
Here is the caller graph for this function:

Member Data Documentation

◆ allowHashes_

std::vector<uint64_t> AuthSync::allowHashes_
private

Definition at line 71 of file AuthSync.h.

◆ authorized_bits

uint8_t* AuthSync::authorized_bits = nullptr
private

◆ denyHashes_

std::vector<uint64_t> AuthSync::denyHashes_
private

Definition at line 72 of file AuthSync.h.

◆ last_etag

String AuthSync::last_etag
private

Definition at line 74 of file AuthSync.h.

Referenced by loadETagFromNVS(), saveETagToNVS(), and syncFromServer().

◆ last_server_probe

unsigned long AuthSync::last_server_probe = 0
private

Definition at line 53 of file AuthSync.h.

Referenced by getCardAuthFromServer(), setServerProbeResult(), and syncFromServer().

◆ last_sync

unsigned long AuthSync::last_sync = 0
private

Definition at line 50 of file AuthSync.h.

Referenced by syncFromServer(), and update().

◆ max_card_id

uint32_t AuthSync::max_card_id = 0
private

◆ MAX_SAFE_BYTES

size_t AuthSync::MAX_SAFE_BYTES = (MAX_SAFE_CARDS + 7) / 8
staticconstexpr

Definition at line 19 of file AuthSync.h.

Referenced by dumpMemoryStats(), loadBitsetFromFS(), and syncFromServer().

◆ MAX_SAFE_CARDS

size_t AuthSync::MAX_SAFE_CARDS = 200000UL
staticconstexpr

Definition at line 17 of file AuthSync.h.

◆ prefs_

Preferences AuthSync::prefs_
private

◆ prefsOpen_

bool AuthSync::prefsOpen_ = false
private

◆ server_base

String AuthSync::server_base
private

Definition at line 44 of file AuthSync.h.

Referenced by AuthSync(), getCardAuthFromServer(), isAuthorized(), and syncFromServer().

◆ server_last_ok

bool AuthSync::server_last_ok = false
private

Definition at line 54 of file AuthSync.h.

Referenced by getCardAuthFromServer(), setServerProbeResult(), and syncFromServer().

◆ serverPreviouslyUnreachable

bool AuthSync::serverPreviouslyUnreachable = false
private

Definition at line 55 of file AuthSync.h.

◆ SYNC_INTERVAL

unsigned long AuthSync::SYNC_INTERVAL = 60000
private

Definition at line 51 of file AuthSync.h.

Referenced by update().


The documentation for this class was generated from the following files: