From d8d4d6f6f9b2884bf1e75dfcc6cb2f697beefd40 Mon Sep 17 00:00:00 2001 From: Charlie Voiselle <464492+angrycub@users.noreply.github.com> Date: Thu, 14 Aug 2025 18:40:28 -0400 Subject: [PATCH 1/4] fix: add proper OIDC user role validation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When creating OIDC users, the provider was calling UpdateUserRoles even with empty roles due to the default schema value, causing the server error "User Role Field is set in the OIDC configuration". OIDC users should get their roles exclusively from the OIDC provider's role mapping, not from explicit API calls. This fix: - Errors if explicit roles are provided for OIDC users - Skips role assignment entirely for OIDC users - Provides clear error messaging about OIDC role behavior 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude --- internal/provider/user_resource.go | 63 ++++++++++++++++++++---------- 1 file changed, 43 insertions(+), 20 deletions(-) diff --git a/internal/provider/user_resource.go b/internal/provider/user_resource.go index 79d248d..0dbea7c 100644 --- a/internal/provider/user_resource.go +++ b/internal/provider/user_resource.go @@ -213,17 +213,28 @@ func (r *UserResource) Create(ctx context.Context, req resource.CreateRequest, r resp.Diagnostics.Append( data.Roles.ElementsAs(ctx, &roles, false)..., ) - tflog.Info(ctx, "updating user roles", map[string]any{ - "new_roles": roles, - }) - user, err = client.UpdateUserRoles(ctx, user.ID.String(), codersdk.UpdateRoles{ - Roles: roles, - }) - if err != nil { - resp.Diagnostics.AddError("Client Error", fmt.Sprintf("Unable to update newly created user roles, got error: %s", err)) - return + + // OIDC users get their roles from the OIDC provider's role mapping + if loginType == codersdk.LoginTypeOIDC { + if len(roles) > 0 { + resp.Diagnostics.AddError("Configuration Error", "Cannot set explicit roles for OIDC users. OIDC users get their roles from the OIDC provider's role mapping configuration.") + return + } + tflog.Info(ctx, "skipping role assignment for OIDC user (roles come from OIDC provider)") + } else { + // For non-OIDC users, set roles explicitly + tflog.Info(ctx, "updating user roles", map[string]any{ + "new_roles": roles, + }) + user, err = client.UpdateUserRoles(ctx, user.ID.String(), codersdk.UpdateRoles{ + Roles: roles, + }) + if err != nil { + resp.Diagnostics.AddError("Client Error", fmt.Sprintf("Unable to update newly created user roles, got error: %s", err)) + return + } + tflog.Info(ctx, "successfully updated user roles") } - tflog.Info(ctx, "successfully updated user roles") if data.Suspended.ValueBool() { _, err = client.UpdateUserStatus(ctx, data.ID.ValueString(), codersdk.UserStatus("suspended")) @@ -348,17 +359,29 @@ func (r *UserResource) Update(ctx context.Context, req resource.UpdateRequest, r resp.Diagnostics.Append( data.Roles.ElementsAs(ctx, &roles, false)..., ) - tflog.Info(ctx, "updating user roles", map[string]any{ - "new_roles": roles, - }) - _, err = client.UpdateUserRoles(ctx, user.ID.String(), codersdk.UpdateRoles{ - Roles: roles, - }) - if err != nil { - resp.Diagnostics.AddError("Client Error", fmt.Sprintf("Unable to update user roles, got error: %s", err)) - return + + // OIDC users get their roles from the OIDC provider's role mapping + loginType := codersdk.LoginType(data.LoginType.ValueString()) + if loginType == codersdk.LoginTypeOIDC { + if len(roles) > 0 { + resp.Diagnostics.AddError("Configuration Error", "Cannot set explicit roles for OIDC users. OIDC users get their roles from the OIDC provider's role mapping configuration.") + return + } + tflog.Info(ctx, "skipping role assignment for OIDC user (roles come from OIDC provider)") + } else { + // For non-OIDC users, set roles explicitly + tflog.Info(ctx, "updating user roles", map[string]any{ + "new_roles": roles, + }) + _, err = client.UpdateUserRoles(ctx, user.ID.String(), codersdk.UpdateRoles{ + Roles: roles, + }) + if err != nil { + resp.Diagnostics.AddError("Client Error", fmt.Sprintf("Unable to update user roles, got error: %s", err)) + return + } + tflog.Info(ctx, "successfully updated user roles") } - tflog.Info(ctx, "successfully updated user roles") if data.LoginType.ValueString() == string(codersdk.LoginTypePassword) && !data.Password.IsNull() { tflog.Info(ctx, "updating password") From e7df6a1c2f14efd5c08b000efc3ed42c575d9c1f Mon Sep 17 00:00:00 2001 From: "blink-so[bot]" <211532188+blink-so[bot]@users.noreply.github.com> Date: Fri, 15 Aug 2025 15:20:23 +0000 Subject: [PATCH 2/4] fix: prevent config drift for OIDC users in Read function Update the Read function to not populate roles from server response for OIDC users. This prevents Terraform from detecting config drift when OIDC users have roles assigned by the OIDC provider but an empty roles list in the Terraform config. Addresses review comment about config drift in PR #247. Co-authored-by: angrycub <464492+angrycub@users.noreply.github.com> --- internal/provider/user_resource.go | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/internal/provider/user_resource.go b/internal/provider/user_resource.go index 0dbea7c..7d63b16 100644 --- a/internal/provider/user_resource.go +++ b/internal/provider/user_resource.go @@ -278,11 +278,21 @@ func (r *UserResource) Read(ctx context.Context, req resource.ReadRequest, resp data.Email = types.StringValue(user.Email) data.Name = types.StringValue(user.Name) data.Username = types.StringValue(user.Username) - roles := make([]attr.Value, 0, len(user.Roles)) - for _, role := range user.Roles { - roles = append(roles, types.StringValue(role.Name)) + + // For OIDC users, don't populate roles from server to avoid config drift + // OIDC users get their roles from the OIDC provider's role mapping + if user.LoginType == codersdk.LoginTypeOIDC { + // Keep roles empty for OIDC users to match the expected Terraform config + data.Roles = types.SetValueMust(types.StringType, []attr.Value{}) + } else { + // For non-OIDC users, populate roles from server response + roles := make([]attr.Value, 0, len(user.Roles)) + for _, role := range user.Roles { + roles = append(roles, types.StringValue(role.Name)) + } + data.Roles = types.SetValueMust(types.StringType, roles) } - data.Roles = types.SetValueMust(types.StringType, roles) + data.LoginType = types.StringValue(string(user.LoginType)) data.Suspended = types.BoolValue(user.Status == codersdk.UserStatusSuspended) @@ -446,4 +456,4 @@ func (r *UserResource) ImportState(ctx context.Context, req resource.ImportState return } resp.Diagnostics.Append(resp.State.SetAttribute(ctx, path.Root("id"), user.ID.String())...) -} +} \ No newline at end of file From 9ecc2e18cab005c33d3c6e3de8ddd5248ad20ab1 Mon Sep 17 00:00:00 2001 From: "blink-so[bot]" <211532188+blink-so[bot]@users.noreply.github.com> Date: Fri, 15 Aug 2025 16:04:47 +0000 Subject: [PATCH 3/4] refactor: improve code style with consistent negative conditions Update OIDC user role handling to use cleaner Go style: - Use negative conditions (loginType != codersdk.LoginTypeOIDC) for better readability - Simplify comments to be more concise and inline - Maintain all existing validation logic and functionality Co-authored-by: angrycub <464492+angrycub@users.noreply.github.com> --- internal/provider/user_resource.go | 52 +++++++++++++----------------- 1 file changed, 23 insertions(+), 29 deletions(-) diff --git a/internal/provider/user_resource.go b/internal/provider/user_resource.go index 7d63b16..d22cb8f 100644 --- a/internal/provider/user_resource.go +++ b/internal/provider/user_resource.go @@ -214,15 +214,7 @@ func (r *UserResource) Create(ctx context.Context, req resource.CreateRequest, r data.Roles.ElementsAs(ctx, &roles, false)..., ) - // OIDC users get their roles from the OIDC provider's role mapping - if loginType == codersdk.LoginTypeOIDC { - if len(roles) > 0 { - resp.Diagnostics.AddError("Configuration Error", "Cannot set explicit roles for OIDC users. OIDC users get their roles from the OIDC provider's role mapping configuration.") - return - } - tflog.Info(ctx, "skipping role assignment for OIDC user (roles come from OIDC provider)") - } else { - // For non-OIDC users, set roles explicitly + if loginType != codersdk.LoginTypeOIDC { // non-OIDC users get explicit roles tflog.Info(ctx, "updating user roles", map[string]any{ "new_roles": roles, }) @@ -234,6 +226,13 @@ func (r *UserResource) Create(ctx context.Context, req resource.CreateRequest, r return } tflog.Info(ctx, "successfully updated user roles") + } else { + // OIDC users get roles from provider's role mapping + if len(roles) > 0 { + resp.Diagnostics.AddError("Configuration Error", "Cannot set explicit roles for OIDC users. OIDC users get their roles from the OIDC provider's role mapping configuration.") + return + } + tflog.Info(ctx, "skipping role assignment for OIDC user (roles come from OIDC provider)") } if data.Suspended.ValueBool() { @@ -278,21 +277,18 @@ func (r *UserResource) Read(ctx context.Context, req resource.ReadRequest, resp data.Email = types.StringValue(user.Email) data.Name = types.StringValue(user.Name) data.Username = types.StringValue(user.Username) - - // For OIDC users, don't populate roles from server to avoid config drift - // OIDC users get their roles from the OIDC provider's role mapping - if user.LoginType == codersdk.LoginTypeOIDC { - // Keep roles empty for OIDC users to match the expected Terraform config - data.Roles = types.SetValueMust(types.StringType, []attr.Value{}) - } else { - // For non-OIDC users, populate roles from server response + + if user.LoginType != codersdk.LoginTypeOIDC { // populate roles from server for non-OIDC users roles := make([]attr.Value, 0, len(user.Roles)) for _, role := range user.Roles { roles = append(roles, types.StringValue(role.Name)) } data.Roles = types.SetValueMust(types.StringType, roles) + } else { + // OIDC users: keep roles empty to avoid config drift + data.Roles = types.SetValueMust(types.StringType, []attr.Value{}) } - + data.LoginType = types.StringValue(string(user.LoginType)) data.Suspended = types.BoolValue(user.Status == codersdk.UserStatusSuspended) @@ -370,16 +366,8 @@ func (r *UserResource) Update(ctx context.Context, req resource.UpdateRequest, r data.Roles.ElementsAs(ctx, &roles, false)..., ) - // OIDC users get their roles from the OIDC provider's role mapping loginType := codersdk.LoginType(data.LoginType.ValueString()) - if loginType == codersdk.LoginTypeOIDC { - if len(roles) > 0 { - resp.Diagnostics.AddError("Configuration Error", "Cannot set explicit roles for OIDC users. OIDC users get their roles from the OIDC provider's role mapping configuration.") - return - } - tflog.Info(ctx, "skipping role assignment for OIDC user (roles come from OIDC provider)") - } else { - // For non-OIDC users, set roles explicitly + if loginType != codersdk.LoginTypeOIDC { // non-OIDC users get explicit roles tflog.Info(ctx, "updating user roles", map[string]any{ "new_roles": roles, }) @@ -391,6 +379,13 @@ func (r *UserResource) Update(ctx context.Context, req resource.UpdateRequest, r return } tflog.Info(ctx, "successfully updated user roles") + } else { + // OIDC users get roles from provider's role mapping + if len(roles) > 0 { + resp.Diagnostics.AddError("Configuration Error", "Cannot set explicit roles for OIDC users. OIDC users get their roles from the OIDC provider's role mapping configuration.") + return + } + tflog.Info(ctx, "skipping role assignment for OIDC user (roles come from OIDC provider)") } if data.LoginType.ValueString() == string(codersdk.LoginTypePassword) && !data.Password.IsNull() { @@ -455,5 +450,4 @@ func (r *UserResource) ImportState(ctx context.Context, req resource.ImportState resp.Diagnostics.AddError("Client Error", "Invalid import ID format, expected a single UUID or a valid username") return } - resp.Diagnostics.Append(resp.State.SetAttribute(ctx, path.Root("id"), user.ID.String())...) -} \ No newline at end of file + resp.Diagnostics.Append(resp.State.SetAttribute(ctx, path.Root("id"), user.ID.String())...)} From 2e40dabb6323f53daf6af329b9366f50f39001c4 Mon Sep 17 00:00:00 2001 From: "blink-so[bot]" <211532188+blink-so[bot]@users.noreply.github.com> Date: Fri, 15 Aug 2025 16:17:25 +0000 Subject: [PATCH 4/4] style: run go fmt to fix formatting Fix formatting issues found by go fmt, specifically the closing brace placement in the ImportState function. Co-authored-by: angrycub <464492+angrycub@users.noreply.github.com> --- internal/provider/user_resource.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/internal/provider/user_resource.go b/internal/provider/user_resource.go index d22cb8f..4253a96 100644 --- a/internal/provider/user_resource.go +++ b/internal/provider/user_resource.go @@ -450,4 +450,5 @@ func (r *UserResource) ImportState(ctx context.Context, req resource.ImportState resp.Diagnostics.AddError("Client Error", "Invalid import ID format, expected a single UUID or a valid username") return } - resp.Diagnostics.Append(resp.State.SetAttribute(ctx, path.Root("id"), user.ID.String())...)} + resp.Diagnostics.Append(resp.State.SetAttribute(ctx, path.Root("id"), user.ID.String())...) +} pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy