Creu Defnyddwyr Google o PowerShell trwy API

Hi!

Bydd yr erthygl hon yn disgrifio gweithrediad rhyngweithio PowerShell ag API Google i drin defnyddwyr G Suite.

Rydym yn defnyddio nifer o wasanaethau mewnol a gwasanaethau cwmwl ar draws y sefydliad. Ar y cyfan, mae awdurdodiad ynddynt yn dod i lawr i Google neu Active Directory, na allwn gadw atgynhyrchiad rhyngddynt; yn unol â hynny, pan fydd gweithiwr newydd yn gadael, mae angen i chi greu / galluogi cyfrif yn y ddwy system hyn. Er mwyn awtomeiddio'r broses, fe benderfynon ni ysgrifennu sgript sy'n casglu gwybodaeth a'i hanfon i'r ddau wasanaeth.

Awdurdodi

Wrth lunio'r gofynion, penderfynasom ddefnyddio gweinyddwyr dynol go iawn ar gyfer awdurdodi; mae hyn yn symleiddio'r dadansoddiad o gamau gweithredu pe bai newidiadau enfawr damweiniol neu fwriadol.

Mae APIs Google yn defnyddio'r protocol OAuth 2.0 ar gyfer dilysu ac awdurdodi. Ceir achosion defnydd a disgrifiadau manylach yma: Defnyddio OAuth 2.0 i gael mynediad i APIs Google.

Dewisais y sgript a ddefnyddir ar gyfer awdurdodi mewn cymwysiadau bwrdd gwaith. Mae yna hefyd opsiwn i ddefnyddio cyfrif gwasanaeth, nad oes angen symudiadau diangen gan y defnyddiwr.

Mae'r llun isod yn ddisgrifiad sgematig o'r senario a ddewiswyd o dudalen Google.

Creu Defnyddwyr Google o PowerShell trwy API

  1. Yn gyntaf, rydym yn anfon y defnyddiwr i dudalen dilysu Cyfrif Google, gan nodi paramedrau GET:
    • id cais
    • ardaloedd y mae'r cais angen mynediad iddynt
    • y cyfeiriad y bydd y defnyddiwr yn cael ei ailgyfeirio iddo ar ôl cwblhau'r weithdrefn
    • y ffordd y byddwn yn diweddaru'r tocyn
    • Cod Diogelwch
    • fformat trosglwyddo cod dilysu

  2. Ar ôl cwblhau'r awdurdodiad, bydd y defnyddiwr yn cael ei ailgyfeirio i'r dudalen a nodir yn y cais cyntaf, gyda gwall neu god awdurdodi wedi'i basio gan baramedrau GET
  3. Bydd angen i'r cais (sgript) dderbyn y paramedrau hyn ac, os derbynnir y cod, gwneud y cais canlynol i gael tocynnau
  4. Os yw'r cais yn gywir, mae API Google yn dychwelyd:
    • Tocyn mynediad y gallwn wneud ceisiadau ag ef
    • Cyfnod dilysrwydd y tocyn hwn
    • Mae angen tocyn adnewyddu i adnewyddu'r tocyn Mynediad.

Yn gyntaf mae angen i chi fynd i'r consol API Google: Manylion - Google API Console, dewiswch y cais a ddymunir ac yn yr adran Credentials creu dynodwr OAuth cleient. Yno (neu'n ddiweddarach, ym mhhriodweddau'r dynodwr a grëwyd) mae angen i chi nodi'r cyfeiriadau y caniateir ailgyfeirio iddynt. Yn ein hachos ni, bydd y rhain yn nifer o gofnodion localhost gyda gwahanol borthladdoedd (gweler isod).

Er mwyn ei gwneud yn fwy cyfleus i ddarllen yr algorithm sgript, gallwch arddangos y camau cyntaf mewn swyddogaeth ar wahân a fydd yn dychwelyd Mynediad ac adnewyddu tocynnau ar gyfer y rhaglen:

$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

Rydym yn gosod yr ID Cleient a Chyfrinach y Cleient a gafwyd ym mhhriodweddau dynodwr cleient OAuth, ac mae'r dilysydd cod yn llinyn o 43 i 128 nod y mae'n rhaid ei gynhyrchu ar hap o nodau heb eu cadw: [AZ] / [az] / [0-9] /"-"/"." / "_" / "~".

