Paghimo sa Google Users gikan sa PowerShell pinaagi sa API

Hello!

Kini nga artikulo maghulagway sa pagpatuman sa PowerShell nga interaksyon sa Google API aron pagmaniobra sa mga tiggamit sa G Suite.

Naggamit kami daghang internal ug cloud nga serbisyo sa tibuok organisasyon. Sa kadaghanan nga bahin, ang pagtugot sa kanila moabut sa Google o Active Directory, diin dili kami makapadayon sa usa ka kopya; sumala niana, kung ang usa ka bag-ong empleyado mobiya, kinahanglan nimo nga maghimo / makahimo usa ka account sa niining duha nga mga sistema. Aron ma-automate ang proseso, nakahukom kami sa pagsulat og script nga nagkolekta og impormasyon ug ipadala kini sa duha ka serbisyo.

Awtorisasyon

Kung giandam ang mga kinahanglanon, nakahukom kami nga gamiton ang tinuod nga mga administrador sa tawo alang sa pagtugot; gipasimple niini ang pag-analisar sa mga aksyon kung adunay aksidente o tinuyo nga daghang pagbag-o.

Gigamit sa mga Google API ang OAuth 2.0 protocol alang sa pag-authenticate ug pagtugot. Ang mga kaso sa paggamit ug mas detalyado nga mga paghulagway makita dinhi: Paggamit sa OAuth 2.0 aron ma-access ang mga Google API.

Gipili nako ang script nga gigamit alang sa pagtugot sa mga aplikasyon sa desktop. Adunay usab usa ka kapilian sa paggamit sa usa ka account sa serbisyo, nga wala magkinahanglan dili kinahanglan nga mga paglihok gikan sa tiggamit.

Ang hulagway sa ubos usa ka eskematiko nga paghulagway sa pinili nga senaryo gikan sa panid sa Google.

Paghimo sa Google Users gikan sa PowerShell pinaagi sa API

  1. Una, among ipadala ang user ngadto sa Google Account authentication page, nga nagpiho sa GET parameters:
    • aplikasyon id
    • mga lugar nga kinahanglan ma-access sa aplikasyon
    • ang adres diin ang user i-redirect human makompleto ang pamaagi
    • ang paagi nga atong i-update ang token
    • Kodigo sa Seguridad
    • verification code transmission format

  2. Human makompleto ang pagtugot, ang user i-redirect sa panid nga gitakda sa unang hangyo, nga adunay sayop o authorization code nga gipasa sa GET parameters
  3. Ang aplikasyon (script) kinahanglan nga makadawat niini nga mga parameter ug, kung nadawat ang code, paghimo sa mosunod nga hangyo aron makakuha og mga token
  4. Kung husto ang hangyo, ang Google API mobalik:
    • Pag-access sa token diin kita makahimo og mga hangyo
    • Ang validity period niini nga token
    • Refresh token gikinahanglan aron ma-refresh ang Access token.

Una kinahanglan nimo nga moadto sa Google API console: Mga Kredensyal - Google API Console, pilia ang gusto nga aplikasyon ug sa seksyon sa Mga Kredensyal paghimo usa ka kliyente nga OAuth identifier. Didto (o sa ulahi, sa mga kabtangan sa gibuhat nga identifier) ​​kinahanglan nimo ipiho ang mga adres kung diin gitugotan ang pag-redirect. Sa among kaso, kini mahimong daghang mga localhost entries nga adunay lainlaing mga pantalan (tan-awa sa ubos).

Aron mas sayon ​​​​ang pagbasa sa script algorithm, mahimo nimong ipakita ang unang mga lakang sa usa ka bulag nga function nga magbalik sa Access ug pag-refresh sa mga token para sa aplikasyon:

$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

Among gibutang ang Client ID ug Client Secret nga nakuha sa OAuth client identifier properties, ug ang code verifier usa ka string nga 43 ngadto sa 128 ka karakter nga kinahanglang random nga mamugna gikan sa unreserved characters: [AZ] / [az] / [0-9 ] / "-" / "." / "_" / "~".

