Skip to content

Commit

Permalink
Feat: add role validator (#832)
Browse files Browse the repository at this point in the history
* Feat: add role validator

* added fixes
  • Loading branch information
TomerHeber committed Apr 21, 2024
1 parent 6df42e0 commit 2a04559
Show file tree
Hide file tree
Showing 12 changed files with 79 additions and 40 deletions.
21 changes: 15 additions & 6 deletions client/builtin_roles.go
Original file line number Diff line number Diff line change
@@ -1,12 +1,21 @@
package client

const (
Admin string = "Admin"
Deployer string = "Deployer"
Planner string = "Planner"
Viewer string = "Viewer"
AdminRole string = "Admin"
DeployerRole string = "Deployer"
PlannerRole string = "Planner"
ViewerRole string = "Viewer"
UserRole string = "User"
)

func IsBuiltinProjectRole(role string) bool {
return role == Admin || role == Deployer || role == Planner || role == Viewer
func IsBuiltinRole(role string) bool {
return role == AdminRole || role == DeployerRole || role == PlannerRole || role == ViewerRole || role == UserRole
}

func IsCustomRole(role string) bool {
return !IsBuiltinRole(role)
}

var ProjectBuiltinRoles = []string{AdminRole, DeployerRole, PlannerRole, ViewerRole}
var EnvironmentBuiltinRoles = []string{AdminRole, DeployerRole, PlannerRole, ViewerRole}
var OrganizationBuiltinRoles = []string{UserRole, AdminRole}
4 changes: 2 additions & 2 deletions client/user_project_assignment_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,11 @@ var _ = Describe("Agent Project Assignment", func() {

assignPayload := &AssignUserToProjectPayload{
UserId: userId,
Role: string(Admin),
Role: string(AdminRole),
}

updatePayload := &UpdateUserProjectAssignmentPayload{
Role: string(Admin),
Role: string(AdminRole),
}

expectedResponse := &UserProjectAssignment{
Expand Down
2 changes: 1 addition & 1 deletion env0/resource_team_environment_assignment.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ func resourceTeamEnvironmentAssignment() *schema.Resource {
Type: schema.TypeString,
Description: "id of the assigned custom role. The following built-in roles can be passed as well: `Viewer`, `Planner`, `Deployer`, `Admin`",
Required: true,
ValidateDiagFunc: ValidateNotEmptyString,
ValidateDiagFunc: NewRoleValidator(client.EnvironmentBuiltinRoles),
},
},
}
Expand Down
2 changes: 1 addition & 1 deletion env0/resource_team_organization_assignment.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ func resourceTeamOrganizationAssignment() *schema.Resource {
Type: schema.TypeString,
Description: "id of the assigned custom role. The following built-in roles can be passed as well: `User`, `Admin`",
Required: true,
ValidateDiagFunc: ValidateNotEmptyString,
ValidateDiagFunc: NewRoleValidator(client.OrganizationBuiltinRoles),
},
},
}
Expand Down
16 changes: 16 additions & 0 deletions env0/resource_team_organization_assignment_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -210,4 +210,20 @@ func TestUnitTeamOrganizationAssignmentResource(t *testing.T) {
)
})
})

