Skip to content

Commit

Permalink
Merge pull request #859 from ydb-platform/obfuscate-access-token
Browse files Browse the repository at this point in the history
* Refactored credentials options (from funcs to interfaces and types)
  • Loading branch information
asmyasnikov committed Oct 17, 2023
2 parents 184cadd + f88d739 commit a0d9205
Show file tree
Hide file tree
Showing 15 changed files with 412 additions and 140 deletions.
3 changes: 3 additions & 0 deletions CHANGELOG.md
@@ -1,3 +1,6 @@
* Refactored credentials options (from funcs to interfaces and types)
* Fixed stringification of credentials object

## v3.53.2
* Fixed panic when try to unwrap values with more than 127 columns with custom ydb unmarshaler

Expand Down
3 changes: 2 additions & 1 deletion connection.go
Expand Up @@ -465,7 +465,8 @@ func connect(ctx context.Context, c *Driver) error {
c.config = c.config.With(config.WithCredentials(
credentials.NewStaticCredentials(
c.userInfo.User, c.userInfo.Password,
c.config,
c.config.Endpoint(),
credentials.WithGrpcDialOptions(c.config.GrpcDialOptions()...),
),
))
}
Expand Down
69 changes: 12 additions & 57 deletions credentials/credentials.go
Expand Up @@ -3,10 +3,7 @@ package credentials
import (
"context"

"google.golang.org/grpc"

"github.com/ydb-platform/ydb-go-sdk/v3/internal/credentials"
"github.com/ydb-platform/ydb-go-sdk/v3/internal/stack"
)

// Credentials is an interface of YDB credentials required for connect with YDB
Expand All @@ -15,67 +12,25 @@ type Credentials interface {
Token(context.Context) (string, error)
}

type optionsHolder struct {
sourceInfo string
}

type option func(h *optionsHolder)

// WithSourceInfo option append to credentials object the source info for reporting source info details on error case
func WithSourceInfo(sourceInfo string) option {
return func(h *optionsHolder) {
h.sourceInfo = sourceInfo
}
}

