Migra del servicio de usuarios de App Engine a Cloud Identity Platform (módulo 21)

1. Descripción general

El objetivo de la serie de codelabs de Serverless Migration Station (instructivos prácticos y de autoaprendizaje) y los videos relacionados es ayudar a los desarrolladores de Google Cloud sin servidores a modernizar sus aplicaciones guiándolos a través de una o más migraciones, principalmente alejándose de los servicios heredados. De esta manera, tus apps serán más portátiles y tendrás más opciones y flexibilidad, lo que te permitirá integrar y acceder a una mayor variedad de productos de Cloud, y actualizarte más fácilmente a versiones de lenguaje más recientes. Si bien, en un principio, se enfoca en los primeros usuarios de Cloud, principalmente los desarrolladores de App Engine (entorno estándar), esta serie es lo suficientemente amplia como para incluir otras plataformas sin servidores, como Cloud Functions y Cloud Run, o en cualquier otro lugar si corresponde.

El objetivo de este codelab es mostrar a los desarrolladores de App Engine con Python 2 cómo migrar de la API o el servicio de usuarios de App Engine a Cloud Identity Platform (GCIP). También hay una migración implícita de App Engine NDB a Cloud NDB para el acceso a Datastore (que se aborda principalmente en el módulo de migración 2), así como una actualización a Python 3.

El módulo 20 abarca cómo agregar el uso de la API de Users a la app de muestra del módulo 1. En este módulo, tomarás la app terminada del módulo 20 y migrarás su uso a Cloud Identity Platform.

En un próximo lab,

  • Reemplaza el uso del servicio de usuarios de App Engine por Cloud Identity Platform
  • Reemplaza el uso de App Engine NDB por Cloud NDB (consulta también el módulo 2)
  • Configura diferentes proveedores de identidad de autenticación con Firebase Auth
  • Usa la API de Cloud Resource Manager para obtener información sobre IAM del proyecto
  • Usa el SDK de Firebase Admin para obtener información del usuario
  • Porta la aplicación de ejemplo a Python 3

Requisitos

Encuesta

¿Cómo usarás este instructivo?

Solo lo leeré Lo leeré y completaré los ejercicios

¿Cómo calificarías tu experiencia en Python?

Principiante Intermedio Avanzado

¿Cómo calificarías tu experiencia en el uso de los servicios de Google Cloud?

Principiante Intermedio Avanzado

2. Fondo

El servicio de usuarios de App Engine es un sistema de autenticación de usuarios que se utiliza en las apps de App Engine. Proporciona Acceso con Google como proveedor de identidad, vínculos convenientes para acceder y salir de las aplicaciones, y admite el concepto de usuarios administradores y funcionalidad solo para administradores. Para mejorar la portabilidad de las aplicaciones, Google Cloud recomienda migrar de los servicios heredados en paquetes de App Engine a los servicios independientes de Cloud, por ejemplo, del servicio de Users a Cloud Identity Platform, entre otros.

Identity Platform se basa en Firebase Authentication y agrega una serie de funciones empresariales, como la autenticación de varios factores, la compatibilidad con el SSO de OIDC y SAML, la arquitectura multiusuario, el ANS del 99.95% y mucho más. Estas diferencias también se destacan en la página de comparación de productos de Identity Platform y Firebase Authentication. Ambos productos tienen muchas más funciones que la funcionalidad que proporciona el servicio de usuarios.

En este codelab del módulo 21, se muestra cómo cambiar la autenticación de usuarios de la app del servicio de Usuarios a las funciones de Identity Platform que reflejan más de cerca la funcionalidad que se muestra en el módulo 20. El módulo 21 también incluye una migración de App Engine NDB a Cloud NDB para el acceso a Datastore, lo que repite la migración del módulo 2.

Si bien el código del módulo 20 se "anuncia" como una app de ejemplo de Python 2, la fuente en sí es compatible con Python 2 y 3, y sigue siendo así incluso después de migrar a Identity Platform (y Cloud NDB) aquí en el módulo 21. Es posible seguir usando el servicio de Users mientras actualizas a Python 3, ya que la migración a Identity Platform es opcional. Consulta el codelab del módulo 17 y el video para obtener información sobre cómo seguir usando los servicios agrupados en paquetes mientras actualizas a entornos de ejecución de segunda generación, como Python 3.

En este instructivo, se incluyen los siguientes pasos:

  1. Configurar/trabajo previo
  2. Actualizar configuración
  3. Modifica el código de la aplicación

3. Configurar/trabajo previo

Esta sección explica cómo:

  1. Configura el proyecto de Cloud
  2. Obtén app de ejemplo del modelo de referencia
  3. (Vuelve a) implementar y validar la app de referencia
  4. Habilita las nuevas APIs o los nuevos servicios de Google Cloud

Estos pasos garantizan que comiences con un código funcional que esté listo para la migración a servicios de Cloud independientes.

1. Configura el proyecto

Si completaste el codelab del módulo 20, vuelve a usar ese mismo proyecto (y el código). De manera alternativa, puedes crear un proyecto nuevo o reutilizar otro proyecto existente. Asegúrate de que el proyecto tenga una cuenta de facturación activa y una app de App Engine habilitada. Busca el ID de tu proyecto y tenlo a mano durante este codelab. Úsalo cada vez que encuentres la variable PROJ_ID.

2. Obtén app de ejemplo del modelo de referencia

