1

Тема: HTA: Active Directory Objects Finder

В общем то я всегда обходился консольной dsquery для поиска и Русиновичевским ADExplorer для детализации.
Мелкософтовский RSAT с его "Центром администрирования" использую крайне редко (уж больно он тяжелый).
А тут товарищу понадобилось находить некие атрибуты учеток которые через стандартный аплет "Active Directory Users and Computers" найти чрезвычайно сложно.
Вот тогда и была написана это HTA-шка.
Помимо собственно пользователей, которых можно искать как по логину так и по имени (или части его) ищет компьютеры, группы, контакты и другие объекты AD.
Результаты снабжены названием типа объекта и раскрашены разными цветами, так что не перепутаете.
Выбрав в комбобоксе с результатами конкретный объект можно просмотреть имена и значения всех его атрибутов.
Пока выводятся только необработанные строковые данные. В будущем планируется преобразовывать их к более наглядному виду.
Также в планах преобразование данных других типов, расширение критериев поиска, подключение к другоиу домену и т.д. и пр.
Если есть желание подключится к совместной разработке - Welcome!
Код старался писать максимально наглядно чтобы желающие могли использовать его в своих злостных целях.
Тут выкладываю самый первый вариант поскольку в нем вся работа с AD написана на vbscript, который многим кажется проще.

<html>
<head>
<meta http-equiv=content-type content="text-html; charset=utf-8">
<meta http-equiv=MSThemeCompatible content=yes>
<hta:application
	id="App"
	applicationName="Active Directory Objects Finder"
	innerBorder="no"
	icon="usercpl.dll"
	scroll="no"
	singleInstance="yes"
	version="0.0.0"
	autor="mozers™"
 />
<style type="text/css">
	* {font:10pt verdana;}
	body {margin:0px;}
	table {border-width:0; border-collapse:collapse;}
	td {white-space:nowrap;}
	.top {height:80px; font:10px courier new; background-color:buttonface;}
	.top td {padding:0px 4px 0px 8px;}
	#idResult td {font:8pt MS Shell Dlg; padding:0px 2px 0px 4px; border-bottom:1px dotted gray;}
</style>
<script type="text/javascript">
	document.title = App.applicationName + ' v.' + App.version;
	window.resizeTo(900,500);

	// Добавляет новый пункт с данными заданного объекта в комбобокс
	function AddOption (sLDAP) {
		var oOption = document.createElement("option");
		oOption.value = sLDAP;
		oOU = GetObject(sLDAP);
		oOption.text = '[' + oOU.Class + '] ' + oOU.cn;
		switch(oOU.Class){
			case 'user':     oOption.style.color = "green"; break;
			case 'computer': oOption.style.color = "blue"; break;
			case 'group':    oOption.style.color = "gray"; break;
		}
		idSelResult.add(oOption);
	}

	// Добавляет новую строчку с данными content1 и content2 в таблицу свойств-значений
	function AddRow (content1, content2) {
		var r = idResult.insertRow();
		r.insertCell().innerText = content1;
		r.insertCell().innerText = content2;
	}

	// Удаляет все дочерние элементы заданного узла
	function RemoveChildren(node) {
		while (node.firstChild) node.removeChild(node.firstChild);
	}

	// Показ всех свойств-значений выбраного в комбобоксе объекта AD
	function ShowADObject() {
		RemoveChildren(idResult);
		if (idSelResult.options.length){
			EnumerateADObject(idSelResult.options[idSelResult.selectedIndex].value);
		}
	}

	// Запуск поиска объектов
	function FindADObjects(){
		RemoveChildren(idSelResult);
		GetADInfo(idObjName.value);
		ShowADObject();
	}
