Afirandina Bikarhênerên Google ji PowerShell bi API

Hello!

Ev gotar dê pêkanîna danûstendina PowerShell bi Google API-ê re diyar bike da ku bikarhênerên G Suite bi kar bîne.

Em li seranserê rêxistinê gelek karûbarên navxweyî û ewr bikar tînin. Bi piranî, destûr di wan de tê xwarê Google an Active Directory, di navbera wan de em nikarin kopiyek biparêzin; Li gorî vê yekê, dema ku karmendek nû derkeve, hûn hewce ne ku di van her du pergalan de hesabek çêbikin/çalak bikin. Ji bo otomatîkkirina pêvajoyê, me biryar da ku em skrîptek binivîsin ku agahdarî berhev dike û ji her du karûbaran re dişîne.

Destûrkirin

Dema ku pêdiviyan berhev dikin, me biryar da ku ji bo destûrnameyê rêveberên mirovî yên rastîn bikar bînin; ev analîzkirina çalakiyan di bûyera guheztinên girseyî yên qezayî an bi mebest de hêsan dike.

Google API ji bo rastkirin û destûrnameyê protokola OAuth 2.0 bikar tîne. Dozên bikar anînê û danasînên berfirehtir dikarin li vir werin dîtin: Bikaranîna OAuth 2.0-ê ji bo gihîştina API-yên Google-ê bikar bînin.

Min skrîpta ku ji bo destûrnameyê di serîlêdanên sermaseyê de tê bikar anîn hilbijart. Di heman demê de vebijarkek ji bo karanîna hesabek karûbarê jî heye, ku ji bikarhêner tevgerên nehewce hewce nake.

Wêneya jêrîn ravekek şematîkî ya senaryoya hilbijartî ji rûpela Google-ê ye.

Afirandina Bikarhênerên Google ji PowerShell bi API

  1. Pêşîn, em bikarhênerê dişînin rûpela rastkirina Hesabê Google, û pîvanên GET destnîşan dikin:
    • id serîlêdanê
    • deverên ku serîlêdanê hewce dike ku bigihîje
    • navnîşana ku bikarhêner dê piştî qedandina prosedurê were veguheztin
    • awayê ku em ê tokenê nûve bikin
    • Koda Ewlekariyê
    • formata veguheztina koda verastkirinê

  2. Piştî ku destûr qediya, bikarhêner dê ber bi rûpela ku di daxwaznameya yekem de hatî destnîşan kirin, bi xeletiyek an kodek destûrnameyê ji hêla parametreyên GET ve hatî derbas kirin ve were rêve kirin.
  3. Pêdivî ye ku serîlêdan (skrîpt) van parameteran werbigire û heke kod were wergirtin, ji bo bidestxistina tokenan daxwaza jêrîn bike
  4. Ger daxwaz rast be, Google API vedigere:
    • Tokena gihîştinê ya ku em dikarin pê re daxwazan bikin
    • Dewreya derbasdar a vê nîşanê
    • Tokena nûvekirinê ji bo nûvekirina nîşana Accessê hewce ye.

Pêşî hûn hewce ne ku biçin konsolê Google API: Pêbawer - Google API Console, serîlêdana xwestî hilbijêrin û di beşa pêbaweriyê de nasnameyek OAuth ya xerîdar biafirînin. Li wir (an paşê, di taybetmendiyên nasnameya hatî afirandin) de hûn hewce ne ku navnîşanên ku ji nû ve verastkirin têne destûr kirin destnîşan bikin. Di doza me de, ew ê çend navnîşên hosteya herêmî yên bi portên cihêreng bin (li jêr binêre).

Ji bo ku hûn xwendina algorîtmaya nivîsarê hêsantir bikin, hûn dikarin gavên pêşîn di fonksiyonek cihêreng de nîşan bidin ku dê Access vegere û nîşanekan ji bo serîlêdanê nûve bike:

$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