Uno de los requisitos previos es una app de App Engine del módulo 20 que funcione, por lo que debes completar su codelab (recomendado; vínculo arriba) o copiar el código del módulo 20 del repositorio. Sin importar si usas el tuyo o el nuestro, aquí es donde comenzaremos ("START"). Este codelab te guía por la migración y concluye con un código que se parece al que se encuentra en la carpeta del repositorio del módulo 21 (“FINISH”).

Copia la carpeta del repo del módulo 20. Debería verse como el siguiente resultado y, posiblemente, tener una carpeta lib si realizaste el codelab del módulo 20:

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

3. (Vuelve a) implementar y validar la app de referencia

Sigue estos pasos para implementar la app del módulo 20:

  1. Borra la carpeta lib si hay una y ejecuta pip install -t lib -r requirements.txt para volver a llenarla. Es posible que debas usar pip2 si tienes instalados Python 2 y 3.
  2. Asegúrate de haber instalado y inicializado la herramienta de línea de comandos de gcloud, y de haber revisado su uso.
  3. Si no quieres ingresar tu PROJ_ID con cada comando gcloud que se emita, primero configura el proyecto de Cloud con gcloud config set project PROJ_ID.
  4. Implementa la app de ejemplo con gcloud app deploy
  5. Confirma que la app se ejecute según lo previsto y sin errores. Si completaste el codelab del módulo 20, la app mostrará la información de acceso del usuario (correo electrónico del usuario, posible "insignia de administrador" y botón de acceso o cierre de sesión) en la parte superior junto con las visitas más recientes (como se ilustra a continuación).

907e64c19ef964f8.png

Si accedes como usuario normal, se mostrará tu dirección de correo electrónico y el botón "Acceder" cambiará a "Salir":

ad7b59916b69a035.png

Cuando se accede como usuario administrador, se muestra la dirección de correo electrónico del usuario junto con "(administrador)":

867bcb3334149e4.png

4. Habilita las nuevas APIs o servicios de Google Cloud

Introducción

La app del módulo 20 usa las APIs de NDB y de Users de App Engine, servicios agrupados que no requieren configuración adicional, pero los servicios independientes de Cloud sí, y la app actualizada empleará tanto Cloud Identity Platform como Cloud Datastore (a través de la biblioteca cliente de Cloud NDB). Además, nuestra necesidad de determinar los usuarios administradores de App Engine también requiere el uso de la API de Cloud Resource Manager.

Costo

  • App Engine y Cloud Datastore tienen cuotas del nivel "Siempre gratuito", por lo que, siempre que te mantengas dentro de esos límites, no deberías incurrir en cargos por completar este instructivo. Consulta también la página de precios de App Engine y la página de precios de Cloud Datastore para obtener más detalles.
  • El uso de Cloud Identity Platform se factura según la cantidad de usuarios activos por mes (MAU) o verificaciones de autenticación. Hay disponible una versión "gratuita" para cada modelo de uso. Consulta su página de precios para obtener más detalles. Además, si bien App Engine y Cloud Datastore requieren facturación, el uso de GCIP por sí solo no requiere que se habilite la facturación, siempre y cuando no excedas sus cuotas diarias sin instrumentación, por lo que debes tener en cuenta esto para los proyectos de Cloud que no involucran APIs o servicios de Cloud que requieren facturación.
  • El uso de la API de Cloud Resource Manager es gratuito en su mayor parte, según su página de precios.

Los usuarios habilitan las APIs de Cloud desde la consola de Cloud o desde la línea de comandos (a través del comando gcloud, que forma parte del SDK de Cloud), según sus preferencias. Comencemos con las APIs de Cloud Datastore y Cloud Resource Manager.

Desde la consola de Cloud

Ve a la página de la biblioteca del Administrador de APIs (para el proyecto correcto) en la consola de Cloud y busca una API con la barra de búsqueda. c7a740304e9d35b.png

Habilita estas APIs:

Busca y haz clic en el botón Habilitar para cada API por separado. Es posible que se te solicite información de facturación. Por ejemplo, esta es la página de la API de Resource Manager:

fc7bd8f4c49d12e5.png

El botón cambia a Administrar cuando se habilita (generalmente, después de unos segundos):

8eca12d6cc7b45b0.png

Habilita Cloud Datastore de la misma manera:

83811599b110e46b.png

Desde la línea de comandos

Si bien es visualmente informativo habilitar las APIs desde la consola, algunas personas prefieren la línea de comandos. Además, puedes habilitar la cantidad de APIs que quieras a la vez. Ejecuta este comando para habilitar las APIs de Cloud Datastore y Cloud Resource Manager, y espera a que se complete la operación, como se ilustra aquí:

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

Es posible que se te solicite información de facturación.

Las "URLs" de cada API que se usa en el comando anterior se denominan nombres de servicio de la API y se pueden encontrar en la parte inferior de la página de la biblioteca de cada API. Si deseas habilitar otras APIs de Cloud para tus propias apps, puedes encontrar sus respectivos nombres de servicio en las páginas de las APIs correspondientes. Este comando enumera todos los nombres de los servicios para las APIs que puedes habilitar:

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

Ya sea en la consola de Cloud o en la línea de comandos, una vez que hayas completado los pasos anteriores, nuestra muestra podrá acceder a esas APIs. Los siguientes pasos son habilitar Cloud Identity Platform y realizar los cambios de código necesarios.

Habilita y configura Cloud Identity Platform (solo en la consola de Cloud)

