Skip to content

Deleghe, Permessi e Livelli di Accesso

Alfio Emanuele edited this page Feb 25, 2016 · 15 revisions

Indice dei contenuti

Schema riassuntivo: https://gist.github.com/AlfioEmanueleFresta/832b210c0cdccd336ab1

E' estremamente importante tenere questo documento aggiornato.


Elenco costanti

Deleghe e Permessi

In questa sezione sono documentati i Permessi che derivano da ogni delega.

  • PRESIDENTE (Sede)
    • GESTIONE_SOCI per tutti i membri della Sede
    • GESTIONE_ATTIVITA_SEDE per la Sede
    • GESTIONE_CORSI_SEDE per la Sede
  • UFFICIO_SOCI (Sede)
    • GESTIONE_SOCI per tutti i membri della Sede
  • REFERENTE_ATTIVITA (Attivita)
    • GESTIONE_ATTIVITA per l'Attivita

Permessi e Livelli di Accesso (aka. Espansione dei Permessi)

In questa sezione sono documentati i vari Permessi e la relativa espansione in Livelli di Accesso.

  • GESTIONE_SEDE (QuerySet{Sede}) Permette la modifica dei dati di locazione, contatto e preferenze di una data Sede.
    • MODIFICA per la sede Selezionata
    • MODIFICA per tutte le Sedi sottostanti
  • GESTIONE_SOCI (QuerySet{Sede}) Permette la modifica dei membri direttamente annessi alle Sedi selezionate. Permette inoltre la lettura dei membri estesi presso le Sedi selezionate.
    • MODIFICA per tutti i membri diretti delle Sedi
    • LETTURA per tutti i membri estesi presso le Sedi
  • ELENCHI_SOCI (QuerySet{Sede}) Permette la lettura degli Elenchi soci. Permette la lettura degli elenchi soci delle Sedi sottostanti.
    • LETTURA per tutti i membri diretti delle Sedi
    • LETTURA per tutti i membri estesi presso le Sedi
    • LETTURA per tutti i membri diretti e estesi presso le Sedi sottostanti.
  • GESTIONE_ATTIVITA_SEDE (QuerySet{Sede})
    • COMPLETO per tutte le attivita' della sede
    • LETTURA per tutti i membri che partecipano alle attivita' della Sede
  • GESTIONE_ATTIVITA_AREA (QuerySet{Sede})
    • COMPLETO per tutte le attivita' dell'Area
    • LETTURA per tutti i membri che partecipano alle attivita' dell'Area
  • GESTIONE_ATTIVITA (QuerySet{Attivita})
    • MODIFICA per l'attivita' selezionata
    • LETTURA per tutti i membri che hanno partecipazioni, confermate o meno, alla attivita' selezionata
  • GESTIONE_CORSI_SEDE (QuerySet{Sede})
    • COMPLETO per tutti i Corsi organizzati dalla Sede
    • MODIFICA per tutti gli Aspiranti iscritti ai Corsi dela Sede
    • LETTURA per tutti i Soci iscritti ai Corsi del Comitato
  • GESTIONE_CORSO (QuerySet{Corso})
    • MODIFICA per il Corso selezionato
    • MODIFICA per tutti gli Aspiranti iscritti al Corso selezionato
    • LETTURA per tutti i Soci iscritti al Corso selezionato

Sicurezza e Protezione (Pagine e Contenuti)

Questa sezione contiene importanti informazioni relative ai metodi di protezione delle pagine, utilizzando il concetto dei permessi e livelli di accesso sopra elencati.

Permessi

Permesso di Accesso (Protezione Pagine)

I nuovi metodi per la protezione delle pagine, basate sui permessi, vanno a sostituire i vecchi metodi di Gaia, basate sulle deleghe, similari a:

paginaApp(APP_PRESIDENTE);
paginaAPP([APP_PRESIDENTE, APP_UFFICIOSOCI]);

In Jorvik, le pagine private, protette con il decoratore

@pagina_privata

possono utilizzare il parametro permessi= per indicare un permesso che deve essere posseduto per accedere alla pagina.

