API ذريعي PowerShell کان گوگل استعمال ڪندڙ ٺاهڻ

هيلو!

هي آرٽيڪل G Suite استعمال ڪندڙن کي هٿي وٺائڻ لاءِ گوگل API سان PowerShell رابطي جي نفاذ جي وضاحت ڪندو.

اسان سڄي تنظيم ۾ ڪيترائي اندروني ۽ ڪلائوڊ خدمتون استعمال ڪندا آهيون. گھڻي ڀاڱي لاءِ، انھن ۾ اختيار ھيٺ اچي ٿو گوگل يا ايڪٽو ڊاريڪٽري، جنھن جي وچ ۾ اسين ريپليڪا برقرار نٿا رکي سگھون؛ ان مطابق، جڏھن ڪو نئون ملازم نڪرندو آھي، توھان کي انھن ٻن سسٽمن ۾ اڪائونٽ ٺاھڻ/ فعال ڪرڻ جي ضرورت آھي. عمل کي خودڪار ڪرڻ لاء، اسان هڪ اسڪرپٽ لکڻ جو فيصلو ڪيو جيڪو معلومات گڏ ڪري ٿو ۽ ان کي ٻنهي خدمتن ڏانهن موڪلي ٿو.

اختيار ڏيڻ

جڏهن ضرورتن کي ترتيب ڏيو، اسان اختيار ڪرڻ لاء حقيقي انساني منتظمين کي استعمال ڪرڻ جو فيصلو ڪيو؛ هي عملن جي تجزيي کي آسان بڻائي ٿو حادثاتي يا ارادي وڏي تبديلين جي صورت ۾.

گوگل APIs استعمال ڪن ٿا OAuth 2.0 پروٽوڪول جي تصديق ۽ اختيار ڪرڻ لاءِ. ڪيس استعمال ڪريو ۽ وڌيڪ تفصيلي وضاحت هتي ملي سگهي ٿي: Google APIs تائين رسائي حاصل ڪرڻ لاءِ OAuth 2.0 استعمال ڪريو.

مون اسڪرپٽ چونڊيو آهي جيڪو ڊيسڪ ٽاپ ايپليڪيشنن ۾ اختيار ڪرڻ لاءِ استعمال ڪيو ويندو آهي. هڪ خدمت اڪائونٽ استعمال ڪرڻ جو اختيار پڻ آهي، جيڪو صارف کان غير ضروري حرڪت جي ضرورت ناهي.

هيٺ ڏنل تصوير گوگل پيج مان چونڊيل منظرنامي جي اسڪيمي وضاحت آهي.

API ذريعي PowerShell کان گوگل استعمال ڪندڙ ٺاهڻ

  1. پهريون، اسان صارف کي موڪليندا آهيون گوگل کاتي جي تصديق واري صفحي تي، وضاحت ڪندي GET پيٽرولر:
    • ايپليڪيشن جي سڃاڻپ
    • جن علائقن تائين ايپليڪيشن کي رسائي جي ضرورت آهي
    • ايڊريس جنهن تي استعمال ڪندڙ کي ريڊائريڪٽ ڪيو ويندو طريقيڪار مڪمل ڪرڻ کان پوءِ
    • جنهن طريقي سان اسان ٽوڪن کي اپڊيٽ ڪنداسين
    • سيڪيورٽي ڪوڊ
    • تصديق ڪوڊ ٽرانسميشن فارميٽ

  2. اختيار مڪمل ٿيڻ کان پوء، صارف کي پھرئين درخواست ۾ بيان ڪيل صفحي ڏانھن ريڊائريڪٽ ڪيو ويندو، ھڪڙي غلطي يا اختيار واري ڪوڊ سان GET پيرا ميٽرز طرفان منظور ٿيل
  3. ايپليڪيشن (اسڪرپٽ) کي انهن پيرا ميٽرز حاصل ڪرڻ جي ضرورت پوندي ۽، جيڪڏهن ڪوڊ موصول ٿيو، ٽوڪن حاصل ڪرڻ لاءِ هيٺين درخواست ڪريو
  4. جيڪڏهن درخواست صحيح آهي، گوگل API واپسي:
    • رسائي ٽوڪن جنهن سان اسان درخواستون ڪري سگهون ٿا
    • هن ٽوڪن جي صحيحيت جي مدت
    • ريفريش ٽوڪن جي ضرورت آهي رسائي ٽوڪن کي ريفريش ڪرڻ لاءِ.

