fix: sensitive-property handling + UI autofill + API key reveal button
truenas_api_key must be optional in options() because PVE extracts sensitive-properties from the POST body before calling check_config, causing a spurious 'missing required option' 500 on storage create. Add a guard in _api() so a missing key produces a clear error. UI: add autocomplete="url" on host and "new-password" on API key to prevent Firefox/Chrome from filling in PVE login credentials. Add a reveal trigger button to show/hide the API key field. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
ea1c2208e1
commit
efcad405e5
|
|
@ -88,7 +88,7 @@ sub options {
|
|||
bwlimit => { optional => 1 },
|
||||
shared => { optional => 1 },
|
||||
truenas_host => { fixed => 1 },
|
||||
truenas_api_key => {},
|
||||
truenas_api_key => { optional => 1 },
|
||||
truenas_ssl => { optional => 1 },
|
||||
truenas_ssl_verify => { optional => 1 },
|
||||
truenas_pool => { fixed => 1 },
|
||||
|
|
@ -128,6 +128,9 @@ sub _ua {
|
|||
sub _api {
|
||||
my ($scfg, $method, $path, $data) = @_;
|
||||
|
||||
die "TrueNAS API key is not configured for storage '$scfg->{truenas_host}'\n"
|
||||
unless $scfg->{truenas_api_key};
|
||||
|
||||
my $scheme = ($scfg->{truenas_ssl} // 1) ? 'https' : 'http';
|
||||
my $url = "$scheme://$scfg->{truenas_host}/api/v2.0$path";
|
||||
|
||||
|
|
|
|||
|
|
@ -5,6 +5,19 @@
|
|||
//
|
||||
// Loaded by index.html.tpl after pvemanagerlib.js, so PVE.Utils is available.
|
||||
|
||||
// Inject CSS for the API key reveal trigger (eye icon via Font Awesome)
|
||||
(function () {
|
||||
let style = document.createElement('style');
|
||||
style.textContent = [
|
||||
'.truenas-reveal-trigger::before {',
|
||||
' font-family: "Font Awesome 5 Free";',
|
||||
' font-weight: 900;',
|
||||
' content: "\\f06e";', // fa-eye
|
||||
'}',
|
||||
].join('');
|
||||
document.head.appendChild(style);
|
||||
}());
|
||||
|
||||
// Register TrueNAS in the storage type dropdown
|
||||
PVE.Utils.storageSchema.truenas = {
|
||||
name: 'TrueNAS (ZFS/iSCSI)',
|
||||
|
|
@ -26,6 +39,9 @@ Ext.define('PVE.storage.TrueNASInputPanel', {
|
|||
fieldLabel: gettext('TrueNAS Host'),
|
||||
name: 'truenas_host',
|
||||
allowBlank: false,
|
||||
autoComplete: false,
|
||||
// 'url' misdirects browser autofill away from username heuristics
|
||||
inputAttrTpl: 'autocomplete="url"',
|
||||
emptyText: gettext('hostname or IP address'),
|
||||
},
|
||||
{
|
||||
|
|
@ -34,9 +50,22 @@ Ext.define('PVE.storage.TrueNASInputPanel', {
|
|||
name: 'truenas_api_key',
|
||||
inputType: 'password',
|
||||
allowBlank: !me.isCreate,
|
||||
autoComplete: false,
|
||||
// 'new-password' prevents browser from filling a saved password here
|
||||
inputAttrTpl: 'autocomplete="new-password"',
|
||||
emptyText: me.isCreate
|
||||
? gettext('Paste API key from TrueNAS UI')
|
||||
: gettext('unchanged — paste new key to change'),
|
||||
triggers: {
|
||||
reveal: {
|
||||
cls: 'truenas-reveal-trigger',
|
||||
tooltip: gettext('Show / hide API key'),
|
||||
handler: function (field) {
|
||||
let dom = field.inputEl.dom;
|
||||
dom.type = dom.type === 'password' ? 'text' : 'password';
|
||||
},
|
||||
},
|
||||
},
|
||||
listeners: {
|
||||
// On edit: don't submit unless the user types a new value
|
||||
afterrender: function (field) {
|
||||
|
|
@ -56,6 +85,7 @@ Ext.define('PVE.storage.TrueNASInputPanel', {
|
|||
fieldLabel: gettext('Pool'),
|
||||
name: 'truenas_pool',
|
||||
allowBlank: false,
|
||||
autoComplete: false,
|
||||
emptyText: gettext('ZFS pool name (e.g. tank)'),
|
||||
},
|
||||
{
|
||||
|
|
@ -63,6 +93,7 @@ Ext.define('PVE.storage.TrueNASInputPanel', {
|
|||
fieldLabel: gettext('Dataset'),
|
||||
name: 'truenas_dataset',
|
||||
allowBlank: true,
|
||||
autoComplete: false,
|
||||
emptyText: gettext('Optional (e.g. proxmox)'),
|
||||
deleteEmpty: !me.isCreate,
|
||||
},
|
||||
|
|
@ -90,6 +121,7 @@ Ext.define('PVE.storage.TrueNASInputPanel', {
|
|||
fieldLabel: gettext('Portal IP'),
|
||||
name: 'truenas_portal_ip',
|
||||
allowBlank: true,
|
||||
autoComplete: false,
|
||||
emptyText: gettext('Optional — defaults to TrueNAS host'),
|
||||
deleteEmpty: !me.isCreate,
|
||||
},
|
||||
|
|
@ -98,6 +130,7 @@ Ext.define('PVE.storage.TrueNASInputPanel', {
|
|||
fieldLabel: gettext('Target IQN'),
|
||||
name: 'truenas_target',
|
||||
allowBlank: true,
|
||||
autoComplete: false,
|
||||
emptyText: gettext('Optional — auto-discovered if blank'),
|
||||
deleteEmpty: !me.isCreate,
|
||||
},
|
||||
|
|
|
|||
Loading…
Reference in New Issue