C# を使用して Active Directory に変更通知を登録する
-
18-09-2019 - |
質問
このリンク http://msdn.microsoft.com/en-us/library/aa772153(VS.85).aspx 言います:
単一の LDAP 接続で最大 5 つの通知リクエストを登録できます。通知を待機し、通知を迅速に処理する専用のスレッドが必要です。ldap_search_ext 関数を呼び出して通知要求を登録すると、関数はその要求を識別するメッセージ識別子を返します。次に、ldap_result 関数を使用して変更通知を待ちます。変更が発生すると、サーバーは、通知を生成した通知リクエストのメッセージ識別子を含む LDAP メッセージを送信します。これにより、ldap_result 関数は、変更されたオブジェクトを識別する検索結果を返します。
.NET ドキュメントを調べても同様の動作が見つかりません。C# でこれを行う方法を知っている人がいたら、教えていただければ幸いです。システム内のすべてのユーザーの属性がいつ変更されるかを確認して、変更内容に応じてカスタム アクションを実行できるようにしたいと考えています。
stackoverflow やその他のソースを調べましたが、うまくいきませんでした。
ありがとう。
解決
必要なことを行うかどうかはわかりませんが、見てください http://dunnry.com/blog/ImplementingChangeNotificationsInNET.aspx
編集:記事からテキストとコードを追加しました。
Active Directory (または ADAM) での変更を確認するには 3 つの方法があります。これらは、MSDN で、適切なタイトルの「」に長い間文書化されてきました。変更追跡手法の概要」。要約すれば:
- uSNChanged を使用した変更のポーリング. 。この手法では、最初に「highestCommittedUSN」値をチェックし、その後、より高い「uSNChanged」値の検索を実行します。「uSNChanged」属性はドメイン コントローラー間でレプリケートされないため、一貫性を保つために毎回同じドメイン コントローラーに戻る必要があります。基本的に、最も高い「uSNChanged」値 + 1 を探す検索を実行し、任意の方法でそれらを追跡する結果を読み取ります。
- 利点
- これが最も互換性のある方法です。これは単純な検索であるため、.NET のすべての言語とすべてのバージョンがこの方法をサポートしています。
- 短所
- ここには開発者が対処しなければならないことがたくさんあります。オブジェクト全体を取得し、オブジェクトで何が変更されたのか (そしてその変更が重要かどうか) を判断する必要があります。
- 削除されたオブジェクトを扱うのは面倒です。
- これはポーリング手法であるため、クエリの頻度に応じてリアルタイムになります。これはアプリケーションによっては良いことになる可能性があります。ここでも中間値は追跡されないことに注意してください。
- 利点
- DirSync コントロールを使用した変更のポーリング。この手法では、ADSI の ADS_SEARCHPREF_DIRSYNC オプションと内部で LDAP_SERVER_DIRSYNC_OID コントロールを使用します。最初の検索を行って Cookie を保存し、後で再度検索して Cookie を送信するだけです。変更されたオブジェクトのみが返されます。
- 利点
- これは従うのが簡単なモデルです。System.DirectoryServices と System.DirectoryServices.Protocols は両方ともこのオプションをサポートしています。
- フィルタリングを行うことで、面倒なことを減らすことができます。たとえば、最初の検索がすべてのユーザー「(objectClass=user)」に対するものである場合、その後は「(sn=dunn)」を使用してポーリングでフィルタリングし、両方のフィルタの組み合わせのみを取得できます。初期フィルターからすべて。
- Windows 2003 以降のオプションでは、このオプション (オブジェクト セキュリティ) の使用に関する管理上の制限が解除されます。
- Windows 2003 以降のオプションでは、大きな複数値の属性で変更された増分値のみを返す機能も提供されます。これは本当に素晴らしい機能です。
- 削除されたオブジェクトを適切に処理します。
- 短所
- これは .NET 2.0 以降のみのオプションです。.NET 1.1 のユーザーは、uSNChanged Tracking を使用する必要があります。スクリプト言語ではこの方法を使用できません。
- 検索の範囲をパーティションにのみ設定できます。特定の OU またはオブジェクトのみを追跡する場合は、後でそれらの結果を自分で整理する必要があります。
- これを Windows 2003 モード以外のドメインで使用するには、使用するレプリケーションの変更取得権限 (デフォルトでは管理者のみ) が必要であるという制限があります。
- これはポーリング技術です。中間値も追跡しません。したがって、追跡するオブジェクトが複数回の検索の間に変更された場合、最後の変更のみが取得されます。これは、アプリケーションによっては利点となる場合があります。
- 利点
- Active Directory での変更通知。この手法では、フィルターに一致するオブジェクトが変更されたときに通知を受け取る別のスレッドに検索を登録します。非同期接続ごとに最大 5 つの通知を登録できます。
- 利点
- 即時通知。他の手法ではポーリングが必要です。
- これは通知であるため、他の 2 つの手法では失われていた中間の変更も含め、すべての変更が取得されます。
- 短所
- 比較的リソースを大量に消費します。コントローラーのスケーラビリティの問題を引き起こす可能性があるため、これらを大量に実行することは望ましくありません。
- これは、オブジェクトが変更されたかどうかのみを示しますが、変更が何であったかはわかりません。関心のある属性が変更されたかどうかを確認する必要があります。そうは言っても、オブジェクトが削除されたかどうかを判断するのは非常に簡単です (少なくとも uSNChanged ポーリングよりは簡単です)。
- これは、アンマネージ コードまたは System.DirectoryServices.Protocols を使用してのみ実行できます。
- 利点
ほとんどの場合、DirSync が事実上あらゆる状況で私にとって最適であることがわかりました。他のテクニックを試してみようとは思いませんでした。しかし、読者から、.NET で変更通知を行う方法はないかとの質問がありました。SDS.P を使用すれば可能だとは思いましたが、試したことはありませんでした。結局のところ、それは可能であり、実際にはそれほど難しいことではありません。
これを書くにあたって最初に考えたのは、 サンプルコード MSDN で見つけて (オプション #3 から参照)、これを System.DirectoryServices.Protocols に変換するだけです。これは行き止まりであることが判明した。SDS.P で行う方法とサンプル コードの動作方法は大きく異なるため、役に立ちません。私が思いついた解決策は次のとおりです。
public class ChangeNotifier : IDisposable
{
LdapConnection _connection;
HashSet<IAsyncResult> _results = new HashSet<IAsyncResult>();
public ChangeNotifier(LdapConnection connection)
{
_connection = connection;
_connection.AutoBind = true;
}
public void Register(string dn, SearchScope scope)
{
SearchRequest request = new SearchRequest(
dn, //root the search here
"(objectClass=*)", //very inclusive
scope, //any scope works
null //we are interested in all attributes
);
//register our search
request.Controls.Add(new DirectoryNotificationControl());
//we will send this async and register our callback
//note how we would like to have partial results
IAsyncResult result = _connection.BeginSendRequest(
request,
TimeSpan.FromDays(1), //set timeout to a day...
PartialResultProcessing.ReturnPartialResultsAndNotifyCallback,
Notify,
request);
//store the hash for disposal later
_results.Add(result);
}
private void Notify(IAsyncResult result)
{
//since our search is long running, we don't want to use EndSendRequest
PartialResultsCollection prc = _connection.GetPartialResults(result);
foreach (SearchResultEntry entry in prc)
{
OnObjectChanged(new ObjectChangedEventArgs(entry));
}
}
private void OnObjectChanged(ObjectChangedEventArgs args)
{
if (ObjectChanged != null)
{
ObjectChanged(this, args);
}
}
public event EventHandler<ObjectChangedEventArgs> ObjectChanged;
#region IDisposable Members
public void Dispose()
{
foreach (var result in _results)
{
//end each async search
_connection.Abort(result);
}
}
#endregion
}
public class ObjectChangedEventArgs : EventArgs
{
public ObjectChangedEventArgs(SearchResultEntry entry)
{
Result = entry;
}
public SearchResultEntry Result { get; set;}
}
これは、検索の登録に使用できる比較的単純なクラスです。重要なのは、コールバック メソッドで GetPartialResults メソッドを使用して、発生したばかりの変更のみを取得することです。結果を返すために使用している非常に単純化された EventArgs クラスも含めました。ここではスレッド処理については何も行っておらず、エラー処理も行っていないことに注意してください (これは単なるサンプルです)。このクラスは次のように使用できます。
static void Main(string[] args)
{
using (LdapConnection connect = CreateConnection("localhost"))
{
using (ChangeNotifier notifier = new ChangeNotifier(connect))
{
//register some objects for notifications (limit 5)
notifier.Register("dc=dunnry,dc=net", SearchScope.OneLevel);
notifier.Register("cn=testuser1,ou=users,dc=dunnry,dc=net", SearchScope.Base);
notifier.ObjectChanged += new EventHandler<ObjectChangedEventArgs>(notifier_ObjectChanged);
Console.WriteLine("Waiting for changes...");
Console.WriteLine();
Console.ReadLine();
}
}
}
static void notifier_ObjectChanged(object sender, ObjectChangedEventArgs e)
{
Console.WriteLine(e.Result.DistinguishedName);
foreach (string attrib in e.Result.Attributes.AttributeNames)
{
foreach (var item in e.Result.Attributes[attrib].GetValues(typeof(string)))
{
Console.WriteLine("\t{0}: {1}", attrib, item);
}
}
Console.WriteLine();
Console.WriteLine("====================");
Console.WriteLine();
}