Nota:

Utilice el gateway de API de Oracle Cloud para crear un servicio de descarga seguro del almacenamiento de objetos con autenticación de función

Introducción

El almacenamiento de objetos de Oracle Cloud es una forma muy barata y eficaz de almacenar archivos grandes o pequeños. El almacenamiento de objetos permite trabajar en una arquitectura basada en eventos, lo que hace que la ejecución del proceso esté cerca de tiempo real. Puede configurar eventos en Object Storage para disparar eventos, y también puede crear servicios REST para escribir o leer archivos de Object Storage de forma segura, rápida y económica.

Si está interesado en ver cómo implantar una arquitectura basada en eventos, consulte Procesar archivos grandes en Autonomous Database y Kafka con Oracle Cloud Infrastructure Data Flow.

Arquitectura

Arquitectura

Integración de Oracle API Gateway

Integración de API Gateway

En este tutorial, mostramos cómo configurar Oracle API Gateway para implantar un servicio REST mediante una función que permita, de forma flexible, autenticar un token de JWT mediante un proveedor de identidad externo u Oracle Cloud Infrastructure (OCI) en sí (Oracle Identity Cloud Service). Independientemente del proceso, puede validar el token de JWT (ya que un atributo puede formar parte del nombre de archivo), utilizar los servicios de OCI a través de su SDK y otros procesos personalizables.

Objetivos

Requisitos

Tarea 1: Configurar una aplicación en Oracle Identity Cloud Service

Puede configurar cualquier proveedor de identidad externo de API de REST para que funcione con JWT, como Auth0 y Oracle Identity Cloud Service.

En este tutorial, trabajaremos con Oracle Identity Cloud Service y llamaremos a la autenticación dentro de la función.

Tarea 2: Revisar el código

Este es el código de Python que se utiliza en un despliegue de gateway de API para autorizar un enlace de autenticación previa para descargar un archivo en Object Storage.

Puede descargar el código de Python aquí: Python fn Project.

python
import io
import json
import logging
import datetime
import jwt
import requests
import base64
import oci

from datetime import timedelta
from fdk import response
from py_zipkin import Encoding #import Zipkin package
from py_zipkin.zipkin import zipkin_span #import Zipkin package
from transport import http_transport #import Zipkin transport

@zipkin_span(service_name='Status: Load File', span_name='statusGetFile')
def auth(ctx, data: io.BytesIO = None):
    auth_token = "invalid"
    token = "invalid"
    apiKey = "invalid"
    expiresAt = (datetime.datetime.utcnow() + timedelta(seconds=60)).replace(tzinfo=datetime.timezone.utc).astimezone().replace(microsecond=0).isoformat()

    config = oci.config.from_file("config")
    object_storage = oci.object_storage.ObjectStorageClient(config)
    namespace = object_storage.get_namespace().data

    try:
        auth_token = json.loads(data.getvalue())
        secretID = auth_token.get("secretID")
        clientID = auth_token.get("clientID")

        details = oci.object_storage.models.CreatePreauthenticatedRequestDetails(name="data", access_type="AnyObjectReadWrite", bucket_listing_action="ListObjects", time_expires=expiresAt)

        preauth = object_storage.create_preauthenticated_request(namespace_name=namespace, bucket_name="data", create_preauthenticated_request_details=details)
        preauthstr = str(preauth.data)

        auth = clientID + ":" + secretID
        auth_bytes = auth.encode("ascii")
        auth_base64_bytes = base64.b64encode(auth_bytes)
        auth_base64_message = auth_base64_bytes.decode("ascii")

        headers = {"Authorization": "Basic " + auth_base64_message, "Content-Type": "application/x-www-form-urlencoded"}

        scope = "cihxxxxxxxxxxxxxxxxxowu.apigateway.us-ashburn-1.oci.customer-oci.com/super-scope"
        grant_type = "client_credentials"

        body = {"scope": scope, "grant_type": grant_type}

        url_post = "https://Oracle Identity Cloud Service-4fxxxxxxxxxxxxxxxxxxxxxx9.identity.oraclecloud.com/oauth2/v1/token"
        post_response = requests.post(url_post, headers=headers, data=body)

        jwtTokenDecoded = jwt.decode(post_response.json()['access_token'], options={"verify_signature": False})

        return response.Response(
            ctx,
            status_code=200,
            response_data=json.dumps({"active": True, "principal": "foo", "scope": "bar", "clientId": "1234", "expiresAt": expiresAt, "context": {"username": "wally", "token":
post_response.json()['access_token'], "jwtTokenDecoded": jwtTokenDecoded, "objectID": preauthstr}})
        )

    except (Exception, ValueError) as ex:
        logging.getLogger().info('error parsing json payload: ' + str(ex))
        pass

    return response.Response(
        ctx,
        status_code=401,
        response_data=json.dumps({"active": False, "wwwAuthenticate": "API-key"})
    )

