> For the complete documentation index, see [llms.txt](https://portal-integracao.intelbras.com.br/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://portal-integracao.intelbras.com.br/defense-ia-and-lite/dicas-de-implementacao/guia-de-implementacao-client-para-gestao-de-pessoas.md).

# Guia de implementação: Client para gestão de pessoas

Client web de referência em Python/FastAPI para integração com o Defense IA & Lite: autenticação BRMS, operações de pessoas via OBMS, tratamento de imagem facial, exibição de fotos e boas práticas de

{% hint style="warning" %}
**Atenção:** este projeto é uma referência técnica para desenvolvimento, homologação e testes. Antes de qualquer uso produtivo, revise autenticação, certificados, controle de acesso, auditoria, logs, proteção de credenciais e hardening do backend local.
{% endhint %}

Este client demonstra como uma aplicação própria pode se autenticar no Defense, consumir APIs de pessoas, enviar foto facial em Base64 e exibir imagens retornadas pelo sistema.

{% embed url="<https://github.com/Integracao-Intelbras-Cftv/Client-De-Pessoas.git>" %}

Objetivo prático:

* facilitar testes de cadastro, listagem, atualização e remoção de pessoas no Defense;
* encapsular a comunicação com o Defense em um backend local, evitando expor credenciais no frontend;
* separar o uso de token de API e credential de imagem, reduzindo erros comuns de integração;
* oferecer uma base simples para validar payloads e fluxos antes de evoluir para um client corporativo.

## Visão Rápida da Arquitetura

Componentes principais:

* main.py: servidor FastAPI, rotas locais, normalização de URLs de imagem e CRUD de pessoas.
* token\_service.py: autenticação no Defense, assinatura MD5, cache em memória de token e credential.
* service.py: cliente HTTP para chamadas OBMS, montagem do header X-Subject-Token e retry em caso de 401.
* image.py: processamento de fotos, crop centralizado, resize, compressão JPEG e conversão para Base64.
* defense.html e assets/: interface web local para operação visual do client.
* requirements.txt: dependências Python necessárias para execução.

## Fluxo de Ponta a Ponta

```mermaid
flowchart LR
    subgraph USER[Camada de Usuário]
        A[Interface Web<br/>Navegador]
    end

    subgraph LOCAL[Client Local]
        B[FastAPI Local<br/>main.py]
        C[Token Service<br/>token_service.py]
        E[Service HTTP<br/>service.py]
        G[Image Service<br/>image.py]
        H[Normalização de URLs<br/>credential nas imagens]
    end

    subgraph DEFENSE[Defense IA]
        D[BRMS<br/>Autenticação]
        F[OBMS<br/>API de Pessoas]
    end

    A --> B

    B --> C
    C --> D
    D --> C
    C --> B

    B --> E
    E --> F
    F --> E
    E --> B

    B --> G
    G --> B

    B --> H
    H --> A

    E -.->|401 Unauthorized<br/>invalida token e refaz login| C

    classDef user fill:#E8F5E9,stroke:#2E7D32,stroke-width:1.5px,color:#1B1B1B;
    classDef local fill:#F1F8E9,stroke:#558B2F,stroke-width:1.5px,color:#1B1B1B;
    classDef defense fill:#FFFDE7,stroke:#F9A825,stroke-width:1.5px,color:#1B1B1B;

    class A user;
    class B,C,E,G,H local;
    class D,F defense;
```

### Inicialização da aplicação

Ao executar o projeto, o FastAPI sobe na porta local 8000, serve a interface HTML e mantém as rotas locais disponíveis para o navegador. A autenticação no Defense ocorre sob demanda quando uma chamada precisa de token.

| <p>python main.py<br># ou<br>uvicorn main:app --host 0.0.0.0 --port 8000 --reload</p> |
| ------------------------------------------------------------------------------------- |

Leitura simplificada:

* o navegador não conversa diretamente com o Defense;
* o backend local recebe a ação da interface e chama o Defense;
* o Token Service entrega o token atual para service.py;
* as respostas são normalizadas antes de voltar para a interface.

## Autenticação no Defense

A autenticação é implementada em token\_service.py e usa o endpoint BRMS de authorize em duas etapas.

{% content-ref url="/pages/No7JHvIzSDarfe8j9EfH" %}
[Implementação de um Token Service](/defense-ia-and-lite/dicas-de-implementacao/implementacao-de-um-token-service.md)
{% endcontent-ref %}

| POST /brms/api/v1.0/accounts/authorize |
| -------------------------------------- |

### First Login

A primeira chamada envia userName, ipAddress e clientType. O retorno esperado contém realm, randomKey e publicKey/publickey, usados para montar a assinatura do segundo passo.

| <p>{<br>"userName": "system",<br>"ipAddress": "",<br>"clientType": "WINPC\_V2"<br>}</p> |
| --------------------------------------------------------------------------------------- |

### Second Login

A segunda chamada envia mac, signature, userName, randomKey, publicKey, encryptType, ipAddress, clientType e userType. O retorno esperado contém token e credential.

| <p>{<br>"mac": "AA:BB:CC:DD:EE:FF",<br>"signature": "\<assinatura\_md5>",<br>"userName": "system",<br>"randomKey": "\<randomKey>",<br>"publicKey": "\<publicKey>",<br>"encryptType": "MD5",<br>"clientType": "WINPC\_V2",<br>"userType": "0"<br>}</p> |
| ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |

### Como a assinatura de login é gerada

A assinatura segue a cadeia de hashes MD5 usada pelo fluxo de autenticação do Defense:

| <p>temp1 = MD5(password)<br>temp2 = MD5(username + temp1)<br>temp3 = MD5(temp2)<br>temp4 = MD5(username + ":" + realm + ":" + temp3)<br>signature = MD5(temp4 + ":" + randomKey)</p> |
| ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |

| **Campo**  | **Uso correto**                                                                                        |
| ---------- | ------------------------------------------------------------------------------------------------------ |
| token      | Usado nas chamadas de API do Defense no header X-Subject-Token.                                        |
| credential | Usado para acessar imagens retornadas pelo Defense, normalmente como parâmetro token na URL da imagem. |

| **Atenção:** token e credential não devem ser tratados como a mesma coisa. O token autentica a API; o credential libera acesso às imagens quando o Defense retorna URLs protegidas. |
| ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |

## Cache de token e retry automático

O projeto mantém token e credential em memória, com validade lógica controlada por TOKEN\_TTL. Antes de reutilizar o token, o código verifica se ainda há margem de validade; se não houver, executa novo login.

| <p>TOKEN\_TTL = 1800 # 30 min<br><br>if \_token and time.time() < (\_token\_expiry - 60):<br>return \_token</p> |
| --------------------------------------------------------------------------------------------------------------- |

Em service.py, todas as chamadas OBMS passam pelo mesmo wrapper HTTP. Se o Defense responder 401, o client invalida o cache e tenta a mesma chamada apenas mais uma vez.

| <p>if response.status\_code == 401 and retry:<br>invalidate\_token()<br>return \_request(method, endpoint, data, params, retry=False)</p> |
| ----------------------------------------------------------------------------------------------------------------------------------------- |

Esse comportamento evita loops infinitos e reduz falhas quando o token expira entre uma operação e outra.

## Endpoints locais do Client

| **Rota local**        | **Método** | **Finalidade**                                                     |
| --------------------- | ---------- | ------------------------------------------------------------------ |
| /                     | GET        | Serve a interface defense.html.                                    |
| /assets/\*            | GET        | Serve arquivos estáticos da interface.                             |
| /pessoas              | GET        | Lista pessoas do Defense.                                          |
| /pessoas              | POST       | Cria uma pessoa no Defense.                                        |
| /pessoas/{person\_id} | PUT        | Atualiza uma pessoa existente.                                     |
| /pessoas/{person\_id} | DELETE     | Remove uma pessoa via batch delete.                                |
| /foto                 | GET        | Proxy auxiliar para buscar imagem do Defense em ambiente de teste. |

## Endpoints do Defense usados

| **Ação**         | **Método**  | **Endpoint Defense**                   |
| ---------------- | ----------- | -------------------------------------- |
| First login      | POST        | /brms/api/v1.0/accounts/authorize      |
| Second login     | POST        | /brms/api/v1.0/accounts/authorize      |
| Listar pessoas   | GET ou POST | /obms/api/v1.1/acs/person/page         |
| Criar pessoa     | POST        | /obms/api/v1.1/acs/person              |
| Atualizar pessoa | PUT         | /obms/api/v1.1/acs/person/{person\_id} |
| Remover pessoa   | POST        | /obms/api/v1.1/acs/person/delete/batch |

Observação: algumas versões podem responder 405 Method Not Allowed para listagem via GET. O client já possui fallback para repetir a listagem por POST.

## Operações de Pessoas

### Listagem de pessoas

A rota local /pessoas monta um payload com paginação, orgCode, containChild e filtros vazios. A resposta do Defense é normalizada para lidar com estruturas como data.pageData, data.list, rows ou records.

| <p>{<br>"page": 1,<br>"pageSize": 100,<br>"orgCode": "001",<br>"containChild": "0",<br>"personName": ""<br>}</p> |
| ---------------------------------------------------------------------------------------------------------------- |

Depois da listagem, o backend percorre cada pessoa e ajusta faceImageUrl e pictureUrl para inserir o credential correto.

### Criação de pessoa

A criação usa POST em /acs/person. O personId é gerado localmente com base no timestamp e os dados são organizados em blocos como baseInfo, extensionInfo, residentInfo, authenticationInfo, accessInfo, faceComparisonInfo e entranceInfo.

| <p>body = {<br>"baseInfo": {<br>"personId": "\<id\_gerado>",<br>"firstName": "\<nome>",<br>"orgCode": "001",<br>"facePictures": \["\<base64>"]<br>},<br>"extensionInfo": {<br>"idType": "0",<br>"nationalityId": "9999",<br>"companyName": "\<empresa>"<br>}<br>}</p> |
| --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |

### Atualização e remoção

A atualização usa PUT em /acs/person/{person\_id}. A remoção usa POST em /acs/person/delete/batch, enviando personIds e mode. Isso mantém compatibilidade com o padrão de exclusão em lote do OBMS.

| <p>{<br>"personIds": \["\<person\_id>"],<br>"mode": "1"<br>}</p> |
| ---------------------------------------------------------------- |

## Fluxo de Imagem Facial

O módulo image.py processa a foto enviada pelo usuário antes de enviar ao Defense. O objetivo é padronizar a imagem facial e reduzir falhas por tamanho, proporção ou peso de arquivo.

| <p>Upload da imagem<br>-> leitura em bytes<br>-> conversão para RGB<br>-> crop centralizado<br>-> resize para 500 x 500<br>-> compressão JPEG até 100 KB<br>-> Base64<br>-> envio no campo facePictures</p> |
| ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |

| **Parâmetro**    | **Valor usado**       |
| ---------------- | --------------------- |
| Dimensão final   | 500 x 500 pixels      |
| Formato de saída | JPEG                  |
| Tamanho alvo     | até 100 KB            |
| Campo de envio   | baseInfo.facePictures |

Quando a imagem inicial não cabe em 100 KB, a qualidade JPEG é reduzida progressivamente. Se ainda assim não couber, o código usa uma versão reduzida para 250 x 250 como último recurso.

## Exibição de imagens retornadas pelo Defense

Na listagem, o Defense pode retornar URLs protegidas em faceImageUrl e pictureUrl. O client trata faceImageUrl como recorte da face e pictureUrl como imagem de contexto.

| <p>https\://\<IP>/v1/s/.../headPic.jpg?token=credentials<br><br># vira<br><br>https\://\<IP>/v1/s/.../headPic.jpg?token=\<credential></p> |
| ----------------------------------------------------------------------------------------------------------------------------------------- |

A rota /foto funciona como proxy auxiliar para buscar a imagem usando o credential na URL e o token de API no header X-Subject-Token. Esse recurso é útil em testes quando o navegador não confia no certificado HTTPS do Defense.

{% hint style="warning" %}
**Atenção:** em produção, o correto é resolver a cadeia de certificados com uma CA confiável ou certificado válido. verify=False e proxy local de imagem devem ser restritos a laboratório e homologação controlada.
{% endhint %}

## Variáveis de Ambiente

Crie um arquivo .env na raiz do projeto, sem versionar credenciais reais.

| <p>HOST=\<IP\_DO\_DEFENSE><br>PORT=443<br>USERNAME=system<br>PASSWORD=senha\_do\_usuario<br>MAC=AA:BB:CC:DD:EE:FF<br>CLIENT\_TYPE=WINPC\_V2<br>USER\_TYPE=0<br>REQUEST\_TIMEOUT=300</p> |
| --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |

| **Variável**     | **Descrição**                                           |
| ---------------- | ------------------------------------------------------- |
| HOST             | IP ou hostname do servidor Defense.                     |
| PORT             | Porta HTTPS do Defense.                                 |
| USERNAME         | Usuário usado na autenticação.                          |
| PASSWORD         | Senha do usuário.                                       |
| MAC              | MAC address informado no payload de login.              |
| CLIENT\_TYPE     | Tipo de client informado ao Defense. Padrão: WINPC\_V2. |
| USER\_TYPE       | Tipo de usuário informado ao Defense. Padrão: 0.        |
| REQUEST\_TIMEOUT | Timeout das chamadas HTTP, em segundos.                 |

## Como executar

Instale as dependências e inicie o servidor local:

| <p>pip install -r requirements.txt<br>python main.py</p> |
| -------------------------------------------------------- |

Acesse a interface em:

| <http://localhost:8000> |
| ----------------------- |

Dependências declaradas no requirements.txt:

* fastapi
* uvicorn\[standard]
* requests
* pillow
* python-multipart

## Boas Práticas para Evoluir o Client

### Autenticação

* Separe claramente token e credential.
* Use token apenas no header X-Subject-Token.
* Use credential apenas para imagens quando o Defense exigir token na URL.
* Respeite a duração real de sessão retornada pelo Defense quando disponível, em vez de depender apenas de TTL fixo.
* Ao receber 401, invalide o cache e refaça login uma única vez antes de falhar.

### Segurança

* Nunca exponha usuário, senha, token ou credential no frontend.
* Não versionar .env com credenciais reais.
* Evitar logs com token, credential, senha ou payloads sensíveis.
* Usar HTTPS com certificado confiável; não usar verify=False em produção.
* Proteger as rotas locais com autenticação se o client for acessível em rede.
* Adicionar auditoria para operações de criar, editar e remover pessoas.

### Integração HTTP

* Centralizar chamadas em um client único, como service.py.
* Configurar timeout explícito.
* Tratar 401, 405, 422, 500 e falhas de rede com mensagens claras.
* Manter o backend intermediário entre UI e Defense, evitando acoplamento direto do frontend com APIs reais.

## Limitações deste Projeto

* Não implementa autenticação de usuário na própria interface.
* Não possui controle de permissão por perfil.
* Não possui auditoria persistente de operações.
* Não criptografa localmente os segredos do .env.
* Não implementa fila de retry para indisponibilidade do Defense.
* Não possui observabilidade completa, métricas ou tracing.
* Não faz tratamento produtivo de certificados.

<mark style="color:$danger;">**Por isso, o uso deve ficar restrito a desenvolvimento, laboratório, homologação e validação técnica controlada.**</mark>

## Erros comuns e diagnóstico rápido

| **Sintoma**                              | **Causa provável**                                                         | **Ação recomendada**                                                                                  |
| ---------------------------------------- | -------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------- |
| 405 Method Not Allowed ao listar pessoas | Firmware aceita /acs/person/page via POST em vez de GET.                   | Usar o fallback POST já previsto no client.                                                           |
| 401 Unauthorized                         | Token expirado, credenciais incorretas ou parâmetros de login divergentes. | Validar USERNAME, PASSWORD, MAC, CLIENT\_TYPE e USER\_TYPE; verificar se o retry único foi executado. |
| Imagem não aparece na interface          | URL sem credential correto ou navegador não confia no certificado.         | Conferir token=\<credential>, testar abertura direta da URL e revisar certificado.                    |
| ERR\_CERT\_AUTHORITY\_INVALID            | Certificado HTTPS do Defense não é confiável para o navegador.             | Instalar CA confiável ou certificado válido. Evitar bypass em produção.                               |
| 422 ao criar/atualizar pessoa            | Campos obrigatórios ausentes ou multipart/form-data inválido.              | Validar formulário, nomes dos campos e envio de arquivo.                                              |
| Timeout nas chamadas                     | Defense indisponível, rede lenta ou REQUEST\_TIMEOUT insuficiente.         | Validar conectividade, porta, firewall e tempo de resposta do servidor.                               |

{% hint style="warning" %}
**Atenção:** antes de apresentar este client como solução para cliente final, transforme a referência em produto: autenticação própria, permissões, logs seguros, auditoria, tratamento de certificados, validações robustas, testes de contrato e política de deploy.
{% endhint %}


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation by asking a question.

Perform an HTTP GET request on the following URL with the `ask` and `goal` query parameters:

```
GET https://portal-integracao.intelbras.com.br/defense-ia-and-lite/dicas-de-implementacao/guia-de-implementacao-client-para-gestao-de-pessoas.md?ask=<question>&goal=<user_goal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is what the user is ultimately trying to achieve, the reason they need the answer. Sharing it helps GitBook give you a better, more relevant answer. A goal is most helpful when it describes the outcome the user wants rather than restating the question. For example, with `ask=how do I create an API token`, a goal like `automate deployments from our CI pipeline` lets GitBook tailor the answer to that use case.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
