Bläddra i källkod

add git installation mw + get route

Alexander Belanger 4 år sedan
förälder
incheckning
f3b0f45726

+ 65 - 0
api/server/authz/git_installation.go

@@ -0,0 +1,65 @@
+package authz
+
+import (
+	"context"
+	"fmt"
+	"net/http"
+
+	"github.com/porter-dev/porter/api/server/authz/policy"
+	"github.com/porter-dev/porter/api/server/shared"
+	"github.com/porter-dev/porter/api/server/shared/apierrors"
+	"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"
+)
+
+type GitInstallationScopedFactory struct {
+	config *shared.Config
+}
+
+func NewGitInstallationScopedFactory(
+	config *shared.Config,
+) *GitInstallationScopedFactory {
+	return &GitInstallationScopedFactory{config}
+}
+
+func (p *GitInstallationScopedFactory) Middleware(next http.Handler) http.Handler {
+	return &GitInstallationScopedMiddleware{next, p.config}
+}
+
+type GitInstallationScopedMiddleware struct {
+	next   http.Handler
+	config *shared.Config
+}
+
+func (p *GitInstallationScopedMiddleware) ServeHTTP(w http.ResponseWriter, r *http.Request) {
+	// read the project to check scopes
+	proj, _ := r.Context().Value(types.ProjectScope).(*models.Project)
+
+	// get the registry id from the URL param context
+	reqScopes, _ := r.Context().Value(RequestScopeCtxKey).(map[types.PermissionScope]*policy.RequestAction)
+	gitInstallationID := reqScopes[types.GitInstallationScope].Resource.UInt
+
+	gitInstallation, err := p.config.Repo.GithubAppInstallation().ReadGithubAppInstallation(proj.ID, gitInstallationID)
+
+	if err != nil {
+		if err == gorm.ErrRecordNotFound {
+			apierrors.HandleAPIError(w, p.config.Logger, apierrors.NewErrForbidden(
+				fmt.Errorf("github app installation with id %d not found in project %d", gitInstallationID, proj.ID),
+			))
+		} else {
+			apierrors.HandleAPIError(w, p.config.Logger, apierrors.NewErrInternal(err))
+		}
+
+		return
+	}
+
+	ctx := NewGitInstallationContext(r.Context(), gitInstallation)
+	r = r.WithContext(ctx)
+	p.next.ServeHTTP(w, r)
+}
+
+func NewGitInstallationContext(ctx context.Context, ga *integrations.GithubAppInstallation) context.Context {
+	return context.WithValue(ctx, types.GitInstallationScope, ga)
+}

+ 32 - 0
api/server/handlers/gitinstallation/get.go

@@ -0,0 +1,32 @@
+package gitinstallation
+
+import (
+	"net/http"
+
+	"github.com/porter-dev/porter/api/server/authz"
+	"github.com/porter-dev/porter/api/server/handlers"
+	"github.com/porter-dev/porter/api/server/shared"
+	"github.com/porter-dev/porter/api/types"
+	"github.com/porter-dev/porter/internal/models/integrations"
+)
+
+type GitInstallationGetHandler struct {
+	handlers.PorterHandlerWriter
+	authz.KubernetesAgentGetter
+}
+
+func NewGitInstallationGetHandler(
+	config *shared.Config,
+	writer shared.ResultWriter,
+) *GitInstallationGetHandler {
+	return &GitInstallationGetHandler{
+		PorterHandlerWriter:   handlers.NewDefaultPorterHandler(config, nil, writer),
+		KubernetesAgentGetter: authz.NewOutOfClusterAgentGetter(config),
+	}
+}
+
+func (c *GitInstallationGetHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
+	ga, _ := r.Context().Value(types.GitInstallationScope).(*integrations.GithubAppInstallation)
+
+	c.WriteResult(w, ga.ToGitInstallationType())
+}

+ 83 - 0
api/server/router/git_installation.go

