Przeglądaj źródła

updated list to allow query by cluster

Stefan McShane 3 lat temu
rodzic
commit
ccc83a166c

+ 66 - 0
api/server/authz/api_contract.go

@@ -0,0 +1,66 @@
+package authz
+
+import (
+	"context"
+	"fmt"
+	"net/http"
+
+	"github.com/google/uuid"
+	"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"
+	"gorm.io/gorm"
+)
+
+type APIContractRevisionScopedFactory struct {
+	config *config.Config
+}
+
+func NewAPIContractRevisionScopedFactory(
+	config *config.Config,
+) *APIContractRevisionScopedFactory {
+	return &APIContractRevisionScopedFactory{config}
+}
+
+func (p *APIContractRevisionScopedFactory) Middleware(next http.Handler) http.Handler {
+	return &APIContractRevisionMiddleware{next, p.config}
+}
+
+type APIContractRevisionMiddleware struct {
+	next   http.Handler
+	config *config.Config
+}
+
+func (n *APIContractRevisionMiddleware) ServeHTTP(w http.ResponseWriter, r *http.Request) {
+	ctx := r.Context()
+	reqScopes, _ := ctx.Value(types.RequestScopeCtxKey).(map[types.PermissionScope]*types.RequestAction)
+	proj, _ := ctx.Value(types.ProjectScope).(*models.Project)
+
+	apiContractRevisionID := reqScopes[types.APIContractRevisionScope].Resource.Name
+
+	uid, err := uuid.Parse(apiContractRevisionID)
+	if err != nil {
+		apierrors.HandleAPIError(n.config.Logger, n.config.Alerter, w, r, apierrors.NewErrInternal(err), true)
+		return
+	}
+
+	rev, err := n.config.Repo.APIContractRevisioner().Get(ctx, uid)
+	if err != nil {
+		if err == gorm.ErrRecordNotFound {
+			apierrors.HandleAPIError(n.config.Logger, n.config.Alerter, w, r, apierrors.NewErrForbidden(
+				fmt.Errorf("revision with id %s not found in project %d", apiContractRevisionID, proj.ID),
+			), true)
+			return
+		}
+		apierrors.HandleAPIError(n.config.Logger, n.config.Alerter, w, r, apierrors.NewErrInternal(err), true)
+		return
+	}
+
+	r = r.Clone(NewAPIContractRevisionContext(ctx, rev))
+	n.next.ServeHTTP(w, r)
+}
+
+func NewAPIContractRevisionContext(ctx context.Context, apiContractRevision models.APIContractRevision) context.Context {
+	return context.WithValue(ctx, types.APIContractRevisionScope, apiContractRevision)
+}

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

@@ -136,6 +136,8 @@ func getRequestActionForEndpoint(
 			resource.UInt, reqErr = requestutils.GetURLParamUint(r, types.URLParamInviteID)
 		case types.GitlabIntegrationScope:
 			resource.UInt, reqErr = requestutils.GetURLParamUint(r, types.URLParamIntegrationID)
+		case types.APIContractRevisionScope:
+			resource.Name, reqErr = requestutils.GetURLParamString(r, types.URLParamAPIContractRevisionID)
 		}
 
 		if reqErr != nil {

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

@@ -1,6 +1,8 @@
 package policy
 
 import (
+	"fmt"
+
 	"github.com/porter-dev/porter/api/types"
 )
 
@@ -26,17 +28,21 @@ func HasScopeAccess(
 			continue
 		}
 
+		fmt.Println("STEFAN1", matchDocs)
 		for matchScope, matchDoc := range matchDocs {
 			// for the matching scope, make sure it matches the allowed resources if the
 			// resource list is explicitly set
 			if len(matchDoc.Resources) > 0 && reqScopes[matchScope].Verb != types.APIVerbList {
 				if !isResourceAllowed(matchDoc, reqScopes[matchScope].Resource) {
+					fmt.Println("STEFANR", matchScope, *matchDoc, reqScopes[matchScope].Resource)
 					isValid = false
 				}
 			}
 
 			// for the matching scope, make sure it matches the allowed verbs
 			if !isVerbAllowed(matchDoc, reqScopes[matchScope].Verb) {
+				fmt.Println("STEFANP", *matchDoc, matchScope, reqScopes[matchScope].Verb)
+
 				isValid = false
 			}
 		}

+ 2 - 7
api/server/handlers/api_contract/list.go

@@ -26,20 +26,15 @@ func NewAPIContractRevisionListHandler(
 	}
 }
 
