From 92299be04683730e5ec62c444b9cb742cab74154 Mon Sep 17 00:00:00 2001 From: Kozyrev Yuri Date: Thu, 28 May 2026 01:09:05 +0400 Subject: [PATCH 01/27] Utilize real default password settings --- src/adldap/ad_defines.h | 11 ++++++ .../pso_results_widget/pso_edit_widget.cpp | 39 ++++++++++++------- 2 files changed, 37 insertions(+), 13 deletions(-) diff --git a/src/adldap/ad_defines.h b/src/adldap/ad_defines.h index 825b6209e..d68e59bfa 100644 --- a/src/adldap/ad_defines.h +++ b/src/adldap/ad_defines.h @@ -149,6 +149,10 @@ enum SystemFlagsBit { #define ATTRIBUTE_MAX_PWD_AGE "maxPwdAge" #define ATTRIBUTE_MIN_PWD_AGE "minPwdAge" #define ATTRIBUTE_LOCKOUT_DURATION "lockoutDuration" +#define ATTRIBUTE_PWD_PROPERTIES "pwdProperties" +#define ATTRIBUTE_PWD_HISTORY_LENGTH "pwdHistoryLength" +#define ATTRIBUTE_MIN_PWD_LENGTH "minPwdLength" +#define ATTRIBUTE_LOCKOUT_THRESHOLD "lockoutThreshold" #define ATTRIBUTE_IS_CRITICAL_SYSTEM_OBJECT "isCriticalSystemObject" #define ATTRIBUTE_GPC_FILE_SYS_PATH "gPCFileSysPath" #define ATTRIBUTE_GPC_FUNCTIONALITY_VERSION "gpCFunctionalityVersion" @@ -407,6 +411,13 @@ const long long MILLIS_TO_100_NANOS = 10000LL; #define ETYPES_AES128_CTS_HMAC_SHA1_96 0x00000008 #define ETYPES_AES256_CTS_HMAC_SHA1_96 0x00000010 +#define SAM_MASK_DOMAIN_PASSWORD_COMPLEX 1 +#define SAM_MASK_DOMAIN_PASSWORD_NO_ANON_CHANGE 2 +#define SAM_MASK_DOMAIN_PASSWORD_NO_CLEAR_CHANGE 4 +#define SAM_MASK_DOMAIN_LOCKOUT_ADMINS 8 +#define SAM_MASK_DOMAIN_PASSWORD_STORE_CLEARTEXT 16 +#define SAM_MASK_DOMAIN_REFUSE_PASSWORD_CHANGE 32 + enum SearchScope { SearchScope_Object, SearchScope_Children, diff --git a/src/admc/results_widgets/pso_results_widget/pso_edit_widget.cpp b/src/admc/results_widgets/pso_results_widget/pso_edit_widget.cpp index cc9acb73b..fa8ccf64a 100644 --- a/src/admc/results_widgets/pso_results_widget/pso_edit_widget.cpp +++ b/src/admc/results_widgets/pso_results_widget/pso_edit_widget.cpp @@ -18,6 +18,7 @@ */ #include "pso_edit_widget.h" +#include "ad_defines.h" #include "ui_pso_edit_widget.h" #include "ad_interface.h" #include "ad_object.h" @@ -26,6 +27,7 @@ #include "managers/icon_manager.h" #include "status.h" #include "globals.h" +#include "ad_config.h" #include @@ -189,19 +191,30 @@ bool PSOEditWidget::settings_are_default() { } void PSOEditWidget::update_defaults() { - // TODO: Get defaults from Default Domain Policy. - - ui->min_passwd_len_spinbox->setValue(7); - ui->history_length_spinbox->setValue(24); - ui->logon_attempts_spinbox->setValue(0); - - ui->lockout_duration_spinbox->setValue(30); - ui->reset_lockout_spinbox->setValue(30); - ui->min_age_spinbox->setValue(1); - ui->max_age_spinbox->setValue(42); - - ui->complexity_req_checkbox->setChecked(true); - ui->store_passwd_checkbox->setChecked(false); + AdInterface ad; + if (!ad.is_connected()) { + return; + } + AdObject result = ad.search_object(g_adconfig->domain_dn(), {ATTRIBUTE_PWD_PROPERTIES, + ATTRIBUTE_PWD_HISTORY_LENGTH, + ATTRIBUTE_MIN_PWD_LENGTH, + ATTRIBUTE_MIN_PWD_AGE, + ATTRIBUTE_MAX_PWD_AGE, + ATTRIBUTE_LOCKOUT_DURATION, + ATTRIBUTE_LOCKOUT_THRESHOLD, + ATTRIBUTE_LOCKOUT_OBSERVATION_WINDOW}); + + ui->min_passwd_len_spinbox->setValue(result.get_int(ATTRIBUTE_MIN_PWD_LENGTH)); + ui->history_length_spinbox->setValue(result.get_int(ATTRIBUTE_PWD_HISTORY_LENGTH)); + ui->logon_attempts_spinbox->setValue(result.get_int(ATTRIBUTE_LOCKOUT_THRESHOLD)); + + ui->lockout_duration_spinbox->setValue(spinbox_timespan_units(result, ATTRIBUTE_LOCKOUT_DURATION)); + ui->reset_lockout_spinbox->setValue(spinbox_timespan_units(result, ATTRIBUTE_LOCKOUT_OBSERVATION_WINDOW)); + ui->min_age_spinbox->setValue(spinbox_timespan_units(result, ATTRIBUTE_MIN_PWD_AGE)); + ui->max_age_spinbox->setValue(spinbox_timespan_units(result, ATTRIBUTE_MAX_PWD_AGE)); + + ui->complexity_req_checkbox->setChecked(result.get_int(ATTRIBUTE_PWD_PROPERTIES) & SAM_MASK_DOMAIN_PASSWORD_COMPLEX); + ui->store_passwd_checkbox->setChecked(result.get_int(ATTRIBUTE_PWD_PROPERTIES) & SAM_MASK_DOMAIN_PASSWORD_STORE_CLEARTEXT); ui->applied_list_widget->clear(); } From 0d34b9d8c691b37d7ba705d73ae3795049e648ed Mon Sep 17 00:00:00 2001 From: Kozyrev Yuri Date: Thu, 11 Jun 2026 16:28:08 +0400 Subject: [PATCH 02/27] Put default password settings in PSO containet widget --- src/admc/CMakeLists.txt | 1 + src/admc/console_impls/item_type.h | 1 + .../object_impl/console_object_operations.cpp | 10 +- .../console_impls/password_settings_impl.cpp | 130 ++++++++++++++++++ .../console_impls/password_settings_impl.h | 57 ++++++++ src/admc/main_window.cpp | 4 + 6 files changed, 201 insertions(+), 2 deletions(-) create mode 100644 src/admc/console_impls/password_settings_impl.cpp create mode 100644 src/admc/console_impls/password_settings_impl.h diff --git a/src/admc/CMakeLists.txt b/src/admc/CMakeLists.txt index b1e0c9197..a644cdbda 100644 --- a/src/admc/CMakeLists.txt +++ b/src/admc/CMakeLists.txt @@ -254,6 +254,7 @@ set(ADMC_SOURCES console_impls/policy_ou_impl.cpp console_impls/found_policy_impl.cpp console_impls/domain_info_impl.cpp + console_impls/password_settings_impl.cpp permission_control_widgets/permissions_widget.cpp permission_control_widgets/creation_deletion_permissions_widget.cpp diff --git a/src/admc/console_impls/item_type.h b/src/admc/console_impls/item_type.h index 71181d43f..17bcfdbb6 100644 --- a/src/admc/console_impls/item_type.h +++ b/src/admc/console_impls/item_type.h @@ -34,6 +34,7 @@ enum ItemType { ItemType_FindPolicy, ItemType_FoundPolicy, ItemType_DomainInfo, + ItemType_PasswordSettings, ItemType_LAST, }; diff --git a/src/admc/console_impls/object_impl/console_object_operations.cpp b/src/admc/console_impls/object_impl/console_object_operations.cpp index 3e25314d9..a5b616d92 100644 --- a/src/admc/console_impls/object_impl/console_object_operations.cpp +++ b/src/admc/console_impls/object_impl/console_object_operations.cpp @@ -613,6 +613,11 @@ bool ConsoleObjectTreeOperations::console_object_deletion_dialog(ConsoleWidget * } void ConsoleObjectTreeOperations::console_tree_add_password_settings(ConsoleWidget *console, AdInterface &ad) { + const QList head_row = console->add_scope_item(ItemType_PasswordSettings, console->domain_info_index()); + auto password_settings_root = head_row[0]; + password_settings_root->setText(QCoreApplication::translate("password_settings_impl", "Password settings")); + password_settings_root->setIcon(g_icon_manager->item_icon(ItemIcon_Password_Settings_Object)); + password_settings_root->setDragEnabled(false); const QString filter = filter_CONDITION(Condition_Equals, ATTRIBUTE_OBJECT_CLASS, CLASS_PSO_CONTAINER); auto search_results = ad.search(g_adconfig->domain_dn(), SearchScope_All, filter, {}); const QString err = QObject::tr("Password settings container is not available"); @@ -621,9 +626,10 @@ void ConsoleObjectTreeOperations::console_tree_add_password_settings(ConsoleWidg return; } + console_object_item_data_load(password_settings_root, search_results.values()[0]); + const int pso_container_sort_idx = 3; - console_tree_add_root_child(console, search_results.values()[0], pso_container_sort_idx, - QObject::tr("Fine-grained password policies")); + console->set_item_sort_index(password_settings_root->index(), pso_container_sort_idx); } QString ConsoleObjectTreeOperations::console_object_count_string(ConsoleWidget *console, const QModelIndex &index) { diff --git a/src/admc/console_impls/password_settings_impl.cpp b/src/admc/console_impls/password_settings_impl.cpp new file mode 100644 index 000000000..34041956e --- /dev/null +++ b/src/admc/console_impls/password_settings_impl.cpp @@ -0,0 +1,130 @@ +/* + * ADMC - AD Management Center + * + * Copyright (C) 2020-2026 BaseALT Ltd. + * Copyright (C) 2026 Yuri Kozyrev + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "console_impls/password_settings_impl.h" + +#include "ad_defines.h" +#include "adldap.h" +#include "console_impls/item_type.h" +#include "console_impls/object_impl/object_impl.h" +#include "console_impls/policy_impl.h" +#include "console_widget/results_view.h" +#include "create_dialogs/create_policy_dialog.h" +#include "fsmo/fsmo_utils.h" +#include "globals.h" +#include "object_impl/console_object_operations.h" +#include "results_widgets/pso_results_widget/pso_results_widget.h" +#include "status.h" +#include "utils.h" + +#include +#include +#include +#include + +PasswordSettingsImpl::PasswordSettingsImpl(ConsoleWidget *console_arg) +: ConsoleImpl(console_arg) { + set_results_widget(new PSOResultsWidget(console_arg)); + + create_pso_action = new QAction(tr("Create password settings object"), this); + + connect( + create_pso_action, &QAction::triggered, + this, [this]() { + const QString parent_dn = get_selected_target_dn(console, ItemType_PasswordSettings, ObjectRole_DN); + ConsoleObjectTreeOperations::console_object_create({console}, CLASS_PSO, parent_dn); + }); +} + +void PasswordSettingsImpl::fetch(const QModelIndex &index) { + AdInterface ad; + if (ad_failed(ad, console)) { + return; + } + + const QString base = g_adconfig->pso_container_dn(); + const SearchScope scope = SearchScope_Children; + const QString filter = filter_CONDITION(Condition_Equals, ATTRIBUTE_OBJECT_CLASS, CLASS_PSO_CONTAINER); + const QList attributes = QList(); + const QHash results = ad.search(base, scope, "", attributes); + + ConsoleObjectTreeOperations::add_objects_to_console(console, results.values(), index); +} + +void PasswordSettingsImpl::refresh(const QList &index_list) { + const QModelIndex index = index_list[0]; + + console->delete_children(index); + fetch(index); +} + +QList PasswordSettingsImpl::get_all_custom_actions() const { + QList out; + + out.append(create_pso_action); + + return out; +} + +QSet PasswordSettingsImpl::get_custom_actions(const QModelIndex &index, const bool single_selection) const { + UNUSED_ARG(index); + UNUSED_ARG(single_selection); + + QSet out; + + out.insert(create_pso_action); + + return out; +} + +QSet PasswordSettingsImpl::get_standard_actions(const QModelIndex &index, const bool single_selection) const { + UNUSED_ARG(index); + UNUSED_ARG(single_selection); + + QSet out; + + out.insert(StandardAction_Refresh); + + return out; +} + +void password_settings_impl_add_objects(ConsoleWidget *console, const QList &object_list, const QModelIndex &parent) { + if (!parent.isValid()) { + return; + } + + const bool parent_was_fetched = console_item_get_was_fetched(parent); + if (!parent_was_fetched) { + return; + } + + for (const AdObject &object : object_list) { + const QList row = console->add_scope_item(ItemType_Object, parent); + ConsoleObjectTreeOperations::console_object_load(row, object); + } +} + +QList PasswordSettingsImpl::column_labels() const { + return ConsoleObjectTreeOperations::object_impl_column_labels(); +} + +QList PasswordSettingsImpl::default_columns() const { + return ConsoleObjectTreeOperations::object_impl_default_columns(); +} diff --git a/src/admc/console_impls/password_settings_impl.h b/src/admc/console_impls/password_settings_impl.h new file mode 100644 index 000000000..cd8beb7f9 --- /dev/null +++ b/src/admc/console_impls/password_settings_impl.h @@ -0,0 +1,57 @@ +/* + * ADMC - AD Management Center + * + * Copyright (C) 2020-2026 BaseALT Ltd. + * Copyright (C) 2026 Yuri Kozyrev + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef PASSWORD_SETTINGS_IMPL_H +#define PASSWORD_SETTINGS_IMPL_H + +#include "console_widget/console_impl.h" +#include "console_widget/console_widget.h" + +class AdObject; +class AdInterface; + +/** + * @class + * @brief Impl for Password Settings Container. Displays global password + * settings for the domain they are applied whenever no PSO can't be used. + */ +class PasswordSettingsImpl final : public ConsoleImpl { + Q_OBJECT + +public: + PasswordSettingsImpl(ConsoleWidget *console_arg); + + void fetch(const QModelIndex &index) override; + void refresh(const QList &index_list) override; + + QList get_all_custom_actions() const override; + QSet get_custom_actions(const QModelIndex &index, const bool single_selection) const override; + QSet get_standard_actions(const QModelIndex &index, const bool single_selection) const override; + + QList column_labels() const override; + QList default_columns() const override; + +private: + QAction *create_pso_action; +}; + +void password_settings_impl_add_objects(ConsoleWidget *console, const QList &object_list, const QModelIndex &parent); + +#endif /* PASSWORD_SETTINGS_IMPL_H */ diff --git a/src/admc/main_window.cpp b/src/admc/main_window.cpp index 63f39384c..4c62be0ec 100644 --- a/src/admc/main_window.cpp +++ b/src/admc/main_window.cpp @@ -20,6 +20,7 @@ */ #include "main_window.h" +#include "console_impls/password_settings_impl.h" #include "ui_main_window.h" #include "about_dialog.h" @@ -483,6 +484,9 @@ void MainWindow::init_on_connect(AdInterface &ad) { auto policy_impl = new PolicyImpl(ui->console); ui->console->register_impl(ItemType_Policy, policy_impl); + auto pso_impl = new PasswordSettingsImpl(ui->console); + ui->console->register_impl(ItemType_PasswordSettings, pso_impl); + auto query_item_impl = new QueryItemImpl(ui->console); ui->console->register_impl(ItemType_QueryItem, query_item_impl); From d5811944181b039874695a23c515f30b15bab8d9 Mon Sep 17 00:00:00 2001 From: Kozyrev Yuri Date: Fri, 12 Jun 2026 03:52:30 +0400 Subject: [PATCH 03/27] Remove some unneeded vars --- .../console_impls/object_impl/console_object_operations.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/admc/console_impls/object_impl/console_object_operations.cpp b/src/admc/console_impls/object_impl/console_object_operations.cpp index a5b616d92..ffa688324 100644 --- a/src/admc/console_impls/object_impl/console_object_operations.cpp +++ b/src/admc/console_impls/object_impl/console_object_operations.cpp @@ -613,8 +613,7 @@ bool ConsoleObjectTreeOperations::console_object_deletion_dialog(ConsoleWidget * } void ConsoleObjectTreeOperations::console_tree_add_password_settings(ConsoleWidget *console, AdInterface &ad) { - const QList head_row = console->add_scope_item(ItemType_PasswordSettings, console->domain_info_index()); - auto password_settings_root = head_row[0]; + QStandardItem *password_settings_root = console->add_scope_item(ItemType_PasswordSettings, console->domain_info_index())[0]; password_settings_root->setText(QCoreApplication::translate("password_settings_impl", "Password settings")); password_settings_root->setIcon(g_icon_manager->item_icon(ItemIcon_Password_Settings_Object)); password_settings_root->setDragEnabled(false); From 3e22954e4e8d970ad34dc96df7067de53cf9a601 Mon Sep 17 00:00:00 2001 From: Kozyrev Yuri Date: Fri, 12 Jun 2026 05:36:48 +0400 Subject: [PATCH 04/27] Make default PSO readonly --- .../results_widgets/pso_results_widget/pso_edit_widget.cpp | 2 ++ .../pso_results_widget/pso_results_widget.cpp | 5 +++++ 2 files changed, 7 insertions(+) diff --git a/src/admc/results_widgets/pso_results_widget/pso_edit_widget.cpp b/src/admc/results_widgets/pso_results_widget/pso_edit_widget.cpp index fa8ccf64a..00ce5e1ba 100644 --- a/src/admc/results_widgets/pso_results_widget/pso_edit_widget.cpp +++ b/src/admc/results_widgets/pso_results_widget/pso_edit_widget.cpp @@ -217,6 +217,8 @@ void PSOEditWidget::update_defaults() { ui->store_passwd_checkbox->setChecked(result.get_int(ATTRIBUTE_PWD_PROPERTIES) & SAM_MASK_DOMAIN_PASSWORD_STORE_CLEARTEXT); ui->applied_list_widget->clear(); + + ui->name_edit->setReadOnly(true); } void PSOEditWidget::on_add() { diff --git a/src/admc/results_widgets/pso_results_widget/pso_results_widget.cpp b/src/admc/results_widgets/pso_results_widget/pso_results_widget.cpp index e948d823c..85460b5c8 100644 --- a/src/admc/results_widgets/pso_results_widget/pso_results_widget.cpp +++ b/src/admc/results_widgets/pso_results_widget/pso_results_widget.cpp @@ -32,6 +32,11 @@ PSOResultsWidget::PSOResultsWidget(QWidget *parent) : ResultsWidgetBase(parent), pso_edit_widget(new PSOEditWidget(this)) { ui->verticalLayout->addWidget(pso_edit_widget); + + ui->edit_button->setDisabled(true); + ui->cancel_button->setDisabled(true); + ui->apply_button->setDisabled(true); + pso_edit_widget->set_read_only(true); } void PSOResultsWidget::update(const QModelIndex &index) { From cbb09dbae3d61e848e4e98b6d72cb6024fa3b8c6 Mon Sep 17 00:00:00 2001 From: Kozyrev Yuri Date: Fri, 12 Jun 2026 05:37:10 +0400 Subject: [PATCH 05/27] Remove comented out code --- src/admc/results_widgets/pso_results_widget/pso_edit_widget.cpp | 2 -- .../results_widgets/pso_results_widget/pso_results_widget.cpp | 2 -- 2 files changed, 4 deletions(-) diff --git a/src/admc/results_widgets/pso_results_widget/pso_edit_widget.cpp b/src/admc/results_widgets/pso_results_widget/pso_edit_widget.cpp index 00ce5e1ba..c472ab09e 100644 --- a/src/admc/results_widgets/pso_results_widget/pso_edit_widget.cpp +++ b/src/admc/results_widgets/pso_results_widget/pso_edit_widget.cpp @@ -31,8 +31,6 @@ #include -// #include - PSOEditWidget::PSOEditWidget(QWidget *parent) : QWidget(parent), ui(new Ui::PSOEditWidget) { diff --git a/src/admc/results_widgets/pso_results_widget/pso_results_widget.cpp b/src/admc/results_widgets/pso_results_widget/pso_results_widget.cpp index 85460b5c8..a529ee9a1 100644 --- a/src/admc/results_widgets/pso_results_widget/pso_results_widget.cpp +++ b/src/admc/results_widgets/pso_results_widget/pso_results_widget.cpp @@ -25,8 +25,6 @@ #include "../ui_results_widget_base.h" #include -// #include - PSOResultsWidget::PSOResultsWidget(QWidget *parent) : ResultsWidgetBase(parent), pso_edit_widget(new PSOEditWidget(this)) { From 4c2bece11233569ede1394a1997f224a718592c2 Mon Sep 17 00:00:00 2001 From: Kozyrev Yuri Date: Wed, 17 Jun 2026 19:07:04 +0400 Subject: [PATCH 06/27] Remove unused function --- .../console_impls/password_settings_impl.cpp | 16 ---------------- src/admc/console_impls/password_settings_impl.h | 2 -- 2 files changed, 18 deletions(-) diff --git a/src/admc/console_impls/password_settings_impl.cpp b/src/admc/console_impls/password_settings_impl.cpp index 34041956e..8ecdef1fd 100644 --- a/src/admc/console_impls/password_settings_impl.cpp +++ b/src/admc/console_impls/password_settings_impl.cpp @@ -105,22 +105,6 @@ QSet PasswordSettingsImpl::get_standard_actions(const QModelInde return out; } -void password_settings_impl_add_objects(ConsoleWidget *console, const QList &object_list, const QModelIndex &parent) { - if (!parent.isValid()) { - return; - } - - const bool parent_was_fetched = console_item_get_was_fetched(parent); - if (!parent_was_fetched) { - return; - } - - for (const AdObject &object : object_list) { - const QList row = console->add_scope_item(ItemType_Object, parent); - ConsoleObjectTreeOperations::console_object_load(row, object); - } -} - QList PasswordSettingsImpl::column_labels() const { return ConsoleObjectTreeOperations::object_impl_column_labels(); } diff --git a/src/admc/console_impls/password_settings_impl.h b/src/admc/console_impls/password_settings_impl.h index cd8beb7f9..192a3101a 100644 --- a/src/admc/console_impls/password_settings_impl.h +++ b/src/admc/console_impls/password_settings_impl.h @@ -52,6 +52,4 @@ class PasswordSettingsImpl final : public ConsoleImpl { QAction *create_pso_action; }; -void password_settings_impl_add_objects(ConsoleWidget *console, const QList &object_list, const QModelIndex &parent); - #endif /* PASSWORD_SETTINGS_IMPL_H */ From a56750e1a4785d8cb3e869dbb7a20c5149221755 Mon Sep 17 00:00:00 2001 From: Kozyrev Yuri Date: Wed, 24 Jun 2026 18:06:12 +0400 Subject: [PATCH 07/27] Move global password settings retrieval to utils --- src/admc/utils.cpp | 15 +++++++++++++++ src/admc/utils.h | 2 ++ 2 files changed, 17 insertions(+) diff --git a/src/admc/utils.cpp b/src/admc/utils.cpp index 5b20b65b0..e38f4b152 100755 --- a/src/admc/utils.cpp +++ b/src/admc/utils.cpp @@ -495,3 +495,18 @@ bool creds_is_saved(const QString &username) { remembered_users.toStringList().contains(username); return saved; } + +AdObject global_password_settings() { + AdInterface ad; + if (!ad.is_connected()) { + return AdObject(); + } + return ad.search_object(g_adconfig->domain_dn(), {ATTRIBUTE_PWD_PROPERTIES, + ATTRIBUTE_PWD_HISTORY_LENGTH, + ATTRIBUTE_MIN_PWD_LENGTH, + ATTRIBUTE_MIN_PWD_AGE, + ATTRIBUTE_MAX_PWD_AGE, + ATTRIBUTE_LOCKOUT_DURATION, + ATTRIBUTE_LOCKOUT_THRESHOLD, + ATTRIBUTE_LOCKOUT_OBSERVATION_WINDOW}); +} diff --git a/src/admc/utils.h b/src/admc/utils.h index db346ef55..88695a50e 100755 --- a/src/admc/utils.h +++ b/src/admc/utils.h @@ -139,4 +139,6 @@ QString current_dc_dns_host_name(AdInterface &ad); bool creds_is_saved(const QString &username); +AdObject global_password_settings(); + #endif /* UTILS_H */ From 6acdda3b9a3d5662fad4436f4537eef306fd6bff Mon Sep 17 00:00:00 2001 From: Kozyrev Yuri Date: Wed, 24 Jun 2026 18:11:39 +0400 Subject: [PATCH 08/27] Rework PSOEditWidget update and get_settings to work with global password settings --- .../pso_results_widget/pso_edit_widget.cpp | 78 ++++++++++++------- .../pso_results_widget/pso_edit_widget.h | 14 +++- 2 files changed, 62 insertions(+), 30 deletions(-) diff --git a/src/admc/results_widgets/pso_results_widget/pso_edit_widget.cpp b/src/admc/results_widgets/pso_results_widget/pso_edit_widget.cpp index c472ab09e..54f897adf 100644 --- a/src/admc/results_widgets/pso_results_widget/pso_edit_widget.cpp +++ b/src/admc/results_widgets/pso_results_widget/pso_edit_widget.cpp @@ -28,6 +28,7 @@ #include "status.h" #include "globals.h" #include "ad_config.h" +#include "utils.h" #include @@ -46,7 +47,6 @@ PSOEditWidget::PSOEditWidget(QWidget *parent) : connect(ui->remove_button, &QPushButton::clicked, this, &PSOEditWidget::on_remove); update_defaults(); - default_setting_values = pso_settings_values(); } PSOEditWidget::~PSOEditWidget() { @@ -54,18 +54,28 @@ PSOEditWidget::~PSOEditWidget() { } void PSOEditWidget::update(const AdObject &passwd_settings_obj) { - ui->name_edit->setText(passwd_settings_obj.get_string(ATTRIBUTE_CN)); + if (is_global != !passwd_settings_obj.contains(ATTRIBUTE_CN)) { + is_global = !passwd_settings_obj.contains(ATTRIBUTE_CN); + ui->name_edit->setVisible(!is_global); + ui->name_label->setVisible(!is_global); + ui->precedence_label->setVisible(!is_global); + ui->precedence_spinbox->setVisible(!is_global); + ui->groupBox->setVisible(!is_global); + ui->line->setVisible(!is_global); + } + + ui->name_edit->setText(passwd_settings_obj.get_string(replace_attribute(ATTRIBUTE_CN))); ui->name_edit->setReadOnly(true); - ui->precedence_spinbox->setValue(passwd_settings_obj.get_int(ATTRIBUTE_MS_DS_PASSWORD_SETTINGS_PRECEDENCE)); - ui->min_passwd_len_spinbox->setValue(passwd_settings_obj.get_int(ATTRIBUTE_MS_DS_MIN_PASSWORD_LENGTH)); - ui->history_length_spinbox->setValue(passwd_settings_obj.get_int(ATTRIBUTE_MS_DS_PASSWORD_HISTORY_LENGTH)); - ui->logon_attempts_spinbox->setValue(passwd_settings_obj.get_int(ATTRIBUTE_MS_DS_LOCKOUT_THRESHOLD)); + ui->precedence_spinbox->setValue(passwd_settings_obj.get_int(replace_attribute(ATTRIBUTE_MS_DS_PASSWORD_SETTINGS_PRECEDENCE))); + ui->min_passwd_len_spinbox->setValue(passwd_settings_obj.get_int(replace_attribute(ATTRIBUTE_MS_DS_MIN_PASSWORD_LENGTH))); + ui->history_length_spinbox->setValue(passwd_settings_obj.get_int(replace_attribute(ATTRIBUTE_MS_DS_PASSWORD_HISTORY_LENGTH))); + ui->logon_attempts_spinbox->setValue(passwd_settings_obj.get_int(replace_attribute(ATTRIBUTE_MS_DS_LOCKOUT_THRESHOLD))); - ui->lockout_duration_spinbox->setValue(spinbox_timespan_units(passwd_settings_obj, ATTRIBUTE_MS_DS_LOCKOUT_DURATION)); - ui->reset_lockout_spinbox->setValue(spinbox_timespan_units(passwd_settings_obj, ATTRIBUTE_MS_DS_LOCKOUT_OBSERVATION_WINDOW)); - ui->min_age_spinbox->setValue(spinbox_timespan_units(passwd_settings_obj, ATTRIBUTE_MS_DS_MIN_PASSWORD_AGE)); - ui->max_age_spinbox->setValue(spinbox_timespan_units(passwd_settings_obj, ATTRIBUTE_MS_DS_MAX_PASSWORD_AGE)); + ui->lockout_duration_spinbox->setValue(spinbox_timespan_units(passwd_settings_obj, replace_attribute(ATTRIBUTE_MS_DS_LOCKOUT_DURATION))); + ui->reset_lockout_spinbox->setValue(spinbox_timespan_units(passwd_settings_obj, replace_attribute(ATTRIBUTE_MS_DS_LOCKOUT_OBSERVATION_WINDOW))); + ui->min_age_spinbox->setValue(spinbox_timespan_units(passwd_settings_obj, replace_attribute(ATTRIBUTE_MS_DS_MIN_PASSWORD_AGE))); + ui->max_age_spinbox->setValue(spinbox_timespan_units(passwd_settings_obj, replace_attribute(ATTRIBUTE_MS_DS_MAX_PASSWORD_AGE))); ui->complexity_req_checkbox->setChecked(passwd_settings_obj.get_bool(ATTRIBUTE_MS_DS_PASSWORD_COMPLEXITY_ENABLED)); ui->store_passwd_checkbox->setChecked(passwd_settings_obj.get_bool(ATTRIBUTE_MS_DS_PASSWORD_REVERSIBLE_ENCRYPTION_ENABLED)); @@ -100,30 +110,33 @@ QHash> PSOEditWidget::pso_settings_values() { QHash> settings; - settings[ATTRIBUTE_CN] = {ui->name_edit->text().trimmed().toUtf8()}; + settings[replace_attribute(ATTRIBUTE_CN)] = {ui->name_edit->text().trimmed().toUtf8()}; - settings[ATTRIBUTE_MS_DS_PASSWORD_SETTINGS_PRECEDENCE] = {QByteArray::number(ui->precedence_spinbox->value())}; - settings[ATTRIBUTE_MS_DS_MIN_PASSWORD_LENGTH] = {QByteArray::number(ui->min_passwd_len_spinbox->value())}; - settings[ATTRIBUTE_MS_DS_PASSWORD_HISTORY_LENGTH] = {QByteArray::number(ui->history_length_spinbox->value())}; - settings[ATTRIBUTE_MS_DS_LOCKOUT_THRESHOLD] = {QByteArray::number(ui->logon_attempts_spinbox->value())}; + settings[replace_attribute(ATTRIBUTE_MS_DS_PASSWORD_SETTINGS_PRECEDENCE)] = {QByteArray::number(ui->precedence_spinbox->value())}; + settings[replace_attribute(ATTRIBUTE_MS_DS_MIN_PASSWORD_LENGTH)] = {QByteArray::number(ui->min_passwd_len_spinbox->value())}; + settings[replace_attribute(ATTRIBUTE_MS_DS_PASSWORD_HISTORY_LENGTH)] = {QByteArray::number(ui->history_length_spinbox->value())}; + settings[replace_attribute(ATTRIBUTE_MS_DS_LOCKOUT_THRESHOLD)] = {QByteArray::number(ui->logon_attempts_spinbox->value())}; - settings[ATTRIBUTE_MS_DS_LOCKOUT_DURATION] = { + settings[replace_attribute(ATTRIBUTE_MS_DS_LOCKOUT_DURATION)] = { QByteArray::number(-duration_cast( - minutes(ui->lockout_duration_spinbox->value())).count() * MILLIS_TO_100_NANOS) - }; - settings[ATTRIBUTE_MS_DS_LOCKOUT_OBSERVATION_WINDOW] = { + minutes(ui->lockout_duration_spinbox->value())) + .count() * + MILLIS_TO_100_NANOS)}; + settings[replace_attribute(ATTRIBUTE_MS_DS_LOCKOUT_OBSERVATION_WINDOW)] = { QByteArray::number(-duration_cast( minutes(ui->reset_lockout_spinbox->value())).count() * MILLIS_TO_100_NANOS) }; - settings[ATTRIBUTE_MS_DS_MIN_PASSWORD_AGE] = { + settings[replace_attribute(ATTRIBUTE_MS_DS_MIN_PASSWORD_AGE)] = { QByteArray::number(-duration_cast( - hours(24 * ui->min_age_spinbox->value())).count() * MILLIS_TO_100_NANOS) - }; - settings[ATTRIBUTE_MS_DS_MAX_PASSWORD_AGE] = { + hours(24 * ui->min_age_spinbox->value())) + .count() * + MILLIS_TO_100_NANOS)}; + settings[replace_attribute(ATTRIBUTE_MS_DS_MAX_PASSWORD_AGE)] = { QByteArray::number(-duration_cast( - hours(24 * ui->max_age_spinbox->value())).count() * MILLIS_TO_100_NANOS) - }; + hours(24 * ui->max_age_spinbox->value())) + .count() * + MILLIS_TO_100_NANOS)}; settings[ATTRIBUTE_MS_DS_PASSWORD_COMPLEXITY_ENABLED] = { QString(ui->complexity_req_checkbox->isChecked() ? LDAP_BOOL_TRUE : @@ -173,14 +186,14 @@ bool PSOEditWidget::settings_are_default() { const QStringList excluded_attrs = { ATTRIBUTE_CN, ATTRIBUTE_MS_DS_PASSWORD_SETTINGS_PRECEDENCE, - ATTRIBUTE_APPLIES_TO - }; - for (const QString &attr : default_setting_values.keys()) { + ATTRIBUTE_APPLIES_TO}; + auto defaults = global_password_settings().get_attributes_data(); + for (const QString &attr : defaults.keys()) { if (excluded_attrs.contains(attr)) { continue; } - if (default_setting_values[attr] != current_values[attr]) { + if (defaults[pso_attributes_to_global_attributes[attr]] != current_values[attr]) { return false; } } @@ -277,3 +290,10 @@ int PSOEditWidget::spinbox_timespan_units(const AdObject &obj, const QString &at return 0; } + +QString PSOEditWidget::replace_attribute(QString attribute_name) { + return is_global ? (pso_attributes_to_global_attributes.contains(attribute_name) ? + pso_attributes_to_global_attributes[attribute_name] : + QString()) : + attribute_name; +}; diff --git a/src/admc/results_widgets/pso_results_widget/pso_edit_widget.h b/src/admc/results_widgets/pso_results_widget/pso_edit_widget.h index f9fbb962a..7331d9bde 100644 --- a/src/admc/results_widgets/pso_results_widget/pso_edit_widget.h +++ b/src/admc/results_widgets/pso_results_widget/pso_edit_widget.h @@ -22,6 +22,8 @@ #include +#include "ad_defines.h" + namespace Ui { class PSOEditWidget; } @@ -60,11 +62,21 @@ class PSOEditWidget final : public QWidget { private: Ui::PSOEditWidget *ui; QStringList dn_applied_list; - QHash> default_setting_values; + bool is_global; + QHash pso_attributes_to_global_attributes = { + {ATTRIBUTE_MS_DS_MIN_PASSWORD_LENGTH, ATTRIBUTE_MIN_PWD_LENGTH}, + {ATTRIBUTE_MS_DS_PASSWORD_HISTORY_LENGTH, ATTRIBUTE_PWD_HISTORY_LENGTH}, + {ATTRIBUTE_MS_DS_LOCKOUT_THRESHOLD, ATTRIBUTE_LOCKOUT_THRESHOLD}, + {ATTRIBUTE_MS_DS_LOCKOUT_DURATION, ATTRIBUTE_LOCKOUT_DURATION}, + {ATTRIBUTE_MS_DS_LOCKOUT_OBSERVATION_WINDOW, ATTRIBUTE_LOCKOUT_OBSERVATION_WINDOW}, + {ATTRIBUTE_MS_DS_MIN_PASSWORD_AGE, ATTRIBUTE_MIN_PWD_AGE}, + {ATTRIBUTE_MS_DS_MAX_PASSWORD_AGE, ATTRIBUTE_MAX_PWD_AGE}}; void on_add(); void on_remove(); + QString replace_attribute(QString attribute_name); + /*! * Returns appropriate timespan unit value depending on given attribute. * It is used to fill password timespan setting checkboxes. From 6ec0aca090d11b413941b45207cfe215f96e0e55 Mon Sep 17 00:00:00 2001 From: Kozyrev Yuri Date: Wed, 24 Jun 2026 18:16:30 +0400 Subject: [PATCH 09/27] Utilize new pso update logic --- src/admc/console_impls/password_settings_impl.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/admc/console_impls/password_settings_impl.cpp b/src/admc/console_impls/password_settings_impl.cpp index 8ecdef1fd..01acff9d1 100644 --- a/src/admc/console_impls/password_settings_impl.cpp +++ b/src/admc/console_impls/password_settings_impl.cpp @@ -41,7 +41,9 @@ PasswordSettingsImpl::PasswordSettingsImpl(ConsoleWidget *console_arg) : ConsoleImpl(console_arg) { - set_results_widget(new PSOResultsWidget(console_arg)); + auto result_widget(new PSOResultsWidget(console_arg)); + result_widget->update(global_password_settings()); + set_results_widget(result_widget); create_pso_action = new QAction(tr("Create password settings object"), this); From 7febcec2eea4ab2c9f1de950a06248ab8a23b7a5 Mon Sep 17 00:00:00 2001 From: Kozyrev Yuri Date: Wed, 24 Jun 2026 18:37:07 +0400 Subject: [PATCH 10/27] Refactor update defaults in PSOEditWidget --- .../pso_results_widget/pso_edit_widget.cpp | 17 +---------------- 1 file changed, 1 insertion(+), 16 deletions(-) diff --git a/src/admc/results_widgets/pso_results_widget/pso_edit_widget.cpp b/src/admc/results_widgets/pso_results_widget/pso_edit_widget.cpp index 54f897adf..ca84ecb41 100644 --- a/src/admc/results_widgets/pso_results_widget/pso_edit_widget.cpp +++ b/src/admc/results_widgets/pso_results_widget/pso_edit_widget.cpp @@ -202,18 +202,7 @@ bool PSOEditWidget::settings_are_default() { } void PSOEditWidget::update_defaults() { - AdInterface ad; - if (!ad.is_connected()) { - return; - } - AdObject result = ad.search_object(g_adconfig->domain_dn(), {ATTRIBUTE_PWD_PROPERTIES, - ATTRIBUTE_PWD_HISTORY_LENGTH, - ATTRIBUTE_MIN_PWD_LENGTH, - ATTRIBUTE_MIN_PWD_AGE, - ATTRIBUTE_MAX_PWD_AGE, - ATTRIBUTE_LOCKOUT_DURATION, - ATTRIBUTE_LOCKOUT_THRESHOLD, - ATTRIBUTE_LOCKOUT_OBSERVATION_WINDOW}); + const AdObject &result = global_password_settings(); ui->min_passwd_len_spinbox->setValue(result.get_int(ATTRIBUTE_MIN_PWD_LENGTH)); ui->history_length_spinbox->setValue(result.get_int(ATTRIBUTE_PWD_HISTORY_LENGTH)); @@ -226,10 +215,6 @@ void PSOEditWidget::update_defaults() { ui->complexity_req_checkbox->setChecked(result.get_int(ATTRIBUTE_PWD_PROPERTIES) & SAM_MASK_DOMAIN_PASSWORD_COMPLEX); ui->store_passwd_checkbox->setChecked(result.get_int(ATTRIBUTE_PWD_PROPERTIES) & SAM_MASK_DOMAIN_PASSWORD_STORE_CLEARTEXT); - - ui->applied_list_widget->clear(); - - ui->name_edit->setReadOnly(true); } void PSOEditWidget::on_add() { From de92cf0a1878ed8e719c3fa15da7a8e1b0fa0b8e Mon Sep 17 00:00:00 2001 From: Kozyrev Yuri Date: Wed, 24 Jun 2026 23:33:23 +0400 Subject: [PATCH 11/27] Add boolean attributes support in global password settings --- .../pso_results_widget/pso_edit_widget.cpp | 31 +++++++++++++------ 1 file changed, 21 insertions(+), 10 deletions(-) diff --git a/src/admc/results_widgets/pso_results_widget/pso_edit_widget.cpp b/src/admc/results_widgets/pso_results_widget/pso_edit_widget.cpp index ca84ecb41..8bdeb01dc 100644 --- a/src/admc/results_widgets/pso_results_widget/pso_edit_widget.cpp +++ b/src/admc/results_widgets/pso_results_widget/pso_edit_widget.cpp @@ -77,8 +77,14 @@ void PSOEditWidget::update(const AdObject &passwd_settings_obj) { ui->min_age_spinbox->setValue(spinbox_timespan_units(passwd_settings_obj, replace_attribute(ATTRIBUTE_MS_DS_MIN_PASSWORD_AGE))); ui->max_age_spinbox->setValue(spinbox_timespan_units(passwd_settings_obj, replace_attribute(ATTRIBUTE_MS_DS_MAX_PASSWORD_AGE))); - ui->complexity_req_checkbox->setChecked(passwd_settings_obj.get_bool(ATTRIBUTE_MS_DS_PASSWORD_COMPLEXITY_ENABLED)); - ui->store_passwd_checkbox->setChecked(passwd_settings_obj.get_bool(ATTRIBUTE_MS_DS_PASSWORD_REVERSIBLE_ENCRYPTION_ENABLED)); + if (is_global) { + int pwd_properties = passwd_settings_obj.get_int(ATTRIBUTE_PWD_PROPERTIES); + ui->complexity_req_checkbox->setChecked(pwd_properties & SAM_MASK_DOMAIN_PASSWORD_COMPLEX); + ui->store_passwd_checkbox->setChecked(pwd_properties & SAM_MASK_DOMAIN_PASSWORD_STORE_CLEARTEXT); + } else { + ui->complexity_req_checkbox->setChecked(passwd_settings_obj.get_bool(ATTRIBUTE_MS_DS_PASSWORD_COMPLEXITY_ENABLED)); + ui->store_passwd_checkbox->setChecked(passwd_settings_obj.get_bool(ATTRIBUTE_MS_DS_PASSWORD_REVERSIBLE_ENCRYPTION_ENABLED)); + } ui->applied_list_widget->clear(); dn_applied_list = passwd_settings_obj.get_strings(ATTRIBUTE_PSO_APPLIES_TO); @@ -138,14 +144,19 @@ QHash> PSOEditWidget::pso_settings_values() { .count() * MILLIS_TO_100_NANOS)}; - settings[ATTRIBUTE_MS_DS_PASSWORD_COMPLEXITY_ENABLED] = { - QString(ui->complexity_req_checkbox->isChecked() ? LDAP_BOOL_TRUE : - LDAP_BOOL_FALSE).toUtf8() - }; - settings[ATTRIBUTE_MS_DS_PASSWORD_REVERSIBLE_ENCRYPTION_ENABLED] = { - QString(ui->store_passwd_checkbox->isChecked() ? LDAP_BOOL_TRUE : - LDAP_BOOL_FALSE).toUtf8() - }; + if (is_global) { + settings[ATTRIBUTE_PWD_PROPERTIES] = {QByteArray::number(ui->complexity_req_checkbox->isChecked() * SAM_MASK_DOMAIN_PASSWORD_COMPLEX + + ui->store_passwd_checkbox->isChecked() * SAM_MASK_DOMAIN_PASSWORD_STORE_CLEARTEXT)}; + } else { + settings[ATTRIBUTE_MS_DS_PASSWORD_COMPLEXITY_ENABLED] = { + QString(ui->complexity_req_checkbox->isChecked() ? LDAP_BOOL_TRUE : + LDAP_BOOL_FALSE) + .toUtf8()}; + settings[ATTRIBUTE_MS_DS_PASSWORD_REVERSIBLE_ENCRYPTION_ENABLED] = { + QString(ui->store_passwd_checkbox->isChecked() ? LDAP_BOOL_TRUE : + LDAP_BOOL_FALSE) + .toUtf8()}; + } if (dn_applied_list.isEmpty()) { settings[ATTRIBUTE_PSO_APPLIES_TO] = QList(); From 9376a014227858168829489ab84557d26665f77d Mon Sep 17 00:00:00 2001 From: Kozyrev Yuri Date: Wed, 24 Jun 2026 23:53:48 +0400 Subject: [PATCH 12/27] Move part of PSOEditWidget update function in a new one --- .../pso_results_widget/pso_edit_widget.cpp | 96 ++++++++++--------- .../pso_results_widget/pso_edit_widget.h | 2 + 2 files changed, 52 insertions(+), 46 deletions(-) diff --git a/src/admc/results_widgets/pso_results_widget/pso_edit_widget.cpp b/src/admc/results_widgets/pso_results_widget/pso_edit_widget.cpp index 8bdeb01dc..764b35787 100644 --- a/src/admc/results_widgets/pso_results_widget/pso_edit_widget.cpp +++ b/src/admc/results_widgets/pso_results_widget/pso_edit_widget.cpp @@ -46,7 +46,7 @@ PSOEditWidget::PSOEditWidget(QWidget *parent) : connect(ui->add_button, &QPushButton::clicked, this, &PSOEditWidget::on_add); connect(ui->remove_button, &QPushButton::clicked, this, &PSOEditWidget::on_remove); - update_defaults(); + update_fields(global_password_settings()); } PSOEditWidget::~PSOEditWidget() { @@ -64,51 +64,7 @@ void PSOEditWidget::update(const AdObject &passwd_settings_obj) { ui->line->setVisible(!is_global); } - ui->name_edit->setText(passwd_settings_obj.get_string(replace_attribute(ATTRIBUTE_CN))); - ui->name_edit->setReadOnly(true); - - ui->precedence_spinbox->setValue(passwd_settings_obj.get_int(replace_attribute(ATTRIBUTE_MS_DS_PASSWORD_SETTINGS_PRECEDENCE))); - ui->min_passwd_len_spinbox->setValue(passwd_settings_obj.get_int(replace_attribute(ATTRIBUTE_MS_DS_MIN_PASSWORD_LENGTH))); - ui->history_length_spinbox->setValue(passwd_settings_obj.get_int(replace_attribute(ATTRIBUTE_MS_DS_PASSWORD_HISTORY_LENGTH))); - ui->logon_attempts_spinbox->setValue(passwd_settings_obj.get_int(replace_attribute(ATTRIBUTE_MS_DS_LOCKOUT_THRESHOLD))); - - ui->lockout_duration_spinbox->setValue(spinbox_timespan_units(passwd_settings_obj, replace_attribute(ATTRIBUTE_MS_DS_LOCKOUT_DURATION))); - ui->reset_lockout_spinbox->setValue(spinbox_timespan_units(passwd_settings_obj, replace_attribute(ATTRIBUTE_MS_DS_LOCKOUT_OBSERVATION_WINDOW))); - ui->min_age_spinbox->setValue(spinbox_timespan_units(passwd_settings_obj, replace_attribute(ATTRIBUTE_MS_DS_MIN_PASSWORD_AGE))); - ui->max_age_spinbox->setValue(spinbox_timespan_units(passwd_settings_obj, replace_attribute(ATTRIBUTE_MS_DS_MAX_PASSWORD_AGE))); - - if (is_global) { - int pwd_properties = passwd_settings_obj.get_int(ATTRIBUTE_PWD_PROPERTIES); - ui->complexity_req_checkbox->setChecked(pwd_properties & SAM_MASK_DOMAIN_PASSWORD_COMPLEX); - ui->store_passwd_checkbox->setChecked(pwd_properties & SAM_MASK_DOMAIN_PASSWORD_STORE_CLEARTEXT); - } else { - ui->complexity_req_checkbox->setChecked(passwd_settings_obj.get_bool(ATTRIBUTE_MS_DS_PASSWORD_COMPLEXITY_ENABLED)); - ui->store_passwd_checkbox->setChecked(passwd_settings_obj.get_bool(ATTRIBUTE_MS_DS_PASSWORD_REVERSIBLE_ENCRYPTION_ENABLED)); - } - - ui->applied_list_widget->clear(); - dn_applied_list = passwd_settings_obj.get_strings(ATTRIBUTE_PSO_APPLIES_TO); - - if (dn_applied_list.isEmpty()) { - ui->remove_button->setDisabled(true); - return; - } - - AdInterface ad; - if (!ad.is_connected()) { - return; - } - - for (const QString &dn : dn_applied_list) { - AdObject applied_object = ad.search_object(dn, {ATTRIBUTE_OBJECT_CATEGORY}); - if (applied_object.is_empty()) { - continue; - } - QListWidgetItem *item = new QListWidgetItem(g_icon_manager->object_icon(applied_object), - dn_get_name(dn), - ui->applied_list_widget); - item->setData(AppliedItemRole_DN, dn); - } + update_fields(passwd_settings_obj); } QHash> PSOEditWidget::pso_settings_values() { @@ -293,3 +249,51 @@ QString PSOEditWidget::replace_attribute(QString attribute_name) { QString()) : attribute_name; }; + +void PSOEditWidget::update_fields(const AdObject &passwd_settings_obj) { + ui->name_edit->setText(passwd_settings_obj.get_string(replace_attribute(ATTRIBUTE_CN))); + ui->name_edit->setReadOnly(true); + + ui->precedence_spinbox->setValue(passwd_settings_obj.get_int(replace_attribute(ATTRIBUTE_MS_DS_PASSWORD_SETTINGS_PRECEDENCE))); + ui->min_passwd_len_spinbox->setValue(passwd_settings_obj.get_int(replace_attribute(ATTRIBUTE_MS_DS_MIN_PASSWORD_LENGTH))); + ui->history_length_spinbox->setValue(passwd_settings_obj.get_int(replace_attribute(ATTRIBUTE_MS_DS_PASSWORD_HISTORY_LENGTH))); + ui->logon_attempts_spinbox->setValue(passwd_settings_obj.get_int(replace_attribute(ATTRIBUTE_MS_DS_LOCKOUT_THRESHOLD))); + + ui->lockout_duration_spinbox->setValue(spinbox_timespan_units(passwd_settings_obj, replace_attribute(ATTRIBUTE_MS_DS_LOCKOUT_DURATION))); + ui->reset_lockout_spinbox->setValue(spinbox_timespan_units(passwd_settings_obj, replace_attribute(ATTRIBUTE_MS_DS_LOCKOUT_OBSERVATION_WINDOW))); + ui->min_age_spinbox->setValue(spinbox_timespan_units(passwd_settings_obj, replace_attribute(ATTRIBUTE_MS_DS_MIN_PASSWORD_AGE))); + ui->max_age_spinbox->setValue(spinbox_timespan_units(passwd_settings_obj, replace_attribute(ATTRIBUTE_MS_DS_MAX_PASSWORD_AGE))); + + if (is_global) { + int pwd_properties = passwd_settings_obj.get_int(ATTRIBUTE_PWD_PROPERTIES); + ui->complexity_req_checkbox->setChecked(pwd_properties & SAM_MASK_DOMAIN_PASSWORD_COMPLEX); + ui->store_passwd_checkbox->setChecked(pwd_properties & SAM_MASK_DOMAIN_PASSWORD_STORE_CLEARTEXT); + } else { + ui->complexity_req_checkbox->setChecked(passwd_settings_obj.get_bool(ATTRIBUTE_MS_DS_PASSWORD_COMPLEXITY_ENABLED)); + ui->store_passwd_checkbox->setChecked(passwd_settings_obj.get_bool(ATTRIBUTE_MS_DS_PASSWORD_REVERSIBLE_ENCRYPTION_ENABLED)); + } + + ui->applied_list_widget->clear(); + dn_applied_list = passwd_settings_obj.get_strings(ATTRIBUTE_PSO_APPLIES_TO); + + if (dn_applied_list.isEmpty()) { + ui->remove_button->setDisabled(true); + return; + } + + AdInterface ad; + if (!ad.is_connected()) { + return; + } + + for (const QString &dn : dn_applied_list) { + AdObject applied_object = ad.search_object(dn, {ATTRIBUTE_OBJECT_CATEGORY}); + if (applied_object.is_empty()) { + continue; + } + QListWidgetItem *item = new QListWidgetItem(g_icon_manager->object_icon(applied_object), + dn_get_name(dn), + ui->applied_list_widget); + item->setData(AppliedItemRole_DN, dn); + } +} diff --git a/src/admc/results_widgets/pso_results_widget/pso_edit_widget.h b/src/admc/results_widgets/pso_results_widget/pso_edit_widget.h index 7331d9bde..8bd320b24 100644 --- a/src/admc/results_widgets/pso_results_widget/pso_edit_widget.h +++ b/src/admc/results_widgets/pso_results_widget/pso_edit_widget.h @@ -75,6 +75,8 @@ class PSOEditWidget final : public QWidget { void on_add(); void on_remove(); + void update_fields(const AdObject &passwd_settings_obj); + QString replace_attribute(QString attribute_name); /*! From 833950a19735725254bb008340d04f061514bd96 Mon Sep 17 00:00:00 2001 From: Kozyrev Yuri Date: Wed, 24 Jun 2026 23:55:12 +0400 Subject: [PATCH 13/27] Remove update_default form PSOEditWidget --- .../pso_results_widget/pso_edit_widget.cpp | 16 ---------------- .../pso_results_widget/pso_edit_widget.h | 1 - 2 files changed, 17 deletions(-) diff --git a/src/admc/results_widgets/pso_results_widget/pso_edit_widget.cpp b/src/admc/results_widgets/pso_results_widget/pso_edit_widget.cpp index 764b35787..2763e0ed2 100644 --- a/src/admc/results_widgets/pso_results_widget/pso_edit_widget.cpp +++ b/src/admc/results_widgets/pso_results_widget/pso_edit_widget.cpp @@ -168,22 +168,6 @@ bool PSOEditWidget::settings_are_default() { return true; } -void PSOEditWidget::update_defaults() { - const AdObject &result = global_password_settings(); - - ui->min_passwd_len_spinbox->setValue(result.get_int(ATTRIBUTE_MIN_PWD_LENGTH)); - ui->history_length_spinbox->setValue(result.get_int(ATTRIBUTE_PWD_HISTORY_LENGTH)); - ui->logon_attempts_spinbox->setValue(result.get_int(ATTRIBUTE_LOCKOUT_THRESHOLD)); - - ui->lockout_duration_spinbox->setValue(spinbox_timespan_units(result, ATTRIBUTE_LOCKOUT_DURATION)); - ui->reset_lockout_spinbox->setValue(spinbox_timespan_units(result, ATTRIBUTE_LOCKOUT_OBSERVATION_WINDOW)); - ui->min_age_spinbox->setValue(spinbox_timespan_units(result, ATTRIBUTE_MIN_PWD_AGE)); - ui->max_age_spinbox->setValue(spinbox_timespan_units(result, ATTRIBUTE_MAX_PWD_AGE)); - - ui->complexity_req_checkbox->setChecked(result.get_int(ATTRIBUTE_PWD_PROPERTIES) & SAM_MASK_DOMAIN_PASSWORD_COMPLEX); - ui->store_passwd_checkbox->setChecked(result.get_int(ATTRIBUTE_PWD_PROPERTIES) & SAM_MASK_DOMAIN_PASSWORD_STORE_CLEARTEXT); -} - void PSOEditWidget::on_add() { auto dialog = new SelectObjectDialog({CLASS_USER, CLASS_GROUP}, SelectObjectDialogMultiSelection_Yes, this); dialog->setWindowTitle(tr("Add applied users/group")); diff --git a/src/admc/results_widgets/pso_results_widget/pso_edit_widget.h b/src/admc/results_widgets/pso_results_widget/pso_edit_widget.h index 8bd320b24..448f41111 100644 --- a/src/admc/results_widgets/pso_results_widget/pso_edit_widget.h +++ b/src/admc/results_widgets/pso_results_widget/pso_edit_widget.h @@ -42,7 +42,6 @@ class PSOEditWidget final : public QWidget { ~PSOEditWidget(); void update(const AdObject &passwd_settings_obj); - void update_defaults(); void set_read_only(bool read_only); /*! From 91f3fe676a5a275412dbac7988af8f59075cb92b Mon Sep 17 00:00:00 2001 From: Kozyrev Yuri Date: Fri, 26 Jun 2026 02:09:54 +0400 Subject: [PATCH 14/27] Fix uneditable PSO name --- src/admc/results_widgets/pso_results_widget/pso_edit_widget.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/admc/results_widgets/pso_results_widget/pso_edit_widget.cpp b/src/admc/results_widgets/pso_results_widget/pso_edit_widget.cpp index 2763e0ed2..6201f8734 100644 --- a/src/admc/results_widgets/pso_results_widget/pso_edit_widget.cpp +++ b/src/admc/results_widgets/pso_results_widget/pso_edit_widget.cpp @@ -193,6 +193,7 @@ void PSOEditWidget::on_remove() { void PSOEditWidget::set_read_only(bool read_only) { QList spinbox_children = findChildren(QString(), Qt::FindChildrenRecursively); + ui->name_edit->setReadOnly(true); for (auto spinbox : spinbox_children) { spinbox->setReadOnly(read_only); } @@ -236,7 +237,6 @@ QString PSOEditWidget::replace_attribute(QString attribute_name) { void PSOEditWidget::update_fields(const AdObject &passwd_settings_obj) { ui->name_edit->setText(passwd_settings_obj.get_string(replace_attribute(ATTRIBUTE_CN))); - ui->name_edit->setReadOnly(true); ui->precedence_spinbox->setValue(passwd_settings_obj.get_int(replace_attribute(ATTRIBUTE_MS_DS_PASSWORD_SETTINGS_PRECEDENCE))); ui->min_passwd_len_spinbox->setValue(passwd_settings_obj.get_int(replace_attribute(ATTRIBUTE_MS_DS_MIN_PASSWORD_LENGTH))); From b54b8730a23fe0e2735c38caac038892a14ba53e Mon Sep 17 00:00:00 2001 From: Kozyrev Yuri Date: Tue, 30 Jun 2026 04:00:00 +0400 Subject: [PATCH 15/27] Fix zeroes in global PSO --- .../results_widgets/pso_results_widget/pso_edit_widget.cpp | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/admc/results_widgets/pso_results_widget/pso_edit_widget.cpp b/src/admc/results_widgets/pso_results_widget/pso_edit_widget.cpp index 6201f8734..821445b79 100644 --- a/src/admc/results_widgets/pso_results_widget/pso_edit_widget.cpp +++ b/src/admc/results_widgets/pso_results_widget/pso_edit_widget.cpp @@ -215,12 +215,15 @@ int PSOEditWidget::spinbox_timespan_units(const AdObject &obj, const QString &at qint64 hundred_nanos = -obj.get_value(attribute).toLongLong(); milliseconds msecs(hundred_nanos / MILLIS_TO_100_NANOS); - if (attribute == ATTRIBUTE_MS_DS_LOCKOUT_OBSERVATION_WINDOW || attribute == ATTRIBUTE_MS_DS_LOCKOUT_DURATION) { + if (attribute == + replace_attribute(ATTRIBUTE_MS_DS_LOCKOUT_OBSERVATION_WINDOW) || + attribute == replace_attribute(ATTRIBUTE_MS_DS_LOCKOUT_DURATION)) { int mins = duration_cast(msecs).count(); return mins; } - if (attribute == ATTRIBUTE_MS_DS_MIN_PASSWORD_AGE || attribute == ATTRIBUTE_MS_DS_MAX_PASSWORD_AGE) { + if (attribute == replace_attribute(ATTRIBUTE_MS_DS_MIN_PASSWORD_AGE) || + attribute == replace_attribute(ATTRIBUTE_MS_DS_MAX_PASSWORD_AGE)) { int days = duration_cast(msecs).count() / 24; return days; } From 744d369de7d91678076a91b4ff3354c4566072c3 Mon Sep 17 00:00:00 2001 From: Kozyrev Yuri Date: Tue, 30 Jun 2026 08:34:39 +0400 Subject: [PATCH 16/27] Add some Doxygen coments --- .../pso_results_widget/pso_edit_widget.cpp | 48 +++++++++++++++++++ .../pso_results_widget/pso_edit_widget.h | 5 ++ src/admc/utils.cpp | 4 ++ 3 files changed, 57 insertions(+) diff --git a/src/admc/results_widgets/pso_results_widget/pso_edit_widget.cpp b/src/admc/results_widgets/pso_results_widget/pso_edit_widget.cpp index 821445b79..a28cd4dd5 100644 --- a/src/admc/results_widgets/pso_results_widget/pso_edit_widget.cpp +++ b/src/admc/results_widgets/pso_results_widget/pso_edit_widget.cpp @@ -35,6 +35,9 @@ PSOEditWidget::PSOEditWidget(QWidget *parent) : QWidget(parent), ui(new Ui::PSOEditWidget) { +/** +* @brief Creates PSOEditWidget and initializes it with global values +*/ ui->setupUi(this); @@ -53,6 +56,15 @@ PSOEditWidget::~PSOEditWidget() { delete ui; } +/** +* @brief Sets fields of PSOEditWidget according to a given object +* @param passwd_settings_obj The object with new values +* @details Object can be a PSO or object representing global password settings +* those types of objects are distiguished based on objects' CN, if CN is present +* the object is considered to be a PSO, othervise it's treated as global +* settings object. If settings are global the following fields are not +* displayed: name, precedence and list of users policy is applied to +*/ void PSOEditWidget::update(const AdObject &passwd_settings_obj) { if (is_global != !passwd_settings_obj.contains(ATTRIBUTE_CN)) { is_global = !passwd_settings_obj.contains(ATTRIBUTE_CN); @@ -67,6 +79,10 @@ void PSOEditWidget::update(const AdObject &passwd_settings_obj) { update_fields(passwd_settings_obj); } +/** +* @brief Returns current values of widget fieds set by the user +* @return Hashmap of new values +*/ QHash> PSOEditWidget::pso_settings_values() { using namespace std::chrono; @@ -126,6 +142,10 @@ QHash> PSOEditWidget::pso_settings_values() { } QHash > PSOEditWidget::pso_settings_string_values() { +/** +* @brief Returns current values of widget fieds set by the user +* @return Hashmap of new values +*/ QHash> string_value_settings; QHash> byte_value_settings = pso_settings_values(); @@ -140,14 +160,24 @@ QHash > PSOEditWidget::pso_settings_string_values() { return string_value_settings; } +/** +* @brief Returns list of users to which the PSO applies +*/ QStringList PSOEditWidget::applied_dn_list() const { return dn_applied_list; } +/** +* @brief Returns the name edit object +*/ QLineEdit *PSOEditWidget::name_line_edit() { return ui->name_edit; } +/** +* @brief Compares current field values to their respective defaults (global +* settings) +*/ bool PSOEditWidget::settings_are_default() { auto current_values = pso_settings_values(); const QStringList excluded_attrs = { @@ -210,6 +240,13 @@ void PSOEditWidget::set_read_only(bool read_only) { } int PSOEditWidget::spinbox_timespan_units(const AdObject &obj, const QString &attribute) { +/** +* @brief Reads specified timespan attribute from given PSO object and converts +* it to apropriate units +* @param obj PSO object to read attribute from +* @param attribute The attribute to retrieve +* @return Attribute value in specified units +*/ using namespace std::chrono; qint64 hundred_nanos = -obj.get_value(attribute).toLongLong(); @@ -231,6 +268,13 @@ int PSOEditWidget::spinbox_timespan_units(const AdObject &obj, const QString &at return 0; } +/** +* @brief Deduces apropriate attribute name from PSO atribute name based on +* whether or not PSO is global. Basicaly method converts PSO attributes to +* global password settings attributes if needed +* @param attribute_name Initial attribute to be converted +* @return The apropriate attribute considering object type +*/ QString PSOEditWidget::replace_attribute(QString attribute_name) { return is_global ? (pso_attributes_to_global_attributes.contains(attribute_name) ? pso_attributes_to_global_attributes[attribute_name] : @@ -238,6 +282,10 @@ QString PSOEditWidget::replace_attribute(QString attribute_name) { attribute_name; }; +/** +* @brief Sets fields of PSOEditWidget according to a given object +* @param passwd_settings_obj The object with new values +*/ void PSOEditWidget::update_fields(const AdObject &passwd_settings_obj) { ui->name_edit->setText(passwd_settings_obj.get_string(replace_attribute(ATTRIBUTE_CN))); diff --git a/src/admc/results_widgets/pso_results_widget/pso_edit_widget.h b/src/admc/results_widgets/pso_results_widget/pso_edit_widget.h index 448f41111..61290e768 100644 --- a/src/admc/results_widgets/pso_results_widget/pso_edit_widget.h +++ b/src/admc/results_widgets/pso_results_widget/pso_edit_widget.h @@ -34,6 +34,11 @@ class QLineEdit; class QSpinBox; class QCheckBox; +/** + * @class PSOEditWidget + * @brief Used to represent password settings including global settings and PSOs + * in PSOResultsWidget and new PSO being created in CreatePSOWidget +*/ class PSOEditWidget final : public QWidget { Q_OBJECT diff --git a/src/admc/utils.cpp b/src/admc/utils.cpp index e38f4b152..f2be54ffd 100755 --- a/src/admc/utils.cpp +++ b/src/admc/utils.cpp @@ -496,6 +496,10 @@ bool creds_is_saved(const QString &username) { return saved; } +/** +* @brief Gets global password settings as AD object +* @return An AD object representing global password settings +*/ AdObject global_password_settings() { AdInterface ad; if (!ad.is_connected()) { From 2f26471c182e56e2e79a06d2135184622407080b Mon Sep 17 00:00:00 2001 From: Kozyrev Yuri Date: Tue, 30 Jun 2026 23:41:06 +0400 Subject: [PATCH 17/27] Remove unneeded code from console object impl --- .../console_impls/object_impl/object_impl.cpp | 28 ++++--------------- .../console_impls/object_impl/object_impl.h | 1 - 2 files changed, 5 insertions(+), 24 deletions(-) diff --git a/src/admc/console_impls/object_impl/object_impl.cpp b/src/admc/console_impls/object_impl/object_impl.cpp index 89dcadbea..9ade1417d 100755 --- a/src/admc/console_impls/object_impl/object_impl.cpp +++ b/src/admc/console_impls/object_impl/object_impl.cpp @@ -212,22 +212,11 @@ void ObjectImpl::activate(const QModelIndex &index) { } QList ObjectImpl::get_all_custom_actions() const { - QList out = { - new_action, - find_action, - add_to_group_action, - enable_action, - disable_action, - reset_password_action, - reset_account_action, - edit_upn_suffixes_action, - move_action, - create_pso_action, - create_subnet_action, - create_site_action, - create_site_link_action, - create_site_link_bridge_action - }; + QList out = {new_action, find_action, add_to_group_action, + enable_action, disable_action, reset_password_action, + reset_account_action, edit_upn_suffixes_action, move_action, + create_subnet_action, create_site_action, create_site_link_action, + create_site_link_bridge_action}; return out; } @@ -247,7 +236,6 @@ QSet ObjectImpl::get_custom_actions(const QModelIndex &index, const b const bool is_group = (object_class == CLASS_GROUP); const bool is_domain = (object_class == CLASS_DOMAIN); const bool is_computer = (object_class == CLASS_COMPUTER); - const bool is_pso_container = (object_class == CLASS_PSO_CONTAINER); const bool is_sites_container = (object_class == CLASS_SITES_CONTAINER); const bool is_subnet_container = (object_class == CLASS_SUBNET_CONTAINER); const bool is_site_links_container = (object_class == CLASS_INTER_SITE_TRANSPORT); @@ -285,10 +273,6 @@ QSet ObjectImpl::get_custom_actions(const QModelIndex &index, const b out.insert(edit_upn_suffixes_action); } - if (is_pso_container) { - out.insert(create_pso_action); - } - if (is_sites_container) { out.insert(create_site_action); } @@ -1003,14 +987,12 @@ void ObjectImpl::setup_actions() { new_menu->addAction(action); } - create_pso_action = new QAction(tr("Create password setting object"), this); create_subnet_action = new QAction(tr("Create subnet"), this); create_site_action = new QAction(tr("Create site"), this); create_site_link_action = new QAction(tr("Create site link"), this); create_site_link_bridge_action = new QAction(tr("Create site link bridge"), this); QHash all_create_action_map {standard_create_action_map}; - all_create_action_map[CLASS_PSO] = create_pso_action; all_create_action_map[CLASS_SUBNET] = create_subnet_action; all_create_action_map[CLASS_SITE] = create_site_action; all_create_action_map[CLASS_SITE_LINK] = create_site_link_action; diff --git a/src/admc/console_impls/object_impl/object_impl.h b/src/admc/console_impls/object_impl/object_impl.h index 647b2cbc7..f2435ef92 100644 --- a/src/admc/console_impls/object_impl/object_impl.h +++ b/src/admc/console_impls/object_impl/object_impl.h @@ -136,7 +136,6 @@ private slots: QAction *reset_account_action; QAction *edit_upn_suffixes_action; QAction *new_action; - QAction *create_pso_action; QAction *create_subnet_action; QAction *create_site_action; QAction *create_site_link_action; From 84f0131464e8428174b4d3f92984b2b5b8c610a4 Mon Sep 17 00:00:00 2001 From: Kozyrev Yuri Date: Wed, 1 Jul 2026 22:03:03 +0400 Subject: [PATCH 18/27] Fix PSO container actions --- .../console_impls/password_settings_impl.cpp | 24 +++++++------------ .../console_impls/password_settings_impl.h | 1 + 2 files changed, 10 insertions(+), 15 deletions(-) diff --git a/src/admc/console_impls/password_settings_impl.cpp b/src/admc/console_impls/password_settings_impl.cpp index 01acff9d1..a19d1ea6b 100644 --- a/src/admc/console_impls/password_settings_impl.cpp +++ b/src/admc/console_impls/password_settings_impl.cpp @@ -78,33 +78,22 @@ void PasswordSettingsImpl::refresh(const QList &index_list) { } QList PasswordSettingsImpl::get_all_custom_actions() const { - QList out; - - out.append(create_pso_action); - - return out; + return {create_pso_action}; } QSet PasswordSettingsImpl::get_custom_actions(const QModelIndex &index, const bool single_selection) const { UNUSED_ARG(index); UNUSED_ARG(single_selection); - QSet out; - - out.insert(create_pso_action); - - return out; + auto all_actions = get_all_custom_actions(); + return {all_actions.begin(), all_actions.end()}; } QSet PasswordSettingsImpl::get_standard_actions(const QModelIndex &index, const bool single_selection) const { UNUSED_ARG(index); UNUSED_ARG(single_selection); - QSet out; - - out.insert(StandardAction_Refresh); - - return out; + return {StandardAction_Refresh, StandardAction_Properties}; } QList PasswordSettingsImpl::column_labels() const { @@ -114,3 +103,8 @@ QList PasswordSettingsImpl::column_labels() const { QList PasswordSettingsImpl::default_columns() const { return ConsoleObjectTreeOperations::object_impl_default_columns(); } + +void PasswordSettingsImpl::properties(const QList &index_list) { + ConsoleObjectTreeOperations::console_object_properties( + {console}, index_list, ObjectRole_DN, {CLASS_PSO_CONTAINER}); +} diff --git a/src/admc/console_impls/password_settings_impl.h b/src/admc/console_impls/password_settings_impl.h index 192a3101a..2b4ae7de6 100644 --- a/src/admc/console_impls/password_settings_impl.h +++ b/src/admc/console_impls/password_settings_impl.h @@ -40,6 +40,7 @@ class PasswordSettingsImpl final : public ConsoleImpl { void fetch(const QModelIndex &index) override; void refresh(const QList &index_list) override; + void properties(const QList &index_list) override; QList get_all_custom_actions() const override; QSet get_custom_actions(const QModelIndex &index, const bool single_selection) const override; From 28029ddac89a7c665679b2bda77255fdef788435 Mon Sep 17 00:00:00 2001 From: Kozyrev Yuri Date: Wed, 1 Jul 2026 22:20:34 +0400 Subject: [PATCH 19/27] chore: Format PasswordSettingsImpl --- .../console_impls/password_settings_impl.cpp | 32 +++++++++++-------- .../console_impls/password_settings_impl.h | 6 ++-- 2 files changed, 23 insertions(+), 15 deletions(-) diff --git a/src/admc/console_impls/password_settings_impl.cpp b/src/admc/console_impls/password_settings_impl.cpp index a19d1ea6b..5c0c2430a 100644 --- a/src/admc/console_impls/password_settings_impl.cpp +++ b/src/admc/console_impls/password_settings_impl.cpp @@ -45,14 +45,15 @@ PasswordSettingsImpl::PasswordSettingsImpl(ConsoleWidget *console_arg) result_widget->update(global_password_settings()); set_results_widget(result_widget); - create_pso_action = new QAction(tr("Create password settings object"), this); - - connect( - create_pso_action, &QAction::triggered, - this, [this]() { - const QString parent_dn = get_selected_target_dn(console, ItemType_PasswordSettings, ObjectRole_DN); - ConsoleObjectTreeOperations::console_object_create({console}, CLASS_PSO, parent_dn); - }); + create_pso_action = + new QAction(tr("Create password settings object"), this); + + connect(create_pso_action, &QAction::triggered, this, [this]() { + const QString parent_dn = get_selected_target_dn( + console, ItemType_PasswordSettings, ObjectRole_DN); + ConsoleObjectTreeOperations::console_object_create( + {console}, CLASS_PSO, parent_dn); + }); } void PasswordSettingsImpl::fetch(const QModelIndex &index) { @@ -63,11 +64,14 @@ void PasswordSettingsImpl::fetch(const QModelIndex &index) { const QString base = g_adconfig->pso_container_dn(); const SearchScope scope = SearchScope_Children; - const QString filter = filter_CONDITION(Condition_Equals, ATTRIBUTE_OBJECT_CLASS, CLASS_PSO_CONTAINER); + const QString filter = filter_CONDITION( + Condition_Equals, ATTRIBUTE_OBJECT_CLASS, CLASS_PSO_CONTAINER); const QList attributes = QList(); - const QHash results = ad.search(base, scope, "", attributes); + const QHash results = + ad.search(base, scope, "", attributes); - ConsoleObjectTreeOperations::add_objects_to_console(console, results.values(), index); + ConsoleObjectTreeOperations::add_objects_to_console( + console, results.values(), index); } void PasswordSettingsImpl::refresh(const QList &index_list) { @@ -81,7 +85,8 @@ QList PasswordSettingsImpl::get_all_custom_actions() const { return {create_pso_action}; } -QSet PasswordSettingsImpl::get_custom_actions(const QModelIndex &index, const bool single_selection) const { +QSet PasswordSettingsImpl::get_custom_actions( + const QModelIndex &index, const bool single_selection) const { UNUSED_ARG(index); UNUSED_ARG(single_selection); @@ -89,7 +94,8 @@ QSet PasswordSettingsImpl::get_custom_actions(const QModelIndex &inde return {all_actions.begin(), all_actions.end()}; } -QSet PasswordSettingsImpl::get_standard_actions(const QModelIndex &index, const bool single_selection) const { +QSet PasswordSettingsImpl::get_standard_actions( + const QModelIndex &index, const bool single_selection) const { UNUSED_ARG(index); UNUSED_ARG(single_selection); diff --git a/src/admc/console_impls/password_settings_impl.h b/src/admc/console_impls/password_settings_impl.h index 2b4ae7de6..96fcdeca4 100644 --- a/src/admc/console_impls/password_settings_impl.h +++ b/src/admc/console_impls/password_settings_impl.h @@ -43,8 +43,10 @@ class PasswordSettingsImpl final : public ConsoleImpl { void properties(const QList &index_list) override; QList get_all_custom_actions() const override; - QSet get_custom_actions(const QModelIndex &index, const bool single_selection) const override; - QSet get_standard_actions(const QModelIndex &index, const bool single_selection) const override; + QSet get_custom_actions( + const QModelIndex &index, const bool single_selection) const override; + QSet get_standard_actions( + const QModelIndex &index, const bool single_selection) const override; QList column_labels() const override; QList default_columns() const override; From 08c0bd94e657f3d09d1c345febee5c615240d89e Mon Sep 17 00:00:00 2001 From: Kozyrev Yuri Date: Wed, 1 Jul 2026 22:27:38 +0400 Subject: [PATCH 20/27] style: Add title for global password settings --- src/admc/results_widgets/pso_results_widget/pso_edit_widget.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/admc/results_widgets/pso_results_widget/pso_edit_widget.cpp b/src/admc/results_widgets/pso_results_widget/pso_edit_widget.cpp index a28cd4dd5..0c398ccc3 100644 --- a/src/admc/results_widgets/pso_results_widget/pso_edit_widget.cpp +++ b/src/admc/results_widgets/pso_results_widget/pso_edit_widget.cpp @@ -74,6 +74,8 @@ void PSOEditWidget::update(const AdObject &passwd_settings_obj) { ui->precedence_spinbox->setVisible(!is_global); ui->groupBox->setVisible(!is_global); ui->line->setVisible(!is_global); + ui->groupBox_2->setTitle(is_global ? tr("Global password settings") : + tr("Password settings")); } update_fields(passwd_settings_obj); From c4cec425cfb440f48d6b711386f658f422f901a1 Mon Sep 17 00:00:00 2001 From: Kozyrev Yuri Date: Wed, 8 Jul 2026 22:40:19 +0400 Subject: [PATCH 21/27] fix(PSO): Disable create pso action when pso container is unavailable --- src/admc/console_impls/password_settings_impl.cpp | 10 ++++++++++ src/admc/console_impls/password_settings_impl.h | 3 +++ 2 files changed, 13 insertions(+) diff --git a/src/admc/console_impls/password_settings_impl.cpp b/src/admc/console_impls/password_settings_impl.cpp index 5c0c2430a..96f95e33d 100644 --- a/src/admc/console_impls/password_settings_impl.cpp +++ b/src/admc/console_impls/password_settings_impl.cpp @@ -69,6 +69,7 @@ void PasswordSettingsImpl::fetch(const QModelIndex &index) { const QList attributes = QList(); const QHash results = ad.search(base, scope, "", attributes); + is_PSO_container_available = !results.isEmpty(); ConsoleObjectTreeOperations::add_objects_to_console( console, results.values(), index); @@ -94,6 +95,15 @@ QSet PasswordSettingsImpl::get_custom_actions( return {all_actions.begin(), all_actions.end()}; } +QSet PasswordSettingsImpl::get_disabled_custom_actions( + const QModelIndex &index, const bool single_selection) const { + UNUSED_ARG(index); + UNUSED_ARG(single_selection); + + return is_PSO_container_available ? QSet{} : + QSet{create_pso_action}; +} + QSet PasswordSettingsImpl::get_standard_actions( const QModelIndex &index, const bool single_selection) const { UNUSED_ARG(index); diff --git a/src/admc/console_impls/password_settings_impl.h b/src/admc/console_impls/password_settings_impl.h index 96fcdeca4..14c2e5efb 100644 --- a/src/admc/console_impls/password_settings_impl.h +++ b/src/admc/console_impls/password_settings_impl.h @@ -45,6 +45,8 @@ class PasswordSettingsImpl final : public ConsoleImpl { QList get_all_custom_actions() const override; QSet get_custom_actions( const QModelIndex &index, const bool single_selection) const override; + QSet get_disabled_custom_actions( + const QModelIndex &index, const bool single_selection) const override; QSet get_standard_actions( const QModelIndex &index, const bool single_selection) const override; @@ -53,6 +55,7 @@ class PasswordSettingsImpl final : public ConsoleImpl { private: QAction *create_pso_action; + bool is_PSO_container_available = false; }; #endif /* PASSWORD_SETTINGS_IMPL_H */ From a6249fd1c97763807333c8dfbf36c8eebb785288 Mon Sep 17 00:00:00 2001 From: Kozyrev Yuri Date: Tue, 14 Jul 2026 01:31:39 +0400 Subject: [PATCH 22/27] refactor: Remove some unneeded vars in password settings impl --- src/admc/console_impls/password_settings_impl.cpp | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/src/admc/console_impls/password_settings_impl.cpp b/src/admc/console_impls/password_settings_impl.cpp index 96f95e33d..7c98a1d09 100644 --- a/src/admc/console_impls/password_settings_impl.cpp +++ b/src/admc/console_impls/password_settings_impl.cpp @@ -62,14 +62,13 @@ void PasswordSettingsImpl::fetch(const QModelIndex &index) { return; } - const QString base = g_adconfig->pso_container_dn(); - const SearchScope scope = SearchScope_Children; - const QString filter = filter_CONDITION( - Condition_Equals, ATTRIBUTE_OBJECT_CLASS, CLASS_PSO_CONTAINER); - const QList attributes = QList(); - const QHash results = - ad.search(base, scope, "", attributes); + QHash results = ad.search( + g_adconfig->pso_container_dn(), SearchScope_All, "", QStringList()); is_PSO_container_available = !results.isEmpty(); + results.removeIf([](QHash::iterator object) { + return object.value().get_string(ATTRIBUTE_OBJECT_CLASS) == + CLASS_PSO_CONTAINER; + }); ConsoleObjectTreeOperations::add_objects_to_console( console, results.values(), index); From eaaa2d34de1fe1fa109477fe65dc88f963ea748b Mon Sep 17 00:00:00 2001 From: Kozyrev Yuri Date: Tue, 14 Jul 2026 02:28:01 +0400 Subject: [PATCH 23/27] refactor: Make result widget and view protected in console impls --- src/admc/console_widget/console_impl.h | 1 - 1 file changed, 1 deletion(-) diff --git a/src/admc/console_widget/console_impl.h b/src/admc/console_widget/console_impl.h index d4f32c335..9faaa4532 100644 --- a/src/admc/console_widget/console_impl.h +++ b/src/admc/console_widget/console_impl.h @@ -141,7 +141,6 @@ class ConsoleImpl : public QObject { void set_results_view(ResultsView *view); void set_results_widget(QWidget *widget); -private: ResultsView *results_view; QWidget *results_widget; }; From 09c18bf63aa4e2095a6ef1fcd7db9c9473291d93 Mon Sep 17 00:00:00 2001 From: Kozyrev Yuri Date: Tue, 14 Jul 2026 02:42:00 +0400 Subject: [PATCH 24/27] fix: Fix refresh of pso console impl --- src/admc/console_impls/password_settings_impl.cpp | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/admc/console_impls/password_settings_impl.cpp b/src/admc/console_impls/password_settings_impl.cpp index 7c98a1d09..28dd2b3bf 100644 --- a/src/admc/console_impls/password_settings_impl.cpp +++ b/src/admc/console_impls/password_settings_impl.cpp @@ -41,9 +41,7 @@ PasswordSettingsImpl::PasswordSettingsImpl(ConsoleWidget *console_arg) : ConsoleImpl(console_arg) { - auto result_widget(new PSOResultsWidget(console_arg)); - result_widget->update(global_password_settings()); - set_results_widget(result_widget); + results_widget = new PSOResultsWidget(console_arg); create_pso_action = new QAction(tr("Create password settings object"), this); @@ -62,6 +60,10 @@ void PasswordSettingsImpl::fetch(const QModelIndex &index) { return; } + //TODO:(kozyrevid) refactor this cast and other like it + dynamic_cast(results_widget) + ->update(global_password_settings()); + QHash results = ad.search( g_adconfig->pso_container_dn(), SearchScope_All, "", QStringList()); is_PSO_container_available = !results.isEmpty(); From 182d244822a54f83b97d96155c5d99f972a35dc9 Mon Sep 17 00:00:00 2001 From: Kozyrev Yuri Date: Tue, 14 Jul 2026 02:55:53 +0400 Subject: [PATCH 25/27] style: Remove extra var --- .../console_impls/object_impl/console_object_operations.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/admc/console_impls/object_impl/console_object_operations.cpp b/src/admc/console_impls/object_impl/console_object_operations.cpp index ffa688324..e36761551 100644 --- a/src/admc/console_impls/object_impl/console_object_operations.cpp +++ b/src/admc/console_impls/object_impl/console_object_operations.cpp @@ -624,11 +624,10 @@ void ConsoleObjectTreeOperations::console_tree_add_password_settings(ConsoleWidg g_status->add_message(err, StatusType_Info); return; } + console->set_item_sort_index(password_settings_root->index(), 3); console_object_item_data_load(password_settings_root, search_results.values()[0]); - const int pso_container_sort_idx = 3; - console->set_item_sort_index(password_settings_root->index(), pso_container_sort_idx); } QString ConsoleObjectTreeOperations::console_object_count_string(ConsoleWidget *console, const QModelIndex &index) { From ddabca7227df1b8d1a47590641f23140de00924d Mon Sep 17 00:00:00 2001 From: Kozyrev Yuri Date: Tue, 14 Jul 2026 02:57:41 +0400 Subject: [PATCH 26/27] fix: Fix order of pso impl when pso container is not available --- .../object_impl/console_object_operations.cpp | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/src/admc/console_impls/object_impl/console_object_operations.cpp b/src/admc/console_impls/object_impl/console_object_operations.cpp index e36761551..4ed90b9d3 100644 --- a/src/admc/console_impls/object_impl/console_object_operations.cpp +++ b/src/admc/console_impls/object_impl/console_object_operations.cpp @@ -620,14 +620,10 @@ void ConsoleObjectTreeOperations::console_tree_add_password_settings(ConsoleWidg const QString filter = filter_CONDITION(Condition_Equals, ATTRIBUTE_OBJECT_CLASS, CLASS_PSO_CONTAINER); auto search_results = ad.search(g_adconfig->domain_dn(), SearchScope_All, filter, {}); const QString err = QObject::tr("Password settings container is not available"); - if (search_results.isEmpty() || search_results.values()[0].is_empty()) { - g_status->add_message(err, StatusType_Info); - return; + if (!search_results.isEmpty() && !search_results.values()[0].is_empty()) { + console_object_item_data_load(password_settings_root, search_results.values()[0]); } console->set_item_sort_index(password_settings_root->index(), 3); - - console_object_item_data_load(password_settings_root, search_results.values()[0]); - } QString ConsoleObjectTreeOperations::console_object_count_string(ConsoleWidget *console, const QModelIndex &index) { From 29f2002edf122a353885c6ca98ce2b7125a4f1af Mon Sep 17 00:00:00 2001 From: Kozyrev Yuri Date: Sun, 2 Aug 2026 21:23:31 +0400 Subject: [PATCH 27/27] chore(translations): Update translations for PSO container --- src/admc/admc_en.ts | 2885 +++++++++++++++++++++++++++++++------------ src/admc/admc_ru.ts | 1386 +++++++++------------ 2 files changed, 2676 insertions(+), 1595 deletions(-) diff --git a/src/admc/admc_en.ts b/src/admc/admc_en.ts index 3afdd52bf..55e141c79 100644 --- a/src/admc/admc_en.ts +++ b/src/admc/admc_en.ts @@ -15,8 +15,9 @@ - Copyright (C) 2022 BaseALT Ltd. - + Copyright (C) 2025 BaseALT Ltd. + Copyright (C) 2022 BaseALT Ltd. + @@ -105,50 +106,60 @@ - + Account disabled - + User cannot change password - + User must change password on next logon - + Don't expire password - + Store password using reversible encryption - + Smartcard is required for interactive logon - + Account is sensitive and cannot be delegated - + Use Kerberos DES encryption types for this account - + Don't require Kerberos pre-authentication + + + Password settings: + + + + + PLACEHOLDER + + AddressMultiTab @@ -214,12 +225,12 @@ AllPoliciesFolderImpl - + Create policy - + Name @@ -227,23 +238,23 @@ AttributeDialog - + View - + Edit - + %1 Multi-Valued %2 This is a dialog title for attribute editors. Example: "Edit Multi-Valued String" - + Attribute: %1 @@ -261,7 +272,17 @@ - + + Loads optional attribute values + + + + + Load optional attributes + + + + Filter @@ -269,20 +290,25 @@ AttributesTabEdit - + Name - + Value - + Type + + + Copy + + AttributesTabFilterMenu @@ -348,7 +374,7 @@ - + Failed to open changelog file. @@ -403,33 +429,93 @@ - Host: - + Domain: + - - No hosts found. - + + Get hosts from default kerberos credential cache. + + + + + Default: + + + + + Get custom domain hosts. + + + + + CUSTOM.DOMAIN.COM + - - Select: + + Find hosts + + + + + Restore defaults + + + + + No hosts found. - + Custom: - + + never + + + + + hard + + + + + demand + + + + + allow + + + + + try + + + + Error - - Select or enter a host. - + + Select a host. + + + + + You are connected to DC without PDC-Emulator role. Group policy editing is prohibited by the setting. + + + + + You are connected to DC without PDC-Emulator role. Group policy editing is available. + @@ -468,42 +554,42 @@ ConsoleWidget - + Copy - + Cut - + Rename - + Delete - + Paste - + Print - + Refresh - + Properties @@ -511,17 +597,17 @@ CreateComputerDialog - + Create Computer - + Name: - + Logon name (pre-Windows 2000): @@ -529,61 +615,66 @@ CreateContactDialog - + Create Contact - + First name: - + Last name: - + Initials: - + Full name: - + Display name: + + + Middle name: + + CreateGroupDialog - + Create Group - + Name: - + Group name (pre-Windows 2000): Logon name (pre-Windows 2000): - + Group scope: - + Group type: @@ -591,17 +682,17 @@ CreateOUDialog - + Create OU - + Name: - + Protect against deletion @@ -609,41 +700,69 @@ CreateObjectHelper - + Failed to create object %1 - + Object %1 was created + + CreatePSODialog + + + Password settings object creation + + + + + Protect against deletion + + + + + At least one password setting (except precedence) should not be default + + + + + Failed to create password settings object %1 + + + + + Password settings object %1 has been successfully created. + + + CreatePolicyDialog - + Create Policy Create GPO - + Name: - + New Group Policy Object - + Error - + Group Policy Object with this name already exists. @@ -651,28 +770,28 @@ CreateQueryFolderDialog - + Create Query Folder Create query folder - + Name: - + Description: - + New folder - + New Folder @@ -680,7 +799,7 @@ CreateQueryItemDialog - + Create Query @@ -688,287 +807,579 @@ CreateSharedFolderDialog - + Create Shared Folder - + Name: - + Network path: - CreateUserDialog + CreateSiteDialog - - Create User - + + Create site + - - First name: - + + Name: + - - Last name: - + + Select the site link object for this site. + - - Full name: - + + Chain name + - - Initials: - + + Transport + + + + CreateSitesLinkDialog - - Logon name: - + + Create sites link + - - Logon name (pre-Windows 2000): - + + Name: + - - Password: - + + PLACEHOLDER + - - Confirm password: - + + Site link object must link two or more sites + - - Show password - + + Create site link + - - Account options: - + + Link bridge object must link two or more site links + - - User must change password on next logon - + + Create site link bridge + - - User cannot change password - + + Site link object must link at least two sites + - - Don't expire password - + + Link bridge object must link at least two site links + - - Account disabled - + + Error + - - Create %1 - + + Failed to create site link object %1 + - - - CustomizeColumnsDialog - - Customize Columns - + + Failed to create site link bridge object %1 + - - - DelegationTab - - Do not trust for delegation - + + Site link object %1 has been successfully created. + - - Trust for delegation to any service using Kerberos - + + Site link bridge object %1 has been successfully created. + - EditQueryFolderDialog - - - Edit Query Folder - Edit query folder - - + CreateSubnetDialog - - Name: - + + Create subnet + - - Description: - + + Enter the prefix in network prefix notation (address/prefix length). Both IPv4 and IPv6 subnet prefixes are supported. + - - - EditQueryItemDialog - - Edit Query - + + IPv4 example: 172.16.0.0/16 + - - - EditQueryItemWidget - - Recursive search - + + IPv6 example: 2001:db8:abcd:0001::/64 + - - Name: - + + Prefix: + - - Description: - + + Active Directory prefix name: + - - Search in: - + + Site object: + - - Filter: - + + Failed to create subnet object %1 + - - Edit filter - + + Subnet object %1 has been successfully created. + - ErrorLogDialog + CreateUserDialog - - Errors Occured + + Create User - - - ErrorTab - - Failed to load object information. Check your connection. + + First name: - - - ExpiryWidget - - Never + + Last name: - - End of: + + Full name: - - - FSMODialog - - Operations Masters + + Initials: - - Domain DNS + + Logon name: - - Forest DNS + + Logon name (pre-Windows 2000): - - PDC Emulation + + Password: - - Schema + + Confirm password: - - Domain Naming + + Show password - - Infrastructure - + + Middle Name: + - - Rid Allocation + + Account options: - - - FSMOTab - - Current master: + + User must change password on next logon - - Change to: + + User cannot change password - - Change + + Don't expire password - - Error + + Account disabled - - This machine is already a master for this role. Switch to a different machine in Connection Options to change master. + + Create %1 - FilterDialog + CreationDeletionPermissionsWidget + + + Create + + + + + + objects + + + + + Delete + + + + + Undefined + + + + + CustomizeColumnsDialog + + + Customize Columns + + + + + DelegationPermissionsWidget + + + Name + + + + + Assigned + + + + + DelegationTab + + + Do not trust for delegation + + + + + Trust for delegation to any service using Kerberos + + + + + DomainInfoImpl + + + Edit FSMO roles + + + + + Open connection options + + + + + Name + + + + + Host not found + + + + + Active directory managment center [ + + + + + Connected to host + + + + + DomainInfoResultsWidget + + + Form + Form + + + + Domain about + + + + + + + + + + PLACEHOLDER + + + + + Domain functionality level: + + + + + Sites count: + + + + + Forest functionality level: + + + + + Domain schema version: + + + + + Domain controllers count: + + + + + Domain controller version: + + + + + Undefined + + + + + EditQueryFolderDialog + + + Edit Query Folder + Edit query folder + + + + + Name: + + + + + Description: + + + + + EditQueryItemDialog + + + Edit Query + + + + + EditQueryItemWidget + + + Recursive search + + + + + Name: + + + + + Description: + + + + + Search in: + + + + + Filter: + + + + + Edit filter + + + + + ErrorLogDialog + + + Errors Occured + + + + + ErrorTab + + + Failed to load object information. Check your connection. + + + + + ExpiryWidget + + + Never + + + + + End of: + + + + + FSMODialog + + + Operations Masters + + + + + Edit policies only under PDC Emulator connection + + + + + Warning! If unchecking this option, group policy edition will be allowed on domain controllers that do not have the PDC-Emulator role. + + + + + Domain DNS + + + + + Forest DNS + + + + + PDC Emulation + + + + + Schema + + + + + Domain Naming + + + + + Infrastructure + + + + + Rid Allocation + + + + + FSMOTab + + + Current master: + + + + + Change to: + + + + + Change + + + + + Error + + + + + This machine is already a master for this role. Switch to a different machine in Connection Options to change master. + + + + + FilterDialog Edit Filter @@ -1065,17 +1476,17 @@ FindObjectDialog - + Find Objects - + &Action - + &View @@ -1083,105 +1494,105 @@ FindPolicyDialog - + Dialog - + Search item: - + Condition: - + Value: - + Add - + Filters: - + Remove - - + + Clear - + Find - + Stop - + &Action - + &View - - + + Name - - + + GUID - + &Icons - + &List - + &Detail - + &Customize Columns - + &Description Bar - + Find results @@ -1202,57 +1613,57 @@ FindWidget - + Find - + Stop - + Clear - + Search in: - + &Icons Icons - + &List List - + &Detail Detail - + &Customize Columns Customize Columns - + &Description Bar Description Bar - + Find results @@ -1260,36 +1671,92 @@ FoundPolicyImpl - + Add link... - + Edit... + + FsmoTableWidget + + + Form + Form + + + + FSMO roles + + + + + FSMO role + + + + + Host + + + + + Role capture + + + + + + Capture + + + + + + + Captured + + + + + FSMO capture + + + + + Take over the role + + + + + Failed to capture role + + + GeneralComputerTab - + Logon name (pre-Windows 2000): - + DNS Host Name: - + Description: Description - + Location: @@ -1297,33 +1764,33 @@ GeneralGroupTab - + Group name (pre-Windows 2000): Logon name (pre-Windows 2000): - + Description: - + E-mail: - + Notes: - + Group Scope: - + Group Type: @@ -1331,32 +1798,32 @@ GeneralOUTab - + Description: - + Street Address: - + City: - + State/Province: - + ZIP/Postal Code: - + Country: @@ -1382,7 +1849,7 @@ GeneralOtherTab - + Description: @@ -1390,27 +1857,27 @@ GeneralPolicyTab - + Created: - + Modified: - + User version: - + Computer version: - + Unique ID: @@ -1426,16 +1893,39 @@ GeneralSharedFolderTab - + Description: - + Keywords + + GeneralSiteTab + + + Form + Form + + + + PLACEHOLDER + + + + + Description: + + + + + Subnets: + + + GeneralUserMultiTab @@ -1482,73 +1972,341 @@ GeneralUserTab - + Description: - + First Name: - + Last Name: - + Display Name: - + Initials: - - E-mail Address: - + + E-mail Address: + + + + + Office Location: + + + + + Telephone Number: + + + + + Web Page Address: + + + + + + Other... + + + + + Middle Name: + + + + + GroupPolicyTab + + + Inherited Policies + + + + + Block policy inheritance + + + + + HexNumberAttributeDialog + + + Dialog + + + + + 0x + + + + + InheritedPoliciesWidget + + + Priority + + + + + Name + + + + + Location + + + + + KrbAuthDialog + + + Kerberos authentication + + + + + Principal: + + + + + user@REALM + + + + + Password: + + + + + Show password + Show password + + + + Ticket available + + + + + Use system credentials + + + + + System cache + + + + + Sign in + + + + + PLACEHOLDER + + + + + Remember credentials + + + + + Enter your Kerberos principal + + + + + Account already in use + + + + + Enter the password + + + + + Authentication failed + + + + + Failed to find system credentials + + + + + LAPSTab + + + Password: + + + + + Password expires: + + + + + Reset expiry + + + + + LAPSV2Tab + + + Form + Form + + + + LAPS local admin account password: + LAPS local admin account password: + + + + Current LAPS password expiration: + Current LAPS password expiration: + + + + Local Administrator Password Solution + Local Administrator Password Solution + + + + Set new LAPS password expiration: + Set new LAPS password expiration: + + + + Show password + Show password + + + + Expire now + Expire now + + + + Copy password + Copy password + + + + LAPS local admin account name: + LAPS local admin account name: + + + + Failed to decode LAPS data. + Failed to decode LAPS data. + + + + Verify that you have the necessary permissions to access LAPS attributes! + Verify that you have the necessary permissions to access LAPS attributes! + + + + LinkedPoliciesWidget + + + Form + Form + + + + Remove link + + + + + Move up + + + + + Move down + + + + + Set all + + + + + Unset all + + + + + Edit... + + + + + Order + - - Office Location: - + + Name + - - Telephone Number: - + + Enforced + - - Web Page Address: - + + Disabled + - - - Other... - + + Organizational unit + - - - LAPSTab - - Password: - + + 's link orders have been succesfuly changed. + - - Password expires: - + + Not found + - - Reset expiry - + + The GPO for this link could not be found. It maybe have been recently created and is being replicated or it could have been deleted. + @@ -1592,86 +2350,33 @@ - - New value: - - - - - Add - - - - - Remove - - - - - Values: - - - - - LogonHoursDialog - - - Edit Logon Hours - - - - - Logon allowed: - - - - - Logon denied: - - - - - Sunday - - - - - Monday - - - - - Tuesday - - - - - Wednesday - + + On all computers + - - Thursday - + + Only on specified computers + - - Friday - + + In the 'Computer Name' field, enter the NetBIOS name or domain name (DNS name) of the computer. + - - Saturday - + + Computer name: + - - Local time + + Add - - UTC time + + Remove @@ -1684,314 +2389,371 @@ - + &Action Action - + &View View - + + &Theme + + + + &Preferences Preferences - + &Language Language - + &Help Help - + Tool Bar - + Message Log - + &Connection Options Connection Options - + &Quit Quit - + Ctrl+Q - + &Manual Manual - + Manual (Alt + 8) - + Alt+8 - + &Changelog Changelog - + &About ADMC About ADMC - + &Icons Icons - + &List List - + &Detail Detail - + &Console Tree Console Tree - + Description &Bar Description Bar - + C&ustomize Columns... Customize Columns... - + &Filter Objects... Filter Objects... - + &Advanced Features Advanced Features - + &Confirm Actions Confirm Actions - + &Put Last Name Before First Name Put Last Name Before First Name - + &Log Searches Log Searches - + &Timestamps in Message Log Timestamps in Message Log - + &Show Non-Container Objects in Console Tree Show Non-Container Objects in Console Tree - + Navigate Back (Alt + -) - + Navigate Forward (Alt + =) - + Refresh (Alt + 9) - + Alt+9 - + Show Login - + &Operations Masters Operations Masters - + Create user - + Create user (Alt + 7) - + Alt+7 - + Create group - + Create group (Alt + 6) - + Alt+6 - + Create organization unit - + Create organization unit (Alt + 5) - + Alt+5 - + + Load optional attribute values + + + + + Show middle name when creating + + + + + Change user + + + + + Use system credentials on start + + + + + Logout + + + + + Show login window on startup + + + + Navigate Up - + Alt+0 - + Navigate Back - + Alt+- - + Navigate Forward - + Alt+= - + Refresh - + + + Authentication required + + + + + Connected to host + + + + Info - + Restart the app to switch to the selected language. + + + You are connected to DC without PDC-Emulator role + + + + + Logged in successfully + + MainWindowConnectionError - Connection Error - + Connection error + Connection Error + - + Failed to connect to domain. - + Retry - + Connection Options - + Quit @@ -2105,32 +2867,32 @@ - + Add Member - + Add to Group - + Can't remove because this group is a primary group to selected user. - + Can't remove because selected group is a primary group to this user. - + Error - + Primary group: @@ -2156,115 +2918,150 @@ ObjectImpl - + User &User - + Computer &Computer - + OU &OU - + Group - + Shared Folder - + inetOrgPerson - + Contact - + Find... - + Move... - + Add to group... - + Enable - + Disable - + Reset password - + Reset account - + Edit UPN suffixes - + New - + + Create subnet + + + + + Create site + + + + + Create site link + + + + + Create site link bridge + + + + [Filtering enabled] - + Are you sure you want to delete this object? - + + Are you sure you want to delete these objects? + + + + + It contains other objects. + + + + + Containers to be deleted contain other objects. + + + + Add to Group - + Edit UPN Suffixes - + Are you sure you want to reset this account? - + Query may be out of date @@ -2307,11 +3104,6 @@ Protect against deletion: - - - Block inheritance: - - OctetAttributeDialog @@ -2336,27 +3128,27 @@ - + Error - + Input must be strings of 2 hexadecimal digits separated by spaces. Example: "0a 00 b5 ff" - + Input must be strings of 8 binary digits separated by spaces. Example: "01010010 01000010 01000010" - + Input must be strings of 3 decimal digits (0-255) separated by spaces. Example: "010 000 191" - + Input must be strings of 3 octal digits (0-377) separated by spaces.. Example: "070 343 301" @@ -2415,14 +3207,135 @@ OrganizationTabEdit - - Name - + + Name + + + + + Folder + + + + + PSOAppliedEdit + + + Default + + + + + Not found + + + + + (directly) + + + + + (via group membership) + + + + + PSOEditWidget + + + Form + Form + + + + Password Settings + + + + + Name: + + + + + Minimum password length: + + + + + Failed log on attempts allowed: + + + + + Minimum password age (days): + + + + + Account lockout duration (mins): + + + + + Enable complexity requirements + + + + + Store passwords using reversible encryption + + + + + Precedence: + + + + + Password history length: + + + + + Reset account lockout after (mins): + + + + + Maximum password age (days): + + + + + Apply to user/grop + + + + + Add... + + + + + Remove + + + + + Global password settings + + + + + Password settings + - - Folder - + + Add applied users/group + @@ -2467,62 +3380,108 @@ PasswordEdit - + Passwords don't match! - - - + + + Error - + Password cannot be empty. - + Password contains invalid characters + + PasswordSettingsImpl + + + Create password settings object + + + + + PermissionsWidget + + + Name + + + + + Allowed + + + + + Denied + + + + + There are no rights for this class of objects + + + PolicyImpl - + Add link... - + Edit... - + + Enforced + + + + + Disabled + + + + Incorrect permissions detected - + Permissions for this policy's GPT don't match the permissions for it's GPC object. Would you like to update GPT permissions? - + Are you sure you want to unlink this policy from the OU? Note that the actual policy object won't be deleted. - + + Are you sure you want to delete these policies and all their links? + + + + Are you sure you want to delete this policy and all of it's links? - + Add Link @@ -2530,37 +3489,37 @@ PolicyOUImpl - + Create OU - + Create a GPO and link to this OU - + Link existing GPO - + Find GPO - + Block inheritance - + All policies - + Name @@ -2568,75 +3527,40 @@ PolicyOUResultsWidget - - Remove link - - - - - Move up - - - - - Move down - - - - - Order - - - - - Name - - - - - Enforced - - - - - Disabled - - - - - Not found - + + Linked policies + - - The GPO for this link could not be found. It maybe have been recently created and is being replicated or it could have been deleted. - + + Inherited policies + PolicyResultsWidget - + Delete link - + Location - + Enforced - + Disabled - + Path @@ -2644,7 +3568,7 @@ PolicyRootImpl - + Name @@ -2688,89 +3612,95 @@ PropertiesDialog - - + + Properties - + %1 Properties "%1" Properties - + General - + Object - + Attributes - + Account - + Organization - + Telephones - + Profile - + + Group policy + + + + + LAPS - + Security - + Members - + Member of - + Managed by - + Operating System - + Delegation - + Address @@ -2778,33 +3708,33 @@ PropertiesMultiDialog - + Properties for Multiple Objects - - + + General - + Account - + Address - + Profile - + Organization @@ -2812,17 +3742,17 @@ PropertiesWarningDialog - + Warning - + You're switching to attributes tab, while another tab has unapplied changes. Choose to apply or discard those changes. - + You're switching from attributes tab, while it has unapplied changes. Choose to apply or discard those changes. @@ -2830,10 +3760,26 @@ QObject - + + Confirm action + + + Password settings container is not available + + + + + Sites container is not available + + + + + Sites + + Can't set "%1" when "%2" is set. @@ -2844,47 +3790,134 @@ Error + + + + Creation is not available + + + + + + ADMC is connected to DC without the PDC-Emulator role - group policy creation is prohibited by the setting. Connect to PDC-Emulator? + + + + + Deletion is not available + + + + + ADMC is connected to DC without the PDC-Emulator role - group policy deletion is prohibited by the setting. Connect to PDC-Emulator? + + + + + Edition is not available + + + + + ADMC is connected to DC without the PDC-Emulator role - group policy editing is prohibited by the setting. Connect to PDC-Emulator? + + + + + Failed to start GPUI. Check that it's installed. + + + + + Loading... + + + + + PDC-Emulator is connected + + + + + Could not open a theme: + + + + + (System) + + + + + Theme from settings not found. System theme is set. + + + + + Enabled + + + + + User configuration disabled + + + + + Computer configuration disabled + + + + + Disabled + + + + + Undefined GPO status + + QueryFolderImpl - + Query folder - + Query item - + New - + Edit - + &Import query... - - + + Error - + Can't cut and paste query folder into itself. - + There's already an item with this name. @@ -2892,30 +3925,54 @@ QueryItemImpl - + Edit... - + Export query... + + ReadWritePermissionsWidget + + + Write + + + + + + property + + + + + Read + + + + + Undefined + + + RenameGroupDialog - + Rename Group - + Name: - + Group name (pre-Windows 2000): Logon name (pre-Windows 2000): @@ -2924,12 +3981,12 @@ RenameObjectHelper - + Object %1 was renamed. - + Failed to rename object %1 @@ -2937,12 +3994,12 @@ RenameOtherDialog - + Rename Object - + Name: @@ -2950,12 +4007,12 @@ RenamePolicyDialog - + Rename Policy - + Name: @@ -2963,53 +4020,172 @@ RenameUserDialog - + Rename User - - Display name: - Display name - + + Display name: + Display name + + + + + First name: + + + + + Last name: + + + + + Full name: + + + + + Logon name: + + + + + Logon name (pre-Windows 2000): + + + + + ResultsWidgetBase + + + Form + Form + + + + Edit... + + + + + Cancel + + + + + Apply + + + + + SDDLViewDialog + + + SDDL view + + + + + Security descriptor (SDDL) + + + + + Show descriptor for current trustee + + + + + Failed to get SDDL formatted security descriptor + + + + + : Domain sid parse failed + + + + + : SDDL encode failed + + + + + SamNameEdit + + + Input field for Logon name (pre-Windows 2000) contains one or more of the following illegal characters: @ " [ ] : ; | = + * ? < > / \ , + + + + + Error + + + + + ScheduleHoursDialog + + + Edit Schedule Hours + + + + + UTC time + + + + + Local time + + + + + Logon allowed: + + + + + Logon denied: + - - First name: - + + Sunday + - - Last name: - + + Monday + - - Full name: - + + Tuesday + - - Logon name: - + + Wednesday + - - Logon name (pre-Windows 2000): - + + Thursday + - - - SamNameEdit - - Input field for Logon name (pre-Windows 2000) contains one or more of the following illegal characters: @ " [ ] : ; | = + * ? < > / \ , - + + Friday + - - Error - + + Saturday + @@ -3038,57 +4214,114 @@ SecurityTab - + + Users and Groups + + + + Add... - + Add well-known trustee... - + Remove - + Permissions - - - SecurityTabEdit - - Name - + + Applied to: + - - Allowed - + + Clear all + - - Denied - + + Common + + + + + Extended + + + + + Creation/deletion + + + + + Read/write + + + + + Task delegation + + + + + More... + + + + + Show descriptor in SDDL + + + + + Rollback to the previous descriptor + + + + + This object + + + + + This object and all child objects + + + + + All child objects + - + + Child objects: + + + + Add Trustee - + - + Error - + - + Failed to add some trustee's because they are already in the list. - + @@ -3116,21 +4349,26 @@ SelectContainerDialog - + Select Container Select a container + + + Failed to define suitable containers + + SelectObjectAdvancedDialog - + Select Object - + &View @@ -3138,85 +4376,85 @@ SelectObjectDialog - + Select Object - + Add - + Name - + Type - + Folder - + Remove - + Advanced - + Classes: - + Search in: - + Name: - + Selected objects: - - - - + + + + Error - + You must select at least one object. - + Failed to find any matches. - + Selected object is already in the list. - + This selection accepts only one object. Remove extra objects to proceed. @@ -3224,12 +4462,12 @@ SelectObjectMatchDialog - + Select Match - + There are multiple matches. Select one or more to add to the list. @@ -3237,7 +4475,7 @@ SelectPolicyDialog - + Select Policy @@ -3250,6 +4488,122 @@ + + SitesLinkCommonWidget + + + Form + Form + + + + Description: + + + + + + PLACEHOLDER + + + + + Add >> + + + + + << Remove + + + + + Sites not in this site link + + + + + Sites in this site link + + + + + Site links not included in this bridge + + + + + Site links included in this bridge + + + + + SitesLinkEdit + + + Site link object must link at least two sites + + + + + Link bridge object must link at least two site links + + + + + Error + + + + + SitesLinkGeneralTab + + + Form + Form + + + + PLACEHOLDER + + + + + SitesLinkPartWidget + + + Form + Form + + + + Cost: + + + + + Replicate every + + + + + min + + + + + Change the schedule ... + + + + + SitesLinkWidget + + + Form + Form + + StringListEdit @@ -3266,6 +4620,34 @@ + + SubnetEditWidget + + + Form + Form + + + + Sites: + + + + + Prefix: + + + + + Description: + + + + + Location: + + + TelephonesTab @@ -3308,6 +4690,19 @@ + + TimeSpanAttributeDialog + + + Dialog + + + + + d:hh:mm:ss or (never) / (none) + + + UnlockEdit @@ -3338,7 +4733,7 @@ country_widget - + None @@ -3346,7 +4741,7 @@ object_impl - + %n object(s) %n object @@ -3357,20 +4752,28 @@ object_impl.cpp - + Failed to connect to server while searching for objects. - + Could not load all objects. Increase object display limit in Filter Options or reduce number of objects by applying a filter. Filter Options is accessible from main window's menubar via the "View" menu. + + password_settings_impl + + + Password settings + + + policy_root_impl - + Group Policy Objects @@ -3378,7 +4781,7 @@ query - + Saved Queries @@ -3386,30 +4789,36 @@ query.cpp - + Name may not be empty - - - - + + + + + Error - + + Could not open a query file. + + + + There's already an item with this name. - + Names cannot contain "/". - + Query file is corrupted. @@ -3417,12 +4826,12 @@ query_folder.cpp - + Name - + Description @@ -3430,7 +4839,7 @@ query_folder_impl.cpp - + Are you sure you want to delete this item? @@ -3438,18 +4847,18 @@ query_item_impl.cpp - + Import Query - - + + JSON (*.json) - + Export Query @@ -3457,72 +4866,14 @@ utils.cpp - + Input field for Name contains one or more of the following illegal characters: # , + " \ < > ; = (leading space) (trailing space) (leading question mark) - + Error - - LAPSV2Tab - - - Form - Form - - - - LAPS local admin account password: - LAPS local admin account password: - - - - Current LAPS password expiration: - Current LAPS password expiration: - - - - Local Administrator Password Solution - Local Administrator Password Solution - - - - Set new LAPS password expiration: - Set new LAPS password expiration: - - - - Show password - Show password - - - - Expire now - Expire now - - - - Copy password - Copy password - - - - LAPS local admin account name: - LAPS local admin account name: - - - - Failed to decode LAPS data. - Failed to decode LAPS data. - - - - Verify that you have the necessary permissions to access LAPS attributes! - Verify that you have the necessary permissions to access LAPS attributes! - - diff --git a/src/admc/admc_ru.ts b/src/admc/admc_ru.ts index e654f5b15..a55453a8b 100644 --- a/src/admc/admc_ru.ts +++ b/src/admc/admc_ru.ts @@ -1,6 +1,6 @@ - + AboutDialog @@ -106,47 +106,47 @@ Параметры учётной записи: - + Account disabled Учётная запись отключена - + User cannot change password Пользователь не может изменить пароль - + User must change password on next logon Пользователь должен сменить пароль при следующем входе в систему - + Don't expire password Пароль не истекает - + Store password using reversible encryption Хранить пароль с использованием обратимого шифрования - + Smartcard is required for interactive logon Смарт-карта необходима для интерактивного входа в систему - + Account is sensitive and cannot be delegated Учётная запись является конфиденциальной и не может быть делегирована - + Use Kerberos DES encryption types for this account Использовать Kerberos DES тип шифрования для этой учетной записи - + Don't require Kerberos pre-authentication Не требовать предварительной аутентификации Kerberos @@ -225,41 +225,36 @@ AllPoliciesFolderImpl - + Create policy Создать политику - + Name Имя - - - PDC-Emulator is connected - Подключено к PDC-Emulator - AttributeDialog - + View Просмотреть - + Edit Изменить - + %1 Multi-Valued %2 This is a dialog title for attribute editors. Example: "Edit Multi-Valued String" %1 %2 (Многозначный) - + Attribute: %1 Атрибут: %1 @@ -295,17 +290,17 @@ AttributesTabEdit - + Name Имя - + Value Значение - + Type Тип @@ -314,19 +309,6 @@ Copy Копировать - - Edit - Изменить - - - View - Просмотреть - - - %1 Multi-Valued %2 - This is a dialog title for attribute editors. Example: "Edit Multi-Valued String" - %1 %2 (Многозначный) - AttributesTabFilterMenu @@ -392,7 +374,7 @@ Журнал изменений - + Failed to open changelog file. Не удалось открыть файл журнала изменений. @@ -483,19 +465,11 @@ Restore defaults По умолчанию - - Host: - Хост: - No hosts found. Хосты не найдены. - - Select: - Выбрать: - Custom: @@ -506,10 +480,6 @@ Error Ошибка - - Select or enter a host. - Выберите или введите хост. - never @@ -541,12 +511,12 @@ Выберите хост. - + You are connected to DC without PDC-Emulator role. Group policy editing is prohibited by the setting. Вы подключены к КД без роли PDC-Emulator - редактирование групповых политик запрещено настройкой. - + You are connected to DC without PDC-Emulator role. Group policy editing is available. Вы подключены к КД без роли PDC-Emulator - редактирование групповых политик доступно. @@ -587,42 +557,42 @@ ConsoleWidget - + Copy Копировать - + Cut Вырезать - + Rename Переименовать - + Delete Удалить - + Paste Вставить - + Print Напечатать - + Refresh Обновить - + Properties Свойства @@ -630,17 +600,17 @@ CreateComputerDialog - + Create Computer Создать рабочую станцию - + Name: Имя: - + Logon name (pre-Windows 2000): Имя для входа (до Windows 2000): @@ -648,32 +618,32 @@ CreateContactDialog - + Create Contact Создать контакт - + First name: Имя: - + Last name: Фамилия: - + Initials: Инициалы: - + Full name: Полное имя: - + Display name: Отображаемое имя: @@ -686,28 +656,28 @@ CreateGroupDialog - + Create Group Создать группу - + Name: Имя: - + Group name (pre-Windows 2000): Logon name (pre-Windows 2000): Имя группы (до Windows 2000): - + Group scope: Область группы: - + Group type: Тип группы: @@ -715,17 +685,17 @@ CreateOUDialog - + Create OU Создать подразделение - + Name: Имя: - + Protect against deletion Защитить от удаления @@ -733,12 +703,12 @@ CreateObjectHelper - + Failed to create object %1 Не удалось создать объект %1 - + Object %1 was created Объект %1 был создан @@ -774,28 +744,28 @@ CreatePolicyDialog - + Create Policy Create GPO Создать групповую политику - + Name: Имя: - + New Group Policy Object Новый объект групповой политики - + Error Ошибка - + Group Policy Object with this name already exists. Объект групповой политики с этим именем уже существует. @@ -803,28 +773,28 @@ CreateQueryFolderDialog - + Create Query Folder Create query folder Создать папку запроса - + Name: Имя: - + Description: Описание: - + New folder Новая папка - + New Folder Новая папка @@ -832,7 +802,7 @@ CreateQueryItemDialog - + Create Query Создать запрос @@ -840,17 +810,17 @@ CreateSharedFolderDialog - + Create Shared Folder Создать общую папку - + Name: Имя: - + Network path: Сетевой путь: @@ -921,37 +891,37 @@ Создать мост связи сайтов - + Site link object must link at least two sites Связь сайтов должна связывать минимум два сайта - + Link bridge object must link at least two site links Мост связей доложен связывать минимум две связи сайтов - + Error Ошибка - + Failed to create site link object %1 Не удалось создать объект связи сайтов %1 - + Failed to create site link bridge object %1 Не удалось создать объект мост связи сайтов %1 - + Site link object %1 has been successfully created. Объект связи сайтов %1 успешно создан. - + Site link bridge object %1 has been successfully created. Объект мост связей %1 сайтов успешно создан. @@ -1067,27 +1037,27 @@ Параметры учётной записи: - + User must change password on next logon Пользователь должен сменить пароль при следующем входе в систему - + User cannot change password Пользователь не может изменить пароль - + Don't expire password Пароль не истекает - + Account disabled Учётная запись отключена - + Create %1 Создать объект - %1 @@ -1126,42 +1096,6 @@ DelegationPermissionsWidget - - Common tasks delegation - Делегирование обычных задач - - - Create/delete child objects - Создание/удаление дочерних объектов - - - Read/write properties - Чтение/запись свойств - - - Create - Создание - - - objects - объектов - - - Delete - Удаление - - - Write - Запись - - - property - свойства - - - Read - Чтение - Name @@ -1189,32 +1123,32 @@ DomainInfoImpl - + Edit FSMO roles Изменить FSMO роли - + Open connection options Параметры подключения - + Name Имя - + Host not found Хост не найден - + Active directory managment center [ Центр управления Active Directory [ - + Connected to host Подключено к хосту @@ -1222,7 +1156,7 @@ DomainInfoResultsWidget - + Form @@ -1237,54 +1171,42 @@ Количество сайтов: - + Domain controllers count: Количество контроллеров домена: - + Domain schema version: Версия схемы домена: - + Domain functionality level: Режим работы домена: - + Domain controller version: Версия контроллера домена: - + Forest functionality level: Режим работы леса: - - - + + + + + + PLACEHOLDER - - Servers - Серверы - - - - FSMO roles - FSMO роли - - - - Failed to get objects count - Не удалось получить количество объектов - - - + Undefined Не определено @@ -1292,18 +1214,18 @@ EditQueryFolderDialog - + Edit Query Folder Edit query folder Изменить папку запроса - + Name: Имя: - + Description: Описание: @@ -1311,7 +1233,7 @@ EditQueryItemDialog - + Edit Query Изменить запрос @@ -1319,32 +1241,32 @@ EditQueryItemWidget - + Recursive search Рекурсивный поиск - + Name: Имя: - + Description: Описание: - + Search in: Искать в: - + Filter: Фильтр: - + Edit filter Изменить фильтр @@ -1381,7 +1303,7 @@ FSMODialog - + Operations Masters Мастера Операций @@ -1401,32 +1323,32 @@ DNS домена - + Forest DNS DNS леса - + PDC Emulation PDC эмуляция - + Schema Схема - + Domain Naming Имена домена - + Infrastructure Инфраструктура - + Rid Allocation RID распределение @@ -1434,27 +1356,27 @@ FSMOTab - + Current master: Текущий мастер: - + Change to: Изменить на: - + Change Изменить - + Error Ошибка - + This machine is already a master for this role. Switch to a different machine in Connection Options to change master. Эта машина уже является мастером этой роли. Переключитесь на другой компьютер в опциях подключения, чтобы изменить мастер. @@ -1557,17 +1479,17 @@ FindObjectDialog - + Find Objects Поиск объектов - + &Action &Действие - + &View &Вид @@ -1575,105 +1497,105 @@ FindPolicyDialog - + Dialog - + Search item: Элемент поиска: - + Condition: Состояние: - + Value: Значение: - + Add Добавить - + Filters: Фильтры: - + Remove Удалить - - + + Clear Очистить - + Find Поиск - + Stop Остановить - + &Action &Действие - + &View &Вид - - + + Name Имя - - + + GUID GUID - + &Icons &Значки - + &List &Список - + &Detail &Подробно - + &Customize Columns &Настроить колонки - + &Description Bar &Область описания - + Find results Результаты поиска @@ -1694,57 +1616,57 @@ FindWidget - + Find Поиск - + Stop Остановить - + Clear Очистить - + Search in: Искать в: - + &Icons Icons &Значки - + &List List &Список - + &Detail Detail &Подробно - + &Customize Columns Customize Columns &Настроить колонки - + &Description Bar Description Bar &Область описания - + Find results Результаты поиска @@ -1752,12 +1674,12 @@ FoundPolicyImpl - + Add link... Добавить связь... - + Edit... Изменить... @@ -1821,23 +1743,23 @@ GeneralComputerTab - + Logon name (pre-Windows 2000): Имя для входа (до Windows 2000): - + DNS Host Name: Имя узла DNS: - + Description: Description Описание: - + Location: Местонахождение: @@ -1845,33 +1767,33 @@ GeneralGroupTab - + Group name (pre-Windows 2000): Logon name (pre-Windows 2000): Имя группы (до Windows 2000): - + Description: Описание: - + E-mail: Электронная почта: - + Notes: Примечания: - + Group Scope: Область группы: - + Group Type: Тип группы: @@ -1879,32 +1801,32 @@ GeneralOUTab - + Description: Описание: - + Street Address: Адрес: - + City: Город: - + State/Province: Регион: - + ZIP/Postal Code: Почтовый индекс: - + Country: Страна: @@ -1930,7 +1852,7 @@ GeneralOtherTab - + Description: Описание: @@ -1938,27 +1860,27 @@ GeneralPolicyTab - + Created: Создан: - + Modified: Изменен: - + User version: Пользовательская версия: - + Computer version: Машинная версия: - + Unique ID: Уникальный ID: @@ -1974,12 +1896,12 @@ GeneralSharedFolderTab - + Description: Описание: - + Keywords Ключевые слова @@ -2053,53 +1975,53 @@ GeneralUserTab - + Description: Описание: - + First Name: Имя: - + Last Name: Фамилия: - + Display Name: Отображаемое имя: - + Initials: Инициалы: - + E-mail Address: Электронная почта: - + Office Location: Расположение офиса: - + Telephone Number: Номер телефона: - + Web Page Address: Адрес веб-страницы: - - + + Other... Другие... @@ -2117,33 +2039,41 @@ Наследуемые политики - + Block policy inheritance Заблокировать наследование политик + + HexNumberAttributeDialog + + + Dialog + + + + + 0x + + + InheritedPoliciesWidget - + Priority Приоритет - + Name Имя - + Location Местонахождение - - - Status - Статус - KrbAuthDialog @@ -2158,22 +2088,22 @@ Принципал: - + Password: Пароль: - + Show password Показать пароль - + user@REALM - + Ticket available Билет доступен @@ -2193,7 +2123,7 @@ Войти - + PLACEHOLDER @@ -2203,27 +2133,27 @@ Запомнить учётные данные - + Enter your Kerberos principal Введите принципал Kerberos - + Account already in use Аккаунт уже используется - + Enter the password Введите пароль - + Authentication failed Аутентификация не удалась - + Failed to find system credentials Не удалость найти системные учётные данные @@ -2307,22 +2237,22 @@ LinkedPoliciesWidget - + Form - + Remove link Удалить связь - + Move up Переместить вверх - + Move down Переместить вниз @@ -2347,37 +2277,37 @@ Порядок - + Name Имя - + Enforced Принудительно - + Disabled Отключено - + Organizational unit Порядки связей подразделения - + 's link orders have been succesfuly changed. были успешно изменены. - + Not found Не найдено - + The GPO for this link could not be found. It maybe have been recently created and is being replicated or it could have been deleted. Не удалось найти объект групповой политики для этой связи. Возможно, он был недавно создан и реплицируется или был удалён. @@ -2422,10 +2352,6 @@ Edit Logon Computers Компьютеры для входа в систему - - New value: - Новое значение: - On all computers @@ -2452,77 +2378,10 @@ Добавить - + Remove Удалить - - Values: - Значения: - - - - LogonHoursDialog - - - Edit Logon Hours - Изменить время входа - - - - Logon allowed: - Вход разрешен: - - - - Logon denied: - Вход запрещён: - - - - Sunday - Воскресенье - - - - Monday - Понедельник - - - - Tuesday - Вторник - - - - Wednesday - Среда - - - - Thursday - Четверг - - - - Friday - Пятница - - - - Saturday - Суббота - - - - Local time - Местное время - - - - UTC time - Время UTC - MainWindow @@ -2533,272 +2392,272 @@ &Файл - + &Action Action &Действие - + &View View &Вид - + &Theme Theme &Тема - + &Preferences Preferences &Настройки - + &Language Language &Язык - + &Help Help &Помощь - + Tool Bar Панель инструментов - + Message Log Журнал сообщений - + &Connection Options Connection Options &Параметры подключения - + &Quit Quit &Выйти - + Ctrl+Q Ctrl+Q - + &Manual Manual &Руководство - + Manual (Alt + 8) Помощь (Alt + 8) - + Alt+8 Alt+8 - + &Changelog Changelog &Журнал изменений - + &About ADMC About ADMC &О приложении ADMC - + &Icons Icons &Значки - + &List List &Список - + &Detail Detail &Подробно - + &Console Tree Console Tree &Дерево консоли - + Description &Bar Description Bar &Область описания - + C&ustomize Columns... Customize Columns... &Настроить колонки... - + &Filter Objects... Filter Objects... &Фильтровать объекты... - + &Advanced Features Advanced Features &Дополнительные возможности - + &Confirm Actions Confirm Actions &Подтверждать действия - + &Put Last Name Before First Name Put Last Name Before First Name &Ставить фамилию перед именем - + &Log Searches Log Searches &Вносить информацию о поиске в журнал сообщений - + &Timestamps in Message Log Timestamps in Message Log &Метки времени в журнале сообщений - + &Show Non-Container Objects in Console Tree Show Non-Container Objects in Console Tree П&оказывать неконтейнерные объекты в дереве консоли - + Navigate Back (Alt + -) Назад (Alt + -) - + Navigate Forward (Alt + =) Вперед (Alt + =) - + Refresh (Alt + 9) Обновить (Alt + 9) - + Alt+9 Alt+9 - + Show Login Показывать логин - + &Operations Masters Operations Masters &Мастера Операций - + Create user Создать пользователя - + Create user (Alt + 7) Создать пользователя (Alt + 7) - + Alt+7 Alt+7 - + Create group Создать группу - + Create group (Alt + 6) Создать группу (Alt + 6) - + Alt+6 Alt+6 - + Create organization unit Создать подразделение - + Create organization unit (Alt + 5) Создать подразделение (Alt + 5) - + Alt+5 Alt+5 - + Load optional attribute values Загружать значения необязательных атрибутов - + Show middle name when creating Показывать отчество при создании - + Change user Сменить пользователя - + Use system credentials on start Системные учётные данные при старте - + Logout Выход из аккаунта @@ -2813,62 +2672,63 @@ Наверх - + Alt+0 Alt+0 - + Navigate Back Назад - + Alt+- Alt+- - + Navigate Forward Вперед - + Alt+= Alt+= - + Refresh Обновить - + Info Информация - + Restart the app to switch to the selected language. Для переключения на выбранный язык необходимо перезапустить приложение. - + You are connected to DC without PDC-Emulator role Вы подключены к КД без роли PDC-Emulator - + Logged in successfully Вход выполнен - + + Authentication required Требуется аутентификация - + Connected to host Подключено к хосту @@ -2882,22 +2742,22 @@ Ошибка соединения - + Failed to connect to domain. Не удалось подключиться к домену. - + Retry Подключиться - + Connection Options Параметры подключения - + Quit Выйти @@ -3011,32 +2871,32 @@ Папка - + Add Member Добавить участника - + Add to Group Добавить в группу - + Can't remove because this group is a primary group to selected user. Не удалось удалить группу, так как она является основной для выбранного пользователя. - + Can't remove because selected group is a primary group to this user. Не удалось удалить выбранную группу, так как она является основной для этого пользователя. - + Error Ошибка - + Primary group: Основная группа: @@ -3062,155 +2922,150 @@ ObjectImpl - + User &User Пользователь - + Computer &Computer Компьютер - + OU &OU Подразделение - + Group Группа - + Shared Folder Общая папка - + inetOrgPerson inetOrgPerson - + Contact Контакт - + Find... Найти... - + Move... Переместить... - + Add to group... Добавить в группу... - + Enable Включить - + Disable Отключить - + Reset password Сбросить пароль - + Reset account Сбросить учётную запись - + Edit UPN suffixes Изменить суффиксы UPN - - Create password setting object - Создать объект парольных настроек - - - + Create subnet Создать подсеть - + Create site Создать сайт - + Create site link Создать связь сайтов - + Create site link bridge Создать мост связей сайтов - + New Создать - + [Filtering enabled] [Фильтр включён] - + Are you sure you want to delete this object? Удалить этот объект? - + Are you sure you want to delete these objects? Удалить эти объекты? - + It contains other objects. Он содержит другие объекты. - + Containers to be deleted contain other objects. Удаляемые контейнеры содержат другие объекты. - + Add to Group Добавить в группу - + Edit UPN Suffixes Изменить суффиксы UPN - + Are you sure you want to reset this account? Вы точно хотите сбросить эту учетную запись? - + Query may be out of date Запрос может быть устаревшим @@ -3253,11 +3108,6 @@ Protect against deletion: Защитить от удаления: - - - Block inheritance: - Блокировать наследование: - OctetAttributeDialog @@ -3282,27 +3132,27 @@ Восьмеричный - + Error Ошибка - + Input must be strings of 2 hexadecimal digits separated by spaces. Example: "0a 00 b5 ff" Ввод должен состоять из строки, содержащей две шестнадцатеричные цифры, разделённые пробелами. Пример: "0a 00 b5 ff" - + Input must be strings of 8 binary digits separated by spaces. Example: "01010010 01000010 01000010" Ввод должен состоять из строки, содержащей восемь двоичных цифр, разделённых пробелами. Пример: "01010010 01000010 01000010" - + Input must be strings of 3 decimal digits (0-255) separated by spaces. Example: "010 000 191" Ввод должен состоять из строки, содержащей три десятичные цифры (0–255), разделённые пробелами. Пример: "010 000 191" - + Input must be strings of 3 octal digits (0-377) separated by spaces.. Example: "070 343 301" Ввод должен состоять из строки, содержащей три восьмеричные цифры (0–377), разделённые пробелами. Пример: "070 343 301" @@ -3402,12 +3252,12 @@ - + Account lockout duration (mins): Длительность блокировки учетной записи (минуты): - + Name: Имя: @@ -3422,92 +3272,74 @@ Минимальная длина пароля: - + Enable complexity requirements Включить требования сложности - + Store passwords using reversible encryption Хранить пароли, используя обратимое шифрование - + Precedence: Приоритет: - + Apply to user/grop Применить к пользователю/группе - + Add... Добавить... - + Remove Удалить - + Minimum password age (days): Минимальный срок действия пароля (дни): - + Reset account lockout after (mins): Время до сброса блокировки (минуты): - - PSO precedence should be greater than 0 - Приоритет должен быть больше 0 - - - + Maximum password age (days): Максимальный срок действия пароля (дни): - + Password history length: Длина истории паролей: - + Failed log on attempts allowed: Разрешено неудачных попыток входа: - - Add applied users/group - Добавить применяемых пользователей/группы - - - - PSOResultsWidget - - - Form - + + Global password settings + Глобальные параметры паролей - - Edit... - Изменить... + + Password settings + Парольная политика - - Cancel - Отменить - - - - Apply - Применить + + Add applied users/group + Добавить применяемых пользователей/группы @@ -3552,28 +3384,36 @@ PasswordEdit - + Passwords don't match! Пароли не совпадают! - - - + + + Error Ошибка - + Password cannot be empty. Пароль не может быть пустым. - + Password contains invalid characters Пароль содержит недопустимые символы + + PasswordSettingsImpl + + + Create password settings object + Создать парольную политику (PSO) + + PermissionsWidget @@ -3592,7 +3432,7 @@ Запрещено - + There are no rights for this class of objects Нет прав для данного класса объектов @@ -3600,37 +3440,37 @@ PolicyImpl - + Add link... Добавить связь... - + Edit... Изменить... - + Enforced Принудительно - + Disabled Отключено - + Incorrect permissions detected Обнаружены неверные разрешения - + Permissions for this policy's GPT don't match the permissions for it's GPC object. Would you like to update GPT permissions? Разрешения шаблона групповой политики у данной политики не совпадают с разрешениями её объекта контейнера групповой политики. Обновить разрешения шаблона групповой политики? - + Are you sure you want to unlink this policy from the OU? Note that the actual policy object won't be deleted. Удалить связь между политикой и подразделением? Обратите внимание, что сама политика не будет удалена. @@ -3645,32 +3485,7 @@ Удалить эту политику и все её связи? - - Failed to delete group policy - Не удалось удалить групповую политику - - - - : this is a critical policy - : данная политика является критической - - - - Failed to delete the following group policies: - - Не удалось удалить следующие групповые политики: - - - - (critical policy) - (критическая политика) - - - Failed to delete some group policies - Не удалось удалить некоторые групповые политики - - - + Add Link Добавление связи @@ -3678,100 +3493,50 @@ PolicyOUImpl - + Create OU Создать подразделение - + Create a GPO and link to this OU Создать политику и связать с этим подразделением - + Link existing GPO Связать существующую политику - + Find GPO Найти объект групповой политики - + Block inheritance Блокировать наследование - + All policies Все политики - + Name Имя - - - PDC-Emulator is connected - Подключено к PDC-Emulator - PolicyOUResultsWidget - - Remove link - Удалить связь - - - - Move up - Переместить вверх - - - - Move down - Переместить вниз - - - - Order - Порядок - - - - Name - Имя - - - - Enforced - Принудительно - - - - Disabled - Отключено - - - - Not found - Не найдено - - - - The GPO for this link could not be found. It maybe have been recently created and is being replicated or it could have been deleted. - Не удалось найти объект групповой политики для этой связи. Возможно, он был недавно создан и реплицируется или был удалён. - - - + Linked policies Привязанные политики - + Inherited policies Inherited Наследуемые политики @@ -3780,43 +3545,35 @@ PolicyResultsWidget - + Delete link Удалить связь - + Location Местонахождение - + Enforced Принудительно - + Disabled Отключено - + Path Путь - - Incorrect permissions detected - Обнаружены неверные разрешения - - - Permissions for this policy's GPT don't match the permissions for it's GPC object. Would you like to update GPT permissions? - Разрешения шаблона групповой политики у данной политики не совпадают с разрешениями её объекта контейнера групповой политики. Обновить разрешения шаблона групповой политики? - PolicyRootImpl - + Name Имя @@ -3860,94 +3617,95 @@ PropertiesDialog - - + + Properties Свойства - + %1 Properties "%1" Properties %1 — свойства - + General Общее - + Object Объект - + Attributes Атрибуты - + Account Учётная запись - + Organization Организация - + Telephones Телефоны - + Profile Профиль - + Group policy Групповая политика - + + LAPS LAPS - + Security Безопасность - + Members Участники - + Member of Группы - + Managed by Руководство - + Operating System Операционная система - + Delegation Делегирование - + Address Адрес @@ -3955,33 +3713,33 @@ PropertiesMultiDialog - + Properties for Multiple Objects Свойства для нескольких объектов - - + + General Общее - + Account Учётная запись - + Address Адрес - + Profile Профиль - + Organization Организация @@ -3989,17 +3747,17 @@ PropertiesWarningDialog - + Warning Предупреждение - + You're switching to attributes tab, while another tab has unapplied changes. Choose to apply or discard those changes. При переключении на вкладку атрибутов на предыдущей вкладке не были применены изменения. Выберите, применить или отменить эти изменения. - + You're switching from attributes tab, while it has unapplied changes. Choose to apply or discard those changes. На вкладке атрибутов не были применены изменения. Выберите, применить или отменить эти изменения. @@ -4007,47 +3765,48 @@ QObject - + + Confirm action Подтверждение действия - - Failed to find password settings container - Не удалось найти контейнер парольных настроек + + Password settings container is not available + - - Fine-grained password policies - Детализированные политики паролей + + Sites container is not available + - + Sites Сайты - + Enabled Включено - + User configuration disabled Параметры конфигурации пользователя отключены - + Computer configuration disabled Параметры конфигурации компьютера отключены - + Disabled Отключено - + Undefined GPO status Статус не определён @@ -4062,65 +3821,64 @@ Ошибка + Failed to start GPUI. Check that it's installed. Не удалось запустить GPUI. Проверьте установлен ли он. - - + + Creation is not available Создание не доступно - - + + ADMC is connected to DC without the PDC-Emulator role - group policy creation is prohibited by the setting. Connect to PDC-Emulator? ADMC подключен к КД без роли PDC-Emulator - создание групповых политик запрещено настройкой. Подключиться к PDC-контроллеру? - + Edition is not available Редактирование не доступно - + ADMC is connected to DC without the PDC-Emulator role - group policy editing is prohibited by the setting. Connect to PDC-Emulator? ADMC подключен к КД без роли PDC-Emulator - редактирование групповых политик запрещено настройкой. Подключиться к PDC-контроллеру? - + PDC-Emulator is connected Подключено к PDC-Emulator - + Deletion is not available Удаление не доступно - + ADMC is connected to DC without the PDC-Emulator role - group policy deletion is prohibited by the setting. Connect to PDC-Emulator? ADMC подключен к КД без роли PDC-Emulator - удаление групповых политик запрещено настройкой. Подключиться к PDC-контроллеру? - Theme from settings not found. Fallback theme is set. - Тема, указанная в настройках, не найдена. Установлена тема по умолчанию. - - - + Theme from settings not found. System theme is set. Тема, указанная в настройках, не найдена. Установлена системная тема. - + (System) (Системная) + Loading... Загрузка... + Could not open a theme: Невозможно загрузить тему: @@ -4128,43 +3886,43 @@ QueryFolderImpl - + Query folder Папка запросов - + Query item Элемент запроса - + New Создать - + Edit Изменить - + &Import query... &Импортировать запрос... - - + + Error Ошибка - + Can't cut and paste query folder into itself. Невозможно вырезать и вставить папку запросов в саму себя. - + There's already an item with this name. Элемент с этим именем уже существует. @@ -4172,12 +3930,12 @@ QueryItemImpl - + Edit... Изменить... - + Export query... Экспортировать запрос... @@ -4209,17 +3967,17 @@ RenameGroupDialog - + Rename Group Переименовать группу - + Name: Имя: - + Group name (pre-Windows 2000): Logon name (pre-Windows 2000): Имя группы (до Windows 2000): @@ -4228,12 +3986,12 @@ RenameObjectHelper - + Object %1 was renamed. Объект %1 был переименован. - + Failed to rename object %1 Не удалось переименовать объект %1 @@ -4241,12 +3999,12 @@ RenameOtherDialog - + Rename Object Переименовать объект - + Name: Имя: @@ -4254,12 +4012,12 @@ RenamePolicyDialog - + Rename Policy Переименовать политику - + Name: Имя: @@ -4267,38 +4025,38 @@ RenameUserDialog - + Rename User Переименовать пользователя - + Display name: Display name Отображаемое имя: - + First name: Имя: - + Last name: Фамилия: - + Full name: Полное имя: - + Logon name: Имя для входа: - + Logon name (pre-Windows 2000): Имя для входа (до Windows 2000): @@ -4466,22 +4224,22 @@ Пользователи и группы - + Add... Добавить... - + Add well-known trustee... Добавить известное доверенное лицо... - + Remove Удалить - + Permissions Разрешения @@ -4501,22 +4259,22 @@ Обычные - + Extended Расширенные - + Creation/deletion Создание/удаление - + Read/write Чтение/запись - + Task delegation Делегирование задач @@ -4525,10 +4283,6 @@ More... Дополнительно... - - Delegation - Делегирование - Show descriptor in SDDL @@ -4545,65 +4299,32 @@ Этот объект - + This object and all child objects Этот объект и все дочерние объекты - + All child objects Все дочерние объекты - + Child objects: Дочерние объекты: - - Add Trustee - Добавить доверенное лицо - - - - Error - Ошибка - - - - Failed to add some trustee's because they are already in the list. - Не удалось добавить некоторых доверенных лиц, так как они уже имеются в списке. - - - - SecurityTabEdit - - - Name - Имя - - - - Allowed - Разрешено - - - - Denied - Запрещено - - - + Add Trustee Добавить доверенное лицо - + Error Ошибка - + Failed to add some trustee's because they are already in the list. Не удалось добавить некоторых доверенных лиц, так как они уже имеются в списке. @@ -4633,7 +4354,7 @@ SelectContainerDialog - + Select Container Select a container Выбор контейнера @@ -4647,12 +4368,12 @@ SelectObjectAdvancedDialog - + Select Object Выбор объекта - + &View &Вид @@ -4660,85 +4381,85 @@ SelectObjectDialog - + Select Object Выбор объекта - + Add Добавить - + Name Имя - + Type Тип - + Folder Папка - + Remove Удалить - + Advanced Продвинутый - + Classes: Классы: - + Search in: Искать в: - + Name: Имя: - + Selected objects: Выбранные объекты: - - - - + + + + Error Ошибка - + You must select at least one object. Требуется выбрать хотя бы один объект. - + Failed to find any matches. Совпадений не найдено. - + Selected object is already in the list. Выбранный объект уже имеется в списке. - + This selection accepts only one object. Remove extra objects to proceed. Здесь можно выбрать только один объект. Для продолжения уберите лишние объекты. @@ -4746,12 +4467,12 @@ SelectObjectMatchDialog - + Select Match Выбор совпадения - + There are multiple matches. Select one or more to add to the list. Было найдено несколько совпадений. Выберите одно или несколько совпадений для добавления в список. @@ -4759,7 +4480,7 @@ SelectPolicyDialog - + Select Policy Выбрать политику @@ -4801,22 +4522,22 @@ << Убрать - + Sites not in this site link Сайты не в этой связи - + Sites in this site link Сайты в этой связи - + Site links not included in this bridge Связи сайтов не в этом мосте - + Site links included in this bridge Связи сайтов в этом мосте @@ -4824,17 +4545,17 @@ SitesLinkEdit - + Site link object must link at least two sites Связь должна связывать минимум два сайта - + Link bridge object must link at least two site links Мост должен связывать минимум две связи сайтов - + Error Ошибка @@ -5017,7 +4738,7 @@ country_widget - + None Нет @@ -5025,7 +4746,7 @@ object_impl - + %n object(s) %n объект @@ -5037,20 +4758,28 @@ object_impl.cpp - + Failed to connect to server while searching for objects. Не удалось подключиться к серверу во время поиска объектов. - + Could not load all objects. Increase object display limit in Filter Options or reduce number of objects by applying a filter. Filter Options is accessible from main window's menubar via the "View" menu. Не удалось загрузить все объекты. Увеличьте лимит отображения объектов в опциях или уменьшите количество объектов, применив фильтр. Параметры фильтра доступны в меню «Вид». + + password_settings_impl + + + Password settings + Параметры паролей + + policy_root_impl - + Group Policy Objects Объекты групповой политики @@ -5058,7 +4787,7 @@ query - + Saved Queries Сохранённые запросы @@ -5066,35 +4795,36 @@ query.cpp - + Name may not be empty Имя не может быть пустым - - - - + + + + + Error Ошибка - + There's already an item with this name. Элемент с этим именем уже существует. - + Names cannot contain "/". Имена не могут содержать «/». - + Query file is corrupted. Файл запроса повреждён. - + Could not open a query file. Невозможно открыть файл запроса. @@ -5102,12 +4832,12 @@ query_folder.cpp - + Name Имя - + Description Описание @@ -5115,7 +4845,7 @@ query_folder_impl.cpp - + Are you sure you want to delete this item? Удалить этот элемент? @@ -5123,18 +4853,18 @@ query_item_impl.cpp - + Import Query Импортировать запрос - - + + JSON (*.json) JSON (*.json) - + Export Query Экспортировать запрос @@ -5142,12 +4872,12 @@ utils.cpp - + Input field for Name contains one or more of the following illegal characters: # , + " \ < > ; = (leading space) (trailing space) (leading question mark) Поле ввода "Имя" содержит один или несколько из следующих недопустимых символов: # , + " \ < > ; = (пробел в начале) (пробел в конце) (? в начале) - + Error Ошибка