Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

DXCDT-428: User permissions resource #578

Merged
merged 8 commits into from
May 12, 2023
Merged
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
2 changes: 2 additions & 0 deletions docs/resources/user_permission.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ description: |-

With this resource, you can manage user permissions.

!> To prevent issues, avoid using this resource together with the `auth0_user_permissions` resource.

## Example Usage

```terraform
Expand Down
91 changes: 91 additions & 0 deletions docs/resources/user_permissions.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
---
page_title: "Resource: auth0_user_permissions"
description: |-
With this resource, you can manage all of a user's permissions.
---

# Resource: auth0_user_permissions

With this resource, you can manage all of a user's permissions.

!> To prevent issues, avoid using this resource together with the `auth0_user_permission` resource.

## Example Usage

```terraform
resource "auth0_resource_server" "resource_server" {
name = "Example Resource Server (Managed by Terraform)"
identifier = "https://api.example.com"
scopes {
value = "create:foo"
description = "Create foos"
}

scopes {
value = "read:foo"
description = "Read foos"
}
}

resource "auth0_user" "user" {
connection_name = "Username-Password-Authentication"
user_id = "12345"
username = "unique_username"
name = "Firstname Lastname"
nickname = "some.nickname"
email = "test@test.com"
email_verified = true
password = "passpass$12$12"
picture = "https://www.example.com/a-valid-picture-url.jpg"
}

resource "auth0_user_permissions" "all_user_permissions" {
user_id = auth0_user.user.id

permissions {
name = tolist(auth0_resource_server.resource_server.scopes)[0]
resource_server_identifier = auth0_resource_server.resource_server.identifier
}

permissions {
name = tolist(auth0_resource_server.resource_server.scopes)[1]
resource_server_identifier = auth0_resource_server.resource_server.identifier
}
}
```

<!-- schema generated by tfplugindocs -->
## Schema

### Required

- `permissions` (Block Set, Min: 1) List of API permissions granted to the user. (see [below for nested schema](#nestedblock--permissions))
- `user_id` (String) ID of the user to associate the permission to.

### Read-Only

- `id` (String) The ID of this resource.

<a id="nestedblock--permissions"></a>
### Nested Schema for `permissions`

Required:

- `name` (String) Name of permission.
- `resource_server_identifier` (String) Resource server identifier associated with the permission.

Read-Only:

- `description` (String) Description of the permission.
- `resource_server_name` (String) Name of resource server that the permission is associated with.

## Import

Import is supported using the following syntax:

```shell
# This resource can be imported by specifying the user ID

