From 92a960541732c267f128b7ea32dfe153a802f505 Mon Sep 17 00:00:00 2001 From: killer-cf Date: Fri, 12 Dec 2025 09:26:30 -0300 Subject: [PATCH] feat: implement permission checks for various actions across multiple resources, including acoes, atas, atestados, ausencias, ferias, and simbolos --- packages/backend/convex/acoes.ts | 20 ++ packages/backend/convex/atas.ts | 51 ++++ packages/backend/convex/atestadosLicencas.ts | 79 ++++++ packages/backend/convex/ausencias.ts | 50 ++++ packages/backend/convex/ferias.ts | 64 +++++ packages/backend/convex/permissoesAcoes.ts | 36 +++ packages/backend/convex/pontos.ts | 277 +++++++++++++++---- packages/backend/convex/simbolos.ts | 28 ++ 8 files changed, 554 insertions(+), 51 deletions(-) diff --git a/packages/backend/convex/acoes.ts b/packages/backend/convex/acoes.ts index 3f515f1..1f23c1c 100644 --- a/packages/backend/convex/acoes.ts +++ b/packages/backend/convex/acoes.ts @@ -1,10 +1,15 @@ import { v } from 'convex/values'; import { mutation, query } from './_generated/server'; import { getCurrentUserFunction } from './auth'; +import { internal } from './_generated/api'; export const list = query({ args: {}, handler: async (ctx) => { + await ctx.runQuery(internal.permissoesAcoes.assertPermissaoAcaoAtual, { + recurso: 'acoes', + acao: 'listar' + }); return await ctx.db.query('acoes').collect(); } }); @@ -15,6 +20,11 @@ export const create = mutation({ tipo: v.union(v.literal('projeto'), v.literal('lei')) }, handler: async (ctx, args) => { + await ctx.runQuery(internal.permissoesAcoes.assertPermissaoAcaoAtual, { + recurso: 'acoes', + acao: 'criar' + }); + const user = await getCurrentUserFunction(ctx); if (!user) throw new Error('Unauthorized'); @@ -33,6 +43,11 @@ export const update = mutation({ tipo: v.union(v.literal('projeto'), v.literal('lei')) }, handler: async (ctx, args) => { + await ctx.runQuery(internal.permissoesAcoes.assertPermissaoAcaoAtual, { + recurso: 'acoes', + acao: 'editar' + }); + const user = await getCurrentUserFunction(ctx); if (!user) throw new Error('Unauthorized'); @@ -48,6 +63,11 @@ export const remove = mutation({ id: v.id('acoes') }, handler: async (ctx, args) => { + await ctx.runQuery(internal.permissoesAcoes.assertPermissaoAcaoAtual, { + recurso: 'acoes', + acao: 'excluir' + }); + const user = await getCurrentUserFunction(ctx); if (!user) throw new Error('Unauthorized'); diff --git a/packages/backend/convex/atas.ts b/packages/backend/convex/atas.ts index e599c7a..70d53bb 100644 --- a/packages/backend/convex/atas.ts +++ b/packages/backend/convex/atas.ts @@ -2,10 +2,15 @@ import { v } from 'convex/values'; import { mutation, query } from './_generated/server'; import type { Id } from './_generated/dataModel'; import { getCurrentUserFunction } from './auth'; +import { internal } from './_generated/api'; export const list = query({ args: {}, handler: async (ctx) => { + await ctx.runQuery(internal.permissoesAcoes.assertPermissaoAcaoAtual, { + recurso: 'atas', + acao: 'listar' + }); return await ctx.db.query('atas').collect(); } }); @@ -13,6 +18,10 @@ export const list = query({ export const get = query({ args: { id: v.id('atas') }, handler: async (ctx, args) => { + await ctx.runQuery(internal.permissoesAcoes.assertPermissaoAcaoAtual, { + recurso: 'atas', + acao: 'ver' + }); return await ctx.db.get(args.id); } }); @@ -20,6 +29,10 @@ export const get = query({ export const getObjetos = query({ args: { id: v.id('atas') }, handler: async (ctx, args) => { + await ctx.runQuery(internal.permissoesAcoes.assertPermissaoAcaoAtual, { + recurso: 'atas', + acao: 'ver' + }); const links = await ctx.db .query('atasObjetos') .withIndex('by_ataId', (q) => q.eq('ataId', args.id)) @@ -35,6 +48,10 @@ export const listByObjetoIds = query({ objetoIds: v.array(v.id('objetos')) }, handler: async (ctx, args) => { + await ctx.runQuery(internal.permissoesAcoes.assertPermissaoAcaoAtual, { + recurso: 'atas', + acao: 'listar' + }); if (args.objetoIds.length === 0) return []; // Buscar todos os vínculos ata-objeto para os objetos informados @@ -66,6 +83,11 @@ export const create = mutation({ objetosIds: v.array(v.id('objetos')) }, handler: async (ctx, args) => { + await ctx.runQuery(internal.permissoesAcoes.assertPermissaoAcaoAtual, { + recurso: 'atas', + acao: 'criar' + }); + const user = await getCurrentUserFunction(ctx); if (!user) throw new Error('Unauthorized'); @@ -103,6 +125,11 @@ export const update = mutation({ objetosIds: v.array(v.id('objetos')) }, handler: async (ctx, args) => { + await ctx.runQuery(internal.permissoesAcoes.assertPermissaoAcaoAtual, { + recurso: 'atas', + acao: 'editar' + }); + const user = await getCurrentUserFunction(ctx); if (!user) throw new Error('Unauthorized'); @@ -139,6 +166,11 @@ export const update = mutation({ export const remove = mutation({ args: { id: v.id('atas') }, handler: async (ctx, args) => { + await ctx.runQuery(internal.permissoesAcoes.assertPermissaoAcaoAtual, { + recurso: 'atas', + acao: 'excluir' + }); + const user = await getCurrentUserFunction(ctx); if (!user) throw new Error('Unauthorized'); @@ -170,6 +202,10 @@ export const remove = mutation({ export const generateUploadUrl = mutation({ args: {}, handler: async (ctx) => { + await ctx.runQuery(internal.permissoesAcoes.assertPermissaoAcaoAtual, { + recurso: 'atas', + acao: 'editar' + }); return await ctx.storage.generateUploadUrl(); } }); @@ -183,6 +219,11 @@ export const saveDocumento = mutation({ tamanho: v.number() }, handler: async (ctx, args) => { + await ctx.runQuery(internal.permissoesAcoes.assertPermissaoAcaoAtual, { + recurso: 'atas', + acao: 'editar' + }); + const user = await getCurrentUserFunction(ctx); if (!user) throw new Error('Unauthorized'); @@ -201,6 +242,11 @@ export const saveDocumento = mutation({ export const removeDocumento = mutation({ args: { id: v.id('atasDocumentos') }, handler: async (ctx, args) => { + await ctx.runQuery(internal.permissoesAcoes.assertPermissaoAcaoAtual, { + recurso: 'atas', + acao: 'editar' + }); + const user = await getCurrentUserFunction(ctx); if (!user) throw new Error('Unauthorized'); @@ -215,6 +261,11 @@ export const removeDocumento = mutation({ export const getDocumentos = query({ args: { ataId: v.id('atas') }, handler: async (ctx, args) => { + await ctx.runQuery(internal.permissoesAcoes.assertPermissaoAcaoAtual, { + recurso: 'atas', + acao: 'ver' + }); + const docs = await ctx.db .query('atasDocumentos') .withIndex('by_ataId', (q) => q.eq('ataId', args.ataId)) diff --git a/packages/backend/convex/atestadosLicencas.ts b/packages/backend/convex/atestadosLicencas.ts index 1b6fcf9..a96042f 100644 --- a/packages/backend/convex/atestadosLicencas.ts +++ b/packages/backend/convex/atestadosLicencas.ts @@ -147,6 +147,11 @@ export async function verificarLicencaAtiva( export const listarTodos = query({ args: {}, handler: async (ctx) => { + await ctx.runQuery(internal.permissoesAcoes.assertPermissaoAcaoAtual, { + recurso: 'atestados_licencas', + acao: 'listar' + }); + try { const [atestados, licencas] = await Promise.all([ ctx.db.query('atestados').collect(), @@ -258,6 +263,11 @@ export const listarTodos = query({ export const listarPorFuncionario = query({ args: { funcionarioId: v.id('funcionarios') }, handler: async (ctx, args) => { + await ctx.runQuery(internal.permissoesAcoes.assertPermissaoAcaoAtual, { + recurso: 'atestados_licencas', + acao: 'listar' + }); + const [atestados, licencas] = await Promise.all([ ctx.db .query('atestados') @@ -285,6 +295,11 @@ export const listarPorPeriodo = query({ dataFim: v.string() }, handler: async (ctx, args) => { + await ctx.runQuery(internal.permissoesAcoes.assertPermissaoAcaoAtual, { + recurso: 'atestados_licencas', + acao: 'listar' + }); + const dataInicioObj = new Date(args.dataInicio); const dataFimObj = new Date(args.dataFim); @@ -327,6 +342,10 @@ export const verificarStatusLicenca = query({ }, returns: v.boolean(), handler: async (ctx, args) => { + await ctx.runQuery(internal.permissoesAcoes.assertPermissaoAcaoAtual, { + recurso: 'atestados_licencas', + acao: 'listar' + }); return await verificarLicencaAtiva(ctx, args.funcionarioId); } }); @@ -339,6 +358,11 @@ export const obterDadosGraficos = query({ periodo: v.optional(v.number()) // dias (padrão: 30) }, handler: async (ctx, args) => { + await ctx.runQuery(internal.permissoesAcoes.assertPermissaoAcaoAtual, { + recurso: 'atestados_licencas', + acao: 'dashboard' + }); + try { const dias = args.periodo || 30; const dataLimite = Date.now() - dias * 24 * 60 * 60 * 1000; @@ -588,6 +612,11 @@ export const obterDadosGraficos = query({ export const obterEstatisticas = query({ args: {}, handler: async (ctx) => { + await ctx.runQuery(internal.permissoesAcoes.assertPermissaoAcaoAtual, { + recurso: 'atestados_licencas', + acao: 'dashboard' + }); + const hoje = new Date(); hoje.setHours(0, 0, 0, 0); const inicioMes = new Date(hoje.getFullYear(), hoje.getMonth(), 1); @@ -657,6 +686,11 @@ export const obterEventosCalendario = query({ ) }, handler: async (ctx, args) => { + await ctx.runQuery(internal.permissoesAcoes.assertPermissaoAcaoAtual, { + recurso: 'atestados_licencas', + acao: 'dashboard' + }); + const eventos: Array<{ id: string; title: string; @@ -837,6 +871,11 @@ export const generateUploadUrl = mutation({ args: {}, returns: v.string(), handler: async (ctx) => { + await ctx.runQuery(internal.permissoesAcoes.assertPermissaoAcaoAtual, { + recurso: 'atestados_licencas', + acao: 'criar' + }); + const usuario = await getUsuarioAutenticado(ctx); if (!usuario) throw new Error('Não autenticado'); @@ -853,6 +892,11 @@ export const obterUrlDocumento = query({ }, returns: v.union(v.string(), v.null()), handler: async (ctx, args) => { + await ctx.runQuery(internal.permissoesAcoes.assertPermissaoAcaoAtual, { + recurso: 'atestados_licencas', + acao: 'listar' + }); + const usuario = await getUsuarioAutenticado(ctx); if (!usuario) throw new Error('Não autenticado'); @@ -874,6 +918,11 @@ export const criarAtestadoMedico = mutation({ }, returns: v.id('atestados'), handler: async (ctx, args) => { + await ctx.runQuery(internal.permissoesAcoes.assertPermissaoAcaoAtual, { + recurso: 'atestados_licencas', + acao: 'criar' + }); + const usuario = await getUsuarioAutenticado(ctx); if (!usuario) throw new Error('Não autenticado'); @@ -932,6 +981,11 @@ export const criarDeclaracaoComparecimento = mutation({ }, returns: v.id('atestados'), handler: async (ctx, args) => { + await ctx.runQuery(internal.permissoesAcoes.assertPermissaoAcaoAtual, { + recurso: 'atestados_licencas', + acao: 'criar' + }); + const usuario = await getUsuarioAutenticado(ctx); if (!usuario) throw new Error('Não autenticado'); @@ -990,6 +1044,11 @@ export const criarLicencaMaternidade = mutation({ }, returns: v.id('licencas'), handler: async (ctx, args) => { + await ctx.runQuery(internal.permissoesAcoes.assertPermissaoAcaoAtual, { + recurso: 'atestados_licencas', + acao: 'criar' + }); + const usuario = await getUsuarioAutenticado(ctx); if (!usuario) throw new Error('Não autenticado'); @@ -1054,6 +1113,11 @@ export const criarLicencaPaternidade = mutation({ }, returns: v.id('licencas'), handler: async (ctx, args) => { + await ctx.runQuery(internal.permissoesAcoes.assertPermissaoAcaoAtual, { + recurso: 'atestados_licencas', + acao: 'criar' + }); + const usuario = await getUsuarioAutenticado(ctx); if (!usuario) throw new Error('Não autenticado'); @@ -1112,6 +1176,11 @@ export const prorrogarLicencaMaternidade = mutation({ }, returns: v.id('licencas'), handler: async (ctx, args) => { + await ctx.runQuery(internal.permissoesAcoes.assertPermissaoAcaoAtual, { + recurso: 'atestados_licencas', + acao: 'editar' + }); + const usuario = await getUsuarioAutenticado(ctx); if (!usuario) throw new Error('Não autenticado'); @@ -1169,6 +1238,11 @@ export const excluirAtestado = mutation({ }, returns: v.null(), handler: async (ctx, args) => { + await ctx.runQuery(internal.permissoesAcoes.assertPermissaoAcaoAtual, { + recurso: 'atestados_licencas', + acao: 'excluir' + }); + const usuario = await getUsuarioAutenticado(ctx); if (!usuario) throw new Error('Não autenticado'); @@ -1215,6 +1289,11 @@ export const excluirLicenca = mutation({ }, returns: v.null(), handler: async (ctx, args) => { + await ctx.runQuery(internal.permissoesAcoes.assertPermissaoAcaoAtual, { + recurso: 'atestados_licencas', + acao: 'excluir' + }); + const usuario = await getUsuarioAutenticado(ctx); if (!usuario) throw new Error('Não autenticado'); diff --git a/packages/backend/convex/ausencias.ts b/packages/backend/convex/ausencias.ts index 8f9f220..819f7e4 100644 --- a/packages/backend/convex/ausencias.ts +++ b/packages/backend/convex/ausencias.ts @@ -11,6 +11,11 @@ import { getCurrentUserFunction } from './auth'; export const listarTodas = query({ args: {}, handler: async (ctx) => { + await ctx.runQuery(internal.permissoesAcoes.assertPermissaoAcaoAtual, { + recurso: 'ausencias', + acao: 'listar' + }); + const solicitacoes = await ctx.db.query('solicitacoesAusencias').collect(); const solicitacoesComDetalhes = await Promise.all( @@ -65,6 +70,11 @@ export const listarMinhasSolicitacoes = query({ _refresh: v.optional(v.number()) // Parâmetro para forçar atualização no frontend }, handler: async (ctx, args) => { + await ctx.runQuery(internal.permissoesAcoes.assertPermissaoAcaoAtual, { + recurso: 'ausencias', + acao: 'criar' + }); + const solicitacoes = await ctx.db .query('solicitacoesAusencias') .withIndex('by_funcionario', (q) => q.eq('funcionarioId', args.funcionarioId)) @@ -104,6 +114,11 @@ export const listarMinhasSolicitacoes = query({ export const listarSolicitacoesSubordinados = query({ args: { gestorId: v.id('usuarios') }, handler: async (ctx, args) => { + await ctx.runQuery(internal.permissoesAcoes.assertPermissaoAcaoAtual, { + recurso: 'ausencias', + acao: 'aprovar' + }); + // Buscar times onde o usuário é gestor const timesGestor = await ctx.db .query('times') @@ -170,6 +185,11 @@ export const listarSolicitacoesSubordinados = query({ export const obterDetalhes = query({ args: { solicitacaoId: v.id('solicitacoesAusencias') }, handler: async (ctx, args) => { + await ctx.runQuery(internal.permissoesAcoes.assertPermissaoAcaoAtual, { + recurso: 'ausencias', + acao: 'listar' + }); + const solicitacao = await ctx.db.get(args.solicitacaoId); if (!solicitacao) return null; @@ -237,6 +257,11 @@ export const obterDetalhes = query({ export const obterNotificacoesNaoLidas = query({ args: { usuarioId: v.id('usuarios') }, handler: async (ctx, args) => { + await ctx.runQuery(internal.permissoesAcoes.assertPermissaoAcaoAtual, { + recurso: 'ausencias', + acao: 'criar' + }); + const notificacoes = await ctx.db .query('notificacoesAusencias') .withIndex('by_destinatario_and_lida', (q) => @@ -253,6 +278,11 @@ export const obterNotificacoesNaoLidas = query({ export const contarPendentesGestor = query({ args: { gestorId: v.id('usuarios') }, handler: async (ctx, args) => { + await ctx.runQuery(internal.permissoesAcoes.assertPermissaoAcaoAtual, { + recurso: 'ausencias', + acao: 'aprovar' + }); + // Buscar times onde o usuário é gestor const timesGestor = await ctx.db .query('times') @@ -359,6 +389,11 @@ export const criarSolicitacao = mutation({ }, returns: v.id('solicitacoesAusencias'), handler: async (ctx, args) => { + await ctx.runQuery(internal.permissoesAcoes.assertPermissaoAcaoAtual, { + recurso: 'ausencias', + acao: 'criar' + }); + // Validações if (args.motivo.trim().length < 10) { throw new Error('O motivo deve ter no mínimo 10 caracteres'); @@ -547,6 +582,11 @@ export const aprovar = mutation({ }, returns: v.null(), handler: async (ctx, args) => { + await ctx.runQuery(internal.permissoesAcoes.assertPermissaoAcaoAtual, { + recurso: 'ausencias', + acao: 'aprovar' + }); + const solicitacao = await ctx.db.get(args.solicitacaoId); if (!solicitacao) { throw new Error('Solicitação não encontrada'); @@ -710,6 +750,11 @@ export const reprovar = mutation({ }, returns: v.null(), handler: async (ctx, args) => { + await ctx.runQuery(internal.permissoesAcoes.assertPermissaoAcaoAtual, { + recurso: 'ausencias', + acao: 'reprovar' + }); + const solicitacao = await ctx.db.get(args.solicitacaoId); if (!solicitacao) { throw new Error('Solicitação não encontrada'); @@ -866,6 +911,11 @@ export const marcarComoLida = mutation({ }, returns: v.null(), handler: async (ctx, args) => { + await ctx.runQuery(internal.permissoesAcoes.assertPermissaoAcaoAtual, { + recurso: 'ausencias', + acao: 'criar' + }); + await ctx.db.patch(args.notificacaoId, { lida: true }); diff --git a/packages/backend/convex/ferias.ts b/packages/backend/convex/ferias.ts index 60c07dd..862fa1a 100644 --- a/packages/backend/convex/ferias.ts +++ b/packages/backend/convex/ferias.ts @@ -76,6 +76,11 @@ function agruparPorSolicitacao(registros: Array>): Array<{ export const listarTodas = query({ args: {}, handler: async (ctx) => { + await ctx.runQuery(internal.permissoesAcoes.assertPermissaoAcaoAtual, { + recurso: 'ferias', + acao: 'listar' + }); + const todasFerias = await ctx.db.query('ferias').collect(); const periodosComDetalhes = await Promise.all( @@ -146,6 +151,11 @@ export const listarMinhasSolicitacoes = query({ _refresh: v.optional(v.number()) // Parâmetro para forçar atualização no frontend }, handler: async (ctx, args) => { + await ctx.runQuery(internal.permissoesAcoes.assertPermissaoAcaoAtual, { + recurso: 'ferias', + acao: 'dashboard' + }); + const todasFerias = await ctx.db .query('ferias') .withIndex('by_funcionario', (q) => q.eq('funcionarioId', args.funcionarioId)) @@ -180,6 +190,11 @@ export const listarMinhasSolicitacoes = query({ export const listarSolicitacoesSubordinados = query({ args: { gestorId: v.id('usuarios') }, handler: async (ctx, args) => { + await ctx.runQuery(internal.permissoesAcoes.assertPermissaoAcaoAtual, { + recurso: 'ferias', + acao: 'dashboard' + }); + // Buscar times onde o usuário é gestor const timesGestor = await ctx.db .query('times') @@ -259,6 +274,11 @@ export const obterDetalhes = query({ feriasId: v.id('ferias') }, handler: async (ctx, args) => { + await ctx.runQuery(internal.permissoesAcoes.assertPermissaoAcaoAtual, { + recurso: 'ferias', + acao: 'ver' + }); + const ferias = await ctx.db.get(args.feriasId); if (!ferias) return null; @@ -333,6 +353,11 @@ export const criarSolicitacao = mutation({ }, returns: v.array(v.id('ferias')), handler: async (ctx, args) => { + await ctx.runQuery(internal.permissoesAcoes.assertPermissaoAcaoAtual, { + recurso: 'ferias', + acao: 'criar' + }); + if (args.periodos.length === 0) { throw new Error('É necessário adicionar pelo menos 1 período'); } @@ -394,6 +419,11 @@ export const aprovar = mutation({ }, returns: v.null(), handler: async (ctx, args) => { + await ctx.runQuery(internal.permissoesAcoes.assertPermissaoAcaoAtual, { + recurso: 'ferias', + acao: 'aprovar' + }); + // Buscar o registro específico const registro = await ctx.db.get(args.feriasId); @@ -503,6 +533,11 @@ export const reprovar = mutation({ }, returns: v.null(), handler: async (ctx, args) => { + await ctx.runQuery(internal.permissoesAcoes.assertPermissaoAcaoAtual, { + recurso: 'ferias', + acao: 'reprovar' + }); + // Buscar o registro específico const registro = await ctx.db.get(args.feriasId); @@ -565,6 +600,11 @@ export const ajustarEAprovar = mutation({ }, returns: v.null(), handler: async (ctx, args) => { + await ctx.runQuery(internal.permissoesAcoes.assertPermissaoAcaoAtual, { + recurso: 'ferias', + acao: 'aprovar' + }); + // Buscar o registro específico const registroAntigo = await ctx.db.get(args.feriasId); @@ -676,6 +716,11 @@ export const verificarStatusFerias = query({ args: { funcionarioId: v.id('funcionarios') }, returns: v.union(v.literal('ativo'), v.literal('em_ferias')), handler: async (ctx, args) => { + await ctx.runQuery(internal.permissoesAcoes.assertPermissaoAcaoAtual, { + recurso: 'ferias', + acao: 'ver' + }); + const hoje = new Date(); hoje.setHours(0, 0, 0, 0); @@ -721,6 +766,11 @@ export const verificarStatusFerias = query({ export const obterNotificacoesNaoLidas = query({ args: { usuarioId: v.id('usuarios') }, handler: async (ctx, args) => { + await ctx.runQuery(internal.permissoesAcoes.assertPermissaoAcaoAtual, { + recurso: 'ferias', + acao: 'dashboard' + }); + return await ctx.db .query('notificacoesFerias') .withIndex('by_destinatario_and_lida', (q) => @@ -735,6 +785,10 @@ export const marcarComoLida = mutation({ args: { notificacaoId: v.id('notificacoesFerias') }, returns: v.null(), handler: async (ctx, args) => { + await ctx.runQuery(internal.permissoesAcoes.assertPermissaoAcaoAtual, { + recurso: 'ferias', + acao: 'dashboard' + }); await ctx.db.patch(args.notificacaoId, { lida: true }); return null; } @@ -755,6 +809,11 @@ export const atualizarStatus = mutation({ }, returns: v.null(), handler: async (ctx, args) => { + await ctx.runQuery(internal.permissoesAcoes.assertPermissaoAcaoAtual, { + recurso: 'ferias', + acao: 'editar_status' + }); + // Buscar o registro específico const registro = await ctx.db.get(args.feriasId); @@ -1184,6 +1243,11 @@ export const atualizarMeuStatus = mutation({ args: {}, returns: v.null(), handler: async (ctx) => { + await ctx.runQuery(internal.permissoesAcoes.assertPermissaoAcaoAtual, { + recurso: 'ferias', + acao: 'atualizar_status' + }); + const usuario = await getCurrentUserFunction(ctx); if (!usuario || !usuario.funcionarioId) { throw new Error('Usuário não encontrado ou não possui funcionário associado'); diff --git a/packages/backend/convex/permissoesAcoes.ts b/packages/backend/convex/permissoesAcoes.ts index 498c47f..d2f1f21 100644 --- a/packages/backend/convex/permissoesAcoes.ts +++ b/packages/backend/convex/permissoesAcoes.ts @@ -55,12 +55,48 @@ const PERMISSOES_BASE = { acao: 'aprovar', descricao: 'Aprovar férias de funcionários' }, + { + nome: 'ferias.reprovar', + recurso: 'ferias', + acao: 'reprovar', + descricao: 'Reprovar férias de funcionários' + }, { nome: 'ferias.dashboard', recurso: 'ferias', acao: 'dashboard', descricao: 'Acessar o painel de férias de funcionários' }, + { + nome: 'ferias.listar', + recurso: 'ferias', + acao: 'listar', + descricao: 'Listar solicitações e períodos de férias' + }, + { + nome: 'ferias.criar', + recurso: 'ferias', + acao: 'criar', + descricao: 'Criar solicitações de férias' + }, + { + nome: 'ferias.editar_status', + recurso: 'ferias', + acao: 'editar_status', + descricao: 'Editar/cancelar status de solicitações de férias' + }, + { + nome: 'ferias.atualizar_status', + recurso: 'ferias', + acao: 'atualizar_status', + descricao: 'Atualizar status automático de férias (administração)' + }, + { + nome: 'ferias.ver', + recurso: 'ferias', + acao: 'ver', + descricao: 'Visualizar detalhes de uma solicitação/período de férias' + }, // Atestados e Licenças { nome: 'atestados_licencas.listar', diff --git a/packages/backend/convex/pontos.ts b/packages/backend/convex/pontos.ts index a0685d1..f626205 100644 --- a/packages/backend/convex/pontos.ts +++ b/packages/backend/convex/pontos.ts @@ -321,6 +321,11 @@ function validarAcelerometro( export const generateUploadUrl = mutation({ args: {}, handler: async (ctx) => { + await ctx.runQuery(internal.permissoesAcoes.assertPermissaoAcaoAtual, { + recurso: 'ponto', + acao: 'registrar' + }); + const usuario = await getCurrentUserFunction(ctx); if (!usuario) { throw new Error('Usuário não autenticado'); @@ -543,6 +548,11 @@ export const registrarPonto = mutation({ justificativa: v.optional(v.string()) }, handler: async (ctx, args) => { + await ctx.runQuery(internal.permissoesAcoes.assertPermissaoAcaoAtual, { + recurso: 'ponto', + acao: 'registrar' + }); + const usuario = await getCurrentUserFunction(ctx); if (!usuario) { throw new Error('Usuário não autenticado'); @@ -921,6 +931,15 @@ export const listarRegistrosDia = query({ _refresh: v.optional(v.number()) // Parâmetro usado pelo frontend para forçar refresh }, handler: async (ctx, args) => { + try { + await ctx.runQuery(internal.permissoesAcoes.assertPermissaoAcaoAtual, { + recurso: 'ponto', + acao: 'ver' + }); + } catch { + return []; + } + const usuario = await getCurrentUserFunction(ctx); if (!usuario || !usuario.funcionarioId) { return []; @@ -967,6 +986,11 @@ export const obterSaldoDiario = query({ data: v.string() // YYYY-MM-DD }, handler: async (ctx, args) => { + await ctx.runQuery(internal.permissoesAcoes.assertPermissaoAcaoAtual, { + recurso: 'banco_horas', + acao: 'ver' + }); + // Buscar banco de horas do dia const bancoHoras = await ctx.db .query('bancoHoras') @@ -1007,15 +1031,22 @@ export const listarRegistrosPeriodo = query({ dataFim: v.string() // YYYY-MM-DD }, handler: async (ctx, args) => { + try { + await ctx.runQuery(internal.permissoesAcoes.assertPermissaoAcaoAtual, { + recurso: 'ponto', + acao: 'ver' + }); + } catch { + return []; + } + const usuario = await getCurrentUserFunction(ctx); if (!usuario) { console.warn('[listarRegistrosPeriodo] Usuário não autenticado'); return []; } - // Verificar permissão (RH ou TI) - // Por enquanto, permitir se tiver funcionarioId ou for admin - // TODO: Implementar verificação de permissão adequada + // Permissão já verificada acima (ponto.ver) // Validar formato das datas if (!args.dataInicio || !args.dataFim) { @@ -1185,6 +1216,22 @@ export const obterEstatisticas = query({ funcionarioId: v.optional(v.id('funcionarios')) }, handler: async (ctx, args) => { + try { + await ctx.runQuery(internal.permissoesAcoes.assertPermissaoAcaoAtual, { + recurso: 'ponto', + acao: 'ver' + }); + } catch { + return { + totalRegistros: 0, + dentroDoPrazo: 0, + foraDoPrazo: 0, + totalFuncionarios: 0, + funcionariosDentroPrazo: 0, + funcionariosForaPrazo: 0 + }; + } + const usuario = await getCurrentUserFunction(ctx); if (!usuario) { // Retornar estatísticas zeradas quando não autenticado @@ -1198,7 +1245,7 @@ export const obterEstatisticas = query({ }; } - // TODO: Verificar permissão (RH ou TI) + // Permissão já verificada acima (ponto.ver) let registros = await ctx.db .query('registrosPonto') @@ -1245,6 +1292,11 @@ export const obterRegistro = query({ registroId: v.id('registrosPonto') }, handler: async (ctx, args) => { + await ctx.runQuery(internal.permissoesAcoes.assertPermissaoAcaoAtual, { + recurso: 'ponto', + acao: 'ver' + }); + const usuario = await getCurrentUserFunction(ctx); if (!usuario) { throw new Error('Usuário não autenticado'); @@ -1255,11 +1307,7 @@ export const obterRegistro = query({ throw new Error('Registro não encontrado'); } - // Verificar se o usuário tem permissão (próprio registro ou RH/TI) - if (registro.funcionarioId !== usuario.funcionarioId) { - // TODO: Verificar se é RH ou TI - // Por enquanto, permitir - } + // Permissão já verificada acima (ponto.ver) const funcionario = await ctx.db.get(registro.funcionarioId); let simbolo = null; @@ -1813,6 +1861,20 @@ export const obterHistoricoESaldoDia = query({ _refresh: v.optional(v.number()) // Parâmetro usado pelo frontend para forçar refresh }, handler: async (ctx, args) => { + try { + await ctx.runQuery(internal.permissoesAcoes.assertPermissaoAcaoAtual, { + recurso: 'ponto', + acao: 'ver' + }); + } catch { + return { + registros: [], + cargaHorariaDiaria: 0, + horasTrabalhadas: 0, + saldoMinutos: 0 + }; + } + const usuario = await getCurrentUserFunction(ctx); if (!usuario || !usuario.funcionarioId) { console.warn('[obterHistoricoESaldoDia] Usuário não autenticado ou sem funcionarioId'); @@ -1825,10 +1887,7 @@ export const obterHistoricoESaldoDia = query({ }; } - // Verificar se é o próprio funcionário ou tem permissão - if (usuario.funcionarioId !== args.funcionarioId) { - // TODO: Verificar permissão de RH - } + // Permissão já verificada acima (ponto.ver) // Buscar registros do dia const registros = await ctx.db @@ -1890,15 +1949,17 @@ export const obterBancoHorasFuncionario = query({ funcionarioId: v.id('funcionarios') }, handler: async (ctx, args) => { + await ctx.runQuery(internal.permissoesAcoes.assertPermissaoAcaoAtual, { + recurso: 'banco_horas', + acao: 'ver' + }); + const usuario = await getCurrentUserFunction(ctx); if (!usuario) { throw new Error('Usuário não autenticado'); } - // Verificar se é o próprio funcionário ou tem permissão - if (usuario.funcionarioId !== args.funcionarioId) { - // TODO: Verificar permissão de RH - } + // Permissão já verificada acima (banco_horas.ver) // Buscar todos os registros de banco de horas do funcionário const bancosHoras = await ctx.db @@ -2056,15 +2117,17 @@ export const obterBancoHorasMensal = query({ mes: v.string() // YYYY-MM }, handler: async (ctx, args) => { + await ctx.runQuery(internal.permissoesAcoes.assertPermissaoAcaoAtual, { + recurso: 'banco_horas', + acao: 'ver' + }); + const usuario = await getCurrentUserFunction(ctx); if (!usuario) { throw new Error('Usuário não autenticado'); } - // Verificar se é o próprio funcionário ou tem permissão - if (usuario.funcionarioId !== args.funcionarioId) { - // TODO: Verificar permissão de RH - } + // Permissão já verificada acima (banco_horas.ver) const bancoMensal = await ctx.db .query('bancoHorasMensal') @@ -2119,15 +2182,17 @@ export const listarHistoricoMensal = query({ mesFim: v.optional(v.string()) // YYYY-MM }, handler: async (ctx, args) => { + await ctx.runQuery(internal.permissoesAcoes.assertPermissaoAcaoAtual, { + recurso: 'banco_horas', + acao: 'ver' + }); + const usuario = await getCurrentUserFunction(ctx); if (!usuario) { throw new Error('Usuário não autenticado'); } - // Verificar se é o próprio funcionário ou tem permissão - if (usuario.funcionarioId !== args.funcionarioId) { - // TODO: Verificar permissão de RH - } + // Permissão já verificada acima (banco_horas.ver) let query = ctx.db .query('bancoHorasMensal') @@ -2242,15 +2307,17 @@ export const verificarAlertasBancoHoras = query({ funcionarioId: v.id('funcionarios') }, handler: async (ctx, args) => { + await ctx.runQuery(internal.permissoesAcoes.assertPermissaoAcaoAtual, { + recurso: 'banco_horas', + acao: 'ver' + }); + const usuario = await getCurrentUserFunction(ctx); if (!usuario) { throw new Error('Usuário não autenticado'); } - // Verificar se é o próprio funcionário ou tem permissão - if (usuario.funcionarioId !== args.funcionarioId) { - // TODO: Verificar permissão de RH - } + // Permissão já verificada acima (banco_horas.ver) // Buscar banco de horas mensal mais recente const hoje = new Date(); @@ -2356,6 +2423,11 @@ export const editarRegistroPonto = mutation({ observacoes: v.optional(v.string()) }, handler: async (ctx, args) => { + await ctx.runQuery(internal.permissoesAcoes.assertPermissaoAcaoAtual, { + recurso: 'ponto', + acao: 'editar' + }); + const usuario = await getCurrentUserFunction(ctx); if (!usuario) { throw new Error('Usuário não autenticado'); @@ -2441,6 +2513,11 @@ export const ajustarBancoHoras = mutation({ ajusteMinutos: v.number() }), handler: async (ctx, args) => { + await ctx.runQuery(internal.permissoesAcoes.assertPermissaoAcaoAtual, { + recurso: 'banco_horas', + acao: 'ajustar' + }); + const usuario = await getCurrentUserFunction(ctx); if (!usuario) { throw new Error('Usuário não autenticado'); @@ -2564,6 +2641,11 @@ export const listarHomologacoes = query({ funcionarioId: v.optional(v.id('funcionarios')) }, handler: async (ctx, args) => { + await ctx.runQuery(internal.permissoesAcoes.assertPermissaoAcaoAtual, { + recurso: 'ponto', + acao: 'editar' + }); + const usuario = await getCurrentUserFunction(ctx); if (!usuario) { throw new Error('Usuário não autenticado'); @@ -2650,6 +2732,11 @@ export const excluirHomologacao = mutation({ homologacaoId: v.id('homologacoesPonto') }, handler: async (ctx, args) => { + await ctx.runQuery(internal.permissoesAcoes.assertPermissaoAcaoAtual, { + recurso: 'ponto', + acao: 'editar' + }); + const usuario = await getCurrentUserFunction(ctx); if (!usuario) { throw new Error('Usuário não autenticado'); @@ -2694,6 +2781,11 @@ export const excluirHomologacao = mutation({ export const obterMotivosAtestados = query({ args: {}, handler: async (ctx) => { + await ctx.runQuery(internal.permissoesAcoes.assertPermissaoAcaoAtual, { + recurso: 'banco_horas', + acao: 'ajustar' + }); + // Buscar tipos de atestados e declarações const atestados = await ctx.db.query('atestados').collect(); const tiposUnicos = new Set(); @@ -2733,6 +2825,11 @@ export const criarDispensaRegistro = mutation({ isento: v.boolean() }, handler: async (ctx, args) => { + await ctx.runQuery(internal.permissoesAcoes.assertPermissaoAcaoAtual, { + recurso: 'ponto', + acao: 'editar' + }); + const usuario = await getCurrentUserFunction(ctx); if (!usuario) { throw new Error('Usuário não autenticado'); @@ -2780,6 +2877,11 @@ export const removerDispensaRegistro = mutation({ dispensaId: v.id('dispensasRegistro') }, handler: async (ctx, args) => { + await ctx.runQuery(internal.permissoesAcoes.assertPermissaoAcaoAtual, { + recurso: 'ponto', + acao: 'editar' + }); + const usuario = await getCurrentUserFunction(ctx); if (!usuario) { throw new Error('Usuário não autenticado'); @@ -2814,6 +2916,11 @@ export const listarDispensas = query({ apenasAtivas: v.optional(v.boolean()) }, handler: async (ctx, args) => { + await ctx.runQuery(internal.permissoesAcoes.assertPermissaoAcaoAtual, { + recurso: 'ponto', + acao: 'editar' + }); + const usuario = await getCurrentUserFunction(ctx); if (!usuario) { throw new Error('Usuário não autenticado'); @@ -2917,12 +3024,17 @@ export const obterEstatisticasBancoHorasGerencial = query({ funcionarioId: v.optional(v.id('funcionarios')) }, handler: async (ctx, args) => { + await ctx.runQuery(internal.permissoesAcoes.assertPermissaoAcaoAtual, { + recurso: 'banco_horas', + acao: 'ver' + }); + const usuario = await getCurrentUserFunction(ctx); if (!usuario) { throw new Error('Usuário não autenticado'); } - // TODO: Verificar permissão de RH/TI + // Permissão já verificada acima (banco_horas.ver) // Buscar todos os bancos de horas do mês let bancosMensais = await ctx.db @@ -2997,15 +3109,17 @@ export const listarHistoricoAlteracoesBancoHoras = query({ mes: v.optional(v.string()) // YYYY-MM - se fornecido, filtra por mês }, handler: async (ctx, args) => { + await ctx.runQuery(internal.permissoesAcoes.assertPermissaoAcaoAtual, { + recurso: 'banco_horas', + acao: 'ver' + }); + const usuario = await getCurrentUserFunction(ctx); if (!usuario) { throw new Error('Usuário não autenticado'); } - // Verificar se é o próprio funcionário ou tem permissão - if (usuario.funcionarioId !== args.funcionarioId) { - // TODO: Verificar permissão de RH - } + // Permissão já verificada acima (banco_horas.ver) // Buscar homologações do funcionário let homologacoes = await ctx.db @@ -3089,6 +3203,11 @@ export const verificarDispensaAtiva = query({ minuto: v.optional(v.number()) }, handler: async (ctx, args) => { + await ctx.runQuery(internal.permissoesAcoes.assertPermissaoAcaoAtual, { + recurso: 'ponto', + acao: 'ver' + }); + const dispensas = await ctx.db .query('dispensasRegistro') .withIndex('by_funcionario', (q) => q.eq('funcionarioId', args.funcionarioId)) @@ -3217,15 +3336,17 @@ export const obterBancoHorasCompleto = query({ ) }), handler: async (ctx, args) => { + await ctx.runQuery(internal.permissoesAcoes.assertPermissaoAcaoAtual, { + recurso: 'banco_horas', + acao: 'ver' + }); + const usuario = await getCurrentUserFunction(ctx); if (!usuario) { throw new Error('Usuário não autenticado'); } - // Verificar se é o próprio funcionário ou tem permissão - if (usuario.funcionarioId !== args.funcionarioId) { - // TODO: Verificar permissão de RH - } + // Permissão já verificada acima (banco_horas.ver) const bancoHoras = await ctx.db .query('bancoHoras') @@ -3314,15 +3435,17 @@ export const listarAjustesBancoHoras = query({ }) ), handler: async (ctx, args) => { + await ctx.runQuery(internal.permissoesAcoes.assertPermissaoAcaoAtual, { + recurso: 'banco_horas', + acao: 'ver' + }); + const usuario = await getCurrentUserFunction(ctx); if (!usuario) { throw new Error('Usuário não autenticado'); } - // Verificar se é o próprio funcionário ou tem permissão - if (usuario.funcionarioId !== args.funcionarioId) { - // TODO: Verificar permissão de RH - } + // Permissão já verificada acima (banco_horas.ver) let query = ctx.db .query('ajustesBancoHoras') @@ -3402,6 +3525,11 @@ export const listarInconsistenciasBancoHoras = query({ }) ), handler: async (ctx, args) => { + await ctx.runQuery(internal.permissoesAcoes.assertPermissaoAcaoAtual, { + recurso: 'banco_horas', + acao: 'ver' + }); + const usuario = await getCurrentUserFunction(ctx); if (!usuario) { throw new Error('Usuário não autenticado'); @@ -3492,12 +3620,17 @@ export const obterConfiguracaoBancoHoras = query({ v.null() ), handler: async (ctx) => { + await ctx.runQuery(internal.permissoesAcoes.assertPermissaoAcaoAtual, { + recurso: 'banco_horas', + acao: 'configurar' + }); + const usuario = await getCurrentUserFunction(ctx); if (!usuario) { throw new Error('Usuário não autenticado'); } - // TODO: Verificar permissão de TI + // Permissão já verificada acima (banco_horas.configurar) const config = await ctx.db.query('configuracaoBancoHoras').order('desc').first(); @@ -3540,12 +3673,17 @@ export const obterAlertasConfigurados = query({ }) ), handler: async (ctx) => { + await ctx.runQuery(internal.permissoesAcoes.assertPermissaoAcaoAtual, { + recurso: 'banco_horas', + acao: 'configurar' + }); + const usuario = await getCurrentUserFunction(ctx); if (!usuario) { throw new Error('Usuário não autenticado'); } - // TODO: Verificar permissão de TI + // Permissão já verificada acima (banco_horas.configurar) // Retornar todos os alertas (ativos e inativos) para permitir edição const alertas = await ctx.db.query('alertasBancoHoras').collect(); @@ -3591,15 +3729,17 @@ export const verificarInconsistencias = query({ }) ), handler: async (ctx, args) => { + await ctx.runQuery(internal.permissoesAcoes.assertPermissaoAcaoAtual, { + recurso: 'banco_horas', + acao: 'ver' + }); + const usuario = await getCurrentUserFunction(ctx); if (!usuario) { throw new Error('Usuário não autenticado'); } - // Verificar se é o próprio funcionário ou tem permissão - if (usuario.funcionarioId !== args.funcionarioId) { - // TODO: Verificar permissão de RH - } + // Permissão já verificada acima (banco_horas.ver) let query = ctx.db .query('inconsistenciasBancoHoras') @@ -3650,6 +3790,11 @@ export const criarAjusteBancoHoras = mutation({ success: v.boolean() }), handler: async (ctx, args) => { + await ctx.runQuery(internal.permissoesAcoes.assertPermissaoAcaoAtual, { + recurso: 'banco_horas', + acao: 'ajustar' + }); + const usuario = await getCurrentUserFunction(ctx); if (!usuario) { throw new Error('Usuário não autenticado'); @@ -3745,6 +3890,11 @@ export const resolverInconsistencia = mutation({ success: v.boolean() }), handler: async (ctx, args) => { + await ctx.runQuery(internal.permissoesAcoes.assertPermissaoAcaoAtual, { + recurso: 'banco_horas', + acao: 'ajustar' + }); + const usuario = await getCurrentUserFunction(ctx); if (!usuario) { throw new Error('Usuário não autenticado'); @@ -3794,12 +3944,17 @@ export const atualizarConfiguracaoBancoHoras = mutation({ configId: v.id('configuracaoBancoHoras') }), handler: async (ctx, args) => { + await ctx.runQuery(internal.permissoesAcoes.assertPermissaoAcaoAtual, { + recurso: 'banco_horas', + acao: 'configurar' + }); + const usuario = await getCurrentUserFunction(ctx); if (!usuario) { throw new Error('Usuário não autenticado'); } - // TODO: Verificar permissão de TI + // Permissão já verificada acima (banco_horas.configurar) // Buscar configuração existente ou criar nova const configExistente = await ctx.db.query('configuracaoBancoHoras').order('desc').first(); @@ -3854,12 +4009,17 @@ export const criarAlertaBancoHoras = mutation({ alertaId: v.id('alertasBancoHoras') }), handler: async (ctx, args) => { + await ctx.runQuery(internal.permissoesAcoes.assertPermissaoAcaoAtual, { + recurso: 'banco_horas', + acao: 'configurar' + }); + const usuario = await getCurrentUserFunction(ctx); if (!usuario) { throw new Error('Usuário não autenticado'); } - // TODO: Verificar permissão de TI + // Permissão já verificada acima (banco_horas.configurar) // Verificar se já existe alerta do mesmo tipo const alertaExistente = await ctx.db @@ -3907,12 +4067,17 @@ export const atualizarConfiguracaoAlerta = mutation({ success: v.boolean() }), handler: async (ctx, args) => { + await ctx.runQuery(internal.permissoesAcoes.assertPermissaoAcaoAtual, { + recurso: 'banco_horas', + acao: 'configurar' + }); + const usuario = await getCurrentUserFunction(ctx); if (!usuario) { throw new Error('Usuário não autenticado'); } - // TODO: Verificar permissão de TI + // Permissão já verificada acima (banco_horas.configurar) const alerta = await ctx.db.get(args.alertaId); if (!alerta) { @@ -3952,6 +4117,11 @@ export const recalcularBancoHoras = mutation({ diasRecalculados: v.number() }), handler: async (ctx, args) => { + await ctx.runQuery(internal.permissoesAcoes.assertPermissaoAcaoAtual, { + recurso: 'banco_horas', + acao: 'configurar' + }); + const usuario = await getCurrentUserFunction(ctx); if (!usuario) { throw new Error('Usuário não autenticado'); @@ -4329,6 +4499,11 @@ export const inicializarAlertasPadrao = mutation({ alertasCriados: v.number() }), handler: async (ctx) => { + await ctx.runQuery(internal.permissoesAcoes.assertPermissaoAcaoAtual, { + recurso: 'banco_horas', + acao: 'configurar' + }); + const usuario = await getCurrentUserFunction(ctx); if (!usuario) { throw new Error('Usuário não autenticado'); diff --git a/packages/backend/convex/simbolos.ts b/packages/backend/convex/simbolos.ts index 2f299be..8d5d4e2 100644 --- a/packages/backend/convex/simbolos.ts +++ b/packages/backend/convex/simbolos.ts @@ -1,5 +1,6 @@ import { v } from 'convex/values'; import { mutation, query } from './_generated/server'; +import { internal } from './_generated/api'; import { simboloTipo } from './tables/funcionarios'; export const getAll = query({ @@ -17,6 +18,10 @@ export const getAll = query({ }) ), handler: async (ctx) => { + await ctx.runQuery(internal.permissoesAcoes.assertPermissaoAcaoAtual, { + recurso: 'simbolos', + acao: 'listar' + }); return await ctx.db.query('simbolos').collect(); } }); @@ -39,6 +44,10 @@ export const getById = query({ v.null() ), handler: async (ctx, args) => { + await ctx.runQuery(internal.permissoesAcoes.assertPermissaoAcaoAtual, { + recurso: 'simbolos', + acao: 'ver' + }); return await ctx.db.get(args.id); } }); @@ -53,6 +62,11 @@ export const create = mutation({ valor: v.optional(v.string()) }, handler: async (ctx, args) => { + await ctx.runQuery(internal.permissoesAcoes.assertPermissaoAcaoAtual, { + recurso: 'simbolos', + acao: 'criar' + }); + let refValor = args.refValor; let vencValor = args.vencValor; let valor = args.valor ?? ''; @@ -90,6 +104,10 @@ export const remove = mutation({ }, returns: v.null(), handler: async (ctx, args) => { + await ctx.runQuery(internal.permissoesAcoes.assertPermissaoAcaoAtual, { + recurso: 'simbolos', + acao: 'excluir' + }); await ctx.db.delete(args.id); return null; } @@ -107,6 +125,11 @@ export const update = mutation({ }, returns: v.null(), handler: async (ctx, args) => { + await ctx.runQuery(internal.permissoesAcoes.assertPermissaoAcaoAtual, { + recurso: 'simbolos', + acao: 'editar' + }); + let refValor = args.refValor; let vencValor = args.vencValor; let valor = args.valor ?? ''; @@ -149,6 +172,11 @@ export const removerDuplicados = mutation({ mantidos: v.number() }), handler: async (ctx) => { + await ctx.runQuery(internal.permissoesAcoes.assertPermissaoAcaoAtual, { + recurso: 'simbolos', + acao: 'excluir' + }); + const todosSimbolos = await ctx.db.query('simbolos').collect(); // Agrupar símbolos por nome