Me Nasnameya Xerîdar û Nepeniya Xerîdar a ku di taybetmendiyên nasnavê xerîdar OAuth de hatine bidestxistin destnîşan dikin, û verastkera kodê rêzek ji 43 heta 128 tîpan e ku divê bi rengekî bêserûber ji karakterên bêparastî were çêkirin: [AZ] / [az] / [0-9] / "-" / "." / "_" / "~".

Dê ev kod paşê dîsa were şandin. Ew qelsiya ku tê de êrîşkarek dikare bersivek ku piştî destûrnameya bikarhêner wekî beralîkirî vedigere asteng dike.
Hûn dikarin di daxwaznameya heyî de verastkerek kodê bi nivîsek zelal bişînin (ya ku wê bêwate dike - ev tenê ji bo pergalên ku SHA256 piştgirî nakin maqûl e), an jî bi karanîna algorîtmaya SHA256, ku divê di BASE64Url de were şîfrekirin (cuda ye ji Base64 bi du tîpên tabloyê) û rakirina dawiya rêza karakteran: =.

Dûv re, pêdivî ye ku em li ser makîneya herêmî dest bi guhdarîkirina http-ê bikin da ku piştî destûrnameyê bersivek bistînin, ku dê wekî beralîkar were vegerandin.

Karên îdarî li ser serverek taybetî têne kirin, em nekarin îhtîmala ku çend rêvebir di heman demê de skrîptê bimeşînin, ji ber vê yekê ew ê ji bo bikarhênerê heyî benderek bêhemdî hilbijêrin, lê min portên pêşwext diyar kirin ji ber ku divê ew di konsolê API de jî wekî pêbawer werin zêdekirin.

access_type=offline tê vê wateyê ku serîlêdan bêyî têkiliya bikarhêner bi gerokê re dikare tokenek qediyayî bixwe nûve bike,
bersiv_type=kod formata ku kod dê çawa were vegerandin destnîşan dike (navdêrek ji rêbaza destûrnameyê ya kevn, dema ku bikarhêner kodê ji gerokê di skrîptê de kopî kir),
çarçoveya çarçove û celebê gihîştinê nîşan dide. Divê ew bi valahiyan an jî %20 (li gorî kodkirina URL-ê) ji hev bên veqetandin. Navnîşek deverên gihîştinê yên bi celeb dikarin li vir werin dîtin: OAuth 2.0 Scopes ji bo Google APIs.

Piştî wergirtina koda destûrnameyê, serîlêdan dê peyamek nêzîk li gerokê vegerîne, guhdarîkirina li portê rawestîne û ji bo bidestxistina tokenê daxwazek POST bişîne. Em di wê de ji API-ya konsolê, navnîşana ku dê bikarhêner jê re were veguheztin û li gorî taybetmendiya protokolê, nasname û nehêniya berê diyarkirî destnîşan dikin.

Di bersivê de, em ê tokenek Accessê, heyama derbasbûna wê di saniyeyan de, û tokenek Nûvekirinê bistînin, ku pê em dikarin tokena Accessê nûve bikin.

Pêdivî ye ku serîlêdan nîşanekan li cîhek ewledar û bi jiyanek dirêj dirêj hilîne, ji ber vê yekê heya ku em gihîştina hatî wergirtin betal nekin, serîlêdan dê tokena nûvekirinê venegerîne. Di dawiyê de, min daxwazek ji bo betalkirina tokenê lê zêde kir; heke serîlêdan bi serfirazî neqede û tokena nûvekirinê nehat vegerandin, ew ê prosedurê ji nû ve bide destpêkirin (me ew ne ewledar dihesibîne ku tokenên herêmî li termînalê hilînin, û em nakin 'dixwazin tiştan bi şîfrekirinê tevlihev bikin an gerokê bi gelemperî vekin).

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
}

Wekî ku we berê jî dît, dema ku tokenek betal dike, Invoke-WebRequest tê bikar anîn. Berevajî Invoke-RestMethod, ew daneyên wergirtî bi rengek bikêr venagerîne û rewşa daxwazê ​​nîşan dide.

Dûv re, skrîpt ji we dipirse ku hûn nav û paşnavê bikarhêner binivîsin, têketinek + e-name çêbike.

Daxwazên

