Skip to content

Commit

Permalink
Validate API specs before loading them (TykTechnologies#2515)
Browse files Browse the repository at this point in the history
This adds  validation checks before loading,  only tcp spec check
is implemented to ensure listening port is provided to avod
crashing the gateway.

Other checks can be added in different PR as it is beyond the scope
of this PR

Fixes TykTechnologies#2511
Fixes TykTechnologies#2480
  • Loading branch information
gernest authored and buger committed Sep 24, 2019
1 parent 69f1034 commit 8249eb4
Show file tree
Hide file tree
Showing 3 changed files with 56 additions and 11 deletions.
23 changes: 23 additions & 0 deletions gateway/api_definition.go
Original file line number Diff line number Diff line change
Expand Up @@ -210,6 +210,29 @@ func (s *APISpec) Release() {
// release all other resources associated with spec
}

// Validate returns nil if s is a valid spec and an error stating why the spec is not valid.
func (s *APISpec) Validate() error {
// For tcp services we need to make sure we can bind to the port.
switch s.Protocol {
case "tcp", "tls":
return s.validateTCP()
default:
return s.validateHTTP()
}
}

func (s *APISpec) validateTCP() error {
if s.ListenPort == 0 {
return errors.New("missing listening port")
}
return nil
}

func (s *APISpec) validateHTTP() error {
// NOOP
return nil
}

// APIDefinitionLoader will load an Api definition from a storage
// system.
type APIDefinitionLoader struct{}
Expand Down
15 changes: 15 additions & 0 deletions gateway/proxy_muxer_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,21 @@ func TestTCPDial_with_service_discovery(t *testing.T) {
}
}

func TestTCP_missing_port(t *testing.T) {
ts := StartTest()
defer ts.Close()
BuildAndLoadAPI(func(spec *APISpec) {
spec.Name = "no -listen-port"
spec.Protocol = "tcp"
})
apisMu.RLock()
n := len(apiSpecs)
apisMu.RUnlock()
if n != 0 {
t.Errorf("expected 0 apis to be loaded got %d", n)
}
}

func TestCheckPortWhiteList(t *testing.T) {
base := config.Global()
cases := []struct {
Expand Down
29 changes: 18 additions & 11 deletions gateway/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -268,10 +268,9 @@ func buildConnStr(resource string) string {

func syncAPISpecs() (int, error) {
loader := APIDefinitionLoader{}

apisMu.Lock()
defer apisMu.Unlock()

var s []*APISpec
if config.Global().UseDBAppConfigs {
connStr := buildConnStr("/system/apis")
tmpSpecs, err := loader.FromDashboardService(connStr, config.Global().NodeSecret)
Expand All @@ -280,35 +279,43 @@ func syncAPISpecs() (int, error) {
return 0, err
}

apiSpecs = tmpSpecs
s = tmpSpecs

mainLog.Debug("Downloading API Configurations from Dashboard Service")
} else if config.Global().SlaveOptions.UseRPC {
mainLog.Debug("Using RPC Configuration")

var err error
apiSpecs, err = loader.FromRPC(config.Global().SlaveOptions.RPCKey)
s, err = loader.FromRPC(config.Global().SlaveOptions.RPCKey)
if err != nil {
return 0, err
}
} else {
apiSpecs = loader.FromDir(config.Global().AppPath)
s = loader.FromDir(config.Global().AppPath)
}

mainLog.Printf("Detected %v APIs", len(apiSpecs))
mainLog.Printf("Detected %v APIs", len(s))

if config.Global().AuthOverride.ForceAuthProvider {
for i := range apiSpecs {
apiSpecs[i].AuthProvider = config.Global().AuthOverride.AuthProvider
for i := range s {
s[i].AuthProvider = config.Global().AuthOverride.AuthProvider
}
}

if config.Global().AuthOverride.ForceSessionProvider {
for i := range apiSpecs {
apiSpecs[i].SessionProvider = config.Global().AuthOverride.SessionProvider
for i := range s {
s[i].SessionProvider = config.Global().AuthOverride.SessionProvider
}
}

var filter []*APISpec
for _, v := range s {
if err := v.Validate(); err != nil {
mainLog.Infof("Skipping loading spec:%q because it failed validation with error:%v", v.Name, err)
continue
}
filter = append(filter, v)
}
apiSpecs = filter
return len(apiSpecs), nil
}

Expand Down

0 comments on commit 8249eb4

Please sign in to comment.