This is an automated email from the git hooks/post-receive script.
mreynolds pushed a commit to branch 389-ds-base-1.4.1
in repository 389-ds-base.
The following commit(s) were added to refs/heads/389-ds-base-1.4.1 by this push:
new f710e6a Issue 50604 - Fix UI validation
f710e6a is described below
commit f710e6a526134598db47fbb502a117cecd4a9bd7
Author: Mark Reynolds <mreynolds(a)redhat.com>
AuthorDate: Mon Sep 16 09:22:38 2019 -0400
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!)
(cherry picked from commit c403a39c8db68243524bd0cc50529167ac0d9fb2)
---
src/cockpit/389-console/src/css/ds.css | 8 ++
src/cockpit/389-console/src/database.jsx | 104 +++++++++++++++----
src/cockpit/389-console/src/ds.js | 10 +-
src/cockpit/389-console/src/index.html | 59 ++++++-----
.../389-console/src/lib/database/backups.jsx | 78 ++++++++++++--
.../389-console/src/lib/database/databaseModal.jsx | 29 +++++-
.../389-console/src/lib/database/referrals.jsx | 9 +-
.../389-console/src/lib/database/suffix.jsx | 39 ++++++-
.../389-console/src/lib/security/ciphers.jsx | 2 +-
src/cockpit/389-console/src/lib/tools.jsx | 11 ++
src/cockpit/389-console/src/replication.js | 15 ++-
src/cockpit/389-console/src/security.jsx | 58 ++++++++---
src/cockpit/389-console/src/servers.html | 22 ++--
src/cockpit/389-console/src/servers.js | 112 +++++++++++++++------
src/lib389/lib389/__init__.py | 7 +-
src/lib389/lib389/_mapped_object.py | 2 +-
src/lib389/lib389/chaining.py | 2 +-
src/lib389/lib389/cli_conf/backend.py | 31 ++++++
src/lib389/lib389/cli_conf/security.py | 5 +-
src/lib389/lib389/configurations/sample.py | 53 ++++++++++
src/lib389/lib389/replica.py | 8 +-
21 files changed, 535 insertions(+), 129 deletions(-)
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}
/>
</div>
@@ -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 {
</Modal.Header>
<Modal.Body>
<Form horizontal autoComplete="off">
- <div className="ds-inline">
- <div>
- <label htmlFor="createSuffix"
className="ds-config-label" title="Database Suffix DN
(nsslapd-suffix)">
- Suffix DN</label><input
onChange={handleChange} className={error.createSuffix ? "ds-input-bad" :
"ds-input"} type="text" id="createSuffix"
size="40" />
- </div>
- <div>
- <label htmlFor="createBeName"
className="ds-config-label" title="Database backend name
(nsslapd-backend)">
- Backend Name</label><input
onChange={handleChange} className={error.createBeName ? "ds-input-bad" :
"ds-input"} type="text" id="createBeName"
size="40" />
- </div>
- <div>
- <p />
- <input type="checkbox"
className="ds-config-checkbox" id="createSampleEntries"
onChange={handleChange} /><label
- htmlFor="createSampleEntries"
className="ds-label" title="Create the datbase with sample
entries"> Create Sample Entries</label>
- </div>
+ <Row title="Database suffix, like
'dc=example,dc=com'. The suffix must be a valid LDAP Distiguished Name
(DN)">
+ <Col sm={3}>
+ <ControlLabel>Suffix DN</ControlLabel>
+ </Col>
+ <Col sm={5}>
+ <input onChange={handleChange}
className={error.createSuffix ? "ds-input-bad" : "ds-input"}
type="text" id="createSuffix" size="40" />
+ </Col>
+ </Row>
+ <p />
+ <Row title="The name for the backend database, like
'userroot'. The name can be a combination of alphanumeric characters, dashes (-),
and underscores (_). No other characters are allowed, and the name must be unique across
all backends.">
+ <Col sm={3}>
+ <ControlLabel>Database
Name</ControlLabel>
+ </Col>
+ <Col sm={5}>
+ <input onChange={handleChange}
className={error.createBeName ? "ds-input-bad" : "ds-input"}
type="text" id="createBeName" size="40" />
+ </Col>
+ </Row>
+ <hr />
+ <div>
+ <Row className="ds-indent">
+ <Radio name="radioGroup"
id="noSuffixInit" onChange={handleRadioChange} checked={noInit} inline>
+ Do Not Initialize Database
+ </Radio>
+ </Row>
+ <Row className="ds-indent">
+ <Radio name="radioGroup"
id="createSuffixEntry" onChange={handleRadioChange} checked={addSuffix}
inline>
+ Create The Top Suffix Entry
+ </Radio>
+ </Row>
+ <Row className="ds-indent">
+ <Radio name="radioGroup"
id="createSampleEntries" onChange={handleRadioChange} checked={addSample}
inline>
+ Add Sample Entries
+ </Radio>
+ </Row>
</div>
</Form>
</Modal.Body>
@@ -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('<option value="'
+ obj['items'][idx] + '" selected="selected">' +
obj['items'][idx] +'</option>');
}
}
+ 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 @@
<p class="ds-modal-error"></p>
<div class="ds-inline">
<div>
- <label for="create-inst-serverid"
class="ds-config-label" title="The instance name, this is what gets
appended to 'slapi-'. The instance name can only contain letters, numbers, and:
# % : - _">
- Instance Name</label><input class="ds-input
ds-inst-input" size="40" type="text"
id="create-inst-serverid" placeholder="Your_Instance_Name" required
/>
+ <label for="create-inst-serverid"
class="ds-config-label ds-input-right" title="The instance name, this is
what gets appended to 'slapi-'. The instance name can only contain letters,
numbers, and: # % : - _">
+ Instance Name</label><input class="ds-input
ds-inst-input ds-left-margin" size="40" type="text"
id="create-inst-serverid" placeholder="Your_Instance_Name" required
/>
</div>
<div>
- <label for="create-inst-port"
class="ds-config-label" title="The server port number">
- Port</label><input class="ds-input ds-inst-input"
size="40" type="text" value="389"
id="create-inst-port" required />
+ <label for="create-inst-port" class="ds-config-label
ds-input-right" title="The server port number">
+ Port</label><input class="ds-input ds-inst-input
ds-left-margin" size="40" type="text" value="389"
id="create-inst-port" required />
</div>
<div>
- <label for="create-inst-secureport"
class="ds-config-label" title="The secure port number for TLS
connections">
- Secure Port</label><input class="ds-input
ds-inst-input" size="40" type="text" value="636"
id="create-inst-secureport" required />
+ <label for="create-inst-secureport"
class="ds-config-label ds-input-right" title="The secure port number for
TLS connections">
+ Secure Port</label><input class="ds-input ds-inst-input
ds-left-margin" size="40" type="text" value="636"
id="create-inst-secureport" required />
</div>
- <div>
- <label for="create-inst-rootdn"
class="ds-config-label" title="The DN for the unrestricted user">
- Directory Manager DN</label><input class="ds-input
ds-inst-input" size="40" autocomplete="username"
value="cn=Directory Manager" type="text"
id="create-inst-rootdn" required />
+ <div class="ds-inst-indent">
+ <input type="checkbox"
class="ds-config-checkbox" id="create-inst-tls" checked><label
+ for="create-inst-tls" class="ds-label"
title="Create a self-signed certificate database">Create Self-Signed TLS
Certificate DB</label>
</div>
<div>
- <label for="rootdn-pw" class="ds-config-label"
title="Directory Manager password.">Directory Manager
Password</label><input
- class="ds-input ds-inst-input" size="40"
type="password" autocomplete="new-password" placeholder="Enter
password" id="rootdn-pw" name="name" required>
+ <label for="create-inst-rootdn"
class="ds-config-label ds-input-right" title="The DN for the unrestricted
user">
+ Directory Manager DN</label><input class="ds-input
ds-inst-input ds-left-margin" size="40" autocomplete="username"
value="cn=Directory Manager" type="text"
id="create-inst-rootdn" required />
</div>
<div>
- <label for="rootdn-pw-confirm"
class="ds-config-label" title="Confirm password">Confirm
Password</label><input
- class="ds-input ds-inst-input" size="40"
type="password" autocomplete="new-password" placeholder="Confirm
password" id="rootdn-pw-confirm" name="name" required>
+ <label for="rootdn-pw" class="ds-config-label
ds-input-right" title="Directory Manager password.">Directory Manager
Password</label><input
+ class="ds-input ds-inst-input ds-left-margin"
size="40" type="password" autocomplete="new-password"
placeholder="Enter password" id="rootdn-pw" name="name"
required>
</div>
- <hr>
<div>
- <label for="backend-suffix"
class="ds-config-label" title="Database suffix, like
'dc=example,dc=com'. The suffix must be a valid LDAP Distiguished Name
(DN)">Database Suffix</label><input
- class="ds-input ds-inst-input" size="40"
placeholder="e.g. dc=example,dc=com" type="text"
id="backend-suffix">
+ <label for="rootdn-pw-confirm" class="ds-config-label
ds-input-right" title="Confirm password">Confirm
Password</label><input
+ class="ds-input ds-inst-input ds-left-margin"
size="40" type="password" autocomplete="new-password"
placeholder="Confirm password" id="rootdn-pw-confirm"
name="name" required>
</div>
+ <hr>
+ <h5 class="ds-center">Optional Database
Settings</h5>
<div>
- <label for="backend-name"
class="ds-config-label" title="The name for the backend database, like
'userroot'. The name can be a combination of alphanumeric characters, dashes (-),
and underscores (_). No other characters are allowed.">Database
Name</label><input
- class="ds-input ds-inst-input" placeholder="e.g.
userRoot" size="40" type="text" id="backend-name">
+ <label for="backend-suffix" class="ds-config-label
ds-input-right" title="Database suffix, like 'dc=example,dc=com'. The
suffix must be a valid LDAP Distiguished Name (DN)">Database
Suffix</label><input
+ class="ds-input ds-inst-input ds-left-margin"
size="40" placeholder="e.g. dc=example,dc=com" type="text"
id="backend-suffix">
</div>
-
<div>
- <label for="create-sample-entries"
class="ds-config-label" title="Create sample entries in the
suffix">Create Sample Entries </label><input
- type="checkbox" class="ds-input
ds-config-checkbox" id="create-sample-entries">
+ <label for="backend-name" class="ds-config-label
ds-input-right" title="The name for the backend database, like
'userroot'. The name can be a combination of alphanumeric characters, dashes (-),
and underscores (_). No other characters are allowed, and the name must be unique across
all backends.">Database Name</label><input
+ class="ds-input ds-inst-input ds-left-margin"
placeholder="e.g. userRoot" size="40" type="text"
id="backend-name">
</div>
- <hr>
- <div>
- <input type="checkbox"
class="ds-config-checkbox" id="create-inst-tls" checked><label
- for="create-inst-tls" class="ds-label"
title="Create a self-signed certificate database">Create Self Signed
Certificate DB</label>
+ <div class="ds-inst-indent ds-margin-top">
+ <div>
+ <input type="radio" name="ds-radio-group"
id="no-init" checked /><label class="ds-left-margin"
for="no-init"
+ title="Do not initialize the backend database">
Do Not Initialize Database</label>
+ </div>
+ <div>
+ <input type="radio" name="ds-radio-group"
id="create-suffix-entry" /><label class="ds-left-margin"
for="create-suffix-entry"
+ title="Create the suffix entry with a basic READ
ACI"> Create Suffix Entry</label>
+ </div>
+ <div>
+ <input type="radio" name="ds-radio-group"
id="create-sample-entries" /><label class="ds-left-margin"
for="create-sample-entries"
+ title="Create sample entries under the suffix">
Create Suffix Entry And Add Sample Entries</label>
+ </div>
</div>
<div id="create-inst-spinner" class="ds-center"
hidden>
<hr>
diff --git a/src/cockpit/389-console/src/lib/database/backups.jsx
b/src/cockpit/389-console/src/lib/database/backups.jsx
index 96d6e97..f04c348 100644
--- a/src/cockpit/389-console/src/lib/database/backups.jsx
+++ b/src/cockpit/389-console/src/lib/database/backups.jsx
@@ -30,7 +30,8 @@ export class Backups extends React.Component {
activeKey: 1,
showConfirmBackupDelete: false,
showConfirmBackup: false,
- showConfirmRestore: false,
+ showConfirmRestoreReplace: false,
+ showConfirmLDIFReplace: false,
showRestoreSpinningModal: false,
showDelBackupSpinningModal: false,
showBackupModal: false,
@@ -40,6 +41,7 @@ export class Backups extends React.Component {
// LDIF
showConfirmLDIFDelete: false,
showConfirmLDIFImport: false,
+ showConfirmRestore: false,
showLDIFSpinningModal: false,
showLDIFDeleteSpinningModal: false,
showExportModal: false,
@@ -68,6 +70,8 @@ export class Backups extends React.Component {
this.closeRestoreSpinningModal = this.closeRestoreSpinningModal.bind(this);
this.showDelBackupSpinningModal = this.showDelBackupSpinningModal.bind(this);
this.closeDelBackupSpinningModal = this.closeDelBackupSpinningModal.bind(this);
+ this.validateBackup = this.validateBackup.bind(this);
+ this.closeConfirmRestoreReplace = this.closeConfirmRestoreReplace.bind(this);
// LDIFS
this.importLDIF = this.importLDIF.bind(this);
this.deleteLDIF = this.deleteLDIF.bind(this);
@@ -80,6 +84,8 @@ export class Backups extends React.Component {
this.doExport = this.doExport.bind(this);
this.showExportModal = this.showExportModal.bind(this);
this.closeExportModal = this.closeExportModal.bind(this);
+ this.validateLDIF = this.validateLDIF.bind(this);
+ this.closeConfirmLDIFReplace = this.closeConfirmLDIFReplace.bind(this);
}
showExportModal () {
@@ -97,6 +103,12 @@ export class Backups extends React.Component {
});
}
+ closeConfirmLDIFReplace () {
+ this.setState({
+ showConfirmLDIFReplace: false
+ });
+ }
+
showLDIFSpinningModal () {
this.setState({
showLDIFSpinningModal: true
@@ -233,6 +245,12 @@ export class Backups extends React.Component {
});
}
+ closeConfirmRestoreReplace () {
+ this.setState({
+ showConfirmRestoreReplace: false,
+ });
+ }
+
importLDIF() {
this.showLDIFSpinningModal();
@@ -288,12 +306,23 @@ export class Backups extends React.Component {
});
}
+ validateBackup() {
+ for (let i = 0; i < this.props.backups.length; i++) {
+ if (this.state.backupName == this.props.backups[i]['name']) {
+ this.setState({
+ showConfirmRestoreReplace: true
+ });
+ return;
+ }
+ }
+ this.doBackup();
+ }
+
doBackup () {
let cmd = [
"dsconf", "-j", "ldapi://%2fvar%2frun%2fslapd-"
+ this.props.serverId + ".socket",
"backup", "create"
];
-
if (this.state.backupName != "") {
if (bad_file_name(this.state.backupName)) {
this.props.addNotification(
@@ -332,8 +361,15 @@ export class Backups extends React.Component {
}
restoreBackup () {
- this.showRestoreSpinningModal();
+ if (this.props.suffixes.length == 0) {
+ this.props.addNotification(
+ "error",
+ `There are no databases defined to restore`
+ );
+ return;
+ }
+ this.showRestoreSpinningModal();
const cmd = [
"dsconf", "-j", "ldapi://%2fvar%2frun%2fslapd-"
+ this.props.serverId + ".socket",
"backup", "restore", this.state.backupName
@@ -405,6 +441,23 @@ export class Backups extends React.Component {
});
}
+ validateLDIF() {
+ let ldifname = this.state.ldifName;
+ if (!ldifname.endsWith(".ldif")) {
+ // dsconf/dsctl adds ".ldif" if not set, so that's what we need
to check
+ ldifname = ldifname + ".ldif";
+ }
+ for (let i = 0; i < this.props.ldifs.length; i++) {
+ if (ldifname == this.props.ldifs[i]['name']) {
+ this.setState({
+ showConfirmLDIFReplace: true
+ });
+ return;
+ }
+ }
+ this.doExport();
+ }
+
doExport() {
let missingArgs = {ldifName: false};
if (this.state.ldifName == "") {
@@ -525,7 +578,7 @@ export class Backups extends React.Component {
showModal={this.state.showExportModal}
closeHandler={this.closeExportModal}
handleChange={this.handleChange}
- saveHandler={this.doExport}
+ saveHandler={this.validateLDIF}
spinning={this.state.exportSpinner}
error={this.state.errObj}
suffixes={this.props.suffixes}
@@ -534,7 +587,7 @@ export class Backups extends React.Component {
showModal={this.state.showBackupModal}
closeHandler={this.closeBackupModal}
handleChange={this.handleChange}
- saveHandler={this.doBackup}
+ saveHandler={this.validateBackup}
spinning={this.state.backupSpinning}
error={this.state.errObj}
/>
@@ -590,7 +643,20 @@ export class Backups extends React.Component {
msg="Are you sure you want to delete this backup?"
msgContent={this.state.backupName}
/>
-
+ <ConfirmPopup
+ showModal={this.state.showConfirmRestoreReplace}
+ closeHandler={this.closeConfirmRestoreReplace}
+ actionFunc={this.doBackup}
+ msg="Replace Existing Backup"
+ msgContent="A backup already eixsts with the same name, do you
want to replace it?"
+ />
+ <ConfirmPopup
+ showModal={this.state.showConfirmLDIFReplace}
+ closeHandler={this.closeConfirmLDIFReplace}
+ actionFunc={this.doExport}
+ msg="Replace Existing LDIF File"
+ msgContent="A LDIF file already eixsts with the same name, do
you want to replace it?"
+ />
</div>
);
}
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 {
</Modal.Header>
<Modal.Body>
<Form horizontal autoComplete="off">
- <Row title="Database Suffix DN
(nsslapd-suffix)">
+ <Row title="Database suffix, like
'dc=example,dc=com'. The suffix must be a valid LDAP Distiguished Name
(DN)">
<Col sm={3}>
<ControlLabel>Sub-Suffix
DN</ControlLabel>
</Col>
@@ -150,9 +155,9 @@ class CreateSubSuffixModal extends React.Component {
</Col>
</Row>
<p />
- <Row title="Database backend name
(nsslapd-backend)">
+ <Row title="The name for the backend database, like
'userroot'. The name can be a combination of alphanumeric characters, dashes (-),
and underscores (_). No other characters are allowed, and the name must be unique across
all backends.">
<Col sm={3}>
- <ControlLabel>Backend
Name</ControlLabel>
+ <ControlLabel>Database
Name</ControlLabel>
</Col>
<Col sm={9}>
<FormControl
@@ -163,6 +168,24 @@ class CreateSubSuffixModal extends React.Component {
/>
</Col>
</Row>
+ <hr />
+ <div>
+ <Row className="ds-indent">
+ <Radio name="radioGroup"
id="noSuffixInit" onChange={handleRadioChange} checked={noInit} inline>
+ Do Not Initialize Database
+ </Radio>
+ </Row>
+ <Row className="ds-indent">
+ <Radio name="radioGroup"
id="createSuffixEntry" onChange={handleRadioChange} checked={addSuffix}
inline>
+ Create The Top Sub-Suffix Entry
+ </Radio>
+ </Row>
+ <Row className="ds-indent">
+ <Radio name="radioGroup"
id="createSampleEntries" onChange={handleRadioChange} checked={addSample}
inline>
+ Add Sample Entries
+ </Radio>
+ </Row>
+ </div>
</Form>
</Modal.Body>
<Modal.Footer>
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}
/>
<ImportModal
diff --git a/src/cockpit/389-console/src/lib/security/ciphers.jsx
b/src/cockpit/389-console/src/lib/security/ciphers.jsx
index 4714fcb..c07c0dc 100644
--- a/src/cockpit/389-console/src/lib/security/ciphers.jsx
+++ b/src/cockpit/389-console/src/lib/security/ciphers.jsx
@@ -89,7 +89,7 @@ export class Ciphers extends React.Component {
.done(() => {
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
</Col>
<Col sm={4}>
- <input id="securePort"
className="ds-input-auto" onChange={this.handleChange} type="text"
defaultValue={this.state.securePort} />
+ <input id="securePort"
className="ds-input-auto" onChange={this.handleChange} type="text"
value={this.state.securePort} />
</Col>
</Row>
<Row className="ds-margin-top" title="This
parameter can be used to restrict the Directory Server instance to a single IP interface
(hostname, or IP address). This parameter specifically sets what interface to use for TLS
traffic. Requires restart. (nsslapd-securelistenhost).">
@@ -611,7 +640,7 @@ export class Security extends React.Component {
Secure Listen Host
</Col>
<Col sm={4}>
- <input id="secureListenhost"
className="ds-input-auto" type="text" onChange={this.handleChange}
defaultValue={this.state.secureListenhost} />
+ <input id="secureListenhost"
className="ds-input-auto" type="text" onChange={this.handleChange}
value={this.state.secureListenhost} />
</Col>
</Row>
<Row className="ds-margin-top" title="The name,
or nickname, of the server certificate inthe NSS datgabase the server should use
(nsSSLPersonalitySSL).">
@@ -635,8 +664,7 @@ export class Security extends React.Component {
Minimum TLS Version
</Col>
<Col sm={4}>
- <select id="sslVersionMin"
className="btn btn-default dropdown ds-select" onChange={this.handleChange}
defaultValue={this.state.sslVersionMin}>
- <option />
+ <select id="sslVersionMin"
className="btn btn-default dropdown ds-select" onChange={this.handleChange}
value={this.state.sslVersionMin}>
<option>TLS1.3</option>
<option>TLS1.2</option>
<option>TLS1.1</option>
@@ -650,8 +678,7 @@ export class Security extends React.Component {
Maximum TLS Version
</Col>
<Col sm={4}>
- <select id="sslVersionMax"
className="btn btn-default dropdown ds-select" onChange={this.handleChange}
defaultValue={this.state.sslVersionMax}>
- <option />
+ <select id="sslVersionMax"
className="btn btn-default dropdown ds-select" onChange={this.handleChange}
value={this.state.sslVersionMax}>
<option>TLS1.3</option>
<option>TLS1.2</option>
<option>TLS1.1</option>
@@ -665,7 +692,7 @@ export class Security extends React.Component {
Client Authentication
</Col>
<Col sm={4}>
- <select id="clientAuth" className="btn
btn-default dropdown ds-select" onChange={this.handleChange}
defaultValue={this.state.clientAuth}>
+ <select id="clientAuth" className="btn
btn-default dropdown ds-select" onChange={this.handleChange}
value={this.state.clientAuth}>
<option>off</option>
<option>allowed</option>
<option>required</option>
@@ -677,7 +704,7 @@ export class Security extends React.Component {
Validate Certificate
</Col>
<Col sm={4}>
- <select id="validateCert"
className="btn btn-default dropdown ds-select" onChange={this.handleChange}
defaultValue={this.state.validateCert}>
+ <select id="validateCert"
className="btn btn-default dropdown ds-select" onChange={this.handleChange}
value={this.state.validateCert}>
<option>warn</option>
<option>on</option>
<option>off</option>
@@ -761,13 +788,20 @@ export class Security extends React.Component {
<Col componentClass={ControlLabel}
sm={2}>
Security Enabled
</Col>
- <Col sm={2}>
+ <Col sm={1}>
<Switch
+ className="ds-switch"
onChange={this.handleSwitchChange}
checked={this.state.securityEnabled}
height={20}
/>
</Col>
+ <Col>
+ <Icon
className="ds-left-margin ds-refresh"
+ type="fa"
name="refresh" title="Refresh security settings"
+
onClick={this.loadSecurityConfig}
+ />
+ </Col>
</Row>
<hr />
{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 @@
<form>
<div>
<label for="nsslapd-rootdn" class="ds-config-label"
title="The DN of the unrestricted directory manager
(nsslapd-rootdn).">Directory Manager DN</label><input
- class="ds-input" type="text"
autocomplete="username" id="nsslapd-rootdn"
placeholder="cn=directory manager" value="cn=Directory Manager"
size="40"/>
+ class="ds-input" type="text" readonly
id="nsslapd-rootdn" value="cn=Directory Manager"
size="40"/>
</div>
<div>
<label for="nsslapd-rootpw" class="ds-config-label"
title="The Directory Manager password (nsslapd-rootpw).">Directory Manager
Password</label><input
- class="ds-input" type="password"
autocomplete="new-password" id="nsslapd-rootpw"
size="40"/>
+ class="ds-input" type="password"
id="nsslapd-rootpw" size="40"/>
</div>
<div>
<label for="nsslapd-rootpw-confirm"
class="ds-config-label" title="Confirm directory manager
password.">Confirm Password</label><input
- class="ds-input" type="password"
autocomplete="new-password" id="nsslapd-rootpw-confirm"
size="40"/>
+ class="ds-input" type="password"
id="nsslapd-rootpw-confirm" size="40"/>
</div>
<div>
<label for="nsslapd-rootpwstoragescheme"
class="ds-config-label" title="Set the Directory Manager password storage
scheme (nsslapd-rootpwstoragescheme).">Password Storage
Scheme</label><select
@@ -525,8 +525,8 @@
<option>day</option>
<option>week</option>
<option>month</option>
- </select> at <input class="ds-input"
type="text" title="Hour"
id="nsslapd-accesslog-logrotationsynchour" placeholder="0"
size="1"/> : <input class="ds-input" type="text"
placeholder="0"
- title="Minute"
id="nsslapd-accesslog-logrotationsyncminute" size="1"/>
+ </select> at <input class="ds-input"
type="text" title="Hour"
id="nsslapd-accesslog-logrotationsynchour" placeholder="0"
size="1"/> : <input class="ds-input" type="text"
placeholder="0"
+ title="Minute"
id="nsslapd-accesslog-logrotationsyncmin" size="1"/>
</div>
</div>
<p></p>
@@ -615,7 +615,7 @@
<option>week</option>
<option>month</option>
</select> at <input class="ds-input" type="text"
title="Hour" id="nsslapd-auditlog-logrotationsynchour"
placeholder="0" size="1"/> : <input class="ds-input"
type="text" placeholder="0"
- title="Minute"
id="nsslapd-auditlog-logrotationsyncminute" size="1"/>
+ title="Minute" id="nsslapd-auditlog-logrotationsyncmin"
size="1"/>
</div>
</div>
<p></p>
@@ -674,7 +674,7 @@
<option>week</option>
<option>month</option>
</select> at <input class="ds-input" type="text"
title="Hour" id="nsslapd-auditfaillog-logrotationsynchour"
placeholder="0" size="1"/> : <input class="ds-input"
type="text" placeholder="0"
- title="Minute"
id="nsslapd-auditfaillog-logrotationsyncminute" size="1"/>
+ title="Minute"
id="nsslapd-auditfaillog-logrotationsyncmin" size="1"/>
</div>
</div>
<h4 class="ds-sub-header">Deletion Policy</h4>
@@ -731,7 +731,7 @@
<option>week</option>
<option>month</option>
</select> at <input class="ds-input" type="text"
title="Hour" id="nsslapd-errorlog-logrotationsynchour"
placeholder="0" size="1"/> : <input class="ds-input"
type="text" placeholder="0"
- title="Minute"
id="nsslapd-errorlog-logrotationsyncminute" size="1"/>
+ title="Minute" id="nsslapd-errorlog-logrotationsyncmin"
size="1"/>
</div>
</div>
@@ -950,14 +950,14 @@
<h3 class="ds-config-header">LDAPI & Autobind
Settings</h3>
<div class="ldapi-attrs ds-inline" hidden>
<div>
- <label for="nsslapd-ldapifilepath" class="ds-config-label"
title="The Unix socket file (nsslapd-ldapifilepath).">LDAPI Socket File
Path</label><input
+ <label for="nsslapd-ldapifilepath" class="ds-config-label"
title="The Unix socket file (nsslapd-ldapifilepath). The UI requires this exact path
so it is a read-only setting.">LDAPI Socket File Path</label><input
class="ds-input" type="text"
id="nsslapd-ldapifilepath" size="35" readonly/>
</div>
<div class="ds-inline">
<div class="autobind-attrs">
<div>
- <label for="nsslapd-ldapimaprootdn"
class="ds-config-label" title="Map the Unix root entry to this Directory
Manager DN (nsslapd-ldapimaprootdn).">DN to map "root"
To</label><input
- class="ds-input" type="text"
id="nsslapd-ldapimaprootdn" placeholder="e.g. cn=Directory Manager"
size="35"/>
+ <label for="nsslapd-ldapimaprootdn"
class="ds-config-label" title="Map the Unix root entry to this Directory
Manager DN (nsslapd-ldapimaprootdn). The UI requires this to be set to the current root
DN so it is a read-only setting">DN to map "root"
To</label><input
+ class="ds-input" type="text"
id="nsslapd-ldapimaprootdn" readonly size="35"/>
</div>
<div>
<p></p>
diff --git a/src/cockpit/389-console/src/servers.js
b/src/cockpit/389-console/src/servers.js
index b2a4b0f..7e34a5a 100644
--- a/src/cockpit/389-console/src/servers.js
+++ b/src/cockpit/389-console/src/servers.js
@@ -211,7 +211,7 @@ function get_and_set_config () {
config_loaded = 1;
check_inst_alive();
}).fail(function(data) {
- popup_err("Error", "Failed to set config\n" + data.message);
+ popup_err("Error", "Failed to get config\n" + data.message);
check_inst_alive(1);
});
}
@@ -311,34 +311,56 @@ function get_and_set_sasl () {
}
function apply_mods(mods) {
- var mod = mods.pop();
+ let mod = mods.pop();
- if (!mod){
- popup_success("Successfully updated configuration");
- return; /* all done*/
+ if (!mod) {
+ return 0; /* all done*/
}
- var cmd = [DSCONF, '-j', 'ldapi://%2fvar%2frun%2f' + server_id +
'.socket','config', 'replace'];
+ let cmd = [DSCONF, '-j', 'ldapi://%2fvar%2frun%2f' + server_id +
'.socket', 'config', 'replace'];
cmd.push(mod.attr + "=" + mod.val);
cockpit.spawn(cmd, { superuser: true, "err": "message",
"environ": [ENV]}).then(function() {
config_values[mod.attr] = mod.val;
// Continue with next mods (if any))
apply_mods(mods);
- }, function(ex) {
- popup_err("Failed to update attribute: " + mod.attr, ex.message);
+ }, function(ex, data) {
+ popup_err("Failed to update attribute: " + mod.attr, data);
// Reset HTML for remaining values that have not been processed
$("#" + mod.attr).val(config_values[mod.attr]);
for (remaining in mods) {
$("#" + remaining.attr).val(config_values[remaining.attr]);
}
check_inst_alive(0);
- return; // Stop on error
+ return -1; // Stop on error
+ });
+}
+
+function delete_mods(mods) {
+ let mod = mods.pop();
+
+ if (!mod) {
+ return 0; /* all done*/
+ }
+ var cmd = [DSCONF, '-j', 'ldapi://%2fvar%2frun%2f' + server_id +
'.socket', 'config', 'delete', mod.attr];
+ cockpit.spawn(cmd, { superuser: true, "err": "message",
"environ": [ENV]}).then(function() {
+ config_values[mod.attr] = "";
+ // Continue with next mods (if any))
+ delete_mods(mods);
+ }, function(ex, data) {
+ popup_err("Failed to delete attribute: " + mod.attr, data);
+ // Reset HTML for remaining values that have not been processed
+ $("#" + mod.attr).val(config_values[mod.attr]);
+ for (remaining in mods) {
+ $("#" + remaining.attr).val(config_values[remaining.attr]);
+ }
+ check_inst_alive(0);
+ return -1; // Stop on error
});
}
function save_config() {
// Loop over current config_values check for differences
- var mod_list = [];
-
+ let mod_list = [];
+ let del_list = [];
for (var attr in config_values) {
var mod = {};
if ( $("#" + attr).is(':checkbox')) {
@@ -360,7 +382,6 @@ function save_config() {
} else {
// Normal input
var val = $("#" + attr).val();
-
// But first check for rootdn-pw changes and check confirm input matches
if (attr == "nsslapd-rootpw") {
if (val != config_values[attr] || val !=
$("#nsslapd-rootpw-confirm").val()) {
@@ -379,16 +400,34 @@ function save_config() {
}
if (attr == "nsslapd-port") {
- if (!valid_num(config_values[attr])) {
+ if (!valid_port(val)) {
popup_msg("Port number is not valid");
$("#nsslapd-port").val(config_values[attr]);
}
}
- if ( val && val != config_values[attr]) {
+ if (attr.indexOf("logrotationsynchour") != -1) {
+ if (!valid_num(val) || val < 0 || val > 23) {
+ popup_msg("Invalid value", "You must use a number between 0
- 23 for: " + attr);
+ $("#" + attr).val(config_values[attr])
+ return;
+ }
+ }
+ if (attr.indexOf("logrotationsyncmin") != -1) {
+ if (!valid_num(val) || val < 0 || val > 59){
+ popup_msg("Invalid value", "You must use a number between 0
- 59 for: " + attr);
+ $("#" + attr).val(config_values[attr])
+ return;
+ }
+ }
+
+ if (val && val != config_values[attr]) {
mod['attr'] = attr;
mod['val'] = val;
mod_list.push(mod);
+ } else if (val == "" && val != config_values[attr]) {
+ mod['attr'] = attr;
+ del_list.push(mod);
}
}
}
@@ -434,8 +473,19 @@ function save_config() {
}
// Build dsconf commands to apply all the mods
- if (mod_list.length) {
- apply_mods(mod_list);
+ if (mod_list.length || del_list.length) {
+ let err = 0;
+ if (mod_list.length) {
+ if (apply_mods(mod_list) == -1) {
+ return;
+ }
+ }
+ if (del_list.length) {
+ if (delete_mods(del_list) == -1) {
+ return;
+ }
+ }
+ popup_success("Successfully updated configuration");
} else {
// No changes to save, log msg? popup_msg()
}
@@ -1500,7 +1550,7 @@ $(document).ready( function() {
report_err($("#create-inst-port"), 'You must provide a port
number');
$("#create-inst-port").css("border-color", "red");
return;
- } else if (!valid_num(server_port)) {
+ } else if (!valid_port(server_port)) {
report_err($("#create-inst-port"), 'Port must be a number between 1
and 65534!');
$("#create-inst-port").css("border-color", "red");
return;
@@ -1514,7 +1564,7 @@ $(document).ready( function() {
report_err($("#create-inst-secureport"), 'You must provide a secure
port number');
$("#create-inst-secureport").css("border-color",
"red");
return;
- } else if (!valid_num(secure_port)) {
+ } else if (!valid_port(secure_port)) {
report_err($("#create-inst-secureport"), 'Secure port must be a
number!');
$("#create-inst-secureport").css("border-color",
"red");
return;
@@ -1584,8 +1634,8 @@ $(document).ready( function() {
}
if ( $("#create-sample-entries").is(":checked") ) {
setup_inf += '\nsample_entries = yes\n';
- } else {
- setup_inf += '\nsample_entries = no\n';
+ } else if ( $("#create-suffix-entry").is(":checked") ) {
+ setup_inf += '\ncreate_suffix_entry = yes\n';
}
}
@@ -1599,9 +1649,9 @@ $(document).ready( function() {
* [5] Create the instance
* [6] Remove setup file
*/
- cockpit.spawn(["hostname", "--fqdn"], { superuser: true,
"err": "message" }).fail(function(ex) {
+ cockpit.spawn(["hostname", "--fqdn"], { superuser: true,
"err": "message" }).fail(function(ex, data) {
// Failed to get FQDN
- popup_err("Failed to get hostname!", ex.message);
+ popup_err("Failed to get hostname!", data);
}).done(function (data){
/*
* We have FQDN, so set the hostname in inf file, and create the setup file
@@ -1610,38 +1660,38 @@ $(document).ready( function() {
var setup_file = "/tmp/389-setup-" + (new Date).getTime() +
".inf";
var rm_cmd = ['rm', setup_file];
var create_file_cmd = ['touch', setup_file];
- cockpit.spawn(create_file_cmd, { superuser: true, "err":
"message" }).fail(function(ex) {
+ cockpit.spawn(create_file_cmd, { superuser: true, "err":
"message" }).fail(function(ex, data) {
// Failed to create setup file
- popup_err("Failed to create installation file!", ex.message);
+ popup_err("Failed to create installation file!", data);
}).done(function (){
/*
* We have our new setup file, now set permissions on that setup file before we
add sensitive data
*/
var chmod_cmd = ['chmod', '600', setup_file];
- cockpit.spawn(chmod_cmd, { superuser: true, "err":
"message" }).fail(function(ex) {
+ cockpit.spawn(chmod_cmd, { superuser: true, "err":
"message" }).fail(function(ex, data) {
// Failed to set permissions on setup file
cockpit.spawn(rm_cmd, { superuser: true }); // Remove Inf file with clear
text password
$("#create-inst-spinner").hide();
- popup_err("Failed to set permission on setup file " + setup_file +
": ", ex.message);
+ popup_err("Failed to set permission on setup file " + setup_file +
": ", data);
}).done(function (){
/*
* Success we have our setup file and it has the correct permissions.
* Now populate the setup file...
*/
- var cmd = ["/bin/sh", "-c", '/usr/bin/echo -e
"' + setup_inf + '" >> ' + setup_file];
- cockpit.spawn(cmd, { superuser: true, "err": "message"
}).fail(function(ex) {
+ let cmd = ["/bin/sh", "-c", '/usr/bin/echo -e
"' + setup_inf + '" >> ' + setup_file];
+ cockpit.spawn(cmd, { superuser: true, "err": "message"
}).fail(function(ex, data) {
// Failed to populate setup file
- popup_err("Failed to populate installation file!", ex.message);
+ popup_err("Failed to populate installation file!", data);
}).done(function (){
/*
* Next, create the instance...
*/
cmd = [DSCREATE, 'from-file', setup_file];
- cockpit.spawn(cmd, { superuser: true, "err": "message",
"environ": [ENV] }).fail(function(ex) {
+ cockpit.spawn(cmd, { superuser: true, "err": "message",
"environ": [ENV] }).fail(function(ex, data) {
// Failed to create the new instance!
cockpit.spawn(rm_cmd, { superuser: true }); // Remove Inf file with
clear text password
$("#create-inst-spinner").hide();
- popup_err("Failed to create instance!", ex.message);
+ popup_err("Failed to create instance!", data);
}).done(function (){
// Success!!! Now cleanup everything up...
cockpit.spawn(rm_cmd, { superuser: true }); // Remove Inf file with
clear text password
diff --git a/src/lib389/lib389/__init__.py b/src/lib389/lib389/__init__.py
index 8e6eb66..0ff4335 100644
--- a/src/lib389/lib389/__init__.py
+++ b/src/lib389/lib389/__init__.py
@@ -2940,7 +2940,7 @@ class DirSrv(SimpleLDAPObject, object):
json_result = {'type': 'list', 'items': []}
for backup in dirlist:
bak = bakdir + "/" + backup
- bak_date = os.path.getctime(bak)
+ bak_date = os.path.getmtime(bak)
bak_date = datetime.fromtimestamp(bak_date).strftime('%Y-%m-%d
%H:%M:%S')
bak_size = subprocess.check_output(['du', '-sh',
bak]).split()[0].decode('utf-8')
if use_json:
@@ -2980,12 +2980,13 @@ class DirSrv(SimpleLDAPObject, object):
json_result = {'type': 'list', 'items': []}
for ldif in dirlist:
fullpath = ldifdir + "/" + ldif
- ldif_date = os.path.getctime(fullpath)
+ ldif_date = os.path.getmtime(fullpath)
ldif_date = datetime.fromtimestamp(ldif_date).strftime('%Y-%m-%d
%H:%M:%S')
ldif_size = subprocess.check_output(['du', '-sh',
fullpath]).split()[0].decode('utf-8')
ldif_suffix = self.getLDIFSuffix(fullpath)
if ldif_suffix == "":
- ldif_suffix = "???"
+ # This is not a valid LDIF file
+ ldif_suffix = "Invalid LDIF"
if use_json:
json_item = [ldif, ldif_date, ldif_size, ldif_suffix]
json_result['items'].append(json_item)
diff --git a/src/lib389/lib389/_mapped_object.py b/src/lib389/lib389/_mapped_object.py
index 36ddc2e..010d964 100644
--- a/src/lib389/lib389/_mapped_object.py
+++ b/src/lib389/lib389/_mapped_object.py
@@ -282,7 +282,7 @@ class DSLdapObject(DSLogging):
mods = []
for arg in args:
- if isinstance(arg[1], list):
+ if isinstance(arg[1], list) or isinstance(arg[1], tuple):
value = ensure_list_bytes(arg[1])
else:
value = [ensure_bytes(arg[1])]
diff --git a/src/lib389/lib389/chaining.py b/src/lib389/lib389/chaining.py
index a25bbb6..7a0401e 100644
--- a/src/lib389/lib389/chaining.py
+++ b/src/lib389/lib389/chaining.py
@@ -124,7 +124,7 @@ class ChainingLink(DSLdapObject):
pass
# Delete the monitoring entry
- monitor = self.get_monitor(rdn)
+ monitor = self.get_monitor()
monitor.delete()
# Delete the link
diff --git a/src/lib389/lib389/cli_conf/backend.py
b/src/lib389/lib389/cli_conf/backend.py
index 36e32ec..0b55ba4 100644
--- a/src/lib389/lib389/cli_conf/backend.py
+++ b/src/lib389/lib389/cli_conf/backend.py
@@ -8,6 +8,12 @@
# --- END COPYRIGHT BLOCK ---
from lib389.backend import Backend, Backends, DatabaseConfig
+from lib389.configurations.sample import (
+ create_base_domain,
+ create_base_org,
+ create_base_orgunit,
+ create_base_cn,
+ )
from lib389.chaining import (ChainingLinks)
from lib389.index import Index, VLVIndex, VLVSearches
from lib389.monitor import MonitorLDBM
@@ -172,6 +178,29 @@ def backend_create(inst, basedn, log, args):
be = Backend(inst)
be.create(properties=props)
+ if args.create_suffix and not args.create_entries:
+ # Set basic ACIs (taken from instance/setup.py)
+ o_aci = '(targetattr="o || description ||
objectClass")(targetfilter="(objectClass=organization)")(version 3.0; acl
"Enable anyone o read"; allow (read, search,
compare)(userdn="ldap:///anyone");)'
+ dc_aci = '(targetattr="dc || description ||
objectClass")(targetfilter="(objectClass=domain)")(version 3.0; acl
"Enable anyone domain read"; allow (read, search,
compare)(userdn="ldap:///anyone");)',
+ ou_aci = '(targetattr="ou || description ||
objectClass")(targetfilter="(objectClass=organizationalUnit)")(version 3.0;
acl "Enable anyone ou read"; allow (read, search,
compare)(userdn="ldap:///anyone");)'
+ cn_aci = '(targetattr="cn || description ||
objectClass")(targetfilter="(objectClass=nscontainer)")(version 3.0; acl
"Enable anyone cn read"; allow (read, search,
compare)(userdn="ldap:///anyone");)'
+ suffix_rdn_attr = args.suffix.split('=')[0].lower()
+ if suffix_rdn_attr == 'dc':
+ domain = create_base_domain(inst, args.suffix)
+ domain.add('aci', dc-aci)
+ elif suffix_rdn_attr == 'o':
+ org = create_base_org(inst, args.suffix)
+ org.add('aci', o_aci)
+ elif suffix_rdn_attr == 'ou':
+ orgunit = create_base_orgunit(inst, args.suffix)
+ orgunit.add('aci', ou_aci)
+ elif suffix_rdn_attr == 'cn':
+ cn = create_base_cn(inst, args.suffix)
+ cn.add('aci', cn_aci)
+ else:
+ # Unsupported rdn
+ raise ValueError("Suffix RDN is not supported for creating suffix
object. Only 'dc', 'o', 'ou', and 'cn' are
supported.")
+
print("The database was sucessfully created")
@@ -1052,6 +1081,8 @@ def create_parser(subparsers):
create_parser.add_argument('--suffix', required=True, help='The database
suffix DN, for example "dc=example,dc=com"')
create_parser.add_argument('--be-name', required=True, help='The database
backend name, for example "userroot"')
create_parser.add_argument('--create-entries', action='store_true',
help='Create sample entries in the database')
+ create_parser.add_argument('--create-suffix', action='store_true',
+ help="Create the suffix object entry in the database. Only suffixes using
the attributes 'dc', 'o', 'ou', or 'cn' are supported in
this feature")
#######################################################
# Delete backend
diff --git a/src/lib389/lib389/cli_conf/security.py
b/src/lib389/lib389/cli_conf/security.py
index 20f2574..0273817 100644
--- a/src/lib389/lib389/cli_conf/security.py
+++ b/src/lib389/lib389/cli_conf/security.py
@@ -91,7 +91,10 @@ def _security_generic_set(inst, basedn, logs, args, attrs_map):
if arg is None:
continue
dsobj = props.cls(inst)
- dsobj.replace(props.attr, arg)
+ if arg != "":
+ dsobj.replace(props.attr, arg)
+ else:
+ dsobj.remove_all(props.attr)
def _security_generic_get_parser(parent, attrs_map, help):
diff --git a/src/lib389/lib389/configurations/sample.py
b/src/lib389/lib389/configurations/sample.py
index 25a1b32..f30b8d6 100644
--- a/src/lib389/lib389/configurations/sample.py
+++ b/src/lib389/lib389/configurations/sample.py
@@ -9,6 +9,9 @@
from ldap import dn
from lib389.idm.domain import Domain
+from lib389.idm.organization import Organization
+from lib389.idm.organizationalunit import OrganizationalUnit
+from lib389.idm.nscontainer import nsContainer
from lib389.utils import ensure_str
@@ -42,3 +45,53 @@ def create_base_domain(instance, basedn):
return domain
+
+def create_base_org(instance, basedn):
+ """Create the base organization object"""
+
+ org = Organization(instance, dn=basedn)
+ # Explode the dn to get the first bit.
+ avas = dn.str2dn(basedn)
+ o_ava = avas[0][0][1]
+
+ org.create(properties={
+ # I think in python 2 this forces unicode return ...
+ 'o': o_ava,
+ 'description': basedn,
+ })
+
+ return org
+
+
+def create_base_orgunit(instance, basedn):
+ """Create the base org unit object for a org unit"""
+
+ orgunit = OrganizationalUnit(instance, dn=basedn)
+ # Explode the dn to get the first bit.
+ avas = dn.str2dn(basedn)
+ ou_ava = avas[0][0][1]
+
+ orgunit.create(properties={
+ # I think in python 2 this forces unicode return ...
+ 'ou': ou_ava,
+ 'description': basedn,
+ })
+
+ return orgunit
+
+
+def create_base_cn(instance, basedn):
+ """Create the base nsContainer object"""
+
+ cn = nsContainer(instance, dn=basedn)
+ # Explode the dn to get the first bit.
+ avas = dn.str2dn(basedn)
+ cn_ava = avas[0][0][1]
+
+ cn.create(properties={
+ # I think in python 2 this forces unicode return ...
+ 'cn': cn_ava,
+ 'description': basedn,
+ })
+
+ return cn
diff --git a/src/lib389/lib389/replica.py b/src/lib389/lib389/replica.py
index 21c1ada..48a14cf 100644
--- a/src/lib389/lib389/replica.py
+++ b/src/lib389/lib389/replica.py
@@ -1264,15 +1264,15 @@ class Replica(DSLdapObject):
raise ValueError('Failed to update replica: ' + str(e))
elif replicarole == ReplicaRole.CONSUMER and newrole == ReplicaRole.MASTER:
try:
- self.replace_many([(REPL_TYPE, str(REPLICA_RDWR_TYPE)),
+ self.replace_many((REPL_TYPE, str(REPLICA_RDWR_TYPE)),
(REPL_FLAGS, str(REPLICA_FLAGS_WRITE)),
- (REPL_ID, str(rid))])
+ (REPL_ID, str(rid)))
except ldap.LDAPError as e:
raise ValueError('Failed to update replica: ' + str(e))
elif replicarole == ReplicaRole.HUB and newrole == ReplicaRole.MASTER:
try:
- self.replace_many([(REPL_TYPE, str(REPLICA_RDWR_TYPE)),
- (REPL_ID, str(rid))])
+ self.replace_many((REPL_TYPE, str(REPLICA_RDWR_TYPE)),
+ (REPL_ID, str(rid)))
except ldap.LDAPError as e:
raise ValueError('Failed to update replica: ' + str(e))
--
To stop receiving notification emails like this one, please contact
the administrator of this repository.