پهرين توهان کي وڃڻ جي ضرورت آهي گوگل API ڪنسول: سندون - گوگل API ڪنسول، گهربل ايپليڪيشن چونڊيو ۽ سند سيڪشن ۾ هڪ ڪلائنٽ OAuth سڃاڻپ ڪندڙ ٺاهيو. اتي (يا بعد ۾، ٺاهيل سڃاڻپ ڪندڙ جي ملڪيتن ۾) توھان کي پتا بيان ڪرڻ جي ضرورت آھي جنھن ڏانھن ريڊائريڪشن جي اجازت آھي. اسان جي صورت ۾، اهي مختلف بندرگاهن سان گڏ ڪيترائي لوڪل هوسٽ داخل ڪيا ويندا (هيٺ ڏسو).

اسڪرپٽ الورورٿم کي پڙهڻ لاءِ وڌيڪ آسان بڻائڻ لاءِ، توهان هڪ الڳ فنڪشن ۾ پهرين مرحلن کي ڊسپلي ڪري سگهو ٿا جيڪي واپس ڪندا Access ۽ ريفريش ٽوڪن ايپليڪيشن لاءِ:

$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

اسان OAuth ڪلائنٽ جي سڃاڻپ ڪندڙ ملڪيتن ۾ حاصل ڪيل ڪلائنٽ ID ۽ ڪلائنٽ راز مقرر ڪريون ٿا، ۽ ڪوڊ تصديق ڪندڙ 43 کان 128 اکرن تي مشتمل اسٽرنگ آھي جيڪو غير محفوظ ڪيل اکرن مان بي ترتيب سان ٺاھيو وڃي: [AZ] / [az] / [0-9 ] / "-" / "." / "_" / "~".

هي ڪوڊ وري منتقل ڪيو ويندو. اهو نقصان کي ختم ڪري ٿو جنهن ۾ هڪ حملو ڪندڙ صارف جي اجازت کان پوء ريڊائريڪٽ جي طور تي موٽڻ واري جواب کي روڪي سگهي ٿو.
توهان موجوده درخواست ۾ هڪ ڪوڊ تصديق ڪندڙ موڪلي سگهو ٿا واضح متن ۾ (جيڪو ان کي بي معنيٰ بڻائي ٿو - اهو صرف انهن سسٽم لاءِ موزون آهي جيڪي SHA256 کي سپورٽ نٿا ڪن)، يا SHA256 الگورتھم استعمال ڪندي هڪ هيش ٺاهي، جنهن کي BASE64Url ۾ انڪوڊ ڪيو وڃي (مختلف Base64 کان ٻن ٽيبل جي اکرن ذريعي) ۽ ڪردار جي لائن جي پڇاڙي کي ختم ڪرڻ: =.

اڳيون، اسان کي اجازت ڏيڻ کان پوء جواب حاصل ڪرڻ لاء مقامي مشين تي http کي ٻڌڻ شروع ڪرڻ جي ضرورت آهي، جيڪا واپسي جي طور تي واپس ڪئي ويندي.

انتظامي ڪمن کي خاص سرور تي انجام ڏنو وڃي ٿو، اسان ان امڪان کي رد نه ٿا ڪري سگھون ته ڪيترائي منتظم هڪ ئي وقت اسڪرپٽ کي هلائيندا، تنهنڪري اهو بي ترتيب طور تي موجوده استعمال ڪندڙ لاء بندرگاهن کي چونڊيو ويندو، پر مون اڳ ۾ بيان ڪيل بندرگاهن جي وضاحت ڪئي آهي ڇاڪاڻ ته انهن کي پڻ شامل ڪيو وڃي جيئن API ڪنسول ۾ قابل اعتماد.

access_type = آف لائن مطلب ته ايپليڪيشن براؤزر سان صارف جي رابطي کان سواءِ پنهنجو پاڻ تي ختم ٿيل ٽوڪن کي اپڊيٽ ڪري سگهي ٿي،
جواب_قسم = ڪوڊ فارميٽ سيٽ ڪري ٿو ته ڪوڊ ڪيئن موٽايو ويندو (پراڻي اجازت ڏيڻ واري طريقي جو حوالو، جڏهن صارف ڪوڊ کي برائوزر کان اسڪرپٽ ۾ نقل ڪيو)،
گنجائش رسائي جو دائرو ۽ قسم ڏيکاري ٿو. انهن کي اسپيس يا %20 (URL انڪوڊنگ جي مطابق) الڳ ڪيو وڃي. قسمن جي رسائي وارن علائقن جي هڪ فهرست هتي ڏسي سگھجي ٿو: Google APIs لاءِ OAuth 2.0 اسڪوپس.

