Novo Ambiente | Atualização 1

This commit is contained in:
Vitoria Azevedo 2025-01-22 12:13:52 -03:00
parent b3f4d4b31c
commit d2c8745c45
48 changed files with 672 additions and 719 deletions

View File

@ -7,6 +7,17 @@ MYSQL_PASSWORD=wNTDu1k79z{(
MYSQL_DB=itguys
SMTP_USERNAME=contato.site@itguys.com.br
SMTP_PASSWORD=j5j@QaSB\Z4<)W]M|hOYbC\605zfGcv:
SMTP_HOST=172.16.150.150
SMTP_HOST=mail.itguys.com.br
SMTP_PORT=465
SMTP_RECIPIENT=comercial@itguys.com.br
SMTP_RECIPIENT=comercial@itguys.com.br
# Configurações LDAP (Windows AD)
LDAP_SERVER=10.10.253.199/itguys.com.br
LDAP_DOMAIN=itguys.com.br
LDAP_BASE_DN=dc=itguys,dc=com,dc=br
LDAP_USER=itguys\teste.dev
LDAP_PASSWORD=123Mudar
ZAMMAD_API=http://10.10.253.59/api/v1/
ZAMMAD_USERNAME=teste.dev@itguys.com.br
ZAMMAD_PASSWORD=123Mudar

Binary file not shown.

Before

Width:  |  Height:  |  Size: 88 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 90 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 90 KiB

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

43
app/__init__.py Normal file
View File

@ -0,0 +1,43 @@
from flask import Flask, send_from_directory
from flask_mysqldb import MySQL
from .config import Config
import os
# Importa os blueprints
from .routes.auth import auth
from .routes.perfil import perfil
from .routes.montagem import montagem
from .routes.zabbix import zabbix
from .routes.zammad import zammad
# Inicializa o MySQL
mysql = MySQL()
# Inicializa a aplicação Flask e outras extensões
def create_app():
app = Flask(__name__)
app.config.from_object(Config)
# Caminho para o diretório onde estão as imagens
app.config['IMAGE_FOLDER'] = '/var/www/Backend/itguys/ambiente_python/repositorio_img'
# Registra o endpoint para servir as imagens
@app.route('/repositorio_img/<path:filename>')
def serve_image(filename):
return send_from_directory(app.config['IMAGE_FOLDER'], filename)
# Inicializa o MySQL
mysql.init_app(app)
# Registra os blueprints
app.register_blueprint(auth)
app.register_blueprint(perfil)
app.register_blueprint(montagem)
app.register_blueprint(zabbix)
app.register_blueprint(zammad)
@app.route('/')
def home():
return "Aplicação Flask funcionando!"
return app

Binary file not shown.

Binary file not shown.

32
app/config.py Normal file
View File

@ -0,0 +1,32 @@
from dotenv import load_dotenv
import os
# Carrega as variáveis de ambiente do arquivo .env, se existir
load_dotenv()
class Config:
# Configurações Gerais
SECRET_KEY = os.getenv('SECRET_KEY', 'd702717e2361ba1a31ce3b98d28ee3e24e2e6a9be8e2afd8e004f842563bacbd')
DEBUG = os.getenv('DEBUG', 'True') == 'True'
JSON_SORT_KEYS = False
# Configurações do MySQL
MYSQL_HOST = os.getenv('MYSQL_HOST', '10.10.253.56')
MYSQL_USER = os.getenv('MYSQL_USER', 'remote')
MYSQL_PASSWORD = os.getenv('MYSQL_PASSWORD', 'wNTDu1k79z{(')
MYSQL_DB = os.getenv('MYSQL_DB', 'itguys')
MYSQL_CURSORCLASS = 'DictCursor'
# Configurações do LDAP (Windows AD)
LDAP_SERVER = os.getenv('LDAP_SERVER', 'ldap://itguys.com.br') # IP/Hostname do servidor LDAP
LDAP_DOMAIN = os.getenv('LDAP_DOMAIN', 'itguys.com.br') # Domínio do AD (FQDN)
LDAP_BASE_DN = os.getenv('LDAP_BASE_DN', 'dc=itguys,dc=com,dc=br') # Distinguished Name (DN)
LDAP_USER = os.getenv('LDAP_USER', 'itguys\\teste.dev') # Usuário de serviço LDAP (formato DOMINIO\\Usuario)
LDAP_PASSWORD = os.getenv('LDAP_PASSWORD', '123Mudar') # Senha do usuário LDAP
#Api Zammad
zammad_api_url = 'http://10.10.253.59/api/v1'
zammad_token = 'L081vJ19kood2uDlTSSt59LWBaewT9-a-MH_VKno8RY'

2
app/routes/__init__.py Normal file
View File

@ -0,0 +1,2 @@
from .auth import auth

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

156
app/routes/auth.py Normal file
View File

