<?php
/*
** Copyright (C) 2001-2025 Zabbix SIA
**
** This program is free software: you can redistribute it and/or modify it under the terms of
** the GNU Affero General Public License as published by the Free Software Foundation, version 3.
**
** This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
** without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
** See the GNU Affero General Public License for more details.
**
** You should have received a copy of the GNU Affero General Public License along with this program.
** If not, see <https://www.gnu.org/licenses/>.
**/


/**
 * @var CView $this
 */

?>
<script>
	const view = new class {

		init({ldap_servers, ldap_default_row_index, db_authentication_type, saml_provision_groups,
				saml_provision_media, templates, mfa_methods, mfa_default_row_index, is_http_auth_allowed
		}) {
			this.form = document.getElementById('authentication-form');
			this.db_authentication_type = db_authentication_type;
			this.saml_provision_status = document.getElementById('saml_provision_status');
			this.saml_provision_groups_table = document.getElementById('saml-group-table');
			this.saml_media_type_mapping_table = document.getElementById('saml-media-type-mapping-table');
			this.ldap_jit_status = document.getElementById('ldap_jit_status');
			this.ldap_servers_table = document.getElementById('ldap-servers');
			this.templates = templates;
			this.is_http_auth_allowed = is_http_auth_allowed;
			this.ldap_provisioning_fields = this.form.querySelectorAll(
				'[name="ldap_jit_status"],[name="ldap_case_sensitive"],[name="jit_provision_interval"]'
			);
			this.jit_provision_interval = this.form.querySelector('[name="jit_provision_interval"]');
			this.ldap_auth_enabled = this.form.querySelector('[type="checkbox"][name="ldap_auth_enabled"]');
			this.mfa_table = document.getElementById('mfa-methods');
			const saml_readonly = !this.form.querySelector('[type="checkbox"][name="saml_auth_enabled"]').checked;
			const ldap_disabled = this.ldap_auth_enabled === null || !this.ldap_auth_enabled.checked;
			const mfa_readonly = !this.form.querySelector('[type="checkbox"][name="mfa_status"]').checked;

			this._addEventListeners();
			this._addLdapServers(ldap_servers, ldap_default_row_index);
			this.#setTableVisiblityState(this.ldap_servers_table, ldap_disabled);
			this.#disableRemoveLinksWithUserGroups(this.ldap_servers_table);
			this._renderProvisionGroups(saml_provision_groups);
			this.#setTableVisiblityState(this.saml_provision_groups_table, saml_readonly);
			this._renderProvisionMedia(saml_provision_media);
			this.#setTableVisiblityState(this.saml_media_type_mapping_table, saml_readonly)
			this.#addMfaMethods(mfa_methods, mfa_default_row_index);
			this.#setTableVisiblityState(this.mfa_table, mfa_readonly);
			this.#disableRemoveLinksWithUserGroups(this.mfa_table);

			this.form.querySelector('[type="checkbox"][name="saml_auth_enabled"]').dispatchEvent(new Event('change'));
		}

		_addEventListeners() {
			this.#addLdapSettingsEventListeners();

			if (this.is_http_auth_allowed) {
				document.getElementById('http_auth_enabled').addEventListener('change', (e) => {
					this.form.querySelectorAll('[name^=http_]').forEach(field => {
						if (!field.isSameNode(e.target)) {
							field.disabled = !e.target.checked;
						}
					});

					if (e.target.checked) {
						let form_fields = this.form.querySelectorAll('[name^=http_]');

						const http_auth_enabled = document.getElementById('http_auth_enabled');
						overlayDialogue({
							'title': <?= json_encode(_('Confirm changes')) ?>,
							'class': 'position-middle',
							'content': document.createElement('span').innerText = <?= json_encode(
								_('Enable HTTP authentication for all users.')
							) ?>,
							'buttons': [
								{
									'title': <?= json_encode(_('Cancel')) ?>,
									'cancel': true,
									'class': '<?= ZBX_STYLE_BTN_ALT ?>',
									'action': function () {
										for (const form_field of form_fields) {
											if (form_field !== http_auth_enabled) {
												form_field.disabled = true;
											}
										}

										http_auth_enabled.checked = false;
										document.getElementById('tab_http').setAttribute('data-indicator-value', '0');
									}
								},
								{
									'title': <?= json_encode(_('Ok')) ?>,
									'focused': true,
									'action': function () {}
								}
							]
						}, e.target);
					}
				});
			}

			this.form.querySelector('[type="checkbox"][name="saml_auth_enabled"]').addEventListener('change', (e) => {
				const is_readonly = !e.target.checked;

				this.form.querySelectorAll('.saml-enabled').forEach(field => {
					field.toggleAttribute('readonly', is_readonly);
					field.toggleAttribute('disabled', is_readonly);
					field.setAttribute('tabindex', is_readonly ? -1 : 0);
				});
				this.#setTableVisiblityState(this.saml_provision_groups_table, is_readonly);
				this.#setTableVisiblityState(this.saml_media_type_mapping_table, is_readonly);
			});

			this.saml_provision_status.addEventListener('change', (e) => {
				this.form.querySelectorAll('.saml-provision-status').forEach(field =>
					field.classList.toggle('<?= ZBX_STYLE_DISPLAY_NONE ?>', !e.target.checked)
				);
			});

			this.saml_provision_groups_table.addEventListener('click', (e) => {
				if (e.target.classList.contains('disabled')) {
					return;
				}
				else if (e.target.classList.contains('js-add')) {
					this.editSamlProvisionGroup();
				}
				else if (e.target.classList.contains('js-edit')) {
					this.editSamlProvisionGroup(e.target.closest('tr'));
				}
				else if (e.target.classList.contains('js-remove')) {
					e.target.closest('tr').remove();
				}
			});

			this.saml_media_type_mapping_table
				.addEventListener('click', (e) => {
					if (e.target.classList.contains('disabled')) {
						return;
					}
					else if (e.target.classList.contains('js-add')) {
						this.editSamlProvisionMedia();
					}
					else if (e.target.classList.contains('js-edit')) {
						this.editSamlProvisionMedia(e.target.closest('tr'));
					}
					else if (e.target.classList.contains('js-remove')) {
						e.target.closest('tr').remove();
					}
				});

			this.form.addEventListener('submit', (e) => {
				if (!this._authFormSubmit()) {
					e.preventDefault();
				}
			});

			this.mfa_table.addEventListener('click', (e) => {
				if (e.target.classList.contains('disabled')) {
					return;
				}
				else if (e.target.classList.contains('js-add')) {
					this.editMfaMethod();
				}
				else if (e.target.classList.contains('js-edit')) {
					this.editMfaMethod(e.target.closest('tr'));
				}
				else if (e.target.classList.contains('js-remove')) {
					const table = e.target.closest('table');
					const mfaid_input = e.target.closest('tr')
						.querySelector('input[name$="[mfaid]"]');

					if (mfaid_input !== null) {
						const input = document.createElement('input');
						input.type = 'hidden';
						input.name = 'mfa_removed_mfaids[]';
						input.value = mfaid_input.value;
						this.form.appendChild(input);
					}

					e.target.closest('tr').remove();

					if (table.querySelector('input[name="mfa_default_row_index"]:checked') === null) {
						const default_mfa = table.querySelector('input[name="mfa_default_row_index"]');

						if (default_mfa !== null) {
							default_mfa.checked = true;
						}
					}
				}
			});

			this.form.querySelector('[type="checkbox"][name="mfa_status"]').addEventListener('change', (e) => {
				const is_readonly = !e.target.checked;
				const default_index = this.form.querySelector('input[name="mfa_default_row_index"]:checked');
				const default_index_hidden = this.form.querySelector('[type="hidden"][name="mfa_default_row_index"]');

				this.#setTableVisiblityState(this.mfa_table, is_readonly);
				this.#disableRemoveLinksWithUserGroups(this.mfa_table);

				if (is_readonly && default_index) {
					default_index_hidden.value = default_index.value;
				}
			});
		}

		#addLdapSettingsEventListeners() {
			if (this.ldap_auth_enabled === null) {
				return;
			}

			this.ldap_servers_table.addEventListener('click', (e) => {
				if (e.target.classList.contains('disabled')) {
					return;
				}
				else if (e.target.classList.contains('js-add')) {
					this.editLdapServer();
				}
				else if (e.target.classList.contains('js-edit')) {
					this.editLdapServer(e.target.closest('tr'));
				}
				else if (e.target.classList.contains('js-remove')) {
					const table = e.target.closest('table');
					const userdirectoryid_input = e.target.closest('tr')
						.querySelector('input[name$="[userdirectoryid]"]');

					if (userdirectoryid_input !== null) {
						const input = document.createElement('input');
						input.type = 'hidden';
						input.name = 'ldap_removed_userdirectoryids[]';
						input.value = userdirectoryid_input.value;
						this.form.appendChild(input);
					}

					e.target.closest('tr').remove();

					if (table.querySelector('input[name="ldap_default_row_index"]:checked') === null) {
						const default_ldap = table.querySelector('input[name="ldap_default_row_index"]');

						if (default_ldap !== null) {
							default_ldap.checked = true;
						}
					}
				}
			});

			this.ldap_jit_status.addEventListener('change', this.#updateLdapFieldsState.bind(this));
			this.ldap_auth_enabled.addEventListener('change', this.#updateLdapFieldsState.bind(this));
		}

		#updateLdapFieldsState() {
			const ldap_disabled = !this.ldap_auth_enabled.checked;
			const provision_disabled = ldap_disabled || !this.ldap_jit_status.checked;

			this.ldap_provisioning_fields.forEach(field => field.toggleAttribute('disabled', ldap_disabled));
			this.#setTableVisiblityState(this.ldap_servers_table, ldap_disabled);
			this.#disableRemoveLinksWithUserGroups(this.ldap_servers_table);
			this.jit_provision_interval.toggleAttribute('disabled', provision_disabled);
		}

		_authFormSubmit() {
			const fields_to_trim = ['#http_strip_domains', '#idp_entityid', '#sso_url', '#slo_url',
				'#username_attribute', '#sp_entityid', '#nameid_format', '#saml_group_name', '#saml_user_username',
				'#saml_user_lastname'
			];
			document.querySelectorAll(fields_to_trim.join(', ')).forEach((elem) => {
				elem.value = elem.value.trim();
			});

			const auth_type = document.querySelector('[name=authentication_type]:checked').value;
			const warning_msg = <?= json_encode(
				_('Switching authentication method will reset all except this session! Continue?')
			) ?>;

			return (auth_type == this.db_authentication_type || confirm(warning_msg));
		}

		_addLdapServers(ldap_servers, ldap_default_row_index) {
			for (const [row_index, ldap] of Object.entries(ldap_servers)) {
				ldap.row_index = row_index;
				ldap.is_default = (ldap.row_index == ldap_default_row_index) ? 'checked' : '';

				this.ldap_servers_table
					.querySelector('tbody')
					.appendChild(this._prepareServerRow(ldap));
			}
		}

		#disableRemoveLinksWithUserGroups(table) {
			table.querySelectorAll('[data-disable_remove] .js-remove').forEach(field => field.disabled = true);
		}

		_renderProvisionGroups(saml_provision_groups) {
			for (const [row_index, saml_provision_group] of Object.entries(saml_provision_groups)) {
				saml_provision_group.row_index = row_index;

				this.saml_provision_groups_table
					.querySelector('tbody')
					.appendChild(this._renderProvisionGroupRow(saml_provision_group));
			}
		}

		_renderProvisionMedia(saml_provision_media) {
			for (const [row_index, saml_media] of Object.entries(saml_provision_media)) {
				saml_media.row_index = row_index;

				this.saml_media_type_mapping_table
					.querySelector('tbody')
					.appendChild(this._renderProvisionMediaRow(saml_media));
			}
		}

		#setTableVisiblityState(table, readonly) {
			table.classList.toggle('disabled', readonly);
			table.querySelectorAll('a,input:not([type="hidden"]),button').forEach(node => {
				node.toggleAttribute('disabled', readonly);
				node.classList.toggle('disabled', readonly);
			});
		}

		editSamlProvisionGroup(row = null) {
			let popup_params = {};
			let row_index = 0;

			if (row !== null) {
				row_index = row.dataset.row_index;

				popup_params.name = row.querySelector(`[name="saml_provision_groups[${row_index}][name]"`).value;

				const user_groups = row.querySelectorAll(
					`[name="saml_provision_groups[${row_index}][user_groups][][usrgrpid]"`
				);
				if (user_groups.length) {
					popup_params.usrgrpid = [...user_groups].map(usrgrp => usrgrp.value);
				}

				const roleid = row.querySelector(`[name="saml_provision_groups[${row_index}][roleid]"`);
				if (roleid) {
					popup_params.roleid = roleid.value;
				}
			}
			else {
				while (this.saml_provision_groups_table.querySelector(`[data-row_index="${row_index}"]`) !== null) {
					row_index++;
				}

				popup_params = {
					add_group: 1,
					name: ''
				};
			}

			popup_params.idp_type = <?= IDP_TYPE_SAML ?>;

			const overlay = PopUp('popup.usergroupmapping.edit', popup_params,
				{dialogueid: 'user_group_edit', dialogue_class: 'modal-popup-medium'}
			);

			overlay.$dialogue[0].addEventListener('dialogue.submit', (e) => {
				const new_row = this._renderProvisionGroupRow({...e.detail, ...{row_index}});

				if (row === null) {
					this.saml_provision_groups_table.querySelector('tbody').appendChild(new_row);
				}
				else {
					row.replaceWith(new_row);
				}
			});
		}

		editSamlProvisionMedia(row = null) {
			let popup_params;
			let row_index = 0;

			if (row !== null) {
				row_index = row.dataset.row_index;

				popup_params = Object.fromEntries(
					[...row.querySelectorAll(`[name^="saml_provision_media[${row_index}]"]`)].map(
						i => [i.name.match(/\[([^\]]+)\]$/)[1], i.value]
				));
			}
			else {
				while (this.saml_media_type_mapping_table.querySelector(`[data-row_index="${row_index}"]`) !== null) {
					row_index++;
				}

				popup_params = {
					add_media_type_mapping: 1
				};
			}

			const overlay = PopUp('popup.mediatypemapping.edit', popup_params,
				{dialogueid: 'media_type_mapping_edit', dialogue_class: 'modal-popup-medium'}
			);

			overlay.$dialogue[0].addEventListener('dialogue.submit', (e) => {
				const saml_media_type_mapping = {...e.detail, ...{row_index: row_index}};

				if (row === null) {
					this.saml_media_type_mapping_table
						.querySelector('tbody')
						.appendChild(this._renderProvisionMediaRow(saml_media_type_mapping));
				}
				else {
					row.replaceWith(this._renderProvisionMediaRow(saml_media_type_mapping));
				}
			});
		}

		editLdapServer(row = null) {
			let popup_params;
			let row_index = 0;

			if (row !== null) {
				row_index = row.dataset.row_index;

				const provision_group_indexes = [...row.querySelectorAll(
					`[name^="ldap_servers[${row_index}][provision_groups]"][name$="[name]"]`
				)].map((element) => {
					let start = 33 + row_index.toString().length;
					let end = element.name.length - 7;
					return element.name.substring(start, end);
				});

				const provision_groups = provision_group_indexes.map((i) => {
					let user_groups = row.querySelectorAll(
						`[name="ldap_servers[${row_index}][provision_groups][${i}][user_groups][][usrgrpid]"`
					);
					let group_name = row.querySelector(
						`[name="ldap_servers[${row_index}][provision_groups][${i}][name]"`
					);
					let provision_group = {
						roleid: row.querySelector(
							`[name="ldap_servers[${row_index}][provision_groups][${i}][roleid]"`
						).value,
						user_groups: [...user_groups].map(usrgrp => usrgrp.value)
					}

					if (group_name) {
						provision_group.name = group_name.value;
					}

					return provision_group;
				});

				const provision_media_indexes = [...row.querySelectorAll(
					`[name^="ldap_servers[${row_index}][provision_media]"][name$="[name]"]`
				)].map((element) => {
					let start = 32 + row_index.toString().length;
					let end = element.name.length - 7;

					return element.name.substring(start, end);
				});
				const provision_media = provision_media_indexes.map((i) => {
					return Object.fromEntries(
						[...row.querySelectorAll(`[name^="ldap_servers[${row_index}][provision_media][${i}]"]`)].map(
							i => [i.name.match(/\[([^\]]+)\]$/)[1], i.value]
					));
				});

				popup_params = {
					row_index,
					add_ldap_server: 0,
					name: row.querySelector(`[name="ldap_servers[${row_index}][name]"`).value,
					host: row.querySelector(`[name="ldap_servers[${row_index}][host]"`).value,
					port: row.querySelector(`[name="ldap_servers[${row_index}][port]"`).value,
					base_dn: row.querySelector(`[name="ldap_servers[${row_index}][base_dn]"`).value,
					search_attribute: row.querySelector(`[name="ldap_servers[${row_index}][search_attribute]"`).value,
					search_filter: row.querySelector(`[name="ldap_servers[${row_index}][search_filter]"`).value,
					start_tls: row.querySelector(`[name="ldap_servers[${row_index}][start_tls]"`).value,
					bind_dn: row.querySelector(`[name="ldap_servers[${row_index}][bind_dn]"`).value,
					description: row.querySelector(`[name="ldap_servers[${row_index}][description]"`).value,
					provision_status: row.querySelector(`[name="ldap_servers[${row_index}][provision_status]"`).value,
					group_basedn: row.querySelector(`[name="ldap_servers[${row_index}][group_basedn]"`).value,
					group_name: row.querySelector(`[name="ldap_servers[${row_index}][group_name]"`).value,
					group_member: row.querySelector(`[name="ldap_servers[${row_index}][group_member]"`).value,
					user_ref_attr: row.querySelector(`[name="ldap_servers[${row_index}][user_ref_attr]"`).value,
					group_filter: row.querySelector(`[name="ldap_servers[${row_index}][group_filter]"`).value,
					group_membership: row.querySelector(`[name="ldap_servers[${row_index}][group_membership]"`).value,
					user_username: row.querySelector(`[name="ldap_servers[${row_index}][user_username]"`).value,
					user_lastname: row.querySelector(`[name="ldap_servers[${row_index}][user_lastname]"`).value,
					provision_groups,
					provision_media
				};

				const userdirectoryid_input = row.querySelector(`[name="ldap_servers[${row_index}][userdirectoryid]"`);
				const bind_password_input = row.querySelector(`[name="ldap_servers[${row_index}][bind_password]"`);

				if (userdirectoryid_input !== null) {
					popup_params['userdirectoryid'] = userdirectoryid_input.value;
				}

				if (bind_password_input !== null) {
					popup_params['bind_password'] = bind_password_input.value;
				}
			}
			else {
				while (document.querySelector(`#ldap-servers [data-row_index="${row_index}"]`) !== null) {
					row_index++;
				}

				popup_params = {
					row_index,
					add_ldap_server: 1
				};
			}

			const overlay = PopUp('popup.ldap.edit', popup_params,
				{dialogueid: 'ldap_edit', dialogue_class: 'modal-popup-generic'}
			);

			overlay.$dialogue[0].addEventListener('dialogue.submit', (e) => {
				const ldap = {...e.detail, ...{row_index: row_index}};

				if (row === null) {
					ldap.is_default = document.getElementById('ldap-servers')
							.querySelector('input[name="ldap_default_row_index"]:checked') === null;
					ldap.usrgrps = 0;

					this.ldap_servers_table
						.querySelector('tbody')
						.appendChild(this._prepareServerRow(ldap));
				}
				else {
					ldap.is_default = row.querySelector('input[name="ldap_default_row_index"]').checked === true;
					ldap.usrgrps = row.querySelector('.js-ldap-usergroups').textContent;

					row.parentNode.insertBefore(this._prepareServerRow(ldap), row);
					row.remove();
				}
			});
		}

		_prepareServerRow(ldap) {
			const template_ldap_server_row = new Template(this.templates.ldap_servers_row);
			const template = document.createElement('template');
			template.innerHTML = template_ldap_server_row.evaluate(ldap).trim();
			const row = template.content.firstChild;

			row.querySelector('[name="ldap_default_row_index"]').toggleAttribute('checked', ldap.is_default);

			if ('provision_groups' in ldap) {
				for (const [group_index, provision_group] of Object.entries(ldap.provision_groups)) {
					for (const [name, value] of Object.entries(provision_group)) {
						if (name === 'user_groups') {
							for (const usrgrp of value) {
								const input = document.createElement('input');
								input.name = 'ldap_servers[' + ldap.row_index + '][provision_groups][' + group_index + '][user_groups][][usrgrpid]';
								input.value = usrgrp.usrgrpid;
								input.type = 'hidden';
								row.appendChild(input);
							}
						}
						else {
							const input = document.createElement('input');
							input.name = 'ldap_servers[' + ldap.row_index + '][provision_groups][' + group_index + '][' + name + ']';
							input.value = value;
							input.type = 'hidden';
							row.appendChild(input);
						}
					}
				}
			}

			if ('provision_media' in ldap) {
				for (const [group_index, media] of Object.entries(ldap.provision_media)) {
					for (const [name, value] of Object.entries(media)) {
						if (name === 'mediatype_name') {
							continue;
						}
						const input = document.createElement('input');
						input.name = 'ldap_servers[' + ldap.row_index + '][provision_media][' + group_index + '][' + name + ']';
						input.value = value;
						input.type = 'hidden';
						row.appendChild(input);
					}
				}
			}

			const optional_fields = ['userdirectoryid', 'bind_password', 'start_tls', 'search_filter'];

			for (const field of optional_fields) {
				if (!(field in ldap)) {
					row.querySelector('input[name="ldap_servers[' + ldap.row_index + '][' + field + ']"]').remove();
				}
			}

			if (ldap.usrgrps > 0) {
				row.querySelector('.js-remove').disabled = true;
				row.dataset.disable_remove = true;
			}

			return row;
		}

		_renderProvisionGroupRow(saml_provision_group) {
			saml_provision_group.user_group_names = ('user_groups' in saml_provision_group)
				? Object.values(saml_provision_group.user_groups).map(user_group => user_group.name).join(', ')
				: '';

			const template = document.createElement('template');
			const template_saml_group_row = new Template(this.templates.saml_provisioning_group_row);
			template.innerHTML = template_saml_group_row.evaluate(saml_provision_group).trim();
			const row = template.content.firstChild;

			if ('user_groups' in saml_provision_group) {
				for (const user_group of Object.values(saml_provision_group.user_groups)) {
					const input = document.createElement('input');
					input.name = 'saml_provision_groups[' + saml_provision_group.row_index + '][user_groups][][usrgrpid]';
					input.value = user_group.usrgrpid;
					input.type = 'hidden';

					row.appendChild(input);
				}
			}

			if ('roleid' in saml_provision_group) {
				const input = document.createElement('input');
				input.name = 'saml_provision_groups[' + saml_provision_group.row_index + '][roleid]';
				input.value = saml_provision_group.roleid;
				input.type = 'hidden';

				row.appendChild(input);
			}

			return row;
		}

		_renderProvisionMediaRow(saml_media) {
			const template_saml_media_mapping_row = new Template(this.templates.saml_provisioning_media_row);
			const template = document.createElement('template');

			template.innerHTML = template_saml_media_mapping_row.evaluate(saml_media).trim();

			if (saml_media.userdirectory_mediaid === undefined) {
				template.content.firstChild.querySelector('[name$="[userdirectory_mediaid]"]').remove();
			}

			return template.content.firstChild;
		}

		#addMfaMethods(mfa_methods, mfa_default_row_index) {
			for (const [row_index, mfa] of Object.entries(mfa_methods)) {
				mfa.row_index = row_index;
				mfa.is_default = (mfa.row_index == mfa_default_row_index) ? 'checked' : '';

				this.mfa_table
					.querySelector('tbody')
					.appendChild(this.#prepareMfaRow(mfa));
			}
		}

		#prepareMfaRow(mfa) {
			const template_mfa_methods_row = new Template(this.templates.mfa_methods_row);
			const template = document.createElement('template');
			template.innerHTML = template_mfa_methods_row.evaluate(mfa).trim();
			const row = template.content.firstChild;

			row.querySelector('[name="mfa_default_row_index"]').toggleAttribute('checked', mfa.is_default);

			const optional_fields = ['mfaid', 'hash_function', 'code_length', 'api_hostname', 'clientid',
				'client_secret'
			];

			for (const field of optional_fields) {
				if (!(field in mfa)) {
					row.querySelector(`input[name="mfa_methods[${mfa.row_index}][${field}]"]`).remove();
				}
			}

			if (mfa.usrgrps > 0) {
				row.querySelector('.js-remove').disabled = true;
				row.dataset.disable_remove = true;
			}

			return row;
		}

		editMfaMethod(row = null) {
			let popup_params;
			let row_index = 0;

			if (row !== null) {
				row_index = row.dataset.row_index;

				popup_params = {
					row_index,
					add_mfa_method: 0,
					type: row.querySelector(`[name="mfa_methods[${row_index}][type]"`).value,
					name: row.querySelector(`[name="mfa_methods[${row_index}][name]"`).value,
					hash_function: row.querySelector(`[name="mfa_methods[${row_index}][hash_function]"`)?.value,
					code_length: row.querySelector(`[name="mfa_methods[${row_index}][code_length]"`)?.value,
					api_hostname: row.querySelector(`[name="mfa_methods[${row_index}][api_hostname]"`)?.value,
					clientid: row.querySelector(`[name="mfa_methods[${row_index}][clientid]"`)?.value,
					client_secret: row.querySelector(`[name="mfa_methods[${row_index}][client_secret]"`)?.value
				};

				const mfaid_input = row.querySelector(`[name="mfa_methods[${row_index}][mfaid]"`);

				if (mfaid_input !== null) {
					popup_params['mfaid'] = mfaid_input.value;
				}
			}
			else {
				while (document.querySelector(`#mfa-methods [data-row_index="${row_index}"]`) !== null) {
					row_index++;
				}

				popup_params = {
					row_index,
					add_mfa_method: 1
				};
			}

			const overlay = PopUp('mfa.edit', popup_params,
				{dialogueid: 'mfa_edit', dialogue_class: 'modal-popup-small'}
			);

			overlay.$dialogue[0].addEventListener('dialogue.submit', (e) => {
				const mfa = {...e.detail, row_index};

				if (row === null) {
					mfa.is_default = document.getElementById('mfa-methods')
						.querySelector('[name="mfa_default_row_index"]:checked') === null;
					mfa.usrgrps = 0;

					this.mfa_table
						.querySelector('tbody')
						.appendChild(this.#prepareMfaRow(mfa));
				}
				else {
					mfa.is_default = row.querySelector('[name="mfa_default_row_index"]').checked === true;
					mfa.usrgrps = row.querySelector('.js-mfa-usergroups').textContent;

					row.parentNode.insertBefore(this.#prepareMfaRow(mfa), row);
					row.remove();
				}
			});
		}
	};
</script>