modulo 18 di 24 / Ore 41-60

Moduli 17-24 / Checkpoint 60 ore

Autorizzazioni minime: chi può fare cosa

Come applicare il principio del minimo privilegio nel Registro del laboratorio scolastico: ruoli, operazioni consentite e come limitarle anche senza autenticazione reale.

25 min Architettura del software

Autorizzazioni minime: chi può fare cosa

Questa lezione appartiene al Modulo 18 — Sicurezza minima e validazione.

Il lavoro continua il progetto guida, il Registro del laboratorio scolastico, aggiungendo un livello di controllo sulle operazioni sensibili senza introdurre un sistema di autenticazione completo.

Obiettivo della lezione

Definisci chi può fare cosa nel Registro e implementa un controllo semplice che blocca le operazioni non consentite per un dato ruolo. Nessun sistema di login reale — ma regole esplicite nel codice.

Alla fine devi avere: una funzione puoEseguire(ruolo, operazione) usata nei casi d’uso prima di procedere, e una documentazione dei ruoli.

Il principio del minimo privilegio

Il principio del minimo privilegio dice: ogni parte del sistema deve avere accesso solo a ciò che è strettamente necessario per fare il suo lavoro. Nel Registro scolastico, significa che non tutti possono fare tutto.

Senza autenticazione reale, puoi comunque modellare i ruoli e farli rispettare nella logica applicativa. L’utente che usa il browser non è autenticato, ma il sistema può sapere in che “modalità” sta operando — impostata manualmente o tramite un semplice parametro.

Ruoli nel Registro del laboratorio

Per il Registro del laboratorio scolastico, tre ruoli sono sufficienti:

  • "studente" — può cercare strumenti e visualizzare i propri prestiti. Non può registrare strumenti né segnalare guasti.
  • "tecnico" — può aprire e chiudere prestiti, segnalare guasti. Non può registrare nuovi strumenti.
  • "responsabile" — può fare tutto: registrare strumenti, gestire prestiti, approvare segnalazioni.

Le operazioni principali del Registro sono:

  • "registra-strumento" — aggiunge uno strumento al catalogo
  • "apri-prestito" — avvia un prestito
  • "chiudi-prestito" — termina un prestito
  • "segnala-guasto" — cambia lo stato di uno strumento a guasto
  • "cerca-strumenti" — legge il catalogo (disponibile a tutti)

Procedura guidata

  1. Crea src/application/autorizzazioni.js con la mappa dei permessi per ruolo.
  2. Scrivi la funzione puoEseguire(ruolo, operazione) che consulta la mappa.
  3. Aggiungi un controllo all’inizio di ogni caso d’uso sensibile.
  4. Aggiungi un campo ruolo al contesto dell’applicazione o passalo come parametro.
  5. Verifica manualmente: chiama un caso d’uso con ruolo "studente" per un’operazione vietata e controlla che venga bloccata.

Attività in classe

Compila la mappa dei permessi prima di scrivere il codice:

## Mappa dei permessi

Operazione: registra-strumento
Ruoli autorizzati:

Operazione: apri-prestito
Ruoli autorizzati:

Operazione: chiudi-prestito
Ruoli autorizzati:

Operazione: segnala-guasto
Ruoli autorizzati:

Operazione: cerca-strumenti
Ruoli autorizzati:

Poi confronta la tua mappa con quella del compagno e discuti le differenze.

Spiegazione guidata per studiare

Il controllo delle autorizzazioni vive nel livello Application, non nel dominio e non nella Presentation. Metterlo nella Presentation significa poterlo aggirare facilmente (basta nascondere un pulsante, non bloccare l’operazione). Metterlo nel dominio significa mescolare regole di business con politiche di accesso — che sono cose diverse.

Il livello Application è il posto giusto: coordina le operazioni, conosce il contesto (chi sta agendo), e può bloccare prima di delegare al dominio.

Un sistema senza login può comunque avere ruoli. Il ruolo corrente può essere impostato all’avvio dell’applicazione tramite una variabile di configurazione, un parametro URL, o una selezione manuale iniziale. Non è sicuro come un sistema di autenticazione reale, ma è sufficiente per modellare il comportamento corretto e ragionarci sopra.

Passo per passo nel progetto

  1. Crea src/application/autorizzazioni.js:
// application/autorizzazioni.js

const PERMESSI = {
  "studente": ["cerca-strumenti"],
  "tecnico": ["cerca-strumenti", "apri-prestito", "chiudi-prestito", "segnala-guasto"],
  "responsabile": ["cerca-strumenti", "registra-strumento", "apri-prestito", "chiudi-prestito", "segnala-guasto"]
};

export function puoEseguire(ruolo, operazione) {
  const permessiRuolo = PERMESSI[ruolo];
  if (!permessiRuolo) return false;
  return permessiRuolo.includes(operazione);
}
  1. Usa puoEseguire nel caso d’uso registraStrumento:
// application/registraStrumento.js
import { puoEseguire } from "./autorizzazioni.js";