اختياري ڪوڊ حاصل ڪرڻ کان پوء، ايپليڪيشن برائوزر ڏانهن ويجھو پيغام واپس ڪندي، بندرگاهه تي ٻڌڻ بند ڪري ۽ ٽوڪن حاصل ڪرڻ لاءِ پوسٽ جي درخواست موڪلي. اسان ان ۾ ڏيکاريون ٿا اڳ ۾ بيان ڪيل id ۽ راز کان ڪنسول API کان، پتو جنهن ڏانهن صارف کي ريڊائريڪٽ ڪيو ويندو ۽ grant_type پروٽوڪول جي وضاحت جي مطابق.

جواب ۾، اسان هڪ رسائي ٽوڪن حاصل ڪنداسين، ان جي صحيح مدت سيڪنڊن ۾، ۽ هڪ ريفريش ٽوڪن، جنهن سان اسان رسائي ٽوڪن کي اپڊيٽ ڪري سگهون ٿا.

ايپليڪيشن کي لازمي طور تي ٽوڪن کي محفوظ جاءِ تي محفوظ رکڻ گهرجي جنهن ۾ ڊگهي شيلف لائف آهي، ان ڪري جيستائين اسان حاصل ڪيل رسائي کي رد نه ڪريون، ايپليڪيشن ريفريش ٽوڪن کي واپس نه ڪندي. آخر ۾، مون ٽوڪن کي رد ڪرڻ جي درخواست شامل ڪئي؛ جيڪڏهن ايپليڪيشن ڪاميابي سان مڪمل نه ڪئي وئي ۽ ريفريش ٽوڪن واپس نه ڪيو ويو، اهو عمل ٻيهر شروع ڪندو (اسان ٽرمينل تي مقامي طور تي ٽوڪن کي ذخيرو ڪرڻ غير محفوظ سمجهيو، ۽ اسان ڊون ٿا نه چاھيو ٿا شيون پيچيدگين سان cryptography يا برائوزر کولڻ بار بار).

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
}

جيئن توهان اڳ ۾ ئي محسوس ڪيو آهي، جڏهن هڪ ٽوڪن کي رد ڪندي، Invoke-WebRequest استعمال ڪيو ويندو آهي. Invoke-RestMethod جي برعڪس، اهو وصول ٿيل ڊيٽا کي قابل استعمال فارميٽ ۾ واپس نٿو ڪري ۽ درخواست جي حيثيت ڏيکاري ٿو.

اڳيون، اسڪرپٽ توهان کي صارف جو پهريون ۽ آخري نالو داخل ڪرڻ لاء پڇي ٿو، هڪ لاگ ان + اي ميل ٺاهي.

درخواستون

ايندڙ درخواستون ٿينديون - سڀ کان پهريان، توهان کي اهو جانچڻ جي ضرورت آهي ته ڇا هڪ ئي لاگ ان سان استعمال ڪندڙ اڳ ۾ ئي موجود آهي ته جيئن هڪ نئون ٺاهڻ يا موجوده هڪ کي فعال ڪرڻ جو فيصلو حاصل ڪرڻ لاء.

مون سڀني درخواستن تي عمل ڪرڻ جو فيصلو ڪيو ھڪڙي فنڪشن جي شڪل ۾ ھڪڙي چونڊ سان، سوئچ استعمال ڪندي:

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

هر درخواست ۾، توهان کي موڪلڻ جي ضرورت آهي اجازت ڏيڻ وارو هيڊر جنهن ۾ ٽوڪن جو قسم ۽ رسائي ٽوڪن شامل آهي. في الحال، ٽوڪن جو قسم هميشه بيئرر آهي. ڇاڪاڻ ته اسان کي چيڪ ڪرڻ جي ضرورت آهي ته ٽوڪن ختم نه ٿيو آهي ۽ ان کي اپڊيٽ ڪيو ويو هڪ ڪلاڪ کان پوء ان کي جاري ڪيو ويو، مون هڪ ٻي فنڪشن لاء درخواست بيان ڪئي جيڪا هڪ رسائي ٽوڪن واپس ڪري ٿي. ڪوڊ جو ساڳيو ٽڪرو اسڪرپٽ جي شروعات ۾ آهي جڏهن پهريون رسائي ٽوڪن حاصل ڪري رهيو آهي:

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
}

