Skip to content

Commit

Permalink
ENG-14563: Policy Sets (#579)
Browse files Browse the repository at this point in the history
* Added policy wizards API. Note! not including the listing of wizards, but only management of policy sets.

* Fixed invalid policy set resource template

* Rename policywizardv1 to policysetv1 and improve URL handling

* removed "v1" from policy set go package name.

* Added MaxItem 1 for scope, and switched to set instead of list.

* Revert to list for scope.

* DefaultContextHandler => HTTPContextHandler

* use gRPC to implement policy sets

* Removed un-used constant apiPathPolicySet, updated dependencies and regenerated docs.

---------

Co-authored-by: Deepak Gupta <[email protected]>
  • Loading branch information
gengdahlCyral and yoursnerdly authored Dec 10, 2024
1 parent b8d78a0 commit 7ec5cfc
Show file tree
Hide file tree
Showing 15 changed files with 797 additions and 40 deletions.
7 changes: 0 additions & 7 deletions cyral/internal/policy/v2/model.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,13 +13,6 @@ import (
"github.com/cyralinc/terraform-provider-cyral/cyral/utils"
)

// ChangeInfo represents information about changes to the policy
type ChangeInfo struct {
Actor string `json:"actor,omitempty"`
ActorType string `json:"actorType,omitempty"`
Timestamp string `json:"timestamp,omitempty"`
}

// changeInfoToMap converts ChangeInfo to a map
func changeInfoToMap(c *msg.ChangeInfo) map[string]interface{} {
return map[string]interface{}{
Expand Down
6 changes: 6 additions & 0 deletions cyral/internal/policyset/constants.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package policyset

const (
resourceName = "cyral_policy_set"
dataSourceName = resourceName
)
105 changes: 105 additions & 0 deletions cyral/internal/policyset/datasource.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
package policyset

import (
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"

"github.com/cyralinc/terraform-provider-cyral/cyral/core"
"github.com/cyralinc/terraform-provider-cyral/cyral/core/types/resourcetype"
)

var dsContextHandler = core.ContextHandler{
ResourceName: dataSourceName,
ResourceType: resourcetype.DataSource,
Read: readPolicySet,
}

func dataSourceSchema() *schema.Resource {
return &schema.Resource{
Description: "This data source provides information about a policy set.",
ReadContext: dsContextHandler.ReadContext,
Schema: map[string]*schema.Schema{
"id": {
Description: "Identifier for the policy set.",
Type: schema.TypeString,
Required: true,
},
"wizard_id": {
Description: "The ID of the policy wizard used to create this policy set.",
Type: schema.TypeString,
Computed: true,
},
"name": {
Description: "Name of the policy set.",
Type: schema.TypeString,
Computed: true,
},
"description": {
Description: "Description of the policy set.",
Type: schema.TypeString,
Computed: true,
},
"tags": {
Description: "Tags associated with the policy set.",
Type: schema.TypeList,
Computed: true,
Elem: &schema.Schema{Type: schema.TypeString},
},
"scope": {
Description: "Scope of the policy set.",
Type: schema.TypeList,
Computed: true,
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"repo_ids": {
Description: "List of repository IDs that are in scope. Empty list means all repositories are in scope.",
Type: schema.TypeList,
Elem: &schema.Schema{Type: schema.TypeString},
Computed: true,
},
},
},
},
"wizard_parameters": {
Description: "Parameters passed to the wizard while creating the policy set.",
Type: schema.TypeString,
Computed: true,
},
"enabled": {
Description: "Indicates if the policy set is enabled.",
Type: schema.TypeBool,
Computed: true,
},
"policies": {
Description: "List of policies that comprise the policy set.",
Type: schema.TypeList,
Computed: true,
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"type": {
Description: "Type of the policy.",
Type: schema.TypeString,
Computed: true,
},
"id": {
Description: "ID of the policy.",
Type: schema.TypeString,
Computed: true,
},
},
},
},
"last_updated": {
Description: "Information about when and by whom the policy set was last updated.",
Type: schema.TypeMap,
Computed: true,
Elem: &schema.Schema{Type: schema.TypeString},
},
"created": {
Description: "Information about when and by whom the policy set was created.",
Type: schema.TypeMap,
Computed: true,
Elem: &schema.Schema{Type: schema.TypeString},
},
},
}
}
168 changes: 168 additions & 0 deletions cyral/internal/policyset/model.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,168 @@
package policyset

import (
"context"
"fmt"
"time"

methods "buf.build/gen/go/cyral/policy/grpc/go/policy/v1/policyv1grpc"
msg "buf.build/gen/go/cyral/policy/protocolbuffers/go/policy/v1"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"

"github.com/cyralinc/terraform-provider-cyral/cyral/client"
"github.com/cyralinc/terraform-provider-cyral/cyral/utils"
)

// ToMap converts PolicySetPolicy to a map
func policySetPolicyToMap(p *msg.PolicySetPolicy) map[string]interface{} {
return map[string]interface{}{
"type": p.GetType().String(),
"id": p.GetId(),
}
}

func policiesToMaps(policies []*msg.PolicySetPolicy) []map[string]interface{} {
var result []map[string]interface{}
for _, policy := range policies {
result = append(result, policySetPolicyToMap(policy))
}
return result
}

