Skip to content

Commit fe5b15f

Browse files
vault: normalize forwarded request IDs before node auth
1 parent db01bba commit fe5b15f

2 files changed

Lines changed: 102 additions & 2 deletions

File tree

core/capabilities/vault/gw_handler.go

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -177,6 +177,10 @@ func (h *GatewayHandler) authorizeAndPrefixRequest(ctx context.Context, req *jso
177177

178178
authReq := *req
179179
authReq.ID = originalRequestID
180+
if err := stripPrefixedRequestIDFromParams(&authReq, originalRequestID); err != nil {
181+
h.lggr.Errorw("failed to normalize gateway request for authorization", "method", req.Method, "requestID", originalRequestID, "error", err)
182+
return "", err
183+
}
180184

181185
h.lggr.Debugw("authorizing gateway request", "method", req.Method, "requestID", originalRequestID)
182186
isAuthorized, owner, err := h.requestAuthorizer.AuthorizeRequest(ctx, authReq)
@@ -196,6 +200,55 @@ func (h *GatewayHandler) authorizeAndPrefixRequest(ctx context.Context, req *jso
196200
return owner, nil
197201
}
198202

203+
func stripPrefixedRequestIDFromParams(req *jsonrpc.Request[json.RawMessage], originalRequestID string) error {
204+
if req.Params == nil {
205+
return nil
206+
}
207+
208+
switch req.Method {
209+
case vaulttypes.MethodSecretsCreate:
210+
parsed := vaultcommon.CreateSecretsRequest{}
211+
if err := json.Unmarshal(*req.Params, &parsed); err != nil {
212+
return err
213+
}
214+
parsed.RequestId = originalRequestID
215+
return rewriteRequestParams(req, parsed)
216+
case vaulttypes.MethodSecretsUpdate:
217+
parsed := vaultcommon.UpdateSecretsRequest{}
218+
if err := json.Unmarshal(*req.Params, &parsed); err != nil {
219+
return err
220+
}
221+
parsed.RequestId = originalRequestID
222+
return rewriteRequestParams(req, parsed)
223+
case vaulttypes.MethodSecretsDelete:
224+
parsed := vaultcommon.DeleteSecretsRequest{}
225+
if err := json.Unmarshal(*req.Params, &parsed); err != nil {
226+
return err
227+
}
228+
parsed.RequestId = originalRequestID
229+
return rewriteRequestParams(req, parsed)
230+
case vaulttypes.MethodSecretsList:
231+
parsed := vaultcommon.ListSecretIdentifiersRequest{}
232+
if err := json.Unmarshal(*req.Params, &parsed); err != nil {
233+
return err
234+
}
235+
parsed.RequestId = originalRequestID
236+
return rewriteRequestParams(req, parsed)
237+
default:
238+
return nil
239+
}
240+
}
241+
242+
func rewriteRequestParams(req *jsonrpc.Request[json.RawMessage], payload any) error {
243+
params, err := json.Marshal(payload)
244+
if err != nil {
245+
return err
246+
}
247+
raw := json.RawMessage(params)
248+
req.Params = &raw
249+
return nil
250+
}
251+
199252
func (h *GatewayHandler) handleSecretsCreate(ctx context.Context, gatewayID string, req *jsonrpc.Request[json.RawMessage], owner string) *jsonrpc.Response[json.RawMessage] {
200253
vaultCapRequest := vaultcommon.CreateSecretsRequest{}
201254
if err := json.Unmarshal(*req.Params, &vaultCapRequest); err != nil {

core/capabilities/vault/gw_handler_test.go

Lines changed: 49 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -119,10 +119,9 @@ func TestGatewayHandler_HandleGatewayMessage(t *testing.T) {
119119
{
120120
name: "failure - invalid request params",
121121
setupMocks: func(ss *vaulttypesmocks.SecretsService, gc *connector_mocks.GatewayConnector, ra *vaultcapmocks.RequestAuthorizer) {
122-
ra.EXPECT().AuthorizeRequest(mock.Anything, mock.Anything).Return(true, "0xabc", nil)
123122
gc.On("SendToGateway", mock.Anything, "gateway-1", mock.MatchedBy(func(resp *jsonrpc.Response[json.RawMessage]) bool {
124123
return resp.Error != nil &&
125-
resp.Error.Code == api.ToJSONRPCErrorCode(api.UserMessageParseError)
124+
resp.Error.Code == api.ToJSONRPCErrorCode(api.FatalError)
126125
})).Return(nil)
127126
},
128127
request: &jsonrpc.Request[json.RawMessage]{
@@ -204,6 +203,54 @@ func TestGatewayHandler_HandleGatewayMessage(t *testing.T) {
204203
},
205204
expectedError: false,
206205
},
206+
{
207+
name: "success - strips owner prefix from forwarded request before authorization",
208+
setupMocks: func(ss *vaulttypesmocks.SecretsService, gc *connector_mocks.GatewayConnector, ra *vaultcapmocks.RequestAuthorizer) {
209+
ra.EXPECT().AuthorizeRequest(mock.Anything, mock.MatchedBy(func(req jsonrpc.Request[json.RawMessage]) bool {
210+
if req.Method != vaulttypes.MethodSecretsCreate || req.ID != "1" || req.Params == nil {
211+
return false
212+
}
213+
214+
var parsed vaultcommon.CreateSecretsRequest
215+
if err := json.Unmarshal(*req.Params, &parsed); err != nil {
216+
return false
217+
}
218+
219+
return parsed.RequestId == "1" &&
220+
len(parsed.EncryptedSecrets) == 1 &&
221+
parsed.EncryptedSecrets[0].Id != nil &&
222+
parsed.EncryptedSecrets[0].Id.Owner == "0xAbC"
223+
})).Return(true, "0xabc", nil)
224+
ss.EXPECT().CreateSecrets(mock.Anything, mock.MatchedBy(func(req *vaultcommon.CreateSecretsRequest) bool {
225+
return req.RequestId == "0xabc"+vaulttypes.RequestIDSeparator+"1"
226+
})).Return(&vaulttypes.Response{ID: "test-secret"}, nil)
227+
228+
gc.On("SendToGateway", mock.Anything, "gateway-1", mock.MatchedBy(func(resp *jsonrpc.Response[json.RawMessage]) bool {
229+
return resp.Error == nil
230+
})).Return(nil)
231+
},
232+
request: &jsonrpc.Request[json.RawMessage]{
233+
Method: vaulttypes.MethodSecretsCreate,
234+
ID: "0xAbC" + vaulttypes.RequestIDSeparator + "1",
235+
Params: func() *json.RawMessage {
236+
params, _ := json.Marshal(vaultcommon.CreateSecretsRequest{
237+
RequestId: "0xAbC" + vaulttypes.RequestIDSeparator + "1",
238+
EncryptedSecrets: []*vaultcommon.EncryptedSecret{
239+
{
240+
Id: &vaultcommon.SecretIdentifier{
241+
Key: "test-secret",
242+
Owner: "0xAbC",
243+
},
244+
EncryptedValue: "encrypted-value",
245+
},
246+
},
247+
})
248+
raw := json.RawMessage(params)
249+
return &raw
250+
}(),
251+
},
252+
expectedError: false,
253+
},
207254
{
208255
name: "failure - owner mismatch against authorized owner",
209256
setupMocks: func(ss *vaulttypesmocks.SecretsService, gc *connector_mocks.GatewayConnector, ra *vaultcapmocks.RequestAuthorizer) {

0 commit comments

Comments
 (0)