Flux de requêtes

Diagrammes de séquence couvrant les opérations principales de gitrust : création de dépôt, clone et push SSH, enregistrement de clé SSH, résolution des permissions, navigation web, inscription utilisateur.

1. Création d'un dépôt (Web)

sequenceDiagram
    actor User as Utilisateur
    participant Web as gitrust-web
    participant RepoSvc as RepositoryService
    participant ResSvc as ResourceService
    participant DB as PostgreSQL
    participant Git as gitrust-git
    participant FS as Système de fichiers

    User->>Web: POST /new {name, description}
    Web->>Web: Extraire user_id (session JWT)

    Web->>RepoSvc: create(db, owner_id, username, base_path, input)
    RepoSvc->>RepoSvc: RepoSlug::from_name(input.name)
    RepoSvc->>DB: SELECT ... WHERE owner_id AND slug
    DB-->>RepoSvc: None (pas de doublon)

    RepoSvc->>RepoSvc: compute_disk_path(base, owner, slug)
    Note over RepoSvc: Validation anti path-traversal

    RepoSvc->>ResSvc: register(db, "repository", repo_id, owner_id)
    ResSvc->>DB: INSERT INTO resources
    DB-->>ResSvc: Ok

    RepoSvc->>DB: INSERT INTO repositories
    DB-->>RepoSvc: repository::Model

    RepoSvc-->>Web: RepositoryOutput

    Web->>Git: BareRepo::init(disk_path)
    Git->>FS: git init --bare
    FS-->>Git: Ok
    Git-->>Web: Ok

    Web-->>User: 302 Redirect /{owner}/{slug}

2. Clone SSH (git clone)

sequenceDiagram
    actor Dev as Développeur
    participant Client as Git CLI
    participant SSH as gitrust-ssh
    participant Auth as SshKeyService
    participant Access as AccessService
    participant Git as gitrust-git
    participant DB as PostgreSQL
    participant FS as Bare repo (.git)

    Dev->>Client: git clone ssh://git@host:2222/alice/my-repo.git
    Client->>SSH: Connexion TCP :2222
    SSH-->>Client: Banner SSH + échange clés

    Client->>SSH: Auth publickey (signature)
    SSH->>Auth: find_by_fingerprint(db, fingerprint)
    Auth->>DB: SELECT FROM ssh_keys WHERE fingerprint = ?
    DB-->>Auth: ssh_key::Model {user_id, ...}
    Auth-->>SSH: user_id

    SSH->>Auth: update_last_used(db, key_id)
    Auth->>DB: UPDATE ssh_keys SET last_used_at = now()

    SSH-->>Client: Auth OK

    Client->>SSH: exec "git-upload-pack 'alice/my-repo.git'"
    SSH->>SSH: CommandHandler::parse("git-upload-pack alice/my-repo.git")
    Note over SSH: Extraction owner=alice, repo=my-repo

    SSH->>Access: effective_role(db, user_id, repo_id)
    Access->>DB: Check ownership + shares + teams
    DB-->>Access: Role::Developer
    Access-->>SSH: can_read = true

    SSH->>Git: pack_protocol::advertise_refs(repo_path)
    Git->>FS: Lecture refs
    FS-->>Git: refs/heads/main, ...
    Git-->>SSH: advertise output

    SSH-->>Client: refs advertisement
    Client->>SSH: want/have negotiation

    SSH->>Git: pack_protocol::serve_pack(repo_path, wants, haves)
    Git->>FS: Pack objects
    FS-->>Git: pack data
    Git-->>SSH: pack stream

    SSH-->>Client: pack data
    Client-->>Dev: Dépôt cloné avec succès

3. Push SSH (git push)

sequenceDiagram
    actor Dev as Développeur
    participant Client as Git CLI
    participant SSH as gitrust-ssh
    participant Access as AccessService
    participant Git as gitrust-git
    participant Hooks as gitrust-hooks
    participant DB as PostgreSQL
    participant FS as Bare repo (.git)

    Note over Client,SSH: Session SSH déjà authentifiée

    Client->>SSH: exec "git-receive-pack 'alice/my-repo.git'"
    SSH->>SSH: CommandHandler::parse(...)
    Note over SSH: Extraction owner=alice, repo=my-repo

    SSH->>Access: effective_role(db, user_id, repo_id)
    Access->>DB: Check ownership + shares + teams
    DB-->>Access: Role::Developer
    Access-->>SSH: can_push = true

    SSH->>Git: pack_protocol::advertise_refs(repo_path)
    Git->>FS: Lecture refs
    Git-->>SSH: refs advertisement

    SSH-->>Client: refs
    Client->>SSH: pack data + commands

    SSH->>Git: pack_protocol::serve_pack(repo_path, receive-pack)
    Git->>FS: Écriture objets + update refs
    FS-->>Git: Ok
    Git-->>SSH: Ok

    SSH->>DB: UPDATE repositories SET is_empty = false, updated_at = now()

    SSH-->>Client: Push OK
    Client-->>Dev: Push réussi

4. Enregistrement d'une clé SSH

sequenceDiagram
    actor User as Utilisateur
    participant Web as gitrust-web
    participant Svc as SshKeyService
    participant DB as PostgreSQL

    User->>Web: POST /settings/keys {title, key_data}
    Web->>Web: Extraire user_id (session)

    Web->>Svc: create(db, user_id, input)

    Svc->>Svc: parse_public_key(key_data)
    Note over Svc: Extraire type + décoder base64

    Svc->>Svc: validate_key_type_and_size(type, bytes)
    Note over Svc: ed25519 OK
RSA >= 4096 bits
ecdsa-p256/p384 OK Svc->>Svc: compute_fingerprint(bytes) Note over Svc: SHA256(key_bytes) -> base64-no-pad Svc->>DB: SELECT FROM ssh_keys WHERE fingerprint = ? DB-->>Svc: None (pas de doublon) Svc->>DB: INSERT INTO ssh_keys DB-->>Svc: ssh_key::Model Svc-->>Web: SshKeyOutput Web-->>User: 200 Clé ajoutée (fingerprint affiché)

5. Résolution des permissions (AccessService)

sequenceDiagram
    participant Caller as Handler / SSH Session
    participant AS as AccessService
    participant RS as ResourceService
    participant DB as PostgreSQL

    Caller->>AS: effective_role(db, user_id, repo_id)

    AS->>RS: effective_permission(db, user_id, "repository", repo_id)
    RS->>DB: SELECT FROM resources WHERE type AND id
    DB-->>RS: resource {owner_id, is_public}

    alt user_id == owner_id
        RS-->>AS: "owner"
    else Share exists
        RS->>DB: SELECT FROM resource_shares WHERE resource AND user
        DB-->>RS: share {permission_level}
        RS-->>AS: permission_level
    else Resource is public
        RS-->>AS: "read"
    else No access
        RS-->>AS: None
    end

    AS->>AS: Role::from_permission(individual_level)

    AS->>DB: SELECT team_members + team_repository_access
WHERE user_id AND repository_id DB-->>AS: Vec AS->>AS: team_role = max(team_permissions) AS->>AS: effective = max(individual_role, team_role) AS-->>Caller: Role (Reader / Developer / Maintainer / Owner)

6. Navigation dans un dépôt (Web)

sequenceDiagram
    actor User as Utilisateur
    participant Web as gitrust-web
    participant Ext as RepoExtractor
    participant Access as AccessService
    participant Git as gitrust-git
    participant DB as PostgreSQL
    participant FS as Bare repo

    User->>Web: GET /alice/my-repo/tree/main/src
    Web->>Ext: RepoContext::extract(owner=alice, repo=my-repo, ref=main, path=src)

    Ext->>DB: SELECT user WHERE username = 'alice'
    DB-->>Ext: user {id}

    Ext->>DB: SELECT repository WHERE owner_id AND slug
    DB-->>Ext: repository::Model

    Ext->>Access: effective_role(db, visitor_id, repo_id)
    Access-->>Ext: Role::Reader

    Ext-->>Web: RepoContext {repo, role, ref, path}

    Web->>Git: tree_browser::list_tree(repo_path, "main", "src")
    Git->>FS: Resolve ref main -> commit -> tree -> src/
    FS-->>Git: Vec

    Git-->>Web: entries (fichiers + dossiers)

    Web->>Git: readme::find_readme(repo_path, "main", "src")
    Git->>FS: Cherche README.md, README, readme.md
    FS-->>Git: Option
    Git-->>Web: Option

    Web->>Web: Render template tree.html
    Web-->>User: HTML (arbre + README)

7. Inscription utilisateur + création répertoire

sequenceDiagram
    actor User as Nouvel utilisateur
    participant Web as rustwarden-core (auth)
    participant Hook as GitrustHooks
    participant DB as PostgreSQL
    participant FS as Système de fichiers

    User->>Web: POST /register {username, email, password}
    Web->>DB: INSERT INTO users
    DB-->>Web: user {id, username}

    Web->>Hook: on_user_registered(db, user_id, username)
    Hook->>Hook: Valider username (anti path-traversal)
    Hook->>FS: create_dir_all(repos_base_path / username)
    FS-->>Hook: Ok
    Hook-->>Web: Ok

    Web-->>User: 302 Redirect /login