Nggawe Pangguna Google saka PowerShell liwat API

Hello!

Artikel iki bakal nerangake implementasine interaksi PowerShell karo Google API kanggo ngapusi pangguna G Suite.

Kita nggunakake sawetara layanan internal lan awan ing saindhenging organisasi. Umume, wewenang kasebut ana ing Google utawa Active Directory, ing antarane kita ora bisa njaga replika; mula, nalika karyawan anyar metu, sampeyan kudu nggawe / ngaktifake akun ing rong sistem kasebut. Kanggo ngotomatisasi proses kasebut, kita mutusake kanggo nulis naskah sing ngumpulake informasi lan dikirim menyang layanan kasebut.

Wewenang

Nalika nggawe syarat, kita mutusake nggunakake administrator manungsa nyata kanggo wewenang; iki nyederhanakake analisis tumindak yen ana owah-owahan sing ora sengaja utawa disengaja.

API Google nggunakake protokol OAuth 2.0 kanggo otentikasi lan wewenang. Kasus panggunaan lan katrangan sing luwih rinci bisa ditemokake ing kene: Nggunakake OAuth 2.0 kanggo Ngakses Google API.

Aku milih script sing digunakake kanggo wewenang ing aplikasi desktop. Ana uga pilihan kanggo nggunakake akun layanan, sing ora mbutuhake gerakan sing ora perlu saka pangguna.

Gambar ing ngisor iki minangka gambaran skematis saka skenario sing dipilih saka kaca Google.

Nggawe Pangguna Google saka PowerShell liwat API

  1. Kaping pisanan, kita ngirim pangguna menyang kaca otentikasi Akun Google, nemtokake parameter GET:
    • id aplikasi
    • wilayah sing aplikasi mbutuhake akses
    • alamat sing pangguna bakal dialihake sawise ngrampungake prosedur kasebut
    • cara kita bakal nganyari token
    • Kode Keamanan
    • format transmisi kode verifikasi

  2. Sawise wewenang rampung, pangguna bakal dialihake menyang kaca sing ditemtokake ing panjalukan pisanan, kanthi kesalahan utawa kode wewenang sing dilewati dening paramèter GET
  3. Aplikasi (skrip) kudu nampa paramèter kasebut lan, yen nampa kode kasebut, panjaluk ing ngisor iki kanggo entuk token
  4. Yen panjalukane bener, Google API bali:
    • Token akses sing bisa kita panjaluk
    • Periode validitas token iki
    • Token refresh dibutuhake kanggo refresh token Akses.

Pisanan sampeyan kudu pindhah menyang Google API console: Kredensial - Google API Console, pilih aplikasi sing dikarepake lan ing bagean Kredensial gawe pengenal OAuth klien. Ing kana (utawa mengko, ing properti pengenal sing digawe) sampeyan kudu nemtokake alamat sing diidini pangalihan. Ing kasus kita, iki bakal dadi sawetara entri localhost kanthi port sing beda (ndeleng ngisor).

Supaya luwih trep maca algoritma skrip, sampeyan bisa nampilake langkah pisanan ing fungsi kapisah sing bakal ngasilake Akses lan refresh token kanggo aplikasi kasebut:

$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

Kita nyetel ID Klien lan Rahasia Klien sing dipikolehi ing properti pengenal klien OAuth, lan verifikasi kode minangka senar saka 43 nganti 128 karakter sing kudu digawe kanthi acak saka karakter sing ora ditemtokake: [AZ] / [az] / [0-9] / "-" / "." / "_" / "~".

Kode iki banjur bakal dikirim maneh. Ngilangi kerentanan ing ngendi panyerang bisa nyegat respon bali minangka pangalihan sawise wewenang pangguna.
Sampeyan bisa ngirim verifikasi kode ing panjalukan saiki ing teks sing cetha (sing ora ana gunane - iki mung cocok kanggo sistem sing ora ndhukung SHA256), utawa kanthi nggawe hash nggunakake algoritma SHA256, sing kudu dienkode ing BASE64Url (beda-beda). saka Base64 dening rong karakter Tabel) lan mbusak endings baris karakter: =.

Sabanjure, kita kudu miwiti ngrungokake http ing mesin lokal kanggo nampa respon sawise wewenang, kang bakal bali minangka pangalihan.

Tugas administratif dileksanakake ing server khusus, kita ora bisa ngilangi kamungkinan sawetara pangurus bakal mbukak script ing wektu sing padha, supaya kanthi acak milih port kanggo pangguna saiki, nanging aku nemtokake port sing wis ditemtokake amarga padha uga kudu ditambahake minangka dipercaya ing console API.

access_type=offline tegese aplikasi bisa nganyari token sing kadaluwarsa dhewe tanpa interaksi pangguna karo browser,
response_type=kode nyetel format carane kode bakal bali (referensi kanggo cara wewenang lawas, nalika pangguna nyalin kode saka browser menyang script),
lingkup nuduhake orane katrangan lan jinis akses. Padha kudu dipisahake dening spasi utawa %20 (miturut URL Encoding). Dhaptar wilayah akses kanthi jinis bisa dideleng ing kene: Cakupan OAuth 2.0 kanggo Google API.

