Ajuste rotas, Menus, Layout, Permissãoes UserRoleGuard

This commit is contained in:
Leonardo
2026-02-24 12:04:59 -03:00
parent b1c0cb47c0
commit d58dc21297
15 changed files with 1925 additions and 259 deletions
+47
View File
@@ -1,3 +1,50 @@
/**
* ---------------------------------------------------------
* useAuth()
* ---------------------------------------------------------
*
* Stack do projeto:
* - Vue 3 (Composition API)
* - PrimeVue (UI)
* - Supabase (Auth + Database)
*
* Responsabilidade:
* Camada global de AUTENTICAÇÃO baseada no Supabase.
*
* O que este composable faz:
* - Obtém a sessão atual do Supabase (auth.getSession)
* - Mantém o estado reativo do usuário autenticado
* - Escuta mudanças de autenticação (login, logout, refresh de token)
* - Expõe apenas a identidade do usuário (user)
*
* O que ele NÃO faz:
* - Não controla permissões
* - Não valida roles
* - Não decide acesso a telas ou botões
* - Não aplica regras de plano (Free/Pro)
*
* Conceito arquitetural:
* Este arquivo trata apenas de IDENTIDADE (Auth).
*
* Auth → "Quem é o usuário autenticado?"
* AuthZ → "O que esse usuário pode acessar ou executar?"
*
* A AUTORIZAÇÃO deve ser tratada em outra camada,
* como por exemplo:
* - useAuthz()
* - tenantStore (membership.role)
* - entitlementsStore (features do plano)
*
* Observação importante:
* O role do usuário NÃO vem do Supabase Auth.
* Ele é definido na tabela de membership (multi-tenant).
*
* Portanto:
* Nunca utilizar apenas `user` para controle de acesso.
*
* Esse composable é apenas a base de identidade do sistema.
*/
import { ref, onMounted } from 'vue'
import { supabase } from '@/lib/supabase/client'
+115
View File
@@ -0,0 +1,115 @@
import { computed } from 'vue'
import { useTenantStore } from '@/stores/tenantStore'
/**
* ---------------------------------------------------------
* useRoleGuard() — RBAC puro (somente PAPEL do tenant)
* ---------------------------------------------------------
*
* Objetivo:
* Controlar visibilidade/ações por PAPEL dentro do tenant (clínica).
* Aqui NÃO entra plano, módulos ou features pagas.
*
* Fonte da verdade do papel (tenant role):
* - public.tenant_members.role → 'tenant_admin' | 'therapist' | 'patient'
* - no frontend: tenantStore.membership.role (ou fallback tenantStore.activeRole)
*
* O que este composable resolve:
* - "Esse papel pode ver/usar este elemento?"
* Ex:
* - paciente não vê botão Configurações
* - therapist e tenant_admin veem
*
* O que ele NÃO resolve (de propósito):
* - liberar feature por plano (Free/Pro)
* - limitar módulos / recursos contratados
*
* Para controle por plano, use o entStore:
* - entStore.can('feature_key')
*
* Padrão recomendado (RBAC + Plano):
* Quando algo depende do PLANO e do PAPEL, combine no template:
*
* v-if="entStore.can('online_scheduling.manage') && canSee('settings.view')"
*
* Interpretação:
* - Gate A (Plano): o tenant tem a feature liberada?
* - Gate B (Papel): o usuário, pelo papel, pode ver/usar isso?
*
* Nota de segurança:
* Isso controla UI/rotas (experiência). Segurança real deve existir no backend (RLS).
* ---------------------------------------------------------
*/
export function useRoleGuard () {
const tenantStore = useTenantStore()
// Roles confirmados no seu banco (tenant_members.role)
const ROLES = Object.freeze({
ADMIN: 'tenant_admin',
THERAPIST: 'therapist',
PATIENT: 'patient'
})
// Papel atual no tenant ativo
const role = computed(() => tenantStore.membership?.role ?? tenantStore.activeRole ?? null)
// Opcional: útil se você quiser segurar render até carregar
const isReady = computed(() => !!role.value)
// Helpers semânticos
const isTenantAdmin = computed(() => role.value === ROLES.ADMIN)
const isTherapist = computed(() => role.value === ROLES.THERAPIST)
const isPatient = computed(() => role.value === ROLES.PATIENT)
const isStaff = computed(() => [ROLES.ADMIN, ROLES.THERAPIST].includes(role.value))
// Matriz RBAC (somente por papel)
// Dica: mantenha chaves no padrão "modulo.acao"
const rbac = Object.freeze({
// Botões/telas de configuração do tenant
'settings.view': [ROLES.ADMIN, ROLES.THERAPIST],
// Perfil/conta (normalmente todos)
'profile.view': [ROLES.ADMIN, ROLES.THERAPIST, ROLES.PATIENT],
// Segurança (normalmente todos; ajuste se quiser restringir)
'security.view': [ROLES.ADMIN, ROLES.THERAPIST, ROLES.PATIENT]
// Exemplos futuros:
// 'agenda.view': [ROLES.ADMIN, ROLES.THERAPIST, ROLES.PATIENT],
// 'agenda.manage': [ROLES.ADMIN, ROLES.THERAPIST],
})
/**
* canSee(key)
* Retorna true se o PAPEL atual estiver autorizado para a chave RBAC.
*
* Política segura:
* - se não carregou role → false
* - se não existe mapeamento pra key → false
*/
function canSee (key) {
const r = role.value
if (!r) return false
const allowed = rbac[key]
if (!allowed) return false
return allowed.includes(r)
}
return {
// estado
role,
isReady,
// constantes & helpers
ROLES,
isTenantAdmin,
isTherapist,
isPatient,
isStaff,
// API
canSee
}
}