Migrer du service Users d'App Engine vers Cloud Identity Platform (module 21)

1. Présentation

La série d'ateliers de programmation Serverless Migration Station (tutoriels pratiques à suivre à votre rythme) et les vidéos associées sont destinées à aider les développeurs Google Cloud sans serveur à moderniser leurs applications en les guidant tout au long d'une ou plusieurs migrations, principalement en les aidant à abandonner les anciens services. Cela rend vos applications plus portables et vous offre plus d'options et de flexibilité, ce qui vous permet de vous intégrer à une plus large gamme de produits Cloud et d'y accéder, et de passer plus facilement aux versions linguistiques plus récentes. Bien qu'elle se concentre initialement sur les premiers utilisateurs de Cloud, principalement les développeurs App Engine (environnement standard), cette série est suffisamment large pour inclure d'autres plates-formes sans serveur comme Cloud Functions et Cloud Run, ou ailleurs si nécessaire.

L'objectif de cet atelier de programmation est de montrer aux développeurs App Engine Python 2 comment migrer de l'API/du service Users App Engine vers Cloud Identity Platform (GCIP). Il existe également une migration implicite d'App Engine NDB vers Cloud NDB pour l'accès à Datastore (principalement abordée dans le module de migration 2), ainsi qu'une mise à niveau vers Python 3.

Le module 20 explique comment ajouter l'utilisation de l'API Users à l'exemple d'application du module 1. Dans ce module, vous allez prendre l'application du module 20 terminée et migrer son utilisation vers Cloud Identity Platform.

Vous apprendrez à

  • Remplacer l'utilisation du service App Engine Users par Cloud Identity Platform
  • Remplacer l'utilisation d'App Engine NDB par Cloud NDB (voir aussi le module 2)
  • Configurer différents fournisseurs d'identité d'authentification à l'aide de Firebase Authentication
  • Utiliser l'API Cloud Resource Manager pour obtenir des informations IAM sur le projet
  • Utiliser le SDK Admin Firebase pour obtenir des informations sur les utilisateurs
  • Transférer l'exemple d'application vers Python 3

Prérequis

Enquête

Comment allez-vous utiliser ce tutoriel ?

Je vais le lire uniquement Je vais le lire et effectuer les exercices

Quel est votre niveau d'expérience avec Python ?

Débutant Intermédiaire Expert

Quel est votre niveau d'expérience avec les services Google Cloud ?

Débutant Intermédiaire Expert

2. Arrière-plan

Le service App Engine Users est un système d'authentification des utilisateurs destiné aux applications App Engine. Il fournit la connexion Google comme fournisseur d'identité, des liens de connexion et de déconnexion pratiques à utiliser dans les applications, et prend en charge le concept d'utilisateurs administrateurs et de fonctionnalités réservées aux administrateurs. Pour améliorer la portabilité des applications, Google Cloud recommande de migrer les anciens services groupés App Engine vers les services Cloud autonomes, par exemple du service Users vers Cloud Identity Platform, entre autres.

Identity Platform est basé sur Firebase Authentication et ajoute un certain nombre de fonctionnalités d'entreprise, telles que l'authentification multifacteur, la prise en charge de l'authentification unique OIDC et SAML, l'architecture mutualisée, un contrat de niveau de service garantissant une disponibilité de 99,95 %, et bien plus encore. Ces différences sont également mises en évidence sur la page de comparaison des produits Identity Platform et Firebase Authentication. Ces deux produits offrent beaucoup plus de fonctionnalités que le service Users.

Cet atelier de programmation du module 21 montre comment passer de l'authentification des utilisateurs de l'application du service Users aux fonctionnalités Identity Platform qui reflètent le plus fidèlement la fonctionnalité présentée dans le module 20. Le module 21 propose également une migration d'App Engine NDB vers Cloud NDB pour l'accès à Datastore, en reprenant la migration du module 2.

Bien que le code du module 20 soit "annoncé" comme un exemple d'application Python 2, la source elle-même est compatible avec Python 2 et 3, et elle le reste même après la migration vers Identity Platform (et Cloud NDB) dans le module 21. Il est possible de continuer à utiliser le service Users lors de la mise à niveau vers Python 3, car la migration vers Identity Platform est facultative. Consultez l'atelier de programmation du module 17 et la vidéo pour découvrir comment continuer à utiliser les services groupés tout en passant aux environnements d'exécution de deuxième génération comme Python 3.

Ce tutoriel comprend les étapes suivantes :

  1. Configuration/Préparation
  2. Mettre à jour la configuration
  3. Modifier le code de l'application

3. Configuration/Préparation

Cette section explique comment effectuer les opérations suivantes :

  1. Configurer votre projet Cloud
  2. Obtenir un exemple d'application de référence
  3. (Re)Déployer et valider l'application de référence
  4. Activer de nouveaux services/API Google Cloud

Ces étapes vous permettent de commencer avec un code fonctionnel prêt à être migré vers des services Cloud autonomes.

1. Configurer le projet

Si vous avez terminé l'atelier de programmation du module 20, réutilisez le même projet (et le même code). Vous pouvez également créer un projet ou réutiliser un autre projet existant. Assurez-vous que le projet dispose d'un compte de facturation actif et d'une application App Engine activée. Trouvez votre ID de projet et gardez-le à portée de main pendant cet atelier de programmation. Utilisez-le chaque fois que vous rencontrez la variable PROJ_ID.

2. Obtenir un exemple d'application de référence

L'une des conditions préalables est de disposer d'une application App Engine fonctionnelle du module 20. Vous pouvez suivre l'atelier de programmation correspondant (recommandé, lien ci-dessus) ou copier le code du module 20 depuis le dépôt. Que vous utilisiez votre application ou la nôtre, c'est ici que nous allons commencer ("START"). Cet atelier de programmation vous guide tout au long de la migration jusqu'à obtenir un code similaire à celui du dossier du dépôt du module 21 ("FINISH").

