aboutsummaryrefslogtreecommitdiff
path: root/internal
diff options
context:
space:
mode:
authorMax Resnick <max@ofmax.li>2022-11-11 15:28:22 -0800
committerMax Resnick <max@ofmax.li>2022-11-11 15:28:22 -0800
commit9317407369c72a43c5f2f0bdf8f006169669cdf8 (patch)
treec6cfbf13f1391907d946b348d909456430b15296 /internal
parent301b7abdf48f843975f02a675fa7995886629eb3 (diff)
downloadgo-git-server-9317407369c72a43c5f2f0bdf8f006169669cdf8.tar.gz
re-org
Diffstat (limited to 'internal')
-rw-r--r--internal/authz/handler.go62
-rw-r--r--internal/authz/model.go62
-rw-r--r--internal/git/handler.go27
3 files changed, 151 insertions, 0 deletions
diff --git a/internal/authz/handler.go b/internal/authz/handler.go
new file mode 100644
index 0000000..e47dd33
--- /dev/null
+++ b/internal/authz/handler.go
@@ -0,0 +1,62 @@
+package authz
+
+import (
+ "context"
+ "encoding/base64"
+ "fmt"
+ "log"
+ "net/http"
+
+ "github.com/casbin/casbin/v2"
+ "golang.org/x/crypto/bcrypt"
+)
+
+// Authentication middleware to enforce authentication of all requests.
+func Authentication(authMap TokenMap, next http.Handler) http.Handler {
+ return http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) {
+ u, p, ok := req.BasicAuth()
+ if !ok {
+ rw.Header().Set("WWW-Authenticate", `Basic realm="git"`)
+ http.Error(rw, "Authentication Required", 401)
+ return
+ }
+ urn := fmt.Sprintf("uid:%s", u)
+ hash, ok := authMap[urn]
+ if !ok {
+ http.Error(rw, "Bad Request", 400)
+ return
+ }
+ token, err := base64.URLEncoding.DecodeString(p)
+ if err != nil {
+ http.Error(rw, "Bad Request", 400)
+ return
+ }
+ if err := bcrypt.CompareHashAndPassword([]byte(hash), token); err != nil {
+ http.Error(rw, "Bad Request", 400)
+ return
+ }
+ ctx := context.WithValue(req.Context(), "urn", urn)
+ next.ServeHTTP(rw, req.WithContext(ctx))
+ })
+}
+
+// Authorization middleware to enforce authoirzation of all requests.
+func Authorization(enf *casbin.Enforcer, next http.Handler) http.Handler {
+ return http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) {
+ ctx := req.Context()
+ urn := ctx.Value("urn")
+ repo := req.URL.Path
+ action := req.Method
+ ok, err := enf.Enforce(urn, repo, action)
+ if err != nil {
+ log.Printf("error running enforce %s", err)
+ http.Error(rw, "Bad Request", http.StatusBadRequest)
+ }
+ if !ok {
+ log.Printf("Access denied")
+ http.Error(rw, "Access denied", http.StatusUnauthorized)
+ }
+ log.Printf("Method %s Url %s", action, repo)
+ next.ServeHTTP(rw, req.WithContext(ctx))
+ })
+}
diff --git a/internal/authz/model.go b/internal/authz/model.go
new file mode 100644
index 0000000..cf9c952
--- /dev/null
+++ b/internal/authz/model.go
@@ -0,0 +1,62 @@
+package authz
+
+import (
+ "crypto/rand"
+ "encoding/base64"
+ "encoding/csv"
+ "fmt"
+ "math/big"
+ "os"
+
+ "golang.org/x/crypto/bcrypt"
+)
+
+// NewTokenMap create a new token map
+func NewTokenMap() TokenMap {
+ return TokenMap{}
+}
+
+// TokenMap a map of username,hash
+type TokenMap map[string]string
+
+// LoadTokens load tokens from a csv into a map
+func (tm TokenMap) LoadTokensFromFile(path string) error {
+ // TODO this should be configurable
+ contents, err := os.Open(path)
+ if err != nil {
+ fmt.Println("File reading error", err)
+ return err
+ }
+ defer contents.Close()
+ r := csv.NewReader(contents)
+ tokens, err := r.ReadAll()
+ if err != nil {
+ fmt.Println("File reading error", err)
+ return err
+ }
+ for _, acctToken := range tokens {
+ acct, hash := acctToken[0], acctToken[1]
+ tm[acct] = hash
+ }
+ return err
+}
+
+// GenerateNewToken generate a new token
+func GenerateNewToken() (string, string, error) {
+ tokenBytes := make([]byte, 28)
+ for i := range tokenBytes {
+ max := big.NewInt(int64(255))
+ randInt, err := rand.Int(rand.Reader, max)
+ if err != nil {
+ return "", "", err
+ }
+ tokenBytes[i] = uint8(randInt.Int64())
+ }
+ hashBytes, err := bcrypt.GenerateFromPassword(tokenBytes, bcrypt.DefaultCost)
+ if err != nil {
+ return "", "", err
+ }
+ token := base64.URLEncoding.EncodeToString(tokenBytes)
+ hash := string(hashBytes)
+ return token, hash, nil
+}
diff --git a/internal/git/handler.go b/internal/git/handler.go
new file mode 100644
index 0000000..5a3b3e6
--- /dev/null
+++ b/internal/git/handler.go
@@ -0,0 +1,27 @@
+package git
+
+import (
+ "fmt"
+ "net/http"
+ "net/http/cgi"
+)
+
+// GitHttpBackendHandler a handler for git cgi
+func GitHttpBackendHandler(reposDir, backendCommand string) http.Handler {
+ projectDirEnv := fmt.Sprintf("GIT_PROJECT_ROOT=%v", reposDir)
+ return http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) {
+ ctx := req.Context()
+ uid := ctx.Value("urn")
+ gitBackendHandler := &cgi.Handler{
+ Path: "/bin/sh",
+ Args: []string{"-c", backendCommand},
+ Dir: ".",
+ Env: []string{
+ projectDirEnv,
+ "GIT_HTTP_EXPORT_ALL=1",
+ fmt.Sprintf("REMOTE_USER=%s", uid),
+ },
+ }
+ gitBackendHandler.ServeHTTP(rw, req)
+ })
+}