Kreye Itilizatè Google soti nan PowerShell atravè API

Hi!

Atik sa a pral dekri aplikasyon entèraksyon PowerShell ak API Google pou manipile itilizatè G Suite.

Nou itilize plizyè sèvis entèn ak nwaj atravè òganizasyon an. Pifò nan yo, otorizasyon yo soti nan Google oswa Active Directory, ant sa nou pa ka kenbe yon kopi; kòmsadwa, lè yon nouvo anplwaye kite, ou bezwen kreye/pèmèt yon kont nan de sistèm sa yo. Pou otomatize pwosesis la, nou deside ekri yon script ki kolekte enfòmasyon epi voye li nan tou de sèvis yo.

Otorizasyon

Lè nou fè egzijans yo, nou te deside sèvi ak administratè imen reyèl pou otorizasyon; sa a senplifye analiz aksyon yo nan ka ta gen chanjman aksidantèl oswa entansyonèl masiv.

API Google yo itilize pwotokòl OAuth 2.0 pou otantifikasyon ak otorizasyon. Ou ka jwenn ka itilize ak deskripsyon plis detay isit la: Sèvi ak OAuth 2.0 pou jwenn aksè nan API Google yo.

Mwen te chwazi script ki itilize pou otorizasyon nan aplikasyon pou Desktop yo. Genyen tou yon opsyon yo sèvi ak yon kont sèvis, ki pa mande pou mouvman nesesè nan men itilizatè a.

Foto ki anba a se yon deskripsyon schematic senaryo chwazi a nan paj Google la.

Kreye Itilizatè Google soti nan PowerShell atravè API

  1. Premyèman, nou voye itilizatè a nan paj otantifikasyon kont Google la, ki espesifye paramèt GET:
    • id aplikasyon
    • zòn aplikasyon an bezwen aksè
    • adrès la kote itilizatè a pral redireksyon apre li fin ranpli pwosedi a
    • fason nou pral mete ajou siy la
    • Kod sekirite
    • fòma transmisyon kòd verifikasyon

  2. Apre otorizasyon fin ranpli, itilizatè a pral redireksyon sou paj ki espesifye nan premye demann lan, ak yon erè oswa kòd otorizasyon pase pa paramèt GET.
  3. Aplikasyon an (script) ap bezwen resevwa paramèt sa yo epi, si yo resevwa kòd la, fè demann sa a pou jwenn siy
  4. Si demann lan kòrèk, API Google retounen:
    • Aksè token ak ki nou ka fè demann
    • Peryòd validite siy sa a
    • Rafrechi siy obligatwa pou rafrechi siy Aksè a.

Premye ou bezwen ale nan konsole Google API: Kalifikasyon - Google API Console, chwazi aplikasyon ou vle a epi nan seksyon Kredansyèl yo kreye yon idantifyan OAuth kliyan. Gen (oswa pita, nan pwopriyete yo nan idantifyan an kreye) ​​ou bezwen presize adrès yo nan ki redireksyon pèmèt. Nan ka nou an, sa yo pral plizyè antre localhost ak pò diferan (gade anba a).

Pou fè li pi fasil pou li algorithm nan script, ou ka montre premye etap yo nan yon fonksyon separe ki pral retounen Aksè ak rafrechi marqueur pou aplikasyon an:

