From 719b63ac40695a069106a821fb18d754ee7f04c7 Mon Sep 17 00:00:00 2001 From: Naden Date: Thu, 3 Jun 2021 20:01:12 +1000 Subject: [PATCH 1/4] Added encrypted_value to Actions + Organizations's secrets --- .../data_source_github_repository_branches.go | 123 ++++++++++++++++++ ...urce_github_actions_organization_secret.go | 22 +++- ...github_actions_organization_secret_test.go | 103 ++++++++++++--- github/resource_github_actions_secret.go | 23 +++- github/resource_github_actions_secret_test.go | 108 ++++++++++++--- .../actions_organization_secret.html.markdown | 21 ++- website/docs/r/actions_secret.html.markdown | 11 +- 7 files changed, 365 insertions(+), 46 deletions(-) create mode 100644 github/data_source_github_repository_branches.go diff --git a/github/data_source_github_repository_branches.go b/github/data_source_github_repository_branches.go new file mode 100644 index 0000000000..d026420e53 --- /dev/null +++ b/github/data_source_github_repository_branches.go @@ -0,0 +1,123 @@ +package github + +import ( + "context" + "strings" + + "github.com/google/go-github/v35/github" + "github.com/hashicorp/terraform-plugin-sdk/helper/schema" +) + +func dataSourceGithubRepositoryBranches() *schema.Resource { + return &schema.Resource{ + Read: dataSourceGithubRepositoryBranchesRead, + Schema: map[string]*schema.Schema{ + "name": { + Type: schema.TypeString, + Required: true, + }, + "protected": { + Type: schema.TypeBool, + Required: true, + }, + }, + } +} + +func dataSourceGithubRepositoryBranchesRead(d *schema.ResourceData, meta interface{}) error { + ctx := context.TODO() + client := meta.(*Owner).v3client + + owner := meta.(*Owner).name + if explicitOwner, ok := d.GetOk("owner"); ok { + owner = explicitOwner.(string) + } + + baseRepository := d.Get("base_repository").(string) + state := d.Get("state").(string) + head := d.Get("head_ref").(string) + base := d.Get("base_ref").(string) + sort := d.Get("sort_by").(string) + direction := d.Get("sort_direction").(string) + + options := &github.PullRequestListOptions{ + ListOptions: github.ListOptions{PerPage: 100}, + State: state, + Head: head, + Base: base, + Sort: sort, + Direction: direction, + } + + results := make([]map[string]interface{}, 0) + + for { + pullRequests, resp, err := client.PullRequests.List(ctx, owner, baseRepository, options) + if err != nil { + return err + } + + for _, pullRequest := range pullRequests { + result := map[string]interface{}{ + "number": pullRequest.GetNumber(), + "body": pullRequest.GetBody(), + "draft": pullRequest.GetDraft(), + "maintainer_can_modify": pullRequest.GetMaintainerCanModify(), + "opened_at": pullRequest.GetCreatedAt().Unix(), + "state": pullRequest.GetState(), + "title": pullRequest.GetTitle(), + "updated_at": pullRequest.GetUpdatedAt().Unix(), + } + + if head := pullRequest.GetHead(); head != nil { + result["head_ref"] = head.GetRef() + result["head_sha"] = head.GetSHA() + + if headRepo := head.GetRepo(); headRepo != nil { + result["head_repository"] = headRepo.GetName() + + if headOwner := headRepo.GetOwner(); headOwner != nil { + result["head_owner"] = headOwner.GetLogin() + } + } + } + + if base := pullRequest.GetBase(); base != nil { + result["base_ref"] = base.GetRef() + result["base_sha"] = base.GetSHA() + } + + labels := []string{} + for _, label := range pullRequest.Labels { + labels = append(labels, label.GetName()) + } + result["labels"] = labels + + if user := pullRequest.GetUser(); user != nil { + result["opened_by"] = user.GetLogin() + } + + results = append(results, result) + } + + if resp.NextPage == 0 { + break + } + + options.Page = resp.NextPage + } + + d.SetId(strings.Join([]string{ + owner, + baseRepository, + state, + head, + base, + sort, + direction, + }, "/")) + + d.Set("results", results) + + return nil +} diff --git a/github/resource_github_actions_organization_secret.go b/github/resource_github_actions_organization_secret.go index 3e55589bcb..e638db1f21 100644 --- a/github/resource_github_actions_organization_secret.go +++ b/github/resource_github_actions_organization_secret.go @@ -31,10 +31,16 @@ func resourceGithubActionsOrganizationSecret() *schema.Resource { ForceNew: true, ValidateFunc: validateSecretNameFunc, }, + "encrypted_value": { + Type: schema.TypeString, + ForceNew: true, + Optional: true, + Sensitive: true, + }, "plaintext_value": { Type: schema.TypeString, - Required: true, ForceNew: true, + Optional: true, Sensitive: true, }, "visibility": { @@ -70,6 +76,7 @@ func resourceGithubActionsOrganizationSecretCreateOrUpdate(d *schema.ResourceDat secretName := d.Get("secret_name").(string) plaintextValue := d.Get("plaintext_value").(string) + var encryptedValue []byte visibility := d.Get("visibility").(string) selectedRepositories, hasSelectedRepositories := d.GetOk("selected_repository_ids") @@ -95,9 +102,13 @@ func resourceGithubActionsOrganizationSecretCreateOrUpdate(d *schema.ResourceDat return err } - encryptedText, err := encryptPlaintext(plaintextValue, publicKey) - if err != nil { - return err + if encryptedText, ok := d.GetOk("encrypted_value"); ok { + encryptedValue = []byte(encryptedText.(string)) + } else { + encryptedValue, err = encryptPlaintext(plaintextValue, publicKey) + if err != nil { + return err + } } // Create an EncryptedSecret and encrypt the plaintext value into it @@ -106,7 +117,7 @@ func resourceGithubActionsOrganizationSecretCreateOrUpdate(d *schema.ResourceDat KeyID: keyId, Visibility: visibility, SelectedRepositoryIDs: selectedRepositoryIDs, - EncryptedValue: base64.StdEncoding.EncodeToString(encryptedText), + EncryptedValue: base64.StdEncoding.EncodeToString(encryptedValue), } _, err = client.Actions.CreateOrUpdateOrgSecret(ctx, owner, eSecret) @@ -136,6 +147,7 @@ func resourceGithubActionsOrganizationSecretRead(d *schema.ResourceData, meta in return err } + d.Set("encrypted_value", d.Get("encrypted_value")) d.Set("plaintext_value", d.Get("plaintext_value")) d.Set("created_at", secret.CreatedAt.String()) d.Set("visibility", secret.Visibility) diff --git a/github/resource_github_actions_organization_secret_test.go b/github/resource_github_actions_organization_secret_test.go index 059d255c8e..771760f3c4 100644 --- a/github/resource_github_actions_organization_secret_test.go +++ b/github/resource_github_actions_organization_secret_test.go @@ -9,13 +9,13 @@ import ( ) func TestAccGithubActionsOrganizationSecret(t *testing.T) { - t.Run("creates and updates secrets without error", func(t *testing.T) { + t.Run("creates and updates plain text secrets without error", func(t *testing.T) { secretValue := "super_secret_value" updatedSecretValue := "updated_super_secret_value" config := fmt.Sprintf(` - resource "github_actions_organization_secret" "test_secret" { - secret_name = "test_secret_name" + resource "github_actions_organization_secret" "plaintext_secret" { + secret_name = "test_plaintext_secret" plaintext_value = "%s" visibility = "private" } @@ -24,26 +24,26 @@ func TestAccGithubActionsOrganizationSecret(t *testing.T) { checks := map[string]resource.TestCheckFunc{ "before": resource.ComposeTestCheckFunc( resource.TestCheckResourceAttr( - "github_actions_organization_secret.test_secret", "plaintext_value", + "github_actions_organization_secret.plaintext_secret", "plaintext_value", secretValue, ), resource.TestCheckResourceAttrSet( - "github_actions_organization_secret.test_secret", "created_at", + "github_actions_organization_secret.plaintext_secret", "created_at", ), resource.TestCheckResourceAttrSet( - "github_actions_organization_secret.test_secret", "updated_at", + "github_actions_organization_secret.plaintext_secret", "updated_at", ), ), "after": resource.ComposeTestCheckFunc( resource.TestCheckResourceAttr( - "github_actions_organization_secret.test_secret", "plaintext_value", + "github_actions_organization_secret.plaintext_secret", "plaintext_value", updatedSecretValue, ), resource.TestCheckResourceAttrSet( - "github_actions_organization_secret.test_secret", "created_at", + "github_actions_organization_secret.plaintext_secret", "created_at", ), resource.TestCheckResourceAttrSet( - "github_actions_organization_secret.test_secret", "updated_at", + "github_actions_organization_secret.plaintext_secret", "updated_at", ), ), } @@ -80,16 +80,89 @@ func TestAccGithubActionsOrganizationSecret(t *testing.T) { }) }) - t.Run("deletes secrets without error", func(t *testing.T) { + t.Run("creates and updates encrypted secrets without error", func(t *testing.T) { secretValue := "super_secret_value" + updatedSecretValue := "updated_super_secret_value" config := fmt.Sprintf(` - resource "github_actions_organization_secret" "test_secret" { - secret_name = "test_secret_name" - plaintext_value = "%s" + resource "github_actions_organization_secret" "encrypted_secret" { + secret_name = "test_encrypted_secret_name" + encrypted_value = "%s" + visibility = "private" + } + `, secretValue) + + checks := map[string]resource.TestCheckFunc{ + "before": resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr( + "github_actions_organization_secret.encrypted_secret", "encrypted_value", + secretValue, + ), + resource.TestCheckResourceAttrSet( + "github_actions_organization_secret.encrypted_secret", "created_at", + ), + resource.TestCheckResourceAttrSet( + "github_actions_organization_secret.encrypted_secret", "updated_at", + ), + ), + "after": resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr( + "github_actions_organization_secret.encrypted_secret", "encrypted_value", + updatedSecretValue, + ), + resource.TestCheckResourceAttrSet( + "github_actions_organization_secret.encrypted_secret", "created_at", + ), + resource.TestCheckResourceAttrSet( + "github_actions_organization_secret.encrypted_secret", "updated_at", + ), + ), + } + + testCase := func(t *testing.T, mode string) { + resource.Test(t, resource.TestCase{ + PreCheck: func() { skipUnlessMode(t, mode) }, + Providers: testAccProviders, + Steps: []resource.TestStep{ + { + Config: config, + Check: checks["before"], + }, + { + Config: strings.Replace(config, + secretValue, + updatedSecretValue, 1), + Check: checks["after"], + }, + }, + }) + } + + t.Run("with an anonymous account", func(t *testing.T) { + t.Skip("anonymous account not supported for this operation") + }) + + t.Run("with an individual account", func(t *testing.T) { + t.Skip("individual account not supported for this operation") + }) + + t.Run("with an organization account", func(t *testing.T) { + testCase(t, organization) + }) + }) + + t.Run("deletes secrets without error", func(t *testing.T) { + config := ` + resource "github_actions_organization_secret" "plaintext_secret" { + secret_name = "test_plaintext_secret" + visibility = "private" + } + + resource "github_actions_organization_secret" "encrypted_secret" { + secret_name = "test_encrypted_secret" visibility = "private" } - `, secretValue) + ` testCase := func(t *testing.T, mode string) { resource.Test(t, resource.TestCase{ @@ -122,7 +195,7 @@ func TestAccGithubActionsOrganizationSecret(t *testing.T) { config := fmt.Sprintf(` resource "github_actions_organization_secret" "test_secret" { - secret_name = "test_secret_name" + secret_name = "test_plaintext_secret" plaintext_value = "%s" visibility = "private" } diff --git a/github/resource_github_actions_secret.go b/github/resource_github_actions_secret.go index c1ad5d784f..f519a51f05 100644 --- a/github/resource_github_actions_secret.go +++ b/github/resource_github_actions_secret.go @@ -30,9 +30,16 @@ func resourceGithubActionsSecret() *schema.Resource { ForceNew: true, ValidateFunc: validateSecretNameFunc, }, + "encrypted_value": { + Type: schema.TypeString, + ForceNew: true, + Optional: true, + Sensitive: true, + }, "plaintext_value": { Type: schema.TypeString, - Required: true, + ForceNew: true, + Optional: true, Sensitive: true, }, "created_at": { @@ -55,22 +62,27 @@ func resourceGithubActionsSecretCreateOrUpdate(d *schema.ResourceData, meta inte repo := d.Get("repository").(string) secretName := d.Get("secret_name").(string) plaintextValue := d.Get("plaintext_value").(string) + var encryptedValue []byte keyId, publicKey, err := getPublicKeyDetails(owner, repo, meta) if err != nil { return err } - encryptedText, err := encryptPlaintext(plaintextValue, publicKey) - if err != nil { - return err + if encryptedText, ok := d.GetOk("encrypted_value"); ok { + encryptedValue = []byte(encryptedText.(string)) + } else { + encryptedValue, err = encryptPlaintext(plaintextValue, publicKey) + if err != nil { + return err + } } // Create an EncryptedSecret and encrypt the plaintext value into it eSecret := &github.EncryptedSecret{ Name: secretName, KeyID: keyId, - EncryptedValue: base64.StdEncoding.EncodeToString(encryptedText), + EncryptedValue: base64.StdEncoding.EncodeToString(encryptedValue), } _, err = client.Actions.CreateOrUpdateRepoSecret(ctx, owner, repo, eSecret) @@ -105,6 +117,7 @@ func resourceGithubActionsSecretRead(d *schema.ResourceData, meta interface{}) e return err } + d.Set("encrypted_value", d.Get("encrypted_value")) d.Set("plaintext_value", d.Get("plaintext_value")) d.Set("created_at", secret.CreatedAt.String()) diff --git a/github/resource_github_actions_secret_test.go b/github/resource_github_actions_secret_test.go index c072b743a7..22089e6b19 100644 --- a/github/resource_github_actions_secret_test.go +++ b/github/resource_github_actions_secret_test.go @@ -64,7 +64,7 @@ func TestAccGithubActionsSecret(t *testing.T) { }) - t.Run("creates and updates secrets without error", func(t *testing.T) { + t.Run("creates and updates plaintext secrets without error", func(t *testing.T) { secretValue := "super_secret_value" updatedSecretValue := "updated_super_secret_value" @@ -74,9 +74,9 @@ func TestAccGithubActionsSecret(t *testing.T) { name = "tf-acc-test-%s" } - resource "github_actions_secret" "test_secret" { + resource "github_actions_secret" "plaintext_secret" { repository = github_repository.test.name - secret_name = "test_secret_name" + secret_name = "text_plaintext_secret" plaintext_value = "%s" } `, randomID, secretValue) @@ -84,26 +84,26 @@ func TestAccGithubActionsSecret(t *testing.T) { checks := map[string]resource.TestCheckFunc{ "before": resource.ComposeTestCheckFunc( resource.TestCheckResourceAttr( - "github_actions_secret.test_secret", "plaintext_value", + "github_actions_secret.plaintext_secret", "plaintext_value", secretValue, ), resource.TestCheckResourceAttrSet( - "github_actions_secret.test_secret", "created_at", + "github_actions_secret.plaintext_secret", "created_at", ), resource.TestCheckResourceAttrSet( - "github_actions_secret.test_secret", "updated_at", + "github_actions_secret.plaintext_secret", "updated_at", ), ), "after": resource.ComposeTestCheckFunc( resource.TestCheckResourceAttr( - "github_actions_secret.test_secret", "plaintext_value", + "github_actions_secret.plaintext_secret", "plaintext_value", updatedSecretValue, ), resource.TestCheckResourceAttrSet( - "github_actions_secret.test_secret", "created_at", + "github_actions_secret.plaintext_secret", "created_at", ), resource.TestCheckResourceAttrSet( - "github_actions_secret.test_secret", "updated_at", + "github_actions_secret.plaintext_secret", "updated_at", ), ), } @@ -141,21 +141,99 @@ func TestAccGithubActionsSecret(t *testing.T) { }) - t.Run("deletes secrets without error", func(t *testing.T) { + t.Run("creates and updates encrypted secrets without error", func(t *testing.T) { secretValue := "super_secret_value" + updatedSecretValue := "updated_super_secret_value" + + config := fmt.Sprintf(` + resource "github_repository" "test" { + name = "tf-acc-test-%s" + } + + resource "github_actions_secret" "encrypted_secret" { + repository = github_repository.test.name + secret_name = "text_encrypted_secret" + encrypted_value = "%s" + } + `, randomID, secretValue) + + checks := map[string]resource.TestCheckFunc{ + "before": resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr( + "github_actions_secret.encrypted_secret", "encrypted_value", + secretValue, + ), + resource.TestCheckResourceAttrSet( + "github_actions_secret.encrypted_secret", "created_at", + ), + resource.TestCheckResourceAttrSet( + "github_actions_secret.encrypted_secret", "updated_at", + ), + ), + "after": resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr( + "github_actions_secret.encrypted_secret", "encrypted_value", + updatedSecretValue, + ), + resource.TestCheckResourceAttrSet( + "github_actions_secret.encrypted_secret", "created_at", + ), + resource.TestCheckResourceAttrSet( + "github_actions_secret.encrypted_secret", "updated_at", + ), + ), + } + + testCase := func(t *testing.T, mode string) { + resource.Test(t, resource.TestCase{ + PreCheck: func() { skipUnlessMode(t, mode) }, + Providers: testAccProviders, + Steps: []resource.TestStep{ + { + Config: config, + Check: checks["before"], + }, + { + Config: strings.Replace(config, + secretValue, + updatedSecretValue, 1), + Check: checks["after"], + }, + }, + }) + } + t.Run("with an anonymous account", func(t *testing.T) { + t.Skip("anonymous account not supported for this operation") + }) + + t.Run("with an individual account", func(t *testing.T) { + testCase(t, individual) + }) + + t.Run("with an organization account", func(t *testing.T) { + testCase(t, organization) + }) + + }) + + t.Run("deletes secrets without error", func(t *testing.T) { config := fmt.Sprintf(` resource "github_repository" "test" { name = "tf-acc-test-%s" } - resource "github_actions_secret" "test_secret" { - repository = github_repository.test.name - secret_name = "test_secret_name" - plaintext_value = "%s" + resource "github_actions_secret" "plaintext_secret" { + repository = github_repository.test.name + secret_name = "test_plaintext_secret" + } + + resource "github_actions_secret" "encrypted_secret" { + repository = github_repository.test.name + secret_name = "test_encrypted_secret" } - `, randomID, secretValue) + `, randomID) testCase := func(t *testing.T, mode string) { resource.Test(t, resource.TestCase{ diff --git a/website/docs/r/actions_organization_secret.html.markdown b/website/docs/r/actions_organization_secret.html.markdown index 63f0e5e08e..800d1e026d 100644 --- a/website/docs/r/actions_organization_secret.html.markdown +++ b/website/docs/r/actions_organization_secret.html.markdown @@ -15,7 +15,7 @@ interoperable with [libsodium](https://libsodium.gitbook.io/doc/). Libsodium is For the purposes of security, the contents of the `plaintext_value` field have been marked as `sensitive` to Terraform, but it is important to note that **this does not hide it from state files**. You should treat state as sensitive always. -It is also advised that you do not store plaintext values in your code but rather populate the `plaintext_value` +It is also advised that you do not store plaintext values in your code but rather populate the `encrypted_value` using fields from a resource, data source or variable as, while encrypted in state, these will be easily accessible in your code. See below for an example of this abstraction. @@ -27,6 +27,12 @@ resource "github_actions_organization_secret" "example_secret" { visibility = "private" plaintext_value = var.some_secret_string } + +resource "github_actions_organization_secret" "example_secret" { + secret_name = "example_secret_name" + visibility = "private" + encrypted_value = var.some_encrypted_secret_string +} ``` ```hcl @@ -40,6 +46,13 @@ resource "github_actions_organization_secret" "example_secret" { plaintext_value = var.some_secret_string selected_repository_ids = [data.github_repository.repo.repo_id] } + +resource "github_actions_organization_secret" "example_secret" { + secret_name = "example_secret_name" + visibility = "selected" + encrypted_value = var.some_encrypted_secret_string + selected_repository_ids = [data.github_repository.repo.repo_id] +} ``` ## Argument Reference @@ -47,7 +60,8 @@ resource "github_actions_organization_secret" "example_secret" { The following arguments are supported: * `secret_name` - (Required) Name of the secret -* `plaintext_value` - (Required) Plaintext value of the secret to be encrypted +* `encrypted_value` - (Optional) Encrypted value of the secret +* `plaintext_value` - (Optional) Plaintext value of the secret to be encrypted * `visiblity` - (Required) Configures the access that repositories have to the organization secret. Must be one of `all`, `private`, `selected`. `selected_repository_ids` is required if set to `selected`. * `selected_repository_ids` - (Optional) An array of repository ids that can access the organization secret. @@ -66,5 +80,4 @@ $ terraform import github_actions_organization_secret.test_secret test_secret_na ``` NOTE: the implementation is limited in that it won't fetch the value of the -`plaintext_value` field when importing. You may need to ignore changes for the -`plaintext_value` as a workaround. +`plaintext_value` or `encrypted_value` fields when importing. You may need to ignore changes for these as a workaround. diff --git a/website/docs/r/actions_secret.html.markdown b/website/docs/r/actions_secret.html.markdown index d4becd7239..a47cf14628 100644 --- a/website/docs/r/actions_secret.html.markdown +++ b/website/docs/r/actions_secret.html.markdown @@ -15,7 +15,7 @@ interoperable with [libsodium](https://libsodium.gitbook.io/doc/). Libsodium is For the purposes of security, the contents of the `plaintext_value` field have been marked as `sensitive` to Terraform, but it is important to note that **this does not hide it from state files**. You should treat state as sensitive always. -It is also advised that you do not store plaintext values in your code but rather populate the `plaintext_value` +It is also advised that you do not store plaintext values in your code but rather populate the `encrypted_value` using fields from a resource, data source or variable as, while encrypted in state, these will be easily accessible in your code. See below for an example of this abstraction. @@ -31,6 +31,12 @@ resource "github_actions_secret" "example_secret" { secret_name = "example_secret_name" plaintext_value = var.some_secret_string } + +resource "github_actions_secret" "example_secret" { + repository = "example_repository" + secret_name = "example_secret_name" + encrypted_value = var.some_encrypted_secret_string +} ``` ## Argument Reference @@ -39,7 +45,8 @@ The following arguments are supported: * `repository` - (Required) Name of the repository * `secret_name` - (Required) Name of the secret -* `plaintext_value` - (Required) Plaintext value of the secret to be encrypted +* `encrypted_value` - (Optional) Encrypted value of the secret +* `plaintext_value` - (Optional) Plaintext value of the secret to be encrypted ## Attributes Reference From 01de2f64763a1558a4b25ff0d342c00962f25997 Mon Sep 17 00:00:00 2001 From: Naden Date: Thu, 3 Jun 2021 21:21:35 +1000 Subject: [PATCH 2/4] Simplified unit tests --- ...github_actions_organization_secret_test.go | 87 +++------------- github/resource_github_actions_secret_test.go | 99 ++++--------------- 2 files changed, 32 insertions(+), 154 deletions(-) diff --git a/github/resource_github_actions_organization_secret_test.go b/github/resource_github_actions_organization_secret_test.go index 771760f3c4..33dca385b4 100644 --- a/github/resource_github_actions_organization_secret_test.go +++ b/github/resource_github_actions_organization_secret_test.go @@ -9,7 +9,7 @@ import ( ) func TestAccGithubActionsOrganizationSecret(t *testing.T) { - t.Run("creates and updates plain text secrets without error", func(t *testing.T) { + t.Run("creates and updates secrets without error", func(t *testing.T) { secretValue := "super_secret_value" updatedSecretValue := "updated_super_secret_value" @@ -19,7 +19,13 @@ func TestAccGithubActionsOrganizationSecret(t *testing.T) { plaintext_value = "%s" visibility = "private" } - `, secretValue) + + resource "github_actions_organization_secret" "encrypted_secret" { + secret_name = "test_encrypted_secret" + encrypted_value = "%s" + visibility = "private" + } + `, secretValue, secretValue) checks := map[string]resource.TestCheckFunc{ "before": resource.ComposeTestCheckFunc( @@ -27,6 +33,10 @@ func TestAccGithubActionsOrganizationSecret(t *testing.T) { "github_actions_organization_secret.plaintext_secret", "plaintext_value", secretValue, ), + resource.TestCheckResourceAttr( + "github_actions_organization_secret.encrypted_secret", "encrypted_value", + secretValue, + ), resource.TestCheckResourceAttrSet( "github_actions_organization_secret.plaintext_secret", "created_at", ), @@ -39,82 +49,15 @@ func TestAccGithubActionsOrganizationSecret(t *testing.T) { "github_actions_organization_secret.plaintext_secret", "plaintext_value", updatedSecretValue, ), - resource.TestCheckResourceAttrSet( - "github_actions_organization_secret.plaintext_secret", "created_at", - ), - resource.TestCheckResourceAttrSet( - "github_actions_organization_secret.plaintext_secret", "updated_at", - ), - ), - } - - testCase := func(t *testing.T, mode string) { - resource.Test(t, resource.TestCase{ - PreCheck: func() { skipUnlessMode(t, mode) }, - Providers: testAccProviders, - Steps: []resource.TestStep{ - { - Config: config, - Check: checks["before"], - }, - { - Config: strings.Replace(config, - secretValue, - updatedSecretValue, 1), - Check: checks["after"], - }, - }, - }) - } - - t.Run("with an anonymous account", func(t *testing.T) { - t.Skip("anonymous account not supported for this operation") - }) - - t.Run("with an individual account", func(t *testing.T) { - t.Skip("individual account not supported for this operation") - }) - - t.Run("with an organization account", func(t *testing.T) { - testCase(t, organization) - }) - }) - - t.Run("creates and updates encrypted secrets without error", func(t *testing.T) { - secretValue := "super_secret_value" - updatedSecretValue := "updated_super_secret_value" - - config := fmt.Sprintf(` - resource "github_actions_organization_secret" "encrypted_secret" { - secret_name = "test_encrypted_secret_name" - encrypted_value = "%s" - visibility = "private" - } - `, secretValue) - - checks := map[string]resource.TestCheckFunc{ - "before": resource.ComposeTestCheckFunc( - resource.TestCheckResourceAttr( - "github_actions_organization_secret.encrypted_secret", "encrypted_value", - secretValue, - ), - resource.TestCheckResourceAttrSet( - "github_actions_organization_secret.encrypted_secret", "created_at", - ), - resource.TestCheckResourceAttrSet( - "github_actions_organization_secret.encrypted_secret", "updated_at", - ), - ), - "after": resource.ComposeTestCheckFunc( resource.TestCheckResourceAttr( "github_actions_organization_secret.encrypted_secret", "encrypted_value", updatedSecretValue, ), resource.TestCheckResourceAttrSet( - "github_actions_organization_secret.encrypted_secret", "created_at", + "github_actions_organization_secret.plaintext_secret", "created_at", ), resource.TestCheckResourceAttrSet( - "github_actions_organization_secret.encrypted_secret", "updated_at", + "github_actions_organization_secret.plaintext_secret", "updated_at", ), ), } @@ -131,7 +74,7 @@ func TestAccGithubActionsOrganizationSecret(t *testing.T) { { Config: strings.Replace(config, secretValue, - updatedSecretValue, 1), + updatedSecretValue, 2), Check: checks["after"], }, }, diff --git a/github/resource_github_actions_secret_test.go b/github/resource_github_actions_secret_test.go index 22089e6b19..8137182ba7 100644 --- a/github/resource_github_actions_secret_test.go +++ b/github/resource_github_actions_secret_test.go @@ -64,7 +64,7 @@ func TestAccGithubActionsSecret(t *testing.T) { }) - t.Run("creates and updates plaintext secrets without error", func(t *testing.T) { + t.Run("creates and updates secrets without error", func(t *testing.T) { secretValue := "super_secret_value" updatedSecretValue := "updated_super_secret_value" @@ -76,10 +76,16 @@ func TestAccGithubActionsSecret(t *testing.T) { resource "github_actions_secret" "plaintext_secret" { repository = github_repository.test.name - secret_name = "text_plaintext_secret" + secret_name = "test_plaintext_secret" plaintext_value = "%s" } - `, randomID, secretValue) + + resource "github_actions_secret" "encrypted_secret" { + repository = github_repository.test.name + secret_name = "test_encrypted_secret" + encrypted_value = "%s" + } + `, randomID, secretValue, secretValue) checks := map[string]resource.TestCheckFunc{ "before": resource.ComposeTestCheckFunc( @@ -87,6 +93,10 @@ func TestAccGithubActionsSecret(t *testing.T) { "github_actions_secret.plaintext_secret", "plaintext_value", secretValue, ), + resource.TestCheckResourceAttr( + "github_actions_secret.encrypted_secret", "encrypted_value", + secretValue, + ), resource.TestCheckResourceAttrSet( "github_actions_secret.plaintext_secret", "created_at", ), @@ -99,88 +109,15 @@ func TestAccGithubActionsSecret(t *testing.T) { "github_actions_secret.plaintext_secret", "plaintext_value", updatedSecretValue, ), - resource.TestCheckResourceAttrSet( - "github_actions_secret.plaintext_secret", "created_at", - ), - resource.TestCheckResourceAttrSet( - "github_actions_secret.plaintext_secret", "updated_at", - ), - ), - } - - testCase := func(t *testing.T, mode string) { - resource.Test(t, resource.TestCase{ - PreCheck: func() { skipUnlessMode(t, mode) }, - Providers: testAccProviders, - Steps: []resource.TestStep{ - { - Config: config, - Check: checks["before"], - }, - { - Config: strings.Replace(config, - secretValue, - updatedSecretValue, 1), - Check: checks["after"], - }, - }, - }) - } - - t.Run("with an anonymous account", func(t *testing.T) { - t.Skip("anonymous account not supported for this operation") - }) - - t.Run("with an individual account", func(t *testing.T) { - testCase(t, individual) - }) - - t.Run("with an organization account", func(t *testing.T) { - testCase(t, organization) - }) - - }) - - t.Run("creates and updates encrypted secrets without error", func(t *testing.T) { - - secretValue := "super_secret_value" - updatedSecretValue := "updated_super_secret_value" - - config := fmt.Sprintf(` - resource "github_repository" "test" { - name = "tf-acc-test-%s" - } - - resource "github_actions_secret" "encrypted_secret" { - repository = github_repository.test.name - secret_name = "text_encrypted_secret" - encrypted_value = "%s" - } - `, randomID, secretValue) - - checks := map[string]resource.TestCheckFunc{ - "before": resource.ComposeTestCheckFunc( - resource.TestCheckResourceAttr( - "github_actions_secret.encrypted_secret", "encrypted_value", - secretValue, - ), - resource.TestCheckResourceAttrSet( - "github_actions_secret.encrypted_secret", "created_at", - ), - resource.TestCheckResourceAttrSet( - "github_actions_secret.encrypted_secret", "updated_at", - ), - ), - "after": resource.ComposeTestCheckFunc( resource.TestCheckResourceAttr( "github_actions_secret.encrypted_secret", "encrypted_value", updatedSecretValue, ), resource.TestCheckResourceAttrSet( - "github_actions_secret.encrypted_secret", "created_at", + "github_actions_secret.plaintext_secret", "created_at", ), resource.TestCheckResourceAttrSet( - "github_actions_secret.encrypted_secret", "updated_at", + "github_actions_secret.plaintext_secret", "updated_at", ), ), } @@ -197,7 +134,7 @@ func TestAccGithubActionsSecret(t *testing.T) { { Config: strings.Replace(config, secretValue, - updatedSecretValue, 1), + updatedSecretValue, 2), Check: checks["after"], }, }, @@ -215,7 +152,6 @@ func TestAccGithubActionsSecret(t *testing.T) { t.Run("with an organization account", func(t *testing.T) { testCase(t, organization) }) - }) t.Run("deletes secrets without error", func(t *testing.T) { @@ -233,7 +169,7 @@ func TestAccGithubActionsSecret(t *testing.T) { repository = github_repository.test.name secret_name = "test_encrypted_secret" } - `, randomID) + `, randomID) testCase := func(t *testing.T, mode string) { resource.Test(t, resource.TestCase{ @@ -261,5 +197,4 @@ func TestAccGithubActionsSecret(t *testing.T) { }) }) - } From cf96f25c90ea0cb6dbcc2da0183420a57e8c95cc Mon Sep 17 00:00:00 2001 From: Naden Date: Sat, 5 Jun 2021 13:52:26 +1000 Subject: [PATCH 3/4] Encrypted value needs to be in Base64 format --- ...urce_github_actions_organization_secret.go | 29 +++++++++++-------- github/resource_github_actions_secret.go | 27 +++++++++-------- .../actions_organization_secret.html.markdown | 2 +- website/docs/r/actions_secret.html.markdown | 2 +- 4 files changed, 34 insertions(+), 26 deletions(-) diff --git a/github/resource_github_actions_organization_secret.go b/github/resource_github_actions_organization_secret.go index e638db1f21..4e23ae5f3e 100644 --- a/github/resource_github_actions_organization_secret.go +++ b/github/resource_github_actions_organization_secret.go @@ -9,6 +9,7 @@ import ( "github.com/google/go-github/v35/github" "github.com/hashicorp/terraform-plugin-sdk/helper/schema" + "github.com/hashicorp/terraform-plugin-sdk/helper/validation" ) func resourceGithubActionsOrganizationSecret() *schema.Resource { @@ -32,16 +33,19 @@ func resourceGithubActionsOrganizationSecret() *schema.Resource { ValidateFunc: validateSecretNameFunc, }, "encrypted_value": { - Type: schema.TypeString, - ForceNew: true, - Optional: true, - Sensitive: true, + Type: schema.TypeString, + ForceNew: true, + Optional: true, + Sensitive: true, + ConflictsWith: []string{"plaintext_value"}, + ValidateFunc: validation.StringIsBase64, }, "plaintext_value": { - Type: schema.TypeString, - ForceNew: true, - Optional: true, - Sensitive: true, + Type: schema.TypeString, + ForceNew: true, + Optional: true, + Sensitive: true, + ConflictsWith: []string{"encrypted_value"}, }, "visibility": { Type: schema.TypeString, @@ -76,7 +80,7 @@ func resourceGithubActionsOrganizationSecretCreateOrUpdate(d *schema.ResourceDat secretName := d.Get("secret_name").(string) plaintextValue := d.Get("plaintext_value").(string) - var encryptedValue []byte + var encryptedValue string visibility := d.Get("visibility").(string) selectedRepositories, hasSelectedRepositories := d.GetOk("selected_repository_ids") @@ -103,12 +107,13 @@ func resourceGithubActionsOrganizationSecretCreateOrUpdate(d *schema.ResourceDat } if encryptedText, ok := d.GetOk("encrypted_value"); ok { - encryptedValue = []byte(encryptedText.(string)) + encryptedValue = encryptedText.(string) } else { - encryptedValue, err = encryptPlaintext(plaintextValue, publicKey) + encryptedBytes, err := encryptPlaintext(plaintextValue, publicKey) if err != nil { return err } + encryptedValue = base64.StdEncoding.EncodeToString(encryptedBytes) } // Create an EncryptedSecret and encrypt the plaintext value into it @@ -117,7 +122,7 @@ func resourceGithubActionsOrganizationSecretCreateOrUpdate(d *schema.ResourceDat KeyID: keyId, Visibility: visibility, SelectedRepositoryIDs: selectedRepositoryIDs, - EncryptedValue: base64.StdEncoding.EncodeToString(encryptedValue), + EncryptedValue: encryptedValue, } _, err = client.Actions.CreateOrUpdateOrgSecret(ctx, owner, eSecret) diff --git a/github/resource_github_actions_secret.go b/github/resource_github_actions_secret.go index f519a51f05..034734d48a 100644 --- a/github/resource_github_actions_secret.go +++ b/github/resource_github_actions_secret.go @@ -31,16 +31,18 @@ func resourceGithubActionsSecret() *schema.Resource { ValidateFunc: validateSecretNameFunc, }, "encrypted_value": { - Type: schema.TypeString, - ForceNew: true, - Optional: true, - Sensitive: true, + Type: schema.TypeString, + ForceNew: true, + Optional: true, + Sensitive: true, + ConflictsWith: []string{"plaintext_value"}, }, "plaintext_value": { - Type: schema.TypeString, - ForceNew: true, - Optional: true, - Sensitive: true, + Type: schema.TypeString, + ForceNew: true, + Optional: true, + Sensitive: true, + ConflictsWith: []string{"encrypted_value"}, }, "created_at": { Type: schema.TypeString, @@ -62,7 +64,7 @@ func resourceGithubActionsSecretCreateOrUpdate(d *schema.ResourceData, meta inte repo := d.Get("repository").(string) secretName := d.Get("secret_name").(string) plaintextValue := d.Get("plaintext_value").(string) - var encryptedValue []byte + var encryptedValue string keyId, publicKey, err := getPublicKeyDetails(owner, repo, meta) if err != nil { @@ -70,19 +72,20 @@ func resourceGithubActionsSecretCreateOrUpdate(d *schema.ResourceData, meta inte } if encryptedText, ok := d.GetOk("encrypted_value"); ok { - encryptedValue = []byte(encryptedText.(string)) + encryptedValue = encryptedText.(string) } else { - encryptedValue, err = encryptPlaintext(plaintextValue, publicKey) + encryptedBytes, err := encryptPlaintext(plaintextValue, publicKey) if err != nil { return err } + encryptedValue = base64.StdEncoding.EncodeToString(encryptedBytes) } // Create an EncryptedSecret and encrypt the plaintext value into it eSecret := &github.EncryptedSecret{ Name: secretName, KeyID: keyId, - EncryptedValue: base64.StdEncoding.EncodeToString(encryptedValue), + EncryptedValue: encryptedValue, } _, err = client.Actions.CreateOrUpdateRepoSecret(ctx, owner, repo, eSecret) diff --git a/website/docs/r/actions_organization_secret.html.markdown b/website/docs/r/actions_organization_secret.html.markdown index 800d1e026d..1f62725fdb 100644 --- a/website/docs/r/actions_organization_secret.html.markdown +++ b/website/docs/r/actions_organization_secret.html.markdown @@ -60,7 +60,7 @@ resource "github_actions_organization_secret" "example_secret" { The following arguments are supported: * `secret_name` - (Required) Name of the secret -* `encrypted_value` - (Optional) Encrypted value of the secret +* `encrypted_value` - (Optional) Encrypted value of the secret using the Github public key in Base64 format. * `plaintext_value` - (Optional) Plaintext value of the secret to be encrypted * `visiblity` - (Required) Configures the access that repositories have to the organization secret. Must be one of `all`, `private`, `selected`. `selected_repository_ids` is required if set to `selected`. diff --git a/website/docs/r/actions_secret.html.markdown b/website/docs/r/actions_secret.html.markdown index a47cf14628..dd4adca7b2 100644 --- a/website/docs/r/actions_secret.html.markdown +++ b/website/docs/r/actions_secret.html.markdown @@ -45,7 +45,7 @@ The following arguments are supported: * `repository` - (Required) Name of the repository * `secret_name` - (Required) Name of the secret -* `encrypted_value` - (Optional) Encrypted value of the secret +* `encrypted_value` - (Optional) Encrypted value of the secret using the Github public key in Base64 format. * `plaintext_value` - (Optional) Plaintext value of the secret to be encrypted ## Attributes Reference From 5dca528dc4835be30161ad8e245b4dbcb82fd70b Mon Sep 17 00:00:00 2001 From: Naden Date: Tue, 8 Jun 2021 09:49:33 +1000 Subject: [PATCH 4/4] Delete data_source_github_repository_branches.go --- .../data_source_github_repository_branches.go | 123 ------------------ 1 file changed, 123 deletions(-) delete mode 100644 github/data_source_github_repository_branches.go diff --git a/github/data_source_github_repository_branches.go b/github/data_source_github_repository_branches.go deleted file mode 100644 index d026420e53..0000000000 --- a/github/data_source_github_repository_branches.go +++ /dev/null @@ -1,123 +0,0 @@ -package github - -import ( - "context" - "strings" - - "github.com/google/go-github/v35/github" - "github.com/hashicorp/terraform-plugin-sdk/helper/schema" -) - -func dataSourceGithubRepositoryBranches() *schema.Resource { - return &schema.Resource{ - Read: dataSourceGithubRepositoryBranchesRead, - Schema: map[string]*schema.Schema{ - "name": { - Type: schema.TypeString, - Required: true, - }, - "protected": { - Type: schema.TypeBool, - Required: true, - }, - }, - } -} - -func dataSourceGithubRepositoryBranchesRead(d *schema.ResourceData, meta interface{}) error { - ctx := context.TODO() - client := meta.(*Owner).v3client - - owner := meta.(*Owner).name - if explicitOwner, ok := d.GetOk("owner"); ok { - owner = explicitOwner.(string) - } - - baseRepository := d.Get("base_repository").(string) - state := d.Get("state").(string) - head := d.Get("head_ref").(string) - base := d.Get("base_ref").(string) - sort := d.Get("sort_by").(string) - direction := d.Get("sort_direction").(string) - - options := &github.PullRequestListOptions{ - ListOptions: github.ListOptions{PerPage: 100}, - State: state, - Head: head, - Base: base, - Sort: sort, - Direction: direction, - } - - results := make([]map[string]interface{}, 0) - - for { - pullRequests, resp, err := client.PullRequests.List(ctx, owner, baseRepository, options) - if err != nil { - return err - } - - for _, pullRequest := range pullRequests { - result := map[string]interface{}{ - "number": pullRequest.GetNumber(), - "body": pullRequest.GetBody(), - "draft": pullRequest.GetDraft(), - "maintainer_can_modify": pullRequest.GetMaintainerCanModify(), - "opened_at": pullRequest.GetCreatedAt().Unix(), - "state": pullRequest.GetState(), - "title": pullRequest.GetTitle(), - "updated_at": pullRequest.GetUpdatedAt().Unix(), - } - - if head := pullRequest.GetHead(); head != nil { - result["head_ref"] = head.GetRef() - result["head_sha"] = head.GetSHA() - - if headRepo := head.GetRepo(); headRepo != nil { - result["head_repository"] = headRepo.GetName() - - if headOwner := headRepo.GetOwner(); headOwner != nil { - result["head_owner"] = headOwner.GetLogin() - } - } - } - - if base := pullRequest.GetBase(); base != nil { - result["base_ref"] = base.GetRef() - result["base_sha"] = base.GetSHA() - } - - labels := []string{} - for _, label := range pullRequest.Labels { - labels = append(labels, label.GetName()) - } - result["labels"] = labels - - if user := pullRequest.GetUser(); user != nil { - result["opened_by"] = user.GetLogin() - } - - results = append(results, result) - } - - if resp.NextPage == 0 { - break - } - - options.Page = resp.NextPage - } - - d.SetId(strings.Join([]string{ - owner, - baseRepository, - state, - head, - base, - sort, - direction, - }, "/")) - - d.Set("results", results) - - return nil -}