From c403a39c8db68243524bd0cc50529167ac0d9fb2 Mon Sep 17 00:00:00 2001 From: Mark Reynolds Date: Sep 16 2019 19:31:15 +0000 Subject: Issue 50604 - Fix UI validation Description: This issue has been opened to track a series of bugzillas that were filed by our QE group during a massive UI testing day. Here are the issues being addressed in this issue: - Replication agreement disappears from table after browser refresh - https://bugzilla.redhat.com/show_bug.cgi?id=1751128 - Fix log rotation time validation - https://bugzilla.redhat.com/show_bug.cgi?id=1751004 - Check backup/ldif name to see if it already exists - https://bugzilla.redhat.com/show_bug.cgi?id=1751007 - https://bugzilla.redhat.com/show_bug.cgi?id=1751009 - Root DN should not be editable - https://bugzilla.redhat.com/show_bug.cgi?id=1751011 - Backup should check if there is a database available - https://bugzilla.redhat.com/show_bug.cgi?id=1751019 - Also fixed backup duplicate timestamp issue - Fixed instance creation error handing - https://bugzilla.redhat.com/show_bug.cgi?id=1751026 - Fixed export/inout issues. Check for existing back or ldif - https://bugzilla.redhat.com/show_bug.cgi?id=1751019 - Validate SSL version min and max - https://bugzilla.redhat.com/show_bug.cgi?id=1751072 - Can not promte/demote replica - https://bugzilla.redhat.com/show_bug.cgi?id=1751145 - Database link creation and deletion issue - https://bugzilla.redhat.com/show_bug.cgi?id=1751157 - Agreement name validation during creation - https://bugzilla.redhat.com/show_bug.cgi?id=1751165 - Validate referral port - https://bugzilla.redhat.com/show_bug.cgi?id=1751173 - Fix deleteion of config attributes - https://bugzilla.redhat.com/show_bug.cgi?id=1751190 There was an overall improvement when creating suffixes/databases on how to initialize them relates: https://pagure.io/389-ds-base/issue/50604 Reviewed by: spichugi(Thanks!) --- diff --git a/src/cockpit/389-console/src/css/ds.css b/src/cockpit/389-console/src/css/ds.css index 6da4b9d..f5b1e4f 100644 --- a/src/cockpit/389-console/src/css/ds.css +++ b/src/cockpit/389-console/src/css/ds.css @@ -76,6 +76,10 @@ font-size: 13px !important; } +.ds-switch { + margin-top: 2px; +} + .ds-refresh:hover { color: DarkGray; background-color: white; @@ -741,6 +745,10 @@ option { width: 100%; } +.ds-inst-indent { + margin-left: 240px; +} + .ds-left-margin { margin-left: 10px !important; } diff --git a/src/cockpit/389-console/src/database.jsx b/src/cockpit/389-console/src/database.jsx index e5adf79..36b38be 100644 --- a/src/cockpit/389-console/src/database.jsx +++ b/src/cockpit/389-console/src/database.jsx @@ -13,9 +13,13 @@ import { Modal, Icon, Form, + Row, + Col, + ControlLabel, Button, noop, TreeView, + Radio, Spinner } from "patternfly-react"; import PropTypes from "prop-types"; @@ -41,7 +45,10 @@ export class Database extends React.Component { showSuffixModal: false, createSuffix: "", createBeName: "", - createRootNode: false, + createSuffixEntry: false, + createSampleEntries: false, + noSuffixInit: true, + // DB config globalDBConfig: {}, configUpdated: 0, @@ -67,6 +74,7 @@ export class Database extends React.Component { this.removeNotification = this.removeNotification.bind(this); this.addNotification = this.addNotification.bind(this); this.handleChange = this.handleChange.bind(this); + this.handleRadioChange = this.handleRadioChange.bind(this); this.loadGlobalConfig = this.loadGlobalConfig.bind(this); this.loadLDIFs = this.loadLDIFs.bind(this); this.loadBackups = this.loadBackups.bind(this); @@ -541,10 +549,32 @@ export class Database extends React.Component { showSuffixModal () { this.setState({ showSuffixModal: true, + createSuffixEntry: false, + createSampleEntries: false, + noSuffixInit: true, errObj: {}, }); } + handleRadioChange(e) { + // Handle the create suffix init option radio button group + let noInit = false; + let addSuffix = false; + let addSample = false; + if (e.target.id == "noSuffixInit") { + noInit = true; + } else if (e.target.id == "createSuffixEntry") { + addSuffix = true; + } else { // createSampleEntries + addSample = true; + } + this.setState({ + noSuffixInit: noInit, + createSuffixEntry: addSuffix, + createSampleEntries: addSample + }); + } + handleChange(e) { const value = e.target.type === 'checkbox' ? e.target.checked : e.target.value; let valueErr = false; @@ -570,7 +600,7 @@ export class Database extends React.Component { let errors = false; let missingArgs = { createSuffix: false, - createBeName: false + createBeName: false, }; if (this.state.createSuffix == "") { @@ -601,9 +631,12 @@ export class Database extends React.Component { "dsconf", "-j", "ldapi://%2fvar%2frun%2fslapd-" + this.props.serverId + ".socket", "backend", "create", "--be-name", this.state.createBeName, '--suffix', this.state.createSuffix, ]; - if (this.state.createSampleEntries == true) { + if (this.state.createSampleEntries) { cmd.push('--create-entries'); } + if (this.state.createSuffixEntry) { + cmd.push('--create-suffix'); + } log_cmd("createSuffix", "Create a new backend", cmd); cockpit @@ -616,6 +649,7 @@ export class Database extends React.Component { ); // Refresh tree this.loadSuffixTree(false); + this.loadSuffixList(); }) .fail(err => { let errMsg = JSON.parse(err); @@ -1133,7 +1167,11 @@ export class Database extends React.Component { showModal={this.state.showSuffixModal} closeHandler={this.closeSuffixModal} handleChange={this.handleChange} + handleRadioChange={this.handleRadioChange} saveHandler={this.createSuffix} + noInit={this.state.noSuffixInit} + addSuffix={this.state.createSuffixEntry} + addSample={this.state.createSampleEntries} error={this.state.errObj} /> @@ -1147,7 +1185,11 @@ class CreateSuffixModal extends React.Component { showModal, closeHandler, handleChange, + handleRadioChange, saveHandler, + noInit, + addSuffix, + addSample, error } = this.props; @@ -1169,20 +1211,40 @@ class CreateSuffixModal extends React.Component {
-
-
- -
-
- -
-
-