</script>
</head>
<body onload="idObjName.focus();" style="width:100%;">
	<table style="height:100%; width:100%">
		<tr>
			<td class="top">
				<table style="width:100%;">
					<tr>
						<td style="width:100%;"><input id="idObjName" onkeydown="if (event.keyCode==13) FindADObjects()" type="text" style="width:100%;"></td>
						<td><button onClick="FindADObjects()" hidefocus><b>Поиск</b></button></td>
					</tr>
					<tr>
						<td style="width:100%;"><select id="idSelResult" onselect="ShowADObject()" onchange="ShowADObject()" style="width:100%;"></select></td>
						<td><b>[<span id="idCount">0</span>]</b></td>
					</tr>
				</table>
				<hr size="1px">
			</td>
		</tr>
		<tr>
			<td>
				<div style="height:100%; width:100%; overflow-y:scroll; overflow-x:hidden;">
					<table id="idResult" style="width:100%;"></table>
				</div>
			</td>
		</tr>
	</table>
</body>
<script type="text/vbscript">
	' Возвращает информацию о объектах Active Directory
	' Входные данные: часть имени объекта (cn или sAMAccountName)
	' Найденные LDAP Strigs помещает в комбобокс
	Sub GetADInfo(str)
		Set ADOConnection = CreateObject("ADODB.Connection")
		ADOConnection.Provider = "ADsDSOObject"
		ADOConnection.Open "Active Directory Provider"
		strDNSDomain = GetObject("LDAP://RootDSE").Get("defaultNamingContext")

		Set adoCommand = CreateObject("ADODB.Command")
		adoCommand.ActiveConnection = ADOConnection
		adoCommand.Properties("Page Size") = 1000
		adoCommand.Properties("Searchscope") = 2 'ADS_SCOPE_SUBTREE
		adoCommand.CommandText = "SELECT * FROM 'LDAP://" & strDNSDomain & "' WHERE cn='" & str & "*' OR sAMAccountName='" & str & "*'"
		Set adoRecordSet = adoCommand.Execute
		idCount.innerText = adoRecordSet.RecordCount
		If adoRecordSet.RecordCount = 0 Then Exit Sub

		adoRecordSet.MoveFirst
		Do Until adoRecordSet.EOF
			AddOption adoRecordSet(0).Value
			adoRecordSet.MoveNext
		Loop

		ADOConnection.Close
	End Sub

	' Перечисляет все свойства выбранного объекта AD
	Sub EnumerateADObject(sLDAP)
		Set oAD = GetObject(sLDAP)
		Set oSchema = GetObject(oAD.Schema)
		For Each prop In oSchema.mayContain
			GetProperty oAD, prop
		Next
	End Sub

	' Пробует извлечь из Obj значение c именем name
	' Если получается - дополняет сторкой таблицу имясвойства - значение
	' TODO: Далеко не все значения хранятся как String. Необходимо дописать обработку других типов данных.
	Sub GetProperty(obj, name)
		On Error Resume Next
		v = obj.Get(name)
		If Err.Number <> 0 Then Exit Sub
		AddRow name, CStr(v)
	End Sub
</script>
</html>

Доработанная и обновляемая версия (уже на чистом javascript) лежит тут.
Конечно жду критики и предложений. Ради этого все и делается...

2

Re: HTA: Active Directory Objects Finder

для поиска в JScript можно добавить условие поиска по displayName

var aFilters0 = ["(cn="+str+"*)", "(sAMAccountName="+str+"*)","(displayName="+str+"*)"];

3 (изменено: mozers, 2016-04-21 23:43:11)

Re: HTA: Active Directory Objects Finder

2badik
Верное предложение, принимается!
Еще реализовал просмотр многострочных значений, GUID-ов, фотографий пользователей, данных времени.
Ну и вообще поправил алгоритм - сейчас извлекается гораздо больше атрибутов.

4

Re: HTA: Active Directory Objects Finder

Серый форум, помог, конвертирует object, возвращенный запросом к LDAP, в  дату
было:

else if (typeof(val) == 'object')    aValues[j] = '[object]';

стало(надо):

else if (typeof(val) == 'object')    {
try {
 var res,intLastLogonTime = val.HighPart* (4294967296) + val.LowPart;
 res = intLastLogonTime / 10000;
 res = res - Math.abs((new Date(1601,0,1)).getTime());
 aValues[j]=new Date(res).toLocaleString()
} catch(e)
 {
  aValues[j] = '[object]';
 }
}

5

Re: HTA: Active Directory Objects Finder

Сдается мне что 4294967296 - не константа. Недаром же её в примере от мелкософта из реестра выуживают

