Skip to content

Commit

Permalink
Consolidated changes for policy engine providers
Browse files Browse the repository at this point in the history
  • Loading branch information
gengdahlCyral committed Jun 12, 2024
1 parent ea947b4 commit b2ad3ba
Show file tree
Hide file tree
Showing 13 changed files with 904 additions and 30 deletions.
22 changes: 22 additions & 0 deletions cyral/internal/policyv2/constants.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package policyv2

const (
resourceName = "cyral_policy_v2"
dataSourceName = resourceName
apiPathLocal = "v2/policies/local"
apiPathGlobal = "v2/policies/global"
apiPathApproval = "v2/policies/approval"
)

func getAPIPath(policyType string) string {
switch policyType {
case "POLICY_TYPE_LOCAL", "local":
return apiPathLocal
case "POLICY_TYPE_GLOBAL", "global":
return apiPathGlobal
case "POLICY_TYPE_APPROVAL", "approval":
return apiPathApproval
default:
return ""
}
}
109 changes: 109 additions & 0 deletions cyral/internal/policyv2/datasource.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
package policyv2

import (
"fmt"

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

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

var dsContextHandler = core.DefaultContextHandler{
ResourceName: dataSourceName,
ResourceType: resourcetype.DataSource,
SchemaWriterFactoryGetMethod: func(_ *schema.ResourceData) core.SchemaWriter { return &PolicyV2{} },
ReadUpdateDeleteURLFactory: func(d *schema.ResourceData, c *client.Client) string {
return fmt.Sprintf("https://%s/%s/%s", c.ControlPlane, getAPIPath(d.Get("type").(string)), d.Get("id").(string))
},
}

func dataSourceSchema() *schema.Resource {
return &schema.Resource{
Description: "This data source provides information about a policy.",
ReadContext: dsContextHandler.ReadContext(),
Schema: map[string]*schema.Schema{
"id": {
Type: schema.TypeString,
Required: true,
Description: "The unique ID for the policy. This field is automatically set.",
},
"type": {
Type: schema.TypeString,
Required: true,
Description: "The type of the policy.",
},
"name": {
Type: schema.TypeString,
Computed: true,
Description: "A human-friendly name for the policy.",
},
"description": {
Type: schema.TypeString,
Computed: true,
Description: "A description for the policy.",
},
"enabled": {
Type: schema.TypeBool,
Computed: true,
Description: "Indicates if the policy is enabled.",
},
"tags": {
Type: schema.TypeList,
Computed: true,
Description: "Tags associated with the policy for categorization.",
Elem: &schema.Schema{Type: schema.TypeString},
},
"scope": {
Type: schema.TypeList,
Computed: true,
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"repo_ids": {
Type: schema.TypeList,
Elem: &schema.Schema{Type: schema.TypeString},
Computed: true,
},
},
},
},
"valid_from": {
Type: schema.TypeString,
Computed: true,
Description: "The time when the policy comes into effect. An unspecified value implies the policy has no start time.",
},
"valid_until": {
Type: schema.TypeString,
Computed: true,
Description: "The time after which the policy is no longer in effect. An unspecified value implies the policy will always apply once it comes into effect.",
},
"document": {
Type: schema.TypeString,
Computed: true,
Description: "The actual policy document in JSON format. It must conform to the schema for the policy type.",
},
"last_updated": {
Description: "Information about when and by whom the policy was last updated.",
Type: schema.TypeMap,
Optional: true,
Elem: &schema.Schema{
Type: schema.TypeString,
},
},
"created": {
Description: "Information about when and by whom the policy was created.",
Type: schema.TypeMap,
Optional: true,
Elem: &schema.Schema{
Type: schema.TypeString,
},
},
"enforced": {
Type: schema.TypeBool,
Computed: true,
Description: "Indicates if the policy is enforced.",
},
},
}
}
170 changes: 170 additions & 0 deletions cyral/internal/policyv2/model.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,170 @@
package policyv2

import (
"fmt"

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

// 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"`
}

// PolicyV2 represents the top-level policy structure
type PolicyV2 struct {
Policy Policy `json:"policy,omitempty"`
}

type Scope struct {
RepoIds []string `json:"repoIds,omitempty"`
}

// Policy represents the policy details
type Policy struct {
ID string `json:"id,omitempty"`
Name string `json:"name,omitempty"`
Description string `json:"description,omitempty"`
Enabled bool `json:"enabled,omitempty"`
Scope *Scope `json:"scope,omitempty"`
Tags []string `json:"tags,omitempty"`
ValidFrom string `json:"validFrom,omitempty"`
ValidUntil string `json:"validUntil,omitempty"`
Document string `json:"document,omitempty"`
LastUpdated ChangeInfo `json:"lastUpdated,omitempty"`
Created ChangeInfo `json:"created,omitempty"`
Enforced bool `json:"enforced,omitempty"`
Type string `json:"type,omitempty"`
}

