Skip to content

Commit

Permalink
[feat]: Adds security_and_analysis block to github_repository res…
Browse files Browse the repository at this point in the history
…ource.
  • Loading branch information
nickfloyd committed Nov 16, 2022
2 parents 63b0f23 + 72cab5d commit e827c6f
Show file tree
Hide file tree
Showing 3 changed files with 224 additions and 0 deletions.
137 changes: 137 additions & 0 deletions github/resource_github_repository.go
Expand Up @@ -54,6 +54,58 @@ func resourceGithubRepository() *schema.Resource {
Computed: true, // is affected by "private"
ValidateFunc: validation.StringInSlice([]string{"public", "private", "internal"}, false),
},
"security_and_analysis": {
Type: schema.TypeList,
Optional: true,
MaxItems: 1,
Description: "Security and analysis settings for the repository. To use this parameter you must have admin permissions for the repository or be an owner or security manager for the organization that owns the repository.",
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"advanced_security": {
Type: schema.TypeList,
Required: true,
MaxItems: 1,
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"status": {
Type: schema.TypeString,
Required: true,
ValidateFunc: validation.StringInSlice([]string{"enabled", "disabled"}, false),
},
},
},
},
"secret_scanning": {
Type: schema.TypeList,
Required: true,
MaxItems: 1,
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"status": {
Type: schema.TypeString,
Required: true,
ValidateFunc: validation.StringInSlice([]string{"enabled", "disabled"}, false),
},
},
},
},
"secret_scanning_push_protection": {
Type: schema.TypeList,
Required: true,
MaxItems: 1,
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"status": {
Type: schema.TypeString,
Required: true,
ValidateFunc: validation.StringInSlice([]string{"enabled", "disabled"}, false),
},
},
},
},
},
},
},
"has_issues": {
Type: schema.TypeBool,
Optional: true,
Expand Down Expand Up @@ -417,6 +469,14 @@ func resourceGithubRepositoryCreate(d *schema.ResourceData, meta interface{}) er
}
}

securityAndAnalysis := expandSecurityAndAnalysis(d.Get("security_and_analysis").([]interface{}))
if securityAndAnalysis != nil {
_, _, err := client.Repositories.Edit(ctx, owner, repoName, securityAndAnalysis)
if err != nil {
return err
}
}

return resourceGithubRepositoryUpdate(d, meta)
}

Expand Down Expand Up @@ -512,6 +572,12 @@ func resourceGithubRepositoryRead(d *schema.ResourceData, meta interface{}) erro
d.Set("vulnerability_alerts", vulnerabilityAlerts)
}

securityAndAnalysis, _, err := client.Repositories.Edit(ctx, owner, repoName, &github.Repository{})
if err != nil {
return fmt.Errorf("Error reading repository security and analysis settings: %v", err)
}
d.Set("security_and_analysis", flattenSecurityAndAnalysis(securityAndAnalysis.GetSecurityAndAnalysis()))

return nil
}

Expand Down Expand Up @@ -563,6 +629,29 @@ func resourceGithubRepositoryUpdate(d *schema.ResourceData, meta interface{}) er
}
}

if d.HasChange("security_and_analysis") && !d.IsNewResource() {
opts := expandSecurityAndAnalysis(d.Get("security_and_analysis").([]interface{}))
if opts != nil {
_, _, err := client.Repositories.Edit(ctx, owner, repoName, opts)
if err != nil {
return err
}
} else { // disable security and analysis
_, _, err := client.Repositories.Edit(ctx, owner, repoName, &github.Repository{
SecurityAndAnalysis: &github.SecurityAndAnalysis{
AdvancedSecurity: &github.AdvancedSecurity{
Status: github.String("disabled")},
SecretScanning: &github.SecretScanning{
Status: github.String("disabled")},
SecretScanningPushProtection: &github.SecretScanningPushProtection{
Status: github.String("disabled")}},
})
if err != nil {
return err
}
}
}

