Skip to content

Commit 1e5d42f

Browse files
authored
Merge pull request #84 from jetstack/teams_sso
Add support for SSO group assignment in Teams
2 parents d00ce21 + 429ee89 commit 1e5d42f

File tree

6 files changed

+135
-21
lines changed

6 files changed

+135
-21
lines changed

docs/resources/team.md

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,13 @@ resource "tlspc_team" "app_team_1" {
1717
name = "App Team 1"
1818
role = "PLATFORM_ADMIN"
1919
owners = [data.tlspc_user.owner.id]
20+
user_matching_rules = [
21+
{
22+
claim_name = "adGroups"
23+
operator = "CONTAINS"
24+
value = "Venafi"
25+
}
26+
]
2027
}
2128
```
2229

@@ -34,6 +41,25 @@ resource "tlspc_team" "app_team_1" {
3441
* RESOURCE_OWNER
3542
* GUEST
3643

44+
### Optional
45+
46+
- `user_matching_rules` (Attributes Set) List of rules to add members via SSO claims. Please refer to the [documentation](https://docs.venafi.cloud/vcs-platform/r-team-membership-rule-guidelines/) for detailed rule configuration. (see [below for nested schema](#nestedatt--user_matching_rules))
47+
3748
### Read-Only
3849

3950
- `id` (String) The ID of this resource.
51+
52+
<a id="nestedatt--user_matching_rules"></a>
53+
### Nested Schema for `user_matching_rules`
54+
55+
Required:
56+
57+
- `claim_name` (String) The SSO property that this rule acts on
58+
- `operator` (String) The operator of this rule, valid options:
59+
* EQUALS
60+
* NOT_EQUALS
61+
* CONTAINS
62+
* NOT_CONTAINS
63+
* STARTS_WITH
64+
* ENDS_WITH
65+
- `value` (String) The value to check for

examples/resources/tlspc_team/resource.tf

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,4 +2,11 @@ resource "tlspc_team" "app_team_1" {
22
name = "App Team 1"
33
role = "PLATFORM_ADMIN"
44
owners = [data.tlspc_user.owner.id]
5+
user_matching_rules = [
6+
{
7+
claim_name = "adGroups"
8+
operator = "CONTAINS"
9+
value = "Venafi"
10+
}
11+
]
512
}

go.mod

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ require (
1919
github.com/hashicorp/go-hclog v1.6.3 // indirect
2020
github.com/hashicorp/go-plugin v1.6.3 // indirect
2121
github.com/hashicorp/go-uuid v1.0.3 // indirect
22+
github.com/hashicorp/terraform-plugin-framework-validators v0.18.0 // indirect
2223
github.com/hashicorp/terraform-plugin-go v0.27.0 // indirect
2324
github.com/hashicorp/terraform-plugin-log v0.9.0 // indirect
2425
github.com/hashicorp/terraform-registry-address v0.2.5 // indirect

go.sum

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,8 @@ github.com/hashicorp/terraform-plugin-framework v1.15.0 h1:LQ2rsOfmDLxcn5EeIwdXF
4646
github.com/hashicorp/terraform-plugin-framework v1.15.0/go.mod h1:hxrNI/GY32KPISpWqlCoTLM9JZsGH3CyYlir09bD/fI=
4747
github.com/hashicorp/terraform-plugin-framework-jsontypes v0.2.0 h1:SJXL5FfJJm17554Kpt9jFXngdM6fXbnUnZ6iT2IeiYA=
4848
github.com/hashicorp/terraform-plugin-framework-jsontypes v0.2.0/go.mod h1:p0phD0IYhsu9bR4+6OetVvvH59I6LwjXGnTVEr8ox6E=
49+
github.com/hashicorp/terraform-plugin-framework-validators v0.18.0 h1:OQnlOt98ua//rCw+QhBbSqfW3QbwtVrcdWeQN5gI3Hw=
50+
github.com/hashicorp/terraform-plugin-framework-validators v0.18.0/go.mod h1:lZvZvagw5hsJwuY7mAY6KUz45/U6fiDR0CzQAwWD0CA=
4951
github.com/hashicorp/terraform-plugin-go v0.27.0 h1:ujykws/fWIdsi6oTUT5Or4ukvEan4aN9lY+LOxVP8EE=
5052
github.com/hashicorp/terraform-plugin-go v0.27.0/go.mod h1:FDa2Bb3uumkTGSkTFpWSOwWJDwA7bf3vdP3ltLDTH6o=
5153
github.com/hashicorp/terraform-plugin-log v0.9.0 h1:i7hOA+vdAItN1/7UrfBqBwvYPQ9TFvymaRGZED3FCV0=

internal/provider/team_resource.go

Lines changed: 81 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -6,14 +6,17 @@ package provider
66
import (
77
"context"
88
"fmt"
9+
"reflect"
910

1011
"terraform-provider-tlspc/internal/tlspc"
1112

13+
"github.com/hashicorp/terraform-plugin-framework-validators/stringvalidator"
1214
"github.com/hashicorp/terraform-plugin-framework/path"
1315
"github.com/hashicorp/terraform-plugin-framework/resource"
1416
"github.com/hashicorp/terraform-plugin-framework/resource/schema"
1517
"github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier"
1618
"github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier"
19+
"github.com/hashicorp/terraform-plugin-framework/schema/validator"
1720
"github.com/hashicorp/terraform-plugin-framework/types"
1821
)
1922

@@ -62,6 +65,35 @@ func (r *teamResource) Schema(_ context.Context, _ resource.SchemaRequest, resp
6265
ElementType: types.StringType,
6366
MarkdownDescription: "List of user ids",
6467
},
68+
"user_matching_rules": schema.SetNestedAttribute{
69+
Optional: true,
70+
MarkdownDescription: "List of rules to add members via SSO claims. Please refer to the [documentation](https://docs.venafi.cloud/vcs-platform/r-team-membership-rule-guidelines/) for detailed rule configuration.",
71+
NestedObject: schema.NestedAttributeObject{
72+
Attributes: map[string]schema.Attribute{
73+
"claim_name": schema.StringAttribute{
74+
Required: true,
75+
MarkdownDescription: "The SSO property that this rule acts on",
76+
},
77+
"operator": schema.StringAttribute{
78+
Required: true,
79+
MarkdownDescription: `The operator of this rule, valid options:
80+
* EQUALS
81+
* NOT_EQUALS
82+
* CONTAINS
83+
* NOT_CONTAINS
84+
* STARTS_WITH
85+
* ENDS_WITH`,
86+
Validators: []validator.String{
87+
stringvalidator.OneOf("EQUALS", "NOT_EQUALS", "CONTAINS", "NOT_CONTAINS", "STARTS_WITH", "ENDS_WITH"),
88+
},
89+
},
90+
"value": schema.StringAttribute{
91+
Required: true,
92+
MarkdownDescription: "The value to check for",
93+
},
94+
},
95+
},
96+
},
6597
},
6698
}
6799
}
@@ -86,10 +118,17 @@ func (r *teamResource) Configure(_ context.Context, req resource.ConfigureReques
86118
}
87119

88120
type teamResourceModel struct {
89-
ID types.String `tfsdk:"id"`
90-
Name types.String `tfsdk:"name"`
91-
Role types.String `tfsdk:"role"`
92-
Owners []types.String `tfsdk:"owners"`
121+
ID types.String `tfsdk:"id"`
122+
Name types.String `tfsdk:"name"`
123+
Role types.String `tfsdk:"role"`
124+
Owners []types.String `tfsdk:"owners"`
125+
UserMatchingRules []userMatchingRule `tfsdk:"user_matching_rules"`
126+
}
127+
128+
type userMatchingRule struct {
129+
ClaimName types.String `tfsdk:"claim_name"`
130+
Operator types.String `tfsdk:"operator"`
131+
Value types.String `tfsdk:"value"`
93132
}
94133

95134
func (r *teamResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) {
@@ -105,11 +144,21 @@ func (r *teamResource) Create(ctx context.Context, req resource.CreateRequest, r
105144
owners = append(owners, v.ValueString())
106145
}
107146

147+
umr := []tlspc.UserMatchingRule{}
148+
for _, v := range plan.UserMatchingRules {
149+
umr = append(umr, tlspc.UserMatchingRule{
150+
ClaimName: v.ClaimName.ValueString(),
151+
Operator: v.Operator.ValueString(),
152+
Value: v.Value.ValueString(),
153+
})
154+
}
155+
108156
team := tlspc.Team{
109-
Name: plan.Name.ValueString(),
110-
Role: plan.Role.ValueString(),
111-
Owners: owners,
112-
Members: []string{},
157+
Name: plan.Name.ValueString(),
158+
Role: plan.Role.ValueString(),
159+
Owners: owners,
160+
Members: []string{},
161+
UserMatchingRules: umr,
113162
}
114163

115164
created, err := r.client.CreateTeam(team)
@@ -153,6 +202,17 @@ func (r *teamResource) Read(ctx context.Context, req resource.ReadRequest, resp
153202
}
154203
state.Owners = owners
155204

205+
umr := []userMatchingRule{}
206+
for _, v := range team.UserMatchingRules {
207+
umr = append(umr, userMatchingRule{
208+
ClaimName: types.StringValue(v.ClaimName),
209+
Operator: types.StringValue(v.Operator),
210+
Value: types.StringValue(v.Value),
211+
})
212+
}
213+
214+
state.UserMatchingRules = umr
215+
156216
diags = resp.State.Set(ctx, state)
157217
resp.Diagnostics.Append(diags...)
158218
}
@@ -171,11 +231,20 @@ func (r *teamResource) Update(ctx context.Context, req resource.UpdateRequest, r
171231
return
172232
}
173233

174-
if state.Name != plan.Name || state.Role != plan.Role {
234+
if state.Name != plan.Name || state.Role != plan.Role || !reflect.DeepEqual(state.UserMatchingRules, plan.UserMatchingRules) {
235+
umr := []tlspc.UserMatchingRule{}
236+
for _, v := range plan.UserMatchingRules {
237+
umr = append(umr, tlspc.UserMatchingRule{
238+
ClaimName: v.ClaimName.ValueString(),
239+
Operator: v.Operator.ValueString(),
240+
Value: v.Value.ValueString(),
241+
})
242+
}
175243
team := tlspc.Team{
176-
ID: state.ID.ValueString(),
177-
Name: plan.Name.ValueString(),
178-
Role: plan.Role.ValueString(),
244+
ID: state.ID.ValueString(),
245+
Name: plan.Name.ValueString(),
246+
Role: plan.Role.ValueString(),
247+
UserMatchingRules: umr,
179248
}
180249
_, err := r.client.UpdateTeam(team)
181250
if err != nil {

internal/tlspc/tlspc.go

Lines changed: 18 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -102,11 +102,18 @@ func (c *Client) GetUser(email string) (*User, error) {
102102
}
103103

104104
type Team struct {
105-
ID string `json:"id,omitempty"`
106-
Name string `json:"name"`
107-
Role string `json:"role"`
108-
Owners []string `json:"owners"`
109-
Members []string `json:"members"`
105+
ID string `json:"id,omitempty"`
106+
Name string `json:"name"`
107+
Role string `json:"role"`
108+
Owners []string `json:"owners"`
109+
Members []string `json:"members"`
110+
UserMatchingRules []UserMatchingRule `json:"userMatchingRules,omitempty"`
111+
}
112+
113+
type UserMatchingRule struct {
114+
ClaimName string `json:"claimName"`
115+
Operator string `json:"operator"`
116+
Value string `json:"value"`
110117
}
111118

112119
func (c *Client) CreateTeam(team Team) (*Team, error) {
@@ -163,8 +170,9 @@ func (c *Client) GetTeam(id string) (*Team, error) {
163170
}
164171

165172
type updateTeam struct {
166-
Name string `json:"name"`
167-
Role string `json:"role"`
173+
Name string `json:"name"`
174+
Role string `json:"role"`
175+
UserMatchingRules []UserMatchingRule `json:"userMatchingRules,omitempty"`
168176
}
169177

170178
func (c *Client) UpdateTeam(team Team) (*Team, error) {
@@ -176,8 +184,9 @@ func (c *Client) UpdateTeam(team Team) (*Team, error) {
176184
path := c.Path(`%s/v1/teams/` + id)
177185

178186
update := updateTeam{
179-
Name: team.Name,
180-
Role: team.Role,
187+
Name: team.Name,
188+
Role: team.Role,
189+
UserMatchingRules: team.UserMatchingRules,
181190
}
182191
body, err := json.Marshal(update)
183192
if err != nil {

0 commit comments

Comments
 (0)