• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

Twingate / terraform-provider-twingate / 12287568180

12 Dec 2024 12:41AM UTC coverage: 57.177% (-4.9%) from 62.107%
12287568180

Pull #619

github

web-flow
Bump golang.org/x/crypto from 0.29.0 to 0.31.0

Bumps [golang.org/x/crypto](https://github.com/golang/crypto) from 0.29.0 to 0.31.0.
- [Commits](https://github.com/golang/crypto/compare/v0.29.0...v0.31.0)

---
updated-dependencies:
- dependency-name: golang.org/x/crypto
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Pull Request #619: Bump golang.org/x/crypto from 0.29.0 to 0.31.0

5079 of 8883 relevant lines covered (57.18%)

0.96 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

34.32
/twingate/internal/provider/resource/user.go
1
package resource
2

3
import (
4
        "context"
5
        "errors"
6
        "fmt"
7

8
        "github.com/Twingate/terraform-provider-twingate/v3/twingate/internal/attr"
9
        "github.com/Twingate/terraform-provider-twingate/v3/twingate/internal/client"
10
        "github.com/Twingate/terraform-provider-twingate/v3/twingate/internal/model"
11
        "github.com/Twingate/terraform-provider-twingate/v3/twingate/internal/utils"
12
        "github.com/hashicorp/terraform-plugin-framework-validators/stringvalidator"
13
        "github.com/hashicorp/terraform-plugin-framework/diag"
14
        "github.com/hashicorp/terraform-plugin-framework/resource"
15
        "github.com/hashicorp/terraform-plugin-framework/resource/schema"
16
        "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier"
17
        "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier"
18
        "github.com/hashicorp/terraform-plugin-framework/schema/validator"
19
        "github.com/hashicorp/terraform-plugin-framework/tfsdk"
20
        "github.com/hashicorp/terraform-plugin-framework/types"
21
)
22

23
var ErrAllowedToChangeOnlyManualUsers = fmt.Errorf("only users of type %s may be modified", model.UserTypeManual)
24

25
// Ensure the implementation satisfies the desired interfaces.
26
var _ resource.Resource = &user{}
27

28
func NewUserResource() resource.Resource {
2✔
29
        return &user{}
2✔
30
}
2✔
31

32
type user struct {
33
        client *client.Client
34
}
35

36
type userModel struct {
37
        ID         types.String `tfsdk:"id"`
38
        Email      types.String `tfsdk:"email"`
39
        FirstName  types.String `tfsdk:"first_name"`
40
        LastName   types.String `tfsdk:"last_name"`
41
        SendInvite types.Bool   `tfsdk:"send_invite"`
42
        IsActive   types.Bool   `tfsdk:"is_active"`
43
        Role       types.String `tfsdk:"role"`
44
        Type       types.String `tfsdk:"type"`
45
}
46

47
func (r *user) Metadata(_ context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) {
2✔
48
        resp.TypeName = TwingateUser
2✔
49
}
2✔
50

51
func (r *user) Configure(_ context.Context, req resource.ConfigureRequest, _ *resource.ConfigureResponse) {
×
52
        if req.ProviderData == nil {
×
53
                return
×
54
        }
×
55

56
        r.client = req.ProviderData.(*client.Client)
×
57
}
58

59
func (r *user) Schema(_ context.Context, _ resource.SchemaRequest, resp *resource.SchemaResponse) {
2✔
60
        resp.Schema = schema.Schema{
2✔
61
                Description: "Users provides different levels of write capabilities across the Twingate Admin Console. For more information, see Twingate's [documentation](https://www.twingate.com/docs/users).",
2✔
62
                Attributes: map[string]schema.Attribute{
2✔
63
                        attr.Email: schema.StringAttribute{
2✔
64
                                Required: true,
2✔
65
                                PlanModifiers: []planmodifier.String{
2✔
66
                                        stringplanmodifier.RequiresReplace(),
2✔
67
                                },
2✔
68
                                Description: "The User's email address",
2✔
69
                        },
2✔
70
                        // optional
2✔
71
                        attr.FirstName: schema.StringAttribute{
2✔
72
                                Optional:    true,
2✔
73
                                Computed:    true,
2✔
74
                                Description: "The User's first name",
2✔
75
                        },
2✔
76
                        attr.LastName: schema.StringAttribute{
2✔
77
                                Optional:    true,
2✔
78
                                Computed:    true,
2✔
79
                                Description: "The User's last name",
2✔
80
                        },
2✔
81
                        attr.SendInvite: schema.BoolAttribute{
2✔
82
                                Optional:    true,
2✔
83
                                Computed:    true,
2✔
84
                                Description: "Determines whether to send an email invitation to the User. True by default.",
2✔
85
                        },
2✔
86
                        attr.IsActive: schema.BoolAttribute{
2✔
87
                                Optional:    true,
2✔
88
                                Computed:    true,
2✔
89
                                Description: "Determines whether the User is active or not. Inactive users will be not able to sign in.",
2✔
90
                        },
2✔
91
                        attr.Role: schema.StringAttribute{
2✔
92
                                Optional:    true,
2✔
93
                                Computed:    true,
2✔
94
                                Description: fmt.Sprintf("Determines the User's role. Either %s.", utils.DocList(model.UserRoles)),
2✔
95
                                Validators: []validator.String{
2✔
96
                                        stringvalidator.OneOf(model.UserRoles...),
2✔
97
                                },
2✔
98
                        },
2✔
99
                        // computed
2✔
100
                        attr.Type: schema.StringAttribute{
2✔
101
                                Computed:    true,
2✔
102
                                Description: fmt.Sprintf("Indicates the User's type. Either %s.", utils.DocList(model.UserTypes)),
2✔
103
                        },
2✔
104
                        attr.ID: schema.StringAttribute{
2✔
105
                                Computed:    true,
2✔
106
                                Description: "Autogenerated ID of the User, encoded in base64.",
2✔
107
                        },
2✔
108
                },
2✔
109
        }
2✔
110
}
2✔
111

112
func (r *user) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) {
×
113
        var plan userModel
×
114

×
115
        resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...)
×
116

×
117
        if resp.Diagnostics.HasError() {
×
118
                return
×
119
        }
×
120

121
        user, err := r.client.CreateUser(ctx, &model.User{
×
122
                Email:      plan.Email.ValueString(),
×
123
                FirstName:  plan.FirstName.ValueString(),
×
124
                LastName:   plan.LastName.ValueString(),
×
125
                SendInvite: convertSendInviteFlag(plan.SendInvite),
×
126
                Role:       withDefaultValue(plan.Role.ValueString(), model.UserRoleMember),
×
127
                IsActive:   convertIsActiveFlag(plan.IsActive),
×
128
        })
×
129

×
130
        r.helper(ctx, user, &plan, &resp.State, &resp.Diagnostics, err, operationCreate)
×
131
}
132

133
func convertSendInviteFlag(val types.Bool) bool {
×
134
        if !val.IsUnknown() {
×
135
                return val.ValueBool()
×
136
        }
×
137

138
        // default value
139
        return true
×
140
}
141

142
func convertIsActiveFlag(val types.Bool) bool {
×
143
        if !val.IsUnknown() {
×
144
                return val.ValueBool()
×
145
        }
×
146

147
        // default value
148
        return true
×
149
}
150

151
func (r *user) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) {
×
152
        var state userModel
×
153

×
154
        resp.Diagnostics.Append(req.State.Get(ctx, &state)...)
×
155

×
156
        if resp.Diagnostics.HasError() {
×
157
                return
×
158
        }
×
159

160
        user, err := r.client.ReadUser(ctx, state.ID.ValueString())
×
161

×
162
        r.helper(ctx, user, &state, &resp.State, &resp.Diagnostics, err, operationRead)
×
163
}
164

165
func (r *user) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) {
×
166
        var plan, state userModel
×
167

×
168
        resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...)
×
169
        resp.Diagnostics.Append(req.State.Get(ctx, &state)...)
×
170
        addErr(&resp.Diagnostics, isAllowedToChangeUser(&state), operationUpdate, TwingateUser)
×
171

×
172
        if resp.Diagnostics.HasError() {
×
173
                return
×
174
        }
×
175

176
        userUpdateReq := &model.UserUpdate{
×
177
                ID: state.ID.ValueString(),
×
178
        }
×
179

×
180
        if plan.FirstName.ValueString() != "" && state.FirstName != plan.FirstName {
×
181
                userUpdateReq.FirstName = plan.FirstName.ValueStringPointer()
×
182
        }
×
183

184
        if plan.LastName.ValueString() != "" && state.LastName != plan.LastName {
×
185
                userUpdateReq.LastName = plan.LastName.ValueStringPointer()
×
186
        }
×
187

188
        if plan.Role.ValueString() != "" && state.Role != plan.Role {
×
189
                userUpdateReq.Role = plan.Role.ValueStringPointer()
×
190
        }
×
191

192
        isActive := convertIsActiveFlag(plan.IsActive)
×
193
        if state.IsActive.ValueBool() != isActive {
×
194
                userUpdateReq.IsActive = &isActive
×
195
        }
×
196

197
        user, err := r.client.UpdateUser(ctx, userUpdateReq)
×
198

×
199
        r.helper(ctx, user, &state, &resp.State, &resp.Diagnostics, err, operationUpdate)
×
200
}
201

202
func isAllowedToChangeUser(state *userModel) error {
×
203
        if state.Type.ValueString() != model.UserTypeManual {
×
204
                return ErrAllowedToChangeOnlyManualUsers
×
205
        }
×
206

207
        return nil
×
208
}
209

210
func (r *user) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) {
×
211
        var state userModel
×
212

×
213
        resp.Diagnostics.Append(req.State.Get(ctx, &state)...)
×
214
        addErr(&resp.Diagnostics, isAllowedToChangeUser(&state), operationDelete, TwingateUser)
×
215

×
216
        if resp.Diagnostics.HasError() {
×
217
                return
×
218
        }
×
219

220
        err := r.client.DeleteUser(ctx, state.ID.ValueString())
×
221
        addErr(&resp.Diagnostics, err, operationDelete, TwingateUser)
×
222
}
223

224
func (r *user) helper(ctx context.Context, user *model.User, state *userModel, respState *tfsdk.State, diagnostics *diag.Diagnostics, err error, operation string) {
×
225
        if err != nil {
×
226
                if errors.Is(err, client.ErrGraphqlResultIsEmpty) {
×
227
                        // clear state
×
228
                        respState.RemoveResource(ctx)
×
229

×
230
                        return
×
231
                }
×
232

233
                addErr(diagnostics, err, operation, TwingateUser)
×
234

×
235
                return
×
236
        }
237

238
        state.ID = types.StringValue(user.ID)
×
239
        state.FirstName = types.StringValue(user.FirstName)
×
240
        state.LastName = types.StringValue(user.LastName)
×
241
        state.Role = types.StringValue(user.Role)
×
242
        state.Type = types.StringValue(user.Type)
×
243
        state.IsActive = types.BoolValue(user.IsActive)
×
244

×
245
        // Set refreshed state
×
246
        diags := respState.Set(ctx, state)
×
247
        diagnostics.Append(diags...)
×
248
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc