From 08df46d7cd555340a654af80fb1becf93cfa52bd Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Wed, 22 May 2024 20:32:23 +1000 Subject: [PATCH] Extensions: support token access for remote repositories This introduce a new "secret" per-repository property of type password as described by #121856. A token or secret may be used by some non blender.org repositories. This only shows for remote repositories and is shown in the "Add Remote Repository" popup. This commit doesn't implement sending to token to the server which will be implemented separately. Ref !121886 Co-authored-by: Dalai Felinto --- scripts/startup/bl_ui/space_userpref.py | 29 ++++++++-- source/blender/blenkernel/BKE_preferences.h | 7 +++ source/blender/blenkernel/intern/blender.cc | 8 ++- .../blender/blenkernel/intern/preferences.cc | 17 ++++++ source/blender/blenloader/intern/readfile.cc | 5 ++ source/blender/blenloader/intern/writefile.cc | 2 + .../editors/space_userpref/userpref_ops.cc | 56 +++++++++++++++++++ source/blender/makesdna/DNA_userdef_types.h | 7 +++ source/blender/makesrna/intern/rna_userdef.cc | 38 +++++++++++++ 9 files changed, 162 insertions(+), 7 deletions(-) diff --git a/scripts/startup/bl_ui/space_userpref.py b/scripts/startup/bl_ui/space_userpref.py index 380f6a625b5..e8a30887c46 100644 --- a/scripts/startup/bl_ui/space_userpref.py +++ b/scripts/startup/bl_ui/space_userpref.py @@ -2137,24 +2137,41 @@ class USERPREF_PT_extensions_repos(Panel): split.prop(active_repo, "remote_url", text="", icon='URL', placeholder="Repository URL") split = row.split() + if active_repo.use_access_token: + access_token_icon = 'LOCKED' if active_repo.access_token else 'UNLOCKED' + row = layout.row() + split = row.split(factor=0.936) + split.prop(active_repo, "access_token", icon=access_token_icon) + split = row.split() + layout.prop(active_repo, "use_sync_on_startup") layout_header, layout_panel = layout.panel("advanced", default_closed=True) layout_header.label(text="Advanced") - if layout_panel: - layout_panel.prop(active_repo, "use_custom_directory") - row = layout_panel.row() + if layout_panel: + layout_panel.use_property_split = True + + col = layout_panel.column(align=False, heading="Custom Directory") + row = col.row(align=True) + sub = row.row(align=True) + sub.prop(active_repo, "use_custom_directory", text="") + sub = sub.row(align=True) + sub.active = active_repo.use_custom_directory if active_repo.use_custom_directory: if active_repo.custom_directory == "": - row.alert = True - row.prop(active_repo, "custom_directory", text="") + sub.alert = True + sub.prop(active_repo, "custom_directory", text="") else: # Show the read-only directory property. # Apart from being consistent with the custom directory UI, # prefer a read-only property over a label because this is not necessarily # valid UTF-8 which will raise a Python exception when passed in as text. - row.prop(active_repo, "directory", text="") + sub.prop(active_repo, "directory", text="") + + if active_repo.use_remote_url: + row = layout_panel.row(align=True, heading="Authentication") + row.prop(active_repo, "use_access_token") layout_panel.prop(active_repo, "use_cache") layout_panel.separator() diff --git a/source/blender/blenkernel/BKE_preferences.h b/source/blender/blenkernel/BKE_preferences.h index b2241e9bfe2..2c736ba8dc7 100644 --- a/source/blender/blenkernel/BKE_preferences.h +++ b/source/blender/blenkernel/BKE_preferences.h @@ -15,6 +15,8 @@ extern "C" { #include "BLI_compiler_attrs.h" #include "BLI_sys_types.h" +struct BlendWriter; +struct BlendReader; struct UserDef; struct bUserExtensionRepo; struct bUserAssetLibrary; @@ -121,6 +123,11 @@ void BKE_preferences_extension_remote_to_name(const char *remote_url, char name[ int BKE_preferences_extension_repo_get_index(const UserDef *userdef, const bUserExtensionRepo *repo); +void BKE_preferences_extension_repo_read_data(struct BlendDataReader *reader, + bUserExtensionRepo *repo); +void BKE_preferences_extension_repo_write_data(struct BlendWriter *writer, + const bUserExtensionRepo *repo); + /** \} */ /* -------------------------------------------------------------------- */ diff --git a/source/blender/blenkernel/intern/blender.cc b/source/blender/blenkernel/intern/blender.cc index 482e8a6beb8..930f7fd0120 100644 --- a/source/blender/blenkernel/intern/blender.cc +++ b/source/blender/blenkernel/intern/blender.cc @@ -340,7 +340,13 @@ void BKE_blender_userdef_data_free(UserDef *userdef, bool clear_fonts) BLI_freelistN(&userdef->autoexec_paths); BLI_freelistN(&userdef->script_directories); BLI_freelistN(&userdef->asset_libraries); - BLI_freelistN(&userdef->extension_repos); + + LISTBASE_FOREACH_MUTABLE (bUserExtensionRepo *, repo_ref, &userdef->extension_repos) { + MEM_SAFE_FREE(repo_ref->access_token); + MEM_freeN(repo_ref); + } + BLI_listbase_clear(&userdef->asset_shelves_settings); + LISTBASE_FOREACH_MUTABLE (bUserAssetShelfSettings *, settings, &userdef->asset_shelves_settings) { BKE_asset_catalog_path_list_free(settings->enabled_catalog_paths); diff --git a/source/blender/blenkernel/intern/preferences.cc b/source/blender/blenkernel/intern/preferences.cc index a715929d757..879802bc608 100644 --- a/source/blender/blenkernel/intern/preferences.cc +++ b/source/blender/blenkernel/intern/preferences.cc @@ -25,6 +25,8 @@ #include "BLT_translation.hh" +#include "BLO_read_write.hh" + #include "DNA_asset_types.h" #include "DNA_defaults.h" #include "DNA_userdef_types.h" @@ -404,6 +406,21 @@ int BKE_preferences_extension_repo_get_index(const UserDef *userdef, { return BLI_findindex(&userdef->extension_repos, repo); } + +void BKE_preferences_extension_repo_read_data(BlendDataReader *reader, bUserExtensionRepo *repo) +{ + if (repo->access_token) { + BLO_read_string(reader, &repo->access_token); + } +} + +void BKE_preferences_extension_repo_write_data(BlendWriter *writer, const bUserExtensionRepo *repo) +{ + if (repo->access_token) { + BLO_write_string(writer, repo->access_token); + } +} + /** \} */ /* -------------------------------------------------------------------- */ diff --git a/source/blender/blenloader/intern/readfile.cc b/source/blender/blenloader/intern/readfile.cc index acb0b83ccc6..89ab186f392 100644 --- a/source/blender/blenloader/intern/readfile.cc +++ b/source/blender/blenloader/intern/readfile.cc @@ -82,6 +82,7 @@ #include "BKE_node.hh" /* for tree type defines */ #include "BKE_object.hh" #include "BKE_packedFile.h" +#include "BKE_preferences.h" #include "BKE_report.hh" #include "BKE_scene.hh" #include "BKE_screen.hh" @@ -3424,6 +3425,10 @@ static BHead *read_userdef(BlendFileData *bfd, FileData *fd, BHead *bhead) IDP_BlendDataRead(reader, &addon->prop); } + LISTBASE_FOREACH (bUserExtensionRepo *, repo_ref, &user->extension_repos) { + BKE_preferences_extension_repo_read_data(reader, repo_ref); + } + LISTBASE_FOREACH (bUserAssetShelfSettings *, shelf_settings, &user->asset_shelves_settings) { BKE_asset_catalog_path_list_blend_read_data(reader, shelf_settings->enabled_catalog_paths); } diff --git a/source/blender/blenloader/intern/writefile.cc b/source/blender/blenloader/intern/writefile.cc index bfa31405a0a..4d53e2d3553 100644 --- a/source/blender/blenloader/intern/writefile.cc +++ b/source/blender/blenloader/intern/writefile.cc @@ -114,6 +114,7 @@ #include "BKE_main_namemap.hh" #include "BKE_node.hh" #include "BKE_packedFile.h" +#include "BKE_preferences.h" #include "BKE_report.hh" #include "BKE_workspace.hh" @@ -934,6 +935,7 @@ static void write_userdef(BlendWriter *writer, const UserDef *userdef) LISTBASE_FOREACH (const bUserExtensionRepo *, repo_ref, &userdef->extension_repos) { BLO_write_struct(writer, bUserExtensionRepo, repo_ref); + BKE_preferences_extension_repo_write_data(writer, repo_ref); } LISTBASE_FOREACH ( const bUserAssetShelfSettings *, shelf_settings, &userdef->asset_shelves_settings) diff --git a/source/blender/editors/space_userpref/userpref_ops.cc b/source/blender/editors/space_userpref/userpref_ops.cc index 026fcd14c29..1c81614436a 100644 --- a/source/blender/editors/space_userpref/userpref_ops.cc +++ b/source/blender/editors/space_userpref/userpref_ops.cc @@ -275,9 +275,11 @@ static int preferences_extension_repo_add_exec(bContext *C, wmOperator *op) char name[sizeof(bUserExtensionRepo::name)] = ""; char remote_url[sizeof(bUserExtensionRepo::remote_url)] = ""; + char *access_token = nullptr; char custom_directory[sizeof(bUserExtensionRepo::custom_dirpath)] = ""; const bool use_custom_directory = RNA_boolean_get(op->ptr, "use_custom_directory"); + const bool use_access_token = RNA_boolean_get(op->ptr, "use_access_token"); const bool use_sync_on_startup = RNA_boolean_get(op->ptr, "use_sync_on_startup"); if (use_custom_directory) { RNA_string_get(op->ptr, "custom_directory", custom_directory); @@ -286,6 +288,12 @@ static int preferences_extension_repo_add_exec(bContext *C, wmOperator *op) if (repo_type == bUserExtensionRepoAddType::Remote) { RNA_string_get(op->ptr, "remote_url", remote_url); + + if (use_access_token) { + if (RNA_string_length(op->ptr, "access_token")) { + access_token = RNA_string_get_alloc(op->ptr, "access_token", nullptr, 0, nullptr); + } + } } /* Setup the name using the following logic: @@ -353,6 +361,13 @@ static int preferences_extension_repo_add_exec(bContext *C, wmOperator *op) if (repo_type == bUserExtensionRepoAddType::Remote) { STRNCPY(new_repo->remote_url, remote_url); new_repo->flag |= USER_EXTENSION_REPO_FLAG_USE_REMOTE_URL; + + if (use_access_token) { + new_repo->flag |= USER_EXTENSION_REPO_FLAG_USE_ACCESS_TOKEN; + } + if (access_token) { + new_repo->access_token = access_token; + } } /* Activate new repository in the UI for further setup. */ @@ -401,6 +416,23 @@ static void preferences_extension_repo_add_ui(bContext * /*C*/, wmOperator *op) case bUserExtensionRepoAddType::Remote: { uiItemR(layout, op->ptr, "remote_url", UI_ITEM_R_IMMEDIATE, nullptr, ICON_NONE); uiItemR(layout, op->ptr, "use_sync_on_startup", UI_ITEM_NONE, nullptr, ICON_NONE); + + uiItemS_ex(layout, 0.2f, LayoutSeparatorType::Line); + + const bool use_access_token = RNA_boolean_get(ptr, "use_access_token"); + const int token_icon = (use_access_token && RNA_string_length(op->ptr, "access_token")) ? + ICON_LOCKED : + ICON_UNLOCKED; + + uiLayout *row = uiLayoutRowWithHeading(layout, true, IFACE_("Authentication")); + uiItemR(row, op->ptr, "use_access_token", UI_ITEM_NONE, nullptr, ICON_NONE); + uiLayout *col = uiLayoutRow(layout, false); + uiLayoutSetActive(col, use_access_token); + /* Use "immediate" flag to refresh the icon. */ + uiItemR(col, op->ptr, "access_token", UI_ITEM_R_IMMEDIATE, nullptr, token_icon); + + uiItemS_ex(layout, 0.2f, LayoutSeparatorType::Line); + break; } case bUserExtensionRepoAddType::Local: { @@ -479,6 +511,30 @@ static void PREFERENCES_OT_extension_repo_add(wmOperatorType *ot) RNA_def_property_flag(prop, PROP_SKIP_SAVE); } + { /* Use Access Token. */ + const char *prop_id = "use_access_token"; + PropertyRNA *prop_ref = RNA_struct_type_find_property(type_ref, prop_id); + PropertyRNA *prop = RNA_def_boolean(ot->srna, + prop_id, + false, + RNA_property_ui_name_raw(prop_ref), + RNA_property_ui_description_raw(prop_ref)); + RNA_def_property_flag(prop, PROP_SKIP_SAVE); + } + + { /* Access Token (dynamic length). */ + const char *prop_id = "access_token"; + PropertyRNA *prop_ref = RNA_struct_type_find_property(type_ref, prop_id); + PropertyRNA *prop = RNA_def_string(ot->srna, + prop_id, + nullptr, + 0, + RNA_property_ui_name_raw(prop_ref), + RNA_property_ui_description_raw(prop_ref)); + RNA_def_property_flag(prop, PROP_SKIP_SAVE); + RNA_def_property_subtype(prop, PROP_PASSWORD); + } + { /* Check for Updated on Startup. */ const char *prop_id = "use_sync_on_startup"; PropertyRNA *prop_ref = RNA_struct_type_find_property(type_ref, prop_id); diff --git a/source/blender/makesdna/DNA_userdef_types.h b/source/blender/makesdna/DNA_userdef_types.h index 05d5a2e2751..9d9d7c6393a 100644 --- a/source/blender/makesdna/DNA_userdef_types.h +++ b/source/blender/makesdna/DNA_userdef_types.h @@ -636,6 +636,12 @@ typedef struct bUserExtensionRepo { */ char module[48]; + /** + * Secret access token for remote repositories (allocated). + * Only use when #USER_EXTENSION_REPO_FLAG_USE_ACCESS_TOKEN is set. + */ + char *access_token; + /** * The "local" directory where extensions are stored. * When unset, use `{BLENDER_USER_EXTENSIONS}/{bUserExtensionRepo::module}`. @@ -654,6 +660,7 @@ typedef enum eUserExtensionRepo_Flag { USER_EXTENSION_REPO_FLAG_USE_CUSTOM_DIRECTORY = 1 << 2, USER_EXTENSION_REPO_FLAG_USE_REMOTE_URL = 1 << 3, USER_EXTENSION_REPO_FLAG_SYNC_ON_STARTUP = 1 << 4, + USER_EXTENSION_REPO_FLAG_USE_ACCESS_TOKEN = 1 << 5, } eUserExtensionRepo_Flag; typedef struct SolidLight { diff --git a/source/blender/makesrna/intern/rna_userdef.cc b/source/blender/makesrna/intern/rna_userdef.cc index de50daaa4d9..29ad3c4ff97 100644 --- a/source/blender/makesrna/intern/rna_userdef.cc +++ b/source/blender/makesrna/intern/rna_userdef.cc @@ -385,6 +385,32 @@ static int rna_userdef_extension_repo_directory_length(PointerRNA *ptr) return BKE_preferences_extension_repo_dirpath_get(repo, dirpath, sizeof(dirpath)); } +static void rna_userdef_extension_repo_access_token_get(PointerRNA *ptr, char *value) +{ + const bUserExtensionRepo *repo = (bUserExtensionRepo *)ptr->data; + if (repo->access_token) { + strcpy(value, repo->access_token); + } + else { + value[0] = '\0'; + } +} + +static int rna_userdef_extension_repo_access_token_length(PointerRNA *ptr) +{ + const bUserExtensionRepo *repo = (bUserExtensionRepo *)ptr->data; + return (repo->access_token) ? strlen(repo->access_token) : 0; +} + +static void rna_userdef_extension_repo_access_token_set(PointerRNA *ptr, const char *value) +{ + bUserExtensionRepo *repo = (bUserExtensionRepo *)ptr->data; + if (repo->access_token) { + MEM_freeN(repo->access_token); + } + repo->access_token = value[0] ? BLI_strdup(value) : nullptr; +} + static void rna_userdef_extension_repo_generic_flag_set_impl(PointerRNA *ptr, const bool value, const int flag) @@ -6693,6 +6719,14 @@ static void rna_def_userdef_filepaths_extension_repo(BlenderRNA *brna) RNA_def_property_translation_context(prop, BLT_I18NCONTEXT_EDITOR_FILEBROWSER); RNA_def_property_update(prop, 0, "rna_userdef_update"); + prop = RNA_def_property(srna, "access_token", PROP_STRING, PROP_PASSWORD); + RNA_def_property_ui_text( + prop, "Secret", "Personal access token, may be required by some repositories"); + RNA_def_property_string_funcs(prop, + "rna_userdef_extension_repo_access_token_get", + "rna_userdef_extension_repo_access_token_length", + "rna_userdef_extension_repo_access_token_set"); + /* NOTE(@ideasman42): this is intended to be used by a package manger component * which is not yet integrated. */ prop = RNA_def_property(srna, "use_cache", PROP_BOOLEAN, PROP_NONE); @@ -6711,6 +6745,10 @@ static void rna_def_userdef_filepaths_extension_repo(BlenderRNA *brna) RNA_def_property_ui_text( prop, "Check for Updates on Startup", "Allow Blender to check for updates upon launch"); + prop = RNA_def_property(srna, "use_access_token", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, nullptr, "flag", USER_EXTENSION_REPO_FLAG_USE_ACCESS_TOKEN); + RNA_def_property_ui_text(prop, "Requires Access Token", "Repository requires an access token"); + prop = RNA_def_property(srna, "use_custom_directory", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna( prop, nullptr, "flag", USER_EXTENSION_REPO_FLAG_USE_CUSTOM_DIRECTORY);