Skip to content

Commit

Permalink
Refactor cyral_repository_access_rules
Browse files Browse the repository at this point in the history
  • Loading branch information
wcmjunior committed Mar 30, 2024
1 parent a236751 commit f11c32a
Show file tree
Hide file tree
Showing 7 changed files with 179 additions and 152 deletions.
5 changes: 5 additions & 0 deletions cyral/internal/repository/accessrules/constants.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package accessrules

const (
resourceName = "cyral_repository_access_rules"
)
123 changes: 123 additions & 0 deletions cyral/internal/repository/accessrules/model.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
package accessrules

import (
"github.com/cyralinc/terraform-provider-cyral/cyral/utils"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
)

type AccessRulesIdentity struct {
Type string `json:"type"`
Name string `json:"name"`
}

type AccessRulesConfig struct {
AuthorizationPolicyInstanceIDs []string `json:"authorizationPolicyInstanceIDs"`
}

type AccessRule struct {
Identity *AccessRulesIdentity `json:"identity"`
ValidFrom *string `json:"validFrom"`
ValidUntil *string `json:"validUntil"`
Config *AccessRulesConfig `json:"config"`
}

type AccessRulesResource struct {
AccessRules []*AccessRule `json:"accessRules"`
}

type AccessRulesResponse struct {
AccessRules []*AccessRule `json:"accessRules"`
}

// WriteToSchema is used when reading a resource. It takes whatever the API
// read call returned and translates it into the Terraform schema.
func (arr *AccessRulesResponse) WriteToSchema(d *schema.ResourceData) error {
d.SetId(
utils.MarshalComposedID(
[]string{
d.Get("repository_id").(string),
d.Get("user_account_id").(string),
},
"/",
),
)
// We'll have to build the access rule set in the format expected by Terraform,
// which boils down to doing a bunch of type casts
rules := make([]interface{}, 0, len(arr.AccessRules))
for _, rule := range arr.AccessRules {
m := make(map[string]interface{})

m["identity"] = []interface{}{
map[string]interface{}{
"type": rule.Identity.Type,
"name": rule.Identity.Name,
},
}

m["valid_from"] = rule.ValidFrom
m["valid_until"] = rule.ValidUntil

if rule.Config != nil && len(rule.Config.AuthorizationPolicyInstanceIDs) > 0 {
m["config"] = []interface{}{
map[string]interface{}{
"policy_ids": rule.Config.AuthorizationPolicyInstanceIDs,
},
}
}

rules = append(rules, m)
}
return d.Set("rule", rules)
}

