Vom App Engine-Nutzerdienst zur Cloud Identity Platform migrieren (Modul 21)

1. Übersicht

Die Codelabs der Reihe „Serverless Migration Station“ (selbstgesteuerte, praktische Anleitungen) und die zugehörigen Videos sollen serverlosen Google Cloud-Entwicklern helfen, ihre Anwendungen zu modernisieren. Dazu werden sie durch eine oder mehrere Migrationen geführt, bei denen es in erster Linie darum geht, von Legacy-Diensten wegzukommen. Dadurch werden Ihre Apps portierbarer und Sie erhalten mehr Optionen und Flexibilität. Sie können eine größere Auswahl an Cloud-Produkten einbinden und darauf zugreifen und einfacher auf neuere Sprachversionen upgraden. Die Reihe konzentriert sich zwar anfangs auf die ersten Cloud-Nutzer, vor allem auf App Engine-Entwickler (Standardumgebung), ist aber breit genug, um auch andere serverlose Plattformen wie Cloud Functions und Cloud Run oder andere Plattformen einzubeziehen, sofern zutreffend.

In diesem Codelab wird Python 2-App Engine-Entwicklern gezeigt, wie sie von der App Engine Users API/dem App Engine Users-Dienst zu Cloud Identity Platform (GCIP) migrieren. Es gibt auch eine implizite Migration von App Engine NDB zu Cloud NDB für den Datastore-Zugriff (hauptsächlich in Migrationsmodul 2 behandelt) sowie ein Upgrade auf Python 3.

In Modul 20 wird beschrieben, wie Sie die Verwendung der Users API in die Beispielanwendung aus Modul 1 einfügen. In diesem Modul migrieren Sie die Verwendung der fertigen App aus Modul 20 zu Cloud Identity Platform.

Lerninhalte

  • App Engine Users-Dienst durch Cloud Identity Platform ersetzen
  • Ersetzen Sie die Verwendung von App Engine NDB durch Cloud NDB (siehe auch Modul 2).
  • Verschiedene Identitätsanbieter für die Authentifizierung mit Firebase Auth einrichten
  • IAM-Informationen für Projekte mit der Cloud Resource Manager API abrufen
  • Nutzerinformationen mit dem Firebase Admin SDK abrufen
  • Beispielanwendung zu Python 3 portieren

Voraussetzungen

Umfrage

Wie werden Sie diese Anleitung verwenden?

Nur lesen Lesen und Übungen durchführen

Wie würden Sie Ihre Erfahrung mit Python bewerten?

Anfänger Mittelstufe Fortgeschrittene

Wie würden Sie Ihre Erfahrungen mit Google Cloud-Diensten bewerten?

Anfänger Mittelstufe Fortgeschritten

2. Hintergrund

Der App Engine Users-Dienst ist ein System zur Nutzerauthentifizierung für App Engine-Apps. Es bietet Google Sign-in als Identitätsanbieter, praktische An- und Abmeldelinks für die Verwendung in Apps und unterstützt das Konzept von Administratornutzern und Funktionen, die nur Administratoren zur Verfügung stehen. Um die Portabilität von Anwendungen zu verbessern, empfiehlt Google Cloud, von den gebündelten Legacy-App Engine-Diensten zu eigenständigen Cloud-Diensten zu migrieren, z. B. vom Users-Dienst zu Cloud Identity Platform.

Identity Platform basiert auf Firebase Authentication und bietet eine Reihe von Unternehmensfunktionen, einschließlich Multi-Faktor-Authentifizierung, OIDC- und SAML-SSO-Unterstützung, Mehrinstanzenfähigkeit, 99, 95% SLA und mehr. Diese Unterschiede werden auch auf der Vergleichsseite für Identity Platform und Firebase Authentication hervorgehoben. Beide Produkte bieten deutlich mehr Funktionen als der Nutzerdienst.

In diesem Codelab zu Modul 21 wird gezeigt, wie die Nutzerauthentifizierung der App vom Users-Dienst auf Identity Platform-Funktionen umgestellt wird, die der in Modul 20 gezeigten Funktionalität am ehesten entsprechen. In Modul 21 wird auch die Migration von App Engine NDB zu Cloud NDB für den Datastore-Zugriff wiederholt, die bereits in Modul 2 beschrieben wird.

Der Code aus Modul 20 wird zwar als Python 2-Beispiel-App „beworben“, der Quellcode selbst ist jedoch mit Python 2 und 3 kompatibel. Das bleibt auch nach der Migration zu Identity Platform (und Cloud NDB) in diesem Modul 21 so. Sie können den Users-Dienst weiterhin verwenden, wenn Sie auf Python 3 umstellen, da die Migration zur Identity Platform optional ist. Im Codelab zu Modul 17 und im zugehörigen Video erfahren Sie, wie Sie die gebündelten Dienste weiterhin verwenden können, wenn Sie auf Laufzeiten der 2. Generation wie Python 3 aktualisieren.

Diese Anleitung umfasst die folgenden Schritte:

  1. Einrichtung/Vorbereitung
  2. Konfiguration aktualisieren
  3. Anwendungscode ändern

3. Einrichtung/Vorbereitung

In diesem Abschnitt wird Folgendes erläutert:

  1. Cloud-Projekt einrichten
  2. Beispiel-App für die Baseline abrufen
  3. Ausgangsanwendung (neu) bereitstellen und validieren
  4. Neue Google Cloud-Dienste/APIs aktivieren

Mit diesen Schritten stellen Sie sicher, dass Sie mit funktionierendem Code beginnen, der für die Migration zu eigenständigen Cloud-Diensten bereit ist.

1. Projekt einrichten

Wenn Sie das Codelab zu Modul 20 abgeschlossen haben, verwenden Sie dasselbe Projekt und denselben Code. Alternativ können Sie ein neues Projekt erstellen oder ein anderes vorhandenes Projekt wiederverwenden. Prüfen Sie, ob das Projekt ein aktives Abrechnungskonto und eine aktivierte App Engine-Anwendung hat. Suchen Sie Ihre Projekt-ID und halten Sie sie für dieses Codelab bereit. Verwenden Sie sie immer, wenn Sie auf die Variable PROJ_ID stoßen.

2. Beispiel-App für die Baseline abrufen

Eine der Voraussetzungen ist eine funktionierende App Engine-App aus Modul 20. Sie können entweder das entsprechende Codelab (empfohlen; siehe Link oben) durcharbeiten oder den Code aus Modul 20 aus dem Repository kopieren. Ganz gleich, ob Sie Ihre oder unsere verwenden – hier beginnen wir („START“). In diesem Codelab wird die Migration beschrieben. Am Ende steht Code, der dem im Repo-Ordner für Modul 21 („FINISH“) ähnelt.

