Script 'mail_helper' called by obssrc Hello community, here is the log from the commit of package 389-ds for openSUSE:Factory checked in at 2022-02-02 22:40:56 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/389-ds (Old) and /work/SRC/openSUSE:Factory/.389-ds.new.1898 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "389-ds" Wed Feb 2 22:40:56 2022 rev:52 rq:950581 version:2.0.14~git3.c9226ad90 Changes: -------- --- /work/SRC/openSUSE:Factory/389-ds/389-ds.changes 2022-01-25 17:37:24.721779808 +0100 +++ /work/SRC/openSUSE:Factory/.389-ds.new.1898/389-ds.changes 2022-02-02 22:41:59.403297459 +0100 @@ -1,0 +2,41 @@ +Wed Feb 02 02:33:42 UTC 2022 - william.br...@suse.com + +- Update to version 2.0.14~git3.c9226ad90: + * Issue 4299 - UI - fix minor issues with ldap editor (table view) + * Issue 4299 - UI - fix minor issues with ldap editor + * Issue 5103 - UI - Add support for TPR to web console (#5111) +- Add improvements for suppor config to show certificate usage + +------------------------------------------------------------------- +Tue Feb 01 00:32:18 UTC 2022 - william.br...@suse.com + +- Update to version 2.0.14~git0.eccfa2af9: + * Bump version to 2.0.14 + * Issue 5127 - ds_selinux_restorecon.sh: always exit 0 + * Issue 5037 - in OpenQA changelog trimming can crashes (#5070) + * Issue 4992 - BUG - slapd.socket container fix (#4993) + * Issue 5079 - BUG - multiple ways to specific primary (#5087) + * Issue 5080 - BUG - multiple index types not handled in openldap migration (#5094) + * Issue 5135 - UI - Disk monitoring threshold does update properly + +------------------------------------------------------------------- +Tue Jan 25 03:13:29 UTC 2022 - William Brown <william.br...@suse.com> + +- Update support config to latest version + +------------------------------------------------------------------- +Tue Jan 25 03:03:03 UTC 2022 - william.br...@suse.com + +- Update to version 2.0.13~git1.72eb93ac9: + * Issue 5129 - BUG - Incorrect fn signature in add_index (#5130) + * Bump version to 2.0.13 + * Issue 5132 - Update Rust crate lru to fix CVE + * Issue 3555 - UI - fix audit issue with npm nanoid + * Issue 4299 - UI - Add ACI editing features + * Issue 4299 - UI LDAP editor - add "edit" and "rename" functionality + * Issue 5127 - run restorecon on /dev/shm at server startup + * Issue 5124 - dscontainer fails to create an instance + * Issue 4312 - fix compiler warning + * Issue 5115 - AttributeError: type object 'build_manpages' has no attribute 'build_manpages' + +------------------------------------------------------------------- Old: ---- 389-ds-base-2.0.13~git1.72eb93ac9.tar.xz supportutils-plugin-dirsrv-v0.1.0~git0.37cb939.tar.xz New: ---- 389-ds-base-2.0.14~git3.c9226ad90.tar.xz supportutils-plugin-dirsrv-v0.1.0~git2.4c54cf4.tar.xz ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ 389-ds.spec ++++++ --- /var/tmp/diff_new_pack.XuFJha/_old 2022-02-02 22:42:01.139285693 +0100 +++ /var/tmp/diff_new_pack.XuFJha/_new 2022-02-02 22:42:01.143285665 +0100 @@ -33,7 +33,7 @@ %define svrcorelib libsvrcore0 Name: 389-ds -Version: 2.0.13~git1.72eb93ac9 +Version: 2.0.14~git3.c9226ad90 Release: 0 Summary: 389 Directory Server License: GPL-3.0-or-later AND MPL-2.0 @@ -43,7 +43,7 @@ Source1: extra-schema.tgz Source2: LICENSE.openldap Source3: vendor.tar.xz -Source4: supportutils-plugin-dirsrv-v0.1.0~git0.37cb939.tar.xz +Source4: supportutils-plugin-dirsrv-v0.1.0~git2.4c54cf4.tar.xz Source5: 70yast.ldif Source9: %{name}-rpmlintrc Source10: %{user_group}-user.conf ++++++ 389-ds-base-2.0.13~git1.72eb93ac9.tar.xz -> 389-ds-base-2.0.14~git3.c9226ad90.tar.xz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/389-ds-base-2.0.13~git1.72eb93ac9/VERSION.sh new/389-ds-base-2.0.14~git3.c9226ad90/VERSION.sh --- old/389-ds-base-2.0.13~git1.72eb93ac9/VERSION.sh 2022-01-25 01:49:42.000000000 +0100 +++ new/389-ds-base-2.0.14~git3.c9226ad90/VERSION.sh 2022-02-01 23:35:34.000000000 +0100 @@ -10,7 +10,7 @@ # PACKAGE_VERSION is constructed from these VERSION_MAJOR=2 VERSION_MINOR=0 -VERSION_MAINT=13 +VERSION_MAINT=14 # NOTE: VERSION_PREREL is automatically set for builds made out of a git tree VERSION_PREREL= VERSION_DATE=$(date -u +%Y%m%d) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/389-ds-base-2.0.13~git1.72eb93ac9/dirsrvtests/tests/data/openldap_2_389/1/slapd.d/cn=config/olcDatabase={1}mdb.ldif new/389-ds-base-2.0.14~git3.c9226ad90/dirsrvtests/tests/data/openldap_2_389/1/slapd.d/cn=config/olcDatabase={1}mdb.ldif --- old/389-ds-base-2.0.13~git1.72eb93ac9/dirsrvtests/tests/data/openldap_2_389/1/slapd.d/cn=config/olcDatabase={1}mdb.ldif 2022-01-25 01:49:42.000000000 +0100 +++ new/389-ds-base-2.0.14~git3.c9226ad90/dirsrvtests/tests/data/openldap_2_389/1/slapd.d/cn=config/olcDatabase={1}mdb.ldif 2022-02-01 23:35:34.000000000 +0100 @@ -9,6 +9,7 @@ olcRootDN: cn=Manager,dc=example,dc=com olcRootPW:: c2VjcmV0 olcDbIndex: objectClass eq +olcDbIndex: uid eq,pres,sub structuralObjectClass: olcMdbConfig entryUUID: 401a528e-eaf5-1039-8667-dbfbf2f5e6dd creatorsName: cn=config diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/389-ds-base-2.0.13~git1.72eb93ac9/dirsrvtests/tests/suites/openldap_2_389/migrate_test.py new/389-ds-base-2.0.14~git3.c9226ad90/dirsrvtests/tests/suites/openldap_2_389/migrate_test.py --- old/389-ds-base-2.0.13~git1.72eb93ac9/dirsrvtests/tests/suites/openldap_2_389/migrate_test.py 2022-01-25 01:49:42.000000000 +0100 +++ new/389-ds-base-2.0.14~git3.c9226ad90/dirsrvtests/tests/suites/openldap_2_389/migrate_test.py 2022-02-01 23:35:34.000000000 +0100 @@ -39,11 +39,20 @@ # Do we have databases? assert len(config.databases) == 2 + # Check that we unpacked uid eq,pres,sub correctly. + assert len(config.databases[0].index) == 4 + assert ('objectClass', 'eq') in config.databases[0].index + assert ('uid', 'eq') in config.databases[0].index + assert ('uid', 'pres') in config.databases[0].index + assert ('uid', 'sub') in config.databases[0].index # Did our schema parse? assert any(['suseModuleConfiguration' in x.names for x in config.schema.classes]) + + + @pytest.mark.skipif(ds_is_older('1.4.3'), reason="Not implemented") def test_migrate_openldap_slapdd(topology_st): """ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/389-ds-base-2.0.13~git1.72eb93ac9/ldap/servers/plugins/replication/cl5_api.c new/389-ds-base-2.0.14~git3.c9226ad90/ldap/servers/plugins/replication/cl5_api.c --- old/389-ds-base-2.0.13~git1.72eb93ac9/ldap/servers/plugins/replication/cl5_api.c 2022-01-25 01:49:42.000000000 +0100 +++ new/389-ds-base-2.0.14~git3.c9226ad90/ldap/servers/plugins/replication/cl5_api.c 2022-02-01 23:35:34.000000000 +0100 @@ -125,6 +125,9 @@ Slapi_Counter *clThreads; /* track threads operating on the changelog */ pthread_mutex_t clLock; /* controls access to trimming configuration and */ /* lock associated to clVar, used to notify threads on close */ + int32_t trimmingOnGoing; /* it is a flag to indicate that a trimming thread is started + * and to prevent another trimming thread to start + */ pthread_cond_t clCvar; /* Condition Variable used to notify threads on close */ pthread_condattr_t clCAttr; /* the pthread condition attr */ void *clcrypt_handle; /* for cl encryption */ @@ -1274,6 +1277,7 @@ } cldb->clThreads = slapi_counter_new(); cldb->dbState = CL5_STATE_OPEN; + cldb->trimmingOnGoing = 0; if (pthread_mutex_init(&(cldb->stLock), NULL) != 0) { slapi_log_err(SLAPI_LOG_ERR, repl_plugin_name_cl, @@ -2192,13 +2196,33 @@ struct timespec prev_time = {0}; Replica *replica = (Replica *)param; cldb_Handle *cldb = replica_get_cl_info(replica); - int32_t trimInterval = cldb->clConf.trimInterval; + int32_t trimInterval; + + if (cldb == NULL) { + /* This can happened in race condition + * when the cldb_SetReplicaDB is called but the + * dispatching of_cl5TrimMain thread is slow. + * So trimming could be have been stopped (ruv reload) that + * clears the cldb + */ + return 0; + } + trimInterval = cldb->clConf.trimInterval; /* Get the initial current time for checking the trim interval */ clock_gettime(CLOCK_MONOTONIC, &prev_time); /* Lock the CL state, and bump the thread count */ pthread_mutex_lock(&(cldb->stLock)); + + /* First check that no other trimming thread is running */ + if (cldb->trimmingOnGoing) { + pthread_mutex_unlock(&(cldb->stLock)); + return 0; + } + + /* Now trimming thread can start */ + cldb->trimmingOnGoing = 1; slapi_counter_increment(cldb->clThreads); while (cldb->dbState == CL5_STATE_OPEN) @@ -2222,6 +2246,7 @@ pthread_mutex_lock(&(cldb->stLock)); } slapi_counter_decrement(cldb->clThreads); + cldb->trimmingOnGoing = 0; pthread_mutex_unlock(&(cldb->stLock)); diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/389-ds-base-2.0.13~git1.72eb93ac9/ldap/servers/plugins/replication/repl5_agmt.c new/389-ds-base-2.0.14~git3.c9226ad90/ldap/servers/plugins/replication/repl5_agmt.c --- old/389-ds-base-2.0.13~git1.72eb93ac9/ldap/servers/plugins/replication/repl5_agmt.c 2022-01-25 01:49:42.000000000 +0100 +++ new/389-ds-base-2.0.14~git3.c9226ad90/ldap/servers/plugins/replication/repl5_agmt.c 2022-02-01 23:35:34.000000000 +0100 @@ -482,7 +482,9 @@ /* DBDB: review this code */ if (slapi_entry_attr_hasvalue(e, "objectclass", "nsDSWindowsReplicationAgreement")) { - if (replica_get_type(replica) == REPLICA_TYPE_PRIMARY) { + if (replica_get_type(replica) == REPLICA_TYPE_PRIMARY + || (replica_get_type(replica) == REPLICA_TYPE_UPDATABLE && replica_is_flag_set(replica, REPLICA_LOG_CHANGES)) + ) { ra->agreement_type = REPLICA_TYPE_WINDOWS; windows_init_agreement_from_entry(ra, e); } else { diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/389-ds-base-2.0.13~git1.72eb93ac9/ldap/servers/slapd/libglobs.c new/389-ds-base-2.0.14~git3.c9226ad90/ldap/servers/slapd/libglobs.c --- old/389-ds-base-2.0.13~git1.72eb93ac9/ldap/servers/slapd/libglobs.c 2022-01-25 01:49:42.000000000 +0100 +++ new/389-ds-base-2.0.14~git3.c9226ad90/ldap/servers/slapd/libglobs.c 2022-02-01 23:35:34.000000000 +0100 @@ -2178,9 +2178,9 @@ errno = 0; threshold = strtoll(value, &endp, 10); - if (*endp != '\0' || threshold <= 4096 || errno == ERANGE) { + if (*endp != '\0' || threshold < 4096 || errno == ERANGE) { slapi_create_errormsg(errorbuf, SLAPI_DSE_RETURNTEXT_SIZE, - "%s: \"%s\" is invalid, threshold must be greater than 4096 and less then %lld", + "%s: \"%s\" is invalid, threshold must be greater than or equal to 4096 and less then %lld", attrname, value, (long long int)LONG_MAX); retVal = LDAP_OPERATIONS_ERROR; return retVal; diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/389-ds-base-2.0.13~git1.72eb93ac9/src/cockpit/389-console/src/LDAPEditor.jsx new/389-ds-base-2.0.14~git3.c9226ad90/src/cockpit/389-console/src/LDAPEditor.jsx --- old/389-ds-base-2.0.13~git1.72eb93ac9/src/cockpit/389-console/src/LDAPEditor.jsx 2022-01-25 01:49:42.000000000 +0100 +++ new/389-ds-base-2.0.14~git3.c9226ad90/src/cockpit/389-console/src/LDAPEditor.jsx 2022-02-01 23:35:34.000000000 +0100 @@ -192,7 +192,8 @@ handleReload(refresh) { const params = { serverId: this.props.serverId, - baseDn: this.state.baseDN + baseDn: this.state.baseDN, + addNotification: this.props.addNotification, }; this.setState({ @@ -209,7 +210,8 @@ const params = { serverId: this.props.serverId, baseDn: suffixDN, - parentId: parentId + parentId: parentId, + addNotification: this.props.addNotification, }; getRootSuffixEntryDetails(params, this.updateTableRootSuffixes); @@ -305,7 +307,8 @@ }); const params = { serverId: this.props.serverId, - baseDn: id + baseDn: id, + addNotification: this.props.addNotification, }; getOneLevelEntries(params, this.processDirectChildren); } @@ -365,18 +368,20 @@ }); const params = { serverId: this.props.serverId, - baseDn: dn + baseDn: dn, + addNotification: this.props.addNotification, }; getOneLevelEntries(params, this.processDirectChildren); } // Process the entries that are direct children. - processDirectChildren = (directChildren) => { + processDirectChildren = (directChildren, params) => { this.setState({ loading: true }); const childrenRows = []; let rowNumber = 0; + directChildren.map(aChild => { const info = JSON.parse(aChild); const numSubCellInfo = parseInt(info.numSubordinates) > 0 @@ -647,7 +652,8 @@ const params = { serverId: this.props.serverId, baseDn: suffixDN, - parentId: parentId + parentId: parentId, + addNotification: this.props.addNotification, }; getRootSuffixEntryDetails(params, this.updateTableRootSuffixes); parentId += 2; // The next DN row will be two rows below. @@ -767,6 +773,7 @@ }); } }, + /* { title: 'Roles ...', isDisabled: true, @@ -780,6 +787,7 @@ title: 'Smart Referrals ...', isDisabled: true }, + */ { isSeparator: true }, @@ -881,22 +889,25 @@ onReload={this.handleReload} refreshing={this.state.refreshing} allObjectclasses={this.state.allObjectclasses} + addNotification={this.props.addNotification} /> </Tab> <Tab eventKey={1} title={<TabTitleText>Table View</TabTitleText>}> - <Breadcrumb className="ds-left-margin ds-margin-top-xlg"> - {navItems.map(({ id, to, label, active }) => ( - <BreadcrumbItem key={id + label} to={to} isActive={active} onClick={() => this.onNavItemClick(id, active) }> - {label} - </BreadcrumbItem> - ))} - </Breadcrumb> - <FontAwesomeIcon - className="ds-left-margin ds-refresh" - icon={faSyncAlt} - title="Refresh" - onClick={this.handleReload} - /> + <div className={this.state.searching ? "ds-disabled" : ""}> + <Breadcrumb className="ds-left-margin ds-margin-top-xlg"> + {navItems.map(({ id, to, label, active }) => ( + <BreadcrumbItem key={id + label} to={to} isActive={active} onClick={() => this.onNavItemClick(id, active) }> + {label} + </BreadcrumbItem> + ))} + </Breadcrumb> + <FontAwesomeIcon + className="ds-left-margin ds-refresh" + icon={faSyncAlt} + title="Refresh" + onClick={this.handleReload} + /> + </div> <div className={this.state.searching ? "ds-margin-top-xlg ds-center" : "ds-hidden"}> <TextContent> <Text component={TextVariants.h3}> @@ -920,6 +931,7 @@ onCollapse={this.handleCollapse} columns={columns} actionResolver={this.actionResolver} + addNotification={this.props.addNotification} /> </div> </Tab> @@ -931,6 +943,7 @@ attributes={this.state.attributes} searchBase={this.state.searchBase} allObjectclasses={this.state.allObjectclasses} + addNotification={this.props.addNotification} /> </Tab> </Tabs> diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/389-ds-base-2.0.13~git1.72eb93ac9/src/cockpit/389-console/src/lib/database/globalPwp.jsx new/389-ds-base-2.0.14~git3.c9226ad90/src/cockpit/389-console/src/lib/database/globalPwp.jsx --- old/389-ds-base-2.0.13~git1.72eb93ac9/src/cockpit/389-console/src/lib/database/globalPwp.jsx 2022-01-25 01:49:42.000000000 +0100 +++ new/389-ds-base-2.0.14~git3.c9226ad90/src/cockpit/389-console/src/lib/database/globalPwp.jsx 2022-02-01 23:35:34.000000000 +0100 @@ -2,9 +2,11 @@ import React from "react"; import { log_cmd } from "../tools.jsx"; import { + Alert, Button, Checkbox, Form, + FormAlert, FormSelect, FormSelectOption, Grid, @@ -19,7 +21,7 @@ TextInput, Text, TextContent, - TextVariants, + TextVariants } from "@patternfly/react-core"; import PropTypes from "prop-types"; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; @@ -80,6 +82,12 @@ "passworddictcheck", ]; +const tpr_attrs = [ + "passwordtprmaxuse", + "passwordtprdelayexpireat", + "passwordtprdelayvalidfrom", +]; + export class GlobalPwPolicy extends React.Component { constructor(props) { super(props); @@ -97,6 +105,7 @@ saveExpDisabled: true, saveLockoutDisabled: true, saveSyntaxDisabled: true, + saveTPRDisabled: true, isSelectOpen: false, }; @@ -115,6 +124,8 @@ this.saveLockout = this.saveLockout.bind(this); this.handleSyntaxChange = this.handleSyntaxChange.bind(this); this.saveSyntax = this.saveSyntax.bind(this); + this.handleTPRChange = this.handleTPRChange.bind(this); + this.saveTPR = this.saveTPR.bind(this); this.loadGlobal = this.loadGlobal.bind(this); // Select Typeahead this.onSelectToggle = this.onSelectToggle.bind(this); @@ -512,6 +523,76 @@ }); } + handleTPRChange(e) { + const value = e.target.value; + const attr = e.target.id; + let disableSaveBtn = true; + + // Check if a setting was changed, if so enable the save button + for (const tpr_attr of tpr_attrs) { + if (attr == tpr_attr && this.state['_' + tpr_attr] != value) { + disableSaveBtn = false; + break; + } + } + + // Now check for differences in values that we did not touch + for (const tpr_attr of tpr_attrs) { + if (attr != tpr_attr && this.state['_' + tpr_attr] != this.state[tpr_attr]) { + disableSaveBtn = false; + break; + } + } + + this.setState({ + [attr]: value, + saveTPRDisabled: disableSaveBtn, + }); + } + + saveTPR() { + this.setState({ + saving: true + }); + + const cmd = [ + 'dsconf', '-j', "ldapi://%2fvar%2frun%2fslapd-" + this.props.serverId + ".socket", + 'config', 'replace' + ]; + + for (const attr of tpr_attrs) { + if (this.state['_' + attr] != this.state[attr]) { + let val = this.state[attr]; + cmd.push(attr + "=" + val); + } + } + + log_cmd("saveTPR", "Saving TPR settings", cmd); + cockpit + .spawn(cmd, { superuser: true, err: "message" }) + .done(content => { + this.loadGlobal(); + this.setState({ + saving: false + }); + this.props.addNotification( + "success", + "Successfully updated password policy configuration" + ); + }) + .fail(err => { + const errMsg = JSON.parse(err); + this.loadGlobal(); + this.setState({ + saving: false + }); + this.props.addNotification( + "error", + `Error updating password policy configuration - ${errMsg.desc}` + ); + }); + } + loadGlobal() { this.setState({ loading: true @@ -618,6 +699,7 @@ saveExpDisabled: true, saveLockoutDisabled: true, saveSyntaxDisabled: true, + saveTPRDisabled: true, // Settings 'nsslapd-pwpolicy-local': pwpLocal, passwordisglobalpolicy: pwIsGlobal, @@ -659,6 +741,9 @@ passwordbadwords: attrs.passwordbadwords[0], passworduserattributes: pwUserAttrs, passwordadmindn: attrs.passwordadmindn[0], + passwordtprmaxuse: attrs.passwordtprmaxuse[0], + passwordtprdelayexpireat: attrs.passwordtprdelayexpireat[0], + passwordtprdelayvalidfrom: attrs.passwordtprdelayvalidfrom[0], // Record original values '_nsslapd-pwpolicy-local': pwpLocal, _passwordisglobalpolicy: pwIsGlobal, @@ -700,6 +785,9 @@ _passwordbadwords: attrs.passwordbadwords[0], _passworduserattributes: pwUserAttrs, _passwordadmindn: attrs.passwordadmindn[0], + _passwordtprmaxuse: attrs.passwordtprmaxuse[0], + _passwordtprdelayexpireat: attrs.passwordtprdelayexpireat[0], + _passwordtprdelayvalidfrom: attrs.passwordtprdelayvalidfrom[0], }), this.props.enableTree() ); }) @@ -1423,6 +1511,97 @@ isLoading={this.state.saving} spinnerAriaValueText={this.state.saving ? "Saving" : undefined} {...extraPrimaryProps} + > + {saveBtnName} + </Button> + </Tab> + <Tab eventKey={4} title={<TabTitleText>Temporary Password Rules</TabTitleText>}> + <Form className="ds-margin-top ds-margin-left" isHorizontal autoComplete="off"> + {this.state.passwordmustchange == false && ( + <FormAlert> + <Alert + variant="info" + title='"User Must Change Password After Reset" must be enabled in General Settings to activate TPR.' + aria-live="polite" + isInline + /> + </FormAlert> + )} + <Grid + title="Number of times the temporary password can be used to authenticate (passwordTPRMaxUse)." + > + <GridItem className="ds-label" span={3}> + Password Max Use + </GridItem> + <GridItem span={9}> + <TextInput + value={this.state.passwordtprmaxuse} + type="number" + id="passwordtprmaxuse" + aria-describedby="horizontal-form-name-helper" + name="passwordtprmaxuse" + isDisabled={!this.state.passwordmustchange} + onChange={(checked, e) => { + this.handleTPRChange(e); + }} + /> + </GridItem> + </Grid> + {pwSyntaxRows} + </Form> + <Form className="ds-margin-top ds-margin-left" isHorizontal autoComplete="off"> + <Grid + title="Number of seconds before the temporary password expires (passwordTPRDelayExpireAt)." + > + <GridItem className="ds-label" span={3}> + Password Expires In + </GridItem> + <GridItem span={9}> + <TextInput + value={this.state.passwordtprdelayexpireat} + type="number" + id="passwordtprdelayexpireat" + aria-describedby="horizontal-form-name-helper" + name="passwordtprdelayexpireat" + isDisabled={!this.state.passwordmustchange} + onChange={(checked, e) => { + this.handleTPRChange(e); + }} + /> + </GridItem> + </Grid> + </Form> + <Form className="ds-margin-top ds-margin-left" isHorizontal autoComplete="off"> + <Grid + title="Number of seconds after which temporary password starts to be valid for authentication (passwordTPRDelayValidFrom)." + > + <GridItem className="ds-label" span={3}> + Password Valid From + </GridItem> + <GridItem span={9}> + <TextInput + value={this.state.passwordtprdelayvalidfrom} + type="number" + id="passwordtprdelayvalidfrom" + aria-describedby="horizontal-form-name-helper" + name="passwordtprdelayvalidfrom" + isDisabled={!this.state.passwordmustchange} + onChange={(checked, e) => { + this.handleTPRChange(e); + }} + /> + </GridItem> + </Grid> + {pwSyntaxRows} + </Form> + <Button + isDisabled={this.state.saveTPRDisabled} + variant="primary" + className="ds-margin-top-xlg ds-margin-left" + onClick={this.saveTPR} + isLoading={this.state.saving} + spinnerAriaValueText={this.state.saving ? "Saving" : undefined} + {...extraPrimaryProps} > {saveBtnName} </Button> diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/389-ds-base-2.0.13~git1.72eb93ac9/src/cockpit/389-console/src/lib/database/localPwp.jsx new/389-ds-base-2.0.14~git3.c9226ad90/src/cockpit/389-console/src/lib/database/localPwp.jsx --- old/389-ds-base-2.0.13~git1.72eb93ac9/src/cockpit/389-console/src/lib/database/localPwp.jsx 2022-01-25 01:49:42.000000000 +0100 +++ new/389-ds-base-2.0.14~git3.c9226ad90/src/cockpit/389-console/src/lib/database/localPwp.jsx 2022-02-01 23:35:34.000000000 +0100 @@ -4,10 +4,12 @@ import { DoubleConfirmModal } from "../notifications.jsx"; import { PwpTable } from "./databaseTables.jsx"; import { + Alert, Button, Checkbox, ExpandableSection, Form, + FormAlert, FormHelperText, FormSelect, FormSelectOption, @@ -81,6 +83,12 @@ "passworddictcheck", ]; +const tpr_attrs = [ + "passwordtprmaxuse", + "passwordtprdelayexpireat", + "passwordtprdelayvalidfrom", +]; + class CreatePolicy extends React.Component { constructor(props) { super(props); @@ -89,6 +97,7 @@ isGeneralExpanded: false, isLockoutExpanded: false, isSyntaxExpanded: false, + isTPRExpanded: false, }; this.onGeneralToggle = (isGeneralExpanded) => { @@ -111,6 +120,11 @@ isSyntaxExpanded }); }; + this.onTPRToggle = (isTPRExpanded) => { + this.setState({ + isTPRExpanded + }); + }; } render() { @@ -768,6 +782,85 @@ </div> </div> </ExpandableSection> + <ExpandableSection + className="ds-margin-top-lg" + toggleText={this.state.isTPRExpanded ? 'Hide Temporary Password Settings' : 'Show Temporary Password Settings'} + onToggle={this.onTPRToggle} + isExpanded={this.state.isTPRExpanded} + > + <div className="ds-margin-left"> + {this.props.create_passwordmustchange == false && ( + <FormAlert> + <Alert + variant="info" + title='"User Must Change Password After Reset" must be enabled in General Settings to activate TPR.' + aria-live="polite" + isInline + /> + </FormAlert> + )} + <Grid + title="Number of times the temporary password can be used to authenticate (passwordTPRMaxUse)." + className="ds-margin-top" + > + <GridItem className="ds-label" span={3}> + Password Max Use + </GridItem> + <GridItem span={9}> + <TextInput + type="number" + id="create_passwordtprmaxuse" + aria-describedby="horizontal-form-name-helper" + name="create_passwordtprmaxuse" + isDisabled={!this.props.create_passwordmustchange} + onChange={(checked, e) => { + this.props.handleChange(e); + }} + /> + </GridItem> + </Grid> + <Grid + title="Number of seconds before the temporary password expires (passwordTPRDelayExpireAt)." + className="ds-margin-top" + > + <GridItem className="ds-label" span={3}> + Password Expires In + </GridItem> + <GridItem span={9}> + <TextInput + type="number" + id="create_passwordtprdelayexpireat" + aria-describedby="horizontal-form-name-helper" + name="create_passwordtprdelayexpireat" + isDisabled={!this.props.create_passwordmustchange} + onChange={(checked, e) => { + this.props.handleChange(e); + }} + /> + </GridItem> + </Grid> + <Grid + title="Number of seconds after which temporary password starts to be valid for authentication (passwordTPRDelayValidFrom)." + className="ds-margin-top" + > + <GridItem className="ds-label" span={3}> + Password Valid From + </GridItem> + <GridItem span={9}> + <TextInput + type="number" + id="create_passwordtprdelayvalidfrom" + aria-describedby="horizontal-form-name-helper" + name="create_passwordtprdelayvalidfrom" + isDisabled={!this.props.create_passwordmustchange} + onChange={(checked, e) => { + this.props.handleChange(e); + }} + /> + </GridItem> + </Grid> + </div> + </ExpandableSection> </Form> <Button isDisabled={this.props.createDisabled} @@ -810,6 +903,7 @@ saveExpDisabled: true, saveLockoutDisabled: true, saveSyntaxDisabled: true, + saveTPRDisabled: true, showDeletePolicy: false, // Edit policy passwordchange: false, @@ -847,6 +941,9 @@ passwordmintokenlength: "0", passwordbadwords: "", passworduserattributes: [], + passwordtprmaxuse: "-1", + passwordtprdelayexpireat: "-1", + passwordtprdelayvalidfrom: "-1", _passwordchange: false, _passwordmustchange: false, _passwordhistory: false, @@ -882,6 +979,9 @@ _passwordmintokenlength: "0", _passwordbadwords: "", _passworduserattributes: [], + _passwordtprmaxuse: "-1", + _passwordtprdelayexpireat: "-1", + _passwordtprdelayvalidfrom: "-1", // Create policy create_passwordchange: false, create_passwordmustchange: false, @@ -918,6 +1018,9 @@ create_passwordmintokenlength: "0", create_passwordbadwords: "", create_passworduserattributes: [], + create_passwordtprmaxuse: "-1", + create_passwordtprdelayexpireat: "-1", + create_passwordtprdelayvalidfrom: "-1", _create_passwordchange: false, _create_passwordmustchange: false, _create_passwordhistory: false, @@ -953,6 +1056,9 @@ _create_passwordmintokenlength: "0", _create_passwordbadwords: "", _create_passworduserattributes: [], + _create_passwordtprmaxuse: "-1", + _create_passwordtprdelayexpireat: "-1", + _create_passwordtprdelayvalidfrom: "-1", // Select typeahead isUserAttrsCreateOpen: false, isUserAttrsEditOpen: false, @@ -994,6 +1100,9 @@ passworduserattributes: "--pwduserattrs", passworddictcheck: "--pwddictcheck", passwordadmindn: "--pwdadmin", + passwordtprmaxuse: "--pwptprmaxuse", + passwordtprdelayexpireat: "--pwptprdelayexpireat", + passwordtprdelayvalidfrom: "--pwptprdelayvalidfrom", }, }; @@ -1046,6 +1155,7 @@ this.handleLockoutChange = this.handleLockoutChange.bind(this); this.handleModalChange = this.handleModalChange.bind(this); this.handleSyntaxChange = this.handleSyntaxChange.bind(this); + this.handleTPRChange = this.handleTPRChange.bind(this); this.loadLocal = this.loadLocal.bind(this); this.loadPolicies = this.loadPolicies.bind(this); this.resetTab = this.resetTab.bind(this); @@ -1053,6 +1163,7 @@ this.saveGeneral = this.saveGeneral.bind(this); this.saveLockout = this.saveLockout.bind(this); this.saveSyntax = this.saveSyntax.bind(this); + this.saveTPR = this.saveTPR.bind(this); this.showDeletePolicy = this.showDeletePolicy.bind(this); } @@ -1105,7 +1216,7 @@ let value; let disableSaveBtn = true; let invalid_dn = false; - const all_attrs = general_attrs.concat(exp_attrs, lockout_attrs, syntax_attrs); + const all_attrs = general_attrs.concat(exp_attrs, lockout_attrs, syntax_attrs, tpr_attrs); if (selection) { attr = "create_passworduserattributes"; @@ -1196,7 +1307,7 @@ } createPolicy() { - const all_attrs = general_attrs.concat(exp_attrs, lockout_attrs, syntax_attrs); + const all_attrs = general_attrs.concat(exp_attrs, lockout_attrs, syntax_attrs, tpr_attrs); let action = "adduser"; this.setState({ @@ -1231,7 +1342,7 @@ cmd.push(this.state.attrMap[attr] + "=" + new_val); } } - + log_cmd("createPolicy", "Create a local password policy", cmd); cockpit .spawn(cmd, { superuser: true, err: "message" }) @@ -1609,6 +1720,76 @@ }); } + handleTPRChange(e) { + const value = e.target.value; + const attr = e.target.id; + let disableSaveBtn = true; + + // Check if a setting was changed, if so enable the save button + for (const tpr_attr of tpr_attrs) { + if (attr == tpr_attr && this.state['_' + tpr_attr] != value) { + disableSaveBtn = false; + break; + } + } + + // Now check for differences in values that we did not touch + for (const tpr_attr of tpr_attrs) { + if (attr != tpr_attr && this.state['_' + tpr_attr] != this.state[tpr_attr]) { + disableSaveBtn = false; + break; + } + } + + this.setState({ + [attr]: value, + saveTPRDisabled: disableSaveBtn, + }); + } + + saveTPR() { + this.setState({ + saving: true + }); + + const cmd = [ + 'dsconf', '-j', "ldapi://%2fvar%2frun%2fslapd-" + this.props.serverId + ".socket", + 'localpwp', 'set', this.state.policyName + ]; + + for (const attr of tpr_attrs) { + if (this.state['_' + attr] != this.state[attr]) { + let val = this.state[attr]; + cmd.push(this.state.attrMap[attr] + "=" + val); + } + } + + log_cmd("saveTPR", "Saving TPR pwpolicy settings", cmd); + cockpit + .spawn(cmd, { superuser: true, err: "message" }) + .done(content => { + this.loadLocal(this.state.policyName); + this.setState({ + saving: false + }); + this.props.addNotification( + "success", + "Successfully updated password policy configuration" + ); + }) + .fail(err => { + const errMsg = JSON.parse(err); + this.loadLocal(this.state.policyName); + this.setState({ + saving: false + }); + this.props.addNotification( + "error", + `Error updating password policy configuration - ${errMsg.desc}` + ); + }); + } + deletePolicy() { this.setState({ loading: true, @@ -1873,6 +2054,9 @@ let pwMaxClassChars = "0"; let pwMinCat = "0"; let pwMinTokenLen = "0"; + let pwTPRMaxUse = "-1"; + let pwTPRDelayExpireAt = "-1"; + let pwTPRDelayValidFrom = "-1"; if ('passwordmintokenlength' in attrs) { pwMinTokenLen = attrs.passwordmintokenlength[0]; @@ -1994,6 +2178,15 @@ pwUserAttrs = attrs.passworduserattributes[0].split(' '); } } + if ('passwordTPRMaxUse' in attrs) { + pwTPRMaxUse = attrs.passwordTPRMaxUse[0]; + } + if ('passwordTPRDelayExpireAt' in attrs) { + pwTPRDelayExpireAt = attrs.passwordTPRDelayExpireAt[0]; + } + if ('passwordTPRDelayValidFrom' in attrs) { + pwTPRDelayValidFrom = attrs.passwordTPRDelayValidFrom[0]; + } this.setState(() => ( { @@ -2044,6 +2237,9 @@ passwordmintokenlength: pwMinTokenLen, passwordbadwords: pwBadWords, passworduserattributes: pwUserAttrs, + passwordtprmaxuse: pwTPRMaxUse, + passwordtprdelayexpireat: pwTPRDelayExpireAt, + passwordtprdelayvalidfrom: pwTPRDelayValidFrom, // Record original values _passwordchange: pwChange, _passwordmustchange: pwMustChange, @@ -2080,6 +2276,9 @@ _passwordmintokenlength: pwMinTokenLen, _passwordbadwords: pwBadWords, _passworduserattributes: pwUserAttrs, + _passwordtprmaxuse: pwTPRMaxUse, + _passwordtprdelayexpireat: pwTPRDelayExpireAt, + _passwordtprdelayvalidfrom: pwTPRDelayValidFrom, }) ); }) @@ -2752,6 +2951,97 @@ isLoading={this.state.saving} spinnerAriaValueText={this.state.saving ? "Saving" : undefined} {...extraPrimaryProps} + > + {saveBtnName} + </Button> + </Tab> + <Tab eventKey={4} title={<TabTitleText>Temporary Password Rules</TabTitleText>}> + <Form className="ds-margin-top ds-margin-left" isHorizontal autoComplete="off"> + {this.state.passwordmustchange == false && ( + <FormAlert> + <Alert + variant="info" + title='"User Must Change Password After Reset" must be enabled in General Settings to activate TPR.' + aria-live="polite" + isInline + /> + </FormAlert> + )} + <Grid + title="Number of times the temporary password can be used to authenticate (passwordTPRMaxUse)." + > + <GridItem className="ds-label" span={3}> + Password Max Use + </GridItem> + <GridItem span={9}> + <TextInput + value={this.state.passwordtprmaxuse} + type="number" + id="passwordtprmaxuse" + aria-describedby="horizontal-form-name-helper" + name="passwordtprmaxuse" + isDisabled={!this.state.passwordmustchange} + onChange={(checked, e) => { + this.handleTPRChange(e); + }} + /> + </GridItem> + </Grid> + {pwSyntaxRows} + </Form> + <Form className="ds-margin-top ds-margin-left" isHorizontal autoComplete="off"> + <Grid + title="Number of seconds before the temporary password expires (passwordTPRDelayExpireAt)." + > + <GridItem className="ds-label" span={3}> + Password Expires In + </GridItem> + <GridItem span={9}> + <TextInput + value={this.state.passwordtprdelayexpireat} + type="number" + id="passwordtprdelayexpireat" + aria-describedby="horizontal-form-name-helper" + name="passwordtprdelayexpireat" + isDisabled={!this.state.passwordmustchange} + onChange={(checked, e) => { + this.handleTPRChange(e); + }} + /> + </GridItem> + </Grid> + </Form> + <Form className="ds-margin-top ds-margin-left" isHorizontal autoComplete="off"> + <Grid + title="Number of seconds after which temporary password starts to be valid for authentication (passwordTPRDelayValidFrom)." + > + <GridItem className="ds-label" span={3}> + Password Valid From + </GridItem> + <GridItem span={9}> + <TextInput + value={this.state.passwordtprdelayvalidfrom} + type="number" + id="passwordtprdelayvalidfrom" + aria-describedby="horizontal-form-name-helper" + name="passwordtprdelayvalidfrom" + isDisabled={!this.state.passwordmustchange} + onChange={(checked, e) => { + this.handleTPRChange(e); + }} + /> + </GridItem> + </Grid> + {pwSyntaxRows} + </Form> + <Button + isDisabled={this.state.saveTPRDisabled} + variant="primary" + className="ds-margin-top-xlg ds-margin-left" + onClick={this.saveTPR} + isLoading={this.state.saving} + spinnerAriaValueText={this.state.saving ? "Saving" : undefined} + {...extraPrimaryProps} > {saveBtnName} </Button> diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/389-ds-base-2.0.13~git1.72eb93ac9/src/cockpit/389-console/src/lib/ldap_editor/lib/ldapNavigator.jsx new/389-ds-base-2.0.14~git3.c9226ad90/src/cockpit/389-console/src/lib/ldap_editor/lib/ldapNavigator.jsx --- old/389-ds-base-2.0.13~git1.72eb93ac9/src/cockpit/389-console/src/lib/ldap_editor/lib/ldapNavigator.jsx 2022-01-25 01:49:42.000000000 +0100 +++ new/389-ds-base-2.0.14~git3.c9226ad90/src/cockpit/389-console/src/lib/ldap_editor/lib/ldapNavigator.jsx 2022-02-01 23:35:34.000000000 +0100 @@ -5,6 +5,7 @@ TreeView } from '@patternfly/react-core'; import { + ExclamationTriangleIcon, FolderIcon, FolderOpenIcon, ResourcesEmptyIcon @@ -34,25 +35,7 @@ }; this.treeOnClick = (evt, treeViewItem, parentItem) => { - if (this.state.ldapFailure) { - const result = ldapPing(this.props.editorLdapServer, - (res, obj) => { - if (res) { - this.setState({ ldapFailure: false }); - console.log('Can reconnect to the LDAP server. Continuing...'); - } else { - console.log('Cannot contact the LDAP server! Aborting...'); - console.log(obj); - } - }); - - // Once the server is again reachable, a new click will update the tree item(s). - // Meanwhile give up for this round. - return; - } - if (treeViewItem.isFakeEntry) { - console.log('Clicked on a loading item. Not processing it...'); return; } @@ -103,6 +86,10 @@ const params = { serverId: this.props.editorLdapServer, baseDn: treeViewItem.dn, + name: treeViewItem.name, + fullEntry: treeViewItem.fullEntry, + modTime: treeViewItem.modTime, + addNotification: this.props.addNotification, filter: this.props.skipLeafEntries ? '(|(&(numSubordinates=*)(numSubordinates>=1))(objectClass=organizationalunit)(objectClass=organization))' : null // getOneLevelEntries() will use its default filter '(|(objectClass=*)(objectClass=ldapSubEntry))' @@ -122,6 +109,31 @@ }); }; + this.updateMyParent = (treeNode, nodeIdObject, nodeChildren, removePreviousChildren) => { + const res = nodeIdObject.remainingId.indexOf('.'); + if (res === -1) { + const insertionNode = treeNode.find(elt => elt.id === nodeIdObject.fullId); + insertionNode.children = nodeChildren; + insertionNode.loadChildren = false; + insertionNode.customBadgeContent = "?"; + } else { + const parentId = nodeIdObject.remainingId.substring(0, res); + const startingId = nodeIdObject.startingId === undefined + ? parentId + : `${nodeIdObject.startingId}.${parentId}`; + + const parentNode = treeNode.find(elt => elt.id === startingId); + const remainingId = nodeIdObject.remainingId.substring(res + 1); + + const newNodeIdObject = { + fullId: nodeIdObject.fullId, + remainingId: remainingId, + startingId: startingId + } + this.updateMyParent(parentNode.children, newNodeIdObject, nodeChildren, removePreviousChildren); + } + }; + this.updateMyChildren = (treeNode, nodeIdObject, childArray, removePreviousChildren) => { const res = nodeIdObject.remainingId.indexOf('.'); if (res === -1) { @@ -310,7 +322,11 @@ ? Math.max(...idArray) : 0; const mySubId = maxId + 1; - const params = { serverId: this.props.editorLdapServer, baseDn: nodeDn }; + const params = { + serverId: this.props.editorLdapServer, + baseDn: nodeDn, + addNotification: this.props.addNotification, + }; // TODO: Change the name of this function to a more generic one!! getRootSuffixEntryDetails(params, @@ -348,7 +364,7 @@ } } - updateDirectChildren = (potentialChildren, resCode) => { + updateDirectChildren = (potentialChildren, params, resCode) => { // When leaf entries ( but Organizations and Organization Units ) should be skipped // run a search with the relevant filter to get the actual number of matching grand children // ( children of the direct children of the current entry [ the active node ]). @@ -358,25 +374,30 @@ } if (potentialChildren === null) { - console.log(resCode); - // TODO: Show Modal dialog + if (resCode.exit_status !== 0) { + this.props.addNotification( + "error", + `Error searching database - ${resCode.msg.split("\n").pop()}` + ); + } return; } const updatedChildren = []; let nbIterations = 0; - const params = { + const child_params = { serverId: this.props.editorLdapServer, scope: 'one', attributes: '1.1', + addNotification: this.props.addNotification, filter: '(|(&(numSubordinates=*)(numSubordinates>=1))(objectClass=organizationalunit)(objectClass=organization))' }; potentialChildren.map(aChild => { const info = JSON.parse(aChild); params.baseDn = info.dn; - runGenericSearch(params, (resArray) => { + runGenericSearch(child_params, (resArray) => { if (resArray.length > 0) { info.showChildren = true; } @@ -385,21 +406,46 @@ nbIterations++; if (nbIterations === potentialChildren.length) { // Now process the selected direct children. - this.processDirectChildren(updatedChildren, null); + this.processDirectChildren(updatedChildren, params, null); } }); }); } // Process the entries that are direct children. - processDirectChildren = (directChildren, resCode) => { + processDirectChildren = (directChildren, params, resCode) => { + // Retrieve the selected node from ==> this.state.activeItems: [treeViewItem, parentItem] + const myActiveNode = this.state.activeItems[0]; + const myChildren = []; + let childId = 0; // Used to quickly locate the node in the tree data. + if (directChildren === null) { // There was a failure to connect to the LDAP server. - // TODO: Show Modal dialog - console.log(resCode); this.setState({ ldapFailure: true }); - this.props.handleNodeOnClick(null); - if (resCode.msg.endsWith('Can\'t contact LDAP server (-1)')) { - this.props.setServerReachabilityStatus(false, resCode.msg); + this.props.showTreeLoadingState(false); + + if (resCode.exit_status !== 0) { + this.props.addNotification( + "error", + `Error searching database - ${resCode.msg.split("\n").pop()}` + ); + + const randomId = Math.random().toString(36).substring(2, 15); + let nodeChildren = [{ + name: 'Encountered an error, unable to display child entries', + id: randomId, + icon: <ExclamationTriangleIcon />, + isFakeEntry: true + }]; + + const treeAllItems = this.state.allItems; + const parentIdObject = { + fullId: myActiveNode.id, + remainingId: myActiveNode.id + } + this.updateMyParent(treeAllItems, parentIdObject, nodeChildren, true); + this.setState({ + allItems: treeAllItems + }); } return; } else { @@ -408,11 +454,6 @@ this.setState({ ldapFailure: false }); } - // Retrieve the selected node from ==> this.state.activeItems: [treeViewItem, parentItem] - const myActiveNode = this.state.activeItems[0]; - const myChildren = []; - let childId = 0; // Used to quickly locate the node in the tree data. - for (const aChild of directChildren) { const info = JSON.parse(aChild); const numSubValue = parseInt(info.numSubordinates); @@ -477,7 +518,7 @@ <div>No Databases</div> </Bullseye> } - <div className="ds-editor-tree"> + <div className={this.props.isDisabled ? "ds-disabled ds-editor-tree" : "ds-editor-tree"}> <TreeView data={allItems} onSelect={this.treeOnClick} diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/389-ds-base-2.0.13~git1.72eb93ac9/src/cockpit/389-console/src/lib/ldap_editor/lib/options.jsx new/389-ds-base-2.0.14~git3.c9226ad90/src/cockpit/389-console/src/lib/ldap_editor/lib/options.jsx --- old/389-ds-base-2.0.13~git1.72eb93ac9/src/cockpit/389-console/src/lib/ldap_editor/lib/options.jsx 2022-01-25 01:49:42.000000000 +0100 +++ new/389-ds-base-2.0.14~git3.c9226ad90/src/cockpit/389-console/src/lib/ldap_editor/lib/options.jsx 2022-02-01 23:35:34.000000000 +0100 @@ -1,24 +1,23 @@ // LDAP options. const LdapOptions = { - sizeLimit: 1000, - timeLimit: 5 + sizeLimit: 2000, + timeLimit: 10 }; + // Size limit. export function setSizeLimit (limit) { - LdapOptions.sizeLimit = limit; - console.log(`LdapOptions.sizeLimit = ${LdapOptions.sizeLimit}`); + LdapOptions.sizeLimit = limit; }; export function getSizeLimit () { - return LdapOptions.sizeLimit; + return LdapOptions.sizeLimit; } // Time limit. export function setTimeLimit (limit) { - LdapOptions.timeLimit = limit; - console.log(`LdapOptions.timeLimit = ${LdapOptions.timeLimit}`); + LdapOptions.timeLimit = limit; }; export function getTimeLimit () { - return LdapOptions.timeLimit; + return LdapOptions.timeLimit; } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/389-ds-base-2.0.13~git1.72eb93ac9/src/cockpit/389-console/src/lib/ldap_editor/lib/utils.jsx new/389-ds-base-2.0.14~git3.c9226ad90/src/cockpit/389-console/src/lib/ldap_editor/lib/utils.jsx --- old/389-ds-base-2.0.13~git1.72eb93ac9/src/cockpit/389-console/src/lib/ldap_editor/lib/utils.jsx 2022-01-25 01:49:42.000000000 +0100 +++ new/389-ds-base-2.0.14~git3.c9226ad90/src/cockpit/389-console/src/lib/ldap_editor/lib/utils.jsx 2022-02-01 23:35:34.000000000 +0100 @@ -238,6 +238,8 @@ params.searchScope, '-l', params.timeLimit, + '-z', + params.sizeLimit, params.searchFilter, '*', '+' @@ -258,7 +260,10 @@ if (err.exit_status === 4) { console.log('Size limit hit'); // Use the partial data. searchResult = data; - // TODO: Check other relevant error codes ( 32, 53 ...) + params.addNotification( + "info", + `Size limit of ${params.sizeLimit} was exceeded. The child entries of "${params.searchBase}" have been truncated.` + ); } else { searchResult = null; resultCallback(null, { status: err.exit_status, msg: err.message }); @@ -515,17 +520,20 @@ searchResult = data; }) .catch((err, data) => { - // .fail(err => { console.log('FAIL err.exit_status ==> ' + err.exit_status); console.log('FAIL err.message ==> ' + err.message); if (err.exit_status === 4) { - console.log('Size limit hit'); // Use the partial data. searchResult = data; - // TODO: Check other relevant error codes ( 32, 53 ...) + const size_limit = getSizeLimit(); + params.addNotification( + "info", + `Size limit of ${size_limit} was exceeded. The child entries of "${params.baseDn}" have been truncated. ` + + `Use the "Search" feature if you want to adjust the size limit and retrieve more entries` + ); } else { searchResult = null; - oneLevelCallback(null, { status: err.exit_status, msg: err.message }); + oneLevelCallback(null, params, { status: err.exit_status, msg: err.message }); } }) .finally(() => { @@ -568,7 +576,7 @@ } }); // Process the list of entries. - oneLevelCallback(allEntries, null); + oneLevelCallback(allEntries, params, null); }); } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/389-ds-base-2.0.13~git1.72eb93ac9/src/cockpit/389-console/src/lib/ldap_editor/search.jsx new/389-ds-base-2.0.14~git3.c9226ad90/src/cockpit/389-console/src/lib/ldap_editor/search.jsx --- old/389-ds-base-2.0.13~git1.72eb93ac9/src/cockpit/389-console/src/lib/ldap_editor/search.jsx 2022-01-25 01:49:42.000000000 +0100 +++ new/389-ds-base-2.0.14~git3.c9226ad90/src/cockpit/389-console/src/lib/ldap_editor/search.jsx 2022-02-01 23:35:34.000000000 +0100 @@ -55,10 +55,11 @@ searchBase: "", searchFilter: "", searchScope: "sub", - sizeLimit: 1000, + sizeLimit: 2000, timeLimit: 30, isExpanded: false, searchSuffix: "", + baseDN: "", searchType: 'Search Text', searchText: "", getOperationalAttrs: false, @@ -201,6 +202,7 @@ searchScope: this.state.searchScope, sizeLimit: this.state.sizeLimit, timeLimit: this.state.timeLimit, + addNotification: this.props.addNotification, }; getSearchEntries(params, this.processResults); }; @@ -256,9 +258,17 @@ componentDidMount() { const suffixList = this.props.suffixList; const searchBase = this.props.searchBase; + let baseDN = searchBase; // Drop down list of selected suffix + for (const suffix of suffixList) { + if (baseDN.includes(suffix)) { + baseDN = suffix; + break; + } + } this.setState({ searchBase: searchBase ? searchBase : suffixList.length > 0 ? suffixList[0] : "", searchSuffix: this.props.suffixList.length > 0 ? this.props.suffixList[0] : "", + baseDN: baseDN }); } @@ -294,7 +304,7 @@ } // Process the entries that are direct children. - processResults = (searchResults) => { + processResults = (searchResults, resObj) => { const resultRows = []; let rowNumber = 0; @@ -332,6 +342,13 @@ // Increment by 2 the row number. rowNumber += 2; }); + } else { + if (resObj.status !== 0) { + this.props.addNotification( + "error", + `Error searching the database: ${resObj.msg}` + ); + } } this.setState({ @@ -362,6 +379,7 @@ this.setState({ searchSuffix: value, searchBase: value, + baseDN: value, }); } @@ -541,7 +559,7 @@ <GridItem span={4}> <FormSelect id="searchSuffix" - value={this.state.searchSuffix} + value={this.state.baseDN} onChange={(value, event) => { this.handleSuffixChange(event); }} @@ -853,7 +871,7 @@ </ExpandableSection> </Form> <div className="ds-indent"> - <div className={this.state.searching ? "ds-margin-top-xlg ds-center" : "ds-hidden"}> + <div className={this.state.searching ? "ds-margin-top-lg ds-center" : "ds-hidden"}> <TextContent> <Text component={TextVariants.h3}> Searching <i>{this.state.searchBase}</i> ... @@ -862,6 +880,9 @@ <Spinner className="ds-margin-top-lg" size="xl" /> </div> <div className={searching ? "ds-hidden" : ""}> + <center> + <p><b>Results:</b> {total}</p> + </center> <EditorTableView key={searching} loading={searching} diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/389-ds-base-2.0.13~git1.72eb93ac9/src/cockpit/389-console/src/lib/ldap_editor/tableView.jsx new/389-ds-base-2.0.14~git3.c9226ad90/src/cockpit/389-console/src/lib/ldap_editor/tableView.jsx --- old/389-ds-base-2.0.13~git1.72eb93ac9/src/cockpit/389-console/src/lib/ldap_editor/tableView.jsx 2022-01-25 01:49:42.000000000 +0100 +++ new/389-ds-base-2.0.14~git3.c9226ad90/src/cockpit/389-console/src/lib/ldap_editor/tableView.jsx 2022-02-01 23:35:34.000000000 +0100 @@ -66,6 +66,7 @@ perPage={this.props.perPage} onSetPage={(_evt, value) => this.props.onSetPage(value)} onPerPageSelect={(_evt, value) => this.props.onPerPageSelect(value)} + dropDirection="up" /> } </div>; diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/389-ds-base-2.0.13~git1.72eb93ac9/src/cockpit/389-console/src/lib/ldap_editor/treeView.jsx new/389-ds-base-2.0.14~git3.c9226ad90/src/cockpit/389-console/src/lib/ldap_editor/treeView.jsx --- old/389-ds-base-2.0.13~git1.72eb93ac9/src/cockpit/389-console/src/lib/ldap_editor/treeView.jsx 2022-01-25 01:49:42.000000000 +0100 +++ new/389-ds-base-2.0.14~git3.c9226ad90/src/cockpit/389-console/src/lib/ldap_editor/treeView.jsx 2022-02-01 23:35:34.000000000 +0100 @@ -117,6 +117,7 @@ this.updateEntryRows(treeViewItem); return; } + this.setState({ searching: true, }, () => { @@ -173,6 +174,12 @@ } } + showEntryLoading = (isEntryLoading) => { + this.setState({ + searching: isEntryLoading ? true : false + }); + }; + showTreeLoadingState = (isTreeLoading) => { this.setState({ isTreeLoading, @@ -388,10 +395,12 @@ }); } }); + // Update the refresh time. this.setState({ latestEntryRefreshTime: Date.now(), }); + this.showEntryLoading(false); }); }; @@ -451,6 +460,7 @@ > ACIs ... </DropdownItem>, + /* <DropdownItem isDisabled key="tree-view-roles" @@ -469,6 +479,7 @@ > Smart Referrals ... </DropdownItem>, + */ <DropdownSeparator key="separator-3" />, <DropdownItem key="tree-view-delete" @@ -560,6 +571,8 @@ showTreeLoadingState={this.showTreeLoadingState} refreshButtonTriggerTime={refreshButtonTriggerTime} handleEntryRefresh={this.refreshEntry} + addNotification={this.props.addNotification} + isDisabled={isTreeLoading} /> } </GridItem> diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/389-ds-base-2.0.13~git1.72eb93ac9/src/cockpit/389-console/src/lib/server/settings.jsx new/389-ds-base-2.0.14~git3.c9226ad90/src/cockpit/389-console/src/lib/server/settings.jsx --- old/389-ds-base-2.0.13~git1.72eb93ac9/src/cockpit/389-console/src/lib/server/settings.jsx 2022-01-25 01:49:42.000000000 +0100 +++ new/389-ds-base-2.0.14~git3.c9226ad90/src/cockpit/389-console/src/lib/server/settings.jsx 2022-02-01 23:35:34.000000000 +0100 @@ -5,6 +5,7 @@ Button, Checkbox, Form, + FormHelperText, FormSelect, FormSelectOption, Grid, @@ -270,6 +271,13 @@ valueErr = true; disableSaveBtn = true; } + if (attr === 'nsslapd-disk-monitoring-threshold') { + const numVal = Number(value); + if (numVal < 4096) { + valueErr = true; + disableSaveBtn = true; + } + } } else if (nav_tab == "adv") { // Handle special cases for anon limit dn if (attr == 'nsslapd-anonlimitsdn' && !valid_dn(value)) { @@ -850,15 +858,17 @@ min={4096} max={9223372036854775807} onMinus={() => { this.onMinusConfig("nsslapd-disk-monitoring-threshold", "diskmon") }} - onChange={(e) => { this.onConfigChange(e, "nsslapd-disk-monitoring-threshold", 4096, 9223372036854775807, "diskmon") }} + onChange={(e) => { this.onConfigChange(e, "nsslapd-disk-monitoring-threshold", 1, 9223372036854775807, "diskmon") }} onPlus={() => { this.onPlusConfig("nsslapd-disk-monitoring-threshold", "diskmon") }} inputName="input" inputAriaLabel="number input" minusBtnAriaLabel="minus" plusBtnAriaLabel="plus" widthChars={8} - validated={this.state.errObjDiskMon.diskThreshold ? ValidatedOptions.error : ValidatedOptions.default} /> + <FormHelperText isError isHidden={!this.state.errObjDiskMon['nsslapd-disk-monitoring-threshold']}> + Value must be greater than or equal to 4096 + </FormHelperText> </GridItem> </Grid> <Grid @@ -880,7 +890,6 @@ minusBtnAriaLabel="minus" plusBtnAriaLabel="plus" widthChars={8} - validated={this.state.errObjDiskMon.diskThreshold ? ValidatedOptions.error : ValidatedOptions.default} /> </GridItem> </Grid> diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/389-ds-base-2.0.13~git1.72eb93ac9/src/lib389/cli/dscontainer new/389-ds-base-2.0.14~git3.c9226ad90/src/lib389/cli/dscontainer --- old/389-ds-base-2.0.13~git1.72eb93ac9/src/lib389/cli/dscontainer 2022-01-25 01:49:42.000000000 +0100 +++ new/389-ds-base-2.0.14~git3.c9226ad90/src/lib389/cli/dscontainer 2022-02-01 23:35:34.000000000 +0100 @@ -253,7 +253,7 @@ s2b.set('ldif_dir', '/data/ldif') s2b.set('run_dir', '/data/run') s2b.set('lock_dir', '/data/run/lock') - s2b.set('ldapi', '/data/run/slapd.socket') + s2b.set('ldapi', '/data/run/slapd-localhost.socket') s2b.set('log_dir', '/data/logs') s2b.set('access_log', '/data/logs/access') diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/389-ds-base-2.0.13~git1.72eb93ac9/src/lib389/lib389/migrate/openldap/config.py new/389-ds-base-2.0.14~git3.c9226ad90/src/lib389/lib389/migrate/openldap/config.py --- old/389-ds-base-2.0.13~git1.72eb93ac9/src/lib389/lib389/migrate/openldap/config.py 2022-01-25 01:49:42.000000000 +0100 +++ new/389-ds-base-2.0.14~git3.c9226ad90/src/lib389/lib389/migrate/openldap/config.py 2022-02-01 23:35:34.000000000 +0100 @@ -113,10 +113,12 @@ self.idx = name.split('}', 1)[0].split('{', 1)[1] self.uuid = ensure_str(self.config[1]['entryUUID'][0]) - self.index = [ - tuple(ensure_str(x).split(' ')) - for x in self.config[1]['olcDbIndex'] - ] + self.index = [] + for x in self.config[1]['olcDbIndex']: + (attr, idx_types) = ensure_str(x).split(' ', 1) + attr = attr.strip() + for idx_type in idx_types.split(','): + self.index.append((attr, idx_type.strip())) self.log.debug(f"settings -> {self.suffix}, {self.idx}, {self.uuid}, {self.index}") diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/389-ds-base-2.0.13~git1.72eb93ac9/wrappers/ds_selinux_restorecon.sh.in new/389-ds-base-2.0.14~git3.c9226ad90/wrappers/ds_selinux_restorecon.sh.in --- old/389-ds-base-2.0.13~git1.72eb93ac9/wrappers/ds_selinux_restorecon.sh.in 2022-01-25 01:49:42.000000000 +0100 +++ new/389-ds-base-2.0.14~git3.c9226ad90/wrappers/ds_selinux_restorecon.sh.in 2022-02-01 23:35:34.000000000 +0100 @@ -29,5 +29,6 @@ exit 0 fi -# Now run restorecon -restorecon ${DS_HOME_DIR} +# Now run restorecon, but don't die if it fails (could be that the +# directory doesn't exist) +restorecon ${DS_HOME_DIR} || : ++++++ 389-ds-base.obsinfo ++++++ --- /var/tmp/diff_new_pack.XuFJha/_old 2022-02-02 22:42:02.267278047 +0100 +++ /var/tmp/diff_new_pack.XuFJha/_new 2022-02-02 22:42:02.271278020 +0100 @@ -1,5 +1,5 @@ name: 389-ds-base -version: 2.0.13~git1.72eb93ac9 -mtime: 1643071782 -commit: 72eb93ac9157e8012fda21834de937c8623aa10e +version: 2.0.14~git3.c9226ad90 +mtime: 1643754934 +commit: c9226ad90f677a02c63c2cc68061690ea88393a8 ++++++ _service ++++++ --- /var/tmp/diff_new_pack.XuFJha/_old 2022-02-02 22:42:02.335277586 +0100 +++ /var/tmp/diff_new_pack.XuFJha/_new 2022-02-02 22:42:02.339277559 +0100 @@ -27,8 +27,9 @@ <param name="url">https://github.com/SUSE/supportutils-plugin-dirsrv.git</param> <param name="versionformat">@PARENT_TAG@~git@TAG_OFFSET@.%h</param> <param name="scm">git</param> - <param name="revision">v0.1.0</param> - <param name="match-tag">v0.1.0</param> + <param name="revision">main</param> + <param name="versionrewrite-pattern">(.*)</param> + <param name="versionrewrite-replacement">\1</param> <param name="changesgenerate">disable</param> <param name="without-version">true</param> </service> ++++++ supportutils-plugin-dirsrv-v0.1.0~git0.37cb939.tar.xz -> supportutils-plugin-dirsrv-v0.1.0~git2.4c54cf4.tar.xz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/supportutils-plugin-dirsrv-v0.1.0~git0.37cb939/dirsrv new/supportutils-plugin-dirsrv-v0.1.0~git2.4c54cf4/dirsrv --- old/supportutils-plugin-dirsrv-v0.1.0~git0.37cb939/dirsrv 2021-04-07 04:58:12.000000000 +0200 +++ new/supportutils-plugin-dirsrv-v0.1.0~git2.4c54cf4/dirsrv 2022-02-02 03:31:48.000000000 +0100 @@ -42,21 +42,26 @@ self.logfile.write("\n") -def run_cmd(cmd, check): +def run_cmd(cmd, check, shell=False): output = subprocess.run( cmd, check=check, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, + shell=shell ) return output.stdout.decode('UTF-8') -def log_cmd(logfile, cmd, check=True): +def log_cmd(logfile, cmd, check=True, shell=False): logfile.write(COMMAND_HDR) logfile.write("%s\n" % " ".join(cmd)) status = True try: - output = run_cmd(cmd, check) + if shell: + cmd = " ".join(cmd) + output = run_cmd(cmd, check, shell) + else: + output = run_cmd(cmd, check, shell) logfile.write(output) except Exception as e: logfile.write("ERROR: subprocess raised error: %s\n" % e.returncode) @@ -173,6 +178,8 @@ try: log_note(logfile, ["Active Server-Cert:"] + [tls.display_cert_details(lib389.nss_ssl.CERT_NAME)]) + log_cmd(logfile, ["certutil", "-V", "-d", inst.ds_paths.cert_dir, "-n", lib389.nss_ssl.CERT_NAME, "-u", "V"]) + log_cmd(logfile, ["certutil", "-L", "-d", inst.ds_paths.cert_dir, "-n", lib389.nss_ssl.CERT_NAME, "-a", "|", "openssl", "x509", "-noout", "-purpose"], shell=True) except: log_error(logfile, "Unable to display active Server-Cert information - using alternate nickname?") ++++++ vendor.tar.xz ++++++ /work/SRC/openSUSE:Factory/389-ds/vendor.tar.xz /work/SRC/openSUSE:Factory/.389-ds.new.1898/vendor.tar.xz differ: char 27, line 1