Kini nga code ipasa pag-usab. Giwagtang niini ang pagkahuyang diin ang usa ka tig-atake mahimong makapugong sa usa ka tubag nga gibalik ingon usa ka pag-redirect pagkahuman sa pagtugot sa gumagamit.
Mahimo nimong ipadala ang code verifier sa kasamtangan nga hangyo sa tin-aw nga teksto (nga naghimo niini nga walay kahulogan - kini angay lamang alang sa mga sistema nga dili mosuporta sa SHA256), o pinaagi sa paghimo og hash gamit ang SHA256 algorithm, nga kinahanglang ma-encode sa BASE64Url (nagkalainlain gikan sa Base64 pinaagi sa duha ka mga karakter sa lamesa) ug pagtangtang sa mga tumoy sa linya sa karakter: =.

Sunod, kinahanglan namon nga magsugod sa pagpaminaw sa http sa lokal nga makina aron makadawat usa ka tubag pagkahuman sa pagtugot, nga ibalik ingon usa ka pag-redirect.

Ang mga buluhaton sa administratibo gihimo sa usa ka espesyal nga server, dili naton mapugngan ang posibilidad nga daghang mga administrador ang magpadagan sa script sa parehas nga oras, mao nga kini random nga mopili usa ka pantalan alang sa karon nga tiggamit, apan gipiho nako ang mga predefined port tungod kay sila kinahanglan usab nga idugang ingon nga gisaligan sa API console.

access_type=offline nagpasabot nga ang aplikasyon maka-update sa usa ka expired nga token sa iyang kaugalingon nga walay interaksyon sa user sa browser,
response_type=code nagtakda sa format kung giunsa ibalik ang code (usa ka pakisayran sa daan nga pamaagi sa pagtugot, kung gikopya sa user ang code gikan sa browser ngadto sa script),
gilangkuban nagpakita sa kasangkaran ug matang sa pag-access. Kinahanglang ibulag sila sa mga espasyo o %20 (sumala sa URL Encoding). Ang usa ka lista sa mga access area nga adunay mga tipo makita dinhi: Mga Saklaw sa OAuth 2.0 para sa mga Google API.

Human madawat ang authorization code, ibalik sa aplikasyon ang usa ka suod nga mensahe sa browser, hunong sa pagpaminaw sa pantalan ug ipadala ang hangyo sa POST aron makuha ang token. Gipakita namo niini ang gipiho kaniadto nga id ug sekreto gikan sa console API, ang adres diin ang user i-redirect ug grant_type subay sa espesipikasyon sa protocol.

Agig tubag, makadawat kami usa ka Access token, ang validity period niini sa mga segundo, ug usa ka Refresh token, diin among ma-update ang Access token.

Ang aplikasyon kinahanglan nga magtipig og mga token sa usa ka luwas nga lugar nga adunay taas nga estante sa kinabuhi, mao nga hangtod nga atong bawion ang access nga nadawat, ang aplikasyon dili ibalik ang refresh token. Sa katapusan, gidugang nako ang usa ka hangyo nga bawion ang token; kung ang aplikasyon wala makompleto nga malampuson ug ang refresh token wala ibalik, kini magsugod pag-usab sa pamaagi (among giisip nga dili luwas ang pagtipig sa mga token sa lokal nga terminal, ug kami dili Dili gusto nga komplikado ang mga butang sa cryptography o ablihan kanunay ang browser).

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
}

Sama sa imong namatikdan, kung gibawi ang usa ka token, gigamit ang Invoke-WebRequest. Dili sama sa Invoke-RestMethod, dili kini ibalik ang nadawat nga datos sa usa ka magamit nga format ug gipakita ang kahimtang sa hangyo.

Sunod, gihangyo ka sa script nga isulod ang una ug apelyido sa user, nga maghimo usa ka login + email.

Mga hangyo

