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
19 changes: 19 additions & 0 deletions core/cli/configure.go
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,25 @@ func runE(cmd *cobra.Command, _ []string) error {
}
config.CliConfig.PermifyURL = url
config.CliConfig.Tenant = tenantIds[tenant]

token, err := tui.StringPrompt("enter authentication token (optional, press enter to skip)", "token", config.CliConfig.Token)
if err != nil {
return err
}
config.CliConfig.Token = token

certPath, err := tui.StringPrompt("enter TLS cert path (optional, press enter to skip)", "/path/to/cert.pem", config.CliConfig.CertPath)
if err != nil {
return err
}
config.CliConfig.CertPath = certPath

certKey, err := tui.StringPrompt("enter TLS cert key path (optional, press enter to skip)", "/path/to/key.pem", config.CliConfig.CertKey)
if err != nil {
return err
}
config.CliConfig.CertKey = certKey

err = config.Write()
if err != nil {
logger.Log.Error(err)
Expand Down
82 changes: 78 additions & 4 deletions core/client/grpc.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,19 +2,93 @@
package client

import (
"context"
"crypto/tls"
"crypto/x509"
"fmt"
"os"

permify "github.com/Permify/permify-go/v1"
"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) {
// Credentials holds authentication and TLS configuration for the permify client
type Credentials struct {
Token string
CertPath string
CertKey string
}

// New initializes a new permify client with the given endpoint and optional credentials
func New(endpoint string, creds ...Credentials) (*permify.Client, error) {
var opts []grpc.DialOption

if len(creds) > 0 && creds[0].Token != "" {
token := creds[0].Token
opts = append(opts, grpc.WithPerRPCCredentials(newTokenCredentials(token)))
}

if len(creds) > 0 && creds[0].CertPath != "" {
tlsCreds, err := loadTLSCredentials(creds[0].CertPath, creds[0].CertKey)
if err != nil {
return nil, fmt.Errorf("failed to load TLS credentials: %w", err)
}
opts = append(opts, grpc.WithTransportCredentials(tlsCreds))
} else {
opts = append(opts, grpc.WithTransportCredentials(insecure.NewCredentials()))
}

client, err := permify.NewClient(
permify.Config{
Endpoint: endpoint,
},
// Todo: Implement secure call with tls certificate
grpc.WithTransportCredentials(insecure.NewCredentials()),
opts...,
)
return client, err
}

// loadTLSCredentials loads TLS certificate and key from file paths
func loadTLSCredentials(certPath, certKeyPath string) (credentials.TransportCredentials, error) {
cert, err := tls.LoadX509KeyPair(certPath, certKeyPath)
if err != nil {
return nil, fmt.Errorf("failed to load key pair: %w", err)
}

caCert, err := os.ReadFile(certPath)
if err != nil {
return nil, fmt.Errorf("failed to read CA certificate: %w", err)
}

caCertPool := x509.NewCertPool()
if !caCertPool.AppendCertsFromPEM(caCert) {
return nil, fmt.Errorf("failed to parse CA certificate")
}

tlsConfig := &tls.Config{
Certificates: []tls.Certificate{cert},
RootCAs: caCertPool,
}

return credentials.NewTLS(tlsConfig), nil
}

// tokenCredentials implements credentials.PerRPCCredentials for token-based auth
type tokenCredentials struct {
token string
}

func newTokenCredentials(token string) *tokenCredentials {
return &tokenCredentials{token: token}
}

func (t *tokenCredentials) GetRequestMetadata(ctx context.Context, uri ...string) (map[string]string, error) {
return map[string]string{
"authorization": "Bearer " + t.token,
}, nil
}

func (t *tokenCredentials) RequireTransportSecurity() bool {
return false
}
8 changes: 7 additions & 1 deletion core/cmd/data/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,13 @@ import (
)

func Client() v1.DataClient {
c, err := client.New(config.CliConfig.PermifyURL)
token, certPath, certKey := config.GetCredentials()
creds := client.Credentials{
Token: token,
CertPath: certPath,
CertKey: certKey,
}
c, err := client.New(config.CliConfig.PermifyURL, creds)
if err != nil {
log.Error("Error initializing permify client. Check the configuration or rerun `permify configure`")
os.Exit(-1)
Expand Down
8 changes: 7 additions & 1 deletion core/cmd/permission/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,13 @@ import (
)

func Client() v1.PermissionClient {
c, err := client.New(config.CliConfig.PermifyURL)
token, certPath, certKey := config.GetCredentials()
creds := client.Credentials{
Token: token,
CertPath: certPath,
CertKey: certKey,
}
c, err := client.New(config.CliConfig.PermifyURL, creds)
if err != nil {
log.Error("Error initializing permify client. Check the configuration or rerun `permify configure`")
os.Exit(-1)
Expand Down
8 changes: 7 additions & 1 deletion core/cmd/schema/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,13 @@ import (
)

func Client() v1.SchemaClient {
c, err := client.New(config.CliConfig.PermifyURL)
token, certPath, certKey := config.GetCredentials()
creds := client.Credentials{
Token: token,
CertPath: certPath,
CertKey: certKey,
}
c, err := client.New(config.CliConfig.PermifyURL, creds)
if err != nil {
log.Error("Error initializing permify client. Check the configuration or rerun `permify configure`")
os.Exit(-1)
Expand Down
8 changes: 7 additions & 1 deletion core/cmd/tenancy/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,13 @@ import (
)

func Client() v1.TenancyClient {
c, err := client.New(config.CliConfig.PermifyURL)
token, certPath, certKey := config.GetCredentials()
creds := client.Credentials{
Token: token,
CertPath: certPath,
CertKey: certKey,
}
c, err := client.New(config.CliConfig.PermifyURL, creds)
if err != nil {
log.Error("Error initializing permify client. Check the configuration or rerun `permify configure`")
os.Exit(-1)
Expand Down
15 changes: 14 additions & 1 deletion core/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,9 @@ type ProfileConfigs struct {
type CoreConfig struct {
PermifyURL string `yaml:"permify_url"`
Tenant string `yaml:"tenant"`
Token string `yaml:"token,omitempty"`
CertPath string `yaml:"cert_path,omitempty"`
CertKey string `yaml:"cert_key,omitempty"`
SslEnabled bool `yaml:"-"`
}

Expand All @@ -53,6 +56,16 @@ func IsConfigured(file string, profile string) error {
return nil
}

// HasCredentials checks if credentials are configured for the current profile
func HasCredentials() bool {
return CliConfig.Token != "" || CliConfig.CertPath != ""
}

// GetCredentials returns the stored credentials
func GetCredentials() (token, certPath, certKey string) {
return CliConfig.Token, CliConfig.CertPath, CliConfig.CertKey
}

// Load the permctl configuration specified by the user into the global variable
func Load(file string, profile string) error {
_, err := os.Stat(file)
Expand All @@ -70,7 +83,7 @@ func Load(file string, profile string) error {
profileConfigs.File = file
profileConfigs.Profile = profile
CliConfig = profileConfigs.Configs[profile]
CliConfig.SslEnabled = strings.HasPrefix(CliConfig.PermifyURL, "https")
CliConfig.SslEnabled = strings.HasPrefix(CliConfig.PermifyURL, "https") || CliConfig.CertPath != ""
return err
}

Expand Down