# Example:
terraform import auth0_user_permissions.all_user_permissions "auth0|111111111111111111111111"
```
4 changes: 4 additions & 0 deletions examples/resources/auth0_user_permissions/import.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
# This resource can be imported by specifying the user ID

# Example:
terraform import auth0_user_permissions.all_user_permissions "auth0|111111111111111111111111"
39 changes: 39 additions & 0 deletions examples/resources/auth0_user_permissions/resource.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
resource "auth0_resource_server" "resource_server" {
name = "Example Resource Server (Managed by Terraform)"
identifier = "https://api.example.com"
scopes {
value = "create:foo"
description = "Create foos"
}

scopes {
value = "read:foo"
description = "Read foos"
}
}

resource "auth0_user" "user" {
connection_name = "Username-Password-Authentication"
user_id = "12345"
username = "unique_username"
name = "Firstname Lastname"
nickname = "some.nickname"
email = "test@test.com"
email_verified = true
password = "passpass$12$12"
picture = "https://www.example.com/a-valid-picture-url.jpg"
}

resource "auth0_user_permissions" "all_user_permissions" {
user_id = auth0_user.user.id

permissions {
name = tolist(auth0_resource_server.resource_server.scopes)[0]
resource_server_identifier = auth0_resource_server.resource_server.identifier
}

permissions {
name = tolist(auth0_resource_server.resource_server.scopes)[1]
resource_server_identifier = auth0_resource_server.resource_server.identifier
}
}
174 changes: 174 additions & 0 deletions internal/auth0/user/resource_permissions.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,174 @@
package user

import (
"context"
"net/http"

"github.com/auth0/go-auth0"
"github.com/auth0/go-auth0/management"
"github.com/hashicorp/terraform-plugin-sdk/v2/diag"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"

"github.com/auth0/terraform-provider-auth0/internal/config"
"github.com/auth0/terraform-provider-auth0/internal/value"
)

// NewPermissionsResource will return a new auth0_connection_client resource.
func NewPermissionsResource() *schema.Resource {
return &schema.Resource{
Schema: map[string]*schema.Schema{
"user_id": {
Type: schema.TypeString,
Required: true,
ForceNew: true,
Description: "ID of the user to associate the permission to.",
},
"permissions": {
Type: schema.TypeSet,
Required: true,
Description: "List of API permissions granted to the user.",
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"name": {
Type: schema.TypeString,
Required: true,
Description: "Name of permission.",
},
"resource_server_identifier": {
Type: schema.TypeString,
Required: true,
Description: "Resource server identifier associated with the permission.",
},
"description": {
Type: schema.TypeString,
Computed: true,
Description: "Description of the permission.",
},
"resource_server_name": {
Type: schema.TypeString,
Computed: true,
Description: "Name of resource server that the permission is associated with.",
},
},
},
},
},
CreateContext: upsertUserPermissions,
UpdateContext: upsertUserPermissions,
ReadContext: readUserPermissions,
willvedd marked this conversation as resolved.
Show resolved Hide resolved
DeleteContext: deleteUserPermissions,
Importer: &schema.ResourceImporter{
StateContext: schema.ImportStatePassthroughContext,
},
Description: "With this resource, you can manage all of a user's permissions.",
}
}

func upsertUserPermissions(ctx context.Context, data *schema.ResourceData, meta interface{}) diag.Diagnostics {
api := meta.(*config.Config).GetAPI()
mutex := meta.(*config.Config).GetMutex()

userID := data.Get("user_id").(string)

if !data.HasChange("permissions") {
return nil
}
willvedd marked this conversation as resolved.
Show resolved Hide resolved

mutex.Lock(userID)
defer mutex.Unlock(userID)

toAdd, toRemove := value.Difference(data, "permissions")
willvedd marked this conversation as resolved.
Show resolved Hide resolved

var addPermissions []*management.Permission
for _, addPermission := range toAdd {
permission := addPermission.(map[string]interface{})
addPermissions = append(addPermissions, &management.Permission{
Name: auth0.String(permission["name"].(string)),
ResourceServerIdentifier: auth0.String(permission["resource_server_identifier"].(string)),
})
}

if len(addPermissions) > 0 {
if err := api.User.AssignPermissions(userID, addPermissions); err != nil {
return diag.FromErr(err)
}
}

var rmPermissions []*management.Permission
for _, rmPermission := range toRemove {
permission := rmPermission.(map[string]interface{})
rmPermissions = append(rmPermissions, &management.Permission{
Name: auth0.String(permission["name"].(string)),
ResourceServerIdentifier: auth0.String(permission["resource_server_identifier"].(string)),
})
}

if len(rmPermissions) > 0 {
if err := api.User.RemovePermissions(userID, rmPermissions); err != nil {
return diag.FromErr(err)
}
}

data.SetId(userID)

return readUserPermissions(ctx, data, meta)
}

func readUserPermissions(_ context.Context, data *schema.ResourceData, meta interface{}) diag.Diagnostics {
api := meta.(*config.Config).GetAPI()

userID := data.Get("user_id").(string)

permissions, err := api.User.Permissions(userID)
if err != nil {
if mErr, ok := err.(management.Error); ok && mErr.Status() == http.StatusNotFound {
data.SetId("")
return nil
}
return diag.FromErr(err)
}

err = data.Set("permissions", flattenUserPermissions(permissions))

return diag.FromErr(err)
}

func deleteUserPermissions(_ context.Context, data *schema.ResourceData, meta interface{}) diag.Diagnostics {
api := meta.(*config.Config).GetAPI()
mutex := meta.(*config.Config).GetMutex()

userID := data.Get("user_id").(string)

mutex.Lock(userID)
defer mutex.Unlock(userID)

permissions, err := api.User.Permissions(userID)
if err != nil {
if mErr, ok := err.(management.Error); ok && mErr.Status() == http.StatusNotFound {
data.SetId("")
return nil
}
return diag.FromErr(err)
}

var rmPermissions []*management.Permission
for _, rmPermission := range permissions.Permissions {
rmPermissions = append(rmPermissions, &management.Permission{
Name: auth0.String(rmPermission.GetName()),
ResourceServerIdentifier: auth0.String(rmPermission.GetResourceServerIdentifier()),
})
}
if err := api.User.RemovePermissions(
userID,
rmPermissions,
); err != nil {
if mErr, ok := err.(management.Error); ok && mErr.Status() == http.StatusNotFound {
data.SetId("")
return nil
}
return diag.FromErr(err)
}

data.SetId("")
return nil
}
Loading