セキュリティ記述子(SecurityDescriptor)の出力の.NETクラス使用版の C# のコードです。
Active Directoryデータのプロパティ出力のCOM対応版(C#)と比較しながら見ていただければと思います。
コードはサンプルアプリに組み込む前提で書いてます。
追加で System.Security.AccessControl 名前空間をインポートしてます。
まずは呼出し側。出力部分はメソッド化しました。
public static void OutputProperties(DirectoryEntry entry, string filePath)
{
var props = entry.Properties.PropertyNames.Cast<string>().OrderBy(s => s).ToList(); //プロパティ名のリスト
using (var writer = new StreamWriter(filePath, false, Encoding.UTF8))
{
foreach (var pname in props) //プロパティ数分
{
if (pname.Equals("nTSecurityDescriptor")) //セキュリティ記述子の時
{
writer.WriteLine(pname);
OutputSecurityDescriptor(entry.ObjectSecurity, writer); //セキュリティ記述子を出力
continue;
}
var val = entry.Properties[pname].Value;
if (val is byte[]) //バイト配列の時
{
var pstr = GetByteValue(pname, (byte[])val); //バイト値を取得
writer.WriteLine("{0}:{1}", pname, pstr);
}
else if (val is IADsLargeInteger) //大きい整数の時
{
var li = GetLargeIntegerValue(pname, (IADsLargeInteger)val); //大きい整数値を取得
writer.WriteLine("{0}:{1}", pname, li);
}
else //それ以外の時
{
foreach (var pval in entry.Properties[pname]) //値数分
{
var value = GetValue(pval); //値を取得
writer.WriteLine("{0}:{1}", pname, value);
}
}
}
}
}
続いて出力処理側。DirectoryEntry.ObjectSecurityプロパティのとこに少し書きましたが、Trustee の名前部分(ユーザ名やグループ名)を取得する必要があるので、別途メソッド化しました。
//セキュリティ記述子を出力
private static void OutputSecurityDescriptor(ActiveDirectorySecurity security, StreamWriter writer)
{
var sd = new CommonSecurityDescriptor(true, true, security.GetSecurityDescriptorBinaryForm(), 0);
var aceList = sd.DiscretionaryAcl.Cast<QualifiedAce>().ToList();
var sidList = aceList.GroupBy(ace => ace.SecurityIdentifier).Select(
group => group.First().SecurityIdentifier.Value).ToList();
var sidNameDic = CreateSidNameDictionary(sidList); //SID/アカウント名のコレクションを作成
var aceGroups = aceList.OrderBy(ace => sidNameDic[ace.SecurityIdentifier.Value]).ThenBy(
ace => ace.AccessMask).ThenBy(ace => ace.AceFlags).ThenBy(ace => ace.AceType).ThenBy(
ace => (ace is ObjectAce) ? ((ObjectAce)ace).ObjectAceFlags : 0).GroupBy(
ace => String.Format("{0}|{1}|{2}|{3}|{4}",
sidNameDic[ace.SecurityIdentifier.Value], ace.AccessMask, ace.AceFlags, ace.AceType,
(ace is ObjectAce) ? ((ObjectAce)ace).ObjectAceFlags : 0)).ToList(); //プロパティ値でグループ化したACE
var ctr = 0;
foreach (var aceGroup in aceGroups) //ACE数分
{
var ace = aceGroup.First();
ctr++;
writer.WriteLine(" {0:D2}. Trustee :{1}", ctr, sidNameDic[ace.SecurityIdentifier.Value]);
writer.WriteLine(" {0:D2}. AccessMask:{1}", ctr, ToEnumValueText(ace.AccessMask, typeof(ActiveDirectoryRights)));
writer.WriteLine(" {0:D2}. AceFlags :{1}", ctr, ToEnumValueText((int)ace.AceFlags, typeof(AceFlags)));
writer.WriteLine(" {0:D2}. AceType :{1}", ctr, ToEnumValueText((int)ace.AceType, typeof(AceType)));
if (ace is ObjectAce) //ディレクトリ オブジェクトに関連付けられたACEの時
{
writer.WriteLine(" {0:D2}. Flags :{1}", ctr,
ToEnumValueText((int)((ObjectAce)ace).ObjectAceFlags, typeof(ObjectAceFlags)));
}
else //CommonAce(ACE)の時
{
writer.WriteLine(" {0:D2}. Flags :0", ctr);
}
}
}
//SID/アカウント名のコレクションを作成
private static Dictionary<string, string> CreateSidNameDictionary(List<string> sidList)
{
var sidNameDic = new Dictionary<string, string>(sidList.Count);
using (var root = GetRootEntry()) //ルートのDirectoryEntryを取得
{
using (var searcher = new DirectorySearcher(root))
{
foreach (var sid in sidList) //SID数分
{
searcher.Filter = String.Format("(objectSid={0})", sid);
var res = searcher.FindOne(); //検索
if (res == null) //見つからなかった時(SYSTEM、SELF、Everyoneなど)
{
var acc = new SecurityIdentifier(sid).Translate(typeof(NTAccount)); //アカウントに変換
sidNameDic.Add(sid, System.IO.Path.GetFileName(acc.Value));
}
else //見つかった時
{
using (var entry = res.GetDirectoryEntry())
{
var objectType = (CategoryType)Enum.Parse(typeof(CategoryType), entry.SchemaClassName, true);
if (objectType == CategoryType.ForeignSecurityPrincipal) //外部のセキュリティプリンシパルの時
{
var acc = new SecurityIdentifier(sid).Translate(typeof(NTAccount)); //アカウントに変換
sidNameDic.Add(sid, System.IO.Path.GetFileName(acc.Value));
}
else //外部のセキュリティプリンシパル以外の時
{
sidNameDic.Add(sid, entry.Properties["cn"].Value.ToString());
}
}
}
}
}
}
return sidNameDic;
}
最後に既存のメソッド。内部実装を変更しました。
//列挙体のプロパティ値をテキスト化
private static string ToEnumValueText(int value, Type enumType)
{
if (enumType == typeof(AceType)) //AceTypeの時
{
return String.Format("{0}({1})", value, Enum.ToObject(enumType, value));
}
Func<int, string> selector = e => Enum.ToObject(enumType, e).ToString();
var values = Enum.GetValues(enumType).Cast<object>().Select(e => Convert.ToInt32(e)).Where(
e => (value & e) == e).OrderBy(selector).Select(selector).ToList(); //設定されている値の列挙体文字列
values.Remove("None");
if (values.Count == 0) //設定されている値がない時
{
return value.ToString();
}
return String.Format("{0}({1})", value, String.Join(" | ", values));
}