if d.HasChange("topics") {
topics := repoReq.Topics
_, _, err = client.Repositories.ReplaceAllTopics(ctx, owner, *repo.Name, topics)
Expand Down Expand Up @@ -715,3 +804,51 @@ func flattenPages(pages *github.Pages) []interface{} {

return []interface{}{pagesMap}
}

func flattenSecurityAndAnalysis(securityAndAnalysis *github.SecurityAndAnalysis) []interface{} {
if securityAndAnalysis == nil {
return []interface{}{}
}

advancedSecurityMap := make(map[string]interface{})
advancedSecurityMap["status"] = securityAndAnalysis.GetAdvancedSecurity().GetStatus()

secretScanningMap := make(map[string]interface{})
secretScanningMap["status"] = securityAndAnalysis.GetSecretScanning().GetStatus()

secretScanningPushProtectionMap := make(map[string]interface{})
secretScanningPushProtectionMap["status"] = securityAndAnalysis.GetSecretScanningPushProtection().GetStatus()

securityAndAnalysisMap := make(map[string]interface{})
securityAndAnalysisMap["advanced_security"] = []interface{}{advancedSecurityMap}
securityAndAnalysisMap["secret_scanning"] = []interface{}{secretScanningMap}
securityAndAnalysisMap["secret_scanning_push_protection"] = []interface{}{secretScanningPushProtectionMap}

return []interface{}{securityAndAnalysisMap}
}

func expandSecurityAndAnalysis(input []interface{}) *github.Repository {
if len(input) == 0 || input[0] == nil {
return nil
}

securityAndAnalysis := input[0].(map[string]interface{})
update := &github.SecurityAndAnalysis{}

advancedSecurity := securityAndAnalysis["advanced_security"].([]interface{})[0].(map[string]interface{})
update.AdvancedSecurity = &github.AdvancedSecurity{
Status: github.String(advancedSecurity["status"].(string)),
}

secretScanning := securityAndAnalysis["secret_scanning"].([]interface{})[0].(map[string]interface{})
update.SecretScanning = &github.SecretScanning{
Status: github.String(secretScanning["status"].(string)),
}

secretScanningPushProtection := securityAndAnalysis["secret_scanning_push_protection"].([]interface{})[0].(map[string]interface{})
update.SecretScanningPushProtection = &github.SecretScanningPushProtection{
Status: github.String(secretScanningPushProtection["status"].(string)),
}

return &github.Repository{SecurityAndAnalysis: update}
}
61 changes: 61 additions & 0 deletions github/resource_github_repository_test.go
Expand Up @@ -809,6 +809,67 @@ func TestAccGithubRepositoryPages(t *testing.T) {

}

func TestAccGithubRepositorySecurity(t *testing.T) {

randomID := acctest.RandStringFromCharSet(5, acctest.CharSetAlphaNum)

t.Run("manages the security feature for a repository", func(t *testing.T) {

config := fmt.Sprintf(`
resource "github_repository" "test" {
name = "tf-acc-%s"
description = "A repository created by Terraform to test security features"
visibility = "internal"
security_and_analysis {
advanced_security {
status = "enabled"
}
secret_scanning {
status = "enabled"
}
secret_scanning_push_protection {
status = "enabled"
}
}
`, randomID)

check := resource.ComposeTestCheckFunc(
resource.TestCheckResourceAttr(
"github_repository.test", "security_and_analysis.0.advanced_security.0.status",
"enabled",
),
resource.TestCheckResourceAttr(
"github_repository.test", "security_and_analysis.0.secret_scanning.0.status",
"enabled",
),
)
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: check,
},
},
})
}

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)
})
})
}

func TestAccGithubRepositoryVisibility(t *testing.T) {

randomID := acctest.RandStringFromCharSet(5, acctest.CharSetAlphaNum)
Expand Down
26 changes: 26 additions & 0 deletions website/docs/r/repository.html.markdown
Expand Up @@ -106,6 +106,8 @@ initial repository creation and create the target branch inside of the repositor

* `pages` - (Optional) The repository's GitHub Pages configuration. See [GitHub Pages Configuration](#github-pages-configuration) below for details.

* `security_and_analysis` - (Optional) The repository's [security and analysis](https://docs.github.com/en/repositories/managing-your-repositorys-settings-and-features/enabling-features-for-your-repository/managing-security-and-analysis-settings-for-your-repository) configuration. See [Security and Analysis Configuration](#security-and-analysis-configuration) below for details.

* `topics` - (Optional) The list of topics of the repository.

* `template` - (Optional) Use a template repository to create this resource. See [Template Repositories](#template-repositories) below for details.
Expand All @@ -132,6 +134,30 @@ The `source` block supports the following:

* `path` - (Optional) The repository directory from which the site publishes (Default: `/`).

### Security and Analysis Configuration

The `security_and_analysis` block supports the following:

* `advanced_security` - (Required) The advanced security configuration for the repository. See [Advanced Security Configuration](#advanced-security-configuration) below for details.

* `secret_scanning` - (Required) The secret scanning configuration for the repository. See [Secret Scanning Configuration](#secret-scanning-configuration) below for details.

* `secret_scanning_push_protection` - (Required) The secret scanning push protection configuration for the repository. See [Secret Scanning Push Protection Configuration](#secret-scanning-push-protection-configuration) below for details.

#### Advanced Security Configuration ####

The `advanced_security` block supports the following:

* `status` - (Required) Set to `enabled` to enable advanced security features on the repository. Can be `enabled` or `disabled`.

#### Secret Scanning Configuration ####

* `status` - (Required) Set to `enabled` to enable secret scanning on the repository. Can be `enabled` or `disabled`.

#### Secret Scanning Push Protection Configuration ####

* `status` - (Required) Set to `enabled` to enable secret scanning push protection on the repository. Can be `enabled` or `disabled`.

### Template Repositories

`template` supports the following arguments:
Expand Down

0 comments on commit e827c6f

Please sign in to comment.