Среда, 24 февраля 2021 19:00

Работаем с OU – вставка, удаление, правка. Работа с LDAP в Qt5. Часть 8.

Россия
Оцените материал
(0 голосов)

Сегодня мы рассмотрим работу с OU – организационными единицами в каталоге LDAP на примере Active Directory.

Модернизируем классы

В LDAP может быть множество типов записей, в общем случае тип записи определяется её классом. Я рекомендую прочитать эту статью - https://habr.com/ru/post/538662/, прежде мы продолжим. В ней подробно рассмотрена иерархия LDAP.

Сегодня помимо Пользователей мы начнём работать с OU, а в последующей статье рассмотрим Контакты.

С точки зрения LDAP запись OU и запись Пользователь, отличаются только классом, который установлен для данной записи.

Например, для Пользователя указываются классы:

objectClass = organizationalPerson, person, top, user

Для OU указываются классы:

objectClass = organizationalUnit, person

Класс QLdapUser содержит методы, позволяющие заполнить этот список, но не устанавливает значение для атрибута objectClass.Давайте это исправим, но сначала модернизируем все классы.

В одной из прошлых статей мы создали класс для работы с группами, в нынешний проект он не попал, давайте исправим это и скопируем его в наш проект из этой статьи.

Базовый класс QLdapEntity

Во всех классах для работы с элементами LPDAP мы использовали методы:

QString getXXXXXValue(QString key) const

void setXXXXValue(const QString &key, const QLdapEntryValues &value) const

Код которых полностью совпадал. Давайте уберем это дублирование кода, для этого добавим новый класс – QLdapEntity

Заголовок:

#ifndef QLDAPENTITY_H
#define QLDAPENTITY_H

#include <QString>
#include "qldap.h"



class QLdapEntity
{
public:
    explicit QLdapEntity(QLdapEntry *entry);
    QString getEntityValue(const QString &key) const;
    void setEntityValue(const QString &key, const QLdapEntryValues &value) const;

    QString toString() const;
    QString operator [](const QString &index) const;
    QLdapEntry *getEntry() const;
private:
    QLdapEntry *entry;
};

QDebug operator<<(QDebug debug, const QLdapEntity &entity);
QDebug operator<<(QDebug debug, const QLdapEntity *entity);

#endif // QLDAPENTITY_H

Реализация:

#include "qldapentity.h"
#include <QDebug>

QLdapEntity::QLdapEntity(QLdapEntry *entry)
{
    this->entry = entry;
}

QString QLdapEntity::getEntityValue(const QString &key) const
{
        return (*this->entry)[key].join(",");
}

void QLdapEntity::setEntityValue(const QString &key, const QLdapEntryValues &value) const
{
        (*this->entry)[key] = value;
}

QString QLdapEntity::toString() const
{
    QString s;
    QTextStream out(&s);

    out << "QLdapEntity( ";
    for(QLdapEntry::iterator i=this->entry->begin();i!=this->entry->end();++i)
    {
        out << i.key() << ": " << i.value().join(",") << ", ";
    }
    out << " )";
    return s;
}


QDebug operator<<(QDebug debug, const QLdapEntity &entity)
{
    QDebugStateSaver saver(debug);


    debug.nospace() << "QLdapEntity( ";
    for(QLdapEntry::iterator i=entity.getEntry()->begin();i!=entity.getEntry()->end();++i)
    {
        debug.nospace() << i.key() << ": " << i.value().join(",") << ", ";
    }
    debug.nospace() << " )";

    return debug;
}


QDebug operator<<(QDebug debug, const QLdapEntity *entity)
{
    QDebugStateSaver saver(debug);
    debug.nospace() << *entity;
    return debug;
}

QString QLdapEntity::operator[] (const QString &index) const
{
    assert(index.compare("") != 0);
    return this->getEntityValue(index);
}

QLdapEntry *QLdapEntity::getEntry() const
{
    return entry;
}