Cloud Identity Platform es un servicio de Marketplace porque se conecta a un recurso fuera de Google Cloud o depende de él, por ejemplo, Firebase Authentication. Por el momento, solo puedes habilitar los servicios de Marketplace desde la consola de Cloud. Sigue estos pasos:

  1. Ve a la página de Cloud Identity Platform en Cloud Marketplace y haz clic en el botón Habilitar. Actualiza Firebase Authentication si se te solicita. Esto desbloquea funciones adicionales, como las que se describieron anteriormente en la sección Antecedentes. Esta es la página de Marketplace en la que se destaca el botón Habilitar: 28475f1c9b29de69.png
  2. Una vez que se habilite Identity Platform, es posible que se te dirija automáticamente a la página Proveedores de identidad. Si no es así, usa este vínculo para llegar. fc2d92d42a5d1dd7.png
  3. Habilita el proveedor de Google Auth. Si no se configuró ningún proveedor, haz clic en Agregar un proveedor y selecciona Google. Cuando vuelvas a esta pantalla, la entrada de Google debería estar habilitada. Google es el único proveedor de autenticación que usamos en este instructivo para replicar el servicio de usuarios de App Engine como un servicio ligero de Acceso con Google. En tus propias apps, puedes habilitar proveedores de autenticación adicionales.
  4. Cuando hayas seleccionado y configurado Google y otros proveedores de autenticación deseados, haz clic en Application Setup Details y, en la ventana de diálogo que aparecerá, copia apiKey y authDomain en el objeto config de la pestaña Web y guárdalos en un lugar seguro. ¿Por qué no copiar todo? El fragmento de este diálogo está codificado y fechado, por lo que solo debes guardar los fragmentos más importantes y usarlos en nuestro código con un uso más simultáneo de Firebase Authentication. Una vez que hayas copiado los valores y los hayas guardado en un lugar seguro, haz clic en el botón Cerrar para completar toda la configuración necesaria. bbb09dcdd9be538e.png

4. Actualizar configuración

Las actualizaciones en la configuración incluyen tanto el cambio de varios archivos de configuración como la creación del equivalente de App Engine, pero dentro del ecosistema de Cloud Identity Platform.

appengine_config.py

  • Si actualizas a Python 3, borra appengine_config.py.
  • Si planeas modernizarte a Identity Platform, pero mantener Python 2, no borres el archivo. En cambio, la actualizaremos más adelante durante la adaptación a Python 2.

requirements.txt

El archivo requirements.txt del módulo 20 solo enumeraba Flask. Para el módulo 21, agrega los siguientes paquetes:

El contenido de requirements.txt debería verse de la siguiente manera:

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

app.yaml

  • Actualizar a Python 3 significa simplificar el archivo app.yaml. Quita todo, excepto la directiva de tiempo de ejecución, y configúrala en una versión de Python 3 compatible actualmente. Actualmente, el ejemplo usa la versión 3.10.
  • Si vas a seguir usando Python 2, no realices ninguna acción aquí todavía.

ANTES:

runtime: python27
threadsafe: yes
api_version: 1

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

La app de ejemplo del módulo 20 no tiene controladores de archivos estáticos. Si tus apps lo hacen, déjalas intactas. Puedes quitar todos los controladores de secuencias de comandos si lo deseas o simplemente dejarlos allí como referencia, siempre y cuando cambies sus identificadores a auto, como se describe en la guía de migración de app.yaml. Con estos cambios, el app.yaml actualizado para Python 3 se simplifica de la siguiente manera:

DESPUÉS:

runtime: python310

Otras actualizaciones de configuración

Ya sea que te quedes en Python 2 o que transfieras a Python 3, si tienes una carpeta lib, bórrala.

5. Modifica el código de la aplicación

En esta sección, se incluyen actualizaciones del archivo principal de la aplicación, main.py, que reemplazan el uso del servicio de usuarios de App Engine por Cloud Identity Platform. Después de actualizar la aplicación principal, actualizarás la plantilla web, templates/index.html.

Actualizar las importaciones y la inicialización

Sigue los pasos que se indican a continuación para actualizar las importaciones y, luego, inicializar los recursos de la aplicación:

  1. En el caso de las importaciones, reemplaza App Engine NDB por Cloud NDB.
  2. Junto con Cloud NDB, también importa Cloud Resource Manager.
  3. Identity Platform se basa en Firebase Auth, por lo que debes importar el SDK de Firebase Admin.
  4. Las APIs de Cloud requieren el uso de un cliente de API, por lo que debes iniciarlo para Cloud NDB justo después de inicializar Flask.

Si bien el paquete de Cloud Resource Manager se importa aquí, lo usaremos en una etapa posterior de la inicialización de la app. A continuación, se muestran las importaciones y la inicialización del módulo 20, seguidas de cómo deberían verse las secciones después de implementar los cambios anteriores:

ANTES:

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

app = Flask(__name__)

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

Compatibilidad con usuarios administradores de App Engine

Hay dos componentes que se deben agregar a la app para que admita el reconocimiento de usuarios administradores:

  • _get_gae_admins(): Recopila el conjunto de usuarios administradores; se llama una vez y se guarda.
  • is_admin(): Verifica si el usuario que accedió es un usuario administrador. Se llama en cualquier acceso de usuario.

La función de utilidad, _get_gae_admins(), llama a la API de Resource Manager para recuperar la política de Cloud IAM de permiso actual. La política de permiso define y aplica qué roles se otorgan a qué principales (usuarios humanos, cuentas de servicio, etcétera). La configuración incluye lo siguiente:

  • Cómo recuperar el ID del proyecto de Cloud (PROJ_ID)
  • Cómo crear un cliente de la API de Resource Manager (rm_client)
  • Crear un conjunto (de solo lectura) de roles de administrador de App Engine (_TARGETS)

