Rooms

Les droits d'accès sont au coeur du mécanisme de synchronisation de Discret. Chaque tuple peut être assigné à une Room (Salle ou Chambre en anglais) qui définit un ensemble de droits:

Chaque tuple de données ne peut être que dans une seule Room, mais vous pouvez déplacer vos données d'une Room à l'autre.

Lors de l'insertion ou modification d'un tuple dans cette Room, les droits d'accès seront utilisés pour vérifier que l'utilisateur a le droit de faire cette opération.

Lors de la connection avec d'autres Pairs, ces Rooms seront utilisées pour savoir quelle données doivent être synchronisées avec ces pairs. Ces pairs recevront uniquement les données des Rooms auxquelles ils appartiennent.

Enfin, lors de la synchronisation des données, chaque tuple sera vérifié pour garantir que les données reçues ont le droit d'exister dans cette Room.

Schéma

Le schéma d'une Room est le suivant:

sys{
    Room {
        admin: [sys.UserAuth],
        authorisations:[sys.Authorisation]
    }
    
    Authorisation {
        name: String,
        rights:[sys.EntityRight] ,
        users:[sys.UserAuth],
        user_admin: [sys.UserAuth],
    }
    
    UserAuth{
        verif_key: Base64,
        enabled: Boolean default true,
    }
    
    EntityRight {
        entity: String,
        mutate_self: Boolean,
        mutate_all: Boolean,
    }
}

L'entité Room est l'entité parente qui contient le reste de la définition:

L'entité Authorisation contient un groupe de droits d'accès à cette Room. Une Room peut contenir plusieurs autorisation:

L'entité UserAuth définit un utilisateur:

Tous les utilisateurs ayant accès à une Room ont des droit de lecture sur toutes les entités. L'entité EntityRight définit les droits de modification pour une entité donnée:

Bien que cela ne soit pas recommandé, il est possible de définir un droit pour toutes les entité en insérant le joker * dans le champ entity. Les droit d'accès définit avec le joker * n'est pas prioritaire et ne sera utilisé que si l'entité n'a pas d'EntityRight propre.

Pour garantir l'intégrité de la synchronisation au fil du temps, certaines contraintes sont appliquées:

Insérer des données dans une Room

Chaque tuple possède un champ système nommé room_id qui référence la Room.

Par exemple, en considérant que $room_id contient l'identifiant d'une Room existante, la requête suivante va insérer un tuple Person dans la Room

mutate {
    Person{
        room_id: $room_id
        name: "Alice"
    }
}

Lors de l'insertion ce tuple sera signé avec votre clé de signature, et votre clé de vérification sera insérée dans le champs système verifying_key

Le système de vérification des droit d'accès va se baser sur cette verifying_key pour vérifier que vous avez le droit d'insérer une Person dans la Room référencée par $room_id

Exemple: droits pour un Blog

Le modèle de données utilisé est le suivant

blog {
    Article {
        title: String,
        content: String
    }

    Comment {
        article: blog.Article
        comment: String
    }
}

L'auteur du blog doit pouvoir:

Le lecteur doit pouvoir:

Dans la Room définissant les droits d'accès au blog, nous allons donc créer deux Authorisations

La Room est créée avec la requête suivante la suivante:

1mutate {
2 sys.Room{
3 admin: [{
4 verif_key:$admin_user
5 }]
6 authorisations:[{
7 name:"authors"
8 rights:[
9 {
10 entity:"blog.Article"
11 mutate_self:true
12 mutate_all:true
13 },
14 {
15 entity:"blog.Comment"
16 mutate_self:true
17 mutate_all:true
18 }
19 ]
20 users:[{
21 verif_key:$author
22 }]
23 },{
24 name:"readers"
25 rights:[ {
26 entity:"blog.Comment"
27 mutate_self:true
28 mutate_all:false
29 }]
30 users:[{
31 verif_key:$reader_1
32 },{
33 verif_key:$reader_1
34 }]
35 }]
36 }
37}

les lignes 7 à 23 décrivent les autorisations pour les auteurs, et les lignes 24 à 34 décrivent les autorisations pour les lecteurs.

Vous noterez que:

Exemple: un calendrier partagé

Cet exemple est plus complexe que le précédent et montre une interaction entre plusieurs Rooms.

Pour un calendrier partagé nous devons pouvoir séparer la visibilité entre la date d'un rendez-vous et son contenu. Par exemple:

Comme les utilisateurs d'une Room ont accès à toutes les données, nous avons besoin de deux Rooms différentes:

Nous utiliserons ce modèle simplifié:

cal {
    Calendar {
        name: String,
        appointments: [cal.Appointment],
    }

    Appointment {
        start: Integer,
        end: Integer,
        detail: cal.AppointmentDetail
    }

    AppointmentDetail {
        title: String,
    }
}

En considérant les utilisateurs suivant:

La Room $room_calendar sera défini de la façon suivante:

mutate {
    sys.Room{
        admin: [{
            verif_key:$author
        }]
        authorisations:[{
            name:"authors"
            rights:[
                {
                    entity:"cal.Calendar"
                    mutate_self:true
                    mutate_all:true
                },
                {
                    entity:"cal.Appointment"
                    mutate_self:true
                    mutate_all:true
                }
            ]
            users:[{
                verif_key:$author
            }]
        },{
            name:"readers"
            users:[{
                verif_key:$team_user
            },{
                verif_key:$collaborator
            }]
        }]
    }
}

Comme les utilisateurs n'ont qu'un accès qu'en lecture seule, aucun rights n'est défini pour l'autorisation readers.

La Room $room_cal_detail sera défini de la façon suivante:

mutate {
    sys.Room{
        admin: [{
            verif_key:$author
        }]
        authorisations:[{
            name:"authors"
            rights:[
                {
                    entity:"cal.AppointmentDetail"
                    mutate_self:true
                    mutate_all:true
                }
            ]
            users:[{
                verif_key:$author
            }]
        },{
            name:"readers"
            users:[{
                verif_key:$team_user
            }]
        }]
    }
}

Vous noterez que seul $team_user est dans le groupe reader.

On peut créer un nouveau calendrier avec la requête suivante:

mutate {
    res: cal.Calendar{
        room_id: $room_calendar
        name: "my calendar"
    }
}

En considérant que le calendrier a pour id $calendar_id, l'insertion d'un nouveau rendez-vous se fera avec la requête suivante:

mutate {
    cal.Calendar {
        id: $calendar_id
        appointments:[{
            room_id: $room_calendar_id
            start:  1720770000000
            end:    1720780000000
            detail:{
                room_id: $room_cal_detail_id
                title: "An important meeting"
            }
        }]
    }
}

Et chaque utilisateur pourra utiliser la requête suivante pour récupérer les rendez-vous:

query {
    res: cal.Calendar (
        id=$calendar_id
    ){
        name
        appointments(
            nullable(detail)
            ){
            start
            end
            detail {
                title
            }
        }
    }
}

L'utilisateur $team_user obtiendra le résultat suivant

{
    "res":[{
        "name":"my calendar",
        "appointments":[{
            "start":1720770000000,
            "end":1720780000000,
            "detail":{
                "title":"An important meeting"
            }
        }]
    }]
}

Tandis que l'utilisateur $collaborator (qui n'a pas accès au détails) obtiendra le résultat suivant:

{
    "res":[{
        "name":"my calendar",
        "appointments":[{
            "start":1720770000000,
            "end":1720780000000,
            "detail":null
        }]
    }]
}