Some checks failed
CI / Run lints and checks (push) Failing after 47s
CI / Run tests (push) Failing after 1s
CI / Run race tests (push) Failing after 5s
CI / Build CLI docs (push) Has been skipped
CI / Run buf actions (push) Failing after 5s
CodeQL / Analyze (go) (push) Failing after 3s
CodeQL / Analyze (javascript) (push) Failing after 2s
Docker Image Scanners / scanners (push) Failing after 3s
Format / format (push) Failing after 3s
Synchronize Issue Labels / Synchronize Issue Labels (push) Has been skipped
CI / Generate release (push) Has been skipped
CI / Draft newsletter (push) Has been skipped
CI / Pending approval Slack notification (push) Has been skipped
CI / Send newsletter (push) Has been skipped
160 lines
5.0 KiB
Go
160 lines
5.0 KiB
Go
// Copyright © 2023 Ory Corp
|
|
// SPDX-License-Identifier: Apache-2.0
|
|
|
|
package client
|
|
|
|
import (
|
|
"context"
|
|
"crypto/tls"
|
|
"fmt"
|
|
"os"
|
|
"strings"
|
|
"time"
|
|
|
|
"golang.org/x/oauth2"
|
|
|
|
"github.com/ory/x/flagx"
|
|
|
|
"github.com/spf13/cobra"
|
|
"github.com/spf13/pflag"
|
|
"google.golang.org/grpc"
|
|
"google.golang.org/grpc/credentials"
|
|
"google.golang.org/grpc/credentials/insecure"
|
|
"google.golang.org/grpc/credentials/oauth"
|
|
)
|
|
|
|
type contextKeys string
|
|
|
|
const (
|
|
FlagReadRemote = "read-remote"
|
|
FlagWriteRemote = "write-remote"
|
|
FlagOplRemote = "syntax-remote"
|
|
|
|
FlagInsecureNoTransportSecurity = "insecure-disable-transport-security"
|
|
FlagInsecureSkipHostVerification = "insecure-skip-hostname-verification"
|
|
FlagAuthority = "authority"
|
|
FlagBlock = "block"
|
|
|
|
EnvReadRemote = "KETO_READ_REMOTE"
|
|
EnvWriteRemote = "KETO_WRITE_REMOTE"
|
|
EnvAuthToken = "KETO_BEARER_TOKEN" //nolint:gosec // just the key, not the value
|
|
EnvAuthority = "KETO_AUTHORITY"
|
|
|
|
ContextKeyTimeout contextKeys = "timeout"
|
|
ContextKeyDialFunc contextKeys = "dial"
|
|
)
|
|
|
|
type connectionDetails struct {
|
|
token, authority string
|
|
skipHostVerification bool
|
|
noTransportSecurity bool
|
|
block bool
|
|
}
|
|
|
|
func (d *connectionDetails) dialOptions() (opts []grpc.DialOption) {
|
|
if d.token != "" {
|
|
opts = append(opts,
|
|
grpc.WithPerRPCCredentials(
|
|
oauth.TokenSource{TokenSource: oauth2.StaticTokenSource(&oauth2.Token{AccessToken: d.token})},
|
|
))
|
|
}
|
|
if d.authority != "" {
|
|
opts = append(opts, grpc.WithAuthority(d.authority))
|
|
}
|
|
|
|
// TLS settings
|
|
switch {
|
|
case d.noTransportSecurity:
|
|
opts = append(opts, grpc.WithTransportCredentials(insecure.NewCredentials()))
|
|
case d.skipHostVerification:
|
|
opts = append(opts, grpc.WithTransportCredentials(credentials.NewTLS(&tls.Config{
|
|
//nolint:gosec // explicity set through scary flag
|
|
InsecureSkipVerify: true,
|
|
})))
|
|
default:
|
|
// Defaults to the default host root CA bundle
|
|
opts = append(opts, grpc.WithTransportCredentials(credentials.NewTLS(nil)))
|
|
}
|
|
if d.block {
|
|
opts = append(opts, grpc.WithBlock()) //nolint:staticcheck // SA1019 grpc.WithBlock is deprecated, but we actually want to block here until the connection is up.
|
|
}
|
|
return opts
|
|
}
|
|
|
|
func getRemote(cmd *cobra.Command, flagRemote, envRemote string) (remote string) {
|
|
defer (func() {
|
|
if strings.HasPrefix(remote, "http://") || strings.HasPrefix(remote, "https://") {
|
|
_, _ = fmt.Fprintf(cmd.ErrOrStderr(), "remote %q seems to be an http URL instead of a remote address\n", remote)
|
|
}
|
|
})()
|
|
|
|
if cmd.Flags().Changed(flagRemote) {
|
|
return flagx.MustGetString(cmd, flagRemote)
|
|
} else if remote, isSet := os.LookupEnv(envRemote); isSet {
|
|
return remote
|
|
}
|
|
|
|
// no value is set, use fallback from the flag and warn about that
|
|
remote = flagx.MustGetString(cmd, flagRemote)
|
|
_, _ = fmt.Fprintf(cmd.ErrOrStderr(), "neither flag --%s nor env var %s are set, falling back to %s\n", flagRemote, envRemote, remote)
|
|
return remote
|
|
}
|
|
|
|
func getAuthority(cmd *cobra.Command) string {
|
|
if cmd.Flags().Changed(FlagAuthority) {
|
|
return flagx.MustGetString(cmd, FlagAuthority)
|
|
}
|
|
return os.Getenv(EnvAuthority)
|
|
}
|
|
|
|
func getConnectionDetails(cmd *cobra.Command) connectionDetails {
|
|
return connectionDetails{
|
|
token: os.Getenv(EnvAuthToken),
|
|
authority: getAuthority(cmd),
|
|
skipHostVerification: flagx.MustGetBool(cmd, FlagInsecureSkipHostVerification),
|
|
noTransportSecurity: flagx.MustGetBool(cmd, FlagInsecureNoTransportSecurity),
|
|
block: flagx.MustGetBool(cmd, "block"),
|
|
}
|
|
}
|
|
|
|
func GetReadConn(cmd *cobra.Command) (*grpc.ClientConn, error) {
|
|
return Conn(cmd.Context(),
|
|
getRemote(cmd, FlagReadRemote, EnvReadRemote),
|
|
getConnectionDetails(cmd),
|
|
)
|
|
}
|
|
|
|
func GetWriteConn(cmd *cobra.Command) (*grpc.ClientConn, error) {
|
|
return Conn(cmd.Context(),
|
|
getRemote(cmd, FlagWriteRemote, EnvWriteRemote),
|
|
getConnectionDetails(cmd),
|
|
)
|
|
}
|
|
|
|
func Conn(ctx context.Context, remote string, details connectionDetails) (*grpc.ClientConn, error) {
|
|
if d, ok := ctx.Value(ContextKeyTimeout).(time.Duration); ok {
|
|
var cancel context.CancelFunc
|
|
ctx, cancel = context.WithTimeout(ctx, d)
|
|
defer cancel()
|
|
}
|
|
|
|
if dial, ok := ctx.Value(ContextKeyDialFunc).(func(context.Context, string) (*grpc.ClientConn, error)); ok {
|
|
return dial(ctx, remote)
|
|
}
|
|
|
|
return grpc.DialContext( // nolint:staticcheck // SA1019 grpc.DialContext is deprecated, but we actually want to connect here right away.
|
|
ctx,
|
|
remote,
|
|
details.dialOptions()...,
|
|
)
|
|
}
|
|
|
|
func RegisterRemoteURLFlags(flags *pflag.FlagSet) {
|
|
flags.String(FlagReadRemote, "127.0.0.1:4466", "Remote address of the read API endpoint.")
|
|
flags.String(FlagWriteRemote, "127.0.0.1:4467", "Remote address of the write API endpoint.")
|
|
flags.String(FlagAuthority, "", "Set the authority header for the remote gRPC server.")
|
|
flags.Bool(FlagInsecureNoTransportSecurity, false, "Disables transport security. Do not use this in production.")
|
|
flags.Bool(FlagInsecureSkipHostVerification, false, "Disables hostname verification. Do not use this in production.")
|
|
flags.Bool(FlagBlock, false, "Block until the connection is up.")
|
|
}
|