$client_secret = 'Our Client Secret'
$client_id = 'Our Client ID'
function Get-GoogleAuthToken {
  if (-not [System.Net.HttpListener]::IsSupported) {
    "HttpListener is not supported."
    exit 1
  }
  $codeverifier = -join ((65..90) + (97..122) + (48..57) + 45 + 46 + 95 + 126 |Get-Random -Count 60| % {[char]$_})
  $hasher = new-object System.Security.Cryptography.SHA256Managed
  $hashByteArray = $hasher.ComputeHash([System.Text.Encoding]::UTF8.GetBytes($codeverifier))
  $base64 = ((([System.Convert]::ToBase64String($hashByteArray)).replace('=','')).replace('+','-')).replace('/','_')
  $ports = @(10600,15084,39700,42847,65387,32079)
  $port = $ports[(get-random -Minimum 0 -maximum 5)]
  Write-Host "Start browser..."
  Start-Process "https://accounts.google.com/o/oauth2/v2/auth?code_challenge_method=S256&code_challenge=$base64&access_type=offline&client_id=$client_id&redirect_uri=http://localhost:$port&response_type=code&scope=https://www.googleapis.com/auth/admin.directory.user https://www.googleapis.com/auth/admin.directory.group"
  $listener = New-Object System.Net.HttpListener
  $listener.Prefixes.Add("http://localhost:"+$port+'/')
  try {$listener.Start()} catch {
    "Unable to start listener."
    exit 1
  }
  while (($code -eq $null)) {
    $context = $listener.GetContext()
    Write-Host "Connection accepted" -f 'mag'
    $url = $context.Request.RawUrl
    $code = $url.split('?')[1].split('=')[1].split('&')[0]
    if ($url.split('?')[1].split('=')[0] -eq 'error') {
      Write-Host "Error!"$code -f 'red'
      $buffer = [System.Text.Encoding]::UTF8.GetBytes("Error!"+$code)
      $context.Response.ContentLength64 = $buffer.Length
      $context.Response.OutputStream.Write($buffer, 0, $buffer.Length)
      $context.Response.OutputStream.Close()
      $listener.Stop()
      exit 1
    }
    $buffer = [System.Text.Encoding]::UTF8.GetBytes("Now you can close this browser tab.")
    $context.Response.ContentLength64 = $buffer.Length
    $context.Response.OutputStream.Write($buffer, 0, $buffer.Length)
    $context.Response.OutputStream.Close()
    $listener.Stop()
  }
  Return Invoke-RestMethod -Method Post -Uri "https://www.googleapis.com/oauth2/v4/token" -Body @{
    code = $code
    client_id = $client_id
    client_secret = $client_secret
    redirect_uri = 'http://localhost:'+$port
    grant_type = 'authorization_code'
    code_verifier   = $codeverifier
  }
  $code = $null

Nou mete ID Kliyan an ak Sekrè Kliyan yo jwenn nan pwopriyete idantifyan kliyan OAuth yo, epi verifikatè kòd la se yon seri 43 a 128 karaktè ki dwe pwodwi owaza apati karaktè san rezèv: [AZ] / [az] / [0-9 ] / "-" / "." / "_" / "~".

Lè sa a, kòd sa a pral transmèt ankò. Li elimine vilnerabilite kote yon atakè te kapab entèsepte yon repons retounen kòm yon redireksyon apre otorizasyon itilizatè.
Ou ka voye yon verifikatè kòd nan demann aktyèl la nan tèks klè (ki fè li san sans - sa a se sèlman apwopriye pou sistèm ki pa sipòte SHA256), oswa lè w kreye yon hachaj lè l sèvi avèk algorithm SHA256, ki dwe kode nan BASE64Url (diferan). soti nan Base64 pa de karaktè tab) epi retire tèminezon liy karaktè yo: =.

Apre sa, nou bezwen kòmanse koute http sou machin lokal la nan lòd yo resevwa yon repons apre otorizasyon, ki pral retounen kòm yon redireksyon.

Travay administratif yo fèt sou yon sèvè espesyal, nou pa ka eskli posibilite ke plizyè administratè pral kouri script la an menm tan an, kidonk li pral owaza chwazi yon pò pou itilizatè aktyèl la, men mwen espesifye pò predefini paske yo dwe ajoute tou kòm ou fè konfyans nan konsole API a.