@zipkin_span(service_name='Status: Load File', span_name='statusGetFile')
def handler(ctx, data: io.BytesIO = None):
    with zipkin_span(
            service_name="Status: Load File", #You can change it as you need
            span_name="statusGetFile", #You can change it as you need
            transport_handler=http_transport, #zipkin transport, will use it to upload trace data to OCI APM
            encoding = Encoding.V2_JSON,
            binary_annotations = {"status":"Load File", "objectID":json.loads(data.getvalue()).get("objectID")}, #Custom tag
            sample_rate=100 # this is optional and can be used to set custom sample rates
    ):
        return auth(ctx, data)


Analicemos el código en partes para comprender cómo:

Esta parte del código guarda una posición de estado en OCI Observability. Utiliza el marco Zipkin para publicar en OCI APM Observability

python
@zipkin_span(service_name='Status: Load File', span_name='statusGetFile')
def handler(ctx, data: io.BytesIO = None):
    with zipkin_span(
            service_name="Status: Load File", #You can change it as you need
            span_name="statusGetFile", #You can change it as you need
            transport_handler=http_transport, #zipkin transport, will use it to upload trace data to OCI APM
            encoding = Encoding.V2_JSON,
            binary_annotations = {"status":"Load File", "objectID":json.loads(data.getvalue()).get("objectID")}, #Custom tag
            sample_rate=100 # this is optional and can be used to set custom sample rates
    ):

Esta es la vista Consola de OCI APM del código y puede encontrar sus archivos con consultas como:

**ServiceName** = 'Status: Load File' and **objectID** = '50 - DR-HA OIC.pdf'

zipkin-oci

El siguiente código establece una fecha y hora de caducidad para el archivo de Object Storage. Se generará una autenticación previa y se utilizará el atributo expiresAt para este objetivo. timedelta agregue 60 segundos desde la hora actual para descargar el archivo.

python
expiresAt = (datetime.datetime.utcnow() + timedelta(seconds=60)).replace(tzinfo=datetime.timezone.utc).astimezone().replace(microsecond=0).isoformat()

Ahora, debemos inicializar el marco de OCI Object Storage en función de las credenciales guardadas en la instalación de la CLI de OCI. La configuración de la CLI de OCI utiliza ~/.oci/config y el archivo .pem del certificado. Por lo tanto, puede instalar la CLI de OCI localmente y configurar un usuario para acceder al almacenamiento de objetos (consulte la documentación de OCI para instalar la CLI de OCI y las políticas de almacenamiento de objetos en la sección Enlaces relacionados) y, a continuación, copiar estos 2 archivos en este proyecto fn.

python
config = oci.config.from_file("config")
object_storage = oci.object_storage.ObjectStorageClient(config)
namespace = object_storage.get_namespace().data

El siguiente paso procederá de los valores de parámetros Cuerpo: secretID, clientID y objectID.

#secretID = the Oracle Identity Cloud Service secretID from the application created to validate the JWT Token
#clientID = the Oracle Identity Cloud Service clientID from the application created to validate the JWT Token
#objectID = the file name in the Object Storage
python
try:
    auth_token = json.loads(data.getvalue())
    secretID = auth_token.get("secretID")
    clientID = auth_token.get("clientID")
    objectID = auth_token.get("objectID")

El SDK de OCI puede soportar Object Storage para muchos servicios, como leer y/o escribir un archivo, mostrar el contenido de un cubo y otros. Por ejemplo, puede activar que el consumidor muestre todo el contenido de un cubo con una URL autenticada previamente que se generará en esta parte del código. La variable bucket_name contiene el nombre del cubo en el Object Storage creado anteriormente y time_expires representa la fecha y la hora de caducidad para descargar el archivo.

python
details = oci.object_storage.models.CreatePreauthenticatedRequestDetails(name="data", access_type="AnyObjectReadWrite", bucket_listing_action="ListObjects", time_expires=expiresAt)

preauth = object_storage.create_preauthenticated_request(namespace_name=namespace, bucket_name="data", create_preauthenticated_request_details=details)
preauthstr = str(preauth.data)

Esta parte del código llama a Oracle Identity Cloud Service para validar clientID y secretID para obtener el token JWT. Un JWT se puede descodificar en una cadena JSON, en este caso, sin firma, pero la firma se puede verificar fácilmente con un certificado.

python
auth = clientID + ":" + secretID
auth_bytes = auth.encode("ascii")
auth_base64_bytes = base64.b64encode(auth_bytes)
auth_base64_message = auth_base64_bytes.decode("ascii")

headers = {"Authorization": "Basic " + auth_base64_message, "Content-Type": "application/x-www-form-urlencoded"}

scope = "xxxxxxxxxxxxxxxxxxxx.apigateway.us-ashburn-1.oci.customer-oci.com/super-scope"
grant_type = "client_credentials"

body = {"scope": scope, "grant_type": grant_type}