Daxwazên paşîn dê ev bin - berî her tiştî, hûn hewce ne ku kontrol bikin ka bikarhênerek bi heman têketinê jixwe heye da ku biryarek li ser afirandina yek nû an çalakkirina ya heyî bistînin.

Min biryar da ku hemî daxwazan di forma yek fonksiyonê de bi hilbijarkek, bi karanîna veguherînê bicîh bikim:

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'])
      }
    }
  }
}

Di her daxwazekê de, hûn hewce ne ku sernavek Desthilatdariyê bişînin ku tê de celebê token û nîşana Access bixwe heye. Heya nuha, celebê tokenê her gav Hilgir e. Bo pêdivî ye ku em kontrol bikin ka nîşan neqediyaye û piştî demjimêrek ji dema ku hatî derxistin ve wê nûve bikin, min daxwazek ji bo fonksiyonek din diyar kir ku nîşanek Access vedigere. Heman perçeya kodê di destpêka skrîptê de ye dema ku yekem nîşaneya Accessê distîne:

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
}

Kontrolkirina têketinê ji bo hebûna:

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
}

Daxwaza e-name:$query dê ji API-yê bixwaze ku bikarhênerek bi wê e-nameyê, tevî navên din, bigere. Her weha hûn dikarin nexşeya çolê bikar bînin: =, :, :{PREFIX}*.

Ji bo bidestxistina daneyan, rêbaza daxwaza GET bikar bînin, ji bo têxistina daneyan (çêkirina hesabek an lê zêdekirina endamek li komekê) - POST, ji bo nûvekirina daneyên heyî - PUT, ji bo jêbirina tomarek (mînak, endamek ji komekê) - JÊBIRIN.

Skrîpt jî dê jimareyek têlefonê (rêzek nerastkirî) û tevlêbûna di komek belavkirina herêmî de bipirse. Ew biryar dide ku kîjan yekîneya rêxistinî divê bikarhêner li ser bingeha Active Directory OU ya bijartî hebe û şîfreyek peyda dike:

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"

Û paşê ew dest bi manîpulekirina hesabê dike:

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

Fonksiyonên nûvekirin û çêkirina hesabek hevoksaziyek wusa heye; ne hemî zeviyên pêvek hewce ne; di beşa bi hejmarên têlefonê de, hûn hewce ne ku rêzek diyar bikin ku dikare heya yek tomar bi hejmar û celebê wê ve hebe.

Ji bo ku di dema lêzêdekirina bikarhênerek li komekê de xeletiyek neyê wergirtin, em dikarin pêşî kontrol bikin ka ew jixwe endamê vê komê ye bi wergirtina navnîşek endamên komê an pêkhateyek ji bikarhêner bixwe.

Pirskirina endametiya komê ya bikarhênerek taybetî dê dubare nebe û dê tenê endametiya rasterast nîşan bide. Tevlî bikarhênerek di komek dêûbav de ku jixwe komek zarok heye ku bikarhêner endamê wê ye, dê biserkeve.

encamê

Tiştê ku dimîne ev e ku bikarhêner şîfreya hesabê nû bişîne. Em vê yekê bi SMS-ê dikin, û agahdariya gelemperî bi rêwerzan û têketinê ji e-nameyek kesane re dişînin, ku digel hejmarek têlefonê, ji hêla beşê peydakirinê ve hatî peyda kirin. Wekî alternatîf, hûn dikarin drav bidin hev û şîfreya xwe bişînin danûstendinek telegramê ya veşartî, ku ev jî dikare wekî faktora duyemîn were hesibandin (MacBooks dê îstîsnayek be).

Spas dikim ji bo xwendina heta dawiyê. Ez ê kêfxweş bibim ku pêşniyarên ji bo baştirkirina şêwaza nivîsandina gotaran bibînim û ji we re dixwazim ku hûn di nivîsandina senaryoyan de kêmtir xeletiyan bigirin =)

Navnîşa girêdanên ku dibe ku ji hêla mijarê ve kêrhatî bin an jî bi tenê bersiva pirsan bidin:

Source: www.habr.com

Add a comment