Mamorona mpampiasa Google avy amin'ny PowerShell amin'ny alàlan'ny API

Miarahaba!

Ity lahatsoratra ity dia hamaritra ny fampiharana ny fifandraisana PowerShell amin'ny Google API hanodinana ireo mpampiasa G Suite.

Mampiasa serivisy anatiny sy rahona maro manerana ny fikambanana izahay. Amin'ny ankapobeny, ny fanomezan-dàlana amin'izy ireo dia midina any amin'ny Google na Active Directory, izay tsy ahafahantsika mitazona kopia; araka izany, rehefa miala ny mpiasa vaovao dia mila mamorona/mampiditra kaonty amin'ireo rafitra roa ireo ianao. Mba hanamafisana ny fizotrany dia nanapa-kevitra ny hanoratra script izahay izay manangona vaovao ary mandefa izany amin'ny serivisy roa.

alàlana

Rehefa nandrafitra ireo fepetra takiana dia nanapa-kevitra izahay ny hampiasa ny tena mpitantana olombelona ho an'ny fanomezan-dàlana; izany dia manamora ny famakafakana ny hetsika raha misy fiovana goavana tsy nahy na niniana natao.

Ny API Google dia mampiasa ny protocol OAuth 2.0 ho an'ny fanamarinana sy fanomezan-dàlana. Ny tranga fampiasana sy ny famaritana amin'ny antsipiriany kokoa dia azo jerena eto: Mampiasa OAuth 2.0 hidirana amin'ny Google API.

Nisafidy ny script ampiasaina amin'ny fanomezan-dàlana amin'ny fampiharana desktop aho. Misy ihany koa ny safidy hampiasa kaonty serivisy, izay tsy mitaky hetsika tsy ilaina amin'ny mpampiasa.

Ny sary eto ambany dia famaritana skematika momba ny toe-javatra voafantina avy amin'ny pejy Google.

Mamorona mpampiasa Google avy amin'ny PowerShell amin'ny alàlan'ny API

  1. Voalohany, alefanay any amin'ny pejin'ny fanamarinana ny kaonty Google ny mpampiasa, mamaritra ny masontsivana GET:
    • fampiharana id
    • faritra ilain'ny fampiharana idirana
    • ny adiresy halefa ny mpampiasa rehefa vita ny dingana
    • ny fomba hanavaozanay ny token
    • Kaody fiarovana
    • endrika fampitana kaody fanamarinana

  2. Rehefa vita ny fanomezan-dàlana dia halefa any amin'ny pejy voatondro ao amin'ny fangatahana voalohany ny mpampiasa, miaraka amin'ny hadisoana na kaody fanomezan-dàlana nolanian'ny masontsivana GET
  3. Ny fampiharana (script) dia mila mahazo ireo mari-pamantarana ireo ary, raha mahazo ny kaody, dia ataovy izao fangatahana manaraka izao mba hahazoana marika
  4. Raha marina ny fangatahana dia miverina ny Google API:
    • Token fidirana izay ahafahantsika manao fangatahana
    • Ny fe-potoana manankery an'ity token ity
    • Refresh token ilaina hamelombelona ny Access token.

Voalohany dia mila mandeha any amin'ny Google API console ianao: Fanomezana fahazoan-dàlana - Google API Console, safidio ny fampiharana tiana ary ao amin'ny fizarana Credentials dia mamorona identifier OAuth mpanjifa. Ao (na any aoriana, ao amin'ny fananan'ny identifier noforonina) dia mila mamaritra ny adiresy izay avela redirection. Amin'ny tranga misy antsika, ireo dia ho fampidirana localhost maromaro misy seranana samihafa (jereo eto ambany).

Mba hahamora kokoa ny famakiana ny algorithm amin'ny script dia azonao atao ny mampiseho ny dingana voalohany amin'ny fiasa mitokana izay hamerina ny Access sy refresh token ho an'ny fampiharana:

$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

Nametraka ny ID Client sy ny tsiambaratelon'ny mpanjifa azo tao amin'ny toetran'ny mpamantatra mpanjifa OAuth izahay, ary ny verifier kaody dia tady misy tarehintsoratra 43 hatramin'ny 128 izay tsy maintsy amboarina kisendrasendra avy amin'ny tarehintsoratra tsy voatokana: [AZ] / [az] / [0-9 ] / "-" / "." / "_" / "~".

