Skip to content

Commit

Permalink
feat: Add user data source
Browse files Browse the repository at this point in the history
Add data source for fetching a single user, either by username or ID.
  • Loading branch information
tmatilai authored and MCBrandenburg committed May 5, 2022
1 parent e436067 commit 09a9bf3
Show file tree
Hide file tree
Showing 3 changed files with 264 additions and 0 deletions.
43 changes: 43 additions & 0 deletions docs/data-sources/user.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
# User Data Source

This data source can be used to fetch information about a specific user.

[Users API](https://fusionauth.io/docs/v1/tech/apis/users)

## Example Usage

```hcl
# Fetch user by username
data "fusionauth_user" "example" {
username = "foo@example.com"
}
```

## Argument Reference

* `tenant_id` - (Optional) The Id of the tenant used to scope this API request.
* `user_id` - (Optional) The Id of the user. Either `user_id` or `username` must be specified.
* `username` - (Optional) The username of the user. Either `user_id` or `username` must be specified.

## Attributes Reference

All of the argument attributes are also exported as result attributes.

The following additional attributes are exported:

* `active` - True if the user is active. False if the user has been deactivated. Deactivated users will not be able to login.
* `birth_date` - An ISO-8601 formatted date of the user’s birthdate such as YYYY-MM-DD.
* `data` - A JSON serialised string that can hold any information about the user.
* `email` - The user’s email address.
* `expiry` - The expiration instant of the user’s account. An expired user is not permitted to login.
* `first_name` - The first name of the user.
* `full_name` - The user’s full name.
* `image_url` - The URL that points to an image file that is the user’s profile image.
* `last_name` - The user’s last name.
* `middle_name` - The user’s middle name.
* `mobile_phone` - The user’s mobile phone number.
* `parent_email` - The email address of the user’s parent or guardian.
* `password_change_required` - Indicates that the user’s password needs to be changed during their next login attempt.
* `preferred_languages` - An array of locale strings that give, in order, the user’s preferred languages.
* `timezone` - The user’s preferred timezone.
* `username_status` - The current status of the username. This is used if you are moderating usernames via CleanSpeak.
220 changes: 220 additions & 0 deletions fusionauth/datasource_fusionauth_user.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,220 @@
package fusionauth

import (
"context"
"net/http"

"github.com/FusionAuth/go-client/pkg/fusionauth"
"github.com/hashicorp/terraform-plugin-sdk/v2/diag"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation"
)

func dataSourceUser() *schema.Resource {
return &schema.Resource{
ReadContext: dataSourceUserRead,
Schema: map[string]*schema.Schema{
"tenant_id": {
Type: schema.TypeString,
Optional: true,
Computed: true,
Description: "The unique Id of the tenant used to scope this API request.",
ValidateFunc: validation.IsUUID,
},
"user_id": {
Type: schema.TypeString,
Optional: true,
Computed: true,
ExactlyOneOf: []string{"user_id", "username"},
Description: "The Id to use for the new User. If not specified a secure random UUID will be generated.",
ValidateFunc: validation.IsUUID,
},
"username": {
Type: schema.TypeString,
Optional: true,
Computed: true,
ExactlyOneOf: []string{"user_id", "username"},
Description: "The username of the User.",
},
"active": {
Type: schema.TypeBool,
Computed: true,
Description: "True if the User is active. False if the User has been deactivated. Deactivated Users will not be able to login.",
},
"birth_date": {
Type: schema.TypeString,
Computed: true,
Description: "An ISO-8601 formatted date of the User’s birthdate such as YYYY-MM-DD.",
},
"data": {
Type: schema.TypeString,
Computed: true,
Description: "A JSON serialised string that can hold any information about the User.",
},
"email": {
Type: schema.TypeString,
Computed: true,
Description: "The User’s email address.",
},
"expiry": {
Type: schema.TypeInt,
Computed: true,
Description: "The expiration instant of the User’s account. An expired user is not permitted to login.",
},
"first_name": {
Type: schema.TypeString,
Computed: true,
Description: "The first name of the User.",
},
"full_name": {
Type: schema.TypeString,
Computed: true,
Description: "The User’s full name.",
},
"image_url": {
Type: schema.TypeString,
Computed: true,
Description: "The URL that points to an image file that is the User’s profile image.",
},
"last_name": {
Type: schema.TypeString,
Computed: true,
Description: "The User’s last name.",
},
"middle_name": {
Type: schema.TypeString,
Computed: true,
Description: "The User’s middle name.",
},
"mobile_phone": {
Type: schema.TypeString,
Computed: true,
Description: "The User’s mobile phone number.",
},
"parent_email": {
Type: schema.TypeString,
Computed: true,
Description: "The email address of the user’s parent or guardian.",
},
"password_change_required": {
Type: schema.TypeBool,
Computed: true,
Description: "Indicates that the User’s password needs to be changed during their next login attempt.",
},
"preferred_languages": {
Type: schema.TypeSet,
Elem: &schema.Schema{Type: schema.TypeString},
Computed: true,
Description: "An array of locale strings that give, in order, the User’s preferred languages.",
},
"timezone": {
Type: schema.TypeString,
Computed: true,
Description: "The User’s preferred timezone.",
},
"username_status": {
Type: schema.TypeString,
Computed: true,
Description: "The current status of the username. This is used if you are moderating usernames via CleanSpeak.",
},
},
}
}

func dataSourceUserRead(_ context.Context, data *schema.ResourceData, i interface{}) diag.Diagnostics {
client := i.(Client)

oldTenantID := client.FAClient.TenantId
client.FAClient.TenantId = data.Get("tenant_id").(string)
defer func() {
client.FAClient.TenantId = oldTenantID
}()

var searchID string
var resp *fusionauth.UserResponse
var faErrs *fusionauth.Errors
var err error

// Either `user_id` or `username` are guaranteed to be set
if userID, ok := data.GetOk("user_id"); ok {
searchID = userID.(string)
resp, faErrs, err = client.FAClient.RetrieveUser(searchID)
} else {
searchID = data.Get("username").(string)
resp, faErrs, err = client.FAClient.RetrieveUserByUsername(searchID)
}
if err != nil {
return diag.FromErr(err)
}
if resp.StatusCode == http.StatusNotFound {
return diag.Errorf("couldn't find %s", searchID)
}
if err := checkResponse(resp.StatusCode, faErrs); err != nil {
return diag.FromErr(err)
}

data.SetId(resp.User.Id)

if err := data.Set("tenant_id", resp.User.TenantId); err != nil {
return diag.Errorf("user.tenant_id: %s", err.Error())
}
if err := data.Set("user_id", resp.User.Id); err != nil {
return diag.Errorf("user.user_id: %s", err.Error())
}
if err := data.Set("username", resp.User.Username); err != nil {
return diag.Errorf("user.username: %s", err.Error())
}

if err := data.Set("active", resp.User.Active); err != nil {
return diag.Errorf("user.active: %s", err.Error())
}
if err := data.Set("birth_date", resp.User.BirthDate); err != nil {
return diag.Errorf("user.birth_date: %s", err.Error())
}
if userData, diags := mapStringInterfaceToJSONString(resp.User.Data); diags != nil {
return diags
} else if err := data.Set("data", userData); err != nil {
return diag.Errorf("user.data: %s", err.Error())
}
if err := data.Set("email", resp.User.Email); err != nil {
return diag.Errorf("user.email: %s", err.Error())
}
if err := data.Set("expiry", resp.User.Expiry); err != nil {
return diag.Errorf("user.expiry: %s", err.Error())
}
if err := data.Set("first_name", resp.User.FirstName); err != nil {
return diag.Errorf("user.first_name: %s", err.Error())
}
if err := data.Set("full_name", resp.User.FullName); err != nil {
return diag.Errorf("user.full_name: %s", err.Error())
}
if err := data.Set("image_url", resp.User.ImageUrl); err != nil {
return diag.Errorf("user.image_url: %s", err.Error())
}
if err := data.Set("last_name", resp.User.LastName); err != nil {
return diag.Errorf("user.last_name: %s", err.Error())
}
if err := data.Set("middle_name", resp.User.MiddleName); err != nil {
return diag.Errorf("user.middle_name: %s", err.Error())
}
if err := data.Set("mobile_phone", resp.User.MobilePhone); err != nil {
return diag.Errorf("user.mobile_phone: %s", err.Error())
}
if err := data.Set("parent_email", resp.User.ParentEmail); err != nil {
return diag.Errorf("user.parent_email: %s", err.Error())
}
if err := data.Set("password_change_required", resp.User.PasswordChangeRequired); err != nil {
return diag.Errorf("user.password_change_required: %s", err.Error())
}
if err := data.Set("preferred_languages", resp.User.PreferredLanguages); err != nil {
return diag.Errorf("user.preferred_languages: %s", err.Error())
}
if err := data.Set("timezone", resp.User.Timezone); err != nil {
return diag.Errorf("user.timezone: %s", err.Error())
}
if err := data.Set("username_status", resp.User.UsernameStatus); err != nil {
return diag.Errorf("user.username_status: %s", err.Error())
}

return nil
}
1 change: 1 addition & 0 deletions fusionauth/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ func Provider() *schema.Provider {
"fusionauth_tenant": dataSourceTenant(),
"fusionauth_application_role": dataSourceApplicationRole(),
"fusionauth_idp": dataSourceIDP(),
"fusionauth_user": dataSourceUser(),
},
ConfigureContextFunc: configureClient,
}
Expand Down

0 comments on commit 09a9bf3

Please sign in to comment.