-// APIContractListRequest contains all parameters required for filtering APIContractRevisions
-type APIContractRevisionsListRequest struct {
-	ProjectID uint `json:"project_id"`
-	ClusterID uint `json:"cluster_id"`
-}
-
 // ServeHTTP returns a list of Porter API contract revisions for a given project.
 // If clusterID is also given, it will list by project_id, cluster_id
 func (c *APIContractRevisionListHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
 	proj, _ := r.Context().Value(types.ProjectScope).(*models.Project)
+	cluster, _ := r.Context().Value(types.ProjectScope).(*models.Cluster)
 
 	ctx := r.Context()
 
-	revisions, err := c.Config().Repo.APIContractRevisioner().List(ctx, proj.ID, 0)
+	revisions, err := c.Config().Repo.APIContractRevisioner().List(ctx, proj.ID, cluster.ID)
 	if err != nil {
 		e := fmt.Errorf("error creating new capi config: %w", err)
 		c.HandleAPIError(w, r, apierrors.NewErrInternal(e))

+ 14 - 1
api/server/handlers/api_contract/update.go

@@ -38,7 +38,7 @@ func (c *APIContractUpdateHandler) ServeHTTP(w http.ResponseWriter, r *http.Requ
 
 	var apiContract porterv1.Contract
 
-	err := helpers.UnmarshalContractObject(r.Body, &apiContract)
+	err := helpers.UnmarshalContractObjectFromReader(r.Body, &apiContract)
 	if err != nil {
 		e := fmt.Errorf("error parsing api contract: %w", err)
 		c.HandleAPIError(w, r, apierrors.NewErrInternal(e))
@@ -53,6 +53,18 @@ func (c *APIContractUpdateHandler) ServeHTTP(w http.ResponseWriter, r *http.Requ
 
 	cl := apiContract.Cluster
 
+	if cl.CloudProviderCredentialsId == "" {
+		e := errors.New("missing cloud_provider_credential_identifier")
+		c.HandleAPIError(w, r, apierrors.NewErrInternal(e))
+		return
+	}
+
+	if cl.GetEksKind() == nil {
+		e := errors.New("missing eks_kind_values")
+		c.HandleAPIError(w, r, apierrors.NewErrInternal(e))
+		return
+	}
+
 	if cl.ClusterId == 0 {
 		dbClusterInput := models.Cluster{
 			ProjectID:                         uint(cl.ProjectId),
@@ -61,6 +73,7 @@ func (c *APIContractUpdateHandler) ServeHTTP(w http.ResponseWriter, r *http.Requ
 			CloudProvider:                     "AWS",
 			CloudProviderCredentialIdentifier: cl.CloudProviderCredentialsId,
 			Name:                              cl.GetEksKind().ClusterName,
+			VanityName:                        cl.GetEksKind().ClusterName,
 		}
 		dbCluster, err := c.Config().Repo.Cluster().CreateCluster(&dbClusterInput)
 		if err != nil {

+ 2 - 1
api/server/handlers/project/create.go

@@ -40,7 +40,8 @@ func (p *ProjectCreateHandler) ServeHTTP(w http.ResponseWriter, r *http.Request)
 	user, _ := r.Context().Value(types.UserScope).(*models.User)
 
 	proj := &models.Project{
-		Name: request.Name,
+		Name:                   request.Name,
+		CapiProvisionerEnabled: true,
 	}
 
 	var err error

+ 31 - 3
api/server/router/project.go

@@ -1262,7 +1262,7 @@ func getProjectRoutes(
 		Router:   r,
 	})
 
-	// POST /api/project/{project_id}/contract -> apiContract.NewAPIContractUpdateHandler
+	// POST /api/projects/{project_id}/contract -> apiContract.NewAPIContractUpdateHandler
 	updateAPIContractEndpoint := factory.NewAPIEndpoint(
 		&types.APIRequestMetadata{
 			Verb:   types.APIVerbCreate,
@@ -1290,7 +1290,7 @@ func getProjectRoutes(
 		Router:   r,
 	})
 
-	// GET /api/project/{project_id}/contracts -> apiContract.NewAPIContractUpdateHandler
+	// GET /api/projects/{project_id}/contracts -> apiContract.NewAPIContractUpdateHandler
 	listAPIContractRevisionsEndpoint := factory.NewAPIEndpoint(
 		&types.APIRequestMetadata{
 			Verb:   types.APIVerbCreate,
@@ -1311,12 +1311,40 @@ func getProjectRoutes(
 		factory.GetDecoderValidator(),
 		factory.GetResultWriter(),
 	)
-
 	routes = append(routes, &router.Route{
 		Endpoint: listAPIContractRevisionsEndpoint,
 		Handler:  listAPIContractRevisionHandler,
 		Router:   r,
 	})
 
+	// DELETE /api/projects/{project_id}/contracts/{revision_id} -> apiContract.NewAPIContractUpdateHandler
+	deleteAPIContractRevisionsEndpoint := factory.NewAPIEndpoint(
+		&types.APIRequestMetadata{
+			Verb:   types.APIVerbDelete,
+			Method: types.HTTPVerbPost,
+			Path: &types.Path{
+				Parent:       basePath,
+				RelativePath: fmt.Sprintf("%s/contracts/{%s}", relPath, types.URLParamAPIContractRevisionID),
+			},
+			Scopes: []types.PermissionScope{
+				types.UserScope,
+				types.ProjectScope,
+				types.APIContractRevisionScope,
+			},
+		},
+	)
+
+	deleteAPIContractRevisionHandler := apiContract.NewAPIContractRevisionDeleteHandler(
+		config,
+		factory.GetDecoderValidator(),
+		factory.GetResultWriter(),
+	)
+
+	routes = append(routes, &router.Route{
+		Endpoint: deleteAPIContractRevisionsEndpoint,
+		Handler:  deleteAPIContractRevisionHandler,
+		Router:   r,
+	})
+
 	return routes, newPath
 }

+ 30 - 16
api/types/policy.go

@@ -5,21 +5,22 @@ import "time"
 type PermissionScope string
 
 const (
-	UserScope               PermissionScope = "user"
-	ProjectScope            PermissionScope = "project"
-	ClusterScope            PermissionScope = "cluster"
-	RegistryScope           PermissionScope = "registry"
-	InviteScope             PermissionScope = "invite"
-	HelmRepoScope           PermissionScope = "helm_repo"
-	InfraScope              PermissionScope = "infra"
-	OperationScope          PermissionScope = "operation"
-	GitInstallationScope    PermissionScope = "git_installation"
-	NamespaceScope          PermissionScope = "namespace"
-	SettingsScope           PermissionScope = "settings"
-	ReleaseScope            PermissionScope = "release"
-	StackScope              PermissionScope = "stack"
-	GitlabIntegrationScope  PermissionScope = "gitlab_integration"
-	PreviewEnvironmentScope PermissionScope = "preview_environment"
+	UserScope                PermissionScope = "user"
+	ProjectScope             PermissionScope = "project"
+	ClusterScope             PermissionScope = "cluster"
+	RegistryScope            PermissionScope = "registry"
+	InviteScope              PermissionScope = "invite"
+	HelmRepoScope            PermissionScope = "helm_repo"
+	InfraScope               PermissionScope = "infra"
+	OperationScope           PermissionScope = "operation"
+	GitInstallationScope     PermissionScope = "git_installation"
+	NamespaceScope           PermissionScope = "namespace"
+	SettingsScope            PermissionScope = "settings"
+	ReleaseScope             PermissionScope = "release"
+	StackScope               PermissionScope = "stack"
+	GitlabIntegrationScope   PermissionScope = "gitlab_integration"
+	PreviewEnvironmentScope  PermissionScope = "preview_environment"
+	APIContractRevisionScope PermissionScope = "contract_revision"
 )
 
 type NameOrUInt struct {
@@ -56,7 +57,8 @@ var ScopeHeirarchy = ScopeTree{
 		InfraScope: {
 			OperationScope: {},
 		},
-		SettingsScope: {},
+		SettingsScope:            {},
+		APIContractRevisionScope: {},
 	},
 }
 
@@ -91,6 +93,10 @@ var AdminPolicy = []*PolicyDocument{
 				Scope: SettingsScope,
 				Verbs: ReadWriteVerbGroup(),
 			},
+			APIContractRevisionScope: {
+				Scope: APIContractRevisionScope,
+				Verbs: ReadWriteVerbGroup(),
+			},
 		},
 	},
 }
@@ -124,6 +130,10 @@ var DeveloperPolicy = []*PolicyDocument{
 				Scope: SettingsScope,
 				Verbs: ReadVerbGroup(),
 			},
+			APIContractRevisionScope: {
+				Scope: APIContractRevisionScope,
+				Verbs: ReadWriteVerbGroup(),
+			},
 		},
 	},
 }
@@ -157,6 +167,10 @@ var ViewerPolicy = []*PolicyDocument{
 				Scope: SettingsScope,
 				Verbs: []APIVerb{},
 			},
+			APIContractRevisionScope: {
+				Scope: APIContractRevisionScope,
+				Verbs: ReadVerbGroup(),
+			},
 		},
 	},
 }

