Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions cloudstack/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -162,6 +162,7 @@ func Provider() *schema.Provider {
"cloudstack_domain": resourceCloudStackDomain(),
"cloudstack_network_service_provider": resourceCloudStackNetworkServiceProvider(),
"cloudstack_role": resourceCloudStackRole(),
"cloudstack_role_permission": resourceCloudStackRolePermission(),
"cloudstack_limits": resourceCloudStackLimits(),
"cloudstack_snapshot_policy": resourceCloudStackSnapshotPolicy(),
"cloudstack_quota_tariff": resourceCloudStackQuotaTariff(),
Expand Down
157 changes: 157 additions & 0 deletions cloudstack/resource_cloudstack_role_permission.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,157 @@
//
// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.
//

package cloudstack

import (
"fmt"
"log"

"github.com/apache/cloudstack-go/v2/cloudstack"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation"
)

func resourceCloudStackRolePermission() *schema.Resource {
return &schema.Resource{
Create: resourceCloudStackRolePermissionCreate,
Read: resourceCloudStackRolePermissionRead,
Update: resourceCloudStackRolePermissionUpdate,
Delete: resourceCloudStackRolePermissionDelete,
Schema: map[string]*schema.Schema{
"role_id": {
Type: schema.TypeString,
Required: true,
ForceNew: true,
Description: "ID of the role the permission (rule) belongs to.",
},
"rule": {
Type: schema.TypeString,
Required: true,
ForceNew: true,
Description: "The API name or wildcard (e.g. 'list*') the permission applies to.",
},
"permission": {
Type: schema.TypeString,
Required: true,
ValidateFunc: validation.StringInSlice([]string{"allow", "deny"}, false),
Description: "Whether the rule is allowed or denied. Valid options are: allow, deny.",
},
"description": {
Type: schema.TypeString,
Optional: true,
ForceNew: true,
Description: "A description for the role permission.",
},
},
}
}

func resourceCloudStackRolePermissionCreate(d *schema.ResourceData, meta interface{}) error {
cs := meta.(*cloudstack.CloudStackClient)

roleID := d.Get("role_id").(string)
rule := d.Get("rule").(string)
permission := d.Get("permission").(string)

// Create a new parameter struct
p := cs.Role.NewCreateRolePermissionParams(permission, roleID, rule)

if description, ok := d.GetOk("description"); ok {
p.SetDescription(description.(string))
}

log.Printf("[DEBUG] Creating Role Permission %s (%s) for role %s", rule, permission, roleID)
r, err := cs.Role.CreateRolePermission(p)

if err != nil {
return fmt.Errorf("Error creating Role Permission: %s", err)
}

log.Printf("[DEBUG] Role Permission %s successfully created", rule)
d.SetId(r.Id)

return resourceCloudStackRolePermissionRead(d, meta)
}

func resourceCloudStackRolePermissionRead(d *schema.ResourceData, meta interface{}) error {
cs := meta.(*cloudstack.CloudStackClient)

roleID := d.Get("role_id").(string)

// The API only supports listing permissions by role, so fetch them all
// and locate the one matching this resource's ID.
p := cs.Role.NewListRolePermissionsParams()
p.SetRoleid(roleID)

l, err := cs.Role.ListRolePermissions(p)
if err != nil {
return fmt.Errorf("Error listing Role Permissions: %s", err)
}

for _, rp := range l.RolePermissions {
if rp.Id == d.Id() {
d.Set("role_id", rp.Roleid)
d.Set("rule", rp.Rule)
d.Set("permission", rp.Permission)
d.Set("description", rp.Description)
return nil
}
}

log.Printf("[DEBUG] Role Permission %s no longer exists", d.Id())
d.SetId("")

return nil
}

func resourceCloudStackRolePermissionUpdate(d *schema.ResourceData, meta interface{}) error {
cs := meta.(*cloudstack.CloudStackClient)

// Only the permission (allow/deny) can be changed in place; the role_id,
// rule and description are all ForceNew.
p := cs.Role.NewUpdateRolePermissionParams(d.Get("role_id").(string))
p.SetRuleid(d.Id())
p.SetPermission(d.Get("permission").(string))

log.Printf("[DEBUG] Updating Role Permission %s", d.Id())
_, err := cs.Role.UpdateRolePermission(p)

if err != nil {
return fmt.Errorf("Error updating Role Permission: %s", err)
}

return resourceCloudStackRolePermissionRead(d, meta)
}

func resourceCloudStackRolePermissionDelete(d *schema.ResourceData, meta interface{}) error {
cs := meta.(*cloudstack.CloudStackClient)

// Create a new parameter struct
p := cs.Role.NewDeleteRolePermissionParams(d.Id())

log.Printf("[DEBUG] Deleting Role Permission %s", d.Id())
_, err := cs.Role.DeleteRolePermission(p)

if err != nil {
return fmt.Errorf("Error deleting Role Permission: %s", err)
}

return nil
}
152 changes: 152 additions & 0 deletions cloudstack/resource_cloudstack_role_permission_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,152 @@
//
// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.
//

package cloudstack

import (
"fmt"
"testing"

"github.com/apache/cloudstack-go/v2/cloudstack"
"github.com/hashicorp/terraform-plugin-testing/helper/resource"
"github.com/hashicorp/terraform-plugin-testing/terraform"
)

func TestAccCloudStackRolePermission_basic(t *testing.T) {
var rolePermission cloudstack.RolePermission

resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
CheckDestroy: testAccCheckCloudStackRolePermissionDestroy,
Steps: []resource.TestStep{
{
Config: testAccCloudStackRolePermission_basic,
Check: resource.ComposeTestCheckFunc(
testAccCheckCloudStackRolePermissionExists("cloudstack_role_permission.foo", &rolePermission),
resource.TestCheckResourceAttr(
"cloudstack_role_permission.foo", "rule", "listVirtualMachines"),
resource.TestCheckResourceAttr(
"cloudstack_role_permission.foo", "permission", "allow"),
resource.TestCheckResourceAttr(
"cloudstack_role_permission.foo", "description", "terraform test role permission"),
),
},
{
Config: testAccCloudStackRolePermission_update,
Check: resource.ComposeTestCheckFunc(
testAccCheckCloudStackRolePermissionExists("cloudstack_role_permission.foo", &rolePermission),
resource.TestCheckResourceAttr(
"cloudstack_role_permission.foo", "permission", "deny"),
),
},
},
})
}

