Implandação da customização das permissões dos usuarios e das notificaçoes
This commit is contained in:
@@ -1,7 +1,10 @@
|
||||
# app/controllers/admin/auditoria_logs_controller.rb
|
||||
class Admin::AuditoriaLogsController < ApplicationController
|
||||
def index
|
||||
authorize AuditoriaLog, policy_class: ApplicationPolicy
|
||||
# Policy própria (AuditoriaLogPolicy). Com `policy_class: ApplicationPolicy`
|
||||
# a régua era a de consolidação: quem podia consolidar abria a auditoria pela
|
||||
# URL, mesmo sem o item no menu.
|
||||
authorize AuditoriaLog
|
||||
|
||||
@logs = AuditoriaLog.recentes.limit(200)
|
||||
@logs = @logs.por_user(params[:user_id]) if params[:user_id].present?
|
||||
|
||||
@@ -36,7 +36,7 @@ class Admin::MensagemTemplatesController < ApplicationController
|
||||
assunto: params[:assunto], blocos: blocos_do_form, ativo: true)
|
||||
provisorio.validate # dispara a limpeza dos blocos antes de renderizar
|
||||
|
||||
dados = Notificacao::Variaveis.amostra(@evento.gatilho)
|
||||
dados = Notificacao::CatalogoVariaveis.amostra(@evento.gatilho)
|
||||
render json: { html: corpo_do_preview(provisorio, dados),
|
||||
assunto: provisorio.renderizador(dados).interpolar(provisorio.assunto) }
|
||||
end
|
||||
|
||||
110
app/controllers/admin/perfis_acesso_controller.rb
Normal file
110
app/controllers/admin/perfis_acesso_controller.rb
Normal file
@@ -0,0 +1,110 @@
|
||||
# app/controllers/admin/perfis_acesso_controller.rb
|
||||
#
|
||||
# CRUD dos perfis de acesso. É aqui que o ADM decide o que cada função enxerga,
|
||||
# sem depender de deploy: o catálogo do que pode ser marcado é código
|
||||
# (Permissao::TODAS), a escolha é dado (PerfilAcesso#permissoes).
|
||||
module Admin
|
||||
class PerfisAcessoController < ApplicationController
|
||||
include Auditavel
|
||||
|
||||
before_action :set_perfil, only: %i[edit update destroy]
|
||||
|
||||
def index
|
||||
authorize PerfilAcesso
|
||||
@perfis = policy_scope(PerfilAcesso).ordenados.includes(:users)
|
||||
end
|
||||
|
||||
def new
|
||||
@perfil = PerfilAcesso.new(ativo: true, permissoes: [])
|
||||
authorize @perfil
|
||||
end
|
||||
|
||||
def create
|
||||
@perfil = PerfilAcesso.new(perfil_params)
|
||||
authorize @perfil
|
||||
|
||||
if @perfil.save
|
||||
auditar!(:criar, @perfil, dados_novos: { nome: @perfil.nome, permissoes: @perfil.permissoes })
|
||||
redirect_to admin_perfis_path, notice: "Perfil #{@perfil.nome} criado."
|
||||
else
|
||||
render :new, status: :unprocessable_entity
|
||||
end
|
||||
end
|
||||
|
||||
def edit; end
|
||||
|
||||
def update
|
||||
anteriores = @perfil.permissoes
|
||||
|
||||
# Ordem importa: aplicamos os valores em memória para a trava conferir o
|
||||
# que o ADM ESTÁ salvando, não o que está no banco.
|
||||
@perfil.assign_attributes(perfil_params)
|
||||
|
||||
if travaria_o_sistema?
|
||||
@perfil.errors.add(:base, mensagem_da_trava)
|
||||
return render(:edit, status: :unprocessable_entity)
|
||||
end
|
||||
|
||||
if @perfil.save
|
||||
auditar!(:editar, @perfil,
|
||||
dados_anteriores: { permissoes: anteriores },
|
||||
dados_novos: { permissoes: @perfil.permissoes, ativo: @perfil.ativo })
|
||||
redirect_to admin_perfis_path, notice: "Perfil #{@perfil.nome} atualizado."
|
||||
else
|
||||
render :edit, status: :unprocessable_entity
|
||||
end
|
||||
end
|
||||
|
||||
def destroy
|
||||
unless @perfil.apagavel?
|
||||
return redirect_to(admin_perfis_path, alert: motivo_para_nao_apagar)
|
||||
end
|
||||
|
||||
@perfil.destroy
|
||||
auditar!(:excluir, @perfil, dados_anteriores: { nome: @perfil.nome })
|
||||
redirect_to admin_perfis_path, notice: 'Perfil excluído.'
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def set_perfil
|
||||
@perfil = PerfilAcesso.find(params[:id])
|
||||
authorize @perfil
|
||||
end
|
||||
|
||||
def perfil_params
|
||||
dados = params.require(:perfil_acesso).permit(:nome, :descricao, :ativo, permissoes: [])
|
||||
# O form manda um campo vazio para o caso de NENHUMA caixa marcada (senão o
|
||||
# parâmetro some e "desmarcar tudo" viraria "não mexer em nada").
|
||||
dados[:permissoes] = Permissao.filtrar(dados[:permissoes])
|
||||
dados
|
||||
end
|
||||
|
||||
# ── Trava do último administrador ────────────────────────
|
||||
# Mesma regra da tela de usuários (User.administradores_de_acesso): salvar um
|
||||
# perfil que perde `admin.perfis`/`admin.usuarios_gerenciar` (ou é desativado)
|
||||
# não pode deixar o sistema sem quem conserte.
|
||||
def travaria_o_sistema?
|
||||
return false if @perfil.administra_acesso? # continua podendo: nada a checar
|
||||
return false unless @perfil.users.ativos.exists? # perfil vazio não tira acesso de ninguém
|
||||
|
||||
# Quem está NESTE perfil perderá o acesso; sobra alguém fora dele?
|
||||
# Usuário sem perfil conta: cai no padrão do papel (admin legado administra).
|
||||
restantes = User.administradores_de_acesso.reject { |u| u.perfil_acesso_id == @perfil.id }
|
||||
restantes.empty?
|
||||
end
|
||||
|
||||
def mensagem_da_trava
|
||||
'Esta alteração deixaria o sistema sem nenhum usuário ativo capaz de gerenciar ' \
|
||||
'perfis e usuários. Garanta que outra pessoa mantenha esse acesso antes de salvar.'
|
||||
end
|
||||
|
||||
def motivo_para_nao_apagar
|
||||
if @perfil.sistema?
|
||||
'Perfis que vêm com o sistema não podem ser excluídos — desative-o ou edite as permissões.'
|
||||
else
|
||||
"Este perfil está em uso por #{@perfil.users.count} usuário(s). Mova essas pessoas para outro perfil antes de excluir."
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -4,7 +4,7 @@ class Admin::UsuariosController < ApplicationController
|
||||
|
||||
def index
|
||||
authorize User
|
||||
@usuarios = policy_scope(User).order(:nome).limit(200)
|
||||
@usuarios = policy_scope(User).includes(:perfil_acesso).order(:nome).limit(200)
|
||||
@roles = User.roles.keys
|
||||
end
|
||||
|
||||
@@ -36,6 +36,13 @@ class Admin::UsuariosController < ApplicationController
|
||||
def update
|
||||
authorize @usuario
|
||||
|
||||
# Trava do último administrador: mudar perfil ou desativar a única pessoa que
|
||||
# ainda administra acesso deixaria o sistema sem quem conserte.
|
||||
if desligaria_o_ultimo_administrador?
|
||||
@usuario.errors.add(:base, MENSAGEM_ULTIMO_ADMIN)
|
||||
return render(:edit, status: :unprocessable_entity)
|
||||
end
|
||||
|
||||
if @usuario.update(usuario_params_update)
|
||||
AuditoriaLog.registrar(user: current_user, acao: 'editar_usuario', entidade: 'User', dados_novos: { detalhes: "Editou usuário #{@usuario.email}" }, request: request)
|
||||
redirect_to admin_usuarios_path, notice: 'Usuário atualizado.'
|
||||
@@ -47,6 +54,10 @@ class Admin::UsuariosController < ApplicationController
|
||||
def destroy
|
||||
authorize @usuario
|
||||
|
||||
if User.ultimo_administrador?(@usuario)
|
||||
return redirect_to(admin_usuarios_path, alert: MENSAGEM_ULTIMO_ADMIN)
|
||||
end
|
||||
|
||||
if @usuario == current_user
|
||||
redirect_to admin_usuarios_path, alert: 'Você não pode excluir sua própria conta.'
|
||||
return
|
||||
@@ -58,7 +69,11 @@ class Admin::UsuariosController < ApplicationController
|
||||
end
|
||||
|
||||
def toggle_ativo
|
||||
authorize @usuario, :update?
|
||||
authorize @usuario, :toggle_ativo?
|
||||
|
||||
if @usuario.ativo? && User.ultimo_administrador?(@usuario)
|
||||
return redirect_to(admin_usuarios_path, alert: MENSAGEM_ULTIMO_ADMIN)
|
||||
end
|
||||
@usuario.update!(ativo: !@usuario.ativo)
|
||||
status = @usuario.ativo? ? 'ativado' : 'desativado'
|
||||
AuditoriaLog.registrar(user: current_user, acao: "usuario_#{status}", entidade: 'User', dados_novos: { detalhes: "#{@usuario.email} foi #{status}" }, request: request)
|
||||
@@ -67,18 +82,53 @@ class Admin::UsuariosController < ApplicationController
|
||||
|
||||
private
|
||||
|
||||
MENSAGEM_ULTIMO_ADMIN = 'Esta alteração deixaria o sistema sem nenhum usuário ativo capaz de ' \
|
||||
'gerenciar perfis e usuários. Dê esse acesso a outra pessoa antes.'.freeze
|
||||
|
||||
def set_usuario
|
||||
@usuario = User.find(params[:id])
|
||||
end
|
||||
|
||||
# Aplica as mudanças em memória e pergunta: sobra alguém administrando?
|
||||
def desligaria_o_ultimo_administrador?
|
||||
simulado = @usuario.dup
|
||||
simulado.id = @usuario.id
|
||||
simulado.assign_attributes(usuario_params_update)
|
||||
simulado.recarregar_permissoes!
|
||||
|
||||
return false if simulado.ativo? && simulado.pode?('admin.perfis') && simulado.pode?('admin.usuarios_gerenciar')
|
||||
|
||||
User.ultimo_administrador?(@usuario)
|
||||
end
|
||||
|
||||
# ⚠️ CAMPOS DE ACESSO SÓ PARA QUEM PODE MEXER NELES.
|
||||
# `UserPolicy#update?` libera editar a PRÓPRIA ficha (nome, senha) — é o que
|
||||
# permite alguém trocar a própria senha. Sem o filtro abaixo, essa mesma
|
||||
# permissão deixava qualquer usuário logado enviar `user[role]=admin` na edição
|
||||
# do próprio cadastro e se promover. Quem decide papel e perfil é
|
||||
# `UserPolicy#alterar_acesso?` (nunca sobre si mesmo).
|
||||
CAMPOS_DE_ACESSO = %i[role perfil_acesso_id ativo].freeze
|
||||
|
||||
def usuario_params
|
||||
params.require(:user).permit(:nome, :email, :password, :password_confirmation,
|
||||
:role, :pin_code, :ativo)
|
||||
permitidos = [:nome, :email, :password, :password_confirmation, :pin_code]
|
||||
permitidos += CAMPOS_DE_ACESSO if pode_alterar_acesso?
|
||||
params.require(:user).permit(permitidos)
|
||||
end
|
||||
|
||||
def usuario_params_update
|
||||
permitted = [:nome, :email, :role, :pin_code, :ativo]
|
||||
permitted += [:password, :password_confirmation] if params[:user][:password].present?
|
||||
params.require(:user).permit(permitted)
|
||||
permitidos = [:nome, :email, :pin_code]
|
||||
permitidos += CAMPOS_DE_ACESSO if pode_alterar_acesso?
|
||||
permitidos += [:password, :password_confirmation] if params[:user][:password].present?
|
||||
params.require(:user).permit(permitidos)
|
||||
end
|
||||
|
||||
def pode_alterar_acesso?
|
||||
if @usuario&.persisted?
|
||||
policy(@usuario).alterar_acesso?
|
||||
else
|
||||
# Criação: não há "si mesmo" para se promover, basta poder gerenciar.
|
||||
current_user.pode?('admin.usuarios_gerenciar')
|
||||
end
|
||||
end
|
||||
helper_method :pode_alterar_acesso?
|
||||
end
|
||||
|
||||
74
app/controllers/admin/variaveis_personalizadas_controller.rb
Normal file
74
app/controllers/admin/variaveis_personalizadas_controller.rb
Normal file
@@ -0,0 +1,74 @@
|
||||
# app/controllers/admin/variaveis_personalizadas_controller.rb
|
||||
#
|
||||
# CRUD das {{variaveis}} que o ADM cria. Uma vez cadastradas, aparecem no editor
|
||||
# de mensagem ao lado das variáveis do sistema e valem para qualquer evento.
|
||||
module Admin
|
||||
class VariaveisPersonalizadasController < ApplicationController
|
||||
include Auditavel
|
||||
|
||||
before_action :set_variavel, only: %i[edit update destroy]
|
||||
|
||||
def index
|
||||
authorize VariavelPersonalizada
|
||||
@variaveis = policy_scope(VariavelPersonalizada).ordenadas
|
||||
# Mostrar o catálogo do sistema junto evita o erro mais comum: cadastrar
|
||||
# {{data}} sem saber que já existe (o model recusa, mas melhor avisar antes).
|
||||
@do_sistema = Notificacao::CatalogoVariaveis.por_origem.except(:personalizada)
|
||||
end
|
||||
|
||||
def new
|
||||
@variavel = VariavelPersonalizada.new(ativo: true)
|
||||
authorize @variavel
|
||||
end
|
||||
|
||||
def create
|
||||
@variavel = VariavelPersonalizada.new(variavel_params)
|
||||
authorize @variavel
|
||||
|
||||
if @variavel.save
|
||||
auditar!(:criar, @variavel, dados_novos: { chave: @variavel.chave, valor: @variavel.valor })
|
||||
redirect_to admin_variaveis_path, notice: "Variável #{@variavel.marcador} criada."
|
||||
else
|
||||
render :new, status: :unprocessable_entity
|
||||
end
|
||||
end
|
||||
|
||||
def edit; end
|
||||
|
||||
def update
|
||||
anterior = @variavel.valor
|
||||
|
||||
if @variavel.update(variavel_params)
|
||||
auditar!(:editar, @variavel,
|
||||
dados_anteriores: { valor: anterior },
|
||||
dados_novos: { valor: @variavel.valor })
|
||||
redirect_to admin_variaveis_path, notice: "Variável #{@variavel.marcador} atualizada."
|
||||
else
|
||||
render :edit, status: :unprocessable_entity
|
||||
end
|
||||
end
|
||||
|
||||
def destroy
|
||||
marcador = @variavel.marcador
|
||||
@variavel.destroy
|
||||
auditar!(:excluir, @variavel, dados_anteriores: { chave: @variavel.chave })
|
||||
|
||||
# A variável some, mas o texto que a usava continua lá: sem valor, o
|
||||
# renderizador troca por vazio. Avisar é melhor do que a mensagem sair com
|
||||
# um buraco que ninguém relaciona a esta exclusão.
|
||||
redirect_to admin_variaveis_path,
|
||||
alert: "Variável #{marcador} excluída. Mensagens que a usavam passam a mostrar espaço em branco no lugar."
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def set_variavel
|
||||
@variavel = VariavelPersonalizada.find(params[:id])
|
||||
authorize @variavel
|
||||
end
|
||||
|
||||
def variavel_params
|
||||
params.require(:variavel_personalizada).permit(:chave, :valor, :descricao, :ativo)
|
||||
end
|
||||
end
|
||||
end
|
||||
Reference in New Issue
Block a user