Yna bydd y cod hwn yn cael ei drosglwyddo eto. Mae'n dileu'r bregusrwydd y gallai ymosodwr ryng-gipio ymateb a ddychwelwyd fel ailgyfeiriad ar ôl awdurdodiad defnyddiwr.
Gallwch anfon dilysydd cod yn y cais cyfredol mewn testun clir (sy'n ei wneud yn ddiystyr - dim ond ar gyfer systemau nad ydynt yn cynnal SHA256 y mae hyn yn addas), neu trwy greu hash gan ddefnyddio'r algorithm SHA256, y mae'n rhaid ei amgodio yn BASE64Url (gwahanol o Base64 gan ddau nod tabl) a thynnu'r terfyniadau llinell nodau: =.

Nesaf, mae angen i ni ddechrau gwrando ar http ar y peiriant lleol er mwyn derbyn ymateb ar ôl awdurdodi, a fydd yn cael ei ddychwelyd fel ailgyfeiriad.

Perfformir tasgau gweinyddol ar weinydd arbennig, ni allwn ddiystyru'r posibilrwydd y bydd sawl gweinyddwr yn rhedeg y sgript ar yr un pryd, felly bydd yn dewis porthladd ar gyfer y defnyddiwr presennol ar hap, ond nodais borthladdoedd wedi'u diffinio ymlaen llaw oherwydd rhaid eu hychwanegu hefyd fel rhai y gellir ymddiried ynddynt yn y consol API.

access_type=all-lein yn golygu y gall y rhaglen ddiweddaru tocyn sydd wedi dod i ben ar ei ben ei hun heb ryngweithio defnyddiwr â'r porwr,
response_type=cod yn gosod fformat sut y bydd y cod yn cael ei ddychwelyd (cyfeiriad at yr hen ddull awdurdodi, pan gopïodd y defnyddiwr y cod o'r porwr i'r sgript),
cwmpas yn nodi cwmpas a math y mynediad. Rhaid eu gwahanu gan fylchau neu % 20 (yn ôl Amgodio URL). Mae rhestr o ardaloedd mynediad gyda mathau i’w gweld yma: OAuth 2.0 Cwmpas ar gyfer APIs Google.

Ar ôl derbyn y cod awdurdodi, bydd y cais yn dychwelyd neges agos i'r porwr, yn rhoi'r gorau i wrando ar y porthladd ac yn anfon cais POST i gael y tocyn. Rydyn ni'n nodi ynddo'r id a'r gyfrinach a nodwyd yn flaenorol o API y consol, y cyfeiriad y bydd y defnyddiwr yn cael ei ailgyfeirio iddo a grant_type yn unol â manyleb y protocol.

Mewn ymateb, byddwn yn derbyn tocyn Mynediad, ei gyfnod dilysrwydd mewn eiliadau, a thocyn Adnewyddu, y gallwn ei ddefnyddio i ddiweddaru'r tocyn Mynediad.

Rhaid i'r cais storio tocynnau mewn man diogel gydag oes silff hir, felly hyd nes y byddwn yn dirymu'r mynediad a dderbyniwyd, ni fydd y cais yn dychwelyd y tocyn adnewyddu. Ar y diwedd, ychwanegais gais i ddirymu'r tocyn; os na chafodd y cais ei gwblhau'n llwyddiannus ac na ddychwelwyd y tocyn adnewyddu, bydd yn dechrau'r weithdrefn eto (roeddem yn ystyried ei bod yn anniogel storio tocynnau'n lleol ar y derfynell, ac ni wnaethom 'ddim eisiau cymhlethu pethau gyda cryptograffeg neu agor y porwr yn aml).

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
}

Fel y sylwoch eisoes, wrth ddirymu tocyn, defnyddir Invoke-WebRequest. Yn wahanol i Invoke-RestMethod, nid yw'n dychwelyd y data a dderbyniwyd mewn fformat y gellir ei ddefnyddio ac mae'n dangos statws y cais.

Nesaf, mae'r sgript yn gofyn ichi nodi enw cyntaf ac olaf y defnyddiwr, gan gynhyrchu mewngofnodi + e-bost.