func testAccCheckCloudStackRolePermissionExists(n string, rolePermission *cloudstack.RolePermission) resource.TestCheckFunc {
return func(s *terraform.State) error {
rs, ok := s.RootModule().Resources[n]
if !ok {
return fmt.Errorf("Not found: %s", n)
}

if rs.Primary.ID == "" {
return fmt.Errorf("No Role Permission ID is set")
}

cs := testAccProvider.Meta().(*cloudstack.CloudStackClient)

p := cs.Role.NewListRolePermissionsParams()
p.SetRoleid(rs.Primary.Attributes["role_id"])

l, err := cs.Role.ListRolePermissions(p)
if err != nil {
return err
}

for _, rp := range l.RolePermissions {
if rp.Id == rs.Primary.ID {
*rolePermission = *rp
return nil
}
}

return fmt.Errorf("Role Permission not found")
}
}

func testAccCheckCloudStackRolePermissionDestroy(s *terraform.State) error {
cs := testAccProvider.Meta().(*cloudstack.CloudStackClient)

for _, rs := range s.RootModule().Resources {
if rs.Type != "cloudstack_role_permission" {
continue
}

if rs.Primary.ID == "" {
return fmt.Errorf("No Role Permission ID is set")
}

p := cs.Role.NewListRolePermissionsParams()
p.SetRoleid(rs.Primary.Attributes["role_id"])

l, err := cs.Role.ListRolePermissions(p)
if err != nil {
// If the parent role is already gone, the permission is too.
continue
}

for _, rp := range l.RolePermissions {
if rp.Id == rs.Primary.ID {
return fmt.Errorf("Role Permission %s still exists", rs.Primary.ID)
}
}
}

return nil
}

const testAccCloudStackRolePermission_basic = `
resource "cloudstack_role" "foo" {
name = "terraform-role"
type = "User"
}

resource "cloudstack_role_permission" "foo" {
role_id = cloudstack_role.foo.id
rule = "listVirtualMachines"
permission = "allow"
description = "terraform test role permission"
}
`

const testAccCloudStackRolePermission_update = `
resource "cloudstack_role" "foo" {
name = "terraform-role"
type = "User"
}

resource "cloudstack_role_permission" "foo" {
role_id = cloudstack_role.foo.id
rule = "listVirtualMachines"
permission = "deny"
description = "terraform test role permission"
}
`
4 changes: 4 additions & 0 deletions website/cloudstack.erb
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,10 @@
<li<%= sidebar_current("docs-cloudstack-resource-role") %>>
<a href="/docs/providers/cloudstack/r/role.html">cloudstack_role</a>
</li>

<li<%= sidebar_current("docs-cloudstack-resource-role-permission") %>>
<a href="/docs/providers/cloudstack/r/role_permission.html">cloudstack_role_permission</a>
</li>
</ul>
</li>
</ul>
Expand Down
61 changes: 61 additions & 0 deletions website/docs/r/role_permission.html.markdown
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
---
layout: "cloudstack"
page_title: "CloudStack: cloudstack_role_permission"
description: |-
Creates a role permission (rule) for a role.
---

# cloudstack_role_permission

Creates a role permission. A role permission is a single rule that allows or
denies a role access to an API (or a wildcard set of APIs).

Rules belonging to the same role are evaluated in the order in which they are
created, and the first matching rule wins. Order the corresponding
`cloudstack_role_permission` resources accordingly (for example with
`depends_on`) when precedence matters.

## Example Usage

```hcl
resource "cloudstack_role" "custom" {
name = "custom-role"
type = "User"
}

# Allow listing virtual machines
resource "cloudstack_role_permission" "list_vms" {
role_id = cloudstack_role.custom.id
rule = "listVirtualMachines"
permission = "allow"
description = "Allow listing virtual machines"
}

# Deny every other API using a wildcard
resource "cloudstack_role_permission" "deny_all" {
role_id = cloudstack_role.custom.id
rule = "*"
permission = "deny"

depends_on = [cloudstack_role_permission.list_vms]
}
```

## Argument Reference

The following arguments are supported:

* `role_id` - (Required) ID of the role the permission belongs to. Changing this
forces a new resource to be created.
* `rule` - (Required) The API name or a wildcard (e.g. `list*` or `*`) the rule
applies to. Changing this forces a new resource to be created.
* `permission` - (Required) Whether the rule is allowed or denied. Valid options
are: `allow`, `deny`.
* `description` - (Optional) A description for the role permission. Changing this
forces a new resource to be created.

## Attributes Reference

The following attributes are exported:

* `id` - The ID of the role permission.
Loading