Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions core/config/system.go
Original file line number Diff line number Diff line change
Expand Up @@ -289,8 +289,9 @@ type ConfigsConfig struct {
ValidConfigsExtensions []string `config:"valid_config_extensions"`
TLSConfig TLSConfig `config:"tls"` //server or client's certs
ManagerConfig struct {
LocalConfigsRepoPath string `config:"local_configs_repo_path"`
BasicAuth BasicAuth `config:"basic_auth"`
LocalConfigsRepoPath string `config:"local_configs_repo_path"`
BasicAuth BasicAuth `config:"basic_auth"`
AccessToken ucfg.SecretString `config:"access_token" yaml:"access_token"`
} `config:"manager"`
AlwaysRegisterAfterRestart bool `config:"always_register_after_restart"`
AllowGeneratedMetricsTasks bool `config:"allow_generated_metrics_tasks"`
Expand Down
29 changes: 29 additions & 0 deletions core/credential/access_token_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package credential

import "testing"

func TestEncodeDecodeAccessToken(t *testing.T) {
cred := &Credential{
Name: "agent-token",
Type: AccessToken,
Payload: map[string]interface{}{
AccessToken: map[string]interface{}{
"access_token": "mock-token",
},
},
}
cred.SetSecret([]byte("12345678901234567890123456789012"))

if err := cred.Encode(); err != nil {
t.Fatalf("Encode() returned error: %v", err)
}

token, err := cred.DecodeAccessToken()
if err != nil {
t.Fatalf("DecodeAccessToken() returned error: %v", err)
}

if got := token.AccessToken.Get(); got != "mock-token" {
t.Fatalf("expected access token %q, got %q", "mock-token", got)
}
}
35 changes: 30 additions & 5 deletions core/credential/credential.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,14 +31,15 @@ import (
"fmt"
"infini.sh/framework/core/model"
"infini.sh/framework/core/orm"
"infini.sh/framework/lib/go-ucfg"
)

type Credential struct {
orm.ORMObjectBase
Name string `json:"name" elastic_mapping:"name:{type:keyword,copy_to:search_text}"`
Type string `json:"type" elastic_mapping:"type:{type:keyword}"`
Tags []string `json:"tags" elastic_mapping:"category:{type:keyword,copy_to:search_text}"`
Payload map[string]interface{} `json:"payload" elastic_mapping:"payload:{type:object,enabled:false}"`
Name string `json:"name" elastic_mapping:"name:{type:keyword,copy_to:search_text}"`
Type CredentialType `json:"type" elastic_mapping:"type:{type:keyword}"`
Tags []string `json:"tags" elastic_mapping:"category:{type:keyword,copy_to:search_text}"`
Payload map[CredentialType]interface{} `json:"payload" elastic_mapping:"payload:{type:object,enabled:false}"`
Encrypt struct {
Type string `json:"type"`
Params map[string]interface{} `json:"params"`
Expand All @@ -48,6 +49,10 @@ type Credential struct {
Invalid bool `json:"invalid" elastic_mapping:"invalid:{type:boolean}"`
}

type AccessTokenPayload struct {
AccessToken ucfg.SecretString `json:"access_token,omitempty" config:"access_token" yaml:"access_token"`
}

func (cred *Credential) SetSecret(secret []byte) {
cred.secret = secret
}
Expand All @@ -69,6 +74,8 @@ func (cred *Credential) Encode() error {
switch cred.Type {
case BasicAuth:
return encodeBasicAuth(cred)
case AccessToken:
return encodeAccessToken(cred)
default:
return fmt.Errorf("unkonow credential type [%s]", cred.Type)
}
Expand All @@ -86,15 +93,33 @@ func (cred *Credential) DecodeBasicAuth() (*model.BasicAuth, error) {
return nil, fmt.Errorf("unkonow credential type [%s]", cred.Type)
}

func (cred *Credential) DecodeAccessToken() (*AccessTokenPayload, error) {
dv, err := cred.Decode()
if err != nil {
return nil, err
}

if token, ok := dv.(AccessTokenPayload); ok {
return &token, nil
}

return nil, fmt.Errorf("unkonow credential type [%s]", cred.Type)
}

func (cred *Credential) Decode() (interface{}, error) {
switch cred.Type {
case BasicAuth:
return decodeBasicAuth(cred)
case AccessToken:
return decodeAccessToken(cred)
default:
return nil, fmt.Errorf("unkonow credential type [%s]", cred.Type)
}
}

type CredentialType string

const (
BasicAuth string = "basic_auth"
BasicAuth CredentialType = "basic_auth"
AccessToken CredentialType = "access_token"
)
77 changes: 76 additions & 1 deletion core/credential/domain.go
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,13 @@ func InitSecret(ks keystore2.Keystore, secret []byte) error {
return nil
}

func getCredentialSecret(cred *Credential) ([]byte, error) {
if cred != nil && cred.secret != nil {
return cred.secret, nil
}
return GetOrInitSecret()
}

func encodeBasicAuth(cred *Credential) error {
var (
params map[string]interface{}
Expand All @@ -100,7 +107,7 @@ func encodeBasicAuth(cred *Credential) error {
if pwd == "" {
return fmt.Errorf("credential parameters password can not be empty")
}
secret, err := GetOrInitSecret()
secret, err := getCredentialSecret(cred)
if err != nil {
return err
}
Expand All @@ -117,6 +124,38 @@ func encodeBasicAuth(cred *Credential) error {
return nil
}

func encodeAccessToken(cred *Credential) error {
var (
params map[string]interface{}
ok bool
token string
)
if params, ok = cred.Payload[cred.Type].(map[string]interface{}); !ok {
return fmt.Errorf("wrong credential parameters for type [%s], expect a map", cred.Type)
}
if token, ok = params["access_token"].(string); !ok {
return fmt.Errorf("wrong credential parameters access_token for type [%s], expect a string", cred.Type)
}
if token == "" {
return fmt.Errorf("credential parameters access_token can not be empty")
}
secret, err := getCredentialSecret(cred)
if err != nil {
return err
}
encodeBytes, salt, err := util.AesGcmEncrypt([]byte(token), secret)
if err != nil {
return fmt.Errorf("encrypt access token error: %w", err)
}
cred.Encrypt.Type = "AES"
cred.Encrypt.Params = map[string]interface{}{
"salt": string(salt),
}
params["access_token"] = string(encodeBytes)
cred.Payload[cred.Type] = params
return nil
}

func decodeBasicAuth(cred *Credential) (basicAuth model.BasicAuth, err error) {
var (
params map[string]interface{}
Expand Down Expand Up @@ -157,6 +196,42 @@ func decodeBasicAuth(cred *Credential) (basicAuth model.BasicAuth, err error) {
return
}

func decodeAccessToken(cred *Credential) (tokenPayload AccessTokenPayload, err error) {
var (
params map[string]interface{}
ok bool
token string
salt string
)
if params, ok = cred.Payload[cred.Type].(map[string]interface{}); !ok {
err = fmt.Errorf("wrong credential parameters for type [%s], expect a map", cred.Type)
return
}
if token, ok = params["access_token"].(string); !ok {
err = fmt.Errorf("wrong credential parameters access_token for type [%s], expect a string", cred.Type)
return
}
if token == "" {
err = fmt.Errorf("credential parameters access_token can not be empty")
return
}
if salt, ok = cred.Encrypt.Params["salt"].(string); !ok {
err = fmt.Errorf("credential encrypt parameters salt can not be empty")
return
}
secret, err := getCredentialSecret(cred)
if err != nil {
return tokenPayload, err
}

plaintext, err := util.AesGcmDecrypt([]byte(token), secret, []byte(salt))
if err != nil {
return tokenPayload, err
}
tokenPayload.AccessToken = ucfg.SecretString(plaintext)
return
}

type ChangeEvent func(credentials *Credential)

var changeEvents []ChangeEvent
Expand Down
8 changes: 8 additions & 0 deletions core/model/const.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
/* Copyright © INFINI LTD. All rights reserved.
* Web: https://infinilabs.com
* Email: hello#infini.ltd */

package model

const API_TOKEN = "X-API-TOKEN"
const CredentialIDSystemKey = "credential_id"
10 changes: 7 additions & 3 deletions core/model/instance.go
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,8 @@ type Instance struct {

BasicAuth *BasicAuth `config:"basic_auth" json:"basic_auth,omitempty" elastic_mapping:"basic_auth:{type:object}"`

AccessToken string `json:"access_token,omitempty" elastic_mapping:"access_token:{type:keyword}"`

Labels map[string]string `json:"labels,omitempty" elastic_mapping:"labels:{type:object}"`
Tags []string `json:"tags,omitempty"`

Expand All @@ -69,8 +71,6 @@ type Instance struct {
Network NetworkInfo `json:"network,omitempty" elastic_mapping:"network: { type: object }"`
Services []ServiceInfo `json:"services,omitempty" elastic_mapping:"services: { type: object }"`
Status string `json:"status,omitempty" elastic_mapping:"status: { type: keyword, copy_to:search_text }"`

//SearchText string `json:"search_text,omitempty" elastic_mapping:"search_text:{type:text,index_prefixes:{},index_phrases:true, analyzer:suggest_text_search }"`
}

type ServiceInfo struct {
Expand Down Expand Up @@ -137,7 +137,11 @@ func GetInstanceInfo() Instance {

_, publicIP, _, _ := util.GetPublishNetworkDeviceInfo(global.Env().SystemConfig.NodeConfig.MajorIpPattern)

instance.Endpoint = global.Env().SystemConfig.APIConfig.GetEndpoint()
if !global.Env().SystemConfig.APIConfig.Enabled && global.Env().SystemConfig.WebAppConfig.Enabled {
instance.Endpoint = global.Env().SystemConfig.WebAppConfig.GetEndpoint()
} else {
instance.Endpoint = global.Env().SystemConfig.APIConfig.GetEndpoint()
}

ips := util.GetLocalIPs()
if len(ips) > 0 {
Expand Down
4 changes: 4 additions & 0 deletions docs/content.en/docs/release-notes/_index.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,10 @@ Information about release notes of INFINI Framework is provided here.
### 🚀 Features
- feat: support team-based scope for sharing services #258
- feat: add semantic, hybrid, and nested query support #265
### 🐛 Bug fix
- fix: use the web endpoint in instance info when the API listener is disabled, and keep managed config sync requests authenticated after registration
### ✈️ Improvements
- chore: add post-register hooks so managed clients can run follow-up steps like token exchange immediately after a successful register
- feat: extract BuildFuzzinessQueryClauses as public API #266
- feat(keystore): support large stdin secrets (>1024 bytes) and multiline #271
- feat(cors): add X-SERVICE-ID to allowed CORS headers #275
Expand Down
Loading
Loading