Resource Manager requiere el ID del proyecto de Cloud, por lo que debes importar google.auth.default() y llamar a esa función para obtener el ID del proyecto. Esa llamada incluye un parámetro que parece una URL, pero es un alcance de permiso de OAuth2. Cuando ejecutas apps en la nube, por ejemplo, en una VM de Compute Engine o una app de App Engine, se proporciona una cuenta de servicio predeterminada que tiene privilegios amplios. Para seguir la práctica recomendada de privilegio mínimo, te sugerimos que crees tus propias cuentas de servicio administradas por el usuario.

En el caso de las llamadas a la API, lo mejor es reducir aún más el alcance de tus apps al nivel mínimo necesario para que funcionen correctamente. La llamada a la API de Resource Manager que realizaremos es get_iam_policy(), que necesita uno de los siguientes permisos para operar:

  • 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

La app de ejemplo solo necesita acceso de solo lectura a la política de permisos. No modifica la política ni necesita acceso a todo el proyecto. Esto significa que la app no necesita ninguno de los tres primeros permisos. La última es la única que se requiere, y es la que implementaremos para la app de ejemplo.

El cuerpo principal de la función crea un conjunto vacío de usuarios administradores (admins), recupera el allow_policy a través de get_iam_policy() y realiza un bucle en todas sus vinculaciones en busca específicamente de roles de administrador de App Engine:

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

Para cada rol objetivo que se encuentra, se recopila qué usuarios pertenecen a ese rol y se los agrega al conjunto general de usuarios administradores. Finaliza devolviendo todos los usuarios administradores encontrados y almacenados en caché como una constante (_ADMINS) durante la vida útil de esta instancia de App Engine. Veremos esa llamada en breve.

Agrega la siguiente definición de función _get_gae_admins() a main.py justo debajo de la instancia del cliente de la API de 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

Cuando los usuarios acceden a la app, ocurre lo siguiente:

  1. Se realiza una verificación rápida desde la plantilla web después de que un usuario accede a Firebase.
  2. Cuando cambia el estado de autenticación en la plantilla, se realiza una llamada fetch() de estilo Ajax a /is_admin, cuyo controlador es la siguiente función, is_admin().
  3. El token de ID de Firebase se pasa en el cuerpo de la solicitud POST a is_admin(), que lo toma de los encabezados y llama al SDK de Firebase Admin para validarlo. Si es un usuario válido, extrae su dirección de correo electrónico y verifica si es un usuario administrador.
  4. Luego, el resultado booleano se devuelve a la plantilla como un 200 exitoso.

Agrega is_admin() a main.py justo después de _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

Todo el código de ambas funciones es necesario para replicar la funcionalidad disponible en el servicio de Users, específicamente su función is_current_user_admin(). Esta llamada a función en el módulo 20 hizo todo el trabajo pesado, a diferencia del módulo 21, en el que implementamos una solución de reemplazo. La buena noticia es que la app ya no depende de un servicio exclusivo de App Engine, lo que significa que puedes migrar tus apps a Cloud Run o a otros servicios. Además, también puedes cambiar la definición de "usuario administrador" para tus propias apps con solo cambiar a los roles deseados en _TARGETS, mientras que el servicio de Usuarios está codificado de forma rígida para los roles de administrador de App Engine.

Inicializa Firebase Auth y almacena en caché a los usuarios administradores de App Engine

Podríamos haber inicializado Firebase Authentication en la parte superior, cerca del mismo lugar en el que se inicializa la app de Flask y se crea el cliente de la API de Cloud NDB, pero no era necesario hasta que se definió todo el código de administrador, que es donde estamos ahora. Del mismo modo, ahora que se definió _get_gae_admins(), llámalo para almacenar en caché la lista de usuarios administradores.

Agrega estas líneas justo debajo del cuerpo de la función de is_admin():

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

Actualizaciones del modelo de datos de visitas

El modelo de datos de Visit no cambia. El acceso a Datastore requiere el uso explícito del administrador de contexto del cliente de la API de Cloud NDB, ds_client.context(). En el código, esto significa que debes encapsular las llamadas a Datastore en store_visit() y fetch_visits() dentro de los bloques with de Python. Esta actualización es idéntica al módulo 2. Realiza los cambios de la siguiente manera:

ANTES:

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)

DESPUÉ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)

Mueve la lógica de acceso del usuario a la plantilla web

El servicio de Usuarios de App Engine se ejecuta en el servidor, mientras que Firebase Auth y Cloud Identity Platform se ejecutan principalmente en el cliente. Como resultado, gran parte del código de administración de usuarios de la app del módulo 20 se traslada a la plantilla web del módulo 21.

En main.py, el contexto web pasa cinco elementos de datos esenciales a la plantilla. Los primeros cuatro que se mencionan están vinculados a la administración de usuarios y difieren según si el usuario accedió a su cuenta o no:

  • who: Es el correo electrónico del usuario si accedió o user en caso contrario.
  • admin: Distintivo de (administrador) si el usuario que accedió es administrador
  • sign: Muestra el botón Acceder o Salir.
  • link: Vínculos de acceso o salida al hacer clic en el botón
  • visits: Visitas más recientes

ANTES:

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

Toda la administración de usuarios se está trasladando a la plantilla web, por lo que solo nos quedan las visitas, lo que hace que el controlador principal vuelva a ser lo que teníamos en la app del módulo 1:

DESPUÉ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)

Actualiza la plantilla web