Ang sunod nga mga hangyo mao - una sa tanan, kinahanglan nimo nga susihon kung ang usa ka tiggamit nga adunay parehas nga pag-login naglungtad na aron makakuha usa ka desisyon sa paghimo og bag-o o pagpagana sa karon.

Nakahukom ko nga ipatuman ang tanan nga mga hangyo sa format sa usa ka function nga adunay usa ka pagpili, gamit ang 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'])
      }
    }
  }
}

Sa matag hangyo, kinahanglan kang magpadala ug Authorization header nga adunay sulod nga tipo sa token ug ang Access token mismo. Sa pagkakaron, ang tipo sa token kanunay nga Bearer. Kay kinahanglan natong susihon nga ang token wala ma-expire ug i-update kini human sa usa ka oras gikan sa higayon nga kini gi-isyu, akong gipiho ang usa ka hangyo alang sa laing function nga nagbalik sa usa ka Access token. Ang sama nga piraso sa code anaa sa sinugdanan sa script sa dihang nakadawat sa unang Access token:

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
}

Pagsusi sa pag-login alang sa paglungtad:

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
}

Ang email:$query request maghangyo sa API sa pagpangita og user nga adunay eksaktong email, lakip ang mga alias. Mahimo usab nimo gamiton ang wildcard: =, :, :{PREFIX}*.

Para makakuha ug data, gamita ang GET request method, para magsal-ot ug data (paghimo ug account o pagdugang ug membro sa usa ka grupo) - POST, pag-update sa kasamtangan nga data - PUT, pagtangtang ug record (pananglitan, usa ka miyembro gikan sa grupo) - DELETE.

Ang script mangayo usab og numero sa telepono (usa ka unvalidated string) ug alang sa paglakip sa usa ka rehiyonal nga grupo sa pag-apod-apod. Nagdesisyon kini kung unsang unit sa organisasyon ang kinahanglan nga naa sa user base sa napili nga Active Directory OU ug adunay usa ka password:

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"

Ug unya nagsugod siya sa pagmaniobra sa account:

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

Ang mga gimbuhaton alang sa pag-update ug paghimo sa usa ka account adunay parehas nga syntax; dili tanan nga dugang nga mga natad ang gikinahanglan; sa seksyon nga adunay mga numero sa telepono, kinahanglan nimo nga ipiho ang usa ka laray nga mahimo’g maglangkob hangtod sa usa ka rekord nga adunay numero ug tipo niini.

Aron dili makadawat og sayop sa dihang magdugang ug user sa usa ka grupo, masusi una nato kung miyembro na ba siya niini nga grupo pinaagi sa pagkuha og listahan sa mga miyembro sa grupo o komposisyon gikan sa user mismo.

Ang pagpangutana sa membership sa grupo sa usa ka partikular nga user dili recursive ug magpakita lang og direktang membership. Ang paglakip sa usa ka tiggamit sa usa ka ginikanan nga grupo nga adunay usa ka grupo sa bata nga ang tiggamit usa ka miyembro ang molampos.

konklusyon

Ang nahabilin mao ang pagpadala sa user sa password alang sa bag-ong account. Gihimo namo kini pinaagi sa SMS, ug ipadala ang kinatibuk-ang impormasyon uban ang mga instruksyon ug pag-login sa usa ka personal nga email, nga, uban sa numero sa telepono, gihatag sa departamento sa pagrekrut. Ingon usa ka alternatibo, mahimo nimong makatipig salapi ug ipadala ang imong password sa usa ka tinago nga chat sa telegrama, nga mahimo usab nga giisip nga ikaduha nga hinungdan (Ang mga Macbook mahimong eksepsiyon).

Salamat sa pagbasa hangtod sa katapusan. Malipay ako nga makakita og mga sugyot alang sa pagpaayo sa istilo sa pagsulat sa mga artikulo ug nanghinaut nga makakuha ka og gamay nga mga sayup sa pagsulat sa mga script =)

Listahan sa mga link nga mahimong magamit sa tema o yano nga pagtubag sa mga pangutana:

Source: www.habr.com

Idugang sa usa ka comment