Copiez le dossier du dépôt du module 20. Il devrait ressembler à la sortie ci-dessous et peut-être contenir un dossier lib si vous avez effectué l'atelier de programmation du module 20 :

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

3. (Re)Déployer et valider l'application de référence

Pour déployer l'application du module 20, procédez comme suit :

  1. Supprimez le dossier lib, le cas échéant, puis exécutez pip install -t lib -r requirements.txt pour le remplir à nouveau. Vous devrez peut-être utiliser pip2 si Python 2 et Python 3 sont tous les deux installés.
  2. Assurez-vous d'avoir installé et initialisé l'outil de ligne de commande gcloud, et d'avoir consulté son utilisation.
  3. Si vous ne souhaitez pas saisir votre PROJ_ID à chaque commande gcloud émise, définissez d'abord le projet Cloud avec gcloud config set project PROJ_ID.
  4. Déployez l'exemple d'application avec gcloud app deploy
  5. Vérifiez que l'application s'exécute comme prévu, sans erreur. Si vous avez terminé l'atelier de programmation du module 20, l'application affiche les informations de connexion de l'utilisateur (adresse e-mail, éventuel "badge d'administrateur" et bouton de connexion/déconnexion) en haut de l'écran, ainsi que les visites les plus récentes (illustrées ci-dessous).

907e64c19ef964f8.png

Si l'utilisateur se connecte en tant qu'utilisateur standard, son adresse e-mail s'affiche et le bouton "Connexion" devient un bouton "Déconnexion" :

ad7b59916b69a035.png

Si vous vous connectez en tant qu'administrateur, votre adresse e-mail s'affiche avec la mention "(administrateur)" à côté :

867bcb3334149e4.png

4. Activer de nouvelles API/de nouveaux services Google Cloud

Introduction

L'application du module 20 utilise les API App Engine NDB et Users, des services groupés qui ne nécessitent aucune configuration supplémentaire. En revanche, les services Cloud autonomes en nécessitent une. L'application mise à jour utilisera à la fois Cloud Identity Platform et Cloud Datastore (via la bibliothèque cliente Cloud NDB). De plus, pour déterminer les utilisateurs administrateurs App Engine, nous devons également utiliser l'API Cloud Resource Manager.

Coût

  • App Engine et Cloud Datastore disposent de quotas Toujours sans frais. Tant que vous ne dépassez pas ces limites, vous ne devriez pas être facturé pour ce tutoriel. Pour en savoir plus, consultez également la page des tarifs d'App Engine et la page des tarifs de Cloud Datastore.
  • L'utilisation de Cloud Identity Platform est facturée en fonction du nombre d'utilisateurs actifs par mois (UAM) ou des validations d'authentification. Une version "sans frais" est disponible pour chaque modèle d'utilisation. Pour en savoir plus, consultez la page des tarifs. De plus, alors qu'App Engine et Cloud Datastore nécessitent la facturation, l'utilisation de GCIP en soi ne nécessite pas l'activation de la facturation tant que vous ne dépassez pas ses quotas quotidiens sans instrumentation. Tenez-en compte pour les projets Cloud qui n'impliquent pas d'API/services Cloud nécessitant la facturation.
  • L'utilisation de l'API Cloud Resource Manager est sans frais dans la plupart des cas, comme indiqué sur la page des tarifs.

Les utilisateurs activent les API Cloud depuis la console Cloud ou depuis la ligne de commande (via la commande gcloud, qui fait partie du SDK Cloud), selon leurs préférences. Commençons par les API Cloud Datastore et Cloud Resource Manager.

Depuis Cloud Console

Accédez à la page Bibliothèque du gestionnaire d'API (pour le bon projet) dans la console Cloud, puis recherchez une API à l'aide de la barre de recherche. c7a740304e9d35b.png

Activez ces API :

Recherchez et cliquez sur le bouton Activer pour chaque API séparément. Vous serez peut-être invité à fournir des informations de facturation. Par exemple, voici la page de l'API Resource Manager :

fc7bd8f4c49d12e5.png

Le bouton devient "Gérer" une fois la fonctionnalité activée (généralement après quelques secondes) :

8eca12d6cc7b45b0.png

Activez Cloud Datastore de la même manière :

83811599b110e46b.png

Depuis la ligne de commande

Bien qu'il soit visuellement informatif d'activer les API depuis la console, certains préfèrent la ligne de commande. Vous pouvez activer autant d'API que vous le souhaitez en même temps. Exécutez cette commande pour activer les API Cloud Datastore et Cloud Resource Manager, puis attendez la fin de l'opération, comme illustré ici :

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

Vous serez peut-être invité à fournir des informations de facturation.

Les "URL" de chaque API utilisée dans la commande ci-dessus sont appelées noms de service de l'API. Vous les trouverez en bas de la page de la bibliothèque pour chaque API. Si vous souhaitez activer d'autres API Cloud pour vos propres applications, vous trouverez leurs noms de service respectifs sur les pages d'API correspondantes. Cette commande liste tous les noms de services pour les API que vous pouvez activer :

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

Que ce soit dans la console Cloud ou sur la ligne de commande, une fois les étapes ci-dessus effectuées, notre exemple peut désormais accéder à ces API. Les étapes suivantes consistent à activer Cloud Identity Platform et à apporter les modifications de code nécessaires.

Activer et configurer Cloud Identity Platform (console Cloud uniquement)