¿Cómo se ven todas las actualizaciones de la sección anterior en la plantilla? Principalmente, se trasladó la administración de usuarios de la app a Firebase Auth, que se ejecuta en la plantilla, y se realizó un port parcial de todo el código que trasladamos a JavaScript. Vimos que main.py se redujo bastante, por lo que esperamos un crecimiento similar en templates/index.html.

ANTES:

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

Reemplaza toda la plantilla web por el siguiente contenido:

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

Hay muchos componentes en este cuerpo HTML, así que analicemos cada uno por separado.

Importaciones de Firebase

Mientras sigues en el encabezado del documento HTML, una vez que pases el título de la página, importa los componentes de Firebase necesarios. Ahora, los componentes de Firebase se dividen en varios módulos para mejorar la eficiencia. El código para inicializar Firebase se importa desde el módulo principal de la app de Firebase, mientras que las funciones que administran la autenticación de Firebase, Google como proveedor de autenticación, el acceso y el cierre de sesión, y la devolución de llamada del cambio de estado de autenticación se importan desde el módulo de 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";

Configuración de Firebase

Anteriormente, durante la parte de configuración de Identity Platform de este instructivo, guardaste apiKey y authDomain del diálogo Detalles de configuración de la aplicación. Agrega esos valores a la variable firebaseConfig en la siguiente sección. En los comentarios, se proporciona un vínculo a instrucciones más detalladas:

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

Inicialización de Firebase

En la siguiente sección, se inicializa Firebase con esta información de configuración.

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

Esto establece la capacidad de usar Google como proveedor de autenticación y proporciona una opción comentada para mostrar el selector de cuentas incluso si solo hay una cuenta de Google registrada en la sesión del navegador. En otras palabras, cuando tienes varias cuentas, se te presenta este "selector de cuentas" como se espera: a38369389b7c4c7e.png Sin embargo, si solo hay un usuario en la sesión, el proceso de acceso se completa automáticamente sin interacción del usuario. (La ventana emergente aparece y, luego, desaparece). Puedes forzar la aparición del diálogo del selector de cuentas para un usuario (en lugar de acceder inmediatamente a la app) quitando la marca de comentario de la línea del parámetro personalizado. Si está habilitado, incluso los inicios de sesión de un solo usuario muestran el selector de cuentas: b75624cb68d94557.png

Funciones de acceso y cierre de sesión

Las siguientes líneas de código componen las funciones para los clics en los botones de acceso o salida:

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

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

Acciones de acceso y cierre de sesión

La última sección principal de este bloque <script> es la función que se llama para cada cambio de autenticación (acceso o cierre de sesión).

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

Aquí se realiza la transición del código del módulo 20 que determina si se debe enviar un contexto de plantilla de "usuario conectado" o un contexto de "usuario desconectado". La condición en la parte superior genera true si el usuario accedió correctamente, lo que activa las siguientes acciones:

  1. Se configuró la dirección de correo electrónico del usuario para que se muestre.
  2. El botón Acceder cambia a Salir.
  3. Se realiza una llamada de estilo Ajax a /is_admin para determinar si se debe mostrar la insignia de usuario administrador (admin).

Cuando el usuario sale de su cuenta, se ejecuta la cláusula else para restablecer toda la información del usuario:

  1. El nombre de usuario se configuró como user
  2. Se quitó cualquier insignia de administrador
  3. El botón Salir volvió a ser Acceder

Variables de plantilla

Después de que finaliza la sección del encabezado, el cuerpo principal comienza con las variables de plantilla que se reemplazan por elementos HTML que cambian según sea necesario:

  1. Nombre de usuario que se muestra
  2. Insignia de administrador (admin) (si corresponde)
  3. Botón de Acceder o Salir
<body>
<p>
Welcome, <span id="who"></span> <span id="admin"><code>(admin)</code></span>
<button id="logbtn"></button>
</p><hr>

Visitas más recientes y variables de elementos HTML

El código de visitas más recientes no cambia, y el bloque <script> final establece las variables para los elementos HTML que cambian para el acceso y el cierre de sesión que se enumeran justo arriba:

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

Con esto, se completan los cambios necesarios en la aplicación y la plantilla web para cambiar de las APIs de App Engine NDB y Users a Cloud NDB y Identity Platform, además de actualizar a Python 3. ¡Felicitaciones por llegar a la nueva app de ejemplo del módulo 21! Nuestra versión está disponible para su revisión en la carpeta del repo del módulo 21b.

La siguiente parte del codelab es opcional (*) y solo para los usuarios cuyas apps deben permanecer en Python 2, y te guía por los pasos necesarios para llegar a una app de Python 2 del módulo 21 que funcione.

6. *Backport de Python 2

Esta sección opcional es para los desarrolladores que realizan una migración a Identity Platform, pero que deben seguir ejecutándose en el entorno de ejecución de Python 2. Si esto no te preocupa, omite esta sección.

Para crear una versión de Python 2 que funcione de la app del Módulo 21, necesitas lo siguiente:

  1. Requisitos de tiempo de ejecución: Archivos de configuración que admiten Python 2 y cambios necesarios en la aplicación principal para evitar incompatibilidades con Python 3
  2. Cambio menor en la biblioteca: Python 2 dejó de estar disponible antes de que se agregaran algunas funciones requeridas a la biblioteca cliente de Resource Manager. Por lo tanto, necesitas una forma alternativa de acceder a esa funcionalidad faltante.

Realicemos esos pasos ahora, comenzando con la configuración.

Restablece appengine_config.py

