«Безопасная» смена пароля пользователя AD.

Приветствую. После того как я полностью автоматизировал AD в организации, появилась острая необходимость в безопасной передаче пароля конечному пользователю. По регламенту организации сотрудники тех поддержки не должны видеть пароль но при этом должны как то его сбросить и передать. Решение было быстрым и тривиальным. Написал простенький инструмент. конвертировал его в EXE и отдал сотрудникам. Сразу же сообщу. В коде много обращений к API внутренних сервисов организации, у вас же это может сильно отличатся. например функции отправки СМС и получения инфы о пользователе завязаны на веб-серверах, у вас это может иметь другую реализацию. На худой конец, вы всегда можете закомментировать не нужные строки и пользоваться кор-функционалом с отправкой данных только на e-mail.

Тыкни
<#
Итак. Все обращения к базам данных HR у нас осуществляются по API. У вас же это может выполнятся разными способами. 
#>
$global:headers = @{
    "content-type"  = "application/json"
    "Authorization" = "Basic SOMEA123123123123123TOKEN"
}
# Запрос к API базы HR  
function Invoke-APIRequest 
{
    param (
        [Parameter(Mandatory)]
        [string]$Uri,
        [Parameter()]
        [string]$Method = 'POST',
        [Parameter()]
        [string]$Body
    )
    return Invoke-RestMethod -Uri $Uri -Method $Method -Headers $global:headers -Body $Body
}
# Получение списка сотрудников
function Get-Employees 
{
$body = @"
{
        "get_child_departments":"True"
}
"@
return $(Invoke-APIRequest -Uri 'https://hr.domain.site/api/employees' -Body $body)
}
# Получение всех Департаментов банка
function Get-Departments 
{
$body = @"
{
        "get_child_departments":"True"
}
"@
    return Invoke-APIRequest -Uri 'https://hr.domain.site/api/gate/department' -Body $body
}
# Очевидно
function get-userinf
{
$ipregex = "^((25[0-5]|(2[0-4]|1\d|[1-9]|)\d)\.?\b){4}$"
[string]$date = get-date -Format "yyyy.MM.dd hh-mm"
$ipaddress = Get-NetIPAddress | ? IPAddress -Match  $ipregex | select IPAddress
$return = @{
"Username" = $($env:USERNAME)
"ComputerName" = $($env:COMPUTERNAME)
"IPAdress" = $ipaddress.IPAddress
"ExecutionDate" = $($date)
}

return ConvertTo-Json $return
}
# Очевидно
function Check-User 
{
    param (
        [Parameter(Mandatory)]
        [string]$samaccountname = "ttestesteronyan"
    )

    try {
        # получаем пользователя
        $user = Get-ADUser -Filter * -Properties GivenName,Surname,Name,Department,Title,Mobile,wWWHomePage,EmployeeID,EmployeeNumber,whenCreated |`
              ? {if(($_.EmployeeID -eq $samaccountname) -or ($_.Name -eq $samaccountname) -or ($_.employeeNumber -eq $samaccountname) -or ($_.SamaccountName -eq $samaccountname)){$_}}
        $HRuser = (get-Employees).data `
                | ? {if(($_.employee_id -eq $samaccountname) -or ($_.Name -eq $samaccountname) -or ($_.system_id -eq $samaccountname) -or ($_.name -eq $user.name)){$_}} `
                | select firstname, lastname, Name, department_id, Employee_ID, email, phone_number ,System_ID 

        # собираем данные
        $output = @()
        if($user){
        $output += "* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *"
        $output += "Информация о пользователе $Username :"
        $output += "====================================="
        $output += "Логин: $($user.samaccountname)"
        $output += "Статус учетки пользователя: $(if($user.enabled){"включена"}else{"выключена"})"
        $output += "Имя: $($user.GivenName)"
        $output += "Фамилия: $($user.Surname)"
        $output += "ФИО: $($user.Name)"
        $output += "Департамент: $($user.Department)"
        $output += "Должность: $($user.Title)"
        $output += "Мобильный телефон: $($user.Mobile)"
        $output += "Личная почта: $($user.wWWHomePage)"
        $output += "Номер сотрудника: $($user.EmployeeID)"
        $output += "ID сотрудника: $($user.EmployeeNumber)"
        $output += "Дата создания: $($user.whenCreated)"
        $output += "====================================="
        }
        else{
        $output = @()
        }
        if($HRuser){
        $output += "Информация о пользователе $samaccountname в базе HR:"
        $output += "====================================="
        $output += "Имя: $($HRuser.firstname)"
        $output += "Фамилия: $($HRuser.lastname)"
        $output += "ФИО: $($HRuser.Name)"
        $output += "Департамент: $(((Get-Departments).data  | ? id -eq $HRuser.department_id).name)"
        $output += "Должность: $($HRuser.post)"
        $output += "Мобильный телефон: $($HRuser.phone_number)"
        $output += "Личная почта: $($HRuser.email)"
        $output += "Номер сотрудника: $($HRuser.Employee_ID)"
        $output += "ID сотрудника: $($HRuser.System_ID)"
        $output += "====================================="
        }

        if(!$user -and !$HRuser){
        return "ПОльзователь не найден ни в АД ни в базе кадров."
        }
        elseif($user -and !$HRuser){
        $output +=  "Пользователь не найден в базе кадров но есть в AD."
        return ($output -join "`r`n")        
        }
        elseif(!$user -and $HRuser){
        $output +=  "Пользователь не найден в AD но найден в базе кадров."
        return ($output -join "`r`n")        
        }
        else{
        return ($output -join "`r`n")        
        }
    }
    catch {
        return "Ошибка при получении информации о пользователе $Username : $_"
    }
}
# Очевидно
function Write-Log 
{
    param (
        [Parameter(ValueFromPipeline = $true)]
        [string]$Message,
        [Parameter(Mandatory = $true)]
        [string]$LogFilePath
    )
    process {
        $timestamp = (Get-Date).ToString("yyyy-MM-dd HH:mm:ss")
        $logEntry = "$($timestamp): $Message"
        Add-Content -Path $LogFilePath -Value $logEntry
    }
}
# Генерация пароля
function Get-RandomPassword 
{
    Param (
        [Parameter(Mandatory)]
        [int] $length
    )
    
    $CharSet = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'.ToCharArray()
    $SpecialCharSet = '!@'.ToCharArray()
    $rng = New-Object System.Security.Cryptography.RNGCryptoServiceProvider
    $bytes = New-Object byte[]($length)
    $rng.GetBytes($bytes)
    $result = New-Object char[]($length)
    
    # Генерация пароля без спецсимволов
    for ($i = 0 ; $i -lt $length ; $i++) {
        $result[$i] = $CharSet[$bytes[$i] % $CharSet.Length]
    }
    
    # Вставка специального символа в случайную позицию
    $randomIndex = Get-Random -Minimum 0 -Maximum $length
    $specialChar = $SpecialCharSet[$(Get-Random -Minimum 0 -Maximum $SpecialCharSet.Length)]
    $result[$randomIndex] = $specialChar
    
    return -join $result
}
# отправка смс по номеру телефона и мыло туда же
function ConvertTo-Lat 
{
    param ([string]$russian)
    $alphabet = @{
        "а" = "a"; "б" = "b"; "в" = "v"; "г" = "g"; "д" = "d";
        "е" = "e"; "ё" = "yo"; "ж" = "j"; "з" = "z"; "и" = "i";
        "й" = "i"; "к" = "k"; "л" = "l"; "м" = "m"; "н" = "n";
        "о" = "o"; "п" = "p"; "р" = "r"; "с" = "s"; "т" = "t";
        "у" = "u"; "ф" = "f"; "х" = "h"; "ц" = "c"; "ч" = "ch";
        "ш" = "sh"; "щ" = "sch"; "ь" = ""; "ъ" = ""; "ы" = "y";
        "э" = "e"; "ю" = "yu"; "я" = "ya"; " " = " ";
    }
    $russian_chars = $russian.ToCharArray()
    $russian_in_lat = ""
    foreach ($char in $russian_chars) {
        $stringChar = $char.ToString()
        $lowerChar = $stringChar.ToLower()
        if ($alphabet.ContainsKey($lowerChar)) {
            $translatedChar = $alphabet[$lowerChar]
            if ($stringChar -ne $lowerChar) {
                $translatedChar = $translatedChar[0].ToUpper() + $translatedChar.Substring(1)
            }
            $russian_in_lat += $translatedChar
        } else {
            $russian_in_lat += $char
        }
    }
    return $russian_in_lat
}
# получение токена авторизации для смс-шлюза
function Get-Token 
{
try{
$headers = New-Object "System.Collections.Generic.Dictionary[[String],[String]]"
$headers.Add("Content-Type", "application/json")
$body = @"
{
    `"username`": `"someUser`",
    `"password`": `"PrettyStronGPa$$w0rd`"
}
"@
$response = Invoke-RestMethod 'https://smsgtw.domain.site/api/auth/token' -Method 'POST' -Headers $headers -Body $body
return $response

}
catch{
return $Error[0].Exception.Message
}
}
# отправка смс по номеру телефона
function Send-SMS 
{
[CmdletBinding(SupportsShouldProcess = $true)]
param(
$AuthToken,
$phone_number,
$purpose,
$message
)

$headers = New-Object "System.Collections.Generic.Dictionary[[String],[String]]"
$headers.Add("Content-Type", "application/json")
$headers.Add("Authorization", "Bearer $AuthToken")

$body = @"
{
    `"purpose`": `"$purpose`",
    `"phone_number`": `"$phone_number`",
    `"message`": `"$message`",
}
"@

$response = Invoke-RestMethod 'https://smsgtw.domain.site/api/messages' -Method 'POST' -Headers $headers -Body $body 
$response.data.when_sent | ConvertTo-Json

}
# отправка смс по номеру телефона и мыло туда же
function Send-Notification 
{
    [CmdletBinding(SupportsShouldProcess = $true)]
    param(
        [string]   $Email,
        [string]   $PhoneNum,
        [string]   $EmailBody,
        [string]   $SmsBody
    )
    $p = ConvertTo-SecureString -String "PrettyStronGPa$$w0rd" -AsPlainText -Force 
    $u = "robot@domain.site"
    $smtpCredential = New-Object -TypeName System.Management.Automation.PSCredential -ArgumentList $u, $p

    $hasEmail = -not [string]::IsNullOrWhiteSpace($Email)
    $hasPhone = -not [string]::IsNullOrWhiteSpace($PhoneNum)

    if (-not $hasEmail -and -not $hasPhone) {
        return "Ошибка: не указан ни адрес электронной почты, ни номер телефона."
    }
    $combinedBody = $EmailBody, $SmsBody -join " | "

    $sentEmail = $false
    $sentSms   = $false

    try {
        if ($hasEmail -and $hasPhone) {
            Send-MailMessage -To $Email `
                             -From $u `
                             -Subject "Уведомление" `
                             -Body $EmailBody `
                             -SmtpServer "smtp.domain.site" `
                             -Credential $smtpCredential `
                             -UseSsl `
                             -Encoding UTF8

            $sentEmail = $true
            Send-SMS         -AuthToken (Get-Token).access_token `
                             -phone_number $PhoneNum `
                             -purpose "Send User's data" `
                             -message "$SmsBody"
            $sentSms = $true
        }
        elseif ($hasEmail) {
            Send-MailMessage -To $Email `
                             -From $u `
                             -Subject "Уведомление" `
                             -Body $combinedBody `
                             -SmtpServer "smtp.domain.site" `
                             -Credential $smtpCredential `
                             -UseSsl `
                             -Encoding UTF8
            $sentEmail = $true
        }
        else {
            Send-SMS         -AuthToken (Get-Token).access_token `
                             -phone_number $PhoneNum `
                             -purpose "Send User's data" `
                             -message "$((ConvertTo-Lat $combinedBody).replace("_"," "))"
            $sentSms = $true
        }

        return [pscustomobject]@{
            DateTime  = Get-Date
            EmailSent = $sentEmail
            SmsSent   = $sentSms
        }
    }
    catch {
        return $_.Exception
    }
}
# Смена пароля пльзователя
function Reset-ADUserPassword
{
param($samaccountname)
try{
    $chekADuser = Get-ADUser -Filter * -Properties GivenName,Surname,Name,Department,Title,MobilePhone,HomePage,EmployeeID,EmployeeNumber,whenCreated |`
                ? {if(($_.EmployeeID -eq $samaccountname) -or ($_.Name -eq $samaccountname) -or ($_.employeeNumber -eq $samaccountname) -or ($_.SamaccountName -eq $samaccountname)){$_}}
        if(!$chekADuser){
            $result = "$(get-date): Не найден пользователь"
        }
        else{
            if(![string]::IsNullOrWhiteSpace($chekADuser.HomePage) -and ![string]::IsNullOrWhiteSpace($chekADuser.MobilePhone)){
             $passsword = Get-RandomPassword -length 10
                 try{
                     Set-ADAccountPassword -Identity $chekADuser.DistinguishedName  `
                                           -NewPassword (ConvertTo-SecureString -String $passsword -AsPlainText -Force) `
                                           -Confirm:$false 
                     Set-ADUser $chekADuser.DistinguishedName -ChangePasswordAtLogon $true -Confirm:$false
                     Send-Notification -Email $chekADuser.HomePage `
                                       -PhoneNum $chekADuser.MobilePhone.Replace('+',"") `
                                       -EmailBody "Пароль пользователя $($chekADuser.samaccountname) был сброшен и отправлен на мобильный телефон."`
                                       -SmsBody "One time password: $passsword" 
                 $result = "$(get-date): Cмена пароля у пользователя $($chekADuser.samaccountname) прошла успешно, пароль отправлен и на E-mail и на мобильный телефон."
                    }
                 catch{
                     $result = "$(get-date): Произошла ошибка при сменен пароля у пользователя $($chekADuser.samaccountname): $($Error[0].exception.message)"
                 }
            }
            elseif(![string]::IsNullOrWhiteSpace($chekADuser.HomePage) -and [string]::IsNullOrWhiteSpace($chekADuser.MobilePhone)){
            # Это если нет телефона
            $passsword = Get-RandomPassword -length 10
                 try{
                     Set-ADAccountPassword -Identity $chekADuser.DistinguishedName  `
                                           -NewPassword (ConvertTo-SecureString -String $passsword -AsPlainText -Force) `
                                           -Confirm:$false 
                     Set-ADUser $chekADuser.DistinguishedName -ChangePasswordAtLogon $true -Confirm:$false
                     Send-Notification -Email $chekADuser.HomePage `
                                       -PhoneNum $chekADuser.MobilePhone `
                                       -EmailBody "Пароль пользователя $($chekADuser.samaccountname) был сброшен." `
                                       -SmsBody "One time password: $passsword"
                     $result = "$(get-date): Cмена пароля у пользователя $($chekADuser.samaccountname) прошла успешно, пароль отправлен на E-mail, МОБИЛЬНЫЙ НЕ УКАЗАН!!!."
                    }
                 catch{
                     $result = "$(get-date): Произошла ошибка при сменен пароля у пользователя $($chekADuser.samaccountname): $($Error[0].exception.message)"
                 }
            }
            elseif([string]::IsNullOrWhiteSpace($chekADuser.HomePage) -and ![string]::IsNullOrWhiteSpace($chekADuser.MobilePhone)){
            # Это если нет Email
            $passsword = Get-RandomPassword -length 10
                 try{
                     Set-ADAccountPassword -Identity $chekADuser.DistinguishedName  `
                                           -NewPassword (ConvertTo-SecureString -String $passsword -AsPlainText -Force) `
                                           -Confirm:$false 
                     Set-ADUser $chekADuser.DistinguishedName -ChangePasswordAtLogon $true -Confirm:$false
                     Send-Notification -Email $chekADuser.HomePage `
                                       -PhoneNum $chekADuser.MobilePhone.Replace('+',"") `
                                       -EmailBody "Hi!"`
                                       -SmsBody "$($chekADuser.samaccountname) | one time password: $passsword" 
                    $result = "$(get-date): Cмена пароля у пользователя $($chekADuser.samaccountname) прошла успешно, пароль отправлен на мобильный телефон, ЭЛЕКТРОННАЯ ПОЧТА НЕ УКАЗАНА!!!."
                    }
                 catch{
                    $result = "$(get-date): Произошла ошибка при сменен пароля у пользователя $($chekADuser.samaccountname): $($Error[0].exception.message)"
                 }
                
            }
            elseif([string]::IsNullOrWhiteSpace($chekADuser.HomePage) -and [string]::IsNullOrWhiteSpace($chekADuser.MobilePhone)){
            # Это если нет Email и телефона
                  $result = "$(get-date): Cмена пароля у пользователя $($chekADuser.samaccountname) отменена, мобильный телефон и электронная почта не указаны в AD и в базе HR!!!."  
            }
            else{
            # Любая другая херня
                $result = "$(get-date): Произошла ошибка при сменен пароля у пользователя необходимо уточнение данных по этому пользователю."
            }
        }
        }
    catch [System.Management.Automation.CommandNotFoundException]{
    $result = "$(get-date): У вас не установлены средства управления AD! Вы точно привелигированный пользователь?? На всякий случай я отправил ваши данные в ДИБ!!!"
    $whois = get-userinf
    Send-Notification -Email alert@domain.site `
                                  -PhoneNum $null `
                                  -EmailBody "Обнаружена попытка сменить пароль пользователя $samaccountname на компьютере с отсутвующими инструментами администрирования AD!!! Возьмите 'на карандаш'!" `
                                  -SmsBody $whois
    }
return ($result  + "`r`n")
}

#

Add-Type -AssemblyName System.Windows.Forms
Add-Type -AssemblyName System.Drawing
# Форма
$form = New-Object System.Windows.Forms.Form
$form.Text = "Сброс пароля пользователя"
$form.Size = New-Object System.Drawing.Size(700,400)
$form.StartPosition = "CenterScreen"
$form.FormBorderStyle = 'FixedDialog'
$form.MaximizeBox = $false

# Метка
$label = New-Object System.Windows.Forms.Label
$label.Text = "Введите логин, или ФИО, или ID пользователя"
$label.Location = New-Object System.Drawing.Point(20,20)
$label.AutoSize = $true
$form.Controls.Add($label)

# Поле ввода
$textBox = New-Object System.Windows.Forms.TextBox
$textBox.Location = New-Object System.Drawing.Point(20,50)
$textBox.Size = New-Object System.Drawing.Size(300,20)
$form.Controls.Add($textBox)

# Кнопка «Сменить пароль!»
$resetButton = New-Object System.Windows.Forms.Button
$resetButton.Text = "Сменить пароль!"
$resetButton.Location = New-Object System.Drawing.Point(510,45)
$resetButton.Size = New-Object System.Drawing.Size(120,30)
$form.Controls.Add($resetButton)

# Кнопка «Проверить пользователя»
$checkButton = New-Object System.Windows.Forms.Button
$checkButton.Text = "Проверить пользователя"
$checkButton.Location = New-Object System.Drawing.Point(340,45)
$checkButton.Size = New-Object System.Drawing.Size(160,30)
$form.Controls.Add($checkButton)

# Текстовое поле для логов
$logBox = New-Object System.Windows.Forms.TextBox
$logBox.Location = New-Object System.Drawing.Point(20,100)
$logBox.Size = New-Object System.Drawing.Size(640,220)
$logBox.Multiline = $true
$logBox.ScrollBars = "Vertical"
$logBox.ReadOnly = $true
$form.Controls.Add($logBox)

# Прогрессбар ниже logBox, не налезает
$progressBar = New-Object System.Windows.Forms.ProgressBar
$progressBar.Location = New-Object System.Drawing.Point(20,340)  # чуть ниже logBox
$progressBar.Size = New-Object System.Drawing.Size(640,20)       # по ширине logBox
$progressBar.Style = 'Marquee'
$progressBar.Visible = $false
$form.Controls.Add($progressBar)

# Действие кнопки
$resetButton.Add_Click({
    $username = $textBox.Text
    if ([string]::IsNullOrWhiteSpace($username)) {
        $logBox.AppendText("$(get-date):* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *`r`n")
        $logBox.AppendText("$(get-date):Введите логин пользователя!`r`n")
        return
    }

    $logBox.AppendText("$(get-date): Сброс пароля для пользователя $username...`r`n")
    try {
        # Вызов твоей функции
        $logBox.AppendText("$(get-date):* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *`r`n")
        $result = Reset-ADUserPassword -samaccountname $username
        $logBox.AppendText($result + "`r`n")
    }
    catch {
        $logBox.AppendText("$(get-date):* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *`r`n")
        $logBox.AppendText("$(get-date): Ошибка: $_`r`n")
    }
})

# Действие кнопки проверки пользователя
$checkButton.Add_Click({
    $progressBar.Visible = $true
    $form.Refresh()
    $username = $textBox.Text
    if ([string]::IsNullOrWhiteSpace($username)) {
        $logBox.AppendText("$(get-date):* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *`r`n")
        $logBox.AppendText("$(get-date): Введите логин пользователя!`r`n")
        $progressBar.Visible = $false
        return
    }
    $logBox.AppendText("$(get-date): Проверка пользователя $username...`r`n")
    try {
        $result = Check-User -samaccountname $username
        $logBox.AppendText("$(get-date):* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *`r`n")
        $logBox.AppendText($result + "`r`n")
    }
    catch {
        $logBox.AppendText("$(get-date):* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *`r`n")
        $logBox.AppendText("Ошибка: $_`r`n")
    }
    $progressBar.Visible = $false
})

# Показ формы
$form.Add_Shown({$form.Activate()})
[void]$form.ShowDialog()

Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *