Skip to content

Commit

Permalink
[1/X] DXCDT-461: Remove roles and permissions fields from user resour…
Browse files Browse the repository at this point in the history
…ce (#703)

Co-authored-by: Will Vedder <[email protected]>
  • Loading branch information
sergiught and willvedd authored Jul 12, 2023
1 parent 63c96bb commit 1168aaf
Show file tree
Hide file tree
Showing 16 changed files with 3,841 additions and 9,563 deletions.
12 changes: 0 additions & 12 deletions docs/resources/user.md
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,6 @@ resource "auth0_role" "admin" {
- `phone_number` (String) Phone number for the user; follows the E.164 recommendation. Used for SMS connections.
- `phone_verified` (Boolean) Indicates whether the phone number has been verified.
- `picture` (String) Picture of the user. This value can only be updated if the connection is a database connection (using the Auth0 store), a passwordless connection (email or sms) or has disabled 'Sync user profile attributes at each login'. For more information, see: [Configure Identity Provider Connection for User Profile Updates](https://auth0.com/docs/manage-users/user-accounts/user-profiles/configure-connection-sync-with-auth0).
- `roles` (Set of String, Deprecated) Set of IDs of roles assigned to the user. Managing roles through this attribute is deprecated and it will be removed in a future major version. Migrate to the `auth0_user_roles` or the `auth0_user_role` resource to manage user roles instead. Check the [MIGRATION GUIDE](https://github.com/auth0/terraform-provider-auth0/blob/main/MIGRATION_GUIDE.md#user-roles) on how to do that.
- `user_id` (String) ID of the user.
- `user_metadata` (String) Custom fields that store info about the user that does not impact a user's core functionality. Examples include work address, home address, and user preferences.
- `username` (String) Username of the user. Only valid if the connection requires a username.
Expand All @@ -60,17 +59,6 @@ resource "auth0_role" "admin" {
### Read-Only

- `id` (String) The ID of this resource.
- `permissions` (Set of Object, Deprecated) List of API permissions granted to the user. Reading permissions through this attribute is deprecated and it will be removed in a future major version. Use the `auth0_user` data source instead. (see [below for nested schema](#nestedatt--permissions))

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

Read-Only:

- `description` (String)
- `name` (String)
- `resource_server_identifier` (String)
- `resource_server_name` (String)

## Import

Expand Down
83 changes: 72 additions & 11 deletions internal/auth0/user/data_source.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,11 @@ package user
import (
"context"

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

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

Expand All @@ -22,21 +23,81 @@ func NewDataSource() *schema.Resource {
func dataSourceSchema() map[string]*schema.Schema {
dataSourceSchema := internalSchema.TransformResourceToDataSource(internalSchema.Clone(NewResource().Schema))

internalSchema.SetExistingAttributesAsOptional(dataSourceSchema, "user_id")
dataSourceSchema["user_id"].Required = true
dataSourceSchema["user_id"].Computed = false
dataSourceSchema["user_id"].Optional = false
dataSourceSchema["user_id"] = &schema.Schema{
Type: schema.TypeString,
Required: true,
Description: "ID of the user.",
}

dataSourceSchema["permissions"] = &schema.Schema{
Type: schema.TypeSet,
Computed: true,
Description: "List of API permissions granted to the user.",
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"name": {
Type: schema.TypeString,
Computed: true,
Description: "Name of the permission.",
},
"description": {
Type: schema.TypeString,
Computed: true,
Description: "Description of the permission.",
},
"resource_server_identifier": {
Type: schema.TypeString,
Computed: true,
Description: "Resource server identifier associated with the permission.",
},
"resource_server_name": {
Type: schema.TypeString,
Computed: true,
Description: "Name of resource server that the permission is associated with.",
},
},
},
}

dataSourceSchema["permissions"].Deprecated = ""
dataSourceSchema["permissions"].Description = "List of API permissions granted to the user."
dataSourceSchema["roles"].Deprecated = ""
dataSourceSchema["roles"].Description = "Set of IDs of roles assigned to the user."
dataSourceSchema["roles"] = &schema.Schema{
Type: schema.TypeSet,
Computed: true,
Elem: &schema.Schema{
Type: schema.TypeString,
},
Description: "Set of IDs of roles assigned to the user.",
}

return dataSourceSchema
}

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

userID := data.Get("user_id").(string)
data.SetId(userID)
return auth0.CheckFor404Error(ctx, readUser, data, meta)

user, err := api.User.Read(ctx, userID)
if err != nil {
return diag.FromErr(err)
}

data.SetId(user.GetID())

roles, err := api.User.Roles(ctx, user.GetID())
if err != nil {
return diag.FromErr(err)
}

permissions, err := api.User.Permissions(ctx, user.GetID())
if err != nil {
return diag.FromErr(err)
}

result := multierror.Append(
flattenUser(data, user),
data.Set("roles", flattenUserRoles(roles)),
data.Set("permissions", flattenUserPermissions(permissions)),
)

return diag.FromErr(result.ErrorOrNil())
}
86 changes: 62 additions & 24 deletions internal/auth0/user/data_source_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,41 @@ import (
"github.com/auth0/terraform-provider-auth0/internal/acctest"
)

const testAccDataSourceUserDoesNotExist = `
data "auth0_user" "user" {
user_id = "auth0|this-user-id-does-not-exist"
}
`

const testAccDataSourceUser = `
resource "auth0_resource_server" "resource_server" {
name = "Acceptance Test - {{.testName}}"
identifier = "https://uat.api.terraform-provider-auth0.com/{{.testName}}"
lifecycle {
ignore_changes = [ scopes ]
}
}
resource "auth0_resource_server_scopes" "my_scopes" {
depends_on = [ auth0_resource_server.resource_server ]
resource_server_identifier = auth0_resource_server.resource_server.identifier
scopes {
name = "read:foo"
description = "Can read Foo"
}
scopes {
name = "create:foo"
description = "Can create Foo"
}
}
resource "auth0_role" "owner" {
depends_on = [ auth0_resource_server_scopes.my_scopes ]
name = "Test Owner {{.testName}}"
description = "Owner {{.testName}}"
}
Expand All @@ -37,7 +70,6 @@ resource "auth0_user" "user" {
family_name = "Lastname"
nickname = "{{.testName}}"
picture = "https://www.example.com/picture.jpg"
roles = [ auth0_role.owner.id, auth0_role.admin.id ]
user_metadata = jsonencode({
"foo": "bar",
"baz": "qux"
Expand All @@ -48,10 +80,33 @@ resource "auth0_user" "user" {
})
}
data "auth0_user" "test" {
resource "auth0_user_permissions" "user_permissions" {
depends_on = [ auth0_user.user ]
user_id = auth0_user.user.id
permissions {
resource_server_identifier = auth0_resource_server.resource_server.identifier
name = "read:foo"
}
permissions {
resource_server_identifier = auth0_resource_server.resource_server.identifier
name = "create:foo"
}
}
resource "auth0_user_roles" "user_roles" {
depends_on = [ auth0_user_permissions.user_permissions ]
user_id = auth0_user.user.id
roles = [ auth0_role.owner.id, auth0_role.admin.id ]
}
data "auth0_user" "test" {
depends_on = [ auth0_user_roles.user_roles ]
user_id = auth0_user.user.id
}
`

Expand All @@ -60,6 +115,10 @@ func TestAccDataSourceUser(t *testing.T) {

acctest.Test(t, resource.TestCase{
Steps: []resource.TestStep{
{
Config: acctest.ParseTestName(testAccDataSourceUserDoesNotExist, testName),
ExpectError: regexp.MustCompile(`Error: 404 Not Found: The user does not exist.`),
},
{
Config: acctest.ParseTestName(testAccDataSourceUser, testName),
Check: resource.ComposeTestCheckFunc(
Expand All @@ -73,32 +132,11 @@ func TestAccDataSourceUser(t *testing.T) {
resource.TestCheckResourceAttr("data.auth0_user.test", "nickname", testName),
resource.TestCheckResourceAttr("data.auth0_user.test", "picture", "https://www.example.com/picture.jpg"),
resource.TestCheckResourceAttr("data.auth0_user.test", "roles.#", "2"),
resource.TestCheckResourceAttr("data.auth0_user.test", "permissions.#", "0"),
resource.TestCheckResourceAttr("data.auth0_user.test", "permissions.#", "2"),
resource.TestCheckResourceAttr("data.auth0_user.test", "user_metadata", `{"baz":"qux","foo":"bar"}`),
resource.TestCheckResourceAttr("data.auth0_user.test", "app_metadata", `{"baz":"qux","foo":"bar"}`),
),
},
},
})
}

const testAccDataSourceUserDoesNotExist = `
data "auth0_user" "test" {
user_id = "auth0|this-user-id-does-not-exist"
}
`

func TestAccDataSourceUserDoesNotExist(t *testing.T) {
testName := strings.ToLower(t.Name())

acctest.Test(t, resource.TestCase{
Steps: []resource.TestStep{
{
Config: acctest.ParseTestName(testAccDataSourceUserDoesNotExist, testName),
ExpectError: regexp.MustCompile(
`no resource found with that identifier \((404\))`,
),
},
},
})
}
106 changes: 106 additions & 0 deletions internal/auth0/user/expand.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
package user

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

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

func expandUser(d *schema.ResourceData) (*management.User, error) {
cfg := d.GetRawConfig()

user := &management.User{}

if d.IsNewResource() {
user.ID = value.String(cfg.GetAttr("user_id"))
}
if d.HasChange("email") {
user.Email = value.String(cfg.GetAttr("email"))
}
if d.HasChange("username") {
user.Username = value.String(cfg.GetAttr("username"))
}
if d.HasChange("password") {
user.Password = value.String(cfg.GetAttr("password"))
}
if d.HasChange("phone_number") {
user.PhoneNumber = value.String(cfg.GetAttr("phone_number"))
}
if d.HasChange("email_verified") {
user.EmailVerified = value.Bool(cfg.GetAttr("email_verified"))
}
if d.HasChange("verify_email") {
user.VerifyEmail = value.Bool(cfg.GetAttr("verify_email"))
}
if d.HasChange("phone_verified") {
user.PhoneVerified = value.Bool(cfg.GetAttr("phone_verified"))
}
if d.HasChange("given_name") {
user.GivenName = value.String(cfg.GetAttr("given_name"))
}
if d.HasChange("family_name") {
user.FamilyName = value.String(cfg.GetAttr("family_name"))
}
if d.HasChange("nickname") {
user.Nickname = value.String(cfg.GetAttr("nickname"))
}
if d.HasChange("name") {
user.Name = value.String(cfg.GetAttr("name"))
}
if d.HasChange("picture") {
user.Picture = value.String(cfg.GetAttr("picture"))
}
if d.HasChange("blocked") {
user.Blocked = value.Bool(cfg.GetAttr("blocked"))
}
if d.HasChange("connection_name") {
user.Connection = value.String(cfg.GetAttr("connection_name"))
}
if d.HasChange("user_metadata") {
userMetadata, err := expandMetadata(d, "user")
if err != nil {
return nil, err
}
user.UserMetadata = &userMetadata
}
if d.HasChange("app_metadata") {
appMetadata, err := expandMetadata(d, "app")
if err != nil {
return nil, err
}
user.AppMetadata = &appMetadata
}

return user, nil
}

func expandMetadata(d *schema.ResourceData, metadataType string) (map[string]interface{}, error) {
oldMetadata, newMetadata := d.GetChange(metadataType + "_metadata")
if oldMetadata == "" {
return value.MapFromJSON(d.GetRawConfig().GetAttr(metadataType + "_metadata"))
}

if newMetadata == "" {
return map[string]interface{}{}, nil
}

oldMap, err := structure.ExpandJsonFromString(oldMetadata.(string))
if err != nil {
return map[string]interface{}{}, err
}

newMap, err := structure.ExpandJsonFromString(newMetadata.(string))
if err != nil {
return map[string]interface{}{}, err
}

for key := range oldMap {
if _, ok := newMap[key]; !ok {
newMap[key] = nil
}
}

return newMap, nil
}
Loading

0 comments on commit 1168aaf

Please sign in to comment.