access_type = offline vle di ke aplikasyon an ka mete ajou yon siy ekspire poukont li san entèraksyon itilizatè a ak navigatè a,
repons_tip = kòd fikse fòma ki jan kòd la pral retounen (yon referans a ansyen metòd otorizasyon, lè itilizatè a kopye kòd la nan navigatè a nan script la),
Dimansyon endike dimansyon ak kalite aksè. Yo dwe separe pa espas oswa %20 (dapre URL Encoding). Ou ka wè yon lis zòn aksè ak kalite yo isit la: OAuth 2.0 Dimansyon pou API Google.

Apre li fin resevwa kòd otorizasyon an, aplikasyon an pral retounen yon mesaj fèmen nan navigatè a, sispann koute sou pò a epi voye yon demann POST pou jwenn siy la. Nou endike nan li idantite deja espesifye ak sekrè ki soti nan API konsole a, adrès itilizatè a pral redireksyon ak grant_type an akò ak spesifikasyon pwotokòl la.

Kòm repons, nou pral resevwa yon siy Aksè, peryòd validite li an segonn, ak yon siy Refresh, ak ki nou ka mete ajou siy Aksè a.

Aplikasyon an dwe estoke marqueur yo nan yon kote ki an sekirite ak yon etajè long, kidonk jiskaske nou revoke aksè a resevwa, aplikasyon an pa pral retounen jeton rafrechisman an. Nan fen a, mwen te ajoute yon demann pou anile siy la; si aplikasyon an pa te konplete avèk siksè epi siy rafrechir la pa te retounen, li pral kòmanse pwosedi a ankò (nou te konsidere li pa an sekirite pou estoke marqueur lokalman sou tèminal la, epi nou pa pa vle konplike bagay yo ak kriptografik oswa louvri navigatè a souvan).

do {
  $token_result = Get-GoogleAuthToken
  $token = $token_result.access_token
  if ($token_result.refresh_token -eq $null) {
    Write-Host ("Session is not destroyed. Revoking token...")
    Invoke-WebRequest -Uri ("https://accounts.google.com/o/oauth2/revoke?token="+$token)
  }
} while ($token_result.refresh_token -eq $null)
$refresh_token = $token_result.refresh_token
$minute = ([int]("{0:mm}" -f ([timespan]::fromseconds($token_result.expires_in))))+((Get-date).Minute)-2
if ($minute -lt 0) {$minute += 60}
elseif ($minute -gt 59) {$minute -=60}
$token_expire = @{
  hour = ([int]("{0:hh}" -f ([timespan]::fromseconds($token_result.expires_in))))+((Get-date).Hour)
  minute = $minute
}

Kòm ou deja remake, lè w ap revoke yon siy, yo itilize Invoke-WebRequest. Kontrèman ak Invoke-RestMethod, li pa retounen done yo resevwa nan yon fòma ka itilize epi li montre estati demann lan.

Apre sa, script la mande w antre non itilizatè a ak siyati, jenere yon login + imèl.

Demann

Pwochen demann yo pral - anvan tout bagay, ou bezwen tcheke si yon itilizatè ki gen menm koneksyon an deja egziste yo nan lòd yo jwenn yon desizyon sou kreye yon nouvo oswa pèmèt yon sèl aktyèl la.

Mwen deside aplike tout demann nan fòma yon sèl fonksyon ak yon seleksyon, lè l sèvi avèk switch:

function GoogleQuery {
  param (
    $type,
    $query
  )
  switch ($type) {
    "SearchAccount" {
      Return Invoke-RestMethod -Method Get -Uri "https://www.googleapis.com/admin/directory/v1/users" -Headers @{Authorization = "Bearer "+(Get-GoogleToken)} -Body @{
        domain = 'rocketguys.com'
        query  = "email:$query"
      }
    }
    "UpdateAccount" {
      $body = @{
        name  = @{
          givenName = $query['givenName']
          familyName = $query['familyName']
        }
        suspended = 'false'
        password = $query['password']
        changePasswordAtNextLogin = 'true'
        phones = @(@{
          primary = 'true'
          value = $query['phone']
          type = "mobile"
        })
        orgUnitPath = $query['orgunit']
      }
      Return Invoke-RestMethod -Method Put -Uri ("https://www.googleapis.com/admin/directory/v1/users/"+$query['email']) -Headers @{Authorization = "Bearer "+(Get-GoogleToken)} -Body (ConvertTo-Json $body) -ContentType 'application/json; charset=utf-8'
    }
    
    "CreateAccount" {
      $body = @{
        primaryEmail = $query['email']
        name  = @{
          givenName = $query['givenName']
          familyName = $query['familyName']
        }
        suspended = 'false'
        password = $query['password']
        changePasswordAtNextLogin = 'true'
        phones = @(@{
          primary = 'true'
          value = $query['phone']
          type = "mobile"
        })
        orgUnitPath = $query['orgunit']
      }
      Return Invoke-RestMethod -Method Post -Uri "https://www.googleapis.com/admin/directory/v1/users" -Headers @{Authorization = "Bearer "+(Get-GoogleToken)} -Body (ConvertTo-Json $body) -ContentType 'application/json; charset=utf-8'
    }
    "AddMember" {
      $body = @{
        userKey = $query['email']
      }
      $ifrequest = Invoke-RestMethod -Method Get -Uri "https://www.googleapis.com/admin/directory/v1/groups" -Headers @{Authorization = "Bearer "+(Get-GoogleToken)} -Body $body
      $array = @()
      foreach ($group in $ifrequest.groups) {$array += $group.email}
      if ($array -notcontains $query['groupkey']) {
        $body = @{
          email = $query['email']
          role = "MEMBER"
        }
        Return Invoke-RestMethod -Method Post -Uri ("https://www.googleapis.com/admin/directory/v1/groups/"+$query['groupkey']+"/members") -Headers @{Authorization = "Bearer "+(Get-GoogleToken)} -Body (ConvertTo-Json $body) -ContentType 'application/json; charset=utf-8'
      } else {
        Return ($query['email']+" now is a member of "+$query['groupkey'])
      }
    }
  }
}

Nan chak demann, ou bezwen voye yon header Otorizasyon ki gen kalite siy la ak siy Aksè a li menm. Kounye a, kalite a siy se toujou Bearer. Paske nou bezwen tcheke si token an pa ekspire epi mete ajou li apre yon èdtan nan moman li te bay, mwen espesifye yon demann pou yon lòt fonksyon ki retounen yon siy Aksè. Menm moso kòd la nan kòmansman script la lè w ap resevwa premye siy Aksè a:

function Get-GoogleToken {
  if (((Get-date).Hour -gt $token_expire.hour) -or (((Get-date).Hour -ge $token_expire.hour) -and ((Get-date).Minute -gt $token_expire.minute))) {
  Write-Host "Token Expired. Refreshing..."
    $request = (Invoke-RestMethod -Method Post -Uri "https://www.googleapis.com/oauth2/v4/token" -ContentType 'application/x-www-form-urlencoded' -Body @{
      client_id = $client_id
      client_secret = $client_secret
      refresh_token = $refresh_token
      grant_type = 'refresh_token'
    })
    $token = $request.access_token
    $minute = ([int]("{0:mm}" -f ([timespan]::fromseconds($request.expires_in))))+((Get-date).Minute)-2
    if ($minute -lt 0) {$minute += 60}
    elseif ($minute -gt 59) {$minute -=60}
    $script:token_expire = @{
      hour = ([int]("{0:hh}" -f ([timespan]::fromseconds($request.expires_in))))+((Get-date).Hour)
      minute = $minute
    }
  }
  return $token
}

Tcheke koneksyon an pou egzistans:

function Check_Google {
  $query = (GoogleQuery 'SearchAccount' $username)
  if ($query.users -ne $null) {
    $user = $query.users[0]
    Write-Host $user.name.fullName' - '$user.PrimaryEmail' - suspended: '$user.Suspended
    $GAresult = $user
  }
  if ($GAresult) {
      $return = $GAresult
  } else {$return = 'gg'}
  return $return
}

