Migracja z usługi Użytkownicy App Engine do Cloud Identity Platform (moduł 21)

1. Omówienie

Seria ćwiczeń z programowania dla bezserwerowych stacji migracji (samouczek, samouczków) i podobnych filmów ma pomóc deweloperom bezserwerowych Google Cloud w modernizacji aplikacji przez przeprowadzenie co najmniej 1 migracji, w wyniku rezygnacji ze starszych usług. W ten sposób Twoje aplikacje stają się bardziej przenośne, mają więcej opcji i elastyczność, co pozwala na integrację z szerszą gamą usług Cloud i uzyskiwanie do nich dostępu, a także łatwiejsze przejście na nowsze wersje językowe. Choć początkowo koncentrowaliśmy się na pierwszych użytkownikach Cloud, głównie deweloperów korzystających ze środowiska App Engine (środowisko standardowe), ta seria jest na tyle szeroka, aby uwzględnić inne platformy bezserwerowe, takie jak Cloud Functions i Cloud Run, oraz inne, w stosownych przypadkach.

Celem tego ćwiczenia w Codelabs jest pokazanie deweloperom aplikacji w języku Python 2 App Engine, jak przejść z interfejsu API/usługi użytkowników App Engine na Cloud Identity Platform (GCIP). Dostępna jest też migracja z App Engine NDB do Cloud NDB na potrzeby dostępu do Datastore (omówiona głównie w module migracji 2), a także przejście na Pythona 3.

W części 20 opisujemy, jak dodać sposób korzystania z interfejsu Users API do przykładowej aplikacji w module 1. W tym module ukończysz aplikację z modułu 20 i przeniesiesz jej wykorzystanie do Cloud Identity Platform.

Dowiesz się, jak:

  • Zastąp użycie usługi App Engine Użytkownicy usługą Cloud Identity Platform.
  • Zastąp korzystanie z App Engine NDB za pomocą Cloud NDB (zobacz też moduł 2).
  • Skonfiguruj różnych dostawców tożsamości uwierzytelniania za pomocą Uwierzytelniania Firebase
  • Uzyskiwanie informacji o uprawnieniach projektu przy użyciu interfejsu Cloud Resource Manager API
  • Uzyskiwanie informacji o użytkownikach za pomocą pakietu Firebase Admin SDK
  • Przenoszenie przykładowej aplikacji do języka Python 3

Czego potrzebujesz

Ankieta

Jak wykorzystasz ten samouczek?

tylko do przeczytania. Przeczytaj go i wykonaj ćwiczenia

Jak oceniasz swoje doświadczenia z językiem Python?

Początkujący Poziom średnio zaawansowany Biegły

Jak oceniasz korzystanie z usług Google Cloud?

Początkujący Poziom średnio zaawansowany Biegły
.

2. Tło

Usługa użytkowników App Engine to system uwierzytelniania użytkowników używany przez aplikacje App Engine. Udostępnia Logowanie przez Google jako dostawcę tożsamości, oferuje wygodne linki do logowania i wylogowywania się w aplikacjach oraz obsługuje koncepcję administratorów i funkcji dostępnych tylko dla administratorów. Aby zwiększyć przenośność aplikacji, Google Cloud zaleca przejście ze starszych pakietów App Engine na samodzielne usługi Cloud, na przykład z usługi Użytkownicy na Cloud Identity Platform.

Identity Platform jest oparty na Uwierzytelnianiu Firebase i dodaje wiele funkcji dla firm, w tym uwierzytelnianie wielopoziomowe, OIDC oraz Obsługa logowania jednokrotnego przez SAML, środowisko wielu najemców, gwarancja jakości usług na poziomie 99, 95% i inne funkcje. Różnice te są również wyróżnione na stronie porównania usług Identity Platform i Uwierzytelnianie Firebase. Obie usługi mają znacznie więcej funkcji niż usługa Użytkownicy.

W tym ćwiczeniu z programowania w module 21 pokazujemy, jak przejść z usługi Użytkownicy na funkcje Identity Platform dotyczące uwierzytelniania użytkowników aplikacji, które najlepiej odzwierciedlają funkcje omówione w module 20. Moduł 21 zawiera również migrację z App Engine NDB do Cloud NDB w celu uzyskania dostępu do Datastore, powtarzając migrację modułu 2.

Z kolei kod modułu 20 jest „reklamowany” jako przykładowa aplikacja w języku Python 2, samo źródło jest zgodne z językiem Python 2 i 3 i nie zmieni się nawet po migracji do Identity Platform (i Cloud NDB) w module 21. Możesz nadal korzystać z usługi Użytkownicy podczas uaktualniania do Pythona 3, ponieważ migracja do Identity Platform jest opcjonalna. Aby dowiedzieć się, jak dalej korzystać z pakietów usług przy jednoczesnym uaktualnieniu środowisk wykonawczych drugiej generacji, takich jak Python 3, zapoznaj się z ćwiczeniem z programowania poświęconym modułowi 17 i filmem.

W tym samouczku omawiamy następujące kroki:

  1. Konfiguracja/praca
  2. Aktualizacja konfiguracji
  3. Modyfikowanie kodu aplikacji

3. Konfiguracja/praca

W tej sekcji dowiesz się, jak:

  1. Konfigurowanie projektu Cloud
  2. Pobierz przykładową aplikację bazową
  3. (Ponowne) wdrażanie i weryfikowanie aplikacji bazowej
  4. Włącz nowe usługi i interfejsy API Google Cloud

Dzięki tym krokom zaczynasz od działającego kodu, który jest gotowy do migracji do samodzielnych usług Cloud.

1. Konfigurowanie projektu

Jeśli udało Ci się ukończyć Moduł 20 z programowania, wykorzystaj ponownie ten sam projekt (i kod). Możesz też utworzyć nowy projekt lub wykorzystać inny istniejący projekt. Sprawdź, czy projekt ma aktywne konto rozliczeniowe i włączoną aplikację App Engine. Znajdź identyfikator projektu i miej go pod ręką podczas tych ćwiczeń z programowania. Użyj go, gdy napotkasz zmienną PROJ_ID.

2. Pobierz przykładową aplikację bazową

Jednym z warunków wstępnych jest działająca aplikacja z modułu 20 App Engine, więc wykonaj ćwiczenia z programowania (zalecane; link powyżej) lub skopiuj kod modułu 20 z repozytorium. Bez względu na to, czy używasz swoich czy naszych danych, od tego zaczniemy („START”). To ćwiczenie w Codelabs przeprowadzi Cię przez proces migracji, zakończy się kodem podobnym do tego, który znajduje się w folderze repozytorium modułu 21 („FINISH”).

Skopiuj folder repozytorium modułu 20. Powinny one wyglądać jak poniższe dane wyjściowe. Jeśli ćwiczenie z programowania w module 20 zostało przez Ciebie wykonane, może zawierać folder lib:

$ ls
README.md               appengine_config.py     templates
app.yaml                main.py                 requirements.txt

3. (Ponowne) wdrażanie i weryfikowanie aplikacji bazowej

Aby wdrożyć aplikację modułu 20, wykonaj te czynności:

  1. Usuń folder lib (jeśli istnieje), a następnie uruchom polecenie pip install -t lib -r requirements.txt, aby ponownie go wypełnić. Jeśli masz zainstalowany zarówno język Python 2, jak i Python 3, może być konieczne użycie metody pip2.
  2. Pamiętaj, aby zainstalować i zainicjować narzędzie wiersza poleceń gcloud, a także sprawdzić jego użycie.
  3. Jeśli nie chcesz wpisywać danych PROJ_ID przy każdym wydanym poleceniu gcloud, ustaw najpierw projekt Google Cloud na wartość gcloud config set project PROJ_ID.
  4. Wdróż przykładową aplikację za pomocą: gcloud app deploy
  5. Potwierdź, że aplikacja działa zgodnie z oczekiwaniami i nie zawiera błędów. Jeśli udało Ci się ukończyć Moduł 20, aplikacja wyświetla dane logowania użytkownika (adres e-mail, możliwe „plakietkę administratora” i przycisk logowania/wylogowania) u góry wraz z ostatnimi wizytami (jak pokazano poniżej).

907e64c19ef964f8.png

Gdy logujesz się jako zwykły użytkownik, wyświetla się jego adres e-mail i jest wyświetlany komunikat „Zaloguj się” przycisk zmienia się na „Wyloguj się”. przycisk:

ad7b59916b69a035.png

Gdy logujesz się jako administrator, adres e-mail użytkownika jest wyświetlany razem z ciągiem „(admin)”. obok niej:

867bcb3334149e4.png

4. Włącz nowe interfejsy API/usługi Google Cloud

Wprowadzenie

Aplikacja Module 20 korzysta z interfejsów App Engine NDB i Users API, usług dostępnych w pakiecie, które nie wymagają dodatkowej konfiguracji, ale samodzielne usługi Cloud już tak. Zaktualizowana aplikacja będzie korzystać zarówno z Cloud Identity Platform, jak i Cloud Datastore (za pomocą biblioteki klienta Cloud NDB). Musimy też określić administratorów App Engine, korzystając z interfejsu Cloud Resource Manager API.

Koszt

  • App Engine i Cloud Datastore mają „Zawsze bezpłatne” poziomu. Jeśli będziesz go przekraczać, nie będziemy naliczać opłat za wykonanie tego samouczka. Więcej informacji znajdziesz też na stronie z cennikiem App Engine i na stronie z cennikiem Cloud Datastore.
  • Opłaty za korzystanie z Cloud Identity Platform są naliczane w zależności od liczby aktywnych użytkowników miesięcznie lub liczby weryfikacji uwierzytelniania. jakaś wersja „bezpłatnej” jest dostępna dla każdego modelu wykorzystania. Więcej informacji znajdziesz na stronie z cennikiem. Co więcej, chociaż App Engine i Cloud Datastore wymagają płatności, samo korzystanie z GCIP nie wymaga włączenia płatności, o ile nie przekraczasz dziennych limitów bez instrumentów. Warto to rozważyć w przypadku projektów Cloud, które nie wymagają rozliczeń ani usług Cloud APIs.
  • Korzystanie z interfejsu Cloud Resource Manager API jest w większości bezpłatne, zgodnie z cennikiem.

W zależności od preferencji użytkownicy włączają interfejsy Cloud APIs za pomocą konsoli Cloud lub wiersza poleceń (za pomocą polecenia gcloud, które jest częścią pakietu SDK Cloud). Zacznijmy od interfejsów Cloud Datastore i Cloud Resource Manager API.

W konsoli Google Cloud

Otwórz stronę Biblioteki menedżera interfejsów API (odpowiedni projekt) w konsoli Cloud i wyszukaj interfejs API, korzystając z paska wyszukiwania. c7a740304e9d35b.png

Włącz te interfejsy API:

Odszukaj i kliknij przycisk Włącz oddzielnie dla każdego interfejsu API. Może pojawić się prośba o podanie informacji rozliczeniowych. Oto przykładowa strona interfejsu Resource Manager API:

fc7bd8f4c49d12e5.png

Po włączeniu przycisk zmienia się na Zarządzaj (zwykle po kilku sekundach):

8eca12d6cc7b45b0.png

Włącz Cloud Datastore w ten sam sposób:

83811599b110e46b.png

Wiersz poleceń

Chociaż włączenie interfejsów API z poziomu konsoli jest dobrze ilustrowane, niektórzy wolą używać wiersza poleceń. Dodatkową zaletą jest to, że możesz włączyć dowolną liczbę interfejsów API jednocześnie. Uruchom to polecenie, by włączyć interfejsy Cloud Datastore i Cloud Resource Manager API oraz poczekać na zakończenie operacji, jak pokazano tutaj:

$ gcloud services enable cloudresourcemanager.googleapis.com datastore.googleapis.com
Operation "operations/acat.p2-aaa-bbb-ccc-ddd-eee-ffffff" finished successfully.

Może pojawić się prośba o podanie informacji rozliczeniowych.

Kolumny „Adresy URL” każdego interfejsu API użytego w powyższym poleceniu są nazywane nazwami usług interfejsów API. Można je znaleźć u dołu strony biblioteki danego interfejsu API. Jeśli chcesz włączyć inne interfejsy Cloud API dla swoich aplikacji, ich nazwy usług znajdziesz na odpowiednich stronach interfejsów API. To polecenie wyświetla listę wszystkich nazw usług dla interfejsów API, które możesz włączyć:

gcloud services list --available --filter="name:googleapis.com".

Po wykonaniu powyższych czynności zarówno w konsoli Cloud, jak i w wierszu poleceń, nasz przykład ma teraz dostęp do tych interfejsów API. Kolejne kroki to włączenie Cloud Identity Platform i wprowadzenie niezbędnych zmian w kodzie.

Włączanie i konfigurowanie Cloud Identity Platform (tylko w konsoli Cloud)

Cloud Identity Platform jest usługą Marketplace, ponieważ łączy się z zasobem spoza Google Cloud lub zależy od niego, na przykład z Uwierzytelnianiem Firebase. Obecnie usługi Marketplace możesz włączać tylko w konsoli Cloud. Wykonaj te czynności:

  1. Otwórz stronę Cloud Identity Platform w Cloud Marketplace i kliknij tam przycisk Włącz. Jeśli pojawi się taka prośba, przejdź na Uwierzytelnianie Firebase, aby odblokować dodatkowe funkcje, takie jak te opisane wcześniej w sekcji Kontekst. Oto strona Marketplace z zaznaczonym przyciskiem Włącz: 28475f1c9b29de69.png
  2. Po włączeniu Identity Platform może nastąpić automatyczne przeniesienie na stronę Dostawcy tożsamości. Jeśli nie, kliknij ten wygodny link, aby przejść do strony. fc2d92d42a5d1dd7.png
  3. Włącz dostawcę uwierzytelniania Google. Jeśli nie masz skonfigurowanych żadnych dostawców, kliknij Add a Provider (Dodaj dostawcę) i wybierz Google (Google). Gdy wrócisz na ten ekran, wpis Google powinien być włączony. Google jest jedynym dostawcą uwierzytelniania używanym w tym samouczku, aby odzwierciedlić usługę Użytkownicy App Engine jako uproszczoną usługę logowania przez Google. We własnych aplikacjach możesz włączyć dodatkowych dostawców uwierzytelniania.
  4. Gdy wybierzesz i skonfigurujesz Google i innych dostawców uwierzytelniania, kliknij Application Setup Details (Szczegóły konfiguracji aplikacji), a następnie w oknie potwierdzenia skopiuj apiKey i authDomain z obiektu config na karcie Sieć, zapisując oba w bezpiecznym miejscu. Może skopiujesz je wszystkie? Fragment tego okna jest zakodowany na stałe i opatrzony datą, dlatego wystarczy zapisać najważniejsze informacje i wykorzystać je w kodzie, jednocześnie korzystając z Uwierzytelniania Firebase. Po skopiowaniu wartości i zapisaniu ich w bezpiecznym miejscu kliknij przycisk Zamknij, aby dokończyć konfigurację. bbb09dcdd9be538e.png

4. Aktualizacja konfiguracji

Zmiany w konfiguracji obejmują zarówno zmianę różnych plików konfiguracji, jak i utworzenie odpowiednika App Engine, ale w ekosystemie Cloud Identity Platform.

appengine_config.py

  • Jeśli przechodzisz na Pythona 3, usuń appengine_config.py
  • Jeśli planujesz modernizację w Identity Platform, ale pozostaw w języku Python 2, nie usuwaj pliku. Zamiast tego zaktualizujemy ją później podczas Backportu Pythona 2.

requirements.txt

W pliku requirements.txt modułu 20 znajduje się tylko Flask. W module 21 dodaj te pakiety:

Zawartość pola requirements.txt powinna teraz wyglądać tak:

flask
google-auth
google-cloud-ndb
google-cloud-resource-manager
firebase-admin

app.yaml

  • Przejście na Pythona 3 oznacza uproszczenie pliku app.yaml. Usuń wszystko oprócz dyrektywy środowiska wykonawczego i ustaw obsługiwaną wersję Pythona 3. W przykładzie jest obecnie używana wersja 3.10.
  • Jeśli korzystasz z Pythona 2, nie rób jeszcze żadnych czynności.

