From bfc3dc3ad8c5d008747aacaa78a9c1b761c27424 Mon Sep 17 00:00:00 2001 From: Hermes Adjutant Date: Sat, 2 May 2026 13:51:19 +0800 Subject: [PATCH 1/2] feat: add support for persistent token and certificate paths in config - Added Token, CertPath, and CertKeyPath to CoreConfig struct - Enhanced 'configure' command to interactively prompt for these fields - Verified YAML persistence with unit tests --- core/cli/configure.go | 21 +++++++++++++++++++ core/config/config.go | 7 +++++-- core/config/config_test.go | 41 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 67 insertions(+), 2 deletions(-) create mode 100644 core/config/config_test.go diff --git a/core/cli/configure.go b/core/cli/configure.go index a533aba..e2219e6 100644 --- a/core/cli/configure.go +++ b/core/cli/configure.go @@ -124,6 +124,27 @@ func runE(cmd *cobra.Command, _ []string) error { } config.CliConfig.PermifyURL = url config.CliConfig.Tenant = tenantIds[tenant] + + // Request for Token + token, err := tui.StringPrompt("enter your auth token", "", config.CliConfig.Token) + if err != nil { + return err + } + config.CliConfig.Token = token + + // Request for Certificate Paths (Optional) + certPath, err := tui.StringPrompt("enter certificate path (optional)", "", config.CliConfig.CertPath) + if err != nil { + return err + } + config.CliConfig.CertPath = certPath + + certKeyPath, err := tui.StringPrompt("enter certificate key path (optional)", "", config.CliConfig.CertKeyPath) + if err != nil { + return err + } + config.CliConfig.CertKeyPath = certKeyPath + err = config.Write() if err != nil { logger.Log.Error(err) diff --git a/core/config/config.go b/core/config/config.go index c9bdebb..35beb2a 100644 --- a/core/config/config.go +++ b/core/config/config.go @@ -25,8 +25,11 @@ type ProfileConfigs struct { // CoreConfig is the config struct type CoreConfig struct { - PermifyURL string `yaml:"permify_url"` - Tenant string `yaml:"tenant"` + PermifyURL string `yaml:"permify_url"` + Tenant string `yaml:"tenant"` + Token string `yaml:"token"` + CertPath string `yaml:"cert_path"` + CertKeyPath string `yaml:"cert_key_path"` SslEnabled bool `yaml:"-"` } diff --git a/core/config/config_test.go b/core/config/config_test.go new file mode 100644 index 0000000..39d7f83 --- /dev/null +++ b/core/config/config_test.go @@ -0,0 +1,41 @@ +package config + +import ( + "os" + "testing" + + "gopkg.in/yaml.v3" +) + +func TestCoreConfigPersistence(t *testing.T) { + tmpFile := "test_config.yaml" + defer os.Remove(tmpFile) + + expected := CoreConfig{ + PermifyURL: "https://localhost:8888", + Tenant: "test-tenant", + Token: "test-token-123", + CertPath: "/path/to/cert", + CertKeyPath: "/path/to/key", + } + + // Test Writing + configs := make(map[string]CoreConfig) + configs["default"] = expected + data, _ := yaml.Marshal(configs) + _ = os.WriteFile(tmpFile, data, 0644) + + // Test Reading + var actualConfigs map[string]CoreConfig + readData, _ := os.ReadFile(tmpFile) + yaml.Unmarshal(readData, &actualConfigs) + + actual := actualConfigs["default"] + + if actual.Token != expected.Token { + t.Errorf("Expected Token %s, got %s", expected.Token, actual.Token) + } + if actual.CertPath != expected.CertPath { + t.Errorf("Expected CertPath %s, got %s", expected.CertPath, actual.CertPath) + } +} From 1c2f8cdc7e921e63d4232cafe45f071c90639da1 Mon Sep 17 00:00:00 2001 From: Hermes Adjutant Date: Sat, 2 May 2026 14:37:26 +0800 Subject: [PATCH 2/2] feat: complete secure gRPC connection with token and TLS support --- core/client/auth.go | 20 +++++++++++++++++ core/client/grpc.go | 53 +++++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 71 insertions(+), 2 deletions(-) create mode 100644 core/client/auth.go diff --git a/core/client/auth.go b/core/client/auth.go new file mode 100644 index 0000000..0c5af55 --- /dev/null +++ b/core/client/auth.go @@ -0,0 +1,20 @@ +package client + +import ( + "context" +) + +// tokenAuth implements grpc.PerRPCCredentials +type tokenAuth struct { + token string +} + +func (t tokenAuth) GetRequestMetadata(ctx context.Context, uri ...string) (map[string]string, error) { + return map[string]string{ + "authorization": "Bearer " + t.token, + }, nil +} + +func (t tokenAuth) RequireTransportSecurity() bool { + return false // 允许在非 TLS 下传(视官方服务端需求而定,通常开发环境需要设为 false) +} diff --git a/core/client/grpc.go b/core/client/grpc.go index 11835df..f7f34bd 100644 --- a/core/client/grpc.go +++ b/core/client/grpc.go @@ -2,19 +2,68 @@ package client import ( + "crypto/tls" + "crypto/x509" + "os" + permify "github.com/Permify/permify-go/v1" + "github.com/Permify/permify-cli/core/config" "google.golang.org/grpc" + "google.golang.org/grpc/credentials" "google.golang.org/grpc/credentials/insecure" ) // New initializes a new permify client func New(endpoint string) (*permify.Client, error) { + var opts []grpc.DialOption + + // Handle TLS Configuration + if config.CliConfig.CertPath != "" { + // Load client certificate if provided + var creds credentials.TransportCredentials + if config.CliConfig.CertKeyPath != "" { + cert, err := tls.LoadX509KeyPair(config.CliConfig.CertPath, config.CliConfig.CertKeyPath) + if err != nil { + return nil, err + } + creds = credentials.NewTLS(&tls.Config{ + Certificates: []tls.Certificate{cert}, + }) + } else { + // Load CA cert + caCert, err := os.ReadFile(config.CliConfig.CertPath) + if err != nil { + return nil, err + } + caCertPool := x509.NewCertPool() + caCertPool.AppendCertsFromPEM(caCert) + creds = credentials.NewTLS(&tls.Config{ + RootCAs: caCertPool, + }) + } + opts = append(opts, grpc.WithTransportCredentials(creds)) + } else { + opts = append(opts, grpc.WithTransportCredentials(insecure.NewCredentials())) + } + + // Handle Token Authentication + // Check environment variable first for maximum flexibility + token := os.Getenv("PERMIFY_TOKEN") + if token == "" { + token = config.CliConfig.Token + } + + if token != "" { + opts = append(opts, grpc.WithPerRPCCredentials(tokenAuth{ + token: token, + })) + } + client, err := permify.NewClient( permify.Config{ Endpoint: endpoint, }, - // Todo: Implement secure call with tls certificate - grpc.WithTransportCredentials(insecure.NewCredentials()), + opts..., ) return client, err }