aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMax Resnick <max@ofmax.li>2022-11-09 22:35:03 -0800
committerMax Resnick <max@ofmax.li>2022-11-09 22:35:03 -0800
commitc55145f51542f2409c2822a60f99f9e3208214df (patch)
tree253e07ac07ce4134b54841e6ae51836492edf987
parente3010e016c716d3f95936752846550e47771cd51 (diff)
downloadgo-git-server-c55145f51542f2409c2822a60f99f9e3208214df.tar.gz
add authz setup
-rw-r--r--go.mod1
-rw-r--r--go.sum2
-rw-r--r--main.go66
3 files changed, 54 insertions, 15 deletions
diff --git a/go.mod b/go.mod
index 0ceffaf..16920ac 100644
--- a/go.mod
+++ b/go.mod
@@ -6,5 +6,6 @@ require golang.org/x/crypto v0.0.0-20221012134737-56aed061732a
require (
github.com/Knetic/govaluate v3.0.1-0.20171022003610-9aa49832a739+incompatible // indirect
+ github.com/casbin/casbin v1.9.1 // indirect
github.com/casbin/casbin/v2 v2.56.0 // indirect
)
diff --git a/go.sum b/go.sum
index 3dcf68b..31ea3fb 100644
--- a/go.sum
+++ b/go.sum
@@ -1,5 +1,7 @@
github.com/Knetic/govaluate v3.0.1-0.20171022003610-9aa49832a739+incompatible h1:1G1pk05UrOh0NlF1oeaaix1x8XzrfjIDK47TY0Zehcw=
github.com/Knetic/govaluate v3.0.1-0.20171022003610-9aa49832a739+incompatible/go.mod h1:r7JcOSlj0wfOMncg0iLm8Leh48TZaKVeNIfJntJ2wa0=
+github.com/casbin/casbin v1.9.1 h1:ucjbS5zTrmSLtH4XogqOG920Poe6QatdXtz1FEbApeM=
+github.com/casbin/casbin v1.9.1/go.mod h1:z8uPsfBJGUsnkagrt3G8QvjgTKFMBJ32UP8HpZllfog=
github.com/casbin/casbin/v2 v2.56.0 h1:4qM+hDfj+i9M6lBbguafWKE/8tJA+9vRY5+l0ZB5WTo=
github.com/casbin/casbin/v2 v2.56.0/go.mod h1:vByNa/Fchek0KZUgG5wEsl7iFsiviAYKRtgrQfcJqHg=
github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4=
diff --git a/main.go b/main.go
index e786c9b..0b08023 100644
--- a/main.go
+++ b/main.go
@@ -1,6 +1,7 @@
package main
import (
+ "context"
"crypto/rand"
"encoding/base64"
"encoding/csv"
@@ -12,17 +13,18 @@ import (
"net/http/cgi"
"os"
+ "github.com/casbin/casbin/v2"
"golang.org/x/crypto/bcrypt"
)
var (
- reposDir = flag.String("r", ".", "Directory containing git repositories")
+ reposDir = flag.String("r", "./repos", "Directory containing git repositories")
backendCommand = flag.String("c", "git http-backend", "CGI binary to execute")
addr = flag.String("l", ":8080", "Address/port to listen on")
newToken = flag.Bool("t", false, "make a new token")
- authMap = map[string]string{}
)
+// LoadTokens load tokens from a csv into a map
func LoadTokens() (map[string]string, error) {
tokenMap := make(map[string]string)
contents, err := os.Open("tokens.csv")
@@ -44,6 +46,7 @@ func LoadTokens() (map[string]string, error) {
return tokenMap, err
}
+// NewToken generate a new token
func NewToken() (string, string, error) {
tokenBytes := make([]byte, 28)
for i := range tokenBytes {
@@ -65,19 +68,26 @@ func NewToken() (string, string, error) {
// GitHttpBackendHandler a handler for git cgi
func GitHttpBackendHandler(reposDir, backendCommand string) http.Handler {
- return &cgi.Handler{
- Path: "/bin/sh",
- Args: []string{"-c", backendCommand},
- Dir: ".",
- Env: []string{
- fmt.Sprintf("GIT_PROJECT_ROOT=%v", reposDir),
- "GIT_HTTP_EXPORT_ALL=1",
- },
- }
+ 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")
+ git := &cgi.Handler{
+ Path: "/bin/sh",
+ Args: []string{"-c", backendCommand},
+ Dir: ".",
+ Env: []string{
+ projectDirEnv,
+ "GIT_HTTP_EXPORT_ALL=1",
+ fmt.Sprintf("REMOTE_USER=%s", uid),
+ },
+ }
+ git.ServeHTTP(rw, req)
+ })
}
// Authentication middleware to enforce authentication of all requests.
-func Authentication(next http.Handler) http.Handler {
+func Authentication(authMap map[string]string, next http.Handler) http.Handler {
return http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) {
u, p, ok := req.BasicAuth()
if !ok {
@@ -85,7 +95,8 @@ func Authentication(next http.Handler) http.Handler {
http.Error(rw, "Authentication Required", 401)
return
}
- hash, ok := authMap[fmt.Sprintf("user:%s", u)]
+ urn := fmt.Sprintf("uid:%s", u)
+ hash, ok := authMap[urn]
if !ok {
http.Error(rw, "Bad Request", 400)
return
@@ -99,10 +110,36 @@ func Authentication(next http.Handler) http.Handler {
http.Error(rw, "Bad Request", 400)
return
}
+ ctx := context.WithValue(req.Context(), "urn", urn)
+ next.ServeHTTP(rw, req.WithContext(ctx))
+ })
+}
+
+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
+ // defer req.Body.Close()
+ // body, _ := io.ReadAll(req.Body)
+ // log.Printf("%s", body)
+ 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))
})
}
func main() {
+ enf, _ := casbin.NewEnforcer("./auth_model.ini", "./policy.csv")
flag.Parse()
if *newToken {
token, hash, err := NewToken()
@@ -118,9 +155,8 @@ func main() {
}
router := http.NewServeMux()
// TODO we don't want to use a global
- authMap = tokens
// de-reference args
router.Handle("/", GitHttpBackendHandler(*reposDir, *backendCommand))
- mux := Authentication(router)
+ mux := Authentication(tokens, Authorization(enf, router))
log.Fatal(http.ListenAndServe(":8080", mux))
}