Add Google & Microsoft OAuth2 authentication methods to SMTP endpoint config. The enableOAuth2 pmxSmtpEditPanel config flag allows consumers to opt into the new feature, so it can be gradually introduced into services.
When disabled, no changes are visible from the UI, and only 'None' and 'Username/Password' are shown as authentication methods. The flag is passed from the schema config, as it is done for defaultMailAuthor. Signed-off-by: Arthur Bied-Charreton <[email protected]> --- src/panel/SmtpEditPanel.js | 200 +++++++++++++++++++++++++++++++-- src/window/EndpointEditBase.js | 1 + 2 files changed, 190 insertions(+), 11 deletions(-) diff --git a/src/panel/SmtpEditPanel.js b/src/panel/SmtpEditPanel.js index 37e4d51..d6b03e3 100644 --- a/src/panel/SmtpEditPanel.js +++ b/src/panel/SmtpEditPanel.js @@ -6,10 +6,24 @@ Ext.define('Proxmox.panel.SmtpEditPanel', { type: 'smtp', + enableOAuth2: false, + + initConfig: function (config) { + this.callParent(arguments); + this.getViewModel().set('enableOAuth2', config.enableOAuth2 || false); + return config; + }, + viewModel: { xtype: 'viewmodel', data: { + enableOAuth2: false, mode: 'tls', + authMethod: 'plain', + oAuth2ClientId: '', + oAuth2ClientSecret: '', + oAuth2TenantId: '', + oAuth2RefreshToken: '', authentication: true, originalAuthentication: true, }, @@ -39,6 +53,30 @@ Ext.define('Proxmox.panel.SmtpEditPanel', { return shouldShowUnchanged ? gettext('Unchanged') : ''; }, + isPlainAuthentication: function (get) { + return get('authMethod') === 'plain'; + }, + isOAuth2Authentication: function (get) { + if (!get('enableOAuth2')) { return false }; + let authMethod = get('authMethod'); + return authMethod === 'google-oauth2' || authMethod === 'microsoft-oauth2'; + }, + isMicrosoftOAuth2Authentication: function (get) { + if (!get('enableOAuth2')) { return false }; + return get('authMethod') === 'microsoft-oauth2'; + }, + enableAuthenticate: function (get) { + if (!get('enableOAuth2')) { return false }; + let clientId = get('oAuth2ClientId'); + let clientSecret = get('oAuth2ClientSecret'); + + if (get('authMethod') === 'microsoft-oauth2') { + let tenantId = get('oAuth2TenantId'); + return clientId && clientId.trim() !== '' && clientSecret && clientSecret.trim() !== '' && tenantId && tenantId.trim() !== ''; + } else { + return clientId && clientId.trim() !== '' && clientSecret && clientSecret.trim() !== ''; + } + } }, }, @@ -102,11 +140,25 @@ Ext.define('Proxmox.panel.SmtpEditPanel', { ], column2: [ { - xtype: 'proxmoxcheckbox', - fieldLabel: gettext('Authenticate'), - name: 'authentication', - bind: { - value: '{authentication}', + xtype: 'proxmoxKVComboBox', + fieldLabel: gettext('Authentication'), + name: 'auth-method', + comboItems: [ + ['none', gettext('None')], + ['plain', gettext('Username/Password')], + ['google-oauth2', gettext('OAuth2 (Google)')], + ['microsoft-oauth2', gettext('OAuth2 (Microsoft)')], + ], + bind: '{authMethod}', + cbind: { + deleteEmpty: '{!isCreate}', + }, + listeners: { + render: function () { + if (!this.up('pmxSmtpEditPanel').enableOAuth2) { + this.getStore().filter('key', /^(none|plain)$/); + } + }, }, }, { @@ -118,7 +170,8 @@ Ext.define('Proxmox.panel.SmtpEditPanel', { deleteEmpty: '{!isCreate}', }, bind: { - disabled: '{!authentication}', + hidden: '{!isPlainAuthentication}', + disabled: '{!isPlainAuthentication}', }, }, { @@ -130,10 +183,96 @@ Ext.define('Proxmox.panel.SmtpEditPanel', { allowBlank: '{!isCreate}', }, bind: { - disabled: '{!authentication}', + hidden: '{!isPlainAuthentication}', + disabled: '{!isPlainAuthentication}', emptyText: '{passwordEmptyText}', }, }, + { + xtype: 'proxmoxtextfield', + fieldLabel: gettext('Client ID'), + name: 'oauth2-client-id', + allowBlank: false, + bind: { + hidden: '{!isOAuth2Authentication}', + disabled: '{!isOAuth2Authentication}', + value: '{oAuth2ClientId}', + }, + cbind: { + deleteEmpty: '{!isCreate}', + }, + }, + { + xtype: 'proxmoxtextfield', + inputType: 'password', + fieldLabel: gettext('Client Secret'), + name: 'oauth2-client-secret', + allowBlank: false, + bind: { + hidden: '{!isOAuth2Authentication}', + disabled: '{!isOAuth2Authentication}', + value: '{oAuth2ClientSecret}', + }, + cbind: { + deleteEmpty: '{!isCreate}', + }, + }, + { + xtype: 'proxmoxtextfield', + fieldLabel: gettext('Tenant ID'), + name: 'oauth2-tenant-id', + allowBlank: false, + bind: { + hidden: '{!isMicrosoftOAuth2Authentication}', + disabled: '{!isMicrosoftOAuth2Authentication}', + value: '{oAuth2TenantId}', + }, + cbind: { + deleteEmpty: '{!isCreate}', + }, + }, + { + xtype: 'button', + text: gettext('Authenticate'), + fieldLabel: gettext('Authenticate'), + handler: async function () { + let panel = this.up('pmxSmtpEditPanel'); + let form = panel.up('form'); + let values = form.getValues(); + + try { + let refreshToken = await panel.handleOAuth2Flow(values); + panel.getViewModel().set('oAuth2RefreshToken', refreshToken); + } catch (e) { + Ext.Msg.alert('Error', e); + } + }, + bind: { + hidden: '{!isOAuth2Authentication}', + disabled: '{!enableAuthenticate}', + }, + cbind: { + deleteEmpty: '{!isCreate}', + }, + }, + { + xtype: 'hiddenfield', + name: 'oauth2-refresh-token', + allowBlank: false, + bind: { + value: '{oAuth2RefreshToken}', + disabled: '{!isOAuth2Authentication}', + }, + getErrors: function () { + if (this.disabled) { + return []; + } + if (!this.getValue()) { + return ['']; + } + return []; + } + } ], columnB: [ { @@ -159,7 +298,6 @@ Ext.define('Proxmox.panel.SmtpEditPanel', { }, }, ], - advancedColumnB: [ { xtype: 'proxmoxtextfield', @@ -172,7 +310,15 @@ Ext.define('Proxmox.panel.SmtpEditPanel', { }, }, ], + handleOAuth2Flow: function (values) { + let authMethod = values['auth-method']; + if (authMethod === 'microsoft-oauth2') { + return Proxmox.OAuth2.handleMicrosoftFlow(values['oauth2-client-id'], values['oauth2-client-secret'], values['oauth2-tenant-id']); + } else if (authMethod === 'google-oauth2') { + return Proxmox.OAuth2.handleGoogleFlow(values['oauth2-client-id'], values['oauth2-client-secret']); + } + }, onGetValues: function (values) { let me = this; @@ -180,9 +326,30 @@ Ext.define('Proxmox.panel.SmtpEditPanel', { values.mailto = values.mailto.split(/[\s,;]+/); } - if (!values.authentication && !me.isCreate) { + if (!this.enableOAuth2) { + delete values['auth-method']; + if (!values.authentication && !me.isCreate) { + Proxmox.Utils.assemble_field_data(values, { delete: 'username' }); + Proxmox.Utils.assemble_field_data(values, { delete: 'password' }); + } + } else if (values['auth-method'] === 'none' && !me.isCreate) { + delete values['auth-method']; Proxmox.Utils.assemble_field_data(values, { delete: 'username' }); Proxmox.Utils.assemble_field_data(values, { delete: 'password' }); + Proxmox.Utils.assemble_field_data(values, { delete: 'oauth2-client-id' }); + Proxmox.Utils.assemble_field_data(values, { delete: 'oauth2-client-secret' }); + Proxmox.Utils.assemble_field_data(values, { delete: 'oauth2-tenant-id' }); + } else if (values['auth-method'] === 'plain' && !me.isCreate) { + Proxmox.Utils.assemble_field_data(values, { delete: 'oauth2-client-id' }); + Proxmox.Utils.assemble_field_data(values, { delete: 'oauth2-client-secret' }); + Proxmox.Utils.assemble_field_data(values, { delete: 'oauth2-tenant-id' }); + } else if (values['auth-method'] === 'microsoft-oauth2' && !me.isCreate) { + Proxmox.Utils.assemble_field_data(values, { delete: 'username' }); + Proxmox.Utils.assemble_field_data(values, { delete: 'password' }); + } else if (values['auth-method'] === 'google-oauth2' && !me.isCreate) { + Proxmox.Utils.assemble_field_data(values, { delete: 'username' }); + Proxmox.Utils.assemble_field_data(values, { delete: 'password' }); + Proxmox.Utils.assemble_field_data(values, { delete: 'oauth2-tenant-id' }); } if (values.enable) { @@ -199,12 +366,23 @@ Ext.define('Proxmox.panel.SmtpEditPanel', { return values; }, - onSetValues: function (values) { let me = this; - values.authentication = !!values.username; values.enable = !values.disable; + + if (values['auth-method'] === undefined && this.enableOAuth2) { + if (values['oauth2-tenant-id']) { + values['auth-method'] = 'microsoft-oauth2'; + } else if (values['oauth2-client-id']) { + values['auth-method'] = 'google-oauth2'; + } else if (values.username) { + values['auth-method'] = 'plain' + } else { + values['auth-method'] = 'none'; + } + } + delete values.disable; // Fix race condition in chromium-based browsers. Without this, the diff --git a/src/window/EndpointEditBase.js b/src/window/EndpointEditBase.js index 8c1bfc1..8df2016 100644 --- a/src/window/EndpointEditBase.js +++ b/src/window/EndpointEditBase.js @@ -47,6 +47,7 @@ Ext.define('Proxmox.window.EndpointEditBase', { baseUrl: me.baseUrl, type: me.type, defaultMailAuthor: endpointConfig.defaultMailAuthor, + enableOAuth2: endpointConfig.enableOAuth2, }, ], }); -- 2.47.3