Hampitaina indray io kaody io avy eo. Izy io dia manafoana ny vulnerable izay ahafahan'ny mpanafika manakana ny valinteny naverina ho redirect taorian'ny fanomezan-dalan'ny mpampiasa.
Azonao atao ny mandefa verifier kaody amin'ny fangatahana amin'izao fotoana izao amin'ny lahatsoratra mazava (izay mahatonga azy io ho tsy misy dikany - mety ho an'ny rafitra tsy manohana SHA256 ihany izany), na amin'ny famoronana hash mampiasa ny algorithm SHA256, izay tsy maintsy fehezina amin'ny BASE64Url (tsy mitovy. avy amin'ny Base64 amin'ny tarehin-tsoratra roa) ary esory ny fiafaran'ny tsipika: =.

Manaraka, mila manomboka mihaino http amin'ny milina eo an-toerana isika mba hahazoana valiny aorian'ny fanomezan-dàlana, izay haverina ho redirect.

Ny asa administratif dia atao amin'ny lohamilina manokana, tsy azontsika atao ny manilika ny mety hisian'ny mpitantana maromaro hampandeha ny script miaraka amin'ny fotoana iray ihany, noho izany dia hisafidy seranan-tsambo ho an'ny mpampiasa ankehitriny izy, saingy nanondro seranana efa voafaritra aho satria tsy maintsy ampiana ihany koa izy ireo araka ny fitokisana ao amin'ny console API.

access_type=tsy an-tserasera midika fa ny fampiharana dia afaka manavao samirery marika lany daty tsy misy fifandraisan'ny mpampiasa amin'ny navigateur,
response_type=code mametraka ny endrika amin'ny fomba hamerenana ny kaody (famantarana ny fomba fanomezan-dàlana taloha, rehefa nalain'ny mpampiasa ny code avy amin'ny navigateur ho ao amin'ny script),
sehatra manondro ny sehatra sy ny karazana fidirana. Tsy maintsy sarahin'ny habaka na %20 izy ireo (araka ny URL Encoding). Ny lisitry ny faritra fidirana misy karazany dia azo jerena eto: OAuth 2.0 Sakan'ny Google API.

Rehefa avy nahazo ny kaody fanomezan-dàlana dia hamerina hafatra akaiky amin'ny navigateur ny fampiharana, atsaharo ny fihainoana eo amin'ny seranan-tsambo ary mandefa fangatahana POST hahazoana ilay marika. Asehonay ao anatin'izany ny id sy tsiambaratelo voatondro teo aloha avy amin'ny console API, ny adiresin'ny mpampiasa ary ny fanomezana_type mifanaraka amin'ny tondrozotra protocol.

Ho setrin'izany dia hahazo mari-pankasitrahana Access isika, ny fe-potoana manan-kery ao anatin'ny segondra, ary marika Refresh, izay ahafahantsika manavao ny famantarana Access.

Ny fampiharana dia tsy maintsy mitahiry mari-pamantarana amin'ny toerana azo antoka ary maharitra ela, ka mandra-panafoana ny fidirana azo dia tsy hamerina ny mari-pamantarana fanavaozana ny fampiharana. Tamin'ny farany dia nanampy fangatahana hanafoana ny mari-pamantarana aho; raha toa ka tsy vita soa aman-tsara ny fangatahana ary tsy naverina ny mari-pamantarana fanavaozana, dia hanomboka indray ny fomba fiasa (heverinay fa tsy azo antoka ny mitahiry marika eo an-toerana eo amin'ny terminal, ary tsy 'tsy te hanasarotra zavatra amin'ny cryptography na hanokatra matetika ny navigateur).

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
}

Araka ny efa voamarikao, rehefa manafoana famantarana iray dia ampiasaina ny Invoke-WebRequest. Tsy toy ny Invoke-RestMethod, tsy mamerina ny angon-drakitra voaray amin'ny endrika azo ampiasaina izy ary mampiseho ny satan'ny fangatahana.

Manaraka, ny script dia mangataka anao hampiditra ny anaran'ny mpampiasa sy ny anaran'ny farany, mamorona fidirana + mailaka.

fangatahana