Kopieren Sie den Repo-Ordner für Modul 20. Die Ausgabe sollte wie unten aussehen. Möglicherweise ist auch ein Ordner lib vorhanden, wenn Sie das Codelab zu Modul 20 durchgearbeitet haben:

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

3. Ausgangsanwendung (neu) bereitstellen und validieren

Führen Sie die folgenden Schritte aus, um die App für Modul 20 bereitzustellen:

  1. Löschen Sie den Ordner lib, falls er vorhanden ist, und führen Sie pip install -t lib -r requirements.txt aus, um ihn neu zu füllen. Möglicherweise müssen Sie pip2 verwenden, wenn sowohl Python 2 als auch Python 3 installiert sind.
  2. Achten Sie darauf, dass Sie das gcloud-Befehlszeilentool installiert und initialisiert haben und sich mit der Verwendung vertraut gemacht haben.
  3. Wenn Sie Ihre PROJ_ID nicht bei jedem gcloud-Befehl eingeben möchten, legen Sie das Cloud-Projekt zuerst mit gcloud config set project PROJ_ID fest.
  4. Beispiel-App mit gcloud app deploy bereitstellen
  5. Prüfen Sie, ob die App wie erwartet und ohne Fehler ausgeführt wird. Wenn Sie das Codelab für Modul 20 abgeschlossen haben, werden oben in der App die Anmeldedaten des Nutzers (E-Mail-Adresse des Nutzers, eventuelles „Admin-Badge“ und Anmelde-/Abmeldeschaltfläche) sowie die letzten Besuche angezeigt (siehe Abbildung unten).

907e64c19ef964f8.png

Wenn sich ein Nutzer als regulärer Nutzer anmeldet, wird seine E‑Mail-Adresse angezeigt und die Schaltfläche „Anmelden“ ändert sich in „Abmelden“:

ad7b59916b69a035.png

Wenn sich ein Nutzer als Administrator anmeldet, wird seine E-Mail-Adresse zusammen mit „(Administrator)“ angezeigt:

867bcb3334149e4.png

4. Neue Google Cloud APIs/Dienste aktivieren

Einführung

In der App für Modul 20 werden die App Engine NDB- und Users-APIs verwendet. Diese gebündelten Dienste erfordern keine zusätzliche Einrichtung. Bei eigenständigen Clouddiensten ist dies jedoch erforderlich. In der aktualisierten App werden sowohl Cloud Identity Platform als auch Cloud Datastore (über die Cloud NDB-Clientbibliothek) verwendet. Außerdem benötigen wir die Cloud Resource Manager API, um App Engine-Administratornutzer zu ermitteln.

Kosten

  • App Engine und Cloud Datastore haben Kontingente für die kostenlose Stufe. Solange Sie diese Limits nicht überschreiten, sollten für diese Anleitung keine Gebühren anfallen. Weitere Informationen finden Sie auch auf der App Engine-Preisseite und der Cloud Datastore-Preisseite.
  • Die Nutzung der Cloud Identity Platform wird je nach Anzahl der monatlich aktiven Nutzer (MAN) oder Authentifizierungsprüfungen abgerechnet. Für jedes Nutzungsmodell ist eine kostenlose Version verfügbar. Weitere Informationen finden Sie auf der Preisseite. Außerdem ist für App Engine und Cloud Datastore zwar die Abrechnung erforderlich, für die Verwendung von GCIP an sich jedoch nicht, solange Sie die täglichen Kontingente ohne Zahlungsmittel nicht überschreiten. Berücksichtigen Sie dies für Cloud-Projekte, für die keine Abrechnung für Cloud-APIs/-Dienste erforderlich ist.
  • Die Nutzung der Cloud Resource Manager API ist größtenteils kostenlos. Weitere Informationen finden Sie auf der Preisseite.

Nutzer aktivieren Cloud APIs je nach Bedarf über die Cloud Console oder über die Befehlszeile (mit dem gcloud-Befehl, der Teil des Cloud SDK ist). Beginnen wir mit den Cloud Datastore- und Cloud Resource Manager-APIs.

Über die Cloud Console

Rufen Sie in der Cloud Console die Seite „Bibliothek“ des API-Managers für das richtige Projekt auf und suchen Sie in der Suchleiste nach einer API. c7a740304e9d35b.png

Aktivieren Sie diese APIs:

Suchen Sie nach jeder API und klicken Sie auf die Schaltfläche Aktivieren. Möglicherweise werden Sie aufgefordert, Abrechnungsinformationen anzugeben. Hier ist beispielsweise die Seite für die Resource Manager API:

fc7bd8f4c49d12e5.png

Wenn die Funktion aktiviert wurde, ändert sich der Button in „Verwalten“ (in der Regel nach einigen Sekunden):

8eca12d6cc7b45b0.png

Aktivieren Sie Cloud Datastore auf dieselbe Weise:

83811599b110e46b.png

Über die Befehlszeile

Das Aktivieren von APIs über die Konsole ist zwar visuell informativ, aber einige Nutzer bevorzugen die Befehlszeile. Sie können beliebig viele APIs gleichzeitig aktivieren. Führen Sie diesen Befehl aus, um sowohl die Cloud Datastore API als auch die Cloud Resource Manager API zu aktivieren, und warten Sie, bis der Vorgang abgeschlossen ist, wie hier dargestellt:

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

Möglicherweise werden Sie aufgefordert, Zahlungsinformationen anzugeben.

Die „URLs“ für jede API, die im obigen Befehl verwendet wird, werden als Dienstnamen der API bezeichnet. Sie finden sie unten auf der Bibliotheksseite für jede API. Wenn Sie andere Cloud APIs für Ihre eigenen Apps aktivieren möchten, finden Sie die entsprechenden Dienstnamen auf den jeweiligen API-Seiten. Mit diesem Befehl werden alle Dienstnamen für APIs aufgelistet, die Sie aktivieren können:

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

Nachdem Sie die oben genannten Schritte ausgeführt haben, kann unser Beispiel sowohl in der Cloud Console als auch in der Befehlszeile auf diese APIs zugreifen. Als Nächstes müssen Sie die Cloud Identity Platform aktivieren und die erforderlichen Codeänderungen vornehmen.

Cloud Identity Platform aktivieren und einrichten (nur Cloud Console)

