DD-011: make Google login the only way in
Remove the free player selection from /benvenuto: without a Supabase session no screen renders, and the VITE_AUTH_OBBLIGATORIA bridge flag is gone. Admin rights now come only from user_roles, so the hardcoded name list in crapp-data.ts is deleted along with its tests. Add migration m4_solo_autenticati, which revokes anon access to the v1.0 tables. Apply it only once the whole team has linked an account. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
+10
-10
@@ -55,19 +55,19 @@ admin) implementati su `develop`, da attivare in produzione seguendo i passaggi
|
||||
|
||||
## Autenticazione e dashboard amministratore
|
||||
|
||||
Implementate su `develop`, **non ancora attive in produzione**. Il codice è additivo: finché
|
||||
i passaggi qui sotto non sono fatti, l'app si comporta esattamente come prima.
|
||||
Implementate su `develop`. **Il login è l'unica via d'accesso** (31/08/2026): la selezione
|
||||
libera del giocatore non esiste più, senza sessione Google si resta su `/benvenuto`, e i
|
||||
permessi di amministrazione arrivano solo da `user_roles`.
|
||||
|
||||
**Stato in sviluppo (30/08/2026):** il codice c'è ed è completo, ma il login non funziona
|
||||
ancora. Con `npm run dev`, «Accedi con Google» risponde:
|
||||
**Attenzione all'ordine:** finché il provider Google è spento in Supabase, «Accedi con
|
||||
Google» risponde
|
||||
|
||||
```
|
||||
{"code":400,"error_code":"validation_failed","msg":"Unsupported provider: provider is not enabled"}
|
||||
```
|
||||
|
||||
Non è un difetto dell'app: l'errore arriva da Supabase, dove il provider Google è spento.
|
||||
È il passo 1 qui sotto, ancora da fare. Tutto il resto dell'app in dev funziona normalmente,
|
||||
perché l'accesso avviene ancora scegliendo il proprio nome.
|
||||
e **nessuno entra nell'app**, né in dev né sulla preview di `develop`. Il passo 1 qui sotto
|
||||
va fatto prima di mandare questa versione in produzione.
|
||||
|
||||
Passaggi in ordine, nessuno dei quali è reversibile a metà:
|
||||
|
||||
@@ -88,9 +88,9 @@ Passaggi in ordine, nessuno dei quali è reversibile a metà:
|
||||
`INSERT INTO public.user_roles (user_id, role) SELECT id, 'admin' FROM auth.users WHERE email = '<mail>';`
|
||||
4. **Collegamento dei 17 account**: ciascuno accede con Google e sceglie il proprio nome una
|
||||
volta sola. Uno slot già collegato può essere liberato solo da un admin.
|
||||
5. **Solo a squadra collegata**: `VITE_AUTH_OBBLIGATORIA=true` su Vercel (fa sparire la
|
||||
selezione libera del giocatore), poi la migration che rimuove le policy `anon` dalle
|
||||
tabelle v1.0. È l'unico passo che cambia il comportamento per tutti.
|
||||
5. **Solo a squadra collegata**: migration `m4_solo_autenticati`, che toglie al ruolo `anon`
|
||||
l'accesso alle tabelle v1.0. Da lì in poi i dati sono raggiungibili solo con una sessione;
|
||||
le route in `src/routes/api/public/` usano la service role e continuano a funzionare.
|
||||
|
||||
Attenzione: dev e produzione condividono lo stesso progetto Supabase. Un account di prova che
|
||||
collega uno slot lo occupa anche in produzione, e va liberato da un admin.
|
||||
|
||||
@@ -46,10 +46,10 @@ docs/ documentazione ufficiale
|
||||
generato) e `client.server.ts` (`supabaseAdmin`, solo server). `types.ts` è generato dallo
|
||||
schema; finché non viene rigenerato, le tabelle introdotte da M1/M2 si usano tramite
|
||||
`client-nuove-tabelle.ts`, con i tipi di riga dichiarati nei moduli di `src/lib/`.
|
||||
- **Autenticazione**: login Google via Supabase Auth (`src/lib/auth.ts`, DD-011); i permessi
|
||||
di amministrazione arrivano da `user_roles` (`src/lib/ruoli.ts`). La variabile
|
||||
`VITE_AUTH_OBBLIGATORIA` decide se `/benvenuto` accetta ancora la selezione libera del
|
||||
giocatore: finché è spenta, login e vecchio accesso convivono.
|
||||
- **Autenticazione**: login Google via Supabase Auth (`src/lib/auth.ts`, DD-011). È l'unica
|
||||
strada di accesso: `__root.tsx` rimanda a `/benvenuto` chi non ha sessione, e l'identità
|
||||
del giocatore è lo slot di `giocatori_squadra` collegato all'account. I permessi di
|
||||
amministrazione arrivano solo da `user_roles` (`src/lib/ruoli.ts`).
|
||||
|
||||
## Livello dati
|
||||
|
||||
|
||||
+9
-3
@@ -11,9 +11,15 @@ qui: sta in [ROADMAP.md](ROADMAP.md).
|
||||
- Login con Google tramite Supabase Auth (DD-011). Al primo accesso l'account si collega a
|
||||
un giocatore di `giocatori_squadra`, e il collegamento non è più modificabile dal
|
||||
giocatore stesso (DD-016 regola 2).
|
||||
- I permessi di amministrazione arrivano da `user_roles` (`src/lib/ruoli.ts`) e non più
|
||||
dalla lista di nomi in `crapp-data.ts`, che resta solo come ponte finché
|
||||
`VITE_AUTH_OBBLIGATORIA` non viene acceso.
|
||||
- La selezione libera del giocatore è stata rimossa: `/benvenuto` offre solo l'accesso con
|
||||
Google e senza sessione non si entra in nessuna schermata. Sparita anche la variabile
|
||||
`VITE_AUTH_OBBLIGATORIA` (non serve più) e il pulsante «Cambia giocatore» in `/profilo`.
|
||||
- I permessi di amministrazione arrivano solo da `user_roles` (`src/lib/ruoli.ts`): la lista
|
||||
di nomi in `crapp-data.ts` è stata eliminata, altrimenti bastava scegliere il nome giusto
|
||||
per amministrare.
|
||||
- Migration `m4_solo_autenticati`: toglie al ruolo `anon` l'accesso alle tabelle v1.0.
|
||||
**Da applicare solo a squadra collegata**, altrimenti chi non ha ancora fatto login vede
|
||||
l'app vuota.
|
||||
- Profilo giocatore: da `/profilo` ognuno compila i propri dati anagrafici e carica
|
||||
documento, certificato medico e foto tessera con le relative scadenze
|
||||
([modules/profilo-giocatore.md](modules/profilo-giocatore.md)).
|
||||
|
||||
+5
-7
@@ -6,13 +6,11 @@ Solo il lavoro in corso o imminente. L'elenco completo delle funzionalità previ
|
||||
## In corso
|
||||
|
||||
- Documentazione tecnica del progetto.
|
||||
- Autenticazione Google e dashboard amministratore: implementate su `develop`, ma in dev il
|
||||
login risponde ancora `provider is not enabled` perché il provider Google non è stato
|
||||
acceso in Supabase. Restano da
|
||||
fare, in quest'ordine: configurazione del provider Google in Supabase, applicazione delle
|
||||
migration M2/M3, inserimento del primo admin in `user_roles`, collegamento dei 17 account,
|
||||
e solo alla fine `VITE_AUTH_OBBLIGATORIA=true` + rimozione delle policy `anon`.
|
||||
Stato di dettaglio in [PROJECT_STATE.md](../PROJECT_STATE.md).
|
||||
- Autenticazione Google e dashboard amministratore: il codice è completo su `develop` e il
|
||||
login è ora l'unica via d'accesso. Restano i passaggi di configurazione, in quest'ordine:
|
||||
provider Google in Supabase (senza, nessuno entra), migration M2/M3, primo admin in
|
||||
`user_roles`, collegamento dei 17 account, e infine la migration M4 che chiude gli accessi
|
||||
`anon`. Stato di dettaglio in [PROJECT_STATE.md](../PROJECT_STATE.md).
|
||||
|
||||
## Prossimo
|
||||
|
||||
|
||||
+3
-8
@@ -3,15 +3,10 @@ import type { Session } from "@supabase/supabase-js";
|
||||
import { supabase } from "@/integrations/supabase/client";
|
||||
|
||||
/**
|
||||
* Autenticazione reale con Google (DD-011). Il login non ha ancora sostituito la
|
||||
* selezione del giocatore: finché `VITE_AUTH_OBBLIGATORIA` non è `true`, `/benvenuto`
|
||||
* offre entrambe le strade, così la produzione continua a funzionare mentre la squadra
|
||||
* collega gli account.
|
||||
* Autenticazione reale con Google (DD-011). Il login ha sostituito la selezione del
|
||||
* giocatore: senza sessione non si entra, e i permessi di amministrazione arrivano solo
|
||||
* da `user_roles` (vedi `ruoli.ts`).
|
||||
*/
|
||||
export function authObbligatoria(): boolean {
|
||||
return import.meta.env["VITE_AUTH_OBBLIGATORIA"] === "true";
|
||||
}
|
||||
|
||||
export function useSessione() {
|
||||
const [sessione, setSessione] = useState<Session | null>(null);
|
||||
const [pronta, setPronta] = useState(false);
|
||||
|
||||
@@ -169,10 +169,5 @@ export function formatData(iso: string) {
|
||||
return d.toLocaleDateString("it-IT", { weekday: "short", day: "2-digit", month: "long" });
|
||||
}
|
||||
|
||||
/** Referenti che possono gestire eventi e sollecitare le risposte. */
|
||||
export const adminNomi = ["Ivan Cacciari", "Iacopo Ricci", "Cristina Titone"];
|
||||
|
||||
export function isAdmin(giocatoreId: string) {
|
||||
const g = giocatori.find((x) => x.id === giocatoreId);
|
||||
return Boolean(g && adminNomi.includes(g.nome));
|
||||
}
|
||||
/* I permessi di amministrazione stanno in `user_roles` (DD-011), non in una lista di nomi:
|
||||
vedi `src/lib/ruoli.ts`. */
|
||||
|
||||
+3
-13
@@ -1,22 +1,13 @@
|
||||
import { useQuery } from "@tanstack/react-query";
|
||||
import { supabase } from "@/integrations/supabase/client";
|
||||
import { isAdmin as nomeInListaAdmin } from "./crapp-data";
|
||||
import { useSessione } from "./auth";
|
||||
import { useGiocatoreBase } from "./user-store";
|
||||
|
||||
export const RUOLI_KEY = ["ruolo-admin"] as const;
|
||||
|
||||
/**
|
||||
* Permessi di amministrazione. La fonte è `user_roles` nel database (DD-011): la lista di
|
||||
* nomi in `crapp-data.ts` resta solo come ponte per chi non ha ancora collegato l'account,
|
||||
* e sparisce quando `VITE_AUTH_OBBLIGATORIA` viene acceso in produzione.
|
||||
*
|
||||
* ponytail: doppia fonte temporanea, si riduce a `ruoloDb` appena l'auth è obbligatoria.
|
||||
* Permessi di amministrazione: unica fonte è `user_roles` nel database (DD-011).
|
||||
* Nessuna lista di nomi, altrimenti basterebbe scegliere il nome giusto per amministrare.
|
||||
*/
|
||||
export function risolviAdmin(ruoloDb: boolean | null, giocatoreId: string | null): boolean {
|
||||
if (ruoloDb !== null) return ruoloDb;
|
||||
return giocatoreId ? nomeInListaAdmin(giocatoreId) : false;
|
||||
}
|
||||
|
||||
/** `null` = nessuna sessione, quindi il database non ha una risposta da dare. */
|
||||
async function fetchRuoloAdmin(utenteId: string | null): Promise<boolean | null> {
|
||||
@@ -33,12 +24,11 @@ async function fetchRuoloAdmin(utenteId: string | null): Promise<boolean | null>
|
||||
|
||||
export function useIsAdmin(): boolean {
|
||||
const { utenteId } = useSessione();
|
||||
const io = useGiocatoreBase();
|
||||
// Il ruolo cambia solo quando un admin lo assegna: una lettura per sessione basta.
|
||||
const query = useQuery({
|
||||
queryKey: [...RUOLI_KEY, utenteId],
|
||||
queryFn: () => fetchRuoloAdmin(utenteId),
|
||||
staleTime: 30 * 60_000,
|
||||
});
|
||||
return risolviAdmin(query.data ?? null, io?.id ?? null);
|
||||
return query.data === true;
|
||||
}
|
||||
|
||||
@@ -18,6 +18,7 @@ import { CelebrazioneBadge } from "../components/crapp/CelebrazioneBadge";
|
||||
import { Toaster } from "../components/ui/sonner";
|
||||
import { TeamLogo } from "../components/crapp/ui-bits";
|
||||
import { useGiocatoreBase } from "../lib/user-store";
|
||||
import { useSessione } from "../lib/auth";
|
||||
|
||||
function NotFoundComponent() {
|
||||
return (
|
||||
@@ -145,17 +146,21 @@ function RootComponent() {
|
||||
const navigate = useNavigate();
|
||||
const location = useLocation();
|
||||
const giocatore = useGiocatoreBase();
|
||||
const { pronta, utenteId } = useSessione();
|
||||
const [mounted, setMounted] = useState(false);
|
||||
const isBenvenuto = location.pathname === "/benvenuto";
|
||||
|
||||
// Senza sessione Google non si entra: l'identità la dà il login, non la scelta del nome
|
||||
// (DD-011). Si aspetta `pronta`, altrimenti il primo render sloggato rimbalzerebbe fuori
|
||||
// chi ha già la sessione in localStorage.
|
||||
useEffect(() => {
|
||||
setMounted(true);
|
||||
if (!giocatore && !isBenvenuto) {
|
||||
if (pronta && (!giocatore || !utenteId) && !isBenvenuto) {
|
||||
navigate({ to: "/benvenuto" });
|
||||
}
|
||||
}, [giocatore, isBenvenuto, navigate]);
|
||||
}, [giocatore, utenteId, pronta, isBenvenuto, navigate]);
|
||||
|
||||
if (!mounted) {
|
||||
if (!mounted || !pronta) {
|
||||
return (
|
||||
<div className="grid min-h-screen place-items-center bg-background">
|
||||
<TeamLogo className="h-16 w-16 animate-pulse" />
|
||||
|
||||
@@ -3,8 +3,7 @@ import { useEffect, useState } from "react";
|
||||
import { LogIn } from "lucide-react";
|
||||
import { toast } from "sonner";
|
||||
import { TeamLogo } from "@/components/crapp/ui-bits";
|
||||
import { giocatori } from "@/lib/crapp-data";
|
||||
import { accediConGoogle, authObbligatoria, useSessione } from "@/lib/auth";
|
||||
import { accediConGoogle, useSessione } from "@/lib/auth";
|
||||
import {
|
||||
nomeCompleto,
|
||||
slotDi,
|
||||
@@ -13,7 +12,7 @@ import {
|
||||
useGiocatoriSquadra,
|
||||
type GiocatoreSquadra,
|
||||
} from "@/lib/giocatori-squadra";
|
||||
import { impostaGiocatore, useGiocatoreCorrente } from "@/lib/user-store";
|
||||
import { impostaGiocatore, resetGiocatore, useGiocatoreCorrente } from "@/lib/user-store";
|
||||
|
||||
export const Route = createFileRoute("/benvenuto")({
|
||||
head: () => ({
|
||||
@@ -65,23 +64,24 @@ function Benvenuto() {
|
||||
const navigate = useNavigate();
|
||||
const giocatore = useGiocatoreCorrente();
|
||||
const { pronta, utenteId } = useSessione();
|
||||
const { righe } = useGiocatoriSquadra();
|
||||
const { righe, daDatabase } = useGiocatoriSquadra();
|
||||
const collega = useCollegaGiocatore();
|
||||
const [inCorso, setInCorso] = useState(false);
|
||||
|
||||
const mioSlot = slotDi(righe, utenteId);
|
||||
// Con l'auth obbligatoria si entra solo da loggati; finché non lo è, la selezione
|
||||
// diretta resta come ponte per chi non ha ancora collegato l'account (DD-011).
|
||||
const puoEntrare = !!giocatore && (!!utenteId || !authObbligatoria());
|
||||
// Si entra solo da loggati e con uno slot collegato (DD-011).
|
||||
const puoEntrare = !!giocatore && !!utenteId;
|
||||
|
||||
useEffect(() => {
|
||||
if (puoEntrare) navigate({ to: "/" });
|
||||
}, [puoEntrare, navigate]);
|
||||
|
||||
// L'account è già collegato a uno slot: nessuna scelta da fare.
|
||||
// Chi sei lo dice lo slot collegato all'account, non quello che c'è in localStorage:
|
||||
// senza slot la scelta salvata dalla vecchia selezione libera va buttata.
|
||||
useEffect(() => {
|
||||
if (mioSlot) impostaGiocatore(mioSlot.id);
|
||||
}, [mioSlot]);
|
||||
else if (utenteId && daDatabase) resetGiocatore();
|
||||
}, [mioSlot, utenteId, daDatabase]);
|
||||
|
||||
async function accedi() {
|
||||
setInCorso(true);
|
||||
@@ -125,25 +125,6 @@ function Benvenuto() {
|
||||
>
|
||||
<LogIn className="h-4 w-4" /> Accedi con Google
|
||||
</button>
|
||||
|
||||
{authObbligatoria() ? null : (
|
||||
<div className="mt-10 w-full max-w-sm">
|
||||
<p className="mb-3 text-center text-xs text-muted-foreground">
|
||||
Oppure entra scegliendo il tuo nome, come prima.
|
||||
</p>
|
||||
<div className="space-y-2">
|
||||
{giocatori.map((g) => (
|
||||
<Scheda
|
||||
key={g.id}
|
||||
titolo={g.nome}
|
||||
sottotitolo={`#${g.numero} · ${g.ruolo}`}
|
||||
iniziali={g.iniziali}
|
||||
onClick={() => impostaGiocatore(g.id)}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
|
||||
+2
-13
@@ -1,7 +1,7 @@
|
||||
import { createFileRoute, Link } from "@tanstack/react-router";
|
||||
import { useEffect, useRef, useState } from "react";
|
||||
import { toast } from "sonner";
|
||||
import { Flame, Camera, Users, Trash2, Bell, LogOut, ShieldCheck } from "lucide-react";
|
||||
import { Flame, Camera, Trash2, Bell, LogOut, ShieldCheck } from "lucide-react";
|
||||
import { cn } from "@/lib/utils";
|
||||
import { PageHeader, Section, StatTile } from "@/components/crapp/ui-bits";
|
||||
import { Avatar } from "@/components/crapp/Avatar";
|
||||
@@ -19,7 +19,7 @@ import {
|
||||
statoNotifiche,
|
||||
} from "@/lib/push-client";
|
||||
import { resetGiocatore } from "@/lib/user-store";
|
||||
import { esci, useSessione } from "@/lib/auth";
|
||||
import { esci } from "@/lib/auth";
|
||||
import { useIsAdmin } from "@/lib/ruoli";
|
||||
import { Reveal } from "@/components/motion/Reveal";
|
||||
|
||||
@@ -42,7 +42,6 @@ function Profilo() {
|
||||
const votiSocial = useVotiSocial();
|
||||
const g = useIo();
|
||||
const admin = useIsAdmin();
|
||||
const { sessione } = useSessione();
|
||||
const ultimoMese = usePresenzeUltimoMese(g?.id);
|
||||
const inputRef = useRef<HTMLInputElement>(null);
|
||||
const foto = useAvatar(g?.id);
|
||||
@@ -228,15 +227,6 @@ function Profilo() {
|
||||
<ShieldCheck className="h-4 w-4 text-muted-foreground" />
|
||||
</Link>
|
||||
) : null}
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => resetGiocatore()}
|
||||
className="flex w-full items-center justify-between gap-3 px-4 py-3 text-sm transition-colors hover:bg-accent/5"
|
||||
>
|
||||
<span className="min-w-0 truncate">Cambia giocatore</span>
|
||||
<Users className="h-4 w-4 text-muted-foreground" />
|
||||
</button>
|
||||
{sessione ? (
|
||||
<button
|
||||
type="button"
|
||||
onClick={logout}
|
||||
@@ -245,7 +235,6 @@ function Profilo() {
|
||||
<span className="min-w-0 truncate">Esci</span>
|
||||
<LogOut className="h-4 w-4 text-muted-foreground" />
|
||||
</button>
|
||||
) : null}
|
||||
</div>
|
||||
</Section>
|
||||
</>
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
-- M4 — Chiusura degli accessi anonimi alle tabelle v1.0 (DD-011).
|
||||
--
|
||||
-- Da applicare SOLO quando tutti hanno collegato l'account Google: da qui in poi il
|
||||
-- ruolo `anon` non legge né scrive più nulla, quindi chi non ha fatto login vede l'app
|
||||
-- vuota. Le route in `src/routes/api/public/` usano la service role e non sono toccate.
|
||||
--
|
||||
-- Le policy restano dichiarate `TO anon, authenticated`: senza GRANT il ruolo anon non
|
||||
-- arriva comunque alla tabella, e le policy continuano a valere per gli autenticati.
|
||||
|
||||
REVOKE ALL ON public.turni_palloni FROM anon;
|
||||
REVOKE ALL ON public.push_subscriptions FROM anon;
|
||||
REVOKE ALL ON public.badge_social_voti FROM anon;
|
||||
REVOKE ALL ON public.scout_sessioni FROM anon;
|
||||
REVOKE ALL ON public.scout_live FROM anon;
|
||||
REVOKE ALL ON public.mvp_voti FROM anon;
|
||||
REVOKE ALL ON public.risposte_presenze FROM anon;
|
||||
REVOKE ALL ON public.eventi_app FROM anon;
|
||||
REVOKE ALL ON public.pagelle_voti FROM anon;
|
||||
REVOKE ALL ON public.cacche_partita FROM anon;
|
||||
@@ -1,14 +1,6 @@
|
||||
/** Check dei dati di base della rosa: `bun test/unit/crapp-data.test.ts`. */
|
||||
import assert from "node:assert/strict";
|
||||
import {
|
||||
adminNomi,
|
||||
classifica,
|
||||
formatData,
|
||||
giocatori,
|
||||
isAdmin,
|
||||
statoMeta,
|
||||
storicoMatch,
|
||||
} from "@/lib/crapp-data";
|
||||
import { classifica, formatData, giocatori, statoMeta, storicoMatch } from "@/lib/crapp-data";
|
||||
|
||||
// --- rosa --------------------------------------------------------------------
|
||||
assert.ok(giocatori.length > 0, "la rosa non è vuota");
|
||||
@@ -32,17 +24,6 @@ for (const g of giocatori) {
|
||||
assert.ok(g.presenze <= g.totaliEventi, `${g.id}: presenze mai oltre gli eventi totali`);
|
||||
}
|
||||
|
||||
// --- amministratori ----------------------------------------------------------
|
||||
for (const nome of adminNomi) {
|
||||
const admin = giocatori.find((g) => g.nome === nome);
|
||||
assert.ok(admin, `l'amministratore ${nome} è in rosa`);
|
||||
assert.equal(isAdmin(admin.id), true);
|
||||
}
|
||||
const nonAdmin = giocatori.find((g) => !adminNomi.includes(g.nome))!;
|
||||
assert.equal(isAdmin(nonAdmin.id), false);
|
||||
assert.equal(isAdmin("g999"), false, "un id inesistente non è amministratore");
|
||||
assert.equal(isAdmin(""), false);
|
||||
|
||||
// --- formatData --------------------------------------------------------------
|
||||
assert.equal(formatData("2026-09-01"), "mar 01 settembre");
|
||||
assert.equal(formatData("2026-01-31"), "sab 31 gennaio");
|
||||
|
||||
@@ -1,24 +0,0 @@
|
||||
/** Check dei permessi di amministrazione: `bun test/unit/ruoli.test.ts`. */
|
||||
import assert from "node:assert/strict";
|
||||
import { risolviAdmin } from "@/lib/ruoli";
|
||||
import { adminNomi, giocatori } from "@/lib/crapp-data";
|
||||
|
||||
const referente = giocatori.find((g) => adminNomi.includes(g.nome))!;
|
||||
const chiunque = giocatori.find((g) => !adminNomi.includes(g.nome))!;
|
||||
|
||||
// Con una sessione attiva decide il database, e basta: la lista di nomi non conta più.
|
||||
assert.equal(risolviAdmin(true, chiunque.id), true, "il ruolo nel database concede");
|
||||
assert.equal(
|
||||
risolviAdmin(false, referente.id),
|
||||
false,
|
||||
"il ruolo nel database nega anche a chi è nella lista dei nomi",
|
||||
);
|
||||
assert.equal(risolviAdmin(true, null), true);
|
||||
|
||||
// Senza sessione (null) resta il ponte temporaneo sulla lista di nomi.
|
||||
assert.equal(risolviAdmin(null, referente.id), true);
|
||||
assert.equal(risolviAdmin(null, chiunque.id), false);
|
||||
assert.equal(risolviAdmin(null, null), false, "nessuna identità, nessun permesso");
|
||||
assert.equal(risolviAdmin(null, "gXX"), false, "un id sconosciuto non concede nulla");
|
||||
|
||||
console.log("ruoli: ok");
|
||||
Reference in New Issue
Block a user