Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions pkg/core/policy/policy.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,12 @@ import "digger/pkg/ci"
type Provider interface {
GetAccessPolicy(organisation string, repository string, projectname string) (string, error)
GetPlanPolicy(organisation string, repository string, projectname string) (string, error)
GetDriftPolicy() (string, error)
GetOrganisation() string
}

type Checker interface {
CheckAccessPolicy(ciService ci.OrgService, SCMOrganisation string, SCMrepository string, projectname string, command string, requestedBy string) (bool, error)
CheckPlanPolicy(SCMrepository string, projectname string, planOutput string) (bool, []string, error)
CheckDriftPolicy(SCMOrganisation string, SCMrepository string, projectname string) (bool, error)
}
15 changes: 12 additions & 3 deletions pkg/digger/digger.go
Original file line number Diff line number Diff line change
Expand Up @@ -278,7 +278,7 @@ func RunCommandsPerProject(
}

case "digger drift-detect":
err := runDriftDetection(projectCommands.ProjectName, requestedBy, eventName, diggerExecutor)
err := runDriftDetection(policyChecker, SCMOrganisation, SCMrepository, projectCommands.ProjectName, requestedBy, eventName, diggerExecutor)
if err != nil {
return false, false, fmt.Errorf("failed to run drift detection. %v", err)
}
Expand Down Expand Up @@ -393,7 +393,7 @@ func RunCommandForProject(
return fmt.Errorf("failed to run digger apply command. %v", err)
}
case "digger drift-detect":
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should we shorten it to just 'digger drift' ?

err = runDriftDetection(commands.ProjectName, requestedBy, eventName, diggerExecutor)
err = runDriftDetection(policyChecker, SCMOrganisation, SCMrepository, commands.ProjectName, requestedBy, eventName, diggerExecutor)
if err != nil {
return fmt.Errorf("failed to run digger drift-detect command. %v", err)
}
Expand All @@ -403,12 +403,21 @@ func RunCommandForProject(
return nil
}