Cloud Identity Platform ist ein Marketplace-Dienst, da er mit einer Ressource außerhalb von Google Cloud verbunden ist oder von ihr abhängt, z. B. Firebase Authentication. Derzeit können Sie Marketplace-Dienste nur über die Cloud Console aktivieren. Gehen Sie dazu so vor:

  1. Rufen Sie die Cloud Identity Platform-Seite im Cloud Marketplace auf und klicken Sie auf die Schaltfläche Aktivieren. Führen Sie ein Upgrade von Firebase Authentication durch, wenn Sie dazu aufgefordert werden. Dadurch werden zusätzliche Funktionen freigeschaltet, z. B. die im Abschnitt Hintergrund beschriebenen. Hier sehen Sie die Marketplace-Seite mit der hervorgehobenen Schaltfläche Aktivieren: 28475f1c9b29de69.png
  2. Sobald Identity Platform aktiviert ist, werden Sie möglicherweise automatisch zur Seite Identitätsanbieter weitergeleitet. Falls nicht, können Sie diesen Link verwenden. fc2d92d42a5d1dd7.png
  3. Aktivieren Sie den Google Auth-Anbieter. Wenn noch keine Anbieter eingerichtet wurden, klicken Sie auf Anbieter hinzufügen und wählen Sie Google aus. Wenn Sie zu diesem Bildschirm zurückkehren, sollte der Eintrag Google aktiviert sein. Google ist der einzige Authentifizierungsanbieter, den wir in diesem Tutorial verwenden, um den App Engine Users-Dienst als einfachen Google-Anmeldedienst zu spiegeln. In Ihren eigenen Apps können Sie zusätzliche Authentifizierungsanbieter aktivieren.
  4. Wenn Sie Google und andere gewünschte Authentifizierungsanbieter ausgewählt und eingerichtet haben, klicken Sie auf Application Setup Details (Details zur Anwendungseinrichtung). Kopieren Sie im daraufhin angezeigten Dialogfeld die apiKey und authDomain im config-Objekt auf dem Tab „Web“ und speichern Sie beide an einem sicheren Ort. Warum wird nicht alles kopiert? Das Snippet in diesem Dialogfeld ist fest codiert und veraltet. Speichern Sie daher nur die wichtigsten Teile und verwenden Sie sie in unserem Code mit einer häufigeren gleichzeitigen Firebase Auth-Nutzung. Nachdem Sie die Werte kopiert und an einem sicheren Ort gespeichert haben, klicken Sie auf die Schaltfläche Schließen, um die Einrichtung abzuschließen. bbb09dcdd9be538e.png

4. Konfiguration aktualisieren

Konfigurationsänderungen umfassen sowohl das Ändern verschiedener Konfigurationsdateien als auch das Erstellen des Äquivalents von App Engine, jedoch innerhalb des Cloud Identity Platform-Ökosystems.

appengine_config.py

  • Wenn Sie ein Upgrade auf Python 3 durchführen, löschen Sie appengine_config.py.
  • Wenn Sie planen, auf Identity Platform umzustellen, aber bei Python 2 bleiben, löschen Sie die Datei nicht. Stattdessen wird sie später während des Python 2-Backports aktualisiert.

requirements.txt

In der requirements.txt-Datei von Modul 20 war nur Flask aufgeführt. Fügen Sie für Modul 21 die folgenden Pakete hinzu:

Der Inhalt von requirements.txt sollte jetzt so aussehen:

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

app.yaml

  • Wenn Sie auf Python 3 umstellen, wird die Datei app.yaml vereinfacht. Entfernen Sie alles mit Ausnahme der Laufzeitanweisung und legen Sie diese auf eine aktuell unterstützte Version von Python 3 fest. Im Beispiel wird derzeit Version 3.10 verwendet.
  • Wenn Sie bei Python 2 bleiben, müssen Sie hier noch nichts unternehmen.

VORHER:

runtime: python27
threadsafe: yes
api_version: 1

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

Die Beispiel-App für Modul 20 hat keine Handler für statische Dateien. Wenn Ihre Apps dies tun, lassen Sie sie unverändert. Sie können alle Ihre Skript-Handler entfernen oder sie nur als Referenz beibehalten, solange Sie ihre Handles in auto ändern, wie im app.yaml-Migrationsleitfaden beschrieben. Durch diese Änderungen wird die aktualisierte app.yaml für Python 3 vereinfacht:

DANACH:

runtime: python310

Weitere Konfigurationsupdates

Unabhängig davon, ob Sie Python 2 beibehalten oder zu Python 3 portieren, sollten Sie den Ordner lib löschen, falls er vorhanden ist.

5. Anwendungscode ändern

In diesem Abschnitt werden Updates an der Hauptanwendungsdatei main.py beschrieben, bei denen der App Engine Users-Dienst durch Cloud Identity Platform ersetzt wird. Nachdem Sie die Hauptanwendung aktualisiert haben, aktualisieren Sie die Webvorlage templates/index.html.

Importe und Initialisierung aktualisieren

Gehen Sie so vor, um die Importe zu aktualisieren und Anwendungsressourcen zu initialisieren:

  1. Ersetzen Sie App Engine NDB durch Cloud NDB.
  2. Importieren Sie neben Cloud NDB auch Cloud Resource Manager.
  3. Identity Platform basiert auf Firebase Auth. Importieren Sie daher das Firebase Admin SDK.
  4. Für Cloud APIs ist die Verwendung eines API-Clients erforderlich. Initialisieren Sie ihn daher für Cloud NDB direkt unter der Initialisierung von Flask.

Das Cloud Resource Manager-Paket wird hier zwar importiert, wir verwenden es aber erst später bei der Initialisierung der App. Unten sehen Sie die Importe und die Initialisierung aus Modul 20 sowie die Abschnitte nach der Implementierung der oben genannten Änderungen:

VORHER:

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

app = Flask(__name__)

DANACH:

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()

Support für App Engine-Administratoren

Es gibt zwei Komponenten, die der App hinzugefügt werden müssen, um die Erkennung von Administratornutzern zu unterstützen:

  • _get_gae_admins() – stellt eine Gruppe von Administratornutzern zusammen; wird einmal aufgerufen und gespeichert
  • is_admin(): Prüft, ob der angemeldete Nutzer ein Administratornutzer ist. Wird bei jeder Nutzeranmeldung aufgerufen.

Die Hilfsfunktion _get_gae_admins() ruft die Resource Manager API auf, um die aktuelle Cloud IAM-Zulassungsrichtlinie abzurufen. Die Zulassungsrichtlinie definiert und erzwingt, welche Rollen welchen Hauptkonten (menschliche Nutzer, Dienstkonten usw.) gewährt werden. Die Einrichtung umfasst:

  • Cloud-Projekt-ID (PROJ_ID) abrufen
  • Resource Manager API-Client erstellen (rm_client)
  • Erstellen einer (schreibgeschützten) Gruppe von App Engine-Administratorrollen (_TARGETS)

Der Resource Manager benötigt die Cloud-Projekt-ID. Importieren Sie also google.auth.default() und rufen Sie diese Funktion auf, um die Projekt-ID abzurufen. Dieser Aufruf enthält einen Parameter, der wie eine URL aussieht, aber ein OAuth2-Bereich ist. Wenn Sie Apps in der Cloud ausführen, z. B. auf einer Compute Engine-VM oder in einer App Engine-Anwendung, wird ein Standarddienstkonto mit umfassenden Berechtigungen bereitgestellt. Entsprechend der Best Practice der geringsten Berechtigung empfehlen wir, eigene nutzerverwaltete Dienstkonten zu erstellen.