PRZED:

runtime: python27
threadsafe: yes
api_version: 1

handlers:
- url: /.*
  script: main.app

Przykładowa aplikacja modułu 20 nie ma statycznych modułów obsługi plików. Jeśli Twoje aplikacje tak mają, pozostaw je bez zmian. W razie potrzeby możesz usunąć wszystkie moduły obsługi skryptów lub pozostawić je w celach informacyjnych, jeśli zmienisz ich nicki na auto, zgodnie z opisem w przewodniku po migracji app.yaml. Dzięki tym zmianom zaktualizowany app.yaml dla Pythona 3 został uproszczony do:

PO:

runtime: python310

Inne aktualizacje konfiguracji

Niezależnie od tego, czy pozostaniesz w języku Python 2, czy przejdziesz do Pythona 3, usuń go, jeśli masz folder lib.

5. Modyfikowanie kodu aplikacji

W tej sekcji omawiamy aktualizacje głównego pliku aplikacji main.py, które zastępują użycie usługi Użytkownicy App Engine usługą Cloud Identity Platform. Po zaktualizowaniu głównej aplikacji zaktualizujesz szablon internetowy templates/index.html.

Aktualizowanie importów i inicjowania

Aby zaktualizować importy i zainicjować zasoby aplikacji, wykonaj te czynności:

  1. Na potrzeby importów zastąp App Engine NDB danymi Cloud NDB.
  2. Oprócz Cloud NDB zaimportuj także usługę Cloud Resource Manager.
  3. Usługa Identity Platform jest oparta na Uwierzytelnianiu Firebase, więc zaimportuj pakiet SDK Firebase Admin.
  4. Interfejsy Cloud APIs wymagają użycia klienta API, więc zainicjuj go dla Cloud NDB tuż poniżej inicjowania Flask.

Podczas importowania pakietu Cloud Resource Manager w tym miejscu użyjemy go na późniejszym etapie podczas inicjowania aplikacji. Poniżej znajdziesz informacje o importach i inicjowaniu z Modułu 20 oraz o tym, jak powinny wyglądać poszczególne sekcje po wprowadzeniu powyższych zmian:

PRZED:

from flask import Flask, render_template, request
from google.appengine.api import users
from google.appengine.ext import ndb

app = Flask(__name__)

PO:

from flask import Flask, render_template, request
from google.auth import default
from google.cloud import ndb, resourcemanager
from firebase_admin import auth, initialize_app

# initialize Flask and Cloud NDB API client
app = Flask(__name__)
ds_client = ndb.Client()

Pomoc dla administratorów App Engine

Do aplikacji można dodać 2 komponenty, które obsługują rozpoznawanie administratorów:

  • _get_gae_admins() – zbiera dane administratorów; wywołano raz i zapisano
  • is_admin() – sprawdza, czy zalogowany użytkownik jest administratorem. wywoływana przy każdym logowaniu użytkownika

Funkcja narzędziowa (_get_gae_admins()) wywołuje interfejs Resource Manager API, aby pobrać bieżącą zasadę Cloud IAM allow-policy. Zasada allow-policy definiuje i egzekwuje, które role są przypisywane do poszczególnych podmiotów zabezpieczeń (użytkownicy, konta usługi itp.). Konfiguracja obejmuje:

  • Pobieram identyfikator projektu Cloud (PROJ_ID)
  • Tworzę klienta interfejsu Resource Manager API (rm_client)
  • Tworzenie zestawu ról administratora App Engine (tylko do odczytu) (_TARGETS)

Menedżer zasobów wymaga identyfikatora projektu Cloud, więc zaimportuj plik google.auth.default() i wywołaj tę funkcję, aby uzyskać identyfikator projektu. Zawiera ono parametr, który wygląda jak adres URL, ale jest zakresem uprawnień OAuth2. Podczas uruchamiania aplikacji w chmurze, na przykład na maszynie wirtualnej Compute Engine lub w aplikacji App Engine, udostępniane jest domyślne konto usługi z szerokimi uprawnieniami. Zgodnie ze sprawdzonymi metodami dotyczącymi jak najmniejszych uprawnień zalecamy utworzenie własnych kont usługi zarządzanych przez użytkowników.

W przypadku wywołań interfejsu API najlepiej jeszcze ograniczyć zakres aplikacji do minimalnego poziomu wymaganego do prawidłowego działania. Wywoływane przez nas wywołanie interfejsu Resource Manager API to get_iam_policy(), które do działania wymaga jednego z tych zakresów:

  • https://www.googleapis.com/auth/cloud-platform
  • https://www.googleapis.com/auth/cloud-platform.read-only
  • https://www.googleapis.com/auth/cloudplatformprojects
  • https://www.googleapis.com/auth/cloudplatformprojects.readonly

Przykładowa aplikacja wymaga dostępu tylko do odczytu zasady allow-policy. Nie modyfikuje zasad ani nie potrzebuje dostępu do całego projektu. Oznacza to, że aplikacja nie potrzebuje żadnego z pierwszych 3 wymaganych uprawnień. Ostatni z nich to wszystko, czego potrzebujesz i tego właśnie wdrażamy w przykładowej aplikacji.

Główna treść funkcji tworzy pusty zbiór administratorów (admins), pobiera allow_policy za pomocą get_iam_policy() i przewraca wszystkie jego powiązania w poszukiwaniu ról administratora App Engine:

  • roles/viewer
  • roles/editor
  • roles/owner
  • roles/appengine.appAdmin

W przypadku każdej znalezionej roli docelowej sprawdza, którzy użytkownicy należą do danej roli, przez dodanie ich do ogólnego zestawu administratorów. Zakończy się, gdy zostaną zwrócone dane wszystkich administratorów znalezionych i zapisanych w pamięci podręcznej w postaci wartości stałej (_ADMINS) przez cały okres istnienia tej instancji App Engine. Wkrótce się z Tobą skontaktujemy.

Dodaj tę definicję funkcji _get_gae_admins() do main.py tuż poniżej tworzenia instancji klienta Cloud NDB API (ds_client):

def _get_gae_admins():
    'return set of App Engine admins'
    # setup constants for calling Cloud Resource Manager API
    _, PROJ_ID = default(  # Application Default Credentials and project ID
            ['https://www.googleapis.com/auth/cloudplatformprojects.readonly'])
    rm_client = resourcemanager.ProjectsClient()
    _TARGETS = frozenset((     # App Engine admin roles
            'roles/viewer',
            'roles/editor',
            'roles/owner',
            'roles/appengine.appAdmin',
    ))

    # collate users who are members of at least one GAE admin role (_TARGETS)
    admins = set()                      # set of all App Engine admins
    allow_policy = rm_client.get_iam_policy(resource='projects/%s' % PROJ_ID)
    for b in allow_policy.bindings:     # bindings in IAM allow-policy
        if b.role in _TARGETS:          # only look at GAE admin roles
            admins.update(user.split(':', 1).pop() for user in b.members)
    return admins

Gdy użytkownicy logują się w aplikacji:

  1. Gdy użytkownik zaloguje się w Firebase, przeprowadzana jest szybka weryfikacja na podstawie szablonu strony internetowej.
  2. Gdy stan uwierzytelniania w szablonie zmieni się, wywołanie fetch() w stylu Ajax zostanie wykonane do elementu /is_admin, którego moduł obsługi jest następną funkcją (is_admin()).
  3. Token identyfikatora Firebase jest przekazywany w treści POST do kodu is_admin(), który pobiera go z nagłówków i wywołuje pakiet SDK Firebase Admin w celu jego weryfikacji. Jeśli adres e-mail jest prawidłowym użytkownikiem, wyodrębnij jego adres e-mail i sprawdź, czy należy do administratora.
  4. Następnie wartość logiczna jest zwracana do szablonu z powodzeniem 200.

Dodaj is_admin() do main.py tuż po _get_gae_admins():

@app.route('/is_admin', methods=['POST'])
def is_admin():
    'check if user (via their Firebase ID token) is GAE admin (POST) handler'
    id_token = request.headers.get('Authorization')
    email = auth.verify_id_token(id_token).get('email')
    return {'admin': email in _ADMINS}, 200