Как видите, мы добавили перегрузку операторов и метод getEntry(), этот геттер позволяет получить прямой доступ к значениям QLdapEntity.

Так же был переписана перегрузка << теперь будут выводиться все действительные значения QLdapEntity.

Теперь нам нужно в классы QLdapUser и QLdapGroup добавить наследование от QLdapEntity и внести некоторые изменения.

Модификация класса QLdapUser

Наследуем класс от QLdapEntity и удалим ненужные методы, в результате класс измениться:

Заголовок: 

#ifndef QLDAPUSER_H
#define QLDAPUSER_H

#include <QString>
#include "qldap.h"
#include "qldapentity.h"

#define QLDAP_AD_USER 0


class QLdapUser : public QLdapEntity
{
public:
    explicit QLdapUser(QLdapEntry *user);

    QString getDisplayName() const;

    QString getGivenName() const;

    QString getInitials() const;

    QString getOfficePhone() const;

    QString getDepartment() const;

    QString getTitle() const;

    QString getUserPrincipalName() const;

    QString getCellPhone() const;

    QString getSamAccountName() const;

    QString getPath() const;

    QString getEmail() const;

    QString getStreetAddress() const;

    QString getOffice() const;

    QString getCompany() const;

    QString getFax() const;

    void setDisplayName(const QString &value) const;

    void setGivenName(const QString &value) const;

    void setInitials(const QString &value) const;

    void setOfficePhone(const QString &value) const;

    void setDepartment(const QString &value) const;

    void setTitle(const QString &value) const;

    void setUserPrincipalName(const QString &value) const;

    void setMobilePhone(const QString &value) const;

    void setSamAccountName(const QString &value) const;

    void setPath(const QString &value) const;

    void setEmail(const QString &value) const;

    void setStreetAddress(const QString &value) const;

    void setOffice(const QString &value) const;

    void setCompany(const QString &value) const;

    void setFax(const QString &value) const;

    void setName(const QString &value) const;

};

QDebug operator<<(QDebug debug, const QLdapUser &user);
QDebug operator<<(QDebug debug, const QLdapUser *user);

#endif // QLDAPUSER_H

Реализация:

#include "qldapuser.h"
#include "qldapuser.h"
#include <QDebug>
#include <QString>

QLdapUser::QLdapUser(QLdapEntry *user) : QLdapEntity(user)
{
}

QString QLdapUser::getDisplayName() const
{
    return this->getEntityValue("displayName");
}


QString QLdapUser::getInitials() const
{
    return this->getEntityValue("initials");
}


QString QLdapUser::getOfficePhone() const
{
    return this->getEntityValue("otherTelephone");
}

QString QLdapUser::getDepartment() const
{
    return this->getEntityValue("department");
}


QString QLdapUser::getTitle() const
{
    return this->getEntityValue("title");
}

QString QLdapUser::getUserPrincipalName() const
{
    return this->getEntityValue("userPrincipalName");
}


QString QLdapUser::getCellPhone() const
{
    return this->getEntityValue("mobile");
}

QString QLdapUser::getSamAccountName() const
{
    return this->getEntityValue("sAMAccountName");
}


QString QLdapUser::getPath() const
{
    return this->getEntityValue("distinguishedName");
}

QString QLdapUser::getEmail() const
{
    return this->getEntityValue("mail");
}

QString QLdapUser::getStreetAddress() const
{
    return this->getEntityValue("streetAddress");
}

QString QLdapUser::getOffice() const
{
    return this->getEntityValue("physicalDeliveryOfficeName");
}

QString QLdapUser::getCompany() const
{
    return this->getEntityValue("company");
}

QString QLdapUser::getFax() const
{
    return this->getEntityValue("facsimileTelephoneNumber");
}




void QLdapUser::setDisplayName(const QString &value) const
{

    this->setEntityValue("displayName",QStringList(value));
}

void QLdapUser::setGivenName(const QString &value) const
{
    this->setEntityValue("givenName",QStringList(value));
}

