Conector Scardua — recebe da API da FLS e entrega no Oracle do cliente
Servico que roda dentro da rede da Comercial Scardua e faz a ponte entre a API publica da FLS (na VPS) e o Oracle privado (10.16.x), inalcancavel pela internet. Fluxo: Holmes -> API na VPS (trata e cifra) -> ESTE conector (abre e valida) -> Oracle. Recebimento: - app/v1/compras.py: POST /v1/compras/dados - app/services/cripto.py: abre o envelope AES-256-GCM + RSA-OAEP-SHA256 com a chave privada. O GCM autentica: corpo adulterado levanta InvalidTag em vez de devolver lixo - app/schemas.py: modulo folha (so pydantic) com a config e o contrato PayloadCompras, que espelha o da API. Fora de sincronia devolve 422 de proposito, pra falhar explicito em vez de gravar dado torto Seguranca: - app/seguranca.py: header X-Token com compare_digest (nao vaza por tempo de resposta) + allowlist de IP da VPS - .gitignore barra configs.json.*, *.bak-*, *.pem e *.key Config: - app/config.py e so o carregamento do configs.json - o bloco "vps" guarda chave privada, token e ips permitidos Estado: ainda NAO persiste. Recebe, valida e descarta (persistido: false). O INSERT no Oracle e a proxima fase, e tem que ser MERGE por id_processo porque o Holmes reentrega webhook. Docs em docs/ — arquitetura, a decisao do conector (opcao A, com as alternativas descartadas), deploy e problemas conhecidos. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
commit
f902ccbecb
26 changed files with 1824 additions and 0 deletions
47
app/services/cripto.py
Normal file
47
app/services/cripto.py
Normal file
|
|
@ -0,0 +1,47 @@
|
|||
"""
|
||||
Abertura do envelope que a API da VPS manda.
|
||||
|
||||
Espelho do app/services/cripto.py de la, so que do lado que DECIFRA.
|
||||
Envelope hibrido: AES-256-GCM nos dados, RSA-OAEP na chave AES.
|
||||
|
||||
O GCM autentica: se o corpo for adulterado no caminho, o decrypt levanta
|
||||
excecao em vez de devolver lixo. Ou seja, decifrou = veio integro.
|
||||
"""
|
||||
|
||||
import base64
|
||||
import json
|
||||
|
||||
from cryptography.hazmat.primitives import hashes, serialization
|
||||
from cryptography.hazmat.primitives.asymmetric import padding
|
||||
from cryptography.hazmat.primitives.ciphers.aead import AESGCM
|
||||
|
||||
ALGORITMO = "RSA-OAEP-256+A256GCM"
|
||||
|
||||
_OAEP = padding.OAEP(
|
||||
mgf=padding.MGF1(algorithm=hashes.SHA256()),
|
||||
algorithm=hashes.SHA256(),
|
||||
label=None,
|
||||
)
|
||||
|
||||
|
||||
def carregar_chave_privada(pem: str, senha: bytes | None = None):
|
||||
return serialization.load_pem_private_key(pem.encode(), password=senha)
|
||||
|
||||
|
||||
def descriptografar(envelope: dict, chave_privada) -> dict:
|
||||
"""
|
||||
Abre o envelope e devolve o dict original.
|
||||
|
||||
Levanta se o algoritmo nao for o esperado, se a chave nao for a par da
|
||||
publica usada la, ou se o conteudo tiver sido mexido.
|
||||
"""
|
||||
if envelope.get("alg") != ALGORITMO:
|
||||
raise ValueError(f"algoritmo inesperado: {envelope.get('alg')!r}")
|
||||
|
||||
chave_aes = chave_privada.decrypt(base64.b64decode(envelope["chave"]), _OAEP)
|
||||
corpo = AESGCM(chave_aes).decrypt(
|
||||
base64.b64decode(envelope["nonce"]),
|
||||
base64.b64decode(envelope["dados"]),
|
||||
None,
|
||||
)
|
||||
return json.loads(corpo)
|
||||
Loading…
Add table
Add a link
Reference in a new issue