all repos

mugit @ 1ea9b7f4fbcfc728bfafbd3b31db50855b215965

🐮 git server that your cow will love
6 files changed, 60 insertions(+), 26 deletions(-)
dont allow to see private repos for unauthorized users
Author: Oleksandr Smirnov olexsmir@gmail.com
Committed at: 2026-01-21 01:49:14 +0200
Change ID: ytwpnunotkptnzqvoxsttzplswyrtqou
Parent: 3d9ab8a
M internal/handlers/git.go

@@ -7,13 +7,42 @@ "log/slog"

"net/http" "path/filepath" + "olexsmir.xyz/mugit/internal/git" "olexsmir.xyz/mugit/internal/git/gitservice" ) +// multiplex, check if the request smells like gitprotocol-http(5), if so, it +// passes it to git smart http, otherwise renders templates +func (h *handlers) multiplex(w http.ResponseWriter, r *http.Request) { + if r.URL.RawQuery == "service=git-receive-pack" { + w.WriteHeader(http.StatusBadRequest) + w.Write([]byte("http pushing isn't supported")) + return + } + + path := r.PathValue("rest") + if path == "info/refs" && r.Method == "GET" && r.URL.RawQuery == "service=git-upload-pack" { + h.infoRefs(w, r) + } else if path == "git-upload-pack" && r.Method == "POST" { + h.uploadPack(w, r) + } else if r.Method == "GET" { + h.repoIndex(w, r) + } +} + func (h *handlers) infoRefs(w http.ResponseWriter, r *http.Request) { - // TODO: 404 for private repos - name := filepath.Clean(r.PathValue("name")) + repo, err := git.Open(filepath.Join(h.c.Repo.Dir, name), "") + if err != nil { + h.write404(w, err) + return + } + + isPrivate, err := repo.IsPrivate() + if isPrivate || err != nil { + h.write404(w, err) + return + } w.Header().Set("content-type", "application/x-git-upload-pack-advertisement") w.WriteHeader(http.StatusOK)

@@ -28,9 +57,18 @@ }

} func (h *handlers) uploadPack(w http.ResponseWriter, r *http.Request) { - // TODO: 404 for private repos + name := filepath.Clean(r.PathValue("name")) + repo, err := git.Open(filepath.Join(h.c.Repo.Dir, name), "") + if err != nil { + h.write404(w, err) + return + } - name := filepath.Clean(r.PathValue("name")) + isPrivate, err := repo.IsPrivate() + if isPrivate || err != nil { + h.write404(w, err) + return + } w.Header().Set("content-type", "application/x-git-upload-pack-result") w.Header().Set("Connection", "Keep-Alive")
M internal/handlers/handlers.go

@@ -39,24 +39,6 @@ mux.HandleFunc("GET /{name}/refs/{$}", h.refs)

return mux } -// multiplex, check if the request smells like gitprotocol-http(5), if so, it -// passes it to git smart http, otherwise renders templates -func (h *handlers) multiplex(w http.ResponseWriter, r *http.Request) { - if r.URL.RawQuery == "service=git-receive-pack" { - w.WriteHeader(http.StatusBadRequest) - w.Write([]byte("http pushing isn't supported")) - return - } - - path := r.PathValue("rest") - if path == "info/refs" && r.Method == "GET" && r.URL.RawQuery == "service=git-upload-pack" { - h.infoRefs(w, r) - } else if path == "git-upload-pack" && r.Method == "POST" { - h.uploadPack(w, r) - } else if r.Method == "GET" { - h.repoIndex(w, r) - } -} func (h *handlers) serveStatic(w http.ResponseWriter, r *http.Request) { f := filepath.Clean(r.PathValue("file"))
M internal/handlers/repo.go

@@ -54,6 +54,11 @@ slog.Error("", "err", err)

continue } + if isPrivate, err := repo.IsPrivate(); err != nil || isPrivate { + slog.Error("", "err", err) + continue + } + repoInfos = append(repoInfos, repoInfo{ Name: name, Desc: desc,

@@ -88,7 +93,7 @@ return

} isPrivate, err := repo.IsPrivate() - if isPrivate || err != nil { // FIX: private = 404, err = 500 + if isPrivate || err != nil { h.write404(w, err) return }
M internal/ssh/server.go

@@ -79,7 +79,7 @@ gitCmd := cmd[0]

repoPath := cmd[1] repoPath = filepath.Join(s.c.Repo.Dir, filepath.Clean(repoPath)) - _, err := git.Open(repoPath, "") + repo, err := git.Open(repoPath, "") if err != nil { slog.Error("ssh: failed to open repo", "err", err) s.repoNotFound(sess)

@@ -88,6 +88,17 @@ }

switch gitCmd { case "git-upload-pack": + isPrivate, err := repo.IsPrivate() + if err != nil { + s.error(sess, err) + return + } + + if isPrivate && !authorized { + s.repoNotFound(sess) + return + } + if err := gitservice.UploadPack(repoPath, false, sess, sess); err != nil { s.error(sess, err) return
M web/templates/404.html

@@ -5,7 +5,6 @@ <title>404</title>

{{ template "head" . }} </head> <body> - <!-- {{ template "nav" . }} --> <main> <h3>404 &mdash; nothing like that here.</h3> </main>
M web/templates/500.html

@@ -5,7 +5,6 @@ <title>500</title>

{{ template "head" . }} </head> <body> - <!-- {{ template "nav" . }} --> <main> <h3>500 &mdash; something broke!</h3> </main>