Imèl:$rekèt demann lan pral mande API a pou chèche yon itilizatè ki gen egzakteman imel sa a, ki gen ladan alyas. Ou kapab tou itilize wildcard: =, :, :{PREFIX}*.

Pou jwenn done, sèvi ak metòd demann GET, pou mete done (kreye yon kont oswa ajoute yon manm nan yon gwoup) - POST, pou mete ajou done ki egziste deja - PUT, pou efase yon dosye (pa egzanp, yon manm nan yon gwoup) - EFASE.

Script la pral mande tou pou yon nimewo telefòn (yon fisèl ki pa valide) ak pou enklizyon nan yon gwoup distribisyon rejyonal. Li deside ki inite òganizasyon itilizatè a ta dwe genyen ki baze sou OU Active Directory chwazi a epi li vini ak yon modpas:

do {
  $phone = Read-Host "Телефон в формате +7хххххххх"
} while (-not $phone)
do {
    $moscow = Read-Host "В Московский офис? (y/n) "
} while (-not (($moscow -eq 'y') -or ($moscow -eq 'n')))
$orgunit = '/'
if ($OU -like "*OU=Delivery,OU=Users,OU=ROOT,DC=rocket,DC=local") {
    Write-host "Будет создана в /Team delivery"
    $orgunit = "/Team delivery"
}
$Password =  -join ( 48..57 + 65..90 + 97..122 | Get-Random -Count 12 | % {[char]$_})+"*Ba"

Apre sa, li kòmanse manipile kont lan:

$query = @{
  email = $email
  givenName = $firstname
  familyName = $lastname
  password = $password
  phone = $phone
  orgunit = $orgunit
}
if ($GMailExist) {
  Write-Host "Запускаем изменение аккаунта" -f mag
  (GoogleQuery 'UpdateAccount' $query) | fl
  write-host "Не забудь проверить группы у включенного $Username в Google."
} else {
  Write-Host "Запускаем создание аккаунта" -f mag
  (GoogleQuery 'CreateAccount' $query) | fl
}
if ($moscow -eq "y"){
  write-host "Добавляем в группу moscowoffice"
  $query = @{
    groupkey = '[email protected]'
    email = $email
  }
  (GoogleQuery 'AddMember' $query) | fl
}

Fonksyon pou mete ajou ak kreye yon kont gen yon sentaks menm jan an; se pa tout jaden adisyonèl yo obligatwa; nan seksyon ki gen nimewo telefòn, ou bezwen presize yon etalaj ki ka genyen jiska yon dosye ak nimewo a ak kalite li yo.

Nan lòd pa resevwa yon erè lè ajoute yon itilizatè nan yon gwoup, nou ka premye tcheke si li se deja yon manm nan gwoup sa a lè w jwenn yon lis manm gwoup oswa konpozisyon nan men itilizatè a li menm.

Kesyon pou manm nan gwoup nan yon itilizatè espesifik pa pral recursive epi yo pral sèlman montre manm dirèk. Enkli yon itilizatè nan yon gwoup paran ki deja gen yon gwoup timoun ke itilizatè a se yon manm nan pral reyisi.

Konklizyon

Tout sa ki rete se voye itilizatè a modpas la pou nouvo kont la. Nou fè sa atravè SMS, epi voye enfòmasyon jeneral ak enstriksyon ak konekte nan yon imèl pèsonèl, ki, ansanm ak yon nimewo telefòn, te bay pa depatman an rekritman. Kòm yon altènativ, ou ka ekonomize lajan epi voye modpas ou nan yon chat telegram sekrè, ki ka konsidere kòm dezyèm faktè a (MacBooks pral yon eksepsyon).

Mèsi paske w li jiska lafen. Mwen pral kontan wè sijesyon pou amelyore style nan ekri atik epi mwen swete ou trape mwens erè lè w ap ekri scripts =)

Lis lyen ki ka itil tematik oswa tou senpleman reponn kesyon:

Sous: www.habr.com

Add nouvo kòmantè