Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
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(),
Comment thread
bddvlpr marked this conversation as resolved.
"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"),
),
},
Comment on lines +51 to +58
},
})
}

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