Loading src/app.cpp +38 −49 Original line number Diff line number Diff line Loading @@ -192,49 +192,36 @@ void App::store_session(const std::string& token, const std::string& username, sessions_[token] = SessionInfo{username, domain_label, group_ids, std::chrono::steady_clock::now()}; } std::string App::get_session_username(const HttpRequest& req) const { // Caller must hold session_mutex_. const App::SessionInfo* App::find_session(const HttpRequest& req) const { auto it = req.headers.find("authorization"); if (it == req.headers.end()) return {}; if (it == req.headers.end()) return nullptr; const std::string& value = it->second; const std::string prefix = "Bearer "; if (value.size() <= prefix.size() || value.compare(0, prefix.size(), prefix) != 0) return {}; const std::string token = value.substr(prefix.size()); std::lock_guard<std::mutex> lk(session_mutex_); auto sit = sessions_.find(token); if (sit == sessions_.end()) return {}; constexpr std::string_view prefix = "Bearer "; if (value.size() <= prefix.size() || value.compare(0, prefix.size(), prefix.data()) != 0) return nullptr; auto sit = sessions_.find(value.substr(prefix.size())); if (sit == sessions_.end()) return nullptr; if (std::chrono::steady_clock::now() - sit->second.created_at > std::chrono::seconds(SESSION_TTL_SECONDS)) return {}; return sit->second.username; return nullptr; return &sit->second; } std::string App::get_session_username(const HttpRequest& req) const { std::lock_guard<std::mutex> lk(session_mutex_); auto* s = find_session(req); return s ? s->username : std::string{}; } std::string App::get_session_domain(const HttpRequest& req) const { auto it = req.headers.find("authorization"); if (it == req.headers.end()) return {}; const std::string& value = it->second; const std::string prefix = "Bearer "; if (value.size() <= prefix.size() || value.compare(0, prefix.size(), prefix) != 0) return {}; const std::string token = value.substr(prefix.size()); std::lock_guard<std::mutex> lk(session_mutex_); auto sit = sessions_.find(token); if (sit == sessions_.end()) return {}; if (std::chrono::steady_clock::now() - sit->second.created_at > std::chrono::seconds(SESSION_TTL_SECONDS)) return {}; return sit->second.domain_label; auto* s = find_session(req); return s ? s->domain_label : std::string{}; } std::vector<std::string> App::get_session_groups(const HttpRequest& req) const { auto it = req.headers.find("authorization"); if (it == req.headers.end()) return {}; const std::string& value = it->second; const std::string prefix = "Bearer "; if (value.size() <= prefix.size() || value.compare(0, prefix.size(), prefix) != 0) return {}; const std::string token = value.substr(prefix.size()); std::lock_guard<std::mutex> lk(session_mutex_); auto sit = sessions_.find(token); if (sit == sessions_.end()) return {}; if (std::chrono::steady_clock::now() - sit->second.created_at > std::chrono::seconds(SESSION_TTL_SECONDS)) return {}; return sit->second.group_ids; auto* s = find_session(req); return s ? s->group_ids : std::vector<std::string>{}; } std::vector<std::string> App::resolve_session_groups(authdb::client::Client& client) { Loading Loading @@ -279,18 +266,8 @@ void App::repopulate_session(const HttpRequest& req) { } bool App::is_session_known(const HttpRequest& req) const { auto it = req.headers.find("authorization"); if (it == req.headers.end()) return false; const std::string& value = it->second; const std::string prefix = "Bearer "; if (value.size() <= prefix.size() || value.compare(0, prefix.size(), prefix) != 0) return false; const std::string token = value.substr(prefix.size()); std::lock_guard<std::mutex> lk(session_mutex_); auto sit = sessions_.find(token); if (sit == sessions_.end()) return false; if (std::chrono::steady_clock::now() - sit->second.created_at > std::chrono::seconds(SESSION_TTL_SECONDS)) return false; return true; return find_session(req) != nullptr; } bool App::is_any_authorized(const HttpRequest& req) { Loading @@ -303,12 +280,18 @@ bool App::is_any_authorized(const HttpRequest& req) { } bool App::has_store_access(const HttpRequest& req, const std::string& store_id) const { const std::string domain = get_session_domain(req); if (domain.empty()) return true; std::string domain; std::vector<std::string> grps; { std::lock_guard<std::mutex> lk(session_mutex_); auto* s = find_session(req); if (!s || s->domain_label.empty()) return true; domain = s->domain_label; grps = s->group_ids; } auto acls = db_.list_store_acls(store_id); if (!acls.empty()) { auto grps = get_session_groups(req); for (const auto& acl : acls) { if (!acl.can_read) continue; for (const auto& gid : grps) { Loading @@ -326,12 +309,18 @@ bool App::has_store_access(const HttpRequest& req, const std::string& store_id) } bool App::has_store_write_access(const HttpRequest& req, const std::string& store_id) const { const std::string domain = get_session_domain(req); if (domain.empty()) return true; std::string domain; std::vector<std::string> grps; { std::lock_guard<std::mutex> lk(session_mutex_); auto* s = find_session(req); if (!s || s->domain_label.empty()) return true; domain = s->domain_label; grps = s->group_ids; } auto acls = db_.list_store_acls(store_id); if (!acls.empty()) { auto grps = get_session_groups(req); for (const auto& acl : acls) { if (!acl.can_write) continue; for (const auto& gid : grps) { Loading src/app.h +1 −0 Original line number Diff line number Diff line Loading @@ -142,6 +142,7 @@ private: mutable std::mutex session_mutex_; std::unordered_map<std::string, SessionInfo> sessions_; void evict_expired_sessions(); const SessionInfo* find_session(const HttpRequest& req) const; // import state std::mutex import_mutex_; Loading src/backend.cpp +14 −6 Original line number Diff line number Diff line Loading @@ -278,6 +278,7 @@ std::vector<AlbumRecord> BinDb::list_albums(const std::string& store_id) const { std::vector<AlbumRecord> out; auto sit = stores_.find(store_id); if (sit == stores_.end()) return out; out.reserve(sit->second.album_ids.size()); for (const auto& aid : sit->second.album_ids) { auto ait = albums_.find(aid); if (ait != albums_.end()) out.push_back(ait->second); Loading Loading @@ -400,6 +401,7 @@ std::vector<MediaRecord> BinDb::list_media(const std::string& album_id) const { std::vector<MediaRecord> out; auto ait = albums_.find(album_id); if (ait == albums_.end()) return out; out.reserve(ait->second.media_ids.size()); for (const auto& mid : ait->second.media_ids) { auto mit = media_.find(mid); if (mit != media_.end()) out.push_back(mit->second); Loading Loading @@ -512,6 +514,10 @@ bool BinDb::export_db(const std::string& path) const { bin_write_str(out, store.created_at); bin_write_u32(out, static_cast<std::uint32_t>(store.album_ids.size())); std::ifstream sin; constexpr std::size_t BUF = 1024 * 1024; std::vector<char> buf(BUF); for (const auto& aid : store.album_ids) { auto ait = albums_.find(aid); if (ait == albums_.end()) { Loading Loading @@ -545,11 +551,10 @@ bool BinDb::export_db(const std::string& path) const { out.write(reinterpret_cast<const char*>(m.pending_data.data()), static_cast<std::streamsize>(m.pending_data.size())); } else if (m.data_offset > 0 && m.size_bytes > 0) { std::ifstream sin(store_file(sid), std::ios::binary); if (!sin.is_open()) sin.open(store_file(sid), std::ios::binary); if (sin.is_open()) { sin.seekg(static_cast<std::streamoff>(m.data_offset)); constexpr std::size_t BUF = 1024 * 1024; std::vector<char> buf(BUF); std::uint64_t remaining = m.size_bytes; while (remaining > 0 && sin.good()) { auto chunk = static_cast<std::streamsize>( Loading Loading @@ -591,6 +596,10 @@ std::vector<std::uint8_t> BinDb::export_db_to_buffer() const { wr_str(store.created_at); wr_u32(static_cast<std::uint32_t>(store.album_ids.size())); std::ifstream sin; constexpr std::size_t BUF = 1024 * 1024; std::vector<char> buf(BUF); for (const auto& aid : store.album_ids) { auto ait = albums_.find(aid); if (ait == albums_.end()) { Loading Loading @@ -624,11 +633,10 @@ std::vector<std::uint8_t> BinDb::export_db_to_buffer() const { oss.write(reinterpret_cast<const char*>(m.pending_data.data()), static_cast<std::streamsize>(m.pending_data.size())); } else if (m.data_offset > 0 && m.size_bytes > 0) { std::ifstream sin(store_file(sid), std::ios::binary); if (!sin.is_open()) sin.open(store_file(sid), std::ios::binary); if (sin.is_open()) { sin.seekg(static_cast<std::streamoff>(m.data_offset)); constexpr std::size_t BUF = 1024 * 1024; std::vector<char> buf(BUF); std::uint64_t remaining = m.size_bytes; while (remaining > 0 && sin.good()) { auto chunk = static_cast<std::streamsize>( Loading src/server.cpp +13 −11 Original line number Diff line number Diff line Loading @@ -10,7 +10,7 @@ #include <algorithm> #include <filesystem> #include <iostream> #include <sstream> #include <string_view> namespace mediadb { Loading Loading @@ -340,16 +340,18 @@ std::unordered_map<std::string, std::string> MediaHttpEvent::parse_query(const s std::unordered_map<std::string, std::string> result; auto qpos = raw_url.find('?'); if (qpos == std::string::npos) return result; std::string qs = raw_url.substr(qpos + 1); std::istringstream stream(qs); std::string pair; while (std::getline(stream, pair, '&')) { auto eq = pair.find('='); if (eq != std::string::npos) { result[pair.substr(0, eq)] = pair.substr(eq + 1); } else { result[pair] = ""; } std::string_view qs(raw_url); qs.remove_prefix(qpos + 1); while (!qs.empty()) { auto amp = qs.find('&'); auto seg = qs.substr(0, amp); auto eq = seg.find('='); if (eq != std::string_view::npos) result[std::string(seg.substr(0, eq))] = std::string(seg.substr(eq + 1)); else if (!seg.empty()) result[std::string(seg)] = ""; if (amp == std::string_view::npos) break; qs.remove_prefix(amp + 1); } return result; } Loading Loading
src/app.cpp +38 −49 Original line number Diff line number Diff line Loading @@ -192,49 +192,36 @@ void App::store_session(const std::string& token, const std::string& username, sessions_[token] = SessionInfo{username, domain_label, group_ids, std::chrono::steady_clock::now()}; } std::string App::get_session_username(const HttpRequest& req) const { // Caller must hold session_mutex_. const App::SessionInfo* App::find_session(const HttpRequest& req) const { auto it = req.headers.find("authorization"); if (it == req.headers.end()) return {}; if (it == req.headers.end()) return nullptr; const std::string& value = it->second; const std::string prefix = "Bearer "; if (value.size() <= prefix.size() || value.compare(0, prefix.size(), prefix) != 0) return {}; const std::string token = value.substr(prefix.size()); std::lock_guard<std::mutex> lk(session_mutex_); auto sit = sessions_.find(token); if (sit == sessions_.end()) return {}; constexpr std::string_view prefix = "Bearer "; if (value.size() <= prefix.size() || value.compare(0, prefix.size(), prefix.data()) != 0) return nullptr; auto sit = sessions_.find(value.substr(prefix.size())); if (sit == sessions_.end()) return nullptr; if (std::chrono::steady_clock::now() - sit->second.created_at > std::chrono::seconds(SESSION_TTL_SECONDS)) return {}; return sit->second.username; return nullptr; return &sit->second; } std::string App::get_session_username(const HttpRequest& req) const { std::lock_guard<std::mutex> lk(session_mutex_); auto* s = find_session(req); return s ? s->username : std::string{}; } std::string App::get_session_domain(const HttpRequest& req) const { auto it = req.headers.find("authorization"); if (it == req.headers.end()) return {}; const std::string& value = it->second; const std::string prefix = "Bearer "; if (value.size() <= prefix.size() || value.compare(0, prefix.size(), prefix) != 0) return {}; const std::string token = value.substr(prefix.size()); std::lock_guard<std::mutex> lk(session_mutex_); auto sit = sessions_.find(token); if (sit == sessions_.end()) return {}; if (std::chrono::steady_clock::now() - sit->second.created_at > std::chrono::seconds(SESSION_TTL_SECONDS)) return {}; return sit->second.domain_label; auto* s = find_session(req); return s ? s->domain_label : std::string{}; } std::vector<std::string> App::get_session_groups(const HttpRequest& req) const { auto it = req.headers.find("authorization"); if (it == req.headers.end()) return {}; const std::string& value = it->second; const std::string prefix = "Bearer "; if (value.size() <= prefix.size() || value.compare(0, prefix.size(), prefix) != 0) return {}; const std::string token = value.substr(prefix.size()); std::lock_guard<std::mutex> lk(session_mutex_); auto sit = sessions_.find(token); if (sit == sessions_.end()) return {}; if (std::chrono::steady_clock::now() - sit->second.created_at > std::chrono::seconds(SESSION_TTL_SECONDS)) return {}; return sit->second.group_ids; auto* s = find_session(req); return s ? s->group_ids : std::vector<std::string>{}; } std::vector<std::string> App::resolve_session_groups(authdb::client::Client& client) { Loading Loading @@ -279,18 +266,8 @@ void App::repopulate_session(const HttpRequest& req) { } bool App::is_session_known(const HttpRequest& req) const { auto it = req.headers.find("authorization"); if (it == req.headers.end()) return false; const std::string& value = it->second; const std::string prefix = "Bearer "; if (value.size() <= prefix.size() || value.compare(0, prefix.size(), prefix) != 0) return false; const std::string token = value.substr(prefix.size()); std::lock_guard<std::mutex> lk(session_mutex_); auto sit = sessions_.find(token); if (sit == sessions_.end()) return false; if (std::chrono::steady_clock::now() - sit->second.created_at > std::chrono::seconds(SESSION_TTL_SECONDS)) return false; return true; return find_session(req) != nullptr; } bool App::is_any_authorized(const HttpRequest& req) { Loading @@ -303,12 +280,18 @@ bool App::is_any_authorized(const HttpRequest& req) { } bool App::has_store_access(const HttpRequest& req, const std::string& store_id) const { const std::string domain = get_session_domain(req); if (domain.empty()) return true; std::string domain; std::vector<std::string> grps; { std::lock_guard<std::mutex> lk(session_mutex_); auto* s = find_session(req); if (!s || s->domain_label.empty()) return true; domain = s->domain_label; grps = s->group_ids; } auto acls = db_.list_store_acls(store_id); if (!acls.empty()) { auto grps = get_session_groups(req); for (const auto& acl : acls) { if (!acl.can_read) continue; for (const auto& gid : grps) { Loading @@ -326,12 +309,18 @@ bool App::has_store_access(const HttpRequest& req, const std::string& store_id) } bool App::has_store_write_access(const HttpRequest& req, const std::string& store_id) const { const std::string domain = get_session_domain(req); if (domain.empty()) return true; std::string domain; std::vector<std::string> grps; { std::lock_guard<std::mutex> lk(session_mutex_); auto* s = find_session(req); if (!s || s->domain_label.empty()) return true; domain = s->domain_label; grps = s->group_ids; } auto acls = db_.list_store_acls(store_id); if (!acls.empty()) { auto grps = get_session_groups(req); for (const auto& acl : acls) { if (!acl.can_write) continue; for (const auto& gid : grps) { Loading
src/app.h +1 −0 Original line number Diff line number Diff line Loading @@ -142,6 +142,7 @@ private: mutable std::mutex session_mutex_; std::unordered_map<std::string, SessionInfo> sessions_; void evict_expired_sessions(); const SessionInfo* find_session(const HttpRequest& req) const; // import state std::mutex import_mutex_; Loading
src/backend.cpp +14 −6 Original line number Diff line number Diff line Loading @@ -278,6 +278,7 @@ std::vector<AlbumRecord> BinDb::list_albums(const std::string& store_id) const { std::vector<AlbumRecord> out; auto sit = stores_.find(store_id); if (sit == stores_.end()) return out; out.reserve(sit->second.album_ids.size()); for (const auto& aid : sit->second.album_ids) { auto ait = albums_.find(aid); if (ait != albums_.end()) out.push_back(ait->second); Loading Loading @@ -400,6 +401,7 @@ std::vector<MediaRecord> BinDb::list_media(const std::string& album_id) const { std::vector<MediaRecord> out; auto ait = albums_.find(album_id); if (ait == albums_.end()) return out; out.reserve(ait->second.media_ids.size()); for (const auto& mid : ait->second.media_ids) { auto mit = media_.find(mid); if (mit != media_.end()) out.push_back(mit->second); Loading Loading @@ -512,6 +514,10 @@ bool BinDb::export_db(const std::string& path) const { bin_write_str(out, store.created_at); bin_write_u32(out, static_cast<std::uint32_t>(store.album_ids.size())); std::ifstream sin; constexpr std::size_t BUF = 1024 * 1024; std::vector<char> buf(BUF); for (const auto& aid : store.album_ids) { auto ait = albums_.find(aid); if (ait == albums_.end()) { Loading Loading @@ -545,11 +551,10 @@ bool BinDb::export_db(const std::string& path) const { out.write(reinterpret_cast<const char*>(m.pending_data.data()), static_cast<std::streamsize>(m.pending_data.size())); } else if (m.data_offset > 0 && m.size_bytes > 0) { std::ifstream sin(store_file(sid), std::ios::binary); if (!sin.is_open()) sin.open(store_file(sid), std::ios::binary); if (sin.is_open()) { sin.seekg(static_cast<std::streamoff>(m.data_offset)); constexpr std::size_t BUF = 1024 * 1024; std::vector<char> buf(BUF); std::uint64_t remaining = m.size_bytes; while (remaining > 0 && sin.good()) { auto chunk = static_cast<std::streamsize>( Loading Loading @@ -591,6 +596,10 @@ std::vector<std::uint8_t> BinDb::export_db_to_buffer() const { wr_str(store.created_at); wr_u32(static_cast<std::uint32_t>(store.album_ids.size())); std::ifstream sin; constexpr std::size_t BUF = 1024 * 1024; std::vector<char> buf(BUF); for (const auto& aid : store.album_ids) { auto ait = albums_.find(aid); if (ait == albums_.end()) { Loading Loading @@ -624,11 +633,10 @@ std::vector<std::uint8_t> BinDb::export_db_to_buffer() const { oss.write(reinterpret_cast<const char*>(m.pending_data.data()), static_cast<std::streamsize>(m.pending_data.size())); } else if (m.data_offset > 0 && m.size_bytes > 0) { std::ifstream sin(store_file(sid), std::ios::binary); if (!sin.is_open()) sin.open(store_file(sid), std::ios::binary); if (sin.is_open()) { sin.seekg(static_cast<std::streamoff>(m.data_offset)); constexpr std::size_t BUF = 1024 * 1024; std::vector<char> buf(BUF); std::uint64_t remaining = m.size_bytes; while (remaining > 0 && sin.good()) { auto chunk = static_cast<std::streamsize>( Loading
src/server.cpp +13 −11 Original line number Diff line number Diff line Loading @@ -10,7 +10,7 @@ #include <algorithm> #include <filesystem> #include <iostream> #include <sstream> #include <string_view> namespace mediadb { Loading Loading @@ -340,16 +340,18 @@ std::unordered_map<std::string, std::string> MediaHttpEvent::parse_query(const s std::unordered_map<std::string, std::string> result; auto qpos = raw_url.find('?'); if (qpos == std::string::npos) return result; std::string qs = raw_url.substr(qpos + 1); std::istringstream stream(qs); std::string pair; while (std::getline(stream, pair, '&')) { auto eq = pair.find('='); if (eq != std::string::npos) { result[pair.substr(0, eq)] = pair.substr(eq + 1); } else { result[pair] = ""; } std::string_view qs(raw_url); qs.remove_prefix(qpos + 1); while (!qs.empty()) { auto amp = qs.find('&'); auto seg = qs.substr(0, amp); auto eq = seg.find('='); if (eq != std::string_view::npos) result[std::string(seg.substr(0, eq))] = std::string(seg.substr(eq + 1)); else if (!seg.empty()) result[std::string(seg)] = ""; if (amp == std::string_view::npos) break; qs.remove_prefix(amp + 1); } return result; } Loading