Skip to content
151 changes: 151 additions & 0 deletions pkg/platformhubpolicies/policy.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,151 @@
package platformhubpolicies

import (
"sync"
"time"

"github.com/go-playground/validator/v10"
"github.com/go-playground/validator/v10/non-standard/validators"
)

// PolicyDraft represents a set of information to create new Hub policy.
type PolicyDraft struct {
GitRef string
Slug string
Name string
Description string
ScopeRego string
ConditionsRego string
ViolationAction string
ViolationReason string
}

// Policy represents a set of information to create new Hub policy.
type Policy interface {
GetName() string
SetName(string)

GetDescription() string
SetDescription(string)

GetScopeRego() string
SetScopeRego(string)

GetConditionsRego() string
SetConditionsRego(string)

GetViolationAction() string
SetViolationAction(string)

GetViolationReason() string
SetViolationReason(string)

Validate() error

PolicyKey
}

type PolicyKey interface {
GetGitRef() string
GetSlug() string
}

// PublishedPolicy represents a read-only view of a published Platform Hub policy version.
type PublishedPolicy interface {
GetID() string
GetPublishedDate() time.Time
GetGitRef() string
GetGitCommit() string
GetName() string
GetDescription() string
GetViolationReason() string
GetViolationAction() string
GetScopeRego() string
GetConditionsRego() string
IsActivated() bool

PublishedPolicyKey
}

type PublishedPolicyKey interface {
GetSlug() string
GetVersion() string
}

type persistedPolicy struct {
GitRef string `json:"GitRef" validate:"required,notblank"`
Slug string `json:"Slug" validate:"required,notblank"`
Name string `json:"Name" validate:"required,notblank"`
Description string `json:"Description,omitempty"`
ScopeRego string `json:"ScopeRego" validate:"required,notblank"`
ConditionsRego string `json:"ConditionsRego" validate:"required,notblank"`
ViolationAction string `json:"ViolationAction" validate:"required,notblank"`
ViolationReason string `json:"ViolationReason,omitempty"`
}

func (p *persistedPolicy) GetName() string { return p.Name }
func (p *persistedPolicy) SetName(name string) { p.Name = name }
func (p *persistedPolicy) GetGitRef() string { return p.GitRef }
func (p *persistedPolicy) GetSlug() string { return p.Slug }
func (p *persistedPolicy) GetDescription() string { return p.Description }
func (p *persistedPolicy) SetDescription(d string) { p.Description = d }
func (p *persistedPolicy) GetScopeRego() string { return p.ScopeRego }
func (p *persistedPolicy) SetScopeRego(s string) { p.ScopeRego = s }
func (p *persistedPolicy) GetConditionsRego() string { return p.ConditionsRego }
func (p *persistedPolicy) SetConditionsRego(c string) { p.ConditionsRego = c }
func (p *persistedPolicy) GetViolationReason() string { return p.ViolationReason }
func (p *persistedPolicy) SetViolationReason(r string) { p.ViolationReason = r }
func (p *persistedPolicy) GetViolationAction() string { return p.ViolationAction }
func (p *persistedPolicy) SetViolationAction(a string) { p.ViolationAction = a }

// Validate checks the state of the policy and returns an error if invalid.
func (p *persistedPolicy) Validate() error {
validate, err := getValidator()
if err != nil {
return err
}

return validate.Struct(p)
}

var getValidator = sync.OnceValues(buildValidator)

func buildValidator() (*validator.Validate, error) {
v := validator.New()
err := v.RegisterValidation("notblank", validators.NotBlank)
if err != nil {
return nil, err
}

return v, nil
}

type publishedPolicyVersion struct {
ID string `json:"Id"`
Slug string `json:"Slug"`
Version string `json:"Version"`
PublishedDate time.Time `json:"PublishedDate"`
GitRef string `json:"GitRef"`
GitCommit string `json:"GitCommit"`
Name string `json:"Name"`
Description string `json:"Description,omitempty"`
ViolationReason string `json:"ViolationReason,omitempty"`
ViolationAction string `json:"ViolationAction"`
RegoScope string `json:"RegoScope"`
RegoConditions string `json:"RegoConditions"`
IsActive bool `json:"IsActive"`
}

func (v *publishedPolicyVersion) GetID() string { return v.ID }
func (v *publishedPolicyVersion) GetSlug() string { return v.Slug }
func (v *publishedPolicyVersion) GetVersion() string { return v.Version }
func (v *publishedPolicyVersion) GetPublishedDate() time.Time { return v.PublishedDate }
func (v *publishedPolicyVersion) GetGitRef() string { return v.GitRef }
func (v *publishedPolicyVersion) GetGitCommit() string { return v.GitCommit }
func (v *publishedPolicyVersion) GetName() string { return v.Name }
func (v *publishedPolicyVersion) GetDescription() string { return v.Description }
func (v *publishedPolicyVersion) GetViolationReason() string { return v.ViolationReason }
func (v *publishedPolicyVersion) GetViolationAction() string { return v.ViolationAction }
func (v *publishedPolicyVersion) GetScopeRego() string { return v.RegoScope }
func (v *publishedPolicyVersion) GetConditionsRego() string { return v.RegoConditions }
func (v *publishedPolicyVersion) IsActivated() bool { return v.IsActive }
205 changes: 205 additions & 0 deletions pkg/platformhubpolicies/policy_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,205 @@
package platformhubpolicies