// ReadFromSchema is called when *creating* or *updating* a resource.
// Essentially, it translates the stuff from the .tf file into whatever the
// API expects. The `AccessRulesResource` will be marshalled verbatim, so
// make sure that it matches *exactly* what the API needs.
func (arr *AccessRulesResource) ReadFromSchema(d *schema.ResourceData) error {
rules := d.Get("rule").([]interface{})
var accessRules []*AccessRule

for _, rule := range rules {
ruleMap := rule.(map[string]interface{})

accessRule := &AccessRule{}

identity := ruleMap["identity"].(*schema.Set).List()[0].(map[string]interface{})
accessRule.Identity = &AccessRulesIdentity{
Type: identity["type"].(string),
Name: identity["name"].(string),
}

validFrom := ruleMap["valid_from"].(string)
if validFrom != "" {
accessRule.ValidFrom = &validFrom
}

validUntil := ruleMap["valid_until"].(string)
if validUntil != "" {
accessRule.ValidUntil = &validUntil
}

conf, ok := ruleMap["config"]
if ok {
confList := conf.(*schema.Set).List()
if len(confList) > 0 {
config := confList[0].(map[string]interface{})
policyIDs := config["policy_ids"].([]interface{})
ids := make([]string, 0, len(policyIDs))
for _, policyID := range policyIDs {
ids = append(ids, policyID.(string))
}
accessRule.Config = &AccessRulesConfig{
AuthorizationPolicyInstanceIDs: ids,
}
}
}

accessRules = append(accessRules, accessRule)
}

arr.AccessRules = accessRules
return nil
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,187 +12,59 @@ import (
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
)

type AccessRulesIdentity struct {
Type string `json:"type"`
Name string `json:"name"`
}

type AccessRulesConfig struct {
AuthorizationPolicyInstanceIDs []string `json:"authorizationPolicyInstanceIDs"`
}

type AccessRule struct {
Identity *AccessRulesIdentity `json:"identity"`
ValidFrom *string `json:"validFrom"`
ValidUntil *string `json:"validUntil"`
Config *AccessRulesConfig `json:"config"`
}

type AccessRulesResource struct {
AccessRules []*AccessRule `json:"accessRules"`
}

type AccessRulesResponse struct {
AccessRules []*AccessRule `json:"accessRules"`
}

// WriteToSchema is used when reading a resource. It takes whatever the API
// read call returned and translates it into the Terraform schema.
func (arr *AccessRulesResponse) WriteToSchema(d *schema.ResourceData) error {
d.SetId(
utils.MarshalComposedID(
[]string{
d.Get("repository_id").(string),
d.Get("user_account_id").(string),
},
"/",
),
var urlFactory = func(d *schema.ResourceData, c *client.Client) string {
return fmt.Sprintf("https://%s/v1/repos/%s/userAccounts/%s/accessRules",
c.ControlPlane,
d.Get("repository_id").(string),
d.Get("user_account_id").(string),
)
// We'll have to build the access rule set in the format expected by Terraform,
// which boils down to doing a bunch of type casts
rules := make([]interface{}, 0, len(arr.AccessRules))
for _, rule := range arr.AccessRules {
m := make(map[string]interface{})

m["identity"] = []interface{}{
map[string]interface{}{
"type": rule.Identity.Type,
"name": rule.Identity.Name,
},
}

m["valid_from"] = rule.ValidFrom
m["valid_until"] = rule.ValidUntil

if rule.Config != nil && len(rule.Config.AuthorizationPolicyInstanceIDs) > 0 {
m["config"] = []interface{}{
map[string]interface{}{
"policy_ids": rule.Config.AuthorizationPolicyInstanceIDs,
},
}
}

rules = append(rules, m)
}
return d.Set("rule", rules)
}

// ReadFromSchema is called when *creating* or *updating* a resource.
// Essentially, it translates the stuff from the .tf file into whatever the
// API expects. The `AccessRulesResource` will be marshalled verbatim, so
// make sure that it matches *exactly* what the API needs.
func (arr *AccessRulesResource) ReadFromSchema(d *schema.ResourceData) error {
rules := d.Get("rule").([]interface{})
var accessRules []*AccessRule

for _, rule := range rules {
ruleMap := rule.(map[string]interface{})

accessRule := &AccessRule{}

identity := ruleMap["identity"].(*schema.Set).List()[0].(map[string]interface{})
accessRule.Identity = &AccessRulesIdentity{
Type: identity["type"].(string),
Name: identity["name"].(string),
}

validFrom := ruleMap["valid_from"].(string)
if validFrom != "" {
accessRule.ValidFrom = &validFrom
}

validUntil := ruleMap["valid_until"].(string)
if validUntil != "" {
accessRule.ValidUntil = &validUntil
}

conf, ok := ruleMap["config"]
if ok {
confList := conf.(*schema.Set).List()
if len(confList) > 0 {
config := confList[0].(map[string]interface{})
policyIDs := config["policy_ids"].([]interface{})
ids := make([]string, 0, len(policyIDs))
for _, policyID := range policyIDs {
ids = append(ids, policyID.(string))
}
accessRule.Config = &AccessRulesConfig{
AuthorizationPolicyInstanceIDs: ids,
}
}
}

accessRules = append(accessRules, accessRule)
}

arr.AccessRules = accessRules
return nil
}

var ReadRepositoryAccessRulesConfig = core.ResourceOperationConfig{
ResourceName: "RepositoryAccessRulesRead",
var readRepositoryAccessRulesConfig = core.ResourceOperationConfig{
ResourceName: resourceName,
Type: operationtype.Read,
HttpMethod: http.MethodGet,
URLFactory: func(d *schema.ResourceData, c *client.Client) string {
return fmt.Sprintf("https://%s/v1/repos/%s/userAccounts/%s/accessRules",
c.ControlPlane,
d.Get("repository_id").(string),
d.Get("user_account_id").(string),
)
},
URLFactory: urlFactory,
SchemaWriterFactory: func(_ *schema.ResourceData) core.SchemaWriter {
return &AccessRulesResponse{}
},
RequestErrorHandler: &core.ReadIgnoreHttpNotFound{ResName: "Repository access rule"},
}

func ResourceRepositoryAccessRules() *schema.Resource {
func resourceSchema() *schema.Resource {
return &schema.Resource{
Description: "Manage access rules",
CreateContext: core.CreateResource(
core.ResourceOperationConfig{
ResourceName: "RepositoryAccessRulesCreate",
Type: operationtype.Create,
HttpMethod: http.MethodPut,
URLFactory: func(d *schema.ResourceData, c *client.Client) string {
repoID := d.Get("repository_id").(string)
userAccountID := d.Get("user_account_id").(string)
return fmt.Sprintf("https://%s/v1/repos/%s/userAccounts/%s/accessRules",
c.ControlPlane,
repoID,
userAccountID,
)
},
ResourceName: resourceName,
Type: operationtype.Create,
HttpMethod: http.MethodPut,
URLFactory: urlFactory,
SchemaReaderFactory: func() core.SchemaReader { return &AccessRulesResource{} },
SchemaWriterFactory: func(_ *schema.ResourceData) core.SchemaWriter { return &AccessRulesResponse{} },
},
ReadRepositoryAccessRulesConfig,
readRepositoryAccessRulesConfig,
),
ReadContext: core.ReadResource(ReadRepositoryAccessRulesConfig),
ReadContext: core.ReadResource(readRepositoryAccessRulesConfig),
UpdateContext: core.UpdateResource(
core.ResourceOperationConfig{
ResourceName: "RepositoryAccessRulesUpdate",
Type: operationtype.Update,
HttpMethod: http.MethodPut,
URLFactory: func(d *schema.ResourceData, c *client.Client) string {
return fmt.Sprintf("https://%s/v1/repos/%s/userAccounts/%s/accessRules",
c.ControlPlane,
d.Get("repository_id").(string),
d.Get("user_account_id").(string),
)
},
ResourceName: resourceName,
Type: operationtype.Update,
HttpMethod: http.MethodPut,
URLFactory: urlFactory,
SchemaReaderFactory: func() core.SchemaReader { return &AccessRulesResource{} },
SchemaWriterFactory: func(_ *schema.ResourceData) core.SchemaWriter { return &AccessRulesResponse{} },
},
ReadRepositoryAccessRulesConfig,
readRepositoryAccessRulesConfig,
),
DeleteContext: core.DeleteResource(
core.ResourceOperationConfig{
ResourceName: "RepositoryAccessRulesDelete",
Type: operationtype.Delete,
HttpMethod: http.MethodDelete,
URLFactory: func(d *schema.ResourceData, c *client.Client) string {

// TODO Discuss why this is really necessary. We should be able
// to use the same factory for all operations.
idPieces, err := utils.UnMarshalComposedID(d.Id(), "/", 2)
if err != nil {
panic(fmt.Sprintf("Failed to unmarshal access rules ID: %v", err))
Expand All @@ -206,6 +78,7 @@ func ResourceRepositoryAccessRules() *schema.Resource {
userAccountID,
)
},
RequestErrorHandler: &core.DeleteIgnoreHttpNotFound{ResName: resourceName},
},
),

Expand Down
26 changes: 26 additions & 0 deletions cyral/internal/repository/accessrules/schema_loader.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package accessrules

import (
"github.com/cyralinc/terraform-provider-cyral/cyral/core"
)

type packageSchema struct {
}

func (p *packageSchema) Name() string {
return "repository_access_rules"
}

func (p *packageSchema) Schemas() []*core.SchemaDescriptor {
return []*core.SchemaDescriptor{
{
Name: resourceName,
Type: core.ResourceSchemaType,
Schema: resourceSchema,
},
}
}

func PackageSchema() core.PackageSchema {
return &packageSchema{}
}
2 changes: 0 additions & 2 deletions cyral/provider/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@ import (
"github.com/cyralinc/terraform-provider-cyral/cyral/internal/policy"
"github.com/cyralinc/terraform-provider-cyral/cyral/internal/policy/rule"
"github.com/cyralinc/terraform-provider-cyral/cyral/internal/regopolicy"
"github.com/cyralinc/terraform-provider-cyral/cyral/internal/repository/accessrules"
"github.com/cyralinc/terraform-provider-cyral/cyral/internal/repository/binding"
"github.com/cyralinc/terraform-provider-cyral/cyral/internal/repository/confanalysis"
"github.com/cyralinc/terraform-provider-cyral/cyral/internal/repository/confauth"
Expand Down Expand Up @@ -165,7 +164,6 @@ func getResourceMap(ps []core.PackageSchema) map[string]*schema.Resource {
schemaMap["cyral_policy"] = policy.ResourcePolicy()
schemaMap["cyral_policy_rule"] = rule.ResourcePolicyRule()
schemaMap["cyral_rego_policy_instance"] = regopolicy.ResourceRegoPolicyInstance()
schemaMap["cyral_repository_access_rules"] = accessrules.ResourceRepositoryAccessRules()
schemaMap["cyral_repository_binding"] = binding.ResourceRepositoryBinding()
schemaMap["cyral_repository_conf_auth"] = confauth.ResourceRepositoryConfAuth()
schemaMap["cyral_repository_conf_analysis"] = confanalysis.ResourceRepositoryConfAnalysis()
Expand Down
Loading

0 comments on commit f11c32a

Please sign in to comment.