* [pmg-devel] [PATCH pmg-gui v2] add OIDC configuration panel for PMG
@ 2025-03-03 8:49 Markus Frank
2025-03-03 8:58 ` Markus Frank
2025-03-10 14:37 ` Dominik Csapak
0 siblings, 2 replies; 4+ messages in thread
From: Markus Frank @ 2025-03-03 8:49 UTC (permalink / raw)
To: pmg-devel
AuthEditOIDC.js is based on AuthEditOpenId from widget-toolkit and
adds additional configuration options for autocreate-role-assignment.
It uses sub/preferred_username for username-claim instead of the old
names (subject/username/email). Removed email option entirely as it is
incompatible with the username scheme.
Signed-off-by: Markus Frank <m.frank@proxmox.com>
---
v2:
* renamed subject to sub
* renamed username to preferred_username
* removed email entirely
js/AuthEditOIDC.js | 270 +++++++++++++++++++++++++++++++++++++++++++++
js/Makefile | 1 +
js/Utils.js | 1 +
3 files changed, 272 insertions(+)
create mode 100644 js/AuthEditOIDC.js
diff --git a/js/AuthEditOIDC.js b/js/AuthEditOIDC.js
new file mode 100644
index 0000000..cda9d68
--- /dev/null
+++ b/js/AuthEditOIDC.js
@@ -0,0 +1,270 @@
+Ext.define('PMG.OIDCInputPanel', {
+ extend: 'Proxmox.panel.InputPanel',
+ xtype: 'pmgAuthOIDCPanel',
+ mixins: ['Proxmox.Mixin.CBind'],
+
+ showDefaultRealm: false,
+
+ type: 'oidc',
+
+ viewModel: {
+ data: {
+ roleSource: '__default__',
+ autocreate: 0,
+ },
+ formulas: {
+ hideRoleAssignment: function(get) {
+ let autocreate = get('autocreate');
+ if (!autocreate) {
+ return 1;
+ }
+ return autocreate === 0;
+ },
+ hideFixedRoleAssignment: function(get) {
+ return get('roleSource') !== 'fixed' || get('hideRoleAssignment');
+ },
+ hideClaimRoleAssignment: function(get) {
+ return get('roleSource') !== 'from-claim' || get('hideRoleAssignment');
+ },
+ },
+ },
+
+ onGetValues: function(values) {
+ let me = this;
+
+ if (me.isCreate && !me.useTypeInUrl) {
+ values.type = me.type;
+ }
+
+ if (values.source) {
+ let autocreateRoleAssignment = {};
+ autocreateRoleAssignment.source = values.source;
+ if (values.source === 'fixed') {
+ autocreateRoleAssignment['fixed-role'] = values['fixed-role'];
+ } else if (values.source === 'from-claim') {
+ autocreateRoleAssignment['role-claim'] = values['role-claim'];
+ }
+ values['autocreate-role-assignment'] =
+ Proxmox.Utils.printPropertyString(autocreateRoleAssignment);
+ }
+
+ if ((!values.autocreate || !values.source) && !me.isCreate) {
+ if (values.delete) {
+ if (Ext.isArray(values.delete)) {
+ values.delete.push('autocreate-role-assignment');
+ } else {
+ values.delete += ',autocreate-role-assignment';
+ }
+ } else {
+ values.delete = 'autocreate-role-assignment';
+ }
+ }
+ delete values.source;
+ delete values['fixed-role'];
+ delete values['role-claim'];
+
+ return values;
+ },
+
+ setValues: function(values) {
+ let autocreateRoleAssignment =
+ Proxmox.Utils.parsePropertyString(values['autocreate-role-assignment']);
+
+ if (autocreateRoleAssignment.source) {
+ values.source = autocreateRoleAssignment.source;
+ } else {
+ values.source = '__default__';
+ }
+
+ if (autocreateRoleAssignment.source === 'fixed') {
+ values['fixed-role'] = autocreateRoleAssignment['fixed-role'];
+ }
+ if (autocreateRoleAssignment.source === 'from-claim') {
+ values['role-claim'] = autocreateRoleAssignment['role-claim'];
+ }
+
+ this.callParent(arguments);
+ },
+
+
+ columnT: [
+ {
+ xtype: 'textfield',
+ name: 'issuer-url',
+ fieldLabel: gettext('Issuer URL'),
+ allowBlank: false,
+ },
+ ],
+
+ column1: [
+ {
+ xtype: 'pmxDisplayEditField',
+ name: 'realm',
+ cbind: {
+ value: '{realm}',
+ editable: '{isCreate}',
+ },
+ fieldLabel: gettext('Realm'),
+ allowBlank: false,
+ },
+ {
+ xtype: 'proxmoxcheckbox',
+ fieldLabel: gettext('Default realm'),
+ name: 'default',
+ value: 0,
+ cbind: {
+ deleteEmpty: '{!isCreate}',
+ hidden: '{!showDefaultRealm}',
+ disabled: '{!showDefaultRealm}',
+ },
+ autoEl: {
+ tag: 'div',
+ 'data-qtip': gettext('Set realm as default for login'),
+ },
+ },
+ {
+ xtype: 'proxmoxtextfield',
+ fieldLabel: gettext('Client ID'),
+ name: 'client-id',
+ allowBlank: false,
+ },
+ {
+ xtype: 'proxmoxtextfield',
+ fieldLabel: gettext('Client Key'),
+ cbind: {
+ deleteEmpty: '{!isCreate}',
+ },
+ name: 'client-key',
+ },
+ ],
+
+ column2: [
+ {
+ xtype: 'pmxDisplayEditField',
+ name: 'username-claim',
+ fieldLabel: gettext('Username Claim'),
+ editConfig: {
+ xtype: 'proxmoxKVComboBox',
+ editable: true,
+ comboItems: [
+ ['__default__', Proxmox.Utils.defaultText],
+ ['sub', 'sub (subject)'],
+ ['preferred_username', 'preferred_username'],
+ ],
+ },
+ cbind: {
+ value: get => get('isCreate') ? '__default__' : Proxmox.Utils.defaultText,
+ deleteEmpty: '{!isCreate}',
+ editable: '{isCreate}',
+ },
+ },
+ {
+ xtype: 'proxmoxtextfield',
+ name: 'scopes',
+ fieldLabel: gettext('Scopes'),
+ emptyText: `${Proxmox.Utils.defaultText} (email profile)`,
+ submitEmpty: false,
+ cbind: {
+ deleteEmpty: '{!isCreate}',
+ },
+ },
+ {
+ xtype: 'proxmoxKVComboBox',
+ name: 'prompt',
+ fieldLabel: gettext('Prompt'),
+ editable: true,
+ emptyText: gettext('Auth-Provider Default'),
+ comboItems: [
+ ['__default__', gettext('Auth-Provider Default')],
+ ['none', 'none'],
+ ['login', 'login'],
+ ['consent', 'consent'],
+ ['select_account', 'select_account'],
+ ],
+ cbind: {
+ deleteEmpty: '{!isCreate}',
+ },
+ },
+ ],
+
+ columnB: [
+ {
+ xtype: 'proxmoxtextfield',
+ name: 'comment',
+ fieldLabel: gettext('Comment'),
+ cbind: {
+ deleteEmpty: '{!isCreate}',
+ },
+ },
+ {
+ xtype: 'displayfield',
+ value: gettext('Autocreate Options'),
+ },
+ {
+ xtype: 'proxmoxcheckbox',
+ fieldLabel: gettext('Autocreate Users'),
+ name: 'autocreate',
+ bind: {
+ value: '{autocreate}',
+ },
+ cbind: {
+ deleteEmpty: '{!isCreate}',
+ },
+ },
+ {
+ xtype: 'proxmoxKVComboBox',
+ name: 'source',
+ fieldLabel: gettext('Source for Role Assignment'),
+ allowBlank: false,
+ deleteEmpty: false,
+ comboItems: [
+ [
+ '__default__',
+ Proxmox.Utils.defaultText
+ + ' (' + gettext('All auto-created users get audit role') + ')',
+ ],
+ ['fixed', 'Fixed role for all auto-created users'],
+ ['from-claim', 'Get role from OIDC claim'],
+ ],
+ bind: {
+ value: '{roleSource}',
+ disabled: '{hideRoleAssignment}',
+ hidden: '{hideRoleAssignment}',
+ },
+ },
+ {
+ xtype: 'pmgRoleSelector',
+ name: 'fixed-role',
+ allowBlank: false,
+ deleteEmpty: false,
+ fieldLabel: gettext('Fixed Role'),
+ bind: {
+ disabled: '{hideFixedRoleAssignment}',
+ hidden: '{hideFixedRoleAssignment}',
+ },
+ },
+ {
+ xtype: 'proxmoxtextfield',
+ name: 'role-claim',
+ allowBlank: false,
+ deleteEmpty: false,
+ fieldLabel: gettext('Role Claim'),
+ bind: {
+ disabled: '{hideClaimRoleAssignment}',
+ hidden: '{hideClaimRoleAssignment}',
+ },
+ },
+ ],
+
+ advancedColumnB: [
+ {
+ xtype: 'proxmoxtextfield',
+ name: 'acr-values',
+ fieldLabel: gettext('ACR Values'),
+ submitEmpty: false,
+ cbind: {
+ deleteEmpty: '{!isCreate}',
+ },
+ },
+ ],
+});
diff --git a/js/Makefile b/js/Makefile
index d1fab9b..c984bf3 100644
--- a/js/Makefile
+++ b/js/Makefile
@@ -78,6 +78,7 @@ JSSRC= \
LDAPConfig.js \
UserEdit.js \
UserView.js \
+ AuthEditOIDC.js \
TFAView.js \
FetchmailEdit.js \
FetchmailView.js \
diff --git a/js/Utils.js b/js/Utils.js
index d4a55a8..9dbc76f 100644
--- a/js/Utils.js
+++ b/js/Utils.js
@@ -871,6 +871,7 @@ Ext.define('PMG.Utils', {
// use oidc instead of openid
Proxmox.Schema.authDomains.oidc = Proxmox.Schema.authDomains.openid;
Proxmox.Schema.authDomains.oidc.useTypeInUrl = false;
+ Proxmox.Schema.authDomains.oidc.ipanel = 'pmgAuthOIDCPanel';
delete Proxmox.Schema.authDomains.openid;
// Disable LDAP/AD as a realm until LDAP/AD login is implemented
--
2.39.5
_______________________________________________
pmg-devel mailing list
pmg-devel@lists.proxmox.com
https://lists.proxmox.com/cgi-bin/mailman/listinfo/pmg-devel
^ permalink raw reply [flat|nested] 4+ messages in thread
* Re: [pmg-devel] [PATCH pmg-gui v2] add OIDC configuration panel for PMG
2025-03-03 8:49 [pmg-devel] [PATCH pmg-gui v2] add OIDC configuration panel for PMG Markus Frank
@ 2025-03-03 8:58 ` Markus Frank
2025-03-10 14:37 ` Dominik Csapak
1 sibling, 0 replies; 4+ messages in thread
From: Markus Frank @ 2025-03-03 8:58 UTC (permalink / raw)
To: pmg-devel
On 2025-03-03 09:49, Markus Frank wrote:
> AuthEditOIDC.js is based on AuthEditOpenId from widget-toolkit and
> adds additional configuration options for autocreate-role-assignment.
>
> It uses sub/preferred_username for username-claim instead of the old
> names (subject/username/email). Removed email option entirely as it is
> incompatible with the username scheme.
I am sorry.
This part should be:
Use sub/preferred_username for username-claim instead of the old names
(subject/username/email). Remove the email option entirely as it is
incompatible with the username scheme.
>
> Signed-off-by: Markus Frank <m.frank@proxmox.com>
> ---
> v2:
> * renamed subject to sub
> * renamed username to preferred_username
> * removed email entirely
>
> js/AuthEditOIDC.js | 270 +++++++++++++++++++++++++++++++++++++++++++++
> js/Makefile | 1 +
> js/Utils.js | 1 +
> 3 files changed, 272 insertions(+)
> create mode 100644 js/AuthEditOIDC.js
>
> diff --git a/js/AuthEditOIDC.js b/js/AuthEditOIDC.js
> new file mode 100644
> index 0000000..cda9d68
> --- /dev/null
> +++ b/js/AuthEditOIDC.js
> @@ -0,0 +1,270 @@
> +Ext.define('PMG.OIDCInputPanel', {
> + extend: 'Proxmox.panel.InputPanel',
> + xtype: 'pmgAuthOIDCPanel',
> + mixins: ['Proxmox.Mixin.CBind'],
> +
> + showDefaultRealm: false,
> +
> + type: 'oidc',
> +
> + viewModel: {
> + data: {
> + roleSource: '__default__',
> + autocreate: 0,
> + },
> + formulas: {
> + hideRoleAssignment: function(get) {
> + let autocreate = get('autocreate');
> + if (!autocreate) {
> + return 1;
> + }
> + return autocreate === 0;
> + },
> + hideFixedRoleAssignment: function(get) {
> + return get('roleSource') !== 'fixed' || get('hideRoleAssignment');
> + },
> + hideClaimRoleAssignment: function(get) {
> + return get('roleSource') !== 'from-claim' || get('hideRoleAssignment');
> + },
> + },
> + },
> +
> + onGetValues: function(values) {
> + let me = this;
> +
> + if (me.isCreate && !me.useTypeInUrl) {
> + values.type = me.type;
> + }
> +
> + if (values.source) {
> + let autocreateRoleAssignment = {};
> + autocreateRoleAssignment.source = values.source;
> + if (values.source === 'fixed') {
> + autocreateRoleAssignment['fixed-role'] = values['fixed-role'];
> + } else if (values.source === 'from-claim') {
> + autocreateRoleAssignment['role-claim'] = values['role-claim'];
> + }
> + values['autocreate-role-assignment'] =
> + Proxmox.Utils.printPropertyString(autocreateRoleAssignment);
> + }
> +
> + if ((!values.autocreate || !values.source) && !me.isCreate) {
> + if (values.delete) {
> + if (Ext.isArray(values.delete)) {
> + values.delete.push('autocreate-role-assignment');
> + } else {
> + values.delete += ',autocreate-role-assignment';
> + }
> + } else {
> + values.delete = 'autocreate-role-assignment';
> + }
> + }
> + delete values.source;
> + delete values['fixed-role'];
> + delete values['role-claim'];
> +
> + return values;
> + },
> +
> + setValues: function(values) {
> + let autocreateRoleAssignment =
> + Proxmox.Utils.parsePropertyString(values['autocreate-role-assignment']);
> +
> + if (autocreateRoleAssignment.source) {
> + values.source = autocreateRoleAssignment.source;
> + } else {
> + values.source = '__default__';
> + }
> +
> + if (autocreateRoleAssignment.source === 'fixed') {
> + values['fixed-role'] = autocreateRoleAssignment['fixed-role'];
> + }
> + if (autocreateRoleAssignment.source === 'from-claim') {
> + values['role-claim'] = autocreateRoleAssignment['role-claim'];
> + }
> +
> + this.callParent(arguments);
> + },
> +
> +
> + columnT: [
> + {
> + xtype: 'textfield',
> + name: 'issuer-url',
> + fieldLabel: gettext('Issuer URL'),
> + allowBlank: false,
> + },
> + ],
> +
> + column1: [
> + {
> + xtype: 'pmxDisplayEditField',
> + name: 'realm',
> + cbind: {
> + value: '{realm}',
> + editable: '{isCreate}',
> + },
> + fieldLabel: gettext('Realm'),
> + allowBlank: false,
> + },
> + {
> + xtype: 'proxmoxcheckbox',
> + fieldLabel: gettext('Default realm'),
> + name: 'default',
> + value: 0,
> + cbind: {
> + deleteEmpty: '{!isCreate}',
> + hidden: '{!showDefaultRealm}',
> + disabled: '{!showDefaultRealm}',
> + },
> + autoEl: {
> + tag: 'div',
> + 'data-qtip': gettext('Set realm as default for login'),
> + },
> + },
> + {
> + xtype: 'proxmoxtextfield',
> + fieldLabel: gettext('Client ID'),
> + name: 'client-id',
> + allowBlank: false,
> + },
> + {
> + xtype: 'proxmoxtextfield',
> + fieldLabel: gettext('Client Key'),
> + cbind: {
> + deleteEmpty: '{!isCreate}',
> + },
> + name: 'client-key',
> + },
> + ],
> +
> + column2: [
> + {
> + xtype: 'pmxDisplayEditField',
> + name: 'username-claim',
> + fieldLabel: gettext('Username Claim'),
> + editConfig: {
> + xtype: 'proxmoxKVComboBox',
> + editable: true,
> + comboItems: [
> + ['__default__', Proxmox.Utils.defaultText],
> + ['sub', 'sub (subject)'],
> + ['preferred_username', 'preferred_username'],
> + ],
> + },
> + cbind: {
> + value: get => get('isCreate') ? '__default__' : Proxmox.Utils.defaultText,
> + deleteEmpty: '{!isCreate}',
> + editable: '{isCreate}',
> + },
> + },
> + {
> + xtype: 'proxmoxtextfield',
> + name: 'scopes',
> + fieldLabel: gettext('Scopes'),
> + emptyText: `${Proxmox.Utils.defaultText} (email profile)`,
> + submitEmpty: false,
> + cbind: {
> + deleteEmpty: '{!isCreate}',
> + },
> + },
> + {
> + xtype: 'proxmoxKVComboBox',
> + name: 'prompt',
> + fieldLabel: gettext('Prompt'),
> + editable: true,
> + emptyText: gettext('Auth-Provider Default'),
> + comboItems: [
> + ['__default__', gettext('Auth-Provider Default')],
> + ['none', 'none'],
> + ['login', 'login'],
> + ['consent', 'consent'],
> + ['select_account', 'select_account'],
> + ],
> + cbind: {
> + deleteEmpty: '{!isCreate}',
> + },
> + },
> + ],
> +
> + columnB: [
> + {
> + xtype: 'proxmoxtextfield',
> + name: 'comment',
> + fieldLabel: gettext('Comment'),
> + cbind: {
> + deleteEmpty: '{!isCreate}',
> + },
> + },
> + {
> + xtype: 'displayfield',
> + value: gettext('Autocreate Options'),
> + },
> + {
> + xtype: 'proxmoxcheckbox',
> + fieldLabel: gettext('Autocreate Users'),
> + name: 'autocreate',
> + bind: {
> + value: '{autocreate}',
> + },
> + cbind: {
> + deleteEmpty: '{!isCreate}',
> + },
> + },
> + {
> + xtype: 'proxmoxKVComboBox',
> + name: 'source',
> + fieldLabel: gettext('Source for Role Assignment'),
> + allowBlank: false,
> + deleteEmpty: false,
> + comboItems: [
> + [
> + '__default__',
> + Proxmox.Utils.defaultText
> + + ' (' + gettext('All auto-created users get audit role') + ')',
> + ],
> + ['fixed', 'Fixed role for all auto-created users'],
> + ['from-claim', 'Get role from OIDC claim'],
> + ],
> + bind: {
> + value: '{roleSource}',
> + disabled: '{hideRoleAssignment}',
> + hidden: '{hideRoleAssignment}',
> + },
> + },
> + {
> + xtype: 'pmgRoleSelector',
> + name: 'fixed-role',
> + allowBlank: false,
> + deleteEmpty: false,
> + fieldLabel: gettext('Fixed Role'),
> + bind: {
> + disabled: '{hideFixedRoleAssignment}',
> + hidden: '{hideFixedRoleAssignment}',
> + },
> + },
> + {
> + xtype: 'proxmoxtextfield',
> + name: 'role-claim',
> + allowBlank: false,
> + deleteEmpty: false,
> + fieldLabel: gettext('Role Claim'),
> + bind: {
> + disabled: '{hideClaimRoleAssignment}',
> + hidden: '{hideClaimRoleAssignment}',
> + },
> + },
> + ],
> +
> + advancedColumnB: [
> + {
> + xtype: 'proxmoxtextfield',
> + name: 'acr-values',
> + fieldLabel: gettext('ACR Values'),
> + submitEmpty: false,
> + cbind: {
> + deleteEmpty: '{!isCreate}',
> + },
> + },
> + ],
> +});
> diff --git a/js/Makefile b/js/Makefile
> index d1fab9b..c984bf3 100644
> --- a/js/Makefile
> +++ b/js/Makefile
> @@ -78,6 +78,7 @@ JSSRC= \
> LDAPConfig.js \
> UserEdit.js \
> UserView.js \
> + AuthEditOIDC.js \
> TFAView.js \
> FetchmailEdit.js \
> FetchmailView.js \
> diff --git a/js/Utils.js b/js/Utils.js
> index d4a55a8..9dbc76f 100644
> --- a/js/Utils.js
> +++ b/js/Utils.js
> @@ -871,6 +871,7 @@ Ext.define('PMG.Utils', {
> // use oidc instead of openid
> Proxmox.Schema.authDomains.oidc = Proxmox.Schema.authDomains.openid;
> Proxmox.Schema.authDomains.oidc.useTypeInUrl = false;
> + Proxmox.Schema.authDomains.oidc.ipanel = 'pmgAuthOIDCPanel';
> delete Proxmox.Schema.authDomains.openid;
>
> // Disable LDAP/AD as a realm until LDAP/AD login is implemented
_______________________________________________
pmg-devel mailing list
pmg-devel@lists.proxmox.com
https://lists.proxmox.com/cgi-bin/mailman/listinfo/pmg-devel
^ permalink raw reply [flat|nested] 4+ messages in thread
* Re: [pmg-devel] [PATCH pmg-gui v2] add OIDC configuration panel for PMG
2025-03-03 8:49 [pmg-devel] [PATCH pmg-gui v2] add OIDC configuration panel for PMG Markus Frank
2025-03-03 8:58 ` Markus Frank
@ 2025-03-10 14:37 ` Dominik Csapak
2025-03-11 10:22 ` Markus Frank
1 sibling, 1 reply; 4+ messages in thread
From: Dominik Csapak @ 2025-03-10 14:37 UTC (permalink / raw)
To: pmg-devel
Works fine during testing, but noticed some things in the code,
see comments inline:
On 3/3/25 09:49, Markus Frank wrote:
> AuthEditOIDC.js is based on AuthEditOpenId from widget-toolkit and
> adds additional configuration options for autocreate-role-assignment.
>
> It uses sub/preferred_username for username-claim instead of the old
> names (subject/username/email). Removed email option entirely as it is
> incompatible with the username scheme.
>
> Signed-off-by: Markus Frank <m.frank@proxmox.com>
> ---
> v2:
> * renamed subject to sub
> * renamed username to preferred_username
> * removed email entirely
>
> js/AuthEditOIDC.js | 270 +++++++++++++++++++++++++++++++++++++++++++++
> js/Makefile | 1 +
> js/Utils.js | 1 +
> 3 files changed, 272 insertions(+)
> create mode 100644 js/AuthEditOIDC.js
>
> diff --git a/js/AuthEditOIDC.js b/js/AuthEditOIDC.js
> new file mode 100644
> index 0000000..cda9d68
> --- /dev/null
> +++ b/js/AuthEditOIDC.js
> @@ -0,0 +1,270 @@
> +Ext.define('PMG.OIDCInputPanel', {
> + extend: 'Proxmox.panel.InputPanel',
> + xtype: 'pmgAuthOIDCPanel',
> + mixins: ['Proxmox.Mixin.CBind'],
> +
> + showDefaultRealm: false,
> +
> + type: 'oidc',
> +
> + viewModel: {
> + data: {
> + roleSource: '__default__',
> + autocreate: 0,
> + },
> + formulas: {
> + hideRoleAssignment: function(get) {
> + let autocreate = get('autocreate');
> + if (!autocreate) {
> + return 1;
> + }
> + return autocreate === 0;
in one branch you return 1, but later true/false
also if 'autocreate' === 0, '!autocreate' is already true..
couldn't you simply use '!autocreate' in the bindings then?
> + },
> + hideFixedRoleAssignment: function(get) {
> + return get('roleSource') !== 'fixed' || get('hideRoleAssignment');
> + },
> + hideClaimRoleAssignment: function(get) {
> + return get('roleSource') !== 'from-claim' || get('hideRoleAssignment');
> + },
> + },
> + },
> +
> + onGetValues: function(values) {
> + let me = this;
> +
> + if (me.isCreate && !me.useTypeInUrl) {
> + values.type = me.type;
> + }
> +
> + if (values.source) {
> + let autocreateRoleAssignment = {};
> + autocreateRoleAssignment.source = values.source;
> + if (values.source === 'fixed') {
> + autocreateRoleAssignment['fixed-role'] = values['fixed-role'];
> + } else if (values.source === 'from-claim') {
> + autocreateRoleAssignment['role-claim'] = values['role-claim'];
> + }
if the content of 'source' would match the indices ('fixed-role' and 'role-claim') you could do
something like this (psuedo-code & untested!)
if (values.source) {
let assignment = {};
assignment[values.source] = values[values.source];
values['aut...'] = printPropertyString(assignment);
}
no?
> + values['autocreate-role-assignment'] =
> + Proxmox.Utils.printPropertyString(autocreateRoleAssignment);
> + }
> +
> + if ((!values.autocreate || !values.source) && !me.isCreate) {
> + if (values.delete) {
> + if (Ext.isArray(values.delete)) {
> + values.delete.push('autocreate-role-assignment');
> + } else {
> + values.delete += ',autocreate-role-assignment';
> + }
> + } else {
> + values.delete = 'autocreate-role-assignment';
> + }
> + }
AFAIR, we already have this behavior factored out in proxmox-widget-toolkit:
'delete_if_default'
> + delete values.source;
> + delete values['fixed-role'];
> + delete values['role-claim'];
> +
> + return values;
> + },
> +
> + setValues: function(values) {
> + let autocreateRoleAssignment =
> + Proxmox.Utils.parsePropertyString(values['autocreate-role-assignment']);
> +
> + if (autocreateRoleAssignment.source) {
> + values.source = autocreateRoleAssignment.source;
> + } else {
> + values.source = '__default__';
> + }
could be simplified to
values.source = autocreateRoleAssignment.source ?? '__default__';
> +
> + if (autocreateRoleAssignment.source === 'fixed') {
> + values['fixed-role'] = autocreateRoleAssignment['fixed-role'];
> + }
> + if (autocreateRoleAssignment.source === 'from-claim') {
> + values['role-claim'] = autocreateRoleAssignment['role-claim'];
> + }
same here but reversed:
values[assignment.source] = assignment[assignment.source];
> +
> + this.callParent(arguments);
> + },
> +
> +
> + columnT: [
> + {
> + xtype: 'textfield',
> + name: 'issuer-url',
> + fieldLabel: gettext('Issuer URL'),
> + allowBlank: false,
> + },
> + ],
> +
> + column1: [
> + {
> + xtype: 'pmxDisplayEditField',
> + name: 'realm',
> + cbind: {
> + value: '{realm}',
> + editable: '{isCreate}',
> + },
> + fieldLabel: gettext('Realm'),
> + allowBlank: false,
> + },
> + {
> + xtype: 'proxmoxcheckbox',
> + fieldLabel: gettext('Default realm'),
> + name: 'default',
> + value: 0,
> + cbind: {
> + deleteEmpty: '{!isCreate}',
> + hidden: '{!showDefaultRealm}',
> + disabled: '{!showDefaultRealm}',
> + },
> + autoEl: {
> + tag: 'div',
> + 'data-qtip': gettext('Set realm as default for login'),
> + },
> + },
> + {
> + xtype: 'proxmoxtextfield',
> + fieldLabel: gettext('Client ID'),
> + name: 'client-id',
> + allowBlank: false,
> + },
> + {
> + xtype: 'proxmoxtextfield',
> + fieldLabel: gettext('Client Key'),
> + cbind: {
> + deleteEmpty: '{!isCreate}',
> + },
> + name: 'client-key',
> + },
> + ],
> +
> + column2: [
> + {
> + xtype: 'pmxDisplayEditField',
> + name: 'username-claim',
> + fieldLabel: gettext('Username Claim'),
> + editConfig: {
> + xtype: 'proxmoxKVComboBox',
> + editable: true,
> + comboItems: [
> + ['__default__', Proxmox.Utils.defaultText],
> + ['sub', 'sub (subject)'],
> + ['preferred_username', 'preferred_username'],
if you're changing the text vs widget-toolkit, why not got the full length
and localize the description?
e.g. `gettext('sub (Subject)')`
?
(preexisting) maybe i just overlooked that with the recent patches, but why are the
claims different than the other products (sub vs subject, preferred_username vs username) ?
> + ],
> + },
> + cbind: {
> + value: get => get('isCreate') ? '__default__' : Proxmox.Utils.defaultText,
> + deleteEmpty: '{!isCreate}',
> + editable: '{isCreate}',
> + },
> + },
> + {
> + xtype: 'proxmoxtextfield',
> + name: 'scopes',
> + fieldLabel: gettext('Scopes'),
> + emptyText: `${Proxmox.Utils.defaultText} (email profile)`,
> + submitEmpty: false,
> + cbind: {
> + deleteEmpty: '{!isCreate}',
> + },
> + },
> + {
> + xtype: 'proxmoxKVComboBox',
> + name: 'prompt',
> + fieldLabel: gettext('Prompt'),
> + editable: true,
> + emptyText: gettext('Auth-Provider Default'),
> + comboItems: [
> + ['__default__', gettext('Auth-Provider Default')],
> + ['none', 'none'],
> + ['login', 'login'],
> + ['consent', 'consent'],
> + ['select_account', 'select_account'],
> + ],
> + cbind: {
> + deleteEmpty: '{!isCreate}',
> + },
> + },
> + ],
> +
> + columnB: [
> + {
> + xtype: 'proxmoxtextfield',
> + name: 'comment',
> + fieldLabel: gettext('Comment'),
> + cbind: {
> + deleteEmpty: '{!isCreate}',
> + },
> + },
> + {
> + xtype: 'displayfield',
> + value: gettext('Autocreate Options'),
> + },
> + {
> + xtype: 'proxmoxcheckbox',
> + fieldLabel: gettext('Autocreate Users'),
> + name: 'autocreate',
> + bind: {
> + value: '{autocreate}',
> + },
> + cbind: {
> + deleteEmpty: '{!isCreate}',
> + },
> + },
> + {
> + xtype: 'proxmoxKVComboBox',
> + name: 'source',
> + fieldLabel: gettext('Source for Role Assignment'),
> + allowBlank: false,
> + deleteEmpty: false,
> + comboItems: [
> + [
> + '__default__',
> + Proxmox.Utils.defaultText
> + + ' (' + gettext('All auto-created users get audit role') + ')',
> + ],
> + ['fixed', 'Fixed role for all auto-created users'],
> + ['from-claim', 'Get role from OIDC claim'],
IMHO those should be put in 'gettext'
> + ],
> + bind: {
> + value: '{roleSource}',
> + disabled: '{hideRoleAssignment}',
> + hidden: '{hideRoleAssignment}',
> + },
> + },
> + {
> + xtype: 'pmgRoleSelector',
> + name: 'fixed-role',
> + allowBlank: false,
> + deleteEmpty: false,
> + fieldLabel: gettext('Fixed Role'),
> + bind: {
> + disabled: '{hideFixedRoleAssignment}',
> + hidden: '{hideFixedRoleAssignment}',
> + },
> + },
> + {
> + xtype: 'proxmoxtextfield',
> + name: 'role-claim',
> + allowBlank: false,
> + deleteEmpty: false,
> + fieldLabel: gettext('Role Claim'),
> + bind: {
> + disabled: '{hideClaimRoleAssignment}',
> + hidden: '{hideClaimRoleAssignment}',
> + },
side question: did you got that to work with keycloak? i couldn't in my tests...
> + },
> + ],
> +
> + advancedColumnB: [
> + {
> + xtype: 'proxmoxtextfield',
> + name: 'acr-values',
> + fieldLabel: gettext('ACR Values'),
> + submitEmpty: false,
> + cbind: {
> + deleteEmpty: '{!isCreate}',
> + },
> + },
> + ],
> +});
> diff --git a/js/Makefile b/js/Makefile
> index d1fab9b..c984bf3 100644
> --- a/js/Makefile
> +++ b/js/Makefile
> @@ -78,6 +78,7 @@ JSSRC= \
> LDAPConfig.js \
> UserEdit.js \
> UserView.js \
> + AuthEditOIDC.js \
> TFAView.js \
> FetchmailEdit.js \
> FetchmailView.js \
> diff --git a/js/Utils.js b/js/Utils.js
> index d4a55a8..9dbc76f 100644
> --- a/js/Utils.js
> +++ b/js/Utils.js
> @@ -871,6 +871,7 @@ Ext.define('PMG.Utils', {
> // use oidc instead of openid
> Proxmox.Schema.authDomains.oidc = Proxmox.Schema.authDomains.openid;
> Proxmox.Schema.authDomains.oidc.useTypeInUrl = false;
> + Proxmox.Schema.authDomains.oidc.ipanel = 'pmgAuthOIDCPanel';
> delete Proxmox.Schema.authDomains.openid;
>
> // Disable LDAP/AD as a realm until LDAP/AD login is implemented
_______________________________________________
pmg-devel mailing list
pmg-devel@lists.proxmox.com
https://lists.proxmox.com/cgi-bin/mailman/listinfo/pmg-devel
^ permalink raw reply [flat|nested] 4+ messages in thread
* Re: [pmg-devel] [PATCH pmg-gui v2] add OIDC configuration panel for PMG
2025-03-10 14:37 ` Dominik Csapak
@ 2025-03-11 10:22 ` Markus Frank
0 siblings, 0 replies; 4+ messages in thread
From: Markus Frank @ 2025-03-11 10:22 UTC (permalink / raw)
To: Dominik Csapak, pmg-devel
I will address most of your comments in v3, thanks.
Some comments/answers inline:
On 2025-03-10 15:37, Dominik Csapak wrote:
> Works fine during testing, but noticed some things in the code,
> see comments inline:
>
> On 3/3/25 09:49, Markus Frank wrote:
>> AuthEditOIDC.js is based on AuthEditOpenId from widget-toolkit and
>> adds additional configuration options for autocreate-role-assignment.
>>
>> It uses sub/preferred_username for username-claim instead of the old
>> names (subject/username/email). Removed email option entirely as it is
>> incompatible with the username scheme.
>>
>> Signed-off-by: Markus Frank <m.frank@proxmox.com>
>> ---
>> v2:
>> * renamed subject to sub
>> * renamed username to preferred_username
>> * removed email entirely
>>
>> js/AuthEditOIDC.js | 270 +++++++++++++++++++++++++++++++++++++++++++++
>> js/Makefile | 1 +
>> js/Utils.js | 1 +
>> 3 files changed, 272 insertions(+)
>> create mode 100644 js/AuthEditOIDC.js
>>
>> diff --git a/js/AuthEditOIDC.js b/js/AuthEditOIDC.js
>> new file mode 100644
>> index 0000000..cda9d68
>> --- /dev/null
>> +++ b/js/AuthEditOIDC.js
>> @@ -0,0 +1,270 @@
>> +Ext.define('PMG.OIDCInputPanel', {
>> + extend: 'Proxmox.panel.InputPanel',
>> + xtype: 'pmgAuthOIDCPanel',
>> + mixins: ['Proxmox.Mixin.CBind'],
>> +
>> + showDefaultRealm: false,
>> +
>> + type: 'oidc',
>> +
>> + viewModel: {
>> + data: {
>> + roleSource: '__default__',
>> + autocreate: 0,
>> + },
>> + formulas: {
>> + hideRoleAssignment: function(get) {
>> + let autocreate = get('autocreate');
>> + if (!autocreate) {
>> + return 1;
>> + }
>> + return autocreate === 0;
>
> in one branch you return 1, but later true/false
> also if 'autocreate' === 0, '!autocreate' is already true..
>
> couldn't you simply use '!autocreate' in the bindings then?
>
>
>> + },
>> + hideFixedRoleAssignment: function(get) {
>> + return get('roleSource') !== 'fixed' || get('hideRoleAssignment');
>> + },
>> + hideClaimRoleAssignment: function(get) {
>> + return get('roleSource') !== 'from-claim' || get('hideRoleAssignment');
>> + },
>> + },
>> + },
>> +
>> + onGetValues: function(values) {
>> + let me = this;
>> +
>> + if (me.isCreate && !me.useTypeInUrl) {
>> + values.type = me.type;
>> + }
>> +
>> + if (values.source) {
>> + let autocreateRoleAssignment = {};
>> + autocreateRoleAssignment.source = values.source;
>> + if (values.source === 'fixed') {
>> + autocreateRoleAssignment['fixed-role'] = values['fixed-role'];
>> + } else if (values.source === 'from-claim') {
>> + autocreateRoleAssignment['role-claim'] = values['role-claim'];
>> + }
>
> if the content of 'source' would match the indices ('fixed-role' and 'role-claim') you could do
> something like this (psuedo-code & untested!)
>
> if (values.source) {
> let assignment = {};
> assignment[values.source] = values[values.source];
> values['aut...'] = printPropertyString(assignment);
> }
>
> no?
Sadly not.
If the content of 'source' would match the indices ('fixed-role' and 'role-claim'),
I would still need to use an if to change the source variable:
if (values.source) {
let assignment = {};
assignment[values.source] = values[values.source];
if (values.source === 'fixed-role') {
assignment.source = 'fixed';
} else if (values.source === 'role-claim') {
assignment.source = 'from-claim';
}
values['aut...'] = printPropertyString(assignment);
}
I am not sure if that would be better.
>
>
>> + values['autocreate-role-assignment'] =
>> + Proxmox.Utils.printPropertyString(autocreateRoleAssignment);
>> + }
>> +
>> + if ((!values.autocreate || !values.source) && !me.isCreate) {
>> + if (values.delete) {
>> + if (Ext.isArray(values.delete)) {
>> + values.delete.push('autocreate-role-assignment');
>> + } else {
>> + values.delete += ',autocreate-role-assignment';
>> + }
>> + } else {
>> + values.delete = 'autocreate-role-assignment';
>> + }
>> + }
> AFAIR, we already have this behavior factored out in proxmox-widget-toolkit:
>
> 'delete_if_default'
>
>> + delete values.source;
>> + delete values['fixed-role'];
>> + delete values['role-claim'];
>> +
>> + return values;
>> + },
>> +
>> + setValues: function(values) {
>> + let autocreateRoleAssignment =
>> + Proxmox.Utils.parsePropertyString(values['autocreate-role-assignment']);
>> +
>> + if (autocreateRoleAssignment.source) {
>> + values.source = autocreateRoleAssignment.source;
>> + } else {
>> + values.source = '__default__';
>> + }
>
> could be simplified to
>
> values.source = autocreateRoleAssignment.source ?? '__default__';
>
>> +
>> + if (autocreateRoleAssignment.source === 'fixed') {
>> + values['fixed-role'] = autocreateRoleAssignment['fixed-role'];
>> + }
>> + if (autocreateRoleAssignment.source === 'from-claim') {
>> + values['role-claim'] = autocreateRoleAssignment['role-claim'];
>> + }
>
> same here but reversed:
>
> values[assignment.source] = assignment[assignment.source];
>
>> +
>> + this.callParent(arguments);
>> + },
>> +
>> +
>> + columnT: [
>> + {
>> + xtype: 'textfield',
>> + name: 'issuer-url',
>> + fieldLabel: gettext('Issuer URL'),
>> + allowBlank: false,
>> + },
>> + ],
>> +
>> + column1: [
>> + {
>> + xtype: 'pmxDisplayEditField',
>> + name: 'realm',
>> + cbind: {
>> + value: '{realm}',
>> + editable: '{isCreate}',
>> + },
>> + fieldLabel: gettext('Realm'),
>> + allowBlank: false,
>> + },
>> + {
>> + xtype: 'proxmoxcheckbox',
>> + fieldLabel: gettext('Default realm'),
>> + name: 'default',
>> + value: 0,
>> + cbind: {
>> + deleteEmpty: '{!isCreate}',
>> + hidden: '{!showDefaultRealm}',
>> + disabled: '{!showDefaultRealm}',
>> + },
>> + autoEl: {
>> + tag: 'div',
>> + 'data-qtip': gettext('Set realm as default for login'),
>> + },
>> + },
>> + {
>> + xtype: 'proxmoxtextfield',
>> + fieldLabel: gettext('Client ID'),
>> + name: 'client-id',
>> + allowBlank: false,
>> + },
>> + {
>> + xtype: 'proxmoxtextfield',
>> + fieldLabel: gettext('Client Key'),
>> + cbind: {
>> + deleteEmpty: '{!isCreate}',
>> + },
>> + name: 'client-key',
>> + },
>> + ],
>> +
>> + column2: [
>> + {
>> + xtype: 'pmxDisplayEditField',
>> + name: 'username-claim',
>> + fieldLabel: gettext('Username Claim'),
>> + editConfig: {
>> + xtype: 'proxmoxKVComboBox',
>> + editable: true,
>> + comboItems: [
>> + ['__default__', Proxmox.Utils.defaultText],
>> + ['sub', 'sub (subject)'],
>> + ['preferred_username', 'preferred_username'],
>
> if you're changing the text vs widget-toolkit, why not got the full length
> and localize the description?
>
> e.g. `gettext('sub (Subject)')`
>
> ?
>
> (preexisting) maybe i just overlooked that with the recent patches, but why are the
> claims different than the other products (sub vs subject, preferred_username vs username) ?
subject and username do not exist (anymore).
We use something like this in our backends:
} elsif ($user_attr eq 'subject') { # stay compat with old versions
$unique_name = $info->{'sub'};
} elsif ($user_attr eq 'username') { # stay compat with old versions
my $username = $info->{'preferred_username'};
die "missing claim 'preferred_username'\n" if !defined($username);
$unique_name = $username;
I guess it was the plan to update these names in the PVE & PBS WebUIs, but it never happened.
>
>> + ],
>> + },
>> + cbind: {
>> + value: get => get('isCreate') ? '__default__' : Proxmox.Utils.defaultText,
>> + deleteEmpty: '{!isCreate}',
>> + editable: '{isCreate}',
>> + },
>> + },
>> + {
>> + xtype: 'proxmoxtextfield',
>> + name: 'scopes',
>> + fieldLabel: gettext('Scopes'),
>> + emptyText: `${Proxmox.Utils.defaultText} (email profile)`,
>> + submitEmpty: false,
>> + cbind: {
>> + deleteEmpty: '{!isCreate}',
>> + },
>> + },
>> + {
>> + xtype: 'proxmoxKVComboBox',
>> + name: 'prompt',
>> + fieldLabel: gettext('Prompt'),
>> + editable: true,
>> + emptyText: gettext('Auth-Provider Default'),
>> + comboItems: [
>> + ['__default__', gettext('Auth-Provider Default')],
>> + ['none', 'none'],
>> + ['login', 'login'],
>> + ['consent', 'consent'],
>> + ['select_account', 'select_account'],
>> + ],
>> + cbind: {
>> + deleteEmpty: '{!isCreate}',
>> + },
>> + },
>> + ],
>> +
>> + columnB: [
>> + {
>> + xtype: 'proxmoxtextfield',
>> + name: 'comment',
>> + fieldLabel: gettext('Comment'),
>> + cbind: {
>> + deleteEmpty: '{!isCreate}',
>> + },
>> + },
>> + {
>> + xtype: 'displayfield',
>> + value: gettext('Autocreate Options'),
>> + },
>> + {
>> + xtype: 'proxmoxcheckbox',
>> + fieldLabel: gettext('Autocreate Users'),
>> + name: 'autocreate',
>> + bind: {
>> + value: '{autocreate}',
>> + },
>> + cbind: {
>> + deleteEmpty: '{!isCreate}',
>> + },
>> + },
>> + {
>> + xtype: 'proxmoxKVComboBox',
>> + name: 'source',
>> + fieldLabel: gettext('Source for Role Assignment'),
>> + allowBlank: false,
>> + deleteEmpty: false,
>> + comboItems: [
>> + [
>> + '__default__',
>> + Proxmox.Utils.defaultText
>> + + ' (' + gettext('All auto-created users get audit role') + ')',
>> + ],
>> + ['fixed', 'Fixed role for all auto-created users'],
>> + ['from-claim', 'Get role from OIDC claim'],
>
> IMHO those should be put in 'gettext'
>
>> + ],
>> + bind: {
>> + value: '{roleSource}',
>> + disabled: '{hideRoleAssignment}',
>> + hidden: '{hideRoleAssignment}',
>> + },
>> + },
>> + {
>> + xtype: 'pmgRoleSelector',
>> + name: 'fixed-role',
>> + allowBlank: false,
>> + deleteEmpty: false,
>> + fieldLabel: gettext('Fixed Role'),
>> + bind: {
>> + disabled: '{hideFixedRoleAssignment}',
>> + hidden: '{hideFixedRoleAssignment}',
>> + },
>> + },
>> + {
>> + xtype: 'proxmoxtextfield',
>> + name: 'role-claim',
>> + allowBlank: false,
>> + deleteEmpty: false,
>> + fieldLabel: gettext('Role Claim'),
>> + bind: {
>> + disabled: '{hideClaimRoleAssignment}',
>> + hidden: '{hideClaimRoleAssignment}',
>> + },
>
> side question: did you got that to work with keycloak? i couldn't in my tests...
I tried it with keycloak and authentik. I was also unsuccessful.
>
>> + },
>> + ],
>> +
>> + advancedColumnB: [
>> + {
>> + xtype: 'proxmoxtextfield',
>> + name: 'acr-values',
>> + fieldLabel: gettext('ACR Values'),
>> + submitEmpty: false,
>> + cbind: {
>> + deleteEmpty: '{!isCreate}',
>> + },
>> + },
>> + ],
>> +});
>> diff --git a/js/Makefile b/js/Makefile
>> index d1fab9b..c984bf3 100644
>> --- a/js/Makefile
>> +++ b/js/Makefile
>> @@ -78,6 +78,7 @@ JSSRC= \
>> LDAPConfig.js \
>> UserEdit.js \
>> UserView.js \
>> + AuthEditOIDC.js \
>> TFAView.js \
>> FetchmailEdit.js \
>> FetchmailView.js \
>> diff --git a/js/Utils.js b/js/Utils.js
>> index d4a55a8..9dbc76f 100644
>> --- a/js/Utils.js
>> +++ b/js/Utils.js
>> @@ -871,6 +871,7 @@ Ext.define('PMG.Utils', {
>> // use oidc instead of openid
>> Proxmox.Schema.authDomains.oidc = Proxmox.Schema.authDomains.openid;
>> Proxmox.Schema.authDomains.oidc.useTypeInUrl = false;
>> + Proxmox.Schema.authDomains.oidc.ipanel = 'pmgAuthOIDCPanel';
>> delete Proxmox.Schema.authDomains.openid;
>> // Disable LDAP/AD as a realm until LDAP/AD login is implemented
>
>
>
> _______________________________________________
> pmg-devel mailing list
> pmg-devel@lists.proxmox.com
> https://lists.proxmox.com/cgi-bin/mailman/listinfo/pmg-devel
>
>
_______________________________________________
pmg-devel mailing list
pmg-devel@lists.proxmox.com
https://lists.proxmox.com/cgi-bin/mailman/listinfo/pmg-devel
^ permalink raw reply [flat|nested] 4+ messages in thread
end of thread, other threads:[~2025-03-11 10:22 UTC | newest]
Thread overview: 4+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2025-03-03 8:49 [pmg-devel] [PATCH pmg-gui v2] add OIDC configuration panel for PMG Markus Frank
2025-03-03 8:58 ` Markus Frank
2025-03-10 14:37 ` Dominik Csapak
2025-03-11 10:22 ` Markus Frank
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