void QLdapUser::setInitials(const QString &value) const
{
    this->setEntityValue("initials",QStringList(value));
}

void QLdapUser::setOfficePhone(const QString &value) const
{
    this->setEntityValue("otherTelephone",QStringList(value));
}

void QLdapUser::setDepartment(const QString &value) const
{
    this->setEntityValue("department",QStringList(value));
}

void QLdapUser::setTitle(const QString &value) const
{
    this->setEntityValue("title",QStringList(value));
}

void QLdapUser::setUserPrincipalName(const QString &value) const
{
    this->setEntityValue("userPrincipalName",QStringList(value));
}

void QLdapUser::setMobilePhone(const QString &value) const
{
    this->setEntityValue("mobile",QStringList(value));
}

void QLdapUser::setSamAccountName(const QString &value) const
{
    this->setEntityValue("sAMAccountName",QStringList(value));
}

void QLdapUser::setPath(const QString &value) const
{
    this->setEntityValue("distinguishedName",QStringList(value));
}

void QLdapUser::setEmail(const QString &value) const
{
    this->setEntityValue("mail",QStringList(value));
}

void QLdapUser::setStreetAddress(const QString &value) const
{
    this->setEntityValue("streetAddress",QStringList(value));
}

void QLdapUser::setOffice(const QString &value) const
{
    this->setEntityValue("physicalDeliveryOfficeName",QStringList(value));
}

void QLdapUser::setCompany(const QString &value) const
{
    this->setEntityValue("company",QStringList(value));
}

void QLdapUser::setFax(const QString &value) const
{
    this->setEntityValue("facsimileTelephoneNumber",QStringList(value));
}

void QLdapUser::setName(const QString &value) const
{
    this->setEntityValue("name",QStringList(value));
}

QDebug operator<<(QDebug debug, const QLdapUser &user)
{
    QDebugStateSaver saver(debug);
    debug.nospace() << "QLdapUserAD( ";
    debug.nospace() << (QLdapEntity *)&user;
    debug.nospace() << " )";
    return debug;
}

QDebug operator<<(QDebug debug, const QLdapUser *user)
{
    QDebugStateSaver saver(debug);
    debug.nospace() << *user;
    return debug;
}

Обратите внимание, мы удалили перегрузку оператора [], так как она уже добавлена в базовый класс.

Но оставили перегрузку << для QDebug так как она объявляется отдельно от класса и должна соответствовать типу нашего класса.

В сеттерах и геттерах мы используем методы базового класса setEntityValue и getEntityValue. 

Изменим строку:

u.setUserValue("objectClass",QStringList({"user","person","top","organizationalPerson"}));

на

u.setEntityValue("objectClass",QStringList({"user","person","top","organizationalPerson"}));

Запустим сборку – всё работает без ошибок.

Теперь отладка выводит содержимое только заполненных атрибутов:

QLdapUser( QLdapEntity( "physicalDeliveryOfficeName": "104", "objectClass": "user,person,top,organizationalPerson", "name": "Иванов И. И.", "displayName": "Иванов Иван Иванович", "otherTelephone": "+7(495)12300024", "company": "Altunin Soft", "streetAddress": "Ленина 1", "givenName": "Иванов И. И.", "department": "Бухгалтерия", "title": "Бухгалтер", "facsimileTelephoneNumber": "", "initials": "И.", "mail": "Этот адрес электронной почты защищен от спам-ботов. Для просмотра адреса в вашем браузере должен быть включен Javascript.", "mobile": "+7(495)876-16-18",  ) )

Новый класс QLdapUserAD

Несмотря на то, что AD полностью поддерживает LDAP, возможно различие в классах записей, а значит и доступных атрибутах, используемых в AD.

Как было сказано выше в LDAP тип записи зависит от классов, которые ей назначены, поэтому мы должны инициализировать objectClass нужным значением, при создании экземпляра класса.