+ 15 - 14
api/types/request.go

@@ -33,20 +33,21 @@ const (
 type URLParam string
 
 const (
-	URLParamProjectID         URLParam = "project_id"
-	URLParamClusterID         URLParam = "cluster_id"
-	URLParamRegistryID        URLParam = "registry_id"
-	URLParamHelmRepoID        URLParam = "helm_repo_id"
-	URLParamGitInstallationID URLParam = "git_installation_id"
-	URLParamInfraID           URLParam = "infra_id"
-	URLParamOperationID       URLParam = "operation_id"
-	URLParamInviteID          URLParam = "invite_id"
-	URLParamNamespace         URLParam = "namespace"
-	URLParamReleaseName       URLParam = "name"
-	URLParamStackID           URLParam = "stack_id"
-	URLParamReleaseVersion    URLParam = "version"
-	URLParamWildcard          URLParam = "*"
-	URLParamIntegrationID     URLParam = "integration_id"
+	URLParamProjectID             URLParam = "project_id"
+	URLParamClusterID             URLParam = "cluster_id"
+	URLParamRegistryID            URLParam = "registry_id"
+	URLParamHelmRepoID            URLParam = "helm_repo_id"
+	URLParamGitInstallationID     URLParam = "git_installation_id"
+	URLParamInfraID               URLParam = "infra_id"
+	URLParamOperationID           URLParam = "operation_id"
+	URLParamInviteID              URLParam = "invite_id"
+	URLParamNamespace             URLParam = "namespace"
+	URLParamReleaseName           URLParam = "name"
+	URLParamStackID               URLParam = "stack_id"
+	URLParamReleaseVersion        URLParam = "version"
+	URLParamWildcard              URLParam = "*"
+	URLParamIntegrationID         URLParam = "integration_id"
+	URLParamAPIContractRevisionID URLParam = "contract_revision_id"
 )
 
 type Path struct {

+ 2 - 2
go.mod

@@ -24,7 +24,7 @@ require (
 	github.com/go-redis/redis/v8 v8.11.0
 	github.com/go-test/deep v1.0.7
 	github.com/golang-jwt/jwt/v4 v4.4.1 // indirect
-	github.com/golang/protobuf v1.5.2
+	github.com/golang/protobuf v1.5.2 // indirect
 	github.com/google/go-github/v39 v39.2.0
 	github.com/google/go-github/v41 v41.0.0
 	github.com/gorilla/schema v1.2.0
@@ -74,7 +74,7 @@ require (
 	github.com/glebarez/sqlite v1.6.0
 	github.com/nats-io/nats.go v1.24.0
 	github.com/open-policy-agent/opa v0.44.0
-	github.com/porter-dev/api-contracts v0.0.38
+	github.com/porter-dev/api-contracts v0.0.39
 	github.com/santhosh-tekuri/jsonschema/v5 v5.0.1
 	github.com/stefanmcshane/helm v0.0.0-20221213002717-88a4a2c6e77d
 	github.com/xanzy/go-gitlab v0.68.0

+ 2 - 4
go.sum

@@ -1466,10 +1466,8 @@ github.com/pmezard/go-difflib v0.0.0-20151028094244-d8ed2627bdf0/go.mod h1:iKH77
 github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
 github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
 github.com/polyfloyd/go-errorlint v0.0.0-20210722154253-910bb7978349/go.mod h1:wi9BfjxjF/bwiZ701TzmfKu6UKC357IOAtNr0Td0Lvw=
-github.com/porter-dev/api-contracts v0.0.37 h1:q9J7d5Z2DsS8HFM/x6lXU+Bescc8D5FhFCH0mxgmofM=
-github.com/porter-dev/api-contracts v0.0.37/go.mod h1:qr2L58mJLr5DUGV5OPw3REiSrQvJq6TgkKyEWP95dyU=
-github.com/porter-dev/api-contracts v0.0.38 h1:rR+z+t++6gA8DKmDYKeFJoPB2LB+ABBkpI/l6iFfSBI=
-github.com/porter-dev/api-contracts v0.0.38/go.mod h1:qr2L58mJLr5DUGV5OPw3REiSrQvJq6TgkKyEWP95dyU=
+github.com/porter-dev/api-contracts v0.0.39 h1:CWMIEXQUaRv8JncDT4l0sisRwXXEKRvfgQCQ6HtAOmE=
+github.com/porter-dev/api-contracts v0.0.39/go.mod h1:qr2L58mJLr5DUGV5OPw3REiSrQvJq6TgkKyEWP95dyU=
 github.com/porter-dev/switchboard v0.0.0-20221019155755-67ff2bf04935 h1:hfb3nt3AJXIBbevu6ARTg9SdOkMP6WLbKBiG5hT5rcc=
 github.com/porter-dev/switchboard v0.0.0-20221019155755-67ff2bf04935/go.mod h1:xSPzqSFMQ6OSbp42fhCi4AbGbQbsm6nRvOkrblFeXU4=
 github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI=

+ 1 - 2
go.work.sum

@@ -7,10 +7,9 @@ github.com/go-redis/redis v6.15.8+incompatible h1:BKZuG6mCnRj5AOaWJXoCgf6rqTYnYJ
 github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
 github.com/jackc/chunkreader v1.0.0 h1:4s39bBR8ByfqH+DKm8rQA3E1LHZWB9XWcrz8fqaZbe0=
 github.com/jackc/pgproto3 v1.1.0 h1:FYYE4yRw+AgI8wXIinMlNjBbp/UitDJwfj5LqqewP1A=
-github.com/nats-io/jwt v0.3.0 h1:xdnzwFETV++jNc4W1mw//qFyJGb2ABOombmZJQS4+Qo=
 github.com/nats-io/nats.go v1.9.1 h1:ik3HbLhZ0YABLto7iX80pZLPw/6dx3T+++MZJwLnMrQ=
 github.com/nats-io/nkeys v0.1.0 h1:qMd4+pRHgdr1nAClu+2h/2a5F2TmKcCzjCDazVgRoX4=
-github.com/nats-io/nuid v1.0.1 h1:5iA8DT8V7q8WK2EScv2padNa/rTESc1KdnPw4TC2paw=
+github.com/pborman/uuid v1.2.0 h1:J7Q5mO4ysT1dv8hyrUGHb9+ooztCXu1D8MY8DZYsu3g=
 github.com/tchap/go-patricia v2.2.6+incompatible h1:JvoDL7JSoIP2HDE8AbDH3zC8QBPxmzYe32HHy5yQ+Ck=
 golang.org/x/net v0.0.0-20221014081412-f15817d10f9b/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk=
 golang.org/x/net v0.3.0/go.mod h1:MBQ8lrhLObU/6UmLb4fmbmk5OcyYmqtbGd/9yIeKjEE=

+ 3 - 0
internal/repository/api_contract.go

@@ -3,6 +3,7 @@ package repository
 import (
 	"context"
 
+	"github.com/google/uuid"
 	"github.com/porter-dev/porter/internal/models"
 )
 
@@ -11,4 +12,6 @@ type APIContractRevisioner interface {
 	Insert(ctx context.Context, conf models.APIContractRevision) (models.APIContractRevision, error)
 	// List returns a slice of APIContractRevision, sorted by created_at descending
 	List(ctx context.Context, projectID uint, clusterID uint) ([]models.APIContractRevision, error)
+	Get(ctx context.Context, revisionID uuid.UUID) (models.APIContractRevision, error)
+	Delete(ctx context.Context, projectID uint, clusterID uint, revisionID uuid.UUID) error
 }

+ 39 - 0
internal/repository/gorm/api_contract.go

@@ -2,6 +2,7 @@ package gorm
 
 import (
 	"context"
+	"errors"
 
 	"github.com/google/uuid"
 	"github.com/porter-dev/porter/internal/models"
@@ -50,3 +51,41 @@ func (cr APIContractRepository) List(ctx context.Context, projectID uint, cluste
 
 	return confs, nil
 }
+
+// Delete deleted a record in the api_contract_revisions table
+func (cr APIContractRepository) Delete(ctx context.Context, projectID uint, clusterID uint, revisionID uuid.UUID) error {
+
+	conf := models.APIContractRevision{
+		ID:        revisionID,
+		ProjectID: int(projectID),
+	}
+
+	if clusterID != 0 {
+		conf.ClusterID = int(clusterID)
+	}
+
+	tx := cr.db.Delete(&conf)
+	if tx.Error != nil {
+		return tx.Error
+	}
+	return nil
+}
+
+// Get returns a record in the api_contract_revisions table
+func (cr APIContractRepository) Get(ctx context.Context, revisionID uuid.UUID) (models.APIContractRevision, error) {
+
+	if revisionID == uuid.Nil {
+		return models.APIContractRevision{}, errors.New("invalid contract revision id supplied")
+	}
+
+	rev, ok := cr.db.Get(revisionID.String())
+	if !ok {
+		return models.APIContractRevision{}, errors.New("no contract revision found for that id")
+	}
+
+	if revision, ok := rev.(models.APIContractRevision); ok {
+		return revision, nil
+	}
+
+	return models.APIContractRevision{}, errors.New("unable to convert response to contract revision")
+}

+ 10 - 4
internal/repository/test/api_contract.go

@@ -4,15 +4,13 @@ import (
 	"context"
 	"errors"
 
+	"github.com/google/uuid"
 	"github.com/porter-dev/porter/internal/models"
 	"github.com/porter-dev/porter/internal/repository"
-	"gorm.io/gorm"
 )
 
 // APIContractRepository uses gorm.DB for querying the database
-type APIContractRepository struct {
-	db *gorm.DB
-}
+type APIContractRepository struct{}
 
 // NewAPIContractRevisioner creates an APIRevision connection
 func NewAPIContractRevisioner() repository.APIContractRevisioner {
@@ -29,3 +27,11 @@ func (cr APIContractRepository) List(ctx context.Context, projectID uint, cluste
 	var confs []models.APIContractRevision
 	return confs, errors.New("not implemented")
 }
+
+func (cr APIContractRepository) Delete(ctx context.Context, projectID uint, clusterID uint, revisionID uuid.UUID) error {
+	return errors.New("not implemented")
+}
+
+func (cr APIContractRepository) Get(ctx context.Context, revisionID uuid.UUID) (models.APIContractRevision, error) {
+	return models.APIContractRevision{}, errors.New("not implemented")
+}