Skip to content

Commit 075be87

Browse files
committed
adding csa jwt generator to org resolver
1 parent 7ec0a00 commit 075be87

File tree

2 files changed

+157
-4
lines changed

2 files changed

+157
-4
lines changed

pkg/services/orgresolver/linking.go

Lines changed: 35 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,12 +8,18 @@ import (
88
"google.golang.org/grpc"
99
"google.golang.org/grpc/credentials"
1010
"google.golang.org/grpc/credentials/insecure"
11+
"google.golang.org/grpc/metadata"
1112

1213
log "github.com/smartcontractkit/chainlink-common/pkg/logger"
1314
"github.com/smartcontractkit/chainlink-common/pkg/services"
1415
linkingclient "github.com/smartcontractkit/chainlink-protos/linking-service/go/v1"
1516
)
1617

18+
// JWTGenerator interface for JWT token creation
19+
type JWTGenerator interface {
20+
CreateJWTForRequest(req any) (string, error)
21+
}
22+
1723
// OrgResolver interface defines methods for resolving organization IDs from workflow owners
1824
type OrgResolver interface {
1925
services.Service
@@ -25,6 +31,7 @@ type Config struct {
2531
TLSEnabled bool
2632
WorkflowRegistryAddress string
2733
WorkflowRegistryChainSelector uint64
34+
JWTGenerator JWTGenerator
2835
}
2936

3037
// orgResolver makes direct calls to the linking service to resolve organization IDs from workflow owners.
@@ -33,9 +40,10 @@ type orgResolver struct {
3340
workflowRegistryAddress string
3441
workflowRegistryChainSelector uint64
3542

36-
client linkingclient.LinkingServiceClient
37-
conn *grpc.ClientConn // nil if client was injected
38-
logger log.SugaredLogger
43+
client linkingclient.LinkingServiceClient
44+
conn *grpc.ClientConn // nil if client was injected
45+
logger log.SugaredLogger
46+
jwtGenerator JWTGenerator
3947
}
4048

4149
// NewOrgResolver creates a new org resolver with the specified configuration
@@ -49,6 +57,7 @@ func NewOrgResolverWithClient(cfg Config, client linkingclient.LinkingServiceCli
4957
workflowRegistryAddress: cfg.WorkflowRegistryAddress,
5058
workflowRegistryChainSelector: cfg.WorkflowRegistryChainSelector,
5159
logger: log.Sugared(logger).Named("OrgResolver"),
60+
jwtGenerator: cfg.JWTGenerator,
5261
}
5362

5463
if client != nil {
@@ -77,13 +86,36 @@ func NewOrgResolverWithClient(cfg Config, client linkingclient.LinkingServiceCli
7786
return resolver, nil
7887
}
7988

89+
// addJWTAuth creates and signs a JWT token, then adds it to the context
90+
func (o *orgResolver) addJWTAuth(ctx context.Context, req any) (context.Context, error) {
91+
// Skip authentication if no JWT generator provided
92+
if o.jwtGenerator == nil {
93+
return ctx, nil
94+
}
95+
96+
// Create JWT token using the JWT generator
97+
jwtToken, err := o.jwtGenerator.CreateJWTForRequest(req)
98+
if err != nil {
99+
return nil, fmt.Errorf("failed to create JWT: %w", err)
100+
}
101+
102+
// Add JWT to Authorization header
103+
return metadata.AppendToOutgoingContext(ctx, "authorization", "Bearer "+jwtToken), nil
104+
}
105+
80106
func (o *orgResolver) Get(ctx context.Context, owner string) (string, error) {
81107
req := &linkingclient.GetOrganizationFromWorkflowOwnerRequest{
82108
WorkflowOwner: owner,
83109
WorkflowRegistryAddress: o.workflowRegistryAddress,
84110
ChainSelector: o.workflowRegistryChainSelector,
85111
}
86112

113+
ctx, err := o.addJWTAuth(ctx, req)
114+
if err != nil {
115+
o.logger.Errorw("Failed to add JWT auth to GetOrganizationFromWorkflowOwner request", "error", err)
116+
return "", err
117+
}
118+
87119
resp, err := o.client.GetOrganizationFromWorkflowOwner(ctx, req)
88120
if err != nil {
89121
return "", fmt.Errorf("failed to fetch organization from workflow owner: %w", err)

pkg/services/orgresolver/linking_test.go

Lines changed: 122 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,14 @@ package orgresolver
22

33
import (
44
"context"
5+
"errors"
56
"net"
67
"testing"
78

89
"github.com/stretchr/testify/require"
910
"google.golang.org/grpc"
1011
"google.golang.org/grpc/credentials/insecure"
12+
"google.golang.org/grpc/metadata"
1113
"google.golang.org/grpc/test/bufconn"
1214

1315
"github.com/smartcontractkit/chainlink-common/pkg/logger"
@@ -24,6 +26,37 @@ func (m *mockLinkingClient) GetOrganizationFromWorkflowOwner(ctx context.Context
2426
}, nil
2527
}
2628

29+
// mockJWTGenerator implements the JWTGenerator interface for testing
30+
type mockJWTGenerator struct {
31+
token string
32+
err error
33+
}
34+
35+
func (m *mockJWTGenerator) CreateJWTForRequest(req any) (string, error) {
36+
return m.token, m.err
37+
}
38+
39+
// mockLinkingClientWithAuthCheck implements the LinkingServiceClient interface and checks for authorization header
40+
type mockLinkingClientWithAuthCheck struct {
41+
expectedAuthHeader string
42+
receivedAuthHeader string
43+
}
44+
45+
func (m *mockLinkingClientWithAuthCheck) GetOrganizationFromWorkflowOwner(ctx context.Context, req *linkingclient.GetOrganizationFromWorkflowOwnerRequest, opts ...grpc.CallOption) (*linkingclient.GetOrganizationFromWorkflowOwnerResponse, error) {
46+
// Extract authorization header from context
47+
md, ok := metadata.FromOutgoingContext(ctx)
48+
if ok {
49+
if authHeaders := md.Get("authorization"); len(authHeaders) > 0 {
50+
m.receivedAuthHeader = authHeaders[0]
51+
}
52+
}
53+
54+
orgID := "org-" + req.WorkflowOwner
55+
return &linkingclient.GetOrganizationFromWorkflowOwnerResponse{
56+
OrganizationId: orgID,
57+
}, nil
58+
}
59+
2760
// mockLinkingServer implements the LinkingServiceServer interface for testing
2861
type mockLinkingServer struct {
2962
linkingclient.UnimplementedLinkingServiceServer
@@ -89,7 +122,9 @@ func TestOrgResolver_NewOrgResolver_WithMockServer(t *testing.T) {
89122
}),
90123
grpc.WithTransportCredentials(insecure.NewCredentials()))
91124
require.NoError(t, err)
92-
defer conn.Close()
125+
defer func() {
126+
_ = conn.Close()
127+
}()
93128

94129
client := linkingclient.NewLinkingServiceClient(conn)
95130

@@ -113,3 +148,89 @@ func TestOrgResolver_NewOrgResolver_WithMockServer(t *testing.T) {
113148
err = resolver.Close()
114149
require.NoError(t, err)
115150
}
151+
152+
func TestOrgResolver_Get_WithJWTGenerator(t *testing.T) {
153+
ctx := context.Background()
154+
client := &mockLinkingClientWithAuthCheck{}
155+
156+
// Test with JWT generator that returns a valid token
157+
jwtGenerator := &mockJWTGenerator{
158+
token: "test-jwt-token-123",
159+
err: nil,
160+
}
161+
162+
cfg := Config{
163+
URL: "test-url",
164+
TLSEnabled: false,
165+
WorkflowRegistryAddress: "0x1234567890abcdef",
166+
WorkflowRegistryChainSelector: 1,
167+
JWTGenerator: jwtGenerator,
168+
}
169+
170+
resolver, err := NewOrgResolverWithClient(cfg, client, logger.Test(t))
171+
require.NoError(t, err)
172+
173+
workflowOwner := "0xabcdef1234567890"
174+
175+
orgID, err := resolver.Get(ctx, workflowOwner)
176+
require.NoError(t, err)
177+
require.Equal(t, "org-"+workflowOwner, orgID)
178+
179+
// Verify that the authorization header was set correctly
180+
require.Equal(t, "Bearer test-jwt-token-123", client.receivedAuthHeader)
181+
}
182+
183+
func TestOrgResolver_Get_WithJWTGeneratorError(t *testing.T) {
184+
ctx := context.Background()
185+
client := &mockLinkingClient{}
186+
187+
// Test with JWT generator that returns an error
188+
jwtGenerator := &mockJWTGenerator{
189+
token: "",
190+
err: errors.New("JWT generation failed"),
191+
}
192+
193+
cfg := Config{
194+
URL: "test-url",
195+
TLSEnabled: false,
196+
WorkflowRegistryAddress: "0x1234567890abcdef",
197+
WorkflowRegistryChainSelector: 1,
198+
JWTGenerator: jwtGenerator,
199+
}
200+
201+
resolver, err := NewOrgResolverWithClient(cfg, client, logger.Test(t))
202+
require.NoError(t, err)
203+
204+
workflowOwner := "0xabcdef1234567890"
205+
206+
// The Get call should fail due to JWT generation error
207+
_, err = resolver.Get(ctx, workflowOwner)
208+
require.Error(t, err)
209+
require.Contains(t, err.Error(), "JWT generation failed")
210+
}
211+
212+
func TestOrgResolver_Get_WithoutJWTGenerator(t *testing.T) {
213+
ctx := context.Background()
214+
client := &mockLinkingClientWithAuthCheck{}
215+
216+
// Test without JWT generator (should not set authorization header)
217+
cfg := Config{
218+
URL: "test-url",
219+
TLSEnabled: false,
220+
WorkflowRegistryAddress: "0x1234567890abcdef",
221+
WorkflowRegistryChainSelector: 1,
222+
JWTGenerator: nil, // No JWT generator
223+
}
224+
225+
resolver, err := NewOrgResolverWithClient(cfg, client, logger.Test(t))
226+
require.NoError(t, err)
227+
228+
workflowOwner := "0xabcdef1234567890"
229+
230+
orgID, err := resolver.Get(ctx, workflowOwner)
231+
require.NoError(t, err)
232+
require.Equal(t, "org-"+workflowOwner, orgID)
233+
234+
// Verify that no authorization header was set
235+
require.Empty(t, client.receivedAuthHeader)
236+
}

0 commit comments

Comments
 (0)