diff --git a/code-platform-auth/platforms/gitee.go b/code-platform-auth/platforms/gitee.go index e84f3ce..5c6f37c 100644 --- a/code-platform-auth/platforms/gitee.go +++ b/code-platform-auth/platforms/gitee.go @@ -61,21 +61,6 @@ func (this *giteeClient) GetAuthorizedEmail() (string, error) { return "", fmt.Errorf(errMsgNoPublicEmail) } -func (this *giteeClient) IsOrgExist(org string) (bool, error) { - orgs, err := this.ListOrg() - if err != nil { - //TODO :is token expiry - return false, err - } - - for _, item := range orgs { - if item == org { - return true, nil - } - } - return false, nil -} - func (this *giteeClient) ListOrg() ([]string, error) { var r []string @@ -101,3 +86,16 @@ func (this *giteeClient) ListOrg() ([]string, error) { return r, nil } + +func (this *giteeClient) HasRepo(org, repo string) (bool, error) { + _, r, err := this.c.RepositoriesApi.GetV5ReposOwnerRepo(context.Background(), org, repo, nil) + if err == nil { + return true, nil + } + + if r.StatusCode == 404 { + return false, nil + } + + return false, err +} diff --git a/code-platform-auth/platforms/github.go b/code-platform-auth/platforms/github.go index 51e091d..607263e 100644 --- a/code-platform-auth/platforms/github.go +++ b/code-platform-auth/platforms/github.go @@ -51,21 +51,6 @@ func (this *githubClient) GetAuthorizedEmail() (string, error) { return "", fmt.Errorf(errMsgNoPublicEmail) } -func (this *githubClient) IsOrgExist(org string) (bool, error) { - orgs, err := this.ListOrg() - if err != nil { - //TODO :is token expiry - return false, err - } - - for _, item := range orgs { - if item == org { - return true, nil - } - } - return false, nil -} - func (this *githubClient) ListOrg() ([]string, error) { var r []string @@ -90,3 +75,16 @@ func (this *githubClient) ListOrg() ([]string, error) { return r, nil } + +func (gc *githubClient) HasRepo(org, repo string) (bool, error) { + _, r, err := gc.c.Repositories.Get(context.Background(), org, repo) + if err == nil { + return true, nil + } + + if r.StatusCode == 404 { + return false, nil + } + + return false, err +} diff --git a/code-platform-auth/platforms/platform.go b/code-platform-auth/platforms/platform.go index 7a9b9b1..d4618d0 100644 --- a/code-platform-auth/platforms/platform.go +++ b/code-platform-auth/platforms/platform.go @@ -12,7 +12,7 @@ const ( type Platform interface { GetUser() (string, error) GetAuthorizedEmail() (string, error) - IsOrgExist(org string) (bool, error) + HasRepo(org, repo string) (bool, error) ListOrg() ([]string, error) } diff --git a/controllers/auth_on_code_platform.go b/controllers/auth_on_code_platform.go index 7c87562..48ffc89 100644 --- a/controllers/auth_on_code_platform.go +++ b/controllers/auth_on_code_platform.go @@ -40,7 +40,7 @@ func (this *AuthController) Callback() { } rs := func(errCode string, reason error) { - this.setCookies(map[string]string{"error_code": errCode, "error_msg": reason.Error()}) + this.setCookies(map[string]string{"error_code": errCode, "error_msg": reason.Error()}, false) this.redirect(authHelper.WebRedirectDir(false)) } @@ -93,12 +93,13 @@ func (this *AuthController) Callback() { return } - cookies := map[string]string{"access_token": at, "platform_token": token} + this.setCookies(map[string]string{apiAccessToken: at}, true) if permission == PermissionIndividualSigner { - cookies["sign_user"] = pl.User - cookies["sign_email"] = pl.Email + this.setCookies(map[string]string{ + "sign_user": pl.User, + "sign_email": pl.Email, + }, false) } - this.setCookies(cookies) this.redirect(authHelper.WebRedirectDir(true)) } @@ -297,18 +298,41 @@ func (this *acForCodePlatformPayload) isOwnerOfOrg(org string) *failedApiResult return nil } - p, err := platforms.NewPlatform(this.PlatformToken, "", this.Platform) + this.refreshOrg() + + if !this.Orgs[org] { + return newFailedApiResult(400, errNotYoursOrg, fmt.Errorf("not the org of owner")) + } + return nil +} + +func (this *acForCodePlatformPayload) refreshOrg() { + pt, err := platforms.NewPlatform(this.PlatformToken, "", this.Platform) if err != nil { - return newFailedApiResult(400, errSystemError, err) + return } - if b, err := p.IsOrgExist(org); err != nil { - // TODO token expiry - return newFailedApiResult(500, errSystemError, err) - } else if !b { - return newFailedApiResult(400, errNotYoursOrg, fmt.Errorf("not the org of owner")) + // TODO token expiry + orgs, err := pt.ListOrg() + if err != nil { + return } - this.Orgs[org] = true - return nil + for _, item := range orgs { + this.Orgs[item] = true + } +} + +func (this *acForCodePlatformPayload) hasRepo(org, repo string) (bool, *failedApiResult) { + pt, err := platforms.NewPlatform(this.PlatformToken, "", this.Platform) + if err != nil { + return false, newFailedApiResult(400, errSystemError, err) + } + + b, err := pt.HasRepo(org, repo) + if err != nil { + return false, newFailedApiResult(400, errSystemError, err) + } + + return b, nil } diff --git a/controllers/base-controller.go b/controllers/base-controller.go index 38c677b..18bc2b2 100644 --- a/controllers/base-controller.go +++ b/controllers/base-controller.go @@ -5,6 +5,9 @@ import ( "io/ioutil" "net" "net/http" + "net/url" + "os" + "path/filepath" "strings" "github.com/astaxie/beego" @@ -34,11 +37,7 @@ type baseController struct { func (this *baseController) sendResponse(body interface{}, statusCode int) { if token, err := this.refreshAccessToken(); err == nil { - // this code must run before `this.Ctx.ResponseWriter.WriteHeader` - // otherwise the header can't be set successfully. - // The reason is relevant to the variable of 'Response.Started' at - // beego/context/context.go - this.Ctx.Output.Header(headerToken, token) + this.setCookies(map[string]string{apiAccessToken: token}, true) } if statusCode != 0 { @@ -220,7 +219,7 @@ func (this *baseController) newAccessController(permission string) *accessContro } func (this *baseController) checkApiReqToken(ac *accessController, permission []string) *failedApiResult { - token := this.apiReqHeader(headerToken) + token := this.Ctx.Input.Cookie(apiAccessToken) if token == "" { return newFailedApiResult(401, errMissingToken, fmt.Errorf("no token passed")) } @@ -285,11 +284,45 @@ func (this *baseController) readInputFile(fileName string, maxSize int) ([]byte, if maxSize > 0 && len(data) > maxSize { return nil, newFailedApiResult(400, errTooBigPDF, fmt.Errorf("big pdf file")) } + + if http.DetectContentType(data) != contentTypeOfPDF { + return nil, newFailedApiResult(400, errNotPDFFile, fmt.Errorf("not pdf file")) + } + return data, nil } -func (this *baseController) downloadFile(path string) { - this.Ctx.Output.Download(path) +func (this *baseController) downloadFile(file string) { + output := this.Ctx.Output + + // check get file error, file not found or other error. + if _, err := os.Stat(file); err != nil { + http.ServeFile(output.Context.ResponseWriter, output.Context.Request, file) + return + } + + fName := filepath.Base(file) + //https://tools.ietf.org/html/rfc6266#section-4.3 + fn := url.PathEscape(fName) + if fName == fn { + fn = "filename=" + fn + } else { + /** + The parameters "filename" and "filename*" differ only in that + "filename*" uses the encoding defined in [RFC5987], allowing the use + of characters not present in the ISO-8859-1 character set + ([ISO-8859-1]). + */ + fn = "filename=" + fName + "; filename*=utf-8''" + fn + } + output.ContentType(filepath.Ext(file)) + output.Header("Content-Disposition", "attachment; "+fn) + output.Header("Content-Description", "File Transfer") + output.Header("Content-Transfer-Encoding", "binary") + output.Header("Expires", "0") + output.Header("Cache-Control", "must-revalidate") + output.Header("Pragma", "public") + http.ServeFile(output.Context.ResponseWriter, output.Context.Request, file) } func (this *baseController) redirect(webRedirectDir string) { @@ -298,17 +331,14 @@ func (this *baseController) redirect(webRedirectDir string) { ) } -func (this *baseController) setCookies(value map[string]string) { +func (this *baseController) setCookies(value map[string]string, isSensitive bool) { for k, v := range value { - this.Ctx.SetCookie(k, v, "3600", "/") + this.Ctx.SetCookie(k, v, 3600, "/", "", true, isSensitive) } } func (this *baseController) getRemoteAddr() (string, *failedApiResult) { ips := this.Ctx.Request.Header.Get("x-forwarded-for") - beego.Info(ips) - beego.Info(this.Ctx.Request.Header.Get("x-real-ip")) - for _, item := range strings.Split(ips, ", ") { if net.ParseIP(item) != nil { return item, nil diff --git a/controllers/error.go b/controllers/error.go index 9dea58d..8356d1b 100644 --- a/controllers/error.go +++ b/controllers/error.go @@ -40,6 +40,7 @@ const ( errNotSameCorp = string(models.ErrNotSameCorp) errFrequentOperation = "frequent_operation" errCanNotFetchClientIP = "can_not_fetch_client_ip" + errNotPDFFile = "not_pdf_file" ) func parseModelError(err models.IModelError) *failedApiResult { diff --git a/controllers/link.go b/controllers/link.go index 744aa02..6c3e4a7 100644 --- a/controllers/link.go +++ b/controllers/link.go @@ -16,11 +16,7 @@ type LinkController struct { } func (this *LinkController) Prepare() { - if strings.HasSuffix(this.routerPattern(), ":apply_to") { - if this.apiReqHeader(headerToken) != "" { - this.apiPrepare(PermissionIndividualSigner) - } - } else { + if !strings.HasSuffix(this.routerPattern(), ":apply_to") { this.apiPrepare(PermissionOwnerOfOrg) } } @@ -214,13 +210,6 @@ func (this *LinkController) ListLinks() { func (this *LinkController) GetCLAForSigning() { action := "fetch signing page info" applyTo := this.GetString(":apply_to") - token := this.apiReqHeader(headerToken) - - if !((token == "" && applyTo == dbmodels.ApplyToCorporation) || - (token != "" && applyTo == dbmodels.ApplyToIndividual)) { - this.sendFailedResponse(400, errUnmatchedCLAType, fmt.Errorf("unmatched cla type"), action) - return - } org, repo := parseOrgAndRepo(this.GetString(":org_id")) orgRepo := buildOrgRepo(this.GetString(":platform"), org, repo) diff --git a/controllers/org-email.go b/controllers/org-email.go index d804086..71c5b4f 100644 --- a/controllers/org-email.go +++ b/controllers/org-email.go @@ -25,7 +25,7 @@ func (this *EmailController) Prepare() { // @router /auth/:platform [get] func (this *EmailController) Auth() { rs := func(errCode string, reason error) { - this.setCookies(map[string]string{"error_code": errCode, "error_msg": reason.Error()}) + this.setCookies(map[string]string{"error_code": errCode, "error_msg": reason.Error()}, false) this.redirect(email.EmailAgent.WebRedirectDir(false)) } @@ -80,7 +80,7 @@ func (this *EmailController) Auth() { } } - this.setCookies(map[string]string{"email": emailAddr}) + this.setCookies(map[string]string{"email": emailAddr}, false) this.redirect(email.EmailAgent.WebRedirectDir(true)) } diff --git a/controllers/org-repo.go b/controllers/org-repo.go new file mode 100644 index 0000000..466a4ee --- /dev/null +++ b/controllers/org-repo.go @@ -0,0 +1,68 @@ +package controllers + +type OrgRepoController struct { + baseController +} + +func (org *OrgRepoController) Prepare() { + org.apiPrepare(PermissionOwnerOfOrg) +} + +// @Title List +// @Description get all orgs +// @Success 200 {string} list +// @Failure 400 missing_token: token is missing +// @Failure 401 unknown_token: token is unknown +// @Failure 402 expired_token: token is expired +// @Failure 403 unauthorized_token: the permission of token is unmatched +// @Failure 500 system_error: system error +// @router / [get] +func (org *OrgRepoController) List() { + action := "list org" + + pl, fr := org.tokenPayloadBasedOnCodePlatform() + if fr != nil { + org.sendFailedResultAsResp(fr, action) + return + } + + pl.refreshOrg() + + r := make([]string, 0, len(pl.Orgs)) + for k := range pl.Orgs { + r = append(r, k) + } + + org.sendSuccessResp(r) +} + +// @Title Check +// @Description check whether the repo exists +// @Param :org path string true "org" +// @Param :repo path string true "repo" +// @Success 200 {string} map +// @Failure 400 missing_url_path_parameter: missing url path parameter +// @Failure 401 missing_token: token is missing +// @Failure 402 unknown_token: token is unknown +// @Failure 403 expired_token: token is expired +// @Failure 404 unauthorized_token: the permission of token is unmatched +// @Failure 500 system_error: system error +// @router /:org/:repo [get] +func (org *OrgRepoController) Check() { + action := "check repo" + + pl, fr := org.tokenPayloadBasedOnCodePlatform() + if fr != nil { + org.sendFailedResultAsResp(fr, action) + return + } + + repo := org.GetString(":repo") + b, err := pl.hasRepo(org.GetString(":org"), repo) + if err != nil { + org.sendFailedResultAsResp(err, action) + return + } + + org.sendSuccessResp(b) +} diff --git a/controllers/util.go b/controllers/util.go index 94aa8d6..4bbf8e4 100644 --- a/controllers/util.go +++ b/controllers/util.go @@ -17,8 +17,9 @@ import ( ) const ( - headerToken = "Token" + apiAccessToken = "access_token" apiAccessController = "access_controller" + contentTypeOfPDF = "application/pdf" fileNameOfUploadingOrgSignatue = "org_signature_file" ) diff --git a/routers/commentsRouter_controllers.go b/routers/commentsRouter_controllers.go index 9372080..16e2cff 100644 --- a/routers/commentsRouter_controllers.go +++ b/routers/commentsRouter_controllers.go @@ -322,6 +322,24 @@ func init() { Filters: nil, Params: nil}) + beego.GlobalControllerRouter["github.com/opensourceways/app-cla-server/controllers:OrgRepoController"] = append(beego.GlobalControllerRouter["github.com/opensourceways/app-cla-server/controllers:OrgRepoController"], + beego.ControllerComments{ + Method: "List", + Router: "/", + AllowHTTPMethods: []string{"get"}, + MethodParams: param.Make(), + Filters: nil, + Params: nil}) + + beego.GlobalControllerRouter["github.com/opensourceways/app-cla-server/controllers:OrgRepoController"] = append(beego.GlobalControllerRouter["github.com/opensourceways/app-cla-server/controllers:OrgRepoController"], + beego.ControllerComments{ + Method: "Check", + Router: "/:org/:repo", + AllowHTTPMethods: []string{"get"}, + MethodParams: param.Make(), + Filters: nil, + Params: nil}) + beego.GlobalControllerRouter["github.com/opensourceways/app-cla-server/controllers:OrgSignatureController"] = append(beego.GlobalControllerRouter["github.com/opensourceways/app-cla-server/controllers:OrgSignatureController"], beego.ControllerComments{ Method: "Get", diff --git a/routers/router.go b/routers/router.go index c63f368..1820418 100644 --- a/routers/router.go +++ b/routers/router.go @@ -70,6 +70,11 @@ func init() { &controllers.OrgSignatureController{}, ), ), + beego.NSNamespace("/org-repo", + beego.NSInclude( + &controllers.OrgRepoController{}, + ), + ), beego.NSNamespace("/verification-code", beego.NSInclude( &controllers.VerificationCodeController{},