Nota:

Utilizza Oracle Cloud API Gateway per creare un servizio di download sicuro dello storage degli oggetti con autenticazione della funzione

Introduzione

Lo storage degli oggetti Oracle Cloud è un modo molto economico ed efficace per memorizzare file di grandi o piccole dimensioni. Lo storage degli oggetti consente di utilizzare un'architettura basata sugli eventi, avvicinandosi all'esecuzione dei processi in tempo reale. Puoi configurare gli eventi nello storage degli oggetti per attivare gli eventi e puoi anche creare servizi REST in modo sicuro, rapido e conveniente per scrivere o leggere i file dallo storage degli oggetti.

Se ti interessa vedere come implementare un'architettura basata sugli eventi, consulta Elaborare file di grandi dimensioni in Autonomous Database e Kafka con Oracle Cloud Infrastructure Data Flow.

Architettura

Architettura

Integrazione di Oracle API Gateway

Integrazione gateway API

In questa esercitazione verrà illustrato come configurare Oracle API Gateway per implementare un servizio REST tramite una funzione che consenta, in modo flessibile, di autenticare un token JWT tramite un provider di identità esterno o la stessa Oracle Cloud Infrastructure (OCI) (Oracle Identity Cloud Service). Indipendentemente dal processo, puoi convalidare il token JWT (poiché un attributo può far parte del nome file), utilizza i servizi OCI tramite il relativo SDK e altri processi personalizzabili.

Obiettivi

Prerequisiti

Task 1: configurazione di un'applicazione in Oracle Identity Cloud Service

È possibile configurare qualsiasi provider di identità esterno API REST in modo che funzioni con JWT come Auth0 e Oracle Identity Cloud Service.

In questa esercitazione, lavoreremo con Oracle Identity Cloud Service e chiameremo l'autenticazione all'interno della funzione.

Task 2: rivedere il codice

Codice Python utilizzato in una distribuzione di gateway API per autorizzare un collegamento preautenticato per scaricare un file nello storage degli oggetti.

È possibile scaricare il codice Python da qui: 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)


Analizziamo il codice nelle parti per capire come:

Questa parte del codice salva una posizione di stato in Osservabilità OCI. Utilizza la struttura Zipkin per pubblicare in 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
    ):

Si tratta della vista console APM OCI relativa al codice ed è possibile trovare i file con query quali:

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

zipkin-oci

Il codice successivo stabilisce una data e un'ora di scadenza per il file Storage degli oggetti. Verrà generata una preautenticazione e l'attributo expiresAt verrà utilizzato per questo obiettivo. timedelta aggiunge 60 secondi dall'ora corrente per lo scaricamento del file.

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

A questo punto, è necessario inizializzare il framework dello storage degli oggetti OCI in base alle credenziali salvate nell'installazione CLI OCI. La configurazione dell'interfaccia CLI OCI utilizza il file ~/.oci/config e il file del certificato .pem. È possibile installare l'interfaccia CLI OCI a livello locale e configurare un utente per accedere allo storage degli oggetti (consultare la documentazione OCI per installare l'interfaccia CLI OCI e i criteri dello storage degli oggetti nella sezione Collegamenti correlati), quindi copiare questi 2 file in questo progetto fn.

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

Il passo successivo deriva dai valori dei parametri 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")

L'SDK OCI può supportare lo storage degli oggetti per molti servizi, ad esempio in lettura e/o scrittura di un file, elencare il contenuto di un bucket e di altri. Ad esempio, puoi consentire al consumer di elencare tutti i contenuti di un bucket con un URL preautenticato che verrà generato in questa parte del codice. La variabile bucket_name contiene il nome del bucket nello storage degli oggetti creato in precedenza e time_expires rappresenta la data e l'ora di scadenza in cui scaricare il file.

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)

Questa parte del codice chiama Oracle Identity Cloud Service per convalidare clientID e secretID per ottenere il token JWT. Un JWT può essere decodificato in una stringa JSON, in questo caso senza firma, ma è possibile verificare facilmente la firma con un certificato.

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

Questa è la parte finale, in cui verranno restituiti tutti i dati con il codice 200. Puoi restituire tutte le informazioni necessarie alla tua applicazione e questa parte del codice convalida l'autenticazione, con conseguente codice 200 (autorizzato/successo) o 401 (non autorizzato).

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"})
)



Task 3: configurare il gateway API OCI per l'autenticazione con fn

Il gateway API OCI può distribuire un'interfaccia API come funzione OCI. Come visto in precedenza, il codice utilizza il corpo contenente le informazioni clientID, secretID e objectID passate dalla configurazione del gateway API. È possibile configurare la distribuzione:

Gateway API 1

Gateway API 2

Gateway API 3

Dopo aver salvato la distribuzione, è possibile ottenere l'endpoint REST qui. Ricorda che l'endpoint completo per il servizio API REST di storage degli oggetti è questo endpoint più il "/" alla fine (hai dichiarato / come percorso in precedenza).

api-endpoint

Task 4: eseguire il test della distribuzione del gateway API

È possibile eseguire il test dell'applicazione con CURL o in 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: l'attributo objectID viene utilizzato qui solo per illustrare l'osservabilità e come visualizzare lo stato in Trace Explorer nella console OCI.

test post-uomo

Se tutto va bene, potete vedere il codice di successo 200:

risultato positivo

Verrà generato un URL per lo storage degli oggetti preautenticato e sarà possibile scaricare il file per i prossimi 60 secondi. È possibile scaricare il file seguendo l'esempio qui:

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

Puoi scaricare, caricare o visualizzare i file bucket con questa riga di 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/``

Task 5: Configura dashboard osservabilità

In sostanza, ogni risorsa OCI può mostrare le metriche in un dashboard e molti eventi in queste risorse possono attivare un'azione. In questa demo, puoi configurare un dashboard per mostrare il numero di file scritti o letti nello storage degli oggetti:

Osservabilità

È possibile configurare la query del dashboard come indicato di seguito.

Conferme

Altre risorse di apprendimento

Esplora altri laboratori su docs.oracle.com/learn o accedi a contenuti di formazione gratuiti sul canale YouTube di Oracle Learning. Inoltre, visitare education.oracle.com/learning-explorer per diventare Explorer di Oracle Learning.

Per la documentazione sul prodotto, visitare il sito Oracle Help Center.