وجود لاءِ لاگ ان چيڪ ڪندي:

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
}

اي ميل: $query جي درخواست API کان پڇي ٿي ته صارف کي انهي اي ميل سان ڳولڻ لاءِ، جنهن ۾ عرف شامل آهن. توھان پڻ استعمال ڪري سگھو ٿا وائلڊ ڪارڊ: =، :، :{PREFIX}*.

ڊيٽا حاصل ڪرڻ لاءِ، GET درخواست جو طريقو استعمال ڪريو، ڊيٽا داخل ڪرڻ لاءِ (اڪاؤنٽ ٺاهڻ يا گروپ ۾ ميمبر شامل ڪرڻ) - پوسٽ ڪريو، موجوده ڊيٽا کي اپڊيٽ ڪرڻ لاءِ - PUT، رڪارڊ کي ختم ڪرڻ لاءِ (مثال طور، گروپ مان ميمبر) - حذف ڪريو.

اسڪرپٽ پڻ هڪ فون نمبر (هڪ غير تصديق ٿيل اسٽرنگ) ۽ علائقائي تقسيم گروپ ۾ شامل ڪرڻ لاء پڇي ويندي. اهو فيصلو ڪري ٿو ته استعمال ڪندڙ کي ڪهڙي تنظيمي يونٽ جي بنياد تي چونڊيل Active Directory OU جي بنياد تي ۽ پاسورڊ سان گڏ اچي ٿو:

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"

۽ پوءِ هو اڪائونٽ کي هٿي وٺائڻ شروع ڪري ٿو:

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

اڪائونٽ کي تازه ڪاري ڪرڻ ۽ ٺاهڻ لاءِ ڪمن ۾ هڪجهڙو نحو هوندو آهي؛ سڀ اضافي فيلڊ گهربل نه هوندا آهن؛ فون نمبرن سان سيڪشن ۾، توهان کي هڪ صف جي وضاحت ڪرڻي پوندي جنهن ۾ نمبر ۽ ان جي قسم سان گڏ هڪ رڪارڊ تائين هجي.

ڪنهن به صارف کي گروپ ۾ شامل ڪرڻ وقت غلطي حاصل نه ڪرڻ لاءِ، اسان پهريان ئي چيڪ ڪري سگھون ٿا ته ڇا هو اڳ ۾ ئي هن گروپ جو ميمبر آهي گروپ جي ميمبرن جي فهرست يا خود استعمال ڪندڙ کان ترتيب حاصل ڪري.

مخصوص استعمال ڪندڙ جي گروپ رڪنيت بابت پڇا ڳاڇا ڪرڻ وارو نه هوندو ۽ صرف سڌي رڪنيت ڏيکاريندو. هڪ صارف کي شامل ڪرڻ ۾ والدين گروپ ۾ جيڪو اڳ ۾ ئي ٻارن جو گروپ آهي جنهن جو صارف ميمبر آهي ڪامياب ٿيندو.

ٿڪل

باقي اهو آهي ته صارف کي نئين اڪائونٽ لاء پاسورڊ موڪلڻ لاء. اسان اهو ايس ايم ايس ذريعي ڪندا آهيون، ۽ هدايتن سان گڏ عام معلومات موڪليندا آهيون ۽ هڪ ذاتي اي ميل تي لاگ ان ڪندا آهيون، جيڪو، هڪ فون نمبر سان گڏ، نوڪري واري کاتي طرفان مهيا ڪيل هو. هڪ متبادل طور، توهان پئسا بچائي سگهو ٿا ۽ پنهنجو پاسورڊ ڳجهي ٽيليگرام چيٽ ڏانهن موڪلي سگهو ٿا، جنهن کي پڻ سمجهي سگهجي ٿو ٻيو عنصر (MacBooks هڪ استثنا هوندو).

آخر تائين پڙهڻ لاءِ مهرباني. مان مضمونن جي لکڻ جي انداز کي بهتر ڪرڻ لاءِ تجويزون ڏسي خوش ٿيندس ۽ چاهيان ٿو ته لکت لکڻ وقت توهان کي گهٽ غلطيون نظر اچن =)

ڪڙين جي فهرست جيڪا ٿي سگهي ٿي موضوعي طور مفيد يا صرف سوالن جا جواب:

جو ذريعو: www.habr.com

تبصرو شامل ڪريو