// NewAccessTokenCredentials makes access token credentials object
// Passed options redefines default values of credentials object internal fields
func NewAccessTokenCredentials(accessToken string, opts ...option) *credentials.AccessToken {
h := &optionsHolder{
sourceInfo: stack.Record(1),
}
for _, o := range opts {
if o != nil {
o(h)
}
}
return credentials.NewAccessTokenCredentials(accessToken, credentials.WithSourceInfo(h.sourceInfo))
func NewAccessTokenCredentials(
accessToken string, opts ...credentials.AccessTokenCredentialsOption,
) *credentials.AccessToken {
return credentials.NewAccessTokenCredentials(accessToken, opts...)
}

// NewAnonymousCredentials makes anonymous credentials object
// Passed options redefines default values of credentials object internal fields
func NewAnonymousCredentials(opts ...option) *credentials.Anonymous {
h := &optionsHolder{
sourceInfo: stack.Record(1),
}
for _, o := range opts {
if o != nil {
o(h)
}
}
return credentials.NewAnonymousCredentials(credentials.WithSourceInfo(h.sourceInfo))
}

type staticCredentialsConfig struct {
authEndpoint string
opts []grpc.DialOption
}

func (s staticCredentialsConfig) Endpoint() string {
return s.authEndpoint
}

func (s staticCredentialsConfig) GrpcDialOptions() []grpc.DialOption {
return s.opts
func NewAnonymousCredentials(
opts ...credentials.AnonymousCredentialsOption,
) *credentials.Anonymous {
return credentials.NewAnonymousCredentials(opts...)
}

// NewStaticCredentials makes static credentials object
func NewStaticCredentials(user, password, authEndpoint string, opts ...grpc.DialOption) *credentials.Static {
return credentials.NewStaticCredentials(user, password,
staticCredentialsConfig{
authEndpoint: authEndpoint,
opts: opts,
},
credentials.WithSourceInfo(stack.Record(1)),
)
func NewStaticCredentials(
user, password, authEndpoint string, opts ...credentials.StaticCredentialsOption,
) *credentials.Static {
return credentials.NewStaticCredentials(user, password, authEndpoint, opts...)
}
17 changes: 17 additions & 0 deletions credentials/options.go
@@ -0,0 +1,17 @@
package credentials

import (
"google.golang.org/grpc"

"github.com/ydb-platform/ydb-go-sdk/v3/internal/credentials"
)

// WithSourceInfo option append to credentials object the source info for reporting source info details on error case
func WithSourceInfo(sourceInfo string) credentials.SourceInfoOption {
return credentials.WithSourceInfo(sourceInfo)
}

// WithGrpcDialOptions option append to static credentials object GRPC dial options
func WithGrpcDialOptions(opts ...grpc.DialOption) credentials.StaticCredentialsOption {
return credentials.WithGrpcDialOptions(opts...)
}
20 changes: 12 additions & 8 deletions internal/balancer/balancer.go
Expand Up @@ -11,6 +11,7 @@ import (
balancerConfig "github.com/ydb-platform/ydb-go-sdk/v3/internal/balancer/config"
"github.com/ydb-platform/ydb-go-sdk/v3/internal/closer"
"github.com/ydb-platform/ydb-go-sdk/v3/internal/conn"
"github.com/ydb-platform/ydb-go-sdk/v3/internal/credentials"
internalDiscovery "github.com/ydb-platform/ydb-go-sdk/v3/internal/discovery"
discoveryConfig "github.com/ydb-platform/ydb-go-sdk/v3/internal/discovery/config"
"github.com/ydb-platform/ydb-go-sdk/v3/internal/endpoint"
Expand Down Expand Up @@ -66,14 +67,10 @@ func (b *Balancer) clusterDiscovery(ctx context.Context) (err error) {
if err = retry.Retry(ctx, func(childCtx context.Context) (err error) {
if err = b.clusterDiscoveryAttempt(childCtx); err != nil {
if xerrors.IsTransportError(err, grpcCodes.Unauthenticated) {
return xerrors.WithStackTrace(
fmt.Errorf(
"cluster discovery failed: %w (endpoint: %q, database: %q, credentials: %q)",
err,
b.driverConfig.Endpoint(),
b.driverConfig.Database(),
b.driverConfig.Credentials(),
),
return credentials.UnauthenticatedError("cluster discovery failed", err,
credentials.WithEndpoint(b.driverConfig.Endpoint()),
credentials.WithDatabase(b.driverConfig.Database()),
credentials.WithCredentials(b.driverConfig.Credentials()),
)
}
// if got err but parent context is not done - mark error as retryable
Expand Down Expand Up @@ -298,6 +295,13 @@ func (b *Balancer) wrapCall(ctx context.Context, f func(ctx context.Context, cc

if err = f(ctx, cc); err != nil {
if conn.UseWrapping(ctx) {
if xerrors.IsTransportError(err, grpcCodes.Unauthenticated) {
err = credentials.UnauthenticatedError("unauthenticated", err,
credentials.WithAddress(cc.Endpoint().String()),
credentials.WithNodeID(cc.Endpoint().NodeID()),
credentials.WithCredentials(b.driverConfig.Credentials()),
)
}
return xerrors.WithStackTrace(err)
}
return err
Expand Down
18 changes: 0 additions & 18 deletions internal/conn/middleware.go
Expand Up @@ -80,21 +80,3 @@ func WithBeforeFunc(
},
}
}

func WithAfterFunc(
cc grpc.ClientConnInterface,
after func(),
) grpc.ClientConnInterface {
return &middleware{
invoke: func(ctx context.Context, method string, args interface{}, reply interface{}, opts ...grpc.CallOption) error {
defer after()
return cc.Invoke(ctx, method, args, reply, opts...)
},
newStream: func(ctx context.Context, desc *grpc.StreamDesc, method string, opts ...grpc.CallOption) (
grpc.ClientStream, error,
) {
defer after()
return cc.NewStream(ctx, desc, method, opts...)
},
}
}
33 changes: 23 additions & 10 deletions internal/credentials/access_token.go
Expand Up @@ -4,33 +4,37 @@ import (
"context"
"fmt"

"github.com/ydb-platform/ydb-go-sdk/v3/internal/allocator"
"github.com/ydb-platform/ydb-go-sdk/v3/internal/secret"
"github.com/ydb-platform/ydb-go-sdk/v3/internal/stack"
)

var (
_ Credentials = (*AccessToken)(nil)
_ fmt.Stringer = (*AccessToken)(nil)
_ Credentials = (*AccessToken)(nil)
_ fmt.Stringer = (*AccessToken)(nil)
_ AccessTokenCredentialsOption = SourceInfoOption("")
)

type AccessTokenCredentialsOption interface {
ApplyAccessTokenCredentialsOption(c *AccessToken)
}

// AccessToken implements Credentials interface with static
// authorization parameters.
type AccessToken struct {
token string
sourceInfo string
}

func NewAccessTokenCredentials(token string, opts ...Option) *AccessToken {
options := optionsHolder{
func NewAccessTokenCredentials(token string, opts ...AccessTokenCredentialsOption) *AccessToken {
c := &AccessToken{
token: token,
sourceInfo: stack.Record(1),
}
for _, opt := range opts {
opt(&options)
}
return &AccessToken{
token: token,
sourceInfo: options.sourceInfo,
opt.ApplyAccessTokenCredentialsOption(c)
}
return c
}

// Token implements Credentials.
Expand All @@ -40,5 +44,14 @@ func (c AccessToken) Token(_ context.Context) (string, error) {

// Token implements Credentials.
func (c AccessToken) String() string {
return fmt.Sprintf("AccessToken(token:%q,from:%q)", secret.Token(c.token), c.sourceInfo)
buffer := allocator.Buffers.Get()
defer allocator.Buffers.Put(buffer)
buffer.WriteString("AccessToken(token:")
fmt.Fprintf(buffer, "%q", secret.Token(c.token))
if c.sourceInfo != "" {
buffer.WriteString(",from:")
fmt.Fprintf(buffer, "%q", c.sourceInfo)
}
buffer.WriteByte(')')
return buffer.String()
}
30 changes: 21 additions & 9 deletions internal/credentials/anonymous.go
Expand Up @@ -4,29 +4,33 @@ import (
"context"
"fmt"

"github.com/ydb-platform/ydb-go-sdk/v3/internal/allocator"
"github.com/ydb-platform/ydb-go-sdk/v3/internal/stack"
)

var (
_ Credentials = (*Anonymous)(nil)
_ fmt.Stringer = (*Anonymous)(nil)
_ Credentials = (*Anonymous)(nil)
_ fmt.Stringer = (*Anonymous)(nil)
_ AnonymousCredentialsOption = SourceInfoOption("")
)

type AnonymousCredentialsOption interface {
ApplyAnonymousCredentialsOption(c *Anonymous)
}

// Anonymous implements Credentials interface with Anonymous access
type Anonymous struct {
sourceInfo string
}

func NewAnonymousCredentials(opts ...Option) *Anonymous {
options := optionsHolder{
func NewAnonymousCredentials(opts ...AnonymousCredentialsOption) *Anonymous {
c := &Anonymous{
sourceInfo: stack.Record(1),
}
for _, opt := range opts {
opt(&options)
}
return &Anonymous{
sourceInfo: options.sourceInfo,
opt.ApplyAnonymousCredentialsOption(c)
}
return c
}

// Token implements Credentials.
Expand All @@ -36,5 +40,13 @@ func (c Anonymous) Token(_ context.Context) (string, error) {

// Token implements Credentials.
func (c Anonymous) String() string {
return fmt.Sprintf("Anonymous(from:%q)", c.sourceInfo)
buffer := allocator.Buffers.Get()
defer allocator.Buffers.Put(buffer)
buffer.WriteString("Anonymous(")
if c.sourceInfo != "" {
buffer.WriteString("from:")
fmt.Fprintf(buffer, "%q", c.sourceInfo)
}
buffer.WriteByte(')')
return buffer.String()
}
13 changes: 0 additions & 13 deletions internal/credentials/options.go

This file was deleted.

20 changes: 20 additions & 0 deletions internal/credentials/source_info.go
@@ -0,0 +1,20 @@
package credentials

type SourceInfoOption string

func (sourceInfo SourceInfoOption) ApplyStaticCredentialsOption(h *Static) {
h.sourceInfo = string(sourceInfo)
}

func (sourceInfo SourceInfoOption) ApplyAnonymousCredentialsOption(h *Anonymous) {
h.sourceInfo = string(sourceInfo)
}

func (sourceInfo SourceInfoOption) ApplyAccessTokenCredentialsOption(h *AccessToken) {
h.sourceInfo = string(sourceInfo)
}

// WithSourceInfo option append to credentials object the source info for reporting source info details on error case
func WithSourceInfo(sourceInfo string) SourceInfoOption {
return SourceInfoOption(sourceInfo)
}

0 comments on commit a0d9205

Please sign in to comment.