// WriteToSchema writes the policy data to the schema
func (r PolicyV2) WriteToSchema(d *schema.ResourceData) error {
if err := d.Set("id", r.Policy.ID); err != nil {
return fmt.Errorf("error setting 'id' field: %w", err)
}
if err := d.Set("name", r.Policy.Name); err != nil {
return fmt.Errorf("error setting 'name' field: %w", err)
}
if err := d.Set("description", r.Policy.Description); err != nil {
return fmt.Errorf("error setting 'description' field: %w", err)
}
if err := d.Set("enabled", r.Policy.Enabled); err != nil {
return fmt.Errorf("error setting 'enabled' field: %w", err)
}
if err := d.Set("tags", r.Policy.Tags); err != nil {
return fmt.Errorf("error setting 'tags' field: %w", err)
}
if err := d.Set("valid_from", r.Policy.ValidFrom); err != nil {
return fmt.Errorf("error setting 'valid_from' field: %w", err)
}
if err := d.Set("valid_until", r.Policy.ValidUntil); err != nil {
return fmt.Errorf("error setting 'valid_until' field: %w", err)
}

if err := d.Set("document", r.Policy.Document); err != nil {
return fmt.Errorf("error setting 'document' field: %w", err)
}

if err := d.Set("last_updated", map[string]interface{}{
"actor": r.Policy.LastUpdated.Actor,
"actor_type": r.Policy.LastUpdated.ActorType,
"timestamp": r.Policy.LastUpdated.Timestamp,
}); err != nil {
return fmt.Errorf("error setting 'last_updated' field: %w", err)
}
if err := d.Set("created", map[string]interface{}{
"actor": r.Policy.Created.Actor,
"actor_type": r.Policy.Created.ActorType,
"timestamp": r.Policy.Created.Timestamp,
}); err != nil {
return fmt.Errorf("error setting 'created' field: %w", err)
}
if err := d.Set("enforced", r.Policy.Enforced); err != nil {
return fmt.Errorf("error setting 'enforced' field: %w", err)
}
if r.Policy.Type != "" {
if err := d.Set("type", r.Policy.Type); err != nil {
return fmt.Errorf("error setting 'type' field: %w", err)
}
}
if err := d.Set("scope", flattenScope(r.Policy.Scope)); err != nil {
return fmt.Errorf("error setting 'scope' field: %w", err)
}
d.SetId(r.Policy.ID)
return nil
}

// ReadFromSchema reads the policy data from the schema
func (r *PolicyV2) ReadFromSchema(d *schema.ResourceData) error {
r.Policy.ID = d.Get("id").(string)
r.Policy.Name = d.Get("name").(string)
r.Policy.Description = d.Get("description").(string)
r.Policy.Enabled = d.Get("enabled").(bool)
r.Policy.Tags = expandStringList(d.Get("tags").([]interface{}))
r.Policy.ValidFrom = d.Get("valid_from").(string)
r.Policy.ValidUntil = d.Get("valid_until").(string)

r.Policy.Document = d.Get("document").(string)

r.Policy.LastUpdated = expandChangeInfo(d.Get("last_updated").(map[string]interface{}))
r.Policy.Created = expandChangeInfo(d.Get("created").(map[string]interface{}))
r.Policy.Enforced = d.Get("enforced").(bool)
r.Policy.Type = d.Get("type").(string)
if v, ok := d.GetOk("scope"); ok {
r.Policy.Scope = expandScope(v.([]interface{}))
}
return nil
}

// expandStringList converts a list of interface{} to a list of strings
func expandStringList(list []interface{}) []string {
result := make([]string, len(list))
for i, v := range list {
result[i] = v.(string)
}
return result
}

// expandChangeInfo converts a map to a ChangeInfo struct
func expandChangeInfo(m map[string]interface{}) ChangeInfo {
return ChangeInfo{
Actor: getStringOrDefault(m["actor"]),
ActorType: getStringOrDefault(m["actor_type"]),
Timestamp: getStringOrDefault(m["timestamp"]),
}
}

// getStringOrDefault returns the string value or an empty string if nil
func getStringOrDefault(v interface{}) string {
if v == nil {
return ""
}
return v.(string)
}

// flattenScope converts the Scope struct to a list of maps
func flattenScope(scope *Scope) []map[string]interface{} {
if scope == nil {
return nil
}
scopeMap := []map[string]interface{}{
{
"repo_ids": scope.RepoIds,
},
}
return scopeMap
}

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

0 comments on commit b2ad3ba

Please sign in to comment.