Skip to content

Commit

Permalink
Propagate the bearer token to the gRPC plugin server (jaegertracing#1822
Browse files Browse the repository at this point in the history
)

* Add bearer token forward to the GRPC storage plugin

Signed-off-by: radekg <[email protected]>

* Apply the changes suggested by @yurishkuro in the review

Signed-off-by: radekg <[email protected]>

* Update gRPC storage plugin readme with bearer token propagation instructions

Signed-off-by: radekg <[email protected]>

* Expose a literal from token propagation context, use literal in gRPC plugin, FindTraceIDs client context.

Signed-off-by: radekg <[email protected]>

* gRPC plugin client, uses original context everywhere, adapt upgrade function

Signed-off-by: radekg <[email protected]>

* Reuse the bearer token key string in the context key

Signed-off-by: radekg <[email protected]>

* upgradeContextWithBearerToken does not need a receiver

Signed-off-by: radekg <[email protected]>

* Apply make fmt

Signed-off-by: radekg <[email protected]>

* Add test coverage for context upgrades

Signed-off-by: radekg <[email protected]>

* D'oh, fmt for tests...

Signed-off-by: radekg <[email protected]>
Signed-off-by: Jonah Back <[email protected]>
  • Loading branch information
radekg authored and backjo committed Dec 19, 2019
1 parent 79aabbb commit bc75c72
Show file tree
Hide file tree
Showing 4 changed files with 73 additions and 6 deletions.
33 changes: 33 additions & 0 deletions plugin/storage/grpc/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -112,3 +112,36 @@ There are more logger options that can be used with `hclog` listed on [godoc](ht

Note: Setting the `Output` option to `os.Stdout` can confuse the `go-plugin` framework and lead it to consider the plugin
errored.

Bearer token propagation from the UI
------------------------------------
When using `--query.bearer-token-propagation=true`, the bearer token will be properly passed on to the gRPC plugin server. To get access to the bearer token in your plugin, use a method similar to:

```golang
import (
// ... other imports
"fmt"
"github.com/jaegertracing/jaeger/storage/spanstore"
"google.golang.org/grpc/metadata"
)

// ... spanReader type declared here

func (r *spanReader) extractBearerToken(ctx context.Context) (string, bool) {
if md, ok := metadata.FromIncomingContext(ctx); ok {
values := md.Get(spanstore.BearerTokenKey)
if len(values) > 0 {
return values[0], true
}
}
return "", false
}

// ... spanReader interface implementation

func (r *spanReader) GetServices(ctx context.Context) ([]string, error) {
str, ok := r.extractBearerToken(ctx)
fmt.Println(fmt.Sprintf("spanReader.GetServices: bearer-token: '%s', wasGiven: '%t'" str, ok))
// ...
}
```
25 changes: 20 additions & 5 deletions plugin/storage/grpc/shared/grpc_client.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import (
"time"

"github.com/pkg/errors"
"google.golang.org/grpc/metadata"

"github.com/jaegertracing/jaeger/model"
"github.com/jaegertracing/jaeger/proto-gen/storage_v1"
Expand All @@ -34,6 +35,20 @@ type grpcClient struct {
depsReaderClient storage_v1.DependenciesReaderPluginClient
}

// upgradeContextWithBearerToken turns the context into a gRPC outgoing context with bearer token
// in the request metadata, if the original context has bearer token attached.
// Otherwise returns original context.
func upgradeContextWithBearerToken(ctx context.Context) context.Context {
bearerToken, hasToken := spanstore.GetBearerToken(ctx)
if hasToken {
requestMetadata := metadata.New(map[string]string{
spanstore.BearerTokenKey: bearerToken,
})
return metadata.NewOutgoingContext(ctx, requestMetadata)
}
return ctx
}

// DependencyReader implements shared.StoragePlugin.
func (c *grpcClient) DependencyReader() dependencystore.Reader {
return c
Expand All @@ -51,7 +66,7 @@ func (c *grpcClient) SpanWriter() spanstore.Writer {

// GetTrace takes a traceID and returns a Trace associated with that traceID
func (c *grpcClient) GetTrace(ctx context.Context, traceID model.TraceID) (*model.Trace, error) {
stream, err := c.readerClient.GetTrace(ctx, &storage_v1.GetTraceRequest{
stream, err := c.readerClient.GetTrace(upgradeContextWithBearerToken(ctx), &storage_v1.GetTraceRequest{
TraceID: traceID,
})
if err != nil {
Expand All @@ -74,7 +89,7 @@ func (c *grpcClient) GetTrace(ctx context.Context, traceID model.TraceID) (*mode

// GetServices returns a list of all known services
func (c *grpcClient) GetServices(ctx context.Context) ([]string, error) {
resp, err := c.readerClient.GetServices(ctx, &storage_v1.GetServicesRequest{})
resp, err := c.readerClient.GetServices(upgradeContextWithBearerToken(ctx), &storage_v1.GetServicesRequest{})
if err != nil {
return nil, errors.Wrap(err, "plugin error")
}
Expand All @@ -84,7 +99,7 @@ func (c *grpcClient) GetServices(ctx context.Context) ([]string, error) {

// GetOperations returns the operations of a given service
func (c *grpcClient) GetOperations(ctx context.Context, service string) ([]string, error) {
resp, err := c.readerClient.GetOperations(ctx, &storage_v1.GetOperationsRequest{
resp, err := c.readerClient.GetOperations(upgradeContextWithBearerToken(ctx), &storage_v1.GetOperationsRequest{
Service: service,
})
if err != nil {
Expand All @@ -96,7 +111,7 @@ func (c *grpcClient) GetOperations(ctx context.Context, service string) ([]strin

// FindTraces retrieves traces that match the traceQuery
func (c *grpcClient) FindTraces(ctx context.Context, query *spanstore.TraceQueryParameters) ([]*model.Trace, error) {
stream, err := c.readerClient.FindTraces(context.Background(), &storage_v1.FindTracesRequest{
stream, err := c.readerClient.FindTraces(upgradeContextWithBearerToken(ctx), &storage_v1.FindTracesRequest{
Query: &storage_v1.TraceQueryParameters{
ServiceName: query.ServiceName,
OperationName: query.OperationName,
Expand Down Expand Up @@ -134,7 +149,7 @@ func (c *grpcClient) FindTraces(ctx context.Context, query *spanstore.TraceQuery

// FindTraceIDs retrieves traceIDs that match the traceQuery
func (c *grpcClient) FindTraceIDs(ctx context.Context, query *spanstore.TraceQueryParameters) ([]model.TraceID, error) {
resp, err := c.readerClient.FindTraceIDs(context.Background(), &storage_v1.FindTraceIDsRequest{
resp, err := c.readerClient.FindTraceIDs(upgradeContextWithBearerToken(ctx), &storage_v1.FindTraceIDsRequest{
Query: &storage_v1.TraceQueryParameters{
ServiceName: query.ServiceName,
OperationName: query.OperationName,
Expand Down
17 changes: 17 additions & 0 deletions plugin/storage/grpc/shared/grpc_client_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import (

"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/mock"
"google.golang.org/grpc/metadata"

"github.com/jaegertracing/jaeger/model"
"github.com/jaegertracing/jaeger/proto-gen/storage_v1"
Expand Down Expand Up @@ -91,6 +92,22 @@ func withGRPCClient(fn func(r *grpcClientTest)) {
fn(r)
}

func TestContextUpgradeWithToken(t *testing.T) {
testBearerToken := "test-bearer-token"
ctx := spanstore.ContextWithBearerToken(context.Background(), testBearerToken)
upgradedToken := upgradeContextWithBearerToken(ctx)
md, ok := metadata.FromOutgoingContext(upgradedToken)
assert.Truef(t, ok, "Expected metadata in context")
bearerTokenFromMetadata := md.Get(spanstore.BearerTokenKey)
assert.Equal(t, []string{testBearerToken}, bearerTokenFromMetadata)
}

func TestContextUpgradeWithoutToken(t *testing.T) {
upgradedToken := upgradeContextWithBearerToken(context.Background())
_, ok := metadata.FromOutgoingContext(upgradedToken)
assert.Falsef(t, ok, "Expected no metadata in context")
}

func TestGRPCClientGetServices(t *testing.T) {
withGRPCClient(func(r *grpcClientTest) {
r.spanReader.On("GetServices", mock.Anything, &storage_v1.GetServicesRequest{}).
Expand Down
4 changes: 3 additions & 1 deletion storage/spanstore/token_propagation.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,9 @@ import "context"

type contextKey string

const bearerToken = contextKey("bearer.token")
// BearerTokenKey is the string literal used internally in the implementation of this context.
const BearerTokenKey = "bearer.token"
const bearerToken = contextKey(BearerTokenKey)

// StoragePropagationKey is a key for viper configuration to pass this option to storage plugins.
const StoragePropagationKey = "storage.propagate.token"
Expand Down

0 comments on commit bc75c72

Please sign in to comment.