diff --git a/.changelog/2691.txt b/.changelog/2691.txt new file mode 100644 index 0000000000..14212a4654 --- /dev/null +++ b/.changelog/2691.txt @@ -0,0 +1,3 @@ +```release-note:new-data-source: cloudflare_user +datasource/cloudflare_user: Create new datasource to get information about current user +``` diff --git a/GNUmakefile b/GNUmakefile index 243b73c86e..2444ef76a5 100644 --- a/GNUmakefile +++ b/GNUmakefile @@ -91,7 +91,7 @@ generate-changelog: @sh -c "'$(CURDIR)/scripts/generate-changelog.sh'" golangci-lint: - @golangci-lint run ./$(PKG_NAME)/... --config .golintci.yml + @golangci-lint run ./internal/... --config .golintci.yml tools: @echo "==> Installing development tooling..." diff --git a/docs/data-sources/user.md b/docs/data-sources/user.md new file mode 100644 index 0000000000..ec06c55bad --- /dev/null +++ b/docs/data-sources/user.md @@ -0,0 +1,41 @@ +--- +page_title: "cloudflare_user Data Source - Cloudflare" +subcategory: "" +description: |- + Use this data source to retrieve information about the currently authenticated user. +--- + +# cloudflare_user (Data Source) + +Use this data source to retrieve information about the currently authenticated user. + +## Example Usage + +```terraform +data "cloudflare_user" "me" {} +data "cloudflare_api_token_permission_groups" "all" {} + +resource "cloudflare_api_token" "example" { + name = "Terraform Cloud (Terraform)" + policy { + permission_groups = [ + data.cloudflare_api_token_permission_groups.all.user["User Details Read"], + + ] + resources = { + "com.cloudflare.api.user.${data.cloudflare_user.me.id}" = "*", + } + } +} +``` + + +## Schema + +### Read-Only + +- `email` (String) The user's email address. +- `id` (String) The user's unique identifier. +- `username` (String) The user's username. + + diff --git a/docs/resources/access_service_token.md b/docs/resources/access_service_token.md index 4df93f4335..949d2e43fa 100644 --- a/docs/resources/access_service_token.md +++ b/docs/resources/access_service_token.md @@ -44,6 +44,7 @@ resource "cloudflare_access_service_token" "my_app" { ### Optional - `account_id` (String) The account identifier to target for the resource. Conflicts with `zone_id`. +- `duration` (String) Length of time the service token is valid for. Available values: `8760h`, `17520h`, `43800h`, `87600h`, `forever`. - `min_days_for_renewal` (Number) Refresh the token if terraform is run within the specified amount of days before expiration. Defaults to `0`. - `zone_id` (String) The zone identifier to target for the resource. Conflicts with `account_id`. diff --git a/docs/resources/waiting_room.md b/docs/resources/waiting_room.md index 0cd355474c..a998b79cc7 100644 --- a/docs/resources/waiting_room.md +++ b/docs/resources/waiting_room.md @@ -47,7 +47,6 @@ resource "cloudflare_waiting_room" "example" { ### Optional -- `queueing_status_code` (Number) HTTP status code returned to a user while in the queue. - `additional_routes` (Block List) A list of additional hostname and paths combination to be applied on the waiting room. (see [below for nested schema](#nestedblock--additional_routes)) - `cookie_suffix` (String) A cookie suffix to be appended to the Cloudflare waiting room cookie name. - `custom_page_html` (String) This is a templated html file that will be rendered at the edge. @@ -58,6 +57,7 @@ resource "cloudflare_waiting_room" "example" { - `path` (String) The path within the host to enable the waiting room on. Defaults to `/`. - `queue_all` (Boolean) If queue_all is true, then all traffic will be sent to the waiting room. - `queueing_method` (String) The queueing method used by the waiting room. Available values: `fifo`, `random`, `passthrough`, `reject`. Defaults to `fifo`. +- `queueing_status_code` (Number) HTTP status code returned to a user while in the queue. Defaults to `200`. - `session_duration` (Number) Lifetime of a cookie (in minutes) set by Cloudflare for users who get access to the origin. Defaults to `5`. - `suspended` (Boolean) Suspends the waiting room. - `timeouts` (Block, Optional) (see [below for nested schema](#nestedblock--timeouts)) diff --git a/examples/data-sources/cloudflare_user/data-source.tf b/examples/data-sources/cloudflare_user/data-source.tf new file mode 100644 index 0000000000..b06b389afe --- /dev/null +++ b/examples/data-sources/cloudflare_user/data-source.tf @@ -0,0 +1,15 @@ +data "cloudflare_user" "me" {} +data "cloudflare_api_token_permission_groups" "all" {} + +resource "cloudflare_api_token" "example" { + name = "Terraform Cloud (Terraform)" + policy { + permission_groups = [ + data.cloudflare_api_token_permission_groups.all.user["User Details Read"], + + ] + resources = { + "com.cloudflare.api.user.${data.cloudflare_user.me.id}" = "*", + } + } +} \ No newline at end of file diff --git a/internal/framework/provider/provider.go b/internal/framework/provider/provider.go index c913aa19d0..2d6382f536 100644 --- a/internal/framework/provider/provider.go +++ b/internal/framework/provider/provider.go @@ -14,6 +14,7 @@ import ( "github.com/cloudflare/terraform-provider-cloudflare/internal/framework/service/r2_bucket" "github.com/cloudflare/terraform-provider-cloudflare/internal/framework/service/rulesets" "github.com/cloudflare/terraform-provider-cloudflare/internal/framework/service/turnstile" + "github.com/cloudflare/terraform-provider-cloudflare/internal/framework/service/user" "github.com/cloudflare/terraform-provider-cloudflare/internal/sdkv2provider" "github.com/cloudflare/terraform-provider-cloudflare/internal/utils" "github.com/hashicorp/terraform-plugin-framework-validators/stringvalidator" @@ -316,7 +317,7 @@ func (p *CloudflareProvider) Resources(ctx context.Context) []func() resource.Re func (p *CloudflareProvider) DataSources(ctx context.Context) []func() datasource.DataSource { return []func() datasource.DataSource{ - // NewExampleDataSource, + user.NewDataSource, } } diff --git a/internal/framework/service/user/data_source.go b/internal/framework/service/user/data_source.go new file mode 100644 index 0000000000..53e366c549 --- /dev/null +++ b/internal/framework/service/user/data_source.go @@ -0,0 +1,60 @@ +package user + +import ( + "context" + "fmt" + "github.com/cloudflare/cloudflare-go" + "github.com/hashicorp/terraform-plugin-framework/datasource" + "github.com/hashicorp/terraform-plugin-framework/types" +) + +var _ datasource.DataSource = &CloudflareUserDataSource{} + +func NewDataSource() datasource.DataSource { + return &CloudflareUserDataSource{} +} + +type CloudflareUserDataSource struct { + client *cloudflare.API +} + +func (r *CloudflareUserDataSource) Metadata(ctx context.Context, req datasource.MetadataRequest, resp *datasource.MetadataResponse) { + resp.TypeName = req.ProviderTypeName + "_user" +} + +func (r *CloudflareUserDataSource) Configure(ctx context.Context, req datasource.ConfigureRequest, resp *datasource.ConfigureResponse) { + if req.ProviderData == nil { + return + } + + client, ok := req.ProviderData.(*cloudflare.API) + + if !ok { + resp.Diagnostics.AddError( + "unexpected resource configure type", + fmt.Sprintf("expected *cloudflare.API, got: %T. Please report this issue to the provider developers.", req.ProviderData), + ) + + return + } + + r.client = client +} + +func (r *CloudflareUserDataSource) Read(ctx context.Context, req datasource.ReadRequest, resp *datasource.ReadResponse) { + var data CloudflareUserDataSourceModel + resp.Diagnostics.Append(req.Config.Get(ctx, &data)...) + if resp.Diagnostics.HasError() { + return + } + user, err := r.client.UserDetails(ctx) + if err != nil { + resp.Diagnostics.AddError("unable to retrieve user details", err.Error()) + return + } + data.ID = types.StringValue(user.ID) + data.Email = types.StringValue(user.Email) + data.Username = types.StringValue(user.Username) + + resp.Diagnostics.Append(resp.State.Set(ctx, &data)...) +} diff --git a/internal/framework/service/user/data_source_test.go b/internal/framework/service/user/data_source_test.go new file mode 100644 index 0000000000..54f0de29cb --- /dev/null +++ b/internal/framework/service/user/data_source_test.go @@ -0,0 +1,32 @@ +package user_test + +import ( + "github.com/cloudflare/terraform-provider-cloudflare/internal/acctest" + "github.com/hashicorp/terraform-plugin-testing/helper/resource" + "os" + "testing" +) + +func TestAccCloudflareUserDataSource(t *testing.T) { + // Temporarily unset CLOUDFLARE_API_TOKEN if it is set as the API token + // permission groups endpoint does not yet support the API tokens, and it + // results in misleading state error messages. + if os.Getenv("CLOUDFLARE_API_TOKEN") != "" { + t.Setenv("CLOUDFLARE_API_TOKEN", "") + } + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { acctest.TestAccPreCheck(t) }, + ProtoV6ProviderFactories: acctest.TestAccProtoV6ProviderFactories, + Steps: []resource.TestStep{ + { + Config: `data "cloudflare_user" "test" {}`, + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttrSet("data.cloudflare_user.test", "id"), + resource.TestCheckResourceAttrSet("data.cloudflare_user.test", "email"), + resource.TestCheckResourceAttrSet("data.cloudflare_user.test", "username"), + ), + }, + }, + }) +} diff --git a/internal/framework/service/user/model.go b/internal/framework/service/user/model.go new file mode 100644 index 0000000000..90e6120cef --- /dev/null +++ b/internal/framework/service/user/model.go @@ -0,0 +1,9 @@ +package user + +import "github.com/hashicorp/terraform-plugin-framework/types" + +type CloudflareUserDataSourceModel struct { + ID types.String `tfsdk:"id"` + Email types.String `tfsdk:"email"` + Username types.String `tfsdk:"username"` +} diff --git a/internal/framework/service/user/schema.go b/internal/framework/service/user/schema.go new file mode 100644 index 0000000000..efd3351bf4 --- /dev/null +++ b/internal/framework/service/user/schema.go @@ -0,0 +1,27 @@ +package user + +import ( + "context" + "github.com/hashicorp/terraform-plugin-framework/datasource" + "github.com/hashicorp/terraform-plugin-framework/datasource/schema" +) + +func (r *CloudflareUserDataSource) Schema(ctx context.Context, req datasource.SchemaRequest, resp *datasource.SchemaResponse) { + resp.Schema = schema.Schema{ + MarkdownDescription: "Use this data source to retrieve information about the currently authenticated user.", + Attributes: map[string]schema.Attribute{ + "id": schema.StringAttribute{ + Description: "The user's unique identifier.", + Computed: true, + }, + "email": schema.StringAttribute{ + Description: "The user's email address.", + Computed: true, + }, + "username": schema.StringAttribute{ + Description: "The user's username.", + Computed: true, + }, + }, + } +}