t.Run("unsupported built-in role", func(t *testing.T) {
testCase := resource.TestCase{
Steps: []resource.TestStep{
{
Config: resourceConfigCreate(resourceType, resourceName, map[string]interface{}{
"team_id": teamId,
"role_id": "Planner",
}),
ExpectError: regexp.MustCompile(`the following built-in role 'Planner' is not supported for this resource, must be one of \[User,Admin\]`),
},
},
}

runUnitTest(t, testCase, func(mock *client.MockApiClientInterface) {})
})
}
6 changes: 3 additions & 3 deletions env0/resource_team_project_assignment.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ func resourceTeamProjectAssignment() *schema.Resource {
Type: schema.TypeString,
Description: "the assigned built-in role (Admin, Planner, Viewer, Deployer)",
Optional: true,
ValidateDiagFunc: ValidateRole,
ValidateDiagFunc: NewRoleValidator(client.ProjectBuiltinRoles),
ExactlyOneOf: []string{"custom_role_id", "role"},
},
"custom_role_id": {
Expand Down Expand Up @@ -94,7 +94,7 @@ func resourceTeamProjectAssignmentRead(ctx context.Context, d *schema.ResourceDa
return diag.Errorf("schema resource data serialization failed: %v", err)
}

if client.IsBuiltinProjectRole(assignment.Role) {
if client.IsBuiltinRole(assignment.Role) {
d.Set("role", assignment.Role)
} else {
d.Set("custom_role_id", assignment.Role)
Expand Down Expand Up @@ -150,7 +150,7 @@ func resourceTeamProjectAssignmentImport(ctx context.Context, d *schema.Resource
return nil, fmt.Errorf("schema resource data serialization failed: %w", err)
}

if client.IsBuiltinProjectRole(assignment.Role) {
if client.IsBuiltinRole(assignment.Role) {
d.Set("role", assignment.Role)
} else {
d.Set("custom_role_id", assignment.Role)
Expand Down
4 changes: 2 additions & 2 deletions env0/resource_team_project_assignment_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,13 +24,13 @@ func TestUnitTeamProjectAssignmentResource(t *testing.T) {
assignment := client.TeamRoleAssignmentPayload{
Id: "assignmentId",
TeamId: "teamId0",
Role: string(client.Admin),
Role: string(client.AdminRole),
}

updateAssignment := client.TeamRoleAssignmentPayload{
Id: "assignmentIdupdate",
TeamId: "teamIdUupdate",
Role: string(client.Admin),
Role: string(client.AdminRole),
}

assignmentCustom := client.TeamRoleAssignmentPayload{
Expand Down
4 changes: 2 additions & 2 deletions env0/resource_user_project_assignment.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ func resourceUserProjectAssignment() *schema.Resource {
Type: schema.TypeString,
Description: "the assigned role (Admin, Planner, Viewer, Deployer)",
Optional: true,
ValidateDiagFunc: ValidateRole,
ValidateDiagFunc: NewRoleValidator([]string{"Admin", "Planner", "Viewer", "Deployer"}),
ExactlyOneOf: []string{"custom_role_id", "role"},
},
"custom_role_id": {
Expand Down Expand Up @@ -90,7 +90,7 @@ func resourceUserProjectAssignmentRead(ctx context.Context, d *schema.ResourceDa
return diag.Errorf("schema resource data serialization failed: %v", err)
}

if client.IsBuiltinProjectRole(assignment.Role) {
if client.IsBuiltinRole(assignment.Role) {
d.Set("role", assignment.Role)
} else {
d.Set("custom_role_id", assignment.Role)
Expand Down
4 changes: 2 additions & 2 deletions env0/resource_user_project_assignment_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,8 @@ func TestUnitUserProjectAssignmentResource(t *testing.T) {
userId := "uid"
projectId := "pid"
id := "id"
role := client.Deployer
updatedRole := client.Viewer
role := client.DeployerRole
updatedRole := client.ViewerRole
customRole := "id1"
updatedCustomRole := "id2"

Expand Down
38 changes: 26 additions & 12 deletions env0/validators.go
Original file line number Diff line number Diff line change
Expand Up @@ -55,18 +55,6 @@ func ValidateRetries(i interface{}, path cty.Path) diag.Diagnostics {
return nil
}

func ValidateRole(i interface{}, path cty.Path) diag.Diagnostics {
role := i.(string)
if role == "" ||
role != client.Admin &&
role != client.Deployer &&
role != client.Viewer &&
role != client.Planner {
return diag.Errorf("must be one of [Admin, Deployer, Viewer, Planner], got: %v", role)
}
return nil
}

func NewRegexValidator(r string) schema.SchemaValidateDiagFunc {
cr := regexp.MustCompile(r)

Expand Down Expand Up @@ -129,3 +117,29 @@ func ValidateTtl(i interface{}, path cty.Path) diag.Diagnostics {

return nil
}

func NewRoleValidator(supportedBuiltInRoles []string) schema.SchemaValidateDiagFunc {
return func(i interface{}, p cty.Path) diag.Diagnostics {
role := i.(string)

if role == "" {
return diag.Errorf("may not be empty")
}

if client.IsCustomRole(role) {
// Custom role.
return nil
}

// Built-in role. Verify it's in the supported list.
for _, supportedRole := range supportedBuiltInRoles {
if role == supportedRole {
// supported.
return nil
}
}

// not supported.
return diag.Errorf("the following built-in role '%s' is not supported for this resource, must be one of %s", role, "["+strings.Join(supportedBuiltInRoles, ",")+"]")
}
}
6 changes: 3 additions & 3 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -71,11 +71,11 @@ require (
github.com/vmihailenco/msgpack/v5 v5.4.1 // indirect
github.com/vmihailenco/tagparser/v2 v2.0.0 // indirect
github.com/zclconf/go-cty v1.14.1 // indirect
golang.org/x/crypto v0.17.0 // indirect
golang.org/x/crypto v0.21.0 // indirect
golang.org/x/exp v0.0.0-20230811145659-89c5cff77bcb // indirect
golang.org/x/mod v0.14.0 // indirect
golang.org/x/net v0.19.0 // indirect
golang.org/x/sys v0.15.0 // indirect
golang.org/x/net v0.23.0 // indirect
golang.org/x/sys v0.18.0 // indirect
golang.org/x/text v0.14.0 // indirect
google.golang.org/appengine v1.6.8 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20231212172506-995d672761c0 // indirect
Expand Down
12 changes: 6 additions & 6 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -247,8 +247,8 @@ golang.org/x/crypto v0.3.0/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4
golang.org/x/crypto v0.3.1-0.20221117191849-2c476679df9a/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4=
golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU=
golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4=
golang.org/x/crypto v0.17.0 h1:r8bRNjWL3GshPW3gkd+RpvzWrZAwPS49OmTGZ/uhM4k=
golang.org/x/crypto v0.17.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4=
golang.org/x/crypto v0.21.0 h1:X31++rzVUdKhX5sWmSOFZxx8UW/ldWx55cbf08iNAMA=
golang.org/x/crypto v0.21.0/go.mod h1:0BP7YvVV9gBbVKyeTG0Gyn+gZm94bibOW5BjDEYAOMs=
golang.org/x/exp v0.0.0-20230811145659-89c5cff77bcb h1:mIKbk8weKhSeLH2GmUTrvx8CjkyJmnU1wFmg59CUjFA=
golang.org/x/exp v0.0.0-20230811145659-89c5cff77bcb/go.mod h1:FXUEEKJgO7OQYeo8N01OfiKP8RXMtf6e8aTskBGqWdc=
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
Expand All @@ -269,8 +269,8 @@ golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc=
golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE=
golang.org/x/net v0.19.0 h1:zTwKpTd2XuCqf8huc7Fo2iSy+4RHPd10s4KzeTnVr1c=
golang.org/x/net v0.19.0/go.mod h1:CfAk/cbD4CthTvqiEl8NpboMuiuOYsAr/7NOjZJtv1U=
golang.org/x/net v0.23.0 h1:7EYJ93RZ9vYSZAIb2x3lnuvqO5zneoD6IvWjuhfxjTs=
golang.org/x/net v0.23.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
Expand Down Expand Up @@ -302,8 +302,8 @@ golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.15.0 h1:h48lPFYpsTvQJZF4EKyI4aLHaev3CxivZmv7yZig9pc=
golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.18.0 h1:DBdB3niSjOA/O0blCZBqDefyWNYveAYMNF1Wum0DYQ4=
golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc=
Expand Down

0 comments on commit 2a04559

Please sign in to comment.