@ -0,0 +1,156 @@
from flask import Blueprint, request, jsonify, current_app
import jwt
from datetime import datetime, timedelta
import re
import ldap3
from ldap3.core.exceptions import LDAPSocketOpenError, LDAPException
from functools import wraps
from flask_mysqldb import MySQL
import logging
# Configuração de logging para depuração
logging.basicConfig(level=logging.DEBUG)
auth = Blueprint('auth', __name__)
mysql = MySQL()
# Decorador para validar o token JWT
def token_required(f):
@wraps(f)
def wrapper(*args, **kwargs):
token = request.headers.get('x-access-token')
if not token:
return jsonify({'msg': 'Token é necessário!'}), 401
try:
data = jwt.decode(token, current_app.config['SECRET_KEY'], algorithms=["HS256"])
except jwt.ExpiredSignatureError:
# Caso o token esteja expirado, gere um novo token
try:
# Obter o payload original para criar um novo token
payload = jwt.decode(token, current_app.config['SECRET_KEY'], algorithms=["HS256"], options={"verify_exp": False})
# Gere um novo token com nova validade
new_token = jwt.encode({
'user_id': payload['user_id'],
'exp': datetime.datetime.utcnow() + datetime.timedelta(hours=1)
}, current_app.config['SECRET_KEY'], algorithm="HS256")
response = jsonify({'msg': 'Token expirado! Novo token gerado.', 'new_token': new_token})
response.status_code = 401
return response
except Exception as e:
return jsonify({'msg': 'Erro ao processar o token expirado.', 'error': str(e)}), 401
except jwt.InvalidTokenError:
return jsonify({'msg': 'Token inválido!'}), 401
return f(data, *args, **kwargs)
return wrapper
# Validação de SQL Injection
def is_valid_input(input_str):
sql_injection_pattern = r"(--|\b(SELECT|UNION|INSERT|UPDATE|DELETE|DROP|CREATE|ALTER)\b|;|'|\")"
return not re.search(sql_injection_pattern, input_str, re.IGNORECASE)
# Validação de e-mail
def is_valid_email(email):
return re.match(r"[^@]+@[^@]+\.[^@]+", email)
# Rota de login
@auth.route('/login', methods=['POST'])
def login():
data = request.get_json()
username_full = data.get('username')
password = data.get('password')
if not username_full or not password:
return jsonify({'msg': 'Usuário e senha são obrigatórios.'}), 400
if not is_valid_input(username_full) or not is_valid_input(password):
return jsonify({'msg': 'Solicitação rejeitada devido a padrões maliciosos.'}), 422
if not is_valid_email(username_full):
return jsonify({'msg': 'Formato de e-mail inválido'}), 400
# Separar nome de usuário e domínio
try:
username, domain = username_full.split('@')
print(f" Domínio extraído: {domain}") # <-- Adiciona este print
except ValueError:
return jsonify({'msg': 'Formato de usuário inválido. Use "usuario@dominio.com".'}), 400
# Buscar informações de domínio no banco de dados
try:
with mysql.connection.cursor() as cur:
cur.execute("SELECT idempresa, ip_dominio FROM empresa WHERE dominio = %s", (domain,))
empresa_result = cur.fetchone()
if not empresa_result:
return jsonify({'msg': 'Domínio não encontrado no banco de dados'}), 404
id_empresa, ip_dominio = empresa_result
# Verificar se o usuário está associado à empresa no MySQL
with mysql.connection.cursor() as cur:
cur.execute("SELECT empresa_id, dominio FROM view_usuario_empresa WHERE usuario = %s AND dominio = %s", (username, domain))
result = cur.fetchone()
if not result:
return jsonify({'msg': 'Usuário não associado à empresa'}), 404
except Exception as e:
logging.error(f"Erro no banco de dados: {str(e)}")
return jsonify({'msg': 'Erro no banco de dados'}), 500
# Conexão LDAP com autenticação
ldap_server = f'ldap://{"itguys.com.br:389"}' # Altere para 'ldaps://{ip_dominio}:636' se LDAPS for usado
ldap_user = f'{domain}\\{username}' # Formato DOMAIN\username
try:
server = ldap3.Server(ldap_server, get_info=ldap3.ALL)
conn = ldap3.Connection(
server,
user=username_full,
password=password,
authentication=ldap3.SIMPLE
)
if conn.bind():
token = jwt.encode({
'user': username_full,
'exp': datetime.utcnow() + timedelta(hours=1)
}, current_app.config['SECRET_KEY'], algorithm="HS256")
conn.unbind()
logging.info(f"Login bem-sucedido para usuário: {username_full}")
return jsonify({'msg': 'Login bem-sucedido', 'token': token}), 200
else:
conn.unbind()
logging.warning(f"Falha na autenticação LDAP para o usuário: {username_full}")
return jsonify({'msg': 'Falha na autenticação LDAP. Verifique credenciais.'}), 401
except LDAPSocketOpenError:
logging.error("Erro de conexão com o servidor LDAP.")
return jsonify({'msg': 'Não foi possível conectar ao servidor LDAP. Verifique o IP e a porta.'}), 503
except LDAPException as e:
logging.error(f"Erro LDAP: {str(e)}")
return jsonify({'msg': 'Erro durante a autenticação no servidor LDAP.'}), 500
except Exception as e:
logging.error(f"Erro inesperado: {str(e)}")
return jsonify({'msg': 'Erro inesperado durante a autenticação.'}), 500

26
app/routes/config.py Normal file
View File

@ -0,0 +1,26 @@
import os
from dotenv import load_dotenv
# Carrega as variáveis de ambiente do arquivo .env, se existir
load_dotenv()
class Config:
# Configurações Gerais
SECRET_KEY = os.getenv('SECRET_KEY', 'd702717e2361ba1a31ce3b98d28ee3e24e2e6a9be8e2afd8e004f842563bacbd')
DEBUG = os.getenv('DEBUG', 'True') == 'True'
JSON_SORT_KEYS = False
# Configurações do MySQL
MYSQL_HOST = os.getenv('MYSQL_HOST', '10.10.253.56')
MYSQL_USER = os.getenv('MYSQL_USER', 'remote')
MYSQL_PASSWORD = os.getenv('MYSQL_PASSWORD', 'wNTDu1k79z{(')
MYSQL_DB = os.getenv('MYSQL_DB', 'itguys')
MYSQL_CURSORCLASS = 'DictCursor'
# Configurações do LDAP (Windows AD)
LDAP_SERVER = os.getenv('LDAP_SERVER', 'ldap://itguys.com.br') # IP/Hostname do servidor LDAP
LDAP_DOMAIN = os.getenv('LDAP_DOMAIN', 'itguys.com.br') # Domínio do AD (FQDN)
LDAP_BASE_DN = os.getenv('LDAP_BASE_DN', 'dc=itguys,dc=com,dc=br') # Distinguished Name (DN)
LDAP_USER = os.getenv('LDAP_USER', 'itguys\\teste.dev') # Usuário de serviço LDAP (formato DOMINIO\\Usuario)
LDAP_PASSWORD = os.getenv('LDAP_PASSWORD', '123Mudar') # Senha do usuário LDAP

60
app/routes/montagem.py Normal file
View File

@ -0,0 +1,60 @@
from flask import Blueprint, request, jsonify, current_app
from flask_mysqldb import MySQL
import jwt
from .auth import token_required # Importa o token_required de auth.py
montagem = Blueprint('montagem', __name__, static_folder='repositorio_img')
mysql = MySQL()
@montagem.route('/mounting', methods=['GET'])
@token_required # Verificação de token para autenticação
def mounting(data):
username_full = data['user']
# Verifica se o usuário está autenticado
if not username_full:
return jsonify({'msg': 'Token inválido!'}), 401
# Extrair o nome de usuário e o domínio do email do usuário autenticado
username = username_full.split('@')[0]
domain = username_full.split('@')[1]
# Consultar o banco de dados para obter as informações do usuário (nome, img_perfil, img_fundo)
cur = mysql.connection.cursor()
cur.execute("SELECT nome, img_perfil, img_fundo FROM usuarios WHERE usuario = %s", (username,))
usuario_result = cur.fetchone()
if usuario_result is None:
cur.close()
return jsonify({'msg': 'Usuário não encontrado no banco de dados'}), 404
nome_usuario, img_perfil_path, img_fundo_path = usuario_result
perfil_MP = usuario_result['img_perfil']
fundo_MP = usuario_result['img_fundo']
# Consultar o banco de dados para obter o nome e o logo da empresa com base no domínio
cur.execute("SELECT nome, logo FROM empresa WHERE dominio = %s", (domain,))
empresa_result = cur.fetchone()
cur.close()
if empresa_result is None:
return jsonify({'msg': 'Empresa não encontrada no banco de dados para o domínio fornecido'}), 404
nome_empresa, logo_empresa_path = empresa_result
logo_MP = empresa_result['logo']
# Montar a resposta JSON com os caminhos relativos das imagens e outras informações
response_data = {
'usuario': {
'nome': username,
'img_perfil': f"/repositorio_img/{perfil_MP}", # Caminho relativo para a imagem de perfil
'img_fundo': f"/repositorio_img/{fundo_MP}" # Caminho relativo para a imagem de fundo
},
'empresa': {
'nome': domain,
'logo': f"/repositorio_img/{logo_MP}" # Caminho relativo para o logo da empresa
}
}
return jsonify(response_data), 200

36
app/routes/perfil.py Normal file
View File

