Observação:

Use o Oracle Cloud API Gateway para criar um serviço de download seguro do Object Storage com autenticação de função

Introdução

O Oracle Cloud Object Storage é uma maneira muito barata e eficaz de armazenar arquivos grandes ou pequenos. O Armazenamento de Objetos permite trabalhar em uma arquitetura Orientada a Eventos, tornando a execução do processo quase em tempo real. Você pode configurar eventos no Object Storage para acionar eventos, e também pode criar serviços REST para gravar ou ler arquivos no Object Storage de forma segura, rápida e econômica.

Se você tiver interesse em ver como implementar uma arquitetura orientada a eventos, consulte Processar arquivos grandes no Autonomous Database e no Kafka com o Oracle Cloud Infrastructure Data Flow.

Arquitetura

Arquitetura

Integração com o Oracle API Gateway

Integração de Gateway de API

Neste tutorial, mostraremos como configurar o Oracle API Gateway para implementar um serviço REST por meio de uma função que permite, de forma flexível, autenticar um token JWT por meio de um Provedor de Identidades externo ou da própria OCI (Oracle Cloud Infrastructure) (Oracle Identity Cloud Service). Independentemente do processo, você pode validar o token JWT (já que um atributo pode fazer parte do nome do arquivo), usar serviços do OCI por meio de seu SDK e outros processos personalizáveis.

Objetivos

Pré-requisitos

Tarefa 1: Configurar um aplicativo no Oracle Identity Cloud Service

Você pode configurar qualquer Provedor de Identidades Externo de API REST para trabalhar com JWT como Auth0 e Oracle Identity Cloud Service.

Neste tutorial, trabalharemos com o Oracle Identity Cloud Service e chamaremos a autenticação dentro da função.

Tarefa 2: Analisar o código

Este é o código Python usado em uma implantação do API Gateway para autorizar um link pré-autenticado a fazer download de um arquivo no Object Storage.

Você pode fazer download do código Python aqui: 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)


Vamos analisar o código em partes para entender como:

Esta parte do código salva uma posição de status em Observabilidade do OCI. Ele usa a estrutura Zipkin para publicar na Observabilidade do OCI APM

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 é a view Console do OCI APM do código e você pode encontrar seus arquivos com consultas como:

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

CEP

O próximo código estabiliza uma data e hora de expiração para o arquivo do serviço Object Storage. Uma pré-autenticação será gerada e o atributo expiresAt será usado para esse objetivo. timedelta adicione 60 segundos do horário atual para fazer download do arquivo.

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

Agora, precisamos inicializar a estrutura do OCI Object Storage com base nas credenciais salvas na instalação da CLI do OCI. A configuração da CLI do OCI usa ~/.oci/config e o arquivo .pem do certificado. Para que você possa instalar a CLI do OCI localmente e configurar um usuário para acessar o Armazenamento de Objetos (consulte a documentação do OCI para instalar a CLI do OCI e as políticas do serviço Object Storage na seção Links Relacionados) e, em seguida, copie esses 2 arquivos para esse projeto fn.

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

A próxima etapa obterá dos valores dos parâmetros de Corpo: secretID, clientID e 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")

O OCI SDK pode suportar o serviço Object Storage para muitos serviços, como ler e/ou gravar um arquivo, listar conteúdo de um bucket e outros. Você pode, por exemplo, permitir que o consumidor liste todo o conteúdo de um bucket com um URL pré-autenticado será gerado nesta parte do código. A variável bucket_name contém o nome do bucket no Object Storage criado anteriormente e time_expires representa a data e a expiração da hora para fazer download do arquivo.

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)

Essa parte do código chama o Oracle Identity Cloud Service para validar clientID e secretID para obter o token JWT. Um JWT pode ser decodificado em uma string JSON, neste caso, sem assinatura, mas a assinatura pode ser verificada facilmente com um 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 é a parte final, na qual todos os dados serão retornados com o código 200. Você pode retornar todas as informações necessárias ao seu aplicativo e essa parte do código valida a autenticação, resultando no código 200 (autorizado/sucesso) ou 401 (não 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"})
)



Tarefa 3: Configurar o Gateway de API do OCI para autenticação com o fn

O OCI API Gateway pode implantar uma API como uma função do OCI. Como você viu anteriormente, o código usa as informações de body que contêm clientID, secretID e objectID especificadas pela configuração do API Gateway. Você pode configurar sua implantação:

Gateway de API 1

Gateway de API 2

Gateway de API 3

Depois de salvar sua implantação, você poderá obter o ponto final REST aqui. Lembre-se de que o ponto final completo do seu serviço de API REST de Armazenamento de Objetos é esse ponto final mais o "/" no final (você declarou / como seu caminho anteriormente).

ponto de extremidade da api

Tarefa 4: Testar a Implantação do API Gateway

Você pode testar o aplicativo com CURL ou em 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"}'

Observação: O atributo objectID é usado aqui apenas para ilustrar a Observabilidade e como mostrar o status no Trace Explorer na Console do OCI.

pós-teste

Se tudo estiver OK, você poderá ver o código de sucesso 200:

resultado do sucesso

Um URL para o Object Storage pré-autenticado será gerado e você poderá fazer download do arquivo pelos próximos 60 segundos. Você pode fazer download do arquivo seguindo o exemplo aqui:

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

Você pode fazer download, fazer upload ou exibir os arquivos de bucket com esta linha de comando:

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

Tarefa 5: Configurar Painel de Controle de Observabilidade

Basicamente, cada recurso do OCI pode mostrar métricas em um painel de controle e muitos eventos nesses recursos podem acionar uma ação. Nesta demonstração, você pode configurar um painel de controle para mostrar quantos arquivos foram gravados ou lidos no Object Storage:

Observabilidade

Você pode configurar a consulta do painel da seguinte forma:

Aquisições

Mais Recursos de Aprendizagem

Explore outros laboratórios no site docs.oracle.com/learn ou acesse mais conteúdo de aprendizado gratuito no canal YouTube do Oracle Learning. Além disso, visite education.oracle.com/learning-explorer para se tornar um Oracle Learning Explorer.

Para obter a documentação do produto, visite o Oracle Help Center.