@@ -0,0 +1,83 @@
+package router
+
+import (
+	"github.com/go-chi/chi"
+	"github.com/porter-dev/porter/api/server/handlers/gitinstallation"
+	"github.com/porter-dev/porter/api/server/shared"
+	"github.com/porter-dev/porter/api/types"
+)
+
+func NewGitInstallationScopedRegisterer(children ...*Registerer) *Registerer {
+	return &Registerer{
+		GetRoutes: GetGitInstallationScopedRoutes,
+		Children:  children,
+	}
+}
+
+func GetGitInstallationScopedRoutes(
+	r chi.Router,
+	config *shared.Config,
+	basePath *types.Path,
+	factory shared.APIEndpointFactory,
+	children ...*Registerer,
+) []*Route {
+	routes, projPath := getGitInstallationRoutes(r, config, basePath, factory)
+
+	if len(children) > 0 {
+		r.Route(projPath.RelativePath, func(r chi.Router) {
+			for _, child := range children {
+				childRoutes := child.GetRoutes(r, config, basePath, factory, child.Children...)
+
+				routes = append(routes, childRoutes...)
+			}
+		})
+	}
+
+	return routes
+}
+
+func getGitInstallationRoutes(
+	r chi.Router,
+	config *shared.Config,
+	basePath *types.Path,
+	factory shared.APIEndpointFactory,
+) ([]*Route, *types.Path) {
+	relPath := "/gitrepos/{git_installation_id}"
+
+	newPath := &types.Path{
+		Parent:       basePath,
+		RelativePath: relPath,
+	}
+
+	routes := make([]*Route, 0)
+
+	// GET /api/projects/{project_id}/gitrepos/{git_installation_id} -> registry.NewGitInstallationGetHandler
+	getEndpoint := factory.NewAPIEndpoint(
+		&types.APIRequestMetadata{
+			Verb:   types.APIVerbGet,
+			Method: types.HTTPVerbGet,
+			Path: &types.Path{
+				Parent:       basePath,
+				RelativePath: relPath,
+			},
+			Scopes: []types.PermissionScope{
+				types.UserScope,
+				types.ProjectScope,
+				types.GitInstallationScope,
+			},
+		},
+	)
+
+	getHandler := gitinstallation.NewGitInstallationGetHandler(
+		config,
+		factory.GetResultWriter(),
+	)
+
+	routes = append(routes, &Route{
+		Endpoint: getEndpoint,
+		Handler:  getHandler,
+		Router:   r,
+	})
+
+	return routes, newPath
+}

+ 8 - 0
api/server/router/router.go