Se il permesso non è posseduto ed attuale, l'utente viene rediretto alla pagina ERRORE_PERMESSI (/errore/permessi/).

Es.:

@pagina_privata(permessi=ELENCHI_SOCI)
def us_elenchi(request, me):

  contesto = {
    msg: me.nome_completo + " ha i permessi di elenco soci"
  }
  return 'us_elenchi.html', contesto

Permessi multipli

E' possibile passare un elenco (lista o tupla) di valori al parametro permessi del decoratore @pagina_privata. In questo caso, tutti i permessi devono essere posseduti dall'utente al fine di poter accedere alla pagina.

Es.:

@pagina_privata(permessi=(ELENCHI_SOCI, GESTIONE_COMITATO))
def presidente_info_comitato(request, me):

  contesto = {
    msg: me.nome_completo + " ha i permessi di elenco soci, e anche di gestione comitato"
  }
  return 'presidente_info_comitato.html', contesto

Controllo Permessi post-accesso

Se per qualsiasi motivo si vuole controllare se l'utente ha dei permessi, ad esempio per visualizzare o meno dei pulsanti, e' possibile effettuare il controllo utilizzando la funzione:

  • Persona.ha_permesso (permesso, al_giorno=date.today())
    • Argomenti
      • permesso è il permesso che si vuole controllare. Vedi l'elenco in testa a questa pagina.
      • al_giorno (Opzionale) e' il giorno in cui si vuole controllare che la Persona abbia il permesso. Default e' oggi.
    • La funzione ritorna True se il permesso è posseduto ed attuale, False altrimenti.

Es.:

@pagina_privata(permessi=ELENCHI_SOCI)
def us_elenchi(request, me):
  
  pulsante_link = "/fai-qualcosa-come-ufficio-soci/"
  
  if me.ha_permesso(GESTIONE_COMITATO):
    pulsante_link = "/fai-qualcosa-come-presidente/"

  contesto = {
    pulsante_testo: "Fai qualcosa",
    pulsante_link: pulsante_link
  }
  return 'us_elenchi.html', contesto

Ottenere Oggetti sui quali si ha Permesso

In varie situazioni si può desiderare ottenere un elenco di oggetti sui quali si ha permesso di agire. Ad esempio, nell'ambito dell'Ufficio Soci, è possibile desiderare di recuperare l'elenco delle Sedi (Comitati) per i quali si ha permesso di ELENCHI_SOCI.

Questo è permesso dalla funzione:

  • Persona.oggetti_permesso (permesso, al_giorno=date.today())
    • Argomenti
      • permesso è il permesso che si vuole controllare. Vedi l'elenco in testa a questa pagina.
      • al_giorno (Opzionale) e' il giorno in cui si vuole controllare che la Persona abbia il permesso. Default e' oggi.
    • La funzione ritorna un QuerySet, pronto all'elaborazione, che contiene tutti gli oggetti per i quali la persona possiede il permesso specificato.

Es.:

