Sfoglia il codice sorgente

add infra scoped mw + get route

Alexander Belanger 4 anni fa
parent
commit
3372951bcb

+ 64 - 0
api/server/authz/infra.go

@@ -0,0 +1,64 @@
+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"
+	"gorm.io/gorm"
+)
+
+type InfraScopedFactory struct {
+	config *shared.Config
+}
+
+func NewInfraScopedFactory(
+	config *shared.Config,
+) *InfraScopedFactory {
+	return &InfraScopedFactory{config}
+}
+
+func (p *InfraScopedFactory) Middleware(next http.Handler) http.Handler {
+	return &InfraScopedMiddleware{next, p.config}
+}
+
+type InfraScopedMiddleware struct {
+	next   http.Handler
+	config *shared.Config
+}
+
+func (p *InfraScopedMiddleware) 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)
+	infraID := reqScopes[types.InfraScope].Resource.UInt
+
+	infra, err := p.config.Repo.Infra().ReadInfra(proj.ID, infraID)
+
+	if err != nil {
+		if err == gorm.ErrRecordNotFound {
+			apierrors.HandleAPIError(w, p.config.Logger, apierrors.NewErrForbidden(
+				fmt.Errorf("infra with id %d not found in project %d", infraID, proj.ID),
+			))
+		} else {
+			apierrors.HandleAPIError(w, p.config.Logger, apierrors.NewErrInternal(err))
+		}
+
+		return
+	}
+
+	ctx := NewInfraContext(r.Context(), infra)
+	r = r.WithContext(ctx)
+	p.next.ServeHTTP(w, r)
+}
+
+func NewInfraContext(ctx context.Context, infra *models.Infra) context.Context {
+	return context.WithValue(ctx, types.InfraScope, infra)
+}

+ 4 - 0
api/server/authz/policy.go

@@ -102,6 +102,10 @@ func getRequestActionForEndpoint(
 			resource.UInt, reqErr = GetURLParamUint(r, string(types.URLParamRegistryID))
 		case types.HelmRepoScope:
 			resource.UInt, reqErr = GetURLParamUint(r, string(types.URLParamHelmRepoID))
+		case types.GitInstallationScope:
+			resource.UInt, reqErr = GetURLParamUint(r, string(types.URLParamGitInstallationID))
+		case types.InfraScope:
+			resource.UInt, reqErr = GetURLParamUint(r, string(types.URLParamInfraID))
 		case types.NamespaceScope:
 			resource.Name, reqErr = GetURLParamString(r, string(types.URLParamNamespace))
 		case types.ReleaseScope:

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

@@ -0,0 +1,32 @@
+package infra
+
+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"
+)
+
+type InfraGetHandler struct {
+	handlers.PorterHandlerWriter
+	authz.KubernetesAgentGetter
+}
+
+func NewInfraGetHandler(
+	config *shared.Config,
+	writer shared.ResultWriter,
+) *InfraGetHandler {
+	return &InfraGetHandler{
+		PorterHandlerWriter:   handlers.NewDefaultPorterHandler(config, nil, writer),
+		KubernetesAgentGetter: authz.NewOutOfClusterAgentGetter(config),
+	}
+}
+
+func (c *InfraGetHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
+	infra, _ := r.Context().Value(types.InfraScope).(*models.Infra)
+
+	c.WriteResult(w, infra.ToInfraType())
+}

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

