istio

Форк
0
/
generator_test.go 
540 строк · 12.7 Кб
1
// Copyright Istio Authors
2
//
3
// Licensed under the Apache License, Version 2.0 (the "License");
4
// you may not use this file except in compliance with the License.
5
// You may obtain a copy of the License at
6
//
7
//     http://www.apache.org/licenses/LICENSE-2.0
8
//
9
// Unless required by applicable law or agreed to in writing, software
10
// distributed under the License is distributed on an "AS IS" BASIS,
11
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
// See the License for the specific language governing permissions and
13
// limitations under the License.
14

15
package model
16

17
import (
18
	"testing"
19

20
	rbacpb "github.com/envoyproxy/go-control-plane/envoy/config/rbac/v3"
21
	"github.com/google/go-cmp/cmp"
22
	"google.golang.org/protobuf/proto"
23
	"google.golang.org/protobuf/testing/protocmp"
24

25
	"istio.io/istio/pkg/util/protomarshal"
26
)
27

28
func TestRequestPrincipal(t *testing.T) {
29
	cases := []struct {
30
		in   string
31
		want string
32
	}{
33
		{
34
			in: "*",
35
			want: `
36
        and_ids:
37
          ids:
38
          - metadata:
39
              filter: envoy.filters.http.jwt_authn
40
              path:
41
              - key: payload
42
              - key: iss
43
              value:
44
                string_match:
45
                  safe_regex: {regex: .+}
46
          - metadata:
47
              filter: envoy.filters.http.jwt_authn
48
              path:
49
              - key: payload
50
              - key: sub
51
              value:
52
                string_match:
53
                  safe_regex: {regex: .+}
54
`,
55
		},
56
		{
57
			in: "foo*",
58
			want: `
59
        and_ids:
60
          ids:
61
          - metadata:
62
              filter: envoy.filters.http.jwt_authn
63
              path:
64
              - key: payload
65
              - key: iss
66
              value:
67
                string_match:
68
                  prefix: foo
69
          - metadata:
70
              filter: envoy.filters.http.jwt_authn
71
              path:
72
              - key: payload
73
              - key: sub
74
              value:
75
                string_match:
76
                  safe_regex: {regex: .+}
77
`,
78
		},
79
		{
80
			in: "foo/*",
81
			want: `
82
        and_ids:
83
          ids:
84
          - metadata:
85
              filter: envoy.filters.http.jwt_authn
86
              path:
87
              - key: payload
88
              - key: iss
89
              value:
90
                string_match:
91
                  exact: foo
92
          - metadata:
93
              filter: envoy.filters.http.jwt_authn
94
              path:
95
              - key: payload
96
              - key: sub
97
              value:
98
                string_match:
99
                  safe_regex: {regex: .+}
100
`,
101
		},
102
		{
103
			in: "foo/bar*",
104
			want: `
105
        and_ids:
106
          ids:
107
          - metadata:
108
              filter: envoy.filters.http.jwt_authn
109
              path:
110
              - key: payload
111
              - key: iss
112
              value:
113
                string_match:
114
                  exact: foo
115
          - metadata:
116
              filter: envoy.filters.http.jwt_authn
117
              path:
118
              - key: payload
119
              - key: sub
120
              value:
121
                string_match:
122
                  prefix: bar
123
`,
124
		},
125
		{
126
			in: "*foo",
127
			want: `
128
        and_ids:
129
          ids:
130
          - metadata:
131
              filter: envoy.filters.http.jwt_authn
132
              path:
133
              - key: payload
134
              - key: iss
135
              value:
136
                string_match:
137
                  safe_regex: {regex: .+}
138
          - metadata:
139
              filter: envoy.filters.http.jwt_authn
140
              path:
141
              - key: payload
142
              - key: sub
143
              value:
144
                string_match:
145
                  suffix: foo
146
`,
147
		},
148
		{
149
			in: "*/foo",
150
			want: `
151
        and_ids:
152
          ids:
153
          - metadata:
154
              filter: envoy.filters.http.jwt_authn
155
              path:
156
              - key: payload
157
              - key: iss
158
              value:
159
                string_match:
160
                  safe_regex: {regex: .+}
161
          - metadata:
162
              filter: envoy.filters.http.jwt_authn
163
              path:
164
              - key: payload
165
              - key: sub
166
              value:
167
                string_match:
168
                  exact: foo
169
`,
170
		},
171
		{
172
			in: "*bar/foo",
173
			want: `
174
        and_ids:
175
          ids:
176
          - metadata:
177
              filter: envoy.filters.http.jwt_authn
178
              path:
179
              - key: payload
180
              - key: iss
181
              value:
182
                string_match:
183
                  suffix: bar
184
          - metadata:
185
              filter: envoy.filters.http.jwt_authn
186
              path:
187
              - key: payload
188
              - key: sub
189
              value:
190
                string_match:
191
                  exact: foo
192
`,
193
		},
194
		{
195
			in: "foo/bar",
196
			want: `
197
        and_ids:
198
          ids:
199
          - metadata:
200
              filter: envoy.filters.http.jwt_authn
201
              path:
202
              - key: payload
203
              - key: iss
204
              value:
205
                string_match:
206
                  exact: foo
207
          - metadata:
208
              filter: envoy.filters.http.jwt_authn
209
              path:
210
              - key: payload
211
              - key: sub
212
              value:
213
                string_match:
214
                  exact: bar
215
`,
216
		},
217
	}
218
	rpg := requestPrincipalGenerator{}
219
	for _, tc := range cases {
220
		t.Run(tc.in, func(t *testing.T) {
221
			got, err := rpg.extendedPrincipal("", []string{tc.in}, false)
222
			if err != nil {
223
				t.Fatal(err)
224
			}
225
			principal := yamlPrincipal(t, tc.want)
226
			if diff := cmp.Diff(got, principal, protocmp.Transform()); diff != "" {
227
				t.Errorf("diff detected: %v", diff)
228
			}
229
		})
230
	}
231
}
232

