github.com/ydb-platform/ydb-go-sdk/v3@v3.89.2/internal/credentials/static.go (about) 1 package credentials 2 3 import ( 4 "context" 5 "fmt" 6 "sync" 7 "time" 8 9 "github.com/golang-jwt/jwt/v4" 10 "github.com/ydb-platform/ydb-go-genproto/Ydb_Auth_V1" 11 "github.com/ydb-platform/ydb-go-genproto/protos/Ydb" 12 "github.com/ydb-platform/ydb-go-genproto/protos/Ydb_Auth" 13 "github.com/ydb-platform/ydb-go-genproto/protos/Ydb_Operations" 14 "google.golang.org/grpc" 15 16 "github.com/ydb-platform/ydb-go-sdk/v3/internal/secret" 17 "github.com/ydb-platform/ydb-go-sdk/v3/internal/stack" 18 "github.com/ydb-platform/ydb-go-sdk/v3/internal/xerrors" 19 "github.com/ydb-platform/ydb-go-sdk/v3/internal/xstring" 20 ) 21 22 const TokenRefreshDivisor = 10 23 24 var ( 25 _ Credentials = (*Static)(nil) 26 _ fmt.Stringer = (*Static)(nil) 27 _ StaticCredentialsOption = grpcDialOptionsOption(nil) 28 ) 29 30 type grpcDialOptionsOption []grpc.DialOption 31 32 func (opts grpcDialOptionsOption) ApplyStaticCredentialsOption(c *Static) { 33 c.opts = opts 34 } 35 36 type StaticCredentialsOption interface { 37 ApplyStaticCredentialsOption(c *Static) 38 } 39 40 func WithGrpcDialOptions(opts ...grpc.DialOption) grpcDialOptionsOption { 41 return opts 42 } 43 44 func NewStaticCredentials(user, password, endpoint string, opts ...StaticCredentialsOption) *Static { 45 c := &Static{ 46 user: user, 47 password: password, 48 endpoint: endpoint, 49 sourceInfo: stack.Record(1), 50 } 51 for _, opt := range opts { 52 if opt != nil { 53 opt.ApplyStaticCredentialsOption(c) 54 } 55 } 56 57 return c 58 } 59 60 var ( 61 _ Credentials = (*Static)(nil) 62 _ fmt.Stringer = (*Static)(nil) 63 ) 64 65 // Static implements Credentials interface with static 66 // authorization parameters. 67 type Static struct { 68 user string 69 password string 70 endpoint string 71 opts []grpc.DialOption 72 token string 73 requestAt time.Time 74 mu sync.Mutex 75 sourceInfo string 76 } 77 78 //nolint:funlen 79 func (c *Static) Token(ctx context.Context) (token string, err error) { 80 c.mu.Lock() 81 defer c.mu.Unlock() 82 if time.Until(c.requestAt) > 0 { 83 return c.token, nil 84 } 85 cc, err := grpc.DialContext(ctx, c.endpoint, c.opts...) //nolint:staticcheck,nolintlint 86 if err != nil { 87 return "", xerrors.WithStackTrace( 88 fmt.Errorf("dial failed: %w", err), 89 ) 90 } 91 defer cc.Close() 92 93 client := Ydb_Auth_V1.NewAuthServiceClient(cc) 94 95 response, err := client.Login(ctx, &Ydb_Auth.LoginRequest{ 96 OperationParams: &Ydb_Operations.OperationParams{ 97 OperationMode: 0, 98 OperationTimeout: nil, 99 CancelAfter: nil, 100 Labels: nil, 101 ReportCostInfo: 0, 102 }, 103 User: c.user, 104 Password: c.password, 105 }) 106 if err != nil { 107 return "", xerrors.WithStackTrace(err) 108 } 109 110 switch { 111 case !response.GetOperation().GetReady(): 112 return "", xerrors.WithStackTrace( 113 fmt.Errorf("operation '%s' not ready: %v", 114 response.GetOperation().GetId(), 115 response.GetOperation().GetIssues(), 116 ), 117 ) 118 119 case response.GetOperation().GetStatus() != Ydb.StatusIds_SUCCESS: 120 return "", xerrors.WithStackTrace( 121 xerrors.Operation( 122 xerrors.FromOperation(response.GetOperation()), 123 xerrors.WithAddress(c.endpoint), 124 ), 125 ) 126 } 127 var result Ydb_Auth.LoginResult 128 if err = response.GetOperation().GetResult().UnmarshalTo(&result); err != nil { 129 return "", xerrors.WithStackTrace(err) 130 } 131 132 expiresAt, err := parseExpiresAt(result.GetToken()) 133 if err != nil { 134 return "", xerrors.WithStackTrace(err) 135 } 136 137 c.requestAt = time.Now().Add(time.Until(expiresAt) / TokenRefreshDivisor) 138 c.token = result.GetToken() 139 140 return c.token, nil 141 } 142 143 func parseExpiresAt(raw string) (expiresAt time.Time, err error) { 144 var claims jwt.RegisteredClaims 145 if _, _, err = jwt.NewParser().ParseUnverified(raw, &claims); err != nil { 146 return expiresAt, xerrors.WithStackTrace(err) 147 } 148 149 return claims.ExpiresAt.Time, nil 150 } 151 152 func (c *Static) String() string { 153 buffer := xstring.Buffer() 154 defer buffer.Free() 155 buffer.WriteString("Static{User:") 156 fmt.Fprintf(buffer, "%q", c.user) 157 buffer.WriteString(",Password:") 158 fmt.Fprintf(buffer, "%q", secret.Password(c.password)) 159 buffer.WriteString(",Token:") 160 fmt.Fprintf(buffer, "%q", secret.Token(c.token)) 161 if c.sourceInfo != "" { 162 buffer.WriteString(",From:") 163 fmt.Fprintf(buffer, "%q", c.sourceInfo) 164 } 165 buffer.WriteByte('}') 166 167 return buffer.String() 168 }