Do odtworzenia funkcji dostępnych w usłudze Użytkownicy, a w szczególności z funkcji is_current_user_admin(), wymagany jest cały kod z obu funkcji. To wywołanie funkcji w module 20 wykonało wszystkie najcięższe zadania, w przeciwieństwie do modułu 21, w którym wdrażamy rozwiązanie zastępcze. Dobra wiadomość jest taka, że aplikacja nie jest już zależna od usługi wyłącznie App Engine, co oznacza, że możesz przenieść swoje aplikacje do Cloud Run lub innych usług. Możesz też zmienić definicję „administratora” dla własnych aplikacji przez przełączenie na odpowiednie role w _TARGETS, a usługa Użytkownicy jest na stałe zakodowana w przypadku ról administratora App Engine.

Inicjowanie administratorów App Engine korzystających z uwierzytelniania Firebase i pamięci podręcznej

Mogliśmy zainicjować Uwierzytelnianie Firebase u góry w pobliżu tego samego miejsca, w którym inicjowano aplikację Flask i utworzyłeś klienta Cloud NDB API. Nie było jednak potrzebne do zdefiniowania całego kodu administratora, czyli właśnie teraz. Podobnie, gdy zdefiniowano już pole _get_gae_admins(), wywołaj je, aby zapisać w pamięci podręcznej listę administratorów.

Dodaj te wiersze tuż pod treścią funkcji is_admin():

# initialize Firebase and fetch set of App Engine admins
initialize_app()
_ADMINS = _get_gae_admins()

Aktualizacje modelu danych wizyt

Model danych Visit się nie zmienia. Dostęp do Datastore wymaga jawnego użycia menedżera kontekstu klienta Cloud NDB API (ds_client.context()). W kodzie oznacza to opakowanie wywołań Datastore zarówno w store_visit(), jak i fetch_visits() wewnątrz bloków Pythona with. Ta aktualizacja jest taka sama jak w module 2. Wprowadź zmiany w następujący sposób:

PRZED:

class Visit(ndb.Model):
    'Visit entity registers visitor IP address & timestamp'
    visitor   = ndb.StringProperty()
    timestamp = ndb.DateTimeProperty(auto_now_add=True)

def store_visit(remote_addr, user_agent):
    'create new Visit entity in Datastore'
    Visit(visitor='{}: {}'.format(remote_addr, user_agent)).put()

def fetch_visits(limit):
    'get most recent visits'
    return Visit.query().order(-Visit.timestamp).fetch(limit)

PO:

class Visit(ndb.Model):
    'Visit entity registers visitor IP address & timestamp'
    visitor   = ndb.StringProperty()
    timestamp = ndb.DateTimeProperty(auto_now_add=True)

def store_visit(remote_addr, user_agent):
    'create new Visit entity in Datastore'
    with ds_client.context():
        Visit(visitor='{}: {}'.format(remote_addr, user_agent)).put()

def fetch_visits(limit):
    'get most recent visits'
    with ds_client.context():
        return Visit.query().order(-Visit.timestamp).fetch(limit)

Przenieś logikę logowania użytkownika do szablonu internetowego

Usługa użytkowników App Engine działa po stronie serwera, a Uwierzytelnianie Firebase i Cloud Identity Platform działają głównie po stronie klienta. W rezultacie duża część kodu zarządzania użytkownikami z aplikacji Moduł 20 jest przenoszona do szablonu internetowego modułu 21.

W main.py kontekst internetowy przekazuje do szablonu 5 zasadniczych danych. Cztery pierwsze z nich są powiązane z zarządzaniem użytkownikami i różnią się w zależności od tego, czy użytkownik jest zalogowany:

  • who – adres e-mail użytkownika, który jest zalogowany, lub użytkownik, jeśli jest zalogowany;
  • admin – plakietka (administrator), jeśli zalogowany użytkownik jest administratorem
  • sign – pokaż przycisk Zaloguj się lub Wyloguj się
  • link – linki umożliwiające zalogowanie się i wylogowanie po kliknięciu przycisku
  • visits – ostatnie wizyty

PRZED:

@app.route('/')
def root():
    'main application (GET) handler'
    store_visit(request.remote_addr, request.user_agent)
    visits = fetch_visits(10)

    # put together users context for web template
    user = users.get_current_user()
    context = {  # logged in
        'who':   user.nickname(),
        'admin': '(admin)' if users.is_current_user_admin() else '',
        'sign':  'Logout',
        'link':  '/_ah/logout?continue=%s://%s/' % (
                      request.environ['wsgi.url_scheme'],
                      request.environ['HTTP_HOST'],
                  ),  # alternative to users.create_logout_url()
    } if user else {  # not logged in
        'who':   'user',
        'admin': '',
        'sign':  'Login',
        'link':  users.create_login_url('/'),
    }

    # add visits to context and render template
    context['visits'] = visits  # display whether logged in or not
    return render_template('index.html', **context)

Wszystkie elementy zarządzania użytkownikami zostaną przeniesione do szablonu internetowego, więc pozostało nam tylko wizyty, przywracając główny moduł obsługi do stanu z aplikacji w module 1:

PO:

@app.route('/')
def root():
    'main application (GET) handler'
    store_visit(request.remote_addr, request.user_agent)
    visits = fetch_visits(10)
    return render_template('index.html', visits=visits)

Zaktualizuj szablon internetowy

Jak wyglądają w szablonie wszystkie aktualizacje z poprzedniej sekcji? Głównie przenieśliśmy zarządzanie użytkownikami z aplikacji do uwierzytelniania Firebase działającego w szablonie i częściowo przenosimy cały kod do JavaScriptu. main.py znacznie zmalało – spodziewaj się podobnego wzrostu w kategorii templates/index.html.

PRZED:

<!doctype html>
<html>
<head>
<title>VisitMe Example</title>
</head>
<body>
<p>
Welcome, {{ who }} <code>{{ admin }}</code>
<button id="logbtn">{{ sign }}</button>
</p><hr>

<h1>VisitMe example</h1>
<h3>Last 10 visits</h3>
<ul>
{% for visit in visits %}
    <li>{{ visit.timestamp.ctime() }} from {{ visit.visitor }}</li>
{% endfor %}
</ul>

<script>
document.getElementById("logbtn").onclick = () => {
    window.location.href = '{{ link }}';
};
</script>
</body>
</html>

Zastąp cały szablon internetowy poniższą treścią:

PO:

<!doctype html>
<html>
<head>
<title>VisitMe Example</title>

<script type="module">
// import Firebase module attributes
import {
        initializeApp
} from "https://www.gstatic.com/firebasejs/9.10.0/firebase-app.js";
import {
        GoogleAuthProvider,
        getAuth,
        onAuthStateChanged,
        signInWithPopup,
        signOut
} from "https://www.gstatic.com/firebasejs/9.10.0/firebase-auth.js";

// Firebase config:
// 1a. Go to: console.cloud.google.com/customer-identity/providers
// 1b. May be prompted to enable GCIP and upgrade from Firebase
// 2. Click: "Application Setup Details" button
// 3. Copy: 'apiKey' and 'authDomain' from 'config' variable
var firebaseConfig = {
        apiKey: "YOUR_API_KEY",
        authDomain: "YOUR_AUTH_DOMAIN",
};

// initialize Firebase app & auth components
initializeApp(firebaseConfig);
var auth = getAuth();
var provider = new GoogleAuthProvider();
//provider.setCustomParameters({prompt: 'select_account'});

// define login and logout button functions
function login() {
    signInWithPopup(auth, provider);
};

function logout() {
    signOut(auth);
};

// check if admin & switch to logout button on login; reset everything on logout
onAuthStateChanged(auth, async (user) => {
    if (user && user != null) {
        var email = user.email;
        who.innerHTML = email;
        logbtn.onclick = logout;
        logbtn.innerHTML = "Logout";
        var idToken = await user.getIdToken();
        var rsp = await fetch("/is_admin", {
                method: "POST",
                headers: {Authorization: idToken}
        });
        var data = await rsp.json();
        if (data.admin) {
            admin.style.display = "inline";
        }
    } else {
        who.innerHTML = "user";
        admin.style.display = "none";
        logbtn.onclick = login;
        logbtn.innerHTML = "Login";
    }
});
</script>
</head>

