Quantcast
Channel: わんくま同盟
Viewing all articles
Browse latest Browse all 994

Active Directoryデータのプロパティ出力のCOM対応版(VB)

$
0
0

以前書いた Active Directory データのプロパティ出力(VBC#スライド) の COM 対応版です。

なんで "大きい整数" 対応版にしてないかというと、"大きい整数" で表される IADsLargeInteger のほかにも COM(ADSI オブジェクト)があるからです。

それはセキュリティ記述子です。これの属性は nTSecurityDescriptor で IADsSecurityDescriptor として扱え、値をこのインターフェイスにキャストできます。

nTSecurityDescriptor 属性は通常のプロパティ(DirectoryEntry.Properties)で取得できます。オプションのプロパティでは取得できません。

 

以前からの変更点

 ・セキュリティ記述子を見やすい形で出力する

 ・"大きい整数" を数値や日付で出力する

 ・構造化例外処理を使わないで値がないかを確認する

 ・COM じゃない値(バイト配列や日付)も正しく出力する

 

説明は別途ということで まずはコードを。

 

Public Shared Sub OutputProperties(entry AsDirectoryEntry, filePath As String)

  Dim props = entry.Properties.PropertyNames.Cast(Of String)().OrderBy(Function(s) s).ToList()  'プロパティ名のリスト

  Using writer As NewStreamWriter(filePath, False, Encoding.UTF8)

    For Each pname In props   'プロパティ数分

      Dim val = entry.Properties.Item(pname).Value

      If TypeOf val Is Byte() Then  'バイト配列の時

        Dim pstr = GetByteValue(pname, DirectCast(val, Byte()))   'バイト値を取得

        writer.WriteLine("{0}:{1}", pname, pstr)

      ElseIf TypeOf val Is IADsSecurityDescriptorThen  'セキュリティ記述子の時

        Dim sd = DirectCast(val, IADsSecurityDescriptor)

        Dim aceGroups = DirectCast(sd.DiscretionaryAcl, IADsAccessControlList).Cast(OfIADsAccessControlEntry)().OrderBy(

          Function(ace) ace.Trustee).ThenBy(Function(ace) ace.AccessMask).ThenBy(

          Function(ace) ace.AceFlags).ThenBy(Function(ace) ace.AceType).ThenBy(Function(ace) ace.Flags).GroupBy(

          Function(ace) String.Format("{0}|{1}|{2}|{3}|{4}",

            ace.Trustee, ace.AccessMask, ace.AceFlags, ace.AceType, ace.Flags)).ToList()  'プロパティ値でグループ化したACE

        Dim ctr = 0

        writer.WriteLine(pname)

        For Each aceGroup In aceGroups  'ACE数分

          Dim ace = aceGroup.First()

          ctr += 1

          writer.WriteLine(" {0:D2}. Trustee   :{1}", ctr, ace.Trustee)

          writer.WriteLine(" {0:D2}. AccessMask:{1}", ctr, ToEnumValueText(ace.AccessMask, GetType(ADS_RIGHTS_ENUM)))

          writer.WriteLine(" {0:D2}. AceFlags  :{1}", ctr, ToEnumValueText(ace.AceFlags, GetType(ADS_ACEFLAG_ENUM)))

          writer.WriteLine(" {0:D2}. AceType   :{1}", ctr, ToEnumValueText(ace.AceType, GetType(ADS_ACETYPE_ENUM)))

          writer.WriteLine(" {0:D2}. Flags     :{1}", ctr, ToEnumValueText(ace.Flags, GetType(ADS_FLAGTYPE_ENUM)))

        Next

      ElseIf TypeOf val Is IADsLargeIntegerThen  '大きい整数の時

        Dim li = GetLargeIntegerValue(pname, DirectCast(val, IADsLargeInteger))  '大きい整数値を取得

        writer.WriteLine("{0}:{1}", pname, li)

      Else  'それ以外の時

        For Each pval In entry.Properties.Item(pname)   '値数分

          Dim value = GetValue(pname, pval)   '値を取得

          writer.WriteLine("{0}:{1}", pname, value)

        Next

      End If

    Next

  End Using

End Sub

 

Public Shared Sub OutputOptionalProperties(entry AsDirectoryEntry, filePath As String)

  Dim schema = DirectCast(entry.SchemaEntry.NativeObject, IADsClass'スキーマ オブジェクト

  Dim props = DirectCast(schema.OptionalProperties, Object())   'オプションのプロパティ

  Using writer As NewStreamWriter(filePath, False, Encoding.UTF8)

    entry.Invoke("GetInfoEx", props, 0)   'プロパティをディレクトリ ストアから読込

    For Each pname As String In props  'オプションのプロパティ数分

      Dim pvcol = entry.Properties.Item(pname)  'PropertyValueCollection

      If pvcol.Value Is Nothing Then  '値がない時

        writer.WriteLine("{0}:<未設定>", pname)

        Continue For

      End If

 

      If TypeOf pvcol.Value Is Byte() Then  'バイト配列の時

        Dim bstr = GetByteValue(pname, DirectCast(pvcol.Value, Byte()))   'バイト値を取得

        writer.WriteLine("{0}:{1}", pname, bstr)

      ElseIf TypeOf pvcol.Value Is IADsLargeIntegerThen  '大きい整数の時

        Dim li = GetLargeIntegerValue(pname, DirectCast(pvcol.Value, IADsLargeInteger))  '大きい整数値を取得

        writer.WriteLine("{0}:{1}", pname, li)

      Else  'それ以外の時

        For Each pval In pvcol  '値数分

          If TypeOf pval Is Byte() Then  'バイト配列の時

            Dim pstr = GetByteValue(pname, DirectCast(pval, Byte()))   'バイト値を取得

            writer.WriteLine("{0}:{1}", pname, pstr)

          Else  'バイト配列以外の時

            Dim value = GetValue(pname, pval)   '値を取得

            writer.WriteLine("{0}:{1}", pname, value)

          End If

        Next

      End If

    Next

  End Using

End Sub

 

'バイト値を取得

Private Shared Function GetByteValue(name As String, value As Byte()) As String

  If name.Equals("objectSid") Then

    Return NewSecurityIdentifier(value, 0).ToString()

  ElseIf name.Equals("objectGUID") Then

    Return NewGuid(value).ToString()

  Else

    ReturnBitConverter.ToString(value)

  End If

End Function

 

'大きい整数値を取得

Private Shared Function GetLargeIntegerValue(name As String, value As IADsLargeInteger) As Object

  Dim lval = Convert.ToInt64(value.HighPart.ToString("x8") & value.LowPart.ToString("x8"), 16)

  If name.Equals("lockoutTime") Then  'ロックアウトしたことがある時

    If lval > 0 Then  'ロックアウト中又はロックアウト期間が過ぎただけの時

      Return String.Format("{0}({1})", lval, DateTime.FromFileTime(lval))  '判り易いよう日付も付ける

    End If

    Return lval   '明示的にロック解除又はロックアウト期間が過ぎてログオン成功しているので値は0

  End If

 

  If IsInteger(name, lval) Then   '整数の時

    Return lval

  End If

  If (lval = 0) OrElse (lval = Int64.MaxValue) Then

    Return"(なし)"

  End If

  Return DateTime.FromFileTime(lval)  'eq. #1/1/1601#.AddTicks(lval).ToLocalTime()

End Function

 

'整数かどうか

Private Shared Function IsInteger(name As String, value As Long) As Boolean

  If name.Equals("maxStorage") Then

    Return True

  End If

  If name.StartsWith("msDS-") Then

    Return False

  End If

 

  If name.StartsWith("uSN") Then

    Return True

  End If

  Return False

End Function

 

'値を取得

Private Shared Function GetValue(name As String, value As Object) As Object

  If (TypeOf value Is Date) = False Then  '日付ではない時

    Return value

  End If

 

  Dim d = Convert.ToDateTime(value)

  Return If(d.Date.Equals(#1/1/1601#), d, d.ToLocalTime())

End Function

 

'列挙体のプロパティ値をテキスト化

Private Shared Function ToEnumValueText(value As Integer, enumType AsType) As String

  Dim selector = Function(e As Integer) [Enum].ToObject(enumType, e).ToString()

  Dim values = [Enum].GetValues(enumType).Cast(Of Integer)().Where(

    Function(e) (value And e) = e).OrderBy(selector).Select(selector).ToList()  '設定されている値の列挙体文字列

 

  If values.Count = 0Then  '設定されている値がない時

    Return value.ToString()

  End If

 

  'AceType(ADS_ACETYPE_ENUM)の時にビットマスクがかぶってしまうので、列挙値が小さい方を削除

  Dim total = values.Select(Function(e) Convert.ToInt32([Enum].Parse(enumType, e))).Sum(Function(e) e)

  If value <> total Then  'ビットマスクがかぶっている時

    values.Remove([Enum].ToObject(enumType, total - value).ToString())

  End If

  Return String.Format("{0}({1})", value, String.Join(" | ", values))

End Function


Viewing all articles
Browse latest Browse all 994

Trending Articles



<script src="https://jsc.adskeeper.com/r/s/rssing.com.1596347.js" async> </script>