mirror of
https://github.com/blender/blender
synced 2026-09-29 04:37:17 +03:00
Asset Library: Authentication token support
This introduces access tokens to Asset Libraries. * The access tokens use the same Bearer Authentication as used by extensions. * This reset the token if "use_auth_token" is disabled. Actually it set it to random values and then set it to null, to avoid it lingering in memory. This could also be implemented for extensions repository. Pull Request: https://projects.blender.org/blender/blender/pulls/162163
This commit is contained in:
parent
963c5719af
commit
a9e061dc90
19 changed files with 203 additions and 20 deletions
|
|
@ -428,6 +428,7 @@ _downloaders: list[_RemoteAssetListingDownloader] = []
|
|||
# Called directly from C++ code.
|
||||
def remote_asset_library_sync(
|
||||
asset_library_url: str,
|
||||
asset_library_auth_token: str,
|
||||
asset_library_local_path: Path,
|
||||
only_if_older_than_sec=0,
|
||||
) -> None:
|
||||
|
|
@ -473,6 +474,7 @@ def remote_asset_library_sync(
|
|||
# Create the downloader and start downloading.
|
||||
downloader = listing_downloader.RemoteAssetListingDownloader(
|
||||
asset_library_url,
|
||||
asset_library_auth_token,
|
||||
asset_library_local_path,
|
||||
on_update_callback=_remote_asset_library_sync_update,
|
||||
on_done_callback=_remote_asset_library_sync_done,
|
||||
|
|
@ -555,14 +557,14 @@ def _remote_asset_library_sync_all_periodic():
|
|||
continue
|
||||
if not asset_lib.use_remote_url:
|
||||
continue
|
||||
remote_asset_library_sync(asset_lib.remote_url, Path(asset_lib.path),
|
||||
remote_asset_library_sync(asset_lib.remote_url, asset_lib.auth_token, Path(asset_lib.path),
|
||||
only_if_older_than_sec=REMOTE_ASSET_LIBS_AUTOSYNC_PERIOD_SEC)
|
||||
|
||||
# The online essentials library is not listed in the 'asset_libraries' list above, because it's not a preference.
|
||||
if prefs.asset_libraries.use_online_essentials:
|
||||
remote_url = bpy.types.AssetLibrary.online_assets_url()
|
||||
cache_path = bpy.types.AssetLibrary.online_assets_cache_path()
|
||||
remote_asset_library_sync(remote_url, Path(cache_path),
|
||||
remote_asset_library_sync(remote_url, "", Path(cache_path),
|
||||
only_if_older_than_sec=REMOTE_ASSET_LIBS_AUTOSYNC_PERIOD_SEC)
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -35,6 +35,7 @@ _preview_downloaders: dict[str, AssetDownloader] = {}
|
|||
|
||||
def download_asset_file(
|
||||
asset_library_url: str,
|
||||
asset_library_auth_token: str,
|
||||
asset_library_local_path: Path,
|
||||
asset_url: str,
|
||||
asset_hash: str,
|
||||
|
|
@ -45,6 +46,8 @@ def download_asset_file(
|
|||
identifier of this library (to create a downloader per library), as well
|
||||
as for resolving relative URLs.
|
||||
|
||||
:param asset_library_auth_token: Optional authentication token for bearer authentication.
|
||||
|
||||
:param asset_library_local_path: Root path of the local asset cache. Used to
|
||||
resolve relative `save_to` paths, but also to find the HTTP metadata
|
||||
cache for this asset library (for conditional downloads).
|
||||
|
|
@ -68,6 +71,7 @@ def download_asset_file(
|
|||
except KeyError:
|
||||
downloader = AssetDownloader(
|
||||
asset_library_url,
|
||||
asset_library_auth_token,
|
||||
asset_library_local_path,
|
||||
reporter=AssetReporter(asset_library_url=asset_library_url),
|
||||
on_queue_empty_callback=on_asset_download_queue_empty,
|
||||
|
|
@ -93,6 +97,7 @@ def download_asset_file(
|
|||
|
||||
def download_preview(
|
||||
asset_library_url: str,
|
||||
asset_library_auth_token: str,
|
||||
asset_library_local_path: Path,
|
||||
preview_url: str,
|
||||
preview_hash: str,
|
||||
|
|
@ -103,6 +108,8 @@ def download_preview(
|
|||
identifier of this library (to create a downloader per library), as well
|
||||
as for resolving relative URLs.
|
||||
|
||||
:param asset_library_auth_token: Authentication tokens optionally required by some servers.
|
||||
|
||||
:param asset_library_local_path: Root path of the local asset cache. Used to
|
||||
resolve relative `save_to` paths, but also to find the HTTP metadata
|
||||
cache for this asset library (for conditional downloads).
|
||||
|
|
@ -137,6 +144,7 @@ def download_preview(
|
|||
except KeyError:
|
||||
downloader = AssetDownloader(
|
||||
asset_library_url,
|
||||
asset_library_auth_token,
|
||||
asset_library_local_path,
|
||||
reporter=PreviewReporter(),
|
||||
on_queue_empty_callback=None,
|
||||
|
|
@ -261,6 +269,7 @@ class AssetDownloader:
|
|||
def __init__(
|
||||
self,
|
||||
remote_url: str,
|
||||
auth_token: str,
|
||||
local_path: Path | str,
|
||||
*,
|
||||
reporter: http_dl.DownloadReporter,
|
||||
|
|
@ -272,6 +281,8 @@ class AssetDownloader:
|
|||
|
||||
:param remote_url: Base URL of the remote asset library server.
|
||||
|
||||
:param auth_token: Optional (may be empty) authentication token.
|
||||
|
||||
:param local_path: The directory to download the index files to.
|
||||
|
||||
:param on_download_done_callback: called with one parameter (this
|
||||
|
|
@ -289,14 +300,16 @@ class AssetDownloader:
|
|||
# Work around a limitation of Blender, see bug report #139720 for details.
|
||||
self.on_timer_event = self.on_timer_event # type: ignore[method-assign]
|
||||
|
||||
http_headers = {'X-Blender': "{:d}.{:d}".format(*bpy.app.version)}
|
||||
if auth_token:
|
||||
http_headers['Authorization'] = "Bearer {:s}".format(auth_token)
|
||||
|
||||
self._downloader_options = http_dl.DownloaderOptions(
|
||||
metadata_provider=http_dl.MetadataProviderFilesystem(
|
||||
cache_location=self._locator.http_metadata_cache_location,
|
||||
),
|
||||
timeout=300,
|
||||
http_headers={
|
||||
'X-Blender': "{:d}.{:d}".format(*bpy.app.version),
|
||||
},
|
||||
http_headers=http_headers,
|
||||
num_parallel_downloads=num_parallel_downloads,
|
||||
)
|
||||
|
||||
|
|
|
|||
|
|
@ -238,6 +238,7 @@ class RemoteAssetListingDownloader:
|
|||
def __init__(
|
||||
self,
|
||||
remote_url: str,
|
||||
auth_token: str,
|
||||
local_path: Path | str,
|
||||
on_update_callback: OnUpdateCallback,
|
||||
on_done_callback: OnDoneCallback,
|
||||
|
|
@ -250,6 +251,8 @@ class RemoteAssetListingDownloader:
|
|||
blender_asset_library_openapi.yaml for the files downloaded from
|
||||
there.
|
||||
|
||||
:param asset_library_auth_token: Optional authentication token for bearer authentication.
|
||||
|
||||
:param local_path: The directory to download the index files to.
|
||||
|
||||
:param on_update_callback: Called with one parameter (this
|
||||
|
|
@ -301,16 +304,20 @@ class RemoteAssetListingDownloader:
|
|||
cache_location=self._locator.http_metadata_cache_location,
|
||||
))
|
||||
|
||||
http_headers = {
|
||||
'Accept': 'application/json',
|
||||
'X-Blender': "{:d}.{:d}".format(*bpy.app.version),
|
||||
}
|
||||
if auth_token:
|
||||
http_headers['Authorization'] = "Bearer {:s}".format(auth_token)
|
||||
|
||||
# Create the background downloader object now, so that it
|
||||
# (hypothetically in some future) can be adjusted before the actual
|
||||
# downloading begins.
|
||||
self._bg_downloader = http_dl.BackgroundDownloader(
|
||||
options=http_dl.DownloaderOptions(
|
||||
metadata_provider=self._http_metadata_provider,
|
||||
http_headers={
|
||||
'Accept': 'application/json',
|
||||
'X-Blender': "{:d}.{:d}".format(*bpy.app.version),
|
||||
},
|
||||
http_headers=http_headers,
|
||||
timeout=300,
|
||||
max_disk_size_bytes=MAX_JSON_FILE_SIZE_MB * 1024 * 1024,
|
||||
),
|
||||
|
|
|
|||
|
|
@ -176,6 +176,8 @@ class AssetLibrary {
|
|||
*/
|
||||
virtual std::optional<StringRefNull> remote_url() const;
|
||||
|
||||
virtual std::optional<StringRefNull> auth_token() const;
|
||||
|
||||
AssetCatalogService &catalog_service() const;
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -29,10 +29,13 @@ namespace asset_system {
|
|||
struct RemoteLibraryDefinitionRef {
|
||||
StringRefNull remote_url;
|
||||
StringRefNull cache_dirpath;
|
||||
std::optional<StringRefNull> auth_token;
|
||||
|
||||
RemoteLibraryDefinitionRef(const bUserAssetLibrary &library_definition);
|
||||
RemoteLibraryDefinitionRef(StringRefNull remote_url, StringRefNull cache_dirpath)
|
||||
: remote_url(remote_url), cache_dirpath(cache_dirpath)
|
||||
RemoteLibraryDefinitionRef(StringRefNull remote_url,
|
||||
StringRefNull cache_dirpath,
|
||||
std::optional<StringRefNull> auth_token = std::nullopt)
|
||||
: remote_url(remote_url), cache_dirpath(cache_dirpath), auth_token(auth_token)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
|
|
|||
|
|
@ -259,6 +259,11 @@ std::optional<StringRefNull> AssetLibrary::remote_url() const
|
|||
return {};
|
||||
};
|
||||
|
||||
std::optional<StringRefNull> AssetLibrary::auth_token() const
|
||||
{
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
AssetCatalogService &AssetLibrary::catalog_service() const
|
||||
{
|
||||
std::lock_guard lock{catalog_service_mutex_};
|
||||
|
|
|
|||
|
|
@ -36,7 +36,8 @@ void AllAssetLibrary::force_remote_listing_download() const
|
|||
[&](AssetLibrary &nested) {
|
||||
const std::optional<StringRefNull> url = nested.remote_url();
|
||||
if (url.has_value()) {
|
||||
remote_library_request_download(RemoteLibraryDefinitionRef{*url, nested.root_path()});
|
||||
remote_library_request_download(
|
||||
RemoteLibraryDefinitionRef{*url, nested.root_path(), nested.auth_token()});
|
||||
}
|
||||
},
|
||||
/*include_all_library=*/false);
|
||||
|
|
|
|||
|
|
@ -55,7 +55,11 @@ static CLG_LogRef LOG = {"asset.remote_library"};
|
|||
namespace blender::asset_system {
|
||||
|
||||
RemoteLibraryDefinitionRef::RemoteLibraryDefinitionRef(const bUserAssetLibrary &library_definition)
|
||||
: remote_url(library_definition.remote_url), cache_dirpath(library_definition.dirpath)
|
||||
: remote_url(library_definition.remote_url),
|
||||
cache_dirpath(library_definition.dirpath),
|
||||
auth_token(library_definition.auth_token ?
|
||||
std::optional<StringRefNull>(library_definition.auth_token) :
|
||||
std::nullopt)
|
||||
{
|
||||
BLI_assert((library_definition.flag & ASSET_LIBRARY_USE_REMOTE_URL) != 0);
|
||||
}
|
||||
|
|
@ -78,7 +82,8 @@ RemoteAssetLibrary::RemoteAssetLibrary(const eAssetLibraryType library_type,
|
|||
|
||||
void RemoteAssetLibrary::force_remote_listing_download() const
|
||||
{
|
||||
remote_library_request_download(RemoteLibraryDefinitionRef{remote_url_, root_path()});
|
||||
remote_library_request_download(
|
||||
RemoteLibraryDefinitionRef{remote_url_, root_path(), auth_token()});
|
||||
}
|
||||
|
||||
std::optional<eAssetImportMethod> RemoteAssetLibrary::import_method() const
|
||||
|
|
@ -148,6 +153,22 @@ bool PreferencesRemoteAssetLibrary::is_enabled() const
|
|||
return (library_definition->flag & ASSET_LIBRARY_DISABLED) == 0;
|
||||
}
|
||||
|
||||
std::optional<StringRefNull> PreferencesRemoteAssetLibrary::auth_token() const
|
||||
{
|
||||
const bUserAssetLibrary *library_definition = user_library_.user_asset_library();
|
||||
if (library_definition == nullptr) {
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
StringRefNull token(library_definition->auth_token);
|
||||
if (token.is_empty()) {
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
BLI_assert(library_definition->flag & ASSET_LIBRARY_USE_AUTH_TOKEN);
|
||||
return token;
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
|
|
@ -628,12 +649,14 @@ void remote_library_request_download(const RemoteLibraryDefinitionRef &library_d
|
|||
"from pathlib import Path\n"
|
||||
"\n"
|
||||
"bl_pkg.remote_asset_library_sync(\n"
|
||||
" library_url, Path(library_path),\n"
|
||||
" library_url, auth_token, Path(library_path)\n"
|
||||
")\n";
|
||||
|
||||
std::unique_ptr locals = bke::idprop::create_group("locals");
|
||||
IDP_AddToGroup(locals.get(), IDP_NewString(library_definition.remote_url, "library_url"));
|
||||
IDP_AddToGroup(locals.get(), IDP_NewString(library_definition.cache_dirpath, "library_path"));
|
||||
IDP_AddToGroup(locals.get(),
|
||||
IDP_NewString(library_definition.auth_token.value_or(""), "auth_token"));
|
||||
|
||||
/* TODO: report errors in the UI somehow. */
|
||||
BPY_run_string_exec_with_locals(nullptr, script, *locals);
|
||||
|
|
@ -723,12 +746,13 @@ static std::optional<std::string> remote_library_request_asset_download_file(
|
|||
"from pathlib import Path\n"
|
||||
"\n"
|
||||
"_result = asset_dl.download_asset_file(\n"
|
||||
" library_url, Path(library_path),\n"
|
||||
" library_url, auth_token, Path(library_path),\n"
|
||||
" asset_url, asset_hash, Path(dst_filepath),\n"
|
||||
")\n";
|
||||
|
||||
std::unique_ptr locals = bke::idprop::create_group("locals");
|
||||
IDP_AddToGroup(locals.get(), IDP_NewString(*library.remote_url(), "library_url"));
|
||||
IDP_AddToGroup(locals.get(), IDP_NewString(library.auth_token().value_or(""), "auth_token"));
|
||||
IDP_AddToGroup(locals.get(), IDP_NewString(library.root_path(), "library_path"));
|
||||
IDP_AddToGroup(locals.get(), IDP_NewString(dst_filepath, "dst_filepath"));
|
||||
IDP_AddToGroup(locals.get(), IDP_NewString(asset_url.url, "asset_url"));
|
||||
|
|
@ -868,12 +892,14 @@ void remote_library_request_preview_download(const bContext &C,
|
|||
"from pathlib import Path\n"
|
||||
"\n"
|
||||
"asset_dl.download_preview(\n"
|
||||
" library_url, Path(library_path),\n"
|
||||
" library_url, library_auth_token, Path(library_path),\n"
|
||||
" preview_url, preview_hash, Path(dst_filepath),\n"
|
||||
")\n";
|
||||
|
||||
std::unique_ptr locals = bke::idprop::create_group("locals");
|
||||
IDP_AddToGroup(locals.get(), IDP_NewString(*library_url, "library_url"));
|
||||
IDP_AddToGroup(locals.get(),
|
||||
IDP_NewString(library.auth_token().value_or(""), "library_auth_token"));
|
||||
IDP_AddToGroup(locals.get(), IDP_NewString(library.root_path(), "library_path"));
|
||||
IDP_AddToGroup(locals.get(), IDP_NewString(*preview_url, "preview_url"));
|
||||
IDP_AddToGroup(locals.get(), IDP_NewString(*preview_hash, "preview_hash"));
|
||||
|
|
|
|||
|
|
@ -41,6 +41,7 @@ class PreferencesRemoteAssetLibrary : public RemoteAssetLibrary {
|
|||
public:
|
||||
PreferencesRemoteAssetLibrary(const bUserAssetLibrary &custom_library);
|
||||
std::optional<AssetLibraryReference> library_reference() const override;
|
||||
std::optional<StringRefNull> auth_token() const override;
|
||||
bool is_enabled() const;
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -122,6 +122,12 @@ bool BKE_preferences_asset_library_is_valid(const UserDef *userdef,
|
|||
|
||||
void BKE_preferences_asset_library_default_add(struct UserDef *userdef) ATTR_NONNULL();
|
||||
|
||||
void BKE_preferences_asset_library_read_data(struct BlendDataReader *reader,
|
||||
struct bUserAssetLibrary *library);
|
||||
|
||||
void BKE_preferences_asset_library_write_data(struct BlendWriter *writer,
|
||||
const struct bUserAssetLibrary *library);
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
|
|
|
|||
|
|
@ -38,6 +38,7 @@
|
|||
#include "BKE_idprop.hh"
|
||||
#include "BKE_main.hh"
|
||||
#include "BKE_node.hh"
|
||||
#include "BKE_preferences.h"
|
||||
#include "BKE_screen.hh"
|
||||
#include "BKE_studiolight.h"
|
||||
|
||||
|
|
@ -377,7 +378,10 @@ void BKE_blender_userdef_data_free(UserDef *userdef, bool clear_fonts)
|
|||
|
||||
userdef->autoexec_paths.free_no_destruct();
|
||||
userdef->script_directories.free_no_destruct();
|
||||
userdef->asset_libraries.free_no_destruct();
|
||||
|
||||
for (bUserAssetLibrary &library_ref : userdef->asset_libraries.items_mutable()) {
|
||||
BKE_preferences_asset_library_remove(userdef, &library_ref);
|
||||
}
|
||||
|
||||
for (bUserExtensionRepo &repo_ref : userdef->extension_repos.items_mutable()) {
|
||||
MEM_SAFE_DELETE(repo_ref.access_token);
|
||||
|
|
|
|||
|
|
@ -88,6 +88,7 @@ bUserAssetLibrary *BKE_preferences_asset_library_add(UserDef *userdef,
|
|||
|
||||
void BKE_preferences_asset_library_remove(UserDef *userdef, bUserAssetLibrary *library)
|
||||
{
|
||||
MEM_delete(library->auth_token);
|
||||
BLI_freelinkN(&userdef->asset_libraries, library);
|
||||
}
|
||||
|
||||
|
|
@ -190,6 +191,21 @@ void BKE_preferences_asset_library_default_add(UserDef *userdef)
|
|||
library->dirpath, sizeof(library->dirpath), documents_path, N_("Blender"), N_("Assets"));
|
||||
}
|
||||
|
||||
void BKE_preferences_asset_library_read_data(BlendDataReader *reader, bUserAssetLibrary *library)
|
||||
{
|
||||
if (library->auth_token) {
|
||||
BLO_read_string(reader, &library->auth_token);
|
||||
}
|
||||
}
|
||||
|
||||
void BKE_preferences_asset_library_write_data(BlendWriter *writer,
|
||||
const bUserAssetLibrary *library)
|
||||
{
|
||||
if (library->auth_token) {
|
||||
writer->write_string(library->auth_token);
|
||||
}
|
||||
}
|
||||
|
||||
bUserAssetLibrary *BKE_preferences_remote_asset_library_add(UserDef *userdef,
|
||||
const char *name,
|
||||
const char *remote_url)
|
||||
|
|
|
|||
|
|
@ -4139,6 +4139,10 @@ static BHead *read_userdef(BlendFileData *bfd, FileData *fd, BHead *bhead)
|
|||
IDP_BlendDataRead(reader, &addon.prop);
|
||||
}
|
||||
|
||||
for (bUserAssetLibrary &asset_library_ref : user->asset_libraries) {
|
||||
BKE_preferences_asset_library_read_data(reader, &asset_library_ref);
|
||||
}
|
||||
|
||||
for (bUserExtensionRepo &repo_ref : user->extension_repos) {
|
||||
BKE_preferences_extension_repo_read_data(reader, &repo_ref);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1315,6 +1315,7 @@ static void write_userdef(BlendWriter *writer, const UserDef *userdef)
|
|||
|
||||
for (const bUserAssetLibrary &asset_library_ref : userdef->asset_libraries) {
|
||||
writer->write_struct(&asset_library_ref);
|
||||
BKE_preferences_asset_library_write_data(writer, &asset_library_ref);
|
||||
}
|
||||
|
||||
for (const bUserExtensionRepo &repo_ref : userdef->extension_repos) {
|
||||
|
|
|
|||
|
|
@ -184,7 +184,8 @@ static void draw_library_list(const bContext &C, ui::Layout &layout)
|
|||
ui::TreeViewBuilder::build_tree_view(C, *tree_view, layout);
|
||||
}
|
||||
|
||||
static void draw_active_library_settings(ui::Layout &layout,
|
||||
static void draw_active_library_settings(const bContext *C,
|
||||
ui::Layout &layout,
|
||||
const AnyAssetLibraryDefinition &library)
|
||||
{
|
||||
if (library.type == ASSET_LIBRARY_ESSENTIALS) {
|
||||
|
|
@ -217,6 +218,23 @@ static void draw_active_library_settings(ui::Layout &layout,
|
|||
IFACE_("Repository URL"));
|
||||
}
|
||||
layout.prop(&library_ptr, "import_method", UI_ITEM_NONE, IFACE_("Import Method"), ICON_NONE);
|
||||
|
||||
if (ui::Layout *panel = layout.panel(C, "advanced", true, IFACE_("Advanced"))) {
|
||||
panel->use_property_split_set(true);
|
||||
ui::Layout &column = panel->column(true, IFACE_("Authentication"));
|
||||
column.prop(&library_ptr, "use_auth_token", UI_ITEM_NONE, std::nullopt, ICON_NONE);
|
||||
|
||||
if (library.user_library->flag & ASSET_LIBRARY_USE_AUTH_TOKEN) {
|
||||
column.prop(&library_ptr,
|
||||
RNA_struct_find_property(&library_ptr, "auth_token"),
|
||||
RNA_NO_INDEX,
|
||||
0,
|
||||
UI_ITEM_NONE,
|
||||
IFACE_("Secret"),
|
||||
library.user_library->auth_token ? ICON_LOCKED : ICON_UNLOCKED,
|
||||
std::nullopt);
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
layout.prop(&library_ptr, "path", UI_ITEM_NONE, std::nullopt, ICON_NONE);
|
||||
|
|
@ -260,7 +278,7 @@ void userpref_asset_libraries_panel_draw(const bContext *C, Panel *panel)
|
|||
|
||||
layout.separator();
|
||||
|
||||
draw_active_library_settings(layout, libraries[U.active_asset_library]);
|
||||
draw_active_library_settings(C, layout, libraries[U.active_asset_library]);
|
||||
}
|
||||
|
||||
} // namespace blender
|
||||
|
|
|
|||
|
|
@ -58,6 +58,7 @@ enum eAssetLibrary_Flag : int {
|
|||
ASSET_LIBRARY_RELATIVE_PATH = (1 << 0),
|
||||
ASSET_LIBRARY_DISABLED = (1 << 1),
|
||||
ASSET_LIBRARY_USE_REMOTE_URL = (1 << 2),
|
||||
ASSET_LIBRARY_USE_AUTH_TOKEN = (1 << 3),
|
||||
};
|
||||
|
||||
enum class AssetAccess : int8_t {
|
||||
|
|
|
|||
|
|
@ -659,6 +659,11 @@ struct bUserAssetLibrary {
|
|||
/** Only for remote asset libraries (#ASSET_LIBRARY_USE_REMOTE_URL is set). Update using
|
||||
* #BKE_preferences_remote_asset_library_url_set() only. */
|
||||
char remote_url[/*FILE_MAX*/ 1024];
|
||||
/**
|
||||
* Secret access token for remote repositories (allocated).
|
||||
* Only use when #ASSET_LIBRARY_USE_AUTH_TOKEN is set.
|
||||
*/
|
||||
char *auth_token = nullptr;
|
||||
|
||||
short import_method = ASSET_IMPORT_PACK; /* eAssetImportMethod */
|
||||
short flag = ASSET_LIBRARY_RELATIVE_PATH; /* eAssetLibrary_Flag */
|
||||
|
|
|
|||
|
|
@ -239,6 +239,7 @@ static const EnumPropertyItem rna_enum_preferences_extension_repo_source_type_it
|
|||
# include "BLI_listbase.hh"
|
||||
# include "BLI_math_vector_c.hh"
|
||||
# include "BLI_memory_cache.hh"
|
||||
# include "BLI_rand_c.hh"
|
||||
# include "BLI_string.hh"
|
||||
# include "BLI_string_utf8.hh"
|
||||
# include "BLI_string_utils.hh"
|
||||
|
|
@ -488,6 +489,54 @@ static void rna_userdef_asset_libraries_use_online_essentials_update(bContext *C
|
|||
rna_userdef_update(CTX_data_main(C), CTX_data_scene(C), ptr);
|
||||
}
|
||||
|
||||
static void rna_userdef_asset_library_auth_token_get(PointerRNA *ptr, char *value)
|
||||
{
|
||||
bUserAssetLibrary *library = static_cast<bUserAssetLibrary *>(ptr->data);
|
||||
if (library->auth_token) {
|
||||
strcpy(value, library->auth_token);
|
||||
}
|
||||
else {
|
||||
value[0] = '\0';
|
||||
}
|
||||
}
|
||||
|
||||
static int rna_userdef_asset_library_auth_token_length(PointerRNA *ptr)
|
||||
{
|
||||
bUserAssetLibrary *library = static_cast<bUserAssetLibrary *>(ptr->data);
|
||||
return (library->auth_token) ? strlen(library->auth_token) : 0;
|
||||
}
|
||||
|
||||
static void rna_userdef_asset_library_auth_token_set(PointerRNA *ptr, const char *value)
|
||||
{
|
||||
bUserAssetLibrary *library = static_cast<bUserAssetLibrary *>(ptr->data);
|
||||
|
||||
if (library->auth_token) {
|
||||
/* Replace the existing token with random characters to avoid leaving it in memory. */
|
||||
RNG *rng = BLI_rng_new_srandom(uint(intptr_t(library->auth_token)));
|
||||
BLI_rng_get_char_n(rng, library->auth_token, strlen(library->auth_token));
|
||||
BLI_rng_free(rng);
|
||||
/* Now we can more comfortably delete the token. */
|
||||
MEM_delete(library->auth_token);
|
||||
library->auth_token = nullptr;
|
||||
}
|
||||
|
||||
if (value[0]) {
|
||||
const StringRef auth_token = StringRef{value}.trim();
|
||||
library->auth_token = BLI_strdupn(auth_token.data(), auth_token.size());
|
||||
}
|
||||
}
|
||||
|
||||
static void rna_userdef_asset_library_use_auth_token_update(bContext * /*C*/, PointerRNA *ptr)
|
||||
{
|
||||
bUserAssetLibrary *library = static_cast<bUserAssetLibrary *>(ptr->data);
|
||||
const bool use_auth_token = (library->flag & ASSET_LIBRARY_USE_AUTH_TOKEN) != 0;
|
||||
|
||||
if (!use_auth_token && library->auth_token) {
|
||||
/* Make sure the token is wiped if we are not using it. */
|
||||
rna_userdef_asset_library_auth_token_set(ptr, "");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Use sparingly as a sync may be time consuming.
|
||||
* Any change that may cause loading remote data to change behavior
|
||||
|
|
@ -7006,6 +7055,23 @@ static void rna_def_userdef_filepaths_asset_library(BlenderRNA *brna)
|
|||
RNA_def_property_boolean_sdna(prop, nullptr, "flag", ASSET_LIBRARY_USE_REMOTE_URL);
|
||||
RNA_def_property_ui_text(prop, "Use Remote", "Synchronize the asset library with a remote URL");
|
||||
RNA_def_property_clear_flag(prop, PROP_EDITABLE);
|
||||
|
||||
prop = RNA_def_property(srna, "use_auth_token", PROP_BOOLEAN, PROP_NONE);
|
||||
RNA_def_property_boolean_sdna(prop, nullptr, "flag", ASSET_LIBRARY_USE_AUTH_TOKEN);
|
||||
RNA_def_property_ui_text(
|
||||
prop, "Requires Access Token", "Asset library requires an authentication token");
|
||||
RNA_def_property_flag(prop, PROP_CONTEXT_UPDATE);
|
||||
RNA_def_property_update(prop, 0, "rna_userdef_asset_library_use_auth_token_update");
|
||||
|
||||
prop = RNA_def_property(srna, "auth_token", PROP_STRING, PROP_PASSWORD);
|
||||
RNA_def_property_ui_text(
|
||||
prop,
|
||||
"Access Token",
|
||||
"Personal authentication token, may be required by some asset libraries");
|
||||
RNA_def_property_string_funcs(prop,
|
||||
"rna_userdef_asset_library_auth_token_get",
|
||||
"rna_userdef_asset_library_auth_token_length",
|
||||
"rna_userdef_asset_library_auth_token_set");
|
||||
}
|
||||
|
||||
static void rna_def_userdef_filepaths_extension_repo(BlenderRNA *brna)
|
||||
|
|
|
|||
|
|
@ -96,6 +96,7 @@ class ListingDownloaderTest(unittest.TestCase):
|
|||
Downloader = listing_downloader.RemoteAssetListingDownloader
|
||||
dl = Downloader(
|
||||
remote_url="http://localhost/",
|
||||
auth_token="",
|
||||
local_path="/tmp/does-not-matter-we-do-not-write",
|
||||
on_update_callback=lambda downloader: None,
|
||||
on_done_callback=lambda downloader: None,
|
||||
|
|
@ -151,6 +152,7 @@ class ListingDownloaderTest(unittest.TestCase):
|
|||
Downloader = listing_downloader.RemoteAssetListingDownloader
|
||||
dl = Downloader(
|
||||
remote_url="http://localhost/",
|
||||
auth_token="",
|
||||
local_path="/tmp/does-not-matter-we-do-not-write",
|
||||
on_update_callback=lambda downloader: None,
|
||||
on_done_callback=lambda downloader: None,
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue