From 060b0e41881991bbc8eda60a92120ef83d85e976 Mon Sep 17 00:00:00 2001 From: andrii-codefresh Date: Fri, 16 Jun 2023 14:08:05 +0300 Subject: [PATCH 01/13] gitops abac rules --- client/gitops_abac_rules.go | 221 +++++++++++++++++++++++++++++++ client/gitops_abac_rules_test.go | 104 +++++++++++++++ client/gql_client.go | 78 +++++++++++ codefresh/env.go | 2 + go.sum | 4 + 5 files changed, 409 insertions(+) create mode 100644 client/gitops_abac_rules.go create mode 100644 client/gitops_abac_rules_test.go create mode 100644 client/gql_client.go diff --git a/client/gitops_abac_rules.go b/client/gitops_abac_rules.go new file mode 100644 index 00000000..a66ebcb6 --- /dev/null +++ b/client/gitops_abac_rules.go @@ -0,0 +1,221 @@ +package client + +import ( + "fmt" +) + +type AbacEntityValues string + +const ( + AbacEntityClusters AbacEntityValues = "clusters" + AbacEntityExecutionContext AbacEntityValues = "executionContext" + AbacEntityGitContexts AbacEntityValues = "gitContexts" + AbacEntityGitopsApplications AbacEntityValues = "gitopsApplications" + AbacEntityHelmCharts AbacEntityValues = "helmCharts" + AbacEntityPipelines AbacEntityValues = "pipelines" + AbacEntityProjects AbacEntityValues = "projects" + AbacEntitySharedConfiguration AbacEntityValues = "sharedConfiguration" + AbacEntityWorkflows AbacEntityValues = "workflows" + AbacEntityWorkflowTemplates AbacEntityValues = "workflowTemplates" +) + +type EntityAbacAttribute struct { + Name string `json:"name"` + Key string `json:"key,omitempty"` + Value string `json:"value"` +} + +// GitopsAbacRule spec +type GitopsAbacRule struct { + ID string `json:"id,omitempty"` + AccountId string `json:"accountId,omitempty"` + EntityType AbacEntityValues `json:"entityType"` + Teams []string `json:"teams"` + Tags []string `json:"tags,omitempty"` + Actions []string `json:"actions"` + Attributes []EntityAbacAttribute `json:"attributes"` +} + +type GitopsAbacRulesListResponse struct { + Data struct { + AbacRules []GitopsAbacRule `json:"abacRules"` + } `json:"data"` +} + +type GitopsAbacRuleResponse struct { + Data struct { + AbacRule GitopsAbacRule `json:"abacRule,omitempty"` + CreateAbacRule GitopsAbacRule `json:"createAbacRule,omitempty"` + RemoveAbacRule GitopsAbacRule `json:"removeAbacRule,omitempty"` + } `json:"data"` +} + +func (client *GraphQLClient) GetAbacRulesList(accountId string, entityType AbacEntityValues) ([]GitopsAbacRule, error) { + request := GraphQLRequest{ + Query: ` + query AbacRules($accountId: String!, $entityType: AbacEntityValues!) { + abacRules(accountId: $accountId, entityType: $entityType) { + id + accountId + entityType + teams + tags + actions + attributes { + name + key + value + } + } + } + `, + Variables: map[string]interface{}{ + "accountId": accountId, + "entityType": entityType, + }, + } + + response, err := client.SendRequest(request) + if err != nil { + fmt.Println("Error:", err) + return nil, err + } + + var gitopsAbacRulesResponse GitopsAbacRulesListResponse + err = DecodeGraphQLResponseInto(response, &gitopsAbacRulesResponse) + if err != nil { + return nil, err + } + + return gitopsAbacRulesResponse.Data.AbacRules, nil +} + +// GetAbacRuleByID - +func (client *GraphQLClient) GetAbacRuleByID(accountId string, id string) (*GitopsAbacRule, error) { + request := GraphQLRequest{ + Query: ` + query AbacRule($accountId: String!, $id: ID!) { + abacRule(accountId: $accountId, id: $id) { + id + accountId + entityType + teams + tags + actions + attributes { + name + key + value + } + } + } + `, + Variables: map[string]interface{}{ + "accountId": accountId, + "id": id, + }, + } + + response, err := client.SendRequest(request) + if err != nil { + fmt.Println("Error:", err) + return nil, err + } + + var gitopsAbacRuleResponse GitopsAbacRuleResponse + err = DecodeGraphQLResponseInto(response, &gitopsAbacRuleResponse) + if err != nil { + return nil, err + } + + return &gitopsAbacRuleResponse.Data.AbacRule, nil +} + +func (client *GraphQLClient) CreateAbacRule(accountId string, gitopsAbacRule *GitopsAbacRule) (*GitopsAbacRule, error) { + + newAbacRule := &GitopsAbacRule{ + EntityType: gitopsAbacRule.EntityType, + Teams: gitopsAbacRule.Teams, + Tags: gitopsAbacRule.Tags, + Actions: gitopsAbacRule.Actions, + Attributes: gitopsAbacRule.Attributes, + } + + request := GraphQLRequest{ + Query: ` + mutation CreateAbacRule($accountId: String!, $createAbacRuleInput: CreateAbacRuleInput!) { + createAbacRule(accountId: $accountId, createAbacRuleInput: $createAbacRuleInput) { + id + accountId + entityType + teams + tags + actions + attributes { + name + key + value + } + } + } + `, + Variables: map[string]interface{}{ + "accountId": accountId, + "createAbacRuleInput": newAbacRule, + }, + } + + response, err := client.SendRequest(request) + if err != nil { + fmt.Println("Error:", err) + return nil, err + } + + var gitopsAbacRuleResponse GitopsAbacRuleResponse + err = DecodeGraphQLResponseInto(response, &gitopsAbacRuleResponse) + if err != nil { + return nil, err + } + + return &gitopsAbacRuleResponse.Data.CreateAbacRule, nil +} + +func (client *GraphQLClient) DeleteAbacRule(accountId string, id string) (*GitopsAbacRule, error) { + request := GraphQLRequest{ + Query: ` + mutation RemoveAbacRule($accountId: String!, $id: ID!) { + removeAbacRule(accountId: $accountId, id: $id) { + id + accountId + entityType + teams + tags + actions + attributes { + name + key + value + } + } + } + `, + Variables: map[string]interface{}{ + "accountId": accountId, + "id": id, + }, + } + + response, err := client.SendRequest(request) + if err != nil { + fmt.Println("Error:", err) + return nil, err + } + + var gitopsAbacRuleResponse GitopsAbacRuleResponse + err = DecodeGraphQLResponseInto(response, &gitopsAbacRuleResponse) + if err != nil { + return nil, err + } + + return &gitopsAbacRuleResponse.Data.RemoveAbacRule, nil +} diff --git a/client/gitops_abac_rules_test.go b/client/gitops_abac_rules_test.go new file mode 100644 index 00000000..e0012cda --- /dev/null +++ b/client/gitops_abac_rules_test.go @@ -0,0 +1,104 @@ +package client_test + +import ( + "fmt" + cfClient "github.com/codefresh-io/terraform-provider-codefresh/client" + "github.com/codefresh-io/terraform-provider-codefresh/codefresh" + "os" + "testing" +) + +func TestMain(m *testing.M) { + // Init tests + + // Run tests + exitCode := m.Run() + + // Cleanup + + // Exit + os.Exit(exitCode) +} + +// TODO: Create and remove account, team +func TestRules(t *testing.T) { + // Validate environment variables + url := os.Getenv("CODEFRESH_API_URL") + if url == "" { + t.Fatalf("CODEFRESH_API_URL variable is not set") + } + url2 := os.Getenv("CODEFRESH_API2_URL") + if url2 == "" { + t.Fatalf("CODEFRESH_API2_URL variable is not set") + } + key := os.Getenv("CODEFRESH_API_KEY") + if key == "" { + t.Fatalf("CODEFRESH_API_KEY variable is not set") + } + + client := cfClient.NewClient(os.Getenv(codefresh.ENV_CODEFRESH_API_URL), os.Getenv(codefresh.ENV_CODEFRESH_API_KEY), "") + + currentAccount, err := client.GetCurrentAccount() + if err != nil { + t.Fatalf("Error: %s", err.Error()) + } + fmt.Println(currentAccount) + + gqlClient := cfClient.NewGqlClient(os.Getenv(codefresh.ENV_CODEFRESH_API2_URL), + codefresh.DEFAULT_CODEFRESH_API2_URL, os.Getenv(codefresh.ENV_CODEFRESH_API_KEY)) + + created, err := gqlClient.CreateAbacRule( + currentAccount.ID, + &cfClient.GitopsAbacRule{ + EntityType: cfClient.AbacEntityGitopsApplications, + Teams: []string{"6365495094c782ba1ba45451"}, + Tags: []string{}, + Actions: []string{"SYNC"}, + Attributes: []cfClient.EntityAbacAttribute{ + { + Name: "LABEL", + Key: "AnyName", + Value: "SomeValue", + }, + { + Name: "NAMESPACE", + Value: "local51", + }, + }, + }, + ) + if err != nil { + t.Fatalf("Error: %s", err.Error()) + } + fmt.Println(created) + if created == nil { + t.Fatalf("Empty rule after creation") + } + + list, err := gqlClient.GetAbacRulesList(currentAccount.ID, cfClient.AbacEntityGitopsApplications) + if err != nil { + t.Fatalf("Error: %s", err.Error()) + } + fmt.Println(list) + if len(list) == 0 { + t.Fatalf("List of rules is empty") + } + + one, err := gqlClient.GetAbacRuleByID(currentAccount.ID, created.ID) + if err != nil { + t.Fatalf("Error: %s", err.Error()) + } + fmt.Println(one) + if created.ID != one.ID { + t.Fatalf("Expected %s, but got %s", created.ID, one.ID) + } + + deleted, err := gqlClient.DeleteAbacRule(currentAccount.ID, created.ID) + if err != nil { + t.Fatalf("Error: %s", err.Error()) + } + fmt.Println(deleted) + if created.ID != deleted.ID { + t.Fatalf("Expected %s, but got %s", created.ID, deleted.ID) + } +} diff --git a/client/gql_client.go b/client/gql_client.go new file mode 100644 index 00000000..a1438c2b --- /dev/null +++ b/client/gql_client.go @@ -0,0 +1,78 @@ +package client + +import ( + "bytes" + "encoding/json" + "errors" + "io/ioutil" + "net/http" +) + +// GraphQLRequest GraphQL query +type GraphQLRequest struct { + Query string `json:"query"` + Variables map[string]interface{} `json:"variables,omitempty"` +} + +// GraphQLClient GraphQL client +type GraphQLClient struct { + Token string + TokenHeader string + Host string + Client *http.Client +} + +// NewGqlClient returns a new graphql client configured to communicate on a server with the +// given hostname and to send an Authorization Header with the value of token +func NewGqlClient(url, defaultUrl, apiKey string) *GraphQLClient { + tokenHeader := "Authorization" + hostname := url + if hostname == "" { + hostname = defaultUrl + } + token := apiKey + return &GraphQLClient{ + Host: hostname, + Token: token, + TokenHeader: tokenHeader, + Client: &http.Client{}, + } +} + +func (client *GraphQLClient) SendRequest(request GraphQLRequest) ([]byte, error) { + jsonRequest, err := json.Marshal(request) + if err != nil { + return nil, err + } + + req, err := http.NewRequest("POST", client.Host, bytes.NewBuffer(jsonRequest)) + if err != nil { + return nil, err + } + + req.Header.Set(client.TokenHeader, client.Token) + req.Header.Set("Content-Type", "application/json; charset=utf-8") + + httpClient := &http.Client{} + resp, err := httpClient.Do(req) + if err != nil { + return nil, err + } + if resp.StatusCode >= 400 { + bodyBytes, _ := ioutil.ReadAll(resp.Body) + return nil, errors.New(resp.Status + " " + string(bodyBytes)) + } + defer resp.Body.Close() + + var buf bytes.Buffer + _, err = buf.ReadFrom(resp.Body) + if err != nil { + return nil, err + } + + return buf.Bytes(), nil +} + +func DecodeGraphQLResponseInto(body []byte, target interface{}) error { + return json.Unmarshal(body, target) +} diff --git a/codefresh/env.go b/codefresh/env.go index 8e16a185..3a53357b 100644 --- a/codefresh/env.go +++ b/codefresh/env.go @@ -4,7 +4,9 @@ const ( ENV_CODEFRESH_PLUGIN_DEBUG = "CODEFRESH_PLUGIN_DEBUG" ENV_CODEFRESH_PLUGIN_ADDR = "CODEFRESH_PLUGIN_ADDR" ENV_CODEFRESH_API_URL = "CODEFRESH_API_URL" + ENV_CODEFRESH_API2_URL = "CODEFRESH_API2_URL" ENV_CODEFRESH_API_KEY = "CODEFRESH_API_KEY" DEFAULT_CODEFRESH_API_URL = "https://g.codefresh.io/api" + DEFAULT_CODEFRESH_API2_URL = "https://g.codefresh.io/2.0/api/graphql" DEFAULT_CODEFRESH_PLUGIN_ADDR = "registry.terraform.io/-/codefresh" ) diff --git a/go.sum b/go.sum index 83f938a6..81d41568 100644 --- a/go.sum +++ b/go.sum @@ -117,6 +117,7 @@ github.com/ashanbrown/makezero v1.1.1/go.mod h1:i1bJLCRSCHOcOa9Y6MyF2FTfMZMFdHvx github.com/aws/aws-sdk-go v1.15.78/go.mod h1:E3/ieXAlvM0XWO57iftYVDLLvQ824smPP3ATZkfNZeM= github.com/aws/aws-sdk-go v1.37.0/go.mod h1:hcU610XS61/+aQV88ixoOzUoG7v3b31pl2zKMmprdro= github.com/benbjohnson/clock v1.1.0 h1:Q92kusRqC1XV2MjkWETPvjJVqKetz1OzxZB7mHJLju8= +github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= @@ -234,6 +235,7 @@ github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vb github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A= +github.com/go-logfmt/logfmt v0.5.1/go.mod h1:WYhtIu8zTZfxdn5+rREduYbwxfcBr/Vr6KEVveWlfTs= github.com/go-logr/logr v1.2.3 h1:2DntVwHkVopvECVRSlL5PSo9eG+cAkDCuckLubN+rq0= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= github.com/go-test/deep v1.0.3 h1:ZrJSEWsXzPOxaZnFteGEfooLba+ju3FYIbOrS+rQd68= @@ -700,6 +702,7 @@ github.com/sashamelentyev/usestdlibvars v1.23.0 h1:01h+/2Kd+NblNItNeux0veSL5cBF1 github.com/sashamelentyev/usestdlibvars v1.23.0/go.mod h1:YPwr/Y1LATzHI93CqoPUN/2BzGQ/6N/cl/KwgR0B/aU= github.com/sclevine/yj v0.0.0-20210612025309-737bdf40a5d1 h1:2cp8mZ+gRxIx/5GoLcQmmXzJuKdM1cSE2dmvFn3udJE= github.com/sclevine/yj v0.0.0-20210612025309-737bdf40a5d1/go.mod h1:PkEqqiiBYB87KgvpQj2r0wtRjDKEhhLRarGCubegp7E= +github.com/sebdah/goldie v1.0.0/go.mod h1:jXP4hmWywNEwZzhMuv2ccnqTSFpuq8iyQhtQdkkZBH4= github.com/securego/gosec/v2 v2.15.0 h1:v4Ym7FF58/jlykYmmhZ7mTm7FQvN/setNm++0fgIAtw= github.com/securego/gosec/v2 v2.15.0/go.mod h1:VOjTrZOkUtSDt2QLSJmQBMWnvwiQPEjg0l+5juIqGk8= github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo= @@ -844,6 +847,7 @@ go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk= go.uber.org/atomic v1.10.0 h1:9qC72Qh0+3MqyJbAn8YU5xVq1frD8bn3JtD2oXtafVQ= go.uber.org/atomic v1.10.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0= go.uber.org/goleak v1.1.11 h1:wy28qYRKZgnJTxGxvye5/wgWr1EKjmUDGYox5mGlRlI= +go.uber.org/goleak v1.1.11/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ= go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= go.uber.org/zap v1.24.0 h1:FiJd5l1UOLj0wCgbSE0rwwXHzEdAZS6hiiSnxJN/D60= From 8d36a0e798681d41b8304e6789785ed22e6ed34e Mon Sep 17 00:00:00 2001 From: andrii-codefresh Date: Sat, 17 Jun 2023 00:41:43 +0300 Subject: [PATCH 02/13] use one client for both services, add resource, update test, try local-running provider --- client/client.go | 4 +- client/gitops_abac_rules.go | 16 +- client/gitops_abac_rules_test.go | 14 +- client/gql_client.go | 29 +-- client/user.go | 2 +- codefresh/provider.go | 15 +- codefresh/resource_abac_rules.go | 253 +++++++++++++++++++++++++++ codefresh/resource_permission.go | 2 +- examples/abac_rules/main.tf | 32 ++++ examples/abac_rules/provider.tf | 5 + examples/abac_rules/terraform.tfvars | 3 + examples/abac_rules/vars.tf | 12 ++ examples/abac_rules/versions.tf | 8 + 13 files changed, 350 insertions(+), 45 deletions(-) create mode 100644 codefresh/resource_abac_rules.go create mode 100644 examples/abac_rules/main.tf create mode 100644 examples/abac_rules/provider.tf create mode 100644 examples/abac_rules/terraform.tfvars create mode 100644 examples/abac_rules/vars.tf create mode 100644 examples/abac_rules/versions.tf diff --git a/client/client.go b/client/client.go index 30abcb93..4228ae43 100644 --- a/client/client.go +++ b/client/client.go @@ -14,6 +14,7 @@ type Client struct { Token string TokenHeader string Host string + HostV2 string Client *http.Client } @@ -29,12 +30,13 @@ type RequestOptions struct { // NewClient returns a new client configured to communicate on a server with the // given hostname and to send an Authorization Header with the value of // token -func NewClient(hostname string, token string, tokenHeader string) *Client { +func NewClient(hostname string, hostnameV2 string, token string, tokenHeader string) *Client { if tokenHeader == "" { tokenHeader = "Authorization" } return &Client{ Host: hostname, + HostV2: hostnameV2, Token: token, TokenHeader: tokenHeader, Client: &http.Client{}, diff --git a/client/gitops_abac_rules.go b/client/gitops_abac_rules.go index a66ebcb6..5fabc0d9 100644 --- a/client/gitops_abac_rules.go +++ b/client/gitops_abac_rules.go @@ -50,7 +50,7 @@ type GitopsAbacRuleResponse struct { } `json:"data"` } -func (client *GraphQLClient) GetAbacRulesList(accountId string, entityType AbacEntityValues) ([]GitopsAbacRule, error) { +func (client *Client) GetAbacRulesList(accountId string, entityType AbacEntityValues) ([]GitopsAbacRule, error) { request := GraphQLRequest{ Query: ` query AbacRules($accountId: String!, $entityType: AbacEntityValues!) { @@ -75,7 +75,7 @@ func (client *GraphQLClient) GetAbacRulesList(accountId string, entityType AbacE }, } - response, err := client.SendRequest(request) + response, err := client.SendGqlRequest(request) if err != nil { fmt.Println("Error:", err) return nil, err @@ -91,7 +91,7 @@ func (client *GraphQLClient) GetAbacRulesList(accountId string, entityType AbacE } // GetAbacRuleByID - -func (client *GraphQLClient) GetAbacRuleByID(accountId string, id string) (*GitopsAbacRule, error) { +func (client *Client) GetAbacRuleByID(accountId string, id string) (*GitopsAbacRule, error) { request := GraphQLRequest{ Query: ` query AbacRule($accountId: String!, $id: ID!) { @@ -116,7 +116,7 @@ func (client *GraphQLClient) GetAbacRuleByID(accountId string, id string) (*Gito }, } - response, err := client.SendRequest(request) + response, err := client.SendGqlRequest(request) if err != nil { fmt.Println("Error:", err) return nil, err @@ -131,7 +131,7 @@ func (client *GraphQLClient) GetAbacRuleByID(accountId string, id string) (*Gito return &gitopsAbacRuleResponse.Data.AbacRule, nil } -func (client *GraphQLClient) CreateAbacRule(accountId string, gitopsAbacRule *GitopsAbacRule) (*GitopsAbacRule, error) { +func (client *Client) CreateAbacRule(accountId string, gitopsAbacRule *GitopsAbacRule) (*GitopsAbacRule, error) { newAbacRule := &GitopsAbacRule{ EntityType: gitopsAbacRule.EntityType, @@ -165,7 +165,7 @@ func (client *GraphQLClient) CreateAbacRule(accountId string, gitopsAbacRule *Gi }, } - response, err := client.SendRequest(request) + response, err := client.SendGqlRequest(request) if err != nil { fmt.Println("Error:", err) return nil, err @@ -180,7 +180,7 @@ func (client *GraphQLClient) CreateAbacRule(accountId string, gitopsAbacRule *Gi return &gitopsAbacRuleResponse.Data.CreateAbacRule, nil } -func (client *GraphQLClient) DeleteAbacRule(accountId string, id string) (*GitopsAbacRule, error) { +func (client *Client) DeleteAbacRule(accountId string, id string) (*GitopsAbacRule, error) { request := GraphQLRequest{ Query: ` mutation RemoveAbacRule($accountId: String!, $id: ID!) { @@ -205,7 +205,7 @@ func (client *GraphQLClient) DeleteAbacRule(accountId string, id string) (*Gitop }, } - response, err := client.SendRequest(request) + response, err := client.SendGqlRequest(request) if err != nil { fmt.Println("Error:", err) return nil, err diff --git a/client/gitops_abac_rules_test.go b/client/gitops_abac_rules_test.go index e0012cda..6f663c68 100644 --- a/client/gitops_abac_rules_test.go +++ b/client/gitops_abac_rules_test.go @@ -36,7 +36,12 @@ func TestRules(t *testing.T) { t.Fatalf("CODEFRESH_API_KEY variable is not set") } - client := cfClient.NewClient(os.Getenv(codefresh.ENV_CODEFRESH_API_URL), os.Getenv(codefresh.ENV_CODEFRESH_API_KEY), "") + client := cfClient.NewClient( + os.Getenv(codefresh.ENV_CODEFRESH_API_URL), + os.Getenv(codefresh.ENV_CODEFRESH_API2_URL), + os.Getenv(codefresh.ENV_CODEFRESH_API_KEY), + "", + ) currentAccount, err := client.GetCurrentAccount() if err != nil { @@ -44,10 +49,7 @@ func TestRules(t *testing.T) { } fmt.Println(currentAccount) - gqlClient := cfClient.NewGqlClient(os.Getenv(codefresh.ENV_CODEFRESH_API2_URL), - codefresh.DEFAULT_CODEFRESH_API2_URL, os.Getenv(codefresh.ENV_CODEFRESH_API_KEY)) - - created, err := gqlClient.CreateAbacRule( + created, err := client.CreateAbacRule( currentAccount.ID, &cfClient.GitopsAbacRule{ EntityType: cfClient.AbacEntityGitopsApplications, @@ -75,7 +77,7 @@ func TestRules(t *testing.T) { t.Fatalf("Empty rule after creation") } - list, err := gqlClient.GetAbacRulesList(currentAccount.ID, cfClient.AbacEntityGitopsApplications) + list, err := client.GetAbacRulesList(currentAccount.ID, cfClient.AbacEntityGitopsApplications) if err != nil { t.Fatalf("Error: %s", err.Error()) } diff --git a/client/gql_client.go b/client/gql_client.go index a1438c2b..d52ea949 100644 --- a/client/gql_client.go +++ b/client/gql_client.go @@ -14,38 +14,13 @@ type GraphQLRequest struct { Variables map[string]interface{} `json:"variables,omitempty"` } -// GraphQLClient GraphQL client -type GraphQLClient struct { - Token string - TokenHeader string - Host string - Client *http.Client -} - -// NewGqlClient returns a new graphql client configured to communicate on a server with the -// given hostname and to send an Authorization Header with the value of token -func NewGqlClient(url, defaultUrl, apiKey string) *GraphQLClient { - tokenHeader := "Authorization" - hostname := url - if hostname == "" { - hostname = defaultUrl - } - token := apiKey - return &GraphQLClient{ - Host: hostname, - Token: token, - TokenHeader: tokenHeader, - Client: &http.Client{}, - } -} - -func (client *GraphQLClient) SendRequest(request GraphQLRequest) ([]byte, error) { +func (client *Client) SendGqlRequest(request GraphQLRequest) ([]byte, error) { jsonRequest, err := json.Marshal(request) if err != nil { return nil, err } - req, err := http.NewRequest("POST", client.Host, bytes.NewBuffer(jsonRequest)) + req, err := http.NewRequest("POST", client.HostV2, bytes.NewBuffer(jsonRequest)) if err != nil { return nil, err } diff --git a/client/user.go b/client/user.go index ba1c6435..47b9d4d4 100644 --- a/client/user.go +++ b/client/user.go @@ -134,7 +134,7 @@ func (client *Client) AddUserToTeamByAdmin(userID string, accountID string, team return err } // new Client for accountAdmin - accountAdminClient := NewClient(client.Host, accountAdminToken, "x-access-token") + accountAdminClient := NewClient(client.Host, "", accountAdminToken, "x-access-token") usersTeam, err := accountAdminClient.GetTeamByName(team) if err != nil { return err diff --git a/codefresh/provider.go b/codefresh/provider.go index fba23292..ffe6c0d1 100644 --- a/codefresh/provider.go +++ b/codefresh/provider.go @@ -23,6 +23,17 @@ func Provider() *schema.Provider { }, Description: fmt.Sprintf("The Codefresh API URL. Defaults to `%s`. Can also be set using the `%s` environment variable.", DEFAULT_CODEFRESH_API_URL, ENV_CODEFRESH_API_URL), }, + "api_url_v2": { + Type: schema.TypeString, + Optional: true, + DefaultFunc: func() (interface{}, error) { + if url := os.Getenv(ENV_CODEFRESH_API2_URL); url != "" { + return url, nil + } + return DEFAULT_CODEFRESH_API2_URL, nil + }, + Description: fmt.Sprintf("The Codefresh gitops API URL. Defaults to `%s`. Can also be set using the `%s` environment variable.", DEFAULT_CODEFRESH_API2_URL, ENV_CODEFRESH_API2_URL), + }, "token": { Type: schema.TypeString, Optional: true, @@ -55,6 +66,7 @@ func Provider() *schema.Provider { "codefresh_step_types": resourceStepTypes(), "codefresh_user": resourceUser(), "codefresh_team": resourceTeam(), + "codefresh_abac_rules": resourceGitopsAbacRule(), }, ConfigureFunc: configureProvider, } @@ -63,9 +75,10 @@ func Provider() *schema.Provider { func configureProvider(d *schema.ResourceData) (interface{}, error) { apiURL := d.Get("api_url").(string) + apiURLV2 := d.Get("api_url_v2").(string) token := d.Get("token").(string) if token == "" { token = os.Getenv(ENV_CODEFRESH_API_KEY) } - return cfClient.NewClient(apiURL, token, ""), nil + return cfClient.NewClient(apiURL, apiURLV2, token, ""), nil } diff --git a/codefresh/resource_abac_rules.go b/codefresh/resource_abac_rules.go new file mode 100644 index 00000000..1d790910 --- /dev/null +++ b/codefresh/resource_abac_rules.go @@ -0,0 +1,253 @@ +package codefresh + +import ( + "fmt" + "log" + + cfClient "github.com/codefresh-io/terraform-provider-codefresh/client" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" +) + +func resourceGitopsAbacRule() *schema.Resource { + return &schema.Resource{ + Description: "Gitops Abac Rules are used to setup access control and allow to define which teams have access to which resources based on tags and attributes.", + Create: resourceGitopsAbacRuleCreate, + Read: resourceGitopsAbacRuleRead, + Update: resourceGitopsAbacRuleUpdate, + Delete: resourceGitopsAbacRuleDelete, + Importer: &schema.ResourceImporter{ + State: schema.ImportStatePassthrough, + }, + Schema: map[string]*schema.Schema{ + "id": { + Description: "The abac rule ID.", + Type: schema.TypeString, + Optional: true, + Computed: true, + }, + "entityType": { + Description: ` +The type of resources the abac rules applies to. Possible values: + * gitopsApplications + `, + Type: schema.TypeString, + Required: true, + ValidateFunc: validation.StringInSlice([]string{ + "gitopsApplications", + }, false), + }, + "teams": { + Description: "The Ids of teams the abac rules apply to.", + Type: schema.TypeSet, + Required: true, + Elem: &schema.Schema{Type: schema.TypeString}, + }, + "tags": { + Description: ` +The effective tags to apply the permission. It supports 2 custom tags: + * untagged is a “tag” which refers to all resources that don't have any tag. + * (the star character) means all tags. + `, + Type: schema.TypeSet, + Optional: true, + Elem: &schema.Schema{ + Type: schema.TypeString, + }, + }, + "actions": { + Description: ` +Action to be allowed. Possible values: + * REFRESH + * SYNC + * TERMINATE_SYNC + * VIEW_POD_LOGS + * APP_ROLLBACK + `, + Type: schema.TypeSet, + Required: true, + Elem: &schema.Schema{Type: schema.TypeString}, + ValidateFunc: ValidateSubset, + }, + "attributes": { + Description: "Resource attributes that need to be validated", + Type: schema.TypeSet, + Required: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "name": { + Type: schema.TypeString, + Required: true, + }, + "key": { + Type: schema.TypeString, + }, + "value": { + Type: schema.TypeString, + Required: true, + }, + }, + }, + }, + }, + } +} + +func ValidateSubset(v interface{}, k string) (warnings []string, errors []error) { + actions, ok := v.(*schema.Set) + if !ok { + errors = append(errors, fmt.Errorf("expected TypeSet for actions")) + return + } + + validActions := []string{"REFRESH", "SYNC", "TERMINATE_SYNC", "VIEW_POD_LOGS", "APP_ROLLBACK"} // Allowed values + + for _, team := range actions.List() { + teamStr, ok := team.(string) + if !ok { + errors = append(errors, fmt.Errorf("expected string element in actions")) + continue + } + + if !contains(validActions, teamStr) { + errors = append(errors, fmt.Errorf("team %s is not a valid action", teamStr)) + } + } + + return +} + +func contains(slice []string, element string) bool { + for _, item := range slice { + if item == element { + return true + } + } + return false +} + +func resourceGitopsAbacRuleCreate(d *schema.ResourceData, meta interface{}) error { + client := meta.(*cfClient.Client) + + abacRule := *mapResourceToGitopsAbacRule(d) + + newGitopsAbacRule, err := client.CreateAbacRule("", &abacRule) + if err != nil { + return err + } + if newGitopsAbacRule == nil { + return fmt.Errorf("resourceGitopsAbacRuleCreate - failed to create abac rule, empty response") + } + + d.SetId(newGitopsAbacRule.ID) + + return resourceGitopsAbacRuleRead(d, meta) +} + +func resourceGitopsAbacRuleRead(d *schema.ResourceData, meta interface{}) error { + + client := meta.(*cfClient.Client) + + abacRuleID := d.Id() + if abacRuleID == "" { + d.SetId("") + return nil + } + + abacRule, err := client.GetAbacRuleByID("", abacRuleID) + if err != nil { + return err + } + + err = mapGitopsAbacRuleToResource(abacRule, d) + if err != nil { + return err + } + + return nil +} + +func resourceGitopsAbacRuleUpdate(d *schema.ResourceData, meta interface{}) error { + client := meta.(*cfClient.Client) + + abacRule := *mapResourceToGitopsAbacRule(d) + resp, err := client.CreateAbacRule("", &abacRule) + if err != nil { + return err + } + + deleteErr := resourceGitopsAbacRuleDelete(d, meta) + if deleteErr != nil { + log.Printf("[WARN] failed to delete permission %v: %v", abacRule, deleteErr) + } + d.SetId(resp.ID) + + return resourceGitopsAbacRuleRead(d, meta) +} + +func resourceGitopsAbacRuleDelete(d *schema.ResourceData, meta interface{}) error { + client := meta.(*cfClient.Client) + + _, err := client.DeleteAbacRule("", d.Id()) + if err != nil { + return err + } + + return nil +} + +func mapGitopsAbacRuleToResource(abacRule *cfClient.GitopsAbacRule, d *schema.ResourceData) error { + + err := d.Set("id", abacRule.ID) + if err != nil { + return err + } + + err = d.Set("entityType", abacRule.EntityType) + if err != nil { + return err + } + + err = d.Set("teams", abacRule.Teams) + if err != nil { + return err + } + + err = d.Set("tags", abacRule.Tags) + if err != nil { + return err + } + + err = d.Set("actions", abacRule.Actions) + if err != nil { + return err + } + + err = d.Set("attributes", abacRule.Attributes) + if err != nil { + return err + } + + return nil +} + +func mapResourceToGitopsAbacRule(d *schema.ResourceData) *cfClient.GitopsAbacRule { + + tagsI := d.Get("tags").(*schema.Set).List() + var tags []string + if len(tagsI) > 0 { + tags = convertStringArr(tagsI) + } else { + tags = []string{"*", "untagged"} + } + abacRule := &cfClient.GitopsAbacRule{ + ID: d.Id(), + EntityType: d.Get("entityType").(cfClient.AbacEntityValues), + Teams: d.Get("teams").([]string), + Tags: tags, + Actions: d.Get("actions").([]string), + Attributes: d.Get("attributes").([]cfClient.EntityAbacAttribute), + } + + return abacRule +} diff --git a/codefresh/resource_permission.go b/codefresh/resource_permission.go index e1b77f3a..82aa53cb 100644 --- a/codefresh/resource_permission.go +++ b/codefresh/resource_permission.go @@ -123,7 +123,7 @@ func resourcePermissionCreate(d *schema.ResourceData, meta interface{}) error { return err } if newPermission == nil { - return fmt.Errorf("resourcePermissionCreate - failed to create permission, empty responce") + return fmt.Errorf("resourcePermissionCreate - failed to create permission, empty response") } d.SetId(newPermission.ID) diff --git a/examples/abac_rules/main.tf b/examples/abac_rules/main.tf new file mode 100644 index 00000000..ea2c0b1f --- /dev/null +++ b/examples/abac_rules/main.tf @@ -0,0 +1,32 @@ +data "codefresh_team" "admins" { + name = "admins" +} + +data "codefresh_team" "users" { + name = "users" +} + +resource "codefresh_abac_rules" "dev_pipeline" { + for_each = toset(["run", "create", "update", "delete", "read"]) + team = data.codefresh_team.users.id + action = each.value + resource = "pipeline" + tags = ["dev", "untagged"] +} + +resource "codefresh_permission" "admin_pipeline" { + for_each = toset(["run", "create", "update", "delete", "read", "approve"]) + team = data.codefresh_team.admins.id + action = each.value + resource = "pipeline" + tags = ["production", "*"] +} + +resource "codefresh_permission" "admin_pipeline_related_resource" { + for_each = toset(["run", "create", "update", "delete", "read", "approve"]) + team = data.codefresh_team.admins.id + action = each.value + resource = "pipeline" + related_resource = "project" + tags = ["production", "*"] +} diff --git a/examples/abac_rules/provider.tf b/examples/abac_rules/provider.tf new file mode 100644 index 00000000..79fc432c --- /dev/null +++ b/examples/abac_rules/provider.tf @@ -0,0 +1,5 @@ +provider "codefresh" { + api_url = var.api_url + api_url_v2 = var.api_url_v2 + token = var.token # If token isn't set the provider expects the $CODEFRESH_API_KEY env variable +} \ No newline at end of file diff --git a/examples/abac_rules/terraform.tfvars b/examples/abac_rules/terraform.tfvars new file mode 100644 index 00000000..badaeaed --- /dev/null +++ b/examples/abac_rules/terraform.tfvars @@ -0,0 +1,3 @@ +api_url = "http://local.codefresh.io/api" +api_url_v2 = "http://local.codefresh.io/2.0/api/graphql" +token = "6365541212f7a3585b91167c.deeceaa9b05dcc6d4a0ee301b35fc9bf" diff --git a/examples/abac_rules/vars.tf b/examples/abac_rules/vars.tf new file mode 100644 index 00000000..578727c4 --- /dev/null +++ b/examples/abac_rules/vars.tf @@ -0,0 +1,12 @@ +variable api_url { + type = string +} + +variable api_url_v2 { + type = string +} + +variable token { + type = string + default = "" +} \ No newline at end of file diff --git a/examples/abac_rules/versions.tf b/examples/abac_rules/versions.tf new file mode 100644 index 00000000..9dee49eb --- /dev/null +++ b/examples/abac_rules/versions.tf @@ -0,0 +1,8 @@ +terraform { + required_providers { + codefresh = { + source = "codefresh-io/codefresh" + version = "~> 0.1" + } + } +} \ No newline at end of file From 86fedc13cd698b381c4ad6c47f87e2dbe8624789 Mon Sep 17 00:00:00 2001 From: andrii-codefresh Date: Sat, 17 Jun 2023 00:42:14 +0300 Subject: [PATCH 03/13] fix test --- client/gitops_abac_rules_test.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/client/gitops_abac_rules_test.go b/client/gitops_abac_rules_test.go index 6f663c68..0835d33e 100644 --- a/client/gitops_abac_rules_test.go +++ b/client/gitops_abac_rules_test.go @@ -86,7 +86,7 @@ func TestRules(t *testing.T) { t.Fatalf("List of rules is empty") } - one, err := gqlClient.GetAbacRuleByID(currentAccount.ID, created.ID) + one, err := client.GetAbacRuleByID(currentAccount.ID, created.ID) if err != nil { t.Fatalf("Error: %s", err.Error()) } @@ -95,7 +95,7 @@ func TestRules(t *testing.T) { t.Fatalf("Expected %s, but got %s", created.ID, one.ID) } - deleted, err := gqlClient.DeleteAbacRule(currentAccount.ID, created.ID) + deleted, err := client.DeleteAbacRule(currentAccount.ID, created.ID) if err != nil { t.Fatalf("Error: %s", err.Error()) } From c7dbbfa5b21584e820679f418d797ee2c361a907 Mon Sep 17 00:00:00 2001 From: andrii-codefresh Date: Mon, 19 Jun 2023 16:46:37 +0300 Subject: [PATCH 04/13] wip --- codefresh/resource_abac_rules.go | 55 ++++++------- codefresh/resource_abac_rules_test.go | 114 ++++++++++++++++++++++++++ examples/abac_rules/main.tf | 33 +++----- 3 files changed, 147 insertions(+), 55 deletions(-) create mode 100644 codefresh/resource_abac_rules_test.go diff --git a/codefresh/resource_abac_rules.go b/codefresh/resource_abac_rules.go index 1d790910..678fdf7b 100644 --- a/codefresh/resource_abac_rules.go +++ b/codefresh/resource_abac_rules.go @@ -1,6 +1,7 @@ package codefresh import ( + "context" "fmt" "log" @@ -9,6 +10,8 @@ import ( "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" ) +var validSetValues = []string{"REFRESH", "SYNC", "TERMINATE_SYNC", "VIEW_POD_LOGS", "APP_ROLLBACK"} + func resourceGitopsAbacRule() *schema.Resource { return &schema.Resource{ Description: "Gitops Abac Rules are used to setup access control and allow to define which teams have access to which resources based on tags and attributes.", @@ -26,7 +29,7 @@ func resourceGitopsAbacRule() *schema.Resource { Optional: true, Computed: true, }, - "entityType": { + "entity_type": { Description: ` The type of resources the abac rules applies to. Possible values: * gitopsApplications @@ -64,15 +67,14 @@ Action to be allowed. Possible values: * VIEW_POD_LOGS * APP_ROLLBACK `, - Type: schema.TypeSet, - Required: true, - Elem: &schema.Schema{Type: schema.TypeString}, - ValidateFunc: ValidateSubset, + Type: schema.TypeSet, + Required: true, + Elem: &schema.Schema{Type: schema.TypeString}, }, "attributes": { Description: "Resource attributes that need to be validated", - Type: schema.TypeSet, - Required: true, + Type: schema.TypeList, + Optional: true, Elem: &schema.Resource{ Schema: map[string]*schema.Schema{ "name": { @@ -80,7 +82,8 @@ Action to be allowed. Possible values: Required: true, }, "key": { - Type: schema.TypeString, + Type: schema.TypeString, + Optional: true, }, "value": { Type: schema.TypeString, @@ -90,31 +93,19 @@ Action to be allowed. Possible values: }, }, }, - } -} - -func ValidateSubset(v interface{}, k string) (warnings []string, errors []error) { - actions, ok := v.(*schema.Set) - if !ok { - errors = append(errors, fmt.Errorf("expected TypeSet for actions")) - return - } - - validActions := []string{"REFRESH", "SYNC", "TERMINATE_SYNC", "VIEW_POD_LOGS", "APP_ROLLBACK"} // Allowed values + CustomizeDiff: func(ctx context.Context, diff *schema.ResourceDiff, v interface{}) error { + actions := diff.Get("actions").(*schema.Set).List() - for _, team := range actions.List() { - teamStr, ok := team.(string) - if !ok { - errors = append(errors, fmt.Errorf("expected string element in actions")) - continue - } + for _, action := range actions { + actionStr := action.(string) + if !contains(validSetValues, actionStr) { + return fmt.Errorf("Invalid action value: %s", actionStr) + } + } - if !contains(validActions, teamStr) { - errors = append(errors, fmt.Errorf("team %s is not a valid action", teamStr)) - } + return nil + }, } - - return } func contains(slice []string, element string) bool { @@ -203,7 +194,7 @@ func mapGitopsAbacRuleToResource(abacRule *cfClient.GitopsAbacRule, d *schema.Re return err } - err = d.Set("entityType", abacRule.EntityType) + err = d.Set("entity_type", abacRule.EntityType) if err != nil { return err } @@ -242,7 +233,7 @@ func mapResourceToGitopsAbacRule(d *schema.ResourceData) *cfClient.GitopsAbacRul } abacRule := &cfClient.GitopsAbacRule{ ID: d.Id(), - EntityType: d.Get("entityType").(cfClient.AbacEntityValues), + EntityType: d.Get("entity_type").(cfClient.AbacEntityValues), Teams: d.Get("teams").([]string), Tags: tags, Actions: d.Get("actions").([]string), diff --git a/codefresh/resource_abac_rules_test.go b/codefresh/resource_abac_rules_test.go new file mode 100644 index 00000000..a5a8cebb --- /dev/null +++ b/codefresh/resource_abac_rules_test.go @@ -0,0 +1,114 @@ +package codefresh + +import ( + "fmt" + cfClient "github.com/codefresh-io/terraform-provider-codefresh/client" + "github.com/hashicorp/terraform-plugin-sdk/v2/terraform" + "strings" + "testing" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" + funk "github.com/thoas/go-funk" +) + +func TestAccCodefreshAbacRulesConfig(t *testing.T) { + resourceName := "codefresh_abac_rules.test" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckCodefreshContextDestroy, + Steps: []resource.TestStep{ + { + Config: testAccCodefreshAbacRulesConfig( + "gitopsApplications", + "LABEL", + "KEY", + "VALUE", + []string{"SYNC", "REFRESH"}, + []string{"production", "*"}, + ), + Check: resource.ComposeTestCheckFunc( + testAccCheckCodefreshAbacRulesExists(resourceName), + resource.TestCheckResourceAttr(resourceName, "entity_type", "gitopsApplications"), + resource.TestCheckResourceAttr(resourceName, "actions.0", "SYNC"), + resource.TestCheckResourceAttr(resourceName, "actions.1", "REFRESH"), + resource.TestCheckResourceAttr(resourceName, "attributes.0.name", "LABEL"), + resource.TestCheckResourceAttr(resourceName, "attributes.0.key", "KEY"), + resource.TestCheckResourceAttr(resourceName, "attributes.0.value", "VALUE"), + resource.TestCheckResourceAttr(resourceName, "tags.0", "*"), + resource.TestCheckResourceAttr(resourceName, "tags.1", "production"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func testAccCheckCodefreshAbacRulesExists(resource string) resource.TestCheckFunc { + return func(state *terraform.State) error { + rs, ok := state.RootModule().Resources[resource] + if !ok { + return fmt.Errorf("Not found: %s", resource) + } + if rs.Primary.ID == "" { + return fmt.Errorf("No Record ID is set") + } + + abacRuleID := rs.Primary.ID + + apiClient := testAccProvider.Meta().(*cfClient.Client) + _, err := apiClient.GetAbacRuleByID("", abacRuleID) + + if err != nil { + return fmt.Errorf("error fetching abac rule with ID %s. %s", abacRuleID, err) + } + return nil + } +} + +// CONFIGS +func testAccCodefreshAbacRulesConfig(entityType, name, key, value string, actions, tags []string) string { + escapeString := func(str string) string { + if str == "null" { + return str // null means Terraform should ignore this field + } + return fmt.Sprintf(`"%s"`, str) + } + tagsEscaped := funk.Map(tags, escapeString).([]string) + actionsEscaped := funk.Map(actions, escapeString).([]string) + + attributes := "" + if name != "" && value != "" { + keyStr := "" + if key != "" { + keyStr = fmt.Sprintf(`key = %s`, escapeString(key)) + } + attributes = fmt.Sprintf(` + attributes = [{ + name = %s + %s + value = %s + }] + `, escapeString(name), keyStr, escapeString(value)) + } + attributes = "" + + return fmt.Sprintf(` + data "codefresh_team" "users" { + name = "users" + } + + resource "codefresh_abac_rules" "test" { + teams = [data.codefresh_team.users.id] + entity_type = %s + actions = [%s] + %s + tags = [%s] + } +`, escapeString(entityType), strings.Join(actionsEscaped[:], ","), attributes, strings.Join(tagsEscaped[:], ",")) +} diff --git a/examples/abac_rules/main.tf b/examples/abac_rules/main.tf index ea2c0b1f..b24394de 100644 --- a/examples/abac_rules/main.tf +++ b/examples/abac_rules/main.tf @@ -6,27 +6,14 @@ data "codefresh_team" "users" { name = "users" } -resource "codefresh_abac_rules" "dev_pipeline" { - for_each = toset(["run", "create", "update", "delete", "read"]) - team = data.codefresh_team.users.id - action = each.value - resource = "pipeline" - tags = ["dev", "untagged"] -} - -resource "codefresh_permission" "admin_pipeline" { - for_each = toset(["run", "create", "update", "delete", "read", "approve"]) - team = data.codefresh_team.admins.id - action = each.value - resource = "pipeline" - tags = ["production", "*"] -} - -resource "codefresh_permission" "admin_pipeline_related_resource" { - for_each = toset(["run", "create", "update", "delete", "read", "approve"]) - team = data.codefresh_team.admins.id - action = each.value - resource = "pipeline" - related_resource = "project" - tags = ["production", "*"] +resource "codefresh_abac_rules" "app_rule" { + entity_type = "gitopsApplications" + teams = [data.codefresh_team.users.id] + actions = ["REFRESH", "SYNC", "TERMINATE_SYNC", "VIEW_POD_LOGS", "APP_ROLLBACK"] +# attributes = [{ +# name = "LABEL" +# key = "KEY" +# value = "VALUE" +# }] + tags = ["dev", "untagged"] } From bef0b644d626b7e4b36b61eaa6cb71612e0100f7 Mon Sep 17 00:00:00 2001 From: andrii-codefresh Date: Mon, 19 Jun 2023 23:33:01 +0300 Subject: [PATCH 05/13] use accountId from authEntity errors in resource_abac_rules_test.go, main.tf .terraformrc provider_installation { dev_overrides { "codefresh-io/codefresh" = "~/go/bin" } direct {} } --- client/gitops_abac_rules.go | 33 ++++++++------------------- client/gitops_abac_rules_test.go | 9 ++++---- codefresh/resource_abac_rules.go | 14 ++++++------ codefresh/resource_abac_rules_test.go | 3 +-- examples/abac_rules/main.tf | 10 ++++---- 5 files changed, 26 insertions(+), 43 deletions(-) diff --git a/client/gitops_abac_rules.go b/client/gitops_abac_rules.go index 5fabc0d9..9ec7e5a8 100644 --- a/client/gitops_abac_rules.go +++ b/client/gitops_abac_rules.go @@ -4,21 +4,6 @@ import ( "fmt" ) -type AbacEntityValues string - -const ( - AbacEntityClusters AbacEntityValues = "clusters" - AbacEntityExecutionContext AbacEntityValues = "executionContext" - AbacEntityGitContexts AbacEntityValues = "gitContexts" - AbacEntityGitopsApplications AbacEntityValues = "gitopsApplications" - AbacEntityHelmCharts AbacEntityValues = "helmCharts" - AbacEntityPipelines AbacEntityValues = "pipelines" - AbacEntityProjects AbacEntityValues = "projects" - AbacEntitySharedConfiguration AbacEntityValues = "sharedConfiguration" - AbacEntityWorkflows AbacEntityValues = "workflows" - AbacEntityWorkflowTemplates AbacEntityValues = "workflowTemplates" -) - type EntityAbacAttribute struct { Name string `json:"name"` Key string `json:"key,omitempty"` @@ -29,7 +14,7 @@ type EntityAbacAttribute struct { type GitopsAbacRule struct { ID string `json:"id,omitempty"` AccountId string `json:"accountId,omitempty"` - EntityType AbacEntityValues `json:"entityType"` + EntityType string `json:"entityType"` Teams []string `json:"teams"` Tags []string `json:"tags,omitempty"` Actions []string `json:"actions"` @@ -50,7 +35,7 @@ type GitopsAbacRuleResponse struct { } `json:"data"` } -func (client *Client) GetAbacRulesList(accountId string, entityType AbacEntityValues) ([]GitopsAbacRule, error) { +func (client *Client) GetAbacRulesList(entityType string) ([]GitopsAbacRule, error) { request := GraphQLRequest{ Query: ` query AbacRules($accountId: String!, $entityType: AbacEntityValues!) { @@ -70,7 +55,7 @@ func (client *Client) GetAbacRulesList(accountId string, entityType AbacEntityVa } `, Variables: map[string]interface{}{ - "accountId": accountId, + "accountId": "", "entityType": entityType, }, } @@ -91,7 +76,7 @@ func (client *Client) GetAbacRulesList(accountId string, entityType AbacEntityVa } // GetAbacRuleByID - -func (client *Client) GetAbacRuleByID(accountId string, id string) (*GitopsAbacRule, error) { +func (client *Client) GetAbacRuleByID(id string) (*GitopsAbacRule, error) { request := GraphQLRequest{ Query: ` query AbacRule($accountId: String!, $id: ID!) { @@ -111,7 +96,7 @@ func (client *Client) GetAbacRuleByID(accountId string, id string) (*GitopsAbacR } `, Variables: map[string]interface{}{ - "accountId": accountId, + "accountId": "", "id": id, }, } @@ -131,7 +116,7 @@ func (client *Client) GetAbacRuleByID(accountId string, id string) (*GitopsAbacR return &gitopsAbacRuleResponse.Data.AbacRule, nil } -func (client *Client) CreateAbacRule(accountId string, gitopsAbacRule *GitopsAbacRule) (*GitopsAbacRule, error) { +func (client *Client) CreateAbacRule(gitopsAbacRule *GitopsAbacRule) (*GitopsAbacRule, error) { newAbacRule := &GitopsAbacRule{ EntityType: gitopsAbacRule.EntityType, @@ -160,7 +145,7 @@ func (client *Client) CreateAbacRule(accountId string, gitopsAbacRule *GitopsAba } `, Variables: map[string]interface{}{ - "accountId": accountId, + "accountId": "", "createAbacRuleInput": newAbacRule, }, } @@ -180,7 +165,7 @@ func (client *Client) CreateAbacRule(accountId string, gitopsAbacRule *GitopsAba return &gitopsAbacRuleResponse.Data.CreateAbacRule, nil } -func (client *Client) DeleteAbacRule(accountId string, id string) (*GitopsAbacRule, error) { +func (client *Client) DeleteAbacRule(id string) (*GitopsAbacRule, error) { request := GraphQLRequest{ Query: ` mutation RemoveAbacRule($accountId: String!, $id: ID!) { @@ -200,7 +185,7 @@ func (client *Client) DeleteAbacRule(accountId string, id string) (*GitopsAbacRu } `, Variables: map[string]interface{}{ - "accountId": accountId, + "accountId": "", "id": id, }, } diff --git a/client/gitops_abac_rules_test.go b/client/gitops_abac_rules_test.go index 0835d33e..f06a45a4 100644 --- a/client/gitops_abac_rules_test.go +++ b/client/gitops_abac_rules_test.go @@ -50,9 +50,8 @@ func TestRules(t *testing.T) { fmt.Println(currentAccount) created, err := client.CreateAbacRule( - currentAccount.ID, &cfClient.GitopsAbacRule{ - EntityType: cfClient.AbacEntityGitopsApplications, + EntityType: "gitopsApplications", Teams: []string{"6365495094c782ba1ba45451"}, Tags: []string{}, Actions: []string{"SYNC"}, @@ -77,7 +76,7 @@ func TestRules(t *testing.T) { t.Fatalf("Empty rule after creation") } - list, err := client.GetAbacRulesList(currentAccount.ID, cfClient.AbacEntityGitopsApplications) + list, err := client.GetAbacRulesList("gitopsApplications") if err != nil { t.Fatalf("Error: %s", err.Error()) } @@ -86,7 +85,7 @@ func TestRules(t *testing.T) { t.Fatalf("List of rules is empty") } - one, err := client.GetAbacRuleByID(currentAccount.ID, created.ID) + one, err := client.GetAbacRuleByID(created.ID) if err != nil { t.Fatalf("Error: %s", err.Error()) } @@ -95,7 +94,7 @@ func TestRules(t *testing.T) { t.Fatalf("Expected %s, but got %s", created.ID, one.ID) } - deleted, err := client.DeleteAbacRule(currentAccount.ID, created.ID) + deleted, err := client.DeleteAbacRule(created.ID) if err != nil { t.Fatalf("Error: %s", err.Error()) } diff --git a/codefresh/resource_abac_rules.go b/codefresh/resource_abac_rules.go index 678fdf7b..3a54570b 100644 --- a/codefresh/resource_abac_rules.go +++ b/codefresh/resource_abac_rules.go @@ -122,7 +122,7 @@ func resourceGitopsAbacRuleCreate(d *schema.ResourceData, meta interface{}) erro abacRule := *mapResourceToGitopsAbacRule(d) - newGitopsAbacRule, err := client.CreateAbacRule("", &abacRule) + newGitopsAbacRule, err := client.CreateAbacRule(&abacRule) if err != nil { return err } @@ -145,7 +145,7 @@ func resourceGitopsAbacRuleRead(d *schema.ResourceData, meta interface{}) error return nil } - abacRule, err := client.GetAbacRuleByID("", abacRuleID) + abacRule, err := client.GetAbacRuleByID(abacRuleID) if err != nil { return err } @@ -162,7 +162,7 @@ func resourceGitopsAbacRuleUpdate(d *schema.ResourceData, meta interface{}) erro client := meta.(*cfClient.Client) abacRule := *mapResourceToGitopsAbacRule(d) - resp, err := client.CreateAbacRule("", &abacRule) + resp, err := client.CreateAbacRule(&abacRule) if err != nil { return err } @@ -179,7 +179,7 @@ func resourceGitopsAbacRuleUpdate(d *schema.ResourceData, meta interface{}) erro func resourceGitopsAbacRuleDelete(d *schema.ResourceData, meta interface{}) error { client := meta.(*cfClient.Client) - _, err := client.DeleteAbacRule("", d.Id()) + _, err := client.DeleteAbacRule(d.Id()) if err != nil { return err } @@ -233,10 +233,10 @@ func mapResourceToGitopsAbacRule(d *schema.ResourceData) *cfClient.GitopsAbacRul } abacRule := &cfClient.GitopsAbacRule{ ID: d.Id(), - EntityType: d.Get("entity_type").(cfClient.AbacEntityValues), - Teams: d.Get("teams").([]string), + EntityType: d.Get("entity_type").(string), + Teams: convertStringArr(d.Get("teams").(*schema.Set).List()), Tags: tags, - Actions: d.Get("actions").([]string), + Actions: convertStringArr(d.Get("actions").(*schema.Set).List()), Attributes: d.Get("attributes").([]cfClient.EntityAbacAttribute), } diff --git a/codefresh/resource_abac_rules_test.go b/codefresh/resource_abac_rules_test.go index a5a8cebb..e8954fc0 100644 --- a/codefresh/resource_abac_rules_test.go +++ b/codefresh/resource_abac_rules_test.go @@ -62,7 +62,7 @@ func testAccCheckCodefreshAbacRulesExists(resource string) resource.TestCheckFun abacRuleID := rs.Primary.ID apiClient := testAccProvider.Meta().(*cfClient.Client) - _, err := apiClient.GetAbacRuleByID("", abacRuleID) + _, err := apiClient.GetAbacRuleByID(abacRuleID) if err != nil { return fmt.Errorf("error fetching abac rule with ID %s. %s", abacRuleID, err) @@ -96,7 +96,6 @@ func testAccCodefreshAbacRulesConfig(entityType, name, key, value string, action }] `, escapeString(name), keyStr, escapeString(value)) } - attributes = "" return fmt.Sprintf(` data "codefresh_team" "users" { diff --git a/examples/abac_rules/main.tf b/examples/abac_rules/main.tf index b24394de..f5cb688f 100644 --- a/examples/abac_rules/main.tf +++ b/examples/abac_rules/main.tf @@ -10,10 +10,10 @@ resource "codefresh_abac_rules" "app_rule" { entity_type = "gitopsApplications" teams = [data.codefresh_team.users.id] actions = ["REFRESH", "SYNC", "TERMINATE_SYNC", "VIEW_POD_LOGS", "APP_ROLLBACK"] -# attributes = [{ -# name = "LABEL" -# key = "KEY" -# value = "VALUE" -# }] + attributes = [{ + name = "LABEL" + key = "KEY" + value = "VALUE" + }] tags = ["dev", "untagged"] } From 7b6686bb7b8543ead87a291d2cb6f00276d5d035 Mon Sep 17 00:00:00 2001 From: andrii-codefresh Date: Wed, 21 Jun 2023 17:11:51 +0300 Subject: [PATCH 06/13] finalizing --- client/gitops_abac_rules_test.go | 105 -------------------------- codefresh/resource_abac_rules.go | 36 +++++++-- codefresh/resource_abac_rules_test.go | 26 +++---- examples/abac_rules/main.tf | 6 +- 4 files changed, 47 insertions(+), 126 deletions(-) delete mode 100644 client/gitops_abac_rules_test.go diff --git a/client/gitops_abac_rules_test.go b/client/gitops_abac_rules_test.go deleted file mode 100644 index f06a45a4..00000000 --- a/client/gitops_abac_rules_test.go +++ /dev/null @@ -1,105 +0,0 @@ -package client_test - -import ( - "fmt" - cfClient "github.com/codefresh-io/terraform-provider-codefresh/client" - "github.com/codefresh-io/terraform-provider-codefresh/codefresh" - "os" - "testing" -) - -func TestMain(m *testing.M) { - // Init tests - - // Run tests - exitCode := m.Run() - - // Cleanup - - // Exit - os.Exit(exitCode) -} - -// TODO: Create and remove account, team -func TestRules(t *testing.T) { - // Validate environment variables - url := os.Getenv("CODEFRESH_API_URL") - if url == "" { - t.Fatalf("CODEFRESH_API_URL variable is not set") - } - url2 := os.Getenv("CODEFRESH_API2_URL") - if url2 == "" { - t.Fatalf("CODEFRESH_API2_URL variable is not set") - } - key := os.Getenv("CODEFRESH_API_KEY") - if key == "" { - t.Fatalf("CODEFRESH_API_KEY variable is not set") - } - - client := cfClient.NewClient( - os.Getenv(codefresh.ENV_CODEFRESH_API_URL), - os.Getenv(codefresh.ENV_CODEFRESH_API2_URL), - os.Getenv(codefresh.ENV_CODEFRESH_API_KEY), - "", - ) - - currentAccount, err := client.GetCurrentAccount() - if err != nil { - t.Fatalf("Error: %s", err.Error()) - } - fmt.Println(currentAccount) - - created, err := client.CreateAbacRule( - &cfClient.GitopsAbacRule{ - EntityType: "gitopsApplications", - Teams: []string{"6365495094c782ba1ba45451"}, - Tags: []string{}, - Actions: []string{"SYNC"}, - Attributes: []cfClient.EntityAbacAttribute{ - { - Name: "LABEL", - Key: "AnyName", - Value: "SomeValue", - }, - { - Name: "NAMESPACE", - Value: "local51", - }, - }, - }, - ) - if err != nil { - t.Fatalf("Error: %s", err.Error()) - } - fmt.Println(created) - if created == nil { - t.Fatalf("Empty rule after creation") - } - - list, err := client.GetAbacRulesList("gitopsApplications") - if err != nil { - t.Fatalf("Error: %s", err.Error()) - } - fmt.Println(list) - if len(list) == 0 { - t.Fatalf("List of rules is empty") - } - - one, err := client.GetAbacRuleByID(created.ID) - if err != nil { - t.Fatalf("Error: %s", err.Error()) - } - fmt.Println(one) - if created.ID != one.ID { - t.Fatalf("Expected %s, but got %s", created.ID, one.ID) - } - - deleted, err := client.DeleteAbacRule(created.ID) - if err != nil { - t.Fatalf("Error: %s", err.Error()) - } - fmt.Println(deleted) - if created.ID != deleted.ID { - t.Fatalf("Expected %s, but got %s", created.ID, deleted.ID) - } -} diff --git a/codefresh/resource_abac_rules.go b/codefresh/resource_abac_rules.go index 3a54570b..648b2564 100644 --- a/codefresh/resource_abac_rules.go +++ b/codefresh/resource_abac_rules.go @@ -71,8 +71,8 @@ Action to be allowed. Possible values: Required: true, Elem: &schema.Schema{Type: schema.TypeString}, }, - "attributes": { - Description: "Resource attributes that need to be validated", + "attribute": { + Description: "Resource attribute that need to be validated", Type: schema.TypeList, Optional: true, Elem: &schema.Resource{ @@ -187,6 +187,18 @@ func resourceGitopsAbacRuleDelete(d *schema.ResourceData, meta interface{}) erro return nil } +func flattenAttributes(attributes []cfClient.EntityAbacAttribute) []map[string]interface{} { + var res = make([]map[string]interface{}, len(attributes)) + for i, attribute := range attributes { + m := make(map[string]interface{}) + m["name"] = attribute.Name + m["key"] = attribute.Key + m["value"] = attribute.Value + res[i] = m + } + return res +} + func mapGitopsAbacRuleToResource(abacRule *cfClient.GitopsAbacRule, d *schema.ResourceData) error { err := d.Set("id", abacRule.ID) @@ -214,9 +226,11 @@ func mapGitopsAbacRuleToResource(abacRule *cfClient.GitopsAbacRule, d *schema.Re return err } - err = d.Set("attributes", abacRule.Attributes) - if err != nil { - return err + if len(abacRule.Attributes) > 0 { + err = d.Set("attribute", flattenAttributes(abacRule.Attributes)) + if err != nil { + return err + } } return nil @@ -231,14 +245,24 @@ func mapResourceToGitopsAbacRule(d *schema.ResourceData) *cfClient.GitopsAbacRul } else { tags = []string{"*", "untagged"} } + abacRule := &cfClient.GitopsAbacRule{ ID: d.Id(), EntityType: d.Get("entity_type").(string), Teams: convertStringArr(d.Get("teams").(*schema.Set).List()), Tags: tags, Actions: convertStringArr(d.Get("actions").(*schema.Set).List()), - Attributes: d.Get("attributes").([]cfClient.EntityAbacAttribute), + Attributes: []cfClient.EntityAbacAttribute{}, } + attributes := d.Get("attribute").([]interface{}) + for idx := range attributes { + attr := cfClient.EntityAbacAttribute{ + Name: d.Get(fmt.Sprintf("attribute.%v.name", idx)).(string), + Key: d.Get(fmt.Sprintf("attribute.%v.key", idx)).(string), + Value: d.Get(fmt.Sprintf("attribute.%v.value", idx)).(string), + } + abacRule.Attributes = append(abacRule.Attributes, attr) + } return abacRule } diff --git a/codefresh/resource_abac_rules_test.go b/codefresh/resource_abac_rules_test.go index e8954fc0..b7f2e657 100644 --- a/codefresh/resource_abac_rules_test.go +++ b/codefresh/resource_abac_rules_test.go @@ -31,11 +31,11 @@ func TestAccCodefreshAbacRulesConfig(t *testing.T) { Check: resource.ComposeTestCheckFunc( testAccCheckCodefreshAbacRulesExists(resourceName), resource.TestCheckResourceAttr(resourceName, "entity_type", "gitopsApplications"), - resource.TestCheckResourceAttr(resourceName, "actions.0", "SYNC"), - resource.TestCheckResourceAttr(resourceName, "actions.1", "REFRESH"), - resource.TestCheckResourceAttr(resourceName, "attributes.0.name", "LABEL"), - resource.TestCheckResourceAttr(resourceName, "attributes.0.key", "KEY"), - resource.TestCheckResourceAttr(resourceName, "attributes.0.value", "VALUE"), + resource.TestCheckResourceAttr(resourceName, "actions.0", "REFRESH"), + resource.TestCheckResourceAttr(resourceName, "actions.1", "SYNC"), + resource.TestCheckResourceAttr(resourceName, "attribute.0.name", "LABEL"), + resource.TestCheckResourceAttr(resourceName, "attribute.0.key", "KEY"), + resource.TestCheckResourceAttr(resourceName, "attribute.0.value", "VALUE"), resource.TestCheckResourceAttr(resourceName, "tags.0", "*"), resource.TestCheckResourceAttr(resourceName, "tags.1", "production"), ), @@ -82,18 +82,18 @@ func testAccCodefreshAbacRulesConfig(entityType, name, key, value string, action tagsEscaped := funk.Map(tags, escapeString).([]string) actionsEscaped := funk.Map(actions, escapeString).([]string) - attributes := "" + attribute := "" if name != "" && value != "" { keyStr := "" if key != "" { keyStr = fmt.Sprintf(`key = %s`, escapeString(key)) } - attributes = fmt.Sprintf(` - attributes = [{ - name = %s - %s - value = %s - }] + attribute = fmt.Sprintf(` + attribute { + name = %s + %s + value = %s + } `, escapeString(name), keyStr, escapeString(value)) } @@ -109,5 +109,5 @@ func testAccCodefreshAbacRulesConfig(entityType, name, key, value string, action %s tags = [%s] } -`, escapeString(entityType), strings.Join(actionsEscaped[:], ","), attributes, strings.Join(tagsEscaped[:], ",")) +`, escapeString(entityType), strings.Join(actionsEscaped[:], ","), attribute, strings.Join(tagsEscaped[:], ",")) } diff --git a/examples/abac_rules/main.tf b/examples/abac_rules/main.tf index f5cb688f..f60c53b4 100644 --- a/examples/abac_rules/main.tf +++ b/examples/abac_rules/main.tf @@ -10,10 +10,12 @@ resource "codefresh_abac_rules" "app_rule" { entity_type = "gitopsApplications" teams = [data.codefresh_team.users.id] actions = ["REFRESH", "SYNC", "TERMINATE_SYNC", "VIEW_POD_LOGS", "APP_ROLLBACK"] - attributes = [{ + + attribute { name = "LABEL" key = "KEY" value = "VALUE" - }] + } + tags = ["dev", "untagged"] } From 00bb4edf6887dcdef1ff4b0acfa98991ec52be5d Mon Sep 17 00:00:00 2001 From: andrii-codefresh Date: Wed, 21 Jun 2023 17:49:24 +0300 Subject: [PATCH 07/13] docs --- docs/index.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/index.md b/docs/index.md index 66b0f942..f6998389 100644 --- a/docs/index.md +++ b/docs/index.md @@ -21,6 +21,7 @@ The key can be passed either as the provider's attribute or as environment varia ### Optional - `api_url` (String) The Codefresh API URL. Defaults to `https://g.codefresh.io/api`. Can also be set using the `CODEFRESH_API_URL` environment variable. +- `api_url_v2` (String) The Codefresh gitops API URL. Defaults to `https://g.codefresh.io/2.0/api/graphql`. Can also be set using the `CODEFRESH_API2_URL` environment variable. - `token` (String) The Codefresh API token. Can also be set using the `CODEFRESH_API_KEY` environment variable. ## Managing Resources Across Different Accounts From 27d3ac161f61fda14260171d95cecbdc710b5f25 Mon Sep 17 00:00:00 2001 From: andrii-codefresh Date: Wed, 21 Jun 2023 23:16:05 +0300 Subject: [PATCH 08/13] docs --- docs/resources/abac_rules.md | 50 ++++++++++++++++++++++++++++++++++++ 1 file changed, 50 insertions(+) create mode 100644 docs/resources/abac_rules.md diff --git a/docs/resources/abac_rules.md b/docs/resources/abac_rules.md new file mode 100644 index 00000000..36b222ae --- /dev/null +++ b/docs/resources/abac_rules.md @@ -0,0 +1,50 @@ +--- +# generated by https://github.com/hashicorp/terraform-plugin-docs +page_title: "codefresh_abac_rules Resource - terraform-provider-codefresh" +subcategory: "" +description: |- + Gitops Abac Rules are used to setup access control and allow to define which teams have access to which resources based on tags and attributes. +--- + +# codefresh_abac_rules (Resource) + +Gitops Abac Rules are used to setup access control and allow to define which teams have access to which resources based on tags and attributes. + + + + +## Schema + +### Required + +- `actions` (Set of String) Action to be allowed. Possible values: + * REFRESH + * SYNC + * TERMINATE_SYNC + * VIEW_POD_LOGS + * APP_ROLLBACK +- `entity_type` (String) The type of resources the abac rules applies to. Possible values: + * gitopsApplications +- `teams` (Set of String) The Ids of teams the abac rules apply to. + +### Optional + +- `attribute` (Block List) Resource attribute that need to be validated (see [below for nested schema](#nestedblock--attribute)) +- `id` (String) The abac rule ID. +- `tags` (Set of String) The effective tags to apply the permission. It supports 2 custom tags: + * untagged is a “tag” which refers to all resources that don't have any tag. + * (the star character) means all tags. + + +### Nested Schema for `attribute` + +Required: + +- `name` (String) +- `value` (String) + +Optional: + +- `key` (String) + + From d242591d2b9d89bb0cbd3d9878dc11ec00a4705b Mon Sep 17 00:00:00 2001 From: andrii-codefresh Date: Thu, 22 Jun 2023 12:49:12 +0300 Subject: [PATCH 09/13] fix comment --- examples/abac_rules/terraform.tfvars | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/examples/abac_rules/terraform.tfvars b/examples/abac_rules/terraform.tfvars index badaeaed..2999903e 100644 --- a/examples/abac_rules/terraform.tfvars +++ b/examples/abac_rules/terraform.tfvars @@ -1,3 +1,3 @@ -api_url = "http://local.codefresh.io/api" -api_url_v2 = "http://local.codefresh.io/2.0/api/graphql" -token = "6365541212f7a3585b91167c.deeceaa9b05dcc6d4a0ee301b35fc9bf" +api_url = "https://my-codefresh.example.com/api" +api_url_v2 = "https://my-codefresh.example.com/2.0/api/graphql" +token = "" From 866e389be5ba9c2c6c3213befe9647ac05a6cb0f Mon Sep 17 00:00:00 2001 From: andrii-codefresh Date: Thu, 22 Jun 2023 16:21:31 +0300 Subject: [PATCH 10/13] fix token --- client/gql_client.go | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/client/gql_client.go b/client/gql_client.go index d52ea949..7987a64d 100644 --- a/client/gql_client.go +++ b/client/gql_client.go @@ -25,7 +25,11 @@ func (client *Client) SendGqlRequest(request GraphQLRequest) ([]byte, error) { return nil, err } - req.Header.Set(client.TokenHeader, client.Token) + tokenHeader := client.TokenHeader + if tokenHeader == "" { + tokenHeader = "Authorization" + } + req.Header.Set(tokenHeader, client.Token) req.Header.Set("Content-Type", "application/json; charset=utf-8") httpClient := &http.Client{} From baf0dfc3e2194626337257bcae8bdcf354462b41 Mon Sep 17 00:00:00 2001 From: andrii-codefresh Date: Fri, 23 Jun 2023 22:28:02 +0300 Subject: [PATCH 11/13] logs --- client/gql_client.go | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/client/gql_client.go b/client/gql_client.go index 7987a64d..3a01c0b4 100644 --- a/client/gql_client.go +++ b/client/gql_client.go @@ -4,6 +4,7 @@ import ( "bytes" "encoding/json" "errors" + "fmt" "io/ioutil" "net/http" ) @@ -32,7 +33,11 @@ func (client *Client) SendGqlRequest(request GraphQLRequest) ([]byte, error) { req.Header.Set(tokenHeader, client.Token) req.Header.Set("Content-Type", "application/json; charset=utf-8") + fmt.Println("Authorization", req.Header.Get("Authorization")) + fmt.Println("x-access-token", req.Header.Get("x-access-token")) httpClient := &http.Client{} + fmt.Println("url", req.URL) + fmt.Println("host", req.Host) resp, err := httpClient.Do(req) if err != nil { return nil, err From baf583bf0c44105fcdf6704e20d04b0b17d7873d Mon Sep 17 00:00:00 2001 From: andrii-codefresh Date: Fri, 23 Jun 2023 22:46:55 +0300 Subject: [PATCH 12/13] remove debug logs --- client/gql_client.go | 5 ----- 1 file changed, 5 deletions(-) diff --git a/client/gql_client.go b/client/gql_client.go index 3a01c0b4..7987a64d 100644 --- a/client/gql_client.go +++ b/client/gql_client.go @@ -4,7 +4,6 @@ import ( "bytes" "encoding/json" "errors" - "fmt" "io/ioutil" "net/http" ) @@ -33,11 +32,7 @@ func (client *Client) SendGqlRequest(request GraphQLRequest) ([]byte, error) { req.Header.Set(tokenHeader, client.Token) req.Header.Set("Content-Type", "application/json; charset=utf-8") - fmt.Println("Authorization", req.Header.Get("Authorization")) - fmt.Println("x-access-token", req.Header.Get("x-access-token")) httpClient := &http.Client{} - fmt.Println("url", req.URL) - fmt.Println("host", req.Host) resp, err := httpClient.Do(req) if err != nil { return nil, err From 8d8c9477a5647acb656d889b9992ae0dadd33dba Mon Sep 17 00:00:00 2001 From: andrii-codefresh Date: Thu, 29 Jun 2023 17:38:32 +0300 Subject: [PATCH 13/13] template --- docs/resources/abac_rules.md | 31 +++++++++++++++++--- templates/resources/abac_rules.md.tmpl | 40 ++++++++++++++++++++++++++ 2 files changed, 67 insertions(+), 4 deletions(-) create mode 100644 templates/resources/abac_rules.md.tmpl diff --git a/docs/resources/abac_rules.md b/docs/resources/abac_rules.md index 36b222ae..c7cdcb0a 100644 --- a/docs/resources/abac_rules.md +++ b/docs/resources/abac_rules.md @@ -1,5 +1,4 @@ --- -# generated by https://github.com/hashicorp/terraform-plugin-docs page_title: "codefresh_abac_rules Resource - terraform-provider-codefresh" subcategory: "" description: |- @@ -10,7 +9,33 @@ description: |- Gitops Abac Rules are used to setup access control and allow to define which teams have access to which resources based on tags and attributes. +See the [Access Control documentation](https://codefresh.io/docs/docs/administration/account-user-management/access-control/). +## Example usage + +```hcl +resource "codefresh_team" "developers" { + name = "developers" + + users = [ + "5efc3cb6355c6647041b6e49", + "59009221c102763beda7cf04" + ] +} + +resource "codefresh_abac_rules" "app_rule" { + entity_type = "gitopsApplications" + teams = [data.codefresh_team.developers.id] + actions = ["REFRESH", "SYNC", "TERMINATE_SYNC", "APP_ROLLBACK"] + + attribute { + name = "LABEL" + key = "KEY" + value = "VALUE" + } +} + +``` ## Schema @@ -45,6 +70,4 @@ Required: Optional: -- `key` (String) - - +- `key` (String) \ No newline at end of file diff --git a/templates/resources/abac_rules.md.tmpl b/templates/resources/abac_rules.md.tmpl new file mode 100644 index 00000000..bbb4b9d5 --- /dev/null +++ b/templates/resources/abac_rules.md.tmpl @@ -0,0 +1,40 @@ +--- +page_title: "{{.Name}} {{.Type}} - {{.ProviderName}}" +subcategory: "" +description: |- +{{ .Description | plainmarkdown | trimspace | prefixlines " " }} +--- + +# {{.Name}} ({{.Type}}) + +{{ .Description | trimspace }} + +See the [Access Control documentation](https://codefresh.io/docs/docs/administration/account-user-management/access-control/). + +## Example usage + +```hcl +resource "codefresh_team" "developers" { + name = "developers" + + users = [ + "5efc3cb6355c6647041b6e49", + "59009221c102763beda7cf04" + ] +} + +resource "codefresh_abac_rules" "app_rule" { + entity_type = "gitopsApplications" + teams = [data.codefresh_team.developers.id] + actions = ["REFRESH", "SYNC", "TERMINATE_SYNC", "APP_ROLLBACK"] + + attribute { + name = "LABEL" + key = "KEY" + value = "VALUE" + } +} + +``` + +{{ .SchemaMarkdown | trimspace }} \ No newline at end of file