Add Moonraker trusted-client auth support (#15550)

Orca sent `X-Api-Key: 88888888` to Moonraker whenever the printer preset's API Key was empty, which Moonraker rejects with HTTP 401 since Arksine/moonraker@93f5d9c. The preset's API Key is now the only thing that sets the header, and an empty key sends none.

Logging out of a printer now marks it revoked rather than only clearing its code, since an empty code is a valid Moonraker binding. Discovery events are delivered through the network agent and dropped if their printer agent was replaced while queued.
This commit is contained in:
TheLegendTubaGuy
2026-10-13 15:13:07 -05:00
committed by GitHub
parent 5a427f2eb6
commit de3af0b7de
19 changed files with 517 additions and 64 deletions
+28 -20
View File
@@ -4,6 +4,7 @@
#include "HMS.hpp"
#include "I18N.hpp"
#include "libslic3r/Thread.hpp"
#include "libslic3r/PresetBundle.hpp"
#include "slic3r/Utils/Http.hpp"
#include "slic3r/Utils/NetworkAgent.hpp"
#include "slic3r/Utils/NetworkAgentFactory.hpp"
@@ -66,6 +67,7 @@
#include <vector>
#include <wx/colour.h>
#include <tuple>
#include <wx/app.h>
#include <wx/dir.h>
#include <wx/string.h>
#include <wx/event.h>
@@ -527,6 +529,18 @@ bool MachineObject::HasRecentLanMessage()
return diff.count() < 5000;
}
bool MachineObject::has_access_right() const
{
return !m_access_revoked && (printer_agent_id == MOONRAKER_PRINTER_AGENT_ID || !get_access_code().empty());
}
void MachineObject::revoke_access()
{
// UI panels retain this object; revoke its binding without invalidating their pointers.
access_code.clear();
m_access_revoked = true;
}
std::string MachineObject::get_access_code() const
{
return access_code;
@@ -535,6 +549,7 @@ std::string MachineObject::get_access_code() const
void MachineObject::set_access_code(std::string code, bool only_refresh)
{
this->access_code = code;
m_access_revoked = false;
if (only_refresh) {
AppConfig* config = m_manager ? m_manager->get_app_config() : GUI::wxGetApp().app_config;
if (config) {
@@ -548,25 +563,10 @@ void MachineObject::set_access_code(std::string code, bool only_refresh)
// session boundary, since parse_user_print_info() always overwrites their code
// fresh from the cloud API's current response, so there's no cross-agent leakage
// risk to guard against there.
if (!code.empty()) {
if (has_access_right()) {
DeviceManager::update_local_machine(*this, config);
} else {
// Only patch an existing record's code - don't persist a brand-new
// never-bound entry just because set_access_code("") was called on it.
const auto& machines = config->get_local_machines();
auto it = machines.find(get_dev_id());
if (it != machines.end()) {
BBLocalMachine local_machine = it->second;
local_machine.access_code = "";
config->update_local_machine(local_machine);
}
// Also clear the pre-scoping flat legacy key when unbinding under BBL, so an
// old BBL-era code can't silently "re-bind" this device again via
// get_access_code_with_legacy_fallback()'s legacy fallback.
if (printer_agent_id == BBL_PRINTER_AGENT_ID || printer_agent_id.empty()) {
config->erase("access_code", get_dev_id());
config->erase("user_access_code", get_dev_id());
}
config->clear_local_machine_access_code(get_dev_id(), printer_agent_id);
}
} else {
if (!code.empty())
@@ -2678,7 +2678,7 @@ void MachineObject::update_print_progress(const json& value)
int MachineObject::connect()
{
if (get_dev_ip().empty()) return -1;
if (m_access_revoked || get_dev_ip().empty()) return -1;
std::string username = m_agent ? m_agent->default_lan_username() : std::string();
std::string password = get_access_code();
@@ -2691,17 +2691,25 @@ int MachineObject::connect()
std::string host = Http::get_host_from_url(input, &port);
std::string ca_file;
if (GUI::wxGetApp().preset_bundle) {
if (wxTheApp && GUI::wxGetApp().preset_bundle) {
const auto& config = GUI::wxGetApp().preset_bundle->printers.get_edited_preset().config;
if (port.empty())
port = config.opt_string("printhost_port");
ca_file = config.opt_string("printhost_cafile");
// Refresh credentials from the matching preset for connections that bypass GUI_App::select_machine().
const std::string print_host = config.opt_string("print_host");
if (printer_agent_id == MOONRAKER_PRINTER_AGENT_ID && config.opt_string("printer_agent") == MOONRAKER_PRINTER_AGENT_ID &&
!print_host.empty() && dev_id_from_address(print_host, config.opt_string("printhost_port")) == get_dev_id()) {
password = config.opt_string("printhost_apikey");
if (password != get_access_code())
set_access_code(password);
}
}
if (host.empty())
host = get_dev_ip();
if (m_agent) {
try {
PrinterConnectionParams params{