- -

+ + + Suffix DN + + + + + +

+ + + Database Name + + + + + +


+
+ + + Do Not Initialize Database + + + + + Create The Top Suffix Entry + + + + + Add Sample Entries + +
@@ -1221,7 +1283,11 @@ CreateSuffixModal.propTypes = { showModal: PropTypes.bool, closeHandler: PropTypes.func, handleChange: PropTypes.func, + handleRadioChange: PropTypes.func, saveHandler: PropTypes.func, + noInit: PropTypes.bool, + addSuffix: PropTypes.bool, + addSample: PropTypes.bool, error: PropTypes.object, }; @@ -1229,6 +1295,10 @@ CreateSuffixModal.defaultProps = { showModal: false, closeHandler: noop, handleChange: noop, + handleRadioChange: noop, saveHandler: noop, + noInit: true, + addSuffix: false, + addSample: false, error: {}, }; diff --git a/src/cockpit/389-console/src/ds.js b/src/cockpit/389-console/src/ds.js index 702ff88..1274b3f 100644 --- a/src/cockpit/389-console/src/ds.js +++ b/src/cockpit/389-console/src/ds.js @@ -76,6 +76,12 @@ function valid_dn (dn){ } function valid_num (val){ + // Validate value is a number + let result = !isNaN(val); + return result; +} + +function valid_port (val){ // Validate value is a number and between 1 and 65535 let result = !isNaN(val); if (result) { @@ -366,6 +372,8 @@ function load_repl_suffix_dropdowns() { $("#" + repl_dropdowns[list]).append(''); } } + get_and_set_repl_agmts(); + get_and_set_repl_winsync_agmts(); if (obj['items'].length == 0){ // Disable create agmt buttons $("#create-agmt").prop("disabled", true); @@ -443,8 +451,6 @@ function load_config (refresh){ // Replication page get_and_set_repl_config(); - get_and_set_repl_agmts(); - get_and_set_repl_winsync_agmts(); get_and_set_cleanallruv(); update_progress(); diff --git a/src/cockpit/389-console/src/index.html b/src/cockpit/389-console/src/index.html index 91993cc..3eef2a7 100644 --- a/src/cockpit/389-console/src/index.html +++ b/src/cockpit/389-console/src/index.html @@ -386,47 +386,56 @@

- +
- +
- +
-
- +
+
- +
- +
-
- +
+
+
Optional Database Settings
- +
-
- +
-
-
- +
+
+ +
+
+ +
+
+ +
); } diff --git a/src/cockpit/389-console/src/lib/database/databaseModal.jsx b/src/cockpit/389-console/src/lib/database/databaseModal.jsx index 01db9bb..092f22d 100644 --- a/src/cockpit/389-console/src/lib/database/databaseModal.jsx +++ b/src/cockpit/389-console/src/lib/database/databaseModal.jsx @@ -4,6 +4,7 @@ import { Row, Col, ControlLabel, + Radio, Icon, Button, Form, @@ -110,8 +111,12 @@ class CreateSubSuffixModal extends React.Component { showModal, closeHandler, handleChange, + handleRadioChange, saveHandler, suffix, + noInit, + addSuffix, + addSample, error } = this.props; @@ -133,7 +138,7 @@ class CreateSubSuffixModal extends React.Component {
- + Sub-Suffix DN @@ -150,9 +155,9 @@ class CreateSubSuffixModal extends React.Component {

- + - Backend Name + Database Name +


+
+ + + Do Not Initialize Database + + + + + Create The Top Sub-Suffix Entry + + + + + Add Sample Entries + + +
diff --git a/src/cockpit/389-console/src/lib/database/referrals.jsx b/src/cockpit/389-console/src/lib/database/referrals.jsx index 22f9c50..a44569b 100644 --- a/src/cockpit/389-console/src/lib/database/referrals.jsx +++ b/src/cockpit/389-console/src/lib/database/referrals.jsx @@ -12,7 +12,7 @@ import { Form, noop } from "patternfly-react"; -import { log_cmd } from "../tools.jsx"; +import { log_cmd, valid_port } from "../tools.jsx"; import PropTypes from "prop-types"; import "../../css/ds.css"; @@ -122,6 +122,13 @@ export class SuffixReferrals extends React.Component { ); missingArgs.refPort = true; errors = true; + } else if (!valid_port(this.state.refPort)) { + this.props.addNotification( + "error", + `Invalid port number, please use a number between 1 and 65535` + ); + missingArgs.refPort = true; + errors = true; } if (errors) { this.setState({ diff --git a/src/cockpit/389-console/src/lib/database/suffix.jsx b/src/cockpit/389-console/src/lib/database/suffix.jsx index 4366c19..8413799 100644 --- a/src/cockpit/389-console/src/lib/database/suffix.jsx +++ b/src/cockpit/389-console/src/lib/database/suffix.jsx @@ -79,6 +79,10 @@ export class Suffix extends React.Component { showSubSuffixModal: false, subSuffixValue: "", subSuffixBeName: "", + createSuffixEntry: false, + noSuffixInit: true, + createSampleEntries: false, + // Create Link showLinkModal: false, createLinkSuffix: "", @@ -100,6 +104,7 @@ export class Suffix extends React.Component { this.showImportModal = this.showImportModal.bind(this); this.closeImportModal = this.closeImportModal.bind(this); this.handleChange = this.handleChange.bind(this); + this.handleRadioChange = this.handleRadioChange.bind(this); this.doImport = this.doImport.bind(this); this.importLDIF = this.importLDIF.bind(this); this.showConfirmLDIFImport = this.showConfirmLDIFImport.bind(this); @@ -411,13 +416,20 @@ export class Suffix extends React.Component { } // Create a new suffix - const cmd = [ + let cmd = [ "dsconf", "-j", "ldapi://%2fvar%2frun%2fslapd-" + this.props.serverId + ".socket", "backend", "create", "--be-name", this.state.subSuffixBeName, "--suffix=" + this.state.subSuffixValue + "," + this.props.suffix, "--parent-suffix=" + this.props.suffix ]; + if (this.state.createSampleEntries) { + cmd.push('--create-entries'); + } + if (this.state.createSuffixEntry) { + cmd.push('--create-suffix'); + } + log_cmd("createSubSuffix", "Create a sub suffix", cmd); cockpit .spawn(cmd, { superuser: true, err: "message" }) @@ -539,7 +551,7 @@ export class Suffix extends React.Component { this.state.createLinkName ]; if (this.state.createUseStartTLS) { - cmd.push("--use-starttls"); + cmd.push("--use-starttls=on"); } log_cmd("createLink", "Create database link", cmd); cockpit @@ -588,6 +600,25 @@ export class Suffix extends React.Component { }, this.checkPasswords); } + handleRadioChange(e) { + // Handle the create suffix init option radio button group + let noInit = false; + let addSuffix = false; + let addSample = false; + if (e.target.id == "noSuffixInit") { + noInit = true; + } else if (e.target.id == "createSuffixEntry") { + addSuffix = true; + } else { // createSampleEntries + addSample = true; + } + this.setState({ + noSuffixInit: noInit, + createSuffixEntry: addSuffix, + createSampleEntries: addSample + }); + } + // // Delete suffix // @@ -847,8 +878,12 @@ export class Suffix extends React.Component { showModal={this.state.showSubSuffixModal} closeHandler={this.closeSubSuffixModal} handleChange={this.handleChange} + handleRadioChange={this.handleRadioChange} saveHandler={this.createSubSuffix} suffix={this.props.suffix} + noInit={this.state.noSuffixInit} + addSuffix={this.state.createSuffixEntry} + addSample={this.state.createSampleEntries} error={this.state.errObj} /> { this.props.addNotification( "success", - `Successfully set cipher preferences. You must restart the server for these changes to take effect.` + `Successfully set cipher preferences. You must restart the Directory Server for these changes to take effect.` ); this.setState({ saving: false, diff --git a/src/cockpit/389-console/src/lib/tools.jsx b/src/cockpit/389-console/src/lib/tools.jsx index eb0a67c..dc8a701 100644 --- a/src/cockpit/389-console/src/lib/tools.jsx +++ b/src/cockpit/389-console/src/lib/tools.jsx @@ -111,3 +111,14 @@ export function bad_file_name(file_name) { } return false; } + +export function valid_port (val) { + // Validate value is a number and between 1 and 65535 + let result = !isNaN(val); + if (result) { + if (val < 1 || val > 65535) { + result = false; + } + } + return result; +} diff --git a/src/cockpit/389-console/src/replication.js b/src/cockpit/389-console/src/replication.js index e28d175..92566ad 100644 --- a/src/cockpit/389-console/src/replication.js +++ b/src/cockpit/389-console/src/replication.js @@ -271,7 +271,6 @@ function get_and_set_repl_agmts () { * Get the replication agreements for the selected suffix */ var suffix = $("#select-repl-agmt-suffix").val(); - if (suffix) { console.log("Loading replication agreements..."); var cmd = [DSCONF, '-j', 'ldapi://%2fvar%2frun%2f' + server_id + '.socket','repl-agmt', 'list', '--suffix=' + suffix ]; @@ -1133,6 +1132,13 @@ $(document).ready( function() { $("#nsds5replicabinddn").css("border-color", "initial"); cmd_args.push('--bind-dn=' + agmt_bind); } + if (agmt_name == "") { + $("#agmt-cn").css("border-color", "red"); + param_err = true; + } else { + $("#agmt-cn").css("border-color", "initial"); + cmd_args.push('"' + agmt_name + '"'); + } if (param_err ){ popup_msg("Error", "Missing required parameters"); return; @@ -1265,13 +1271,6 @@ $(document).ready( function() { if (agmt_init == "online-init") { init_replica = true; } - if ( agmt_name == "") { - $("#agmt-cn").css("border-color", "red"); - param_err = true; - } else { - $("#agmt-cn").css("border-color", "initial"); - cmd_args.push('"' + agmt_name + '"'); - } // Create agreement in DS if ( editing ) { diff --git a/src/cockpit/389-console/src/security.jsx b/src/cockpit/389-console/src/security.jsx index 77b25f9..fd681e2 100644 --- a/src/cockpit/389-console/src/security.jsx +++ b/src/cockpit/389-console/src/security.jsx @@ -2,7 +2,7 @@ import cockpit from "cockpit"; import React from "react"; import Switch from "react-switch"; import { NotificationController, ConfirmPopup } from "./lib/notifications.jsx"; -import { log_cmd } from "./lib/tools.jsx"; +import { log_cmd, valid_port } from "./lib/tools.jsx"; import { Typeahead } from "react-bootstrap-typeahead"; import { CertificateManagement } from "./lib/security/certificateManagement.jsx"; import { SecurityEnableModal } from "./lib/security/securityModals.jsx"; @@ -18,6 +18,7 @@ import { ControlLabel, Button, Checkbox, + Icon, Spinner } from "patternfly-react"; import PropTypes from "prop-types"; @@ -475,6 +476,26 @@ export class Security extends React.Component { } saveSecurityConfig () { + // Validate some setting first + let sslMin = this.state._sslVersionMin; + let sslMax = this.state._sslVersionMax; + if (this.state._sslVersionMin != this.state.sslVersionMin) { + sslMin = this.state.sslVersionMin; + } + if (this.state._sslVersionMax != this.state.sslVersionMax) { + sslMax = this.state.sslVersionMax; + } + + if (sslMin > sslMax) { + this.addNotification( + "error", + `The TLS minimum version but be less than or equal to the TLS maximum version` + ); + // Reset page + this.loadSecurityConfig(); + return; + } + let cmd = [ 'dsconf', '-j', 'ldapi://%2fvar%2frun%2fslapd-' + this.props.serverId + '.socket', 'security', 'set' @@ -493,6 +514,15 @@ export class Security extends React.Component { cmd.push("--tls-client-auth=" + this.state.clientAuth); } if (this.state._securePort != this.state.securePort) { + if (!valid_port(this.state.securePort)) { + this.addNotification( + "error", + `The Secure Port is invalid, it must be a number between 1 and 65535` + ); + // Reset page + this.loadSecurityConfig(); + return; + } cmd.push("--secure-port=" + this.state.securePort); } if (this.state._secureListenhost != this.state.secureListenhost) { @@ -522,7 +552,7 @@ export class Security extends React.Component { if (cmd.length > 5) { log_cmd("saveSecurityConfig", "Applying security config change", cmd); - let msg = "Successfully updated security configuration. You must restart the server for these changes to take effect."; + let msg = "Successfully updated security configuration. You must restart the Directory Server for these changes to take effect."; this.setState({ // Start the spinner @@ -592,7 +622,6 @@ export class Security extends React.Component { render() { let securityPage = ""; let serverCert = [this.state.nssslpersonalityssl]; - if (this.state.loaded && !this.state.saving) { let configPage = ""; if (this.state.securityEnabled) { @@ -603,7 +632,7 @@ export class Security extends React.Component { Server Secure Port - + @@ -611,7 +640,7 @@ export class Security extends React.Component { Secure Listen Host - + @@ -635,8 +664,7 @@ export class Security extends React.Component { Minimum TLS Version - @@ -650,8 +678,7 @@ export class Security extends React.Component { Maximum TLS Version - @@ -665,7 +692,7 @@ export class Security extends React.Component { Client Authentication - @@ -677,7 +704,7 @@ export class Security extends React.Component { Validate Certificate - @@ -761,13 +788,20 @@ export class Security extends React.Component { Security Enabled - + + + +
{configPage} diff --git a/src/cockpit/389-console/src/servers.html b/src/cockpit/389-console/src/servers.html index 04678e8..1d17872 100644 --- a/src/cockpit/389-console/src/servers.html +++ b/src/cockpit/389-console/src/servers.html @@ -136,15 +136,15 @@
+ class="ds-input" type="text" readonly id="nsslapd-rootdn" value="cn=Directory Manager" size="40"/>
+ class="ds-input" type="password" id="nsslapd-rootpw" size="40"/>
+ class="ds-input" type="password" id="nsslapd-rootpw-confirm" size="40"/>
at : + at :

@@ -615,7 +615,7 @@ at : + title="Minute" id="nsslapd-auditlog-logrotationsyncmin" size="1"/>

@@ -674,7 +674,7 @@ at : + title="Minute" id="nsslapd-auditfaillog-logrotationsyncmin" size="1"/>

Deletion Policy

@@ -731,7 +731,7 @@ at : + title="Minute" id="nsslapd-errorlog-logrotationsyncmin" size="1"/> @@ -950,14 +950,14 @@

LDAPI & Autobind Settings