Ny fangatahana manaraka dia - voalohany indrindra, mila manamarina ianao raha efa misy ny mpampiasa mitovy amin'ny fidirana mba hahazoana fanapahan-kevitra amin'ny famoronana vaovao na mamela ny ankehitriny.

Nanapa-kevitra ny hampihatra ny fangatahana rehetra amin'ny endrika fiasa iray misy safidy aho, amin'ny fampiasana 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'])
      }
    }
  }
}

Amin'ny fangatahana tsirairay dia mila mandefa lohatenin'ny Fanomezan-dàlana misy ny karazana famantarana sy ny famantarana Access ianao. Amin'izao fotoana izao, ny karazana famantarana dia mitondra foana. SATRIA mila manamarina isika fa tsy lany daty ny token ary manavao azy aorian'ny adiny iray manomboka amin'ny fotoana namoahana azy, dia nanondro fangatahana asa hafa aho izay mamerina ny token'ny Access. Ny kaody mitovy dia eo am-piandohan'ny script rehefa mahazo ny famantarana Access voalohany:

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
}

Fanamarinana ny fidirana raha misy:

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
}

Ny mailaka: $query fangatahana dia hangataka ny API hitady mpampiasa manana io mailaka io indrindra, ao anatin'izany ny anarana. Azonao atao koa ny mampiasa wildcard: =, :, :{PREFIX}*.

Mba hahazoana angon-drakitra dia ampiasao ny fomba fangatahana GET, hampiditra data (mamorona kaonty na manampy mpikambana ao amin'ny vondrona) - POST, hanavao ny angona efa misy - PUT, hamafa rakitra (ohatra, mpikambana ao amin'ny vondrona) - DELETE.

Ny script koa dia hangataka nomeraon-telefaona (tady tsy voamarina) ary hampidirina ao amin'ny vondrona fizarana faritra. Izy io no manapa-kevitra hoe iza amin'ireo vondron'orinasa tokony hananan'ny mpampiasa mifototra amin'ny Active Directory OU voafantina ary misy tenimiafina:

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"

Ary avy eo dia manomboka manodinkodina ny kaonty izy:

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

Ny fiasa amin'ny fanavaozana sy famoronana kaonty dia manana fehezanteny mitovy; tsy ny saha fanampiny rehetra no ilaina; ao amin'ny fizarana misy nomeraon-telefaona dia mila mamaritra ny laharan-tariby afaka mirakitra rakitsoratra iray miaraka amin'ny isa sy ny karazany ianao.

Mba tsy hisian'ny hadisoana rehefa manampy mpampiasa iray amin'ny vondrona iray, dia azontsika atao ny manamarina aloha raha efa mpikambana ao amin'ity vondrona ity izy amin'ny alàlan'ny fahazoana lisitr'ireo mpikambana ao amin'ny vondrona na famoronana avy amin'ny mpampiasa azy.

Ny fametraham-panontaniana momba ny maha-mpikambana ao amin'ny vondrona mpampiasa iray manokana dia tsy hiverimberina ary tsy hiseho afa-tsy ny maha-mpikambana mivantana. Hahomby ny fampidirana mpampiasa iray ao amin'ny vondrona ray aman-dreny izay efa manana vondron'ankizy izay maha-mpikambana azy.

famaranana

Ny hany sisa tavela dia ny mandefa ny mpampiasa ny tenimiafina ho an'ny kaonty vaovao. Manao izany amin'ny alàlan'ny SMS izahay, ary mandefa fampahalalana ankapobeny miaraka amin'ny torolàlana ary midira amin'ny mailaka manokana, izay, miaraka amin'ny laharan-telefaona, nomen'ny sampana fandraisana mpiasa. Ho solon'izay, afaka mitahiry vola ianao ary mandefa ny tenimiafinao amin'ny chat telegrama miafina, izay azo raisina ho toy ny antony faharoa ihany koa (MacBooks dia tsy misy afa-tsy).

Misaotra anao namaky hatramin'ny farany. Ho faly aho hahita soso-kevitra hanatsarana ny fomba fanoratana lahatsoratra ary mirary anao hahazo lesoka vitsy kokoa rehefa manoratra script =)

Lisitr'ireo rohy izay mety ilaina amin'ny lohahevitra na mamaly fanontaniana tsotra:

Source: www.habr.com

Add a comment