Cloud Identity Platform est un service Marketplace, car il se connecte à une ressource en dehors de Google Cloud ou en dépend, par exemple Firebase Authentication. Pour le moment, vous ne pouvez activer les services Marketplace que depuis la console Cloud. Procédez comme suit :

  1. Accédez à la page Cloud Identity Platform sur Cloud Marketplace, puis cliquez sur le bouton Activer. Si vous y êtes invité, effectuez la mise à niveau depuis Firebase Authentication. Vous pourrez ainsi accéder à des fonctionnalités supplémentaires, comme celles décrites précédemment dans la section Contexte. Voici la page Marketplace où le bouton Activer est mis en évidence : 28475f1c9b29de69.png
  2. Une fois Identity Platform activé, vous serez peut-être redirigé automatiquement vers la page Fournisseurs d'identité. Si ce n'est pas le cas, cliquez sur ce lien pratique pour y accéder. fc2d92d42a5d1dd7.png
  3. Activez le fournisseur Google Auth. Si aucun fournisseur n'a été configuré, cliquez sur Ajouter un fournisseur, puis sélectionnez Google. Lorsque vous revenez à cet écran, l'entrée Google doit être activée. Google est le seul fournisseur d'authentification que nous utilisons dans ce tutoriel pour refléter le service App Engine Users en tant que service Google Sign-In léger. Dans vos propres applications, vous pouvez activer d'autres fournisseurs d'authentification.
  4. Après avoir sélectionné et configuré Google et les autres fournisseurs d'authentification souhaités, cliquez sur Application Setup Details (Détails de configuration de l'application). Dans la boîte de dialogue qui s'affiche, copiez apiKey et authDomain dans l'objet config de l'onglet "Web", puis enregistrez-les dans un endroit sûr. Pourquoi ne pas tout copier ? L'extrait de code de cette boîte de dialogue est codé en dur et daté. Il vous suffit donc d'enregistrer les éléments les plus importants et de les utiliser dans notre code avec une utilisation plus simultanée de Firebase Authentication. Une fois que vous avez copié les valeurs et que vous les avez enregistrées dans un endroit sûr, cliquez sur le bouton Fermer pour terminer la configuration nécessaire. bbb09dcdd9be538e.png

4. Mettre à jour la configuration

Les mises à jour de configuration incluent la modification de différents fichiers de configuration et la création de l'équivalent d'App Engine, mais dans l'écosystème Cloud Identity Platform.

appengine_config.py

  • Si vous passez à Python 3, supprimez appengine_config.py
  • Si vous prévoyez de moderniser votre système pour passer à Identity Platform, mais que vous souhaitez rester sur Python 2, ne supprimez pas le fichier. Nous le mettrons à jour ultérieurement lors du rétroportage de Python 2.

requirements.txt

Le fichier requirements.txt du module 20 ne listait que Flask. Pour le module 21, ajoutez les packages suivants :

Le contenu de requirements.txt devrait désormais se présenter comme suit :

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

app.yaml

  • La mise à niveau vers Python 3 implique de simplifier le fichier app.yaml. Supprimez tout, à l'exception de la directive d'exécution, et définissez-la sur une version actuellement compatible de Python 3. L'exemple utilise actuellement la version 3.10.
  • Si vous restez avec Python 2, n'effectuez aucune action pour le moment.

AVANT :

runtime: python27
threadsafe: yes
api_version: 1

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

L'application exemple du module 20 ne comporte pas de gestionnaires de fichiers statiques. Si vos applications le font, laissez-les intactes. Vous pouvez supprimer tous vos gestionnaires de script si vous le souhaitez, ou simplement les laisser en place pour référence, à condition de remplacer leurs gestionnaires par auto, comme décrit dans le guide de migration app.yaml. Avec ces modifications, le app.yaml mis à jour pour Python 3 est simplifié comme suit :

APRÈS :

runtime: python310

Autres mises à jour de configuration

Que vous restiez sur Python 2 ou que vous passiez à Python 3, supprimez le dossier lib si vous en avez un.

5. Modifier le code de l'application

Cette section présente les mises à jour du fichier d'application principal, main.py, qui remplacent l'utilisation du service App Engine Users par Cloud Identity Platform. Après avoir mis à jour l'application principale, vous allez mettre à jour le modèle Web, templates/index.html.

Mettre à jour les importations et l'initialisation

Suivez les étapes ci-dessous pour mettre à jour les importations et initialiser les ressources de l'application :

  1. Pour les importations, remplacez App Engine NDB par Cloud NDB.
  2. En plus de Cloud NDB, importez également Cloud Resource Manager.
  3. Identity Platform étant basé sur Firebase Auth, importez le SDK Admin Firebase.
  4. Les API Cloud nécessitent l'utilisation d'un client d'API. Initialisez-le pour Cloud NDB juste après l'initialisation de Flask.

Bien que le package Cloud Resource Manager soit importé ici, nous l'utiliserons ultérieurement lors de l'initialisation de l'application. Vous trouverez ci-dessous les importations et l'initialisation du module 20, suivies de l'apparence des sections après l'implémentation des modifications ci-dessus :

AVANT :

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

app = Flask(__name__)

APRÈS :

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

Assistance pour les utilisateurs de l'administration App Engine

Deux composants doivent être ajoutés à l'application pour permettre la reconnaissance des utilisateurs administrateurs :

  • _get_gae_admins() : regroupe l'ensemble des utilisateurs administrateurs. Appelée une seule fois et enregistrée.
  • is_admin() : vérifie si l'utilisateur connecté est un administrateur. Cette méthode est appelée à chaque connexion d'un utilisateur.

La fonction utilitaire _get_gae_admins() appelle l'API Resource Manager pour récupérer la stratégie d'autorisation Cloud IAM actuelle. La stratégie d'autorisation définit et applique les rôles attribués aux comptes principaux (utilisateurs humains, comptes de service, etc.). La configuration inclut les éléments suivants :

  • Récupération de l'ID du projet Cloud (PROJ_ID)
  • Créer un client API Resource Manager (rm_client)
  • Créer un ensemble (en lecture seule) de rôles d'administrateur App Engine (_TARGETS)

Le gestionnaire de ressources nécessite l'ID du projet Cloud. Importez donc google.auth.default() et appelez cette fonction pour obtenir l'ID du projet. Cet appel comporte un paramètre qui ressemble à une URL, mais qui est un champ d'application d'autorisation OAuth2. Lorsque vous exécutez des applications dans le cloud, par exemple sur une VM Compute Engine ou une application App Engine, un compte de service par défaut est fourni et dispose de privilèges étendus. Conformément aux bonnes pratiques du moindre privilège, nous vous recommandons de créer vos propres comptes de service gérés par l'utilisateur.

