Skip to content

Commit

Permalink
Only fetch the list of Frontegg roles once per TF provider invocation (
Browse files Browse the repository at this point in the history
…#595)

* Only fetch the list of Frontegg roles once per TF provider invocation

* Fix failing GetSCIMGroupByID requests
  • Loading branch information
bobbyiliev authored Jul 8, 2024
1 parent 5e7c16b commit b8b9ccf
Show file tree
Hide file tree
Showing 14 changed files with 59 additions and 54 deletions.
2 changes: 1 addition & 1 deletion pkg/frontegg/scim_groups.go
Original file line number Diff line number Diff line change
Expand Up @@ -131,7 +131,7 @@ func DeleteSCIMGroup(ctx context.Context, client *clients.FronteggClient, groupI

// GetSCIMGroupByID fetches a single SCIM group by its ID.
func GetSCIMGroupByID(ctx context.Context, client *clients.FronteggClient, groupID string) (*ScimGroup, error) {
endpoint := fmt.Sprintf("%s%s/%s?_groupsRelations=rolesAndUsers", client.Endpoint, SCIMGroupsApiPathV1, groupID)
endpoint := fmt.Sprintf("%s%s/%s/?_groupsRelations=rolesAndUsers", client.Endpoint, SCIMGroupsApiPathV1, groupID)
resp, err := doRequest(ctx, client, "GET", endpoint, nil)
if err != nil {
return nil, err
Expand Down
2 changes: 1 addition & 1 deletion pkg/frontegg/sso_default_roles.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ type FronteggRole struct {
}

// ListRoles fetches roles from the Frontegg API and returns a map of role names to their IDs.
func ListSSORoles(ctx context.Context, client *clients.FronteggClient) (map[string]string, error) {
func ListFronteggRoles(ctx context.Context, client *clients.FronteggClient) (map[string]string, error) {
endpoint := fmt.Sprintf("%s%s", client.Endpoint, SSORolesApiPathV2)
resp, err := doRequest(ctx, client, "GET", endpoint, nil)
if err != nil {
Expand Down
4 changes: 2 additions & 2 deletions pkg/frontegg/sso_default_roles_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import (
"github.com/stretchr/testify/assert"
)

func TestListSSORolesSuccess(t *testing.T) {
func TestListFronteggRolesSuccess(t *testing.T) {
assert := assert.New(t)

rolesResponse := FronteggRolesResponse{
Expand All @@ -33,7 +33,7 @@ func TestListSSORolesSuccess(t *testing.T) {
HTTPClient: mockServer.Client(),
}

roles, err := ListSSORoles(context.Background(), client)
roles, err := ListFronteggRoles(context.Background(), client)
assert.NoError(err)
assert.Equal(2, len(roles))
assert.Equal("role-id-1", roles["Admin"])
Expand Down
8 changes: 8 additions & 0 deletions pkg/provider/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (

"github.com/MaterializeInc/terraform-provider-materialize/pkg/clients"
"github.com/MaterializeInc/terraform-provider-materialize/pkg/datasources"
"github.com/MaterializeInc/terraform-provider-materialize/pkg/frontegg"
"github.com/MaterializeInc/terraform-provider-materialize/pkg/resources"
"github.com/MaterializeInc/terraform-provider-materialize/pkg/utils"

Expand Down Expand Up @@ -229,13 +230,20 @@ func providerConfigure(ctx context.Context, d *schema.ResourceData, version stri

log.Printf("[DEBUG] Initialized DB clients for regions: %v\n", dbClients)

// Fetch Frontegg roles and store them in the provider meta
fronteggRoles, err := frontegg.ListFronteggRoles(ctx, fronteggClient)
if err != nil {
return nil, diag.Errorf("Unable to fetch Frontegg roles: %s", err)
}

// Construct and return the provider meta with all clients initialized.
providerMeta := &utils.ProviderMeta{
DB: dbClients,
Frontegg: fronteggClient,
CloudAPI: cloudAPIClient,
DefaultRegion: clients.Region(defaultRegion),
RegionsEnabled: regionsEnabled,
FronteggRoles: fronteggRoles,
}

return providerMeta, nil
Expand Down
17 changes: 4 additions & 13 deletions pkg/resources/resource_app_password.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ package resources

import (
"context"
"fmt"
"sort"
"time"

Expand Down Expand Up @@ -124,12 +123,8 @@ func appPasswordCreate(ctx context.Context, d *schema.ResourceData, meta interfa
return diag.Errorf("at least one role is required for a service-type app password")
}

// TODO: only fetch the list of SSO roles once per TF provider
// invocation.
roleMap, err := frontegg.ListSSORoles(ctx, client)
if err != nil {
return diag.FromErr(fmt.Errorf("error fetching roles: %s", err))
}
roleMap := providerMeta.FronteggRoles

var roleIDs []string
for _, role := range roles {
if roleID, ok := roleMap[role]; ok {
Expand Down Expand Up @@ -201,12 +196,8 @@ func appPasswordRead(ctx context.Context, d *schema.ResourceData, meta interface
// We don't update secret and password because those fields can only be
// determined at creation time.
} else {
// TODO: only fetch the list of SSO roles once per TF provider
// invocation.
roleMap, err := frontegg.ListSSORoles(ctx, client)
if err != nil {
return diag.FromErr(fmt.Errorf("error fetching roles: %s", err))
}
roleMap := providerMeta.FronteggRoles

roleReverseMap := make(map[string]string)
for roleName, roleId := range roleMap {
roleReverseMap[roleId] = roleName
Expand Down
14 changes: 5 additions & 9 deletions pkg/resources/resource_scim_group_roles.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ import (
"log"
"strings"

"github.com/MaterializeInc/terraform-provider-materialize/pkg/clients"
"github.com/MaterializeInc/terraform-provider-materialize/pkg/frontegg"
"github.com/MaterializeInc/terraform-provider-materialize/pkg/utils"
"github.com/hashicorp/terraform-plugin-sdk/v2/diag"
Expand Down Expand Up @@ -54,7 +53,7 @@ func scimGroupRoleCreate(ctx context.Context, d *schema.ResourceData, meta inter
}
client := providerMeta.Frontegg

roleIDs, err := getRoleIDsByName(ctx, client, roleNames)
roleIDs, err := getRoleIDsByName(ctx, providerMeta, roleNames)
if err != nil {
return diag.FromErr(fmt.Errorf("error getting role IDs: %s", err))
}
Expand Down Expand Up @@ -141,7 +140,7 @@ func scimGroupRoleUpdate(ctx context.Context, d *schema.ResourceData, meta inter

// Add the new roles to the group
newRoleNames := expandStringSet(d.Get("roles").(*schema.Set))
newRoleIDs, err := getRoleIDsByName(ctx, client, newRoleNames)
newRoleIDs, err := getRoleIDsByName(ctx, providerMeta, newRoleNames)
if err != nil {
return diag.FromErr(fmt.Errorf("error getting role IDs: %s", err))
}
Expand Down Expand Up @@ -169,7 +168,7 @@ func scimGroupRoleDelete(ctx context.Context, d *schema.ResourceData, meta inter
}
client := providerMeta.Frontegg

roleIDs, err := getRoleIDsByName(ctx, client, roleNames)
roleIDs, err := getRoleIDsByName(ctx, providerMeta, roleNames)
if err != nil {
return diag.FromErr(fmt.Errorf("error getting role IDs: %s", err))
}
Expand All @@ -186,11 +185,8 @@ func scimGroupRoleDelete(ctx context.Context, d *schema.ResourceData, meta inter
}

// Helper function to expand a set of role names to a list of role IDs
func getRoleIDsByName(ctx context.Context, client *clients.FronteggClient, roleNames []string) ([]string, error) {
roleMap, err := frontegg.ListSSORoles(ctx, client)
if err != nil {
return nil, err
}
func getRoleIDsByName(ctx context.Context, providerMeta *utils.ProviderMeta, roleNames []string) ([]string, error) {
roleMap := providerMeta.FronteggRoles

var roleIDs []string
for _, roleName := range roleNames {
Expand Down
4 changes: 4 additions & 0 deletions pkg/resources/resource_scim_group_roles_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,10 @@ func TestScimGroupRoleResourceCreate(t *testing.T) {

providerMeta := &utils.ProviderMeta{
Frontegg: client,
FronteggRoles: map[string]string{
"Admin": "1",
"Member": "2",
},
}

if err := scimGroupRoleCreate(context.TODO(), d, providerMeta); err != nil {
Expand Down
10 changes: 2 additions & 8 deletions pkg/resources/resource_sso_default_roles.go
Original file line number Diff line number Diff line change
Expand Up @@ -54,10 +54,7 @@ func ssoDefaultRolesCreateOrUpdate(ctx context.Context, d *schema.ResourceData,
ssoConfigID := d.Get("sso_config_id").(string)
roleNames := convertToStringSlice(d.Get("roles").(*schema.Set).List())

roleMap, err := frontegg.ListSSORoles(ctx, client)
if err != nil {
return diag.FromErr(err)
}
roleMap := providerMeta.FronteggRoles

var roleIDs []string
for _, roleName := range roleNames {
Expand Down Expand Up @@ -95,10 +92,7 @@ func ssoDefaultRolesRead(ctx context.Context, d *schema.ResourceData, meta inter
return diag.FromErr(err)
}

roleMap, err := frontegg.ListSSORoles(ctx, client)
if err != nil {
return diag.FromErr(err)
}
roleMap := providerMeta.FronteggRoles

var roleNames []string
for _, roleID := range roleIDs {
Expand Down
12 changes: 12 additions & 0 deletions pkg/resources/resource_sso_default_roles_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,10 @@ func TestSSODefaultRolesCreateOrUpdate(t *testing.T) {

providerMeta := &utils.ProviderMeta{
Frontegg: client,
FronteggRoles: map[string]string{
"Admin": "1",
"Member": "2",
},
}

// Create a new ResourceData object
Expand Down Expand Up @@ -70,6 +74,10 @@ func TestSSODefaultRolesRead(t *testing.T) {

providerMeta := &utils.ProviderMeta{
Frontegg: client,
FronteggRoles: map[string]string{
"Admin": "1",
"Member": "2",
},
}

// Set the initial state with "sso_config_id" and "roles" as a list of strings
Expand Down Expand Up @@ -110,6 +118,10 @@ func TestSSODefaultRolesDelete(t *testing.T) {

providerMeta := &utils.ProviderMeta{
Frontegg: client,
FronteggRoles: map[string]string{
"Admin": "1",
"Member": "2",
},
}

// Set the initial state with "sso_config_id" and "roles" as a list of strings
Expand Down
17 changes: 3 additions & 14 deletions pkg/resources/resource_sso_group_mapping.go
Original file line number Diff line number Diff line change
Expand Up @@ -63,11 +63,7 @@ func ssoGroupMappingCreate(ctx context.Context, d *schema.ResourceData, meta int
group := d.Get("group").(string)
roleNames := convertToStringSlice(d.Get("roles").(*schema.Set).List())

// Fetch role IDs based on role names.
roleMap, err := frontegg.ListSSORoles(ctx, client)
if err != nil {
return diag.FromErr(fmt.Errorf("error fetching roles: %s", err))
}
roleMap := providerMeta.FronteggRoles

var roleIDs []string
for _, roleName := range roleNames {
Expand Down Expand Up @@ -106,11 +102,7 @@ func ssoGroupMappingRead(ctx context.Context, d *schema.ResourceData, meta inter
return diag.FromErr(err)
}

// Fetch role mappings from the API.
roleMap, err := frontegg.ListSSORoles(ctx, client)
if err != nil {
return diag.FromErr(fmt.Errorf("error fetching roles: %s", err))
}
roleMap := providerMeta.FronteggRoles

for _, group := range *groups {
if group.ID == groupID {
Expand Down Expand Up @@ -153,10 +145,7 @@ func ssoGroupMappingUpdate(ctx context.Context, d *schema.ResourceData, meta int
groupID := d.Id()
roleNames := convertToStringSlice(d.Get("roles").(*schema.Set).List())

roleMap, err := frontegg.ListSSORoles(ctx, client)
if err != nil {
return diag.FromErr(fmt.Errorf("error fetching roles: %s", err))
}
roleMap := providerMeta.FronteggRoles

var roleIDs []string
for _, roleName := range roleNames {
Expand Down
8 changes: 8 additions & 0 deletions pkg/resources/resource_sso_group_mapping_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,10 @@ func TestSSORoleGroupMappingCreate(t *testing.T) {

providerMeta := &utils.ProviderMeta{
Frontegg: client,
FronteggRoles: map[string]string{
"Admin": "1",
"Member": "2",
},
}

// Set the expected values for sso_config_id, group, and roles
Expand Down Expand Up @@ -96,6 +100,10 @@ func TestSSORoleGroupMappingUpdate(t *testing.T) {

providerMeta := &utils.ProviderMeta{
Frontegg: client,
FronteggRoles: map[string]string{
"Admin": "1",
"Member": "2",
},
}

// Set the initial state with "group" and "roles"
Expand Down
7 changes: 1 addition & 6 deletions pkg/resources/resource_user.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ package resources

import (
"context"
"fmt"
"strings"

"github.com/MaterializeInc/terraform-provider-materialize/pkg/frontegg"
Expand Down Expand Up @@ -82,11 +81,7 @@ func userCreate(ctx context.Context, d *schema.ResourceData, meta interface{}) d
}
}

// Fetch role IDs based on role names.
roleMap, err := frontegg.ListSSORoles(ctx, client)
if err != nil {
return diag.FromErr(fmt.Errorf("error fetching roles: %s", err))
}
roleMap := providerMeta.FronteggRoles

var roleIDs []string
for _, roleName := range roleNames {
Expand Down
4 changes: 4 additions & 0 deletions pkg/testhelpers/helpers.go
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,10 @@ func WithMockProviderMeta(t *testing.T, f func(*utils.ProviderMeta, sqlmock.Sqlm
TokenExpiry: time.Date(9999, 1, 1, 0, 0, 0, 0, time.UTC),
},
CloudAPI: nil,
FronteggRoles: map[string]string{
"Admin": "1",
"Member": "2",
},
}

mock.MatchExpectationsInOrder(true)
Expand Down
4 changes: 4 additions & 0 deletions pkg/utils/provider_meta.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,10 @@ type ProviderMeta struct {
// RegionsEnabled is a map indicating which regions are currently enabled
// for use. This can be used to quickly check the availability in different regions.
RegionsEnabled map[clients.Region]bool

// Frontegg Roles is a map that associates each Frontegg role with its corresponding ID.
// This is used to map role names to role IDs when creating/updating users.
FronteggRoles map[string]string
}

var DefaultRegion string
Expand Down

0 comments on commit b8b9ccf

Please sign in to comment.