From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from firstgate.proxmox.com (firstgate.proxmox.com [212.224.123.68]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits)) (No client certificate requested) by lists.proxmox.com (Postfix) with ESMTPS id 67A857221C for ; Tue, 15 Jun 2021 09:58:14 +0200 (CEST) Received: from firstgate.proxmox.com (localhost [127.0.0.1]) by firstgate.proxmox.com (Proxmox) with ESMTP id 6295B2B162 for ; Tue, 15 Jun 2021 09:58:14 +0200 (CEST) Received: from proxmox-new.maurer-it.com (proxmox-new.maurer-it.com [94.136.29.106]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits)) (No client certificate requested) by firstgate.proxmox.com (Proxmox) with ESMTPS id 2E5792B0F1 for ; Tue, 15 Jun 2021 09:58:11 +0200 (CEST) Received: from proxmox-new.maurer-it.com (localhost.localdomain [127.0.0.1]) by proxmox-new.maurer-it.com (Proxmox) with ESMTP id 289CE437CA for ; Tue, 15 Jun 2021 09:58:05 +0200 (CEST) To: Lorenz Stechauner , Proxmox VE development discussion References: <20210614090557.33455-1-l.stechauner@proxmox.com> <20210614090557.33455-8-l.stechauner@proxmox.com> From: Lorenz Stechauner Message-ID: Date: Tue, 15 Jun 2021 09:58:03 +0200 User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:78.0) Gecko/20100101 Thunderbird/78.11.0 MIME-Version: 1.0 In-Reply-To: <20210614090557.33455-8-l.stechauner@proxmox.com> Content-Type: text/plain; charset=utf-8; format=flowed Content-Transfer-Encoding: 7bit Content-Language: en-US X-SPAM-LEVEL: Spam detection results: 0 AWL 1.261 Adjusted score from AWL reputation of From: address BAYES_00 -1.9 Bayes spam probability is 0 to 1% KAM_DMARC_STATUS 0.01 Test Rule for DKIM or SPF Failure with Strict Alignment NICE_REPLY_A -0.489 Looks like a legit reply (A) SPF_HELO_NONE 0.001 SPF: HELO does not publish an SPF Record SPF_PASS -0.001 SPF: sender matches SPF record URIBL_BLOCKED 0.001 ADMINISTRATOR NOTICE: The query to URIBL was blocked. See http://wiki.apache.org/spamassassin/DnsBlocklists#dnsbl-block for more information. [caps.storage] Subject: Re: [pve-devel] [PATCH v7 manager 5/5] fix #1710: ui: storage: add download from url button X-BeenThere: pve-devel@lists.proxmox.com X-Mailman-Version: 2.1.29 Precedence: list List-Id: Proxmox VE development discussion List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Tue, 15 Jun 2021 07:58:14 -0000 uses the common function PVE::Tools::download_file_from_url to download a iso image or container template. note: Only users with permissions `Sys.Audit` and `Sys.Modify` on `/` are permitted to use the api endpoints due to security reasons. (it is possible to download files from internal networks which would be not visible/accessible from outside) On 14.06.21 11:05, Lorenz Stechauner wrote: > Signed-off-by: Lorenz Stechauner > --- > www/manager6/storage/Browser.js | 8 + > www/manager6/storage/ContentView.js | 247 +++++++++++++++++++++++++--- > 2 files changed, 231 insertions(+), 24 deletions(-) > > diff --git a/www/manager6/storage/Browser.js b/www/manager6/storage/Browser.js > index 5fee94c7..fe5df3e2 100644 > --- a/www/manager6/storage/Browser.js > +++ b/www/manager6/storage/Browser.js > @@ -53,6 +53,9 @@ Ext.define('PVE.storage.Browser', { > let plugin = res.plugintype; > let contents = res.content.split(','); > > + let enableUpload = !!caps.storage['Datastore.AllocateTemplate']; > + let enableDownloadUrl = enableUpload && !!(caps.nodes['Sys.Audit'] && caps.nodes['Sys.Modify']); > + > if (contents.includes('backup')) { > me.items.push({ > xtype: 'pveStorageBackupView', > @@ -91,6 +94,8 @@ Ext.define('PVE.storage.Browser', { > itemId: 'contentIso', > content: 'iso', > pluginType: plugin, > + enableUploadButton: enableUpload, > + enableDownloadUrlButton: enableDownloadUrl, > useUploadButton: true, > }); > } > @@ -101,6 +106,9 @@ Ext.define('PVE.storage.Browser', { > iconCls: 'fa fa-file-o lxc', > itemId: 'contentVztmpl', > pluginType: plugin, > + enableUploadButton: enableUpload, > + enableDownloadUrlButton: enableDownloadUrl, > + useUploadButton: true, > }); > } > if (contents.includes('snippets')) { > diff --git a/www/manager6/storage/ContentView.js b/www/manager6/storage/ContentView.js > index dd6df4b1..6171d30c 100644 > --- a/www/manager6/storage/ContentView.js > +++ b/www/manager6/storage/ContentView.js > @@ -191,6 +191,191 @@ Ext.define('PVE.storage.Upload', { > }, > }); > > +Ext.define('PVE.storage.DownloadUrl', { > + extend: 'Proxmox.window.Edit', > + alias: 'widget.pveStorageDownloadUrl', > + mixins: ['Proxmox.Mixin.CBind'], > + > + isCreate: true, > + > + method: 'POST', > + > + showTaskViewer: true, > + > + title: gettext('Download from URL'), > + submitText: gettext('Download'), > + > + cbindData: function(initialConfig) { > + var me = this; > + return { > + nodename: me.nodename, > + storage: me.storage, > + contents: me.contents, > + content: me.contents[0], > + }; > + }, > + > + cbind: { > + url: '/nodes/{nodename}/storage/{storage}/download-url', > + }, > + > + controller: { > + xclass: 'Ext.app.ViewController', > + > + urlChange: function(field) { > + let me = this; > + let view = me.getView(); > + field = view.down('[name=url]'); > + field.setValidation("Waiting for response..."); > + field.validate(); > + view.setValues({ size: "" }); > + Proxmox.Utils.API2Request({ > + url: `/nodes/${view.nodename}/query-url-metadata`, > + method: 'GET', > + params: { > + url: field.getValue(), > + 'verify-certificates': view.getValues()['verify-certificates'], > + }, > + failure: function(res, opt) { > + field.setValidation(res.result.message); > + field.validate(); > + view.setValues({ > + size: "", > + mimetype: "", > + }); > + }, > + success: function(res, opt) { > + field.setValidation(); > + field.validate(); > + > + let data = res.result.data; > + view.setValues({ > + filename: data.filename || "", > + size: (data.size && Proxmox.Utils.format_size(data.size)) || "", > + mimetype: data.mimetype || "", > + }); > + }, > + }); > + }, > + > + hashChange: function(field) { > + let checksum = Ext.getCmp('downloadUrlChecksum'); > + if (field.getValue() === '__default__') { > + checksum.setDisabled(true); > + checksum.setValue(""); > + checksum.allowBlank = true; > + } else { > + checksum.setDisabled(false); > + checksum.allowBlank = false; > + } > + }, > + }, > + > + items: [ > + { > + xtype: 'inputpanel', > + waitMsgTarget: true, > + border: false, > + columnT: [ > + { > + xtype: 'textfield', > + name: 'url', > + allowBlank: false, > + fieldLabel: gettext('URL'), > + listeners: { > + change: { > + buffer: 500, > + fn: 'urlChange', > + }, > + }, > + }, > + { > + xtype: 'textfield', > + name: 'filename', > + allowBlank: false, > + fieldLabel: gettext('File name'), > + }, > + ], > + column1: [ > + { > + xtype: 'pveContentTypeSelector', > + fieldLabel: gettext('Content'), > + name: 'content', > + allowBlank: false, > + cbind: { > + cts: '{contents}', > + value: '{content}', > + }, > + }, > + ], > + column2: [ > + { > + xtype: 'textfield', > + name: 'size', > + disabled: true, > + fieldLabel: gettext('File size'), > + emptyText: gettext('unknown'), > + }, > + ], > + advancedColumn1: [ > + { > + xtype: 'pveHashAlgorithmSelector', > + name: 'checksum-algorithm', > + fieldLabel: gettext('Hash algorithm'), > + allowBlank: true, > + hasNoneOption: true, > + value: '__default__', > + listeners: { > + change: 'hashChange', > + }, > + }, > + { > + xtype: 'textfield', > + name: 'checksum', > + fieldLabel: gettext('Checksum'), > + allowBlank: true, > + disabled: true, > + emptyText: gettext('none'), > + id: 'downloadUrlChecksum', > + }, > + ], > + advancedColumn2: [ > + { > + xtype: 'textfield', > + fieldLabel: gettext('MIME type'), > + name: 'mimetype', > + disabled: true, > + editable: false, > + emptyText: gettext('unknown'), > + }, > + { > + xtype: 'proxmoxcheckbox', > + name: 'verify-certificates', > + fieldLabel: gettext('Verify certificates'), > + uncheckedValue: 0, > + checked: true, > + listeners: { > + change: 'urlChange', > + }, > + }, > + ], > + }, > + ], > + > + initComponent: function() { > + var me = this; > + > + if (!me.nodename) { > + throw "no node name specified"; > + } > + if (!me.storage) { > + throw "no storage ID specified"; > + } > + > + me.callParent(); > + }, > +}); > + > Ext.define('PVE.storage.ContentView', { > extend: 'Ext.grid.GridPanel', > > @@ -249,36 +434,50 @@ Ext.define('PVE.storage.ContentView', { > > Proxmox.Utils.monStoreErrors(me, store); > > - let uploadButton = Ext.create('Proxmox.button.Button', { > - text: gettext('Upload'), > - handler: function() { > - let win = Ext.create('PVE.storage.Upload', { > - nodename: nodename, > - storage: storage, > - contents: [content], > - }); > - win.show(); > - win.on('destroy', reload); > - }, > - }); > - > - let removeButton = Ext.create('Proxmox.button.StdRemoveButton', { > - selModel: sm, > - delay: 5, > - callback: function() { > - reload(); > - }, > - baseurl: baseurl + '/', > - }); > - > if (!me.tbar) { > me.tbar = []; > } > if (me.useUploadButton) { > - me.tbar.push(uploadButton); > + me.tbar.unshift( > + { > + xtype: 'button', > + text: gettext('Upload'), > + disabled: !me.enableUploadButton, > + handler: function() { > + Ext.create('PVE.storage.Upload', { > + nodename: nodename, > + storage: storage, > + contents: [content], > + autoShow: true, > + taskDone: () => reload(), > + }); > + }, > + }, > + { > + xtype: 'button', > + text: gettext('Download from URL'), > + disabled: !me.enableDownloadUrlButton, > + handler: function() { > + Ext.create('PVE.storage.DownloadUrl', { > + nodename: nodename, > + storage: storage, > + contents: [content], > + autoShow: true, > + taskDone: () => reload(), > + }); > + }, > + }, > + '-', > + ); > } > if (!me.useCustomRemoveButton) { > - me.tbar.push(removeButton); > + me.tbar.push({ > + xtype: 'proxmoxStdRemoveButton', > + selModel: sm, > + delay: 5, > + callback: () => reload(), > + baseurl: baseurl + '/', > + }); > } > me.tbar.push( > '->',