В предыдущих статьях (раз и два) мы говорили о том, как получить схему БД центра сертификации и как получать определённые свойства каждой записи в БД. Но очень часто нам нужно будет ограничивать вывод БД по каким-то критериям. Например, мы можем хотеть получать сведения о запросах на сертификат, которые выданы на определённое имя (common name). А можем и хотеть получать сведения о ещё не одобренных запросах (хранящихся в папке Pending Requests). А может хотим отфильтровать вывод по дате, когда заканчивается срок действия сертификата. Вобщем, хотелок может быть очень много. Самый простой вариант фильтрации — получить все строки из БД, а потом через Where-Object отфильтровать нужные. Практически во всех случаях это будет плохим решением, потому что оно медленное, создаёт нагрузку на БД и очень ресурсоёмким.
Решение этой проблемы мы можем возложить на COM интерфейсы, которые будут самостоятельно извлекать только те данные, которые нам нужны (подпадают под определённый фильтр). Для этого у ICertView2 есть метод SetRestriction:
CCertView.SetRestriction( _
ByVal ColumnIndex, _
ByVal SeekOperator, _
ByVal SortOrder, _
ByVal pvarValue _
)
В свойстве ColumnIndex мы указываем номер колонки или название таблицы. Если мы захотим посмотреть запросы сертификатов, находящиеся в папке Pending Requests мы можем указать таблицу CV_COLUMN_QUEUE_DEFAULT, а если только отклонённые/ошибочные запросы, то таблицу CV_COLUMN_LOG_FAILED_DEFAULT. Вот значения для этих параметров:
- CV_COLUMN_QUEUE_DEFAULT = –1
- CV_COLUMN_LOG_DEFAULT = –2
- CV_COLUMN_LOG_FAILED_DEFAULT = –3
Как видите, при использовании отрицательных значений мы задаём фильтр на уровне таблиц. Если хотим фильтровать на уровне столбцов (т.е. значение определённого столбца соответствует чему-то), значение этого аргумента должно быть натуральным и это значение должно быть равно номеру столбца. Как мы уже знаем, номер столбца можно получить при помощи метода GetColumnIndex.
Дальнейшие аргументы имеют смысл только если ColumnIndex является натуральным числом. SeekOperator задаёт уровень сравнения и они достаточно понятно расписаны в таблице. Единственное, что тут хочу отметить — это числовые значения операторов сравнения:
- CVR_SEEK_NONE = 0x0 (должно быть указано, если ColumnIndex не является натуральным числом)
- CVR_SEEK_EQ = 0x1
- CVR_SEEK_LT = 0x2
- CVR_SEEK_LE = 0x4
- CVR_SEEK_GE = 0x8
- CVR_SEEK_GT = 0x10
Далее идёт аргумент, задающий порядок сортировки:
- CVR_SORT_NONE = 0
- CVR_SORT_ASCEND = 1
- CVR_SORT_DESCEND = 2
По большому счёту этот аргумент использовать не нужно, особенно учитывая, что иногда его использовать нельзя. И последний аргумент указывает маску, которой должен соответствовать фильтр.
Возьмём пример, мы хотим получить все строки БД, у которых столбец Request Common Name равен "contoso-dc2-ca" (сертификаты выданные подчинённому CA). Для этого в ColumnIndex укажем 60 (номер столбца CommonName), в SeekOperator укажем 1, а в pvarValue укажем значение, которому должно оно соответствовать: contoso-dc2-ca.
Давайте попробуем создать такой фильтр. Сначала обязательная часть по инициализации интерфейса и определения отображаемых столбцов выходных объектов.
$CaView = New-Object -ComObject CertificateAuthority.View
$CaView.OpenConnection("dc1\contoso ca")
$properties = "RequestID","RequesterName","CommonName","NotBefore","NotAfter","SerialNumber"
$CaView.SetResultColumnCount($properties.Count)
$properties | %{$CAView.SetResultColumn($CAView.GetColumnIndex($False, $_))}
Примечание: столбец, по которому устанавливается фильтр не обязательно должен присустствовать в выходе. Вы можете вообще показывать только столбец RequestID, а фильтры устанавливать по другим столбцам.
И сам фильтр:
# получаем номер столбца, по которому будет производиться фильтрация
$RColumn = $CAView.GetColumnIndex($False, "CommonName")
# устанавливаем сам фильтр
$CaView.SetRestriction($RColumn,1,0,"contoso-dc2-ca")
По умолчанию фильтр устанавливается на таблице CV_COLUMN_LOG_DEFAULT, которая содержит запросы сертификатов, содержащихся в папках Revoked Certificates, Issued Certificates и Failed Requests. Мы не можем в пределах одного фильтра указать конкретную таблицу. Но вы можете использовать несколько фильтров одновременно и каждый из них будет обрабатываться по очереди с применением логического оператора И. Т.е. в результате мы получим только те строки БД, которые подпадают под каждый фильтр. Если строка не соответствует какому-то фильтру, она будет отфильтрована и не будет отображена на выходе. Поэтому я вам покажу ещё один вариант полезного фильтра, который фильтрует строки БД по папкам (Revoked Certificates, Issued Certificates, Pending Requests, FailedRequests). В БД есть такой столбец под названием Disposition. Этот столбец имеет тип Long и вот какие полезные значения может принимать:
- 9 – запрос ожидает одобрения (находится в папке Pending Requests);
- 12 – архивированный сторонний сертификат;
- 15 – обновление сертификата CA;
- 16 – цепочка сертификатов текущего CA;
- 20 – сертификат успешно выдан (находится в папке Issued Certificates);
- 21 – сертификат отозван (находится в папке Revoked Certificates);
- 30, 31 — запросы, хранящиеся в папке Failed Requests.
Поэтому если мы хотим получить выданные и неотозванные сертификаты на имя contoso-dc2-ca, мы создадим ещё один фильтр по столбцу Disposition (и значением равным 20):
$RColumn = $CAView.GetColumnIndex($False, "Disposition")
$CaView.SetRestriction($RColumn,1,0,20)
Если мы хотим добавить фильтр, что сертификат должен быть действующий, т.е. столбец NotAfter должен быть больше, чем текущие дата и время:
$RColumn = $CAView.GetColumnIndex($False, "NotAfter")
$CaView.SetRestriction($RColumn,0x10,0,[datetime]::Now)
Когда с фильтрами покончено, можно начинать шахматы:
$Row = $CaView.OpenView()
while ($Row.Next() -ne -1) {
$cert = New-Object psobject
$Column = $Row.EnumCertViewColumn()
while ($Column.Next() -ne -1) {
$current = $Column.GetName()
$Cert | Add-Member -MemberType NoteProperty $($Column.GetDisplayName()) -Value $($Column.GetValue(1)) -Force
}
$Cert
$Column.Reset()
}
$Row.Reset()
и что мы получили на выходе:
Issued Request ID : 21
Requester Name : CONTOSO\Administrator
Issued Common Name : contoso-DC2-CA
Certificate Effective Date : 2009.03.30. 13:56:53
Certificate Expiration Date : 2011.03.30. 14:06:53
Serial Number : 6127fbc7000000000015
Issued Request ID : 40
Requester Name : CONTOSO\Administrator
Issued Common Name : contoso-DC2-CA
Certificate Effective Date : 2010.03.06. 10:56:53
Certificate Expiration Date : 2015.03.05. 10:56:53
Serial Number : 158f4a3b000100000028
Issued Request ID : 42
Requester Name : CONTOSO\Administrator
Issued Common Name : contoso-DC2-CA
Certificate Effective Date : 2010.03.06. 11:10:31
Certificate Expiration Date : 2015.03.05. 11:10:31
Serial Number : 159bc07a00010000002a
т.е. мы получили только те строки БД, которые подпали под каждый указанный фильтр. Если запустить этот же код 1 апреля, мы уже не получим строку под номером 21, потому что NotAfter (Certificate Expiration Date) не будет больше, чем текущее время. На сегодня всё.