mirror of
https://github.com/coder/coder.git
synced 2025-07-12 00:14:10 +00:00
fix: defensively handle nil maps and slices in marshaling (#18418)
Adds a custom marshaler to handle some cases where nils were being marshaled to nulls, causing the web UI to throw an error. --------- Co-authored-by: Steven Masley <stevenmasley@gmail.com>
This commit is contained in:
@ -274,6 +274,17 @@ func (s *GroupSyncSettings) String() string {
|
||||
return runtimeconfig.JSONString(s)
|
||||
}
|
||||
|
||||
func (s *GroupSyncSettings) MarshalJSON() ([]byte, error) {
|
||||
if s.Mapping == nil {
|
||||
s.Mapping = make(map[string][]uuid.UUID)
|
||||
}
|
||||
|
||||
// Aliasing the struct to avoid infinite recursion when calling json.Marshal
|
||||
// on the struct itself.
|
||||
type Alias GroupSyncSettings
|
||||
return json.Marshal(&struct{ *Alias }{Alias: (*Alias)(s)})
|
||||
}
|
||||
|
||||
type ExpectedGroup struct {
|
||||
OrganizationID uuid.UUID
|
||||
GroupID *uuid.UUID
|
||||
|
@ -2,6 +2,7 @@ package idpsync_test
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"regexp"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
@ -9,6 +10,49 @@ import (
|
||||
"github.com/coder/coder/v2/coderd/idpsync"
|
||||
)
|
||||
|
||||
// TestMarshalJSONEmpty ensures no empty maps are marshaled as `null` in JSON.
|
||||
func TestMarshalJSONEmpty(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
t.Run("Group", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
output, err := json.Marshal(&idpsync.GroupSyncSettings{
|
||||
RegexFilter: regexp.MustCompile(".*"),
|
||||
})
|
||||
require.NoError(t, err, "marshal empty group settings")
|
||||
require.NotContains(t, string(output), "null")
|
||||
|
||||
require.JSONEq(t,
|
||||
`{"field":"","mapping":{},"regex_filter":".*","auto_create_missing_groups":false}`,
|
||||
string(output))
|
||||
})
|
||||
|
||||
t.Run("Role", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
output, err := json.Marshal(&idpsync.RoleSyncSettings{})
|
||||
require.NoError(t, err, "marshal empty group settings")
|
||||
require.NotContains(t, string(output), "null")
|
||||
|
||||
require.JSONEq(t,
|
||||
`{"field":"","mapping":{}}`,
|
||||
string(output))
|
||||
})
|
||||
|
||||
t.Run("Organization", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
output, err := json.Marshal(&idpsync.OrganizationSyncSettings{})
|
||||
require.NoError(t, err, "marshal empty group settings")
|
||||
require.NotContains(t, string(output), "null")
|
||||
|
||||
require.JSONEq(t,
|
||||
`{"field":"","mapping":{},"assign_default":false}`,
|
||||
string(output))
|
||||
})
|
||||
}
|
||||
|
||||
func TestParseStringSliceClaim(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
|
@ -234,6 +234,17 @@ func (s *OrganizationSyncSettings) String() string {
|
||||
return runtimeconfig.JSONString(s)
|
||||
}
|
||||
|
||||
func (s *OrganizationSyncSettings) MarshalJSON() ([]byte, error) {
|
||||
if s.Mapping == nil {
|
||||
s.Mapping = make(map[string][]uuid.UUID)
|
||||
}
|
||||
|
||||
// Aliasing the struct to avoid infinite recursion when calling json.Marshal
|
||||
// on the struct itself.
|
||||
type Alias OrganizationSyncSettings
|
||||
return json.Marshal(&struct{ *Alias }{Alias: (*Alias)(s)})
|
||||
}
|
||||
|
||||
// ParseClaims will parse the claims and return the list of organizations the user
|
||||
// should sync to.
|
||||
func (s *OrganizationSyncSettings) ParseClaims(ctx context.Context, db database.Store, mergedClaims jwt.MapClaims) ([]uuid.UUID, error) {
|
||||
|
@ -291,3 +291,14 @@ func (s *RoleSyncSettings) String() string {
|
||||
}
|
||||
return runtimeconfig.JSONString(s)
|
||||
}
|
||||
|
||||
func (s *RoleSyncSettings) MarshalJSON() ([]byte, error) {
|
||||
if s.Mapping == nil {
|
||||
s.Mapping = make(map[string][]string)
|
||||
}
|
||||
|
||||
// Aliasing the struct to avoid infinite recursion when calling json.Marshal
|
||||
// on the struct itself.
|
||||
type Alias RoleSyncSettings
|
||||
return json.Marshal(&struct{ *Alias }{Alias: (*Alias)(s)})
|
||||
}
|
||||
|
@ -836,6 +836,9 @@ func (api *API) idpSyncClaimFieldValues(orgID uuid.UUID, rw http.ResponseWriter,
|
||||
httpapi.InternalServerError(rw, err)
|
||||
return
|
||||
}
|
||||
if fieldValues == nil {
|
||||
fieldValues = []string{}
|
||||
}
|
||||
|
||||
httpapi.Write(ctx, rw, http.StatusOK, fieldValues)
|
||||
}
|
||||
|
Reference in New Issue
Block a user