Bei API-Aufrufen ist es am besten, den Umfang Ihrer Apps weiter auf ein Minimum zu reduzieren, das für die ordnungsgemäße Funktion erforderlich ist. Der Resource Manager API-Aufruf, den wir ausführen, ist get_iam_policy(). Für diesen Vorgang ist einer der folgenden Bereiche erforderlich:

  • 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

Die Beispiel-App benötigt nur Lesezugriff auf die Zulassungsrichtlinie. Die Richtlinie wird dadurch nicht geändert und es ist kein Zugriff auf das gesamte Projekt erforderlich. Das bedeutet, dass die App keine der ersten drei erforderlichen Berechtigungen benötigt. Das ist alles, was erforderlich ist, und das implementieren wir für die Beispiel-App.

Im Hauptteil der Funktion wird eine leere Gruppe von Administratornutzern (admins) erstellt, die allow_policy über get_iam_policy() abgerufen und alle Bindungen durchlaufen, wobei speziell nach App Engine-Administratorrollen gesucht wird:

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

Für jede gefundene Zielrolle wird erfasst, welche Nutzer zu dieser Rolle gehören, und sie werden der Gesamtgruppe der Administratornutzer hinzugefügt. Am Ende werden alle gefundenen und als Konstante (_ADMINS) für die Lebensdauer dieser App Engine-Instanz im Cache gespeicherten Administratornutzer zurückgegeben. Wir werden diesen Anruf in Kürze sehen.

Fügen Sie die folgende _get_gae_admins()-Funktionsdefinition in main.py direkt unter der Instanziierung des Cloud NDB API-Clients (ds_client) ein:

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

Wenn sich Nutzer in der App anmelden, passiert Folgendes:

  1. Nachdem sich ein Nutzer in Firebase angemeldet hat, wird eine kurze Prüfung über die Webvorlage durchgeführt.
  2. Wenn sich der Authentifizierungsstatus in der Vorlage ändert, wird ein fetch()-Aufruf im Ajax-Stil an /is_admin gesendet. Der Handler dafür ist die nächste Funktion, is_admin().
  3. Das Firebase-ID-Token wird im POST-Body an is_admin() übergeben. Dort wird es aus den Headern abgerufen und das Firebase Admin SDK wird aufgerufen, um es zu validieren. Wenn es sich um einen gültigen Nutzer handelt, extrahieren Sie seine E-Mail-Adresse und prüfen Sie, ob es sich um einen Administratornutzer handelt.
  4. Das boolesche Ergebnis wird dann als erfolgreicher 200-Status an die Vorlage zurückgegeben.

Fügen Sie is_admin() zu main.py direkt nach _get_gae_admins() hinzu:

@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

Der gesamte Code beider Funktionen ist erforderlich, um die Funktionalität des Users-Dienstes, insbesondere die Funktion is_current_user_admin(), zu replizieren. Dieser Funktionsaufruf in Modul 20 hat die Hauptarbeit erledigt, im Gegensatz zu Modul 21, in dem wir eine Ersatzlösung implementieren. Die gute Nachricht ist, dass die App nicht mehr von einem reinen App Engine-Dienst abhängig ist. Sie können Ihre Apps also zu Cloud Run oder anderen Diensten migrieren. Außerdem können Sie die Definition von „Administratornutzer“ für Ihre eigenen Apps ändern, indem Sie einfach die gewünschten Rollen in _TARGETS auswählen. Der Users-Dienst ist jedoch für die App Engine-Administratorrollen fest codiert.

Firebase Auth initialisieren und App Engine-Administratoren im Cache speichern

Wir hätten Firebase Auth oben in der Nähe der Stelle initialisieren können, an der die Flask-App initialisiert und der Cloud NDB API-Client erstellt wird. Das war jedoch erst erforderlich, nachdem der gesamte Admin-Code definiert wurde. Da _get_gae_admins() jetzt definiert ist, rufen Sie es auf, um die Liste der Administratornutzer im Cache zu speichern.

Fügen Sie diese Zeilen direkt unter dem Funktionskörper von is_admin() ein:

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

Datenmodellaktualisierungen

Das Visit-Datenmodell ändert sich nicht. Für den Datastore-Zugriff ist die explizite Verwendung des Cloud NDB API-Clientkontextmanagers ds_client.context() erforderlich. Im Code bedeutet das, dass Sie Datastore-Aufrufe sowohl in store_visit() als auch in fetch_visits() innerhalb von Python-with-Blöcken einschließen. Diese Aktualisierung ist identisch mit Modul 2. Nehmen Sie die Änderungen so vor:

VORHER:

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)

DANACH:

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)

Nutzeranmeldelogik in Webvorlage verschieben

Der App Engine Users-Dienst ist serverseitig, während Firebase Auth und Cloud Identity Platform hauptsächlich clientseitig sind. Daher wird ein Großteil des Codes für die Nutzerverwaltung in der App aus Modul 20 in die Webvorlage aus Modul 21 verschoben.

Im main.py-Webkontext werden fünf wichtige Daten an die Vorlage übergeben. Die ersten vier sind mit der Nutzerverwaltung verknüpft und unterscheiden sich je nachdem, ob der Nutzer angemeldet ist oder nicht:

  • who – E-Mail-Adresse des Nutzers, wenn er angemeldet ist, andernfalls user
  • admin – (Administrator)-Symbol, wenn der angemeldete Nutzer ein Administrator ist
  • sign – Schaltfläche Anmelden oder Abmelden anzeigen
  • link – Anmelde- oder Abmeldelinks bei Klick auf die Schaltfläche
  • visits – letzte Besuche

VORHER:

@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)

Die gesamte Nutzerverwaltung wird in die Webvorlage verschoben. Es bleiben nur noch die Besuche übrig. Der Haupthandler ist also wieder so, wie er in der App aus Modul 1 war:

DANACH:

@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)

Webvorlage aktualisieren

Wie sehen alle Aktualisierungen aus dem vorherigen Abschnitt in der Vorlage aus? Die Nutzerverwaltung wurde hauptsächlich von der App zu Firebase Auth verlagert, die im Template ausgeführt wird. Außerdem wurde ein Teil des Codes in JavaScript portiert. main.py ist deutlich gesunken. Wir gehen daher von einem ähnlichen Wachstum bei templates/index.html aus.

VORHER:

<!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>

Ersetzen Sie die gesamte Webvorlage durch den folgenden Inhalt:

DANACH:

<!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>

Dieser HTML-Body enthält viele Komponenten. Sehen wir uns diese einzeln an.

Firebase-Importe