// changeInfoToMap converts ChangeInfo to a map
func changeInfoToMap(c *msg.ChangeInfo) map[string]interface{} {
return map[string]interface{}{
"actor": c.GetActor(),
"actor_type": c.GetActorType().String(),
"timestamp": c.GetTimestamp().AsTime().Format(time.RFC3339),
}
}

// scopeToMap converts Scope to a list of maps
func scopeToMap(s *msg.Scope) []map[string]interface{} {
return []map[string]interface{}{
{
"repo_ids": s.GetRepoIds(),
},
}
}

// updateSchema writes the policy set data to the schema
func updateSchema(ps *msg.PolicySet, d *schema.ResourceData) error {
if err := d.Set("id", ps.GetId()); err != nil {
return fmt.Errorf("error setting 'id' field: %w", err)
}
if err := d.Set("wizard_id", ps.GetWizardId()); err != nil {
return fmt.Errorf("error setting 'id' field: %w", err)
}
if err := d.Set("name", ps.GetName()); err != nil {
return fmt.Errorf("error setting 'name' field: %w", err)
}
if err := d.Set("description", ps.GetDescription()); err != nil {
return fmt.Errorf("error setting 'description' field: %w", err)
}
if err := d.Set("enabled", ps.GetEnabled()); err != nil {
return fmt.Errorf("error setting 'enabled' field: %w", err)
}
if err := d.Set("tags", ps.GetTags()); err != nil {
return fmt.Errorf("error setting 'tags' field: %w", err)
}
if err := d.Set("wizard_parameters", ps.GetWizardParameters()); err != nil {
return fmt.Errorf("error setting 'document' field: %w", err)
}

if err := d.Set("policies", policiesToMaps(ps.GetPolicies())); err != nil {
return fmt.Errorf("error setting 'policies' field: %w", err)
}
if ps.GetScope() != nil {
if err := d.Set("scope", scopeToMap(ps.GetScope())); err != nil {
return fmt.Errorf("error setting 'scope' field: %w", err)
}
}
// Use the changeInfoToMap method to set the last_updated and created fields
if err := d.Set("last_updated", changeInfoToMap(ps.GetLastUpdated())); err != nil {
return fmt.Errorf("error setting 'last_updated' field: %w", err)
}
if err := d.Set("created", changeInfoToMap(ps.GetCreated())); err != nil {
return fmt.Errorf("error setting 'created' field: %w", err)
}
d.SetId(ps.GetId())
return nil
}

func policySetFromSchema(d *schema.ResourceData) *msg.PolicySet {
p := &msg.PolicySet{
Id: d.Get("id").(string),
Name: d.Get("name").(string),
Description: d.Get("description").(string),
Enabled: d.Get("enabled").(bool),
Tags: utils.ConvertFromInterfaceList[string](d.Get("tags").([]interface{})),
WizardId: d.Get("wizard_id").(string),
WizardParameters: d.Get("wizard_parameters").(string),
}

if v, ok := d.GetOk("scope"); ok {
p.Scope = scopeFromInterface(v.([]interface{}))
}
return p
}

// scopeFromInterface converts the map to a Scope struct
func scopeFromInterface(s []interface{}) *msg.Scope {
if len(s) == 0 || s[0] == nil {
return nil
}
m := s[0].(map[string]interface{})
return &msg.Scope{
RepoIds: utils.ConvertFromInterfaceList[string](m["repo_ids"].([]interface{})),
}
}

func createPolicySet(ctx context.Context, cl *client.Client, rd *schema.ResourceData) error {
ps := policySetFromSchema(rd)
req := &msg.CreatePolicySetRequest{
PolicySet: ps,
}
grpcClient := methods.NewPolicyWizardServiceClient(cl.GRPCClient())
resp, err := grpcClient.CreatePolicySet(ctx, req)
if err != nil {
return err
}
rd.SetId(resp.GetPolicySet().GetId())
return nil
}

func readPolicySet(ctx context.Context, cl *client.Client, rd *schema.ResourceData) error {
req := &msg.ReadPolicySetRequest{
Id: rd.Get("id").(string),
}
grpcClient := methods.NewPolicyWizardServiceClient(cl.GRPCClient())
resp, err := grpcClient.ReadPolicySet(ctx, req)
if err != nil {
return err
}
return updateSchema(resp.GetPolicySet(), rd)
}

func updatePolicySet(ctx context.Context, cl *client.Client, rd *schema.ResourceData) error {
ps := policySetFromSchema(rd)
req := &msg.UpdatePolicySetRequest{
Id: ps.GetId(),
PolicySet: ps,
}
grpcClient := methods.NewPolicyWizardServiceClient(cl.GRPCClient())
resp, err := grpcClient.UpdatePolicySet(ctx, req)
if err != nil {
return err
}
return updateSchema(resp.GetPolicySet(), rd)
}

func deletePolicySet(ctx context.Context, cl *client.Client, rd *schema.ResourceData) error {
req := &msg.DeletePolicySetRequest{
Id: rd.Get("id").(string),
}
grpcClient := methods.NewPolicyWizardServiceClient(cl.GRPCClient())
_, err := grpcClient.DeletePolicySet(ctx, req)
return err
}
Loading

0 comments on commit 7ec5cfc

Please sign in to comment.