Files
chatc2/src/controllers/authController.js
T
2026-06-23 12:42:14 +00:00

186 lines
5.6 KiB
JavaScript

const jwt = require('jsonwebtoken');
const path = require('path');
const db = require('../database');
const authConfig = require('../config/auth');
// ── Rate limit de login (em memória) — proteção anti força bruta ──
const LOGIN_MAX = 10; // tentativas falhas permitidas
const LOGIN_JANELA_MS = 15 * 60 * 1000; // por janela de 15 minutos
const loginTentativas = new Map();
function chaveLogin(req, login) { return (req.ip || '') + '|' + (login || ''); }
function loginBloqueado(req, login) {
const e = loginTentativas.get(chaveLogin(req, login));
if (!e) return false;
if (Date.now() - e.inicio > LOGIN_JANELA_MS) { loginTentativas.delete(chaveLogin(req, login)); return false; }
return e.count >= LOGIN_MAX;
}
function registrarFalhaLogin(req, login) {
const k = chaveLogin(req, login);
const agora = Date.now();
const e = loginTentativas.get(k);
if (!e || agora - e.inicio > LOGIN_JANELA_MS) loginTentativas.set(k, { count: 1, inicio: agora });
else e.count++;
}
function limparTentativasLogin(req, login) { loginTentativas.delete(chaveLogin(req, login)); }
class AuthController {
/**
* Exibe a página de login
* Endpoint: GET /app/:alias/login
*/
static async loginPage(req, res) {
const loginPath = path.resolve(__dirname, '../public/login.html');
res.sendFile(loginPath);
}
/**
* Login do usuário no aplicativo
* Endpoint: POST /app/:alias/login
* Body: { USU_LOGIN, USU_SENHA }
*/
static async login(req, res) {
try {
const { alias } = req.params;
const { USU_LOGIN, USU_SENHA } = req.body;
// Validação dos campos obrigatórios
if (!USU_LOGIN || !USU_SENHA) {
return res.status(400).json({
success: false,
error: 'Os campos USU_LOGIN e USU_SENHA são obrigatórios.',
});
}
if (loginBloqueado(req, USU_LOGIN)) {
return res.status(429).json({
success: false,
error: 'Muitas tentativas de login. Aguarde alguns minutos e tente novamente.',
});
}
// Busca o usuário pelo login
const sql = `
SELECT
USU_CODIGO_ID,
USU_NIVEL_ID,
USU_NOME,
USU_LOGIN,
USU_EMAIL,
USU_STATUS,
USU_ACESSO_WEB,
USU_CAMINHO_FOTO,
USU_DT_ULTIMO_ACESSO,
USU_TIPO,
USU_TIPO_CHAT
FROM USUARIOS
WHERE USU_LOGIN = ?
AND (USU_SENHA = ? OR USU_SENHA_WEB = ?)
AND COALESCE(USU_ACESSO_WEB, '0') IN ('1', 'S')
AND USU_STATUS = 'A'
`;
const result = await db.query(alias, sql, [USU_LOGIN, USU_SENHA, USU_SENHA]);
if (result.length === 0) {
registrarFalhaLogin(req, USU_LOGIN);
return res.status(401).json({
success: false,
error: 'Credenciais inválidas ou usuário sem permissão de acesso.',
});
}
// Login bem-sucedido: zera o contador de tentativas
limparTentativasLogin(req, USU_LOGIN);
const user = result[0];
// Busca as empresas que o usuário tem acesso
const empresas = await db.query(alias,
'SELECT USE_EMPRESA_ID FROM USUARIOS_EMPRESA WHERE USE_USUARIO_ID = ?',
[user.USU_CODIGO_ID]
);
const empresasIds = empresas.map(e => Number(e.USE_EMPRESA_ID)).filter(n => !isNaN(n));
// Atualiza a data do último acesso
await db.execute(
alias,
`UPDATE USUARIOS SET USU_DT_ULTIMO_ACESSO = CURRENT_DATE WHERE USU_CODIGO_ID = ?`,
[user.USU_CODIGO_ID]
);
// Dados do usuário para o token
const payload = {
id: user.USU_CODIGO_ID,
nivelId: user.USU_NIVEL_ID,
nome: user.USU_NOME.trim(),
login: user.USU_LOGIN.trim(),
email: user.USU_EMAIL ? user.USU_EMAIL.trim() : null,
tipo: user.USU_TIPO ? user.USU_TIPO.trim() : null,
tipoChat: user.USU_TIPO_CHAT ? user.USU_TIPO_CHAT.trim() : null,
alias,
empresas: empresasIds, // empresas que o usuário pode acessar
};
// Gera o token JWT
const token = jwt.sign(payload, authConfig.secret, {
expiresIn: authConfig.expiresIn,
issuer: authConfig.issuer,
});
// Retorna o token e dados do usuário
return res.json({
success: true,
message: 'Login realizado com sucesso.',
alias,
token,
expiresIn: authConfig.expiresIn,
user: {
...payload,
foto: user.USU_CAMINHO_FOTO ? user.USU_CAMINHO_FOTO.trim() : null,
ultimoAcesso: user.USU_DT_ULTIMO_ACESSO,
},
});
} catch (err) {
console.error('Erro no login:', err);
return res.status(500).json({
success: false,
error: `Erro interno ao realizar login: ${err.message}`,
});
}
}
/**
* Valida o token atual e retorna dados atualizados do usuário
* Endpoint: GET /app/:alias/me
*/
static async me(req, res) {
try {
return res.json({
success: true,
alias: req.params.alias,
user: {
...req.user,
authType: req.authType || 'jwt',
},
});
} catch (err) {
console.error('Erro ao buscar usuário:', err);
return res.status(500).json({
success: false,
error: `Erro ao buscar dados do usuário: ${err.message}`,
});
}
}
/**
* Dashboard do app
* Endpoint: GET /app/:alias/dashboard
*/
static async dashboard(req, res) {
const dashboardPath = path.resolve(__dirname, '../public/dashboard.html');
res.sendFile(dashboardPath);
}
}
module.exports = AuthController;