<body>
<p>
Welcome, <span id="who"></span> <span id="admin"><code>(admin)</code></span>
<button id="logbtn"></button>
</p><hr>

<h1>VisitMe example</h1>
<h3>Last 10 visits</h3>
<ul>
{% for visit in visits %}
    <li>{{ visit.timestamp.ctime() }} from {{ visit.visitor }}</li>
{% endfor %}
</ul>

<script>
var who    = document.getElementById("who");
var admin  = document.getElementById("admin");
var logbtn = document.getElementById("logbtn");
</script>
</body>
</html>

Treść HTML składa się z wielu komponentów, więc omówimy je po kolei.

Importy Firebase

Nie wychodząc z nagłówka dokumentu HTML, zaimportuj potrzebne komponenty Firebase za tytułem strony. Aby zwiększyć wydajność, komponenty Firebase są teraz podzielone na wiele modułów. Kod inicjujący Firebase jest importowany z głównego modułu aplikacji Firebase, a funkcje zarządzające uwierzytelnianiem Firebase, Google jako dostawca uwierzytelniania, logowanie i wylogowywanie oraz zmiana stanu uwierzytelniania „wywołanie zwrotne” zostaną zaimportowane z modułu Uwierzytelniania Firebase:

<!doctype html>
<html>
<head>
<title>VisitMe Example</title>

<script type="module">
// import Firebase module attributes
import {
        initializeApp
} from "https://www.gstatic.com/firebasejs/9.10.0/firebase-app.js";
import {
        GoogleAuthProvider,
        getAuth,
        onAuthStateChanged,
        signInWithPopup,
        signOut
} from "https://www.gstatic.com/firebasejs/9.10.0/firebase-auth.js";

Konfiguracja Firebase

Wcześniej podczas konfigurowania Identity Platform w tym samouczku zapisano apiKey i authDomain w oknie Szczegóły konfiguracji aplikacji. Dodaj te wartości do zmiennej firebaseConfig w następnej sekcji. Link do szczegółowych instrukcji znajdziesz w komentarzach:

// Firebase config:
// 1a. Go to: console.cloud.google.com/customer-identity/providers
// 1b. May be prompted to enable GCIP and upgrade from Firebase
// 2. Click: "Application Setup Details" button
// 3. Copy: 'apiKey' and 'authDomain' from 'config' variable
var firebaseConfig = {
        apiKey: "YOUR_API_KEY",
        authDomain: "YOUR_AUTH_DOMAIN",
};

Inicjalizacja Firebase

W następnej sekcji zainicjujesz Firebase, korzystając z tych informacji o konfiguracji.

// initialize Firebase app & auth components
initializeApp(firebaseConfig);
var auth = getAuth();
var provider = new GoogleAuthProvider();
//provider.setCustomParameters({prompt: 'select_account'});

Pozwala to używać Google jako dostawcy uwierzytelniania i udostępnia opcję bez komentarza, która umożliwia wyświetlenie selektora kont, nawet jeśli w sesji przeglądarki jest zarejestrowane tylko jedno konto Google. Innymi słowy, jeśli masz kilka kont, zobaczysz selektor kont zgodnie z oczekiwaniami: a38369389b7c4c7e.png Jeśli jednak w sesji będzie tylko jeden użytkownik, proces logowania zakończy się automatycznie bez interakcji ze strony użytkownika. (Pojawi się wyskakujące okienko, po czym znika). Możesz wymusić wyświetlanie okna selektora kont u jednego użytkownika (zamiast od razu logować się w aplikacji), usuwając komentarz do wiersza parametru niestandardowego. Jeśli ta opcja jest włączona, nawet logowanie pojedynczego użytkownika powoduje wyświetlenie selektora kont: b75624cb68d94557.png

Funkcje logowania i wylogowywania

Następne wiersze kodu tworzą funkcje kliknięć przycisku logowania lub wylogowania:

// define login and logout button functions
function login() {
    signInWithPopup(auth, provider);
};

function logout() {
    signOut(auth);
};

Działania związane z logowaniem i wylogowywaniem się

Ostatnia główna sekcja tego bloku <script> to funkcja, która jest wywoływana przy każdej zmianie uwierzytelniania (zalogowaniu się lub wylogowaniu).

// check if admin & switch to logout button on login; reset everything on logout
onAuthStateChanged(auth, async (user) => {
    if (user && user != null) {
        var email = user.email;
        who.innerHTML = email;
        logbtn.onclick = logout;
        logbtn.innerHTML = "Logout";
        var idToken = await user.getIdToken();
        var rsp = await fetch("/is_admin", {
                method: "POST",
                headers: {Authorization: idToken}
        });
        var data = await rsp.json();
        if (data.admin) {
            admin.style.display = "inline";
        }
    } else {
        who.innerHTML = "user";
        admin.style.display = "none";
        logbtn.onclick = login;
        logbtn.innerHTML = "Login";
    }
});
</script>
</head>

Kod w module 20 określający, czy należy wysłać „zalogowania użytkownika” kontekst szablonu a „użytkownik wylogowany” kontekst został przeniesiony w tym miejscu. Wartość warunkowa na górze powoduje wyświetlenie wyniku true, jeśli użytkownik się zalogował, co powoduje wykonanie tych działań:

  1. Adres e-mail użytkownika jest skonfigurowany do wyświetlania.
  2. Przycisk Zaloguj zmieni się w Wyloguj.
  3. Wywołanie /is_admin w stylu Ajax pozwala określić, czy należy wyświetlać plakietkę administratora (admin).

Gdy użytkownik się wyloguje, wykonywana jest klauzula else, aby zresetować wszystkie informacje o użytkowniku:

  1. Nazwa użytkownika ustawiona na user
  2. Usunięto plakietkę administratora
  3. Przycisk Wyloguj się zmienił się z powrotem na Login

Zmienne szablonu

Po zakończeniu sekcji nagłówka główna treść zaczyna się od zmiennych szablonu, które są zastępowane elementami HTML, które zmieniają się zależnie od potrzeb:

  1. Wyświetlana nazwa użytkownika
  2. Plakietka administratora (admin) (jeśli jest potrzebna)
  3. Przycisk Zaloguj się lub Wyloguj się
<body>
<p>
Welcome, <span id="who"></span> <span id="admin"><code>(admin)</code></span>
<button id="logbtn"></button>
</p><hr>

Najnowsze wizyty i zmienne elementów HTML

Kod ostatniej wizyty nie zmienia się, a końcowy blok <script> ustawia zmienne dla elementów HTML, które zmieniają się w przypadku wymienionego powyżej sposobu logowania i wylogowywania:

<h1>VisitMe example</h1>
<h3>Last 10 visits</h3>
<ul>
{% for visit in visits %}
    <li>{{ visit.timestamp.ctime() }} from {{ visit.visitor }}</li>
{% endfor %}
</ul>

<script>
var who    = document.getElementById("who");
var admin  = document.getElementById("admin");
var logbtn = document.getElementById("logbtn");
</script>
</body>
</html>

To już koniec zmian, które trzeba wprowadzić w aplikacji i szablonie internetowym, aby przejść z interfejsów App Engine NDB i Users API na Cloud NDB i Identity Platform, a także przejść na Pythona 3. Gratulujemy udostępnienia nowej, przykładowej aplikacji w module 21. Nasza wersja jest dostępna do przejrzenia w folderze repozytorium modułu 21b.

Następna część ćwiczenia w Codelabs jest opcjonalna * i tylko dla użytkowników, których aplikacje muszą pozostać w języku Python 2. Prowadzą Cię one przez czynności niezbędne do znalezienia działającej aplikacji modułu Python 21.

6. *Backport Pythona 2

Ta opcjonalna sekcja jest przeznaczona dla deweloperów przeprowadzających migrację z Identity Platform, ale którzy muszą nadal korzystać ze środowiska wykonawczego Pythona 2. Jeśli ten problem Cię nie dotyczy, pomiń tę sekcję.

