diff --git a/BigFiles/go.mod b/BigFiles/go.mod index d38866c..206e93f 100644 --- a/BigFiles/go.mod +++ b/BigFiles/go.mod @@ -6,3 +6,5 @@ require ( github.com/akrylysov/algnhsa v0.12.1 github.com/metalogical/BigFiles v0.0.0-20201031233020-8aa76a58aba7 ) + +replace github.com/metalogical/BigFiles => github.com/opensourceways/BigFiles v0.0.0-20240913072808-2dbf11feb204 diff --git a/BigFiles/go.sum b/BigFiles/go.sum index 625c4fd..fe8be23 100644 --- a/BigFiles/go.sum +++ b/BigFiles/go.sum @@ -3,24 +3,27 @@ github.com/akrylysov/algnhsa v0.12.1/go.mod h1:xAcJ/X8DV+81e+dUjIoB/r5CbISrSXV9/ github.com/aws/aws-lambda-go v1.9.0 h1:r9TWtk8ozLYdMW+aelUeWny8z2mjghJCMx6/uUwOLNo= github.com/aws/aws-lambda-go v1.9.0/go.mod h1:zUsUQhAUjYzR8AuduJPCfhBuKWUaDbQiPOG+ouzmE1A= 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/go-chi/chi v4.1.2+incompatible h1:fGFk2Gmi/YKXk0OmGfBh0WgmN3XB8lVnEyNz34tQRec= github.com/go-chi/chi v4.1.2+incompatible/go.mod h1:eB3wogJHnLi3x/kFX2A+IbTBlXxmMeXJVKy9tTv1XzQ= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/uuid v1.1.1 h1:Gkbcsh/GbpXz7lPftLA3P6TYMwjCLYm83jiFQZF/3gY= github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1 h1:EGx4pi6eqNxGaHF6qqu48+N2wcFQ5qg5FXgOdqsJ5d8= github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= github.com/json-iterator/go v1.1.10 h1:Kz6Cvnvv2wGdaG/V8yMvfkmNiXq9Ya2KUv4rouJJr68= github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= +github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo= github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= github.com/klauspost/cpuid v1.2.3/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek= github.com/klauspost/cpuid v1.3.1 h1:5JNjFYYQrZeKRJ0734q51WCEEn2huer72Dc7K+R/b6s= github.com/klauspost/cpuid v1.3.1/go.mod h1:bYW4mA6ZgKPob1/Dlai2LviZJO7KGI3uoWLd42rAQw4= +github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= -github.com/metalogical/BigFiles v0.0.0-20201031233020-8aa76a58aba7 h1:eSaJmOvPgVx/ARHCkfmldLvAlHZu1F94ttYGN+bxQ1k= -github.com/metalogical/BigFiles v0.0.0-20201031233020-8aa76a58aba7/go.mod h1:uTTfhdJzUTvmZEVl3RqOblXiZqlWS2imssQl2EEJabU= github.com/minio/md5-simd v1.1.0 h1:QPfiOqlZH+Cj9teu0t9b1nTBfPbyTl16Of5MeuShdK4= github.com/minio/md5-simd v1.1.0/go.mod h1:XpBqgZULrMYD3R+M28PcmP0CkI7PEMzB3U77ZrKZ0Gw= github.com/minio/minio-go/v7 v7.0.5 h1:I2NIJ2ojwJqD/YByemC1M59e1b4FW9kS7NlOar7HPV4= @@ -35,13 +38,19 @@ github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJ github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/modern-go/reflect2 v1.0.1 h1:9f412s+6RmYXLWZSEzVVgPGK7C2PphHj5RJrvfx9AWI= github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= +github.com/opensourceways/BigFiles v0.0.0-20240913072808-2dbf11feb204 h1:dxbM1M8kSEG+1LRn1Rx4wgH+s1eOCDpG/OpVEbkvsUc= +github.com/opensourceways/BigFiles v0.0.0-20240913072808-2dbf11feb204/go.mod h1:uTTfhdJzUTvmZEVl3RqOblXiZqlWS2imssQl2EEJabU= +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/rs/xid v1.2.1 h1:mhH9Nq+C1fY2l1XIpgxIiUOfNpRBYH1kKcr+qfKgjRc= github.com/rs/xid v1.2.1/go.mod h1:+uKXf+4Djp6Md1KODXJxgGQPKngRmWyn10oCKFzNHOQ= +github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d h1:zE9ykElWQ6/NYmHa3jpm/yHnI4xSofP+UP6SpjHcSeM= github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= +github.com/smartystreets/goconvey v0.0.0-20190330032615-68dc04aab96a h1:pa8hGb/2YqsZKovtsgrwcDH1RZhVbTKCjLp47XpqCDs= github.com/smartystreets/goconvey v0.0.0-20190330032615-68dc04aab96a/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= @@ -62,8 +71,10 @@ golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/ini.v1 v1.57.0 h1:9unxIsFcTt4I55uWluz+UmL95q4kdJ0buvQ1ZIqVQww= gopkg.in/ini.v1 v1.57.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10= gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= diff --git a/README.md b/README.md index 3e7a4fa..7ab4a72 100644 --- a/README.md +++ b/README.md @@ -8,10 +8,11 @@ v2.12.0](https://github.com/git-lfs/git-lfs/tree/v2.12.0/docs/api) server. **TODO**: -1. 外部参数校验:username、password进行格式校验。 +1. 外部参数校验:username、password、repo_id等进行格式校验。 2. 添加配置文件,将AKSK等写入配置文件中。可参考merlin-server配置文件的格式与读取方式。 3. 添加测试用例。 -4. 认证方式支持token。 -5. 认证时校验用户在仓库内权限。 +4. ~~认证方式支持token。~~ +5. ~~认证时校验用户在仓库内权限。~~ 6. 支持ssh。 -7. 仓库添加github action。 +7. ~~仓库添加github action。~~ +8. 添加日志。 diff --git a/auth/client.go b/auth/client.go new file mode 100644 index 0000000..a2a4d68 --- /dev/null +++ b/auth/client.go @@ -0,0 +1,45 @@ +package auth + +import ( + "encoding/json" + "errors" + "fmt" + "io" + "net/http" + "strings" +) + +type Client struct { +} + +// getParsedResponse gets response data from gitee +func getParsedResponse(method, path string, header http.Header, body io.Reader, obj interface{}) error { + req, err := http.NewRequest(method, path, body) + if err != nil { + panic(err) + } + req.Header = header + fmt.Println(strings.Split(path, "?")[0]) + response, err := http.DefaultClient.Do(req) + if err != nil { + panic(err) + } + defer response.Body.Close() + if response.StatusCode/100 != 2 { + if response.StatusCode == http.StatusNotFound { + return errors.New("repository not found") + } else if response.StatusCode == http.StatusUnauthorized { + return errors.New("unauthorized") + } + return errors.New("error occurred accessing gitee") + } + data, err := io.ReadAll(response.Body) + if err != nil { + panic(err) + } + err = json.Unmarshal(data, &obj) + if err != nil { + panic(err) + } + return nil +} diff --git a/auth/gitee.go b/auth/gitee.go index d695e63..ab7784d 100644 --- a/auth/gitee.go +++ b/auth/gitee.go @@ -1,41 +1,105 @@ package auth import ( - "encoding/json" "errors" - "io" + "fmt" "net/http" "net/url" "os" "strings" ) +var ( + allowedRepos = []string{"openeuler", "src-openeuler"} + uploadPermissions = []string{"admin", "developer"} + downloadPermissions = []string{"admin", "developer", "read"} +) + type giteeUser struct { - Login string `json:"login"` - // Permission string `json:"permission"` + Login string `json:"login"` + Permission string `json:"permission"` +} + +type UserInRepo struct { + Repo string + Owner string + Token string + Username string + Password string + Operation string +} + +type parent struct { + Fullname string `json:"full_name"` +} + +type Repo struct { + Parent parent `json:"parent"` + Fullname string `json:"full_name"` } type AccessToken struct { Token string `json:"access_token"` } -func GiteeAuth() func(string, string) error { - return func(username, password string) error { - token, err := getToken(username, password) - if err != nil { +func GiteeAuth() func(UserInRepo) error { + return func(userInRepo UserInRepo) error { + if userInRepo.Password != "" { + token, err := getToken(userInRepo.Username, userInRepo.Password) + if err != nil { + userInRepo.Token = userInRepo.Password + } else { + userInRepo.Token = token + } + } + + if err := CheckRepoOwner(userInRepo); err != nil { return err } - return verifyUser(username, token) + return verifyUser(userInRepo) } } +// CheckRepoOwner checks whether the owner of a repo is allowed to use lfs server +func CheckRepoOwner(userInRepo UserInRepo) error { + path := fmt.Sprintf( + "https://gitee.com/api/v5/repos/%s/%s", + userInRepo.Owner, + userInRepo.Repo, + ) + if userInRepo.Token != "" { + path += fmt.Sprintf("?access_token=%s", userInRepo.Token) + } + headers := http.Header{"Content-Type": []string{"application/json;charset=UTF-8"}} + repo := new(Repo) + err := getParsedResponse("GET", path, headers, nil, &repo) + if err != nil { + return err + } + for _, allowedRepo := range allowedRepos { + if strings.Split(repo.Fullname, "/")[0] == allowedRepo { + return nil + } + } + + if repo.Parent.Fullname != "" { + for _, allowedRepo := range allowedRepos { + if strings.Split(repo.Parent.Fullname, "/")[0] == allowedRepo { + return nil + } + } + } + + return errors.New("your repository does not appear to have permission to use this lfs service") +} + // getToken gets access_token by username and password func getToken(username, password string) (string, error) { clientId := os.Getenv("CLIENT_ID") clientSecret := os.Getenv("CLIENT_SECRET") form := url.Values{} - form.Add("scope", "user_info") + form.Add("scope", "user_info projects") form.Add("grant_type", "password") form.Add("username", username) form.Add("password", password) @@ -43,56 +107,52 @@ func getToken(username, password string) (string, error) { form.Add("client_secret", clientSecret) path := "https://gitee.com/oauth/token" - response, err := http.Post(path, "application/x-www-form-urlencoded", strings.NewReader(form.Encode())) + headers := http.Header{"Content-Type": []string{"application/x-www-form-urlencoded"}} + accessToken := new(AccessToken) + err := getParsedResponse("POST", path, headers, strings.NewReader(form.Encode()), &accessToken) if err != nil { - panic(err) + return "", err } - defer response.Body.Close() - if response.StatusCode != http.StatusOK { - return "", errors.New("invalid credentials") - } - body, err := io.ReadAll(response.Body) - if err != nil { - panic(err) - } - var accessToken AccessToken - err = json.Unmarshal(body, &accessToken) - if err != nil { - panic(err) - } return accessToken.Token, nil } -// verifyUser verifies user info by access_token -func verifyUser(username, token string) error { - path := "https://gitee.com/api/v5/user?access_token=" + token - req, err := http.NewRequest("GET", path, nil) - if err != nil { - panic(err) - } - req.Header.Set("Content-Type", "application/json;charset=UTF-8") - - response, err := http.DefaultClient.Do(req) - if err != nil { - panic(err) - } - defer response.Body.Close() - if response.StatusCode != http.StatusOK { - return errors.New("invalid credentials") +// verifyUser verifies user permission in repo by access_token and operation +func verifyUser(userInRepo UserInRepo) error { + path := fmt.Sprintf( + "https://gitee.com/api/v5/repos/%s/%s/collaborators/%s/permission", + userInRepo.Owner, + userInRepo.Repo, + userInRepo.Username, + ) + if userInRepo.Token != "" { + path += fmt.Sprintf("?access_token=%s", userInRepo.Token) } - body, err := io.ReadAll(response.Body) + headers := http.Header{"Content-Type": []string{"application/json;charset=UTF-8"}} + giteeUser := new(giteeUser) + err := getParsedResponse("GET", path, headers, nil, &giteeUser) if err != nil { - panic(err) + return err } - var giteeUser giteeUser - err = json.Unmarshal(body, &giteeUser) - if err != nil { - panic(err) + + if giteeUser.Login != userInRepo.Username { + return errors.New("username does not match") } - if giteeUser.Login == username { - return nil + if userInRepo.Operation == "upload" { + for _, v := range uploadPermissions { + if giteeUser.Permission == v { + return nil + } + } + return errors.New("user has no permission uploading to the repository") + } else if userInRepo.Operation == "download" { + for _, v := range downloadPermissions { + if giteeUser.Permission == v { + return nil + } + } + return errors.New("user has no permission downloading in the repository") } else { - return errors.New("username does not match") + return errors.New("unknow operation") } } diff --git a/go.sum b/go.sum index 9177364..48fca58 100644 --- a/go.sum +++ b/go.sum @@ -1,25 +1,27 @@ 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/go-chi/chi v1.0.0 h1:s/kv1cTXfivYjdKJdyUzNGyAWZ/2t7duW1gKn5ivu+c= github.com/go-chi/chi v4.1.2+incompatible h1:fGFk2Gmi/YKXk0OmGfBh0WgmN3XB8lVnEyNz34tQRec= github.com/go-chi/chi v4.1.2+incompatible/go.mod h1:eB3wogJHnLi3x/kFX2A+IbTBlXxmMeXJVKy9tTv1XzQ= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/uuid v1.1.1 h1:Gkbcsh/GbpXz7lPftLA3P6TYMwjCLYm83jiFQZF/3gY= github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1 h1:EGx4pi6eqNxGaHF6qqu48+N2wcFQ5qg5FXgOdqsJ5d8= github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= github.com/json-iterator/go v1.1.10 h1:Kz6Cvnvv2wGdaG/V8yMvfkmNiXq9Ya2KUv4rouJJr68= github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= +github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo= github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= github.com/klauspost/cpuid v1.2.3/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek= github.com/klauspost/cpuid v1.3.1 h1:5JNjFYYQrZeKRJ0734q51WCEEn2huer72Dc7K+R/b6s= github.com/klauspost/cpuid v1.3.1/go.mod h1:bYW4mA6ZgKPob1/Dlai2LviZJO7KGI3uoWLd42rAQw4= +github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/minio/md5-simd v1.1.0 h1:QPfiOqlZH+Cj9teu0t9b1nTBfPbyTl16Of5MeuShdK4= github.com/minio/md5-simd v1.1.0/go.mod h1:XpBqgZULrMYD3R+M28PcmP0CkI7PEMzB3U77ZrKZ0Gw= -github.com/minio/minio-go v1.0.0 h1:ooSujki+Z1PRGZsYffJw5jnF5eMBvzMVV86TLAlM0UM= -github.com/minio/minio-go v6.0.14+incompatible h1:fnV+GD28LeqdN6vT2XdGKW8Qe/IfjJDswNVuni6km9o= github.com/minio/minio-go/v7 v7.0.5 h1:I2NIJ2ojwJqD/YByemC1M59e1b4FW9kS7NlOar7HPV4= github.com/minio/minio-go/v7 v7.0.5/go.mod h1:TA0CQCjJZHM5SJj9IjqR0NmpmQJ6bCbXifAJ3mUU6Hw= github.com/minio/sha256-simd v0.1.1 h1:5QHSlgo3nt5yKOJrC7W8w7X+NFl8cMPZm96iu8kKUJU= @@ -32,13 +34,17 @@ github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJ github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/modern-go/reflect2 v1.0.1 h1:9f412s+6RmYXLWZSEzVVgPGK7C2PphHj5RJrvfx9AWI= github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= +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/rs/xid v1.2.1 h1:mhH9Nq+C1fY2l1XIpgxIiUOfNpRBYH1kKcr+qfKgjRc= github.com/rs/xid v1.2.1/go.mod h1:+uKXf+4Djp6Md1KODXJxgGQPKngRmWyn10oCKFzNHOQ= +github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d h1:zE9ykElWQ6/NYmHa3jpm/yHnI4xSofP+UP6SpjHcSeM= github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= +github.com/smartystreets/goconvey v0.0.0-20190330032615-68dc04aab96a h1:pa8hGb/2YqsZKovtsgrwcDH1RZhVbTKCjLp47XpqCDs= github.com/smartystreets/goconvey v0.0.0-20190330032615-68dc04aab96a/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= @@ -59,8 +65,10 @@ golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/ini.v1 v1.57.0 h1:9unxIsFcTt4I55uWluz+UmL95q4kdJ0buvQ1ZIqVQww= gopkg.in/ini.v1 v1.57.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10= gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= diff --git a/server/server.go b/server/server.go index e782843..746aa27 100644 --- a/server/server.go +++ b/server/server.go @@ -11,6 +11,7 @@ import ( "time" "github.com/go-chi/chi" + "github.com/metalogical/BigFiles/auth" "github.com/metalogical/BigFiles/batch" "github.com/minio/minio-go/v7" "github.com/minio/minio-go/v7/pkg/credentials" @@ -35,7 +36,7 @@ type Options struct { TTL time.Duration // defaults to 1 hour Prefix string - IsAuthorized func(string, string) error + IsAuthorized func(auth.UserInRepo) error } func (o Options) imputeFromEnv() (Options, error) { @@ -105,7 +106,7 @@ type server struct { ttl time.Duration prefix string - isAuthorized func(string, string) error + isAuthorized func(auth.UserInRepo) error } func (s *server) key(oid string) string { @@ -116,17 +117,32 @@ func (s *server) handleBatch(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "application/vnd.git-lfs+json") w.Header().Set("X-Content-Type-Options", "nosniff") - if s.isAuthorized != nil { - var err error + var req batch.Request + err := json.NewDecoder(r.Body).Decode(&req) + if err != nil { + w.WriteHeader(404) + must(json.NewEncoder(w).Encode(batch.ErrorResponse{ + Message: "could not parse request", + DocURL: "https://github.com/git-lfs/git-lfs/blob/v2.12.0/docs/api/batch.md#requests", + })) + return + } + + var userInRepo auth.UserInRepo + userInRepo.Operation = req.Operation + userInRepo.Owner = chi.URLParam(r, "owner") + userInRepo.Repo = chi.URLParam(r, "repo") + if err = auth.CheckRepoOwner(userInRepo); req.Operation == "upload" || err != nil { if username, password, ok := r.BasicAuth(); ok { - err = s.isAuthorized(username, password) + userInRepo.Username = username + userInRepo.Password = password + err = s.isAuthorized(userInRepo) if err != nil { err = fmt.Errorf("unauthorized: %w", err) } } else { err = errors.New("Unauthorized") } - if err != nil { w.Header().Set("LFS-Authenticate", `Basic realm="Git LFS"`) w.WriteHeader(401) @@ -137,17 +153,6 @@ func (s *server) handleBatch(w http.ResponseWriter, r *http.Request) { } } - var req batch.Request - err := json.NewDecoder(r.Body).Decode(&req) - if err != nil { - w.WriteHeader(404) - must(json.NewEncoder(w).Encode(batch.ErrorResponse{ - Message: "could not parse request", - DocURL: "https://github.com/git-lfs/git-lfs/blob/v2.12.0/docs/api/batch.md#requests", - })) - return - } - var resp batch.Response for _, in := range req.Objects {