Function MakeDate(oLInt)
	Set objShell = CreateObject("Wscript.Shell")
	lngBiasKey = objShell.RegRead("HKLM\System\CurrentControlSet\Control\TimeZoneInformation\ActiveTimeBias")
	If UCase(TypeName(lngBiasKey)) = "LONG" Then
		glngBias = lngBiasKey
	ElseIf UCase(TypeName(lngBiasKey)) = "VARIANT()" Then
		glngBias = 0
		For k = 0 To UBound(lngBiasKey)
			glngBias = lngBias + (lngBiasKey(k) * 256 ^ k)
		Next
	End If
	dtmDate = #1/1/1601# + (((oLInt.HighPart * (2 ^ 32)) + oLInt.LowPart) / 600000000 - glngBias) / 1440
	MakeDate = dtmDate
End Function

Потом проверка показала, что не все атрибуты типа 'object' и успешно обрабатываемые этим алгоритмом возвращают внятную дату. В общем - видимо придется фильтровать по имени конкретные атрибуты, другие дата-'object'ы надо обрабатывать как то иначе...

6

Re: HTA: Active Directory Objects Finder

Наконец то выкроил время чтобы разобраться с датами. В качестве эталона использовал Sysinternals ADExplorer.
ActiveTimeBias - текущий часовой пояс. В VBS нет встроенных функций для его извлечения, поэтому авторы скрипта (выше) тянут его из реестра.
В JS все решается на порядок проще:

function GetTime(val) {
	var i8High = val.HighPart;
	var i8Low = val.LowPart;
	if (i8Low < 0) i8High += 1;
	if ((i8High == 0) && (i8Low == 0)) {
		return '0';
	} else {
		var LargeIntegerToDate = (i8High * 4294967296 + i8Low) / 10000 + Date.UTC(1601,0,1);
		return new Date(LargeIntegerToDate).toLocaleString();
	}
}

Так извлекаются даты из атрибутов badPasswordTime, lastLogoff, lastLogon, lastLogonTimestamp.
В остальных атрибутах (таких как dSCorePropagationData, msExchWhenMailboxCreated, whenChanged, whenCreated) тоже необходимо учитывать таймзону:


var a = new Date(val).valueOf();
var b = new Date().getTimezoneOffset()*60000;
return new Date(a-b).toLocaleString();

Единственный неучтенный момент - это когда дата была создана в таймзоне, отличной от текущей. Тут мой алгоритм ошибается (в отличии от ADExplorer).
Закомиттил так как есть (замучили уже эти даты, есть и другие интересные атрибуты). Если кто предложит лучшее решение - буду благодарен.

7 (изменено: Flasher, 2016-05-05 20:21:36)

Re: HTA: Active Directory Objects Finder

mozers пишет:

ActiveTimeBias - текущий часовой пояс. В VBS нет встроенных функций для его извлечения

>>>

strComputer = "."
Set WMI = GetObject("winmgmts:\\" & strComputer & "\root\CIMV2")
For Each Item in WMI.ExecQuery("SELECT Bias, Caption, Description, DayLightName, StandardName FROM Win32_TimeZone")
   Wscript.Echo "------------------------------"               & vbCr &_
                "Win32_TimeZone instance"                      & vbCr &_
                "------------------------------"               & vbCr &_
                "Bias:"    & vbTab & vbTab & Item.Bias         & vbCr &_
                "Caption:" & vbTab & vbTab & Item.Caption      & vbCr &_
                "Description:"     & vbTab & Item.Description  & vbCr &_
                "DayLight Name:"   & vbTab & Item.DayLightName & vbCr &_
                "Standard Name:"   & vbTab & Item.StandardName
Next

8

Re: HTA: Active Directory Objects Finder

2Flasher
Можно и так, можно и как в примере 2 постами выше. Но это же не встроенные функции, как Date.getTimezoneOffset() в JS.

9

Re: HTA: Active Directory Objects Finder

Я понимаю. В комбинированном HTA особо и нет смысла заморачиваться. А так ...:

With CreateObject("ScriptControl") .Language = "JScript" : b = .Eval("new Date().getTimezoneOffset()*60000") End With