Pour les appels d'API, il est préférable de réduire davantage le champ d'application de vos applications au niveau minimal nécessaire à leur bon fonctionnement. L'appel d'API Resource Manager que nous allons effectuer est get_iam_policy(), qui nécessite l'un des champs d'application suivants pour fonctionner :

  • 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

L'application exemple n'a besoin que d'un accès en lecture seule à la règle d'autorisation. Il ne modifie pas la règle et n'a pas besoin d'accéder à l'intégralité du projet. Cela signifie que l'application n'a besoin d'aucune des trois premières autorisations. La dernière est la seule requise. C'est celle que nous allons implémenter pour l'application exemple.

Le corps principal de la fonction crée un ensemble vide d'utilisateurs administrateurs (admins), récupère le allow_policy via get_iam_policy() et parcourt toutes ses liaisons en recherchant spécifiquement les rôles d'administrateur App Engine :

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

Pour chaque rôle cible trouvé, il rassemble les utilisateurs qui appartiennent à ce rôle et les ajoute à l'ensemble global des utilisateurs administrateurs. Il se termine en renvoyant tous les utilisateurs administrateurs trouvés et mis en cache en tant que constante (_ADMINS) pour la durée de vie de cette instance App Engine. Nous allons voir cet appel arriver sous peu.

Ajoutez la définition de fonction _get_gae_admins() suivante à main.py juste en dessous de l'instanciation du client de l'API Cloud NDB (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

Lorsque les utilisateurs se connectent à l'application, les événements suivants se produisent :

  1. Une vérification rapide est effectuée à partir du modèle Web lorsqu'un utilisateur se connecte à Firebase.
  2. Lorsque l'état d'authentification change dans le modèle, un appel fetch() de type Ajax est effectué vers /is_admin, dont le gestionnaire est la fonction suivante, is_admin().
  3. Le jeton d'identité Firebase est transmis dans le corps POST à is_admin(), qui le récupère dans les en-têtes et appelle le SDK Admin Firebase pour le valider. S'il s'agit d'un utilisateur valide, extrais son adresse e-mail et vérifie s'il s'agit d'un administrateur.
  4. Le résultat booléen est ensuite renvoyé au modèle en tant que code 200 (opération réussie).

Ajoutez is_admin() à main.py juste après _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

L'intégralité du code des deux fonctions est nécessaire pour reproduire les fonctionnalités disponibles dans le service Users, en particulier sa fonction is_current_user_admin(). Cet appel de fonction dans le module 20 a fait tout le travail, contrairement au module 21 où nous implémentons une solution de remplacement. Bonne nouvelle : l'application ne dépend plus d'un service App Engine uniquement. Vous pouvez donc migrer vos applications vers Cloud Run ou d'autres services. De plus, vous pouvez également modifier la définition d'"utilisateur administrateur" pour vos propres applications en passant aux rôles souhaités dans _TARGETS, alors que le service Utilisateurs est codé en dur pour les rôles d'administrateur App Engine.

Initialiser Firebase Authentication et mettre en cache les utilisateurs administrateurs App Engine

Nous aurions pu initialiser Firebase Auth en haut de la page, à peu près au même endroit que l'application Flask et le client de l'API Cloud NDB, mais ce n'était pas nécessaire tant que tout le code d'administration n'avait pas été défini, ce qui est le cas maintenant. De même, maintenant que _get_gae_admins() est défini, appelez-le pour mettre en cache la liste des utilisateurs administrateurs.

Ajoutez les lignes suivantes juste en dessous du corps de la fonction is_admin() :

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

Accéder aux mises à jour du modèle de données

Le modèle de données Visit ne change pas. L'accès à Datastore nécessite l'utilisation explicite du gestionnaire de contexte du client de l'API Cloud NDB, ds_client.context(). Dans le code, cela signifie que vous encapsulez les appels Datastore dans store_visit() et fetch_visits() à l'intérieur des blocs with Python. Cette mise à jour est identique au module 2. Apportez les modifications suivantes :

AVANT :

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)

APRÈS :

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)

Déplacer la logique de connexion des utilisateurs vers le modèle Web

Le service App Engine Users est côté serveur, tandis que Firebase Auth et Cloud Identity Platform sont principalement côté client. Par conséquent, une grande partie du code de gestion des utilisateurs de l'application du module 20 est déplacée vers le modèle Web du module 21.

Dans main.py, le contexte Web transmet cinq éléments de données essentiels au modèle. Les quatre premiers sont liés à la gestion des utilisateurs et diffèrent selon que l'utilisateur est connecté ou non :

  • who : adresse e-mail de l'utilisateur s'il est connecté, ou user dans le cas contraire
  • admin : badge (administrateur) si l'utilisateur connecté est un administrateur
  • sign : affiche le bouton Connexion ou Déconnexion.
  • link : liens de connexion ou de déconnexion au clic sur un bouton
  • visits : visites les plus récentes

AVANT :

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

Toute la gestion des utilisateurs est transférée vers le modèle Web. Il ne nous reste donc que les visites, ce qui ramène le gestionnaire principal à ce que nous avions dans l'application du module 1 :

APRÈS :

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

Mettre à jour un modèle Web

À quoi ressemblent toutes les modifications de la section précédente dans le modèle ? Nous avons principalement transféré la gestion des utilisateurs de l'application vers Firebase Auth s'exécutant dans le modèle, et nous avons partiellement transféré tout le code que nous avons déplacé vers JavaScript. Nous avons constaté une diminution assez importante de main.py. Nous prévoyons donc une croissance similaire pour templates/index.html.

AVANT :

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

Remplacez l'intégralité du modèle Web par le contenu ci-dessous :

APRÈS :

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

Ce corps HTML comporte de nombreux composants. Examinons-les un par un.

Importations Firebase

