Skip to main content
Glama
backend_documentation.md23.5 kB
# Documentação Técnica do Backend (MCP Gateway) Esta documentação visa fornecer um entendimento detalhado da API backend para facilitar o desenvolvimento de testes automatizados e a manutenção do sistema. **1. Visão Geral** O backend do MCP Gateway é uma API RESTful construída com FastAPI (Python). Ele gerencia usuários, grupos, ferramentas e o acesso a essas ferramentas através de um sistema de controle de acesso baseado em papéis (RBAC). Os dados são persistidos em arquivos JSON (`data/rbac.json` e `data/requests.json`). **2. Configuração** * **Arquivo Principal:** `app/main.py` * **Variáveis de Ambiente (via `.env`):** * `RBAC_FILE`: Caminho para o arquivo de dados RBAC (padrão: `data/rbac.json`). * **Constantes de Configuração (`app/config.py`):** * `settings.SECRET_KEY`: Chave secreta para assinatura de JWTs (atualmente 'changeme', **deve ser alterada para produção**). * `settings.RBAC_FILE`: Caminho para o arquivo RBAC, derivado da variável de ambiente ou padrão. **3. Autenticação e Autorização** * **Módulo:** `app/auth.py` * **Fluxo de Autenticação:** 1. Cliente envia `username` e `password` para `POST /tools/login`. 2. `authenticate_user` verifica as credenciais. * Suporta senhas em texto plano (legado) e hashes bcrypt. 3. Se válido, `create_jwt_for_user` gera um JWT. 4. JWT é retornado ao cliente. * **Token JWT:** * **Payload:** `sub` (username), `grupos` (lista de nomes de grupos), `papel` (`user`, `admin`, `global_admin`), `exp` (timestamp de expiração). * **Algoritmo:** HS256. * **Validação:** `get_current_user` (dependência FastAPI) valida o token em rotas protegidas. * **Papéis:** * `user`: Acesso básico, pode solicitar entrada em grupos e usar ferramentas de seus grupos. * `admin`: Permissões de `user`, mais gerenciar usuários e ferramentas dentro dos grupos que administra. Pode aprovar/rejeitar solicitações para seus grupos. * `global_admin`: Controle total sobre todos os usuários, grupos e ferramentas. **4. Estrutura de Dados (Persistência)** * **`data/rbac.json`:** * `grupos`: Dicionário. Chave é o nome do grupo. * `descricao`: String (opcional). * `admins`: Lista de usernames dos administradores do grupo. * `users`: Lista de usernames dos membros do grupo. * `ferramentas`: Lista de IDs de ferramentas disponíveis para o grupo. * `usuarios`: Dicionário. Chave é o username. * `senha`: String (hash bcrypt ou texto plano legado). * `grupos`: Lista de nomes de grupos aos quais o usuário pertence. * `papel`: String (`user`, `admin`, `global_admin`). * `ferramentas`: Dicionário (opcional, para definições globais de ferramentas). Chave é o ID da ferramenta. * `nome`: String (nome amigável). * `url_base`: String (URL da ferramenta). * `descricao`: String (opcional). * **`data/requests.json`:** * `requests`: Lista de objetos de solicitação de acesso. * `request_id`: UUID. * `username`: Username do solicitante. * `grupo`: Nome do grupo solicitado. * `status`: `pending`, `approved`, `rejected`. * `justificativa`: String. * `created_at`: ISO datetime. * `updated_at`: ISO datetime (opcional). * `reviewed_by`: Username do admin que revisou (opcional). * `review_comment`: Comentário da revisão (opcional). **5. Endpoints da API** Os endpoints são divididos em dois roteadores principais montados sob o prefixo `/tools`. **5.1. Roteador Principal (`app/groups/routes.py`)** * **Tag: Auth** * `POST /login` * **Descrição:** Autentica um usuário. * **Request Body:** `{"username": "str", "password": "str"}` * **Response (200):** `{"access_token": "jwt_string", "token_type": "bearer"}` * **Response (400):** "Usuário e senha obrigatórios." * **Response (401):** "Usuário ou senha inválidos" * **Response (500):** Erro interno. * `POST /refresh-token` * **Descrição:** Renova um token JWT para o usuário autenticado. * **Auth:** Requer token JWT válido. * **Response (200):** `{"access_token": "new_jwt_string", "token_type": "bearer"}` * **Response (500):** Erro interno. * **Tag: Infra** * `GET /health` * **Descrição:** Verifica a saúde da aplicação. * **Response (200):** `{"status": "ok"}` * **Tag: Admin (Gerenciamento de Grupos)** * `GET /grupos` * **Descrição:** Lista todos os grupos com detalhes. * **Auth:** `global_admin`. * **Response (200):** `List[GroupDetailSchema]` (ver modelos Pydantic abaixo). * **Response (403):** "Acesso restrito ao admin global." * `POST /grupos` * **Descrição:** Cria um novo grupo. * **Auth:** `global_admin`. * **Request Body:** `CreateGroupRequest` (`{"nome": "str", "descricao": "Optional[str]"}`) * **Response (200):** `{"message": "Grupo '<nome>' criado com sucesso."}` * **Response (400):** "Nome do grupo é obrigatório." * **Response (403):** "Acesso restrito ao admin global." * **Response (409):** "Grupo já existe." * `PUT /grupos/{grupo}` * **Descrição:** Edita nome e/ou descrição de um grupo. * **Auth:** `global_admin`. * **Path Param:** `grupo` (nome do grupo a editar). * **Request Body:** `EditGroupRequest` (`{"nome": "Optional[str]", "descricao": "Optional[str]"}`) * **Response (200):** `{"message": "Grupo '<novo_nome_ou_nome_antigo>' editado com sucesso."}` ou "Nenhuma alteração fornecida." * **Response (403):** "Acesso restrito ao admin global." * **Response (404):** "Grupo não encontrado." * **Response (409):** "Já existe um grupo com o nome '<novo_nome>'." * `DELETE /grupos/{grupo}` * **Descrição:** Remove um grupo. * **Auth:** `global_admin`. * **Path Param:** `grupo` (nome do grupo a remover). * **Response (200):** `{"message": "Grupo '<nome>' removido com sucesso."}` * **Response (403):** "Acesso restrito ao admin global." * **Response (404):** "Grupo não encontrado." * `POST /grupos/{grupo}/admins` * **Descrição:** Designa um usuário como administrador de um grupo. * **Auth:** `global_admin`. * **Path Param:** `grupo`. * **Request Body:** `{"username": "str"}` * **Response (200):** `{"message": "Usuário '<username>' agora é admin do grupo '<grupo>'"}` * **Response (400):** "Usuário inválido." ou "Usuário '<username>' não é membro do grupo '<grupo>'. Adicione como membro primeiro." * **Response (403):** "Acesso restrito ao admin global." * **Response (404):** "Grupo não encontrado." * `DELETE /grupos/{grupo}/admins/{username_param}` * **Descrição:** Remove um administrador de um grupo. * **Auth:** `global_admin` ou `admin` do grupo. * **Path Params:** `grupo`, `username_param` (admin a ser removido). * **Response (200):** `{"message": "Usuário '<username_param>' não é mais admin do grupo '<grupo>'."}` * **Response (400):** "Não é possível remover o último administrador do grupo." (se não for `global_admin`). * **Response (403):** "Acesso restrito ao admin global ou admin do grupo." * **Response (404):** "Grupo '<grupo>' não encontrado.", "Usuário admin '<username_param>' não encontrado.", "Usuário '<username_param>' não é admin do grupo '<grupo>'." * `POST /grupos/{grupo}/usuarios` * **Descrição:** Adiciona um usuário a um grupo. * **Auth:** `global_admin` ou `admin` do grupo. * **Path Param:** `grupo`. * **Request Body:** `{"username": "str"}` * **Response (200):** `{"message": "Usuário '<username>' adicionado ao grupo '<grupo>'"}` * **Response (400):** "Usuário inválido." * **Response (403):** "Acesso restrito ao admin do grupo ou global." * **Response (404):** "Grupo não encontrado." * `DELETE /grupos/{grupo}/usuarios/{username}` * **Descrição:** Remove um usuário de um grupo. * **Auth:** `global_admin` ou `admin` do grupo. * **Path Params:** `grupo`, `username` (usuário a ser removido). * **Response (200):** `{"message": "Usuário '<username>' removido do grupo '<grupo>'"}` * **Response (403):** "Acesso restrito ao admin do grupo ou global." * **Response (404):** "Grupo não encontrado." ou "Usuário não está no grupo." * `POST /grupos/{grupo}/promover-admin` * **Descrição:** Promove um membro do grupo a administrador do grupo. * **Auth:** `global_admin` ou `admin` do grupo. * **Path Param:** `grupo`. * **Request Body:** `{"username": "str"}` * **Response (200):** `{"message": "Usuário '<username>' agora é admin do grupo '<grupo>'"}` * **Response (400):** "Usuário inválido." * **Response (403):** "Acesso restrito ao admin do grupo ou global." * **Response (404):** "Grupo não encontrado." * `GET /grupos/{grupo}/usuarios` * **Descrição:** Lista administradores e usuários de um grupo. * **Auth:** `global_admin` ou `admin` do grupo (ou membro do grupo para ver a lista). * **Path Param:** `grupo`. * **Response (200):** `{"admins": List[str], "users": List[str]}` * **Response (403):** "Acesso restrito..." (se não for membro nem admin). * **Response (404):** "Grupo não encontrado." * `POST /grupos/{grupo}/ferramentas` * **Descrição:** Adiciona uma ferramenta existente (global) a um grupo. * **Auth:** `global_admin` ou `admin` do grupo. * **Path Param:** `grupo`. * **Request Body:** `{"tool_id": "str"}` (ID da ferramenta global). * **Response (200):** `{"message": "Ferramenta '<tool_id>' adicionada com sucesso ao grupo '<grupo>'"}` * **Response (400):** "ID da ferramenta (tool_id) é obrigatório." * **Response (403):** "Acesso restrito ao admin do grupo ou global." * **Response (404):** "Grupo não encontrado." ou "Ferramenta com ID '<tool_id>' não encontrada nas definições globais." * **Response (409):** "Ferramenta '<tool_id>' já existe no grupo '<grupo>'." * `DELETE /grupos/{grupo}/ferramentas/{tool_id}` * **Descrição:** Remove uma ferramenta de um grupo. * **Auth:** `global_admin` ou `admin` do grupo. * **Path Params:** `grupo`, `tool_id`. * **Response (200):** `{"message": "Ferramenta '<tool_id>' removida com sucesso do grupo '<grupo>'"}` * **Response (403):** "Acesso restrito ao admin do grupo ou global." * **Response (404):** "Grupo '<grupo>' não encontrado." ou "Ferramenta '<tool_id>' não encontrada no grupo '<grupo>'." * **Tag: Ferramentas (Gerenciamento Global e Acesso)** * `GET /ferramentas` * **Descrição:** Lista todas as ferramentas definidas globalmente no sistema. * **Auth:** Requer token JWT válido (qualquer usuário autenticado). * **Response (200):** `List[ToolResponseSchema]` * `GET /ferramenta_x`, `GET /ferramenta_y`, `GET /ferramenta_z` (e seus `OPTIONS`) * **Descrição:** Endpoints de exemplo para acessar ferramentas. * **Auth:** Requer token JWT válido. Acesso permitido se o usuário for `global_admin` ou pertencer a um grupo que tenha a ferramenta. * **Response (200):** `{"result": "Execução da ferramenta <nome> por <username>"}` * **Response (403):** "Acesso negado" * `GET /user_tools` * **Descrição:** Lista todas as ferramentas que o usuário logado pode acessar, com base nos seus grupos. * **Auth:** Requer token JWT válido. * **Response (200):** `List[ToolResponseSchema]` * **Response (403):** "Usuário não identificado." * **Tag: Admin (Gerenciamento de Usuários Globais)** * `POST /usuarios` * **Descrição:** Cria um novo usuário. * **Auth:** `global_admin`. * **Request Body:** `{"username": "str", "password": "str", "papel": "Optional[str]", "grupos": "Optional[List[str]]"}` * **Response (200):** `{"message": "Usuário '<username>' criado com sucesso."}` * **Response (400):** "Username e password são obrigatórios.", "Papel inválido...", "Grupo '<grupo>' não existe.", "A senha não atende aos requisitos..." * **Response (403):** "Acesso restrito ao admin global." * **Response (409):** "Usuário já existe." * `GET /usuarios` * **Descrição:** Lista todos os usuários. * **Auth:** `global_admin`. * **Response (200):** `List[UserDetailResponse]` * **Response (403):** "Acesso restrito ao admin global." * `GET /usuarios/{username_param}` * **Descrição:** Obtém detalhes de um usuário específico. * **Auth:** `global_admin` ou o próprio usuário solicitando seus dados. * **Path Param:** `username_param`. * **Response (200):** `UserDetailResponse` * **Response (403):** "Acesso restrito." (Se não for global_admin nem o próprio usuário) * **Response (404):** "Usuário '<username_param>' não encontrado." * `PUT /usuarios/{username_param}` * **Descrição:** Atualiza o papel e/ou grupos de um usuário. * **Auth:** `global_admin`. * **Path Param:** `username_param`. * **Request Body:** `UserUpdateRequest` (`{"papel": "Optional[str]", "grupos": "Optional[List[str]]"}`) * **Response (200):** `UserDetailResponse` (com os dados atualizados). * **Response (400):** "Papel inválido...", "Grupo '<grupo_nome>' não encontrado.", "Não é possível remover o último administrador global." * **Response (403):** "Acesso restrito ao admin global." * **Response (404):** "Usuário '<username_param>' não encontrado." * `DELETE /usuarios/{username_param}` * **Descrição:** Deleta um usuário. * **Auth:** `global_admin`. * **Path Param:** `username_param`. * **Response (200):** `{"message": "Usuário '<username_param>' deletado com sucesso."}` * **Response (400):** "Não é possível deletar a si mesmo." * **Response (403):** "Acesso restrito ao admin global." * **Response (404):** "Usuário '<username_param>' não encontrado." * `POST /admin/migrate-passwords` * **Descrição:** Migra senhas em texto plano para hashes bcrypt no `rbac.json`. * **Auth:** `global_admin`. * **Response (200):** `{"message": "Migração de senhas concluída com sucesso."}` * **Response (403):** "Acesso restrito ao admin global." * **Response (500):** "Erro ao migrar senhas." * **Tag: User (Funcionalidades do Usuário)** * `POST /usuarios/alterar-senha` * **Descrição:** Permite ao usuário logado alterar sua própria senha. * **Auth:** Requer token JWT válido. * **Request Body:** `{"senha_atual": "str", "nova_senha": "str"}` * **Response (200):** `{"message": "Senha alterada com sucesso"}` * **Response (400):** "Senha atual e nova senha são obrigatórias", "A nova senha não pode ser igual à senha atual", "A senha não atende aos requisitos..." * **Response (401):** "Senha atual incorreta" * `GET /usuarios/requisitos-senha` * **Descrição:** Retorna os requisitos de segurança para senhas. * **Auth:** Nenhuma (público, mas geralmente acessado por usuários logados ou na tela de criação/alteração de senha). * **Response (200):** Dicionário com os requisitos (min_length, require_uppercase, etc.). * **Tag: Grupos (Descoberta)** * `GET /grupos/disponivel` * **Descrição:** Lista grupos dos quais o usuário logado não faz parte (para solicitar acesso). * **Auth:** Requer token JWT válido. * **Response (200):** `{"grupos": List[str]}` **5.2. Roteador de Solicitações (`app/groups/requests_routes.py`)** * **Prefixo:** `/requests` (resultando em `/tools/requests/...`) * **Tag: Solicitações** * `POST /` * **Descrição:** Cria uma nova solicitação de acesso a um grupo. * **Auth:** Requer token JWT válido. * **Request Body:** `GroupAccessRequestCreate` (`{"grupo": "str", "justificativa": "str"}`) * **Response (200):** `GroupAccessRequestResponse` * **Response (400):** "Você já pertence ao grupo '<grupo>'" * **Response (404):** "Grupo '<grupo>' não encontrado" * `GET /me` * **Descrição:** Lista todas as solicitações do usuário atual. * **Auth:** Requer token JWT válido. * **Response (200):** `List[GroupAccessRequestResponse]` * `GET /admin` * **Descrição:** Lista solicitações pendentes para grupos onde o usuário é admin (ou todas para `global_admin`). * **Auth:** `admin` ou `global_admin`. * **Response (200):** `List[GroupAccessRequestResponse]` * **Response (403):** "Acesso restrito a administradores" * `GET /{request_id}` * **Descrição:** Obtém detalhes de uma solicitação específica. * **Auth:** Usuário da solicitação, `admin` do grupo da solicitação, ou `global_admin`. * **Path Param:** `request_id`. * **Response (200):** `GroupAccessRequestResponse` * **Response (403):** "Sem permissão para acessar esta solicitação" * **Response (404):** "Solicitação não encontrada" * `POST /{request_id}/review` * **Descrição:** Aprova ou rejeita uma solicitação de acesso. * **Auth:** `admin` do grupo da solicitação ou `global_admin`. * **Path Param:** `request_id`. * **Request Body:** `GroupAccessRequestReview` (`{"status": "RequestStatus ('approved'|'rejected')", "comment": "Optional[str]"}`) * **Response (200):** `GroupAccessRequestResponse` (com o status atualizado). * **Response (400):** "Solicitação já foi <status>" * **Response (403):** "Acesso restrito a administradores" ou "Sem permissão para administrar este grupo" * **Response (404):** "Solicitação não encontrada" * **Response (500):** "Erro ao processar revisão" ou "Solicitação aprovada, mas houve erro ao adicionar usuário ao grupo" (se `apply_approved_request` falhar). **6. Modelos Pydantic Principais (Schemas)** * **`app/groups/routes.py`:** * `ToolResponseSchema(BaseModel)`: * `id: str` (nome/ID da ferramenta) * `nome: str` * `url_base: str` * `descricao: Optional[str]` * `GroupDetailSchema(BaseModel)`: * `id: str` (nome do grupo) * `nome: str` * `descricao: Optional[str]` * `administradores: List[str]` * `usuarios: List[str]` * `ferramentas_disponiveis: List[ToolResponseSchema]` * `UserDetailResponse(BaseModel)`: * `username: str` * `papel: str` * `grupos: List[str]` * `UserUpdateRequest(BaseModel)`: * `papel: Optional[str]` * `grupos: Optional[List[str]]` * `ferramentas_disponiveis: List[ToolResponseSchema]` (Nota: este campo parece desalinhado com a lógica do endpoint `PUT /usuarios/{username_param}`, que não atualiza ferramentas diretamente no usuário, mas sim seus grupos e papel). * `CreateGroupRequest(BaseModel)`: * `nome: str` * `descricao: Optional[str]` * `EditGroupRequest(BaseModel)`: * `nome: Optional[str]` * `descricao: Optional[str]` * **`app/models/requests.py`:** * `RequestStatus(str, Enum)`: `PENDING`, `APPROVED`, `REJECTED`. * `GroupAccessRequestCreate(BaseModel)`: * `grupo: str` * `justificativa: str` (min_length=5, max_length=500) * `GroupAccessRequestReview(BaseModel)`: * `status: RequestStatus` * `comment: Optional[str]` (max_length=500) * `GroupAccessRequestResponse(BaseModel)`: * `request_id: str` * `username: str` * `grupo: str` * `status: RequestStatus` * `justificativa: str` * `created_at: datetime` * `updated_at: Optional[datetime]` * `reviewed_by: Optional[str]` * `review_comment: Optional[str]` **7. Lógica de Negócios Auxiliar** * **`app/utils/password_validator.py`:** * `PasswordValidator` class e `default_validator` instance. * `validate_password()`: Verifica comprimento mínimo, maiúsculas, minúsculas, dígitos, caracteres especiais, caracteres únicos. * `get_requirements_text()`: Gera texto descritivo dos requisitos. * **`app/utils/password.py`:** * `hash_password()`: Gera hash bcrypt (usado na migração). * `migrate_rbac_passwords()`: Itera sobre usuários no `rbac.json` e converte senhas em texto plano para hash. * **`app/utils/request_manager.py`:** * Gerencia o ciclo de vida das solicitações de acesso no `requests.json`. * `apply_approved_request()`: Modifica o `rbac.json` para adicionar um usuário a um grupo após a aprovação da solicitação. Garante que o usuário seja adicionado à lista `users` do grupo e à lista `grupos` do próprio usuário. **8. Considerações para Testes (Resumo)** * **Testes Unitários:** * Funções em `app/auth.py` (verificação de senha, hashing, criação/validação de JWT). * Funções em `app/utils/password_validator.py`. * Funções em `app/utils/password.py`. * Funções em `app/utils/request_manager.py` (simulando a leitura/escrita de arquivos JSON). * Função `has_permission` em `app/groups/routes.py`. * **Testes de Integração (API Endpoints):** * Para cada endpoint, testar: * **Caminho Feliz:** Requisição válida com permissões corretas. * **Validação de Payload:** Campos ausentes, tipos incorretos, valores inválidos. * **Autorização:** Tentativas de acesso com papéis insuficientes ou sem ser o "dono" do recurso. * **Casos de Borda:** Entidades não encontradas (404), conflitos (409), remoção do último admin, etc. * **Respostas:** Código de status HTTP e schema do corpo da resposta. * **Efeitos Colaterais:** Verificar se os arquivos `rbac.json` e `requests.json` são atualizados corretamente. * **Fluxos Completos:** * Criação de usuário -> Login -> Solicitação de acesso a grupo -> Aprovação pelo admin -> Verificação de acesso à ferramenta do grupo. * Criação de grupo -> Adição de usuário -> Promoção a admin -> Remoção de admin. * Alteração de senha. * Migração de senhas.

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/jowpereira/mcp-server'

If you have feedback or need assistance with the MCP directory API, please join our Discord server