Skip to content

Commit 5f7c435

Browse files
committed
feat: enhance remote claim builder: introduce Path Value options
- enables the use of remote claim builders that rely on url path values - update tests - update config example
1 parent 303244f commit 5f7c435

File tree

5 files changed

+152
-24
lines changed

5 files changed

+152
-24
lines changed

themis.yaml

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,13 @@ token:
106106
- key: uuid
107107
header: X-Midt-Uuid
108108
parameter: uuid
109+
pathValues:
110+
- key: mac
111+
header: X-Midt-Mac-Address
112+
parameter: mac
113+
remote:
114+
method: GET
115+
url: https://localhost:443/device/{mac}/claims?format=json
109116
partnerID:
110117
claim: partner-id
111118
metadata: pid

token/factory.go

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,10 @@ type Request struct {
3434
// metadata is available to lower levels of infrastructure used by the Factory.
3535
Metadata map[string]interface{}
3636

37+
// PathValues holds non-claim information about the request, usually garnered from the original HTTP request. This
38+
// PathValues is available to remote claim builders.
39+
PathValues map[string]interface{}
40+
3741
// TLS represents the state of any underlying TLS connection.
3842
// For non-tls connections, this field is unset.
3943
TLS *tls.ConnectionState
@@ -42,9 +46,10 @@ type Request struct {
4246
// NewRequest returns an empty, fully initialized token Request
4347
func NewRequest() *Request {
4448
return &Request{
45-
Logger: sallust.Default(),
46-
Claims: make(map[string]interface{}),
47-
Metadata: make(map[string]interface{}),
49+
Logger: sallust.Default(),
50+
Claims: make(map[string]interface{}),
51+
Metadata: make(map[string]interface{}),
52+
PathValues: make(map[string]interface{}),
4853
}
4954
}
5055

token/options.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,10 @@ type PartnerID struct {
9191
// is set and thus the partner id won't be transmitted to remote systems.
9292
Metadata string
9393

94+
// PathValue is the name of the path value key for the partner id. If unset, no path value
95+
// is set and thus the partner id won't be transmitted to remote systems via url path value.
96+
PathValue string
97+
9498
// Header is the HTTP header containing the partner id
9599
Header string
96100

@@ -200,6 +204,10 @@ type Options struct {
200204
// Metadata describes non-claim data, which can be statically configured or supplied via a request
201205
Metadata []Value
202206

207+
// PathValues holds non-claim information about the request, usually garnered from the original HTTP request. This
208+
// PathValues is available to remote claim builders.
209+
PathValues []Value
210+
203211
// PartnerID is the optional partner id configuration. If unset, no partner id processing is
204212
// performed, though a partner id may still be configured as part of the claims.
205213
PartnerID *PartnerID

token/transport.go

Lines changed: 39 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,10 @@ func metadataSetter(key string, value interface{}, tr *Request) {
111111
tr.Metadata[key] = value
112112
}
113113

114+
func pathValuesSetter(key string, value interface{}, tr *Request) {
115+
tr.PathValues[key] = value
116+
}
117+
114118
type headerParameterRequestBuilder struct {
115119
key string
116120
header string
@@ -204,6 +208,10 @@ func (prb partnerIDRequestBuilder) Build(original *http.Request, tr *Request) er
204208
if len(prb.Metadata) > 0 {
205209
tr.Metadata[prb.Metadata] = partnerID
206210
}
211+
212+
if len(prb.PathValue) > 0 {
213+
tr.PathValues[prb.PathValue] = partnerID
214+
}
207215
}
208216

209217
return nil
@@ -282,7 +290,37 @@ func NewRequestBuilders(o Options) (RequestBuilders, error) {
282290
}
283291
}
284292

285-
if o.PartnerID != nil && (len(o.PartnerID.Claim) > 0 || len(o.PartnerID.Metadata) > 0) {
293+
for _, value := range o.PathValues {
294+
switch {
295+
case len(value.Key) == 0:
296+
return nil, ErrMissingKey
297+
298+
case len(value.Header) > 0 || len(value.Parameter) > 0:
299+
if len(value.Variable) > 0 {
300+
return nil, ErrVariableNotAllowed
301+
}
302+
303+
rb = append(rb,
304+
headerParameterRequestBuilder{
305+
key: value.Key,
306+
header: http.CanonicalHeaderKey(value.Header),
307+
parameter: value.Parameter,
308+
setter: pathValuesSetter,
309+
},
310+
)
311+
312+
case len(value.Variable) > 0:
313+
rb = append(rb,
314+
variableRequestBuilder{
315+
key: value.Key,
316+
variable: value.Variable,
317+
setter: pathValuesSetter,
318+
},
319+
)
320+
}
321+
}
322+
323+
if o.PartnerID != nil && (len(o.PartnerID.Claim) > 0 || len(o.PartnerID.Metadata) > 0 || len(o.PartnerID.PathValue) > 0) {
286324
rb = append(rb,
287325
partnerIDRequestBuilder{
288326
PartnerID: *o.PartnerID,

token/transport_test.go

Lines changed: 90 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,22 @@ func testNewRequestBuildersInvalidMetadata(t *testing.T) {
5555
assert.Empty(rb)
5656
}
5757

58+
func testNewRequestBuildersInvalidPathValues(t *testing.T) {
59+
assert := assert.New(t)
60+
rb, err := NewRequestBuilders(Options{
61+
PathValues: []Value{
62+
{
63+
Key: "bad",
64+
Header: "xxx",
65+
Parameter: "yyy",
66+
Variable: "zzz",
67+
},
68+
},
69+
})
70+
71+
assert.Equal(ErrVariableNotAllowed, err)
72+
assert.Empty(rb)
73+
}
5874
func testNewRequestBuildersSuccess(t *testing.T) {
5975
testData := []struct {
6076
options Options
@@ -89,16 +105,24 @@ func testNewRequestBuildersSuccess(t *testing.T) {
89105
Header: "X-Missing",
90106
},
91107
},
108+
PathValues: []Value{
109+
{
110+
Key: "fromHeader",
111+
Header: "X-PathVlaue",
112+
},
113+
},
92114
PartnerID: &PartnerID{
93-
Claim: "partner-id-claim",
94-
Metadata: "partner-id-metadata",
95-
Header: "X-Midt-Partner-ID",
115+
Claim: "partner-id-claim",
116+
Metadata: "partner-id-metadata",
117+
PathValue: "partner-id-pathValue",
118+
Header: "X-Midt-Partner-ID",
96119
},
97120
},
98121
uri: "/test",
99122
header: http.Header{
100123
"X-Claim": []string{"foo"},
101124
"X-Metadata": []string{"bar"},
125+
"X-PathVlaue": []string{"foobar"},
102126
"X-Midt-Partner-ID": []string{"test"},
103127
},
104128
expected: &Request{
@@ -111,6 +135,10 @@ func testNewRequestBuildersSuccess(t *testing.T) {
111135
"fromHeader": "bar",
112136
"partner-id-metadata": "test",
113137
},
138+
PathValues: map[string]any{
139+
"fromHeader": "foobar",
140+
"partner-id-pathValue": "test",
141+
},
114142
},
115143
},
116144
{
@@ -135,13 +163,24 @@ func testNewRequestBuildersSuccess(t *testing.T) {
135163
Parameter: "missing",
136164
},
137165
},
166+
PathValues: []Value{
167+
{
168+
Key: "fromParameter",
169+
Parameter: "pathValue",
170+
},
171+
{
172+
Key: "missing",
173+
Parameter: "missing",
174+
},
175+
},
138176
PartnerID: &PartnerID{
139177
Claim: "partner-id-claim",
140178
Metadata: "partner-id-metadata",
179+
PathValue: "partner-id-pathValue",
141180
Parameter: "pid",
142181
},
143182
},
144-
uri: "/test?pid=test&claim=foo&metadata=bar",
183+
uri: "/test?pid=test&claim=foo&metadata=bar&pathValue=foobar",
145184
expected: &Request{
146185
Logger: sallust.Default(),
147186
Claims: map[string]interface{}{
@@ -152,6 +191,10 @@ func testNewRequestBuildersSuccess(t *testing.T) {
152191
"fromParameter": "bar",
153192
"partner-id-metadata": "test",
154193
},
194+
PathValues: map[string]any{
195+
"fromParameter": "foobar",
196+
"partner-id-pathValue": "test",
197+
},
155198
},
156199
},
157200
{
@@ -168,17 +211,25 @@ func testNewRequestBuildersSuccess(t *testing.T) {
168211
Variable: "metadata",
169212
},
170213
},
214+
PathValues: []Value{
215+
{
216+
Key: "fromVariable",
217+
Variable: "pathValues",
218+
},
219+
},
171220
PartnerID: &PartnerID{
172221
Claim: "partner-id-claim",
173222
Metadata: "partner-id-metadata",
223+
PathValue: "partner-id-pathValue",
174224
Parameter: "pid",
175225
Default: "test",
176226
},
177227
},
178228
uri: "/test/foo/bar",
179229
urlVariables: map[string]string{
180-
"claim": "foo",
181-
"metadata": "bar",
230+
"claim": "foo",
231+
"metadata": "bar",
232+
"pathValues": "foobar",
182233
},
183234
expected: &Request{
184235
Logger: sallust.Default(),
@@ -190,6 +241,10 @@ func testNewRequestBuildersSuccess(t *testing.T) {
190241
"fromVariable": "bar",
191242
"partner-id-metadata": "test",
192243
},
244+
PathValues: map[string]any{
245+
"fromVariable": "foobar",
246+
"partner-id-pathValue": "test",
247+
},
193248
},
194249
},
195250
{
@@ -206,11 +261,18 @@ func testNewRequestBuildersSuccess(t *testing.T) {
206261
Variable: "metadata",
207262
},
208263
},
264+
PathValues: []Value{
265+
{
266+
Key: "fromVariable",
267+
Variable: "pathValue",
268+
},
269+
},
209270
},
210271
uri: "/test/foo/bar",
211272
urlVariables: map[string]string{
212-
"claim": "foo",
213-
"metadata": "bar",
273+
"claim": "foo",
274+
"metadata": "bar",
275+
"pathValue": "foobar",
214276
},
215277
expected: &Request{
216278
Logger: sallust.Default(),
@@ -220,6 +282,9 @@ func testNewRequestBuildersSuccess(t *testing.T) {
220282
Metadata: map[string]interface{}{
221283
"fromVariable": "bar",
222284
},
285+
PathValues: map[string]any{
286+
"fromVariable": "foobar",
287+
},
223288
},
224289
},
225290
}
@@ -316,6 +381,7 @@ func testNewRequestBuildersInvalidPartnerID(t *testing.T) {
316381
func TestNewRequestBuilders(t *testing.T) {
317382
t.Run("InvalidClaim", testNewRequestBuildersInvalidClaim)
318383
t.Run("InvalidMetadata", testNewRequestBuildersInvalidMetadata)
384+
t.Run("InvalidPathValues", testNewRequestBuildersInvalidPathValues)
319385
t.Run("MissingVariable", testNewRequestBuildersMissingVariable)
320386
t.Run("InvalidPartnerID", testNewRequestBuildersInvalidPartnerID)
321387
t.Run("Success", testNewRequestBuildersSuccess)
@@ -341,9 +407,10 @@ func testBuildRequestSuccess(t *testing.T) {
341407
}),
342408
},
343409
expected: &Request{
344-
Logger: sallust.Default(),
345-
Claims: map[string]interface{}{"claim": []int{1, 2, 3}},
346-
Metadata: make(map[string]interface{}),
410+
Logger: sallust.Default(),
411+
Claims: map[string]interface{}{"claim": []int{1, 2, 3}},
412+
Metadata: make(map[string]interface{}),
413+
PathValues: make(map[string]interface{}),
347414
},
348415
},
349416
{
@@ -354,9 +421,10 @@ func testBuildRequestSuccess(t *testing.T) {
354421
}),
355422
},
356423
expected: &Request{
357-
Logger: sallust.Default(),
358-
Claims: make(map[string]interface{}),
359-
Metadata: map[string]interface{}{"metadata": -75.8},
424+
Logger: sallust.Default(),
425+
Claims: make(map[string]interface{}),
426+
Metadata: map[string]interface{}{"metadata": -75.8},
427+
PathValues: make(map[string]interface{}),
360428
},
361429
},
362430
{
@@ -376,9 +444,10 @@ func testBuildRequestSuccess(t *testing.T) {
376444
}),
377445
},
378446
expected: &Request{
379-
Logger: sallust.Default(),
380-
Claims: map[string]interface{}{"claim1": 238947123, "claim2": []byte{1, 2, 3}},
381-
Metadata: map[string]interface{}{"metadata1": "value1", "metadata2": 15.7},
447+
Logger: sallust.Default(),
448+
Claims: map[string]interface{}{"claim1": 238947123, "claim2": []byte{1, 2, 3}},
449+
Metadata: map[string]interface{}{"metadata1": "value1", "metadata2": 15.7},
450+
PathValues: make(map[string]interface{}),
382451
},
383452
},
384453
}
@@ -569,9 +638,10 @@ func testDecodeServerRequestSuccess(t *testing.T) {
569638
require.IsType((*Request)(nil), v)
570639
assert.Equal(
571640
Request{
572-
Logger: sallust.Default(),
573-
Claims: map[string]interface{}{"claim": "value"},
574-
Metadata: make(map[string]interface{}),
641+
Logger: sallust.Default(),
642+
Claims: map[string]interface{}{"claim": "value"},
643+
Metadata: make(map[string]interface{}),
644+
PathValues: make(map[string]interface{}),
575645
},
576646
*v.(*Request),
577647
)

0 commit comments

Comments
 (0)