Selaa lähdekoodia

refactor DoesUserHaveGitInstallationAccess

Ivan Galakhov 4 vuotta sitten
vanhempi
sitoutus
c11f6af9a4
3 muutettua tiedostoa jossa 125 lisäystä ja 72 poistoa
  1. 2 2
      server/api/git_repo_handler.go
  2. 113 53
      server/middleware/auth.go
  3. 10 17
      server/router/router.go

+ 2 - 2
server/api/git_repo_handler.go

@@ -466,14 +466,14 @@ type HandleGetRepoZIPDownloadURLResp struct {
 // HandleGetRepoZIPDownloadURL gets the URL for downloading a zip file from a Github
 // repository
 func (app *App) HandleGetRepoZIPDownloadURL(w http.ResponseWriter, r *http.Request) {
-	tok, err := app.githubTokenFromRequest(r)
+
+	client, err := app.githubAppClientFromRequest(r)
 
 	if err != nil {
 		app.handleErrorInternal(err, w)
 		return
 	}
 
-	client := github.NewClient(app.GithubProjectConf.Client(oauth2.NoContext, tok))
 	owner := chi.URLParam(r, "owner")
 	name := chi.URLParam(r, "name")
 	branch := chi.URLParam(r, "branch")

+ 113 - 53
server/middleware/auth.go

@@ -2,8 +2,11 @@ package middleware
 
 import (
 	"bytes"
+	"context"
 	"encoding/json"
 	"errors"
+	"github.com/google/go-github/github"
+	"golang.org/x/oauth2"
 	"io/ioutil"
 	"net/http"
 	"net/url"
@@ -19,10 +22,11 @@ import (
 
 // Auth implements the authorization functions
 type Auth struct {
-	store      sessions.Store
-	cookieName string
-	tokenConf  *token.TokenGeneratorConf
-	repo       *repository.Repository
+	store             sessions.Store
+	cookieName        string
+	tokenConf         *token.TokenGeneratorConf
+	repo              *repository.Repository
+	GithubProjectConf *oauth2.Config
 }
 
 // NewAuth returns a new Auth instance
@@ -31,8 +35,9 @@ func NewAuth(
 	cookieName string,
 	tokenConf *token.TokenGeneratorConf,
 	repo *repository.Repository,
+	GithubProjectConf *oauth2.Config,
 ) *Auth {
-	return &Auth{store, cookieName, tokenConf, repo}
+	return &Auth{store, cookieName, tokenConf, repo, GithubProjectConf}
 }
 
 // BasicAuthenticate just checks that a user is logged in
@@ -394,10 +399,11 @@ func (auth *Auth) DoesUserHaveRegistryAccess(
 	})
 }
 
-// DoesUserHaveGitRepoAccess looks for a project_id parameter and a
-// git_repo_id parameter, and verifies that the git repo belongs
-// to the project
-func (auth *Auth) DoesUserHaveGitRepoAccess(
+// DoesUserHaveGitInstallationAccess checks that a user has access to an installation id
+// by ensuring the installation id exists for one org or account they have access to
+// note that this makes a github API request, but the endpoint is fast so this doesn't add
+// much overhead
+func (auth *Auth) DoesUserHaveGitInstallationAccess(
 	next http.Handler,
 	projLoc IDLocation,
 	gitRepoLoc IDLocation,
@@ -405,46 +411,99 @@ func (auth *Auth) DoesUserHaveGitRepoAccess(
 	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
 		// TODO: needs to use new github integration implementation
 
-		next.ServeHTTP(w, r)
-
-		//grID, err := findGitRepoIDInRequest(r, gitRepoLoc)
-		//
-		//if err != nil {
-		//	http.Error(w, http.StatusText(http.StatusForbidden), http.StatusForbidden)
-		//	return
-		//}
-		//
-		//projID, err := findProjIDInRequest(r, projLoc)
-		//
-		//if err != nil {
-		//	http.Error(w, http.StatusText(http.StatusForbidden), http.StatusForbidden)
-		//	return
-		//}
-		//
-		//// get the service accounts belonging to the project
-		//grs, err := auth.repo.GitRepo.ListGitReposByProjectID(uint(projID))
-		//
-		//if err != nil {
-		//	http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
-		//	return
-		//}
-		//
-		//doesExist := false
-		//
-		//for _, gr := range grs {
-		//	if gr.ID == uint(grID) {
-		//		doesExist = true
-		//		break
-		//	}
-		//}
-		//
-		//if doesExist {
-		//	next.ServeHTTP(w, r)
-		//	return
-		//}
-		//
-		//http.Error(w, http.StatusText(http.StatusForbidden), http.StatusForbidden)
-		//return
+		grID, err := findGitInstallationIDInRequest(r, gitRepoLoc)
+
+		if err != nil {
+			http.Error(w, http.StatusText(http.StatusForbidden), http.StatusForbidden)
+			return
+		}
+
+		tok := auth.getTokenFromRequest(r)
+
+		var userID uint
+
+		if tok != nil {
+			userID = tok.IBy
+		} else {
+			session, err := auth.store.Get(r, auth.cookieName)
+
+			if err != nil {
+				http.Error(w, http.StatusText(http.StatusForbidden), http.StatusForbidden)
+				return
+			}
+
+			sessionUserID, ok := session.Values["user_id"]
+			userID = sessionUserID.(uint)
+
+			if !ok {
+				http.Error(w, http.StatusText(http.StatusForbidden), http.StatusForbidden)
+				return
+			}
+		}
+
+		user, err := auth.repo.User.ReadUser(userID)
+
+		if err != nil {
+			http.Error(w, http.StatusText(http.StatusForbidden), http.StatusForbidden)
+			return
+		}
+
+		oauthInt, err := auth.repo.GithubAppOAuthIntegration.ReadGithubAppOauthIntegration(user.GithubAppIntegrationID)
+
+		if err != nil {
+			http.Error(w, http.StatusText(http.StatusForbidden), http.StatusForbidden)
+			return
+		}
+
+		client := github.NewClient(auth.GithubProjectConf.Client(oauth2.NoContext, &oauth2.Token{
+			AccessToken:  string(oauthInt.AccessToken),
+			RefreshToken: string(oauthInt.RefreshToken),
+			TokenType:    "Bearer",
+		}))
+
+		accountIDs := make([]int64, 0)
+
+		AuthUser, _, err := client.Users.Get(context.Background(), "")
+
+		if err != nil {
+			http.Error(w, http.StatusText(http.StatusForbidden), http.StatusForbidden)
+			return
+		}
+
+		accountIDs = append(accountIDs, *AuthUser.ID)
+
+		opts := &github.ListOptions{
+			PerPage: 100,
+			Page:    1,
+		}
+
+		for {
+			orgs, pages, err := client.Organizations.List(context.Background(), "", opts)
+
+			if err != nil {
+				http.Error(w, http.StatusText(http.StatusForbidden), http.StatusForbidden)
+				return
+			}
+
+			for _, org := range orgs {
+				accountIDs = append(accountIDs, *org.ID)
+			}
+
+			if pages.NextPage == 0 {
+				break
+			}
+		}
+
+		installations, err := auth.repo.GithubAppInstallation.ReadGithubAppInstallationByAccountIDs(accountIDs)
+
+		for _, installation := range installations {
+			if uint64(installation.InstallationID) == grID {
+				next.ServeHTTP(w, r)
+				return
+			}
+		}
+
+		http.Error(w, http.StatusText(http.StatusForbidden), http.StatusForbidden)
 	})
 }
 
@@ -942,12 +1001,13 @@ func findRegistryIDInRequest(r *http.Request, registryLoc IDLocation) (uint64, e
 	return regID, nil
 }
 
-func findGitRepoIDInRequest(r *http.Request, gitRepoLoc IDLocation) (uint64, error) {
+// findGitInstallationIDInRequest extracts and installation ID from a request
+func findGitInstallationIDInRequest(r *http.Request, gitRepoLoc IDLocation) (uint64, error) {
 	var grID uint64
 	var err error
 
 	if gitRepoLoc == URLParam {
-		grID, err = strconv.ParseUint(chi.URLParam(r, "git_repo_id"), 0, 64)
+		grID, err = strconv.ParseUint(chi.URLParam(r, "installation_id"), 0, 64)
 
 		if err != nil {
 			return 0, err
@@ -977,10 +1037,10 @@ func findGitRepoIDInRequest(r *http.Request, gitRepoLoc IDLocation) (uint64, err
 			return 0, err
 		}
 
-		if regStrArr, ok := vals["git_repo_id"]; ok && len(regStrArr) == 1 {
+		if regStrArr, ok := vals["installation_id"]; ok && len(regStrArr) == 1 {
 			grID, err = strconv.ParseUint(regStrArr[0], 10, 64)
 		} else {
-			return 0, errors.New("git repo id not found")
+			return 0, errors.New("git app installation id not found")
 		}
 	}
 

+ 10 - 17
server/router/router.go

@@ -23,7 +23,7 @@ func New(a *api.App) *chi.Mux {
 
 	auth := mw.NewAuth(a.Store, a.ServerConf.CookieName, &token.TokenGeneratorConf{
 		TokenSecret: a.ServerConf.TokenGeneratorSecret,
-	}, a.Repo)
+	}, a.Repo, a.GithubProjectConf)
 
 	r.Route("/api", func(r chi.Router) {
 		r.Use(mw.ContentTypeJSON)
@@ -1129,14 +1129,13 @@ func New(a *api.App) *chi.Mux {
 				),
 			)
 
-			r.Method(
+			r.Method( // this endpoint isn't going to be used anymore, should it get deleted?
 				"DELETE",
 				"/projects/{project_id}/gitrepos/{git_repo_id}",
 				auth.DoesUserHaveProjectAccess(
-					auth.DoesUserHaveGitRepoAccess(
+					auth.DoesUserHaveGitInstallationAccess(
 						requestlog.NewHandler(a.HandleDeleteProjectGitRepo, l),
 						mw.URLParam,
-						mw.URLParam,
 					),
 					mw.URLParam,
 					mw.WriteAccess,
@@ -1147,10 +1146,9 @@ func New(a *api.App) *chi.Mux {
 				"GET",
 				"/projects/{project_id}/gitrepos/{installation_id}/repos",
 				auth.DoesUserHaveProjectAccess(
-					auth.DoesUserHaveGitRepoAccess(
+					auth.DoesUserHaveGitInstallationAccess(
 						requestlog.NewHandler(a.HandleListRepos, l),
 						mw.URLParam,
-						mw.URLParam,
 					),
 					mw.URLParam,
 					mw.ReadAccess,
@@ -1161,10 +1159,9 @@ func New(a *api.App) *chi.Mux {
 				"GET",
 				"/projects/{project_id}/gitrepos/{installation_id}/repos/{kind}/{owner}/{name}/branches",
 				auth.DoesUserHaveProjectAccess(
-					auth.DoesUserHaveGitRepoAccess(
+					auth.DoesUserHaveGitInstallationAccess(
 						requestlog.NewHandler(a.HandleGetBranches, l),
 						mw.URLParam,
-						mw.URLParam,
 					),
 					mw.URLParam,
 					mw.ReadAccess,
@@ -1175,10 +1172,9 @@ func New(a *api.App) *chi.Mux {
 				"GET",
 				"/projects/{project_id}/gitrepos/{installation_id}/repos/{kind}/{owner}/{name}/{branch}/buildpack/detect",
 				auth.DoesUserHaveProjectAccess(
-					auth.DoesUserHaveGitRepoAccess(
+					auth.DoesUserHaveGitInstallationAccess(
 						requestlog.NewHandler(a.HandleDetectBuildpack, l),
 						mw.URLParam,
-						mw.URLParam,
 					),
 					mw.URLParam,
 					mw.ReadAccess,
@@ -1189,10 +1185,9 @@ func New(a *api.App) *chi.Mux {
 				"GET",
 				"/projects/{project_id}/gitrepos/{installation_id}/repos/{kind}/{owner}/{name}/{branch}/contents",
 				auth.DoesUserHaveProjectAccess(
-					auth.DoesUserHaveGitRepoAccess(
+					auth.DoesUserHaveGitInstallationAccess(
 						requestlog.NewHandler(a.HandleGetBranchContents, l),
 						mw.URLParam,
-						mw.URLParam,
 					),
 					mw.URLParam,
 					mw.ReadAccess,
@@ -1203,10 +1198,9 @@ func New(a *api.App) *chi.Mux {
 				"GET",
 				"/projects/{project_id}/gitrepos/{installation_id}/repos/{kind}/{owner}/{name}/{branch}/procfile",
 				auth.DoesUserHaveProjectAccess(
-					auth.DoesUserHaveGitRepoAccess(
+					auth.DoesUserHaveGitInstallationAccess(
 						requestlog.NewHandler(a.HandleGetProcfileContents, l),
 						mw.URLParam,
-						mw.URLParam,
 					),
 					mw.URLParam,
 					mw.ReadAccess,
@@ -1215,12 +1209,11 @@ func New(a *api.App) *chi.Mux {
 
 			r.Method(
 				"GET",
-				"/projects/{project_id}/gitrepos/{git_repo_id}/repos/{kind}/{owner}/{name}/{branch}/tarball_url",
+				"/projects/{project_id}/gitrepos/{installation_id}/repos/{kind}/{owner}/{name}/{branch}/tarball_url",
 				auth.DoesUserHaveProjectAccess(
-					auth.DoesUserHaveGitRepoAccess(
+					auth.DoesUserHaveGitInstallationAccess(
 						requestlog.NewHandler(a.HandleGetRepoZIPDownloadURL, l),
 						mw.URLParam,
-						mw.URLParam,
 					),
 					mw.URLParam,
 					mw.ReadAccess,