Anteriormente en este instructivo, se te indicó que borraras appengine_config.py, ya que no lo usa el entorno de ejecución de Python 3 de App Engine. En el caso de Python 2, no solo se debe conservar, sino que también se debe actualizar el módulo 20 appengine_config.py para admitir el uso de bibliotecas integradas de terceros, es decir, grpcio y setuptools. Estos paquetes son obligatorios siempre que tu app de App Engine use bibliotecas cliente de Cloud, como las de Cloud NDB y Cloud Resource Manager.

Agregarás esos paquetes a app.yaml en un momento, pero, para que tu app acceda a ellos, se debe llamar a la función pkg_resources.working_set.add_entry() desde setuptools. Esto permite que las bibliotecas de terceros copiadas (incluidas o vendidas) instaladas en la carpeta lib puedan comunicarse con las bibliotecas integradas.

Implementa las siguientes actualizaciones en tu archivo appengine_config.py para aplicar estos cambios:

ANTES:

from google.appengine.ext import vendor

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

Este código por sí solo no es suficiente para admitir el uso de setuptools y grpcio. Se necesitan algunas líneas más, por lo que debes actualizar appengine_config.py para que se vea de la siguiente manera:

DESPUÉ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)

En la documentación sobre la migración de servicios agrupados en paquetes, encontrarás más detalles sobre los cambios necesarios para admitir las bibliotecas cliente de Cloud.

app.yaml

Al igual que con appengine_config.py, el archivo app.yaml debe revertirse a uno que admita Python 2. Comencemos con el módulo 20 original app.yaml:

ANTES:

runtime: python27
threadsafe: yes
api_version: 1

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

Además de setuptools y grpcio, como se mencionó anteriormente, hay una dependencia (no relacionada explícitamente con la migración de Identity Platform) que requiere el uso de la biblioteca cliente de Cloud Storage, y esta necesita otro paquete integrado de terceros, ssl. Agrega los tres en una nueva sección de libraries, seleccionando las versiones disponibles más recientes de esos paquetes, a app.yaml:

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

En el módulo 21, agregamos Google Auth, Cloud NDB, Cloud Resource Manager y Firebase Admin SDK a requirements.txt de Python 3. La situación de Python 2 es más compleja:

  • La API de Resource Manager proporciona la funcionalidad de política de permisos necesaria para la app de ejemplo. Lamentablemente, esta compatibilidad aún no estaba disponible en la versión final de Python 2 de la biblioteca cliente de Cloud Resource Manager. (Solo está disponible en la versión de Python 3).
  • Por lo tanto, se requiere una forma alternativa de acceder a esta función desde la API. La solución es usar la biblioteca cliente de las APIs de Google de nivel inferior para comunicarse con la API. Para cambiar a esta biblioteca cliente, reemplaza google-cloud-resource-manager por el paquete google-api-python-client de nivel inferior.
  • Debido a que Python 2 dejó de estar disponible, el gráfico de dependencias que admite el módulo 21 requiere bloquear ciertos paquetes en versiones específicas. Algunos paquetes deben mencionarse incluso si no se especifican en app.yaml de Python 3.

ANTES:

flask

Comenzando con el requirements.txt del módulo 20, actualízalo a lo siguiente para que funcione la app del módulo 21:

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

Los números de paquete y versión se actualizarán en el repo a medida que cambien las dependencias, pero este app.yaml es suficiente para una app que funcione en el momento de escribir este artículo.

Otras actualizaciones de configuración

Si no borraste la carpeta lib de antes en este codelab, hazlo ahora. Con el requirements.txt recién actualizado, ejecuta este comando conocido para instalar estos requisitos en lib:

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

Si tienes instalados Python 2 y 3 en tu sistema de desarrollo, es posible que debas usar pip2 en lugar de pip.

Modifica el código de la aplicación

Por suerte, la mayoría de los cambios necesarios se encuentran en los archivos de configuración. El único cambio necesario en el código de la aplicación es una actualización menor para usar la biblioteca cliente de la API de Google de nivel inferior en lugar de la biblioteca cliente de Resource Manager para acceder a la API. No se requieren actualizaciones para la plantilla web de templates/index.html.

Actualizar las importaciones y la inicialización

Reemplaza la biblioteca cliente de Resource Manager (google.cloud.resourcemanager) por la biblioteca cliente de las APIs de Google (googleapiclient.discovery), como se ilustra a continuación:

ANTES:

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

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

Compatibilidad con usuarios administradores de App Engine

Se necesitan algunos cambios en _get_gae_admins() para admitir el uso de la biblioteca cliente de nivel inferior. Primero, analicemos los cambios y, luego, te proporcionaremos todo el código para que realices la actualización.

El código de Python 2 requiere el uso de las credenciales y el ID del proyecto que se devuelven desde google.auth.default(). Las credenciales no se usan en Python 3, por lo que se asignaron a una variable ficticia genérica de guion bajo ( _). Como es necesario para la versión de Python 2, cambia el guion bajo por CREDS. Además, en lugar de crear un cliente de la API de Resource Manager, crearás un extremo de servicio de la API, similar en concepto a un cliente de la API, por lo que mantendremos el mismo nombre de variable (rm_client). Una diferencia es que la creación de instancias de un extremo de servicio requiere credenciales (CREDS).

Estos cambios se reflejan en el siguiente código:

ANTES:

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

DESPUÉ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)

La otra diferencia es que la biblioteca cliente de Resource Manager devuelve objetos allow-policy que usan la notación de atributos con puntos, mientras que la biblioteca cliente de nivel inferior devuelve diccionarios de Python en los que se usan corchetes ( [ ]); por ejemplo, usa binding.role para la biblioteca cliente de Resource Manager en comparación con binding['role'] para la biblioteca de nivel inferior. La primera también usa nombres "separados_por_guiones_bajos" en comparación con la biblioteca de nivel inferior que prefiere nombres "ConFormatoDeCapa" y una forma ligeramente diferente de pasar parámetros de la API.

