mirror of
https://github.com/OrcaSlicer/OrcaSlicer.git
synced 2026-10-04 14:20:58 +00:00
Compare commits
2
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ae3e41eb02 | ||
|
|
92ab583ecc |
@@ -1340,7 +1340,7 @@ void PlateData::parse_filament_info(GCodeProcessorResult *result)
|
|||||||
|
|
||||||
bool _handle_start_relationship(const char** attributes, unsigned int num_attributes);
|
bool _handle_start_relationship(const char** attributes, unsigned int num_attributes);
|
||||||
|
|
||||||
void _generate_current_object_list(std::vector<Component> &sub_objects, Id object_id, IdToCurrentObjectMap& current_objects);
|
bool _generate_current_object_list(std::vector<Component> &sub_objects, Id object_id, IdToCurrentObjectMap& current_objects);
|
||||||
bool _generate_volumes_new(ModelObject& object, const std::vector<Component> &sub_objects, const ObjectMetadata::VolumeMetadataList& volumes, ConfigSubstitutionContext& config_substitutions);
|
bool _generate_volumes_new(ModelObject& object, const std::vector<Component> &sub_objects, const ObjectMetadata::VolumeMetadataList& volumes, ConfigSubstitutionContext& config_substitutions);
|
||||||
//bool _generate_volumes(ModelObject& object, const Geometry& geometry, const ObjectMetadata::VolumeMetadataList& volumes, ConfigSubstitutionContext& config_substitutions);
|
//bool _generate_volumes(ModelObject& object, const Geometry& geometry, const ObjectMetadata::VolumeMetadataList& volumes, ConfigSubstitutionContext& config_substitutions);
|
||||||
|
|
||||||
@@ -2055,7 +2055,8 @@ void PlateData::parse_filament_info(GCodeProcessorResult *result)
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
std::vector<Component> object_id_list;
|
std::vector<Component> object_id_list;
|
||||||
_generate_current_object_list(object_id_list, object.first, m_current_objects);
|
if (!_generate_current_object_list(object_id_list, object.first, m_current_objects))
|
||||||
|
return false;
|
||||||
|
|
||||||
ObjectMetadata::VolumeMetadataList volumes;
|
ObjectMetadata::VolumeMetadataList volumes;
|
||||||
ObjectMetadata::VolumeMetadataList* volumes_ptr = nullptr;
|
ObjectMetadata::VolumeMetadataList* volumes_ptr = nullptr;
|
||||||
@@ -2154,7 +2155,8 @@ void PlateData::parse_filament_info(GCodeProcessorResult *result)
|
|||||||
}*/
|
}*/
|
||||||
|
|
||||||
std::vector<Component> object_id_list;
|
std::vector<Component> object_id_list;
|
||||||
_generate_current_object_list(object_id_list, object.first, m_current_objects);
|
if (!_generate_current_object_list(object_id_list, object.first, m_current_objects))
|
||||||
|
return false;
|
||||||
|
|
||||||
ObjectMetadata::VolumeMetadataList volumes;
|
ObjectMetadata::VolumeMetadataList volumes;
|
||||||
ObjectMetadata::VolumeMetadataList* volumes_ptr = nullptr;
|
ObjectMetadata::VolumeMetadataList* volumes_ptr = nullptr;
|
||||||
@@ -5002,31 +5004,45 @@ void PlateData::parse_filament_info(GCodeProcessorResult *result)
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void _BBS_3MF_Importer::_generate_current_object_list(std::vector<Component> &sub_objects, Id object_id, IdToCurrentObjectMap ¤t_objects)
|
bool _BBS_3MF_Importer::_generate_current_object_list(std::vector<Component> &sub_objects, Id object_id, IdToCurrentObjectMap ¤t_objects)
|
||||||
{
|
{
|
||||||
std::list<std::pair<Component, Transform3d>> id_list;
|
// A chain of component references longer than the number of objects has to visit an object
|
||||||
id_list.push_back(std::make_pair(Component(object_id, Transform3d::Identity()), Transform3d::Identity()));
|
// twice, so the component graph contains a cycle and the expansion below would not stop.
|
||||||
|
const size_t max_depth = current_objects.size();
|
||||||
|
// An acyclic graph may still expand exponentially, so bound the number of expanded components
|
||||||
|
// as well. Way above the number of parts of any real object.
|
||||||
|
static constexpr size_t max_components = 100000;
|
||||||
|
|
||||||
|
std::list<std::tuple<Component, Transform3d, size_t>> id_list;
|
||||||
|
id_list.push_back(std::make_tuple(Component(object_id, Transform3d::Identity()), Transform3d::Identity(), 0));
|
||||||
|
|
||||||
|
size_t num_components = 0;
|
||||||
while (!id_list.empty())
|
while (!id_list.empty())
|
||||||
{
|
{
|
||||||
auto current_item = id_list.front();
|
auto current_item = id_list.front();
|
||||||
Component current_id = current_item.first;
|
Component current_id = std::get<0>(current_item);
|
||||||
id_list.pop_front();
|
id_list.pop_front();
|
||||||
|
if (std::get<2>(current_item) > max_depth || ++ num_components > max_components) {
|
||||||
|
add_error("invalid 3mf: cyclic or too deeply nested components");
|
||||||
|
sub_objects.clear();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
IdToCurrentObjectMap::iterator current_object = current_objects.find(current_id.object_id);
|
IdToCurrentObjectMap::iterator current_object = current_objects.find(current_id.object_id);
|
||||||
if (current_object != current_objects.end()) {
|
if (current_object != current_objects.end()) {
|
||||||
//found one
|
//found one
|
||||||
if (!current_object->second.components.empty()) {
|
if (!current_object->second.components.empty()) {
|
||||||
for (const Component &comp : current_object->second.components) {
|
for (const Component &comp : current_object->second.components) {
|
||||||
id_list.push_back(std::pair(comp, current_item.second * comp.transform));
|
id_list.push_back(std::make_tuple(comp, std::get<1>(current_item) * comp.transform, std::get<2>(current_item) + 1));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (!(current_object->second.geometry.empty())) {
|
else if (!(current_object->second.geometry.empty())) {
|
||||||
//CurrentObject* ptr = &(current_objects[current_id]);
|
//CurrentObject* ptr = &(current_objects[current_id]);
|
||||||
//CurrentObject* ptr2 = &(current_object->second);
|
//CurrentObject* ptr2 = &(current_object->second);
|
||||||
sub_objects.push_back({ current_object->first, current_item.second});
|
sub_objects.push_back({ current_object->first, std::get<1>(current_item)});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool _BBS_3MF_Importer::_generate_volumes_new(ModelObject& object, const std::vector<Component> &sub_objects, const ObjectMetadata::VolumeMetadataList& volumes, ConfigSubstitutionContext& config_substitutions)
|
bool _BBS_3MF_Importer::_generate_volumes_new(ModelObject& object, const std::vector<Component> &sub_objects, const ObjectMetadata::VolumeMetadataList& volumes, ConfigSubstitutionContext& config_substitutions)
|
||||||
|
|||||||
@@ -819,9 +819,7 @@ int OrcaCloudServiceAgent::user_logout(bool request)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// An explicit logout also wipes the backend the token storage option is not using, so a token
|
clear_session();
|
||||||
// stranded by switching that option cannot sign the account back in later.
|
|
||||||
clear_session(/*all_backends=*/request);
|
|
||||||
return BAMBU_NETWORK_SUCCESS;
|
return BAMBU_NETWORK_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1606,9 +1604,7 @@ void OrcaCloudServiceAgent::persist_user_secret(const std::string& secret)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (stored) {
|
(void) stored;
|
||||||
secret_stored = true;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool OrcaCloudServiceAgent::load_user_secret(std::string& out_secret)
|
bool OrcaCloudServiceAgent::load_user_secret(std::string& out_secret)
|
||||||
@@ -1648,7 +1644,6 @@ bool OrcaCloudServiceAgent::load_user_secret(std::string& out_secret)
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (integrity_ok && aes256gcm_decrypt(encoded_payload, key, plain) && !plain.empty()) {
|
if (integrity_ok && aes256gcm_decrypt(encoded_payload, key, plain) && !plain.empty()) {
|
||||||
secret_stored = true;
|
|
||||||
out_secret = plain;
|
out_secret = plain;
|
||||||
// Upgrade legacy payloads to signed format
|
// Upgrade legacy payloads to signed format
|
||||||
if (payload.rfind("v2:", 0) != 0) {
|
if (payload.rfind("v2:", 0) != 0) {
|
||||||
@@ -1666,7 +1661,6 @@ bool OrcaCloudServiceAgent::load_user_secret(std::string& out_secret)
|
|||||||
if (store.Load(SECRET_STORE_SERVICE, username, secret) && secret.IsOk()) {
|
if (store.Load(SECRET_STORE_SERVICE, username, secret) && secret.IsOk()) {
|
||||||
out_secret.assign(static_cast<const char*>(secret.GetData()), secret.GetSize());
|
out_secret.assign(static_cast<const char*>(secret.GetData()), secret.GetSize());
|
||||||
if (!out_secret.empty()) {
|
if (!out_secret.empty()) {
|
||||||
secret_stored = true;
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1676,20 +1670,11 @@ bool OrcaCloudServiceAgent::load_user_secret(std::string& out_secret)
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void OrcaCloudServiceAgent::clear_user_secret(bool all_backends)
|
void OrcaCloudServiceAgent::clear_user_secret()
|
||||||
{
|
{
|
||||||
// Nothing this process loaded or saved: leave the store alone. Deleting would only cost a
|
wxSecretStore store = wxSecretStore::GetDefault();
|
||||||
// keychain round trip (or a hang while the keychain is unresponsive) and could remove a
|
if (store.IsOk()) {
|
||||||
// login another instance just saved.
|
store.Delete(SECRET_STORE_SERVICE);
|
||||||
if (!secret_stored.exchange(false) && !all_backends) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (all_backends || !m_use_encrypted_token_file) {
|
|
||||||
wxSecretStore store = wxSecretStore::GetDefault();
|
|
||||||
if (store.IsOk()) {
|
|
||||||
store.Delete(SECRET_STORE_SERVICE);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
compute_fallback_path();
|
compute_fallback_path();
|
||||||
@@ -2038,13 +2023,13 @@ bool OrcaCloudServiceAgent::set_user_session(const json& session_json, bool noti
|
|||||||
return success;
|
return success;
|
||||||
}
|
}
|
||||||
|
|
||||||
void OrcaCloudServiceAgent::clear_session(bool all_backends)
|
void OrcaCloudServiceAgent::clear_session()
|
||||||
{
|
{
|
||||||
{
|
{
|
||||||
std::lock_guard<std::mutex> lock(session_mutex);
|
std::lock_guard<std::mutex> lock(session_mutex);
|
||||||
session = SessionInfo{};
|
session = SessionInfo{};
|
||||||
}
|
}
|
||||||
clear_user_secret(all_backends);
|
clear_user_secret();
|
||||||
}
|
}
|
||||||
|
|
||||||
// ============================================================================
|
// ============================================================================
|
||||||
|
|||||||
@@ -326,7 +326,7 @@ public:
|
|||||||
|
|
||||||
void persist_user_secret(const std::string& secret);
|
void persist_user_secret(const std::string& secret);
|
||||||
bool load_user_secret(std::string& out_secret);
|
bool load_user_secret(std::string& out_secret);
|
||||||
void clear_user_secret(bool all_backends = false);
|
void clear_user_secret();
|
||||||
|
|
||||||
// Token refresh helpers
|
// Token refresh helpers
|
||||||
bool refresh_if_expiring(std::chrono::seconds skew, const std::string& reason);
|
bool refresh_if_expiring(std::chrono::seconds skew, const std::string& reason);
|
||||||
@@ -344,7 +344,7 @@ public:
|
|||||||
bool persist = true);
|
bool persist = true);
|
||||||
// Accepts either nested Orca cloud / GoTrue session JSON or flat WebView token JSON.
|
// Accepts either nested Orca cloud / GoTrue session JSON or flat WebView token JSON.
|
||||||
bool set_user_session(const nlohmann::json& session_json, bool notify_login = true);
|
bool set_user_session(const nlohmann::json& session_json, bool notify_login = true);
|
||||||
void clear_session(bool all_backends = false);
|
void clear_session();
|
||||||
|
|
||||||
static std::string generate_uuid_for_setting_id(const std::string& name, const std::string& user_id = "");
|
static std::string generate_uuid_for_setting_id(const std::string& name, const std::string& user_id = "");
|
||||||
|
|
||||||
@@ -413,11 +413,6 @@ private:
|
|||||||
// Member variables - auth state
|
// Member variables - auth state
|
||||||
PkceBundle pkce_bundle;
|
PkceBundle pkce_bundle;
|
||||||
std::string secret_fallback_path;
|
std::string secret_fallback_path;
|
||||||
// Set once this process has read a secret from the store or written one. Unless the user logs
|
|
||||||
// out explicitly, clear_user_secret() only touches the store while it is set, so a logged-out
|
|
||||||
// instance (the GUI polls the login status every 2 s) makes no keychain calls and cannot wipe
|
|
||||||
// a login another instance saved.
|
|
||||||
std::atomic_bool secret_stored{false};
|
|
||||||
SessionHandler session_handler;
|
SessionHandler session_handler;
|
||||||
OnLoginCompleteHandler on_login_complete_handler;
|
OnLoginCompleteHandler on_login_complete_handler;
|
||||||
SessionInfo session;
|
SessionInfo session;
|
||||||
|
|||||||
@@ -27,6 +27,7 @@
|
|||||||
#include <Eigen/Geometry>
|
#include <Eigen/Geometry>
|
||||||
#include <type_traits> // for std::enable_if_t
|
#include <type_traits> // for std::enable_if_t
|
||||||
#include <typeinfo> // for typeid
|
#include <typeinfo> // for typeid
|
||||||
|
#include <regex>
|
||||||
|
|
||||||
namespace Catch {
|
namespace Catch {
|
||||||
template <typename T>
|
template <typename T>
|
||||||
@@ -321,6 +322,30 @@ TEST_CASE("A project with a plate id below 1 fails to load", "[3mf][Regression]"
|
|||||||
REQUIRE_FALSE(loaded);
|
REQUIRE_FALSE(loaded);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_CASE("A project whose components reference themselves fails to load", "[3mf][Regression]")
|
||||||
|
{
|
||||||
|
ScopedTemporaryFile temp(".3mf");
|
||||||
|
store_painted_cube(temp.string());
|
||||||
|
|
||||||
|
// Point the component back at the object that holds it. Expanding that reference used to push
|
||||||
|
// into the work list forever, growing it until the process ran out of memory.
|
||||||
|
REQUIRE(rewrite_3mf_entries(temp.string(), [](std::string& name, std::string& data) {
|
||||||
|
if (!boost::algorithm::ends_with(name, "3dmodel.model"))
|
||||||
|
return false;
|
||||||
|
std::smatch match;
|
||||||
|
if (!std::regex_search(data, match, std::regex("<object id=\"([0-9]+)\"[^>]*>\\s*<components")))
|
||||||
|
return false;
|
||||||
|
data = std::regex_replace(data, std::regex("objectid=\"[0-9]+\""), "objectid=\"" + match[1].str() + "\"");
|
||||||
|
return true;
|
||||||
|
}));
|
||||||
|
|
||||||
|
ScopedTemporaryDir backup_dir("orca_cycle_dst");
|
||||||
|
Model model;
|
||||||
|
bool loaded = true;
|
||||||
|
REQUIRE_NOTHROW(loaded = load_project(temp.string(), model, backup_dir));
|
||||||
|
REQUIRE_FALSE(loaded);
|
||||||
|
}
|
||||||
|
|
||||||
TEST_CASE("A project with malformed paint data loads without the damaged facet", "[3mf][Regression]")
|
TEST_CASE("A project with malformed paint data loads without the damaged facet", "[3mf][Regression]")
|
||||||
{
|
{
|
||||||
ScopedTemporaryFile temp(".3mf");
|
ScopedTemporaryFile temp(".3mf");
|
||||||
|
|||||||
@@ -13,7 +13,6 @@ add_executable(${_TEST_NAME}_tests
|
|||||||
test_prebuild_queue.cpp
|
test_prebuild_queue.cpp
|
||||||
test_staged_build.cpp
|
test_staged_build.cpp
|
||||||
test_network_versions.cpp
|
test_network_versions.cpp
|
||||||
test_orca_cloud_agent.cpp
|
|
||||||
test_action_source.cpp
|
test_action_source.cpp
|
||||||
test_plugin_host_api.cpp
|
test_plugin_host_api.cpp
|
||||||
# Exercise seam enums and predicates through the embedded Python host API.
|
# Exercise seam enums and predicates through the embedded Python host API.
|
||||||
|
|||||||
@@ -6,7 +6,6 @@
|
|||||||
#include <boost/filesystem.hpp>
|
#include <boost/filesystem.hpp>
|
||||||
#include <memory.h>
|
#include <memory.h>
|
||||||
#include <stdexcept>
|
#include <stdexcept>
|
||||||
#include <string>
|
|
||||||
#include <pybind11/embed.h>
|
#include <pybind11/embed.h>
|
||||||
#include <pybind11/pybind11.h>
|
#include <pybind11/pybind11.h>
|
||||||
|
|
||||||
@@ -26,15 +25,8 @@ void ensure_python_initialized()
|
|||||||
config.parse_argv = 0;
|
config.parse_argv = 0;
|
||||||
|
|
||||||
const auto python_home = boost::dll::program_location().parent_path() / "python";
|
const auto python_home = boost::dll::program_location().parent_path() / "python";
|
||||||
#ifdef _WIN32
|
|
||||||
const auto stdlib = python_home / "Lib";
|
|
||||||
#else
|
|
||||||
const auto stdlib = python_home / "lib" /
|
|
||||||
("python" + std::to_string(PY_MAJOR_VERSION) + "." + std::to_string(PY_MINOR_VERSION));
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// Only a real runtime: a stray python/ folder (packages a test left behind) is not a home.
|
if (boost::filesystem::exists(python_home)) {
|
||||||
if (boost::filesystem::exists(stdlib / "encodings")) {
|
|
||||||
const std::string home = python_home.string();
|
const std::string home = python_home.string();
|
||||||
const PyStatus status = PyConfig_SetBytesString(&config, &config.home, home.c_str());
|
const PyStatus status = PyConfig_SetBytesString(&config, &config.home, home.c_str());
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,39 @@
|
|||||||
#include <catch2/catch_all.hpp>
|
#include <catch2/catch_all.hpp>
|
||||||
|
|
||||||
#include "slic3r/Utils/Http.hpp"
|
#include "slic3r/Utils/Http.hpp"
|
||||||
|
#include "slic3r/Utils/OrcaCloudServiceAgent.hpp"
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
nlohmann::json flat_session_json(const nlohmann::json& fields)
|
||||||
|
{
|
||||||
|
nlohmann::json session = {
|
||||||
|
{"access_token", "test-token"},
|
||||||
|
{"user_id", "test-user-id"}
|
||||||
|
};
|
||||||
|
session.update(fields);
|
||||||
|
return session;
|
||||||
|
}
|
||||||
|
|
||||||
|
nlohmann::json nested_session_json(const nlohmann::json& metadata)
|
||||||
|
{
|
||||||
|
return {
|
||||||
|
{"access_token", "test-token"},
|
||||||
|
{"user", {
|
||||||
|
{"id", "test-user-id"},
|
||||||
|
{"user_metadata", metadata}
|
||||||
|
}}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string resolved_display_name(const nlohmann::json& session)
|
||||||
|
{
|
||||||
|
Slic3r::OrcaCloudServiceAgent agent("");
|
||||||
|
REQUIRE(agent.set_user_session(session, false));
|
||||||
|
return agent.get_user_nickname();
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
TEST_CASE("Check SSL certificates paths", "[Http][NotWorking]") {
|
TEST_CASE("Check SSL certificates paths", "[Http][NotWorking]") {
|
||||||
|
|
||||||
@@ -20,6 +53,62 @@ TEST_CASE("Check SSL certificates paths", "[Http][NotWorking]") {
|
|||||||
REQUIRE(status == 200);
|
REQUIRE(status == 200);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_CASE("Orca cloud flat session resolves display name consistently", "[OrcaCloudServiceAgent]")
|
||||||
|
{
|
||||||
|
CHECK(resolved_display_name(flat_session_json({
|
||||||
|
{"username", "orca_username"},
|
||||||
|
{"display_name", "Display Name"},
|
||||||
|
{"nickname", "Nickname"}
|
||||||
|
})) == "Display Name");
|
||||||
|
|
||||||
|
CHECK(resolved_display_name(flat_session_json({
|
||||||
|
{"username", "orca_username"},
|
||||||
|
{"nickname", "Nickname"}
|
||||||
|
})) == "Nickname");
|
||||||
|
|
||||||
|
CHECK(resolved_display_name(flat_session_json({
|
||||||
|
{"username", "orca_username"},
|
||||||
|
{"full_name", "Full Name"}
|
||||||
|
})) == "Full Name");
|
||||||
|
|
||||||
|
CHECK(resolved_display_name(flat_session_json({
|
||||||
|
{"username", "orca_username"},
|
||||||
|
{"name", "Provider Name"}
|
||||||
|
})) == "Provider Name");
|
||||||
|
|
||||||
|
CHECK(resolved_display_name(flat_session_json({
|
||||||
|
{"username", "orca_username"}
|
||||||
|
})) == "orca_username");
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("Orca cloud nested session resolves display name consistently", "[OrcaCloudServiceAgent]")
|
||||||
|
{
|
||||||
|
CHECK(resolved_display_name(nested_session_json({
|
||||||
|
{"username", "orca_username"},
|
||||||
|
{"display_name", "Display Name"},
|
||||||
|
{"nickname", "Nickname"}
|
||||||
|
})) == "Display Name");
|
||||||
|
|
||||||
|
CHECK(resolved_display_name(nested_session_json({
|
||||||
|
{"username", "orca_username"},
|
||||||
|
{"nickname", "Nickname"}
|
||||||
|
})) == "Nickname");
|
||||||
|
|
||||||
|
CHECK(resolved_display_name(nested_session_json({
|
||||||
|
{"username", "orca_username"},
|
||||||
|
{"full_name", "Full Name"}
|
||||||
|
})) == "Full Name");
|
||||||
|
|
||||||
|
CHECK(resolved_display_name(nested_session_json({
|
||||||
|
{"username", "orca_username"},
|
||||||
|
{"name", "Provider Name"}
|
||||||
|
})) == "Provider Name");
|
||||||
|
|
||||||
|
CHECK(resolved_display_name(nested_session_json({
|
||||||
|
{"username", "orca_username"}
|
||||||
|
})) == "orca_username");
|
||||||
|
}
|
||||||
|
|
||||||
TEST_CASE("Http digest authentication", "[Http][NotWorking]") {
|
TEST_CASE("Http digest authentication", "[Http][NotWorking]") {
|
||||||
Slic3r::Http g = Slic3r::Http::get("https://httpbingo.org/digest-auth/auth/guest/guest");
|
Slic3r::Http g = Slic3r::Http::get("https://httpbingo.org/digest-auth/auth/guest/guest");
|
||||||
|
|
||||||
|
|||||||
@@ -1,173 +0,0 @@
|
|||||||
#include <catch2/catch_all.hpp>
|
|
||||||
|
|
||||||
#include <boost/filesystem.hpp>
|
|
||||||
#include <boost/filesystem/fstream.hpp>
|
|
||||||
|
|
||||||
#include <memory>
|
|
||||||
#include <string>
|
|
||||||
|
|
||||||
#include "slic3r/Utils/OrcaCloudServiceAgent.hpp"
|
|
||||||
#include "test_utils.hpp"
|
|
||||||
|
|
||||||
using namespace Slic3r;
|
|
||||||
namespace fs = boost::filesystem;
|
|
||||||
|
|
||||||
namespace {
|
|
||||||
|
|
||||||
// The encrypted token file is the one secret backend a test can observe without a system
|
|
||||||
// keychain. Every agent pointed at the same directory shares it, like separate app instances
|
|
||||||
// share the keychain entry.
|
|
||||||
std::unique_ptr<OrcaCloudServiceAgent> make_file_backed_agent(const fs::path& dir)
|
|
||||||
{
|
|
||||||
auto agent = std::make_unique<OrcaCloudServiceAgent>(dir.string());
|
|
||||||
agent->set_use_encrypted_token_file(true);
|
|
||||||
agent->set_config_dir(dir.string());
|
|
||||||
return agent;
|
|
||||||
}
|
|
||||||
|
|
||||||
fs::path secret_file(const fs::path& dir) { return dir / secret_constants::USER_SECRET_FILENAME; }
|
|
||||||
|
|
||||||
nlohmann::json flat_session_json(const nlohmann::json& fields)
|
|
||||||
{
|
|
||||||
nlohmann::json session = {
|
|
||||||
{"access_token", "test-token"},
|
|
||||||
{"user_id", "test-user-id"}
|
|
||||||
};
|
|
||||||
session.update(fields);
|
|
||||||
return session;
|
|
||||||
}
|
|
||||||
|
|
||||||
nlohmann::json nested_session_json(const nlohmann::json& metadata)
|
|
||||||
{
|
|
||||||
return {
|
|
||||||
{"access_token", "test-token"},
|
|
||||||
{"user", {
|
|
||||||
{"id", "test-user-id"},
|
|
||||||
{"user_metadata", metadata}
|
|
||||||
}}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
// set_user_session() persists the session, so it goes to a throwaway token file rather than the
|
|
||||||
// system keychain of whoever runs the tests.
|
|
||||||
std::string resolved_display_name(const nlohmann::json& session)
|
|
||||||
{
|
|
||||||
ScopedTemporaryDir dir("orca-secret");
|
|
||||||
auto agent = make_file_backed_agent(dir.path());
|
|
||||||
REQUIRE(agent->set_user_session(session, false));
|
|
||||||
return agent->get_user_nickname();
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace
|
|
||||||
|
|
||||||
TEST_CASE("Logging out removes the secret this instance saved", "[OrcaCloudServiceAgent]")
|
|
||||||
{
|
|
||||||
ScopedTemporaryDir dir("orca-secret");
|
|
||||||
auto agent = make_file_backed_agent(dir.path());
|
|
||||||
|
|
||||||
agent->persist_user_secret("refresh-token");
|
|
||||||
REQUIRE(fs::exists(secret_file(dir.path())));
|
|
||||||
|
|
||||||
agent->user_logout(false);
|
|
||||||
CHECK_FALSE(fs::exists(secret_file(dir.path())));
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST_CASE("Logging out removes a secret this instance loaded from the store", "[OrcaCloudServiceAgent]")
|
|
||||||
{
|
|
||||||
ScopedTemporaryDir dir("orca-secret");
|
|
||||||
make_file_backed_agent(dir.path())->persist_user_secret("refresh-token");
|
|
||||||
|
|
||||||
auto agent = make_file_backed_agent(dir.path());
|
|
||||||
std::string secret;
|
|
||||||
REQUIRE(agent->load_user_secret(secret));
|
|
||||||
CHECK(secret == "refresh-token");
|
|
||||||
|
|
||||||
agent->user_logout(false);
|
|
||||||
CHECK_FALSE(fs::exists(secret_file(dir.path())));
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST_CASE("Logging out leaves a secret this instance never loaded or saved alone", "[OrcaCloudServiceAgent]")
|
|
||||||
{
|
|
||||||
ScopedTemporaryDir dir("orca-secret");
|
|
||||||
make_file_backed_agent(dir.path())->persist_user_secret("refresh-token");
|
|
||||||
|
|
||||||
// A logged-out instance is asked to log out on every login-status poll.
|
|
||||||
auto other = make_file_backed_agent(dir.path());
|
|
||||||
other->user_logout(false);
|
|
||||||
other->user_logout(false);
|
|
||||||
CHECK(fs::exists(secret_file(dir.path())));
|
|
||||||
|
|
||||||
std::string secret;
|
|
||||||
REQUIRE(make_file_backed_agent(dir.path())->load_user_secret(secret));
|
|
||||||
CHECK(secret == "refresh-token");
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST_CASE("Logging out leaves a secret this instance could not read alone", "[OrcaCloudServiceAgent]")
|
|
||||||
{
|
|
||||||
ScopedTemporaryDir dir("orca-secret");
|
|
||||||
// Written under another encryption key, e.g. by another OS user sharing the data directory.
|
|
||||||
fs::ofstream(secret_file(dir.path())) << "v2:0000:not-a-payload-this-user-can-decrypt";
|
|
||||||
|
|
||||||
auto agent = make_file_backed_agent(dir.path());
|
|
||||||
std::string secret;
|
|
||||||
REQUIRE_FALSE(agent->load_user_secret(secret));
|
|
||||||
|
|
||||||
agent->user_logout(false);
|
|
||||||
CHECK(fs::exists(secret_file(dir.path())));
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST_CASE("Orca cloud flat session resolves display name consistently", "[OrcaCloudServiceAgent]")
|
|
||||||
{
|
|
||||||
CHECK(resolved_display_name(flat_session_json({
|
|
||||||
{"username", "orca_username"},
|
|
||||||
{"display_name", "Display Name"},
|
|
||||||
{"nickname", "Nickname"}
|
|
||||||
})) == "Display Name");
|
|
||||||
|
|
||||||
CHECK(resolved_display_name(flat_session_json({
|
|
||||||
{"username", "orca_username"},
|
|
||||||
{"nickname", "Nickname"}
|
|
||||||
})) == "Nickname");
|
|
||||||
|
|
||||||
CHECK(resolved_display_name(flat_session_json({
|
|
||||||
{"username", "orca_username"},
|
|
||||||
{"full_name", "Full Name"}
|
|
||||||
})) == "Full Name");
|
|
||||||
|
|
||||||
CHECK(resolved_display_name(flat_session_json({
|
|
||||||
{"username", "orca_username"},
|
|
||||||
{"name", "Provider Name"}
|
|
||||||
})) == "Provider Name");
|
|
||||||
|
|
||||||
CHECK(resolved_display_name(flat_session_json({
|
|
||||||
{"username", "orca_username"}
|
|
||||||
})) == "orca_username");
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST_CASE("Orca cloud nested session resolves display name consistently", "[OrcaCloudServiceAgent]")
|
|
||||||
{
|
|
||||||
CHECK(resolved_display_name(nested_session_json({
|
|
||||||
{"username", "orca_username"},
|
|
||||||
{"display_name", "Display Name"},
|
|
||||||
{"nickname", "Nickname"}
|
|
||||||
})) == "Display Name");
|
|
||||||
|
|
||||||
CHECK(resolved_display_name(nested_session_json({
|
|
||||||
{"username", "orca_username"},
|
|
||||||
{"nickname", "Nickname"}
|
|
||||||
})) == "Nickname");
|
|
||||||
|
|
||||||
CHECK(resolved_display_name(nested_session_json({
|
|
||||||
{"username", "orca_username"},
|
|
||||||
{"full_name", "Full Name"}
|
|
||||||
})) == "Full Name");
|
|
||||||
|
|
||||||
CHECK(resolved_display_name(nested_session_json({
|
|
||||||
{"username", "orca_username"},
|
|
||||||
{"name", "Provider Name"}
|
|
||||||
})) == "Provider Name");
|
|
||||||
|
|
||||||
CHECK(resolved_display_name(nested_session_json({
|
|
||||||
{"username", "orca_username"}
|
|
||||||
})) == "orca_username");
|
|
||||||
}
|
|
||||||
@@ -38,9 +38,6 @@ namespace {
|
|||||||
// before this destructor's shutdown() runs.
|
// before this destructor's shutdown() runs.
|
||||||
struct ScopedPluginManager
|
struct ScopedPluginManager
|
||||||
{
|
{
|
||||||
// Before initialize(): the interpreter creates {data_dir}/python/packages and {data_dir}/log,
|
|
||||||
// which would otherwise land in the working directory.
|
|
||||||
ScopedDataDir python_data_dir{"plugin-python"};
|
|
||||||
bool initialized = PluginManager::instance().initialize();
|
bool initialized = PluginManager::instance().initialize();
|
||||||
|
|
||||||
~ScopedPluginManager()
|
~ScopedPluginManager()
|
||||||
|
|||||||
@@ -31,9 +31,6 @@ namespace {
|
|||||||
// same as any other plugin.
|
// same as any other plugin.
|
||||||
struct ScopedManagerShutdown
|
struct ScopedManagerShutdown
|
||||||
{
|
{
|
||||||
// Before initialize(): the interpreter creates {data_dir}/python/packages and {data_dir}/log,
|
|
||||||
// which would otherwise land in the working directory.
|
|
||||||
ScopedDataDir python_data_dir{"plugin-python"};
|
|
||||||
bool initialized = PluginManager::instance().initialize();
|
bool initialized = PluginManager::instance().initialize();
|
||||||
|
|
||||||
~ScopedManagerShutdown()
|
~ScopedManagerShutdown()
|
||||||
|
|||||||
@@ -42,9 +42,6 @@ namespace {
|
|||||||
// Declare this FIRST in a test so it is destroyed last.
|
// Declare this FIRST in a test so it is destroyed last.
|
||||||
struct ScopedPluginManager
|
struct ScopedPluginManager
|
||||||
{
|
{
|
||||||
// Before initialize(): the interpreter creates {data_dir}/python/packages and {data_dir}/log,
|
|
||||||
// which would otherwise land in the working directory.
|
|
||||||
ScopedDataDir python_data_dir{"plugin-python"};
|
|
||||||
bool initialized = false;
|
bool initialized = false;
|
||||||
|
|
||||||
ScopedPluginManager() { initialized = PluginManager::instance().initialize(); }
|
ScopedPluginManager() { initialized = PluginManager::instance().initialize(); }
|
||||||
|
|||||||
@@ -12,8 +12,6 @@
|
|||||||
#include <memory>
|
#include <memory>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
#include "plugin_test_utils.hpp"
|
|
||||||
|
|
||||||
namespace py = pybind11;
|
namespace py = pybind11;
|
||||||
using namespace Slic3r;
|
using namespace Slic3r;
|
||||||
|
|
||||||
@@ -23,9 +21,6 @@ namespace {
|
|||||||
// into Python unless PythonInterpreter::instance() reports initialized.
|
// into Python unless PythonInterpreter::instance() reports initialized.
|
||||||
struct ScopedPluginManager
|
struct ScopedPluginManager
|
||||||
{
|
{
|
||||||
// Before initialize(): the interpreter creates {data_dir}/python/packages and {data_dir}/log,
|
|
||||||
// which would otherwise land in the working directory.
|
|
||||||
ScopedDataDir python_data_dir{"plugin-python"};
|
|
||||||
bool initialized = PluginManager::instance().initialize();
|
bool initialized = PluginManager::instance().initialize();
|
||||||
|
|
||||||
~ScopedPluginManager()
|
~ScopedPluginManager()
|
||||||
|
|||||||
@@ -35,9 +35,6 @@ namespace {
|
|||||||
|
|
||||||
struct ScopedPluginManager
|
struct ScopedPluginManager
|
||||||
{
|
{
|
||||||
// Before initialize(): the interpreter creates {data_dir}/python/packages and {data_dir}/log,
|
|
||||||
// which would otherwise land in the working directory.
|
|
||||||
ScopedDataDir python_data_dir{"plugin-python"};
|
|
||||||
bool initialized = false;
|
bool initialized = false;
|
||||||
|
|
||||||
ScopedPluginManager() { initialized = PluginManager::instance().initialize(); }
|
ScopedPluginManager() { initialized = PluginManager::instance().initialize(); }
|
||||||
|
|||||||
Reference in New Issue
Block a user