@pagina_privata(permessi=ELENCHI_SOCI)
def us_elenchi(request, me):
  
  sedi = me.oggetti_permesso(ELENCHI_SOCI)
  contesto = {
    sedi: sedi
  }
  return 'us_elenchi.html', contesto
{% extends 'base_vuota.html` %}

{% block pagina_principale %}

<h2>Elenco delle Sedi</h2>
<p>Clicca su una Sede per visionare l'elenco dei Membri.</p>
<ul>

  {% for sede in sedi %}
    <li>
      {{ sede.nome_completo }}
      <a href="/us/elenchi/{{ sede.pk }}/">
        Vedi elenco
      </a>
    </li>

  {% empty %}
    <li>Nessuna sede.</li>

  {% endfor %}

</ul>

{% endblock %}

Livelli di Accesso (Protezione dei Contenuti)

I livelli di accesso possono essere utilizzati per controllare, per un singolo oggetto, se si hanno i permessi o meno di effettuare delle determinate operazioni (es. lettura, modifica, eliminazione).

Questo va a sosituzione dei vecchi metodi inconsistenti, in Gaia, sui modelli:

$me->puoModificare($attivita);
$attivita->modificabileDa($me);
$oggetto->modificabileDa($me);

Controllo Livello di Accesso minimo

Un controllo binario rapido puo' essere fatto per verificare che un utente abbia un livello minimo di permessi su di un determinato oggetto. Ad esempio, e' possibile verificare, in testa alla pagina che permette la modifica della scheda di un socio, che l'utente attuale disponga effettivamente dei permessi per la modifica dell'anagrafica.

Questo puo' essere fatto con la funzione:

  • Persona.permessi_almeno (oggetto, minimo, al_giorno=date.today())
    • Argomenti
      • oggetto e' l'oggetto per il quale si vogliono controllare i permessi,
      • minimo e' il livello minimo che si intende testare, es. MODIFICA. Un intero, tra NESSUNO, LETTURA, MODIFICA, ..., COMPLETO.
      • al_giorno (Opzionale) e' il giorno in cui si vuole controllare che la Persona abbia il permesso. Default e' oggi.
    • La funzione ritorna True se i permessi sono esistenti per l'oggetto e di livello pari ad almeno quanto specificato in minimo, False altrimenti.
      • Nota bene: Alcuni oggetti sono di natura pubblica, quindi sempre leggibili. Per controllare o modificare i livelli minimi per ogni tipologia di oggetto, fare riferimento al dizionario PERMESSI_MINIMO in anagrafica/permessi/costanti.py.

Es.:

@pagina_privata(permessi=GESTIONE_SOCI)
def us_modifica_socio(request, me):
  
  socio = get_object_or_404(Persona, pk=request.GET['id'])
  
  # Controllo dei permessi
  if not me.permessi_almeno(socio, MODIFICA):
    return redirect(ERRORE_PERMESSI)

  contesto = {
    socio: socio
  }
  
  return 'us_modifica_socio.html', contesto

Ottenere Livello di Accesso su di un oggetto

Se non si vuole fare un controllo binario, ma si vuole controllare esattamente che livello di accesso un utente abbia su un determinato contenuto, e' possibile utilizzare la funzione:

  • Persona.permessi (oggetto, minimo, al_giorno=date.today())
    • Argomenti
      • oggetto e' l'oggetto per il quale si vogliono controllare i permessi,
      • al_giorno (Opzionale) e' il giorno in cui si vuole controllare che la Persona abbia il permesso. Default e' oggi.
    • La funzione ritorna un valore intero tra NESSUNO, LETTURA, MODIFICA, ..., COMPLETO. Se il permesso non è esistente, viene ritornato il valore minimo per la categoria di oggetti. A scopo esemplificativo, i Commenti sono di natura pubblica, quindi passando un oggetto di tipo Commento per oggetto, non si otterrà mai un valore minore a LETTURA.
      • Nota bene, per controllare o modificare i livelli minimi per ogni tipologia di oggetto, fare riferimento al dizionario PERMESSI_MINIMO in anagrafica/permessi/costanti.py.
      • IMPORTANTE, alcuni oggetti possono specificare le proprie costanti di livello. E' bene utilizzare sempre operatori non esatti (es. >= e <= quando si ha a che fare con i livelli di permessi).

Es.:

@pagina_privata(permessi=ELENCHI_SOCI)
def us_scheda_socio(request, me):
  
  socio = get_object_or_404(Persona, pk=request.GET['id'])
  
  # Controllo dei permessi minimi
  if not me.permessi_almeno(socio, LETTURA):
    return redirect(ERRORE_PERMESSI)

  # Controllo dei permessi aggiuntivi
  permessi = me.permessi(socio)

  posso_cancellare = False
  posso_modificare = False

  if permessi >= COMPLETO:
    posso_cancellare = True
    posso_modificare = True

  if permessi >= MODIFICA:
    posso_modificare = True
  
  contesto = {
    socio: socio
    posso_cancellare: posso_cancellare,
    posso_modificare: posso_modificare
  }

  return 'us_scheda_socio.html', contesto

Guide creazine Deleghe e Permessi

Creare una nuova Delega (ie. Ruolo)

  1. In anagrafica/permessi/applicazioni.py 1. Creare una costante ed assegnarvi un valore di due caratteri, es.

    UFFICIO_SOCI = 'US'
2. Assegnare un nome alla delega creata, modificando `PERMESSI_NOMI`, es.

    ```python
    PERMESSI_NOMI = (
      ...,
      (UFFICIO_SOCI,    "Ufficio Soci"),
    )
    ```

3. Assegnare [uno slug](https://en.wikipedia.org/wiki/Semantic_URL#Slug), modificando `APPLICAZIONI_SLUG`, es.

    ```python
    APPLICAZIONI_SLUG = (
      ...,
      (UFFICIO_SOCI,    "ufficio-soci"),
    )
    ```
  1. In anagrafica/permessi/costanti.py 1. Assegnare una tipo di oggetto alla Delega. Ad esempio, si è delegati Ufficio Soci di una Sede (Comitato). In modo diverso, ad esempio, si è referenti di una Attività. Es.

    DELEGHE_OGGETTI = (
      ...,
      # Notasi che il secondo parametro è una Stringa, pari al nome del modello assegnato,
      #  piuttosto che una referenza al modello. Questo è voluto e necessario, al fine di
      #  evitare referenze circolari (oh, python...).
      (UFFICIO_SOCI,    'Sede'),
    )
  2. In anagrafica/permessi/funzioni.py 1. Creare una funzione che restituisca i permessi che scaturiscono dalla delega creata. Es.

    def permessi_ufficio_soci(sede):
        """
        Permessi della delega di UFFICIO SOCI.
    
        :param sede: Sede di cui si e' ufficio soci.
        :return: Lista di permessi.
        """
        from anagrafica.models import Sede
        return [
            (GESTIONE_SOCI,     Sede.objects.filter(pk=sede.pk)),
            (ELENCHI_SOCI,      sede.esplora()),
        ]

    Notasi che:

    • La funzione deve aver nome di tipo permessi_{nome_delega}
    • La funzione accetta un solo parametro. Questo è un oggetto del tipo specificato al punto (2).
    • La funzione ritorna una lista di tuple, nel formato (Permesso, QuerySetOggetti).
      • Il Permesso è una costante tra i permessi dichiarati in anagrafica/permessi/costanti.py
      • Il QuerySet è l'elenco degli oggetti sui quali è valido il permesso.
        • Il tipo di modello del QuerySet deve essere concorde da quello aspettato dal Permesso, specificato e verificabile in PERMESSI_OGGETTI all'interno di anagrafica/permessi/costanti.py
      • La lista deve essere elencata in ordine dal permesso più vasto a quello più restrittivo.
    • Gli import dei modelli necessari deve essere fatto internamente alla funzione, piuttosto che in testa al file (per evitare referenze circolari).
    • IMPORTANTE La funzione non deve effettuare operazioni sul database (hit), ma ritornare solo oggetti QuerySet che non sono ancora stati elaborati. Altrimenti, le performance dell'intero sistema potrebbero essere seriamente compromesse. 2. Aggiungere la funzione a PERMESSI_FUNZIONI per la relativa Delega creata. Es.
    PERMESSI_FUNZIONI = (
      ...,
      (UFFICIO_SOCI,      permessi_ufficio_soci),  # Il secondo parametro è il nome della funzione
                                                   #  sopra creata, SENZA parantesi o argomenti. 
    )
  3. Finito. Verificarne il funzionamento. * Se adeguato, creare un test in anagrafica/tests.py.

Creare un nuovo Permesso

Seguendo questi punti è possibile creare un nuovo permesso (es. ELENCHI_SOCI) ed assegnarvi dei relativi livelli di permesso su degli oggetti in Jorvik.

  1. In anagrafica/permessi/costanti.py 1. Creare una costante di permesso ed assegnarvi lo stesso nome come stringa, per valore, es.

    ELENCHI_SOCI = "ELENCHI_SOCI"
2. Aggiungere la tipologia di QuerySet di input per i permessi in `PERMESSI_OGGETTI`. Ad esempio, `ELENCHI_SOCI` agisce su un elenco di Sedi (Comitati) per i quali si hanno i permessi di gestire gli elenchi. Al contrario, `GESTIONE_ATTIVITA` agisce direttamente su un elenco di attività che si possono gestire.

    ```python
    PERMESSI_OGGETTI = (
      ...,
      # Notasi che il secondo parametro è una Stringa, pari al nome del modello assegnato,
      #  piuttosto che una referenza al modello. Questo è voluto e necessario, al fine di
      #  evitare referenze circolari (oh, python...).
      (ELENCHI_SOCI,     'Sede')  
    )
    ```
  1. In anagrafica/permessi/espansioni.py 1. Creare una funzione che restituisca i livelli di permesso che scaturiscono dal permesso creato. Es.

    def espandi_elenchi_soci(qs_sedi, al_giorno=date.today()):
        from anagrafica.models import Persona, Appartenenza, Sede
        return [
            # 1. Modifica su tutti i membri diretti delle Sedi sulle quali si ha il permesso
            (MODIFICA,  Persona.objects.filter(Appartenenza.query_attuale(al_giorno=al_giorno, sede__in=qs_sedi, membro__in=Appartenenza.MEMBRO_DIRETTO).via("appartenenze"))),
            # 2. Lettura su tutti i membri estesi delle Sedi sulle quali si ha il permesso
            (LETTURA,  Persona.objects.filter(Appartenenza.query_attuale(al_giorno=al_giorno, sede__in=qs_sedi, membro__in=Appartenenza.MEMBRO_ESTESO).via("appartenenze"))),
            # 3. Lettura su tutti i membri delle Sedi figlie di quelle sulle quali si ha il permesso
            (LETTURA,  Persona.objects.filter(Appartenenza.query_attuale(al_giorno=al_giorno, sede__in=Sede.objects.get_queryset_descendants(qs_sedi, include_self=True)).via("appartenenze"))),
        ]

    Notasi che:

    • La funzione deve aver nome di tipo espandi_{nome_permesso}
    • La funzione accetta esattamente due parametri.
      • Il primo è il QuerySet contenente gli oggetti sui quali si ha il permesso. In questo esempio, è l'elenco delle Sedi per le quali si ha il permesso di ELENCO_SOCI. Questo parametro è un oggetto queryset del modello specificato al punto (1.2).
      • Il secondo deve avere firma al_giorno=date.today() ed è relativo al momento in cui vengono controllati i permessi.
        • Se applicabile, gli elenchi degli oggetti devono essere ottenuti tenendo conto del momento della verifica dei permessi.
        • Se non applicabile, il parametro deve comunque essere mantenuto in firma, ma può essere non utilizzato nel corpo della funzione.
    • La funzione ritorna una lista di tuple, nel formato (Livello, QuerySetOggetti).
      • Il Livello è una costante intera tra quelle dichiarate in anagrafica/permessi/costanti.py (es. LETTURA, ..., COMPLETO)
      • Il QuerySet è l'elenco degli oggetti per i quali, al livello di permesso corrispondente, viene dato accesso dal permesso creato.
        • Gli oggetti ritornati possono essere DI QUALUNQUE TIPO.
      • La lista deve essere elencata in ordine dal livello più vasto (in termine di numeri di oggetti aspettati) a quello più restrittivo, per motivi di performance.
    • Gli import dei modelli necessari deve essere fatto internamente alla funzione, piuttosto che in testa al file (per evitare referenze circolari).
    • IMPORTANTE La funzione non deve effettuare operazioni sul database (hit), ma ritornare solo oggetti QuerySet che non sono ancora stati elaborati. Altrimenti, le performance dell'intero sistema potrebbero essere seriamente compromesse.
  2. Finito. Verificarne il funzionamento. * Se adeguato, creare un test in anagrafica/tests.py.