Перейти к содержанию

AD Sync — справочник настройки

Синхронизация пользователей и групп из Active Directory / OpenLDAP в 1Форму. Односторонняя: AD → 1Форма.

1. Архитектура

Точки запуска синхронизации

Триггер Класс Что синхронизирует
По расписанию (ежедневно 20:00) ADSyncJob (Quartz) Все активные профили: пользователи + группы + членство + вложенность
При логине пользователя UserActiveDirectoryLookupService Один пользователь (on-demand), может создать нового
SmartAction StandartActionSyncUserWithAD Один пользователь (по UserID), может привязать SID
API (admin) UserManageController.SyncWithAd Один пользователь
API (admin) GroupsController.SyncAllGroups Все группы

Ключевые классы

Класс Namespace Файл Роль
ADSyncJob TCClassLib.QuartzJobs.Jobs TCClassLib/QuartzJobs/Jobs/ADSyncJob.cs Quartz job, точка входа для расписания
ADSync TCClassLib.DirectoryInterop TCClassLib/DirectoryInterop/ADSync.cs Ядро (~1400 строк, partial class). Вся логика синхронизации
ADSyncFactory Там же Фабрика контекстов ADSync через delegate injection
DirectoryContextFactory TCClassLib.DirectoryInterop TCClassLib/DirectoryInterop/DirectoryContextFactory.cs Подключение к LDAP (System.DirectoryServices)
SynchronizationProfilesService TCClassLib.Synchronization.ActiveDirectory TCClassLib/Synchronization/ActiveDirectory/SynchronizationProfilesService.cs CRUD профилей синхронизации
SynchronizationPropertiesMappingService TCClassLib/Synchronization/ActiveDirectory/SynchronizationPropertiesMappingService.cs Маппинг свойств AD → поля 1Ф
LinkUsersService TCClassLib/Synchronization/ActiveDirectory/LinkUsersService.cs Массовое связывание пользователей (батчи по 30 LDAP-запросов)
DomainEntriesTreeService TCClassLib/Synchronization/ActiveDirectory/DomainEntriesTreeService.cs Навигация по дереву OU для UI
UserActiveDirectoryLookupService TCClassLib.Users TCClassLib/Users/UserActiveDirectoryLookupService.cs On-demand поиск/синк при логине
UserAdminService TCClassLib.Users TCClassLib/Users/UserAdminService.cs SyncUserWithAd() — синк одного пользователя
AdminGroups TCClassLib TCClassLib/AdminGroups.cs Синхронизация групп с AD

Partial-классы ADSync (провайдеры данных)

Файл Роль
CachedUserProvider.cs Кэширующий провайдер пользователей (bulk-операции)
NonCachedUserProvider.cs Некэширующий провайдер (единичные операции)
CachedGroupsProvider.cs Кэширующий провайдер групп
NonCachedGroupProvider.cs Некэширующий провайдер групп
IUserProvider.cs Интерфейс провайдера пользователей
IGroupProvider.cs Интерфейс провайдера групп

Линковка DB ↔ AD: через SID (Users.SID = objectSid, Groups.ADSID = objectSid).

Фильтрация для синка: - Пользователи: SID != null && SID.Trim().Length > 0 && IsFired_2 == false - Группы: ADSID != null && ADSID.Trim().Length > 0 && CustomerID == current

2. ADSyncJob — порядок выполнения

Cron: DailyAtHourAndMinute(20, 00). Атрибут: [DisallowConcurrentExecution] + OS Mutex Global\ADSyncJob. Timeout транзакции: 300 минут. Контекст: SystemRobot.

Шаги SyncGroupsAndUsers()

# Шаг Флаг (SyncSettings) Метод ADSync
1 Создание отсутствующих групп SyncGroupCreation && SyncExistingUsers CreateAbsentGroups()
2 Создание новых пользователей из AD SyncUserDataBySchedule FetchActiveDirectoryUsers()
3 Синхронизация данных групп (SID, DisplayName, Domain) SyncGroupData && SyncExistingUsers SyncGroups()
4 Синхронизация данных пользователей (по маппингу) SyncUserData && SyncExistingUsers SyncUsers()
5 Синхронизация вложенности групп (GroupParents) SyncGroupNesting && SyncExistingUsers SyncNestingFromAD()
6 Синхронизация членства user-group SyncUserGroupMembership && SyncExistingUsers SyncActualUsersMembership()
7 Нормализация (SP) SyncUserGroupMembership && SyncExistingUsers NormalizeGroupUsersMembershipProcedureService.Launch()
8 Проверка лимита строк MaximumADSyncRows throw TCLogicException если rows > limit
9 Перезагрузка кэша пользователей всегда TCUser.ReloadAllUsersCache()
10 Smart events (созданные) для новых пользователей EventsActions.ExecuteUserActions(AfterUserWasCreated)
11 Smart events (обновлённые) для изменённых EventsActions.ExecuteUserActions(AfterUserWasUpdated)

