Aller au contenu

Installation de Grist

logo

👋 Présentation

Grist est une application web opens-source (Apache-2.0 license) et gratuite, à mi-chemin entre Excel et Access, éditée par l'entreprise Gristlabs. La ressemblance avec les logiciels privateurs s'arrête là car l'outil est bien plus puissant.

tableur

En effet, il permet de développer des applications collaboratives de manipulation des données sans être obligatoirement développeur. On peut classer la solution entre les applications no-code pour une utilisation simple et low-code pour une utilisation plus complexe. Vous pouvez retrouver des exemples d'utilisation sur cette page https://www.getgrist.com/templates/.

À noter que l'Agence nationale de la cohésion des territoires (ANCT) contribue activement à Grist.

Prérequis

  • Un nom de domaine de portée locale ou globale
  • Un serveur GNU/Linux avec Docker installé
  • Une autorité de certification (Let's Encrypt, Step CA...)
  • Un reverse-proxy (Traefik, Nginx...)
  • Un IAM (Authelia, Keycloak...)

Dans cet exemple d'installation, j'utilise un nom de domaine de portée locale avec Step CA comme autorité de certification, Traefik pour le reverse-proxy et Authelia comme IAM. Ce dernier est indispensable pour bénéficier de la fonctionnalité collaborative car Grist ne propose pas de gestion des utilisateurs intégrée à l'application.

Installation de Grist avec Docker

Authelia

Dans mon cas, Authelia s'appuie sur la solution LLDAP pour la partie utilisateur. J'avais rédigé une documentation ici : Authelia/LLDAP

Génération du client ID et du client secret
# Client ID
$ docker run authelia/authelia:latest authelia crypto rand --length 64 --charset alphanumeric

Random Value: b4xofr6M0fcJNO6Mf2SAhy2icnRUsufVyRJMscDogByyQtw4T9Vphsx9LoqYxK0o

# Client secret en version claire et en version chiffrée
$ docker run authelia/authelia:latest authelia crypto hash generate pbkdf2 --variant sha512 --random --random.length 72 --random.charset rfc3986

Random Password: 0WBDZec0XQpUaQrQpr6X3VeXspCxcfYR.70EdShxaUV45FctMsAPEnlhpBC_o_cQPsOyjFd~
Digest: $pbkdf2-sha512$310000$OnqAv9QUbmxJar0YtHJHRg$.7bCvW.pkli4GVQZB.4v0Qsp3fVHvkmEWOVUYCJXkCf9HhmmRuRy.OYxTxkR9hZYK1DdV5iQT1kobr1EcKGr7g

Il faut ensuite déclarer le client dans le fichier de configuration d'Authelia.

Exrait du fichier de configuration d'Authelia
identity_providers:
  oidc:
    jwks:
      - key: {{ secret "/secrets/private.pem" | mindent 10 "|" | msquote }}
        certificate_chain: |
          -----BEGIN CERTIFICATE-----
          (...)
          -----END CERTIFICATE-----
    lifespans.access_token: 1h
    lifespans.authorize_code: 1m
    lifespans.id_token: 1h
    lifespans.refresh_token: 90m
    enable_client_debug_messages: false
    clients:
      (...)
      - client_id: 'b4xofr6M0fcJNO6Mf2SAhy2icnRUsufVyRJMscDogByyQtw4T9Vphsx9LoqYxK0o'
        authorization_policy: 'one_factor'
        client_name: 'Grist'
        client_secret: '$pbkdf2-sha512$310000$OnqAv9QUbmxJar0YtHJHRg$.7bCvW.pkli4GVQZB.4v0Qsp3fVHvkmEWOVUYCJXkCf9HhmmRuRy.OYxTxkR9hZYK1DdV5iQT1kobr1EcKGr7g'
        public: false
        redirect_uris:
          - 'https://grist.maison.lab/oauth2/callback'
        scopes:
          - 'openid'
          - 'profile'
          - 'email'
        require_pkce: true
        pkce_challenge_method: 'S256'
        grant_types:
          - 'implicit'
          - 'authorization_code'
          - 'refresh_token'
        response_types:
          - 'code'
        response_modes:
          - 'form_post'
        consent_mode: 'auto'
        pre_configured_consent_duration: '1y'

Grist

Je me suis basé sur cet exemple que j'ai adapté à mon environnement.

Nous commençons par créer l'arborescence.

Arborescence de /srv/grist
.
|-- grist
|   |-- data
|   |-- database

Passons à la création du compose.yml

Traefik

Traefik n'étant pas installé sur la même VM. J'ai donc paramétré les accès à Grist en déclarant le router et le service dans la configuration dynamique de Traefik.

/srv/grist/compose.yml
services:
  grist:
    image: gristlabs/grist:stable
    container_name: grist
    user: "1000" # id du user qui instanceria le conteneur
    env_file:
      - ./grist.env
    volumes:
      - ./data:/persist
    ports:
      - 3000:8080
    depends_on:
      - database

  database:
    image: postgres:15-alpine
    container_name: grist_db
    command: -c jit=off # https://support.getgrist.com/self-managed/#what-is-a-home-database
    #user: "1000" # Commenté pour la phase d'initialisation
    env_file:
      - ./grist_db.env
    volumes:
      - ./database/data:/var/lib/postgresql/data

Il nous faut maintenant générer le mot de passe pour Postgresql.

Mot de passe Postgresql
$ tr -cd '[:alnum:]' < /dev/urandom | fold -w "64" | head -n 1

XNQUhXTEzkijp5GLlg33SC7DSnuBK07z1AyyE2dScGI7dFhT2O7TJRhTZOA1cYea

Nous terminons avec les variables d'environnement. Nous allons configurer Grist pour être utilisé par une seule équipe puisque je n'ai qu'un seul site (ici maison défini dans la variable GRIST_SINGLE_ORG). L'administrateur par défaut sera précisé dans la variable GRIST_DEFAULT_EMAIL (plus d'infos sur https://support.getgrist.com/self-managed/#how-do-i-set-up-a-team)

/srv/grist/grist.env
PORT=8080
APP_HOME_URL=https://grist.maison.lab
GRIST_DOMAIN=grist.maison.lab
GRIST_SINGLE_ORG=maison
GRIST_HIDE_UI_ELEMENTS=billing
GRIST_LIST_PUBLIC_SITES=false
GRIST_MAX_UPLOAD_ATTACHMENT_MB=50
GRIST_MAX_UPLOAD_IMPORT_MB=300
GRIST_ORG_IN_PATH=false
GRIST_PAGE_TITLE_SUFFIX=_blank
GRIST_FORCE_LOGIN=true
GRIST_SUPPORT_ANON=false
GRIST_THROTTLE_CPU=true
# Saisissez un email existant dans votre backend d'auth (LLDAP dans mon cas).
# Dans le cadre du lab, j'ai utulisé mon email perso. Ainsi mon utilisateur
# sera par défaut l'administrateur de l'application. 
GRIST_DEFAULT_EMAIL=<VOTRE_EMAIL>

GRIST_SANDBOX_FLAVOR=gvisor
PYTHON_VERSION=3
PYTHON_VERSION_ON_CREATION=3

# Database
TYPEORM_DATABASE=grist
TYPEORM_USERNAME=grist
TYPEORM_HOST=grist_db
TYPEORM_LOGGING=false
TYPEORM_PASSWORD=XNQUhXTEzkijp5GLlg33SC7DSnuBK07z1AyyE2dScGI7dFhT2O7TJRhTZOA1cYea
TYPEORM_PORT=5432
TYPEORM_TYPE=postgres

# OIDC
GRIST_OIDC_SP_HOST=https://grist.maison.lab
GRIST_OIDC_IDP_ISSUER=https://auth.maison.lab/
GRIST_OIDC_IDP_SCOPES=openid profile email
GRIST_OIDC_IDP_CLIENT_ID=b4xofr6M0fcJNO6Mf2SAhy2icnRUsufVyRJMscDogByyQtw4T9Vphsx9LoqYxK0o
# J'ai utilisé la version en clair du client secret mais il y a certainement un moyen d'améliorer cela avec docker secrets
GRIST_OIDC_IDP_CLIENT_SECRET=0WBDZec0XQpUaQrQpr6X3VeXspCxcfYR.70EdShxaUV45FctMsAPEnlhpBC_o_cQPsOyjFd~
GRIST_OIDC_IDP_SKIP_END_SESSION_ENDPOINT=true
/srv/grist/grist_db.env
$ cat grist_db.env 
POSTGRES_DB=grist
POSTGRES_USER=grist
POSTGRES_PASSWORD=XNQUhXTEzkijp5GLlg33SC7DSnuBK07z1AyyE2dScGI7dFhT2O7TJRhTZOA1cYea

On termine en lançant le conteneur. Une fois ce dernier instancié, on arrête le conteneur. Il faut ensuite modifier les permissions pour l'utilisateur ayant l'ID 1000 du répertoire database et son contenu. On décommente la directive user du service database. On relance le conteneur et on vérifie qu'il n'y ait pas d'erreurs de permission.

Utilisation de base

Connexion

Connectez-vous à l'url de votre application. Par défaut vous serez redirigé sur votre IAM. Authentifiez-vous avec l'utilisateur dont l'email a été déclaré dans la variable GRIST_DEFAULT_EMAIL.

Vous arrivez alors sur cette page.

Connexion admin

Dès lors vous pouvez créer des documents.

Cet utilisateur est important car il va vous permettre d'administrer les équipes et les règles d'accès aux documents.

Connectez-vous maintenant avec un autre utilisateur. Vous arrivez alors sur une page vous indiquant que l'utilisateur n'a aucun accès aux documents.

Connexion utilisateur

Nous allons voir comment lui donner accès à un document.

Gestion des équipes

Je dois avouer que la gestion des utilisateurs est un peu déroutante au départ. En tant qu'admin, vous ne pouvez qu'inviter des utilisateurs s'étant déjà connectés à l'application en précisant leur email. L'email est juste utilisé pour sélectionner le ou les utilisateurs, aucune invitation n'est envoyée par mail. D'ailleurs il n'existe aucune option pour paramétrer un serveur SMTP.

L'invitation n'a pas besoin d'être acceptée par l'utilisateur destinataire.

Vous pouvez gérer les utilisateurs sur trois niveaux :

  1. Au niveau de l'organisation (pour rappel ici c'est l'organisation maison). Sur ce premier niveau vous donnez l'accès à l'ensemble des documents avec quatres types de permissions :
    • Pas d'accès : c'est le niveau par défaut.
    • Propriétaire : c'est l'équivalent d'admin.
    • Editeur : vous pouvez créer et modifier des documents.
    • Lecture seule : vous ne pouvez que consulter.
  2. Au niveau du document : trois niveau de permissions sont proposés :
    • Propriétaire
    • Éditeur
    • Lecture seule
  3. Au niveau des tables contenues dans un document.

J'ai commencé par inviter un utilisateur au niveau de l'organisation avec la permission Pas d'accès. Pour cela j'ai cliqué sur le bouton "Gérer les équipes". Malgré la connexion initiale de l'utilisateur que je souhaite intégrer, il n'apparait pas dans la liste proposée. J'ai du passer par l'option proposée Invitation multiple. Cela était lié à l'extension Bitwarden qui me cachait une partie du menu proposant d'inviter le nouveau membre en question.

Ensuite j'ai invité l'utilisateur directement sur un document en cliquant sur l'option "Gérer les utilisateurs".

invitation document

Dans la case Entrer l'adresse e-mail, l'utilisateur concerné est proposé. Une fois les permsisions attribuées à l'utilisateur, le document apparait sur sa page d'accueil.

page accueil

Documents

Un document contient par défaut une vue. Les vues permettent d'organiser le document en découpant celui-ci en sections et sous-sections.

Les vues contiennent des graphiques, des formulaires,... en lien avec les tables.

Document et vues

Les tables

Les tables sont à la base de la gestion des données. Leur utilisation est très proche de celle d'un tableur. D'ailleurs on peut importer une fichier csv ou xlsx.

CSV import

On peut choisir le type de colonne adpaté aux données qu'elle contiendra.

Type de colonne

Les colonnes de type références sont la clé des données relationnelles dans Grist. Ils permettent à une ligne de pointer (ou de faire référence) vers une autre. Si vous avez déjà fait cela dans Excel ou LibreOffice, vous constaterez la simplicité de sa mise en place dans Grist.

API

L'autre force du logiciel comparée à un tableur classique est son API.

API

En effet vous pouvez requêter jusqu'aux enregistrements des tables contenues dans les documents. On peut donc imaginer par exemple Grist en référentiel de données alimenté par des services du système d'information et alimentant d'autres services.

Un exemple d'automatisation est donné dans ce webinaire de l'ANCT https://tube.numerique.gouv.fr/w/nXf1XuLswdgHq1j4a1JNMn avec n8n en cours de remplacement par Active Pieces comme expliqué dans la vidéo.

Snapshot

Grist propose un système de snapshot des documents. Pratique pour restaurer un document ou comparer des changements. Cependant attention, il vous faudra déployer une plateforme de stockage d'objets tel que MinIO pour bénéficier de cette fonctionnalité https://support.getgrist.com/self-managed/#how-do-i-set-up-snapshots.

Conclusion

J'utilise Grist en test depuis quelques semaines au travail et j'en suis très satisfait. On peut rapidement développer des outils simples et pratiques. Cette application va remplacer de nombreux fichiers excel et va m'aider à améliorer des process métiers tout en gagnant du temps sur la phase développement des outils.

L'outil n'est pas exempt de bugs. Par exemple la fonctionnalité de calendrier bugue sur Firefox et pas sur Chromium (problème de javascript selon toute vraisemblance).

Cela ne venait pas de Firefox mais de l'extension Privacy Badger qui bloquait le domaine "uicdn.toast.com" :

Privacy Badger

En passant le curseur au milieu, le calendrier s'affiche sans problème.

Voilà pour ce tour rapide de présentation et de prise en main de Grist. Suivant le temps que j'aurai, je rédigerai d'autres documentations sur des exemples d'utilisation.

En attendant, n'hésitez pas à consulter la documentation qui contient déjà beaucoup d'exemples d'utilisation : https://support.getgrist.com/examples/ et à visionner les vidéos de présentation de l'outil par l'ANCT : https://tube.numerique.gouv.fr/c/grist/videos.