Sawise nampa kode wewenang, aplikasi bakal ngasilake pesen sing cedhak menyang browser, mandheg ngrungokake port lan ngirim panjaluk POST kanggo entuk token. Kita nunjukake id lan rahasia sing wis ditemtokake sadurunge saka API konsol, alamat sing bakal dialihake pangguna lan grant_type sesuai karo spesifikasi protokol.

Kanggo nanggepi, kita bakal nampa token Akses, wektu validitas ing sawetara detik, lan token Refresh, sing bisa nganyari token Akses.

Aplikasi kudu nyimpen token ing panggonan sing aman kanthi umur simpan sing dawa, supaya nganti kita mbatalake akses sing ditampa, aplikasi kasebut ora bakal ngasilake token refresh. Ing pungkasan, aku nambahake panjaluk kanggo mbatalake token kasebut; yen aplikasi ora rampung kanthi sukses lan token refresh ora bali, prosedur kasebut bakal diwiwiti maneh (kita nganggep ora aman kanggo nyimpen token sacara lokal ing terminal, lan kita ora bisa Ora pengin nggawe rumit karo kriptografi utawa mbukak browser kanthi kerep).

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
}

Kaya sing wis sampeyan ngerteni, nalika mbatalake token, Invoke-WebRequest digunakake. Ora kaya Invoke-RestMethod, ora ngasilake data sing ditampa ing format sing bisa digunakake lan nuduhake status panjaluk kasebut.

Sabanjure, skrip njaluk sampeyan ngetik jeneng ngarep lan mburi pangguna, ngasilake login + email.

Njaluk

Panjaluk sabanjure yaiku - pisanan, sampeyan kudu mriksa manawa pangguna kanthi login sing padha wis ana kanggo entuk keputusan nggawe sing anyar utawa ngaktifake sing saiki.

Aku mutusake kanggo ngetrapake kabeh panjaluk ing format siji fungsi kanthi pilihan, nggunakake 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'])
      }
    }
  }
}

Ing saben panyuwunan, sampeyan kudu ngirim header Wewenang sing ngemot jinis token lan token Akses dhewe. Saiki, jinis token tansah Bearer. Amarga kita kudu mriksa sing token wis ora kadaluwarsa lan nganyari sawise jam saka wayahe iki ditanggepi, Aku kasebut panjalukan kanggo fungsi liyane sing ngasilake token Akses. Potongan kode sing padha ana ing wiwitan skrip nalika nampa token Akses pisanan:

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
}

Priksa manawa login:

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
}

Email:$query request bakal njaluk API kanggo nggoleki pangguna kanthi persis email kasebut, kalebu alias. Sampeyan uga bisa nggunakake wildcard: =, :, :{PREFIX}*.

Kanggo entuk data, gunakake metode panyuwunan GET, kanggo nglebokake data (nggawe akun utawa nambah anggota menyang grup) - POST, kanggo nganyari data sing ana - PUT, kanggo mbusak rekaman (contone, anggota saka grup) - Mbusak.

Skrip kasebut uga bakal njaluk nomer telpon (senar sing ora valid) lan dilebokake ing grup distribusi regional. Iki mutusake apa unit organisasi pangguna kudu adhedhasar Active Directory OU sing dipilih lan menehi sandhi:

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"

Banjur dheweke wiwit ngapusi akun kasebut:

$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
}

Fungsi kanggo nganyari lan nggawe akun duwe sintaks sing padha; ora kabeh kolom tambahan dibutuhake; ing bagean kanthi nomer telpon, sampeyan kudu nemtokake larik sing bisa ngemot nganti siji rekaman kanthi nomer lan jinise.

Supaya ora nampa kesalahan nalika nambah pangguna menyang grup, kita bisa mriksa luwih dhisik apa dheweke wis dadi anggota grup iki kanthi entuk dhaptar anggota grup utawa komposisi saka pangguna dhewe.

Njaluk anggota grup pangguna tartamtu ora bakal rekursif lan mung nuduhake anggota langsung. Kalebu pangguna ing grup induk sing wis duwe grup anak sing dadi anggota pangguna bakal sukses.

kesimpulan

Kabeh sing isih ana yaiku ngirim pangguna sandhi kanggo akun anyar. Kita nindakake iki liwat SMS, lan ngirim informasi umum karo instruksi lan mlebet menyang email pribadi, kang, bebarengan karo nomer telpon, diwenehake dening departemen recruitment. Minangka alternatif, sampeyan bisa ngirit dhuwit lan ngirim tembung sandhi menyang obrolan telegram rahasia, sing uga bisa dianggep minangka faktor liya (MacBooks bakal dadi pangecualian).

Matur nuwun kanggo maca nganti pungkasan. Aku bakal seneng ndeleng saran kanggo nambah gaya nulis artikel lan pengin sampeyan entuk luwih sithik kesalahan nalika nulis skrip =)

Dhaptar pranala sing bisa migunani kanthi tema utawa mung mangsuli pitakon:

Source: www.habr.com

Add a comment