Методы ADSync (детально)

Метод Что делает
CreateAbsentGroups() Читает маски из ADGroupSyncMasks, находит группы в AD, создаёт в Groups с заполненным ADSID
SyncGroups() Обновляет ADSID, DisplayName, Domain у существующих групп
SyncNestingFromAD() Синхронизирует GroupParents (parent-child связи групп)
SyncActualUsersMembership() Синхронизирует UserGroupsActual (прямое членство user-group)
SyncUsers() Обновляет поля пользователей по маппингу ADPropertyMapping
FetchActiveDirectoryUsers() Создаёт новых пользователей (фильтр по OU и UserAccountControl)
SyncUserInternal() Синк одного пользователя: Nick, SID, mapped-свойства, ext-свойства, оргструктура

3. База данных

Таблицы профилей и настроек

ServicesSettings (ServiceType=ActiveDirectory|OpenLDAP)
  ├── LDAPServicesCredentials (FK ServiceId)
  ├── OpenLDAPServicesCredentials (FK ServiceId)
  └── SynchronizationProfiles (FK ServiceId, UNIQUE)
        ├── SynchronizationProfilesADSettings (FK SynchronizationProfileId, 1:1)
        │     ├── SynchronizationProfilesADOrgUnits (FK SynchronizationProfileADSettingsId)
        │     └── ADGroupSyncMasks (FK SynchronizationProfileADSettingsId)
        └── ADPropertyMapping (FK SynchronizationProfileId)
Таблица Назначение
ServicesSettings Реестр внешних сервисов. ServiceType = ActiveDirectory, OpenLDAP, SAML, OAuth, Radius
LDAPServicesCredentials Домен, логин, пароль (зашифрован), IsDomainHasForestState
OpenLDAPServicesCredentials Аналогично для OpenLDAP
SynchronizationProfiles Профиль: IsActive, SyncType, ServiceId, CustomerId
SynchronizationProfilesADSettings Все флаги синхронизации (per-profile)
SynchronizationProfilesADOrgUnits Фильтр по OU
ADPropertyMapping Маппинг: OfProperty (поле 1Ф) → AdProperty (атрибут AD), per-profile
ADGroupSyncMasks Wildcards для автосоздания групп

AD-связанные поля в основных таблицах

Таблица Колонки Назначение
Users SID (VARCHAR 200, index IX_Users_SID), DomainController Идентификатор пользователя в AD
Groups ADSID (VARCHAR 8000), EnableADSync (computed: ADSID IS NOT NULL AND LEN > 0), Domain Идентификатор группы в AD
UserADNickHistory UserID, history data История смены AD-логинов

Хранимые процедуры

SP Назначение PG
tc_NormalizeGroupUsersMembership Рекурсивный CTE по GroupParents → нормализует UserGroups и GroupUsersMembership с учётом вложенности Нет
sp_SyncAutoManagedGroup Синхронизация автоуправляемых групп ?
RefreshUserNames Обновление кэша имён пользователей Да

tc_NormalizeGroupUsersMembership — алгоритм (4 шага)

  1. Вставляет отсутствующие записи в GroupUsersMembership из UserGroups
  2. CTE NestingMembershipProjection — рекурсивный обход GroupParents, добавляет membership с учётом вложенности
  3. Закрывает устаревшие записи membership (EndDate)
  4. Синхронизирует UserGroups: удаляет не подтверждённые через вложенность, добавляет новые

ORM-операции (без SP)

Операция Таблица MSSQL PG
Создание групп Groups DbContext.Groups.AddObject() (EF) GroupsEntityService.Insert() (LinqToDB)
Обновление групп Groups EF tracking + SaveChanges() GroupsEntityService.Update()
Вложенность GroupParents EntityService EntityService
Членство UserGroupsActual EntityService EntityService
Обновление пользователей Users DbContext.SaveChanges() UsersEntityService.Update()
Создание пользователей Users UserAdminService.AddUser() UserAdminService.AddUser()
Ext-свойства UserInfoExtValues EntityService EntityService

