Skip to content

ADMC architecture

Dmitry Degtyarev edited this page Nov 9, 2021 · 12 revisions

Data on the server

The main data that ADMC deals with is objects which are stored on the AD server.

Objects

Objects are laid out in a tree with each object having a parent, except for the head object which represents the domain. Each object is uniquely identified by a "distinguished name" or "DN". DN's also acts as the full "address" of the object in the tree because they contain object's names and names of it's ancestors in order like this: "CN=Object,CN=Parent 1,CN=Parent 2...". Each object has "attributes" which provide information about the object. Objects also have a special attribute called "objectClass" - the class of the object.

Attributes

Attributes have a name and value. Name is always a string, while value can be of different data types. Values are downloaded from the server as raw byte strings and then converted to appropriate data types. Attributes can have single values or multiple values(essentially lists). Note that multiple valued attributes are the default, while single valued attributes are an exception to the rule.

Configuration objects

There are normal objects which represent users, groups, organizational units, containers and other. There are also special "data" objects called "schema" and "display specifiers". They aren't intended to be shown to the user but are used by the client to know how to deal with and display object data. You can see those objects if you turn on "Advanced mode" and "Dev mode" in view settings and preferences. These settings will make the "Configuration" object visible which will contain "Schema" and "DisplaySpecifiers" objects.

Schema

This is the information about how different attributes and classes should be treated and displayed. Using this we can find out various properties of the attribute:

  • is it a string
  • is it a number
  • is it modifiable
  • does it have a max length
  • and others

Display Specifiers

They contain various strings which are meant to be shown to the user instead of raw untranslated strings that name AD constructs. For example, if you've got the "telephoneNumber" attribute, there will be a display specifier which will contain "Telephone Number" string. There are display specifiers for most languages broken down into folders named by language codes windows uses. 409 is English and 419 is Russian. There are also display strings for class names, column names and others.

Data on client (ADMC)

The client attempts to display server data and give user the ability to modify it, all while minimizing the amount of communication required between client and server.

adldap library

This library is internal to the project and contains functionality for communicating with the domain. All of the classes and files that start with "AdSomething" belong to this library.

AdInterface

This is the interface between the app and the AD server. It's purpose is mainly to convert C-style data to Qt classes when talking to the server. It uses the LDAP library to communicate with the server. It provides ways to get object data from server and send modification requests to server. It also uses smbclient library internally to communicate with the SMB share.

AdConfig

Loaded once during app startup and then reused after. AdConfig acts as a storage for schema and display specifiers. It processes this data from it's originally hard to use state into more manageable f-ns broken down by type and purpose. It loads that data once during connection and after that the app can use that data without having to get it from the server.

Data requests

This is about how data gets from the server to the client and into various widgets. Most of the things is done with search() function. LDAP library has the search() function and AdInterface provides a wrapper over it. It does two things: provides a list of objects that match the given filter and also returns attributes of those objects. Which attributes are returned can be selected to reduce download size. So you use search to get objects that give a certain filter and also to get attributes of objects in general. So sometimes there's really no "search" because you already know the DN of the object but you still use search() function.

AdObject

This is the result of data requests. They are returned from searches as Hash maps mapped from DN's to AdObjects. AdObjects provide easy access to underlying attributes. AdObject stores attributes as raw bytes and it's getter functions convert those bytes into data types. Note that these should be temporary storage and transformed into other appropriate data structures.

Data modifications

There are two types of modifications: modifications of objects and modifications of attributes. Object modifications are creating/moving/deleting/renaming. Attribute modifications are creating/deleting/changing. LDAP library has a small number of functions that perform those operations. AdInterface wraps those functions and specializes them for ease of use. For example, the simplest function is attribute_replace_value(). attribute_replace_int() and user_set_pass() are wrappers over that.

Data in widgets

Data from the server comes as AdObjects, widgets process that data into the form that they need and discard AdObjects. A lot of the widgets and dialogs are temporary and modal so they request data about objects once and then don't update it. Only the Console is permanent so it has to try to keep the state inside it up to date. Console should 100% sync with all modifications made by the app. It does NOT sync with modifications made by other clients connected to the same domain. For those cases there is "Refresh" action. The syncing with app modifications is done by the code that performs the modification. After the modification is done, modifying code should update all the parts of the console that should are affected.

Notable Widgets

Console

Shows the tree of objects in the left pane called "Scope" and children of selected scope node in right pane called "Results". A standard Qt model called QStandardItemModel is used to store object data for display. Scope pane displays the model as a tree with a single column. Results pane displays the children of the item that is currently selected in the scope pane. Results pane displays multiple columns of the model, which are customize-able. Since the total amount of objects in a domain can be very large (100,000), objects are loaded incrementally. Scope model loads children when a node is expanded and children are loaded in a separate thread in batches of 1000. Note that the Console itself doesn't implement "domain logic", which is the specifics about what kind of items it displays. The Console just operates on "items", without caring about whether they are objects, policies or whatever else. That logic is implemented outside of the Console in ConsoleImpl's.

ConsoleImpl

This class implements logic for each item type used in the console. It contains functions that will be called by the console when needed. It implements:

  • how child items are loaded
  • drag and drop logic
  • standard actions (actions like delete, rename)
  • custom actions
  • other functionality

In addition to logic, impl also implements how results is displayed for an item type. Note that results refers to how children of an item type are displayed, they don't necessarily have to be the same item type! There are 3 possible ways for results to work:

  • a tree view, showing a list with columns
  • a custom widget
  • a custom widget showing a tree view inside it

Impl also defines the labels on columns for it's results and which columns are shown by default.

Status

Singleton that routes success and error messages from the whole app into places where they are displayed to the user. Messages usually come from AdInterface as a result of operations, but also from some widgets and dialogs. They are then shown in status bar, status log and error log (opened if there were errors). Status bar shows the whole history of messages. Status log shows the most recent one. Error log shows all errors that happened while a group of operations was performed.

Properties Dialog

The main way to read and modify attributes of an object. Shows attributes laid out in "tabs". Different kinds of attributes are grouped together in each tab. User edits attributes and then the dialog applies changes when user clicks "Apply" or "OK" buttons.

Attribute edits

These are the primary components for dialogs that display or modify object properties. For example, each tab in the Properties Dialog is a collection of different attribute edits. Attribute edits are not widgets on their own, instead they wrap existing qt widgets. Then they handle things like:

  • loading data into wrapped widget from an AD object
  • verifying input
  • applying changes back to AD object

The simplest example of an attribute edit is a string edit which wraps QLineEdit and then loads some string attribute. Other attribute edits may use more elaborate qt widgets or combinations of widgets. Parent widgets that contain attribute edits then load/apply/reset all of the edits together. Loading typically happens when widget is shown, apply on "OK" or "Apply" button press and reset on "Reset" button press.

Attribute dialogs

Used mostly in Attributes tab. Unlike attribute edits, which wrap widgets embedded in dialogs, attribute dialogs and create their subwidgets. They also don't apply changes, but instead return new values to the parent that created them. The parent then applies the changes, if needed.

Filter Widget

Allows user to enter an LDAP filter through a GUI. Designed to be easier to use than just plain text format that LDAP filters usually come in. Used in many places: find dialog, filter menu for console, queries (in the future) and select dialog (inside find dialog).