Importieren Sie die erforderlichen Firebase-Komponenten im Header des HTML-Dokuments nach dem Seitentitel. Firebase-Komponenten sind jetzt zur Effizienzsteigerung in mehrere Module unterteilt. Der Code zum Initialisieren von Firebase wird aus dem Hauptmodul der Firebase-App importiert. Funktionen, die die Firebase-Authentifizierung, Google als Authentifizierungsanbieter, das An- und Abmelden sowie den „Callback“ für Änderungen des Authentifizierungsstatus verwalten, werden alle aus dem Firebase Auth-Modul importiert:

<!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-Konfiguration

Im Rahmen der Einrichtung von Identity Platform in dieser Anleitung haben Sie bereits die apiKey und authDomain aus dem Dialogfeld Einrichtungsdetails für die Anwendung gespeichert. Fügen Sie diese Werte der Variablen firebaseConfig im nächsten Abschnitt hinzu. In den Kommentaren finden Sie einen Link zu einer ausführlicheren Anleitung:

// 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",
};

Firebase-Initialisierung

Im nächsten Abschnitt wird Firebase mit diesen Konfigurationsinformationen initialisiert.

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

Dadurch wird die Möglichkeit festgelegt, Google als Authentifizierungsanbieter zu verwenden. Außerdem wird eine auskommentierte Option bereitgestellt, mit der die Kontoauswahl auch dann angezeigt wird, wenn in Ihrer Browsersitzung nur ein Google-Konto registriert ist. Wenn Sie mehrere Konten haben, wird die Kontoauswahl wie erwartet angezeigt: a38369389b7c4c7e.png Wenn in der Sitzung jedoch nur ein Nutzer vorhanden ist, wird der Anmeldevorgang automatisch ohne Nutzerinteraktion abgeschlossen. Das Pop‑up wird angezeigt und dann wieder ausgeblendet. Sie können erzwingen, dass das Kontoauswahlfeld für einen Nutzer angezeigt wird (anstatt sich sofort in der App anzumelden), indem Sie die Zeile mit dem benutzerdefinierten Parameter auskommentieren. Wenn diese Option aktiviert ist, wird auch bei Einzelnutzeranmeldungen die Kontoauswahl angezeigt: b75624cb68d94557.png

An- und Abmeldefunktionen

Die nächsten Codezeilen enthalten die Funktionen für Klicks auf die Anmelde- oder Abmeldeschaltfläche:

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

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

An- und Abmeldeaktionen

Der letzte wichtige Abschnitt in diesem <script>-Block ist die Funktion, die bei jeder Authentifizierungsänderung (Anmeldung oder Abmeldung) aufgerufen wird.

// 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>

Der Code in Modul 20, der bestimmt, ob ein Vorlagenkontext für „Nutzer angemeldet“ oder „Nutzer abgemeldet“ gesendet wird, wird hier übertragen. Die Bedingung oben führt zu true, wenn sich der Nutzer erfolgreich angemeldet hat. Dadurch werden die folgenden Aktionen ausgelöst:

  1. Die E‑Mail-Adresse des Nutzers wird angezeigt.
  2. Die Schaltfläche Anmelden ändert sich in Abmelden.
  3. Ein Ajax-Aufruf an /is_admin wird ausgeführt, um zu ermitteln, ob das Administratorsymbol (admin) angezeigt werden soll.

Wenn sich der Nutzer abmeldet, wird die else-Klausel ausgeführt, um alle Nutzerinformationen zurückzusetzen:

  1. Nutzername auf user festgelegt
  2. Administrator-Badge entfernt
  3. Die Schaltfläche Abmelden wurde wieder in Anmelden geändert.

Vorlagenvariablen

Nach dem Header-Abschnitt beginnt der Hauptteil mit den Vorlagenvariablen, die durch HTML-Elemente ersetzt werden, die sich nach Bedarf ändern:

  1. Angezeigter Nutzername
  2. (admin)-Administratorbadge (falls zutreffend)
  3. Schaltfläche Anmelden oder Abmelden
<body>
<p>
Welcome, <span id="who"></span> <span id="admin"><code>(admin)</code></span>
<button id="logbtn"></button>
</p><hr>

Variablen für die letzten Besuche und HTML-Elemente

Der Code für die letzten Besuche ändert sich nicht und im letzten <script>-Block werden die Variablen für die HTML-Elemente festgelegt, die sich für die Anmeldung und Abmeldung ändern (siehe oben):

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

Damit sind die Änderungen abgeschlossen, die in der Anwendung und der Webvorlage erforderlich sind, um von App Engine NDB und Users APIs zu Cloud NDB und Identity Platform zu wechseln und auf Python 3 zu aktualisieren. Herzlichen Glückwunsch! Sie haben die Beispiel-App für Modul 21 erreicht. Unsere Version ist im Repo-Ordner für Modul 21b verfügbar.

Der nächste Teil des Codelabs ist optional (*) und nur für Nutzer, deren Apps weiterhin auf Python 2 basieren müssen. Er führt Sie durch die erforderlichen Schritte, um eine funktionierende Python 2-App für Modul 21 zu erstellen.

6. *Python 2-Backport

Dieser optionale Abschnitt richtet sich an Entwickler, die eine Identity Platform-Migration durchführen, aber weiterhin die Python 2-Laufzeit verwenden müssen. Wenn dies für Sie kein Problem darstellt, können Sie diesen Abschnitt überspringen.

Um eine funktionierende Python 2-Version der App aus Modul 21 zu erstellen, benötigen Sie Folgendes:

  1. Laufzeitanforderungen: Konfigurationsdateien, die Python 2 unterstützen, und erforderliche Änderungen in der Hauptanwendung, um Inkompatibilitäten mit Python 3 zu vermeiden
  2. Geringfügige Bibliotheksänderung:Python 2 wurde eingestellt, bevor einige erforderliche Funktionen zur Resource Manager-Clientbibliothek hinzugefügt wurden. Daher benötigen Sie eine alternative Möglichkeit, auf diese fehlende Funktion zuzugreifen.

Gehen wir diese Schritte jetzt durch. Beginnen wir mit der Konfiguration.

appengine_config.py wiederherstellen

Weiter oben in dieser Anleitung wurde beschrieben, wie Sie appengine_config.py löschen, da sie von der Python 3-App Engine-Laufzeit nicht verwendet wird. Für Python 2 muss sie nicht nur beibehalten werden, sondern das Modul 20 appengine_config.py muss aktualisiert werden, um die Verwendung von integrierten Drittanbieterbibliotheken, nämlich grpcio und setuptools, zu unterstützen. Diese Pakete sind immer erforderlich, wenn Ihre App Engine-Anwendung Cloud-Clientbibliotheken wie die für Cloud NDB und Cloud Resource Manager verwendet.