Aby utworzyć działającą wersję aplikacji Moduł 21 w języku Python 2, potrzebujesz:

  1. Wymagania dotyczące środowiska wykonawczego: pliki konfiguracji, które obsługują Pythona 2, oraz zmiany w głównej aplikacji, które pozwalają uniknąć niezgodności z Pythonem 3
  2. Drobna zmiana w bibliotece: język Python 2 został wycofany, zanim do biblioteki klienta Menedżera zasobów zostały dodane niektóre wymagane funkcje. W związku z tym potrzebujesz innego sposobu na dostęp do tej brakującej funkcji.

Zacznijmy od konfiguracji.

Przywróć appengine_config.py

Na początku tego samouczka poinstruowaliśmy Cię, by usunąć plik appengine_config.py, ponieważ nie jest on używany przez środowisko wykonawcze App Engine w języku Python 3. W przypadku Pythona 2 nie tylko należy ją zachować. Trzeba też zaktualizować Moduł 20 appengine_config.py, aby obsługiwał korzystanie z wbudowanych bibliotek zewnętrznych (grpcio i setuptools). Te pakiety są wymagane zawsze, gdy aplikacja App Engine używa bibliotek klienta Cloud, takich jak Cloud NDB czy Cloud Resource Manager.

Za chwilę dodasz te pakiety do usługi app.yaml, ale aby aplikacja mogła uzyskać do nich dostęp, musi zostać wywołana funkcja pkg_resources.working_set.add_entry() z usługi setuptools. Dzięki temu skopiowane (dodane do siebie lub dostarczone przez dostawcę) biblioteki innych firm zainstalowane w folderze lib będą mogły komunikować się z wbudowanymi bibliotekami.

Aby wprowadzić te zmiany, wprowadź w pliku appengine_config.py te aktualizacje:

PRZED:

from google.appengine.ext import vendor

# Set PATH to your libraries folder.
PATH = 'lib'
# Add libraries installed in the PATH folder.
vendor.add(PATH)

Ten kod nie wystarczy do korzystania z elementów setuptools i grpcio. Potrzebnych jest jeszcze kilka wierszy, więc zaktualizuj appengine_config.py, aby wyglądał tak:

PO:

import pkg_resources
from google.appengine.ext import vendor

# Set PATH to your libraries folder.
PATH = 'lib'
# Add libraries installed in the PATH folder.
vendor.add(PATH)
# Add libraries to pkg_resources working set to find the distribution.
pkg_resources.working_set.add_entry(PATH)

Więcej informacji o zmianach wymaganych do obsługi bibliotek klienta Cloud znajdziesz w dokumentacji migracji pakietów usług.

app.yaml

Podobnie jak w przypadku appengine_config.py plik app.yaml musi zostać przywrócony do wersji, która obsługuje język Python 2. Zacznijmy od oryginalnego modułu 20 app.yaml:

PRZED:

runtime: python27
threadsafe: yes
api_version: 1

handlers:
- url: /.*
  script: main.app

Oprócz wspomnianych wcześniej rozwiązań setuptools i grpcio istnieje zależność (niezwiązana bezpośrednio z migracją Identity Platform) wymagająca użycia biblioteki klienta Cloud Storage oraz która wymaga innego wbudowanego pakietu innych firm: ssl. Dodaj wszystkie trzy w nowej sekcji libraries, wybierając „najnowsze” dostępnych wersji tych pakietów do usługi app.yaml:

PO:

runtime: python27
threadsafe: yes
api_version: 1

handlers:
- url: /.*
  script: main.app

libraries:
- name: grpcio
  version: latest
- name: setuptools
  version: latest
- name: ssl
  version: latest

requirements.txt

W module 21 dodaliśmy do Pythona 3 requirements.txt Google Auth, Cloud NDB, Cloud Resource Manager i Firebase Admin SDK. Sytuacja w języku Python 2 jest bardziej złożona:

  • Interfejs Resource Manager API zapewnia funkcję allow-policy wymaganą w przykładowej aplikacji. Ta funkcja nie była jeszcze dostępna w ostatecznej wersji Pythona 2 w bibliotece klienta Cloud Resource Manager. (Jest dostępny tylko w wersji Pythona 3).
  • W związku z tym wymagany jest alternatywny sposób dostępu do tej funkcji z poziomu interfejsu API. Rozwiązaniem jest używanie biblioteki klienta interfejsów API Google niższego poziomu do komunikacji z interfejsem API. Aby przełączyć się na tę bibliotekę klienta, zastąp google-cloud-resource-manager pakietem google-api-python-client niższego poziomu.
  • Ponieważ Python 2 został wycofany, graf zależności obsługujący moduł 21 wymaga zablokowania niektórych pakietów w określonych wersjach. Niektóre pakiety muszą być wywoływane, nawet jeśli nie zostały określone w zasadzie app.yaml w języku Python 3.

PRZED:

flask

Począwszy od modułu 20 requirements.txt, zaktualizuj go do następującego fragmentu w przypadku działającego modułu 21:

PO:

grpcio==1.0.0
protobuf<3.18.0
six>=1.13.0
flask
google-gax<0.13.0
google-api-core==1.31.1
google-api-python-client<=1.11.0
google-auth<2.0dev
google-cloud-datastore==1.15.3
google-cloud-firestore==1.9.0
google-cloud-ndb
google-cloud-pubsub==1.7.0
firebase-admin

Numery pakietu i wersji będą aktualizowane w repozytorium w miarę zmiany zależności, ale w momencie tworzenia tego tekstu app.yaml wystarczy w przypadku działającej aplikacji.

Inne aktualizacje konfiguracji

Jeśli folder lib nie został usunięty z wcześniejszej części tego ćwiczenia z programowania, zrób to teraz. W przypadku zaktualizowanej wersji requirements.txt uruchom to znane polecenie, aby zainstalować te wymagania w systemie lib:

pip install -t lib -r requirements.txt  # or pip2

Jeśli w Twoim systemie programistycznym masz zainstalowany język Python 2 i 3, może być konieczne użycie pip2 zamiast pip.

Modyfikowanie kodu aplikacji

Na szczęście większość wymaganych zmian znajduje się w plikach konfiguracji. Jedyną wymaganą zmianą w kodzie aplikacji jest drobna aktualizacja umożliwiająca korzystanie z biblioteki klienta interfejsu API Google niższego poziomu zamiast biblioteki klienta Menedżera zasobów do uzyskiwania dostępu do interfejsu API. Szablon internetowy templates/index.html nie wymaga aktualizacji.

Importowanie i inicjowanie aktualizacji

Zastąp bibliotekę klienta Menedżera zasobów (google.cloud.resourcemanager) biblioteką klienta interfejsów API Google (googleapiclient.discovery), jak pokazano poniżej:

PRZED:

from flask import Flask, render_template, request
from google.auth import default
from google.cloud import ndb, resourcemanager
from firebase_admin import auth, initialize_app

PO:

from flask import Flask, render_template, request
from google.auth import default
from google.cloud import ndb
from googleapiclient import discovery
from firebase_admin import auth, initialize_app

Pomoc dla administratorów App Engine

Aby umożliwić korzystanie z biblioteki klienta niższego poziomu, trzeba wprowadzić w _get_gae_admins() kilka zmian. Omówmy najpierw, co się zmienia, a potem przekażę Ci cały kod do zaktualizowania.

Kod Pythona 2 wymaga użycia zarówno danych logowania, jak i identyfikatora projektu zwróconego z narzędzia google.auth.default(). Dane logowania nie są używane w Pythonie 3, więc zostały przypisane do ogólnej zmiennej podkreślenia ( _). Jest ono wymagane w przypadku wersji Pythona 2, dlatego zmień podkreślenie na CREDS. Poza tym zamiast tworzyć klienta interfejsu Resource Manager API, utworzysz punkt końcowy usługi interfejsu API podobnie jak w przypadku klienta API, więc zachowamy tę samą nazwę zmiennej (rm_client). Jedna z różnic polega na tym, że utworzenie instancji punktu końcowego usługi wymaga danych logowania (CREDS).

Te zmiany znajdują odzwierciedlenie w poniższym kodzie:

