• 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

21.51
/twingate/internal/provider/resource/group.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
        tfattr "github.com/hashicorp/terraform-plugin-framework/attr"
13
        "github.com/hashicorp/terraform-plugin-framework/diag"
14
        "github.com/hashicorp/terraform-plugin-framework/path"
15
        "github.com/hashicorp/terraform-plugin-framework/resource"
16
        "github.com/hashicorp/terraform-plugin-framework/resource/schema"
17
        "github.com/hashicorp/terraform-plugin-framework/tfsdk"
18
        "github.com/hashicorp/terraform-plugin-framework/types"
19
)
20

21
func ErrAllowedToChangeOnlyManualGroups(group *model.Group) error {
×
22
        return fmt.Errorf("Only groups of type %s may be modified. Group %s is a %s type group.", model.GroupTypeManual, group.Name, group.Type) //nolint
×
23
}
×
24

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

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

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

36
type groupModel struct {
37
        ID               types.String `tfsdk:"id"`
38
        Name             types.String `tfsdk:"name"`
39
        IsAuthoritative  types.Bool   `tfsdk:"is_authoritative"`
40
        UserIDs          types.Set    `tfsdk:"user_ids"`
41
        SecurityPolicyID types.String `tfsdk:"security_policy_id"`
42
}
43

44
func (r *group) Metadata(_ context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) {
2✔
45
        resp.TypeName = TwingateGroup
2✔
46
}
2✔
47

48
func (r *group) Configure(_ context.Context, req resource.ConfigureRequest, _ *resource.ConfigureResponse) {
×
49
        if req.ProviderData == nil {
×
50
                return
×
51
        }
×
52

53
        r.client = req.ProviderData.(*client.Client)
×
54
}
55

56
func (r *group) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) {
×
57
        resource.ImportStatePassthroughID(ctx, path.Root(attr.ID), req, resp)
×
58
}
×
59

60
func (r *group) Schema(_ context.Context, _ resource.SchemaRequest, resp *resource.SchemaResponse) {
2✔
61
        resp.Schema = schema.Schema{
2✔
62
                Description: "Groups are how users are authorized to access Resources. For more information, see Twingate's [documentation](https://docs.twingate.com/docs/groups).",
2✔
63
                Attributes: map[string]schema.Attribute{
2✔
64
                        attr.Name: schema.StringAttribute{
2✔
65
                                Required:    true,
2✔
66
                                Description: "The name of the group",
2✔
67
                        },
2✔
68
                        attr.IsAuthoritative: schema.BoolAttribute{
2✔
69
                                Optional:    true,
2✔
70
                                Computed:    true,
2✔
71
                                Description: "Determines whether User assignments to this Group will override any existing assignments. Default is `true`. If set to `false`, assignments made outside of Terraform will be ignored.",
2✔
72
                        },
2✔
73
                        attr.UserIDs: schema.SetAttribute{
2✔
74
                                ElementType: types.StringType,
2✔
75
                                Optional:    true,
2✔
76
                                Description: "List of User IDs that have permission to access the Group.",
2✔
77
                        },
2✔
78
                        attr.SecurityPolicyID: schema.StringAttribute{
2✔
79
                                Optional:    true,
2✔
80
                                Computed:    true,
2✔
81
                                Description: "Defines which Security Policy applies to this Group. The Security Policy ID can be obtained from the `twingate_security_policy` and `twingate_security_policies` data sources.",
2✔
82
                        },
2✔
83
                        // computed
2✔
84
                        attr.ID: schema.StringAttribute{
2✔
85
                                Computed:    true,
2✔
86
                                Description: "Autogenerated ID of the Resource, encoded in base64",
2✔
87
                        },
2✔
88
                },
2✔
89
        }
2✔
90
}
2✔
91

92
func (r *group) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) {
×
93
        var plan groupModel
×
94

×
95
        resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...)
×
96

×
97
        if resp.Diagnostics.HasError() {
×
98
                return
×
99
        }
×
100

101
        group, err := r.client.CreateGroup(ctx, convertGroup(&plan))
×
102

×
103
        r.helper(ctx, group, &plan, &resp.State, &resp.Diagnostics, err, operationCreate)
×
104
}
105

106
func (r *group) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) {
×
107
        var state groupModel
×
108

×
109
        resp.Diagnostics.Append(req.State.Get(ctx, &state)...)
×
110

×
111
        if resp.Diagnostics.HasError() {
×
112
                return
×
113
        }
×
114

115
        group, err := r.client.ReadGroup(ctx, state.ID.ValueString())
×
116
        if group != nil {
×
117
                group.IsAuthoritative = convertAuthoritativeFlag(state.IsAuthoritative)
×
118
        }
×
119

120
        r.helper(ctx, group, &state, &resp.State, &resp.Diagnostics, err, operationRead)
×
121
}
122

123
func (r *group) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) {
×
124
        var plan, state groupModel
×
125

×
126
        resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...)
×
127
        resp.Diagnostics.Append(req.State.Get(ctx, &state)...)
×
128

×
129
        group := convertGroup(&plan)
×
130
        remoteGroup, err := r.isAllowedToChangeGroup(ctx, state.ID.ValueString())
×
131
        addErr(&resp.Diagnostics, err, operationUpdate, TwingateGroup)
×
132

×
133
        if resp.Diagnostics.HasError() {
×
134
                return
×
135
        }
×
136

137
        oldIDs := getOldGroupUserIDs(&state, group, remoteGroup)
×
138
        if err := r.client.DeleteGroupUsers(ctx, state.ID.ValueString(), setDifference(oldIDs, group.Users)); err != nil {
×
139
                addErr(&resp.Diagnostics, err, operationUpdate, TwingateGroup)
×
140

×
141
                return
×
142
        }
×
143

144
        group.ID = state.ID.ValueString()
×
145
        group, err = r.client.UpdateGroup(ctx, group)
×
146

×
147
        r.helper(ctx, group, &plan, &resp.State, &resp.Diagnostics, err, operationUpdate)
×
148
}
149

150
func getOldGroupUserIDs(state *groupModel, group, remoteGroup *model.Group) []string {
×
151
        if group.IsAuthoritative {
×
152
                return remoteGroup.Users
×
153
        }
×
154

155
        return convertUsers(state.UserIDs.Elements())
×
156
}
157

158
func (r *group) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) {
×
159
        var state groupModel
×
160

×
161
        resp.Diagnostics.Append(req.State.Get(ctx, &state)...)
×
162

×
163
        if resp.Diagnostics.HasError() {
×
164
                return
×
165
        }
×
166

167
        if _, err := r.isAllowedToChangeGroup(ctx, state.ID.ValueString()); err != nil {
×
168
                addErr(&resp.Diagnostics, err, operationDelete, TwingateGroup)
×
169

×
170
                return
×
171
        }
×
172

173
        err := r.client.DeleteGroup(ctx, state.ID.ValueString())
×
174
        addErr(&resp.Diagnostics, err, operationDelete, TwingateGroup)
×
175
}
176

177
func (r *group) isAllowedToChangeGroup(ctx context.Context, groupID string) (*model.Group, error) {
×
178
        group, err := r.client.ReadGroup(ctx, groupID)
×
179
        if err != nil {
×
180
                return nil, err //nolint
×
181
        }
×
182

183
        if group.Type != model.GroupTypeManual {
×
184
                return nil, ErrAllowedToChangeOnlyManualGroups(group)
×
185
        }
×
186

187
        return group, nil
×
188
}
189

190
func (r *group) helper(ctx context.Context, group *model.Group, state *groupModel, respState *tfsdk.State, diagnostics *diag.Diagnostics, err error, operation string) {
×
191
        if err != nil {
×
192
                if errors.Is(err, client.ErrGraphqlResultIsEmpty) {
×
193
                        // clear state
×
194
                        respState.RemoveResource(ctx)
×
195

×
196
                        return
×
197
                }
×
198

199
                addErr(diagnostics, err, operation, TwingateGroup)
×
200

×
201
                return
×
202
        }
203

204
        if !group.IsAuthoritative {
×
205
                group.Users = setIntersection(convertUsers(state.UserIDs.Elements()), group.Users)
×
206
        }
×
207

208
        state.ID = types.StringValue(group.ID)
×
209
        state.Name = types.StringValue(group.Name)
×
210
        state.SecurityPolicyID = types.StringValue(group.SecurityPolicyID)
×
211
        state.IsAuthoritative = types.BoolValue(group.IsAuthoritative)
×
212

×
213
        if !state.UserIDs.IsNull() {
×
214
                userIDs, diags := types.SetValueFrom(ctx, types.StringType, group.Users)
×
215

×
216
                diagnostics.Append(diags...)
×
217

×
218
                if diagnostics.HasError() {
×
219
                        return
×
220
                }
×
221

222
                state.UserIDs = userIDs
×
223
        }
224

225
        // Set refreshed state
226
        diags := respState.Set(ctx, state)
×
227
        diagnostics.Append(diags...)
×
228
}
229

230
func convertGroup(data *groupModel) *model.Group {
×
231
        return &model.Group{
×
232
                ID:               data.ID.ValueString(),
×
233
                Name:             data.Name.ValueString(),
×
234
                Users:            convertUsers(data.UserIDs.Elements()),
×
235
                IsAuthoritative:  convertAuthoritativeFlag(data.IsAuthoritative),
×
236
                SecurityPolicyID: data.SecurityPolicyID.ValueString(),
×
237
        }
×
238
}
×
239

240
func convertUsers(userIDs []tfattr.Value) []string {
×
241
        return utils.Map(userIDs, func(item tfattr.Value) string {
×
242
                return item.(types.String).ValueString()
×
243
        })
×
244
}
245

246
func convertAuthoritativeFlag(val types.Bool) bool {
×
247
        if !val.IsUnknown() {
×
248
                return val.ValueBool()
×
249
        }
×
250

251
        // default value
252
        return true
×
253
}
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