Sie fügen diese Pakete gleich app.yaml hinzu. Damit Ihre App darauf zugreifen kann, muss jedoch die pkg_resources.working_set.add_entry()-Funktion aus setuptools aufgerufen werden. Dadurch können kopierte (selbst gebündelte oder von Drittanbietern stammende) Drittanbieterbibliotheken, die im Ordner lib installiert sind, mit integrierten Bibliotheken kommunizieren.

Nehmen Sie die folgenden Änderungen an Ihrer appengine_config.py-Datei vor, damit diese Änderungen wirksam werden:

VORHER:

from google.appengine.ext import vendor

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

Dieser Code allein reicht nicht aus, um die Verwendung von setuptools und grpcio zu unterstützen. Es sind noch einige Zeilen erforderlich. Aktualisieren Sie appengine_config.py so, dass es so aussieht:

DANACH:

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)

Weitere Informationen zu den Änderungen, die für die Unterstützung von Cloud-Clientbibliotheken erforderlich sind, finden Sie in der Dokumentation zur Migration gebündelter Dienste.

app.yaml

Ähnlich wie bei appengine_config.py muss die Datei app.yaml auf eine Version zurückgesetzt werden, die Python 2 unterstützt. Beginnen wir mit dem ursprünglichen Modul 20 app.yaml:

VORHER:

runtime: python27
threadsafe: yes
api_version: 1

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

Zusätzlich zu setuptools und grpcio, wie oben erwähnt, gibt es eine Abhängigkeit (die nicht explizit mit der Identity Platform-Migration zusammenhängt), die die Verwendung der Cloud Storage-Clientbibliothek erfordert. Dafür ist ein weiteres integriertes Drittanbieterpaket, ssl, erforderlich. Fügen Sie alle drei in einem neuen libraries-Abschnitt hinzu und wählen Sie die jeweils „neuesten“ verfügbaren Versionen dieser Pakete für app.yaml aus:

DANACH:

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

Für Modul 21 haben wir Google Auth, Cloud NDB, Cloud Resource Manager und Firebase Admin SDK zum Python 3-requirements.txt hinzugefügt. Die Situation für Python 2 ist komplexer:

  • Die Resource Manager API bietet die für die Beispiel-App erforderliche Funktion für die Zulassungsrichtlinie. Leider war diese Unterstützung in der finalen Python 2-Version der Cloud Resource Manager-Clientbibliothek noch nicht verfügbar. Sie ist nur in der Python 3-Version verfügbar.
  • Daher ist eine alternative Möglichkeit erforderlich, um über die API auf diese Funktion zuzugreifen. Die Lösung besteht darin, die Google APIs-Clientbibliothek auf niedrigerer Ebene für die Kommunikation mit der API zu verwenden. Wenn Sie zu dieser Clientbibliothek wechseln möchten, ersetzen Sie google-cloud-resource-manager durch das Paket google-api-python-client auf niedrigerer Ebene.
  • Da Python 2 eingestellt wurde, müssen im Abhängigkeitsdiagramm für Modul 21 bestimmte Pakete auf bestimmte Versionen festgelegt werden. Einige Pakete müssen angegeben werden, auch wenn sie nicht in der Python 3-app.yaml angegeben sind.

VORHER:

flask

Beginnen Sie mit Modul 20 requirements.txt und aktualisieren Sie es für eine funktionierende App für Modul 21 so:

DANACH:

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

Die Paket- und Versionsnummern werden im Repository aktualisiert, wenn sich Abhängigkeiten ändern. Die app.yaml reicht jedoch zum Zeitpunkt der Erstellung dieses Dokuments für eine funktionierende App aus.

Weitere Konfigurationsupdates

Wenn Sie den Ordner lib aus dem vorherigen Codelab noch nicht gelöscht haben, tun Sie das jetzt. Mit dem neu aktualisierten requirements.txt können Sie diesen bekannten Befehl ausführen, um die Anforderungen in lib zu installieren:

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

Wenn Sie sowohl Python 2 als auch Python 3 auf Ihrem Entwicklungssystem installiert haben, müssen Sie möglicherweise pip2 anstelle von pip verwenden.

Anwendungscode ändern

Glücklicherweise befinden sich die meisten erforderlichen Änderungen in den Konfigurationsdateien. Die einzige Änderung, die im Anwendungscode erforderlich ist, ist eine geringfügige Aktualisierung, um anstelle der Resource Manager-Clientbibliothek die Google API-Clientbibliothek auf niedrigerer Ebene für den Zugriff auf die API zu verwenden. Für die templates/index.html-Webvorlage sind keine Aktualisierungen erforderlich.

Importe und Initialisierung aktualisieren

Ersetzen Sie die Resource Manager-Clientbibliothek (google.cloud.resourcemanager) durch die Google APIs-Clientbibliothek (googleapiclient.discovery), wie unten dargestellt:

VORHER:

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

DANACH:

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

Support für App Engine-Administratoren

Es sind einige Änderungen in _get_gae_admins() erforderlich, um die Verwendung der Clientbibliothek auf niedrigerer Ebene zu unterstützen. Sehen wir uns zuerst an, was sich ändert, und stellen wir Ihnen dann den gesamten Code für das Update zur Verfügung.

Für den Python 2-Code müssen sowohl die Anmeldedaten als auch die Projekt-ID verwendet werden, die von google.auth.default() zurückgegeben werden. Die Anmeldedaten werden in Python 3 nicht verwendet. Daher wurde ihnen eine generische Platzhaltervariable mit Unterstrich ( _ ) zugewiesen. Da es für die Python 2-Version erforderlich ist, ändern Sie den Unterstrich in CREDS. Außerdem erstellen Sie keinen Resource Manager API-Client, sondern einen API-Dienstendpunkt, der konzeptionell einem API-Client ähnelt. Daher behalten wir denselben Variablennamen (rm_client) bei. Ein Unterschied besteht darin, dass für die Instanziierung eines Dienstendpunkts Anmeldedaten (CREDS) erforderlich sind.

Diese Änderungen sind im folgenden Code zu sehen:

VORHER:

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

DANACH:

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)

Ein weiterer Unterschied besteht darin, dass die Resource Manager-Clientbibliothek „allow-policy“-Objekte zurückgibt, die die Notation mit Attributpunkten verwenden, während die Clientbibliothek auf niedrigerer Ebene Python-Dictionaries zurückgibt, in denen eckige Klammern ( [ ]) verwendet werden. Verwenden Sie beispielsweise binding.role für die Resource Manager-Clientbibliothek und binding['role'] für die Bibliothek auf niedrigerer Ebene. Bei der ersteren werden auch „underscore_separated“-Namen verwendet, während bei der Low-Level-Bibliothek „CamelCased“-Namen bevorzugt werden. Außerdem werden API-Parameter etwas anders übergeben.

Diese Nutzungsunterschiede sind unten dargestellt:

