Skip to content

Commit

Permalink
Fix safe problem (#90)
Browse files Browse the repository at this point in the history
* restrict the content type of uploading and downloading file

* can't show sensitive data in cookie
  • Loading branch information
zengchen1024 authored Apr 9, 2021
1 parent efdc5d6 commit d941aa2
Show file tree
Hide file tree
Showing 12 changed files with 205 additions and 73 deletions.
28 changes: 13 additions & 15 deletions code-platform-auth/platforms/gitee.go
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand All @@ -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
}
28 changes: 13 additions & 15 deletions code-platform-auth/platforms/github.go
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand All @@ -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
}
2 changes: 1 addition & 1 deletion code-platform-auth/platforms/platform.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}

Expand Down
52 changes: 38 additions & 14 deletions controllers/auth_on_code_platform.go
Original file line number Diff line number Diff line change
Expand Up @@ -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))
}

Expand Down Expand Up @@ -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))
}

Expand Down Expand Up @@ -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
}
56 changes: 43 additions & 13 deletions controllers/base-controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@ import (
"io/ioutil"
"net"
"net/http"
"net/url"
"os"
"path/filepath"
"strings"

"github.com/astaxie/beego"
Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -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"))
}
Expand Down Expand Up @@ -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) {
Expand All @@ -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
Expand Down
1 change: 1 addition & 0 deletions controllers/error.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
13 changes: 1 addition & 12 deletions controllers/link.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}
}
Expand Down Expand Up @@ -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)
Expand Down
4 changes: 2 additions & 2 deletions controllers/org-email.go
Original file line number Diff line number Diff line change
Expand Up @@ -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))
}

Expand Down Expand Up @@ -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))
}

Expand Down
68 changes: 68 additions & 0 deletions controllers/org-repo.go
Original file line number Diff line number Diff line change
@@ -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)
}
3 changes: 2 additions & 1 deletion controllers/util.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,9 @@ import (
)

const (
headerToken = "Token"
apiAccessToken = "access_token"
apiAccessController = "access_controller"
contentTypeOfPDF = "application/pdf"
fileNameOfUploadingOrgSignatue = "org_signature_file"
)

Expand Down
Loading

0 comments on commit d941aa2

Please sign in to comment.