Parcourir la source

Merge branch 'nafees/rbac-improvements' of github.com:porter-dev/porter into nico/rbac-crud-operations

jnfrati il y a 3 ans
Parent
commit
40ec55916d

+ 1 - 1
api/server/handlers/policy/create.go

@@ -33,7 +33,7 @@ func (p *PolicyCreateHandler) ServeHTTP(w http.ResponseWriter, r *http.Request)
 	user, _ := r.Context().Value(types.UserScope).(*models.User)
 	proj, _ := r.Context().Value(types.ProjectScope).(*models.Project)
 
-	req := &types.CreatePolicy{}
+	req := &types.CreatePolicyRequest{}
 
 	if ok := p.DecodeAndValidate(w, r, req); !ok {
 		return

+ 58 - 0
api/server/handlers/policy/delete.go

@@ -0,0 +1,58 @@
+package policy
+
+import (
+	"net/http"
+
+	"github.com/porter-dev/porter/api/server/handlers"
+	"github.com/porter-dev/porter/api/server/shared"
+	"github.com/porter-dev/porter/api/server/shared/apierrors"
+	"github.com/porter-dev/porter/api/server/shared/config"
+	"github.com/porter-dev/porter/api/server/shared/requestutils"
+	"github.com/porter-dev/porter/api/types"
+	"github.com/porter-dev/porter/internal/models"
+)
+
+type PolicyDeleteHandler struct {
+	handlers.PorterHandlerReadWriter
+}
+
+func NewPolicyDeleteHandler(
+	config *config.Config,
+	decoderValidator shared.RequestDecoderValidator,
+	writer shared.ResultWriter,
+) *PolicyDeleteHandler {
+	return &PolicyDeleteHandler{
+		PorterHandlerReadWriter: handlers.NewDefaultPorterHandler(config, decoderValidator, writer),
+	}
+}
+
+func (p *PolicyDeleteHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
+	proj, _ := r.Context().Value(types.ProjectScope).(*models.Project)
+
+	policyID, reqErr := requestutils.GetURLParamString(r, types.URLParamPolicyID)
+
+	if reqErr != nil {
+		p.HandleAPIError(w, r, reqErr)
+		return
+	}
+
+	policy, err := p.Repo().Policy().ReadPolicy(proj.ID, policyID)
+
+	if err == nil {
+		policy, err = p.Repo().Policy().DeletePolicy(policy)
+
+		if err != nil {
+			p.HandleAPIError(w, r, apierrors.NewErrInternal(err))
+			return
+		}
+	}
+
+	res, err := policy.ToAPIPolicyType()
+
+	if err != nil {
+		p.HandleAPIError(w, r, apierrors.NewErrInternal(err))
+		return
+	}
+
+	p.WriteResult(w, r, res)
+}

+ 91 - 0
api/server/handlers/policy/update.go

@@ -0,0 +1,91 @@
+package policy
+
+import (
+	"bytes"
+	"encoding/json"
+	"errors"
+	"fmt"
+	"net/http"
+
+	"github.com/porter-dev/porter/api/server/handlers"
+	"github.com/porter-dev/porter/api/server/shared"
+	"github.com/porter-dev/porter/api/server/shared/apierrors"
+	"github.com/porter-dev/porter/api/server/shared/config"
+	"github.com/porter-dev/porter/api/server/shared/requestutils"
+	"github.com/porter-dev/porter/api/types"
+	"github.com/porter-dev/porter/internal/models"
+	"gorm.io/gorm"
+)
+
+type PolicyUpdateHandler struct {
+	handlers.PorterHandlerReadWriter
+}
+
+func NewPolicyUpdateHandler(
+	config *config.Config,
+	decoderValidator shared.RequestDecoderValidator,
+	writer shared.ResultWriter,
+) *PolicyUpdateHandler {
+	return &PolicyUpdateHandler{
+		PorterHandlerReadWriter: handlers.NewDefaultPorterHandler(config, decoderValidator, writer),
+	}
+}
+
+func (p *PolicyUpdateHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
+	proj, _ := r.Context().Value(types.ProjectScope).(*models.Project)
+
+	policyID, reqErr := requestutils.GetURLParamString(r, types.URLParamPolicyID)
+
+	if reqErr != nil {
+		p.HandleAPIError(w, r, reqErr)
+		return
+	}
+
+	req := &types.UpdatePolicyRequest{}
+
+	if ok := p.DecodeAndValidate(w, r, req); !ok {
+		return
+	}
+
+	policy, err := p.Repo().Policy().ReadPolicy(proj.ID, policyID)
+
+	if err != nil {
+		if errors.Is(err, gorm.ErrRecordNotFound) {
+			p.HandleAPIError(w, r, apierrors.NewErrPassThroughToClient(
+				fmt.Errorf("policy with id %s not found in project", policyID),
+				http.StatusNotFound,
+			))
+			return
+		}
+
+		p.HandleAPIError(w, r, apierrors.NewErrInternal(err))
+		return
+	}
+
+	policyBytes, err := json.Marshal(req.Policy)
+
+	if err != nil {
+		p.HandleAPIError(w, r, apierrors.NewErrInternal(err))
+		return
+	}
+
+	if !bytes.Equal(policyBytes, policy.PolicyBytes) {
+		policy.PolicyBytes = policyBytes
+
+		policy, err = p.Repo().Policy().UpdatePolicy(policy)
+
+		if err != nil {
+			p.HandleAPIError(w, r, apierrors.NewErrInternal(err))
+			return
+		}
+	}
+
+	res, err := policy.ToAPIPolicyType()
+
+	if err != nil {
+		p.HandleAPIError(w, r, apierrors.NewErrInternal(err))
+		return
+	}
+
+	p.WriteResult(w, r, res)
+}

+ 58 - 0
api/server/router/project.go

@@ -1035,6 +1035,64 @@ func getProjectRoutes(
 		Router:   r,
 	})
 