VORHER:

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)

DANACH:

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

Ersetzen Sie das Python 3-_get_gae_admins() durch diese entsprechende Python 2-Version:

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

Die Funktion is_admin() muss nicht aktualisiert werden, da sie auf _get_gae_admins() basiert, das bereits aktualisiert wurde.

Damit sind die Änderungen abgeschlossen, die zum Backportieren der Python 3-App aus Modul 21 zu Python 2 erforderlich sind. Willkommen bei der aktualisierten Beispiel-App für Modul 21. Den gesamten Code finden Sie im Repo-Ordner für Modul 21a.

7. Zusammenfassung/Bereinigung

In den letzten Schritten des Codelabs müssen Sie dafür sorgen, dass Principals (Nutzer oder Dienstkonten), die diese App ausführen, die entsprechenden Berechtigungen haben. Stellen Sie dann Ihre App bereit, um zu bestätigen, dass sie wie vorgesehen funktioniert und die Änderungen in der Ausgabe berücksichtigt werden.

Kann IAM-Zulassungsrichtlinien lesen

Wir haben Ihnen bereits die vier Rollen vorgestellt, die erforderlich sind, um als App Engine-Administratornutzer anerkannt zu werden. Es gibt jedoch eine fünfte Rolle, die Sie kennen sollten:

  • roles/viewer
  • roles/editor
  • roles/owner
  • roles/appengine.appAdmin
  • roles/resourcemanager.projectIamAdmin (für Hauptkonten, die auf die IAM-Zulassungsrichtlinie zugreifen)

Mit der Rolle roles/resourcemanager.projectIamAdmin können Hauptkonten ermitteln, ob ein Endnutzer Mitglied einer der App Engine-Administratorrollen ist. Ohne Mitgliedschaft in roles/resourcemanager.projectIamAdmin schlagen Aufrufe der Cloud Resource Manager API zum Abrufen der Zulassungsrichtlinie fehl.

Sie müssen hier keine expliziten Maßnahmen ergreifen, da Ihre App unter dem App Engine-Standarddienstkonto ausgeführt wird, dem automatisch die Mitgliedschaft in dieser Rolle gewährt wird. Auch wenn Sie in der Entwicklungsphase das Standarddienstkonto verwenden, empfehlen wir dringend, ein nutzerverwaltetes Dienstkonto mit den minimalen Berechtigungen zu erstellen und zu verwenden, die für die ordnungsgemäße Funktion Ihrer App erforderlich sind. Führen Sie den folgenden Befehl aus, um einem solchen Dienstkonto eine Mitgliedschaft zu gewähren:

$ 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 ist die Cloud-Projekt-ID und USR_MGD_SVC_ACCT@PROJ_ID.iam.gserviceaccount.com das nutzerverwaltete Dienstkonto, das Sie für Ihre App erstellen. Mit diesem Befehl wird die aktualisierte IAM-Richtlinie für Ihr Projekt ausgegeben. Dort können Sie bestätigen, dass das Dienstkonto Mitglied von roles/resourcemanager.projectIamAdmin ist. Weitere Informationen finden Sie in der Referenzdokumentation. Wie gesagt, müssen Sie diesen Befehl in diesem Codelab nicht ausführen. Speichern Sie ihn aber als Referenz für die Modernisierung Ihrer eigenen Apps.

Anwendung bereitstellen und überprüfen

Laden Sie Ihre App mit dem Standardbefehl gcloud app deploy in die Cloud hoch. Nach der Bereitstellung sollten Sie fast identische Funktionen wie in der App aus Modul 20 sehen. Der einzige Unterschied besteht darin, dass Sie den App Engine-Nutzerdienst durch Cloud Identity Platform (und Firebase Auth) für die Nutzerverwaltung ersetzt haben:

3a83ae745121d70.png

Im Vergleich zu Modul 20 sehen Sie, dass beim Klicken auf „Anmelden“ ein Pop-up-Fenster anstelle einer Weiterleitung angezeigt wird. Dies ist in einigen der Screenshots unten zu sehen. Wie in Modul 20 beschrieben, unterscheidet sich das Verhalten jedoch leicht, je nachdem, wie viele Google-Konten im Browser registriert sind.

Wenn keine Nutzer im Browser registriert sind oder nur ein Nutzer, der sich noch nicht angemeldet hat, wird ein allgemeines Google-Anmelde-Pop-up angezeigt:

8437f5f3d489a942.png

Wenn ein einzelner Nutzer in Ihrem Browser registriert ist, sich aber an anderer Stelle anmeldet, wird kein Dialogfeld angezeigt (oder es wird eingeblendet und sofort wieder geschlossen) und die App wechselt in den angemeldeten Status (die E‑Mail-Adresse des Nutzers und die Schaltfläche Abmelden werden angezeigt).

Einige Entwickler möchten möglicherweise auch für einen einzelnen Nutzer eine Kontoauswahl anbieten:

b75624cb68d94557.png

Entfernen Sie dazu die Kommentarzeichen für die Zeile provider.setCustomParameters({prompt: 'select_account'}); in der Webvorlage, wie oben beschrieben.

Wenn es mehrere Nutzer gibt, wird das Kontoauswahlfeld eingeblendet (siehe unten). Wenn der Nutzer noch nicht angemeldet ist, wird er dazu aufgefordert. Wenn Sie bereits angemeldet sind, wird das Pop-up-Fenster geschlossen und die App wechselt in den angemeldeten Status.

c454455b6020d5e4.png

Der Anmeldestatus von Modul 21 sieht genauso aus wie die Benutzeroberfläche von Modul 20:

49ebe4dcc1eff11f.png

Das gilt auch, wenn sich ein Administratornutzer angemeldet hat:

44302f35b39856eb.png

Anders als in Modul 21 wird in Modul 20 immer über die App (serverseitiger Code) auf die Logik für den Inhalt der Webvorlage zugegriffen. Ein Fehler in Modul 20 besteht darin, dass ein Besuch registriert wird, wenn der Endnutzer die App zum ersten Mal aufruft, und ein weiterer, wenn sich ein Nutzer anmeldet.

In Modul 21 findet die Anmeldelogik nur in der Webvorlage (clientseitiger Code) statt. Es ist kein serverseitiger Aufruf erforderlich, um zu ermitteln, welche Inhalte angezeigt werden sollen. Der einzige Aufruf, der an den Server gesendet wird, ist die Prüfung auf Administratorkonten, nachdem sich ein Endnutzer angemeldet hat. Das bedeutet, dass durch An- und Abmeldungen keine zusätzlichen Besuche registriert werden. Die Liste der letzten Besuche bleibt also bei Aktionen zur Nutzerverwaltung unverändert. Auf den Screenshots oben sind dieselben vier Besuche bei mehreren Nutzeranmeldungen zu sehen.

