1
0
mirror of https://github.com/altlinux/admc.git synced 2025-03-28 18:50:45 +03:00

Merge pull request #191 from altlinux/policy-results

Policy results
This commit is contained in:
Dmitry Degtyarev 2021-04-07 17:12:06 +04:00 committed by GitHub
commit 87f54264a3
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 424 additions and 6 deletions

View File

@ -35,6 +35,7 @@ add_executable(admc
utils.cpp
settings.cpp
policy_model.cpp
policy_results_widget.cpp
move_dialog.cpp
rename_dialog.cpp

View File

@ -36,10 +36,12 @@
#include "find_dialog.h"
#include "password_dialog.h"
#include "rename_policy_dialog.h"
#include "select_dialog.h"
#include "console_widget/console_widget.h"
#include "console_widget/results_view.h"
#include "editors/multi_editor.h"
#include "gplink.h"
#include "policy_results_widget.h"
#include <QDebug>
#include <QAbstractItemView>
@ -70,6 +72,7 @@ CentralWidget::CentralWidget()
object_actions = new ObjectActions(this);
create_policy_action = new QAction(tr("New policy"), this);
auto add_link_action = new QAction(tr("Add link"), this);
auto rename_policy_action = new QAction(tr("Rename"), this);
auto delete_policy_action = new QAction(tr("Delete"), this);
@ -80,6 +83,7 @@ CentralWidget::CentralWidget()
// NOTE: add policy actions to this list so that they
// are processed
policy_actions = {
add_link_action,
rename_policy_action,
delete_policy_action,
};
@ -103,6 +107,9 @@ CentralWidget::CentralWidget()
policies_results->detail_view()->header()->setDefaultSectionSize(200);
policies_results_id = console_widget->register_results(policies_results, policy_model_header_labels(), policy_model_default_columns());
policy_results_widget = new PolicyResultsWidget();
policy_links_results_id = console_widget->register_results(policy_results_widget);
auto layout = new QVBoxLayout();
layout->setContentsMargins(0, 0, 0, 0);
layout->setSpacing(0);
@ -179,6 +186,9 @@ CentralWidget::CentralWidget()
connect(
create_policy_action, &QAction::triggered,
this, &CentralWidget::create_policy);
connect(
add_link_action, &QAction::triggered,
this, &CentralWidget::add_link);
connect(
rename_policy_action, &QAction::triggered,
this, &CentralWidget::rename_policy);
@ -188,7 +198,7 @@ CentralWidget::CentralWidget()
connect(
console_widget, &ConsoleWidget::current_scope_item_changed,
this, &CentralWidget::update_description_bar);
this, &CentralWidget::on_current_scope_changed);
connect(
console_widget, &ConsoleWidget::results_count_changed,
this, &CentralWidget::update_description_bar);
@ -255,7 +265,7 @@ void CentralWidget::go_online(AdInterface &ad) {
for (const AdObject &object : search_results.values()) {
QStandardItem *scope_item;
QList<QStandardItem *> results_row;
console_widget->add_buddy_scope_and_results(policies_results_id, ScopeNodeType_Static, policies_item->index(), &scope_item, &results_row);
console_widget->add_buddy_scope_and_results(policy_links_results_id, ScopeNodeType_Static, policies_item->index(), &scope_item, &results_row);
setup_policy_scope_item(scope_item, object);
setup_policy_results_row(results_row, object);
@ -473,6 +483,62 @@ void CentralWidget::create_policy() {
// unfinished)
}
void CentralWidget::add_link() {
const QList<QModelIndex> selected = console_widget->get_selected_items();
if (selected.size() == 0) {
return;
}
auto dialog = new SelectDialog({CLASS_OU}, SelectDialogMultiSelection_Yes, this);
QObject::connect(
dialog, &SelectDialog::accepted,
[=]() {
AdInterface ad;
if (ad_failed(ad)) {
return;
}
show_busy_indicator();
const QList<QString> gpos =
[selected]() {
QList<QString> out;
for (const QModelIndex &index : selected) {
const QString gpo = index.data(PolicyRole_DN).toString();
out.append(gpo);
}
return out;
}();
const QList<QString> ou_list = dialog->get_selected();
for (const QString &ou_dn : ou_list) {
const QHash<QString, AdObject> results = ad.search(QString(), {ATTRIBUTE_GPLINK}, SearchScope_Object, ou_dn);
const AdObject ou_object = results[ou_dn];
const QString gplink_string = ou_object.get_string(ATTRIBUTE_GPLINK);
Gplink gplink = Gplink(gplink_string);
for (const QString &gpo : gpos) {
gplink.add(gpo);
}
ad.attribute_replace_string(ou_dn, ATTRIBUTE_GPLINK, gplink.to_string());
}
const QModelIndex current_scope = console_widget->get_current_scope_item();
policy_results_widget->update(current_scope);
hide_busy_indicator();
STATUS()->display_ad_messages(ad, this);
});
dialog->open();
}
void CentralWidget::rename_policy() {
const QList<QModelIndex> indexes = console_widget->get_selected_items();
if (indexes.size() != 1) {
@ -612,6 +678,13 @@ void CentralWidget::on_items_dropped(const QList<QModelIndex> &dropped_list, con
STATUS()->display_ad_messages(ad, nullptr);
}
void CentralWidget::on_current_scope_changed() {
const QModelIndex current_scope = console_widget->get_current_scope_item();
policy_results_widget->update(current_scope);
update_description_bar();
}
void CentralWidget::refresh_head() {
show_busy_indicator();

View File

@ -43,6 +43,7 @@ class AdInterface;
class ConsoleWidget;
class ResultsView;
class ObjectActions;
class PolicyResultsWidget;
template <typename T> class QList;
enum ItemType {
@ -89,6 +90,7 @@ private slots:
void edit_upn_suffixes();
void create_policy();
void add_link();
void rename_policy();
void delete_policy();
@ -97,12 +99,16 @@ private slots:
void on_items_can_drop(const QList<QModelIndex> &dropped, const QModelIndex &target, bool *ok);
void on_items_dropped(const QList<QModelIndex> &dropped, const QModelIndex &target);
void on_current_scope_changed();
private:
int object_results_id;
int policies_results_id;
int policy_links_results_id;
QPersistentModelIndex scope_head_index;
QPersistentModelIndex policies_index;
FilterDialog *filter_dialog;
PolicyResultsWidget *policy_results_widget;
ObjectActions *object_actions;

View File

@ -356,6 +356,9 @@ int ConsoleWidget::register_results(QWidget *widget, ResultsView *view, const QL
connect(
view, &ResultsView::context_menu,
d, &ConsoleWidgetPrivate::on_context_menu);
connect(
view, &ResultsView::selection_changed,
d, &ConsoleWidgetPrivate::on_selection_changed);
// Hide non-default results view columns. Note that
// at creation, view header doesnt have any
@ -374,10 +377,6 @@ int ConsoleWidget::register_results(QWidget *widget, ResultsView *view, const QL
});
}
connect(
view, &ResultsView::selection_changed,
d, &ConsoleWidgetPrivate::on_selection_changed);
return id;
}

View File

@ -0,0 +1,281 @@
/*
* ADMC - AD Management Center
*
* Copyright (C) 2020 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 <http://www.gnu.org/licenses/>.
*/
#include "policy_results_widget.h"
#include "adldap.h"
#include "console_widget/console_widget.h"
#include "central_widget.h"
#include "policy_model.h"
#include "utils.h"
#include "gplink.h"
#include "status.h"
#include <QModelIndex>
#include <QTreeView>
#include <QDebug>
#include <QTreeView>
#include <QStandardItemModel>
#include <QVBoxLayout>
#include <QAction>
#include <QMenu>
#include <QHeaderView>
enum PolicyResultsColumn {
PolicyResultsColumn_Name,
PolicyResultsColumn_Enforced,
PolicyResultsColumn_Disabled,
PolicyResultsColumn_Path,
PolicyResultsColumn_COUNT,
};
enum PolicyResultsRole {
PolicyResultsRole_DN = Qt::UserRole,
PolicyResultsRole_GplinkString = Qt::UserRole + 1,
PolicyResultsRole_COUNT,
};
const QSet<PolicyResultsColumn> option_columns = {
PolicyResultsColumn_Disabled,
PolicyResultsColumn_Enforced
};
const QHash<PolicyResultsColumn, GplinkOption> column_to_option = {
{PolicyResultsColumn_Disabled, GplinkOption_Disabled},
{PolicyResultsColumn_Enforced, GplinkOption_Enforced}
};
// TODO: need to sync this with changes done through group
// policy tab (just call load after properties is closed?)
PolicyResultsWidget::PolicyResultsWidget() {
auto delete_link_action = new QAction(tr("Delete link"), this);
context_menu = new QMenu(this);
context_menu->addAction(delete_link_action);
view = new QTreeView(this);
view->setContextMenuPolicy(Qt::CustomContextMenu);
view->setAllColumnsShowFocus(true);
view->setSortingEnabled(true);
view->setSelectionMode(QAbstractItemView::ExtendedSelection);
model = new QStandardItemModel(0, PolicyResultsColumn_COUNT, this);
set_horizontal_header_labels_from_map(model, {
{PolicyResultsColumn_Name, tr("Location")},
{PolicyResultsColumn_Enforced, tr("Enforced")},
{PolicyResultsColumn_Disabled, tr("Disabled")},
{PolicyResultsColumn_Path, tr("Path")},
});
view->setModel(model);
view->header()->resizeSection(0, 300);
view->header()->resizeSection(1, 100);
view->header()->resizeSection(2, 100);
view->header()->resizeSection(3, 500);
const auto layout = new QVBoxLayout();
setLayout(layout);
layout->setContentsMargins(0, 0, 0, 0);
layout->setSpacing(0);
layout->addWidget(view);
connect(
model, &QStandardItemModel::itemChanged,
this, &PolicyResultsWidget::on_item_changed);
connect(
view, &QWidget::customContextMenuRequested,
this, &PolicyResultsWidget::open_context_menu);
connect(
delete_link_action, &QAction::triggered,
this, &PolicyResultsWidget::delete_link);
}
void PolicyResultsWidget::update(const QModelIndex &scope_index) {
const ItemType type = (ItemType) scope_index.data(ConsoleRole_Type).toInt();
if (type != ItemType_Policy) {
return;
}
AdInterface ad;
if (ad_failed(ad)) {
return;
}
model->removeRows(0, model->rowCount());
gpo = scope_index.data(PolicyRole_DN).toString();
const QList<QString> search_attributes = {ATTRIBUTE_NAME, ATTRIBUTE_GPLINK};
const QString filter = filter_CONDITION(Condition_Contains, ATTRIBUTE_GPLINK, gpo);
const QHash<QString, AdObject> search_results = ad.search(filter, search_attributes, SearchScope_All);
for (const AdObject &object : search_results.values()) {
const QList<QStandardItem *> row = make_item_row(PolicyResultsColumn_COUNT);
const QString dn = object.get_dn();
const QString name = dn_get_name(dn);
row[PolicyResultsColumn_Name]->setText(name);
row[PolicyResultsColumn_Path]->setText(dn_get_parent_canonical(dn));
const QString gplink_string = object.get_string(ATTRIBUTE_GPLINK);
const Gplink gplink = Gplink(gplink_string);
const Qt::CheckState enforced_checkstate =
[&]() {
const bool is_enforced = gplink.get_option(dn, GplinkOption_Enforced);
if (is_enforced) {
return Qt::Checked;
} else {
return Qt::Unchecked;
}
}();
row[PolicyResultsColumn_Enforced]->setCheckable(true);
row[PolicyResultsColumn_Enforced]->setCheckState(enforced_checkstate);
for (const auto column : option_columns) {
QStandardItem *item = row[column];
item->setCheckable(true);
const Qt::CheckState checkstate =
[=]() {
const GplinkOption option = column_to_option[column];
const bool option_is_set = gplink.get_option(dn, option);
if (option_is_set) {
return Qt::Checked;
} else {
return Qt::Unchecked;
}
}();
item->setCheckState(checkstate);
}
row[0]->setData(dn, PolicyResultsRole_DN);
row[0]->setData(gplink_string, PolicyResultsRole_GplinkString);
row[0]->setIcon(QIcon::fromTheme("folder-documents"));
model->appendRow(row);
}
}
void PolicyResultsWidget::on_item_changed(QStandardItem *item) {
const PolicyResultsColumn column = (PolicyResultsColumn) item->column();
if (!option_columns.contains(column)) {
return;
}
const QModelIndex this_index = item->index();
const QModelIndex index = this_index.siblingAtColumn(0);
const QString dn = index.data(PolicyResultsRole_DN).toString();
const GplinkOption option = column_to_option[column];
const bool is_checked = (item->checkState() == Qt::Checked);
const QString gplink_string = index.data(PolicyResultsRole_GplinkString).toString();
Gplink gplink = Gplink(gplink_string);
gplink.set_option(gpo, option, is_checked);
const QString updated_gplink_string = gplink.to_string();
const bool gplink_didnt_change = gplink.equals(gplink_string);
if (gplink_didnt_change) {
return;
}
AdInterface ad;
if (ad_failed(ad)) {
return;
}
show_busy_indicator();
const bool success = ad.attribute_replace_string(dn, ATTRIBUTE_GPLINK, updated_gplink_string);
if (success) {
model->setData(index, updated_gplink_string, PolicyResultsRole_GplinkString);
} else {
const Qt::CheckState undo_check_state =
[&]() {
if (item->checkState() == Qt::Checked) {
return Qt::Unchecked;
} else {
return Qt::Checked;
}
}();
item->setCheckState(undo_check_state);
}
STATUS()->display_ad_messages(ad, this);
hide_busy_indicator();
}
void PolicyResultsWidget::open_context_menu(const QPoint &pos) {
const QModelIndex index = view->indexAt(pos);
if (!index.isValid()) {
return;
}
const QPoint global_pos = view->mapToGlobal(pos);
context_menu->popup(global_pos);
}
void PolicyResultsWidget::delete_link() {
AdInterface ad;
if (ad_failed(ad)) {
return;
}
show_busy_indicator();
const QList<QModelIndex> selected = view->selectionModel()->selectedRows();
QList<QPersistentModelIndex> removed_indexes;
for (const QModelIndex &index : selected) {
const QString dn = index.data(PolicyResultsRole_DN).toString();
const QString gplink_string = index.data(PolicyResultsRole_GplinkString).toString();
Gplink gplink = Gplink(gplink_string);
gplink.remove(gpo);
const QString updated_gplink_string = gplink.to_string();
const bool gplink_didnt_change = gplink.equals(gplink_string);
if (gplink_didnt_change) {
continue;
}
const bool success = ad.attribute_replace_string(dn, ATTRIBUTE_GPLINK, updated_gplink_string);
if (success) {
removed_indexes.append(QPersistentModelIndex(index));
}
}
for (const QPersistentModelIndex &index : removed_indexes) {
model->removeRow(index.row());
}
STATUS()->display_ad_messages(ad, this);
hide_busy_indicator();
}

View File

@ -0,0 +1,57 @@
/*
* ADMC - AD Management Center
*
* Copyright (C) 2020 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 <http://www.gnu.org/licenses/>.
*/
#ifndef POLICY_RESULTS_WIDGET_H
#define POLICY_RESULTS_WIDGET_H
/**
* Displays OU's linked to currently selected policy.
*/
#include <QWidget>
#include <QString>
class QModelIndex;
class QTreeView;
class QStandardItemModel;
class QStandardItem;
class QMenu;
class PolicyResultsWidget final : public QWidget {
Q_OBJECT
public:
PolicyResultsWidget();
// Loads links for this policy. Nothing is done if given
// index is not a policy.
void update(const QModelIndex &scope_index);
private:
QTreeView *view;
QStandardItemModel *model;
QString gpo;
QMenu *context_menu;
void on_item_changed(QStandardItem *item);
void open_context_menu(const QPoint &pos);
void delete_link();
};
#endif /* POLICY_RESULTS_WIDGET_H */

View File

@ -41,6 +41,7 @@ set(TEST_SOURCES
${PROJECT_SOURCE_DIR}/src/admc/utils.cpp
${PROJECT_SOURCE_DIR}/src/admc/settings.cpp
${PROJECT_SOURCE_DIR}/src/admc/policy_model.cpp
${PROJECT_SOURCE_DIR}/src/admc/policy_results_widget.cpp
${PROJECT_SOURCE_DIR}/src/admc/move_dialog.cpp
${PROJECT_SOURCE_DIR}/src/admc/rename_dialog.cpp