import (
"testing"

"github.com/stretchr/testify/require"
)

func TestPolicyValidate_Name(t *testing.T) {
policy := newPolicyBuilder().Build()

// Valid
policy.SetName("Valid Name")
require.NoError(t, policy.Validate())

// Invalid
policy.SetName("")
require.ErrorContains(t, policy.Validate(), "Name")

policy.SetName(" ")
require.ErrorContains(t, policy.Validate(), "Name")
}

func TestPolicyValidate_GitRef(t *testing.T) {
policy := newPolicyBuilder().WithGitRef("main").Build()

// Valid
require.NoError(t, policy.Validate())

// Invalid
policy = newPolicyBuilder().WithGitRef("").Build()
require.ErrorContains(t, policy.Validate(), "GitRef")

policy = newPolicyBuilder().WithGitRef(" ").Build()
require.ErrorContains(t, policy.Validate(), "GitRef")
}

func TestPolicyValidate_Slug(t *testing.T) {
policy := newPolicyBuilder().WithSlug("valid_slug").Build()

// Valid
require.NoError(t, policy.Validate())

// Invalid
policy = newPolicyBuilder().WithSlug("").Build()
require.ErrorContains(t, policy.Validate(), "Slug")

policy = newPolicyBuilder().WithSlug(" ").Build()
require.ErrorContains(t, policy.Validate(), "Slug")
}

func TestPolicyValidate_Description(t *testing.T) {
policy := newPolicyBuilder().Build()

// Description is optional
policy.SetDescription("Description")
require.NoError(t, policy.Validate())

policy.SetDescription("")
require.NoError(t, policy.Validate())
}

func TestPolicyValidate_ScopeRego(t *testing.T) {
policy := newPolicyBuilder().Build()

// Valid
policy.SetScopeRego("package scope")
require.NoError(t, policy.Validate())

// Invalid
policy.SetScopeRego("")
require.ErrorContains(t, policy.Validate(), "ScopeRego")

policy.SetScopeRego(" ")
require.ErrorContains(t, policy.Validate(), "ScopeRego")
}

func TestPolicyValidate_ConditionsRego(t *testing.T) {
policy := newPolicyBuilder().Build()

// Valid
policy.SetConditionsRego("package conditions")
require.NoError(t, policy.Validate())

// Invalid
policy.SetConditionsRego("")
require.ErrorContains(t, policy.Validate(), "ConditionsRego")

policy.SetConditionsRego(" ")
require.ErrorContains(t, policy.Validate(), "ConditionsRego")
}

func TestPolicyValidate_ViolationAction(t *testing.T) {
policy := newPolicyBuilder().Build()

// Valid
policy.SetViolationAction("block")
require.NoError(t, policy.Validate())

// Invalid
policy.SetViolationAction("")
require.ErrorContains(t, policy.Validate(), "ViolationAction")

policy.SetViolationAction(" ")
require.ErrorContains(t, policy.Validate(), "ViolationAction")
}

func TestPolicyValidate_ViolationReason(t *testing.T) {
policy := newPolicyBuilder().Build()

// ViolationReason is optional
policy.SetViolationReason("Some reason")
require.NoError(t, policy.Validate())

policy.SetViolationReason("")
require.NoError(t, policy.Validate())
}

type policyBuilder struct {
name string
gitRef string
slug string
description string
scopeRego string
conditionsRego string
violationReason string
violationAction string
}

func newPolicyBuilder() *policyBuilder {
return &policyBuilder{
name: "Dummy",
gitRef: "main",
slug: "dummy",
scopeRego: "package dummy",
conditionsRego: "package dummy",
violationAction: "block",
}
}

func (b *policyBuilder) WithName(name string) *policyBuilder {
b.name = name
return b
}

func (b *policyBuilder) WithGitRef(gitRef string) *policyBuilder {
b.gitRef = gitRef
return b
}

func (b *policyBuilder) WithSlug(slug string) *policyBuilder {
b.slug = slug
return b
}

func (b *policyBuilder) WithDescription(description string) *policyBuilder {
b.description = description
return b
}

func (b *policyBuilder) WithScopeRego(scopeRego string) *policyBuilder {
b.scopeRego = scopeRego
return b
}

func (b *policyBuilder) WithConditionsRego(conditionsRego string) *policyBuilder {
b.conditionsRego = conditionsRego
return b
}

func (b *policyBuilder) WithViolationReason(violationReason string) *policyBuilder {
b.violationReason = violationReason
return b
}

func (b *policyBuilder) WithViolationAction(violationAction string) *policyBuilder {
b.violationAction = violationAction
return b
}

func (b *policyBuilder) Build() Policy {
return &persistedPolicy{
Name: b.name,
GitRef: b.gitRef,
Slug: b.slug,
ScopeRego: b.scopeRego,
ConditionsRego: b.conditionsRego,
ViolationAction: b.violationAction,
Description: b.description,
ViolationReason: b.violationReason,
}
}

func (b *policyBuilder) BuildDraft() *PolicyDraft {
return &PolicyDraft{
Name: b.name,
GitRef: b.gitRef,
Slug: b.slug,
ScopeRego: b.scopeRego,
ConditionsRego: b.conditionsRego,
ViolationAction: b.violationAction,
Description: b.description,
ViolationReason: b.violationReason,
}
}
Loading
Loading