Die Screenshots in Modul 20 zeigen den „Double-Visit-Fehler“ am Anfang dieses Codelabs. Für jede An- oder Abmeldung werden separate Besuchslogs angezeigt. Sehen Sie sich die Zeitstempel des letzten Besuchs für jeden Screenshot an, um die chronologische Reihenfolge zu sehen.

Bereinigen

Allgemein

Wenn Sie die App vorerst nicht mehr benötigen, empfehlen wir Ihnen, sie zu deaktivieren, um Abrechnungen zu vermeiden. Wenn Sie jedoch weitere Tests durchführen möchten, bietet die App Engine-Plattform ein kostenloses Kontingent. Solange Sie diese Nutzungsebene nicht überschreiten, werden Ihnen keine Gebühren berechnet. Das gilt für die Rechenleistung. Es können aber auch Gebühren für relevante App Engine-Dienste anfallen. Weitere Informationen finden Sie auf der Preisseite. Wenn bei dieser Migration andere Cloud-Dienste beteiligt sind, werden diese separat abgerechnet. Sehen Sie sich in beiden Fällen gegebenenfalls den Abschnitt „Spezifisch für dieses Codelab“ unten an.

Die Bereitstellung auf einer serverlosen Google Cloud-Compute-Plattform wie App Engine verursacht geringe Build- und Speicherkosten. Cloud Build und Cloud Storage haben jeweils ein eigenes kostenloses Kontingent. Das Speichern dieses Bildes verbraucht einen Teil dieses Kontingents. Möglicherweise leben Sie jedoch in einer Region, in der es kein solches kostenloses Kontingent gibt. Achten Sie daher auf Ihre Speichernutzung, um potenzielle Kosten zu minimieren. Folgende Cloud Storage-„Ordner“ sollten Sie sich ansehen:

  • 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
  • Die oben genannten Speicherlinks hängen von Ihrem PROJECT_ID und LOC ab, z. B. „us“, wenn Ihre App in den USA gehostet wird.

Wenn Sie diese Anwendung oder andere zugehörige Migrations-Codelabs nicht weiter verwenden und alles vollständig löschen möchten, beenden Sie Ihr Projekt.

Spezifisch für dieses Codelab

Die unten aufgeführten Dienste sind nur für dieses Codelab verfügbar. Weitere Informationen finden Sie in der Dokumentation der einzelnen Produkte:

  • Der Dienst App Engine Datastore wird von Cloud Datastore (Cloud Firestore im Datastore-Modus) bereitgestellt, das ebenfalls eine kostenlose Stufe bietet. Weitere Informationen finden Sie auf der Preisseite.
  • Die Nutzung der Cloud Identity Platform ist je nach den verwendeten Diensten bis zu einem gewissen Grad kostenlos. Weitere Informationen finden Sie auf der Preisseite.
  • Die Nutzung der Cloud Resource Manager API ist größtenteils kostenlos. Weitere Informationen finden Sie auf der Preisseite.

Nächste Schritte

Neben dieser Anleitung gibt es weitere Migrationsmodule, die sich mit der Umstellung von den gebündelten Legacy-Diensten befassen:

  • Modul 2: Von App Engine ndb zu Cloud NDB migrieren
  • Module 7–9: Migration von App Engine Task Queue (Push-Aufgaben) zu Cloud Tasks
  • Module 12–13: Von App Engine Memcache zu Cloud Memorystore migrieren
  • Module 15–16: Migration von App Engine Blobstore zu Cloud Storage
  • Module 18–19: Von App Engine-Aufgabenwarteschlange (Pull-Aufgaben) zu Cloud Pub/Sub migrieren

App Engine ist nicht mehr die einzige serverlose Plattform in Google Cloud. Wenn Sie eine kleine App Engine-Anwendung oder eine Anwendung mit eingeschränkter Funktionalität haben und sie in einen eigenständigen Mikrodienst umwandeln möchten oder eine monolithische Anwendung in mehrere wiederverwendbare Komponenten aufteilen möchten, sind dies gute Gründe für einen Wechsel zu Cloud Functions. Wenn die Containerisierung Teil Ihres Anwendungsentwicklungs-Workflows geworden ist, insbesondere wenn er aus einer CI/CD-Pipeline (Continuous Integration/Continuous Delivery oder Deployment) besteht, sollten Sie eine Migration zu Cloud Run in Betracht ziehen. Diese Szenarien werden in den folgenden Modulen behandelt:

  • Von App Engine zu Cloud Functions migrieren: Modul 11
  • Von App Engine zu Cloud Run migrieren: Modul 4 enthält Informationen zum Containerisieren Ihrer App mit Docker und Modul 5 zum Containerisieren ohne Container, Docker-Kenntnisse oder Dockerfiles.

Der Wechsel zu einer anderen serverlosen Plattform ist optional. Wir empfehlen, die besten Optionen für Ihre Apps und Anwendungsfälle zu prüfen, bevor Sie Änderungen vornehmen.

Unabhängig davon, welches Migrationsmodul Sie als Nächstes in Betracht ziehen, können Sie auf alle Serverless Migration Station-Inhalte (Codelabs, Videos, Quellcode [sofern verfügbar]) über das zugehörige Open-Source-Repository zugreifen. Das README des Repositorys enthält auch Informationen dazu, welche Migrationen infrage kommen und welche Reihenfolge der Migrationsmodule relevant ist.

8. Zusätzliche Ressourcen

Unten finden Sie weitere Ressourcen für Entwickler, die sich näher mit diesem oder ähnlichen Migrationsmodulen befassen möchten. Unten können Sie Feedback zu diesen Inhalten geben und Links zum Code sowie verschiedene Dokumentationen finden, die für Sie nützlich sein könnten.

Probleme mit Codelabs/Feedback

Wenn Sie Probleme mit diesem Codelab feststellen, suchen Sie bitte zuerst nach Ihrem Problem, bevor Sie es melden. Links zum Suchen und Erstellen neuer Probleme:

Migrationsressourcen

Links zu den Repository-Ordnern für Modul 20 (START) und Modul 21 (FINISH) finden Sie in der Tabelle unten.

Codelab

Python 2

Python 3

Modul 20

code

(n/a)

Modul 21 (dieses Codelab)

code

code

Online-Referenzen

Unten finden Sie Ressourcen, die für diese Anleitung relevant sind:

Cloud Identity Platform und Cloud Marketplace

Cloud Resource Manager, Cloud IAM, Firebase Admin SDK

App Engine-Nutzer, App Engine NDB, Cloud NDB, Cloud Datastore

Andere Referenzen für das Migrationsmodul

App Engine-Migration

App Engine-Plattform

Cloud SDK

Andere Cloud-Informationen

Videos

Lizenz

Dieser Text ist mit einer Creative Commons Attribution 2.0 Generic License lizenziert.