Для этого добавим новый класс QLdapUserAD и наследуем его от QLdapUser

Заголовок:

#ifndef QLDAPUSERAD_H
#define QLDAPUSERAD_H

#include "qldapuser.h"

class QLdapUserAD : public QLdapUser
{
public:
    explicit QLdapUserAD(QLdapEntry *user);
};

QDebug operator<<(QDebug debug, const QLdapUserAD &user);
QDebug operator<<(QDebug debug, const QLdapUserAD *user);


#endif // QLDAPUSERAD_H

Реализация: 

#include "qldapuserad.h"
#include <QDebug>

QLdapUserAD::QLdapUserAD(QLdapEntry *user) : QLdapUser(user)
{
    this->setEntityValue("objectClass",QStringList({"user","person","top","organizationalPerson"}));
}

QDebug operator<<(QDebug debug, const QLdapUserAD &user)
{
    QDebugStateSaver saver(debug);
    debug.nospace() << "QLdapUserAD( ";
    debug.nospace() << (QLdapEntity *)&user;
    debug.nospace() << " )";
    return debug;
}

QDebug operator<<(QDebug debug, const QLdapUserAD *user)
{
    QDebugStateSaver saver(debug);
    debug.nospace() << *user;
    return debug;
}

В конструкторе главной формы заменим QLdapUser на QLdapUserAD.

Удалим строку:

u.setEntityValue("objectClass",QStringList({"user","person","top","organizationalPerson"}));

Запустим – всё отработало как обычно, значит своей правкой мы ничего не сломали!

Подобным образом был модернизирован класс – QldapGroup.

Создаем класс для OU

Очистим конструктор главной формы от кода для работы с пользователями, он нам больше не понадобится.

OU является такой же записью как и пользователь, разница между ними в присвоенном классе:

objectClass = organizationalUnit

Давайте добавим класс для OU - QLdapOU:

Заголовок:

#ifndef QLDAPOU_H
#define QLDAPOU_H

#include <QString>
#include "qldap.h"
#include "qldapentity.h"


class QLdapOU : public QLdapEntity
{
public:
    explicit QLdapOU(QLdapEntry *ou);

    void setDecription(const QString &value) const;
    QString getDecription() const;

    void setName(const QString &value) const;
    QString getName() const;
};


QDebug operator<<(QDebug debug, const QLdapOU &user);
QDebug operator<<(QDebug debug, const QLdapOU *user);

#endif // QLDAPOU_H

Реализация:

#include "qldapou.h"
#include <QDebug>

QLdapOU::QLdapOU(QLdapEntry *ou) : QLdapEntity(ou)
{
    this->setEntityValue("objectClass",QStringList({"organizationalUnit"}));
}


QString QLdapOU::getDecription() const
{
    return this->getEntityValue("description");
}

void QLdapOU::setDecription(const QString &value) const
{

    this->setEntityValue("description",QStringList(value));
}


void QLdapOU::setName(const QString &value) const
{
    this->setEntityValue("name",QStringList(value));
}

QString QLdapOU::getName() const
{
    return this->getEntityValue("name");
}

QDebug operator<<(QDebug debug, const QLdapOU &user)
{
    QDebugStateSaver saver(debug);
    debug.nospace() << "QLdapOU( ";
    debug.nospace() << (QLdapEntity *)&user;
    debug.nospace() << " )";
    return debug;
}

QDebug operator<<(QDebug debug, const QLdapOU *user)
{
    QDebugStateSaver saver(debug);
    debug.nospace() << *user;
    return debug;
}

В данный момент в OU нас интересуют атрибуты name, description и ou.

Добавляем новое OU

Добавим новое OU – otdel5