func runDriftDetection(projectName string, requestedBy string, eventName string, diggerExecutor execution.Executor) error {
func runDriftDetection(policyChecker policy.Checker, SCMOrganisation string, SCMrepository string, projectName string, requestedBy string, eventName string, diggerExecutor execution.Executor) error {
err := usage.SendUsageRecord(requestedBy, eventName, "drift-detect")
if err != nil {
log.Printf("Failed to send usage report. %v", err)
}
policyAllowed, err := policyChecker.CheckDriftPolicy(SCMOrganisation, SCMrepository, projectName)
if err != nil {
log.Printf("failed to check drift policy. %v", err)
return err
}

if !policyAllowed {
log.Printf("skipping this drift application since drift policy does not allow it")
return nil
}
planPerformed, nonEmptyPlan, plan, _, err := diggerExecutor.Plan()
if err != nil {
log.Printf("Failed to run digger plan command. %v", err)
Expand Down
93 changes: 93 additions & 0 deletions pkg/policy/policy.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,10 @@ func (p NoOpPolicyChecker) CheckPlanPolicy(_ string, _ string, _ string) (bool,
return true, nil, nil
}

func (p NoOpPolicyChecker) CheckDriftPolicy(SCMOrganisation string, SCMrepository string, projectname string) (bool, error) {
return true, nil
}

func getAccessPolicyForOrganisation(p *DiggerHttpPolicyProvider) (string, *http.Response, error) {
organisation := p.DiggerOrganisation
u, err := url.Parse(p.DiggerHost)
Expand Down Expand Up @@ -84,6 +88,32 @@ func getPlanPolicyForOrganisation(p *DiggerHttpPolicyProvider) (string, *http.Re
return string(body), resp, nil
}

func getDriftPolicyForOrganisation(p *DiggerHttpPolicyProvider) (string, *http.Response, error) {
organisation := p.DiggerOrganisation
u, err := url.Parse(p.DiggerHost)
if err != nil {
log.Fatalf("Not able to parse digger cloud url: %v", err)
}
u.Path = "/orgs/" + organisation + "/drift-policy"
req, err := http.NewRequest("GET", u.String(), nil)
if err != nil {
return "", nil, err
}
req.Header.Add("Authorization", "Bearer "+p.AuthToken)

resp, err := p.HttpClient.Do(req)
if err != nil {
return "", nil, err
}
defer resp.Body.Close()

body, err := io.ReadAll(resp.Body)
if err != nil {
return "", resp, nil
}
return string(body), resp, nil
}

func getAccessPolicyForNamespace(p *DiggerHttpPolicyProvider, namespace string, projectName string) (string, *http.Response, error) {
// fetch RBAC policies for project from Digger API
u, err := url.Parse(p.DiggerHost)
Expand Down Expand Up @@ -200,6 +230,20 @@ func (p *DiggerHttpPolicyProvider) GetPlanPolicy(organisation string, repo strin
}
}

func (p *DiggerHttpPolicyProvider) GetDriftPolicy() (string, error) {
content, resp, err := getDriftPolicyForOrganisation(p)
if err != nil {
return "", err
}
if resp.StatusCode == 200 {
return content, nil
} else if resp.StatusCode == 404 {
return "", nil
} else {
return "", errors.New(fmt.Sprintf("unexpected response while fetching organisation policy: %v, code %v", content, resp.StatusCode))
}
}

func (p *DiggerHttpPolicyProvider) GetOrganisation() string {
return p.DiggerOrganisation
}
Expand Down Expand Up @@ -330,3 +374,52 @@ func (p DiggerPolicyChecker) CheckPlanPolicy(SCMrepository string, projectName s

return true, []string{}, nil
}

func (p DiggerPolicyChecker) CheckDriftPolicy(SCMOrganisation string, SCMrepository string, projectName string) (bool, error) {
// TODO: Get rid of organisation if its not needed
//organisation := p.PolicyProvider.GetOrganisation()
policy, err := p.PolicyProvider.GetDriftPolicy()
if err != nil {
fmt.Printf("Error while fetching drift policy: %v", err)
return false, err
}

input := map[string]interface{}{
"organisation": SCMOrganisation,
"project": projectName,
}

if policy == "" {
return true, nil
}

ctx := context.Background()
fmt.Printf("DEBUG: passing the following input policy: %v ||| text: %v", input, policy)
query, err := rego.New(
rego.Query("data.digger.allow"),
rego.Module("digger", policy),
).PrepareForEval(ctx)

if err != nil {
return false, err
}

results, err := query.Eval(ctx, rego.EvalInput(input))
if len(results) == 0 || len(results[0].Expressions) == 0 {
return false, fmt.Errorf("no result found")
}

expressions := results[0].Expressions

for _, expression := range expressions {
decision, ok := expression.Value.(bool)
if !ok {
return false, fmt.Errorf("decision is not a boolean")
}
if !decision {
return false, nil
}
}

return true, nil
}
8 changes: 8 additions & 0 deletions pkg/policy/policy_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,10 @@ func (s *DiggerExamplePolicyProvider) GetPlanPolicy(_ string, _ string, _ string
return "package digger\n", nil
}

func (s *DiggerExamplePolicyProvider) GetDriftPolicy() (string, error) {
return "package digger\n", nil
}

func (s *DiggerExamplePolicyProvider) GetOrganisation() string {
return "ORGANISATIONDIGGER"
}
Expand Down Expand Up @@ -99,6 +103,10 @@ func (s *DiggerExamplePolicyProvider2) GetPlanPolicy(_ string, _ string, _ strin
return "package digger\n\ndeny[sprintf(message, [resource.address])] {\n message := \"Cannot create EC2 instances!\"\n resource := input.terraform.resource_changes[_]\n resource.change.actions[_] == \"create\"\n resource[type] == \"aws_instance\"\n}\n", nil
}

func (s *DiggerExamplePolicyProvider2) GetDriftPolicy() (string, error) {
return "package digger\n", nil
}

func (s *DiggerExamplePolicyProvider2) GetOrganisation() string {
return "ORGANISATIONDIGGER"
}
Expand Down
4 changes: 4 additions & 0 deletions pkg/utils/mocks.go
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,10 @@ func (t MockPolicyChecker) CheckPlanPolicy(projectName string, command string, r
return false, nil, nil
}

func (t MockPolicyChecker) CheckDriftPolicy(SCMOrganisation string, SCMrepository string, projectname string) (bool, error) {
return true, nil
}

type MockPullRequestManager struct {
ChangedFiles []string
Teams []string
Expand Down