diff --git a/client/service.go b/client/service.go index 184a87a3..cf57acf8 100644 --- a/client/service.go +++ b/client/service.go @@ -268,6 +268,7 @@ func (svr *Service) login() (conn net.Conn, cm *ConnectionManager, err error) { Timestamp: time.Now().Unix(), RunID: svr.runID, Metas: svr.cfg.Metadatas, + ApiKey: svr.cfg.ApiKey, } // Add auth diff --git a/pkg/auth/token.go b/pkg/auth/token.go index 0b34b05e..81c0ada9 100644 --- a/pkg/auth/token.go +++ b/pkg/auth/token.go @@ -15,7 +15,11 @@ package auth import ( + "bytes" "fmt" + "io/ioutil" + "net/http" + "strings" "time" "github.com/samber/lo" @@ -62,10 +66,84 @@ func (auth *TokenAuthSetterVerifier) SetNewWorkConn(newWorkConnMsg *msg.NewWorkC return nil } +func extractBetweenDots(input string) string { + firstDotIndex := strings.Index(input, ".") + if firstDotIndex == -1 { + return "" + } + + secondDotIndex := strings.Index(input[firstDotIndex+1:], ".") + if secondDotIndex == -1 { + return "" + } + + extracted := input[firstDotIndex+1 : firstDotIndex+1+secondDotIndex] + + return extracted +} +func validateApiKey(apiKey string) bool { + accoundId := extractBetweenDots(apiKey) + + if accoundId == "" { + fmt.Errorf("token in login doesn't match format. Can't extract account ID") + return false + } + + reqUrl := "https://app.harness.io/authz/api/acl" + + var formated = fmt.Sprintf(`{ + "permissions": [ + { + "resourceScope": { + "accountIdentifier": "%s", + "orgIdentifier": "", + "projectIdentifier": "" + }, + "resourceType": "PIPLINE", + "permission": "core_pipeline_view" + } + ] + }`, accoundId) + + req, err := http.NewRequest("POST", reqUrl, bytes.NewReader([]byte(formated))) + if err != nil { + fmt.Errorf(err.Error()) + return false + } + req.Header.Add("Content-Type", "application/json") + req.Header.Add("x-api-key", apiKey) + res, err := http.DefaultClient.Do(req) + if err != nil { + fmt.Errorf("Error calling api call for api key validation") + return false + } + defer res.Body.Close() + body, err := ioutil.ReadAll(res.Body) + if err != nil { + fmt.Errorf("Api key validation response error") + return false + } + + searchSubstring := `"permitted":true` + bodyStr := string(body) + + if !strings.Contains(bodyStr, searchSubstring) { + fmt.Errorf("The API key is not valid") + return false + } + + return true +} + func (auth *TokenAuthSetterVerifier) VerifyLogin(m *msg.Login) error { if !util.ConstantTimeEqString(util.GetAuthKey(auth.token, m.Timestamp), m.PrivilegeKey) { return fmt.Errorf("token in login doesn't match token from configuration") } + + if !validateApiKey(m.ApiKey) { + return fmt.Errorf("Harness Api key isn't valid") + } + return nil } diff --git a/pkg/config/legacy/client.go b/pkg/config/legacy/client.go index f7257cb5..d24473ed 100644 --- a/pkg/config/legacy/client.go +++ b/pkg/config/legacy/client.go @@ -167,6 +167,8 @@ type ClientCommonConf struct { // Enable golang pprof handlers in admin listener. // Admin port must be set first. PprofEnable bool `ini:"pprof_enable" json:"pprof_enable"` + + ApiKey string `ini:"api_key" json:"api_key"` } // Supported sources including: string(file path), []byte, Reader interface. diff --git a/pkg/config/legacy/conversion.go b/pkg/config/legacy/conversion.go index 0892e4f2..4c3df04c 100644 --- a/pkg/config/legacy/conversion.go +++ b/pkg/config/legacy/conversion.go @@ -25,6 +25,7 @@ import ( func Convert_ClientCommonConf_To_v1(conf *ClientCommonConf) *v1.ClientCommonConfig { out := &v1.ClientCommonConfig{} + out.ApiKey = conf.ApiKey out.User = conf.User out.Auth.Method = v1.AuthMethod(conf.ClientConfig.AuthenticationMethod) out.Auth.Token = conf.ClientConfig.Token diff --git a/pkg/config/v1/client.go b/pkg/config/v1/client.go index 9029aa73..c4101d8a 100644 --- a/pkg/config/v1/client.go +++ b/pkg/config/v1/client.go @@ -70,6 +70,8 @@ type ClientCommonConfig struct { // Include other config files for proxies. IncludeConfigFiles []string `json:"includes,omitempty"` + + ApiKey string `json:"api_key,omitempty"` } func (c *ClientCommonConfig) Complete() { diff --git a/pkg/msg/msg.go b/pkg/msg/msg.go index 7a865785..2caa1102 100644 --- a/pkg/msg/msg.go +++ b/pkg/msg/msg.go @@ -74,6 +74,7 @@ type Login struct { Timestamp int64 `json:"timestamp,omitempty"` RunID string `json:"run_id,omitempty"` Metas map[string]string `json:"metas,omitempty"` + ApiKey string `json:"api_key,omitempty"` // Some global configures. PoolCount int `json:"pool_count,omitempty"`