A continuación, se muestran estas diferencias de uso:

ANTES:

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)

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

Si juntas todos estos cambios, reemplaza el _get_gae_admins() de Python 3 por esta versión equivalente de Python 2:

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

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

La función is_admin() no requiere ninguna actualización porque se basa en _get_gae_admins(), que ya se actualizó.

Con esto, se completan los cambios necesarios para portar la app del módulo 21 de Python 3 a Python 2. ¡Felicitaciones por llegar a la app de ejemplo actualizada del módulo 21! Encontrarás todo el código en la carpeta del repositorio del módulo 21a.

7. Resumen/Limpieza

Los últimos pasos del codelab consisten en asegurarse de que las entidades principales (usuarios o cuentas de servicio) que ejecutan esta app tengan los permisos adecuados para hacerlo y, luego, implementar la app para confirmar que funciona según lo previsto y que los cambios se reflejan en el resultado.

Capacidad de leer la política de permisos de IAM

Anteriormente, te presentamos los cuatro roles necesarios para ser reconocido como usuario administrador de App Engine, pero ahora hay un quinto rol que debes conocer:

  • roles/viewer
  • roles/editor
  • roles/owner
  • roles/appengine.appAdmin
  • roles/resourcemanager.projectIamAdmin (para las entidades principales que acceden a la política de IAM de permiso)

El rol roles/resourcemanager.projectIamAdmin permite que las entidades principales determinen si un usuario final es miembro de alguno de los roles de administrador de App Engine. Si no eres miembro de roles/resourcemanager.projectIamAdmin, fallarán las llamadas a la API de Cloud Resource Manager para obtener la política de permisos.

No es necesario que realices ninguna acción explícita aquí, ya que tu app se ejecutará con la cuenta de servicio predeterminada de App Engine, a la que se le otorga automáticamente la membresía en este rol. Incluso si usas la cuenta de servicio predeterminada durante la fase de desarrollo, te recomendamos que crees y uses una cuenta de servicio administrada por el usuario con los permisos mínimos necesarios para que tu app funcione correctamente. Para otorgar membresía a una cuenta de servicio de este tipo, ejecuta el siguiente comando:

$ 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 es el ID del proyecto de Cloud y USR_MGD_SVC_ACCT@PROJ_ID.iam.gserviceaccount.com es la cuenta de servicio administrada por el usuario que creas para tu app. Este comando genera la política de IAM actualizada para tu proyecto, en la que puedes confirmar que la cuenta de servicio es miembro de roles/resourcemanager.projectIamAdmin. Para obtener más información, consulta la documentación de referencia. Para reiterar, no es necesario que ejecutes ese comando en este codelab, pero guárdalo como referencia para modernizar tus propias apps.

Implementa y verifica la aplicación

Sube tu app a la nube con el comando estándar gcloud app deploy. Una vez que se implemente, deberías ver una funcionalidad casi idéntica a la de la app del módulo 20, excepto que reemplazaste correctamente el servicio de App Engine Users por Cloud Identity Platform (y Firebase Auth) para la administración de usuarios:

3a83ae745121d70.png

Una diferencia que notarás en comparación con el módulo 20 es que, cuando haces clic en Acceder, se muestra una ventana emergente en lugar de un redireccionamiento, como se muestra en algunas de las capturas de pantalla a continuación. Sin embargo, al igual que en el módulo 20, el comportamiento varía ligeramente según la cantidad de Cuentas de Google que se registraron en el navegador.

Si no hay usuarios registrados en el navegador o hay un solo usuario que aún no accedió, aparecerá una ventana emergente genérica de Acceso con Google:

8437f5f3d489a942.png

Si un solo usuario está registrado en tu navegador, pero accede a su cuenta en otro lugar, no aparecerá ningún diálogo (o aparecerá y se cerrará de inmediato), y la app entrará en un estado de acceso (mostrará el correo electrónico del usuario y el botón Cerrar sesión).

Es posible que algunos desarrolladores quieran proporcionar un selector de cuentas, incluso para un solo usuario:

b75624cb68d94557.png

Para implementar esto, quita los comentarios de la línea provider.setCustomParameters({prompt: 'select_account'}); en la plantilla web, como se describió anteriormente.

Si hay varios usuarios, aparecerá el diálogo del selector de cuentas (consulta a continuación). Si aún no accedió, se le solicitará al usuario que lo haga. Si ya accediste, la ventana emergente desaparecerá y la app entrará en el estado de acceso.

c454455b6020d5e4.png

El estado de acceso de Module 21 se ve idéntico a la interfaz de usuario de Module 20:

49ebe4dcc1eff11f.png

Lo mismo sucede cuando un usuario administrador accede:

44302f35b39856eb.png

A diferencia del módulo 21, el módulo 20 siempre accede a la lógica del contenido de la plantilla web desde la app (código del servidor). Un defecto del módulo 20 es que se registra una visita cuando el usuario final ingresa a la app por primera vez y otra cuando accede.

En el módulo 21, la lógica de acceso se lleva a cabo solo en la plantilla web (código del cliente). No se requiere un viaje del servidor para determinar qué contenido mostrar. La única llamada que se realiza al servidor es la verificación de usuarios administradores después de que un usuario final accede. Esto significa que los accesos y los cierres de sesión no registran visitas adicionales, por lo que la lista de visitas más recientes se mantiene constante para las acciones de administración de usuarios. Observa que las capturas de pantalla anteriores muestran el mismo conjunto de cuatro visitas en varios accesos de usuarios.