url_post = "https://Oracle Identity Cloud Service-xxxxxxxxxxxxxxxxxxxxxxx.identity.oraclecloud.com/oauth2/v1/token"
post_response = requests.post(url_post, headers=headers, data=body)

jwtTokenDecoded = jwt.decode(post_response.json()['access_token'], options={"verify_signature": False})

Esta es la parte final, donde se devolverán todos los datos con el código 200. Puede devolver toda la información necesaria a la aplicación y esta parte del código valida la autenticación, lo que da como resultado el código 200 (autorizado/correcto) o 401 (no autorizado).

python
    return response.Response(
        ctx,
        status_code=200,
        response_data=json.dumps({"active": True, "principal": "foo", "scope": "bar", "clientId": "1234", "expiresAt": expiresAt, "context": {"username": "wally", "token": post_response.json()['access_token'], "jwtTokenDecoded": jwtTokenDecoded, "objectID": preauthstr}})
    )

except (Exception, ValueError) as ex:
    logging.getLogger().info('error parsing json payload: ' + str(ex))
    pass

return response.Response(
    ctx,
    status_code=401,
    response_data=json.dumps({"active": False, "wwwAuthenticate": "API-key"})
)



Tarea 3: Configurar el gateway de API de OCI para autenticarse con fn

El gateway de API de OCI puede desplegar una API como función de OCI. Como ha visto anteriormente, el código utiliza el cuerpo que contiene la información de clientID, secretID y objectID transferida por la configuración de gateway de API. Puede configurar el despliegue:

Gateway de API 1

Gateway de API 2

Gateway de API 3

Después de guardar el despliegue, puede obtener el punto final de REST aquí. Recuerde que el punto final completo de su servicio de API de REST de Object Storage es este punto final más "/" al final (declaró / como su ruta de acceso anteriormente).

api-endpoint

Tarea 4: Prueba del despliegue de gateway de API

Puede probar la aplicación con CURL o en Postman:

bash
curl --location 'https://xxxxxxxxxxxxxxxxxxx.apigateway.us-ashburn-1.oci.customer-oci.com/dummyauthorizer/' \
--header 'Content-Type: text/plain' \
--data '{"clientID": "e3exxxxxxxxxxxxxxxxxc2f", "secretID": "8exxxxxxx-fa5e-xxcf-8xxxc-xxxxxxxxxxx87", "objectID": "any-file.txt"}'

Nota: El atributo objectID se utiliza aquí solo para ilustrar la observabilidad y cómo mostrar el estado en el Explorador de rastreo de la consola de OCI.

prueba postman

Si todo está bien, puede ver el código de éxito 200:

resultado de éxito

Se generará una URL para el almacenamiento de objetos autenticado previamente y podrá descargar el archivo durante los próximos 60 segundos. Puede descargar el archivo siguiendo el ejemplo aquí:

https://objectstorage.us-ashburn-1.oraclecloud.com + [access_uri]

or

https://objectstorage.us-ashburn-1.oraclecloud.com/p/eL5C0R0luN_cTNn-vUF7_Dx_z2N4w7IXemKr5y61cSRxZZPRXcR2Yj1dNCaJBDK8/n/idavixsf5sbx/b/data/o/calico.yaml

Puede descargar, cargar o ver los archivos del cubo con esta línea de comandos:

- TO UPLOAD A FILE: `curl https://objectstorage.us-ashburn-1.oraclecloud.com/p/HoPudIF45Bj6J5-Qy3J1D9dOplLuKtECRFhvOTkKAtBjJXkOTDx0Pt8gXbOOEoRx/n/idavixsf5sbx/b/data/o/ --upload-file func.py`

- TO DOWNLOAD A FILE: `curl https://objectstorage.us-ashburn-1.oraclecloud.com/p/3ZyXd6PchrTFrp1oxmedamSG1ojwQa3BxPUyonAA-q1mf3QAe5STpDrt89eYITPf/n/idavixsf5sbx/b/data/o/func.py`

- TO LIST BUCKET: `curl https://objectstorage.us-ashburn-1.oraclecloud.com/p/ODVRMB71kD0SHWuoY4ojVd93nmIiy8u0zrxA56T7FBaohAgA7k8KOLAIlhxjcveE/n/idavixsf5sbx/b/data/o/``

Tarea 5: Configurar panel de control de observabilidad

Básicamente, cada recurso de OCI puede mostrar métricas en un panel de control y muchos eventos en estos recursos pueden disparar una acción. En esta demostración, puede configurar un panel de control para mostrar cuántos archivos se han escrito o leído en Object Storage:

Observación

Puede configurar la consulta del panel de control de la siguiente manera:

Acuses de recibo

Más recursos de aprendizaje

Explore otros laboratorios en docs.oracle.com/learn o acceda a más contenido de aprendizaje gratuito en el canal YouTube de Oracle Learning. Además, visite education.oracle.com/learning-explorer para convertirse en un explorador de Oracle Learning.

Para obtener documentación sobre los productos, visite Oracle Help Center.