Toujours dans l'en-tête du document HTML, une fois le titre de la page passé, importez les composants Firebase nécessaires. Les composants Firebase sont désormais divisés en plusieurs modules pour plus d'efficacité. Le code permettant d'initialiser Firebase est importé à partir du module principal de l'application Firebase, tandis que les fonctions qui gèrent l'authentification Firebase, Google en tant que fournisseur d'authentification, la connexion et la déconnexion, ainsi que le "callback" de changement d'état d'authentification sont toutes importées à partir du module Firebase Auth :

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

Configuration de Firebase

Plus tôt dans ce tutoriel, lors de la configuration d'Identity Platform, vous avez enregistré apiKey et authDomain à partir de la boîte de dialogue Informations sur la configuration de l'application. Ajoutez ces valeurs à la variable firebaseConfig dans la section suivante. Un lien vers des instructions plus détaillées est fourni dans les commentaires :

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

Initialisation de Firebase

La section suivante initialise Firebase avec ces informations de configuration.

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

Cela permet d'utiliser Google comme fournisseur d'authentification et fournit une option commentée pour afficher le sélecteur de compte même s'il n'y a qu'un seul compte Google enregistré dans votre session de navigateur. En d'autres termes, lorsque vous avez plusieurs comptes, ce sélecteur de compte s'affiche comme prévu : a38369389b7c4c7e.png. Toutefois, s'il n'y a qu'un seul utilisateur dans la session, le processus de connexion se termine automatiquement sans aucune interaction de l'utilisateur. (Le pop-up s'affiche, puis disparaît.) Vous pouvez forcer l'affichage de la boîte de dialogue du sélecteur de compte pour un utilisateur (au lieu de se connecter immédiatement à l'application) en décommentant la ligne du paramètre personnalisé. Si cette option est activée, le sélecteur de compte s'affiche même pour les connexions à un seul utilisateur : b75624cb68d94557.png

Fonctions de connexion et de déconnexion

Les lignes de code suivantes constituent les fonctions pour les clics sur les boutons de connexion ou de déconnexion :

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

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

Actions de connexion et de déconnexion

La dernière section majeure de ce bloc <script> est la fonction appelée pour chaque modification de l'authentification (connexion ou déconnexion).

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

Le code du module 20 qui détermine s'il faut envoyer un contexte de modèle "utilisateur connecté" ou "utilisateur déconnecté" est transféré ici. La condition en haut du fichier entraîne true si l'utilisateur s'est connecté avec succès, ce qui déclenche les actions suivantes :

  1. L'adresse e-mail de l'utilisateur est définie pour être affichée.
  2. Le bouton Connexion devient Déconnexion.
  3. Un appel de type Ajax à /is_admin est effectué pour déterminer s'il faut afficher le badge d'utilisateur administrateur (admin).

Lorsque l'utilisateur se déconnecte, la clause else est exécutée pour réinitialiser toutes les informations utilisateur :

  1. Nom d'utilisateur défini sur user
  2. Badge d'administrateur supprimé
  3. Le bouton Déconnexion a été remplacé par Connexion.

Variables de modèle

Une fois la section d'en-tête terminée, le corps principal commence par les variables de modèle qui sont remplacées par des éléments HTML qui changent si nécessaire :

  1. Nom d'utilisateur affiché
  2. Badge d'administrateur (admin) (le cas échéant)
  3. Bouton Se connecter ou Se déconnecter
<body>
<p>
Welcome, <span id="who"></span> <span id="admin"><code>(admin)</code></span>
<button id="logbtn"></button>
</p><hr>

Variables d'éléments HTML et visites les plus récentes

Le code des visites les plus récentes ne change pas, et le bloc <script> final définit les variables pour les éléments HTML qui changent pour la connexion et la déconnexion listées juste au-dessus :

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

Les modifications à apporter à l'application et au modèle Web pour passer des API App Engine NDB et Users à Cloud NDB et Identity Platform, ainsi que pour passer à Python 3, sont maintenant terminées. Félicitations ! Vous êtes arrivé à l'application exemple du module 21. Notre version est disponible dans le dossier du dépôt du module 21b.

La partie suivante de l'atelier de programmation est facultative (*) et ne s'adresse qu'aux utilisateurs dont les applications doivent rester sur Python 2. Elle vous guide à travers les étapes nécessaires pour obtenir une application Python 2 du module 21 fonctionnelle.

6. *Rétroportage Python 2

Cette section facultative s'adresse aux développeurs qui migrent vers Identity Platform, mais qui doivent continuer à s'exécuter sur l'environnement d'exécution Python 2. Si cela ne vous pose pas de problème, ignorez cette section.

Pour créer une version Python 2 fonctionnelle de l'application du module 21, vous avez besoin des éléments suivants :

  1. Exigences concernant l'environnement d'exécution : fichiers de configuration compatibles avec Python 2 et modifications requises dans l'application principale pour éviter les incompatibilités avec Python 3
  2. Modification mineure de la bibliothèque : Python 2 a été abandonné avant que certaines fonctionnalités requises ne soient ajoutées à la bibliothèque cliente Resource Manager. Vous avez donc besoin d'une autre façon d'accéder à cette fonctionnalité manquante.

Suivons ces étapes maintenant, en commençant par la configuration.

Restaurer appengine_config.py

Plus tôt dans ce tutoriel, vous avez été invité à supprimer appengine_config.py, car il n'est pas utilisé par l'environnement d'exécution Python 3 App Engine. Pour Python 2, il doit non seulement être conservé, mais le module 20 appengine_config.py doit être mis à jour pour prendre en charge l'utilisation des bibliothèques tierces intégrées, à savoir grpcio et setuptools. Ces packages sont requis chaque fois que votre application App Engine utilise des bibliothèques clientes Cloud, comme celles de Cloud NDB et de Cloud Resource Manager.

