From 0be01490e171aa1aacba57616940f22b20481e7d Mon Sep 17 00:00:00 2001 From: Simon Pichugin Date: Jan 31 2019 14:45:22 +0000 Subject: Issue 50041 - CLI and WebUI - Add memberOf plugin functionality Description: Add the main functionality to memberOf plugin tab. Increase the eslint max line length from 80 to 100. Rework plugin properties to be more compact. Eslint webpack config. Add react-bootstrap-typeahead for multivalued attributes. Fix the word 'successfully' typos. https://pagure.io/389-ds-base/issue/50041 Reviewed by: mreynolds, wibrown (Thanks!) --- diff --git a/src/cockpit/389-console/.eslintrc.json b/src/cockpit/389-console/.eslintrc.json index 11a82c8..ee0a235 100644 --- a/src/cockpit/389-console/.eslintrc.json +++ b/src/cockpit/389-console/.eslintrc.json @@ -44,7 +44,8 @@ "eqeqeq": "off", "import/no-webpack-loader-syntax": "off", "object-property-newline": "off", - "react/jsx-no-bind": "off" + "react/jsx-no-bind": "off", + "max-len": ["error", { "code": 100 }] }, "globals": { "require": false, diff --git a/src/cockpit/389-console/package.json b/src/cockpit/389-console/package.json index 054a373..0699691 100644 --- a/src/cockpit/389-console/package.json +++ b/src/cockpit/389-console/package.json @@ -14,13 +14,13 @@ "@babel/core": "^7.0.0", "@babel/preset-env": "^7.0.0", "@babel/preset-react": "^7.0.0", + "ajv": "^6.0.0", "babel-eslint": "^9.0.0", "babel-loader": "^8.0.0", "chrome-remote-interface": "^0.25.5", "compression-webpack-plugin": "^1.1.11", "copy-webpack-plugin": "^4.5.2", "css-loader": "^0.28.11", - "style-loader": "^0.23.1", "eslint": "^5.4.0", "eslint-config-standard": "^11.0.0", "eslint-config-standard-react": "^6.0.0", @@ -34,17 +34,20 @@ "extract-text-webpack-plugin": "^4.0.0-beta.0", "htmlparser": "^1.7.7", "jed": "^1.1.1", - "ajv": "^6.0.0", "sass-loader": "^7.0.3", "sizzle": "^2.3.3", "stdio": "^0.2.7", + "style-loader": "^0.23.1", "webpack": "^4.17.1", "webpack-cli": "^3.1.0" }, "dependencies": { "patternfly": "3.58.0", "patternfly-react": "2.24.5", + "bootstrap": "4.2.1", + "node-sass": "4.11.0", "react-bootstrap": "0.32.4", + "react-bootstrap-typeahead": "3.2.4", "react": "16.6.1", "react-dom": "16.6.1", "prop-types": "15.6.2", diff --git a/src/cockpit/389-console/src/css/ds.css b/src/cockpit/389-console/src/css/ds.css index d38ff55..8e9ba21 100644 --- a/src/cockpit/389-console/src/css/ds.css +++ b/src/cockpit/389-console/src/css/ds.css @@ -1310,7 +1310,7 @@ option { .ds-td { padding-left: 10px !important; padding-top: 8px !important; - padding-bottom: 0px ~important; + padding-bottom: 0px !important; valign: middle !important; } @@ -1454,6 +1454,11 @@ tbody:nth-child(even) { bottom: 10px; } +.ds-plugin-button { + margin-right: 5px; + margin-left: 5px; +} + .ds-plugin-spinner { margin-top: 20px; margin-bottom: 10px; @@ -1494,3 +1499,12 @@ tbody:nth-child(even) { .control-label { text-align: left !important; } + +.rbt-token { + background-color: #ededed; + color: #363636; +} + +.rbt-input-multi { + height: auto !important; +} diff --git a/src/cockpit/389-console/src/index.html b/src/cockpit/389-console/src/index.html index 6b3f213..b23a722 100644 --- a/src/cockpit/389-console/src/index.html +++ b/src/cockpit/389-console/src/index.html @@ -30,6 +30,7 @@ + diff --git a/src/cockpit/389-console/src/lib/customCollapse.jsx b/src/cockpit/389-console/src/lib/customCollapse.jsx index 71ea0f5..b992982 100644 --- a/src/cockpit/389-console/src/lib/customCollapse.jsx +++ b/src/cockpit/389-console/src/lib/customCollapse.jsx @@ -26,7 +26,6 @@ class CustomCollapse extends React.Component { > {" "} {open ? textOpened : textClosed} diff --git a/src/cockpit/389-console/src/lib/plugins/memberOf.jsx b/src/cockpit/389-console/src/lib/plugins/memberOf.jsx index 33a2091..51ecd59 100644 --- a/src/cockpit/389-console/src/lib/plugins/memberOf.jsx +++ b/src/cockpit/389-console/src/lib/plugins/memberOf.jsx @@ -1,24 +1,975 @@ +import cockpit from "cockpit"; import React from "react"; -import { noop } from "patternfly-react"; +import { + Icon, + Modal, + Button, + Row, + Col, + Form, + noop, + FormGroup, + FormControl, + Checkbox, + ControlLabel +} from "patternfly-react"; +import { Typeahead } from "react-bootstrap-typeahead"; import PropTypes from "prop-types"; import PluginBasicConfig from "./pluginBasicConfig.jsx"; +import { log_cmd } from "../tools.jsx"; import "../../css/ds.css"; class MemberOf extends React.Component { + componentWillMount(prevProps) { + this.updateFields(); + } + + componentDidUpdate(prevProps) { + if (this.props.rows !== prevProps.rows) { + this.updateFields(); + } + } + + constructor(props) { + super(props); + + this.updateFields = this.updateFields.bind(this); + this.handleFieldChange = this.handleFieldChange.bind(this); + this.handleCheckboxChange = this.handleCheckboxChange.bind(this); + this.openModal = this.openModal.bind(this); + this.closeModal = this.closeModal.bind(this); + this.addConfig = this.addConfig.bind(this); + this.editConfig = this.editConfig.bind(this); + this.deleteConfig = this.deleteConfig.bind(this); + this.cmdOperation = this.cmdOperation.bind(this); + this.runFixup = this.runFixup.bind(this); + this.toggleFixupModal = this.toggleFixupModal.bind(this); + + this.state = { + memberOfAttr: [], + memberOfGroupAttr: [], + memberOfEntryScope: "", + memberOfEntryScopeExcludeSubtree: "", + memberOfAutoAddOC: "", + memberOfAllBackends: false, + memberOfSkipNested: false, + memberOfConfigEntry: "", + configEntryModalShow: false, + fixupModalShow: false, + + configDN: "", + configAttr: [], + configGroupAttr: [], + configEntryScope: "", + configEntryScopeExcludeSubtree: "", + configAutoAddOC: "", + configAllBackends: false, + configSkipNested: false, + newEntry: true, + + fixupDN: "", + fixupFilter: "" + }; + } + + toggleFixupModal() { + this.setState(prevState => ({ + fixupModalShow: !prevState.fixupModalShow, + fixupDN: "", + fixupFilter: "" + })); + } + + runFixup() { + if (!this.state.fixupDN) { + this.props.addNotification("warning", "Fixup DN is required."); + } else { + let cmd = [ + "dsconf", + "-j", + "ldapi://%2fvar%2frun%2fslapd-" + this.props.serverId + ".socket", + "plugin", + "memberof", + "fixup", + this.state.fixupDN + ]; + + if (this.state.fixupFilter) { + cmd = [...cmd, "--filter", this.state.fixupFilter]; + } + + this.props.toggleLoadingHandler(); + log_cmd("runFixup", "Run fixup MemberOf Plugin ", cmd); + cockpit + .spawn(cmd, { + superuser: true, + err: "message" + }) + .done(content => { + this.props.addNotification( + "success", + `Fixup task for ${this.state.fixupDN} was successfull` + ); + this.props.toggleLoadingHandler(); + this.setState({ + fixupModalShow: false + }); + }) + .fail(err => { + this.props.addNotification( + "error", + `Fixup task for ${this.state.fixupDN} has failed ${err}` + ); + this.props.toggleLoadingHandler(); + this.setState({ + fixupModalShow: false + }); + }); + } + } + + openModal() { + if (!this.state.memberOfConfigEntry) { + this.setState({ + configEntryModalShow: true, + newEntry: true, + configDN: "", + configAttr: [], + configGroupAttr: [], + configEntryScope: "", + configEntryScopeExcludeSubtree: "", + configAutoAddOC: "", + configAllBackends: false, + configSkipNested: false + }); + } else { + let configAttrObjectList = []; + let configGroupAttrObjectList = []; + let cmd = [ + "dsconf", + "-j", + "ldapi://%2fvar%2frun%2fslapd-" + this.props.serverId + ".socket", + "plugin", + "memberof", + "config-entry", + "show", + this.state.memberOfConfigEntry + ]; + + this.props.toggleLoadingHandler(); + log_cmd("openMemberOfModal", "Fetch the MemberOf Plugin config entry", cmd); + cockpit + .spawn(cmd, { + superuser: true, + err: "message" + }) + .done(content => { + let configEntry = JSON.parse(content).attrs; + this.setState({ + configEntryModalShow: true, + newEntry: false, + configDN: this.state.memberOfConfigEntry, + configAutoAddOC: + configEntry["memberofautoaddoc"] === undefined + ? "" + : configEntry["memberofautoaddoc"][0], + configAllBackends: !( + configEntry["memberofallbackends"] === undefined || + configEntry["memberofallbackends"][0] == "off" + ), + configSkipNested: !( + configEntry["memberofskipnested"] === undefined || + configEntry["memberofskipnested"][0] == "off" + ), + configConfigEntry: + configEntry["nsslapd-pluginConfigArea"] === undefined + ? "" + : configEntry["nsslapd-pluginConfigArea"][0], + configEntryScope: + configEntry["memberofentryscope"] === undefined + ? "" + : configEntry["memberofentryscope"][0], + configEntryScopeExcludeSubtree: + configEntry["memberofentryscopeexcludesubtree"] === undefined + ? "" + : configEntry["memberofentryscopeexcludesubtree"][0] + }); + if (configEntry["memberofattr"] === undefined) { + this.setState({ configAttr: [] }); + } else { + for (let value of configEntry["memberofattr"]) { + configAttrObjectList = [ + ...configAttrObjectList, + { id: value, label: value } + ]; + } + this.setState({ configAttr: configAttrObjectList }); + } + if (configEntry["memberofgroupattr"] === undefined) { + this.setState({ configGroupAttr: [] }); + } else { + for (let value of configEntry["memberofgroupattr"]) { + configGroupAttrObjectList = [ + ...configGroupAttrObjectList, + { id: value, label: value } + ]; + } + this.setState({ configGroupAttr: configGroupAttrObjectList }); + this.props.toggleLoadingHandler(); + } + }) + .fail(_ => { + this.setState({ + configEntryModalShow: true, + newEntry: true, + configDN: this.state.memberOfConfigEntry, + configAttr: [], + configGroupAttr: [], + configEntryScope: "", + configEntryScopeExcludeSubtree: "", + configAutoAddOC: "", + configAllBackends: false, + configSkipNested: false + }); + this.props.toggleLoadingHandler(); + }); + } + } + + closeModal() { + this.setState({ configEntryModalShow: false }); + } + + cmdOperation(action) { + const { + configDN, + configAttr, + configGroupAttr, + configEntryScope, + configEntryScopeExcludeSubtree, + configAutoAddOC, + configAllBackends, + configSkipNested + } = this.state; + + if (configAttr.length == 0 || configGroupAttr.length == 0) { + this.props.addNotification( + "warning", + "Config Attribute and Group Attribute are required." + ); + } else { + let cmd = [ + "dsconf", + "-j", + "ldapi://%2fvar%2frun%2fslapd-" + this.props.serverId + ".socket", + "plugin", + "memberof", + "config-entry", + action, + configDN, + "--scope", + configEntryScope || action == "add" ? configEntryScope : "delete", + "--exclude", + configEntryScopeExcludeSubtree || action == "add" + ? configEntryScopeExcludeSubtree + : "delete", + "--autoaddoc", + configAutoAddOC || action == "add" ? configAutoAddOC : "delete", + "--allbackends", + configAllBackends ? "on" : "off", + "--skipnested", + configSkipNested ? "on" : "off" + ]; + + // Delete attributes if the user set an empty value to the field + cmd = [...cmd, "--attr"]; + if (configAttr.length != 0) { + for (let value of configAttr) { + cmd = [...cmd, value.label]; + } + } + cmd = [...cmd, "--groupattr"]; + if (configGroupAttr.length != 0) { + for (let value of configGroupAttr) { + cmd = [...cmd, value.label]; + } + } + + this.props.toggleLoadingHandler(); + log_cmd("memberOfOperation", `Do the ${action} operation on the MemberOf Plugin`, cmd); + cockpit + .spawn(cmd, { + superuser: true, + err: "message" + }) + .done(content => { + console.info("memberOfOperation", "Result", content); + this.props.addNotification( + "success", + `Config entry ${configDN} was successfully ${action}ed` + ); + this.props.pluginListHandler(); + this.closeModal(); + this.props.toggleLoadingHandler(); + }) + .fail(err => { + this.props.addNotification( + "error", + `Error during the config entry ${action} operation - ${err}` + ); + this.props.pluginListHandler(); + this.closeModal(); + this.props.toggleLoadingHandler(); + }); + } + } + + deleteConfig() { + let cmd = [ + "dsconf", + "-j", + "ldapi://%2fvar%2frun%2fslapd-" + this.props.serverId + ".socket", + "plugin", + "memberof", + "config-entry", + "delete", + this.state.configDN + ]; + + this.props.toggleLoadingHandler(); + log_cmd("deleteConfig", "Delete the MemberOf Plugin config entry", cmd); + cockpit + .spawn(cmd, { + superuser: true, + err: "message" + }) + .done(content => { + console.info("deleteConfig", "Result", content); + this.props.addNotification( + "success", + `Config entry ${this.state.configDN} was successfully deleted` + ); + this.props.pluginListHandler(); + this.closeModal(); + this.props.toggleLoadingHandler(); + }) + .fail(err => { + this.props.addNotification( + "error", + `Error during the config entry removal operation - ${err}` + ); + this.props.pluginListHandler(); + this.closeModal(); + this.props.toggleLoadingHandler(); + }); + } + + addConfig() { + this.cmdOperation("add"); + } + + editConfig() { + this.cmdOperation("edit"); + } + + handleCheckboxChange(e) { + this.setState({ + [e.target.id]: e.target.checked + }); + } + + handleFieldChange(e) { + this.setState({ + [e.target.id]: e.target.value + }); + } + + updateFields() { + let memberOfAttrObjectList = []; + let memberOfGroupAttrObjectList = []; + + if (this.props.rows.length > 0) { + const pluginRow = this.props.rows.find(row => row.cn[0] === "MemberOf Plugin"); + + this.setState({ + memberOfAutoAddOC: + pluginRow["memberofautoaddoc"] === undefined + ? "" + : pluginRow["memberofautoaddoc"][0], + memberOfAllBackends: !( + pluginRow["memberofallbackends"] === undefined || + pluginRow["memberofallbackends"][0] == "off" + ), + memberOfSkipNested: !( + pluginRow["memberofskipnested"] === undefined || + pluginRow["memberofskipnested"][0] == "off" + ), + memberOfConfigEntry: + pluginRow["nsslapd-pluginConfigArea"] === undefined + ? "" + : pluginRow["nsslapd-pluginConfigArea"][0], + memberOfEntryScope: + pluginRow["memberofentryscope"] === undefined + ? "" + : pluginRow["memberofentryscope"][0], + memberOfEntryScopeExcludeSubtree: + pluginRow["memberofentryscopeexcludesubtree"] === undefined + ? "" + : pluginRow["memberofentryscopeexcludesubtree"][0] + }); + if (pluginRow["memberofattr"] === undefined) { + this.setState({ memberOfAttr: [] }); + } else { + for (let value of pluginRow["memberofattr"]) { + memberOfAttrObjectList = [ + ...memberOfAttrObjectList, + { id: value, label: value } + ]; + } + this.setState({ memberOfAttr: memberOfAttrObjectList }); + } + if (pluginRow["memberofgroupattr"] === undefined) { + this.setState({ memberOfGroupAttr: [] }); + } else { + for (let value of pluginRow["memberofgroupattr"]) { + memberOfGroupAttrObjectList = [ + ...memberOfGroupAttrObjectList, + { id: value, label: value } + ]; + } + this.setState({ memberOfGroupAttr: memberOfGroupAttrObjectList }); + } + } + } + render() { + const { + memberOfAttr, + memberOfGroupAttr, + memberOfEntryScope, + memberOfEntryScopeExcludeSubtree, + memberOfAutoAddOC, + memberOfAllBackends, + memberOfSkipNested, + memberOfConfigEntry, + configDN, + configEntryModalShow, + configAttr, + configGroupAttr, + configEntryScope, + configEntryScopeExcludeSubtree, + configAutoAddOC, + configAllBackends, + configSkipNested, + newEntry, + fixupModalShow, + fixupDN, + fixupFilter + } = this.state; + + let specificPluginCMD = [ + "dsconf", + "-j", + "ldapi://%2fvar%2frun%2fslapd-" + this.props.serverId + ".socket", + "plugin", + "memberof", + "edit", + "--scope", + memberOfEntryScope || "delete", + "--exclude", + memberOfEntryScopeExcludeSubtree || "delete", + "--autoaddoc", + memberOfAutoAddOC || "delete", + "--config-entry", + memberOfConfigEntry || "delete", + "--allbackends", + memberOfAllBackends ? "on" : "off", + "--skipnested", + memberOfSkipNested ? "on" : "off" + ]; + + // Delete attributes if the user set an empty value to the field + specificPluginCMD = [...specificPluginCMD, "--attr"]; + if (memberOfAttr.length != 0) { + for (let value of memberOfAttr) { + specificPluginCMD = [...specificPluginCMD, value.label]; + } + } else { + specificPluginCMD = [...specificPluginCMD, "delete"]; + } + + specificPluginCMD = [...specificPluginCMD, "--groupattr"]; + if (memberOfGroupAttr.length != 0) { + for (let value of memberOfGroupAttr) { + specificPluginCMD = [...specificPluginCMD, value.label]; + } + } else { + specificPluginCMD = [...specificPluginCMD, "delete"]; + } + return (
+ +
+ + + Fixup MemberOf Task + + + + +
+ + + Base DN + + + + + + + + Filter DN + + + + + +
+ +
+
+ + + + +
+
+ +
+ + + Manage MemberOf Plugin Shared Config Entry + + + + +
+ + + Config DN + + + + + + + + Attribute + + + { + this.setState({ + configAttr: values + }); + }} + selected={configAttr} + newSelectionPrefix="Add a member: " + options={[ + { + id: "memberOf", + label: "memberOf" + } + ]} + placeholder="Type a member attribute..." + /> + + + + + Group Attribute + + + { + this.setState({ + configGroupAttr: values + }); + }} + selected={configGroupAttr} + newSelectionPrefix="Add a group member: " + options={[ + { + id: "member", + label: "member" + } + ]} + placeholder="Type a member group attribute..." + /> + + +
+ +
+ + +
+ + + Entry Scope + + + + + + + All Backends + + + + + + Entry Scope Exclude Subtree + + + + + + + Skip Nested + + + +
+ +
+ + +
+ + + Auto Add OC + + + + + +
+ +
+
+ + + + + + +
+
+ > + + +
+ + + Attribute + + + { + this.setState({ + memberOfAttr: values + }); + }} + selected={memberOfAttr} + newSelectionPrefix="Add a member: " + options={[ + { + id: "member", + label: "member" + }, + { + id: "memberCertificate", + label: "memberCertificate" + }, + { + id: "uniqueMember", + label: "uniqueMember" + } + ]} + placeholder="Type a member attribute..." + /> + + + + + Group Attribute + + + { + this.setState({ + memberOfGroupAttr: values + }); + }} + selected={memberOfGroupAttr} + newSelectionPrefix="Add a group member: " + options={[ + { + id: "groupOfNames", + label: "groupOfNames" + }, + { + id: "groupOfURLs", + label: "groupOfURLs" + }, + { + id: "groupOfUniqueNames", + label: "groupOfUniqueNames" + }, + { + id: "groupOfCertificates", + label: "groupOfCertificates" + } + ]} + placeholder="Type a member group attribute..." + /> + + +
+ +
+ + +
+ + + Entry Scope + + + + + + + All Backends + + + + + + Entry Scope Exclude Subtree + + + + + + + Skip Nested + + + +
+ +
+ + +
+ + + Shared Config Entry + + + + + + + + +
+ +
+ + +
+ + + Auto Add OC + + + + + +
+ +
+ + + + + +
); } diff --git a/src/cockpit/389-console/src/lib/plugins/pluginBasicConfig.jsx b/src/cockpit/389-console/src/lib/plugins/pluginBasicConfig.jsx index a61356e..ae37334 100644 --- a/src/cockpit/389-console/src/lib/plugins/pluginBasicConfig.jsx +++ b/src/cockpit/389-console/src/lib/plugins/pluginBasicConfig.jsx @@ -43,7 +43,9 @@ class PluginBasicConfig extends React.Component { currentPluginId: "", currentPluginVendor: "", currentPluginVersion: "", - currentPluginDescription: "" + currentPluginDescription: "", + currentPluginDependsOnType: "", + currentPluginDependsOnNamed: "" }; } @@ -62,9 +64,7 @@ class PluginBasicConfig extends React.Component { addNotification, toggleLoadingHandler } = this.props; - const new_status = this.state.currentPluginEnabled - ? "disable" - : "enable"; + const new_status = this.state.currentPluginEnabled ? "disable" : "enable"; const cmd = [ "dsconf", "-j", @@ -76,11 +76,7 @@ class PluginBasicConfig extends React.Component { toggleLoadingHandler(); this.setState({ disableSwitch: true }); - log_cmd( - "handleSwitchChange", - "Switch plugin states from the plugin tab", - cmd - ); + log_cmd("handleSwitchChange", "Switch plugin states from the plugin tab", cmd); cockpit .spawn(cmd, { superuser: true, err: "message" }) .done(content => { @@ -88,7 +84,8 @@ class PluginBasicConfig extends React.Component { pluginListHandler(); addNotification( "success", - `${pluginName} plugin was successfully ${new_status}d` + `${pluginName} plugin was successfully ${new_status}d. + Please, restart the instance.` ); toggleLoadingHandler(); }) @@ -104,9 +101,7 @@ class PluginBasicConfig extends React.Component { updateFields() { if (this.props.rows.length > 0) { - const pluginRow = this.props.rows.find( - row => row.cn[0] === this.props.cn - ); + const pluginRow = this.props.rows.find(row => row.cn[0] === this.props.cn); this.setState({ currentPluginType: pluginRow["nsslapd-pluginType"][0], @@ -115,8 +110,15 @@ class PluginBasicConfig extends React.Component { currentPluginId: pluginRow["nsslapd-pluginId"][0], currentPluginVendor: pluginRow["nsslapd-pluginVendor"][0], currentPluginVersion: pluginRow["nsslapd-pluginVersion"][0], - currentPluginDescription: - pluginRow["nsslapd-pluginDescription"][0] + currentPluginDescription: pluginRow["nsslapd-pluginDescription"][0], + currentPluginDependsOnType: + pluginRow["nsslapd-plugin-depends-on-type"] === undefined + ? "" + : pluginRow["nsslapd-plugin-depends-on-type"][0], + currentPluginDependsOnNamed: + pluginRow["nsslapd-plugin-depends-on-named"] === undefined + ? "" + : pluginRow["nsslapd-plugin-depends-on-named"][0] }); } this.updateSwitch(); @@ -124,9 +126,7 @@ class PluginBasicConfig extends React.Component { updateSwitch() { if (this.props.rows.length > 0) { - const pluginRow = this.props.rows.find( - row => row.cn[0] === this.props.cn - ); + const pluginRow = this.props.rows.find(row => row.cn[0] === this.props.cn); var pluginEnabled; if (pluginRow["nsslapd-pluginEnabled"][0] === "on") { @@ -158,19 +158,21 @@ class PluginBasicConfig extends React.Component { currentPluginVendor, currentPluginVersion, currentPluginDescription, + currentPluginDependsOnType, + currentPluginDependsOnNamed, disableSwitch } = this.state; const modalFieldsCol1 = { currentPluginType: this.state.currentPluginType, currentPluginPath: this.state.currentPluginPath, - currentPluginInitfunc: this.state.currentPluginInitfunc, - currentPluginId: this.state.currentPluginId + currentPluginInitfunc: this.state.currentPluginInitfunc }; const modalFieldsCol2 = { currentPluginVendor: this.state.currentPluginVendor, currentPluginVersion: this.state.currentPluginVersion, - currentPluginDescription: this.state.currentPluginDescription + currentPluginDescription: this.state.currentPluginDescription, + currentPluginId: this.state.currentPluginId }; return (
@@ -184,11 +186,10 @@ class PluginBasicConfig extends React.Component { - - + + Status - this.handleSwitchChange( - currentPluginEnabled - ) - } + onChange={() => this.handleSwitchChange(currentPluginEnabled)} animate={false} disabled={disableSwitch} /> @@ -213,94 +210,100 @@ class PluginBasicConfig extends React.Component {
- {Object.entries(modalFieldsCol1).map( - ([id, value]) => ( - - - Plugin{" "} - {id.replace( - "currentPlugin", - "" - )} - - - - - - ) - )} + {Object.entries(modalFieldsCol1).map(([id, value]) => ( + + + {this.props.memberOfAttr} Plugin{" "} + {id.replace("currentPlugin", "")} + + + + + + ))} + + + Plugin Depends On Type + + + + + + + + Plugin Depends On Named + + + + +
- {Object.entries(modalFieldsCol2).map( - ([id, value]) => ( - - - Plugin{" "} - {id.replace( - "currentPlugin", - "" - )} - - - - - - ) - )} + {Object.entries(modalFieldsCol2).map(([id, value]) => ( + + + Plugin {id.replace("currentPlugin", "")} + + + + + + ))}
- - - - - + + + + +
); } @@ -313,6 +316,7 @@ PluginBasicConfig.propTypes = { cn: PropTypes.string, pluginName: PropTypes.string, cmdName: PropTypes.string, + specificPluginCMD: PropTypes.array, savePluginHandler: PropTypes.func, pluginListHandler: PropTypes.func, addNotification: PropTypes.func, @@ -325,6 +329,7 @@ PluginBasicConfig.defaultProps = { cn: "", pluginName: "", cmdName: "", + specificPluginCMD: [], savePluginHandler: noop, pluginListHandler: noop, addNotification: noop, diff --git a/src/cockpit/389-console/src/lib/plugins/pluginModal.jsx b/src/cockpit/389-console/src/lib/plugins/pluginModal.jsx index 2572475..d8358e9 100644 --- a/src/cockpit/389-console/src/lib/plugins/pluginModal.jsx +++ b/src/cockpit/389-console/src/lib/plugins/pluginModal.jsx @@ -16,18 +16,14 @@ import "../../css/ds.css"; class PluginEditModal extends React.Component { render() { - const modalFields = { - currentPluginType: this.props.currentPluginType, - currentPluginPath: this.props.currentPluginPath, - currentPluginInitfunc: this.props.currentPluginInitfunc, - currentPluginId: this.props.currentPluginId, - currentPluginVendor: this.props.currentPluginVendor, - currentPluginVersion: this.props.currentPluginVersion, - currentPluginDescription: this.props.currentPluginDescription - }; const { showModal, closeHandler, + handleChange, + handleSwitchChange, + savePluginHandler + } = this.props; + const { currentPluginName, currentPluginEnabled, currentPluginType, @@ -37,10 +33,19 @@ class PluginEditModal extends React.Component { currentPluginVendor, currentPluginVersion, currentPluginDescription, - handleChange, - handleSwitchChange, - savePluginHandler - } = this.props; + currentPluginDependsOnType, + currentPluginDependsOnNamed + } = this.props.pluginData; + const modalFields = { + currentPluginType: currentPluginType, + currentPluginPath: currentPluginPath, + currentPluginInitfunc: currentPluginInitfunc, + currentPluginId: currentPluginId, + currentPluginVendor: currentPluginVendor, + currentPluginVersion: currentPluginVersion, + currentPluginDescription: currentPluginDescription + }; + return (
@@ -53,9 +58,7 @@ class PluginEditModal extends React.Component { > - - Edit Plugin - {currentPluginName} - + Edit Plugin - {currentPluginName}
@@ -64,34 +67,26 @@ class PluginEditModal extends React.Component { controlId="currentPluginEnabled" disabled={false} > - + Plugin Status - + - handleSwitchChange( - currentPluginEnabled - ) - } + onChange={() => handleSwitchChange(currentPluginEnabled)} animate={false} /> {Object.entries(modalFields).map(([id, value]) => ( - - + + Plugin {id.replace("currentPlugin", "")} - + ))} + + + Plugin Depends On Type + + + + + + + + Plugin Depends On Type + + + + +
- - @@ -136,15 +163,19 @@ class PluginEditModal extends React.Component { PluginEditModal.propTypes = { handleChange: PropTypes.func, handleSwitchChange: PropTypes.func, - currentPluginName: PropTypes.string, - currentPluginType: PropTypes.string, - currentPluginEnabled: PropTypes.bool, - currentPluginPath: PropTypes.string, - currentPluginInitfunc: PropTypes.string, - currentPluginId: PropTypes.string, - currentPluginVendor: PropTypes.string, - currentPluginVersion: PropTypes.string, - currentPluginDescription: PropTypes.string, + pluginData: PropTypes.exact({ + currentPluginName: PropTypes.string, + currentPluginType: PropTypes.string, + currentPluginEnabled: PropTypes.bool, + currentPluginPath: PropTypes.string, + currentPluginInitfunc: PropTypes.string, + currentPluginId: PropTypes.string, + currentPluginVendor: PropTypes.string, + currentPluginVersion: PropTypes.string, + currentPluginDescription: PropTypes.string, + currentPluginDependsOnType: PropTypes.string, + currentPluginDependsOnNamed: PropTypes.string + }), closeHandler: PropTypes.func, savePluginHandler: PropTypes.func, showModal: PropTypes.bool @@ -153,15 +184,19 @@ PluginEditModal.propTypes = { PluginEditModal.defaultProps = { handleChange: noop, handleSwitchChange: noop, - currentPluginName: "", - currentPluginType: "", - currentPluginEnabled: false, - currentPluginPath: "", - currentPluginInitfunc: "", - currentPluginId: "", - currentPluginVendor: "", - currentPluginVersion: "", - currentPluginDescription: "", + pluginData: { + currentPluginName: "", + currentPluginType: "", + currentPluginEnabled: false, + currentPluginPath: "", + currentPluginInitfunc: "", + currentPluginId: "", + currentPluginVendor: "", + currentPluginVersion: "", + currentPluginDescription: "", + currentPluginDependsOnType: "", + currentPluginDependsOnNamed: "" + }, closeHandler: noop, savePluginHandler: noop, showModal: false diff --git a/src/cockpit/389-console/src/plugins.jsx b/src/cockpit/389-console/src/plugins.jsx index 7afb619..2b455ba 100644 --- a/src/cockpit/389-console/src/plugins.jsx +++ b/src/cockpit/389-console/src/plugins.jsx @@ -69,7 +69,9 @@ export class Plugins extends React.Component { currentPluginId: "", currentPluginVendor: "", currentPluginVersion: "", - currentPluginDescription: "" + currentPluginDescription: "", + currentPluginDependsOnType: "", + currentPluginDependsOnNamed: "" }; } @@ -147,6 +149,14 @@ export class Plugins extends React.Component { currentPluginVendor: rowData["nsslapd-pluginVendor"][0], currentPluginVersion: rowData["nsslapd-pluginVersion"][0], currentPluginDescription: rowData["nsslapd-pluginDescription"][0], + currentPluginDependsOnType: + rowData["nsslapd-plugin-depends-on-type"] === undefined + ? "" + : rowData["nsslapd-plugin-depends-on-type"][0], + currentPluginDependsOnNamed: + rowData["nsslapd-plugin-depends-on-named"] === undefined + ? "" + : rowData["nsslapd-plugin-depends-on-named"][0], showPluginModal: true }); } @@ -179,7 +189,9 @@ export class Plugins extends React.Component { } savePlugin(data) { - cmd = [ + let nothingToSetErr = false; + let basicPluginSuccess = false; + let cmd = [ "dsconf", "-j", "ldapi://%2fvar%2frun%2fslapd-" + this.props.serverId + ".socket", @@ -187,19 +199,23 @@ export class Plugins extends React.Component { "edit", data.name, "--type", - data.type, + data.type || "delete", "--path", - data.path, + data.path || "delete", "--initfunc", - data.initfunc, + data.initfunc || "delete", "--id", - data.id, + data.id || "delete", "--vendor", - data.vendor, + data.vendor || "delete", "--version", - data.version, + data.version || "delete", "--description", - data.description + data.description || "delete", + "--depends-on-type", + data.dependsOnType || "delete", + "--depends-on-named", + data.dependsOnNamed || "delete" ]; if ("enabled" in data) { @@ -207,26 +223,80 @@ export class Plugins extends React.Component { } this.toggleLoading(); - log_cmd("savePlugin", "Edit the plugin from the modal form", cmd); + + log_cmd("savePlugin", "Edit the plugin", cmd); cockpit .spawn(cmd, { superuser: true, err: "message" }) .done(content => { console.info("savePlugin", "Result", content); + basicPluginSuccess = true; this.addNotification( "success", `Plugin ${data.name} was successfully modified` ); - this.pluginList(); this.closePluginModal(); this.toggleLoading(); }) .fail(err => { - this.addNotification( - "error", - `Error during plugin ${data.name} modification - ${err}` - ); + if (err.message.indexOf("nothing to set") >= 0) { + nothingToSetErr = true; + } else { + this.addNotification( + "error", + `${err.message} error during ${data.name} modification` + ); + } this.closePluginModal(); this.toggleLoading(); + }) + .always(() => { + if ("specificPluginCMD" in data) { + this.toggleLoading(); + log_cmd( + "savePlugin", + "Edit the plugin from the plugin config tab", + data.specificPluginCMD + ); + cockpit + .spawn(data.specificPluginCMD, { + superuser: true, + err: "message" + }) + .done(content => { + // Notify success only one time + if (!basicPluginSuccess) { + this.addNotification( + "success", + `Plugin ${data.name} was successfully modified` + ); + } + this.pluginList(); + this.toggleLoading(); + console.info("savePlugin", "Result", content); + }) + .fail(err => { + if ( + (err.message.indexOf( + "nothing to set" + ) >= 0 && + nothingToSetErr) || + err.message.indexOf("nothing to set") < 0 + ) { + if (basicPluginSuccess) { + this.addNotification( + "success", + `Plugin ${data.name} was successfully modified` + ); + this.pluginList(); + } + this.addNotification( + "error", + `${err.message} error during ${data.name} modification` + ); + } + this.toggleLoading(); + }); + } }); } @@ -235,10 +305,7 @@ export class Plugins extends React.Component { allPlugins: { name: "All Plugins", component: ( - + ) }, accountPolicy: { @@ -424,24 +491,20 @@ export class Plugins extends React.Component { - {Object.entries(selectPlugins).map( - ([id, item]) => ( - - {item.component} - - ) - )} + {Object.entries(selectPlugins).map(([id, item]) => ( + + {item.component} + + ))} @@ -449,17 +512,19 @@ export class Plugins extends React.Component { li a { + overflow: hidden; + text-overflow: ellipsis; +} + +.rbt-menu > li a:focus { + outline: none; +} + +.rbt-menu-pagination-option { + text-align: center; +} + +.rbt .rbt-input-main::-ms-clear { + display: none; +} + +.rbt-input-multi { + cursor: text; + overflow: hidden; + position: relative; + height: auto; +} + +.rbt-input-multi.focus { + -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 8px rgba(102, 175, 233, 0.6); + -moz-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 8px rgba(102, 175, 233, 0.6); + box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 8px rgba(102, 175, 233, 0.6); + border-color: #66afe9; + outline: 0; +} + +.rbt-input-multi.form-control[disabled] { + background-color: #e9ecef; + opacity: 1; +} + +.rbt-input-multi input::-moz-placeholder { + color: #999; + opacity: 1; +} + +.rbt-input-multi input:-ms-input-placeholder { + color: #999; +} + +.rbt-input-multi input::-webkit-input-placeholder { + color: #999; +} + +.rbt-input-multi .rbt-input-wrapper { + margin-bottom: -4px; + margin-top: -1px; + overflow: hidden; +} + +.rbt-input-multi .rbt-input-main { + height: 20px; + margin: 1px 0 4px; +} + +.rbt-input-multi .rbt-input-hint-container { + display: inline-block; +} + +.rbt-input-multi.input-lg .rbt-input-main, .rbt-input-multi.form-control-lg .rbt-input-main { + height: 24px; +} + +.rbt-input-multi.input-sm .rbt-input-main, .rbt-input-multi.form-control-sm .rbt-input-main { + height: 18px; +} + +.rbt-close { + z-index: 1; +} + +.rbt-close-lg { + font-size: 24px; +} + +.rbt-token { + background-color: #e7f4ff; + border: 0; + border-radius: 2px; + color: #1f8dd6; + display: inline-block; + line-height: 1em; + margin: 0 3px 3px 0; + padding: 4px 7px; + position: relative; +} + +.rbt-token-disabled { + background-color: #ddd; + color: #888; + pointer-events: none; +} + +.rbt-token-removeable { + cursor: pointer; + padding-right: 21px; +} + +.rbt-token-active { + background-color: #1f8dd6; + color: #fff; + outline: none; + text-decoration: none; +} + +.rbt-token .rbt-token-remove-button { + bottom: 0; + color: inherit; + font-size: inherit; + font-weight: normal; + opacity: 1; + outline: none; + padding: 3px 7px; + position: absolute; + right: 0; + text-shadow: none; + top: -2px; +} + +.rbt-loader { + -webkit-animation: loader-animation 600ms infinite linear; + -o-animation: loader-animation 600ms infinite linear; + animation: loader-animation 600ms infinite linear; + border: 1px solid #d5d5d5; + border-radius: 50%; + border-top-color: #1f8dd6; + display: block; + height: 16px; + width: 16px; +} + +.rbt-loader-lg { + height: 20px; + width: 20px; +} + +.rbt-aux { + display: -webkit-box; + display: -moz-box; + display: -ms-flexbox; + display: -webkit-flex; + display: flex; + align-items: center; + bottom: 0; + justify-content: center; + pointer-events: none; + /* Don't block clicks on the input */ + position: absolute; + right: 0; + top: 0; + width: 34px; +} + +.rbt-aux-lg { + width: 46px; +} + +.rbt-aux .rbt-close { + margin-top: -4px; + pointer-events: auto; + /* Override pointer-events: none; above */ +} + +.has-aux .rbt-input { + padding-right: 34px; +} + +.rbt-highlight-text { + background-color: inherit; + color: inherit; + font-weight: bold; + padding: 0; +} + +/* Input Groups */ +.input-group > .rbt { + -webkit-box-flex: 1; + -moz-box-flex: 1; + -webkit-flex: 1; + -ms-flex: 1; + flex: 1; +} + +.input-group > .rbt .rbt-input-hint-container { + display: -webkit-box; + display: -moz-box; + display: -ms-flexbox; + display: -webkit-flex; + display: flex; +} + +.input-group > .rbt .rbt-input-hint { + z-index: 5; +} + +.input-group > .rbt .rbt-aux { + z-index: 5; +} + +.input-group > .rbt:not(:first-child) .form-control { + border-top-left-radius: 0; + border-bottom-left-radius: 0; +} + +.input-group > .rbt:not(:last-child) .form-control { + border-top-right-radius: 0; + border-bottom-right-radius: 0; +} + +/* Validation States */ +.has-error .rbt-input-multi.focus { + border-color: #843534; + -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #ce8483; + -moz-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #ce8483; + box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #ce8483; +} + +.has-warning .rbt-input-multi.focus { + border-color: #66512c; + -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #c0a16b; + -moz-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #c0a16b; + box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #c0a16b; +} + +.has-success .rbt-input-multi.focus { + border-color: #2b542c; + -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #67b168; + -moz-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #67b168; + box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #67b168; +} + +@keyframes loader-animation { + to { + transform: rotate(1turn); + } +} + +@-webkit-keyframes loader-animation { + to { + -webkit-transform: rotate(1turn); + } +} diff --git a/src/cockpit/389-console/webpack.config.js b/src/cockpit/389-console/webpack.config.js index 6d7cbec..2387cd9 100644 --- a/src/cockpit/389-console/webpack.config.js +++ b/src/cockpit/389-console/webpack.config.js @@ -6,24 +6,22 @@ const webpack = require("webpack"); const CompressionPlugin = require("compression-webpack-plugin"); var externals = { - "cockpit": "cockpit", + cockpit: "cockpit" }; /* These can be overridden, typically from the Makefile.am */ const srcdir = (process.env.SRCDIR || __dirname) + path.sep + "src"; -const builddir = (process.env.SRCDIR || __dirname); +const builddir = process.env.SRCDIR || __dirname; const distdir = builddir + path.sep + "dist"; const section = process.env.ONLYDIR || null; -const nodedir = path.resolve((process.env.SRCDIR || __dirname), "node_modules"); +const nodedir = path.resolve(process.env.SRCDIR || __dirname, "node_modules"); /* A standard nodejs and webpack pattern */ -var production = process.env.NODE_ENV === 'production'; +var production = process.env.NODE_ENV === "production"; var info = { entries: { - "index": [ - "./index.es6" - ] + index: ["./index.es6"] }, files: [ "backend.html", @@ -46,14 +44,14 @@ var info = { "servers.html", "servers.js", "static", - "manifest.json", - ], + "manifest.json" + ] }; var output = { path: distdir, filename: "[name].js", - sourceMapFilename: "[file].map", + sourceMapFilename: "[file].map" }; /* @@ -67,8 +65,7 @@ var output = { function vpath(/* ... */) { var filename = Array.prototype.join.call(arguments, path.sep); var expanded = builddir + path.sep + filename; - if (fs.existsSync(expanded)) - return expanded; + if (fs.existsSync(expanded)) return expanded; expanded = srcdir + path.sep + filename; return expanded; } @@ -81,10 +78,8 @@ Object.keys(info.entries).forEach(function(key) { } info.entries[key] = info.entries[key].map(function(value) { - if (value.indexOf("/") === -1) - return value; - else - return vpath(value); + if (value.indexOf("/") === -1) return value; + else return vpath(value); }); }); @@ -96,26 +91,25 @@ info.files.forEach(function(value) { }); info.files = files; -var plugins = [ - new copy(info.files), - new extract("[name].css") -]; +var plugins = [new copy(info.files), new extract("[name].css")]; /* Only minimize when in production mode */ if (production) { /* Rename output files when minimizing */ output.filename = "[name].min.js"; - plugins.unshift(new CompressionPlugin({ - asset: "[path].gz[query]", - test: /\.(js|html)$/, - minRatio: 0.9, - deleteOriginalAssets: true - })); + plugins.unshift( + new CompressionPlugin({ + asset: "[path].gz[query]", + test: /\.(js|html)$/, + minRatio: 0.9, + deleteOriginalAssets: true + }) + ); } module.exports = { - mode: production ? 'production' : 'development', + mode: production ? "production" : "development", entry: info.entries, externals: externals, output: output, @@ -123,42 +117,44 @@ module.exports = { module: { rules: [ { - enforce: 'pre', + enforce: "pre", exclude: /node_modules/, - loader: 'eslint-loader', + loader: "eslint-loader", test: /\.jsx$/ }, { - enforce: 'pre', + enforce: "pre", exclude: /node_modules/, - loader: 'eslint-loader', + loader: "eslint-loader", test: /\.es6$/ }, { exclude: /node_modules/, - loader: 'babel-loader', + loader: "babel-loader", test: /\.js$/ }, { exclude: /node_modules/, - loader: 'babel-loader', + loader: "babel-loader", test: /\.jsx$/ }, { exclude: /node_modules/, - loader: 'babel-loader', + loader: "babel-loader", test: /\.es6$/ }, { + // Transform our own .css files with PostCSS and CSS-modules + test: /\.css$/, exclude: /node_modules/, - loader: extract.extract('css-loader!sass-loader'), - test: /\.scss$/ + use: ['style-loader', 'css-loader'], }, { test: /\.css$/, - use: [ 'style-loader', 'css-loader' ] + include: /node_modules/, + use: ['style-loader', 'css-loader'], } ] }, plugins: plugins -} +}; diff --git a/src/lib389/lib389/_mapped_object.py b/src/lib389/lib389/_mapped_object.py index e1f9adb..06a2821 100644 --- a/src/lib389/lib389/_mapped_object.py +++ b/src/lib389/lib389/_mapped_object.py @@ -270,7 +270,14 @@ class DSLdapObject(DSLogging): :param *args: tuples of key,value to replace. :type *args: (str, str) """ - mods = list(map(lambda t: (ldap.MOD_REPLACE, ensure_str(t[0]), ensure_bytes(t[1])), args)) + + mods = [] + for arg in args: + if isinstance(arg[1], list): + value = ensure_list_bytes(arg[1]) + else: + value = [ensure_bytes(arg[1])] + mods.append((ldap.MOD_REPLACE, ensure_str(arg[0]), value)) return self._instance.modify_ext_s(self._dn, mods, serverctrls=self._server_controls, clientctrls=self._client_controls) # This needs to work on key + val, and key @@ -371,7 +378,7 @@ class DSLdapObject(DSLogging): def apply_mods(self, mods): """Perform modification operation using several mods at once - :param mods: [(action, key, value),] + :param mods: [(action, key, value),] or [(ldap.MOD_DELETE, key),] :type mods: list of tuples :raises: ValueError - if a provided mod op is invalid """ @@ -381,25 +388,25 @@ class DSLdapObject(DSLogging): if len(mod) < 2: # Error raise ValueError('Not enough arguments in the mod op') - elif len(mod) == 2: # no action - action = ldap.MOD_REPLACE - key, value = mod + elif len(mod) == 2: # delete all attributes action + action, key = mod + if action != ldap.MOD_DELETE: + raise ValueError('Not enough arguments in the mod op') + mod_list.append((action, key, None)) elif len(mod) == 3: action, key, value = mod if action != ldap.MOD_REPLACE and \ action != ldap.MOD_ADD and \ action != ldap.MOD_DELETE: raise ValueError('Invalid mod action(%s)' % str(action)) + if isinstance(value, list): + value = ensure_list_bytes(value) + else: + value = [ensure_bytes(value)] + mod_list.append((action, key, value)) else: # Error too many items raise ValueError('Too many arguments in the mod op') - - if isinstance(value, list): - value = ensure_list_bytes(value) - else: - value = [ensure_bytes(value)] - - mod_list.append((action, key, value)) return self._instance.modify_ext_s(self._dn, mod_list, serverctrls=self._server_controls, clientctrls=self._client_controls) @classmethod diff --git a/src/lib389/lib389/cli_conf/__init__.py b/src/lib389/lib389/cli_conf/__init__.py index a969724..1ba3b4a 100644 --- a/src/lib389/lib389/cli_conf/__init__.py +++ b/src/lib389/lib389/cli_conf/__init__.py @@ -5,6 +5,67 @@ # License: GPL (version 3 or any later version). # See LICENSE for details. # --- END COPYRIGHT BLOCK --- +import ldap +from lib389 import ensure_list_str + + +def _args_to_attrs(args, arg_to_attr): + attrs = {} + for arg in vars(args): + val = getattr(args, arg) + if arg in arg_to_attr and val is not None: + attrs[arg_to_attr[arg]] = val + elif arg == 'DN': + # Extract RDN from the DN + attribute = ldap.dn.str2dn(val)[0][0][0] + value = ldap.dn.str2dn(val)[0][0][1] + attrs[attribute] = value + return attrs + + +def generic_object_add(dsldap_object, log, args, arg_to_attr, props={}): + """Create an entry using DSLdapObject interface + + dsldap_object should be a single instance of DSLdapObject with a set dn + """ + + log = log.getChild('generic_object_add') + # Gather the attributes + attrs = _args_to_attrs(args, arg_to_attr) + # Update the parameters (which should have at least 'cn') with arg attributes + props.update({attr: value for (attr, value) in attrs.items() if value != ""}) + new_object = dsldap_object.create(properties=props) + log.info("Successfully created the %s", new_object.dn) + + +def generic_object_edit(dsldap_object, log, args, arg_to_attr): + """Create an entry using DSLdapObject interface + + dsldap_object should be a single instance of DSLdapObject with a set dn + """ + + log = log.getChild('generic_object_edit') + # Gather the attributes + attrs = _args_to_attrs(args, arg_to_attr) + existing_attributes = dsldap_object.get_all_attrs() + + modlist = [] + for attr, value in attrs.items(): + # Delete the attribute only if the user set it to 'delete' value + if value in ("delete", ["delete"]): + if attr in existing_attributes: + modlist.append((ldap.MOD_DELETE, attr)) + else: + if not isinstance(value, list): + value = [value] + if not (attr in existing_attributes and value == ensure_list_str(existing_attributes[attr])): + modlist.append((ldap.MOD_REPLACE, attr, value)) + if len(modlist) > 0: + dsldap_object.apply_mods(modlist) + log.info("Successfully changed the %s", dsldap_object.dn) + else: + raise ValueError("There is nothing to set in the %s plugin entry" % dsldap_object.dn) + def generic_show(inst, basedn, log, args): """Display plugin configuration.""" @@ -15,27 +76,27 @@ def generic_show(inst, basedn, log, args): def generic_enable(inst, basedn, log, args): plugin = args.plugin_cls(inst) if plugin.status(): - print("Plugin '%s' already enabled" % plugin.rdn) + print("Plugin '%s' already enabled", plugin.rdn) else: plugin.enable() - print("Enabled plugin '%s'" % plugin.rdn) + print("Enabled plugin '%s'", plugin.rdn) def generic_disable(inst, basedn, log, args): plugin = args.plugin_cls(inst) if not plugin.status(): - print("Plugin '%s' already disabled " % plugin.rdn) + print("Plugin '%s' already disabled", plugin.rdn) else: plugin.disable() - print("Disabled plugin '%s'" % plugin.rdn) + print("Disabled plugin '%s'", plugin.rdn) def generic_status(inst, basedn, log, args): plugin = args.plugin_cls(inst) if plugin.status() is True: - print("Plugin '%s' is enabled" % plugin.rdn) + print("Plugin '%s' is enabled", plugin.rdn) else: - print("Plugin '%s' is disabled" % plugin.rdn) + print("Plugin '%s' is disabled", plugin.rdn) def add_generic_plugin_parsers(subparser, plugin_cls): diff --git a/src/lib389/lib389/cli_conf/backend.py b/src/lib389/lib389/cli_conf/backend.py index c970e84..fdb5dd5 100644 --- a/src/lib389/lib389/cli_conf/backend.py +++ b/src/lib389/lib389/cli_conf/backend.py @@ -170,7 +170,7 @@ def backend_create(inst, basedn, log, args): be = Backend(inst) be.create(properties=props) - print("The database was sucessfully created") + print("The database was successfully created") def _recursively_del_backends(be): @@ -197,7 +197,7 @@ def backend_delete(inst, basedn, log, args, warn=True): _recursively_del_backends(be) be.delete() - print("The database, and any sub-suffixes, were sucessfully deleted") + print("The database, and any sub-suffixes, were successfully deleted") def backend_import(inst, basedn, log, args): @@ -307,7 +307,7 @@ def backend_set(inst, basedn, log, args): be.enable() if args.disable: be.disable() - print("The backend configuration was sucessfully updated") + print("The backend configuration was successfully updated") def db_config_get(inst, basedn, log, args): diff --git a/src/lib389/lib389/cli_conf/plugin.py b/src/lib389/lib389/cli_conf/plugin.py index ea43a8d..5cc7c8c 100644 --- a/src/lib389/lib389/cli_conf/plugin.py +++ b/src/lib389/lib389/cli_conf/plugin.py @@ -13,6 +13,7 @@ from lib389.cli_base import ( _generic_get, _get_arg, ) +from lib389.cli_conf import generic_object_edit from lib389.cli_conf.plugins import memberof as cli_memberof from lib389.cli_conf.plugins import usn as cli_usn from lib389.cli_conf.plugins import rootdn_ac as cli_rootdn_ac @@ -31,6 +32,19 @@ SINGULAR = Plugin MANY = Plugins RDN = 'cn' +arg_to_attr = { + 'initfunc': 'nsslapd-pluginInitfunc', + 'enabled': 'nsslapd-pluginEnabled', + 'path': 'nsslapd-pluginPath', + 'type': 'nsslapd-pluginType', + 'id': 'nsslapd-pluginId', + 'version': 'nsslapd-pluginVersion', + 'vendor': 'nsslapd-pluginVendor', + 'description': 'nsslapd-pluginDescription', + 'depends_on_type': 'nsslapd-plugin-depends-on-type', + 'depends_on_named': 'nsslapd-plugin-depends-on-named' +} + def plugin_list(inst, basedn, log, args): plugin_log = log.getChild('plugin_list') @@ -77,25 +91,7 @@ def plugin_edit(inst, basedn, log, args): rdn = _get_arg(args.selector, msg="Enter %s to retrieve" % RDN) plugins = Plugins(inst) plugin = plugins.get(rdn) - - if args.enabled is not None and args.enabled.lower() not in ["on", "off"]: - raise ValueError("Plugin enabled argument should be 'on' or 'off'") - - plugin_args = {'nsslapd-pluginInitfunc': args.initfunc, - 'nsslapd-pluginEnabled': args.enabled, - 'nsslapd-pluginPath': args.path, - 'nsslapd-pluginType': args.type, - 'nsslapd-pluginId': args.id, - 'nsslapd-pluginVersion': args.version, - 'nsslapd-pluginVendor': args.vendor, - 'nsslapd-pluginDescription': args.description} - mods = vaidate_args(plugin, plugin_args) - - if len(mods) > 0: - plugin.replace_many(*mods) - log.info("Successfully changed the plugin %s", rdn) - else: - raise ValueError("Nothing to change") + generic_object_edit(plugin, log, args, arg_to_attr) def create_parser(subparsers): @@ -128,11 +124,17 @@ def create_parser(subparsers): edit_parser.set_defaults(func=plugin_edit) edit_parser.add_argument('selector', nargs='?', help='The plugin to edit') edit_parser.add_argument('--type', help='The type of plugin.') - edit_parser.add_argument('--enabled', - help='Identifies whether or not the plugin is enabled. It should have "on" or "off" values.') + edit_parser.add_argument('--enabled', choices=['on', 'off'], + help='Identifies whether or not the plugin is enabled.') edit_parser.add_argument('--path', help='The plugin library name (without the library suffix).') edit_parser.add_argument('--initfunc', help='An initialization function of the plugin.') edit_parser.add_argument('--id', help='The plugin ID.') edit_parser.add_argument('--vendor', help='The vendor of plugin.') edit_parser.add_argument('--version', help='The version of plugin.') edit_parser.add_argument('--description', help='The description of the plugin.') + edit_parser.add_argument('--depends-on-type', + help='All plug-ins with a type value which matches one of the values ' + 'in the following valid range will be started by the server prior to this plug-in.') + edit_parser.add_argument('--depends-on-named', + help='The plug-in name matching one of the following values will be ' + 'started by the server prior to this plug-in') diff --git a/src/lib389/lib389/cli_conf/plugins/memberof.py b/src/lib389/lib389/cli_conf/plugins/memberof.py index b9865a8..0ccbed3 100644 --- a/src/lib389/lib389/cli_conf/plugins/memberof.py +++ b/src/lib389/lib389/cli_conf/plugins/memberof.py @@ -7,199 +7,92 @@ # --- END COPYRIGHT BLOCK --- import ldap - -from lib389.plugins import MemberOfPlugin -from lib389.cli_conf import add_generic_plugin_parsers - - -def manage_attr(inst, basedn, log, args): - if args.value is not None: - set_attr(inst, basedn, log, args) - else: - display_attr(inst, basedn, log, args) - -def display_attr(inst, basedn, log, args): - plugin = MemberOfPlugin(inst) - log.info(plugin.get_attr_formatted()) - -def set_attr(inst, basedn, log, args): - plugin = MemberOfPlugin(inst) - try: - plugin.set_attr(args.value) - except ldap.UNWILLING_TO_PERFORM: - log.error('Error: Illegal value "{}". Failed to set.'.format(args.value)) - else: - log.info('memberOfAttr set to "{}"'.format(args.value)) - -def display_groupattr(inst, basedn, log, args): - plugin = MemberOfPlugin(inst) - log.info(plugin.get_groupattr_formatted()) - -def add_groupattr(inst, basedn, log, args): - plugin = MemberOfPlugin(inst) - try: - plugin.add_groupattr(args.value) - except ldap.UNWILLING_TO_PERFORM: - log.error('Error: Illegal value "{}". Failed to add.'.format(args.value)) - except ldap.TYPE_OR_VALUE_EXISTS: - log.info('Value "{}" already exists.'.format(args.value)) - else: - log.info('successfully added memberOfGroupAttr value "{}"'.format(args.value)) - -def remove_groupattr(inst, basedn, log, args): - plugin = MemberOfPlugin(inst) - try: - plugin.remove_groupattr(args.value) - except ldap.UNWILLING_TO_PERFORM: - log.error("Error: Failed to delete. memberOfGroupAttr is required.") - except ldap.NO_SUCH_ATTRIBUTE: - log.error('Error: Failed to delete. No value "{0}" found.'.format(args.value)) - else: - log.info('successfully removed memberOfGroupAttr value "{}"'.format(args.value)) - -def display_allbackends(inst, basedn, log, args): - plugin = MemberOfPlugin(inst) - val = plugin.get_allbackends_formatted() - if not val: - log.info("memberOfAllBackends is not set") +from lib389.plugins import MemberOfPlugin, Plugins, MemberOfSharedConfig +from lib389.cli_conf import add_generic_plugin_parsers, generic_object_edit, generic_object_add + +arg_to_attr = { + 'initfunc': 'nsslapd-pluginInitfunc', + 'attr': 'memberOfAttr', + 'groupattr': 'memberOfGroupAttr', + 'allbackends': 'memberOfAllBackends', + 'skipnested': 'memberOfSkipNested', + 'scope': 'memberOfEntryScope', + 'exclude': 'memberOfEntryScopeExcludeSubtree', + 'autoaddoc': 'memberOfAutoAddOC', + 'config_entry': 'nsslapd-pluginConfigArea' +} + + +def memberof_edit(inst, basedn, log, args): + log = log.getChild('memberof_edit') + plugins = Plugins(inst) + plugin = plugins.get("MemberOf Plugin") + generic_object_edit(plugin, log, args, arg_to_attr) + + +def memberof_add_config(inst, basedn, log, args): + log = log.getChild('memberof_add_config') + targetdn = args.DN + config = MemberOfSharedConfig(inst, targetdn) + generic_object_add(config, log, args, arg_to_attr) + plugins = Plugins(inst) + plugin = plugins.get("MemberOf Plugin") + plugin.replace('nsslapd-pluginConfigArea', config.dn) + log.info('MemberOf attribute nsslapd-pluginConfigArea (config-entry) ' + 'was set in the main plugin config') + + +def memberof_edit_config(inst, basedn, log, args): + log = log.getChild('memberof_edit_config') + targetdn = args.DN + config = MemberOfSharedConfig(inst, targetdn) + generic_object_edit(config, log, args, arg_to_attr) + + +def memberof_show_config(inst, basedn, log, args): + log = log.getChild('memberof_show_config') + targetdn = args.DN + config = MemberOfSharedConfig(inst, targetdn) + + if not config.exists(): + raise ldap.NO_SUCH_OBJECT("Entry %s doesn't exists" % targetdn) + if args and args.json: + o_str = config.get_all_attrs_json() + print(o_str) else: - log.info(val) + print(config.display()) -def enable_allbackends(inst, basedn, log, args): - plugin = MemberOfPlugin(inst) - plugin.enable_allbackends() - log.info("memberOfAllBackends enabled successfully") - -def disable_allbackends(inst, basedn, log, args): - plugin = MemberOfPlugin(inst) - plugin.disable_allbackends() - log.info("memberOfAllBackends disabled successfully") -def display_skipnested(inst, basedn, log, args): - plugin = MemberOfPlugin(inst) - val = plugin.get_skipnested_formatted() - if not val: - log.info("memberOfSkipNested is not set") - else: - log.info(val) - -def enable_skipnested(inst, basedn, log, args): - plugin = MemberOfPlugin(inst) - plugin.enable_skipnested() - log.info("memberOfSkipNested set successfully") +def memberof_del_config(inst, basedn, log, args): + log = log.getChild('memberof_del_config') + targetdn = args.DN + config = MemberOfSharedConfig(inst, targetdn) + config.delete() + log.info("Successfully deleted the %s", targetdn) -def disable_skipnested(inst, basedn, log, args): - plugin = MemberOfPlugin(inst) - plugin.disable_skipnested() - log.info("memberOfSkipNested unset successfully") - -def manage_autoaddoc(inst, basedn, log, args): - if args.value == "del": - remove_autoaddoc(inst, basedn, log, args) - elif args.value is not None: - set_autoaddoc(inst, basedn, log, args) - else: - display_autoaddoc(inst, basedn, log, args) - -def display_autoaddoc(inst, basedn, log, args): - plugin = MemberOfPlugin(inst) - val = plugin.get_autoaddoc_formatted() - if not val: - log.info("memberOfAutoAddOc is not set") - else: - log.info(val) - -def set_autoaddoc(inst, basedn, log, args): - plugin = MemberOfPlugin(inst) - d = {'nsmemberof': 'nsMemberOf', 'inetuser': 'inetUser', 'inetadmin': 'inetAdmin'} - plugin.set_autoaddoc(d[args.value]) - log.info('memberOfAutoAddOc set to "{}"'.format(d[args.value])) - -def remove_autoaddoc(inst, basedn, log, args): - plugin = MemberOfPlugin(inst) - if not plugin.get_autoaddoc(): - log.info("memberOfAutoAddOc was not set") - else: - plugin.remove_autoaddoc() - log.info("memberOfAutoAddOc attribute deleted") - -def display_scope(inst, basedn, log, args): - plugin = MemberOfPlugin(inst) - val = plugin.get_entryscope_formatted() - if not val: - log.info("memberOfEntryScope is not set") - else: - log.info(val) - -def add_scope(inst, basedn, log, args): - plugin = MemberOfPlugin(inst) - try: - plugin.add_entryscope(args.value) - except ldap.UNWILLING_TO_PERFORM as ex: - if "is also listed as an exclude suffix" in ex.args[0]['info']: - log.error('Error: Include suffix ({0}) is also listed as an exclude suffix.'.format(args.value)) - else: - log.error('Error: Invalid DN "{}". Failed to add.'.format(args.value)) - except ldap.TYPE_OR_VALUE_EXISTS: - log.info('Value "{}" already exists.'.format(args.value)) - else: - log.info('successfully added memberOfEntryScope value "{}"'.format(args.value)) - -def remove_scope(inst, basedn, log, args): - plugin = MemberOfPlugin(inst) - try: - plugin.remove_entryscope(args.value) - except ldap.NO_SUCH_ATTRIBUTE: - log.error('Error: Failed to delete. No value "{0}" found.'.format(args.value)) - else: - log.info('successfully removed memberOfEntryScope value "{}"'.format(args.value)) - -def remove_all_scope(inst, basedn, log, args): - plugin = MemberOfPlugin(inst) - plugin.remove_all_entryscope() - log.info('successfully removed all memberOfEntryScope values') - -def display_excludescope(inst, basedn, log, args): - plugin = MemberOfPlugin(inst) - val = plugin.get_excludescope_formatted() - if not val: - log.info("memberOfEntryScopeExcludeSubtree is not set") - else: - log.info(val) - -def add_excludescope(inst, basedn, log, args): - plugin = MemberOfPlugin(inst) - try: - plugin.add_excludescope(args.value) - except ldap.UNWILLING_TO_PERFORM as ex: - if "is also listed as an exclude suffix" in ex.args[0]['info']: - log.error('Error: Suffix ({0}) is listed in entry scope.'.format(args.value)) - else: - log.error('Error: Invalid DN "{}". Failed to add.'.format(args.value)) - except ldap.TYPE_OR_VALUE_EXISTS: - log.info('Value "{}" already exists.'.format(args.value)) - else: - log.info('successfully added memberOfEntryScopeExcludeSubtree value "{}"'.format(args.value)) - -def remove_excludescope(inst, basedn, log, args): - plugin = MemberOfPlugin(inst) - try: - plugin.remove_excludescope(args.value) - except ldap.NO_SUCH_ATTRIBUTE: - log.error('Error: Failed to delete. No value "{0}" found.'.format(args.value)) - else: - log.info('successfully removed memberOfEntryScopeExcludeSubtree value "{}"'.format(args.value)) - -def remove_all_excludescope(inst, basedn, log, args): - plugin = MemberOfPlugin(inst) - plugin.remove_all_excludescope() - log.info('successfully removed all memberOfEntryScopeExcludeSubtree values') def fixup(inst, basedn, log, args): plugin = MemberOfPlugin(inst) log.info('Attempting to add task entry... This will fail if MemberOf plug-in is not enabled.') - fixup_task = plugin.fixup(args.basedn, args.filter) - log.info('Successfully added task entry ' + fixup_task.dn) + assert plugin.status(), "'%s' is disabled. Fix up task can't be executed" % plugin.rdn + fixup_task = plugin.fixup(args.DN, args.filter) + fixup_task.wait() + exitcode = fixup_task.get_exit_code() + assert exitcode == 0, 'MemberOf fixup task for %s has failed. Please, check logs' + log.info('Successfully added task entry for %s', args.DN) + + +def _add_parser_args(parser): + parser.add_argument('--attr', nargs='+', help='The value to set as memberOfAttr') + parser.add_argument('--groupattr', nargs='+', help='The value to set as memberOfGroupAttr') + parser.add_argument('--allbackends', choices=['on', 'off'], type=str.lower, + help='The value to set as memberOfAllBackends') + parser.add_argument('--skipnested', choices=['on', 'off'], type=str.lower, + help='The value to set as memberOfSkipNested') + parser.add_argument('--scope', help='The value to set as memberOfEntryScope') + parser.add_argument('--exclude', help='The value to set as memberOfEntryScopeExcludeSubtree') + parser.add_argument('--autoaddoc', type=str.lower, help='The value to set as memberOfAutoAddOC') + def create_parser(subparsers): memberof_parser = subparsers.add_parser('memberof', help='Manage and configure MemberOf plugin') @@ -208,69 +101,32 @@ def create_parser(subparsers): add_generic_plugin_parsers(subcommands, MemberOfPlugin) - attr_parser = subcommands.add_parser('attr', help='get or set memberofattr') - attr_parser.set_defaults(func=manage_attr) - attr_parser.add_argument('value', nargs='?', help='The value to set as memberofattr') - - groupattr_parser = subcommands.add_parser('groupattr', help='get or manage memberofgroupattr') - groupattr_parser.set_defaults(func=display_groupattr) - groupattr_subcommands = groupattr_parser.add_subparsers(help='action') - add_groupattr_parser = groupattr_subcommands.add_parser('add', help='add memberofgroupattr value') - add_groupattr_parser.set_defaults(func=add_groupattr) - add_groupattr_parser.add_argument('value', help='The value to add in memberofgroupattr') - del_groupattr_parser = groupattr_subcommands.add_parser('del', help='remove memberofgroupattr value') - del_groupattr_parser.set_defaults(func=remove_groupattr) - del_groupattr_parser.add_argument('value', help='The value to remove from memberofgroupattr') - - allbackends_parser = subcommands.add_parser('allbackends', help='get or manage memberofallbackends') - allbackends_parser.set_defaults(func=display_allbackends) - allbackends_subcommands = allbackends_parser.add_subparsers(help='action') - on_allbackends_parser = allbackends_subcommands.add_parser('on', help='enable all backends for memberof') - on_allbackends_parser.set_defaults(func=enable_allbackends) - off_allbackends_parser = allbackends_subcommands.add_parser('off', help='disable all backends for memberof') - off_allbackends_parser.set_defaults(func=disable_allbackends) - - skipnested_parser = subcommands.add_parser('skipnested', help='get or manage memberofskipnested') - skipnested_parser.set_defaults(func=display_skipnested) - skipnested_subcommands = skipnested_parser.add_subparsers(help='action') - on_skipnested_parser = skipnested_subcommands.add_parser('on', help='skip nested groups for memberof') - on_skipnested_parser.set_defaults(func=enable_skipnested) - off_skipnested_parser = skipnested_subcommands.add_parser('off', help="don't skip nested groups for memberof") - off_skipnested_parser.set_defaults(func=disable_skipnested) - - autoaddoc_parser = subcommands.add_parser('autoaddoc', help='get or set memberofautoaddoc') - autoaddoc_parser.set_defaults(func=manage_autoaddoc) - autoaddoc_parser.add_argument('value', nargs='?', choices=['nsmemberof', 'inetuser', 'inetadmin', 'del'], - type=str.lower, help='The value to set as memberofautoaddoc or del to remove the attribute') - - scope_parser = subcommands.add_parser('scope', help='get or manage memberofentryscope') - scope_parser.set_defaults(func=display_scope) - scope_subcommands = scope_parser.add_subparsers(help='action') - add_scope_parser = scope_subcommands.add_parser('add', help='add memberofentryscope value') - add_scope_parser.set_defaults(func=add_scope) - add_scope_parser.add_argument('value', help='The value to add in memberofentryscope') - del_scope_parser = scope_subcommands.add_parser('del', help='remove memberofentryscope value') - del_scope_parser.set_defaults(func=remove_scope) - del_scope_parser.add_argument('value', help='The value to remove from memberofentryscope') - delall_scope_parser = scope_subcommands.add_parser('delall', help='remove all memberofentryscope values') - delall_scope_parser.set_defaults(func=remove_all_scope) - - exclude_parser = subcommands.add_parser('exclude', help='get or manage memberofentryscopeexcludesubtree') - exclude_parser.set_defaults(func=display_excludescope) - exclude_subcommands = exclude_parser.add_subparsers(help='action') - add_exclude_parser = exclude_subcommands.add_parser('add', help='add memberofentryscopeexcludesubtree value') - add_exclude_parser.set_defaults(func=add_excludescope) - add_exclude_parser.add_argument('value', help='The value to add in memberofentryscopeexcludesubtree') - del_exclude_parser = exclude_subcommands.add_parser('del', help='remove memberofentryscopeexcludesubtree value') - del_exclude_parser.set_defaults(func=remove_excludescope) - del_exclude_parser.add_argument('value', help='The value to remove from memberofentryscopeexcludesubtree') - delall_exclude_parser = exclude_subcommands.add_parser('delall', help='remove all memberofentryscopeexcludesubtree values') - delall_exclude_parser.set_defaults(func=remove_all_excludescope) - - fixup_parser = subcommands.add_parser('fixup', help='run the fix-up task for memberof plugin') + edit_parser = subcommands.add_parser('edit', help='Edit the plugin') + edit_parser.set_defaults(func=memberof_edit) + _add_parser_args(edit_parser) + edit_parser.add_argument('--config-entry', help='The value to set as nsslapd-pluginConfigArea') + + config_parser = subcommands.add_parser('config-entry', help='Manage the config entry') + config_subcommands = config_parser.add_subparsers(help='action') + add_config_parser = config_subcommands.add_parser('add', help='Add the config entry') + add_config_parser.set_defaults(func=memberof_add_config) + add_config_parser.add_argument('DN', help='The config entry full DN') + _add_parser_args(add_config_parser) + edit_config_parser = config_subcommands.add_parser('edit', help='Edit the config entry') + edit_config_parser.set_defaults(func=memberof_edit_config) + edit_config_parser.add_argument('DN', help='The config entry full DN') + _add_parser_args(edit_config_parser) + show_config_parser = config_subcommands.add_parser('show', help='Display the config entry') + show_config_parser.set_defaults(func=memberof_show_config) + show_config_parser.add_argument('DN', help='The config entry full DN') + del_config_parser = config_subcommands.add_parser('delete', help='Delete the config entry') + del_config_parser.set_defaults(func=memberof_del_config) + del_config_parser.add_argument('DN', help='The config entry full DN') + + fixup_parser = subcommands.add_parser('fixup', help='Run the fix-up task for memberOf plugin') fixup_parser.set_defaults(func=fixup) - fixup_parser.add_argument('-b', '--basedn', required=True, help="base DN that contains entries to fix up") - fixup_parser.add_argument('-f', '--filter', help="Filter for entries to fix up.\n" - "If omitted, all entries with objectclass inetuser/inetadmin/nsmemberof under the\n" - "specified base will have their memberOf attribute regenerated." - ) + fixup_parser.add_argument('DN', help="base DN that contains entries to fix up") + fixup_parser.add_argument('-f', '--filter', + help='Filter for entries to fix up.\n If omitted, all entries with objectclass ' + 'inetuser/inetadmin/nsmemberof under the specified base will have ' + 'their memberOf attribute regenerated.') diff --git a/src/lib389/lib389/cli_ctl/instance.py b/src/lib389/lib389/cli_ctl/instance.py index ae17307..b70ec7c 100644 --- a/src/lib389/lib389/cli_ctl/instance.py +++ b/src/lib389/lib389/cli_ctl/instance.py @@ -68,7 +68,7 @@ def instance_create(inst, log, args): sd = SetupDs(args.verbose, args.dryrun, log, args.containerised) if sd.create_from_inf(args.file): - # print("Sucessfully created instance") + # print("Successfully created instance") return True else: # print("Failed to create instance") @@ -200,6 +200,3 @@ def create_parser(subcommands): remove_parser.set_defaults(func=instance_remove) remove_parser.add_argument('--do-it', dest="ack", help="By default we do a dry run. This actually initiates the removal of the instance.", action='store_true', default=False) - - - diff --git a/src/lib389/lib389/cli_idm/__init__.py b/src/lib389/lib389/cli_idm/__init__.py index cb4fd11..fcf0175 100644 --- a/src/lib389/lib389/cli_idm/__init__.py +++ b/src/lib389/lib389/cli_idm/__init__.py @@ -107,13 +107,13 @@ def _generic_create(inst, basedn, log, manager_class, kwargs, args=None): mc = manager_class(inst, basedn) o = mc.create(properties=kwargs) o_str = o.__unicode__() - log.info('Sucessfully created %s' % o_str) + log.info('Successfully created %s' % o_str) def _generic_delete(inst, basedn, log, object_class, dn, args=None): # Load the oc direct o = object_class(inst, dn) o.delete() - log.info('Sucessfully deleted %s' % dn) + log.info('Successfully deleted %s' % dn) # vim: tabstop=4 expandtab shiftwidth=4 softtabstop=4 diff --git a/src/lib389/lib389/clitools/ds_setup b/src/lib389/lib389/clitools/ds_setup index 212a34a..9e8b0d0 100755 --- a/src/lib389/lib389/clitools/ds_setup +++ b/src/lib389/lib389/clitools/ds_setup @@ -66,7 +66,7 @@ By setting this value you acknowledge and take responsibility for the fact this print('Launching ...') sd = SetupDs(args.verbose, args.dryrun) if sd.create_from_inf(args.file): - print("Sucessfully created instance") + print("Successfully created instance") else: print("Failed to create instance") sys.exit(1) @@ -75,4 +75,3 @@ By setting this value you acknowledge and take responsibility for the fact this if __name__ == '__main__': ds_setup_main() - diff --git a/src/lib389/lib389/replica.py b/src/lib389/lib389/replica.py index cc695c7..a462c98 100644 --- a/src/lib389/lib389/replica.py +++ b/src/lib389/lib389/replica.py @@ -1073,15 +1073,15 @@ class Replica(DSLdapObject): raise ValueError('Failed to update replica: ' + str(e)) elif replicarole == ReplicaRole.CONSUMER and newrole == ReplicaRole.MASTER: try: - self.apply_mods([(REPL_TYPE, str(REPLICA_RDWR_TYPE)), - (REPL_FLAGS, str(REPLICA_FLAGS_WRITE)), - (REPL_ID, str(rid))]) + self.replace_many([(REPL_TYPE, str(REPLICA_RDWR_TYPE)), + (REPL_FLAGS, str(REPLICA_FLAGS_WRITE)), + (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.apply_mods([(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)) @@ -1103,15 +1103,15 @@ class Replica(DSLdapObject): # Demote it - set the replica type, flags and rid if replicarole == ReplicaRole.MASTER and newrole == ReplicaRole.HUB: try: - self.apply_mods([(REPL_TYPE, str(REPLICA_RDONLY_TYPE)), - (REPL_ID, str(CONSUMER_REPLICAID))]) + self.replace_many([(REPL_TYPE, str(REPLICA_RDONLY_TYPE)), + (REPL_ID, str(CONSUMER_REPLICAID))]) except ldap.LDAPError as e: raise ValueError('Failed to update replica: ' + str(e)) elif replicarole == ReplicaRole.MASTER and newrole == ReplicaRole.CONSUMER: try: - self.apply_mods([(REPL_TYPE, str(REPLICA_RDONLY_TYPE)), - (REPL_FLAGS, str(REPLICA_FLAGS_RDONLY)), - (REPL_ID, str(CONSUMER_REPLICAID))]) + self.replace_many([(REPL_TYPE, str(REPLICA_RDONLY_TYPE)), + (REPL_FLAGS, str(REPLICA_FLAGS_RDONLY)), + (REPL_ID, str(CONSUMER_REPLICAID))]) except ldap.LDAPError as e: raise ValueError('Failed to update replica: ' + str(e)) elif replicarole == ReplicaRole.HUB and newrole == ReplicaRole.CONSUMER: diff --git a/src/lib389/lib389/tests/cli/conf_backend_test.py b/src/lib389/lib389/tests/cli/conf_backend_test.py index 5043622..351d2bf 100644 --- a/src/lib389/lib389/tests/cli/conf_backend_test.py +++ b/src/lib389/lib389/tests/cli/conf_backend_test.py @@ -53,7 +53,7 @@ def create_backend(topology_st, request): args.suffix = SUFFIX backend_create(topology_st.standalone, None, None, args) - check_output("The database was sucessfully created") + check_output("The database was successfully created") def fin(): sys.stdout = io.StringIO() @@ -66,7 +66,7 @@ def create_backend(topology_st, request): # Delete backend backend_delete(topology_st.standalone, None, None, args, warn=False) - check_output("sucessfully deleted") + check_output("successfully deleted") # Verify it's removed args.suffix = False @@ -137,7 +137,7 @@ def test_backend_cli(topology_st, create_backend): args.suffix = SUB_SUFFIX args.be_name = SUB_BE_NAME backend_create(topology_st.standalone, None, topology_st.logcap.log, args) - check_output("The database was sucessfully created") + check_output("The database was successfully created") # Verify subsuffix args.suffix = False @@ -155,7 +155,7 @@ def test_backend_cli(topology_st, create_backend): args.enable_readonly = True # Setting nsslapd-readonly to "on" args.disable_readonly = False backend_set(topology_st.standalone, None, topology_st.logcap.log, args) - check_output("sucessfully updated") + check_output("successfully updated") # Verify modified worked args.selector = SUB_BE_NAME @@ -165,7 +165,7 @@ def test_backend_cli(topology_st, create_backend): # Delete subsuffix args.suffix = SUB_SUFFIX backend_delete(topology_st.standalone, None, topology_st.logcap.log, args, warn=False) - check_output("sucessfully deleted") + check_output("successfully deleted") # Verify it is deleted args.suffix = False @@ -175,7 +175,7 @@ def test_backend_cli(topology_st, create_backend): # Modify backend (use same args from subsuffix modify) args.be_name = BE_NAME backend_set(topology_st.standalone, None, topology_st.logcap.log, args) - check_output("sucessfully updated") + check_output("successfully updated") # Verify modified worked args.selector = BE_NAME diff --git a/src/lib389/lib389/tests/cli/conf_chaining_test.py b/src/lib389/lib389/tests/cli/conf_chaining_test.py index be19077..0b44770 100644 --- a/src/lib389/lib389/tests/cli/conf_chaining_test.py +++ b/src/lib389/lib389/tests/cli/conf_chaining_test.py @@ -46,7 +46,7 @@ def create_backend(topology_st, request): args.create_entries = True args.suffix = SUFFIX backend_create(topology_st.standalone, None, None, args) - check_output("The database was sucessfully created") + check_output("The database was successfully created") def fin(): sys.stdout = io.StringIO() @@ -60,7 +60,7 @@ def create_backend(topology_st, request): # Delete backend backend_delete(topology_st.standalone, None, None, args, warn=False) - check_output("sucessfully deleted") + check_output("successfully deleted") # Verify it's removed args.suffix = False diff --git a/src/lib389/lib389/tests/cli/idm_group_test.py b/src/lib389/lib389/tests/cli/idm_group_test.py index 51ad85e..a9bf921 100644 --- a/src/lib389/lib389/tests/cli/idm_group_test.py +++ b/src/lib389/lib389/tests/cli/idm_group_test.py @@ -35,7 +35,7 @@ def test_group_tasks(topology): topology.logcap.flush() g_args.cn = 'testgroup' create(topology.standalone, DEFAULT_SUFFIX, topology.logcap.log, g_args) - assert(topology.logcap.contains("Sucessfully created testgroup")) + assert(topology.logcap.contains("Successfully created testgroup")) # Assert it exists topology.logcap.flush() @@ -54,7 +54,7 @@ def test_group_tasks(topology): u_args.uidNumber = '5000' u_args.gidNumber = '5000' create_user(topology.standalone, DEFAULT_SUFFIX, topology.logcap.log, u_args) - assert(topology.logcap.contains("Sucessfully created testuser")) + assert(topology.logcap.contains("Successfully created testuser")) # Add them to the group as a member topology.logcap.flush() @@ -86,5 +86,4 @@ def test_group_tasks(topology): topology.logcap.flush() g_args.dn = "cn=testgroup,ou=groups,dc=example,dc=com" delete(topology.standalone, DEFAULT_SUFFIX, topology.logcap.log, g_args, warn=False) - assert(topology.logcap.contains("Sucessfully deleted cn=testgroup,ou=groups,dc=example,dc=com")) - + assert(topology.logcap.contains("Successfully deleted cn=testgroup,ou=groups,dc=example,dc=com")) diff --git a/src/lib389/lib389/tests/cli/idm_user_test.py b/src/lib389/lib389/tests/cli/idm_user_test.py index 42abd02..fdbc17d 100644 --- a/src/lib389/lib389/tests/cli/idm_user_test.py +++ b/src/lib389/lib389/tests/cli/idm_user_test.py @@ -52,7 +52,7 @@ def test_user_tasks(topology): u_args.gidNumber = '5000' create(topology.standalone, DEFAULT_SUFFIX, topology.logcap.log, u_args) - assert(topology.logcap.contains("Sucessfully created testuser")) + assert(topology.logcap.contains("Successfully created testuser")) # Assert they exist topology.logcap.flush() get(topology.standalone, DEFAULT_SUFFIX, topology.logcap.log, u_args) @@ -82,9 +82,8 @@ def test_user_tasks(topology): # Enroll a dummy sshkey - # Delete it + # Delete it topology.logcap.flush() u_args.dn = 'uid=testuser,ou=people,dc=example,dc=com' delete(topology.standalone, DEFAULT_SUFFIX, topology.logcap.log, u_args, warn=False) - assert(topology.logcap.contains('Sucessfully deleted uid=testuser,ou=people,dc=example,dc=com')) - + assert(topology.logcap.contains('Successfully deleted uid=testuser,ou=people,dc=example,dc=com'))