diff options
author | Pavel Vomacka <pvomacka@redhat.com> | 2016-10-05 12:06:27 +0200 |
---|---|---|
committer | Martin Basti <mbasti@redhat.com> | 2017-03-14 10:40:10 +0100 |
commit | 39d7ef3de4b0345274b4b8e8f6918e3b714879ad (patch) | |
tree | a256c51f2c0bcfa46319c7eb3e0dc3a3f589b61e | |
parent | 587b7324fb1f6899deb151c30662362c18c5258e (diff) | |
download | freeipa-39d7ef3de4b0345274b4b8e8f6918e3b714879ad.tar.gz freeipa-39d7ef3de4b0345274b4b8e8f6918e3b714879ad.tar.xz freeipa-39d7ef3de4b0345274b4b8e8f6918e3b714879ad.zip |
WebUI: add vault management
Add vault management into WebUI, there are some constraints:
- There is no crypto library so Symmetric and Assymetric vaults
are not supported in WebUI. Also retrieving or archiving data
is not supported.
- There aren't any container support right now
Supported is:
- Browsing vaults
- Adding Standard vaults (users, service, shared)
- Removing vaults
- Adding and removing owners
- Adding and removing members
https://fedorahosted.org/freeipa/ticket/5426
Reviewed-By: Martin Basti <mbasti@redhat.com>
Reviewed-By: Petr Vobornik <pvoborni@redhat.com>
-rw-r--r-- | install/ui/src/freeipa/app.js | 3 | ||||
-rw-r--r-- | install/ui/src/freeipa/ipa.js | 12 | ||||
-rw-r--r-- | install/ui/src/freeipa/navigation/menu_spec.js | 53 | ||||
-rw-r--r-- | install/ui/src/freeipa/vault.js | 819 | ||||
-rw-r--r-- | install/ui/test/data/ipa_init.json | 25 | ||||
-rw-r--r-- | ipaserver/plugins/internal.py | 38 |
6 files changed, 948 insertions, 2 deletions
diff --git a/install/ui/src/freeipa/app.js b/install/ui/src/freeipa/app.js index 5e70dbae3..093737b8f 100644 --- a/install/ui/src/freeipa/app.js +++ b/install/ui/src/freeipa/app.js @@ -51,11 +51,12 @@ define([ './selinux', './serverconfig', './service', + './stageuser', './sudo', './trust', './topology', './user', - './stageuser', + './vault', 'dojo/domReady!' ],function(app_container) { return app_container; diff --git a/install/ui/src/freeipa/ipa.js b/install/ui/src/freeipa/ipa.js index 46b033a00..0ddbd0744 100644 --- a/install/ui/src/freeipa/ipa.js +++ b/install/ui/src/freeipa/ipa.js @@ -240,6 +240,18 @@ var IPA = function () { } })); + batch.add_command(rpc.command({ + entity: 'vaultconfig', + method: 'show', + retry: false, + on_success: function(data, text_status, xhr) { + that.vault_enabled = true; + }, + on_error: function(xhr, text_status, error_thrown) { + that.vault_enabled = false; + } + })); + batch.execute(); }; diff --git a/install/ui/src/freeipa/navigation/menu_spec.js b/install/ui/src/freeipa/navigation/menu_spec.js index 8d13da106..4f78e4bf9 100644 --- a/install/ui/src/freeipa/navigation/menu_spec.js +++ b/install/ui/src/freeipa/navigation/menu_spec.js @@ -215,6 +215,32 @@ var nav = {}; { entity: 'dnsserver' }, { entity: 'dnsconfig' } ] + }, + { + name: 'vault', + entity: 'vault', + facet: 'search', + children: [ + { + entity: 'vault', + facet: 'user_search', + hidden: true + }, + { + entity: 'vault', + facet: 'service_search', + hidden: true + }, + { + entity: 'vault', + facet: 'shared_search', + hidden: true + }, + { + entity: 'vaultconfig', + hidden: true + } + ] } ] }, @@ -298,7 +324,32 @@ nav.self_service = { name: 'self-service', items: [ { entity: 'user' }, - { entity: 'otptoken' } + { entity: 'otptoken' }, + { + entity: 'vault', + facet: 'search', + children: [ + { + entity: 'vault', + facet: 'user_search', + hidden: true + }, + { + entity: 'vault', + facet: 'service_search', + hidden: true + }, + { + entity: 'vault', + facet: 'shared_search', + hidden: true + }, + { + entity: 'vaultconfig', + hidden: true + } + ] + } ] }; diff --git a/install/ui/src/freeipa/vault.js b/install/ui/src/freeipa/vault.js new file mode 100644 index 000000000..b764bfbe3 --- /dev/null +++ b/install/ui/src/freeipa/vault.js @@ -0,0 +1,819 @@ +// +// Copyright (C) 2017 FreeIPA Contributors see COPYING for license +// + +define([ + 'dojo/on', + './ipa', + './jquery', + './phases', + './menu', + './rpc', + './reg', + './text', + './widget'], + function(on, IPA, $, phases, menu, rpc, reg, text, widget) { + +/** + * Vault module + * @class vault + * @alternateClassName IPA.vault + * @singleton + */ +var vault = IPA.vault = { + + search_facet_group: { + name: 'vaults', + facets: { + vault_search: 'vault_search', + user_search: 'vault_user_search', + service_search: 'vault_service_search', + shared_search: 'vault_shared_search', + vaultconfig_details: 'vaultconfig_details' + } + } +}; + + +/** + * Create general specification of "* Vaults" details facets. + */ +var make_vaults_details_page_spec = function() { + return { + $type: 'details', + $factory: vault.custom_details_facet, + update_command_name: 'mod_internal', + disable_facet_tabs: true, + sections: [ + { + name: 'global_info', + layout_ccs_class: 'col-sm-12', + fields: [ + 'cn', + 'description', + { + name: 'ipavaulttype', + read_only: true + }, + { + $type: 'text', + name: 'ipavaultsalt', + visible: false + }, + { + $type: 'pub_key', + name: 'ipavaultpublickey', + visible: false + } + ] + }, + { + $factory: IPA.section, + name: 'divider', + layout_css_class: 'col-sm-12', + fields: [] + }, + { + $factory: IPA.section, + name: 'members', + label: '@i18n:objects.vault.members', + fields: [ + { + $type: 'association_table', + id: 'member_user_cn', + name: 'member_user', + acl_param: 'member', + columns: [ + { + name: 'member_user', + label: '@i18n:objects.vault.user' + } + ] + }, + { + $type: 'association_table', + id: 'member_group_cn', + name: 'member_group', + other_entity: 'group', + acl_param: 'member', + columns: [ + { + name: 'member_group', + label: '@i18n:objects.vault.group' + } + ] + }, + { + $type: 'association_table', + id: 'member_service_cn', + name: 'member_service', + other_entity: 'service', + other_option_name: 'services', + acl_param: 'member', + columns: [ + { + name: 'member_service', + label: '@i18n:objects.vault.service' + } + ] + } + ] + }, + { + $factory: IPA.section, + name: 'owners', + label: '@i18n:objects.vault.owners', + fields: [ + { + $type: 'association_table', + id: 'owner_user_cn', + name: 'owner_user', + add_method: 'add_owner', + remove_method: 'remove_owner', + other_entity: 'user', + acl_param: 'owner', + columns: [ + { + name: 'owner_user', + label: '@i18n:objects.vault.user' + } + ] + }, + { + $type: 'association_table', + id: 'owner_group_cn', + name: 'owner_group', + add_method: 'add_owner', + remove_method: 'remove_owner', + other_entity: 'group', + acl_param: 'owner', + columns: [ + { + name: 'owner_group', + label: '@i18n:objects.vault.group' + } + ] + }, + { + $type: 'association_table', + id: 'owner_service_cn', + name: 'owner_service', + add_method: 'add_owner', + remove_method: 'remove_owner', + other_entity: 'service', + other_option_name: 'services', + acl_param: 'owner', + columns: [ + { + name: 'owner_service', + label: '@i18n:objects.vault.service' + } + ] + } + ] + } + ] + }; +}; + + +/** + * Create entity spec for whole vaults and also spec for search facet, adder + * and deleter dialog. + */ +var make_my_vault_spec = function() { + var entity = { + name: 'vault', + facets: [ + { + $type: 'search', + tab_label: '@i18n:objects.vault.my_vaults_title', + facet_groups: [vault.search_facet_group], + facet_group: 'vaults', + disable_facet_tabs: false, + search_all_entries: true, + tabs_in_sidebar: true, + custom_actions: [ + { + $type: 'add', + hide_cond: [] + }, + { + $type: 'batch_remove', + hide_cond: [] + } + ], + columns: [ + 'cn', + 'ipavaulttype' + ], + policies: [ + vault.config_sidebar_policy + ] + } + ], + adder_dialog: { + $factory: vault.custom_adder_dialog, + name: 'add', + method: 'add_internal', + policies: [ + { $factory: vault.adder_policy } + ] + }, + deleter_dialog: { + // Each parametr is present only in one facet. It could cause errors + // in case that table on each facet gather more columns with these names. + // I.e. facet with user vaults get column with name 'service', then + // the value of 'service' column will be also added to command options. + additional_table_attrs: ['username', 'service', 'shared'] + } + }; + + /** + * This function extends general details facet - so the same declaration + * of facet (which would differ only in several lines) + * should not be present six times. + */ + var update_facet_spec = function(facet, facet_type) { + facet.sections[0].fields.push(facet_type); + facet.refresh_attribute = facet_type; + facet.update_attribute = facet_type; + var user_members = facet.sections[2].fields[0]; + var group_members = facet.sections[2].fields[1]; + var service_members = facet.sections[2].fields[2]; + var user_owners = facet.sections[3].fields[0]; + var group_owners = facet.sections[3].fields[1]; + var service_owners = facet.sections[3].fields[2]; + + var attributes = { + refresh_attribute: facet_type, + additional_add_del_field: facet_type + }; + + $.extend(user_members, attributes); + $.extend(user_owners, attributes); + $.extend(group_members, attributes); + $.extend(group_owners, attributes); + $.extend(service_members, attributes); + $.extend(service_owners, attributes); + }; + + // Create details page for my vauls: + var details_spec = make_vaults_details_page_spec(); + entity.facets.push(details_spec); + + // Create details page for user vaults and modify it + details_spec = make_vaults_details_page_spec(); + + details_spec.name = 'vault_user'; + update_facet_spec(details_spec, 'username'); + details_spec.redirect_info = { + facet: 'user_search' + }; + + entity.facets.push(details_spec); + + // Create details page for service vaults and modify it + details_spec = make_vaults_details_page_spec(); + + details_spec.name = 'vault_service'; + update_facet_spec(details_spec, 'service'); + details_spec.redirect_info = { + facet: 'service_search' + }; + + entity.facets.push(details_spec); + + // Create details page for shared vaults and modify it + details_spec = make_vaults_details_page_spec(); + + details_spec.name = 'vault_shared'; + update_facet_spec(details_spec, 'shared'); + details_spec.redirect_info = { + facet: 'shared_search' + }; + + entity.facets.push(details_spec); + + return entity; +}; + + +vault.custom_details_facet = function(spec) { + spec = spec || {}; + + var that = IPA.details_facet(spec); + + that.load = function(data) { + that.details_facet_load(data); + + // show fields according to the type of vault + + var type_f = that.fields.get_field('ipavaulttype'); + var type = type_f.value[0]; + var salt_w = that.fields.get_field('ipavaultsalt').widget; + var pub_key_w = that.fields.get_field('ipavaultpublickey').widget; + + if (type === 'symmetric') { + pub_key_w.set_visible(false); + salt_w.set_visible(true); + } else if (type === 'asymmetric') { + pub_key_w.set_visible(true); + salt_w.set_visible(false); + } else { + pub_key_w.set_visible(false); + salt_w.set_visible(false); + } + }; + + return that; +}; + + +vault.public_key_widget = function(spec) { + spec = spec || {}; + + var that = IPA.sshkey_widget(spec); + + that.set_user_value = function(value) { + + var previous = that.key; + that.key = value; + that.update_link(); + + if (value !== previous) { + that.value_changed.notify([], that); + that.emit('value-change', { source: that }); + } + }; + + that.update = function(value) { + var key = value[0]; + + if (key) that.key = key; + + if (that.key && that.key !== '') { + that.originally_set = true; + that.original_key = that.key; + } + that.update_link(); + that.on_value_changed(value); + }; + + that.get_status = function() { + + var status = ''; + var value = that.key; + + if (that.original_key) { + + if (value !== that.original_key) { + if (value === '') { + status = text.get('@i18n:objects.publickey.status_mod_ns'); + } else { + status = text.get('@i18n:objects.publickey.status_mod_s'); + } + } else { + // f00c is code of check icon + var decimal_check_i = parseInt('f00c', 16); + status = String.fromCharCode(decimal_check_i); + } + } else { + if (!value || value === '') { + status = text.get('@i18n:objects.publickey.status_new_ns'); + } else { + status = text.get('@i18n:objects.publickey.status_new_ns'); + } + } + + return status; + }; + + that.create_edit_dialog = function() { + + var writable = that.is_writable(); + + var dialog = IPA.dialog({ + name: 'pubkey-edit-dialog', + title: '@i18n:objects.publickey.set_dialog_title', + width: 500, + height: 380 + }); + + dialog.message = text.get('@i18n:objects.publickey.set_dialog_help'); + + dialog.create_button({ + name: 'update', + label: '@i18n:buttons.set', + click: function() { + var value = dialog.textarea.val(); + that.set_user_value(value); + dialog.close(); + } + }); + + dialog.create_button({ + name: 'cancel', + label: '@i18n:buttons.cancel', + click: function() { + dialog.close(); + } + }); + + dialog.create_content = function() { + + dialog.container.append(dialog.message); + + dialog.textarea = $('<textarea/>', { + 'class': 'certificate', + disabled: !that.enabled + }).appendTo(dialog.container); + + var key = that.key || ''; + dialog.textarea.val(key); + }; + + return dialog; + }; + + return that; +}; + + +/** + * Adder policy handles realtime showing and hiding fields when user switch + * between User/Service/Shared vault in adder dialog. + * + * @extends IPA.facet_policy + */ +vault.adder_policy = function(spec) { + + var that = IPA.facet_policy(spec); + + that.init = function() { + var type_f = that.container.fields.get_field('type'); + on(type_f, 'value-change', that.on_type_change); + }; + + that.on_type_change = function() { + var type_f = that.container.fields.get_field('type'); + var user_f = that.container.fields.get_field('username'); + var service_f = that.container.fields.get_field('service'); + var mode = type_f.get_value()[0]; + var user = true; + var service = true; + + if (mode === 'user') service = false; + else if (mode === 'service') user = false; + else if (mode === 'shared') user = service = false; + + user_f.set_enabled(user); + user_f.widget.set_visible(user); + service_f.set_enabled(service); + service_f.widget.set_visible(service); + }; + + return that; +}; + + +/** + * Custom adder dialog. + * + * @extends IPA.entity_adder_dialog + */ +vault.custom_adder_dialog = function(spec) { + spec = spec || {}; + + spec.sections = spec.sections || []; + + var section_warn_arch_ret= { + show_header: false, + name: 'warning_ar', + fields: [ + { + field: false, + $type: 'html', + name: 'warn_arch_ret' + } + ], + layout: { + $factory: widget.fluid_layout, + widget_cls: "col-sm-12 controls", + label_cls: "hide" + } + }; + + var section_f = { + show_header: false, + fields: [ + { + $type: 'radio', + name: 'type', + flags: ['no_command'], + label: '@i18n:objects.vault.type', + options: [ + { + value: 'user', + label: '@i18n:objects.vault.user' + }, + { + value: 'service', + label: '@i18n:objects.vault.service' + }, + { + value: 'shared', + label: '@i18n:objects.vault.shared' + } + ] + }, + { + $type: 'entity_select', + name: 'username', + other_entity: 'user', + other_field: 'uid' + }, + { + $type: 'entity_select', + name: 'service', + other_entity: 'service', + other_field: 'krbprincipalname' + }, + 'cn', + 'description', + { + $type: 'radio', + name: 'ipavaulttype', + default_value: 'standard', + read_only: true, + options: [ + { + value: 'standard', + label: '@i18n:objects.vault.standard_type' + }, + { + label: '@i18n:objects.vault.symmetric_type' + }, + { + label: '@i18n:objects.vault.asymmetric_type' + } + ], + tooltip: "@i18n:objects.vault.type_tooltip" + } + ] + }; + + var section_warn_standard = { + name: 'warning_st', + fields: [ + { + field: false, + $type: 'html', + name: 'warn_standard' + } + ], + layout: { + $factory: widget.fluid_layout, + widget_cls: "col-sm-12 controls", + label_cls: "hide" + } + }; + + spec.sections.push(section_warn_arch_ret); + spec.sections.push(section_f); + spec.sections.push(section_warn_standard); + + var that = IPA.entity_adder_dialog(spec); + + that.create_add_command = function(record) { + var command = that.entity_adder_dialog_create_add_command(record); + + var type_f = that.fields.get_field('type'); + var type = type_f.save()[0]; + + if (type === 'shared') command.set_option(type, true); + + return command; + }; + + that.create_content = function() { + var warn_arch_ret_w = that.widgets.get_widget('warning_ar.warn_arch_ret'); + var warn_st_w = that.widgets.get_widget('warning_st.warn_standard'); + + var warn_arch_text = text.get('@i18n:objects.vault.add_warn_arch_ret'); + var warn_st_text = text.get('@i18n:objects.vault.add_warn_standard'); + + var warn_arch_ret = IPA.alert_helper.create_alert('arch', warn_arch_text); + var warn_standard = IPA.alert_helper.create_alert('standard', + warn_st_text); + + warn_st_w.html = IPA.alert_helper.render_alert(warn_standard); + warn_arch_ret_w.html = IPA.alert_helper.render_alert(warn_arch_ret); + + that.entity_adder_dialog_create_content(); + + var facet_name = that.entity.facet.name; + facet_name = facet_name.substr(0, facet_name.indexOf('_')); + + var type_f = that.fields.get_field('type'); + type_f.set_pristine_value([facet_name]); + + if (IPA.is_selfservice) type_f.set_writable(false); + }; + + return that; +}; + + +/** + * Creates specification of search facet for User Vaults + */ +var make_user_vault_search_spec = function() { + return { + $type: 'search', + entity: 'vault', + managed_entity: 'vault', + name: 'user_search', + tab_label: '@i18n:objects.vault.user_vaults_title', + label: '@i18n:objects.vault.user_vaults_title', + facet_groups: [vault.search_facet_group], + facet_group: 'vaults', + custom_actions: [ + { + $type: 'add', + hide_cond: [] + }, + { + $type: 'batch_remove', + hide_cond: [] + } + ], + additional_navigation_arguments: ['username'], + show_values_with_dup_key: true, + details_facet: 'vault_user', + show_command_additional_attr: 'username', + disable_facet_tabs: false, + tabs_in_sidebar: true, + command_options: { + 'users': true + }, + columns: [ + 'cn', + 'username', + 'ipavaulttype' + ], + policies: [ + vault.config_sidebar_policy + ] + }; +}; + + +var make_service_vault_spec = function() { + return { + $type: 'search', + entity: 'vault', + managed_entity: 'vault', + name: 'service_search', + tab_label: '@i18n:objects.vault.service_vaults_title', + label: '@i18n:objects.vault.service_vaults_title', + facet_groups: [vault.search_facet_group], + facet_group: 'vaults', + additional_navigation_arguments: ['service'], + show_values_with_dup_key: true, + details_facet: 'vault_service', + show_command_additional_attr: 'service', + disable_facet_tabs: false, + tabs_in_sidebar: true, + command_options: { + 'services': true + }, + columns: [ + 'cn', + 'service', + 'ipavaulttype' + ], + policies: [ + vault.config_sidebar_policy + ] + }; +}; + + +var make_shared_vault_spec = function() { + return { + $type: 'search', + entity: 'vault', + managed_entity: 'vault', + tab_label: '@i18n:objects.vault.shared_vaults_title', + name: 'shared_search', + label: '@i18n:objects.vault.shared_vaults_title', + facet_groups: [vault.search_facet_group], + facet_group: 'vaults', + additional_navigation_arguments: ['shared'], + show_values_with_dup_key: true, + show_command_additional_attr: 'shared', + details_facet: 'vault_shared', + disable_facet_tabs: false, + tabs_in_sidebar: true, + command_options: { + 'shared': true + }, + columns: [ + 'cn', + 'shared', + 'ipavaulttype' + ], + policies: [ + vault.config_sidebar_policy + ] + }; +}; + + +var make_vaultconfig_spec = function() { + return { + name: 'vaultconfig', + facets: [ + { + $type: 'details', + label: '@i18n:objects.vault.config_title', + tab_label: '@i18n:objects.vault.config_title', + facet_groups: [vault.search_facet_group], + facet_group: 'vaults', + disable_facet_tabs: false, + tabs_in_sidebar: true, + check_rights: false, + no_update: true, + fields: [ + 'kra_server_server', + { + $type: 'textarea', + name: 'transport_cert', + read_only: true, + style: { + width: '550px', + height: '350px' + } + } + ], + policies: [ + vault.config_sidebar_policy + ] + } + ] + }; +}; + + +vault.config_sidebar_policy = function(spec) { + + var that = IPA.facet_policy(spec); + + that.post_create = function(data) { + if (IPA.is_selfservice && that.container && + that.container.tabs_in_sidebar) { + var header = that.container.header; + + if (header) header.tabs_widget.hide_tab('vaultconfig_details'); + } + }; + + return that; +}; + + +vault.remove_vault_menu_item = function() { + if (!IPA.vault_enabled) { + menu.remove_item('network_services/vault'); + } +}; + +vault.my_vault_spec = make_my_vault_spec(); + +vault.user_vault_search_spec = make_user_vault_search_spec(); + +vault.service_vault_spec = make_service_vault_spec(); + +vault.shared_vault_spec = make_shared_vault_spec(); + +vault.vaultconfig_spec = make_vaultconfig_spec(); + +vault.register = function() { + var e = reg.entity; + var fa = reg.facet; + var w = reg.widget; + + w.register('pub_key', vault.public_key_widget); + e.register({type: 'vault', spec: vault.my_vault_spec}); + e.register({type: 'vaultconfig', spec: vault.vaultconfig_spec}); + fa.register_from_spec('vault_user_search', vault.user_vault_search_spec); + fa.register_from_spec('vault_service_search', vault.service_vault_spec); + fa.register_from_spec('vault_shared_search', vault.shared_vault_spec); +}; + +phases.on('registration', vault.register); +phases.on('profile', vault.remove_vault_menu_item, 20); + +return vault; +}); diff --git a/install/ui/test/data/ipa_init.json b/install/ui/test/data/ipa_init.json index 820f7aa77..6f223e197 100644 --- a/install/ui/test/data/ipa_init.json +++ b/install/ui/test/data/ipa_init.json @@ -669,6 +669,31 @@ "status_link": "Click to ${action}", "unlock": "Unlock", "unlock_confirm": "Are you sure you want to unlock user ${object}?" + }, + "vault": { + "add_warn_arch_ret": "Secrets can be added/retrieved + to vault only by using vault-archive and + vault-retrieve from CLI.", + "add_warn_standard": "Content of 'standard' vaults + can be seen by users with higher privileges + (admins).", + "asymmetric_type": "Asymmetric", + "config_title": "Vaults Config", + "group": "Group", + "members": "Members", + "my_vaults_title": "My User Vaults", + "owners": "Owners", + "service": "Service", + "service_vaults_title": "Service Vaults", + "shared": "Shared", + "shared_vaults_title": "Shared Vaults", + "standard_type": "Standard", + "symmetric_type": "Symmetric", + "type": "Vault Type", + "type_tooltip": "Only standard vaults can be created + in WebUI, use CLI for other types of vaults.", + "user": "User", + "user_vaults_title": "User Vaults" } }, "password": { diff --git a/ipaserver/plugins/internal.py b/ipaserver/plugins/internal.py index 5e70a79e4..9fa1b6de8 100644 --- a/ipaserver/plugins/internal.py +++ b/ipaserver/plugins/internal.py @@ -692,6 +692,15 @@ class i18n_messages(Command): "privilege": { "identity": _("Privilege Settings"), }, + "publickey": { + "set_dialog_help": _("Public key:"), + "set_dialog_title": _("Set public key"), + "show_set_key": _("Show/Set key"), + "status_mod_ns": _("Modified: key not set"), + "status_mod_s": _("Modified"), + "status_new_ns": _("New: key not set"), + "status_new_s": _("New: key set"), + }, "pwpolicy": { "identity": _("Password Policy"), }, @@ -852,6 +861,35 @@ class i18n_messages(Command): "unlock": _("Unlock"), "unlock_confirm": _("Are you sure you want to unlock user ${object}?"), }, + "vault": { + "add_warn_arch_ret": _( + "Secrets can be added/retrieved to vault only by using " + "vault-archive and vault-retrieve from CLI." + ), + "add_warn_standard": _( + "Content of 'standard' vaults can be seen by users with " + "higher privileges (admins)." + ), + "asymmetric_type": _("Asymmetric"), + "config_title": _("Vaults Config"), + "group": _("Group"), + "members": _("Members"), + "my_vaults_title": _("My User Vaults"), + "owners": _("Owners"), + "service": _("Service"), + "service_vaults_title": _("Service Vaults"), + "shared": _("Shared"), + "shared_vaults_title": _("Shared Vaults"), + "standard_type": _("Standard"), + "symmetric_type": _("Symmetric"), + "type": _("Vault Type"), + "type_tooltip": _( + "Only standard vaults can be created in WebUI, use CLI " + "for other types of vaults." + ), + "user": _("User"), + "user_vaults_title": _("User Vaults"), + }, }, "password": { "current_password": _("Current Password"), |