Vous ajouterez ces packages à app.yaml dans un instant, mais pour que votre application puisse y accéder, la fonction pkg_resources.working_set.add_entry() de setuptools doit être appelée. Cela permet aux bibliothèques tierces copiées (auto-groupées ou fournies) installées dans le dossier lib de communiquer avec les bibliothèques intégrées.

Pour appliquer ces modifications, implémentez les mises à jour suivantes dans votre fichier appengine_config.py :

AVANT :

from google.appengine.ext import vendor

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

Ce code seul ne suffit pas pour prendre en charge l'utilisation de setuptools et grpcio. Quelques lignes supplémentaires sont nécessaires. Mettez à jour appengine_config.py pour qu'il ressemble à ceci :

APRÈS :

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)

Pour en savoir plus sur les modifications requises pour prendre en charge les bibliothèques clientes Cloud, consultez la documentation sur la migration des services groupés.

app.yaml

Comme pour appengine_config.py, le fichier app.yaml doit être rétabli sur une version compatible avec Python 2. Commençons par le module 20 d'origine app.yaml :

AVANT :

runtime: python27
threadsafe: yes
api_version: 1

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

En plus de setuptools et grpcio mentionnés précédemment, il existe une dépendance (qui n'est pas explicitement liée à la migration Identity Platform) qui nécessite l'utilisation de la bibliothèque cliente Cloud Storage. Celle-ci a besoin d'un autre package tiers intégré, ssl. Ajoutez les trois dans une nouvelle section libraries, en sélectionnant les dernières versions disponibles de ces packages, à app.yaml :

APRÈS :

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

Pour le module 21, nous avons ajouté Google Auth, Cloud NDB, Cloud Resource Manager et le SDK Firebase Admin à requirements.txt Python 3. La situation est plus complexe pour Python 2 :

  • L'API Resource Manager fournit la fonctionnalité allow-policy nécessaire à l'application exemple. Malheureusement, cette fonctionnalité n'était pas encore disponible dans la version finale Python 2 de la bibliothèque cliente Cloud Resource Manager. (Elle n'est disponible que dans la version Python 3.)
  • Par conséquent, une autre méthode d'accès à cette fonctionnalité à partir de l'API est nécessaire. La solution consiste à utiliser la bibliothèque cliente des API Google de niveau inférieur pour communiquer avec l'API. Pour passer à cette bibliothèque cliente, remplacez google-cloud-resource-manager par le package google-api-python-client de niveau inférieur.
  • Étant donné que Python 2 a été abandonné, le graphique de dépendances compatible avec le module 21 nécessite de verrouiller certains packages sur des versions spécifiques. Certains packages doivent être mentionnés même s'ils ne sont pas spécifiés dans le app.yaml Python 3.

AVANT :

flask

En commençant par le requirements.txt du module 20, mettez-le à jour comme suit pour obtenir une application du module 21 fonctionnelle :

APRÈS :

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

Les numéros de package et de version seront mis à jour dans le dépôt à mesure que les dépendances changeront, mais ce app.yaml suffit pour une application fonctionnelle au moment de la rédaction de cet article.

Autres mises à jour de configuration

Si vous n'avez pas supprimé le dossier lib plus tôt dans cet atelier de programmation, faites-le maintenant. Avec la version mise à jour de requirements.txt, exécutez cette commande habituelle pour installer ces exigences dans lib :

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

Si Python 2 et Python 3 sont tous les deux installés sur votre système de développement, vous devrez peut-être utiliser pip2 au lieu de pip.

Modifier le code de l'application

Heureusement, la plupart des modifications requises se trouvent dans les fichiers de configuration. La seule modification nécessaire dans le code de l'application est une mise à jour mineure pour utiliser la bibliothèque cliente de l'API Google de niveau inférieur au lieu de la bibliothèque cliente Resource Manager pour accéder à l'API. Aucune mise à jour n'est requise pour le modèle Web templates/index.html.

Mettre à jour les importations et l'initialisation

Remplacez la bibliothèque cliente Resource Manager (google.cloud.resourcemanager) par la bibliothèque cliente des API Google (googleapiclient.discovery), comme illustré ci-dessous :

AVANT :

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

APRÈS :

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

Assistance pour les utilisateurs d'App Engine Admin

Quelques modifications sont nécessaires dans _get_gae_admins() pour permettre l'utilisation de la bibliothèque cliente de niveau inférieur. Commençons par examiner les modifications, puis nous vous fournirons tout le code à mettre à jour.

Le code Python 2 nécessite l'utilisation des identifiants et de l'ID de projet renvoyés par google.auth.default(). Les identifiants n'étant pas utilisés dans Python 3, ils ont été attribués à une variable fictive générique de type underscore ( _). Étant donné qu'il est nécessaire pour la version Python 2, remplacez le trait de soulignement par CREDS. De plus, au lieu de créer un client d'API Resource Manager, vous allez créer un point de terminaison de service d'API, dont le concept est semblable à celui d'un client d'API. Nous conservons donc le même nom de variable (rm_client). Une différence est que l'instanciation d'un point de terminaison de service nécessite des identifiants (CREDS).

Ces modifications sont reflétées dans le code ci-dessous :

AVANT :

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

APRÈS :

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)

L'autre différence est que la bibliothèque cliente Resource Manager renvoie des objets de règles d'autorisation qui utilisent la notation par attributs avec points, tandis que la bibliothèque cliente de niveau inférieur renvoie des dictionnaires Python où des crochets ( [ ]) sont utilisés. Par exemple, utilisez binding.role pour la bibliothèque cliente Resource Manager et binding['role'] pour la bibliothèque de niveau inférieur. La première utilise également des noms "underscore_separated" (séparés par des traits de soulignement) alors que la bibliothèque de niveau inférieur préfère les noms "CamelCased" (en casse mixte) et une façon légèrement différente de transmettre les paramètres de l'API.

Ces différences d'utilisation sont indiquées ci-dessous :

AVANT :

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)

APRÈS :

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

En combinant toutes ces modifications, remplacez le _get_gae_admins() Python 3 par cette version Python 2 équivalente :

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