Настройки в таблице Settings (legacy, [Obsolete])

Колонка Default Мигрирована в
MaximumADSyncRows 10 SynchronizationProfilesADSettings.MaximumADSyncRows
ADSyncGroupCreation true .SyncGroupCreation
ADSyncGroupData true .SyncGroupData
ADSyncGroupNesting true .SyncGroupNesting
ADSyncUserData true .SyncUserData
ADSyncUserGroupMembership true .SyncUserGroupMembership
ADSyncOrganizationUnits NULL .SynchronizationProfilesADOrgUnits
ADSyncUserDataBySchedule false .SyncUserDataBySchedule
IncludeDomainInNick .IncludeDomainInNick
DomainController NULL LDAPServicesCredentials.DomainController
DomainControllerUser NULL LDAPServicesCredentials.DomainControllerUser
DomainControllerPassword NULL LDAPServicesCredentials.DomainControllerPassword

4. Флаги настроек (SynchronizationProfilesADSettings)

Флаг Тип Описание
SyncExistingUsers bool Главный выключатель. Все шаги (кроме 2) требуют true
SyncUserData bool Синхронизировать данные пользователей (шаг 4)
SyncGroupData bool Синхронизировать данные групп (шаг 3)
SyncGroupCreation bool Создавать отсутствующие группы (шаг 1)
SyncUserGroupMembership bool Синхронизировать членство user-group (шаги 6, 7)
SyncGroupNesting bool Синхронизировать вложенность групп (шаг 5)
CreateUsers bool Разрешить создание пользователей при логине
SyncUserDataBySchedule bool Создавать новых пользователей при schedule-синке (шаг 2)
SyncOnlyADEntity bool Синхронизировать только AD-сущности в оргструктуре
IncludeDomainInNick bool Добавлять домен к нику (DOMAIN\user)
MaximumADSyncRows int? Лимит строк (защита от массового повреждения). Default в Settings = 10
UsersADFilter string LDAP-фильтр пользователей (max 1000 символов)
GroupsADFilter string LDAP-фильтр групп
ChangePasswordException string Исключения смены пароля
EwsServiceSettingsId int? FK на EWS-настройки (Exchange)

5. Маппинг свойств AD → 1Ф

Стандартные AD-атрибуты

assistant, comment, company, department, description, displayName, division,
givenName, homePhone, mail, manager, mobile, sn, telephoneNumber, otherTelephone,
title, userPrincipalName, physicalDeliveryOfficeName, co, l

Блоки маппинга (UI)

Блок Поля 1Ф
Рабочая информация Динамические типы оргединиц (OrgUnitType{Id})
Личные данные Nick, LastName, FirstName, MiddleName, BirthDate, DisplayName, ImgAvatar, MaidenName, EnglishDisplayName, UserText
География Country, City, Room
Контакты Phone, Phone2, Phone3, CellPhone, HomePhone, Fax, Email, ExternalEmail, ICQ (Telegram), Skype, LiveJournal (—), Twitter (WhatsApp), SIP, TimeZone
Функционал BusinessFunctions
Прочее Notes

Расширенные свойства (UserInfoExt)

В SyncUserInternal(): для каждого UserInfoExt с заполненным AdProperty значение копируется из AD в UserInfoExtValues. Настраивается через вкладку «Расширенные настройки пользователя» в UI синхронизации.

6. REST API

SyncAdController — /api/sync-ad (admin)

HTTP Route Метод Сервис
GET profiles/{id}/settings GetProfileSettings SynchronizationProfilesService.GetAdSyncProfileSettings
POST profiles/{id}/settings/update Update SynchronizationProfilesService.UpdateSettings
GET profiles/{id}/domain/folders-tree GetDomainTree DomainEntriesTreeService.GetOrgUnitFoldersTree
GET profiles/{id}/domain/root-folder GetRootFolder DomainEntriesTreeService.GetDomainRootFolders
POST profiles/{id}/domain/folder GetRootFolder DomainEntriesTreeService.LoadFolder
POST profiles/{id}/domain/unload-users UnloadUsers LinkUsersService.UnloadUsers
POST profiles/{id}/users-for-link GetRootFolder LinkUsersService.GetUserForLink
POST link-users LinkUsers LinkUsersService.LinkUsers
GET ad-properties-names GetAdPropertiesNames SynchronizationPropertiesMappingService.GetAdPropertiesNames
GET profiles/{id}/sync-properties/blocks GetSynchronizationPropertyBlocks SynchronizationPropertiesMappingService.GetSynchronizationPropertyBlocks
POST profiles/{id}/sync-properties/update Update SynchronizationPropertiesMappingService.UpdateSynchronizationProperties
GET extra-sync-properties GetExtraSyncProperties SynchronizationPropertiesMappingService.GetExtraSyncPropirties
POST extra-sync-properties/update UpdateExtraSyncProperty SynchronizationPropertiesMappingService.UpdateExtraSyncProperty

