Skip to content

Commit

Permalink
fix: verify jwt by default when deploying without config
Browse files Browse the repository at this point in the history
  • Loading branch information
sweatybridge committed Feb 17, 2025
1 parent bb550d0 commit 03249d7
Show file tree
Hide file tree
Showing 8 changed files with 43 additions and 55 deletions.
9 changes: 6 additions & 3 deletions internal/functions/deploy/deploy.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ import (
"github.com/spf13/afero"
"github.com/supabase/cli/internal/utils"
"github.com/supabase/cli/internal/utils/flags"
"github.com/supabase/cli/pkg/cast"
"github.com/supabase/cli/pkg/config"
"github.com/supabase/cli/pkg/function"
)
Expand Down Expand Up @@ -87,7 +86,11 @@ func GetFunctionConfig(slugs []string, importMapPath string, noVerifyJWT *bool,
}
functionConfig := make(config.FunctionConfig, len(slugs))
for _, name := range slugs {
function := utils.Config.Functions[name]
function, ok := utils.Config.Functions[name]
if !ok {
function.Enabled = true
function.VerifyJWT = true
}
// Precedence order: flag > config > fallback
functionDir := filepath.Join(utils.FunctionsDir, name)
if len(function.Entrypoint) == 0 {
Expand All @@ -112,7 +115,7 @@ func GetFunctionConfig(slugs []string, importMapPath string, noVerifyJWT *bool,
}
}
if noVerifyJWT != nil {
function.VerifyJWT = cast.Ptr(!*noVerifyJWT)
function.VerifyJWT = !*noVerifyJWT
}
functionConfig[name] = function
}
Expand Down
4 changes: 2 additions & 2 deletions internal/functions/deploy/upload.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ func deploy(ctx context.Context, functionConfig config.FunctionConfig, fsys afer
bundleOnly := len(functionConfig) > 1
var toUpdate []api.BulkUpdateFunctionBody
for slug, fc := range functionConfig {
if !fc.IsEnabled() {
if !fc.Enabled {
fmt.Fprintln(os.Stderr, "Skipped deploying Function:", slug)
continue
}
Expand All @@ -39,7 +39,7 @@ func deploy(ctx context.Context, functionConfig config.FunctionConfig, fsys afer
Name: &slug,
EntrypointPath: fc.Entrypoint,
ImportMapPath: &fc.ImportMap,
VerifyJwt: fc.VerifyJWT,
VerifyJwt: &fc.VerifyJWT,
}
if len(fc.StaticFiles) > 0 {
meta.StaticPatterns = &fc.StaticFiles
Expand Down
2 changes: 1 addition & 1 deletion internal/functions/deploy/upload_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -195,7 +195,7 @@ func TestDeployAll(t *testing.T) {

t.Run("throws error on network failure", func(t *testing.T) {
errNetwork := errors.New("network")
c := config.FunctionConfig{"demo": {}}
c := config.FunctionConfig{"demo": {Enabled: true}}
// Setup in-memory fs
fsys := afero.NewMemMapFs()
// Setup mock api
Expand Down
2 changes: 1 addition & 1 deletion internal/functions/serve/serve.go
Original file line number Diff line number Diff line change
Expand Up @@ -218,7 +218,7 @@ func populatePerFunctionConfigs(cwd, importMapPath string, noVerifyJWT *bool, fs
}
binds := []string{}
for slug, fc := range functionsConfig {
if !fc.IsEnabled() {
if !fc.Enabled {
fmt.Fprintln(os.Stderr, "Skipped serving Function:", slug)
continue
}
Expand Down
22 changes: 12 additions & 10 deletions pkg/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -201,8 +201,8 @@ type (
FunctionConfig map[string]function

function struct {
Enabled *bool `toml:"enabled" json:"-"`
VerifyJWT *bool `toml:"verify_jwt" json:"verifyJWT"`
Enabled bool `toml:"enabled" json:"-"`
VerifyJWT bool `toml:"verify_jwt" json:"verifyJWT"`
ImportMap string `toml:"import_map" json:"importMapPath,omitempty"`
Entrypoint string `toml:"entrypoint" json:"entrypointPath,omitempty"`
StaticFiles []string `toml:"static_files" json:"staticFiles,omitempty"`
Expand Down Expand Up @@ -236,11 +236,6 @@ type (
}
)

func (f function) IsEnabled() bool {
// If Enabled is not defined, or defined and set to true
return f.Enabled == nil || *f.Enabled
}

func (a *auth) Clone() auth {
copy := *a
if copy.Captcha != nil {
Expand Down Expand Up @@ -456,10 +451,17 @@ func (c *config) loadFromReader(v *viper.Viper, r io.Reader) error {
v.Set("project_id", baseId)
}
}
// Manually parse [functions.*] to empty struct for backwards compatibility
// Set default values for [functions.*] when config struct is empty
for key, value := range v.GetStringMap("functions") {
if m, ok := value.(map[string]any); ok && len(m) == 0 {
v.Set("functions."+key, function{})
if _, ok := value.(map[string]any); !ok {
// Leave validation to decode hook
continue
}
if k := fmt.Sprintf("functions.%s.enabled", key); !v.IsSet(k) {
v.Set(k, true)
}
if k := fmt.Sprintf("functions.%s.verify_jwt", key); !v.IsSet(k) {
v.Set(k, true)
}
}
if err := v.UnmarshalExact(c, func(dc *mapstructure.DecoderConfig) {
Expand Down
17 changes: 9 additions & 8 deletions pkg/config/config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -319,6 +319,7 @@ func TestLoadSeedPaths(t *testing.T) {
"supabase/seeds/another.sql",
}, config.SqlPaths)
})

t.Run("returns seed files matching patterns skip duplicates", func(t *testing.T) {
// Setup in-memory fs
fsys := fs.MapFS{
Expand Down Expand Up @@ -454,9 +455,9 @@ func TestLoadFunctionErrorMessageParsing(t *testing.T) {
// Run test
err := config.Load("", fsys)
// Check error contains both decode errors
assert.Error(t, err)
assert.Contains(t, err.Error(), invalidFunctionsConfigFormat)
assert.ErrorContains(t, err, invalidFunctionsConfigFormat)
})

t.Run("returns error with function slug for invalid non-existent field", func(t *testing.T) {
config := NewConfig()
fsys := fs.MapFS{
Expand All @@ -469,9 +470,9 @@ func TestLoadFunctionErrorMessageParsing(t *testing.T) {
// Run test
err := config.Load("", fsys)
// Check error contains both decode errors
assert.Error(t, err)
assert.Contains(t, err.Error(), "'functions[hello]' has invalid keys: unknown_field")
assert.ErrorContains(t, err, "* 'functions[hello]' has invalid keys: unknown_field")
})

t.Run("returns error with function slug for invalid field value", func(t *testing.T) {
config := NewConfig()
fsys := fs.MapFS{
Expand All @@ -484,9 +485,9 @@ func TestLoadFunctionErrorMessageParsing(t *testing.T) {
// Run test
err := config.Load("", fsys)
// Check error contains both decode errors
assert.Error(t, err)
assert.Contains(t, err.Error(), "cannot parse 'functions[hello].verify_jwt' as bool: strconv.ParseBool: parsing \"not-a-bool\"")
assert.ErrorContains(t, err, `* cannot parse 'functions[hello].verify_jwt' as bool: strconv.ParseBool: parsing "not-a-bool"`)
})

t.Run("returns error for unknown function fields", func(t *testing.T) {
config := NewConfig()
fsys := fs.MapFS{
Expand All @@ -499,7 +500,7 @@ func TestLoadFunctionErrorMessageParsing(t *testing.T) {
}
// Run test
err := config.Load("", fsys)
assert.Error(t, err)
assert.Contains(t, err.Error(), invalidFunctionsConfigFormat)
assert.ErrorContains(t, err, `* 'functions[name]' expected a map, got 'string'`)
assert.ErrorContains(t, err, `* 'functions[verify_jwt]' expected a map, got 'bool'`)
})
}
36 changes: 9 additions & 27 deletions pkg/config/decode_hooks.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,15 +10,6 @@ import (

var envPattern = regexp.MustCompile(`^env\((.*)\)$`)

const invalidFunctionsConfigFormat = `Invalid functions config format. Functions should be configured as:
[functions.<function-name>]
field = value
Example:
[functions.hello]
verify_jwt = true`

// LoadEnvHook is a mapstructure decode hook that loads environment variables
// from strings formatted as env(VAR_NAME).
func LoadEnvHook(f reflect.Kind, t reflect.Kind, data interface{}) (interface{}, error) {
Expand All @@ -34,6 +25,15 @@ func LoadEnvHook(f reflect.Kind, t reflect.Kind, data interface{}) (interface{},
return value, nil
}

const invalidFunctionsConfigFormat = `Invalid functions config format. Functions should be configured as:
[functions.<function-name>]
field = value
Example:
[functions.hello]
verify_jwt = true`

// ValidateFunctionsHook is a mapstructure decode hook that validates the functions config format.
func ValidateFunctionsHook(f reflect.Type, t reflect.Type, data interface{}) (interface{}, error) {
// Only handle FunctionConfig type
Expand All @@ -46,23 +46,5 @@ func ValidateFunctionsHook(f reflect.Type, t reflect.Type, data interface{}) (in
return nil, errors.New(invalidFunctionsConfigFormat)
}

// Check if any fields are defined directly under [functions] instead of [functions.<name>]
if m, ok := data.(map[string]interface{}); ok {
for _, value := range m {
// Skip nil values and empty function configs as they're valid
if value == nil {
continue
}
// If it's already a function type, it's valid
if _, isFunction := value.(function); isFunction {
continue
}
// If the value is not a map, it means it's defined directly under [functions]
if _, isMap := value.(map[string]interface{}); !isMap {
return nil, errors.New(invalidFunctionsConfigFormat)
}
}
}

return data, nil
}
6 changes: 3 additions & 3 deletions pkg/function/batch.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ func (s *EdgeRuntimeAPI) UpsertFunctions(ctx context.Context, functionConfig con
exists[f.Slug] = struct{}{}
}
for slug, function := range functionConfig {
if !function.IsEnabled() {
if !function.Enabled {
fmt.Fprintln(os.Stderr, "Skipped deploying Function:", slug)
continue
}
Expand All @@ -52,7 +52,7 @@ func (s *EdgeRuntimeAPI) UpsertFunctions(ctx context.Context, functionConfig con
upsert := func() error {
if _, ok := exists[slug]; ok {
if resp, err := s.client.V1UpdateAFunctionWithBodyWithResponse(ctx, s.project, slug, &api.V1UpdateAFunctionParams{
VerifyJwt: function.VerifyJWT,
VerifyJwt: &function.VerifyJWT,
ImportMapPath: toFileURL(function.ImportMap),
EntrypointPath: toFileURL(function.Entrypoint),
}, eszipContentType, bytes.NewReader(body.Bytes())); err != nil {
Expand All @@ -64,7 +64,7 @@ func (s *EdgeRuntimeAPI) UpsertFunctions(ctx context.Context, functionConfig con
if resp, err := s.client.V1CreateAFunctionWithBodyWithResponse(ctx, s.project, &api.V1CreateAFunctionParams{
Slug: &slug,
Name: &slug,
VerifyJwt: function.VerifyJWT,
VerifyJwt: &function.VerifyJWT,
ImportMapPath: toFileURL(function.ImportMap),
EntrypointPath: toFileURL(function.Entrypoint),
}, eszipContentType, bytes.NewReader(body.Bytes())); err != nil {
Expand Down

0 comments on commit 03249d7

Please sign in to comment.