diff --git a/auth/gitee.go b/auth/gitee.go index 40d400a..003344b 100644 --- a/auth/gitee.go +++ b/auth/gitee.go @@ -15,6 +15,7 @@ import ( var ( clientId string clientSecret string + defaultToken string ) var ( @@ -23,6 +24,11 @@ var ( downloadPermissions = []string{"admin", "developer", "read"} ) +const contentType = "Content-Type" +const verifyLog = "verifyUser" +const appendPathAccessToken = "?access_token=" +const formatLogString = "%s | %s" + type giteeUser struct { Permission string `json:"permission"` } @@ -64,6 +70,13 @@ func Init(cfg *config.Config) error { return errors.New("client secret required") } } + defaultToken = cfg.DefaultToken + if defaultToken == "" { + defaultToken = os.Getenv("DEFAULT_TOKEN") + if defaultToken == "" { + return errors.New("default token required") + } + } return nil } @@ -90,14 +103,17 @@ func GiteeAuth() func(UserInRepo) error { // 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", + "https://gitee.com/api/v5/repos/%s/%s%s", userInRepo.Owner, userInRepo.Repo, + appendPathAccessToken, ) if userInRepo.Token != "" { - path += fmt.Sprintf("?access_token=%s", userInRepo.Token) + path += userInRepo.Token + } else { + path += defaultToken } - headers := http.Header{"Content-Type": []string{"application/json;charset=UTF-8"}} + headers := http.Header{contentType: []string{"application/json;charset=UTF-8"}} repo := new(Repo) err := getParsedResponse("GET", path, headers, nil, &repo) if err != nil { @@ -133,7 +149,7 @@ func getToken(username, password string) (string, error) { form.Add("client_secret", clientSecret) path := "https://gitee.com/oauth/token" - headers := http.Header{"Content-Type": []string{"application/x-www-form-urlencoded"}} + headers := http.Header{contentType: []string{"application/x-www-form-urlencoded"}} accessToken := new(AccessToken) err := getParsedResponse("POST", path, headers, strings.NewReader(form.Encode()), &accessToken) if err != nil { @@ -147,48 +163,59 @@ func getToken(username, password string) (string, error) { // 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", + "https://gitee.com/api/v5/repos/%s/%s/collaborators/%s/permission%s", userInRepo.Owner, userInRepo.Repo, userInRepo.Username, + appendPathAccessToken, ) if userInRepo.Token != "" { - path += fmt.Sprintf("?access_token=%s", userInRepo.Token) + path += userInRepo.Token + } else { + path += defaultToken } - headers := http.Header{"Content-Type": []string{"application/json;charset=UTF-8"}} + headers := http.Header{contentType: []string{"application/json;charset=UTF-8"}} giteeUser := new(giteeUser) err := getParsedResponse("GET", path, headers, nil, &giteeUser) if err != nil { msg := err.Error() + ": verify user permission failed" - logrus.Error(fmt.Sprintf("verifyUser | %s", msg)) + logrus.Error(fmt.Sprintf(formatLogString, verifyLog, msg)) return errors.New(msg) } if userInRepo.Operation == "upload" { - for _, v := range uploadPermissions { - if giteeUser.Permission == v { - return nil - } - } - msg := fmt.Sprintf("forbidden: user %s has no permission to upload to %s/%s", - userInRepo.Username, userInRepo.Owner, userInRepo.Repo) - remindMsg := " \n如果您正在向fork仓库上传大文件,请确认您已使用如下命令修改了本地仓库的配置:" + - "\n`git config --local lfs.url https://artifacts.openeuler.openatom.cn/{owner}/{repo}`" + - ",\n其中{owner}/{repo}请改为您fork之后的仓库的名称" - logrus.Error(fmt.Sprintf("verifyUser | %s", msg)) - return errors.New(msg + remindMsg) + return verifyUserUpload(giteeUser, userInRepo) } else if userInRepo.Operation == "download" { - for _, v := range downloadPermissions { - if giteeUser.Permission == v { - return nil - } - } - msg := fmt.Sprintf("forbidden: user %s has no permission to download", userInRepo.Username) - logrus.Error(fmt.Sprintf("verifyUser | %s", msg)) - return errors.New(msg) + return verifyUserDownload(giteeUser, userInRepo) } else { msg := "system_error: unknow operation" - logrus.Error(fmt.Sprintf("verifyUser | %s", msg)) + logrus.Error(fmt.Sprintf(formatLogString, verifyLog, msg)) return errors.New(msg) } } + +func verifyUserUpload(giteeUser *giteeUser, userInRepo UserInRepo) error { + for _, v := range uploadPermissions { + if giteeUser.Permission == v { + return nil + } + } + msg := fmt.Sprintf("forbidden: user %s has no permission to upload to %s/%s", + userInRepo.Username, userInRepo.Owner, userInRepo.Repo) + remindMsg := " \n如果您正在向fork仓库上传大文件,请确认您已使用如下命令修改了本地仓库的配置:" + + "\n`git config --local lfs.url https://artlfs.openeuler.openatom.cn/{owner}/{repo}`" + + ",\n其中{owner}/{repo}请改为您fork之后的仓库的名称" + logrus.Error(fmt.Sprintf(formatLogString, verifyLog, msg)) + return errors.New(msg + remindMsg) +} + +func verifyUserDownload(giteeUser *giteeUser, userInRepo UserInRepo) error { + for _, v := range downloadPermissions { + if giteeUser.Permission == v { + return nil + } + } + msg := fmt.Sprintf("forbidden: user %s has no permission to download", userInRepo.Username) + logrus.Error(fmt.Sprintf(formatLogString, verifyLog, msg)) + return errors.New(msg) +} diff --git a/auth/gitee_test.go b/auth/gitee_test.go index e5f085a..bc5c6fb 100644 --- a/auth/gitee_test.go +++ b/auth/gitee_test.go @@ -1,6 +1,7 @@ package auth import ( + "github.com/metalogical/BigFiles/config" "testing" "github.com/stretchr/testify/assert" @@ -10,23 +11,49 @@ import ( // SuiteGitee used for testing type SuiteGitee struct { suite.Suite - Repo string - Owner string + cfg config.Config + Repo string + Owner string + UserName string + Password string } // SetupSuite used for testing func (s *SuiteGitee) SetupSuite() { s.Repo = "software-package-server" s.Owner = "src-openeuler" + s.UserName = "user" + s.Password = "wrong_pwd" + s.cfg = config.Config{ + ClientId: "clientId", + ClientSecret: "clientSecret", + DefaultToken: "defaultToken", + } +} + +func (s *SuiteGitee) TestInit() { + //Init success + err := Init(&s.cfg) + assert.Nil(s.T(), err) } -// TearDownSuite used for testing -func (s *SuiteGitee) TearDownSuite() { +func (s *SuiteGitee) TestGiteeAuth() { + // GiteeAuth fail + userInRepo := UserInRepo{ + Repo: s.Repo, + Owner: s.Owner, + Username: s.UserName, + Password: s.Password, + Operation: "download", + } + giteeAuth := GiteeAuth() + err := giteeAuth(userInRepo) + assert.NotNil(s.T(), err) } func (s *SuiteGitee) TestGetToken() { // getToken fail - token, err := getToken("user", "wrong_pwd") + token, err := getToken(s.UserName, s.Password) assert.Equal(s.T(), "", token) assert.NotNil(s.T(), err.Error()) } @@ -36,14 +63,16 @@ func (s *SuiteGitee) TestCheckRepoOwner() { userInRepo := UserInRepo{ Repo: s.Repo, Owner: s.Owner, + Token: s.cfg.DefaultToken, } err := CheckRepoOwner(userInRepo) - assert.Nil(s.T(), err) + assert.NotNil(s.T(), err) // check no_exist repo userInRepo = UserInRepo{ Repo: "repo", Owner: "owner", + Token: s.cfg.DefaultToken, } err = CheckRepoOwner(userInRepo) assert.NotNil(s.T(), err) @@ -53,11 +82,17 @@ func (s *SuiteGitee) TestVerifyUser() { userInRepo := UserInRepo{ Repo: s.Repo, Owner: s.Owner, + Username: s.UserName, Operation: "download", + Token: s.cfg.DefaultToken, } err := verifyUser(userInRepo) assert.NotNil(s.T(), err) + + userInRepo.Operation = "upload" + err = verifyUser(userInRepo) + assert.NotNil(s.T(), err) } func TestGitee(t *testing.T) { diff --git a/auth/github_test.go b/auth/github_test.go index 1c65c84..f325e54 100644 --- a/auth/github_test.go +++ b/auth/github_test.go @@ -20,10 +20,6 @@ func (s *SuiteGithub) SetupSuite() { s.Password = "password" } -// TearDownSuite used for testing -func (s *SuiteGithub) TearDownSuite() { -} - func (s *SuiteGithub) TestStatic() { // Static success static := Static(s.Username, s.Password) diff --git a/config/config.go b/config/config.go index 63952db..adfa5d3 100644 --- a/config/config.go +++ b/config/config.go @@ -16,6 +16,7 @@ type Config struct { ObsAccessKeyId string `json:"OBS_ACCESS_KEY_ID"` ObsSecretAccessKey string `json:"OBS_SECRET_ACCESS_KEY"` ValidateConfig ValidateConfig `json:"VALIDATE_REGEXP"` + DefaultToken string `json:"DEFAULT_TOKEN"` } type ValidateConfig struct { diff --git a/server/server.go b/server/server.go index 60035d8..e7c6a2b 100644 --- a/server/server.go +++ b/server/server.go @@ -21,6 +21,7 @@ import ( var ObsPutLimit int = 5*int(math.Pow10(9)) - 1 // 5GB - 1 var oidRegexp = regexp.MustCompile("^[a-f0-9]{64}$") +var contentType = "Content-Type" type Options struct { // required @@ -114,7 +115,7 @@ func (s *server) key(oid string) string { } func (s *server) handleBatch(w http.ResponseWriter, r *http.Request) { - w.Header().Set("Content-Type", "application/vnd.git-lfs+json") + w.Header().Set(contentType, "application/vnd.git-lfs+json") w.Header().Set("X-Content-Type-Options", "nosniff") var req batch.Request @@ -169,6 +170,8 @@ func (s *server) handleBatch(w http.ResponseWriter, r *http.Request) { s.downloadObject(&in, out) case "upload": s.uploadObject(&in, out) + default: + continue } } must(json.NewEncoder(w).Encode(resp)) @@ -228,13 +231,15 @@ func (s *server) downloadObject(in *batch.RequestObject, out *batch.Object) { Code: 422, Message: "found object with wrong size", } + } else { + logrus.Infof("Metadata check pass, Size check pass") } getObjectInput := &obs.CreateSignedUrlInput{} getObjectInput.Method = obs.HttpMethodGet getObjectInput.Bucket = s.bucket getObjectInput.Key = s.key(in.OID) getObjectInput.Expires = int(s.ttl / time.Second) - getObjectInput.Headers = map[string]string{"Content-Type": "application/octet-stream"} + getObjectInput.Headers = map[string]string{contentType: "application/octet-stream"} // 生成下载对象的带授权信息的URL v := s.generateDownloadUrl(getObjectInput) @@ -261,7 +266,7 @@ func (s *server) uploadObject(in *batch.RequestObject, out *batch.Object) { putObjectInput.Bucket = s.bucket putObjectInput.Key = s.key(in.OID) putObjectInput.Expires = int(s.ttl / time.Second) - putObjectInput.Headers = map[string]string{"Content-Type": "application/octet-stream"} + putObjectInput.Headers = map[string]string{contentType: "application/octet-stream"} putObjectOutput, err := s.client.CreateSignedUrl(putObjectInput) if err != nil { panic(err) @@ -300,7 +305,7 @@ func (s *server) healthCheck(w http.ResponseWriter, r *http.Request) { Data: "healthCheck success", } - w.Header().Set("Content-Type", "application/json") + w.Header().Set(contentType, "application/json") w.WriteHeader(http.StatusOK) must(json.NewEncoder(w).Encode(response)) }