233
func TestGenerator(t *testing.T) {
234
	cases := []struct {
235
		name   string
236
		g      generator
237
		key    string
238
		value  string
239
		forTCP bool
240
		want   any
241
	}{
242
		{
243
			name:  "destIPGenerator",
244
			g:     destIPGenerator{},
245
			value: "1.2.3.4",
246
			want: yamlPermission(t, `
247
         destinationIp:
248
          addressPrefix: 1.2.3.4
249
          prefixLen: 32`),
250
		},
251
		{
252
			name:  "destPortGenerator",
253
			g:     destPortGenerator{},
254
			value: "80",
255
			want: yamlPermission(t, `
256
         destinationPort: 80`),
257
		},
258
		{
259
			name:  "connSNIGenerator",
260
			g:     connSNIGenerator{},
261
			value: "exact.com",
262
			want: yamlPermission(t, `
263
         requestedServerName:
264
          exact: exact.com`),
265
		},
266
		{
267
			name:  "envoyFilterGenerator-string",
268
			g:     envoyFilterGenerator{},
269
			key:   "experimental.a.b.c[d]",
270
			value: "val",
271
			want: yamlPermission(t, `
272
         metadata:
273
          filter: a.b.c
274
          path:
275
          - key: d
276
          value:
277
            stringMatch:
278
              exact: val`),
279
		},
280
		{
281
			name:  "envoyFilterGenerator-invalid",
282
			g:     envoyFilterGenerator{},
283
			key:   "experimental.a.b.c]",
284
			value: "val",
285
		},
286
		{
287
			name:  "envoyFilterGenerator-list",
288
			g:     envoyFilterGenerator{},
289
			key:   "experimental.a.b.c[d]",
290
			value: "[v1, v2]",
291
			want: yamlPermission(t, `
292
         metadata:
293
          filter: a.b.c
294
          path:
295
          - key: d
296
          value:
297
            listMatch:
298
              oneOf:
299
                stringMatch:
300
                  exact: v1, v2`),
301
		},
302
		{
303
			name:  "srcIPGenerator",
304
			g:     srcIPGenerator{},
305
			value: "1.2.3.4",
306
			want: yamlPrincipal(t, `
307
         directRemoteIp:
308
          addressPrefix: 1.2.3.4
309
          prefixLen: 32`),
310
		},
311
		{
312
			name:  "remoteIPGenerator",
313
			g:     remoteIPGenerator{},
314
			value: "1.2.3.4",
315
			want: yamlPrincipal(t, `
316
         remoteIp:
317
          addressPrefix: 1.2.3.4
318
          prefixLen: 32`),
319
		},
320
		{
321
			name:  "srcNamespaceGenerator-http",
322
			g:     srcNamespaceGenerator{},
323
			value: "foo",
324
			want: yamlPrincipal(t, `
325
         filter_state:
326
           key: io.istio.peer_principal
327
           string_match:
328
            safeRegex:
329
              regex: .*/ns/foo/.*`),
330
		},
331
		{
332
			name:   "srcNamespaceGenerator-tcp",
333
			g:      srcNamespaceGenerator{},
334
			value:  "foo",
335
			forTCP: true,
336
			want: yamlPrincipal(t, `
337
         filter_state:
338
           key: io.istio.peer_principal
339
           string_match:
340
            safeRegex:
341
              regex: .*/ns/foo/.*`),
342
		},
343
		{
344
			name:  "srcPrincipalGenerator-http",
345
			g:     srcPrincipalGenerator{},
346
			key:   "source.principal",
347
			value: "foo",
348
			want: yamlPrincipal(t, `
349
         filter_state:
350
           key: io.istio.peer_principal
351
           string_match:
352
            exact: spiffe://foo`),
353
		},
354
		{
355
			name:   "srcPrincipalGenerator-tcp",
356
			g:      srcPrincipalGenerator{},
357
			key:    "source.principal",
358
			value:  "foo",
359
			forTCP: true,
360
			want: yamlPrincipal(t, `
361
         filter_state:
362
           key: io.istio.peer_principal
363
           string_match:
364
            exact: spiffe://foo`),
365
		},
366
		{
367
			name:  "requestPrincipalGenerator",
368
			g:     requestPrincipalGenerator{},
369
			key:   "request.auth.principal",
370
			value: "foo",
371
			want: yamlPrincipal(t, `
372
         metadata:
373
          filter: istio_authn
374
          path:
375
          - key: request.auth.principal
376
          value:
377
            stringMatch:
378
              exact: foo`),
379
		},
380
		{
381
			name:  "requestAudiencesGenerator",
382
			g:     requestAudiencesGenerator{},
383
			key:   "request.auth.audiences",
384
			value: "foo",
385
			want: yamlPrincipal(t, `
386
         metadata:
387
          filter: istio_authn
388
          path:
389
          - key: request.auth.audiences
390
          value:
391
            stringMatch:
392
              exact: foo`),
393
		},
394
		{
395
			name:  "requestPresenterGenerator",
396
			g:     requestPresenterGenerator{},
397
			key:   "request.auth.presenter",
398
			value: "foo",
399
			want: yamlPrincipal(t, `
400
         metadata:
401
          filter: istio_authn
402
          path:
403
          - key: request.auth.presenter
404
          value:
405
            stringMatch:
406
              exact: foo`),
407
		},
408
		{
409
			name:  "requestHeaderGenerator",
410
			g:     requestHeaderGenerator{},
411
			key:   "request.headers[x-foo]",
412
			value: "foo",
413
			want: yamlPrincipal(t, `
414
        header:
415
          name: x-foo
416
          stringMatch:
417
            exact: foo`),
418
		},
419
		{
420
			name:  "requestClaimGenerator",
421
			g:     requestClaimGenerator{},
422
			key:   "request.auth.claims[bar]",
423
			value: "foo",
424
			want: yamlPrincipal(t, `
425
         metadata:
426
          filter: istio_authn
427
          path:
428
          - key: request.auth.claims
429
          - key: bar
430
          value:
431
            listMatch:
432
              oneOf:
433
                stringMatch:
434
                  exact: foo`),
435
		},
436
		{
437
			name:  "requestNestedClaimsGenerator",
438
			g:     requestClaimGenerator{},
439
			key:   "request.auth.claims[bar][baz]",
440
			value: "foo",
441
			want: yamlPrincipal(t, `
442
         metadata:
443
          filter: istio_authn
444
          path:
445
          - key: request.auth.claims
446
          - key: bar
447
          - key: baz
448
          value:
449
            listMatch:
450
              oneOf:
451
                stringMatch:
452
                  exact: foo`),
453
		},
454
		{
455
			name:  "hostGenerator",
456
			g:     hostGenerator{},
457
			value: "foo",
458
			want: yamlPermission(t, `
459
         header:
460
          stringMatch:
461
            exact: foo
462
            ignoreCase: true
463
          name: :authority`),
464
		},
465
		{
466
			name:  "pathGenerator",
467
			g:     pathGenerator{},
468
			value: "/abc",
469
			want: yamlPermission(t, `
470
         urlPath:
471
          path:
472
            exact: /abc`),
473
		},
474
		{
475
			name:  "methodGenerator",
476
			g:     methodGenerator{},
477
			value: "GET",
478
			want: yamlPermission(t, `
479
         header:
480
          name: :method
481
          stringMatch:
482
            exact: GET`),
483
		},
484
	}
485

486
	for _, tc := range cases {
487
		t.Run(tc.name, func(t *testing.T) {
488
			var got any
489
			var err error
490
			// nolint: gocritic
491
			if _, ok := tc.want.(*rbacpb.Permission); ok {
492
				got, err = tc.g.permission(tc.key, tc.value, tc.forTCP)
493
				if err != nil {
494
					t.Errorf("both permission and principal returned error")
495
				}
496
			} else if _, ok := tc.want.(*rbacpb.Principal); ok {
497
				got, err = tc.g.principal(tc.key, tc.value, tc.forTCP, false)
498
				if err != nil {
499
					t.Errorf("both permission and principal returned error")
500
				}
501
			} else {
502
				_, err1 := tc.g.principal(tc.key, tc.value, tc.forTCP, false)
503
				_, err2 := tc.g.permission(tc.key, tc.value, tc.forTCP)
504
				if err1 == nil || err2 == nil {
505
					t.Fatalf("wanted error")
506
				}
507
				return
508
			}
509
			if diff := cmp.Diff(got, tc.want, protocmp.Transform()); diff != "" {
510
				var gotYaml string
511
				gotProto, ok := got.(proto.Message)
512
				if !ok {
513
					t.Fatal("failed to extract proto")
514
				}
515
				if gotYaml, err = protomarshal.ToYAML(gotProto); err != nil {
516
					t.Fatalf("%s: failed to parse yaml: %s", tc.name, err)
517
				}
518
				t.Errorf("got:\n %v\n but want:\n %v", gotYaml, tc.want)
519
			}
520
		})
521
	}
522
}
523

524
func yamlPermission(t *testing.T, yaml string) *rbacpb.Permission {
525
	t.Helper()
526
	p := &rbacpb.Permission{}
527
	if err := protomarshal.ApplyYAML(yaml, p); err != nil {
528
		t.Fatalf("failed to parse yaml: %s", err)
529
	}
530
	return p
531
}
532

533
func yamlPrincipal(t *testing.T, yaml string) *rbacpb.Principal {
534
	t.Helper()
535
	p := &rbacpb.Principal{}
536
	if err := protomarshal.ApplyYAML(yaml, p); err != nil {
537
		t.Fatalf("failed to parse yaml: %s", err)
538
	}
539
	return p
540
}
541

Использование cookies

Мы используем файлы cookie в соответствии с Политикой конфиденциальности и Политикой использования cookies.

Нажимая кнопку «Принимаю», Вы даете АО «СберТех» согласие на обработку Ваших персональных данных в целях совершенствования нашего веб-сайта и Сервиса GitVerse, а также повышения удобства их использования.

Запретить использование cookies Вы можете самостоятельно в настройках Вашего браузера.