diff options
| -rw-r--r-- | cmd/main.go | 20 | ||||
| -rw-r--r-- | gitserver.yaml | 9 | ||||
| -rw-r--r-- | go.mod | 29 | ||||
| -rw-r--r-- | go.sum | 75 | ||||
| -rw-r--r-- | internal/admin/middleware.go | 15 | ||||
| -rw-r--r-- | internal/admin/model.go | 102 | ||||
| -rw-r--r-- | internal/admin/model_test.go | 164 | ||||
| -rw-r--r-- | internal/admin/service.go | 56 | ||||
| -rw-r--r-- | internal/admin/service_test.go | 132 | ||||
| -rw-r--r-- | internal/authz/middleware.go | 2 | ||||
| -rw-r--r-- | internal/authz/middleware_test.go | 15 | ||||
| -rw-r--r-- | policy.csv | 19 | ||||
| -rw-r--r-- | testpolicy.csv | 11 |
13 files changed, 512 insertions, 137 deletions
diff --git a/cmd/main.go b/cmd/main.go index f2f1c63..bf3697d 100644 --- a/cmd/main.go +++ b/cmd/main.go @@ -12,14 +12,15 @@ import ( ) var ( - 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") - modelPath = flag.String("m", "./auth_model.ini", "Authentication model") - policyPath = flag.String("p", "./policy.csv", "auth policy") - serverConfig = flag.String("s", "./gitserver.yaml", "serverconfig path") - newToken = flag.Bool("t", false, "make a new token") - updatePolicies = flag.Bool("u", false, "update policies") + reposDir = flag.String("r", "./repos", "Directory containing git repositories") + mgmtRepo = flag.Bool("a", true, "mgmt repo used for configuration") + backendCommand = flag.String("c", "git http-backend", "CGI binary to execute") + addr = flag.String("l", ":8080", "Address/port to listen on") + modelPath = flag.String("m", "./auth_model.ini", "Authentication model") + policyPath = flag.String("p", "./policy.csv", "auth policy") + serverConfigPath = flag.String("s", "/gitserver.yaml", "serverconfig path") + newToken = flag.Bool("t", false, "make a new token") + updatePolicies = flag.Bool("u", false, "update policies") ) func main() { @@ -32,7 +33,7 @@ func main() { fmt.Printf("token: %s\nhash: %s\n", token, hash) return } - adminSvc := admin.NewService(*modelPath, *policyPath, *serverConfig) + adminSvc := admin.NewService(*modelPath, *policyPath, *serverConfigPath, *reposDir, *mgmtRepo) adminSvc.InitServer() tokens := authz.NewTokenMap() err := tokens.LoadTokensFromFile("./tokens.csv") @@ -42,6 +43,7 @@ func main() { router := http.NewServeMux() // TODO we don't want to use a global // de-reference args + router.Handle("/mgmt/", admin.AdminHooks(adminSvc, git.GitHttpBackendHandler(*reposDir, *backendCommand))) router.Handle("/", git.GitHttpBackendHandler(*reposDir, *backendCommand)) mux := authz.Authentication(tokens, authz.Authorization(adminSvc, router)) log.Fatal(http.ListenAndServe(":8080", mux)) diff --git a/gitserver.yaml b/gitserver.yaml index 1ffc59d..2a2c828 100644 --- a/gitserver.yaml +++ b/gitserver.yaml @@ -3,13 +3,18 @@ name: "go-git-server" version: "v1alpha1" basepath: ./repos repos: + - name: mgmt + public: false + permissions: + - role: admin + mode: 1 - name: testmerepo public: true git_web_config: owner: grumps - description: |- + description: >- A wrapper to git http-backend providing authentcation and authorization - Inspired by gitolite + inspired by gitolite. permissions: - role: maintainers mode: 1 @@ -2,32 +2,33 @@ module git.ofmax.li/go-git-server go 1.19 -require golang.org/x/crypto v0.3.0 +require ( + github.com/casbin/casbin/v2 v2.56.0 + github.com/go-git/go-billy v4.2.0+incompatible + github.com/go-git/go-billy/v5 v5.4.1 + github.com/go-git/go-git/v5 v5.6.1 + golang.org/x/crypto v0.6.0 + gopkg.in/ini.v1 v1.67.0 + gopkg.in/yaml.v2 v2.4.0 + sigs.k8s.io/yaml v1.3.0 +) require ( github.com/Knetic/govaluate v3.0.1-0.20171022003610-9aa49832a739+incompatible // indirect github.com/Microsoft/go-winio v0.5.2 // indirect - github.com/ProtonMail/go-crypto v0.0.0-20221026131551-cf6655e29de4 // indirect - github.com/acomagu/bufpipe v1.0.3 // indirect - github.com/casbin/casbin v1.9.1 // indirect - github.com/casbin/casbin/v2 v2.56.0 // indirect + github.com/ProtonMail/go-crypto v0.0.0-20230217124315-7d5c6f04bbb8 // indirect + github.com/acomagu/bufpipe v1.0.4 // indirect github.com/cloudflare/circl v1.1.0 // indirect github.com/emirpasic/gods v1.18.1 // indirect github.com/go-git/gcfg v1.5.0 // indirect - github.com/go-git/go-billy/v5 v5.3.1 // indirect - github.com/go-git/go-git/v5 v5.5.1 // indirect github.com/imdario/mergo v0.3.13 // indirect github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 // indirect github.com/kevinburke/ssh_config v1.2.0 // indirect - github.com/pjbgf/sha1cd v0.2.3 // indirect + github.com/pjbgf/sha1cd v0.3.0 // indirect github.com/sergi/go-diff v1.1.0 // indirect github.com/skeema/knownhosts v1.1.0 // indirect - github.com/subpop/go-ini v0.1.4 // indirect github.com/xanzy/ssh-agent v0.3.3 // indirect - golang.org/x/net v0.2.0 // indirect - golang.org/x/sys v0.2.0 // indirect - gopkg.in/ini.v1 v1.67.0 // indirect + golang.org/x/net v0.7.0 // indirect + golang.org/x/sys v0.5.0 // indirect gopkg.in/warnings.v0 v0.1.2 // indirect - gopkg.in/yaml.v2 v2.4.0 // indirect - sigs.k8s.io/yaml v1.3.0 // indirect ) @@ -2,34 +2,42 @@ github.com/Knetic/govaluate v3.0.1-0.20171022003610-9aa49832a739+incompatible h1 github.com/Knetic/govaluate v3.0.1-0.20171022003610-9aa49832a739+incompatible/go.mod h1:r7JcOSlj0wfOMncg0iLm8Leh48TZaKVeNIfJntJ2wa0= github.com/Microsoft/go-winio v0.5.2 h1:a9IhgEQBCUEk6QCdml9CiJGhAws+YwffDHEMp1VMrpA= github.com/Microsoft/go-winio v0.5.2/go.mod h1:WpS1mjBmmwHBEWmogvA2mj8546UReBk4v8QkMxJ6pZY= -github.com/ProtonMail/go-crypto v0.0.0-20221026131551-cf6655e29de4 h1:ra2OtmuW0AE5csawV4YXMNGNQQXvLRps3z2Z59OPO+I= -github.com/ProtonMail/go-crypto v0.0.0-20221026131551-cf6655e29de4/go.mod h1:UBYPn8k0D56RtnR8RFQMjmh4KrZzWJ5o7Z9SYjossQ8= -github.com/acomagu/bufpipe v1.0.3 h1:fxAGrHZTgQ9w5QqVItgzwj235/uYZYgbXitB+dLupOk= -github.com/acomagu/bufpipe v1.0.3/go.mod h1:mxdxdup/WdsKVreO5GpW4+M/1CE2sMG4jeGJ2sYmHc4= +github.com/ProtonMail/go-crypto v0.0.0-20230217124315-7d5c6f04bbb8 h1:wPbRQzjjwFc0ih8puEVAOFGELsn1zoIIYdxvML7mDxA= +github.com/ProtonMail/go-crypto v0.0.0-20230217124315-7d5c6f04bbb8/go.mod h1:I0gYDMZ6Z5GRU7l58bNFSkPTFN6Yl12dsUlAZ8xy98g= +github.com/acomagu/bufpipe v1.0.4 h1:e3H4WUzM3npvo5uv95QuJM3cQspFNtFBzvJ2oNjKIDQ= +github.com/acomagu/bufpipe v1.0.4/go.mod h1:mxdxdup/WdsKVreO5GpW4+M/1CE2sMG4jeGJ2sYmHc4= +github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be h1:9AeTilPcZAjCFIImctFaOjnTIavg87rW78vTPkQqLI8= github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be/go.mod h1:ySMOLuWl6zY27l47sB3qLNK6tF2fkHG55UZxx8oIVo4= +github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio= github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs= github.com/bwesterb/go-ristretto v1.2.0/go.mod h1:fUIoIZaG73pV5biE2Blr2xEzDoMj7NFEuV9ekS419A0= -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/cloudflare/circl v1.1.0 h1:bZgT/A+cikZnKIwn7xL2OBj012Bmvho/o6RpRvv3GKY= github.com/cloudflare/circl v1.1.0/go.mod h1:prBCrKB9DV4poKZY1l9zBXg2QJY7mvgRvtMxxK7fi4I= +github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/emirpasic/gods v1.18.1 h1:FXtiHYKDGKCW2KzwZKx0iC0PQmdlorYgdFG9jPXJ1Bc= github.com/emirpasic/gods v1.18.1/go.mod h1:8tpGGwCnJ5H4r6BWwaV6OrWmMoPhUl5jm/FMNAnJvWQ= +github.com/gliderlabs/ssh v0.3.5 h1:OcaySEmAQJgyYcArR+gGGTHCyE7nvhEMTlYY+Dp8CpY= github.com/gliderlabs/ssh v0.3.5/go.mod h1:8XB4KraRrX39qHhT6yxPsHedjA08I/uBVwj4xC+/+z4= github.com/go-git/gcfg v1.5.0 h1:Q5ViNfGF8zFgyJWPqYwA7qGFoMTEiBmdlkcfRmpIMa4= github.com/go-git/gcfg v1.5.0/go.mod h1:5m20vg6GwYabIxaOonVkTdrILxQMpEShl1xiMF4ua+E= -github.com/go-git/go-billy/v5 v5.3.1 h1:CPiOUAzKtMRvolEKw+bG1PLRpT7D3LIs3/3ey4Aiu34= +github.com/go-git/go-billy v4.2.0+incompatible h1:Z6QtVXd5tjxUtcODLugkJg4WaZnGg13CD8qB9pr+7q0= +github.com/go-git/go-billy v4.2.0+incompatible/go.mod h1:hedUGslB3n31bx5SW9KMjV/t0CUKnrapjVG9fT7xKX4= github.com/go-git/go-billy/v5 v5.3.1/go.mod h1:pmpqyWchKfYfrkb/UVH4otLvyi/5gJlGI4Hb3ZqZ3W0= +github.com/go-git/go-billy/v5 v5.4.1 h1:Uwp5tDRkPr+l/TnbHOQzp+tmJfLceOlbVucgpTz8ix4= +github.com/go-git/go-billy/v5 v5.4.1/go.mod h1:vjbugF6Fz7JIflbVpl1hJsGjSHNltrSw45YK/ukIvQg= +github.com/go-git/go-git-fixtures/v4 v4.3.1 h1:y5z6dd3qi8Hl+stezc8p3JxDkoTRqMAlKnXHuzrfjTQ= github.com/go-git/go-git-fixtures/v4 v4.3.1/go.mod h1:8LHG1a3SRW71ettAD/jW13h8c6AqjVSeL11RAdgaqpo= -github.com/go-git/go-git/v5 v5.5.1 h1:5vtv2TB5PM/gPM+EvsHJ16hJh4uAkdGcKilcwY7FYwo= -github.com/go-git/go-git/v5 v5.5.1/go.mod h1:uz5PQ3d0gz7mSgzZhSJToM6ALPaKCdSnl58/Xb5hzr8= +github.com/go-git/go-git/v5 v5.6.1 h1:q4ZRqQl4pR/ZJHc1L5CFjGA1a10u76aV1iC+nh+bHsk= +github.com/go-git/go-git/v5 v5.6.1/go.mod h1:mvyoL6Unz0PiTQrGQfSfiLFhBH1c1e84ylC2MDs4ee8= +github.com/golang/mock v1.4.4 h1:l75CXGRSwbaYNpl/Z2X1XIIAMSCquvXgpVZDhwEIJsc= github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4= -github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/imdario/mergo v0.3.13 h1:lFzP57bqS/wsqKssCGmtLAb8A0wKjLGrve2q3PPVcBk= github.com/imdario/mergo v0.3.13/go.mod h1:4lJ1jqUDcsbIECGy0RUJAXNIhg+6ocWgb1ALK2O4oXg= @@ -39,15 +47,22 @@ github.com/jessevdk/go-flags v1.5.0/go.mod h1:Fw0T6WPc1dYxT4mKEZRfG5kJhaTDP9pj1c github.com/kevinburke/ssh_config v1.2.0 h1:x584FjTGwHzMwvHx18PXxbBVzfnxogHaAReU4gf13a4= github.com/kevinburke/ssh_config v1.2.0/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF4nAY/ojJ6r6mM= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= +github.com/kr/pretty v0.2.1 h1:Fmg33tUaq4/8ym9TJN1x7sLJnHVwhP33CNkpYV/7rwI= github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/pty v1.1.8/go.mod h1:O1sed60cT9XZ5uDucP5qwvh+TE3NnUj51EiZO/lmSfw= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= +github.com/matryer/is v1.2.0 h1:92UTHpy8CDwaJ08GqLDzhhuixiBUUD1p3AU6PHddz4A= github.com/matryer/is v1.2.0/go.mod h1:2fLPjFQM9rhQ15aVEtbuwhJinnOqrmgXPNdZsdwlWXA= +github.com/mmcloughlin/avo v0.5.0/go.mod h1:ChHFdoV7ql95Wi7vuq2YT1bwCJqiWdZrQ1im3VujLYM= github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= -github.com/pjbgf/sha1cd v0.2.3 h1:uKQP/7QOzNtKYH7UTohZLcjF5/55EnTw0jO/Ru4jZwI= -github.com/pjbgf/sha1cd v0.2.3/go.mod h1:HOK9QrgzdHpbc2Kzip0Q1yi3M2MFGPADtR6HjG65m5M= +github.com/pjbgf/sha1cd v0.3.0 h1:4D5XXmUUBUl/xQ6IjCkEAbqXskkq/4O7LmGn0AqMDs4= +github.com/pjbgf/sha1cd v0.3.0/go.mod h1:nZ1rrWOcGJ5uZgEEVL1VUM9iRQiZvWdbZjkKyFzPPsI= +github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/sergi/go-diff v1.1.0 h1:we8PVUC3FE2uYfodKH/nBHMSetSfHDR6scGdBi+erh0= github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM= @@ -57,33 +72,36 @@ github.com/skeema/knownhosts v1.1.0/go.mod h1:sKFq3RD6/TKZkSWn8boUbDC7Qkgcv+8XXi github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= +github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/subpop/go-ini v0.1.4 h1:+OVDOLyoQQCkk36v48bDcBscw2GCn9cesQc6PFLYdg8= -github.com/subpop/go-ini v0.1.4/go.mod h1:q0fhdlbGE3dI9dHPgUntXh1ggwR+SpfXL/kogOefaBE= github.com/xanzy/ssh-agent v0.3.3 h1:+/15pJfg/RsTxqYcX6fHqOXZwwMP+2VyYWJeWM2qQFM= github.com/xanzy/ssh-agent v0.3.3/go.mod h1:6dzNDKs0J9rVPHPhaGCukekBHKqfl+L3KghI1Bc68Uw= github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= +golang.org/x/arch v0.1.0/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20220525230936-793ad666bf5e/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.0.0-20220826181053-bd7e27e6170d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= -golang.org/x/crypto v0.0.0-20221012134737-56aed061732a h1:NmSIgad6KjE6VvHciPZuNRTKxGhlPfD6OA87W/PLkqg= -golang.org/x/crypto v0.0.0-20221012134737-56aed061732a/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= -golang.org/x/crypto v0.3.0 h1:a06MkbcxBrEFc0w0QIZWXrH/9cCX6KJyWbBOIwAn+7A= -golang.org/x/crypto v0.3.0/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4= +golang.org/x/crypto v0.1.0/go.mod h1:RecgLatLF4+eUMCP1PoPZQb+cVrJcOPbHkTkbkB9sbw= +golang.org/x/crypto v0.6.0 h1:qfktjS5LUO+fFKeJXZ+ikTRijMmljikvG68fpMMruSc= +golang.org/x/crypto v0.6.0/go.mod h1:OFC/31mSvZgRz0V1QTNCzfAI1aIRzbiufJtkMIlEp58= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= +golang.org/x/mod v0.6.0/go.mod h1:4mET923SAdbXp2ki8ey+zGs1SLqsuM2Y0uvdZR/fUNI= golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/net v0.0.0-20220826154423-83b083e8dc8b/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk= -golang.org/x/net v0.2.0 h1:sZfSu1wtKLGlWI4ZZayP0ck9Y73K1ynO6gqzTdBVdPU= -golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY= +golang.org/x/net v0.1.0/go.mod h1:Cx3nUiGt4eDBEyega/BKRp+/AlGL8hYe7U9odMt2Cco= +golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= +golang.org/x/net v0.7.0 h1:rJrUqqhjsgNp7KqAIc25s9pZnjU7TUcSY7HcVZjdn1g= +golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -97,25 +115,34 @@ golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220825204002-c680a09ffe64/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.2.0 h1:ljd4t30dBnAvMZaQCevtY0xLLD0A+bRZXbgLMLU1F/A= -golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.5.0 h1:MUK/U/4lj1t1oPg0HfuXDN/Z1wv31ZJ/YcPiGccS4DU= +golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.0.0-20220722155259-a9ba230a4035/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= -golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc= +golang.org/x/term v0.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= +golang.org/x/term v0.5.0 h1:n2a8QNdAb0sZNpU9R1ALUXBbY+w51fCQDN+7EdxNBsY= +golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= +golang.org/x/text v0.7.0 h1:4BRB4x83lYWy72KwLD/qYDuTu7q9PjSagHvijDw7cLo= +golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= +golang.org/x/tools v0.2.0/go.mod h1:y4OqIKeOV/fWJetJ8bXPU1sEVniLMIyDAZWeHdV+NTA= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA= gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= @@ -126,6 +153,8 @@ gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.0 h1:hjy8E9ON/egN1tAYqKb61G10WtihqetD4sz2H+8nIeA= gopkg.in/yaml.v3 v3.0.0/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4= sigs.k8s.io/yaml v1.3.0 h1:a2VclLzOGrwOHDiV8EfBGhvjHvP46CtW5j6POvhYGGo= sigs.k8s.io/yaml v1.3.0/go.mod h1:GeOyir5tyXNByN85N/dRIT9es5UQNerPYEKK56eTBm8= diff --git a/internal/admin/middleware.go b/internal/admin/middleware.go new file mode 100644 index 0000000..56d4797 --- /dev/null +++ b/internal/admin/middleware.go @@ -0,0 +1,15 @@ +package admin + +import ( + "log" + "net/http" +) + +// Admin middleware to handle requests to the admin repo. +func AdminHooks(adminSvc *Servicer, next http.Handler) http.Handler { + return http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) { + log.Printf("stuffs about to reload %s", "now") + next.ServeHTTP(rw, req) + go adminSvc.Reload() + }) +} diff --git a/internal/admin/model.go b/internal/admin/model.go index cf69fcd..5a7f984 100644 --- a/internal/admin/model.go +++ b/internal/admin/model.go @@ -10,9 +10,11 @@ import ( "os" "path/filepath" + "github.com/go-git/go-billy/v5/memfs" "github.com/go-git/go-billy/v5/osfs" "github.com/go-git/go-git/v5" "github.com/go-git/go-git/v5/storage/filesystem" + "github.com/go-git/go-git/v5/storage/memory" "gopkg.in/ini.v1" @@ -28,6 +30,8 @@ const ( Admin = 2 // GitExportMagic magic file name for daemon export GitExportMagic = "git-daemon-export-ok" + // GitWebExportMagic + GitWebExportMagic = "git-web-export-ok" ) // Action composite type for modes @@ -67,22 +71,65 @@ type ServerRepos struct { BasePath string `json:"basepath"` } -func loadServerConfig(configPath string) *ServerRepos { - file, err := os.Open(configPath) +func loadFromGit(gitUrl, filePath string) ([]byte, error) { + fs := memfs.New() + storer := memory.NewStorage() + _, err := git.Clone(storer, fs, &git.CloneOptions{ + URL: gitUrl, + }) if err != nil { - log.Fatalf("Failed to open gitserver config %s", err) + // log.error + fmt.Printf("coudln't clone mgmt repo %s", err) + return []byte(""), errors.New("coudln't clone mgmt repo") + } + file, err := fs.Open(filePath) + if err != nil { + fmt.Printf("Failed to open gitserver config %s", err) + return []byte(""), errors.New("coudln't open git config file from mgmt repo") } defer file.Close() - b, err := io.ReadAll(file) + return io.ReadAll(file) +} + +func loadLocalFile(path string) ([]byte, error) { + file, err := os.Open(path) + if err != nil { + log.Printf("config file not opened %s", path) + return []byte{}, err + } + configBytes, err := io.ReadAll(file) if err != nil { - log.Fatalf("Failed to read the gitserver config %s", err) + log.Print("config file not read") + return []byte{}, err + } + return configBytes, nil +} + +func loadServerConfig(mgmtRepo bool, baseDir, configPath string) (*ServerRepos, error) { + configBytes := []byte{} + var err error + if mgmtRepo { + repoURI := filepath.Join("file:///", baseDir, "mgmt.git") + configBytes, err = loadFromGit(repoURI, configPath) + if err != nil { + // log.error + log.Print("Failed to load config file from git") + return &ServerRepos{}, err + } + } else { + configBytes, err = loadLocalFile(filepath.Join(baseDir, configPath)) + if err != nil { + // log.error + log.Print("Failed to load config file from git") + return &ServerRepos{}, err + } } config := &ServerRepos{} - err = yaml.Unmarshal(b, &config) + err = yaml.Unmarshal(configBytes, &config) if err != nil { - log.Fatalf("Failed to parse gitserver config %s", err) + return &ServerRepos{}, errors.New("Could not parse gitserver config") } - return config + return config, nil } // ServerPolicies generate casbin policies @@ -108,19 +155,20 @@ func readOnlyPaths(role, repoName string) [][]string { } } func writePaths(role, repoName string) [][]string { - return [][]string{[]string{role, fmt.Sprintf("/%s/git-recieve-pack", repoName), "POST"}} + return [][]string{[]string{role, fmt.Sprintf("/%s/git-receive-pack", repoName), "POST"}} } // Policy generate policy for repo base on mode func (p *Permission) Policy(repoName string) [][]string { policies := [][]string{} // if read mode or greater e.g. write mode + roleName := fmt.Sprintf("role:%s", p.Role) if p.Mode >= Read { - policies = append(policies, readOnlyPaths(p.Role, repoName)...) + policies = append(policies, readOnlyPaths(roleName, repoName)...) } // if write mode if p.Mode >= Write { - policies = append(policies, writePaths(p.Role, repoName)...) + policies = append(policies, writePaths(roleName, repoName)...) } return policies } @@ -145,8 +193,18 @@ func (r *GitRepo) ReconcileRepo(basePath string) { strg := filesystem.NewStorage(fs, nil) _, _ = git.Init(strg, nil) } + // set export file for git-http-backend + okExport := filepath.Join(repoBase, GitExportMagic) + _, err = os.Stat(okExport) + if errors.Is(err, fs.ErrNotExist) { + // Create web export + f, err := os.Create(okExport) + f.Close() + if err != nil { + log.Fatalf("%s coudln't be created %s", GitExportMagic, err) + } + } r.ConfigureExport(repoBase) - if r.GitWebConfig == nil { r.GitWebConfig = &GitWeb{} } @@ -155,24 +213,10 @@ func (r *GitRepo) ReconcileRepo(basePath string) { // ConfigureExport setup repo for sharing and configure web settings func (r *GitRepo) ConfigureExport(repoBase string) { - // do nothing on public repos - okExport := filepath.Join(repoBase, GitExportMagic) - _, err := os.Stat(okExport) - // Not public but the export setting is setting exists - if !r.Public && err == nil { - // delete file - os.Remove(okExport) - return - } - // Not public and the file doesn't exist - if !r.Public && errors.Is(err, fs.ErrNotExist) { - return - } - // - f, err := os.Create(okExport) - defer f.Close() + okExport := filepath.Join(repoBase, GitWebExportMagic) + _, err := os.Create(okExport) if err != nil { - log.Fatalf("git-daemon-export-ok coudln't be created %s", err) + log.Fatalf("%s coudln't be created %s", GitWebExportMagic, err) } } diff --git a/internal/admin/model_test.go b/internal/admin/model_test.go index 79e3cb5..7f816f5 100644 --- a/internal/admin/model_test.go +++ b/internal/admin/model_test.go @@ -1,8 +1,10 @@ package admin import ( + "bytes" "errors" "fmt" + "io" "io/fs" "io/ioutil" "os" @@ -10,18 +12,21 @@ import ( "strings" "testing" + "github.com/go-git/go-billy/v5/osfs" + "github.com/go-git/go-git/v5" + "github.com/go-git/go-git/v5/storage/filesystem" "gopkg.in/ini.v1" ) func TestCasbinPolicies(t *testing.T) { - roleName := "mr:role" + roleName := "myrole" repoName := "myrepo" pRO := &Permission{ Role: roleName, Mode: 0, } pW := &Permission{ - Role: "my:admin", + Role: "admin", Mode: 1, } @@ -50,7 +55,7 @@ func TestCasbinPolicies(t *testing.T) { if wPolicies[0][0] != roleName { t.Fatal("Role name doesn't match") } - if wPolicies[0][1] != fmt.Sprintf("/%s/git-recieve-pack", repoName) { + if wPolicies[0][1] != fmt.Sprintf("/%s/git-receive-pack", repoName) { t.Fatal("Policy missing write path") } }) @@ -76,6 +81,128 @@ func TestCasbinPolicies(t *testing.T) { }) } +func TestLoadServerConfig(t *testing.T) { + t.Run("testing server config from file", func(t *testing.T) { + localDir := t.TempDir() + // TODO Refactor next touch + localFile := filepath.Join(localDir, "stuff.yaml") + srcFile, err := os.Open("../../gitserver.yaml") + if err != nil { + t.Fatalf("Error opening base config %s", err) + } + defer srcFile.Close() + + // dest + destFile, err := os.OpenFile(localFile, os.O_RDWR|os.O_CREATE, 0755) + if err != nil { + t.Fatalf("failed to open destination in git repo %s", err) + } + defer destFile.Close() + + // copy + if _, err := io.Copy(destFile, srcFile); err != nil { + t.Fatalf("Error copying file %s", err) + } + + // end copy file + loadedFile, err := loadServerConfig(false, localDir, "stuff.yaml") + if err != nil { + t.Fatal(err) + } + if len(loadedFile.Repos) != 2 { + t.Fatalf("expected to find 2 repos found %d", len(loadedFile.Repos)) + } + }) + + t.Run("testing server config from git", func(t *testing.T) { + + }) +} + +func TestLocalFile(t *testing.T) { + localDir := t.TempDir() + localFile := filepath.Join(localDir, "stuff.yaml") + os.WriteFile(localFile, []byte("stuff"), 0750) + loadedFile, err := loadLocalFile(localFile) + if err != nil { + t.Fatal(err) + } + + if !bytes.Contains(loadedFile, []byte("stuff")) { + t.Fatal("failed to find expected contents in localfile") + } + _, err = loadLocalFile("dne.txt") + if err == nil { + t.Fatal("Expected to find and error and didn't") + } +} + +func TestMgmtGitConfig(t *testing.T) { + // setup tempdir + gitDir := t.TempDir() + // init git repo + gitFs := osfs.New(gitDir) + strg := filesystem.NewStorage(gitFs, nil) + repo, _ := git.Init(strg, gitFs) + // add file + + // src + srcFile, err := os.Open("../../gitserver.yaml") + if err != nil { + t.Fatalf("Error opening base config %s", err) + } + defer srcFile.Close() + + // file name + // fileToCommit := fs.Join(gitDir, "gitserver.yaml") + fileToCommit := "gitserver.yaml" + + // dest + destFile, err := gitFs.OpenFile(fileToCommit, os.O_RDWR|os.O_CREATE, 0755) + if err != nil { + t.Fatalf("failed to open destination in git repo %s", err) + } + defer destFile.Close() + + // copy + if _, err := io.Copy(destFile, srcFile); err != nil { + t.Fatalf("Error copying file %s", err) + } + // commit + wt, err := repo.Worktree() + if err != nil { + t.Fatal(err) + } + wt.Add(fileToCommit) + _, err = wt.Commit(fileToCommit, &git.CommitOptions{}) + if err != nil { + t.Fatalf("Error creating commit %s", err) + } + + // run load func + content, err := loadFromGit(gitDir, "gitserver.yaml") + if err != nil { + t.Fatal(err) + } + + // "go-git-server" + if !bytes.Contains(content, []byte("go-git-server")) { + t.Fatal("config missing expected") + } + + // check couldnt clone err + _, err = loadFromGit("/dne/bar", "gitserver.yaml") + if err == nil { + t.Fatal("expected an cloning repo didn't find one") + } + // check couldnt open file err + _, err = loadFromGit(gitDir, "dne.yaml") + if err == nil { + t.Fatal("expected an error opening config file didn't find one") + } + // TODO run via serverLoadConfig +} + func TestConfigReconcile(t *testing.T) { tempDir := t.TempDir() defer os.RemoveAll(tempDir) @@ -89,8 +216,7 @@ func TestConfigReconcile(t *testing.T) { } f.Close() repo := &GitRepo{ - Public: true, - Name: "testrepo", + Name: "testrepo", } t.Run("test add gitweb section and remove it", func(t *testing.T) { // make "fake" repo @@ -126,7 +252,7 @@ func TestConfigReconcile(t *testing.T) { } }) t.Run("test magic export file is created", func(t *testing.T) { - exportPath := filepath.Join(testRepo, GitExportMagic) + exportPath := filepath.Join(testRepo, GitWebExportMagic) repo.ConfigureExport(testRepo) _, err := os.Stat(exportPath) if errors.Is(err, fs.ErrNotExist) { @@ -135,13 +261,6 @@ func TestConfigReconcile(t *testing.T) { if err != nil { t.Fatalf("encountered an error %s", err) } - // copy repo - pvtRepo := repo - pvtRepo.Public = false - pvtRepo.ConfigureExport(testRepo) - if _, err := os.Stat(exportPath); err == nil { - t.Fatal("expected export file exist, but does not exist") - } }) } @@ -150,8 +269,7 @@ func TestRepoReconcile(t *testing.T) { print(tempDir) // defer os.RemoveAll(tempDir) repo := &GitRepo{ - Public: true, - Name: "testrepo", + Name: "TestMeRepo", GitWebConfig: &GitWeb{ "owner", "description", @@ -181,23 +299,9 @@ bare = true if !strings.Contains(string(content), "description") { t.Fatal("expected to find 'description' in config, didn't found", string(content)) } - gitExportMagicPath := filepath.Join(tempDir, fmt.Sprintf("%s.git", repo.Name), GitExportMagic) + gitExportMagicPath := filepath.Join(tempDir, fmt.Sprintf("%s.git", repo.Name), GitWebExportMagic) if _, err := os.Stat(gitExportMagicPath); errors.Is(err, fs.ErrNotExist) { t.Fatal("expected git export magic to be created, but does not exist") } - // Test that repo is switched back to private - repo.Public = false - // re-write the base config to repo - ioutil.WriteFile(tempConfigFile, defaultFile, 0644) - // re-reconcile - repo.ReconcileRepo(tempDir) - // check if description is *NOT* in the file - if !strings.Contains(string(content), "description") { - t.Fatal("expected to *NOT* find 'description' in config, didn't found", string(content)) - } - // make sure export is removed - if _, err := os.Stat(gitExportMagicPath); !errors.Is(err, fs.ErrNotExist) { - t.Fatal("expected git export magic to not exist, but *does* exist") - } } diff --git a/internal/admin/service.go b/internal/admin/service.go index 80056b7..84547fa 100644 --- a/internal/admin/service.go +++ b/internal/admin/service.go @@ -9,28 +9,72 @@ import ( // Servicer container for dependencies and functions type Servicer struct { *casbin.SyncedEnforcer - Conf *ServerRepos + Conf *ServerRepos + serverConfigPath string + reposDir string + mgmtRepo bool +} + +// Reload reoload server config and sync policies +func (s *Servicer) Reload() { + tmpConfig, err := loadServerConfig(s.mgmtRepo, s.reposDir, s.serverConfigPath) + if err != nil { + // log.error + log.Printf("failed to load config %s", err) + log.Print("refusing to reload config") + return + } + s.Conf = tmpConfig + s.InitServer() } // InitServer initialize a git server and configure func (s *Servicer) InitServer() { policies := s.Conf.ServerPolicies() - s.AddPolicies(policies) - s.SavePolicy() - s.LoadPolicy() + log.Print("policies generated") + numAdded := 0 + for _, policy := range policies { + added, err := s.AddPolicy(policy[0], policy[1], policy[2]) + if err != nil { + // log.error + log.Printf("error adding policy %s %s %s error %s", policy[0], policy[1], policy[2], err) + continue + } + if added { + numAdded += 1 + } + } + log.Printf("policies added %d", numAdded) + if err := s.SavePolicy(); err != nil { + log.Print("couldn't save policy") + } + log.Printf("policies saved") + if err := s.LoadPolicy(); err != nil { + log.Print("cloudn't load policy") + } + log.Print("policies loaded") s.Conf.ConfigureRepos() + log.Print("configured repos") } // NewService create a new admin service, load config, and generate policies -func NewService(modelPath, policyPath, serverConfigPath string) *Servicer { +func NewService(modelPath, policyPath, serverConfigPath, reposDir string, mgmtRepo bool) *Servicer { enf, err := casbin.NewSyncedEnforcer(modelPath, policyPath) if err != nil { log.Fatalf("Couldn't load the enforcer encountered the following error: %s", err) } - conf := loadServerConfig(serverConfigPath) + + conf, err := loadServerConfig(mgmtRepo, reposDir, serverConfigPath) + if err != nil { + // log.error + log.Fatalf("Coudln't load server config %s", err) + } svc := &Servicer{ enf, conf, + serverConfigPath, + reposDir, + mgmtRepo, } svc.InitServer() return svc diff --git a/internal/admin/service_test.go b/internal/admin/service_test.go new file mode 100644 index 0000000..fdd3aa6 --- /dev/null +++ b/internal/admin/service_test.go @@ -0,0 +1,132 @@ +package admin + +import ( + "io" + "log" + "os" + "path/filepath" + "strings" + "testing" +) + +var ( + updatedServerConfig []byte = []byte(` +--- +name: "go-git-server" +version: "v1alpha1" +basepath: ./repos +repos: + - name: mgmt + permissions: + - role: admin + mode: 1 + - name: testmerepo + git_web_config: + owner: grumps + description: >- + A wrapper to git http-backend providing authentcation and authorization + inspired by gitolite. + permissions: + - role: maintainers + mode: 1 + - name: thisismynewrepo + git_web_config: + owner: grumps + description: >- + A wrapper to git http-backend providing authentcation and authorization + inspired by gitolite. + permissions: + - role: maintainers + mode: 1 +`) +) + +func copyFile(t *testing.T, srcFilePath, destPath string) { + + srcFile, err := os.Open(srcFilePath) + if err != nil { + t.Fatalf("Error opening base config %s", err) + } + defer srcFile.Close() + + // dest + destFile, err := os.OpenFile(destPath, os.O_RDWR|os.O_CREATE, 0755) + if err != nil { + t.Fatalf("failed to open destination in git repo %s", err) + } + defer destFile.Close() + + // copy + if _, err := io.Copy(destFile, srcFile); err != nil { + t.Fatalf("Error copying file %s", err) + } +} + +func TestInitServer(t *testing.T) { + tempDir := t.TempDir() + tempRepoDir := t.TempDir() + + // auth model + destModelFile := filepath.Join(tempDir, "auth_model.ini") + srcModelFile := "../../auth_model.ini" + copyFile(t, srcModelFile, destModelFile) + // end auth model + + // policy + destPolicyFile := filepath.Join(tempDir, "testpolicy.csv") + srcPolicyFile := "../../testpolicy.csv" + copyFile(t, srcPolicyFile, destPolicyFile) + // end policy + + // config + destConfigFile := filepath.Join(tempRepoDir, "gitserver.yaml") + srcConfigFile := "../../gitserver.yaml" + copyFile(t, srcConfigFile, destConfigFile) + // end config + + t.Run("test reload config success", func(t *testing.T) { + svc := NewService(destModelFile, + destPolicyFile, + "gitserver.yaml", + tempRepoDir, + false) + err := os.WriteFile(destConfigFile, updatedServerConfig, 0755) + if err != nil { + t.Fatal(err) + } + // stuff + svc.Reload() + // check policy file to make sure it was saved + data, err := os.ReadFile(destPolicyFile) + if err != nil { + t.Fatal(err) + } + if !strings.Contains(string(data), "thisismynewrepo") { + t.Fatal("expected to find test new repo but didn't") + } + + }) + t.Run("test reload config err", func(t *testing.T) { + svc := NewService(destModelFile, + destPolicyFile, + "gitserver.yaml", + tempRepoDir, + false) + notAGoodConfig := []byte("this is not valid yaml") + err := os.WriteFile(destConfigFile, notAGoodConfig, 0755) + if err != nil { + t.Fatal(err) + } + // stuff + svc.Reload() + // check policy file to make sure it wasn't saved + data, err := os.ReadFile(destPolicyFile) + if err != nil { + log.Fatal(err) + } + if !strings.Contains(string(data), "mgmt") { + log.Fatal("expected to mgmt repo but didn't in policy") + } + + }) +} diff --git a/internal/authz/middleware.go b/internal/authz/middleware.go index f01f262..a35b6b4 100644 --- a/internal/authz/middleware.go +++ b/internal/authz/middleware.go @@ -43,7 +43,7 @@ func Authentication(authMap TokenMap, next http.Handler) http.Handler { func Authorization(adminSvc *admin.Servicer, next http.Handler) http.Handler { return http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) { ctx := req.Context() - urn := ctx.Value("urn") + urn := ctx.Value("urn").(string) repo := req.URL.Path action := req.Method ok, err := adminSvc.Enforce(urn, repo, action) diff --git a/internal/authz/middleware_test.go b/internal/authz/middleware_test.go index 5795b3f..cc3f6d1 100644 --- a/internal/authz/middleware_test.go +++ b/internal/authz/middleware_test.go @@ -8,7 +8,6 @@ import ( "testing" "git.ofmax.li/go-git-server/internal/admin" - "github.com/casbin/casbin/v2" ) func junkTestHandler() http.HandlerFunc { @@ -85,10 +84,6 @@ func TestAuthentication(t *testing.T) { func TestAuthorization(t *testing.T) { t.Log("Starting authorization tests") baseURL := "http://test" - enf, err := casbin.NewSyncedEnforcer("../../auth_model.ini", "../../testpolicy.csv") - if err != nil { - t.Fatalf("Failed to load policies\n%s", err) - } cases := []struct { url string user string @@ -108,10 +103,12 @@ func TestAuthorization(t *testing.T) { description: "an unautorized action should yield a 403", }, } - svcr := &admin.Servicer{ - enf, - &admin.ServerRepos{}, - } + svcr := admin.NewService( + "../../auth_model.ini", + "../../testpolicy.csv", + "../../gitserver.yaml", + "../../repos", + false) for _, tc := range cases { t.Logf("test case: %s", tc.description) authHandler := Authorization(svcr, junkTestHandler()) @@ -1,13 +1,10 @@ -p, role:admin, config, admin -p, role:maintainers, /go-git-server/git-upload-pack, POST -p, role:maintainers, /go-git-server/info/refs, GET -p, maintainers, /go-git-server/info/refs, GET -p, maintainers, /go-git-server/git-upload-pack, POST -p, maintainers, /go-git-server/git-recieve-pack, POST -p, maintainers, /testmerepo/info/refs, GET -p, maintainers, /testmerepo/git-upload-pack, POST -p, maintainers, /testmerepo/git-recieve-pack, POST +p, role:admin, /mgmt/info/refs, GET +p, role:admin, /mgmt/git-upload-pack, POST +p, role:admin, /mgmt/git-receive-pack, POST +p, role:maintainers, /testmerepo/info/refs, GET +p, role:maintainers, /testmerepo/git-upload-pack, POST +p, role:maintainers, /testmerepo/git-receive-pack, POST g, role:admin, role:maintainers -g, admin, role:admin +g, uid:admin, role:admin g, uid:grumps, role:maintainers -g, aid:argo, role:bots
\ No newline at end of file +g, aid:argo, role:bots diff --git a/testpolicy.csv b/testpolicy.csv index 3420ec1..686784c 100644 --- a/testpolicy.csv +++ b/testpolicy.csv @@ -1,3 +1,8 @@ -p,role:test,/repo/url,GET - -g,uid:jack,role:test +p, role:test, /repo/url, GET +p, role:admin, /mgmt/info/refs, GET +p, role:admin, /mgmt/git-upload-pack, POST +p, role:admin, /mgmt/git-receive-pack, POST +p, role:maintainers, /testmerepo/info/refs, GET +p, role:maintainers, /testmerepo/git-upload-pack, POST +p, role:maintainers, /testmerepo/git-receive-pack, POST +g, uid:jack, role:test
\ No newline at end of file |