@ -0,0 +1,36 @@
import os
import mimetypes
from flask import Blueprint, jsonify, send_file
from .auth import token_required
perfil = Blueprint('perfil', __name__)
def get_protected_image_path(filename):
# Definindo o caminho base UNC corretamente, sem modificá-lo
base_directory = r"\\10.10.253.56\\Backend\\itguys\\ambiente_python\\repositorio_img"
# Concatene o caminho do arquivo com base_directory
file_path = os.path.join(base_directory, filename)
print(f"Caminho gerado para a imagem: {file_path}")
# Verifique se o caminho de file_path está dentro do diretório base_directory
if os.path.commonpath([base_directory, file_path]) == base_directory:
return file_path
else:
return None
@perfil.route('/repositorio_img/<path:filename>', methods=['GET'])
@token_required
def get_profile_picture(data, filename):
file_path = get_protected_image_path(filename)
if file_path and os.path.isfile(file_path):
mimetype, _ = mimetypes.guess_type(file_path)
if not mimetype:
mimetype = 'application/octet-stream'
return send_file(file_path, mimetype=mimetype)
else:
return jsonify({'msg': 'Imagem não encontrada ou acesso não permitido'}), 404

189
app/routes/zabbix.py Normal file
View File

@ -0,0 +1,189 @@
from flask import Blueprint, jsonify, request
from flask_mysqldb import MySQL
import requests
import json
from MySQLdb.cursors import DictCursor
zabbix = Blueprint('zabbix', __name__)
mysql = MySQL()
# Função para buscar as configurações dos servidores Zabbix do banco de dados
def get_zabbix_configs():
cursor = mysql.connection.cursor(DictCursor) # Usando DictCursor para retornar dicionários
query = "SELECT * FROM connections_zabbix"
cursor.execute(query)
configs = cursor.fetchall()
cursor.close()
return configs
# Função para autenticar no Zabbix via API JSON-RPC
def zabbix_login(url, username, password):
headers = {'Content-Type': 'application/json-rpc'}
payload = {
"jsonrpc": "2.0",
"method": "user.login",
"params": {
"username": username,
"password": password
},
"id": 1,
"auth": None
}
response = requests.post(url, headers=headers, data=json.dumps(payload))
result = response.json()
if 'result' in result:
return result['result'] # Retorna o token de autenticação
else:
raise Exception(f"Erro na autenticação: {result}")
# Funções auxiliares para buscar dados no Zabbix
def get_zabbix_host_groups(auth_token, url):
headers = {'Content-Type': 'application/json-rpc'}
payload = {
"jsonrpc": "2.0",
"method": "hostgroup.get",
"params": {
"output": ["groupid", "name"],
"real_hosts": True
},
"auth": auth_token,
"id": 1
}
response = requests.post(url, headers=headers, data=json.dumps(payload))
return response.json().get("result", [])
def get_zabbix_hosts(auth_token, url, group_id):
headers = {'Content-Type': 'application/json-rpc'}
payload = {
"jsonrpc": "2.0",
"method": "host.get",
"params": {
"groupids": group_id,
"output": ["hostid", "name"]
},
"auth": auth_token,
"id": 2
}
response = requests.post(url, headers=headers, data=json.dumps(payload))
return response.json().get("result", [])
def get_zabbix_items(auth_token, url, host_id):
headers = {'Content-Type': 'application/json-rpc'}
payload = {
"jsonrpc": "2.0",
"method": "item.get",
"params": {
"hostids": host_id,
"output": ["itemid", "name", "key_"]
},
"auth": auth_token,
"id": 3
}
response = requests.post(url, headers=headers, data=json.dumps(payload))
return response.json().get("result", [])
def get_item_data(auth_token, url, item_id):
headers = {'Content-Type': 'application/json-rpc'}
payload = {
"jsonrpc": "2.0",
"method": "history.get",
"params": {
"output": "extend",
"history": 0,
"itemids": item_id,
"sortfield": "clock",
"sortorder": "DESC",
"limit": 10
},
"auth": auth_token,
"id": 4
}
response = requests.post(url, headers=headers, data=json.dumps(payload))
return response.json().get("result", [])
# Rota para listar as configurações dos servidores Zabbix
@zabbix.route('/servers', methods=['GET'])
def get_zabbix_servers(data):
try:
zabbix_configs = get_zabbix_configs()
if not zabbix_configs:
return jsonify({"message": "Nenhum servidor Zabbix encontrado"}), 404
servers = [{"name": config['name'], "type": config['type'], "url": config['url']} for config in zabbix_configs]
return jsonify(servers)
except Exception as e:
return jsonify({"error": str(e)}), 500
# Rota para listar os grupos de um servidor específico
@zabbix.route('/<server_name>/groups', methods=['GET'])
def get_host_groups_route(data, server_name):
try:
zabbix_configs = get_zabbix_configs()
config = next((conf for conf in zabbix_configs if conf['name'] == server_name), None)
if config is None:
return jsonify({"error": "Servidor Zabbix não encontrado"}), 404
auth_token = zabbix_login(config['url'], config['username'], config['password'])
groups = get_zabbix_host_groups(auth_token, config['url'])
return jsonify(groups)
except Exception as e:
return jsonify({"error": str(e)}), 500
# Rota para listar os hosts de um grupo específico
@zabbix.route('/<server_name>/groups/<group_id>/hosts', methods=['GET'])
def get_hosts_by_group(data, server_name, group_id):
try:
zabbix_configs = get_zabbix_configs()
config = next((conf for conf in zabbix_configs if conf['name'] == server_name), None)
if config is None:
return jsonify({"error": "Servidor Zabbix não encontrado"}), 404
auth_token = zabbix_login(config['url'], config['username'], config['password'])
hosts = get_zabbix_hosts(auth_token, config['url'], group_id)
return jsonify(hosts)
except Exception as e:
return jsonify({"error": str(e)}), 500
# Rota para listar os itens de um host específico
@zabbix.route('/<server_name>/hosts/<host_id>/items', methods=['GET'])
def get_items_by_host(data, server_name, host_id):
try:
zabbix_configs = get_zabbix_configs()
config = next((conf for conf in zabbix_configs if conf['name'] == server_name), None)
if config is None:
return jsonify({"error": "Servidor Zabbix não encontrado"}), 404
auth_token = zabbix_login(config['url'], config['username'], config['password'])
items = get_zabbix_items(auth_token, config['url'], host_id)
return jsonify(items)
except Exception as e:
return jsonify({"error": str(e)}), 500
# Rota para retornar os dados de um item específico
@zabbix.route('/<server_name>/items/<item_id>/data', methods=['GET'])
def get_item_history_route(data, server_name, item_id):
try:
zabbix_configs = get_zabbix_configs()
config = next((conf for conf in zabbix_configs if conf['name'] == server_name), None)
if config is None:
return jsonify({"error": "Servidor Zabbix não encontrado"}), 404
auth_token = zabbix_login(config['url'], config['username'], config['password'])
item_data = get_item_data(auth_token, config['url'], item_id)
return jsonify(item_data)
except Exception as e:
return jsonify({"error": str(e)}), 500

41
app/routes/zammad.py Normal file
View File

