【Access VBA 秘伝の書】CurrentDb.Containersで暴くデータベースの「所有者」と「権限」の闇
こんにちは。チーフアーキテクトの私だ。
現場で「Accessが急に動かなくなった」「誰がこのテーブルのアクセス権限を変更したんだ?」という阿鼻叫喚のトラブルに直面したことはないだろうか。
Accessのセキュリティ管理、特にMDB/ACCDB内部のオブジェクト所有者(Owner)や権限(Permissions)の監査において、システムテーブル(`MSysObjects`など)を直接SQLで叩くような愚行をしていないだろうか?
直叩きは、Accessの内部構造の変化に極めて脆弱であり、何よりDAO(Data Access Objects)が提供する正規のオブジェクトモデルを無視したアンチパターンだ。
今回は、DAOの `Containers` コレクションを駆使し、データベース内の全オブジェクトの所有者と権限を安全かつ高速に監査・管理する、プロダクション品質のコードを授けよう。
—
なぜ `CurrentDb.Containers` なのか?
Access VBAにおいて、データベースのメタデータを取得するアプローチは大きく分けて2つある。
1. システムテーブル(`MSysObjects`等)の直接参照
- 【評価:最悪】 内部仕様への依存度が高く、バージョンアップで突然動かなくなるリスクがある。読み取り専用であり、権限の制御には使えない。
2. DAOオブジェクトモデル(`CurrentDb.Containers` / `Documents`)の活用
- 【評価:至高】 DAOが公式に提供する抽象レイヤー。エンジン内部の変更からコードを保護し、オブジェクトの所有者(Owner)やセキュリティ属性(Permissions)に直接アクセスできる唯一の安全な手段である。
特に、レガシーなADP(Accessプロジェクト)が廃止され、ACCDB主流となった現在でも、DAOのコンテナ構造を理解しているか否かで、エンタープライズレベルのAccess開発者か、単なるマクロ職人かが分かれる。
—
押さえておくべきDAOセキュリティの3つの鉄則
`Containers` を操作する前に、DAOのセキュリティモデルにおける重要な仕様を頭に叩き込んでおいてほしい。
1. コンテナ(Container)とドキュメント(Document)の概念
- `Containers` コレクションには、「Tables」「Forms」「Reports」「Modules」「Scripts」などのカテゴリ(コンテナ)が格納されている。
- 各コンテナの中には、具体的なオブジェクト名を持つ `Documents` コレクションが存在する。
2. 所有者(Owner)の重要性
- オブジェクトを作成したユーザーやグループが `Owner` プロパティに格納される。権限トラブルの多くはこの所有者の意図しない変更に起因する。
3. 実行時のパフォーマンスコスト
- `Containers` や `Documents` の走査は、内部的にシステムリソースを消費する。無駄なループを回さず、必要なプロパティだけをピンポイントで取得する設計が求められる。
—
【実践】オブジェクト権限・所有者 監査ツール
それでは、実務でそのまま使えるプロダクションコードを公開しよう。
このコードは、現在のデータベース内にある「テーブル」「フォーム」「レポート」などの主要コンテナを巡回し、各オブジェクトの名前と所有者をイミディエイトウィンドウに出力する監査ロジックだ。
さらに、実務でよくある「エラーハンドリングの欠落」や「オブジェクトの解放漏れ(メモリリーク)」を防ぐための堅牢な設計(RAII思想のVBA的実装)を取り入れている。
プロダクションコード
Option Compare Database
Option Explicit
‘ =========================================================================
‘ 模範的モジュール: データベースオブジェクトの所有者監査
‘ アーキテクト推奨: エラーハンドリングとリソース解放を完璧に担保した実装
‘ =========================================================================
Public Sub AuditDatabasePermissions()
Dim dbs As DAO.Database
Dim cnts As DAO.Containers
Dim cnt As DAO.Container
dim doc As DAO.Document
Dim targetContainers(3) As String
Dim i As Long
‘ 監査対象とするコンテナ名を定義(TablesはForms等とは挙動が異なるため注意)
targetContainers(0) = “Tables”
targetContainers(1) = “Forms”
targetContainers(2) = “Reports”
targetContainers(3) = “Modules”
On Error GoTo ErrorHandler
‘ CurrentDbは呼び出すたびに新しいインスタンスを生成するため、変数に格納して使い回す(鉄則)
Set dbs = CurrentDb()
Set cnts = dbs.Containers
Debug.Print “==================================================”
Debug.Print ” データベースオブジェクト 監査レポート”
Debug.Print ” 実行日時: ” & Now()
Debug.Print “==================================================”
‘ 指定したコンテナ群を走査
For i = LBound(targetContainers) To UBound(targetContainers)
‘ コンテナが存在するか安全に確認して処理
If ContainerExists(cnts, targetContainers(i)) Then
Set cnt = cnts(targetContainers(i))
Debug.Print vbCrLf & “■ コンテナ: ” & cnt.Name
Debug.Print String(40, “-“)
If cnt.Documents.Count = 0T Then
Debug.Print ” (オブジェクトなし)”
Else
For Each doc In cnt.Documents
‘ 各ドキュメント(オブジェクト)の名称と所有者を出力
‘ ※ UserNameプロパティやPermissionsプロパティをここで評価することも可能
Debug.Print ” [オブジェクト名]: ” & doc.Name & _
” | [所有者]: ” & SafeGetOwner(doc)
Next doc
End If
End If
Next i
Debug.Print vbCrLf & “==================================================”
Debug.Print ” 監査完了正常終了”
Debug.Print “==================================================”
CleanUp:
‘ ———————————————————————
‘ 【重要】オブジェクト変数の確実な解放
‘ Access VBAでは、DAOオブジェクトの解放漏れがメモリリークやロックの温床となる
‘ ———————————————————————
Set doc = Nothing
Set cnt = Nothing
Set cnts = Nothing
Set dbs = Nothing
Exit Sub
ErrorHandler:
MsgBox “予期せぬエラーが発生しました。” & vbCrLf & _
“Error: ” & Err.Number & ” – ” & Err.Description, vbCritical, “監査プロセス異常終了”
Resume CleanUp
End Sub
‘ =========================================================================
‘ 補助関数: コンテナの存在確認
‘ =========================================================================
Private Function ContainerExists(ByVal cnts As DAO.Containers, ByVal containerName As String) As Boolean
Dim c As DAO.Container
On Error Resume Next
Set c = cnts(containerName)
ContainerExists = (Err.Number = 0)
On Error GoTo 0
End Function
‘ =========================================================================
‘ 補助関数: 安全なOwnerプロパティの取得(システム保護オブジェクト対策)
‘ =========================================================================
Private Function SafeGetOwner(ByVal doc As DAO.Document) As String
Dim ownerName As String
On Error GoTo Catch
‘ システム内部の隠しオブジェクト等ではOwner取得時にエラーになることがあるためガード
ownerName = doc.Owner
SafeGetOwner = ownerName
Exit Function
Catch:
SafeGetOwner = “(取得不可/システム予約)”
On Error GoTo 0
End Function
—
アーキテクトからの実践的アドバイス
1. `CurrentDb` の乱用に気をつけろ
ループ内で `CurrentDb.Containers(…)` のように直接メソッドチェーンを書く初学者がいるが、あれは毎回DAOのインスタンスを生成・破棄しているため極めて低速だ。上記のコードのように、一度 `Set dbs = CurrentDb()` で変数に受け、そこから参照を辿るのがプロの作法だ。
2. システムオブジェクトの罠
`Tables` コンテナには、ユーザーが作成したテーブルだけでなく、Accessが内部で使う隠しテーブルも含まれる。権限変更や監査を行う際は、名前に `~` が含まれる一時テーブルや、`MSys` で始まるシステムテーブルをフィルタリングするロジックを前段に挟むと、より実用的なツールに仕上がるだろう。
3. セキュリティコンテキストの限界
ACCDB形式(ファイル共有ベース)におけるDAOセキュリティは、ワークグループファイル(.mdw)を用いた旧来のユーザーレベルセキュリティ(ULS)の遺産を一部引き継いでいる。現代の環境では、OSのファイル権限や、SQL Server等のバックエンドDB側の権限管理と組み合わせた多層防御を構築すべきだ。
—
結びに代えて
Accessは「手軽に作れる」がゆえに、こうしたオブジェクトモデルの深い部分がブラックボックス化されがちだ。しかし、プロのエンジニアとして現場を支えるのであれば、DAOの裏側で何が起きているのかを完全に掌握していなければならない。
今回の `CurrentDb.Containers` を利用したアプローチをあなたの引き出しに加え、属人化しがちなAccessデータベースのガバナンスを、その手でしっかりとコントロールしてほしい。