@@ -0,0 +1,83 @@
+package router
+
+import (
+	"github.com/go-chi/chi"
+	"github.com/porter-dev/porter/api/server/handlers/infra"
+	"github.com/porter-dev/porter/api/server/shared"
+	"github.com/porter-dev/porter/api/types"
+)
+
+func NewInfraScopedRegisterer(children ...*Registerer) *Registerer {
+	return &Registerer{
+		GetRoutes: GetInfraScopedRoutes,
+		Children:  children,
+	}
+}
+
+func GetInfraScopedRoutes(
+	r chi.Router,
+	config *shared.Config,
+	basePath *types.Path,
+	factory shared.APIEndpointFactory,
+	children ...*Registerer,
+) []*Route {
+	routes, projPath := getInfraRoutes(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 getInfraRoutes(
+	r chi.Router,
+	config *shared.Config,
+	basePath *types.Path,
+	factory shared.APIEndpointFactory,
+) ([]*Route, *types.Path) {
+	relPath := "/infras/{infra_id}"
+
+	newPath := &types.Path{
+		Parent:       basePath,
+		RelativePath: relPath,
+	}
+
+	routes := make([]*Route, 0)
+
+	// GET /api/projects/{project_id}/infras/{infra_id} -> infra.NewInfraGetHandler
+	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.InfraScope,
+			},
+		},
+	)
+
+	getHandler := infra.NewInfraGetHandler(
+		config,
+		factory.GetResultWriter(),
+	)
+
+	routes = append(routes, &Route{
+		Endpoint: getEndpoint,
+		Handler:  getHandler,
+		Router:   r,
+	})
+
+	return routes, newPath
+}

+ 14 - 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)
+	infraRegisterer := NewInfraScopedRegisterer()
 	gitInstallationRegisterer := NewGitInstallationScopedRegisterer()
 	registryRegisterer := NewRegistryScopedRegisterer()
 	helmRepoRegisterer := NewHelmRepoScopedRegisterer()
@@ -33,6 +34,7 @@ func NewAPIRouter(config *shared.Config) *chi.Mux {
 		helmRepoRegisterer,
 		inviteRegisterer,
 		gitInstallationRegisterer,
+		infraRegisterer,
 	)
 	userRegisterer := NewUserScopedRegisterer(projRegisterer)
 
@@ -107,6 +109,14 @@ func registerRoutes(config *shared.Config, routes []*Route) {
 	// after authorization. Each subsequent http.Handler can lookup the gitinstallation in context.
 	gitInstallationFactory := authz.NewGitInstallationScopedFactory(config)
 
+	// Create a new "invite-scoped" factory which will create a new invite-scoped request
+	// after authorization. Each subsequent http.Handler can lookup the invite in context.
+	inviteFactory := authz.NewInviteScopedFactory(config)
+
+	// Create a new "infra-scoped" factory which will create a new infra-scoped request
+	// after authorization. Each subsequent http.Handler can lookup the infra in context.
+	infraFactory := authz.NewInfraScopedFactory(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)
@@ -137,8 +147,12 @@ func registerRoutes(config *shared.Config, routes []*Route) {
 				atomicGroup.Use(helmRepoFactory.Middleware)
 			case types.RegistryScope:
 				atomicGroup.Use(registryFactory.Middleware)
+			case types.InviteScope:
+				atomicGroup.Use(inviteFactory.Middleware)
 			case types.GitInstallationScope:
 				atomicGroup.Use(gitInstallationFactory.Middleware)
+			case types.InfraScope:
+				atomicGroup.Use(infraFactory.Middleware)
 			case types.ReleaseScope:
 				atomicGroup.Use(releaseFactory.Middleware)
 			}

+ 6 - 2
api/types/policy.go

@@ -9,6 +9,7 @@ const (
 	RegistryScope        PermissionScope = "registry"
 	InviteScope          PermissionScope = "invite"
 	HelmRepoScope        PermissionScope = "helm_repo"
+	InfraScope           PermissionScope = "infra"
 	GitInstallationScope PermissionScope = "git_installation"
 	NamespaceScope       PermissionScope = "namespace"
 	SettingsScope        PermissionScope = "settings"
@@ -39,8 +40,11 @@ var ScopeHeirarchy = ScopeTree{
 				ReleaseScope: {},
 			},
 		},
-		RegistryScope: {},
-		SettingsScope: {},
+		RegistryScope:        {},
+		HelmRepoScope:        {},
+		GitInstallationScope: {},
+		InfraScope:           {},
+		SettingsScope:        {},
 	},
 }
 

+ 1 - 0
api/types/request.go