@ -0,0 +1,41 @@
from flask import Blueprint, jsonify, request
import requests
# Blueprint para rotas relacionadas ao Zammad
zammad = Blueprint('zammad', __name__)
# Configurações da API do Zammad
base_url = "http://10.10.253.59/api/v1" # URL base da API Zammad
zammad_token = "kT0IXO8aVhPoTLcMRNL290rqd9jbRhhM0zf8MgBo3n00NLChToSU6rOGnMgWA0M2" # Token da API Zammad
# Rota para listar tickets
@zammad.route('/tickets', methods=['GET'])
def listar_tickets():
try:
# Cabeçalho de autenticação para a API Zammad
headers = {
"Authorization": f"Token token={zammad_token}",
"Content-Type": "application/json"
}
# Faz a requisição para a API Zammad
url = f"{base_url}/tickets"
response = requests.get(url, headers=headers, timeout=10) # Timeout adicionado para evitar requisições travadas
# Processa a resposta
if response.status_code == 200:
tickets = response.json()
return jsonify(tickets), 200
else:
return jsonify({
"error": f"Erro ao buscar tickets: {response.status_code}",
"details": response.text
}), response.status_code
except requests.exceptions.RequestException as e:
# Captura problemas de conexão ou requisição
return jsonify({"error": "Erro na requisição à API", "details": str(e)}), 500
except Exception as e:
# Captura quaisquer outros erros inesperados
return jsonify({"error": "Erro interno ao processar a solicitação", "details": str(e)}), 500

48
fullchain1.pem Normal file
View File

@ -0,0 +1,48 @@
-----BEGIN CERTIFICATE-----
MIIDojCCAyegAwIBAgISAxzDNNJ+D1PrnQBdJr/XblhmMAoGCCqGSM49BAMDMDIx
CzAJBgNVBAYTAlVTMRYwFAYDVQQKEw1MZXQncyBFbmNyeXB0MQswCQYDVQQDEwJF
NTAeFw0yNDA3MTgxOTE1MzlaFw0yNDEwMTYxOTE1MzhaMBwxGjAYBgNVBAMTEWRl
di5pdGd1eXMuY29tLmJyMHYwEAYHKoZIzj0CAQYFK4EEACIDYgAE+OCuDmslFa26
pscnWiCESN+KsGgeEZSxAzDFMmJ0ck6wAZS2xdcFz7JJD3vHXnLSSbcJs65RotpW
Nu5MfiHyrkQe5cWYn0viyMZnnVl+ojKcDkDN24/hcsQcevr7Qm11o4ICFDCCAhAw
DgYDVR0PAQH/BAQDAgeAMB0GA1UdJQQWMBQGCCsGAQUFBwMBBggrBgEFBQcDAjAM
BgNVHRMBAf8EAjAAMB0GA1UdDgQWBBSCqHCjFtevuQn0TV9XYV+xrAlogjAfBgNV
HSMEGDAWgBSfK1/PPCFPnQS37SssxMZwi9LXDTBVBggrBgEFBQcBAQRJMEcwIQYI
KwYBBQUHMAGGFWh0dHA6Ly9lNS5vLmxlbmNyLm9yZzAiBggrBgEFBQcwAoYWaHR0
cDovL2U1LmkubGVuY3Iub3JnLzAcBgNVHREEFTATghFkZXYuaXRndXlzLmNvbS5i
cjATBgNVHSAEDDAKMAgGBmeBDAECATCCAQUGCisGAQQB1nkCBAIEgfYEgfMA8QB2
AN/hVuuqBa+1nA+GcY2owDJOrlbZbqf1pWoB0cE7vlJcAAABkMd8ctUAAAQDAEcw
RQIhAJaDpgmOCf/Z4lLL3gZfo1SMIEoSD6JjVBogpQexgEH5AiB8SHzgLDF9z4rk
DcUZVpukXMF+OtcuValcNEpfkZffpQB3AEiw42vapkc0D+VqAvqdMOscUgHLVt0s
gdm7v6s52IRzAAABkMd8cg0AAAQDAEgwRgIhAIvVuiNpWs0HSrUj1RHt/76L00kB
+E+MbdQF/5uMaOlEAiEApu3u5t3vrZRdt2UpTiu7Uzzl1mDWd4fC7Pmbf1HPIqgw
CgYIKoZIzj0EAwMDaQAwZgIxAMom8JO8+pOT2PpHj+cUNC3UnrbYMfrKq40gjdxH
qLA28Fo2sBBo6WSPAXSHJ23oZwIxAK60hbs1MVqUSXR8vxLEni48kc6gJ4Al4s8l
Ncumyp1du4+bi8j5oNZ7btZg8cDuig==
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
MIIEVzCCAj+gAwIBAgIRAIOPbGPOsTmMYgZigxXJ/d4wDQYJKoZIhvcNAQELBQAw
TzELMAkGA1UEBhMCVVMxKTAnBgNVBAoTIEludGVybmV0IFNlY3VyaXR5IFJlc2Vh
cmNoIEdyb3VwMRUwEwYDVQQDEwxJU1JHIFJvb3QgWDEwHhcNMjQwMzEzMDAwMDAw
WhcNMjcwMzEyMjM1OTU5WjAyMQswCQYDVQQGEwJVUzEWMBQGA1UEChMNTGV0J3Mg
RW5jcnlwdDELMAkGA1UEAxMCRTUwdjAQBgcqhkjOPQIBBgUrgQQAIgNiAAQNCzqK
a2GOtu/cX1jnxkJFVKtj9mZhSAouWXW0gQI3ULc/FnncmOyhKJdyIBwsz9V8UiBO
VHhbhBRrwJCuhezAUUE8Wod/Bk3U/mDR+mwt4X2VEIiiCFQPmRpM5uoKrNijgfgw
gfUwDgYDVR0PAQH/BAQDAgGGMB0GA1UdJQQWMBQGCCsGAQUFBwMCBggrBgEFBQcD
ATASBgNVHRMBAf8ECDAGAQH/AgEAMB0GA1UdDgQWBBSfK1/PPCFPnQS37SssxMZw
i9LXDTAfBgNVHSMEGDAWgBR5tFnme7bl5AFzgAiIyBpY9umbbjAyBggrBgEFBQcB
AQQmMCQwIgYIKwYBBQUHMAKGFmh0dHA6Ly94MS5pLmxlbmNyLm9yZy8wEwYDVR0g
BAwwCjAIBgZngQwBAgEwJwYDVR0fBCAwHjAcoBqgGIYWaHR0cDovL3gxLmMubGVu
Y3Iub3JnLzANBgkqhkiG9w0BAQsFAAOCAgEAH3KdNEVCQdqk0LKyuNImTKdRJY1C
2uw2SJajuhqkyGPY8C+zzsufZ+mgnhnq1A2KVQOSykOEnUbx1cy637rBAihx97r+
bcwbZM6sTDIaEriR/PLk6LKs9Be0uoVxgOKDcpG9svD33J+G9Lcfv1K9luDmSTgG
6XNFIN5vfI5gs/lMPyojEMdIzK9blcl2/1vKxO8WGCcjvsQ1nJ/Pwt8LQZBfOFyV
XP8ubAp/au3dc4EKWG9MO5zcx1qT9+NXRGdVWxGvmBFRAajciMfXME1ZuGmk3/GO
koAM7ZkjZmleyokP1LGzmfJcUd9s7eeu1/9/eg5XlXd/55GtYjAM+C4DG5i7eaNq
cm2F+yxYIPt6cbbtYVNJCGfHWqHEQ4FYStUyFnv8sjyqU8ypgZaNJ9aVcWSICLOI
E1/Qv/7oKsnZCWJ926wU6RqG1OYPGOi1zuABhLw61cuPVDT28nQS/e6z95cJXq0e
K1BcaJ6fJZsmbjRgD5p3mvEf5vdQM7MCEvU0tHbsx2I5mHHJoABHb8KVBgWp/lcX
GWiWaeOyB7RP+OfDtvi2OsapxXiV7vNVs7fMlrRjY1joKaqmmycnBvAq14AEbtyL
sVfOS66B8apkeFX2NY4XPEYV4ZSCe8VHPrdrERk2wILG3T/EGmSIkCYVUMSnjmJd
VQD9F6Na/+zmXCc=
-----END CERTIFICATE-----

6
privkey1.pem Normal file
View File

@ -0,0 +1,6 @@
-----BEGIN PRIVATE KEY-----
MIG2AgEAMBAGByqGSM49AgEGBSuBBAAiBIGeMIGbAgEBBDCLn1GV8AI0K5D46tNo
AlfOmKAIUjcEN4lSQ8/zb7fr2xJlb1kEV4CqgqS/pEqClXShZANiAAT44K4OayUV
rbqmxydaIIRI34qwaB4RlLEDMMUyYnRyTrABlLbF1wXPskkPe8dectJJtwmzrlGi
2lY27kx+IfKuRB7lxZifS+LIxmedWX6iMpwOQM3bj+FyxBx6+vtCbXU=
-----END PRIVATE KEY-----

View File

@ -8,4 +8,3 @@ Flask-WTF==1.1.1 # Proteção contra CSRF (Cross-Site Request Forgery)
Flask-Talisman==0.8.1 # Para melhorar a segurança da aplicação
influxdb-client==1.35.0 # Cliente para InfluxDB
gunicorn==20.1.0 # Servidor WSGI (opcional, caso deseje usar em produção)
requests==2.26.0

View File

@ -1,716 +0,0 @@
from flask import Flask, request, jsonify, send_from_directory,url_for,send_file
from flask_mysqldb import MySQL
from MySQLdb.cursors import DictCursor
import ldap3
import re
import requests
import os
import json
from functools import wraps
from collections import OrderedDict
from dotenv import load_dotenv
import jwt # Importa a biblioteca JWT para gerar e validar tokens
from datetime import datetime, timedelta
from ldap3.core.exceptions import LDAPSocketOpenError, LDAPException # Importa as exceções específicas de ldap3
from flask_cors import CORS
from flask_wtf.csrf import CSRFProtect # Importa CSRFProtect para proteção CSRF
from flask_talisman import Talisman
import influxdb_client, time
from influxdb_client import InfluxDBClient, Point, WritePrecision
from influxdb_client.client.write_api import SYNCHRONOUS
import ldap3
# Carregar variáveis de ambiente do arquivo .env
load_dotenv()
app = Flask(__name__)
# Configuração de cookies seguros
app.config['SESSION_COOKIE_SECURE'] = True # Cookies serão enviados apenas em conexões HTTPS
app.config['SESSION_COOKIE_HTTPONLY'] = True # Cookies não estarão acessíveis via JavaScript
Talisman(app)
# Configuração da chave secreta necessária para CSRF e JWT
app.config['SECRET_KEY'] = os.getenv('JWT_SECRET') or 'sua_chave_secreta' # Defina uma chave secreta segura
# Inicializa a proteção CSRF
csrf = CSRFProtect(app)
# Permitir apenas uma origem específica
CORS(app, resources={r"/*": {"origins": "https://dev.itguys.com.br"}})
# Configuração do MySQL usando variáveis de ambiente
app.config['MYSQL_HOST'] = os.getenv('MYSQL_HOST')
app.config['MYSQL_USER'] = os.getenv('MYSQL_USER')
app.config['MYSQL_PASSWORD'] = os.getenv('MYSQL_PASSWORD')
app.config['MYSQL_DB'] = os.getenv('MYSQL_DB')
#Configuração do SMTP usando variáveis de ambiente
app.config['SMTP_USERNAME'] = os.getenv('SMTP_USERNAME')
app.config['SMTP_HOST'] = os.getenv('SMTP_HOST')
app.config['SMTP_PASSWORD'] = os.getenv('SMTP_PASSWORD')
app.config['SMTP_PORT'] = os.getenv('SMTP_PORT')
app.config['SMTP_RECIPIENT'] = os.getenv('SMTP_RECIPIENT')
# Diretório onde as imagens sigilosas estão armazenadas
PROTECTED_FOLDER = '/var/www/ambiente_python/Respositorio_img/'
mysql = MySQL(app)
# Função auxiliar para obter o caminho completo da imagem
def get_protected_image_path(filename):
file_path = os.path.join(PROTECTED_FOLDER, filename)
# Verifica se o arquivo existe no diretório seguro
if os.path.isfile(file_path):
return file_path
return None
def token_required(f):
@wraps(f) # Adiciona wraps aqui para manter o nome da função original
def wrapper(*args, **kwargs):
token = request.headers.get('x-access-token')
if not token:
return jsonify({'msg': 'Token é necessário!'}), 401
try:
# Decodifica o token para validar
data = jwt.decode(token, app.config['SECRET_KEY'], algorithms=["HS256"])
except jwt.ExpiredSignatureError:
# O token expirou
return jsonify({'msg': 'Token expirado! Por favor, faça login novamente.'}), 401
except jwt.InvalidTokenError:
# Token inválido por qualquer outro motivo
return jsonify({'msg': 'Token inválido!'}), 401
return f(*args, **kwargs)
return wrapper
def get_influxdb_config(domain, mysql):
# Consultar o banco de dados para obter o nome da empresa com base no domínio
cur = mysql.connection.cursor()
cur.execute("SELECT nome FROM empresa WHERE dominio = %s", (domain,))
empresa_result = cur.fetchone()
cur.close()
# Verifica se a empresa foi encontrada
if empresa_result is None:
raise ValueError('Empresa não encontrada no banco de dados')
# Obtém o nome da empresa (empresa_result é uma tupla)
nome_empresa = empresa_result[0]
# Definir o caminho do diretório onde os arquivos JSON estão armazenados
json_directory = './json/' # Substitua pelo caminho real do diretório
# Nome do arquivo JSON correspondente ao nome da empresa
json_filename = f"{nome_empresa}.json"
# Caminho completo para o arquivo JSON
json_filepath = os.path.join(json_directory, json_filename)
# Verificar se o arquivo JSON existe
if not os.path.isfile(json_filepath):
raise FileNotFoundError('Arquivo JSON não encontrado para a empresa fornecida')
# Carregar o conteúdo do arquivo JSON mantendo a ordem
with open(json_filepath, 'r') as json_file:
json_data = json.load(json_file, object_pairs_hook=OrderedDict)
return json_data # Retorna todo o JSON carregado
@app.route('/login', methods=['POST'])
@csrf.exempt # Endpoints de autenticação como este podem ser excluídos da proteção CSRF, remova se quiser aplicar.
def login():
data = request.get_json()
username_full = data.get('username') # Extrai o e-mail completo do usuário dos dados da requisição.
password = data.get('password') # Extrai a senha dos dados da requisição.
# Verificação básica para detecção de padrões de SQL Injection
sql_injection_pattern = r"(--|\b(SELECT|UNION|INSERT|UPDATE|DELETE|DROP|CREATE|ALTER)\b|;|'|\")"
# Checar se o username_full ou o password contêm padrões suspeitos
if re.search(sql_injection_pattern, username_full, re.IGNORECASE) or re.search(sql_injection_pattern, password, re.IGNORECASE):
return jsonify({'msg': 'A solicitação foi bem formada, mas não pôde ser atendida devido a erros semânticos.'}), 422 # Retorna o status 422
# Validação do formato do e-mail
if not re.match(r"[^@]+@[^@]+\.[^@]+", username_full):
return jsonify({'msg': 'Formato de e-mail inválido'}), 400
# Extrair o nome de usuário (parte antes do @)
username = username_full.split('@')[0]
# Separar o domínio
domain = username_full.split('@')[1]
# Verificar se o domínio existe na tabela empresa
cur = mysql.connection.cursor()
cur.execute("SELECT idempresa, ip_dominio FROM empresa WHERE dominio = %s", (domain,))
empresa_result = cur.fetchone()
if empresa_result is None: # Verifica se o domínio não existe na tabela empresa.
cur.close()
return jsonify({'msg': 'Domínio não encontrado no banco de dados'}), 404
id_empresa, ip_dominio = empresa_result # Armazena o ID da empresa e o IP do domínio encontrados no banco de dados.
# Verificar se o usuário está associado à empresa no MySQL usando a view
cur.execute("SELECT empresa_id, dominio FROM view_usuario_empresa WHERE usuario = %s AND dominio = %s", (username, domain))
result = cur.fetchone()
cur.close()
if result is None: # Verifica se o usuário não está associado à empresa.
return jsonify({'msg': 'Usuário não associado à empresa'}), 404
empresa_id, dominio_empresa = result
# Conectar ao servidor LDAP correspondente e autenticar o usuário
ldap_server = f'ldap://{ip_dominio}:389'
try:
# Configurar o servidor LDAP
server = ldap3.Server(ldap_server)
# Tentar criar a conexão
conn = ldap3.Connection(server, user=username_full, password=password)
# Tentar autenticar o usuário
if conn.bind():
# Gerar o token JWT após autenticação bem-sucedida
token = jwt.encode({
'user': username_full,
'exp': datetime.utcnow() + timedelta(hours=1) # Token expira em 1 hora
}, app.config['SECRET_KEY'], algorithm="HS256")
return jsonify({'msg': 'Login bem-sucedido', 'token': token}), 200
else:
return jsonify({'msg': 'Falha na autenticação LDAP'}), 401
except LDAPSocketOpenError:
# Erro específico para problemas de conexão com o servidor LDAP
return jsonify({'msg': 'Não foi possível conectar ao servidor LDAP. Verifique a conexão de rede e as configurações do servidor.'}), 503
except LDAPException as e:
# Captura qualquer outra exceção LDAP genérica
return jsonify({'Erro 500': f'Erro LDAP: {str(e)}'}), 500
@app.route('/get_profile_picture/<path:filename>', methods=['GET'])
@token_required # Garante que apenas usuários autenticados possam acessar
def get_profile_picture(filename):
# O 'filename' já pode incluir subpastas, por exemplo: "usuarios/perfil/joao_perfil.jpg"
file_path = get_protected_image_path(filename)
if file_path and os.path.isfile(file_path):
return send_file(file_path, mimetype='image/jpg') # Ajuste o mimetype conforme o tipo da imagem
else:
return jsonify({'msg': 'Imagem não encontrada ou acesso não permitido'}), 404
@app.route('/mounting', methods=['GET'])
@token_required # Verificação de token para autenticação
def mounting():
# Decodifica o token para obter o e-mail do usuário
token = request.headers.get('x-access-token')
data = jwt.decode(token, app.config['SECRET_KEY'], algorithms=["HS256"])
username_full = data['user']
# Verifica se o usuário está autenticado
if not username_full:
return jsonify({'msg': 'Token inválido!'}), 401
# Extrair o nome de usuário e o domínio do email do usuário autenticado
username = username_full.split('@')[0]
domain = username_full.split('@')[1]
# Consultar o banco de dados para obter as informações do usuário (nome, img_perfil, img_fundo)
cur = mysql.connection.cursor()
cur.execute("SELECT nome, img_perfil, img_fundo FROM usuarios WHERE usuario = %s", (username,))
usuario_result = cur.fetchone()
if usuario_result is None:
cur.close()
return jsonify({'msg': 'Usuário não encontrado no banco de dados'}), 404
nome_usuario, img_perfil_path, img_fundo_path = usuario_result
# Consultar o banco de dados para obter o nome e o logo da empresa com base no domínio
cur.execute("SELECT nome, logo FROM empresa WHERE dominio = %s", (domain,))
empresa_result = cur.fetchone()
cur.close()
if empresa_result is None:
return jsonify({'msg': 'Empresa não encontrada no banco de dados para o domínio fornecido'}), 404
nome_empresa, logo_empresa_path = empresa_result
# Montar a resposta JSON com os caminhos relativos das imagens e outras informações
response_data = {
'usuario': {
'nome': nome_usuario,
'img_perfil': f"/get_profile_picture/{img_perfil_path}", # Caminho relativo para a imagem de perfil
'img_fundo': f"/get_profile_picture/{img_fundo_path}" # Caminho relativo para a imagem de fundo
},
'empresa': {
'nome': nome_empresa,
'logo': f"/get_profile_picture/{logo_empresa_path}" # Caminho relativo para o logo da empresa
}
}
return jsonify(response_data), 200
@app.route('/api/options', methods=['GET'])
@token_required
@csrf.exempt
def get_options():
client = None
try:
# Decodifica o token para obter o e-mail do usuário
token = request.headers.get('x-access-token')
data = jwt.decode(token, app.config['SECRET_KEY'], algorithms=["HS256"])
username_full = data['user']
# Extrair o domínio do email do usuário autenticado
domain = username_full.split('@')[1]
# Carregar as configurações do InfluxDB a partir do JSON
json_data = get_influxdb_config(domain, mysql)
# Extrair o token de 'config_influxdb'
config_influxdb = json_data.get('config_influxdb', [])
if not config_influxdb or not isinstance(config_influxdb, list):
return jsonify({'msg': 'config_influxdb não encontrado ou inválido'}), 404
# Acessa o primeiro objeto da lista e obtém os detalhes necessários
influxdb_token = config_influxdb[0].get('Token', '')
influxdb_org = config_influxdb[0].get('org', '')
influxdb_url = config_influxdb[0].get('url', '')
# Verifica se todos os dados necessários foram encontrados
if not influxdb_token:
return jsonify({'msg': 'Token não encontrado em config_influxdb'}), 404
if not influxdb_org:
return jsonify({'msg': 'Org não encontrado em config_influxdb'}), 404
if not influxdb_url:
return jsonify({'msg': 'URL não encontrado em config_influxdb'}), 404
# Inicializa o cliente InfluxDB
client = influxdb_client.InfluxDBClient(url=influxdb_url, token=influxdb_token, org=influxdb_org)
query_api = client.query_api()
# Consulta para listar os buckets disponíveis
buckets_query = '''
buckets()
'''
buckets_result = query_api.query(org=influxdb_org, query=buckets_query)
# Processa a lista de buckets
buckets = []
for table in buckets_result:
for record in table.records:
bucket_name = record.values.get('name') # Acessa diretamente o nome do bucket
if bucket_name:
buckets.append(bucket_name)
if len(buckets) == 0:
return jsonify({'msg': 'Nenhum bucket encontrado'}), 404
# Para cada bucket, listar os nodenames diretamente
nodenames = {}
for bucket in buckets:
nodenames_query = f"""
from(bucket: \"{bucket}\")
|> range(start: -1d)
|> keep(columns: [\"nodename\"])
|> distinct(column: \"nodename\")
"""
nodenames_result = query_api.query(org=influxdb_org, query=nodenames_query)
bucket_nodenames = []
for table in nodenames_result:
for record in table.records:
if record.get_value() is not None: # Ignorar valores nulos
bucket_nodenames.append(record.get_value())
nodenames[bucket] = bucket_nodenames
# Retorna a lista de buckets e nodenames como resposta JSON
return jsonify({
"buckets": buckets,
"nodenames": nodenames
})
except Exception as e:
return jsonify({'msg': 'Erro ao recuperar opções', 'error': str(e)}), 500
finally:
if client is not None:
client.close()
@app.route('/api/execute_all_queries', methods=['POST'])
@token_required
@csrf.exempt
def execute_all_queries():
client = None
try:
# Decodifica o token para obter o e-mail do usuário
token = request.headers.get('x-access-token')
data = jwt.decode(token, app.config['SECRET_KEY'], algorithms=["HS256"])
username_full = data['user']
domain = username_full.split('@')[1]
# Carregar as configurações do InfluxDB e queries a partir do JSON
json_data = get_influxdb_config(domain, mysql)
# Extrair as informações da requisição
request_data = request.get_json()
bucket = request_data.get('bucket')
host = request_data.get('host')
start_time = request_data.get('startTime')
end_time = request_data.get('endTime')
window_period = request_data.get('windowPeriod')
# Inicializa o cliente InfluxDB
influxdb_token = json_data['config_influxdb'][0]['Token']
influxdb_org = json_data['config_influxdb'][0]['org']
influxdb_url = json_data['config_influxdb'][0]['url']
client = influxdb_client.InfluxDBClient(url=influxdb_url, token=influxdb_token, org=influxdb_org)
query_api = client.query_api()
# Lista para armazenar os resultados de cada query
all_results = []
# Itera sobre todas as queries no JSON
for query_data in json_data['queries']:
# Substitui as variáveis na query
query_template = query_data['query']
query = query_template.replace("${Bucket}", bucket).replace("${server}", host)
query = query.replace("v.timeRangeStart", start_time).replace("v.timeRangeStop", end_time)
query = query.replace("v.windowPeriod", window_period)
# Executa a query
result = query_api.query(org=influxdb_org, query=query)
# Processa os resultados da query
output = []
for table in result:
for record in table.records:
output.append({
"time": record.get_time(),
"value": record.get_value(),
"measurement": record.get_measurement(),
"tags": record.values # Inclui as tags relacionadas ao registro
})
# Adiciona os resultados processados à lista de todas as queries
all_results.append({
"uid": query_data['uid'],
"name": query_data['query_name'],
"result": output
})
# Retorna o resultado de todas as queries
return jsonify(all_results)
except Exception as e:
return jsonify({'msg': 'Erro ao executar as queries', 'error': str(e)}), 500
finally:
if client:
client.close()
@app.route('/api/execute_query/<uid>', methods=['POST'])
@token_required
@csrf.exempt
def execute_query(uid):
client = None
try:
# Decodifica o token para obter o e-mail do usuário
token = request.headers.get('x-access-token')
data = jwt.decode(token, app.config['SECRET_KEY'], algorithms=["HS256"])
username_full = data['user']
domain = username_full.split('@')[1]
# Carregar as configurações de InfluxDB e queries a partir do JSON
json_data = get_influxdb_config(domain, mysql)
# Extrair as informações da requisição
request_data = request.get_json()
bucket = request_data.get('bucket')
host = request_data.get('host')
start_time = request_data.get('startTime')
end_time = request_data.get('endTime')
window_period = request_data.get('windowPeriod')
# Encontra a query específica com o UID fornecido
query_data = next((q for q in json_data['queries'] if q['uid'] == uid), None)
if not query_data:
return jsonify({'msg': 'Consulta não encontrada'}), 404
# Substitui as variáveis na query
query_template = query_data['query']
query = query_template.replace("${Bucket}", bucket).replace("${server}", host)
query = query.replace("v.timeRangeStart", start_time).replace("v.timeRangeStop", end_time)
query = query.replace("v.windowPeriod", window_period)
# Inicializa o cliente InfluxDB
influxdb_token = json_data['config_influxdb'][0]['Token']
influxdb_org = json_data['config_influxdb'][0]['org']
influxdb_url = json_data['config_influxdb'][0]['url']
client = influxdb_client.InfluxDBClient(url=influxdb_url, token=influxdb_token, org=influxdb_org)
query_api = client.query_api()
# Executa a query
result = query_api.query(org=influxdb_org, query=query)
# Processa os resultados da query
output = []
for table in result:
for record in table.records:
output.append({
"time": record.get_time(),
"value": record.get_value(),
"measurement": record.get_measurement(),
"tags": record.values # Inclui as tags relacionadas ao registro
})
# Retorna o resultado da consulta específica
return jsonify({
"uid": query_data['uid'],
"name": query_data['query_name'],
"result": output
})
except Exception as e:
return jsonify({'msg': 'Erro ao executar a query', 'error': str(e)}), 500
finally:
if client:
client.close()
# Função para buscar as configurações dos servidores Zabbix do banco de dados
# Ajustar o cursor para usar DictCursor
def get_zabbix_configs():
cursor = mysql.connection.cursor(DictCursor) # Usando DictCursor para retornar dicionários
query = "SELECT * FROM connections_zabbix"
cursor.execute(query)
configs = cursor.fetchall() # Agora retorna uma lista de dicionários
cursor.close()
return configs
# Função para autenticar no Zabbix via API JSON-RPC
def zabbix_login(url, username, password):
headers = {'Content-Type': 'application/json-rpc'}
payload = {
"jsonrpc": "2.0",
"method": "user.login",
"params": {
"username": username, # Certifique-se de que é 'username' e não 'user'
"password": password
},
"id": 1,
"auth": None
}
response = requests.post(url, headers=headers, data=json.dumps(payload))
result = response.json()
if 'result' in result:
return result['result'] # Retorna o token de autenticação
else:
raise Exception(f"Erro na autenticação: {result}")
def get_zabbix_host_groups(auth_token, url):
headers = {'Content-Type': 'application/json-rpc'}
payload = {
"jsonrpc": "2.0",
"method": "hostgroup.get",
"params": {
"output": ["groupid", "name"], # Retorna o ID e o nome dos grupos
"real_hosts": True # Apenas grupos com hosts reais, excluindo templates
},
"auth": auth_token,
"id": 1
}
# Faz a requisição para a API do Zabbix
response = requests.post(url, headers=headers, data=json.dumps(payload))
# Captura e retorna o resultado da resposta
return response.json().get("result", [])
# Função para buscar os hosts de um grupo no Zabbix
def get_zabbix_hosts(auth_token, url, group_id):
headers = {'Content-Type': 'application/json-rpc'}
payload = {
"jsonrpc": "2.0",
"method": "host.get",
"params": {
"groupids": group_id,
"output": ["hostid", "name"] # Retorna o ID e o nome dos hosts
},
"auth": auth_token,
"id": 2
}
response = requests.post(url, headers=headers, data=json.dumps(payload))
return response.json().get("result", [])
# Função para buscar os itens de um host no Zabbix
def get_zabbix_items(auth_token, url, host_id):
headers = {'Content-Type': 'application/json-rpc'}
payload = {
"jsonrpc": "2.0",
"method": "item.get",
"params": {
"hostids": host_id,
"output": ["itemid", "name", "key_"] # Retorna o ID, nome e chave dos itens
},
"auth": auth_token,
"id": 3
}
response = requests.post(url, headers=headers, data=json.dumps(payload))
return response.json().get("result", [])
# Função para buscar o valor de um item específico no Zabbix
def get_item_data(auth_token, url, item_id):
headers = {'Content-Type': 'application/json-rpc'}
payload = {
"jsonrpc": "2.0",
"method": "history.get",
"params": {
"output": "extend",
"history": 0, # Tipo 0 para dados numéricos
"itemids": item_id,
"sortfield": "clock", # Ordena pelos mais recentes
"sortorder": "DESC",
"limit": 10 # Limite de 10 resultados
},
"auth": auth_token,
"id": 4
}
response = requests.post(url, headers=headers, data=json.dumps(payload))
return response.json().get("result", [])
@app.route('/zabbix/servers', methods=['GET'])
@token_required
def get_zabbix_servers():
try:
# Busca as configurações do banco de dados
zabbix_configs = get_zabbix_configs() # Certifique-se de que essa função está retornando dados válidos
# Verifique se não há servidores Zabbix
if not zabbix_configs:
return jsonify({"message": "Nenhum servidor Zabbix encontrado"}), 404
# Formata os servidores para o frontend
servers = []
for config in zabbix_configs:
servers.append({
"name": config['name'], # Acessa a coluna 'name'
"type": config['type'], # Acessa a coluna 'type'
"url": config['url'] # Acessa a coluna 'url'
})
return jsonify(servers)
except Exception as e:
# Mostra o erro exato que está ocorrendo para facilitar o debug
return jsonify({"error": str(e)}), 500
@app.route('/zabbix/<server_name>/groups', methods=['GET'])
@token_required
def get_host_groups(server_name):
try:
# Busca as configurações do banco de dados para o servidor Zabbix com o nome "server_name"
zabbix_configs = get_zabbix_configs()
config = next((conf for conf in zabbix_configs if conf['name'] == server_name), None)
if config is None:
return jsonify({"error": "Servidor Zabbix não encontrado"}), 404
# Autentica no servidor Zabbix
auth_token = zabbix_login(config['url'], config['username'], config['password'])
# Busca os grupos de hosts reais, excluindo os templates
groups = get_zabbix_host_groups(auth_token, config['url'])
return jsonify(groups)
except Exception as e:
return jsonify({"error": str(e)}), 500
# Rota para listar os hosts de um grupo específico
@app.route('/zabbix/<server_name>/groups/<group_id>/hosts', methods=['GET'])
@token_required
def get_hosts_by_group(server_name, group_id):
try:
# Busca as configurações do servidor Zabbix no banco de dados
zabbix_configs = get_zabbix_configs()
config = next((conf for conf in zabbix_configs if conf['name'] == server_name), None)
if config is None:
return jsonify({"error": "Servidor Zabbix não encontrado"}), 404
# Autentica no servidor Zabbix
auth_token = zabbix_login(config['url'], config['username'], config['password'])
# Busca os hosts do grupo
hosts = get_zabbix_hosts(auth_token, config['url'], group_id)
return jsonify(hosts)
except Exception as e:
return jsonify({"error": str(e)}), 500
# Rota para listar os itens de um host específico
@app.route('/zabbix/<server_name>/hosts/<host_id>/items', methods=['GET'])
@token_required
def get_items_by_host(server_name, host_id):
try:
# Busca as configurações do servidor Zabbix no banco de dados
zabbix_configs = get_zabbix_configs()
config = next((conf for conf in zabbix_configs if conf['name'] == server_name), None)
if config is None:
return jsonify({"error": "Servidor Zabbix não encontrado"}), 404
# Autentica no servidor Zabbix
auth_token = zabbix_login(config['url'], config['username'], config['password'])
# Busca os itens do host
items = get_zabbix_items(auth_token, config['url'], host_id)
return jsonify(items)
except Exception as e:
return jsonify({"error": str(e)}), 500
# Rota para retornar os dados de um item específico
@app.route('/zabbix/<server_name>/items/<item_id>/data', methods=['GET'])
@token_required
def get_item_history(server_name, item_id):
try:
# Busca as configurações do servidor Zabbix no banco de dados
zabbix_configs = get_zabbix_configs()
config = next((conf for conf in zabbix_configs if conf['name'] == server_name), None)
if config is None:
return jsonify({"error": "Servidor Zabbix não encontrado"}), 404
# Autentica no servidor Zabbix
auth_token = zabbix_login(config['url'], config['username'], config['password'])
# Busca os dados do item
item_data = get_item_data(auth_token, config['url'], item_id)
return jsonify(item_data)
except Exception as e:
return jsonify({"error": str(e)}), 500
if __name__ == '__main__':
app.run(host="0.0.0.0", port=5000, ssl_context=('./fullchain1.pem', './privkey1.pem'))

