mirror of
https://github.com/OrcaSlicer/OrcaSlicer.git
synced 2026-09-27 02:41:17 +00:00
Merge branch 'main' into feat/plugin-auditing
This commit is contained in:
@@ -113,39 +113,6 @@ bool CloudPluginService::request_cloud_unsubscribe(const PluginDescriptor& plugi
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CloudPluginService::request_cloud_delete(const PluginDescriptor& plugin, std::string& error) const
|
||||
{
|
||||
if (!m_orca_agent) {
|
||||
error = "No cloud agent.";
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!plugin.is_cloud_plugin()) {
|
||||
error = "Only cloud plugins can be deleted.";
|
||||
return false;
|
||||
}
|
||||
|
||||
const std::string cloud_uuid = plugin.cloud_uuid();
|
||||
if (cloud_uuid.empty()) {
|
||||
error = "Cloud plugin key is missing UUID.";
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!plugin.cloud.has_value() || !plugin.cloud->is_mine) {
|
||||
error = "Only your own plugins can be deleted from the cloud.";
|
||||
return false;
|
||||
}
|
||||
|
||||
int result = m_orca_agent->delete_my_plugin(cloud_uuid);
|
||||
|
||||
if (result != 0) {
|
||||
error = "Failed to delete plugin from cloud, see logs for more info.";
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CloudPluginService::download_cloud_plugin(PluginDescriptor& entry,
|
||||
const std::string& requested_version,
|
||||
CloudPluginDownload& download,
|
||||
|
||||
@@ -29,7 +29,6 @@ public:
|
||||
std::vector<std::string>& unauthorized) const;
|
||||
bool request_cloud_subscribe(const std::string& plugin_uuid, std::string& error) const;
|
||||
bool request_cloud_unsubscribe(const PluginDescriptor& plugin, std::string& error) const;
|
||||
bool request_cloud_delete(const PluginDescriptor& plugin, std::string& error) const;
|
||||
bool download_cloud_plugin(PluginDescriptor& entry,
|
||||
const std::string& requested_version,
|
||||
CloudPluginDownload& download,
|
||||
|
||||
@@ -324,12 +324,6 @@ bool PluginConfig::dirty() const
|
||||
return m_dirty;
|
||||
}
|
||||
|
||||
std::string plugin_overrides_of(const Preset& preset)
|
||||
{
|
||||
const auto* opt = dynamic_cast<const ConfigOptionString*>(preset.config.option(PLUGIN_OVERRIDES_OPTION_KEY));
|
||||
return opt == nullptr ? std::string() : opt->value;
|
||||
}
|
||||
|
||||
bool parse_plugin_overrides(const std::string& raw, CapabilityConfigDocument& document, std::string& error)
|
||||
{
|
||||
document = CapabilityConfigDocument();
|
||||
@@ -357,9 +351,9 @@ std::string serialize_plugin_overrides(const CapabilityConfigDocument& document)
|
||||
return document.empty() ? std::string() : document.serialize_entries().dump();
|
||||
}
|
||||
|
||||
bool prune_stale_plugin_overrides(DynamicConfig& config)
|
||||
bool prune_stale_plugin_overrides(DynamicConfig& config, const std::string& overrides_key)
|
||||
{
|
||||
const auto* overrides_opt = dynamic_cast<const ConfigOptionString*>(config.option(PLUGIN_OVERRIDES_OPTION_KEY));
|
||||
const auto* overrides_opt = dynamic_cast<const ConfigOptionString*>(config.option(overrides_key));
|
||||
if (overrides_opt == nullptr || overrides_opt->value.empty())
|
||||
return false;
|
||||
|
||||
@@ -397,7 +391,7 @@ bool prune_stale_plugin_overrides(DynamicConfig& config)
|
||||
if (!overrides.prune_unreferenced(referenced))
|
||||
return false;
|
||||
|
||||
config.set_key_value(PLUGIN_OVERRIDES_OPTION_KEY, new ConfigOptionString(serialize_plugin_overrides(overrides)));
|
||||
config.set_key_value(overrides_key, new ConfigOptionString(serialize_plugin_overrides(overrides)));
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -470,27 +464,29 @@ EffectiveCapabilityConfig active_capability_config(const PluginCapabilityId& id)
|
||||
|
||||
if (bundle != nullptr) {
|
||||
const std::string type_key = plugin_capability_type_to_string(id.type);
|
||||
// The edited preset of each type that can hold plugin-backed options, keyed by its option list.
|
||||
const std::pair<const std::vector<std::string>*, const Preset*> scopes[] = {
|
||||
{&Preset::print_options(), &bundle->prints.get_edited_preset()},
|
||||
{&Preset::printer_options(), &bundle->printers.get_edited_preset()},
|
||||
{&Preset::filament_options(), &bundle->filaments.get_edited_preset()},
|
||||
};
|
||||
for (const auto& [key, def] : print_config_def.options) {
|
||||
if (def.plugin_type != type_key)
|
||||
continue;
|
||||
|
||||
const auto& print_options = Preset::print_options();
|
||||
if (std::find(print_options.begin(), print_options.end(), key) != print_options.end()) {
|
||||
preset = &bundle->prints.get_edited_preset();
|
||||
for (const auto& [options, edited] : scopes)
|
||||
if (contains(*options, key)) {
|
||||
preset = edited;
|
||||
break;
|
||||
}
|
||||
if (preset != nullptr)
|
||||
break;
|
||||
}
|
||||
|
||||
const auto& printer_options = Preset::printer_options();
|
||||
if (std::find(printer_options.begin(), printer_options.end(), key) != printer_options.end()) {
|
||||
preset = &bundle->printers.get_edited_preset();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (preset != nullptr) {
|
||||
const auto* stored = dynamic_cast<const ConfigOptionString*>(preset->config.option(Preset::plugin_overrides_key(preset->type)));
|
||||
std::string error;
|
||||
if (!parse_plugin_overrides(plugin_overrides_of(*preset), overrides, error)) {
|
||||
if (!parse_plugin_overrides(stored == nullptr ? std::string() : stored->value, overrides, error)) {
|
||||
// Text we cannot read is not an override: log it and resolve against the base config.
|
||||
BOOST_LOG_TRIVIAL(error) << "Preset \"" << preset->name << "\": " << error;
|
||||
overrides = CapabilityConfigDocument();
|
||||
|
||||
@@ -17,7 +17,6 @@
|
||||
|
||||
namespace Slic3r {
|
||||
|
||||
class Preset;
|
||||
class DynamicConfig;
|
||||
struct CapabilityConfigEntry
|
||||
{
|
||||
@@ -50,19 +49,15 @@ private:
|
||||
std::vector<nlohmann::json> m_opaque_entries;
|
||||
};
|
||||
|
||||
inline constexpr const char* PLUGIN_OVERRIDES_OPTION_KEY = "plugin_config_overrides";
|
||||
|
||||
std::string plugin_overrides_of(const Preset& preset);
|
||||
bool parse_plugin_overrides(const std::string& raw, CapabilityConfigDocument& document, std::string& error);
|
||||
std::string serialize_plugin_overrides(const CapabilityConfigDocument& document);
|
||||
|
||||
// Drops plugin_config_overrides entries for capabilities no longer named by any plugin-backed
|
||||
// option's current value in `config` (e.g. slicing_pipeline_plugin cleared or switched to a
|
||||
// different capability), and writes the result back if anything changed. Called wherever a
|
||||
// plugin-backed option's value changes, so a saved preset never carries configuration for a
|
||||
// capability it no longer references. Returns true if `config` was modified, so a caller holding a
|
||||
// GUI field over PLUGIN_OVERRIDES_OPTION_KEY knows it must refresh that field's displayed value.
|
||||
bool prune_stale_plugin_overrides(DynamicConfig& config);
|
||||
// Drops override entries for capabilities no longer named by any plugin-backed option in `config`
|
||||
// (e.g. slicing_pipeline_plugin cleared or pointed at another capability) and writes the result back
|
||||
// to `overrides_key`. Call it wherever such an option changes, so a saved preset never carries
|
||||
// configuration for a capability it no longer references. Returns true if `config` was modified, so a
|
||||
// caller holding a GUI field over `overrides_key` knows to refresh it.
|
||||
bool prune_stale_plugin_overrides(DynamicConfig& config, const std::string& overrides_key);
|
||||
|
||||
struct EffectiveCapabilityConfig
|
||||
{
|
||||
|
||||
@@ -54,7 +54,7 @@ struct PluginDescriptor
|
||||
std::string description; // Plugin description
|
||||
std::string author; // Plugin author from manifest, if available
|
||||
std::string version; // Selected plugin version
|
||||
std::string latest_version; // Latest available cloud version fallback when changelog is unavailable.
|
||||
std::string latest_version; // Authoritative latest available cloud version.
|
||||
std::string installed_version; // Locally installed package version. Preserved across cloud merges, which overwrite `version` with the latest cloud version. Empty when not installed.
|
||||
std::vector<std::string> display_types; // Display-only "compatibility" labels (cloud: raw service labels; local: from real capabilities). Never used for dispatch.
|
||||
std::string plugin_root; // Installed plugin directory, even when entry_path is invalid or ambiguous.
|
||||
@@ -66,6 +66,10 @@ struct PluginDescriptor
|
||||
std::string error; // Blocking error message. Non-empty means the plugin is in an error state.
|
||||
std::optional<CloudPluginState> cloud; // Extra cloud state layered on top of a normal plugin descriptor.
|
||||
bool metadata_valid = false; // Manifest/package validity stays separate from the user-facing error field.
|
||||
// Whether the discovery pass successfully read .install_state.json. This is transient scan
|
||||
// metadata, used to distinguish an explicit enabled=false from a sidecar that was unavailable
|
||||
// during a live package replacement.
|
||||
bool install_state_valid = false;
|
||||
// Package auto-load flag, read from .install_state.json. Defaults to FALSE: a package with no
|
||||
// sidecar has never been installed through Orca and carries no auto-load intent, so it must not
|
||||
// be loaded at startup. Installing a package writes the sidecar with enabled = true.
|
||||
@@ -113,10 +117,6 @@ struct PluginDescriptor
|
||||
bool is_unauthorized() const { return get_update_status() == PluginUpdateStatus::Unauthorized; }
|
||||
std::string latest_available_version() const
|
||||
{
|
||||
for (const PluginChangelog& entry : changelog) {
|
||||
if (!entry.version.empty())
|
||||
return entry.version;
|
||||
}
|
||||
if (!latest_version.empty())
|
||||
return latest_version;
|
||||
return version;
|
||||
|
||||
@@ -172,7 +172,7 @@ void scan_plugin_directory(const std::string& dir_path, std::vector<PluginDescri
|
||||
// its error, rather than dropping it silently.
|
||||
if (entry_path.empty()) {
|
||||
descriptor.set_error(entry_error);
|
||||
read_install_state(plugin_dir, descriptor);
|
||||
descriptor.install_state_valid = read_install_state(plugin_dir, descriptor);
|
||||
assign_discovered_plugin_key(descriptor, plugin_dir);
|
||||
out.push_back(std::move(descriptor));
|
||||
BOOST_LOG_TRIVIAL(warning) << "Invalid plugin package: " << plugin_dir.string() << " - " << out.back().error;
|
||||
@@ -185,7 +185,7 @@ void scan_plugin_directory(const std::string& dir_path, std::vector<PluginDescri
|
||||
read_python_plugin_metadata(entry_path, descriptor, meta_error);
|
||||
if (!parsed) {
|
||||
descriptor.set_error(meta_error);
|
||||
read_install_state(plugin_dir, descriptor);
|
||||
descriptor.install_state_valid = read_install_state(plugin_dir, descriptor);
|
||||
assign_discovered_plugin_key(descriptor, entry_path);
|
||||
out.push_back(std::move(descriptor));
|
||||
BOOST_LOG_TRIVIAL(warning) << (is_wheel ? "Invalid wheel plugin: " : "Invalid .py plugin: ")
|
||||
@@ -199,7 +199,7 @@ void scan_plugin_directory(const std::string& dir_path, std::vector<PluginDescri
|
||||
|
||||
// Cloud identity and the package-level auto-load flag. plugin_key is always derived
|
||||
// below, never read from the sidecar.
|
||||
read_install_state(plugin_dir, descriptor);
|
||||
descriptor.install_state_valid = read_install_state(plugin_dir, descriptor);
|
||||
assign_discovered_plugin_key(descriptor, entry_path);
|
||||
|
||||
out.push_back(std::move(descriptor));
|
||||
@@ -735,11 +735,11 @@ bool extract_zip_to_directory(const boost::filesystem::path& zip_path, const boo
|
||||
return true;
|
||||
}
|
||||
|
||||
void read_install_state(const boost::filesystem::path& plugin_dir, PluginDescriptor& entry)
|
||||
bool read_install_state(const boost::filesystem::path& plugin_dir, PluginDescriptor& entry)
|
||||
{
|
||||
PluginInstallState state;
|
||||
if (!read_install_state(plugin_dir, state))
|
||||
return;
|
||||
return false;
|
||||
|
||||
// The cloud identity and the persisted installed version are read back. plugin_key
|
||||
// is always derived by the catalog scan (filename for local, the cloud uuid for
|
||||
@@ -756,6 +756,7 @@ void read_install_state(const boost::filesystem::path& plugin_dir, PluginDescrip
|
||||
// capability has no existence — and so no state — until it is materialized, at which point the
|
||||
// loader seeds the flag onto the capability itself.
|
||||
entry.enabled = state.enabled;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool read_install_state(const boost::filesystem::path& plugin_dir, PluginInstallState& out)
|
||||
|
||||
@@ -166,8 +166,9 @@ bool write_install_state(const boost::filesystem::path& plugin_dir, const Plugin
|
||||
// Convenience overload: write(dir, entry, /*enabled=*/true, /*capabilities=*/{}).
|
||||
bool write_install_state(const boost::filesystem::path& plugin_dir, const PluginDescriptor& entry);
|
||||
|
||||
// Reads only the cloud identity (uuid) back into the descriptor; plugin_key is always derived.
|
||||
void read_install_state(const boost::filesystem::path& plugin_dir, PluginDescriptor& entry);
|
||||
// Reads install state back into the descriptor; plugin_key is always derived. Returns whether the
|
||||
// sidecar was present and valid.
|
||||
bool read_install_state(const boost::filesystem::path& plugin_dir, PluginDescriptor& entry);
|
||||
// Full read of the sidecar; returns false if there is no/invalid sidecar.
|
||||
bool read_install_state(const boost::filesystem::path& plugin_dir, PluginInstallState& out);
|
||||
|
||||
|
||||
@@ -322,6 +322,8 @@ void PluginManager::merge_discovered_plugins(std::vector<PluginDescriptor> disco
|
||||
|
||||
// A manifest-only rescan has nothing to say about capabilities, so the live module and
|
||||
// instances are left alone.
|
||||
if (!descriptor.install_state_valid)
|
||||
descriptor.enabled = existing->descriptor.enabled;
|
||||
existing->descriptor = std::move(descriptor);
|
||||
}
|
||||
|
||||
@@ -1897,7 +1899,7 @@ bool PluginManager::unsubscribe_cloud_plugin(const std::string& plugin_key, std:
|
||||
}
|
||||
|
||||
if (descriptor.cloud && descriptor.cloud->is_mine) {
|
||||
error = "Cannot unsubscribe your own plugins. Use Delete from Cloud instead.";
|
||||
error = "Cannot unsubscribe your own plugins.";
|
||||
set_plugin_error(plugin_key, error);
|
||||
return false;
|
||||
}
|
||||
@@ -1930,7 +1932,7 @@ bool PluginManager::delete_and_unsubscribe_cloud_plugin(const std::string& plugi
|
||||
}
|
||||
|
||||
if (descriptor.cloud->is_mine) {
|
||||
error = "Use Delete local and cloud for owned plugins.";
|
||||
error = "Cannot unsubscribe your own plugins. Use Delete to remove the local files.";
|
||||
set_plugin_error(plugin_key, error);
|
||||
return false;
|
||||
}
|
||||
@@ -1943,72 +1945,6 @@ bool PluginManager::delete_and_unsubscribe_cloud_plugin(const std::string& plugi
|
||||
return finalize_cloud_plugin_removal(descriptor, false, error);
|
||||
}
|
||||
|
||||
bool PluginManager::delete_mine_plugin_from_cloud(const std::string& plugin_key, std::string& error)
|
||||
{
|
||||
if (!wait_for_discovery(std::chrono::milliseconds::max(), error))
|
||||
return false;
|
||||
|
||||
error.clear();
|
||||
|
||||
PluginDescriptor descriptor;
|
||||
if (!try_get_plugin_descriptor(plugin_key, descriptor)) {
|
||||
error = "Plugin not found: " + plugin_key;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!descriptor.is_cloud_plugin()) {
|
||||
error = "Only owned cloud plugins can be deleted from the cloud.";
|
||||
set_plugin_error(plugin_key, error);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!descriptor.cloud->is_mine) {
|
||||
error = "Only your own plugins can be deleted from the cloud.";
|
||||
set_plugin_error(plugin_key, error);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!m_cloud_service.request_cloud_delete(descriptor, error)) {
|
||||
set_plugin_error(plugin_key, error);
|
||||
return false;
|
||||
}
|
||||
|
||||
return finalize_cloud_plugin_removal(descriptor, true, error);
|
||||
}
|
||||
|
||||
bool PluginManager::delete_mine_local_and_cloud_plugin(const std::string& plugin_key, std::string& error)
|
||||
{
|
||||
if (!wait_for_discovery(std::chrono::milliseconds::max(), error))
|
||||
return false;
|
||||
|
||||
error.clear();
|
||||
|
||||
PluginDescriptor descriptor;
|
||||
if (!try_get_plugin_descriptor(plugin_key, descriptor)) {
|
||||
error = "Plugin not found: " + plugin_key;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!descriptor.is_cloud_plugin()) {
|
||||
error = "Only owned cloud plugins can be deleted from local and cloud.";
|
||||
set_plugin_error(plugin_key, error);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!descriptor.cloud->is_mine) {
|
||||
error = "Only your own plugins can be deleted from local and cloud.";
|
||||
set_plugin_error(plugin_key, error);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!m_cloud_service.request_cloud_delete(descriptor, error)) {
|
||||
set_plugin_error(plugin_key, error);
|
||||
return false;
|
||||
}
|
||||
|
||||
return finalize_cloud_plugin_removal(descriptor, false, error);
|
||||
}
|
||||
|
||||
ExecutionResult PluginManager::run_script_capability(const std::string& plugin_key, const std::string& capability_name, std::string& error)
|
||||
{
|
||||
if (plugin_key.empty() || capability_name.empty()) {
|
||||
|
||||
@@ -198,8 +198,6 @@ public:
|
||||
bool delete_plugin(const std::string& plugin_key, std::string& error);
|
||||
bool unsubscribe_cloud_plugin(const std::string& plugin_key, std::string& error);
|
||||
bool delete_and_unsubscribe_cloud_plugin(const std::string& plugin_key, std::string& error);
|
||||
bool delete_mine_plugin_from_cloud(const std::string& plugin_key, std::string& error);
|
||||
bool delete_mine_local_and_cloud_plugin(const std::string& plugin_key, std::string& error);
|
||||
|
||||
ExecutionResult run_script_capability(const std::string& plugin_key, const std::string& capability_name, std::string& error);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user