diff --git a/README.md b/README.md index 7a3fffc..493398a 100644 --- a/README.md +++ b/README.md @@ -143,6 +143,51 @@ $ terraform import bitbucketserver_repository.test TEST/test-01 ``` +### Assign Repository Permissions for Group + +```hcl +resource "bitbucketserver_repository_permissions_group" "test" { + project = "TEST" + repository = "my-repo" + group = "stash-users" + permission = "REPO_WRITE" +} +``` + +* `project` - Required. Project key to set permissions for. +* `repository` - Required. Repository slug to set permissions for. +* `group` - Required. Name of the group permissions are for. +* `permission` - Required. The permission to grant. Available repository permissions are: `REPO_READ`, `REPO_WRITE`, `REPO_ADMIN` + +#### Import Group Permission for Repository + +```bash +$ terraform import bitbucketserver_repository_permissions_group.test TEST/my-repo/stash-users +``` + + +### Assign Repository Permissions for User + +```hcl +resource "bitbucketserver_repository_permissions_user" "test" { + project = "TEST" + repository = "my-repo" + user = "admin" + permission = "REPO_WRITE" +} +``` + +* `project` - Required. Project key to set permissions for. +* `user` - Required. Name of the user permissions are for. +* `permission` - Required. The permission to grant. Available repository permissions are: `REPO_READ`, `REPO_WRITE`, `REPO_ADMIN` + +#### Import Group Permission for Repository + +```bash +$ terraform import bitbucketserver_repository_permissions_user.test TEST/my-repo/admin +``` + + ### Create a Bitbucket User ```hcl @@ -404,7 +449,7 @@ data "bitbucketserver_project_permissions_groups" "proj" { #### Attributes -* `groups` - List of maps containing `name` and `permission` keys. +* `groups` - List of maps containing `name` and `permission` keys. Available permissions are: `PROJECT_READ`, `PROJECT_WRITE`, `PROJECT_ADMIN` ### Project Permissions - Users @@ -422,7 +467,47 @@ data "bitbucketserver_project_permissions_users" "proj" { #### Attributes -* `users` - List of maps containing `name`, `email_address`, `display_name`, `active` and `permission` keys. +* `users` - List of maps containing `name`, `email_address`, `display_name`, `active` and `permission` keys. Available permissions are: `PROJECT_READ`, `PROJECT_WRITE`, `PROJECT_ADMIN` + + +### Repository Permissions - Groups + +Retrieve a list of groups that have been granted at least one permission for the specified repository. + +```hcl +data "bitbucketserver_project_permissions_groups" "proj" { + project = "TEST" + repository = "my-repo" +} +``` + +* `project` - Required. Project Key to lookup permissions for. +* `repository` - Required. Repository slug to lookup permissions for. +* `filter` - Optional. If specified only group names containing the supplied string will be returned. + +#### Attributes + +* `groups` - List of maps containing `name` and `permission` keys. Available permissions are: `REPO_READ`, `REPO_WRITE`, `REPO_ADMIN` + + +### Repository Permissions - Users + +Retrieve a list of users that have been granted at least one permission for the specified repository. + +```hcl +data "bitbucketserver_project_permissions_users" "proj" { + project = "TEST" + repository = "my-repo" +} +``` + +* `project` - Required. Project Key to lookup permissions for. +* `repository` - Required. Repository slug to lookup permissions for. +* `filter` - Optional. If specified only user names containing the supplied string will be returned. + +#### Attributes + +* `users` - List of maps containing `name`, `email_address`, `display_name`, `active` and `permission` keys. Available permissions are: `REPO_READ`, `REPO_WRITE`, `REPO_ADMIN` --- diff --git a/bitbucket/data_repository_permissions_groups.go b/bitbucket/data_repository_permissions_groups.go new file mode 100644 index 0000000..3728d7e --- /dev/null +++ b/bitbucket/data_repository_permissions_groups.go @@ -0,0 +1,141 @@ +package bitbucket + +import ( + "encoding/json" + "fmt" + "github.com/hashicorp/terraform/helper/schema" + "net/url" +) + +type PaginatedRepositoryPermissionsGroupsValue struct { + Group struct { + Name string `json:"name,omitempty"` + } `json:"group,omitempty"` + Permission string `json:"permission,omitempty"` +} + +type RepositoryPermissionsGroup struct { + Name string + Permission string +} + +type PaginatedRepositoryPermissionsGroups struct { + Values []PaginatedRepositoryPermissionsGroupsValue `json:"values,omitempty"` + Size int `json:"size,omitempty"` + Limit int `json:"limit,omitempty"` + IsLastPage bool `json:"isLastPage,omitempty"` + Start int `json:"start,omitempty"` + NextPageStart int `json:"nextPageStart,omitempty"` +} + +func dataSourceRepositoryPermissionsGroups() *schema.Resource { + return &schema.Resource{ + Read: dataSourceRepositoryPermissionsGroupsRead, + + Schema: map[string]*schema.Schema{ + "project": { + Type: schema.TypeString, + Required: true, + }, + "repository": { + Type: schema.TypeString, + Required: true, + }, + "filter": { + Type: schema.TypeString, + Optional: true, + }, + "groups": { + Type: schema.TypeList, + Computed: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "name": { + Type: schema.TypeString, + Computed: true, + }, + "permission": { + Type: schema.TypeString, + Computed: true, + }, + }, + }, + }, + }, + } +} + +func dataSourceRepositoryPermissionsGroupsRead(d *schema.ResourceData, m interface{}) error { + groups, err := readRepositoryPermissionsGroups(m, d.Get("project").(string), d.Get("repository").(string), d.Get("filter").(string)) + if err != nil { + return err + } + + d.SetId(fmt.Sprintf("%s/%s", d.Get("project").(string), d.Get("repository").(string))) + + var terraformGroups []interface{} + for _, group := range groups { + g := make(map[string]interface{}) + g["name"] = group.Name + g["permission"] = group.Permission + terraformGroups = append(terraformGroups, g) + } + + _ = d.Set("groups", terraformGroups) + return nil +} + +func readRepositoryPermissionsGroups(m interface{}, project string, repository string, filter string) ([]RepositoryPermissionsGroup, error) { + client := m.(*BitbucketClient) + + resourceURL := fmt.Sprintf("/rest/api/1.0/projects/%s/repos/%s/permissions/groups", + url.QueryEscape(project), + url.QueryEscape(repository), + ) + + if filter != "" { + resourceURL += "?filter=" + url.QueryEscape(filter) + } + + var projectGroups PaginatedRepositoryPermissionsGroups + var groups []RepositoryPermissionsGroup + + for { + resp, err := client.Get(resourceURL) + if err != nil { + return nil, err + } + + decoder := json.NewDecoder(resp.Body) + err = decoder.Decode(&projectGroups) + if err != nil { + return nil, err + } + + for _, group := range projectGroups.Values { + g := RepositoryPermissionsGroup{ + Name: group.Group.Name, + Permission: group.Permission, + } + groups = append(groups, g) + } + + if projectGroups.IsLastPage == false { + resourceURL = fmt.Sprintf("/rest/api/1.0/projects/%s/repos/%s/permissions/groups?start=%d", + url.QueryEscape(project), + url.QueryEscape(repository), + projectGroups.NextPageStart, + ) + + if filter != "" { + resourceURL += "&filter=" + url.QueryEscape(filter) + } + + projectGroups = PaginatedRepositoryPermissionsGroups{} + } else { + break + } + } + + return groups, nil +} diff --git a/bitbucket/data_repository_permissions_groups_test.go b/bitbucket/data_repository_permissions_groups_test.go new file mode 100644 index 0000000..f7e99d1 --- /dev/null +++ b/bitbucket/data_repository_permissions_groups_test.go @@ -0,0 +1,85 @@ +package bitbucket + +import ( + "fmt" + "math/rand" + "testing" + "time" + + "github.com/hashicorp/terraform/helper/resource" +) + +func TestAccBitbucketDataRepositoryPermissionsGroups_simple(t *testing.T) { + projectKey := fmt.Sprintf("TEST%v", rand.New(rand.NewSource(time.Now().UnixNano())).Int()) + config := fmt.Sprintf(` + resource "bitbucketserver_project" "test" { + key = "%v" + name = "%v" + } + + resource "bitbucketserver_repository" "test" { + name = "test" + project = bitbucketserver_project.test.key + } + + resource "bitbucketserver_repository_permissions_group" "test" { + repository = bitbucketserver_repository.test.name + project = bitbucketserver_repository.test.project + group = "stash-users" + permission = "REPO_WRITE" + } + + data "bitbucketserver_repository_permissions_groups" "test" { + repository = bitbucketserver_repository_permissions_group.test.repository + project = bitbucketserver_repository_permissions_group.test.project + } + `, projectKey, projectKey) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + Steps: []resource.TestStep{ + { + Config: config, + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr("data.bitbucketserver_repository_permissions_groups.test", "groups.#", "1"), + resource.TestCheckResourceAttr("data.bitbucketserver_repository_permissions_groups.test", "groups.0.name", "stash-users"), + resource.TestCheckResourceAttr("data.bitbucketserver_repository_permissions_groups.test", "groups.0.permission", "REPO_WRITE"), + ), + }, + }, + }) +} + +func TestAccBitbucketDataRepositoryPermissionsGroups_empty(t *testing.T) { + projectKey := fmt.Sprintf("TEST%v", rand.New(rand.NewSource(time.Now().UnixNano())).Int()) + config := fmt.Sprintf(` + resource "bitbucketserver_project" "test" { + key = "%v" + name = "test-repo-for-repository-test" + } + + resource "bitbucketserver_repository" "test" { + name = "test" + project = bitbucketserver_project.test.key + } + + data "bitbucketserver_repository_permissions_groups" "test" { + repository = bitbucketserver_repository.test.name + project = bitbucketserver_repository.test.project + } + `, projectKey) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + Steps: []resource.TestStep{ + { + Config: config, + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr("data.bitbucketserver_repository_permissions_groups.test", "groups.#", "0"), + ), + }, + }, + }) +} diff --git a/bitbucket/data_repository_permissions_users.go b/bitbucket/data_repository_permissions_users.go new file mode 100644 index 0000000..ed10a71 --- /dev/null +++ b/bitbucket/data_repository_permissions_users.go @@ -0,0 +1,165 @@ +package bitbucket + +import ( + "encoding/json" + "fmt" + "github.com/hashicorp/terraform/helper/schema" + "net/url" +) + +type PaginatedRepositoryPermissionsUsersValue struct { + User struct { + Name string `json:"name,omitempty"` + EmailAddress string `json:"emailAddress,omitempty"` + DisplayName string `json:"displayName,omitempty"` + Active bool `json:"active,omitempty"` + } `json:"user,omitempty"` + Permission string `json:"permission,omitempty"` +} + +type RepositoryPermissionsUser struct { + Name string + EmailAddress string + DisplayName string + Active bool + Permission string +} + +type PaginatedRepositoryPermissionsUsers struct { + Values []PaginatedRepositoryPermissionsUsersValue `json:"values,omitempty"` + Size int `json:"size,omitempty"` + Limit int `json:"limit,omitempty"` + IsLastPage bool `json:"isLastPage,omitempty"` + Start int `json:"start,omitempty"` + NextPageStart int `json:"nextPageStart,omitempty"` +} + +func dataSourceRepositoryPermissionsUsers() *schema.Resource { + return &schema.Resource{ + Read: dataSourceRepositoryPermissionsUsersRead, + + Schema: map[string]*schema.Schema{ + "project": { + Type: schema.TypeString, + Required: true, + }, + "repository": { + Type: schema.TypeString, + Required: true, + }, + "filter": { + Type: schema.TypeString, + Optional: true, + }, + "users": { + Type: schema.TypeList, + Computed: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "name": { + Type: schema.TypeString, + Computed: true, + }, + "email_address": { + Type: schema.TypeString, + Computed: true, + }, + "display_name": { + Type: schema.TypeString, + Computed: true, + }, + "active": { + Type: schema.TypeBool, + Computed: true, + }, + "permission": { + Type: schema.TypeString, + Computed: true, + }, + }, + }, + }, + }, + } +} + +func dataSourceRepositoryPermissionsUsersRead(d *schema.ResourceData, m interface{}) error { + users, err := readRepositoryPermissionsUsers(m, d.Get("project").(string), d.Get("repository").(string), d.Get("filter").(string)) + if err != nil { + return err + } + + d.SetId(fmt.Sprintf("%s/%s", d.Get("project").(string), d.Get("repository").(string))) + + var terraformUsers []interface{} + for _, group := range users { + g := make(map[string]interface{}) + g["name"] = group.Name + g["email_address"] = group.EmailAddress + g["display_name"] = group.DisplayName + g["active"] = group.Active + g["permission"] = group.Permission + terraformUsers = append(terraformUsers, g) + } + + _ = d.Set("users", terraformUsers) + return nil +} + +func readRepositoryPermissionsUsers(m interface{}, project string, repository string, filter string) ([]RepositoryPermissionsUser, error) { + client := m.(*BitbucketClient) + + resourceURL := fmt.Sprintf("/rest/api/1.0/projects/%s/repos/%s/permissions/users", + url.QueryEscape(project), + url.QueryEscape(repository), + ) + + if filter != "" { + resourceURL += "?filter=" + url.QueryEscape(filter) + } + + var projectUsers PaginatedRepositoryPermissionsUsers + var users []RepositoryPermissionsUser + + for { + resp, err := client.Get(resourceURL) + if err != nil { + return nil, err + } + + decoder := json.NewDecoder(resp.Body) + err = decoder.Decode(&projectUsers) + if err != nil { + return nil, err + } + + for _, user := range projectUsers.Values { + g := RepositoryPermissionsUser{ + Name: user.User.Name, + EmailAddress: user.User.EmailAddress, + DisplayName: user.User.DisplayName, + Active: user.User.Active, + Permission: user.Permission, + } + users = append(users, g) + } + + if projectUsers.IsLastPage == false { + resourceURL = fmt.Sprintf("/rest/api/1.0/projects/%s/repos/%s/permissions/users?start=%d", + url.QueryEscape(project), + url.QueryEscape(repository), + projectUsers.NextPageStart, + ) + + if filter != "" { + resourceURL += "&filter=" + url.QueryEscape(filter) + } + + projectUsers = PaginatedRepositoryPermissionsUsers{} + } else { + break + } + } + + return users, nil +} diff --git a/bitbucket/data_repository_permissions_users_test.go b/bitbucket/data_repository_permissions_users_test.go new file mode 100644 index 0000000..c0ed0e0 --- /dev/null +++ b/bitbucket/data_repository_permissions_users_test.go @@ -0,0 +1,93 @@ +package bitbucket + +import ( + "fmt" + "math/rand" + "testing" + "time" + + "github.com/hashicorp/terraform/helper/resource" +) + +func TestAccBitbucketDataRepositoryPermissionsUsers_basic(t *testing.T) { + projectKey := fmt.Sprintf("TEST%v", rand.New(rand.NewSource(time.Now().UnixNano())).Int()) + config := fmt.Sprintf(` + resource "bitbucketserver_project" "test" { + key = "%v" + name = "%v" + } + + resource "bitbucketserver_repository" "test" { + name = "test" + project = bitbucketserver_project.test.key + } + + resource "bitbucketserver_repository_permissions_user" "test" { + repository = bitbucketserver_repository.test.name + project = bitbucketserver_project.test.key + user = "admin" + permission = "REPO_WRITE" + } + + data "bitbucketserver_repository_permissions_users" "test" { + repository = bitbucketserver_repository_permissions_user.test.repository + project = bitbucketserver_repository_permissions_user.test.project + } + `, projectKey, projectKey) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + Steps: []resource.TestStep{ + { + Config: config, + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr("data.bitbucketserver_repository_permissions_users.test", "users.#", "1"), + resource.TestCheckResourceAttr("data.bitbucketserver_repository_permissions_users.test", "users.0.name", "admin"), + resource.TestCheckResourceAttr("data.bitbucketserver_repository_permissions_users.test", "users.0.display_name", "Admin"), + resource.TestCheckResourceAttr("data.bitbucketserver_repository_permissions_users.test", "users.0.email_address", "admin@example.com"), + resource.TestCheckResourceAttr("data.bitbucketserver_repository_permissions_users.test", "users.0.active", "true"), + resource.TestCheckResourceAttr("data.bitbucketserver_repository_permissions_users.test", "users.0.permission", "REPO_WRITE"), + ), + }, + }, + }) +} + +func TestAccBitbucketDataRepositoryPermissionsUsers_empty(t *testing.T) { + projectKey := fmt.Sprintf("TEST%v", rand.New(rand.NewSource(time.Now().UnixNano())).Int()) + config := fmt.Sprintf(` + resource "bitbucketserver_project" "test" { + key = "%v" + name = "test-repo-for-repository-test" + } + + resource "bitbucketserver_repository" "test" { + name = "test" + project = bitbucketserver_project.test.key + } + + data "bitbucketserver_repository_permissions_users" "test" { + repository = bitbucketserver_repository.test.name + project = bitbucketserver_project.test.key + } + `, projectKey) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + Steps: []resource.TestStep{ + { + Config: config, + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr("data.bitbucketserver_repository_permissions_users.test", "users.#", "0"), + resource.TestCheckResourceAttr("data.bitbucketserver_repository_permissions_users.test", "users.0.name", "admin"), + resource.TestCheckResourceAttr("data.bitbucketserver_repository_permissions_users.test", "users.0.display_name", "Admin"), + resource.TestCheckResourceAttr("data.bitbucketserver_repository_permissions_users.test", "users.0.email_address", "admin@example.com"), + resource.TestCheckResourceAttr("data.bitbucketserver_repository_permissions_users.test", "users.0.active", "true"), + resource.TestCheckResourceAttr("data.bitbucketserver_repository_permissions_users.test", "users.0.permission", "PROJECT_ADMIN"), + ), + }, + }, + }) +} diff --git a/bitbucket/provider.go b/bitbucket/provider.go index 3fd91b6..9643e50 100644 --- a/bitbucket/provider.go +++ b/bitbucket/provider.go @@ -29,26 +29,30 @@ func Provider() terraform.ResourceProvider { }, ConfigureFunc: providerConfigure, DataSourcesMap: map[string]*schema.Resource{ - "bitbucketserver_application_properties": dataSourceApplicationProperties(), - "bitbucketserver_global_permissions_groups": dataSourceGlobalPermissionsGroups(), - "bitbucketserver_global_permissions_users": dataSourceGlobalPermissionsUsers(), - "bitbucketserver_groups": dataSourceGroups(), - "bitbucketserver_group_users": dataSourceGroupUsers(), - "bitbucketserver_project_permissions_groups": dataSourceProjectPermissionsGroups(), - "bitbucketserver_project_permissions_users": dataSourceProjectPermissionsUsers(), + "bitbucketserver_application_properties": dataSourceApplicationProperties(), + "bitbucketserver_global_permissions_groups": dataSourceGlobalPermissionsGroups(), + "bitbucketserver_global_permissions_users": dataSourceGlobalPermissionsUsers(), + "bitbucketserver_groups": dataSourceGroups(), + "bitbucketserver_group_users": dataSourceGroupUsers(), + "bitbucketserver_project_permissions_groups": dataSourceProjectPermissionsGroups(), + "bitbucketserver_project_permissions_users": dataSourceProjectPermissionsUsers(), + "bitbucketserver_repository_permissions_groups": dataSourceRepositoryPermissionsGroups(), + "bitbucketserver_repository_permissions_users": dataSourceRepositoryPermissionsUsers(), }, ResourcesMap: map[string]*schema.Resource{ - "bitbucketserver_global_permissions_group": resourceGlobalPermissionsGroup(), - "bitbucketserver_global_permissions_user": resourceGlobalPermissionsUser(), - "bitbucketserver_group": resourceGroup(), - "bitbucketserver_license": resourceLicense(), - "bitbucketserver_mail_server": resourceMailServer(), - "bitbucketserver_project": resourceProject(), - "bitbucketserver_project_permissions_group": resourceProjectPermissionsGroup(), - "bitbucketserver_project_permissions_user": resourceProjectPermissionsUser(), - "bitbucketserver_repository": resourceRepository(), - "bitbucketserver_user": resourceUser(), - "bitbucketserver_user_group": resourceUserGroup(), + "bitbucketserver_global_permissions_group": resourceGlobalPermissionsGroup(), + "bitbucketserver_global_permissions_user": resourceGlobalPermissionsUser(), + "bitbucketserver_group": resourceGroup(), + "bitbucketserver_license": resourceLicense(), + "bitbucketserver_mail_server": resourceMailServer(), + "bitbucketserver_project": resourceProject(), + "bitbucketserver_project_permissions_group": resourceProjectPermissionsGroup(), + "bitbucketserver_project_permissions_user": resourceProjectPermissionsUser(), + "bitbucketserver_repository": resourceRepository(), + "bitbucketserver_repository_permissions_group": resourceRepositoryPermissionsGroup(), + "bitbucketserver_repository_permissions_user": resourceRepositoryPermissionsUser(), + "bitbucketserver_user": resourceUser(), + "bitbucketserver_user_group": resourceUserGroup(), }, } } diff --git a/bitbucket/resource_repository_permissions_group.go b/bitbucket/resource_repository_permissions_group.go new file mode 100644 index 0000000..cd7bc1b --- /dev/null +++ b/bitbucket/resource_repository_permissions_group.go @@ -0,0 +1,115 @@ +package bitbucket + +import ( + "fmt" + "github.com/hashicorp/terraform/helper/schema" + "github.com/hashicorp/terraform/helper/validation" + "net/url" + "strings" +) + +func resourceRepositoryPermissionsGroup() *schema.Resource { + return &schema.Resource{ + Create: resourceRepositoryPermissionsGroupCreate, + Update: resourceRepositoryPermissionsGroupUpdate, + Read: resourceRepositoryPermissionsGroupRead, + Delete: resourceRepositoryPermissionsGroupDelete, + Importer: &schema.ResourceImporter{ + State: schema.ImportStatePassthrough, + }, + + Schema: map[string]*schema.Schema{ + "project": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + }, + "repository": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + }, + "group": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + }, + "permission": { + Type: schema.TypeString, + Required: true, + ValidateFunc: validation.StringInSlice([]string{"REPO_READ", "REPO_WRITE", "REPO_ADMIN"}, false), + }, + }, + } +} + +func resourceRepositoryPermissionsGroupUpdate(d *schema.ResourceData, m interface{}) error { + client := m.(*BitbucketClient) + _, err := client.Put(fmt.Sprintf("/rest/api/1.0/projects/%s/repos/%s/permissions/groups?permission=%s&name=%s", + url.QueryEscape(d.Get("project").(string)), + url.QueryEscape(d.Get("repository").(string)), + url.QueryEscape(d.Get("permission").(string)), + url.QueryEscape(d.Get("group").(string)), + ), nil) + + if err != nil { + return err + } + + return resourceRepositoryPermissionsGroupRead(d, m) +} + +func resourceRepositoryPermissionsGroupCreate(d *schema.ResourceData, m interface{}) error { + err := resourceRepositoryPermissionsGroupUpdate(d, m) + if err != nil { + return err + } + + d.SetId(fmt.Sprintf("%s/%s/%s", d.Get("project").(string), d.Get("repository").(string), d.Get("group").(string))) + return resourceRepositoryPermissionsGroupRead(d, m) +} + +func resourceRepositoryPermissionsGroupRead(d *schema.ResourceData, m interface{}) error { + id := d.Id() + if id != "" { + parts := strings.Split(id, "/") + if len(parts) == 3 { + _ = d.Set("project", parts[0]) + _ = d.Set("repository", parts[1]) + _ = d.Set("group", parts[2]) + } else { + return fmt.Errorf("incorrect ID format, should match `project/repository/group`") + } + } + + group := d.Get("group").(string) + groups, err := readRepositoryPermissionsGroups(m, d.Get("project").(string), d.Get("repository").(string), group) + if err != nil { + return err + } + + // API only filters but we need to find an exact match + for _, g := range groups { + if g.Name == group { + _ = d.Set("permission", g.Permission) + break + } + } + + return nil +} + +func resourceRepositoryPermissionsGroupDelete(d *schema.ResourceData, m interface{}) error { + client := m.(*BitbucketClient) + _, err := client.Delete(fmt.Sprintf("/rest/api/1.0/projects/%s/repos/%s/permissions/groups?name=%s", + url.QueryEscape(d.Get("project").(string)), + url.QueryEscape(d.Get("repository").(string)), + url.QueryEscape(d.Get("group").(string)), + )) + + if err != nil { + return err + } + + return resourceRepositoryPermissionsGroupRead(d, m) +} diff --git a/bitbucket/resource_repository_permissions_group_test.go b/bitbucket/resource_repository_permissions_group_test.go new file mode 100644 index 0000000..5c0fef6 --- /dev/null +++ b/bitbucket/resource_repository_permissions_group_test.go @@ -0,0 +1,49 @@ +package bitbucket + +import ( + "fmt" + "math/rand" + "testing" + "time" + + "github.com/hashicorp/terraform/helper/resource" +) + +func TestAccBitbucketResourceRepositoryPermissionsGroup(t *testing.T) { + projectKey := fmt.Sprintf("TEST%v", rand.New(rand.NewSource(time.Now().UnixNano())).Int()) + config := fmt.Sprintf(` + resource "bitbucketserver_project" "test" { + key = "%v" + name = "%v" + } + + resource "bitbucketserver_repository" "test" { + name = "test" + project = bitbucketserver_project.test.key + } + + resource "bitbucketserver_repository_permissions_group" "test" { + repository = bitbucketserver_repository.test.name + project = bitbucketserver_repository.test.project + group = "stash-users" + permission = "REPO_WRITE" + } + `, projectKey, projectKey) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + Steps: []resource.TestStep{ + { + Config: config, + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr("bitbucketserver_repository_permissions_group.test", "id", projectKey+"/test/stash-users"), + resource.TestCheckResourceAttr("bitbucketserver_repository_permissions_group.test", "project", projectKey), + resource.TestCheckResourceAttr("bitbucketserver_repository_permissions_group.test", "repository", "test"), + resource.TestCheckResourceAttr("bitbucketserver_repository_permissions_group.test", "group", "stash-users"), + resource.TestCheckResourceAttr("bitbucketserver_repository_permissions_group.test", "permission", "REPO_WRITE"), + ), + }, + }, + }) +} diff --git a/bitbucket/resource_repository_permissions_user.go b/bitbucket/resource_repository_permissions_user.go new file mode 100644 index 0000000..0cf9bdd --- /dev/null +++ b/bitbucket/resource_repository_permissions_user.go @@ -0,0 +1,115 @@ +package bitbucket + +import ( + "fmt" + "github.com/hashicorp/terraform/helper/schema" + "github.com/hashicorp/terraform/helper/validation" + "net/url" + "strings" +) + +func resourceRepositoryPermissionsUser() *schema.Resource { + return &schema.Resource{ + Create: resourceRepositoryPermissionsUserCreate, + Update: resourceRepositoryPermissionsUserUpdate, + Read: resourceRepositoryPermissionsUserRead, + Delete: resourceRepositoryPermissionsUserDelete, + Importer: &schema.ResourceImporter{ + State: schema.ImportStatePassthrough, + }, + + Schema: map[string]*schema.Schema{ + "project": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + }, + "repository": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + }, + "user": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + }, + "permission": { + Type: schema.TypeString, + Required: true, + ValidateFunc: validation.StringInSlice([]string{"REPO_READ", "REPO_WRITE", "REPO_ADMIN"}, false), + }, + }, + } +} + +func resourceRepositoryPermissionsUserUpdate(d *schema.ResourceData, m interface{}) error { + client := m.(*BitbucketClient) + _, err := client.Put(fmt.Sprintf("/rest/api/1.0/projects/%s/repos/%s/permissions/users?permission=%s&name=%s", + url.QueryEscape(d.Get("project").(string)), + url.QueryEscape(d.Get("repository").(string)), + url.QueryEscape(d.Get("permission").(string)), + url.QueryEscape(d.Get("user").(string)), + ), nil) + + if err != nil { + return err + } + + return resourceRepositoryPermissionsUserRead(d, m) +} + +func resourceRepositoryPermissionsUserCreate(d *schema.ResourceData, m interface{}) error { + err := resourceRepositoryPermissionsUserUpdate(d, m) + if err != nil { + return err + } + + d.SetId(fmt.Sprintf("%s/%s/%s", d.Get("project").(string), d.Get("repository").(string), d.Get("user").(string))) + return resourceRepositoryPermissionsUserRead(d, m) +} + +func resourceRepositoryPermissionsUserRead(d *schema.ResourceData, m interface{}) error { + id := d.Id() + if id != "" { + parts := strings.Split(id, "/") + if len(parts) == 3 { + _ = d.Set("project", parts[0]) + _ = d.Set("repository", parts[1]) + _ = d.Set("user", parts[2]) + } else { + return fmt.Errorf("incorrect ID format, should match `project/repository/user`") + } + } + + user := d.Get("user").(string) + users, err := readRepositoryPermissionsUsers(m, d.Get("project").(string), d.Get("repository").(string), user) + if err != nil { + return err + } + + // API only filters but we need to find an exact match + for _, g := range users { + if g.Name == user { + _ = d.Set("permission", g.Permission) + break + } + } + + return nil +} + +func resourceRepositoryPermissionsUserDelete(d *schema.ResourceData, m interface{}) error { + client := m.(*BitbucketClient) + _, err := client.Delete(fmt.Sprintf("/rest/api/1.0/projects/%s/repos/%s/permissions/users?name=%s", + url.QueryEscape(d.Get("project").(string)), + url.QueryEscape(d.Get("repository").(string)), + url.QueryEscape(d.Get("user").(string)), + )) + + if err != nil { + return err + } + + return resourceRepositoryPermissionsUserRead(d, m) +} diff --git a/bitbucket/resource_repository_permissions_user_test.go b/bitbucket/resource_repository_permissions_user_test.go new file mode 100644 index 0000000..0812bae --- /dev/null +++ b/bitbucket/resource_repository_permissions_user_test.go @@ -0,0 +1,55 @@ +package bitbucket + +import ( + "fmt" + "math/rand" + "testing" + "time" + + "github.com/hashicorp/terraform/helper/resource" +) + +func TestAccBitbucketResourceRepositoryPermissionsUser(t *testing.T) { + projectKey := fmt.Sprintf("TEST%v", rand.New(rand.NewSource(time.Now().UnixNano())).Int()) + config := fmt.Sprintf(` + resource "bitbucketserver_project" "test" { + key = "%v" + name = "%v" + } + + resource "bitbucketserver_repository" "test" { + name = "test" + project = bitbucketserver_project.test.key + } + + resource "bitbucketserver_user" "mreynolds" { + name = "mreynolds" + display_name = "Malcolm Reynolds" + email_address = "browncoat@example.com" + } + + resource "bitbucketserver_repository_permissions_user" "test" { + project = bitbucketserver_project.test.key + repository = bitbucketserver_repository.test.name + user = bitbucketserver_user.mreynolds.name + permission = "REPO_WRITE" + } + `, projectKey, projectKey) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + Steps: []resource.TestStep{ + { + Config: config, + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr("bitbucketserver_repository_permissions_user.test", "id", projectKey+"/test/mreynolds"), + resource.TestCheckResourceAttr("bitbucketserver_repository_permissions_user.test", "project", projectKey), + resource.TestCheckResourceAttr("bitbucketserver_repository_permissions_user.test", "repository", "test"), + resource.TestCheckResourceAttr("bitbucketserver_repository_permissions_user.test", "user", "mreynolds"), + resource.TestCheckResourceAttr("bitbucketserver_repository_permissions_user.test", "permission", "REPO_WRITE"), + ), + }, + }, + }) +}