LdapController — /api/admin/ldap (admin)

HTTP Route Метод Сервис
GET providers GetDomains SynchronizationProfilesService.GetActiveADSyncProfiles
POST search-users FindUserInLdap UserActiveDirectoryLookupService.FindADUserByName
POST search-groups FindGroupsInLdap UserActiveDirectoryLookupService.FindAdGroupByName

Прочие endpoints

HTTP Route Метод
POST /api/admin/users/{userId}/sync-with-ad UserManageController.SyncWithAd
GET /api/admin/groups/domains GroupsController.GetDomains
POST /api/admin/groups (sync all) GroupsController.SyncAllGroups
CRUD /api/admin/services-settings/{id} ServicesSettingsController (GET/POST/PUT/DELETE)

MCP-серверы

Сервер Тулов Доступ
mcp-admin-api-ldap 3 admin
mcp-user-api-v2-sync-ad 13 user
mcp-admin-api-services-settings 6 admin

SmartScript action: action_sync_user_with_a_d — параметры: param_0 (UserID), param_1 (SynchronizationProfileADDto, optional).

7. Конфигурация

Источник Ключ Описание
Configuration UsePostgreSQLDatabase Переключатель MSSQL/PG
Configuration ExcludeAdSubdomains Исключённые поддомены при навигации по дереву AD
Configuration SystemRobotId ID робота для контекста выполнения ADSyncJob
web.config ActiveDirectoryAuthenticationMode Режим аутентификации: DirectoryServices (default) или PrincipalContext (для одноимённых учёток в лесе)
SettingsCustom LDAP_AdGlobalCatalogHosts Оптимизация: список GC-хостов для ускорения загрузки дерева AD

8. MSSQL vs PostgreSQL — различия

Критический пробел: PG не поддерживает полный AD sync

Область MSSQL PG
Persistence EF ObjectContext (legacy) + SaveChanges() LinqToDB через EntityService
SP tc_NormalizeGroupUsersMembership Вызывается (шаг 7) Не вызывается (if (!db.IsPostgreDB))
Создание групп DbContext.Groups.AddObject() GroupsEntityService.Insert()
Обновление пользователей EF tracking + SaveChanges() UsersEntityService.Update() (AutoMapper)
SaveChanges() в финале Да Нет (if (!Configuration.UsePostgreSQLDatabase))

Последствия отсутствия tc_NormalizeGroupUsersMembership на PG: - Вложенность групп не нормализуется после синхронизации - UserGroups не получает записи, основанные на рекурсивном обходе GroupParents - GroupUsersMembership не ведёт историю - Создание новых групп из AD на PG не работает

9. Поток данных

Active Directory (LDAP)              1Forma DB
=======================              =========

Users                                Users
  objectSid         ──────────────►    SID
  sAMAccountName    ──────────────►    Nick
  givenName         ──────────────►    FirstName
  sn                ──────────────►    LastName
  mail              ──────────────►    Email
  (mapped attrs)    ──────────────►    (mapped fields)
  (ext attrs)       ──────────────►    UserInfoExtValues

Groups                               Groups
  objectSid         ──────────────►    ADSID
  displayName       ──────────────►    Descr
  (domain)          ──────────────►    Domain

Membership
  group.member      ──────────────►    UserGroupsActual (прямое)
                    ──[SP]────────►    UserGroups (развёрнутое с учётом вложенности)
                    ──[SP]────────►    GroupUsersMembership (история)

Nesting
  group.memberOf    ──────────────►    GroupParents (parent-child)

10. Диагностика

SQL-запросы для проверки состояния AD sync

-- Активные профили синхронизации
SELECT sp.Id, ss.Description, ss.ServiceType,
       ads.SyncUserData, ads.SyncGroupData, ads.SyncGroupCreation,
       ads.SyncUserGroupMembership, ads.SyncGroupNesting,
       ads.MaximumADSyncRows
