Ajuste rotas, Menus, Layout, Permissãoes UserRoleGuard
This commit is contained in:
@@ -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'
|
||||
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user