|
|
@@ -5,12 +5,15 @@ import (
|
|
|
"fmt"
|
|
|
"net/http"
|
|
|
|
|
|
+ "github.com/google/go-github/github"
|
|
|
+ "golang.org/x/oauth2"
|
|
|
+
|
|
|
"github.com/porter-dev/porter/api/server/shared/apierrors"
|
|
|
"github.com/porter-dev/porter/api/server/shared/config"
|
|
|
"github.com/porter-dev/porter/api/types"
|
|
|
"github.com/porter-dev/porter/internal/models"
|
|
|
"github.com/porter-dev/porter/internal/models/integrations"
|
|
|
- "gorm.io/gorm"
|
|
|
+ "github.com/porter-dev/porter/internal/oauth"
|
|
|
)
|
|
|
|
|
|
type GitInstallationScopedFactory struct {
|
|
|
@@ -33,24 +36,22 @@ type GitInstallationScopedMiddleware struct {
|
|
|
}
|
|
|
|
|
|
func (p *GitInstallationScopedMiddleware) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
|
|
- // read the project to check scopes
|
|
|
- proj, _ := r.Context().Value(types.ProjectScope).(*models.Project)
|
|
|
+ // read the user to perform authorization
|
|
|
+ user, _ := r.Context().Value(types.UserScope).(*models.User)
|
|
|
|
|
|
// get the registry id from the URL param context
|
|
|
reqScopes, _ := r.Context().Value(types.RequestScopeCtxKey).(map[types.PermissionScope]*types.RequestAction)
|
|
|
gitInstallationID := reqScopes[types.GitInstallationScope].Resource.UInt
|
|
|
|
|
|
- gitInstallation, err := p.config.Repo.GithubAppInstallation().ReadGithubAppInstallation(proj.ID, gitInstallationID)
|
|
|
+ gitInstallation, err := p.config.Repo.GithubAppInstallation().ReadGithubAppInstallationByInstallationID(gitInstallationID)
|
|
|
|
|
|
if err != nil {
|
|
|
- if err == gorm.ErrRecordNotFound {
|
|
|
- apierrors.HandleAPIError(p.config, w, r, apierrors.NewErrForbidden(
|
|
|
- fmt.Errorf("github app installation with id %d not found in project %d", gitInstallationID, proj.ID),
|
|
|
- ))
|
|
|
- } else {
|
|
|
- apierrors.HandleAPIError(p.config, w, r, apierrors.NewErrInternal(err))
|
|
|
- }
|
|
|
+ apierrors.HandleAPIError(p.config, w, r, apierrors.NewErrInternal(err))
|
|
|
+ return
|
|
|
+ }
|
|
|
|
|
|
+ if err := p.doesUserHaveGitInstallationAccess(user.GithubAppIntegrationID, gitInstallationID); err != nil {
|
|
|
+ apierrors.HandleAPIError(p.config, w, r, apierrors.NewErrInternal(err))
|
|
|
return
|
|
|
}
|
|
|
|
|
|
@@ -62,3 +63,70 @@ func (p *GitInstallationScopedMiddleware) ServeHTTP(w http.ResponseWriter, r *ht
|
|
|
func NewGitInstallationContext(ctx context.Context, ga *integrations.GithubAppInstallation) context.Context {
|
|
|
return context.WithValue(ctx, types.GitInstallationScope, ga)
|
|
|
}
|
|
|
+
|
|
|
+// 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 (p *GitInstallationScopedMiddleware) doesUserHaveGitInstallationAccess(githubIntegrationID, gitInstallationID uint) error {
|
|
|
+ oauthInt, err := p.config.Repo.GithubAppOAuthIntegration().ReadGithubAppOauthIntegration(githubIntegrationID)
|
|
|
+
|
|
|
+ if err != nil {
|
|
|
+ return err
|
|
|
+ }
|
|
|
+
|
|
|
+ if _, _, err = oauth.GetAccessToken(oauthInt.SharedOAuthModel,
|
|
|
+ p.config.GithubConf,
|
|
|
+ oauth.MakeUpdateGithubAppOauthIntegrationFunction(oauthInt, p.config.Repo)); err != nil {
|
|
|
+ return err
|
|
|
+ }
|
|
|
+
|
|
|
+ client := github.NewClient(p.config.GithubConf.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 {
|
|
|
+ return err
|
|
|
+ }
|
|
|
+
|
|
|
+ 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 {
|
|
|
+ return err
|
|
|
+ }
|
|
|
+
|
|
|
+ for _, org := range orgs {
|
|
|
+ accountIDs = append(accountIDs, *org.ID)
|
|
|
+ }
|
|
|
+
|
|
|
+ if pages.NextPage == 0 {
|
|
|
+ break
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ installations, err := p.config.Repo.GithubAppInstallation().ReadGithubAppInstallationByAccountIDs(accountIDs)
|
|
|
+
|
|
|
+ for _, installation := range installations {
|
|
|
+ if uint(installation.InstallationID) == gitInstallationID {
|
|
|
+ return nil
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ return apierrors.NewErrForbidden(
|
|
|
+ fmt.Errorf("user does not have access to github app installation %d", gitInstallationID),
|
|
|
+ )
|
|
|
+}
|