Binary file not shown.

After

Width:  |  Height:  |  Size: 124 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

View File

Before

Width:  |  Height:  |  Size: 90 KiB

After

Width:  |  Height:  |  Size: 90 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.6 KiB

View File

Before

Width:  |  Height:  |  Size: 6.3 KiB

After

Width:  |  Height:  |  Size: 6.3 KiB

View File

Before

Width:  |  Height:  |  Size: 6.3 KiB

After

Width:  |  Height:  |  Size: 6.3 KiB

View File

Before

Width:  |  Height:  |  Size: 6.3 KiB

After

Width:  |  Height:  |  Size: 6.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 590 KiB

View File

Before

Width:  |  Height:  |  Size: 90 KiB

After

Width:  |  Height:  |  Size: 90 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 67 KiB

10
requirements.txt Normal file
View File

@ -0,0 +1,10 @@
Flask==2.3.2 # Framework para criar APIs
Flask-MySQLdb==0.2.0 # Integração com MySQL
ldap3==2.9 # Biblioteca para autenticação com servidores LDAP
python-dotenv==1.0.0 # Carregar variáveis de ambiente de arquivos .env
PyJWT==2.6.0 # Para gerar e decodificar tokens JWT
Flask-Cors==3.0.10 # Suporte a CORS (Cross-Origin Resource Sharing)
Flask-WTF==1.1.1 # Proteção contra CSRF (Cross-Site Request Forgery)
Flask-Talisman==0.8.1 # Para melhorar a segurança da aplicação
influxdb-client==1.35.0 # Cliente para InfluxDB
gunicorn==20.1.0 # Servidor WSGI (opcional, caso deseje usar em produção)

10
start.py Normal file
View File

@ -0,0 +1,10 @@
import sys
sys.path.append
from app import create_app
# Cria a aplicação usando a função de fábrica do __init__.py
app = create_app()
# Inicia a aplicação
if __name__ == '__main__':
app.run(host="0.0.0.0", port=5000, ssl_context=('./fullchain1.pem', './privkey1.pem'))