В конструктор главной формы добавим код:

    QLdapEntry *ou1 = new QLdapEntry();
    QLdapOU ou = QLdapOU(ou1);

    ou.setName("otdel5");
    ou.setDecription("Отдел №5");

    qDebug() << ou;

    QLdapMod *mod2 = new QLdapMod(ou1, LDAP_MOD_ADD);
    LDAPMod **m2 = mod2->getMods();

    result = ldap->add("OU=Company,DC=altuninvv,DC=local", ou1, m2);

    delete mod2;

    if ( result != LDAP_SUCCESS )
    {
        QString msg = QString("QLDAP ou add() error: ") + QString(ldap_err2string(result));
        qDebug("%s",msg.toLatin1().constData());
        //return;
    }

Запустим:

QLdapOU( QLdapEntity( "name": "otdel5", "description": "Отдел №5", "ou": "otdel5", "objectClass": "organizationalUnit",  ) )
GetMods
OP:  0
new dn =  "CN=otdel5,OU=Company,DC=altuninvv,DC=local"
Added
QLDAP ou add() error: Insufficient access
Close result =  Success

Как видите, не хватает прав на создание OU.

Добавление прав для создания OU в AD

В предыдущих статьях мы уже настраивали права доступа для добавления пользователей и правки атрибутов.

На этот раз, в дополнение к предыдущим разрешениям, установите галочки напротив:

Создание объектов: Подразделение (Create Organizational Unit Objects)

Удаление объектов: Подразделение (Delete Organizational Unit Objects)

Запустим:

QLdapOU( QLdapEntity( "name": "otdel5", "description": "Отдел №5", "objectClass": "organizationalUnit",  ) )
GetMods
OP:  0
new dn =  "OU=otdel5,OU=Company,DC=altuninvv,DC=local"
Added
Close result =  Success

Проверим админку AD:

2021-02-20_15-56-08.png

OU добавлена.

Удаление OU

Попробуем удалить OU, добавим код выше кода создания OU, таким образом у нас всегда будет новый элемент.

    result = ldap->del("ou=otdel5,OU=Company,DC=altuninvv,DC=local");

    if ( result != LDAP_SUCCESS )
    {
        QString msg = QString("QLDAP OU del() error: ") + QString(ldap_err2string(result));
        qDebug("%s",msg.toLatin1().constData());
        //return;
    }

Запустим:

GetMods
OP:  0
Delete DN =  "ou=otdel5,OU=Company,DC=altuninvv,DC=local"
Deleted

Вы можете через админку AD поменять описание созданной OU и запустить код еще раз. После запуска название измениться.

Правка OU

Давайте изменим описание OU:

    QLdapEntry *oue = new QLdapEntry();
    QLdapOU ou2 = QLdapOU(oue);
    ou2.setDecription("Новый Отдел №5");

    QLdapMod *mod1 = new QLdapMod(oue, LDAP_MOD_REPLACE);
    LDAPMod **m1 = mod1->getMods();

    result = ldap->edit("ou=otdel5,OU=Company,DC=altuninvv,DC=local", m1);

    delete mod1;

 

Запустим: 

GetMods
OP:  2
Edit DN =  "ou=otdel5,OU=Company,DC=altuninvv,DC=local"
Edited

 Обратите внимание, вы не можете переименовать OU таким способом, при попытке изменить атрибут name вы получите ошибку:

QLDAP OU edit() error: Operation not allowed on RDN

Для переименования используется функция OpenLDAP - ldap_rename_s(), но о ней мы поговорим в одной из следующих статей.

Заключение

Сегодня мы рассмотрели добавление, правку и удаление OU из каталога LDAP.

Была произведена модернизация классов для работы с записями LDAP – введен класс QLdapEntity от которого теперь наследуются классы QLdapUser и QLdapGroup.

Были внесены изменения в классы QLdapUser и QLdapGroup.

Мы добавили класс для работы с OU – QLdapOU.

Настроили права доступа в AD для создания и удаления OU.

Привели примеры добавления удаления, правки OU в каталоге LDAP.

Скачать исходный код вы можете на Github.

Прочитано 317 раз Последнее изменение Среда, 24 февраля 2021 19:41