From b5f3a834b2726799ff3aded1c0cb691fecfeb7c1 Mon Sep 17 00:00:00 2001 From: Semyon Knyazev Date: Tue, 11 Jun 2024 23:41:05 +0400 Subject: [PATCH] Complete PSOEditWidget Did UI edits loading, applied user/groups list widget loading, edit byte values getter and etc. --- .../pso_results_widget/pso_edit_widget.cpp | 250 +++++- .../pso_results_widget/pso_edit_widget.h | 61 +- .../pso_results_widget/pso_edit_widget.ui | 740 +++++++++++++----- 3 files changed, 870 insertions(+), 181 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 f04a0b6b..b387224d 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 @@ -1,14 +1,262 @@ +/* + * ADMC - AD Management Center + * + * Copyright (C) 2020-2024 BaseALT Ltd. + * + * 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 "pso_edit_widget.h" #include "ui_pso_edit_widget.h" #include "ad_interface.h" +#include "ad_object.h" +#include "ad_utils.h" +#include "select_dialogs/select_object_dialog.h" +#include "icon_manager/icon_manager.h" +#include "status.h" +#include "globals.h" + +#include + +#include PSOEditWidget::PSOEditWidget(QWidget *parent) : QWidget(parent), - ui(new Ui::PsoEditWidget) { + ui(new Ui::PSOEditWidget) { ui->setupUi(this); + + connect(ui->applied_list_widget, &QListWidget::itemSelectionChanged, this, + [this]() { + ui->remove_button->setDisabled(ui->applied_list_widget->count() == 0); + }); + + connect(ui->add_button, &QPushButton::clicked, this, &PSOEditWidget::on_add); + connect(ui->remove_button, &QPushButton::clicked, this, &PSOEditWidget::on_remove); + + update_defaults(); + default_setting_values = pso_settings_values(); } PSOEditWidget::~PSOEditWidget() { delete ui; } + +void PSOEditWidget::update(const AdObject &passwd_settings_obj) { + ui->name_edit->setText(passwd_settings_obj.get_string(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->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->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->get_object_icon(applied_object), + dn_get_name(dn), + ui->applied_list_widget); + item->setData(AppliedItemRole_DN, dn); + } +} + +QHash> PSOEditWidget::pso_settings_values() { + using namespace std::chrono; + + QHash> settings; + + settings[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[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] = { + QByteArray::number(-duration_cast( + minutes(ui->reset_lockout_spinbox->value())).count() * MILLIS_TO_100_NANOS) + }; + + settings[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] = { + QByteArray::number(-duration_cast( + 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 : + 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() + }; + + for (const QString &dn : dn_applied_list) { + settings[ATTRIBUTE_PSO_APPLIES_TO].append(dn.toUtf8()); + } + + return settings; +} + +QHash > PSOEditWidget::pso_settings_string_values() { + QHash> string_value_settings; + QHash> byte_value_settings = pso_settings_values(); + + for (const QString &attr : byte_value_settings.keys()) { + QList string_values; + for (const QByteArray &value : byte_value_settings[attr]) { + string_values.append(value); + } + string_value_settings[attr] = string_values; + } + + return string_value_settings; +} + +QStringList PSOEditWidget::applied_dn_list() const { + return dn_applied_list; +} + +QLineEdit *PSOEditWidget::name_line_edit() { + return ui->name_edit; +} + +bool PSOEditWidget::settings_are_default() { + auto current_values = pso_settings_values(); + const QStringList excluded_attrs = { + ATTRIBUTE_CN, + ATTRIBUTE_MS_DS_PASSWORD_SETTINGS_PRECEDENCE, + ATTRIBUTE_APPLIES_TO + }; + for (const QString &attr : default_setting_values.keys()) { + if (excluded_attrs.contains(attr)) { + continue; + } + + if (default_setting_values[attr] != current_values[attr]) { + return false; + } + } + + return true; +} + +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); + + ui->applied_list_widget->clear(); +} + +void PSOEditWidget::on_add() { + auto dialog = new SelectObjectDialog({CLASS_USER, CLASS_GROUP}, SelectObjectDialogMultiSelection_Yes, this); + dialog->setWindowTitle(tr("Add applied users/group")); + dialog->open(); + + connect(dialog, &SelectObjectDialog::accepted, this, [this, dialog]() { + for (auto selected_data : dialog->get_selected_advanced()) { + QListWidgetItem *item = new QListWidgetItem(g_icon_manager->get_object_icon(dn_get_name(selected_data.category)), + dn_get_name(selected_data.dn), + ui->applied_list_widget); + item->setData(AppliedItemRole_DN, selected_data.dn); + dn_applied_list.append(selected_data.dn); + } + }); +} + +void PSOEditWidget::on_remove() { + for (auto item : ui->applied_list_widget->selectedItems()) { + dn_applied_list.removeAll(item->data(AppliedItemRole_DN).toString()); + delete item; + } +} + +void PSOEditWidget::set_read_only(bool read_only) { + QList spinbox_children = findChildren(QString(), Qt::FindChildrenRecursively); + for (auto spinbox : spinbox_children) { + spinbox->setReadOnly(read_only); + } + + QList checkbox_children = findChildren(QString(), Qt::FindChildrenRecursively); + for (auto checkbox : checkbox_children) { + checkbox->setDisabled(read_only); + } + + ui->add_button->setDisabled(read_only); + // Only true because no items are selected after list widget enabling + ui->remove_button->setDisabled(true); + ui->applied_list_widget->setDisabled(read_only); +} + +int PSOEditWidget::spinbox_timespan_units(const AdObject &obj, const QString &attribute) { + using namespace std::chrono; + + 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) { + int mins = duration_cast(msecs).count(); + return mins; + } + + if (attribute == ATTRIBUTE_MS_DS_MIN_PASSWORD_AGE || attribute == ATTRIBUTE_MS_DS_MAX_PASSWORD_AGE) { + int days = duration_cast(msecs).count() / 24; + return days; + } + + return 0; +} 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 f019d8a7..e2f6dfc2 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 @@ -1,12 +1,37 @@ +/* + * ADMC - AD Management Center + * + * Copyright (C) 2020-2024 BaseALT Ltd. + * + * 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 PSO_EDIT_WIDGET_H #define PSO_EDIT_WIDGET_H #include namespace Ui { -class PsoEditWidget; +class PSOEditWidget; } +class AdObject; +class QListWidgetItem; +class QLineEdit; +class QSpinBox; +class QCheckBox; + class PSOEditWidget final : public QWidget { Q_OBJECT @@ -14,15 +39,41 @@ public: explicit PSOEditWidget(QWidget *parent = nullptr); ~PSOEditWidget(); - QString get_pso_dn() const; - QStringList get_applied_dn_list() const; + void update(const AdObject &passwd_settings_obj); + void update_defaults(); + void set_read_only(bool read_only); + + /*! + * Gets password setting attribute's values hash + */ + QHash> pso_settings_values(); + QHash> pso_settings_string_values(); + QStringList applied_dn_list() const; + QLineEdit *name_line_edit(); + + /*! + * Compares current settings with default (except name and precedence). + * Returns true if at least one is not default. + */ + bool settings_are_default(); private: - Ui::PsoEditWidget *ui; + Ui::PSOEditWidget *ui; + QStringList dn_applied_list; + QHash> default_setting_values; - void load_defaults(); void on_add(); void on_remove(); + + /*! + * Returns appropriate timespan unit value depending on given attribute. + * It is used to fill password timespan setting checkboxes. + */ + int spinbox_timespan_units(const AdObject &obj, const QString &attribute); + + enum AppliedItemRole { + AppliedItemRole_DN = Qt::UserRole + }; }; #endif // PSO_EDIT_WIDGET_H diff --git a/src/admc/results_widgets/pso_results_widget/pso_edit_widget.ui b/src/admc/results_widgets/pso_results_widget/pso_edit_widget.ui index 655b5ee1..95d9c051 100644 --- a/src/admc/results_widgets/pso_results_widget/pso_edit_widget.ui +++ b/src/admc/results_widgets/pso_results_widget/pso_edit_widget.ui @@ -1,95 +1,576 @@ - PsoEditWidget - + PSOEditWidget + + + true + 0 0 - 600 - 559 + 616 + 610 Form - - - - - + + + 2 + + + 2 + + + 2 + + + 2 + + + + + true - - 0 + + Password Settings + + + 4 + + + 4 + + + 4 + + + 4 + + + + + 4 + + + + + Name: + + + + + + + + 170 + 16777215 + + + + + + + 0 + + + false + + + + + + + Qt::Vertical + + + QSizePolicy::Minimum + + + + 20 + 5 + + + + + + + + Minimum password length: + + + + + + + + 40 + 0 + + + + + 60 + 16777215 + + + + false + + + QAbstractSpinBox::NoButtons + + + 99 + + + 0 + + + + + + + Qt::Vertical + + + QSizePolicy::Minimum + + + + 20 + 5 + + + + + + + + Failed log on attempts allowed: + + + + + + + + 40 + 0 + + + + + 60 + 16777215 + + + + false + + + QAbstractSpinBox::NoButtons + + + + + + + Qt::Vertical + + + QSizePolicy::Minimum + + + + 20 + 5 + + + + + + + + Minimum password age (days): + + + + + + + + 40 + 0 + + + + + 60 + 16777215 + + + + + + + false + + + QAbstractSpinBox::NoButtons + + + + + + 0 + + + + + + + Qt::Vertical + + + QSizePolicy::Minimum + + + + 20 + 5 + + + + + + + + Account lockout duration (mins): + + + + + + + + 40 + 0 + + + + + 60 + 16777215 + + + + false + + + QAbstractSpinBox::NoButtons + + + 0 + + + + + + + Qt::Vertical + + + QSizePolicy::Minimum + + + + 20 + 5 + + + + + + + + Qt::LeftToRight + + + Enable complexity requirements + + + true + + + false + + + + + + + Store passwords using reversible encryption + + + true + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + + 4 + + + QLayout::SetDefaultConstraint + + + + + Precedence: + + + + + + + + 40 + 0 + + + + + 60 + 16777215 + + + + + + + + + + + + + false + + + QAbstractSpinBox::NoButtons + + + + + + 1 + + + + + + + Qt::Vertical + + + QSizePolicy::Minimum + + + + 20 + 5 + + + + + + + + Password history length: + + + + + + + + 40 + 0 + + + + + 60 + 16777215 + + + + false + + + QAbstractSpinBox::NoButtons + + + 0 + + + + + + + Qt::Vertical + + + QSizePolicy::Minimum + + + + 20 + 5 + + + + + + + + Reset account lockout after (mins): + + + + + + + + 40 + 0 + + + + + 60 + 16777215 + + + + false + + + QAbstractSpinBox::NoButtons + + + + + + 0 + + + + + + + Qt::Vertical + + + QSizePolicy::Minimum + + + + 20 + 5 + + + + + + + + Maximum password age (days): + + + + + + + + 40 + 0 + + + + + 60 + 16777215 + + + + QAbstractSpinBox::NoButtons + + + 0 + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + - - - - Account lockout duration (mins): - - - - - - - Name: - - - - - - - Minimum password length: - - - - - - - Qt::LeftToRight - - - Enable complexity requirements - - - - - - - Store passwords using reversible encryption - - - - - - - - - - QAbstractSpinBox::NoButtons - - - - - - - - - - Precendence: - - - - + Qt::Horizontal - + @@ -107,8 +588,20 @@ false + + 0 + + + 0 + + + 0 + + + 0 + - + @@ -147,109 +640,6 @@ - - - - QAbstractSpinBox::NoButtons - - - - - - - Minimum password age (days): - - - - - - - QAbstractSpinBox::NoButtons - - - - - - - Reset account lockout after (mins): - - - - - - - QAbstractSpinBox::NoButtons - - - - - - - - - - QAbstractSpinBox::NoButtons - - - 99 - - - - - - - QAbstractSpinBox::NoButtons - - - - - - - QAbstractSpinBox::NoButtons - - - - - - - Maximum password age (days): - - - - - - - Qt::Horizontal - - - - 301 - 20 - - - - - - - - QAbstractSpinBox::NoButtons - - - - - - - Password history length: - - - - - - - Failed log on attempts allowed: - - -