From 35f2f89aab9d9b900d92deb945da6ff2673bde0d Mon Sep 17 00:00:00 2001 From: Mark Reynolds Date: Tue, 3 Jan 2023 17:32:20 -0500 Subject: [PATCH] Issue 5521 - UI - Update plugins for new split PAM and LDAP pass thru auth Description: Previously PAM and LDAP pass thru auth plugins were merged. This change separates them into their own plugins in the UI. Also improved memory reporting in monitor tab. relates: https://github.com/389ds/389-ds-base/issues/5521 Reviewed by: spichugi(Thanks!) --- .../src/lib/monitor/serverMonitor.jsx | 27 +- .../src/lib/plugins/pamPassThru.jsx | 935 ++++++++++++++++++ .../lib/plugins/passthroughAuthentication.jsx | 884 +---------------- .../src/lib/plugins/pluginBasicConfig.jsx | 2 +- .../src/lib/plugins/pluginTables.jsx | 188 ++-- src/cockpit/389-console/src/plugins.jsx | 22 +- 6 files changed, 1084 insertions(+), 974 deletions(-) create mode 100644 src/cockpit/389-console/src/lib/plugins/pamPassThru.jsx diff --git a/src/cockpit/389-console/src/lib/monitor/serverMonitor.jsx b/src/cockpit/389-console/src/lib/monitor/serverMonitor.jsx index 94f47cd1ca..4d97cff603 100644 --- a/src/cockpit/389-console/src/lib/monitor/serverMonitor.jsx +++ b/src/cockpit/389-console/src/lib/monitor/serverMonitor.jsx @@ -112,6 +112,29 @@ export class ServerMonitor extends React.Component { }); } + convertMemory(mem_str) { + mem_str = mem_str.replace(",", ".") + if (mem_str.endsWith('m')) { + // Convert MB to KB + let mem = mem_str.slice(0, -1); + return parseInt(Math.round(mem * 1024)); + } else if (mem_str.endsWith('g')) { + // Convert GB to KB + let mem = mem_str.slice(0, -1); + return parseInt(Math.round(mem * 1024 * 1024)); + } else if (mem_str.endsWith('t')) { + // Convert TB to KB + let mem = mem_str.slice(0, -1); + return parseInt(Math.round(mem * 1024 * 1024 * 1024)); + } else if (mem_str.endsWith('p')) { + // Convert PB to KB + let mem = mem_str.slice(0, -1); + return parseInt(Math.round(mem * 1024 * 1024 * 1024 * 1024)); + } else { + return mem_str; + } + } + refreshCharts() { const cmd = "ps -ef | grep -v grep | grep dirsrv/slapd-" + this.props.serverId; let cpu = 0; @@ -135,8 +158,8 @@ export class ServerMonitor extends React.Component { .script(cpu_cmd, [], { superuser: true, err: "message" }) .done(top_output => { const top_parts = top_output.trim().split(/\s+/); - virt_mem = top_parts[4]; - res_mem = top_parts[5]; + virt_mem = this.convertMemory(top_parts[4]); + res_mem = this.convertMemory(top_parts[5]); cpu = parseInt(top_parts[8]); const mem_cmd = "awk '/MemTotal/{print $2}' /proc/meminfo"; // log_cmd("refreshCharts", "Get total memory", [mem_cmd]); diff --git a/src/cockpit/389-console/src/lib/plugins/pamPassThru.jsx b/src/cockpit/389-console/src/lib/plugins/pamPassThru.jsx new file mode 100644 index 0000000000..33ff61cd22 --- /dev/null +++ b/src/cockpit/389-console/src/lib/plugins/pamPassThru.jsx @@ -0,0 +1,935 @@ +import cockpit from "cockpit"; +import React from "react"; +import { + Button, + Checkbox, + Form, + FormSelect, + FormSelectOption, + Grid, + GridItem, + Modal, + ModalVariant, + NumberInput, + Select, + SelectOption, + SelectVariant, + TextInput, + ValidatedOptions, +} from "@patternfly/react-core"; +import { PassthroughAuthConfigsTable } from "./pluginTables.jsx"; +import PluginBasicConfig from "./pluginBasicConfig.jsx"; +import PropTypes from "prop-types"; +import { log_cmd, valid_dn, listsEqual } from "../tools.jsx"; +import { DoubleConfirmModal } from "../notifications.jsx"; + +class PAMPassthroughAuthentication extends React.Component { + constructor(props) { + super(props); + this.state = { + firstLoad: true, + pamConfigRows: [], + tableKey: 1, + error: {}, + modalSpinning: false, + modalChecked: false, + + pamConfigName: "", + pamExcludeSuffix: [], + pamIncludeSuffix: [], + pamMissingSuffix: "", + pamFilter: "", + pamIDAttr: "", + pamIDMapMethod: "RDN", + pamFallback: false, + pamSecure: false, + pamService: "ldapserver", + _pamConfigName: "", + _pamExcludeSuffix: [], + _pamIncludeSuffix: [], + _pamMissingSuffix: "", + _pamFilter: "", + _pamIDAttr: "", + _pamIDMapMethod: "RDN", + _pamFallback: false, + _pamSecure: false, + _pamService: "ldapserver", + isExcludeOpen: false, + excludeOptions: [], + isIncludeOpen: false, + includeOptions: [], + showConfirmDeleteConfig: false, + saveBtnDisabledPAM: true, + savingPAM: false, + newPAMConfigEntry: false, + pamConfigEntryModalShow: false, + }; + + this.onExcludeToggle = isExcludeOpen => { + this.setState({ + isExcludeOpen + }); + }; + this.clearExcludeSelection = () => { + this.setState({ + pamExcludeSuffix: [], + isExcludeOpen: false + }, () => { this.validatePAM() }); + }; + this.onExcludeSelect = (event, selection) => { + if (this.state.pamExcludeSuffix.includes(selection)) { + this.setState( + prevState => ({ + pamExcludeSuffix: prevState.pamExcludeSuffix.filter(item => item !== selection), + isExcludeOpen: false + }), () => { this.validatePAM() } + ); + } else { + this.setState( + prevState => ({ + pamExcludeSuffix: [...prevState.pamExcludeSuffix, selection], + isExcludeOpen: false + }), () => { this.validatePAM() } + ); + } + }; + this.onCreateExcludeOption = newValue => { + if (!this.state.excludeOptions.includes(newValue)) { + this.setState({ + excludeOptions: [...this.state.excludeOptions, newValue], + isExcludeOpen: false + }, () => { this.validatePAM() }); + } + }; + + this.onIncludeToggle = isIncludeOpen => { + this.setState({ + isIncludeOpen + }); + }; + this.clearIncludeSelection = () => { + this.setState({ + pamIncludeSuffix: [], + isIncludeOpen: false + }, () => { this.validatePAM() }); + }; + this.onIncludeSelect = (event, selection) => { + if (this.state.pamIncludeSuffix.includes(selection)) { + this.setState( + prevState => ({ + pamIncludeSuffix: prevState.pamIncludeSuffix.filter(item => item !== selection), + isIncludeOpen: false + }) + ); + } else { + this.setState( + prevState => ({ + pamIncludeSuffix: [...prevState.pamIncludeSuffix, selection], + isIncludeOpen: false + }) + ); + } + }; + this.onCreateIncludeOption = newValue => { + if (!this.state.includeOptions.includes(newValue)) { + this.setState({ + includeOptions: [...this.state.includeOptions, newValue], + isIncludeOpen: false + }); + } + }; + + this.maxValue = 20000000; + this.onMinusConfig = (id) => { + this.setState({ + [id]: Number(this.state[id]) - 1 + }, () => { this.validatePassthru() }); + }; + this.onConfigChange = (event, id, min) => { + const newValue = isNaN(event.target.value) ? 0 : Number(event.target.value); + this.setState({ + [id]: newValue > this.maxValue ? this.maxValue : newValue < min ? min : newValue + }, () => { this.validatePassthru() }); + }; + this.onPlusConfig = (id) => { + this.setState({ + [id]: Number(this.state[id]) + 1 + }, () => { this.validatePassthru() }); + }; + + // This vastly improves rendering performance during handleChange() + this.attrRows = this.props.attributes.map((attr) => ( + + )); + + this.handlePAMChange = this.handlePAMChange.bind(this); + this.handleModalChange = this.handleModalChange.bind(this); + this.validatePAM = this.validatePAM.bind(this); + this.loadPAMConfigs = this.loadPAMConfigs.bind(this); + this.openPAMModal = this.openPAMModal.bind(this); + this.closePAMModal = this.closePAMModal.bind(this); + this.showEditPAMConfigModal = this.showEditPAMConfigModal.bind(this); + this.showAddPAMConfigModal = this.showAddPAMConfigModal.bind(this); + this.cmdPAMOperation = this.cmdPAMOperation.bind(this); + this.deletePAMConfig = this.deletePAMConfig.bind(this); + this.addPAMConfig = this.addPAMConfig.bind(this); + this.editPAMConfig = this.editPAMConfig.bind(this); + this.showConfirmDeleteConfig = this.showConfirmDeleteConfig.bind(this); + this.closeConfirmDeleteConfig = this.closeConfirmDeleteConfig.bind(this); + } + + componentDidMount() { + this.loadPAMConfigs(); + } + + validatePAM() { + const errObj = {}; + let all_good = true; + + const reqAttrs = ['pamConfigName', 'pamIDAttr']; + const dnAttrLists = ['pamExcludeSuffix', 'pamExcludeSuffix']; + + // Check we have our required attributes set + for (const attr of reqAttrs) { + if (this.state[attr] == "") { + errObj[attr] = true; + all_good = false; + break; + } + } + + // Check the DN's of our lists + for (const attrList of dnAttrLists) { + for (const dn of this.state[attrList]) { + if (!valid_dn(dn)) { + errObj[attrList] = true; + all_good = false; + break; + } + } + } + + if (all_good) { + // Check for value differences to see if the save btn should be enabled + all_good = false; + const attrLists = ['pamExcludeSuffix', 'pamIncludeSuffix']; + for (const check_attr of attrLists) { + if (!listsEqual(this.state[check_attr], this.state['_' + check_attr])) { + all_good = true; + break; + } + } + + const configAttrs = [ + 'pamConfigName', 'pamFilter', 'pamMissingSuffix', 'pamIDMapMethod', + 'pamIDAttr', 'pamFallback', 'pamSecure', 'pamService' + ]; + for (const check_attr of configAttrs) { + if (this.state[check_attr] != this.state['_' + check_attr]) { + all_good = true; + break; + } + } + } + this.setState({ + saveBtnDisabledPAM: !all_good, + error: errObj + }); + } + + handlePAMChange(e) { + const value = e.target.type === 'checkbox' ? e.target.checked : e.target.value; + this.setState({ + [e.target.id]: value + }, () => { this.validatePAM() }); + } + + handleModalChange(e) { + const value = e.target.type === 'checkbox' ? e.target.checked : e.target.value; + this.setState({ + [e.target.id]: value + }); + } + + showConfirmDeleteConfig (name) { + this.setState({ + showConfirmDeleteConfig: true, + modalChecked: false, + modalSpinning: false, + deleteName: name + }); + } + + closeConfirmDeleteConfig () { + this.setState({ + showConfirmDeleteConfig: false, + modalChecked: false, + modalSpinning: false, + deleteName: "" + }); + } + + loadPAMConfigs() { + this.setState({ + firstLoad: false + }); + const cmd = [ + "dsconf", + "-j", + "ldapi://%2fvar%2frun%2fslapd-" + this.props.serverId + ".socket", + "plugin", + "pam-pass-through-auth", + "list", + ]; + this.props.toggleLoadingHandler(); + log_cmd("loadPAMConfigs", "Get PAM Passthough Authentication Plugin Configs", cmd); + cockpit + .spawn(cmd, { superuser: true, err: "message" }) + .done(content => { + const myObject = JSON.parse(content); + const tableKey = this.state.tableKey + 1; + this.setState({ + pamConfigRows: myObject.items.map(item => item.attrs), + tableKey: tableKey, + }); + this.props.toggleLoadingHandler(); + }) + .fail(err => { + const errMsg = JSON.parse(err); + if (err != 0) { + console.log("loadPAMConfigs failed", errMsg.desc); + } + this.props.toggleLoadingHandler(); + }); + } + + showEditPAMConfigModal(rowData) { + this.openPAMModal(rowData); + } + + showAddPAMConfigModal() { + this.openPAMModal(); + } + + openPAMModal(name) { + if (!name) { + this.setState({ + pamConfigEntryModalShow: true, + newPAMConfigEntry: true, + pamConfigName: "", + pamExcludeSuffix: [], + pamIncludeSuffix: [], + pamMissingSuffix: "", + pamFilter: "", + pamIDAttr: "", + pamIDMapMethod: "RDN", + pamFallback: false, + pamSecure: false, + pamService: "ldapserver", + saveBtnDisabledPAM: true, + }); + } else { + let pamExcludeSuffixList = []; + let pamIncludeSuffixList = []; + const cmd = [ + "dsconf", + "-j", + "ldapi://%2fvar%2frun%2fslapd-" + this.props.serverId + ".socket", + "plugin", + "pam-pass-through-auth", + "config", + name, + "show" + ]; + + this.props.toggleLoadingHandler(); + log_cmd( + "openModal", + "Fetch the PAM Passthough Authentication Plugin pamConfig entry", + cmd + ); + cockpit + .spawn(cmd, { + superuser: true, + err: "message" + }) + .done(content => { + const pamConfigEntry = JSON.parse(content).attrs; + const tableKey = this.state.tableKey + 1; + + if (pamConfigEntry.pamexcludesuffix !== undefined) { + for (const value of pamConfigEntry.pamexcludesuffix) { + pamExcludeSuffixList = [...pamExcludeSuffixList, value]; + } + } + if (pamConfigEntry.pamincludesuffix !== undefined) { + for (const value of pamConfigEntry.pamincludesuffix) { + pamIncludeSuffixList = [...pamIncludeSuffixList, value]; + } + } + + this.setState({ + tableKey: tableKey, + saveBtnDisabledPAM: true, + pamConfigEntryModalShow: true, + newPAMConfigEntry: false, + pamExcludeSuffix: pamExcludeSuffixList, + pamIncludeSuffix: pamIncludeSuffixList, + pamIDAttr: pamConfigEntry.pamidattr[0], + pamConfigName: + pamConfigEntry.cn === undefined ? "" : pamConfigEntry.cn[0], + pamMissingSuffix: + pamConfigEntry.pammissingsuffix === undefined + ? "" + : pamConfigEntry.pammissingsuffix[0], + pamFilter: + pamConfigEntry.pamfilter === undefined + ? "" + : pamConfigEntry.pamfilter[0], + pamIDMapMethod: + pamConfigEntry.pamidmapmethod === undefined + ? "RDN" + : pamConfigEntry.pamidmapmethod[0], + pamFallback: !( + pamConfigEntry.pamfallback === undefined || + pamConfigEntry.pamfallback[0] == "FALSE" + ), + pamSecure: !( + pamConfigEntry.pamsecure === undefined || + pamConfigEntry.pamsecure[0] == "FALSE" + ), + pamService: + pamConfigEntry.pamservice === undefined + ? "ldapserver" + : pamConfigEntry.pamservice[0], + // Backup values + _pamExcludeSuffix: [...pamExcludeSuffixList], + _pamIncludeSuffix: [...pamIncludeSuffixList], + _pamIDAttr: pamConfigEntry.pamidattr[0], + _pamConfigName: + pamConfigEntry.cn === undefined ? "" : pamConfigEntry.cn[0], + _pamMissingSuffix: + pamConfigEntry.pammissingsuffix === undefined + ? "" + : pamConfigEntry.pammissingsuffix[0], + _pamFilter: + pamConfigEntry.pamfilter === undefined + ? "" + : pamConfigEntry.pamfilter[0], + _pamIDMapMethod: + pamConfigEntry.pamidmapmethod === undefined + ? "ldapserver" + : pamConfigEntry.pamidmapmethod[0], + _pamFallback: !( + pamConfigEntry.pamfallback === undefined || + pamConfigEntry.pamfallback[0] == "FALSE" + ), + _pamSecure: !( + pamConfigEntry.pamsecure === undefined || + pamConfigEntry.pamsecure[0] == "FALSE" + ), + _pamService: + pamConfigEntry.pamservice === undefined + ? "ldapserver" + : pamConfigEntry.pamservice[0], + }); + this.props.toggleLoadingHandler(); + }) + .fail(_ => { + this.setState({ + pamConfigEntryModalShow: true, + newPAMConfigEntry: true, + pamConfigName: "", + pamExcludeSuffix: [], + pamIncludeSuffix: [], + pamMissingSuffix: "", + pamFilter: "", + pamIDAttr: "", + pamIDMapMethod: "", + pamFallback: false, + pamSecure: false, + pamService: "", + saveBtnDisabledPAM: true, + }); + this.props.toggleLoadingHandler(); + }); + } + } + + closePAMModal() { + this.setState({ + pamConfigEntryModalShow: false, + savingPAM: false, + }); + } + + deletePAMConfig() { + const cmd = [ + "dsconf", + "-j", + "ldapi://%2fvar%2frun%2fslapd-" + this.props.serverId + ".socket", + "plugin", + "pam-pass-through-auth", + "config", + this.state.deleteName, + "delete" + ]; + + this.setState({ + modalSpinning: true + }); + + this.props.toggleLoadingHandler(); + log_cmd( + "deletePAMConfig", + "Delete the PAM Passthough Authentication Plugin pamConfig entry", + cmd + ); + cockpit + .spawn(cmd, { + superuser: true, + err: "message" + }) + .done(content => { + console.info("deletePAMConfig", "Result", content); + this.props.addNotification( + "success", + `PAMConfig entry ${this.state.deleteName} was successfully deleted` + ); + this.loadPAMConfigs(); + this.closeConfirmDeleteConfig(); + this.props.toggleLoadingHandler(); + }) + .fail(err => { + const errMsg = JSON.parse(err); + this.props.addNotification( + "error", + `Error during the pamConfig entry removal operation - ${errMsg.desc}` + ); + this.loadPAMConfigs(); + this.closeConfirmDeleteConfig(); + this.props.toggleLoadingHandler(); + }); + } + + addPAMConfig() { + this.cmdPAMOperation("add"); + } + + editPAMConfig() { + this.cmdPAMOperation("set"); + } + + cmdPAMOperation(action) { + // Save table here too + const { + pamConfigName, + pamExcludeSuffix, + pamIncludeSuffix, + pamMissingSuffix, + pamFilter, + pamIDAttr, + pamIDMapMethod, + pamFallback, + pamSecure, + pamService + } = this.state; + + let cmd = [ + "dsconf", + "-j", + "ldapi://%2fvar%2frun%2fslapd-" + this.props.serverId + ".socket", + "plugin", + "pam-pass-through-auth", + "config", + pamConfigName, + action, + "--missing-suffix", + pamMissingSuffix || action == "add" ? pamMissingSuffix : "delete", + "--filter", + pamFilter || action == "add" ? pamFilter : "delete", + "--id_map_method", + pamIDMapMethod || action == "add" ? pamIDMapMethod : "delete", + "--fallback", + pamFallback ? "TRUE" : "FALSE", + "--secure", + pamSecure ? "TRUE" : "FALSE", + "--service", + pamService || action == "add" ? pamService : "delete" + ]; + + cmd = [...cmd, "--exclude-suffix"]; + if (pamExcludeSuffix.length != 0) { + for (const value of pamExcludeSuffix) { + cmd = [...cmd, value]; + } + } else if (action == "add") { + cmd = [...cmd, ""]; + } else { + cmd = [...cmd, "delete"]; + } + cmd = [...cmd, "--include-suffix"]; + if (pamIncludeSuffix.length != 0) { + for (const value of pamIncludeSuffix) { + cmd = [...cmd, value]; + } + } else if (action == "add") { + cmd = [...cmd, ""]; + } else { + cmd = [...cmd, "delete"]; + } + cmd = [...cmd, "--id-attr"]; + if (pamIDAttr != "") { + cmd = [...cmd, pamIDAttr]; + } else if (action == "add") { + cmd = [...cmd, ""]; + } else { + cmd = [...cmd, "delete"]; + } + + this.setState({ + savingPAM: true + }); + log_cmd( + "pamPassthroughAuthOperation", + `Do the ${action} operation on the PAM Passthough Authentication Plugin`, + cmd + ); + cockpit + .spawn(cmd, { + superuser: true, + err: "message" + }) + .done(content => { + console.info("pamPassthroughAuthOperation", "Result", content); + this.props.addNotification( + "success", + `The ${action} operation was successfully done on "${pamConfigName}" entry` + ); + this.loadPAMConfigs(); + this.closePAMModal(); + }) + .fail(err => { + const errMsg = JSON.parse(err); + this.props.addNotification( + "error", + `Error during the pamConfig entry ${action} operation - ${errMsg.desc}` + ); + this.loadPAMConfigs(); + this.closePAMModal(); + }); + } + + render() { + const { + pamConfigRows, + pamConfigName, + pamMissingSuffix, + pamExcludeSuffix, + pamIncludeSuffix, + pamFilter, + pamIDAttr, + pamIDMapMethod, + pamFallback, + pamSecure, + pamService, + newPAMConfigEntry, + pamConfigEntryModalShow, + error, + savingPAM, + } = this.state; + + let saveBtnName = "Save Config"; + const extraPrimaryProps = {}; + if (this.state.savingPAM) { + saveBtnName = "Saving Config ..."; + extraPrimaryProps.spinnerAriaValueText = "Saving"; + } + + const title = (newPAMConfigEntry ? "Add" : "Edit") + " PAM Passthough Auth Config Entry"; + + return ( +
+ + {saveBtnName} + , + + ]} + > +
+ + + Config Name + + + { + this.handlePAMChange(e); + }} + isDisabled={!newPAMConfigEntry} + validated={error.pamConfigName ? ValidatedOptions.error : ValidatedOptions.default} + /> + + + + + Exclude Suffix + + + + + + + + Include Suffix + + + + + + + + ID Attribute + + + { + this.handlePAMChange(event); + }} + aria-label="FormSelect Input" + > + + {this.attrRows} + + + + + + Missing Suffix + + + { + this.handlePAMChange(event); + }} + aria-label="FormSelect Input" + > + + + + + + + + + Filter + + + { + this.handlePAMChange(e); + }} + validated={error.pamFilter ? ValidatedOptions.error : ValidatedOptions.default} + /> + + + + + Map Method + + + { + this.handlePAMChange(event); + }} + aria-label="FormSelect Input" + > + + + + + + + + + Service + + + { + this.handlePAMChange(event); + }} + aria-label="FormSelect Input" + > + + + + + + + + Fallback Auth Enabled + + + { this.handlePAMChange(e) }} + title="Sets whether to fallback to regular LDAP authentication if PAM authentication fails (pamFallback)" + /> + + + + + Require Secure Connection + + + { this.handlePAMChange(e) }} + title="Requires secure TLS connection for PAM authentication (pamSecure)" + /> + + +
+
+ + + + + + +
+ ); + } +} + +PAMPassthroughAuthentication.propTypes = { + rows: PropTypes.array, + serverId: PropTypes.string, + savePluginHandler: PropTypes.func, + pluginListHandler: PropTypes.func, + addNotification: PropTypes.func, + toggleLoadingHandler: PropTypes.func +}; + +PAMPassthroughAuthentication.defaultProps = { + rows: [], + serverId: "", +}; + +export default PAMPassthroughAuthentication; diff --git a/src/cockpit/389-console/src/lib/plugins/passthroughAuthentication.jsx b/src/cockpit/389-console/src/lib/plugins/passthroughAuthentication.jsx index 158a7d5092..fbb7e751d7 100644 --- a/src/cockpit/389-console/src/lib/plugins/passthroughAuthentication.jsx +++ b/src/cockpit/389-console/src/lib/plugins/passthroughAuthentication.jsx @@ -14,64 +14,25 @@ import { Select, SelectOption, SelectVariant, - Tab, - Tabs, - TabTitleText, TextInput, ValidatedOptions, } from "@patternfly/react-core"; -import { PassthroughAuthURLsTable, PassthroughAuthConfigsTable } from "./pluginTables.jsx"; -import PluginBasicPAMConfig from "./pluginBasicConfig.jsx"; +import { PassthroughAuthURLsTable } from "./pluginTables.jsx"; +import PluginBasicConfig from "./pluginBasicConfig.jsx"; import PropTypes from "prop-types"; import { log_cmd, valid_dn, listsEqual } from "../tools.jsx"; import { DoubleConfirmModal } from "../notifications.jsx"; class PassthroughAuthentication extends React.Component { - componentDidMount() { - this.loadPAMConfigs(); - this.loadURLs(); - } - constructor(props) { super(props); this.state = { firstLoad: true, - pamConfigRows: [], urlRows: [], tableKey: 1, - activeTabKey: 0, error: {}, modalSpinning: false, modalChecked: false, - - pamConfigName: "", - pamExcludeSuffix: [], - pamIncludeSuffix: [], - pamMissingSuffix: "", - pamFilter: "", - pamIDAttr: "", - pamIDMapMethod: "RDN", - pamFallback: false, - pamSecure: false, - pamService: "ldapserver", - _pamConfigName: "", - _pamExcludeSuffix: [], - _pamIncludeSuffix: [], - _pamMissingSuffix: "", - _pamFilter: "", - _pamIDAttr: "", - _pamIDMapMethod: "RDN", - _pamFallback: false, - _pamSecure: false, - _pamService: "ldapserver", - isExcludeOpen: false, - excludeOptions: [], - isIncludeOpen: false, - includeOptions: [], - showConfirmDeleteConfig: false, - saveBtnDisabledPAM: true, - savingPAM: false, - oldURL: "", urlConnType: "ldaps", urlAuthDS: "", @@ -95,93 +56,10 @@ class PassthroughAuthentication extends React.Component { saveBtnDisabledPassthru: true, savingPassthru: false, - newPAMConfigEntry: false, newURLEntry: false, - pamConfigEntryModalShow: false, urlEntryModalShow: false }; - // Toggle currently active tab - this.handleNavSelect = (event, tabIndex) => { - this.setState({ - activeTabKey: tabIndex - }); - }; - - this.onExcludeToggle = isExcludeOpen => { - this.setState({ - isExcludeOpen - }); - }; - this.clearExcludeSelection = () => { - this.setState({ - pamExcludeSuffix: [], - isExcludeOpen: false - }, () => { this.validatePAM() }); - }; - this.onExcludeSelect = (event, selection) => { - if (this.state.pamExcludeSuffix.includes(selection)) { - this.setState( - prevState => ({ - pamExcludeSuffix: prevState.pamExcludeSuffix.filter(item => item !== selection), - isExcludeOpen: false - }), () => { this.validatePAM() } - ); - } else { - this.setState( - prevState => ({ - pamExcludeSuffix: [...prevState.pamExcludeSuffix, selection], - isExcludeOpen: false - }), () => { this.validatePAM() } - ); - } - }; - this.onCreateExcludeOption = newValue => { - if (!this.state.excludeOptions.includes(newValue)) { - this.setState({ - excludeOptions: [...this.state.excludeOptions, newValue], - isExcludeOpen: false - }, () => { this.validatePaAM() }); - } - }; - - this.onIncludeToggle = isIncludeOpen => { - this.setState({ - isIncludeOpen - }); - }; - this.clearIncludeSelection = () => { - this.setState({ - pamIncludeSuffix: [], - isIncludeOpen: false - }, () => { this.validatePAM() }); - }; - this.onIncludeSelect = (event, selection) => { - if (this.state.pamIncludeSuffix.includes(selection)) { - this.setState( - prevState => ({ - pamIncludeSuffix: prevState.pamIncludeSuffix.filter(item => item !== selection), - isIncludeOpen: false - }) - ); - } else { - this.setState( - prevState => ({ - pamIncludeSuffix: [...prevState.pamIncludeSuffix, selection], - isIncludeOpen: false - }) - ); - } - }; - this.onCreateIncludeOption = newValue => { - if (!this.state.includeOptions.includes(newValue)) { - this.setState({ - includeOptions: [...this.state.includeOptions, newValue], - isIncludeOpen: false - }); - } - }; - this.maxValue = 20000000; this.onMinusConfig = (id) => { this.setState({ @@ -206,23 +84,9 @@ class PassthroughAuthentication extends React.Component { )); this.handlePassthruChange = this.handlePassthruChange.bind(this); - this.handlePAMChange = this.handlePAMChange.bind(this); this.handleModalChange = this.handleModalChange.bind(this); this.validatePassthru = this.validatePassthru.bind(this); - this.validatePAM = this.validatePAM.bind(this); - - this.loadPAMConfigs = this.loadPAMConfigs.bind(this); this.loadURLs = this.loadURLs.bind(this); - - this.openPAMModal = this.openPAMModal.bind(this); - this.closePAMModal = this.closePAMModal.bind(this); - this.showEditPAMConfigModal = this.showEditPAMConfigModal.bind(this); - this.showAddPAMConfigModal = this.showAddPAMConfigModal.bind(this); - this.cmdPAMOperation = this.cmdPAMOperation.bind(this); - this.deletePAMConfig = this.deletePAMConfig.bind(this); - this.addPAMConfig = this.addPAMConfig.bind(this); - this.editPAMConfig = this.editPAMConfig.bind(this); - this.openURLModal = this.openURLModal.bind(this); this.closeURLModal = this.closeURLModal.bind(this); this.showEditURLModal = this.showEditURLModal.bind(this); @@ -231,12 +95,14 @@ class PassthroughAuthentication extends React.Component { this.deleteURL = this.deleteURL.bind(this); this.addURL = this.addURL.bind(this); this.editURL = this.editURL.bind(this); - this.showConfirmDeleteConfig = this.showConfirmDeleteConfig.bind(this); this.showConfirmDeleteURL = this.showConfirmDeleteURL.bind(this); - this.closeConfirmDeleteConfig = this.closeConfirmDeleteConfig.bind(this); this.closeConfirmDeleteURL = this.closeConfirmDeleteURL.bind(this); } + componentDidMount() { + this.loadURLs(); + } + validatePassthru() { const errObj = {}; let all_good = true; @@ -284,61 +150,6 @@ class PassthroughAuthentication extends React.Component { }); } - validatePAM() { - const errObj = {}; - let all_good = true; - - const reqAttrs = ['pamConfigName', 'pamIDAttr']; - const dnAttrLists = ['pamExcludeSuffix', 'pamExcludeSuffix']; - - // Check we have our required attributes set - for (const attr of reqAttrs) { - if (this.state[attr] == "") { - errObj[attr] = true; - all_good = false; - break; - } - } - - // Check the DN's of our lists - for (const attrList of dnAttrLists) { - for (const dn of this.state[attrList]) { - if (!valid_dn(dn)) { - errObj[attrList] = true; - all_good = false; - break; - } - } - } - - if (all_good) { - // Check for value differences to see if the save btn should be enabled - all_good = false; - const attrLists = ['pamExcludeSuffix', 'pamIncludeSuffix']; - for (const check_attr of attrLists) { - if (!listsEqual(this.state[check_attr], this.state['_' + check_attr])) { - all_good = true; - break; - } - } - - const configAttrs = [ - 'pamConfigName', 'pamFilter', 'pamMissingSuffix', 'pamIDMapMethod', - 'pamIDAttr', 'pamFallback', 'pamSecure', 'pamService' - ]; - for (const check_attr of configAttrs) { - if (this.state[check_attr] != this.state['_' + check_attr]) { - all_good = true; - break; - } - } - } - this.setState({ - saveBtnDisabledPAM: !all_good, - error: errObj - }); - } - handlePassthruChange(e) { // Pass thru const value = e.target.type === 'checkbox' ? e.target.checked : e.target.value; @@ -347,13 +158,6 @@ class PassthroughAuthentication extends React.Component { }, () => { this.validatePassthru() }); } - handlePAMChange(e) { - const value = e.target.type === 'checkbox' ? e.target.checked : e.target.value; - this.setState({ - [e.target.id]: value - }, () => { this.validatePAM() }); - } - handleModalChange(e) { const value = e.target.type === 'checkbox' ? e.target.checked : e.target.value; this.setState({ @@ -397,50 +201,17 @@ class PassthroughAuthentication extends React.Component { }); } - loadPAMConfigs() { - this.setState({ - firstLoad: false - }); - const cmd = [ - "dsconf", - "-j", - "ldapi://%2fvar%2frun%2fslapd-" + this.props.serverId + ".socket", - "plugin", - "pass-through-auth", - "list", - "pam-configs" - ]; - log_cmd("loadPAMConfigs", "Get PAM Passthough Authentication Plugin pamConfigs", cmd); - cockpit - .spawn(cmd, { superuser: true, err: "message" }) - .done(content => { - const myObject = JSON.parse(content); - const tableKey = this.state.tableKey + 1; - this.setState({ - pamConfigRows: myObject.items.map(item => item.attrs), - tableKey: tableKey, - }); - }) - .fail(err => { - const errMsg = JSON.parse(err); - if (err != 0) { - console.log("loadPAMConfigs failed", errMsg.desc); - } - }); - } - loadURLs() { const cmd = [ "dsconf", "-j", "ldapi://%2fvar%2frun%2fslapd-" + this.props.serverId + ".socket", "plugin", - "pass-through-auth", - "list", - "urls" + "ldap-pass-through-auth", + "list" ]; this.props.toggleLoadingHandler(); - log_cmd("loadURLs", "Get PAM Passthough Authentication Plugin pamConfigs", cmd); + log_cmd("loadURLs", "Get Passthough Authentication Plugin Configs", cmd); cockpit .spawn(cmd, { superuser: true, err: "message" }) .done(content => { @@ -461,323 +232,6 @@ class PassthroughAuthentication extends React.Component { }); } - showEditPAMConfigModal(rowData) { - this.openPAMModal(rowData); - } - - showAddPAMConfigModal() { - this.openPAMModal(); - } - - openPAMModal(name) { - if (!name) { - this.setState({ - pamConfigEntryModalShow: true, - newPAMConfigEntry: true, - pamConfigName: "", - pamExcludeSuffix: [], - pamIncludeSuffix: [], - pamMissingSuffix: "", - pamFilter: "", - pamIDAttr: "", - pamIDMapMethod: "RDN", - pamFallback: false, - pamSecure: false, - pamService: "ldapserver", - saveBtnDisabledPAM: true, - }); - } else { - let pamExcludeSuffixList = []; - let pamIncludeSuffixList = []; - const cmd = [ - "dsconf", - "-j", - "ldapi://%2fvar%2frun%2fslapd-" + this.props.serverId + ".socket", - "plugin", - "pass-through-auth", - "pam-config", - name, - "show" - ]; - - this.props.toggleLoadingHandler(); - log_cmd( - "openModal", - "Fetch the PAM Passthough Authentication Plugin pamConfig entry", - cmd - ); - cockpit - .spawn(cmd, { - superuser: true, - err: "message" - }) - .done(content => { - const pamConfigEntry = JSON.parse(content).attrs; - const tableKey = this.state.tableKey + 1; - - if (pamConfigEntry.pamexcludesuffix !== undefined) { - for (const value of pamConfigEntry.pamexcludesuffix) { - pamExcludeSuffixList = [...pamExcludeSuffixList, value]; - } - } - if (pamConfigEntry.pamincludesuffix !== undefined) { - for (const value of pamConfigEntry.pamincludesuffix) { - pamIncludeSuffixList = [...pamIncludeSuffixList, value]; - } - } - - this.setState({ - tableKey: tableKey, - saveBtnDisabledPAM: true, - pamConfigEntryModalShow: true, - newPAMConfigEntry: false, - pamExcludeSuffix: pamExcludeSuffixList, - pamIncludeSuffix: pamIncludeSuffixList, - pamIDAttr: pamConfigEntry.pamidattr[0], - pamConfigName: - pamConfigEntry.cn === undefined ? "" : pamConfigEntry.cn[0], - pamMissingSuffix: - pamConfigEntry.pammissingsuffix === undefined - ? "" - : pamConfigEntry.pammissingsuffix[0], - pamFilter: - pamConfigEntry.pamfilter === undefined - ? "" - : pamConfigEntry.pamfilter[0], - pamIDMapMethod: - pamConfigEntry.pamidmapmethod === undefined - ? "RDN" - : pamConfigEntry.pamidmapmethod[0], - pamFallback: !( - pamConfigEntry.pamfallback === undefined || - pamConfigEntry.pamfallback[0] == "FALSE" - ), - pamSecure: !( - pamConfigEntry.pamsecure === undefined || - pamConfigEntry.pamsecure[0] == "FALSE" - ), - pamService: - pamConfigEntry.pamservice === undefined - ? "ldapserver" - : pamConfigEntry.pamservice[0], - // Backup values - _pamExcludeSuffix: [...pamExcludeSuffixList], - _pamIncludeSuffix: [...pamIncludeSuffixList], - _pamIDAttr: pamConfigEntry.pamidattr[0], - _pamConfigName: - pamConfigEntry.cn === undefined ? "" : pamConfigEntry.cn[0], - _pamMissingSuffix: - pamConfigEntry.pammissingsuffix === undefined - ? "" - : pamConfigEntry.pammissingsuffix[0], - _pamFilter: - pamConfigEntry.pamfilter === undefined - ? "" - : pamConfigEntry.pamfilter[0], - _pamIDMapMethod: - pamConfigEntry.pamidmapmethod === undefined - ? "ldapserver" - : pamConfigEntry.pamidmapmethod[0], - _pamFallback: !( - pamConfigEntry.pamfallback === undefined || - pamConfigEntry.pamfallback[0] == "FALSE" - ), - _pamSecure: !( - pamConfigEntry.pamsecure === undefined || - pamConfigEntry.pamsecure[0] == "FALSE" - ), - _pamService: - pamConfigEntry.pamservice === undefined - ? "ldapserver" - : pamConfigEntry.pamservice[0], - }); - this.props.toggleLoadingHandler(); - }) - .fail(_ => { - this.setState({ - pamConfigEntryModalShow: true, - newPAMConfigEntry: true, - pamConfigName: "", - pamExcludeSuffix: [], - pamIncludeSuffix: [], - pamMissingSuffix: "", - pamFilter: "", - pamIDAttr: "", - pamIDMapMethod: "", - pamFallback: false, - pamSecure: false, - pamService: "", - saveBtnDisabledPAM: true, - }); - this.props.toggleLoadingHandler(); - }); - } - } - - closePAMModal() { - this.setState({ - pamConfigEntryModalShow: false, - savingPAM: false, - }); - } - - deletePAMConfig() { - const cmd = [ - "dsconf", - "-j", - "ldapi://%2fvar%2frun%2fslapd-" + this.props.serverId + ".socket", - "plugin", - "pass-through-auth", - "pam-config", - this.state.deleteName, - "delete" - ]; - - this.setState({ - modalSpinning: true - }); - - this.props.toggleLoadingHandler(); - log_cmd( - "deletePAMConfig", - "Delete the PAM Passthough Authentication Plugin pamConfig entry", - cmd - ); - cockpit - .spawn(cmd, { - superuser: true, - err: "message" - }) - .done(content => { - console.info("deletePAMConfig", "Result", content); - this.props.addNotification( - "success", - `PAMConfig entry ${this.state.deleteName} was successfully deleted` - ); - this.loadPAMConfigs(); - this.closeConfirmDeleteConfig(); - this.props.toggleLoadingHandler(); - }) - .fail(err => { - const errMsg = JSON.parse(err); - this.props.addNotification( - "error", - `Error during the pamConfig entry removal operation - ${errMsg.desc}` - ); - this.loadPAMConfigs(); - this.closeConfirmDeleteConfig(); - this.props.toggleLoadingHandler(); - }); - } - - addPAMConfig() { - this.cmdPAMOperation("add"); - } - - editPAMConfig() { - this.cmdPAMOperation("set"); - } - - cmdPAMOperation(action) { - // Save table here too - const { - pamConfigName, - pamExcludeSuffix, - pamIncludeSuffix, - pamMissingSuffix, - pamFilter, - pamIDAttr, - pamIDMapMethod, - pamFallback, - pamSecure, - pamService - } = this.state; - - let cmd = [ - "dsconf", - "-j", - "ldapi://%2fvar%2frun%2fslapd-" + this.props.serverId + ".socket", - "plugin", - "pass-through-auth", - "pam-config", - pamConfigName, - action, - "--missing-suffix", - pamMissingSuffix || action == "add" ? pamMissingSuffix : "delete", - "--filter", - pamFilter || action == "add" ? pamFilter : "delete", - "--id_map_method", - pamIDMapMethod || action == "add" ? pamIDMapMethod : "delete", - "--fallback", - pamFallback ? "TRUE" : "FALSE", - "--secure", - pamSecure ? "TRUE" : "FALSE", - "--service", - pamService || action == "add" ? pamService : "delete" - ]; - - cmd = [...cmd, "--exclude-suffix"]; - if (pamExcludeSuffix.length != 0) { - for (const value of pamExcludeSuffix) { - cmd = [...cmd, value]; - } - } else if (action == "add") { - cmd = [...cmd, ""]; - } else { - cmd = [...cmd, "delete"]; - } - cmd = [...cmd, "--include-suffix"]; - if (pamIncludeSuffix.length != 0) { - for (const value of pamIncludeSuffix) { - cmd = [...cmd, value]; - } - } else if (action == "add") { - cmd = [...cmd, ""]; - } else { - cmd = [...cmd, "delete"]; - } - cmd = [...cmd, "--id-attr"]; - if (pamIDAttr != "") { - cmd = [...cmd, pamIDAttr]; - } else if (action == "add") { - cmd = [...cmd, ""]; - } else { - cmd = [...cmd, "delete"]; - } - - this.setState({ - savingPAM: true - }); - log_cmd( - "pamPassthroughAuthOperation", - `Do the ${action} operation on the PAM Passthough Authentication Plugin`, - cmd - ); - cockpit - .spawn(cmd, { - superuser: true, - err: "message" - }) - .done(content => { - console.info("pamPassthroughAuthOperation", "Result", content); - this.props.addNotification( - "success", - `The ${action} operation was successfully done on "${pamConfigName}" entry` - ); - this.loadPAMConfigs(); - this.closePAMModal(); - }) - .fail(err => { - const errMsg = JSON.parse(err); - this.props.addNotification( - "error", - `Error during the pamConfig entry ${action} operation - ${errMsg.desc}` - ); - this.loadPAMConfigs(); - this.closePAMModal(); - }); - } - showEditURLModal(rowData) { this.openURLModal(rowData); } @@ -837,8 +291,7 @@ class PassthroughAuthentication extends React.Component { "-j", "ldapi://%2fvar%2frun%2fslapd-" + this.props.serverId + ".socket", "plugin", - "pass-through-auth", - "url", + "ldap-pass-through-auth", "delete", this.state.deleteName ]; @@ -901,8 +354,7 @@ class PassthroughAuthentication extends React.Component { "-j", "ldapi://%2fvar%2frun%2fslapd-" + this.props.serverId + ".socket", "plugin", - "pass-through-auth", - "url", + "ldap-pass-through-auth", action ]; if (oldURL != "" && action == "modify") { @@ -947,17 +399,6 @@ class PassthroughAuthentication extends React.Component { render() { const { urlRows, - pamConfigRows, - pamConfigName, - pamMissingSuffix, - pamExcludeSuffix, - pamIncludeSuffix, - pamFilter, - pamIDAttr, - pamIDMapMethod, - pamFallback, - pamSecure, - pamService, urlConnType, urlLDVer, urlAuthDS, @@ -967,12 +408,9 @@ class PassthroughAuthentication extends React.Component { urlTimeout, urlConnLifeTime, urlStartTLS, - newPAMConfigEntry, newURLEntry, - pamConfigEntryModalShow, urlEntryModalShow, error, - savingPAM, savingPassthru } = this.state; @@ -1019,242 +457,15 @@ class PassthroughAuthentication extends React.Component { let saveBtnName = "Save Config"; const extraPrimaryProps = {}; - if (this.state.savingPassthru || this.state.savingPAM) { + if (this.state.savingPassthru) { saveBtnName = "Saving Config ..."; extraPrimaryProps.spinnerAriaValueText = "Saving"; } - const title = (newPAMConfigEntry ? "Add" : "Edit") + " PAM Passthough Auth Plugin Config Entry"; - const title_url = (newPAMConfigEntry ? "Add " : "Edit ") + "Pass-Though Authentication Plugin URL"; + const title_url = (newURLEntry ? "Add " : "Edit ") + "Pass-Though Authentication URL"; return ( -
- - {saveBtnName} - , - - ]} - > -
- - - Config Name - - - { - this.handlePAMChange(e); - }} - isDisabled={!newPAMConfigEntry} - validated={error.pamConfigName ? ValidatedOptions.error : ValidatedOptions.default} - /> - - - - - Exclude Suffix - - - - - - - - Include Suffix - - - - - - - - ID Attribute - - - { - this.handlePAMChange(event); - }} - aria-label="FormSelect Input" - > - - {this.attrRows} - - - - - - Missing Suffix - - - { - this.handlePAMChange(event); - }} - aria-label="FormSelect Input" - > - - - - - - - - - Filter - - - { - this.handlePAMChange(e); - }} - validated={error.pamFilter ? ValidatedOptions.error : ValidatedOptions.default} - /> - - - - - Map Method - - - { - this.handlePAMChange(event); - }} - aria-label="FormSelect Input" - > - - - - - - - - - Service - - - { - this.handlePAMChange(event); - }} - aria-label="FormSelect Input" - > - - - - - - - - Fallback Auth Enabled - - - { this.handlePAMChange(e) }} - title="Sets whether to fallback to regular LDAP authentication if PAM authentication fails (pamFallback)" - /> - - - - - Require Secure Connection - - - { this.handlePAMChange(e) }} - title="Requires secure TLS connection for PAM authentication (pamSecure)" - /> - - -
-
- +
- - - PTA Configurations}> -
- - -
-
- PAM Configurations}> -
- - -
-
-
-
- +
+ + +
+ -
+
{this.props.children}
{this.state.currentPluginPath !== "" && diff --git a/src/cockpit/389-console/src/lib/plugins/pluginTables.jsx b/src/cockpit/389-console/src/lib/plugins/pluginTables.jsx index 6eaf417baf..8094b5657b 100644 --- a/src/cockpit/389-console/src/lib/plugins/pluginTables.jsx +++ b/src/cockpit/389-console/src/lib/plugins/pluginTables.jsx @@ -240,16 +240,12 @@ class PluginTable extends React.Component { return (
- - - this.onSearchChange('', evt)} - /> - - + this.onSearchChange('', evt)} + /> - - - this.onSearchChange('', evt)} - /> - - + this.onSearchChange('', evt)} + />
- - - this.onSearchChange('', evt)} - /> - - + this.onSearchChange('', evt)} + />
- - - this.onSearchChange('', evt)} - /> - - + this.onSearchChange('', evt)} + />
- - - this.onSearchChange('', evt)} - /> - - + this.onSearchChange('', evt)} + />
- - - this.onSearchChange('', evt)} - /> - - + this.onSearchChange('', evt)} + />
- - - this.onSearchChange('', evt)} - /> - - + this.onSearchChange('', evt)} + />
- - - this.onSearchChange('', evt)} - /> - - + this.onSearchChange('', evt)} + />
- - - this.onSearchChange('', evt)} - /> - - + this.onSearchChange('', evt)} + />
- - - this.onSearchChange('', evt)} - /> - - + this.onSearchChange('', evt)} + />
- - - this.onSearchChange('', evt)} - /> - - + this.onSearchChange('', evt)} + />
) }, + pamPassthroughAuthentication: { + name: "PAM Pass Through Auth", + icon: this.getIconAndName("PAM Pass Through Auth", "PAM Pass Through Auth"), + component: ( + + ) + }, winsync: { name: "Posix Winsync", icon: this.getIconAndName("Posix Winsync", "Posix Winsync API"),