export function registraStrumento({ nome, categoria, ruolo }, archivio) {
  if (!puoEseguire(ruolo, "registra-strumento")) {
    throw new Error("OPERAZIONE_NON_AUTORIZZATA");
  }
  if (!nome || nome.trim() === "") {
    throw new Error("NOME_OBBLIGATORIO");
  }
  const strumenti = archivio.caricaTutti();
  const nuovoStrumento = {
    id: crypto.randomUUID(),
    nome: nome.trim(),
    categoria,
    stato: "disponibile"
  };
  strumenti.push(nuovoStrumento);
  archivio.salva(strumenti);
  return nuovoStrumento;
}
  1. Usa puoEseguire nel caso d’uso apriPrestito:
// application/apriPrestito.js
import { puoEseguire } from "./autorizzazioni.js";

export function apriPrestito({ strumentoId, ruolo }, archivioStrumenti, archivioPrestiti) {
  if (!puoEseguire(ruolo, "apri-prestito")) {
    throw new Error("OPERAZIONE_NON_AUTORIZZATA");
  }
  const strumenti = archivioStrumenti.caricaTutti();
  const strumento = strumenti.find(s => s.id === strumentoId);
  if (!strumento) throw new Error("STRUMENTO_NON_TROVATO");
  if (strumento.stato !== "disponibile") throw new Error("STRUMENTO_NON_DISPONIBILE");
  strumento.stato = "in-prestito";
  archivioStrumenti.salva(strumenti);
  const prestiti = archivioPrestiti.caricaTutti();
  const nuovoPrestito = {
    id: crypto.randomUUID(),
    strumentoId,
    dataApertura: new Date().toISOString().split("T")[0],
    dataChiusura: null
  };
  prestiti.push(nuovoPrestito);
  archivioPrestiti.salva(prestiti);
  return nuovoPrestito;
}
  1. In main.js, passa il ruolo corrente ai casi d’uso:
// main.js
const RUOLO_CORRENTE = "tecnico"; // cambia per testare diversi ruoli

btnPresta.addEventListener("click", () => {
  try {
    const prestito = apriPrestito(
      { strumentoId: inputId.value, ruolo: RUOLO_CORRENTE },
      archivioStrumenti,
      archivioPrestiti
    );
    mostraMessaggio(`Prestito aperto: ${prestito.id}`);
  } catch (errore) {
    if (errore.message === "OPERAZIONE_NON_AUTORIZZATA") {
      mostraMessaggio("Non hai i permessi per questa operazione.");
    } else {
      mostraMessaggio("Errore: " + errore.message);
    }
  }
});

Esempio JS — verifica manuale in console

import { puoEseguire } from "./src/application/autorizzazioni.js";

console.log(puoEseguire("studente", "apri-prestito"));          // false
console.log(puoEseguire("tecnico", "apri-prestito"));           // true
console.log(puoEseguire("responsabile", "registra-strumento")); // true
console.log(puoEseguire("studente", "cerca-strumenti"));        // true
console.log(puoEseguire("ruolo-inesistente", "apri-prestito")); // false

Prova di comprensione

Rispondi per iscritto:

  • Perché il controllo del ruolo non sta nella Presentation (nel gestore del click)?
  • Cosa cambia se aggiungiamo un quarto ruolo "osservatore" che può solo cercare?
  • Perché l’errore si chiama "OPERAZIONE_NON_AUTORIZZATA" e non "Accesso negato"?
  • Come modificheresti puoEseguire per loggare ogni tentativo non autorizzato?

Errori da evitare

  • nascondere i pulsanti nell’UI come unico controllo — è una misura di comodità, non di sicurezza;
  • mettere la logica dei permessi direttamente nei gestori degli eventi;
  • usare condizioni if (ruolo === "responsabile") sparse in tutto il codice invece di centralizzare in autorizzazioni.js;
  • lanciare eccezioni con messaggi tecnici che arrivano all’utente — converti sempre l’errore in un messaggio leggibile nella Presentation.

Prodotto da consegnare

Il file src/application/autorizzazioni.js con la mappa dei permessi e puoEseguire, più almeno due casi d’uso che la usano. Come verifica: chiama un caso d’uso con un ruolo non autorizzato e mostra che viene bloccato con un messaggio chiaro.

Checklist di chiusura

  • puoEseguire è in application/, non in domain/ né in presentation/;
  • almeno due casi d’uso usano il controllo di autorizzazione;
  • la Presentation converte "OPERAZIONE_NON_AUTORIZZATA" in un messaggio leggibile;
  • la mappa dei permessi è centralizzata e non duplicata;
  • ho testato manualmente almeno un caso di operazione vietata.

Risultato atteso

Ho creato autorizzazioni.js con la mappa dei permessi per tre ruoli.
I casi d'uso registraStrumento e apriPrestito controllano il ruolo prima di procedere.
Ho verificato che uno studente non può aprire un prestito.
Il prossimo passo: aggiungere un quarto ruolo "osservatore" per le consultazioni pubbliche.