@@ -38,6 +38,7 @@ const (
 	URLParamRegistryID        URLParam = "registry_id"
 	URLParamHelmRepoID        URLParam = "helm_repo_id"
 	URLParamGitInstallationID URLParam = "git_installation_id"
+	URLParamInfraID           URLParam = "infra_id"
 	URLParamInviteID          URLParam = "invite_id"
 	URLParamNamespace         URLParam = "namespace"
 	URLParamReleaseName       URLParam = "name"

+ 3 - 3
internal/kubernetes/provisioner/global_stream.go

@@ -107,7 +107,7 @@ func GlobalStreamListener(
 			kind, projID, infraID, err := models.ParseUniqueName(fmt.Sprintf("%v", msg.Values["id"]))
 
 			if fmt.Sprintf("%v", msg.Values["status"]) == "created" {
-				infra, err := repo.Infra().ReadInfra(infraID)
+				infra, err := repo.Infra().ReadInfra(projID, infraID)
 
 				if err != nil {
 					continue
@@ -306,7 +306,7 @@ func GlobalStreamListener(
 					}
 				}
 			} else if fmt.Sprintf("%v", msg.Values["status"]) == "error" {
-				infra, err := repo.Infra().ReadInfra(infraID)
+				infra, err := repo.Infra().ReadInfra(projID, infraID)
 
 				if err != nil {
 					continue
@@ -320,7 +320,7 @@ func GlobalStreamListener(
 					continue
 				}
 			} else if fmt.Sprintf("%v", msg.Values["status"]) == "destroyed" {
-				infra, err := repo.Infra().ReadInfra(infraID)
+				infra, err := repo.Infra().ReadInfra(projID, infraID)
 
 				if err != nil {
 					continue

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

@@ -52,10 +52,10 @@ func (repo *InfraRepository) CreateInfra(infra *models.Infra) (*models.Infra, er
 }
 
 // ReadInfra gets a aws infra specified by a unique id
-func (repo *InfraRepository) ReadInfra(id uint) (*models.Infra, error) {
+func (repo *InfraRepository) ReadInfra(projectID, infraID uint) (*models.Infra, error) {
 	infra := &models.Infra{}
 
-	if err := repo.db.Where("id = ?", id).First(&infra).Error; err != nil {
+	if err := repo.db.Where("project_id = ? AND id = ?", projectID, infraID).First(&infra).Error; err != nil {
 		return nil, err
 	}
 

+ 1 - 1
internal/repository/gorm/infra_test.go

@@ -31,7 +31,7 @@ func TestCreateInfra(t *testing.T) {
 		t.Fatalf("%v\n", err)
 	}
 
-	infra, err = tester.repo.Infra().ReadInfra(infra.Model.ID)
+	infra, err = tester.repo.Infra().ReadInfra(tester.initProjects[0].Model.ID, infra.Model.ID)
 
 	if err != nil {
 		t.Fatalf("%v\n", err)

+ 1 - 1
internal/repository/infra.go

@@ -7,7 +7,7 @@ import (
 // InfraRepository represents the set of queries on the Infra model
 type InfraRepository interface {
 	CreateInfra(repo *models.Infra) (*models.Infra, error)
-	ReadInfra(id uint) (*models.Infra, error)
+	ReadInfra(projectID, infraID uint) (*models.Infra, error)
 	ListInfrasByProjectID(projectID uint) ([]*models.Infra, error)
 	UpdateInfra(repo *models.Infra) (*models.Infra, error)
 }

+ 3 - 2
internal/repository/test/infra.go

@@ -10,8 +10,8 @@ import (
 
 // InfraRepository implements repository.InfraRepository
 type InfraRepository struct {
-	canQuery  bool
-	infras []*models.Infra
+	canQuery bool
+	infras   []*models.Infra
 }
 
 // NewInfraRepository will return errors if canQuery is false
@@ -38,6 +38,7 @@ func (repo *InfraRepository) CreateInfra(
 
 // ReadInfra finds a aws infra by id
 func (repo *InfraRepository) ReadInfra(
+	projectID,
 	id uint,
 ) (*models.Infra, error) {
 	if !repo.canQuery {