From: Markus Frank <m.frank@proxmox.com>
To: pmg-devel@lists.proxmox.com
Subject: [pmg-devel] [PATCH pmg-gui v6 11/12] login: add option to login with OIDC realm
Date: Tue, 25 Feb 2025 14:36:18 +0100 [thread overview]
Message-ID: <20250225133619.42012-12-m.frank@proxmox.com> (raw)
In-Reply-To: <20250225133619.42012-1-m.frank@proxmox.com>
By adding a viewModel with an oidc variable, the username & password
fields are disabled/hidden when an OIDC realm is selected.
Signed-off-by: Markus Frank <m.frank@proxmox.com>
---
v6: changed realm baseUrl to '/access/auth-realm'
js/LoginView.js | 209 ++++++++++++++++++++++++++++++++++++------------
1 file changed, 158 insertions(+), 51 deletions(-)
diff --git a/js/LoginView.js b/js/LoginView.js
index b5da19a..c375d75 100644
--- a/js/LoginView.js
+++ b/js/LoginView.js
@@ -2,6 +2,21 @@ Ext.define('PMG.LoginView', {
extend: 'Ext.container.Container',
xtype: 'loginview',
+ viewModel: {
+ data: {
+ oidc: false,
+ },
+ formulas: {
+ button_text: function(get) {
+ if (get("oidc") === true) {
+ return gettext("Login (OpenID Connect redirect)");
+ } else {
+ return gettext("Login");
+ }
+ },
+ },
+ },
+
controller: {
xclass: 'Ext.app.ViewController',
@@ -46,50 +61,77 @@ Ext.define('PMG.LoginView', {
submitForm: async function() {
let me = this;
- let view = me.getView();
- let loginForm = me.lookupReference('loginForm');
- var unField = me.lookupReference('usernameField');
- var saveunField = me.lookupReference('saveunField');
- if (loginForm.isValid()) {
- if (loginForm.isVisible()) {
- loginForm.mask(gettext('Please wait...'), 'x-mask-loading');
- }
+ let loginForm = this.lookupReference('loginForm');
+ let unField = this.lookupReference('usernameField');
+ let saveunField = this.lookupReference('saveunField');
+ let view = this.getView();
- // set or clear username for admin view
- if (view.targetview !== 'quarantineview') {
- var sp = Ext.state.Manager.getProvider();
- if (saveunField.getValue() === true) {
- sp.set(unField.getStateId(), unField.getValue());
- } else {
- sp.clear(unField.getStateId());
- }
- sp.set(saveunField.getStateId(), saveunField.getValue());
+ if (!loginForm.isValid()) {
+ return;
+ }
+
+ if (loginForm.isVisible()) {
+ loginForm.mask(gettext('Please wait...'), 'x-mask-loading');
+ }
+
+ // set or clear username for admin view
+ if (view.targetview !== 'quarantineview') {
+ let sp = Ext.state.Manager.getProvider();
+ if (saveunField.getValue() === true) {
+ sp.set(unField.getStateId(), unField.getValue());
+ } else {
+ sp.clear(unField.getStateId());
}
+ sp.set(saveunField.getStateId(), saveunField.getValue());
+ }
+
+ let creds = loginForm.getValues();
- let creds = loginForm.getValues();
+ if (this.getViewModel().data.oidc === true) {
+ const redirectURL = location.origin;
+ Proxmox.Utils.API2Request({
+ url: '/api2/extjs/access/oidc/auth-url',
+ params: {
+ realm: creds.realm,
+ "redirect-url": redirectURL,
+ },
+ method: 'POST',
+ success: function(resp, opts) {
+ window.location = resp.result.data;
+ },
+ failure: function(resp, opts) {
+ Proxmox.Utils.authClear();
+ loginForm.unmask();
+ Ext.MessageBox.alert(
+ gettext('Error'),
+ gettext('OpenID Connect redirect failed.') + `<br>${resp.htmlStatus}`,
+ );
+ },
+ });
+ return;
+ }
- try {
- let resp = await Proxmox.Async.api2({
- url: '/api2/extjs/access/ticket',
- params: creds,
- method: 'POST',
- });
+ try {
+ let resp = await Proxmox.Async.api2({
+ url: '/api2/extjs/access/ticket',
+ params: creds,
+ method: 'POST',
+ });
- let data = resp.result.data;
- if (data.ticket.startsWith('PMG:!tfa!')) {
- data = await me.performTFAChallenge(data);
- }
- PMG.Utils.updateLoginData(data);
- PMG.app.changeView(view.targetview);
- } catch (error) {
- Proxmox.Utils.authClear();
- loginForm.unmask();
- Ext.MessageBox.alert(
- gettext('Error'),
- gettext('Login failed. Please try again'),
- );
+ let data = resp.result.data;
+ if (data.ticket.startsWith('PMG:!tfa!')) {
+ data = await me.performTFAChallenge(data);
}
+ PMG.Utils.updateLoginData(data);
+ PMG.app.changeView(view.targetview);
+ } catch (error) {
+ Proxmox.Utils.authClear();
+ loginForm.unmask();
+ Ext.MessageBox.alert(
+ gettext('Error'),
+ gettext('Login failed. Please try again'),
+ );
}
},
@@ -115,6 +157,15 @@ Ext.define('PMG.LoginView', {
return resp.result.data;
},
+ success: function(data) {
+ let me = this;
+ let view = me.getView();
+ let handler = view.handler || Ext.emptyFn;
+ handler.call(me, data);
+ PMG.Utils.updateLoginData(data);
+ PMG.app.changeView(view.targetview);
+ },
+
openQuarantineLinkWindow: function() {
let me = this;
me.lookup('loginwindow').setVisible(false);
@@ -150,6 +201,14 @@ Ext.define('PMG.LoginView', {
window.location.reload();
},
},
+ 'field[name=realm]': {
+ change: function(f, value) {
+ let record = f.store.getById(value);
+ if (record === undefined) return;
+ let data = record.data;
+ this.getViewModel().set("oidc", data.type === "oidc");
+ },
+ },
'button[reference=quarantineButton]': {
click: 'openQuarantineLinkWindow',
},
@@ -161,19 +220,54 @@ Ext.define('PMG.LoginView', {
let me = this;
let view = me.getView();
if (view.targetview !== 'quarantineview') {
- var sp = Ext.state.Manager.getProvider();
- var checkboxField = this.lookupReference('saveunField');
- var unField = this.lookupReference('usernameField');
+ let sp = Ext.state.Manager.getProvider();
+ let checkboxField = this.lookupReference('saveunField');
+ let unField = this.lookupReference('usernameField');
- var checked = sp.get(checkboxField.getStateId());
+ let checked = sp.get(checkboxField.getStateId());
checkboxField.setValue(checked);
if (checked === true) {
- var username = sp.get(unField.getStateId());
+ let username = sp.get(unField.getStateId());
unField.setValue(username);
- var pwField = this.lookupReference('passwordField');
+ let pwField = this.lookupReference('passwordField');
pwField.focus();
}
+
+ let auth = Proxmox.Utils.getOpenIDRedirectionAuthorization();
+ if (auth !== undefined) {
+ Proxmox.Utils.authClear();
+
+ let loginForm = this.lookupReference('loginForm');
+ loginForm.mask(gettext('OpenID Connect login - please wait...'), 'x-mask-loading');
+
+ const redirectURL = location.origin;
+
+ Proxmox.Utils.API2Request({
+ url: '/api2/extjs/access/oidc/login',
+ params: {
+ state: auth.state,
+ code: auth.code,
+ "redirect-url": redirectURL,
+ },
+ method: 'POST',
+ failure: function(response) {
+ loginForm.unmask();
+ let error = response.htmlStatus;
+ Ext.MessageBox.alert(
+ gettext('Error'),
+ gettext('OpenID Connect login failed, please try again') + `<br>${error}`,
+ () => { window.location = redirectURL; },
+ );
+ },
+ success: function(response, options) {
+ loginForm.unmask();
+ let data = response.result.data;
+ history.replaceState(null, '', redirectURL);
+ me.success(data);
+ },
+ });
+ }
}
},
},
@@ -250,6 +344,10 @@ Ext.define('PMG.LoginView', {
reference: 'usernameField',
stateId: 'login-username',
inputAttrTpl: 'autocomplete=username',
+ bind: {
+ visible: "{!oidc}",
+ disabled: "{oidc}",
+ },
},
{
xtype: 'textfield',
@@ -258,6 +356,17 @@ Ext.define('PMG.LoginView', {
name: 'password',
reference: 'passwordField',
inputAttrTpl: 'autocomplete=current-password',
+ bind: {
+ visible: "{!oidc}",
+ disabled: "{oidc}",
+ },
+ },
+ {
+ xtype: 'pmxRealmComboBox',
+ reference: 'realmfield',
+ name: 'realm',
+ baseUrl: '/access/auth-realm',
+ value: 'pam',
},
{
xtype: 'proxmoxLanguageSelector',
@@ -266,12 +375,6 @@ Ext.define('PMG.LoginView', {
name: 'lang',
submitValue: false,
},
- {
- xtype: 'hiddenfield',
- reference: 'realmfield',
- name: 'realm',
- value: 'pmg',
- },
],
buttons: [
{
@@ -283,15 +386,19 @@ Ext.define('PMG.LoginView', {
labelAlign: 'right',
labelWidth: 150,
submitValue: false,
+ bind: {
+ visible: "{!oidc}",
+ },
},
{
text: gettext('Request Quarantine Link'),
reference: 'quarantineButton',
},
{
- text: gettext('Login'),
+ bind: {
+ text: "{button_text}",
+ },
reference: 'loginButton',
- formBind: true,
},
],
},
--
2.39.5
_______________________________________________
pmg-devel mailing list
pmg-devel@lists.proxmox.com
https://lists.proxmox.com/cgi-bin/mailman/listinfo/pmg-devel
next prev parent reply other threads:[~2025-02-25 13:38 UTC|newest]
Thread overview: 19+ messages / expand[flat|nested] mbox.gz Atom feed top
2025-02-25 13:36 [pmg-devel] [PATCH pve-common/perl-rs/pmg-api/widget-toolkit/pmg-gui v6 0/12] fix #3892: OpenID Connect Markus Frank
2025-02-25 13:36 ` [pmg-devel] [PATCH pve-common v6 1/12] add Schema package with auth module that contains realm sync options Markus Frank
2025-02-25 17:24 ` Thomas Lamprecht
2025-02-25 13:36 ` [pmg-devel] [PATCH proxmox-perl-rs v6 2/12] move openid code from pve-rs to common Markus Frank
2025-02-25 13:36 ` [pmg-devel] [PATCH proxmox-perl-rs v6 3/12] remove empty PMG::RS::OpenId package to avoid confusion Markus Frank
2025-02-25 13:36 ` [pmg-devel] [PATCH pmg-api v6 4/12] config: add plugin system for authentication realms Markus Frank
2025-02-25 16:32 ` Stoiko Ivanov
2025-02-25 13:36 ` [pmg-devel] [PATCH pmg-api v6 5/12] config: add oidc type authentication realm Markus Frank
2025-02-26 10:20 ` Mira Limbeck
2025-02-25 13:36 ` [pmg-devel] [PATCH pmg-api v6 6/12] api: add/update/remove authentication realms like in PVE Markus Frank
2025-02-25 13:36 ` [pmg-devel] [PATCH pmg-api v6 7/12] api: oidc login similar to PVE Markus Frank
2025-02-25 13:36 ` [pmg-devel] [PATCH widget-toolkit v6 08/12] fix: window: AuthEditBase: rename variable 'realm' to 'type' Markus Frank
2025-02-25 13:36 ` [pmg-devel] [PATCH widget-toolkit v6 09/12] fix: panel: AuthView: change API path in pmx-domains model Markus Frank
2025-02-25 17:33 ` Markus Frank
2025-02-25 13:36 ` [pmg-devel] [PATCH widget-toolkit v6 10/12] form: RealmComboBox: add option to change the API path Markus Frank
2025-02-25 13:36 ` Markus Frank [this message]
2025-02-25 13:36 ` [pmg-devel] [PATCH pmg-gui v6 12/12] add realms panel to user management Markus Frank
2025-02-26 11:08 ` [pmg-devel] [PATCH pve-common/perl-rs/pmg-api/widget-toolkit/pmg-gui v6 0/12] fix #3892: OpenID Connect Mira Limbeck
2025-02-26 11:30 ` Lukas Wagner
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20250225133619.42012-12-m.frank@proxmox.com \
--to=m.frank@proxmox.com \
--cc=pmg-devel@lists.proxmox.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox
Service provided by Proxmox Server Solutions GmbH | Privacy | Legal