💾 Archived View for gemini.rmf-dev.com › repo › Vaati › Gemigit › files › ca074e618760e5bff246766f16… captured on 2023-03-20 at 18:03:51. Gemini links have been rewritten to link to archived content
⬅️ Previous capture (2023-01-29)
-=-=-=-=-=-=-
0 package httpgit
1
2 import (
3 "gemigit/access"
4 "gemigit/config"
5 "gemigit/db"
6 "log"
7 "net/http"
8 "strconv"
9 "strings"
10 "time"
11
12 githttpxfer "github.com/nulab/go-git-http-xfer/githttpxfer"
13 )
14
15 func Listen(path string, address string, port int) {
16 ghx, err := githttpxfer.New(path, "git")
17 if err != nil {
18 log.Fatalln("GitHTTPXfer instance could not be created. ",
19 err.Error())
20 }
21
22 chain := newChain()
23 chain.use(logging)
24 chain.use(basicAuth)
25 handler := chain.build(ghx)
26
27 log.Println("Http server started on port", port)
28 if err := http.ListenAndServe(":"+strconv.Itoa(port), handler);
29 err != nil {
30 log.Fatalln("ListenAndServe: ", err.Error())
31 }
32 }
33
34 type middleware func(http.Handler) http.Handler
35
36 func newChain() *chain {
37 return &chain{[]middleware{}}
38 }
39
40 type chain struct {
41 middlewares []middleware
42 }
43
44 func (c *chain) use(m middleware) {
45 c.middlewares = append(c.middlewares, m)
46 }
47
48 func (c *chain) build(h http.Handler) http.Handler {
49 for i := range c.middlewares {
50 h = c.middlewares[len(c.middlewares)-1-i](h)
51 }
52 return h
53 }
54
55 func logging(next http.Handler) http.Handler {
56 return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
57 t1 := time.Now()
58 next.ServeHTTP(w, r)
59 t2 := time.Now()
60 realIP := r.Header.Get("X-Real-IP")
61 log.Println("["+realIP+"]["+r.Method+"]",
62 r.URL.String(), t2.Sub(t1))
63 })
64 }
65
66 func basicAuth(next http.Handler) http.Handler {
67 return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
68 readOnly := false
69 params := strings.Split(r.URL.Path[1:], "/")
70 if len(params) < 2 {
71 renderNotFound(w)
72 return
73 }
74 if strings.Contains(r.URL.Path, "git-upload-pack") ||
75 strings.Contains(r.URL.RawQuery, "git-upload-pack") {
76 readOnly = true
77 b, err := db.IsRepoPublic(params[1], params[0])
78 if err != nil {
79 renderNotFound(w)
80 return
81 }
82 if b {
83 next.ServeHTTP(w, r)
84 return
85 }
86 }
87
88 username, password, ok := r.BasicAuth()
89 if !ok {
90 renderUnauthorized(w)
91 return
92 }
93 if username == "root#" && password == config.Cfg.Git.Key {
94 next.ServeHTTP(w, r)
95 return
96 }
97 if config.Cfg.Ldap.Enabled {
98 err := access.Login(username, password)
99 if err != nil {
100 log.Println(err.Error())
101 renderUnauthorized(w)
102 return
103 }
104
105 } else {
106 err := db.CheckAuth(username, password)
107 if err != nil {
108 log.Println(err.Error())
109 renderUnauthorized(w)
110 return
111 }
112 }
113 var err error
114 if readOnly {
115 err = access.HasReadAccess(params[1], params[0],
116 username)
117 } else {
118 err = access.HasWriteAccess(params[1], params[0],
119 username)
120 }
121 if err != nil {
122 log.Println(err.Error())
123 renderUnauthorized(w)
124 return
125 }
126 next.ServeHTTP(w, r)
127 })
128 }
129
130 func renderNotFound(w http.ResponseWriter) {
131 w.WriteHeader(http.StatusNotFound)
132 w.Write([]byte(http.StatusText(http.StatusNotFound)))
133 w.Header().Set("Content-Type", "text/plain")
134 }
135
136 func renderUnauthorized(w http.ResponseWriter) {
137 w.Header().Set("WWW-Authenticate", "Basic realm=\"" +
138 "Please enter your username and password.\"")
139 w.WriteHeader(http.StatusUnauthorized)
140 w.Write([]byte(http.StatusText(http.StatusUnauthorized)))
141 w.Header().Set("Content-Type", "text/plain")
142 }
143