@@ -23,6 +23,7 @@ func NewAPIRouter(config *shared.Config) *chi.Mux {
 	releaseRegisterer := NewReleaseScopedRegisterer()
 	namespaceRegisterer := NewNamespaceScopedRegisterer(releaseRegisterer)
 	clusterRegisterer := NewClusterScopedRegisterer(namespaceRegisterer)
+	gitInstallationRegisterer := NewGitInstallationScopedRegisterer()
 	registryRegisterer := NewRegistryScopedRegisterer()
 	helmRepoRegisterer := NewHelmRepoScopedRegisterer()
 	inviteRegisterer := NewInviteScopedRegisterer()
@@ -31,6 +32,7 @@ func NewAPIRouter(config *shared.Config) *chi.Mux {
 		registryRegisterer,
 		helmRepoRegisterer,
 		inviteRegisterer,
+		gitInstallationRegisterer,
 	)
 	userRegisterer := NewUserScopedRegisterer(projRegisterer)
 
@@ -101,6 +103,10 @@ func registerRoutes(config *shared.Config, routes []*Route) {
 	// after authorization. Each subsequent http.Handler can lookup the registry in context.
 	registryFactory := authz.NewRegistryScopedFactory(config)
 
+	// Create a new "gitinstallation-scoped" factory which will create a new gitinstallation-scoped request
+	// after authorization. Each subsequent http.Handler can lookup the gitinstallation in context.
+	gitInstallationFactory := authz.NewGitInstallationScopedFactory(config)
+
 	// Create a new "release-scoped" factory which will create a new release-scoped request
 	// after authorization. Each subsequent http.Handler can lookup the release in context.
 	releaseFactory := authz.NewReleaseScopedFactory(config)
@@ -131,6 +137,8 @@ func registerRoutes(config *shared.Config, routes []*Route) {
 				atomicGroup.Use(helmRepoFactory.Middleware)
 			case types.RegistryScope:
 				atomicGroup.Use(registryFactory.Middleware)
+			case types.GitInstallationScope:
+				atomicGroup.Use(gitInstallationFactory.Middleware)
 			case types.ReleaseScope:
 				atomicGroup.Use(releaseFactory.Middleware)
 			}

+ 13 - 0
api/types/git_installation.go

@@ -0,0 +1,13 @@
+package types
+
+type GitInstallation struct {
+	ID uint `json:"id"`
+
+	// Can belong to either a user or an organization
+	AccountID int64 `json:"account_id"`
+
+	// Installation ID (used for authentication)
+	InstallationID int64 `json:"installation_id"`
+}
+
+type GetGitInstallationResponse GitInstallation

+ 2 - 5
api/types/helm_repo.go

@@ -1,7 +1,5 @@
 package types
 
-import "github.com/porter-dev/porter/internal/models/integrations"
-
 type HelmRepo struct {
 	ID uint `json:"id"`
 
@@ -12,7 +10,6 @@ type HelmRepo struct {
 	Name string `json:"name"`
 
 	RepoURL string `json:"repo_name"`
-
-	// The integration service for this registry
-	Service integrations.IntegrationService `json:"service"`
 }
+
+type GetHelmRepoResponse HelmRepo

+ 2 - 0
api/types/invite.go

@@ -8,3 +8,5 @@ type Invite struct {
 	Accepted bool   `json:"accepted"`
 	Kind     string `json:"kind"`
 }
+
+type GetInviteResponse Invite

+ 10 - 9
api/types/policy.go

@@ -3,15 +3,16 @@ package types
 type PermissionScope string
 
 const (
-	UserScope      PermissionScope = "user"
-	ProjectScope   PermissionScope = "project"
-	ClusterScope   PermissionScope = "cluster"
-	RegistryScope  PermissionScope = "registry"
-	InviteScope    PermissionScope = "invite"
-	HelmRepoScope  PermissionScope = "helm_repo"
-	NamespaceScope PermissionScope = "namespace"
-	SettingsScope  PermissionScope = "settings"
-	ReleaseScope   PermissionScope = "release"
+	UserScope            PermissionScope = "user"
+	ProjectScope         PermissionScope = "project"
+	ClusterScope         PermissionScope = "cluster"
+	RegistryScope        PermissionScope = "registry"
+	InviteScope          PermissionScope = "invite"
+	HelmRepoScope        PermissionScope = "helm_repo"
+	GitInstallationScope PermissionScope = "git_installation"
+	NamespaceScope       PermissionScope = "namespace"
+	SettingsScope        PermissionScope = "settings"
+	ReleaseScope         PermissionScope = "release"
 )
 
 type NameOrUInt struct {

+ 10 - 3
api/types/registry.go

@@ -1,7 +1,5 @@
 package types
 
-import "github.com/porter-dev/porter/internal/models/integrations"
-
 type Registry struct {
 	ID uint `json:"id"`
 
@@ -15,8 +13,17 @@ type Registry struct {
 	URL string `json:"url"`
 
 	// The integration service for this registry
-	Service integrations.IntegrationService `json:"service"`
+	Service RegistryService `json:"service"`
 
 	// The infra id, if registry was provisioned with Porter
 	InfraID uint `json:"infra_id"`
 }
+
+type RegistryService string
+
+const (
+	GCR       RegistryService = "gcr"
+	ECR       RegistryService = "ecr"
+	DOCR      RegistryService = "docr"
+	DockerHub RegistryService = "dockerhub"
+)

+ 9 - 8
api/types/request.go

@@ -33,14 +33,15 @@ const (
 type URLParam string
 
 const (
-	URLParamProjectID      URLParam = "project_id"
-	URLParamClusterID      URLParam = "cluster_id"
-	URLParamRegistryID     URLParam = "registry_id"
-	URLParamHelmRepoID     URLParam = "helm_repo_id"
-	URLParamInviteID       URLParam = "invite_id"
-	URLParamNamespace      URLParam = "namespace"
-	URLParamReleaseName    URLParam = "name"
-	URLParamReleaseVersion URLParam = "version"
+	URLParamProjectID         URLParam = "project_id"
+	URLParamClusterID         URLParam = "cluster_id"
+	URLParamRegistryID        URLParam = "registry_id"
+	URLParamHelmRepoID        URLParam = "helm_repo_id"
+	URLParamGitInstallationID URLParam = "git_installation_id"
+	URLParamInviteID          URLParam = "invite_id"
+	URLParamNamespace         URLParam = "namespace"
+	URLParamReleaseName       URLParam = "name"
+	URLParamReleaseVersion    URLParam = "version"
 )
 
 type Path struct {

+ 0 - 11
internal/models/helm_repo.go

@@ -72,21 +72,10 @@ func (hr *HelmRepo) Externalize() *HelmRepoExternal {
 
 // ToHelmRepoType generates an external HelmRepo to be shared over REST
 func (hr *HelmRepo) ToHelmRepoType() *types.HelmRepo {
-	var serv integrations.IntegrationService
-
-	if hr.BasicAuthIntegrationID != 0 {
-		serv = integrations.HelmRepo
-	} else if hr.AWSIntegrationID != 0 {
-		serv = integrations.S3
-	} else if hr.GCPIntegrationID != 0 {
-		serv = integrations.GCS
-	}
-
 	return &types.HelmRepo{
 		ID:        hr.ID,
 		ProjectID: hr.ProjectID,
 		Name:      hr.Name,
 		RepoURL:   hr.RepoURL,
-		Service:   serv,
 	}
 }

+ 12 - 1
internal/models/integrations/github_app.go

@@ -1,6 +1,9 @@
 package integrations
 
-import "gorm.io/gorm"
+import (
+	"github.com/porter-dev/porter/api/types"
+	"gorm.io/gorm"
+)
 
 // GithubAppInstallation is an instance of the porter github app
 // we need to store account/installation id pairs in order to authenticate as the installation
@@ -31,3 +34,11 @@ func (r *GithubAppInstallation) Externalize() *GithubAppInstallationExternal {
 		InstallationID: r.InstallationID,
 	}
 }
+
+func (r *GithubAppInstallation) ToGitInstallationType() *types.GitInstallation {
+	return &types.GitInstallation{
+		ID:             r.ID,
+		AccountID:      r.AccountID,
+		InstallationID: r.InstallationID,
+	}
+}

+ 5 - 5
internal/models/registry.go

@@ -90,16 +90,16 @@ func (r *Registry) Externalize() *RegistryExternal {
 }
 
 func (r *Registry) ToRegistryType() *types.Registry {
-	var serv integrations.IntegrationService
+	var serv types.RegistryService
 
 	if r.AWSIntegrationID != 0 {
-		serv = integrations.ECR
+		serv = types.ECR
 	} else if r.GCPIntegrationID != 0 {
-		serv = integrations.GCR
+		serv = types.GCR
 	} else if r.DOIntegrationID != 0 {
-		serv = integrations.DOCR
+		serv = types.DOCR
 	} else if strings.Contains(r.URL, "index.docker.io") {
-		serv = integrations.DockerHub
+		serv = types.DockerHub
 	}
 
 	uri := r.URL

+ 2 - 2
internal/repository/gorm/auth.go

@@ -1107,10 +1107,10 @@ func (repo *GithubAppInstallationRepository) CreateGithubAppInstallation(am *int
 }
 
 // ReadGithubAppInstallation finds a GithubAppInstallation by id
-func (repo *GithubAppInstallationRepository) ReadGithubAppInstallation(id uint) (*ints.GithubAppInstallation, error) {
+func (repo *GithubAppInstallationRepository) ReadGithubAppInstallation(projectID, gaID uint) (*ints.GithubAppInstallation, error) {
 	ret := &ints.GithubAppInstallation{}
 
-	if err := repo.db.Where("id = ?", id).First(&ret).Error; err != nil {
+	if err := repo.db.Where("project_id = ? AND id = ?", projectID, gaID).First(&ret).Error; err != nil {
 		return nil, err
 	}
 

+ 1 - 1
internal/repository/integrations.go

@@ -72,7 +72,7 @@ type GCPIntegrationRepository interface {
 // GithubAppInstallationRepository represents the set of queries for github app installations
 type GithubAppInstallationRepository interface {
 	CreateGithubAppInstallation(am *ints.GithubAppInstallation) (*ints.GithubAppInstallation, error)
-	ReadGithubAppInstallation(id uint) (*ints.GithubAppInstallation, error)
+	ReadGithubAppInstallation(projectID, gaID uint) (*ints.GithubAppInstallation, error)
 	ReadGithubAppInstallationByAccountID(accountID int64) (*ints.GithubAppInstallation, error)
 	ReadGithubAppInstallationByAccountIDs(accountIDs []int64) ([]*ints.GithubAppInstallation, error)
 	DeleteGithubAppInstallationByAccountID(accountID int64) error

+ 2 - 1
internal/repository/test/auth.go

@@ -2,6 +2,7 @@ package test
 
 import (
 	"errors"
+
 	"github.com/porter-dev/porter/internal/repository"
 	"gorm.io/gorm"
 
@@ -451,7 +452,7 @@ func (repo *GithubAppInstallationRepository) CreateGithubAppInstallation(am *int
 	return am, nil
 }
 
-func (repo *GithubAppInstallationRepository) ReadGithubAppInstallation(id uint) (*ints.GithubAppInstallation, error) {
+func (repo *GithubAppInstallationRepository) ReadGithubAppInstallation(projectID, id uint) (*ints.GithubAppInstallation, error) {
 	if !repo.canQuery {
 		return nil, errors.New("cannot write database")
 	}