+	// PATCH /api/projects/{project_id}/policies/{policy_id} -> policy.NewPolicyUpdateHandler
+	policyUpdateEndpoint := factory.NewAPIEndpoint(
+		&types.APIRequestMetadata{
+			Verb:   types.APIVerbUpdate,
+			Method: types.HTTPVerbPatch,
+			Path: &types.Path{
+				Parent:       basePath,
+				RelativePath: fmt.Sprintf("%s/policies/{%s}", relPath, types.URLParamPolicyID),
+			},
+			Scopes: []types.PermissionScope{
+				types.UserScope,
+				types.ProjectScope,
+				types.SettingsScope,
+			},
+		},
+	)
+
+	policyUpdateHandler := policy.NewPolicyUpdateHandler(
+		config,
+		factory.GetDecoderValidator(),
+		factory.GetResultWriter(),
+	)
+
+	routes = append(routes, &router.Route{
+		Endpoint: policyUpdateEndpoint,
+		Handler:  policyUpdateHandler,
+		Router:   r,
+	})
+
+	// DELETE /api/projects/{project_id}/policies/{policy_id} -> policy.NewPolicyDeleteHandler
+	policyDeleteEndpoint := factory.NewAPIEndpoint(
+		&types.APIRequestMetadata{
+			Verb:   types.APIVerbDelete,
+			Method: types.HTTPVerbDelete,
+			Path: &types.Path{
+				Parent:       basePath,
+				RelativePath: fmt.Sprintf("%s/policies/{%s}", relPath, types.URLParamPolicyID),
+			},
+			Scopes: []types.PermissionScope{
+				types.UserScope,
+				types.ProjectScope,
+				types.SettingsScope,
+			},
+		},
+	)
+
+	policyDeleteHandler := policy.NewPolicyDeleteHandler(
+		config,
+		factory.GetDecoderValidator(),
+		factory.GetResultWriter(),
+	)
+
+	routes = append(routes, &router.Route{
+		Endpoint: policyDeleteEndpoint,
+		Handler:  policyDeleteHandler,
+		Router:   r,
+	})
+
 	//  POST /api/projects/{project_id}/api_token -> api_token.NewAPITokenCreateHandler
 	apiTokenCreateEndpoint := factory.NewAPIEndpoint(
 		&types.APIRequestMetadata{

+ 5 - 1
api/types/policy.go

@@ -157,11 +157,15 @@ var ViewerPolicy = []*PolicyDocument{
 	},
 }
 
-type CreatePolicy struct {
+type CreatePolicyRequest struct {
 	Name   string            `json:"name" form:"required"`
 	Policy []*PolicyDocument `json:"policy" form:"required"`
 }
 
+type UpdatePolicyRequest struct {
+	Policy []*PolicyDocument `json:"policy" form:"required"`
+}
+
 const URLParamPolicyID URLParam = "policy_id"
 
 type APIPolicyMeta struct {

+ 6 - 0
internal/models/project.go

@@ -48,6 +48,12 @@ type Project struct {
 	// provisioned aws infra
 	Infras []Infra `json:"infras"`
 
+	// linked policy documents
+	ProjectPolicies []Policy
+
+	// project roles
+	ProjectRoles []ProjectRole
+
 	// auth mechanisms
 	KubeIntegrations   []ints.KubeIntegration   `json:"kube_integrations"`
 	BasicIntegrations  []ints.BasicIntegration  `json:"basic_integrations"`

+ 18 - 0
internal/models/project_role.go

@@ -0,0 +1,18 @@
+package models
+
+import (
+	"gorm.io/gorm"
+)
+
+type ProjectRole struct {
+	gorm.Model
+
+	ProjectID uint
+
+	UniqueID string `gorm:"unique"`
+
+	Name string
+
+	Policies []Policy `gorm:"many2many:role_policies"`
+	Users    []User   `gorm:"many2many:user_roles"`
+}

+ 1 - 0
internal/repository/gorm/migrate.go

@@ -50,6 +50,7 @@ func AutoMigrate(db *gorm.DB, debug bool) error {
 		&models.Allowlist{},
 		&models.APIToken{},
 		&models.Policy{},
+		&models.ProjectRole{},
 		&models.Tag{},
 		&models.Stack{},
 		&models.StackRevision{},

+ 37 - 7
internal/repository/gorm/policy.go

@@ -17,21 +17,34 @@ func NewPolicyRepository(db *gorm.DB) repository.PolicyRepository {
 	return &PolicyRepository{db}
 }
 
-func (repo *PolicyRepository) CreatePolicy(a *models.Policy) (*models.Policy, error) {
-	if err := repo.db.Create(a).Error; err != nil {
+func (repo *PolicyRepository) CreatePolicy(policy *models.Policy) (*models.Policy, error) {
+	var project models.Project
+
+	if err := repo.db.Where("id = ?", policy.ProjectID).First(&project).Error; err != nil {
+		return nil, err
+	}
+
+	assoc := repo.db.Model(&project).Association("ProjectPolicies")
+
+	if assoc.Error != nil {
+		return nil, assoc.Error
+	}
+
+	if err := assoc.Append(policy); err != nil {
 		return nil, err
 	}
-	return a, nil
+
+	return policy, nil
 }
 
 func (repo *PolicyRepository) ListPoliciesByProjectID(projectID uint) ([]*models.Policy, error) {
-	policys := []*models.Policy{}
+	var policies []*models.Policy
 
-	if err := repo.db.Where("project_id = ?", projectID).Find(&policys).Error; err != nil {
+	if err := repo.db.Where("project_id = ?", projectID).Find(&policies).Error; err != nil {
 		return nil, err
 	}
 
-	return policys, nil
+	return policies, nil
 }
 
 func (repo *PolicyRepository) ReadPolicy(projectID uint, uid string) (*models.Policy, error) {
@@ -57,8 +70,25 @@ func (repo *PolicyRepository) UpdatePolicy(
 func (repo *PolicyRepository) DeletePolicy(
 	policy *models.Policy,
 ) (*models.Policy, error) {
-	if err := repo.db.Delete(&policy).Error; err != nil {
+	var project models.Project
+
+	if err := repo.db.Where("id = ?", policy.ProjectID).First(&project).Error; err != nil {
 		return nil, err
 	}
+
+	assoc := repo.db.Model(&project).Association("ProjectPolicies")
+
+	if assoc.Error != nil {
+		return nil, assoc.Error
+	}
+
+	if err := assoc.Delete(policy); err != nil {
+		return nil, err
+	}
+
+	if err := repo.db.Delete(policy).Error; err != nil {
+		return nil, err
+	}
+
 	return policy, nil
 }