Tworzenie użytkowników Google z PowerShell za pośrednictwem API
Hi!
W tym artykule opisano implementację interakcji PowerShell z Google API w celu manipulowania użytkownikami G Suite.
W całej organizacji korzystamy z kilku usług wewnętrznych i chmurowych. W większości autoryzacja w nich sprowadza się do Google lub Active Directory, pomiędzy którymi nie możemy utrzymywać repliki, dlatego też, gdy odchodzi nowy pracownik, należy założyć/włączyć konto w tych dwóch systemach. Aby zautomatyzować proces, postanowiliśmy napisać skrypt, który zbiera informacje i wysyła je do obu serwisów.
autoryzacja
Przygotowując wymagania, zdecydowaliśmy się na wykorzystanie do autoryzacji prawdziwych administratorów, co upraszcza analizę działań w przypadku przypadkowych lub zamierzonych masowych zmian.
Wybrałem skrypt, który służy do autoryzacji w aplikacjach desktopowych. Istnieje również możliwość skorzystania z konta serwisowego, które nie wymaga od użytkownika zbędnych ruchów.
Poniższy obrazek to schematyczny opis wybranego scenariusza ze strony Google.
Najpierw odsyłamy użytkownika na stronę uwierzytelniania konta Google, podając parametry GET:
identyfikator aplikacji
obszary, do których aplikacja potrzebuje dostępu
adres, na który użytkownik zostanie przekierowany po zakończeniu procedury
sposób, w jaki zaktualizujemy token
Kod bezpieczeństwa
format transmisji kodu weryfikacyjnego
Po zakończeniu autoryzacji użytkownik zostanie przekierowany na stronę podaną w pierwszym żądaniu, z błędem lub kodem autoryzacyjnym przekazanym przez parametry GET
Aplikacja (skrypt) będzie musiała otrzymać te parametry i po otrzymaniu kodu wykonać następujące żądanie uzyskania tokenów
Jeśli żądanie jest prawidłowe, Google API zwraca:
Token dostępu, za pomocą którego możemy składać żądania
Okres ważności tego tokena
Odśwież token wymagany do odświeżenia tokenu dostępu.
Najpierw musisz przejść do konsoli Google API: Dane logowania — konsola Google API, wybierz żądaną aplikację i w sekcji Poświadczenia utwórz identyfikator OAuth klienta. Tam (lub później, we właściwościach utworzonego identyfikatora) musisz określić adresy, na które dozwolone jest przekierowanie. W naszym przypadku będzie to kilka wpisów localhost z różnymi portami (patrz niżej).
Aby ułatwić odczytanie algorytmu skryptu, możesz wyświetlić pierwsze kroki w osobnej funkcji, która zwróci tokeny dostępu i odświeżenia dla aplikacji:
Ustawiamy Client ID i Client Secret uzyskane we właściwościach identyfikatora klienta OAuth, a weryfikatorem kodu jest ciąg od 43 do 128 znaków, który należy wygenerować losowo ze znaków niezarezerwowanych: [AZ] / [az] / [0-9 ] / „-” / „.” / „_” / „~”.
Kod ten zostanie następnie przesłany ponownie. Eliminuje lukę, w wyniku której atakujący mógłby przechwycić odpowiedź zwróconą jako przekierowanie po autoryzacji użytkownika.
Weryfikator kodu możesz wysłać w bieżącym żądaniu w postaci czystego tekstu (co czyni go bezsensownym - nadaje się tylko dla systemów, które nie obsługują SHA256) lub tworząc skrót za pomocą algorytmu SHA256, który musi być zakodowany w BASE64Url (różniący się z Base64 o dwa znaki tabeli) i usunięcie końcówek linii znakowych: =.
Następnie musimy rozpocząć nasłuchiwanie http na lokalnej maszynie, aby po autoryzacji otrzymać odpowiedź, która zostanie zwrócona jako przekierowanie.
Zadania administracyjne wykonywane są na specjalnym serwerze, nie możemy wykluczyć, że kilku administratorów uruchomi skrypt w tym samym czasie, więc losowo wybierze port dla bieżącego użytkownika, ale podałem porty predefiniowane, ponieważ należy je także dodać jako zaufane w konsoli API.
typ_dostępu=offline oznacza, że aplikacja może samodzielnie zaktualizować wygasły token bez interakcji użytkownika z przeglądarką, typ_odpowiedzi=kod ustawia format w jaki sposób zostanie zwrócony kod (nawiązanie do starej metody autoryzacji, gdy użytkownik kopiował kod z przeglądarki do skryptu), zakres wskazuje zakres i rodzaj dostępu. Muszą być oddzielone spacjami lub%20 (zgodnie z kodowaniem URL). Listę obszarów dostępu z typami można zobaczyć tutaj: Zakresy protokołu OAuth 2.0 dla interfejsów API Google.
Po otrzymaniu kodu autoryzacyjnego aplikacja zwróci do przeglądarki komunikat o zamknięciu, przestanie nasłuchiwać na porcie i wyśle żądanie POST w celu uzyskania tokena. Podajemy w nim podany wcześniej identyfikator i sekret z API konsoli, adres na który użytkownik zostanie przekierowany oraz typ_grantu zgodny ze specyfikacją protokołu.
W odpowiedzi otrzymamy token dostępu, jego okres ważności w sekundach oraz token odświeżenia, za pomocą którego będziemy mogli zaktualizować token dostępu.
Aplikacja musi przechowywać tokeny w bezpiecznym miejscu z długim terminem ważności, dlatego dopóki nie odwołamy otrzymanego dostępu, aplikacja nie zwróci tokena odświeżenia. Na koniec dodałem prośbę o unieważnienie tokena; jeśli aplikacja nie została pomyślnie zakończona i token odświeżenia nie został zwrócony, procedura rozpocznie się od nowa (uważaliśmy, że przechowywanie tokenów lokalnie na terminalu jest niebezpieczne i tego nie robimy nie chcę komplikować rzeczy z kryptografią ani często otwierać przeglądarki).
Jak już zauważyłeś, podczas unieważniania tokena używany jest Invoke-WebRequest. W przeciwieństwie do Invoke-RestMethod nie zwraca odebranych danych w użytecznym formacie i pokazuje status żądania.
Następnie skrypt poprosi o podanie imienia i nazwiska użytkownika, generując login + e-mail.
wnioski
Kolejnymi żądaniami będą - w pierwszej kolejności należy sprawdzić, czy użytkownik o tym samym loginie już istnieje, aby uzyskać decyzję o utworzeniu nowego lub włączeniu bieżącego.
Postanowiłem zaimplementować wszystkie żądania w formacie jednej funkcji z wyborem, używając przełącznika:
W każdym żądaniu należy wysłać nagłówek Authorization zawierający typ tokena oraz sam token dostępu. Obecnie typem tokena jest zawsze Bearer. Ponieważ musimy sprawdzić czy token nie wygasł i zaktualizować go po godzinie od momentu jego wystawienia, podałem żądanie innej funkcji zwracającej token Access. Ten sam fragment kodu znajduje się na początku skryptu podczas odbierania pierwszego tokena Access:
Żądanie email:$query poprosi interfejs API o wyszukanie użytkownika o dokładnie tym adresie e-mail, łącznie z aliasami. Możesz także użyć symbolu wieloznacznego: =, :, :{PREFIX}*.
Aby uzyskać dane, użyj metody żądania GET, wprowadź dane (zakładając konto lub dodając członka do grupy) - POST, aby zaktualizować istniejące dane - PUT, aby usunąć rekord (np. członka z grupy) - USUWAĆ.
Skrypt poprosi również o numer telefonu (niezweryfikowany ciąg znaków) i o włączenie do regionalnej grupy dystrybucyjnej. Decyduje jaką jednostkę organizacyjną powinien posiadać użytkownik na podstawie wybranej jednostki organizacyjnej Active Directory i podaje hasło:
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"
A potem zaczyna manipulować kontem:
$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
}
Funkcje aktualizacji i założenia konta mają podobną składnię, nie wszystkie dodatkowe pola są wymagane, w sekcji z numerami telefonów należy określić tablicę, która może zawierać maksymalnie jeden rekord z numerem i jego typem.
Aby nie otrzymać błędu przy dodawaniu użytkownika do grupy, możemy w pierwszej kolejności sprawdzić, czy jest on już członkiem tej grupy, pozyskując listę członków grupy lub skład od samego użytkownika.
Zapytanie o członkostwo określonego użytkownika w grupie nie będzie rekurencyjne i wyświetli jedynie bezpośrednie członkostwo. Dołączenie użytkownika do grupy nadrzędnej, która ma już grupę podrzędną, której członkiem jest ten użytkownik, zakończy się sukcesem.
wniosek
Pozostaje tylko wysłać użytkownikowi hasło do nowego konta. Robimy to za pomocą wiadomości SMS, a ogólne informacje wraz z instrukcją i loginem wysyłamy na prywatny adres e-mail, który wraz z numerem telefonu przekazał nam dział rekrutacji. Alternatywnie możesz zaoszczędzić pieniądze i wysłać hasło na tajny czat telegramowy, co można również uznać za drugi czynnik (MacBooki będą wyjątkiem).
Dziękuję za przeczytanie do końca. Chętnie zobaczę sugestie dotyczące poprawy stylu pisania artykułów i życzę wychwytywania mniejszej liczby błędów podczas pisania skryptów =)
Lista linków, które mogą być przydatne tematycznie lub po prostu odpowiedzieć na pytania: