Skip to content

Commit 07e8248

Browse files
Resource LDAP Team Mapping / Release 1.12.0 (#77)
* Add dependencytrack_ldap_team_mapping resource. * Resolved linting. * Add docs and example. * Add CHANGELOG. Update log and error messages, to be more relevant to location within code.
1 parent 4cfda89 commit 07e8248

File tree

10 files changed

+393
-4
lines changed

10 files changed

+393
-4
lines changed

.golangci.yml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -527,6 +527,9 @@ linters:
527527
- path: internal/provider/team_apikey_resource_test.go
528528
linters:
529529
- godox
530+
- path: internal/provider/ldap_team_mapping_resource.go
531+
linters:
532+
- lll
530533

531534
formatters:
532535
enable:

CHANGELOG.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,11 @@
1+
## 1.12.0
2+
3+
#### FEATURES
4+
- Add `dependencytrack_ldap_team_mapping` resource to manage dynamic membership of Teams, from LDAP Servers.
5+
6+
#### FIXES
7+
- Fix example for `dependencytrack_oidc_group_mapping` incorrectly using `dependencytrack_oidc_group` value for `team`.
8+
19
## 1.11.0
210

311
#### FEATURES
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
---
2+
# generated by https://github.com/hashicorp/terraform-plugin-docs
3+
page_title: "dependencytrack_ldap_team_mapping Resource - dependencytrack"
4+
subcategory: ""
5+
description: |-
6+
Manages a mapping from LDAP Distinguished Name to Team.
7+
---
8+
9+
# dependencytrack_ldap_team_mapping (Resource)
10+
11+
Manages a mapping from LDAP Distinguished Name to Team.
12+
13+
## Example Usage
14+
15+
```terraform
16+
resource "dependencytrack_team" "example" {
17+
name = "Example Team Name"
18+
}
19+
20+
resource "dependencytrack_ldap_team_mapping" "example" {
21+
team = dependencytrack_team.example.id
22+
distinguished_name = "example.ldap.server"
23+
}
24+
```
25+
26+
<!-- schema generated by tfplugindocs -->
27+
## Schema
28+
29+
### Required
30+
31+
- `distinguished_name` (String) Distinguished Name used for mapping.
32+
- `team` (String) UUID for the Team to which to map users.
33+
34+
### Read-Only
35+
36+
- `id` (String) UUID for the Mapping, as generated by DependencyTrack.

docs/resources/oidc_group_mapping.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ resource "dependencytrack_oidc_group" "example" {
2323
2424
resource "dependencytrack_oidc_group_mapping" "example" {
2525
group = dependencytrack_oidc_group.example.id
26-
team = dependencytrack_oidc_group.example.team
26+
team = dependencytrack_team.example.id
2727
}
2828
```
2929

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
resource "dependencytrack_team" "example" {
2+
name = "Example Team Name"
3+
}
4+
5+
resource "dependencytrack_ldap_team_mapping" "example" {
6+
team = dependencytrack_team.example.id
7+
distinguished_name = "example.ldap.server"
8+
}

examples/resources/dependencytrack_oidc_group_mapping/resource.tf

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,5 +8,5 @@ resource "dependencytrack_oidc_group" "example" {
88

99
resource "dependencytrack_oidc_group_mapping" "example" {
1010
group = dependencytrack_oidc_group.example.id
11-
team = dependencytrack_oidc_group.example.team
11+
team = dependencytrack_team.example.id
1212
}
Lines changed: 279 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,279 @@
1+
package provider
2+
3+
import (
4+
"context"
5+
"fmt"
6+
"github.com/google/uuid"
7+
8+
dtrack "github.com/DependencyTrack/client-go"
9+
"github.com/hashicorp/terraform-plugin-framework/path"
10+
"github.com/hashicorp/terraform-plugin-framework/resource"
11+
"github.com/hashicorp/terraform-plugin-framework/resource/schema"
12+
"github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier"
13+
"github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier"
14+
"github.com/hashicorp/terraform-plugin-framework/types"
15+
"github.com/hashicorp/terraform-plugin-log/tflog"
16+
)
17+
18+
var (
19+
_ resource.Resource = &ldapTeamMappingResource{}
20+
_ resource.ResourceWithConfigure = &ldapTeamMappingResource{}
21+
)
22+
23+
func NewLDAPTeamMappingResource() resource.Resource {
24+
return &ldapTeamMappingResource{}
25+
}
26+
27+
type ldapTeamMappingResource struct {
28+
client *dtrack.Client
29+
semver *Semver
30+
}
31+
32+
type ldapTeamMappingResourceModel struct {
33+
ID types.String `tfsdk:"id"`
34+
Team types.String `tfsdk:"team"`
35+
DistinguishedName types.String `tfsdk:"distinguished_name"`
36+
}
37+
38+
func (r *ldapTeamMappingResource) Metadata(_ context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) {
39+
resp.TypeName = req.ProviderTypeName + "_ldap_team_mapping"
40+
}
41+
42+
func (r *ldapTeamMappingResource) Schema(_ context.Context, _ resource.SchemaRequest, resp *resource.SchemaResponse) {
43+
resp.Schema = schema.Schema{
44+
Description: "Manages a mapping from LDAP Distinguished Name to Team.",
45+
Attributes: map[string]schema.Attribute{
46+
"id": schema.StringAttribute{
47+
Description: "UUID for the Mapping, as generated by DependencyTrack.",
48+
Computed: true,
49+
PlanModifiers: []planmodifier.String{
50+
stringplanmodifier.UseStateForUnknown(),
51+
},
52+
},
53+
"team": schema.StringAttribute{
54+
Description: "UUID for the Team to which to map users.",
55+
Required: true,
56+
PlanModifiers: []planmodifier.String{
57+
stringplanmodifier.RequiresReplace(),
58+
},
59+
},
60+
"distinguished_name": schema.StringAttribute{
61+
Description: "Distinguished Name used for mapping.",
62+
Required: true,
63+
PlanModifiers: []planmodifier.String{
64+
stringplanmodifier.RequiresReplace(),
65+
},
66+
},
67+
},
68+
}
69+
}
70+
71+
func (r *ldapTeamMappingResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) {
72+
var plan ldapTeamMappingResourceModel
73+
diags := req.Plan.Get(ctx, &plan)
74+
resp.Diagnostics.Append(diags...)
75+
if resp.Diagnostics.HasError() {
76+
return
77+
}
78+
team, err := uuid.Parse(plan.Team.ValueString())
79+
if err != nil {
80+
resp.Diagnostics.AddAttributeError(
81+
path.Root("team"),
82+
"Within Create, unable to parse team into UUID",
83+
"Error from: "+err.Error(),
84+
)
85+
return
86+
}
87+
distinguishedName := plan.DistinguishedName.ValueString()
88+
89+
mappingReq := dtrack.MappedLdapGroupRequest{
90+
Team: team,
91+
DistinguishedName: distinguishedName,
92+
}
93+
tflog.Debug(ctx, "Mapping ldap distinguished name "+mappingReq.DistinguishedName+" to team "+mappingReq.Team.String())
94+
95+
mappingRes, err := r.client.LDAP.AddMapping(ctx, mappingReq)
96+
if err != nil {
97+
resp.Diagnostics.AddError(
98+
"Error creating ldap mapping",
99+
"Unexpected error: "+err.Error(),
100+
)
101+
return
102+
}
103+
104+
plan = ldapTeamMappingResourceModel{
105+
ID: types.StringValue(mappingRes.UUID.String()),
106+
DistinguishedName: types.StringValue(mappingRes.DistinguishedName),
107+
// Response does not include Team UUID
108+
Team: types.StringValue(team.String()),
109+
}
110+
111+
diags = resp.State.Set(ctx, plan)
112+
resp.Diagnostics.Append(diags...)
113+
if resp.Diagnostics.HasError() {
114+
return
115+
}
116+
tflog.Debug(ctx, "Created ldap mapping with id: "+plan.ID.ValueString()+", for distinguished name: "+plan.DistinguishedName.ValueString()+", to team, with id: "+plan.Team.ValueString())
117+
}
118+
119+
func (r *ldapTeamMappingResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) {
120+
// Fetch state
121+
var state ldapTeamMappingResourceModel
122+
diags := req.State.Get(ctx, &state)
123+
resp.Diagnostics.Append(diags...)
124+
if resp.Diagnostics.HasError() {
125+
return
126+
}
127+
tflog.Debug(ctx, "Refreshing ldap mapping with id: "+state.ID.ValueString()+", for team: "+state.Team.ValueString()+", and distinguished name: "+state.DistinguishedName.ValueString())
128+
129+
// Refresh
130+
id, err := uuid.Parse(state.ID.ValueString())
131+
if err != nil {
132+
resp.Diagnostics.AddAttributeError(
133+
path.Root("id"),
134+
"Within Read, unable to parse id into UUID",
135+
"Error from: "+err.Error(),
136+
)
137+
}
138+
team, err := uuid.Parse(state.Team.ValueString())
139+
if err != nil {
140+
resp.Diagnostics.AddAttributeError(
141+
path.Root("team"),
142+
"Within Read, unable to parse team into UUID",
143+
"Error from: "+err.Error(),
144+
)
145+
}
146+
distinguishedName := state.DistinguishedName.ValueString()
147+
if resp.Diagnostics.HasError() {
148+
return
149+
}
150+
151+
mappedGroups, err := r.client.LDAP.GetTeamMappings(ctx, team)
152+
if err != nil {
153+
resp.Diagnostics.AddError(
154+
"Within Read, unable to retrieve team mappings",
155+
"Error from: "+err.Error(),
156+
)
157+
return
158+
}
159+
mappingInfo, err := Find(mappedGroups, func(mapping dtrack.MappedLdapGroup) bool {
160+
return mapping.UUID == id
161+
})
162+
163+
if err != nil {
164+
resp.Diagnostics.AddError(
165+
"Within Read, unable to locate ldap mapping",
166+
"Error with finding mapping with id: "+id.String()+", for team: "+team.String()+", and distinguished name: "+distinguishedName+", in original error: "+err.Error(),
167+
)
168+
return
169+
}
170+
state = ldapTeamMappingResourceModel{
171+
ID: types.StringValue(mappingInfo.UUID.String()),
172+
Team: types.StringValue(team.String()),
173+
DistinguishedName: types.StringValue(mappingInfo.DistinguishedName),
174+
}
175+
176+
// Update state
177+
diags = resp.State.Set(ctx, &state)
178+
resp.Diagnostics.Append(diags...)
179+
if resp.Diagnostics.HasError() {
180+
return
181+
}
182+
tflog.Debug(ctx, "Refreshed ldap mapping for team: "+state.Team.ValueString()+", and distinguished name: "+state.DistinguishedName.ValueString())
183+
}
184+
185+
func (r *ldapTeamMappingResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) {
186+
// Nothing to Update. This resource only has Create, Delete actions
187+
// Get State
188+
var plan ldapTeamMappingResourceModel
189+
diags := req.Plan.Get(ctx, &plan)
190+
resp.Diagnostics.Append(diags...)
191+
if resp.Diagnostics.HasError() {
192+
return
193+
}
194+
195+
id, err := uuid.Parse(plan.ID.ValueString())
196+
if err != nil {
197+
resp.Diagnostics.AddAttributeError(
198+
path.Root("id"),
199+
"Within Update, unable to parse id into UUID",
200+
"Error from: "+err.Error(),
201+
)
202+
}
203+
team, err := uuid.Parse(plan.Team.ValueString())
204+
if err != nil {
205+
resp.Diagnostics.AddAttributeError(
206+
path.Root("team"),
207+
"Within Update, unable to parse team into UUID",
208+
"Error from: "+err.Error(),
209+
)
210+
}
211+
distingushedName := plan.DistinguishedName.ValueString()
212+
if resp.Diagnostics.HasError() {
213+
return
214+
}
215+
216+
state := ldapTeamMappingResourceModel{
217+
ID: types.StringValue(id.String()),
218+
Team: types.StringValue(team.String()),
219+
DistinguishedName: types.StringValue(distingushedName),
220+
}
221+
222+
// Update State
223+
diags = resp.State.Set(ctx, state)
224+
resp.Diagnostics.Append(diags...)
225+
if resp.Diagnostics.HasError() {
226+
return
227+
}
228+
tflog.Debug(ctx, "Updated ldap mapping with id: "+plan.ID.ValueString()+", for team: "+plan.Team.ValueString()+", and distinguished name: "+plan.DistinguishedName.ValueString())
229+
}
230+
231+
func (r *ldapTeamMappingResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) {
232+
// Load state
233+
var state ldapTeamMappingResourceModel
234+
diags := req.State.Get(ctx, &state)
235+
resp.Diagnostics.Append(diags...)
236+
if resp.Diagnostics.HasError() {
237+
return
238+
}
239+
240+
// Map TF to SDK
241+
id, err := uuid.Parse(state.ID.ValueString())
242+
if err != nil {
243+
resp.Diagnostics.AddAttributeError(
244+
path.Root("id"),
245+
"Within Delete, unable to parse id into UUID",
246+
"Error parsing UUID from: "+state.ID.ValueString()+", error: "+err.Error(),
247+
)
248+
return
249+
}
250+
251+
// Execute
252+
tflog.Debug(ctx, "Deleting ldap mapping with id: "+id.String()+", for team with id: "+state.Team.ValueString()+", and distinguished name: "+state.DistinguishedName.ValueString())
253+
err = r.client.LDAP.RemoveMapping(ctx, id)
254+
if err != nil {
255+
resp.Diagnostics.AddError(
256+
"Unable to delete ldap mapping",
257+
"Unexpected error when trying to delete ldap mapping with id: "+id.String()+", error: "+err.Error(),
258+
)
259+
return
260+
}
261+
tflog.Debug(ctx, "Deleted ldap mapping with id: "+id.String())
262+
}
263+
264+
func (r *ldapTeamMappingResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) {
265+
if req.ProviderData == nil {
266+
return
267+
}
268+
clientInfo, ok := req.ProviderData.(clientInfo)
269+
270+
if !ok {
271+
resp.Diagnostics.AddError(
272+
"Unexpected Configure Type",
273+
fmt.Sprintf("Expected provider.clientInfo, got %T. Please report this issue to the provider developers.", req.ProviderData),
274+
)
275+
return
276+
}
277+
r.client = clientInfo.client
278+
r.semver = clientInfo.semver
279+
}

0 commit comments

Comments
 (0)