PRZED:

_, PROJ_ID = default(  # Application Default Credentials and project ID
        ['https://www.googleapis.com/auth/cloudplatformprojects.readonly'])
rm_client = resourcemanager.ProjectsClient()

PO:

CREDS, PROJ_ID = default(  # Application Default Credentials and project ID
        ['https://www.googleapis.com/auth/cloud-platform'])
rm_client = discovery.build('cloudresourcemanager', 'v1', credentials=CREDS)

Inna różnica polega na tym, że biblioteka klienta Menedżera zasobów zwraca obiekty zasady allow-policy, które używają notacji atrybutów kropkowanych, a biblioteka klienta niższego poziomu zwraca słowniki Pythona, w których używane są nawiasy kwadratowe ( [ ]). Na przykład użyj binding.role w przypadku biblioteki klienta Menedżera zasobów, a nie binding['role'] w przypadku biblioteki niższego poziomu. W pierwszym przypadku użyto również argumentu „podkreślenie_oddzielone”. zamiast nazw z bibliotek niższego poziomu preferujących format „CamelCased” oraz nieco inny sposób przekazywania parametrów interfejsu API.

Poniżej przedstawiono różnice w wykorzystaniu:

PRZED:

allow_policy = rm_client.get_iam_policy(resource='projects/%s' % PROJ_ID)
for b in allow_policy.bindings:     # bindings in IAM allow-policy
    if b.role in _TARGETS:          # only look at GAE admin roles
        admins.update(user.split(':', 1).pop() for user in b.members)

PO:

allow_policy = rm_client.projects().getIamPolicy(resource=PROJ_ID).execute()
for b in allow_policy['bindings']:  # bindings in IAM allow-policy
    if b['role'] in _TARGETS:       # only look at GAE admin roles
        admins.update(user.split(':', 1).pop() for user in b['members'])

Po połączeniu wszystkich tych zmian zastąp zmienną _get_gae_admins() w języku Python 3 tą równoważną wersją języka Python 2:

def _get_gae_admins():
    'return set of App Engine admins'
    # setup constants for calling Cloud Resource Manager API
    CREDS, PROJ_ID = default(  # Application Default Credentials and project ID
            ['https://www.googleapis.com/auth/cloud-platform'])
    rm_client = discovery.build('cloudresourcemanager', 'v1', credentials=CREDS)
    _TARGETS = frozenset((     # App Engine admin roles
            'roles/viewer',
            'roles/editor',
            'roles/owner',
            'roles/appengine.appAdmin',
    ))

    # collate users who are members of at least one GAE admin role (_TARGETS)
    admins = set()                      # set of all App Engine admins
    allow_policy = rm_client.projects().getIamPolicy(resource=PROJ_ID).execute()
    for b in allow_policy['bindings']:  # bindings in IAM allow-policy
        if b['role'] in _TARGETS:       # only look at GAE admin roles
            admins.update(user.split(':', 1).pop() for user in b['members'])
    return admins

Funkcja is_admin() nie wymaga żadnych aktualizacji, ponieważ opiera się na metodzie _get_gae_admins(), która została już zaktualizowana.

To już koniec zmian wymaganych do przeniesienia aplikacji z modułu 21 w języku Python 3 do Pythona 2. Gratulujemy udostępnienia zaktualizowanej przykładowej aplikacji modułu 21. Cały kod znajdziesz w folderze repozytorium modułu 21a.

7. Podsumowanie/Czyszczenie

Ostatnim krokiem w Codelabs jest zapewnienie podmiotów zabezpieczeń (użytkowników lub kont usługi) uruchamiających tę aplikację, które mają do tego odpowiednie uprawnienia. Następnie wdróż aplikację, aby sprawdzić, czy działa prawidłowo, a zmiany zostały uwzględnione w danych wyjściowych.

Możliwość odczytu zasady zezwolenia na uprawnienia

Omówiliśmy już 4 role wymagane do zostania administratorem App Engine, ale musisz się zapoznać z piątą:

  • roles/viewer
  • roles/editor
  • roles/owner
  • roles/appengine.appAdmin
  • roles/resourcemanager.projectIamAdmin (w przypadku podmiotów zabezpieczeń uzyskujących dostęp do zezwalającej zasady uprawnień)

Rola roles/resourcemanager.projectIamAdmin umożliwia podmiotom zabezpieczeń określenie, czy użytkownik jest członkiem dowolnej z ról administratora App Engine. Jeśli nie masz członkostwa w zasadzie roles/resourcemanager.projectIamAdmin, wywołania interfejsu API Cloud Resource Manager w celu pobrania zasady allow-policy zakończą się niepowodzeniem.

Nie musisz podejmować żadnych działań, ponieważ Twoja aplikacja będzie działać z domyślnym kontem usługi App Engine, które automatycznie otrzymuje członkostwo w tej roli. Nawet jeśli na etapie programowania używasz domyślnego konta usługi, zdecydowanie zalecamy utworzenie i korzystanie z konta usługi zarządzanego przez użytkownika z minimalnymi uprawnieniami wymaganymi do prawidłowego działania aplikacji. Aby przyznać członkostwo takiemu kontu usługi, uruchom to polecenie:

$ gcloud projects add-iam-policy-binding PROJ_ID --member="serviceAccount:USR_MGD_SVC_ACCT@PROJ_ID.iam.gserviceaccount.com" --role=roles/resourcemanager.projectIamAdmin

PROJ_ID to identyfikator projektu Cloud, a USR_MGD_SVC_ACCT@PROJ_ID.iam.gserviceaccount.com to zarządzane przez użytkownika konto usługi, które tworzysz dla swojej aplikacji. To polecenie wyświetla zaktualizowane zasady uprawnień w projekcie, w których możesz sprawdzić, czy konto usługi ma członkostwo w środowisku roles/resourcemanager.projectIamAdmin. Więcej informacji znajdziesz w dokumentacji referencyjnej. Nie musisz uruchamiać tego polecenia w tym ćwiczeniu z programowania, ale możesz je zapisać jako materiał referencyjny do modernizacji swoich aplikacji.

Wdróż i zweryfikuj aplikację

Prześlij aplikację do chmury za pomocą standardowego polecenia gcloud app deploy. Po wdrożeniu usługi powinny być widoczne prawie takie same jak w aplikacji z modułu 20, z wyjątkiem tego, że na potrzeby zarządzania użytkownikami zastąpiono usługę użytkowników App Engine usługą Cloud Identity Platform (i Uwierzytelnianiem Firebase).

3a83ae745121d70.png

Jedną z różnic w porównaniu z Modułem 20 jest to, że kliknięcie przycisku logowania powoduje wyświetlenie wyskakującego okienka, a nie przekierowania. Pokazuje to niektóre z poniższych zrzutów ekranu. Podobnie jak w przypadku Modułu 20, działanie różni się nieco w zależności od tego, ile kont Google zostało zarejestrowanych w przeglądarce.

Jeśli nie ma żadnego użytkownika zarejestrowanego w przeglądarce lub użytkownik, który jeszcze się nie zalogował, pojawi się ogólne wyskakujące okienko Logowanie przez Google:

8437f5f3d489a942.png

Jeśli 1 użytkownik jest zarejestrowany w przeglądarce, ale loguje się w innym miejscu, okno nie jest wyświetlane (lub pojawia się i natychmiast zamyka), a aplikacja przechodzi w stan zalogowania (wyświetla adres e-mail użytkownika i przycisk Wyloguj się).

Niektórzy deweloperzy mogą chcieć udostępnić selektor kont, nawet w przypadku pojedynczego użytkownika:

b75624cb68d94557.png

Aby to zrobić, usuń znacznik komentarza z wiersza provider.setCustomParameters({prompt: 'select_account'}); w szablonie internetowym w sposób opisany powyżej.

W przypadku wielu użytkowników pojawi się okno selektora konta (patrz poniżej). Jeśli użytkownik nie jest jeszcze zalogowany, wyświetli się odpowiedni komunikat. Jeśli jesteś już zalogowany(-a), wyskakujące okienko znika, a aplikacja przechodzi w stan zalogowania.

c454455b6020d5e4.png

Stan zalogowania w module 21 wygląda tak samo jak w interfejsie modułu 20:

49ebe4dcc1eff11f.png

To samo dotyczy sytuacji, gdy użytkownik zaloguje się jako administrator:

44302f35b39856eb.png

W przeciwieństwie do modułu 21 Moduł 20 zawsze uzyskuje dostęp do logiki zawartości szablonu internetowego z aplikacji (kod po stronie serwera). Wadą Modułu 20 jest to, że jedna wizyta jest rejestrowana w chwili pierwszego uruchomienia aplikacji przez użytkownika, a druga przy pierwszym logowaniu się użytkownika.

W module 21 logika logowania odbywa się tylko w szablonie internetowym (kodem po stronie klienta). Aby określić, jakie treści wyświetlić, nie trzeba podróżować po stronie serwera. Jedynym wywołaniem do serwera jest sprawdzenie, czy użytkownik nie zaloguje się na swoje konto. Oznacza to, że na zalogowanie się i wylogowanie nie są rejestrowane dodatkowe wizyty, a lista ostatnich wizyt nie zmienia się w przypadku działań związanych z zarządzaniem użytkownikami. Zwróć uwagę, że powyższe zrzuty ekranu przedstawiają ten sam zestaw 4 wizyt za pomocą różnych loginów użytkownika.

Zrzuty ekranu z modułu 20 pokazują „błęd związany z podwójną wizytą”. na początku tego ćwiczenia. Dla każdego logowania lub wylogowania wyświetlane są osobne dzienniki wizyt. Sprawdź sygnatury czasowe ostatniej wizyty dla każdego zrzutu ekranu uporządkowanego chronologicznie.

Czyszczenie danych

Ogólne

Jeśli na razie wszystko jest gotowe, wyłącz aplikację App Engine, aby uniknąć naliczania opłat. Jeśli jednak chcesz jeszcze bardziej przetestować lub poeksperymentować, platforma App Engine ma bezpłatny limit. Dopóki nie przekroczysz tego limitu, nie pobierzemy żadnych opłat. Oznacza to obliczenia, ale mogą być też naliczane opłaty za odpowiednie usługi App Engine. Więcej informacji znajdziesz na stronie z cennikiem. Jeśli ta migracja obejmuje inne usługi Cloud, są one rozliczane osobno. W obu przypadkach zapoznaj się z sekcją „Zapoznaj się z tymi ćwiczeniami”. sekcji poniżej.

Aby w pełni wyjaśnić wszystkie kwestie, wdrożenie na bezserwerowej platformie obliczeniowej Google Cloud, takiej jak App Engine, wiąże się z niewielkimi kosztami kompilacji i przechowywania danych. Cloud Build ma własny bezpłatny limit, podobnie jak Cloud Storage. Przechowywanie tego obrazu wykorzystuje część tego limitu. Możesz jednak mieszkać w regionie, w którym nie ma takiego poziomu bezpłatnego. Dlatego pamiętaj o wykorzystaniu miejsca na dane, aby zminimalizować potencjalne koszty. Określone „foldery” Cloud Storage należy sprawdzić m.in.:

  • console.cloud.google.com/storage/browser/LOC.artifacts.PROJECT_ID.appspot.com/containers/images
  • console.cloud.google.com/storage/browser/staging.PROJECT_ID.appspot.com
  • Powyższe linki do przechowywania danych zależą od Twoich danych PROJECT_ID i LOC, np. „us” jeśli aplikacja jest hostowana w Stanach Zjednoczonych.

Jeśli natomiast nie zamierzasz dalej korzystać z tej aplikacji lub innych powiązanych z nią ćwiczeń w Codelabs i chcesz całkowicie usunąć wszystko, zamknij projekt.

Powiązane z tym ćwiczeniam z programowania

Wymienione poniżej usługi są dostępne tylko w ramach tego ćwiczenia z programowania. Więcej informacji znajdziesz w dokumentacji poszczególnych usług:

  • Usługa App Engine Datastore jest świadczona przez Cloud Datastore (Cloud Firestore w trybie Datastore), który również ma poziom bezpłatny. więcej informacji znajdziesz na stronie z cennikiem.
  • Korzystanie z Cloud Identity Platform jest w pewnym stopniu „bezpłatne”, w zależności od tego, z których usług korzystasz. Więcej informacji znajdziesz na stronie z cennikiem.
  • Korzystanie z interfejsu Cloud Resource Manager API jest w większości bezpłatne, zgodnie z cennikiem.

Dalsze kroki

Oprócz tego samouczka dostępne są też inne moduły migracji koncentrujące się na odejściu od starszych pakietów usług, które warto rozważyć:

  • Moduł 2. Migracja z App Engine ndb do Cloud NDB
  • Moduły 7–9. Migracja z kolejki zadań App Engine (zadań push) do Cloud Tasks
  • Moduły 12–13: migracja z App Engine Memcache do Cloud Memorystore
  • Moduły 15–16: migracja z App Engine Blobstore do Cloud Storage
  • Moduły 18–19: migracja z kolejki zadań App Engine (pobierania zadań) do Cloud Pub/Sub

App Engine nie jest już jedyną bezserwerową platformą w Google Cloud. Jeśli masz małą aplikację App Engine lub taką, która ma ograniczoną funkcjonalność i chcesz przekształcić ją w samodzielny mikroserwis, albo chcesz podzielić aplikację monolityczną na kilka komponentów wielokrotnego użytku, rozważ przejście na Cloud Functions. Jeśli konteneryzacja stała się częścią przepływu pracy przy tworzeniu aplikacji, zwłaszcza jeśli składa się z potoku CI/CD (ciągła integracja/ciągłe dostarczanie lub wdrażanie), rozważ migrację do Cloud Run. Te scenariusze są opisane w tych modułach:

  • Migracja z App Engine do Cloud Functions: patrz Moduł 11.
  • Migracja z App Engine do Cloud Run: zapoznaj się z Modułem 4, aby skonteneryzować aplikację za pomocą Dockera, lub Moduł 5, aby zrobić to bez kontenerów, Dockera lub Dockerfile.

Przejście na inną bezserwerową platformę jest opcjonalne. Zalecamy, aby przed wprowadzeniem jakichkolwiek zmian wybrać najlepsze opcje dla swoich aplikacji i przypadków użycia.

Niezależnie od tego, który moduł migracji wykorzystasz w następnej kolejności, wszystkie materiały z serwerowej platformy migracji (laboratorium, filmy, kod źródłowy [jeśli jest dostępne]) są dostępne w repozytorium open source. README repozytorium zawiera też wskazówki dotyczące migracji, które warto wziąć pod uwagę, i wszelkich odpowiednich „zamówień” modułów migracji.

8. Dodatkowe materiały

Poniżej znajdziesz dodatkowe materiały dla deweloperów zainteresowanych tym lub powiązanymi modułami migracji. Poniżej możesz przesłać opinię na temat tych treści, znaleźć linki do kodu i różne artykuły, które mogą Ci się przydać.

Problemy/opinie dotyczące ćwiczeń z programowania

Jeśli podczas korzystania z tych ćwiczeń z programowania zauważysz jakiekolwiek problemy, najpierw je wyszukaj. Linki do wyszukiwania i tworzenia nowych problemów:

Zasoby migracji

Linki do folderów repozytorium w modułach 20 (START) i modułach 21 (FINISH) znajdziesz w tabeli poniżej.

Codelab

Python 2

Python 3

Część 20

kod

(nd.)

Moduł 21 (to ćwiczenia z programowania)

kod

kod

Dokumentacja online

Poniżej znajdziesz informacje na temat tego samouczka:

Cloud Identity Platform i Cloud Marketplace

Cloud Resource Manager, Cloud IAM, pakiet Firebase Admin SDK

Użytkownicy App Engine, App Engine NDB, Cloud NDB, Cloud Datastore

Inne odwołania do modułu migracji

Migracja do App Engine

Platforma App Engine

Pakiet SDK Cloud

Inne informacje o Google Cloud

Filmy

Licencja

To zadanie jest licencjonowane na podstawie ogólnej licencji Creative Commons Attribution 2.0.