💾 Archived View for gemini.rmf-dev.com › repo › Vaati › Gemigit › files › accba6af8454e1250e45fcd23b… captured on 2023-09-08 at 16:30:56. Gemini links have been rewritten to link to archived content

View Raw

More Information

⬅️ Previous capture (2023-04-19)

🚧 View Differences

-=-=-=-=-=-=-

Go Back

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 var err error

69 readOnly := false

70 public := false

71 params := strings.Split(r.URL.Path[1:], "/")

72 if len(params) < 2 {

73 renderNotFound(w)

74 return

75 }

76 owner := params[0]

77 repo := params[1]

78 if strings.Contains(r.URL.Path, "git-upload-pack") ||

79 strings.Contains(r.URL.RawQuery, "git-upload-pack") {

80 readOnly = true

81 public, err = db.IsRepoPublic(repo, owner)

82 if err != nil {

83 renderNotFound(w)

84 return

85 }

86 if config.Cfg.Git.Public && public {

87 next.ServeHTTP(w, r)

88 return

89 }

90 }

91

92 username, password, ok := r.BasicAuth()

93 if !ok {

94 renderUnauthorized(w)

95 return

96 }

97 /* The git key in the configuration file is empty by default,

98 so that root# authentication is disabled by default.

99 The root authentication is necessary to run instance in

100 stateless mode. */

101 if config.Cfg.Git.Key != "" && username == "root#" &&

102 password == config.Cfg.Git.Key {

103 next.ServeHTTP(w, r)

104 return

105 }

106 /* check if it is allowed to use password instead of token*/

107 pass, err := db.CanUsePassword(repo, owner, username)

108 if err != nil {

109 log.Println(err.Error())

110 renderUnauthorized(w)

111 return

112 }

113 err = access.Login(username, password, true, pass)

114 if err != nil {

115 log.Println(err.Error())

116 renderUnauthorized(w)

117 return

118 }

119 if readOnly && public {

120 next.ServeHTTP(w, r)

121 return

122 }

123 if readOnly {

124 err = access.HasReadAccess(repo, owner, username)

125 } else {

126 err = access.HasWriteAccess(repo, owner, username)

127 }

128 if err != nil {

129 log.Println(err.Error())

130 renderUnauthorized(w)

131 return

132 }

133 next.ServeHTTP(w, r)

134 })

135 }

136

137 func renderNotFound(w http.ResponseWriter) {

138 w.WriteHeader(http.StatusNotFound)

139 w.Write([]byte(http.StatusText(http.StatusNotFound)))

140 w.Header().Set("Content-Type", "text/plain")

141 }

142

143 func renderUnauthorized(w http.ResponseWriter) {

144 w.Header().Set("WWW-Authenticate", "Basic realm=\"" +

145 "Please enter your username and password.\"")

146 w.WriteHeader(http.StatusUnauthorized)

147 w.Write([]byte(http.StatusText(http.StatusUnauthorized)))

148 w.Header().Set("Content-Type", "text/plain")

149 }

150