FROM SynchronizationProfiles sp
JOIN ServicesSettings ss ON sp.ServiceId = ss.Id
JOIN SynchronizationProfilesADSettings ads ON ads.SynchronizationProfileId = sp.Id
WHERE sp.IsActive = 1;

-- Пользователи с SID (привязаны к AD)
SELECT COUNT(*) AS TotalLinked FROM Users WHERE SID IS NOT NULL AND LEN(LTRIM(RTRIM(SID))) > 0;

-- Группы с AD sync
SELECT GroupID, Descr, ADSID, Domain, EnableADSync
FROM Groups WHERE EnableADSync = 1;

-- Маски для создания групп
SELECT m.Wildcard, ads.SynchronizationProfileId
FROM ADGroupSyncMasks m
JOIN SynchronizationProfilesADSettings ads ON m.SynchronizationProfileADSettingsId = ads.Id;

-- Маппинг свойств
SELECT OfProperty, AdProperty, Entity
FROM ADPropertyMapping WHERE SynchronizationProfileId = @profileId;

-- Последний запуск ADSyncJob (Quartz)
SELECT TOP 1 * FROM QRTZ_FIRED_TRIGGERS WHERE JOB_NAME LIKE '%ADSync%' ORDER BY FIRED_TIME DESC;

Типовые проблемы

Симптом Причина Диагностика
Группы не создаются из AD PG: SP отсутствует; MSSQL: маска не задана или SyncGroupCreation = false Проверить флаги + маски + UsePostgreSQLDatabase
Пользователи не синхронизируются SyncExistingUsers = false (главный выключатель) SELECT SyncExistingUsers FROM SynchronizationProfilesADSettings
Silent failure (нет ошибок, нет изменений) MaximumADSyncRows = 10 (default), rows > limit → rollback Проверить значение лимита, увеличить
Дубликаты SID → exception В Users две записи с одинаковым SID SELECT SID, COUNT(*) FROM Users GROUP BY SID HAVING COUNT(*) > 1
NullReferenceException в GlobalCatalogHostsForce Некорректная настройка LDAP_AdGlobalCatalogHosts Разбор: ServiceFlow/adsync-nullref-fix.md
Фильтр OU не работает Баг: при включённом фильтре OU пользователи не создаются Разбор: ServiceFlow/archive/adsync-ou-filter-broken.md

11. Связанные документы

Документ Содержание
../integrations/support-guide-1c.md Пользовательская документация: настройки синхронизации, маппинг, фильтры
архивной инструкции по AD-синхронизации пользователей Возможности и ограничения, последовательность настройки, удаление записей
admin.md Настройки сервиса AD
архивной инструкции по AD-синхронизации групп Синхронизация групп
docs/domains/auth/support-guide-ad-sso.md Support guide для ТП 1-й линии
docs/domains/auth/admin.md Таблицы, dbadmin-формы, контроллеры
docs/domains/auth/database.md Схема таблиц auth
docs/platform/quartz-jobs.md ADSyncJob в реестре заданий
ServiceFlow/adsync-nullref-fix.md Разбор NullReferenceException в GlobalCatalogHostsForce
ServiceFlow/archive/adsync-ou-filter-broken.md Разбор бага с фильтром OU

12. Известные ограничения и edge cases

  1. PG: нет tc_NormalizeGroupUsersMembership — вложенность групп не нормализуется автоматически. Создание новых групп из AD на PG не работает.
  2. Два параллельных пути persistence — на MSSQL: EF ObjectContext (legacy), на PG: LinqToDB через EntityService. Код содержит if (UsePostgreSQLDatabase) ветвления.
  3. OS Mutex Global\ADSyncJob — защита от параллельного ручного + автоматического запуска.
  4. Транзакция 300 минут — при большом количестве пользователей может не хватить.
  5. MaximumADSyncRows default = 10 — очень мало для реальных площадок, часто вызывает silent rollback.
  6. SID-дубликатыCachedUserProvider бросает исключение при дубликатах SID в БД.
  7. Динамические группы AD не поддерживаются (п.15 users-ad.md).
  8. Символы # и & в логинах/паролях — запрещены, вызывают ошибку при синхронизации.
  9. ActiveDirectoryAuthenticationMode — при одноимённых учётках в лесе AD нужен PrincipalContext, иначе ошибки аутентификации.
  10. Refresh-token invalidation — при смене пароля в AD фоновый процесс (UserAuthenticationProviderVerificationJob, каждые 15 мин) автоматически инвалидирует ранее выданные токены.