Las capturas de pantalla del módulo 20 muestran el "error de doble visita" al comienzo de este codelab. Los registros de visitas independientes se muestran para cada acción de acceso o salida. Verifica las marcas de tiempo de la visita más reciente para cada captura de pantalla que muestre el orden cronológico.

Limpia

General

Si terminaste por ahora, te recomendamos que inhabilites tu app de App Engine para evitar incurrir en cargos de facturación. Sin embargo, si deseas realizar más pruebas o experimentos, la plataforma de App Engine tiene una cuota gratuita, por lo que no se te cobrará siempre que no excedas ese nivel de uso. Esto se aplica a la capacidad de procesamiento, pero también puede haber cargos por los servicios relevantes de App Engine, por lo que debes consultar su página de precios para obtener más información. Si esta migración involucra otros servicios de Cloud, estos se facturarán por separado. En cualquier caso, si corresponde, consulta la sección "Específico para este codelab" que se encuentra más abajo.

Para divulgar toda la información, la implementación en una plataforma de computación sin servidores de Google Cloud, como App Engine, genera costos menores de compilación y almacenamiento. Cloud Build tiene su propia cuota gratuita, al igual que Cloud Storage. El almacenamiento de esa imagen usa parte de esa cuota. Sin embargo, es posible que vivas en una región que no tenga ese nivel gratuito, por lo que debes tener en cuenta tu uso de almacenamiento para minimizar los costos potenciales. Las "carpetas" específicas de Cloud Storage que debes revisar incluyen las siguientes:

  • 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
  • Los vínculos de almacenamiento anteriores dependen de tu PROJECT_ID y LOCación. Por ejemplo, "us" si tu app está alojada en EE.UU.

Por otro lado, si no vas a continuar con esta aplicación ni con otros codelabs de migración relacionados y quieres borrar todo por completo, cierra tu proyecto.

Específico para este codelab

Los servicios que se indican a continuación son exclusivos de este codelab. Consulta la documentación de cada producto para obtener más información:

Próximos pasos

Además de este instructivo, otros módulos de migración que se enfocan en dejar de usar los servicios agrupados heredados que debes tener en cuenta son los siguientes:

  • Módulo 2: Migra de ndb de App Engine a Cloud NDB
  • Módulos 7 a 9: Migra de la lista de tareas en cola de App Engine (tareas de envío) a Cloud Tasks
  • Módulos 12 y 13: Migra de Memcache de App Engine a Cloud Memorystore
  • Módulos 15 y 16: Migra de Blobstore de App Engine a Cloud Storage
  • Módulos 18 y 19: Migra de la lista de tareas en cola de App Engine (tareas de extracción) a Cloud Pub/Sub

App Engine ya no es la única plataforma sin servidores de Google Cloud. Si tienes una app de App Engine pequeña o una que tiene funcionalidad limitada y deseas convertirla en un microservicio independiente, o bien quieres dividir una app monolítica en varios componentes reutilizables, estos son buenos motivos para considerar la posibilidad de migrar a Cloud Functions. Si la contenerización se convirtió en parte de tu flujo de trabajo de desarrollo de aplicaciones, en especial si consta de una canalización de CI/CD (integración continua/entrega o implementación continua), considera migrar a Cloud Run. Estos casos se abordan en los siguientes módulos:

  • Migra de App Engine a Cloud Functions: Consulta el módulo 11
  • Migra de App Engine a Cloud Run: Consulta el módulo 4 para organizar tu app en contenedores con Docker o el módulo 5 para hacerlo sin contenedores, conocimientos de Docker ni Dockerfiles.

Cambiar a otra plataforma sin servidores es opcional, y te recomendamos que consideres las mejores opciones para tus apps y casos de uso antes de realizar cualquier cambio.

Independientemente del módulo de migración que consideres a continuación, se puede acceder a todo el contenido de Serverless Migration Station (codelabs, videos, código fuente [cuando esté disponible]) en su repositorio de código abierto. El README del repo también proporciona orientación sobre qué migraciones considerar y cualquier "orden" relevante de los módulos de migración.

8. Recursos adicionales

A continuación, se enumeran recursos adicionales para los desarrolladores que deseen explorar más a fondo este módulo de migración o los relacionados. A continuación, puedes proporcionar comentarios sobre este contenido, encontrar vínculos al código y varios documentos que podrían resultarte útiles.

Problemas o comentarios de los Codelabs

Si encuentras algún problema con este Codelab, primero busca el problema antes de enviarlo. Vínculos para buscar y crear problemas nuevos:

Recursos de migración

En la siguiente tabla, puedes encontrar vínculos a las carpetas del repositorio para el módulo 20 (INICIAR) y el módulo 21 (FINALIZAR).

Codelab

Python 2

Python 3

Módulo 20

código

(n/a)

Módulo 21 (este codelab)

código

código

Referencias en línea

A continuación, se incluyen recursos pertinentes para este instructivo:

Cloud Identity Platform y Cloud Marketplace

Cloud Resource Manager, Cloud IAM y SDK de Firebase Admin

Usuarios de App Engine, App Engine NDB, Cloud NDB y Cloud Datastore

Otras referencias del módulo de migración

Migración de App Engine

Plataforma de App Engine

SDK de Cloud

Otra información de la nube

Videos

Licencia

Este trabajo cuenta con una licencia Atribución 2.0 Genérica de Creative Commons.