Ceisiadau

Y ceisiadau nesaf fydd - yn gyntaf oll, mae angen i chi wirio a yw defnyddiwr gyda'r un mewngofnodi eisoes yn bodoli er mwyn cael penderfyniad ar greu un newydd neu alluogi'r un cyfredol.

Penderfynais weithredu pob cais ar ffurf un swyddogaeth gyda detholiad, gan ddefnyddio switsh:

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

Ym mhob cais, mae angen i chi anfon pennawd Awdurdodi sy'n cynnwys y math o docyn a'r tocyn Mynediad ei hun. Ar hyn o bryd, mae'r math tocyn bob amser yn Cludwr. Achos mae angen inni wirio nad yw'r tocyn wedi dod i ben a'i ddiweddaru ar ôl awr o'r eiliad y'i cyhoeddwyd, nodais gais am swyddogaeth arall sy'n dychwelyd tocyn Mynediad. Mae'r un darn o god ar ddechrau'r sgript wrth dderbyn y tocyn Mynediad cyntaf:

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
}

Gwirio'r mewngofnodi am fodolaeth:

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
}

Bydd y cais e-bost:$query yn gofyn i'r API chwilio am ddefnyddiwr gyda'r e-bost hwnnw'n union, gan gynnwys arallenwau. Gallwch hefyd ddefnyddio cerdyn gwyllt: =, :, :{ RHAGAIR}*.

I gael data, defnyddiwch y dull cais GET, i fewnosod data (creu cyfrif neu ychwanegu aelod at grŵp) - POST, i ddiweddaru data presennol - PUT, i ddileu cofnod (er enghraifft, aelod o grŵp) - DILEU.

Bydd y sgript hefyd yn gofyn am rif ffôn (llinyn heb ei ddilysu) ac i'w gynnwys mewn grŵp dosbarthu rhanbarthol. Mae'n penderfynu pa uned sefydliadol y dylai'r defnyddiwr fod wedi'i seilio ar y Active Directory OU a ddewiswyd ac mae'n cynnwys cyfrinair:

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"

Ac yna mae'n dechrau trin y cyfrif:

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

Mae gan y swyddogaethau ar gyfer diweddaru a chreu cyfrif gystrawen debyg; nid oes angen pob maes ychwanegol; yn yr adran â rhifau ffôn, mae angen i chi nodi arae a all gynnwys hyd at un cofnod gyda'r rhif a'i fath.

Er mwyn peidio â derbyn gwall wrth ychwanegu defnyddiwr at grŵp, gallwn wirio yn gyntaf a yw eisoes yn aelod o'r grŵp hwn trwy gael rhestr o aelodau'r grŵp neu gyfansoddiad gan y defnyddiwr ei hun.

Ni fydd cwestiynu aelodaeth grŵp defnyddiwr penodol yn ailadroddus a bydd yn dangos aelodaeth uniongyrchol yn unig. Bydd cynnwys defnyddiwr mewn grŵp rhieni sydd eisoes â grŵp plant y mae'r defnyddiwr yn aelod ohono yn llwyddo.

Casgliad

Y cyfan sydd ar ôl yw anfon y cyfrinair ar gyfer y cyfrif newydd at y defnyddiwr. Rydym yn gwneud hyn trwy SMS, ac yn anfon gwybodaeth gyffredinol gyda chyfarwyddiadau a mewngofnodi i e-bost personol, a ddarparwyd, ynghyd â rhif ffôn, gan yr adran recriwtio. Fel dewis arall, gallwch arbed arian ac anfon eich cyfrinair i sgwrs telegram cyfrinachol, y gellir ei ystyried hefyd yn ail ffactor (bydd MacBooks yn eithriad).

Diolch am ddarllen hyd y diwedd. Byddaf yn falch o weld awgrymiadau ar gyfer gwella arddull ysgrifennu erthyglau ac yn dymuno i chi ddal llai o wallau wrth ysgrifennu sgriptiau =)

Rhestr o ddolenni a all fod yn ddefnyddiol yn thematig neu ateb cwestiynau yn unig:

Ffynhonnell: hab.com

Ychwanegu sylw