La fonction is_admin() ne nécessite aucune mise à jour, car elle s'appuie sur _get_gae_admins(), qui a déjà été mise à jour.

Les modifications requises pour rétrotransférer l'application du module 21 Python 3 vers Python 2 sont à présent terminées. Félicitations ! Vous êtes arrivé à l'application exemple du module 21 mise à jour. Vous trouverez tout le code dans le dossier du dépôt du module 21a.

7. Résumé/Nettoyage

Les dernières étapes de l'atelier de programmation consistent à s'assurer que les comptes principaux (utilisateurs ou comptes de service) exécutant cette application disposent des autorisations appropriées. Ensuite, déployez votre application pour vérifier qu'elle fonctionne comme prévu et que les modifications sont reflétées dans le résultat.

Permet de lire la stratégie d'autorisation IAM

Nous vous avons déjà présenté les quatre rôles requis pour être reconnu comme administrateur App Engine, mais il en existe désormais un cinquième à connaître :

  • roles/viewer
  • roles/editor
  • roles/owner
  • roles/appengine.appAdmin
  • roles/resourcemanager.projectIamAdmin (pour les comptes principaux qui accèdent à la stratégie d'autorisation IAM)

Le rôle roles/resourcemanager.projectIamAdmin permet aux comptes principaux de déterminer si un utilisateur final est membre de l'un des rôles d'administrateur App Engine. Si vous n'êtes pas membre de roles/resourcemanager.projectIamAdmin, les appels à l'API Cloud Resource Manager pour obtenir la stratégie d'autorisation échoueront.

Vous n'avez pas besoin d'effectuer d'action explicite ici, car votre application s'exécutera sous le compte de service par défaut d'App Engine, qui est automatiquement membre de ce rôle. Même si vous utilisez le compte de service par défaut pendant la phase de développement, nous vous recommandons vivement de créer et d'utiliser un compte de service géré par l'utilisateur doté des autorisations minimales requises pour que votre application fonctionne correctement. Pour accorder l'accès à un tel compte de service, exécutez la commande suivante :

$ 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 correspond à l'ID du projet Cloud et USR_MGD_SVC_ACCT@PROJ_ID.iam.gserviceaccount.com au compte de service géré par l'utilisateur que vous créez pour votre application. Cette commande génère la stratégie IAM mise à jour pour votre projet, dans laquelle vous pouvez vérifier que le compte de service est membre de roles/resourcemanager.projectIamAdmin. Pour en savoir plus, consultez la documentation de référence. Pour rappel, vous n'avez pas besoin d'exécuter cette commande dans cet atelier de programmation, mais enregistrez-la comme référence pour moderniser vos propres applications.

Déployer et vérifier l'application

Importez votre application dans le cloud à l'aide de la commande standard gcloud app deploy. Une fois déployée, vous devriez voir une fonctionnalité presque identique à l'application du module 20, sauf que vous avez remplacé le service App Engine Users par Cloud Identity Platform (et Firebase Auth) pour la gestion des utilisateurs :

3a83ae745121d70.png

Par rapport au module 20, vous remarquerez une différence : lorsque vous cliquez sur "Login" (Connexion), un pop-up s'affiche au lieu d'une redirection. Vous pouvez le voir sur certaines des captures d'écran ci-dessous. Comme pour le module 20, le comportement diffère légèrement selon le nombre de comptes Google enregistrés dans le navigateur.

Si aucun utilisateur n'est enregistré dans le navigateur ou si un seul utilisateur n'est pas encore connecté, un pop-up de connexion Google générique s'affiche :

8437f5f3d489a942.png

Si un seul utilisateur est enregistré dans votre navigateur, mais qu'il se connecte ailleurs, aucune boîte de dialogue ne s'affiche (ou elle s'affiche et se ferme immédiatement), et l'application passe à l'état connecté (l'adresse e-mail de l'utilisateur et le bouton Se déconnecter s'affichent).

Certains développeurs peuvent souhaiter fournir un sélecteur de compte, même pour un seul utilisateur :

b75624cb68d94557.png

Pour ce faire, supprimez les commentaires de la ligne provider.setCustomParameters({prompt: 'select_account'}); dans le modèle Web, comme décrit précédemment.

S'il y a plusieurs utilisateurs, la boîte de dialogue du sélecteur de compte s'affiche (voir ci-dessous). Si l'utilisateur n'est pas encore connecté, il sera invité à le faire. Si vous êtes déjà connecté, le pop-up disparaît et l'application passe à l'état connecté.

c454455b6020d5e4.png

L'état connecté du module 21 ressemble à l'interface utilisateur du module 20 :

49ebe4dcc1eff11f.png

Il en va de même lorsqu'un utilisateur administrateur s'est connecté :

44302f35b39856eb.png

Contrairement au module 21, le module 20 accède toujours à la logique du contenu du modèle Web à partir de l'application (code côté serveur). Le module 20 présente un défaut : une visite est enregistrée lorsque l'utilisateur final accède à l'application pour la première fois, et une autre est enregistrée lorsqu'il se connecte.

Pour le module 21, la logique de connexion se déroule uniquement dans le modèle Web (code côté client). Aucun aller-retour côté serveur n'est requis pour déterminer le contenu à afficher. Le seul appel effectué au serveur est la vérification des utilisateurs administrateurs après la connexion d'un utilisateur final. Cela signifie que les connexions et déconnexions n'enregistrent pas de visites supplémentaires. La liste des visites les plus récentes reste donc constante pour les actions de gestion des utilisateurs. Notez que les captures d'écran ci-dessus affichent le même ensemble de quatre visites pour plusieurs identifiants utilisateur.

Les captures d'écran du module 20 illustrent le "bug de double visite" au début de cet atelier de programmation. Des journaux de visites distincts s'affichent pour chaque action de connexion ou de déconnexion. Vérifiez les codes temporels de la visite la plus récente pour chaque capture d'écran afin de vérifier l'ordre chronologique.

Effectuer un nettoyage

Général

Si vous avez terminé pour le moment, nous vous recommandons de désactiver votre application App Engine pour éviter d'être facturé. Toutefois, si vous souhaitez effectuer d'autres tests ou expériences, la plate-forme App Engine dispose d'un quota sans frais. Tant que vous ne dépassez pas ce niveau d'utilisation, aucun frais ne devrait vous être facturé. Cela concerne le calcul, mais des frais peuvent également s'appliquer aux services App Engine concernés. Pour en savoir plus, consultez la page de tarification. Si cette migration implique d'autres services Cloud, ceux-ci sont facturés séparément. Dans les deux cas, le cas échéant, consultez la section "Spécifique à cet atelier de programmation" ci-dessous.

Pour être tout à fait transparent, le déploiement sur une plate-forme de calcul sans serveur Google Cloud comme App Engine entraîne de légers coûts de compilation et de stockage. Cloud Build et Cloud Storage disposent chacun de leur propre quota sans frais. Le stockage de cette image utilise une partie de ce quota. Toutefois, il est possible que vous résidiez dans une région où ce niveau sans frais n'est pas disponible. Veillez donc à surveiller votre utilisation de l'espace de stockage pour minimiser les coûts potentiels. Voici quelques "dossiers" Cloud Storage spécifiques que vous devez examiner :

  • 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
  • Les liens de stockage ci-dessus dépendent de votre PROJECT_ID et de votre LOC, par exemple "us" si votre application est hébergée aux États-Unis.

En revanche, si vous ne souhaitez pas poursuivre avec cette application ni avec d'autres ateliers de programmation de migration associés et que vous souhaitez tout supprimer complètement, arrêtez votre projet.

Spécifique à cet atelier de programmation

Les services listés ci-dessous sont propres à cet atelier de programmation. Pour en savoir plus, consultez la documentation de chaque produit :

  • Le service App Engine Datastore est fourni par Cloud Datastore (Cloud Firestore en mode Datastore), qui propose également un niveau sans frais. Pour en savoir plus, consultez sa page de tarification.
  • L'utilisation de la plate-forme Cloud Identity est sans frais dans une certaine mesure, selon les services que vous utilisez. Pour en savoir plus, consultez la page des tarifs.
  • L'utilisation de l'API Cloud Resource Manager est sans frais dans la plupart des cas, comme indiqué sur la page des tarifs.

Étapes suivantes

Au-delà de ce tutoriel, voici d'autres modules de migration qui se concentrent sur l'abandon des anciens services groupés :

  • Module 2 : migrer depuis App Engine ndb vers Cloud NDB
  • Modules 7 à 9 : migrer d'App Engine Task Queue (tâches push) vers Cloud Tasks
  • Modules 12 et 13 : migrer d'App Engine Memcache vers Cloud Memorystore
  • Modules 15 et 16 : migrer d'App Engine Blobstore vers Cloud Storage
  • Modules 18 et 19 : migrer depuis la file d'attente de tâches App Engine (tâches de retrait) vers Cloud Pub/Sub

App Engine n'est plus la seule plate-forme sans serveur de Google Cloud. Si vous avez une petite application App Engine ou une application dont les fonctionnalités sont limitées et que vous souhaitez la transformer en microservice autonome, ou si vous souhaitez diviser une application monolithique en plusieurs composants réutilisables, ce sont de bonnes raisons d'envisager de passer à Cloud Functions. Si la conteneurisation fait désormais partie de votre workflow de développement d'applications, en particulier s'il s'agit d'un pipeline CI/CD (intégration continue/livraison ou déploiement continus), envisagez de migrer vers Cloud Run. Ces scénarios sont couverts par les modules suivants :

  • Migrer d'App Engine vers Cloud Functions : consultez le module 11.
  • Migrer d'App Engine vers Cloud Run : consultez le module 4 pour conteneuriser votre application avec Docker, ou le module 5 pour le faire sans conteneurs, sans connaissances sur Docker ni Dockerfile.

Le passage à une autre plate-forme serverless est facultatif. Nous vous recommandons d'examiner les meilleures options pour vos applications et vos cas d'utilisation avant d'apporter des modifications.

Quel que soit le module de migration que vous envisagez ensuite, vous pouvez accéder à l'ensemble du contenu Serverless Migration Station (ateliers de programmation, vidéos, code source [le cas échéant]) dans son dépôt Open Source. Le README du dépôt fournit également des conseils sur les migrations à envisager et sur l'"ordre" pertinent des modules de migration.

8. Ressources supplémentaires

Vous trouverez ci-dessous des ressources supplémentaires pour les développeurs qui souhaitent en savoir plus sur ce module de migration ou sur d'autres modules associés. Vous trouverez ci-dessous des liens vers le code et diverses ressources de documentation qui pourraient vous être utiles. Vous pouvez également nous faire part de vos commentaires sur ce contenu.

Problèmes/commentaires concernant les ateliers de programmation

Si vous rencontrez des problèmes avec cet atelier de programmation, commencez par faire une recherche avant de les signaler. Liens vers la recherche et la création d'un signalement :

Ressources de migration

Vous trouverez des liens vers les dossiers des dépôts du module 20 (code de départ "START") et du module 21 (code final "FINISH") dans le tableau ci-dessous.

Atelier de programmation

Python 2

Python 3

Module 20

code

(n/a)

Module 21 (cet atelier de programmation)

code

code

Références en ligne

Vous trouverez ci-dessous des ressources utiles pour ce tutoriel :

Cloud Identity Platform et Cloud Marketplace

Cloud Resource Manager, Cloud IAM, SDK Admin Firebase

Utilisateurs App Engine, App Engine NDB, Cloud NDB, Cloud Datastore

Autres références du module de migration

Migration App Engine

Plate-forme App Engine

SDK Cloud

Informations sur les autres clouds

Vidéos

Licence

Ce document est publié sous une licence Creative Commons Attribution 2.0 Generic.