gitee.com/mysnapcore/mysnapd@v0.1.0/asserts/constraint_test.go (about) 1 // -*- Mode: Go; indent-tabs-mode: t -*- 2 3 /* 4 * Copyright (C) 2015-2022 Canonical Ltd 5 * 6 * This program is free software: you can redistribute it and/or modify 7 * it under the terms of the GNU General Public License version 3 as 8 * published by the Free Software Foundation. 9 * 10 * This program is distributed in the hope that it will be useful, 11 * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 * GNU General Public License for more details. 14 * 15 * You should have received a copy of the GNU General Public License 16 * along with this program. If not, see <http://www.gnu.org/licenses/>. 17 * 18 */ 19 20 package asserts_test 21 22 import ( 23 "fmt" 24 "regexp" 25 26 . "gopkg.in/check.v1" 27 "gopkg.in/yaml.v2" 28 29 "gitee.com/mysnapcore/mysnapd/asserts" 30 "gitee.com/mysnapcore/mysnapd/metautil" 31 "gitee.com/mysnapcore/mysnapd/snap" 32 "gitee.com/mysnapcore/mysnapd/testutil" 33 ) 34 35 type attrMatcherSuite struct { 36 testutil.BaseTest 37 } 38 39 var _ = Suite(&attrMatcherSuite{}) 40 41 func vals(yml string) map[string]interface{} { 42 var vs map[string]interface{} 43 err := yaml.Unmarshal([]byte(yml), &vs) 44 if err != nil { 45 panic(err) 46 } 47 v, err := metautil.NormalizeValue(vs) 48 if err != nil { 49 panic(err) 50 } 51 return v.(map[string]interface{}) 52 } 53 54 func (s *attrMatcherSuite) SetUpTest(c *C) { 55 s.BaseTest.SetUpTest(c) 56 s.BaseTest.AddCleanup(snap.MockSanitizePlugsSlots(func(snapInfo *snap.Info) {})) 57 } 58 59 func (s *attrMatcherSuite) TestSimple(c *C) { 60 m, err := asserts.ParseHeaders([]byte(`attrs: 61 foo: FOO 62 bar: BAR`)) 63 c.Assert(err, IsNil) 64 65 domatch, err := asserts.CompileAttrMatcher(m["attrs"].(map[string]interface{}), nil) 66 c.Assert(err, IsNil) 67 68 values := map[string]interface{}{ 69 "foo": "FOO", 70 "bar": "BAR", 71 "baz": "BAZ", 72 } 73 err = domatch(values, nil) 74 c.Check(err, IsNil) 75 76 values = map[string]interface{}{ 77 "foo": "FOO", 78 "bar": "BAZ", 79 "baz": "BAZ", 80 } 81 err = domatch(values, nil) 82 c.Check(err, ErrorMatches, `field "bar" value "BAZ" does not match \^\(BAR\)\$`) 83 84 values = map[string]interface{}{ 85 "foo": "FOO", 86 "baz": "BAZ", 87 } 88 err = domatch(values, nil) 89 c.Check(err, ErrorMatches, `field "bar" has constraints but is unset`) 90 } 91 92 func (s *attrMatcherSuite) TestSimpleAnchorsVsRegexpAlt(c *C) { 93 m, err := asserts.ParseHeaders([]byte(`attrs: 94 bar: BAR|BAZ`)) 95 c.Assert(err, IsNil) 96 97 domatch, err := asserts.CompileAttrMatcher(m["attrs"].(map[string]interface{}), nil) 98 c.Assert(err, IsNil) 99 100 values := map[string]interface{}{ 101 "bar": "BAR", 102 } 103 err = domatch(values, nil) 104 c.Check(err, IsNil) 105 106 values = map[string]interface{}{ 107 "bar": "BARR", 108 } 109 err = domatch(values, nil) 110 c.Check(err, ErrorMatches, `field "bar" value "BARR" does not match \^\(BAR|BAZ\)\$`) 111 112 values = map[string]interface{}{ 113 "bar": "BBAZ", 114 } 115 err = domatch(values, nil) 116 c.Check(err, ErrorMatches, `field "bar" value "BAZZ" does not match \^\(BAR|BAZ\)\$`) 117 118 values = map[string]interface{}{ 119 "bar": "BABAZ", 120 } 121 err = domatch(values, nil) 122 c.Check(err, ErrorMatches, `field "bar" value "BABAZ" does not match \^\(BAR|BAZ\)\$`) 123 124 values = map[string]interface{}{ 125 "bar": "BARAZ", 126 } 127 err = domatch(values, nil) 128 c.Check(err, ErrorMatches, `field "bar" value "BARAZ" does not match \^\(BAR|BAZ\)\$`) 129 } 130 131 func (s *attrMatcherSuite) TestNested(c *C) { 132 m, err := asserts.ParseHeaders([]byte(`attrs: 133 foo: FOO 134 bar: 135 bar1: BAR1 136 bar2: BAR2`)) 137 c.Assert(err, IsNil) 138 139 domatch, err := asserts.CompileAttrMatcher(m["attrs"].(map[string]interface{}), nil) 140 c.Assert(err, IsNil) 141 142 err = domatch(vals(` 143 foo: FOO 144 bar: 145 bar1: BAR1 146 bar2: BAR2 147 bar3: BAR3 148 baz: BAZ 149 `), nil) 150 c.Check(err, IsNil) 151 152 err = domatch(vals(` 153 foo: FOO 154 bar: BAZ 155 baz: BAZ 156 `), nil) 157 c.Check(err, ErrorMatches, `field "bar" must be a map`) 158 159 err = domatch(vals(` 160 foo: FOO 161 bar: 162 bar1: BAR1 163 bar2: BAR22 164 bar3: BAR3 165 baz: BAZ 166 `), nil) 167 c.Check(err, ErrorMatches, `field "bar\.bar2" value "BAR22" does not match \^\(BAR2\)\$`) 168 169 err = domatch(vals(` 170 foo: FOO 171 bar: 172 bar1: BAR1 173 bar2: 174 bar22: true 175 bar3: BAR3 176 baz: BAZ 177 `), nil) 178 c.Check(err, ErrorMatches, `field "bar\.bar2" must be a scalar or list`) 179 } 180 181 func (s *attrMatcherSuite) TestAlternative(c *C) { 182 m, err := asserts.ParseHeaders([]byte(`attrs: 183 - 184 foo: FOO 185 bar: BAR 186 - 187 foo: FOO 188 bar: BAZ`)) 189 c.Assert(err, IsNil) 190 191 domatch, err := asserts.CompileAttrMatcher(m["attrs"], nil) 192 c.Assert(err, IsNil) 193 194 values := map[string]interface{}{ 195 "foo": "FOO", 196 "bar": "BAR", 197 "baz": "BAZ", 198 } 199 err = domatch(values, nil) 200 c.Check(err, IsNil) 201 202 values = map[string]interface{}{ 203 "foo": "FOO", 204 "bar": "BAZ", 205 "baz": "BAZ", 206 } 207 err = domatch(values, nil) 208 c.Check(err, IsNil) 209 210 values = map[string]interface{}{ 211 "foo": "FOO", 212 "bar": "BARR", 213 "baz": "BAR", 214 } 215 err = domatch(values, nil) 216 c.Check(err, ErrorMatches, `no alternative matches: field "bar" value "BARR" does not match \^\(BAR\)\$`) 217 } 218 219 func (s *attrMatcherSuite) TestNestedAlternative(c *C) { 220 m, err := asserts.ParseHeaders([]byte(`attrs: 221 foo: FOO 222 bar: 223 bar1: BAR1 224 bar2: 225 - BAR2 226 - BAR22`)) 227 c.Assert(err, IsNil) 228 229 domatch, err := asserts.CompileAttrMatcher(m["attrs"].(map[string]interface{}), nil) 230 c.Assert(err, IsNil) 231 232 err = domatch(vals(` 233 foo: FOO 234 bar: 235 bar1: BAR1 236 bar2: BAR2 237 `), nil) 238 c.Check(err, IsNil) 239 240 err = domatch(vals(` 241 foo: FOO 242 bar: 243 bar1: BAR1 244 bar2: BAR22 245 `), nil) 246 c.Check(err, IsNil) 247 248 err = domatch(vals(` 249 foo: FOO 250 bar: 251 bar1: BAR1 252 bar2: BAR3 253 `), nil) 254 c.Check(err, ErrorMatches, `no alternative for field "bar\.bar2" matches: field "bar\.bar2" value "BAR3" does not match \^\(BAR2\)\$`) 255 } 256 257 func (s *attrMatcherSuite) TestAlternativeMatchingStringList(c *C) { 258 toMatch := vals(` 259 write: 260 - /var/tmp 261 - /var/lib/snapd/snapshots 262 `) 263 m, err := asserts.ParseHeaders([]byte(`attrs: 264 write: /var/(tmp|lib/snapd/snapshots)`)) 265 c.Assert(err, IsNil) 266 267 domatch, err := asserts.CompileAttrMatcher(m["attrs"].(map[string]interface{}), nil) 268 c.Assert(err, IsNil) 269 270 err = domatch(toMatch, nil) 271 c.Check(err, IsNil) 272 273 m, err = asserts.ParseHeaders([]byte(`attrs: 274 write: 275 - /var/tmp 276 - /var/lib/snapd/snapshots`)) 277 c.Assert(err, IsNil) 278 279 domatchLst, err := asserts.CompileAttrMatcher(m["attrs"].(map[string]interface{}), nil) 280 c.Assert(err, IsNil) 281 282 err = domatchLst(toMatch, nil) 283 c.Check(err, IsNil) 284 } 285 286 func (s *attrMatcherSuite) TestAlternativeMatchingComplex(c *C) { 287 toMatch := vals(` 288 mnt: [{what: "/dev/x*", where: "/foo/*", options: ["rw", "nodev"]}, {what: "/bar/*", where: "/baz/*", options: ["rw", "bind"]}] 289 `) 290 291 m, err := asserts.ParseHeaders([]byte(`attrs: 292 mnt: 293 - 294 what: /(bar/|dev/x)\* 295 where: /(foo|baz)/\* 296 options: rw|bind|nodev`)) 297 c.Assert(err, IsNil) 298 299 domatch, err := asserts.CompileAttrMatcher(m["attrs"].(map[string]interface{}), nil) 300 c.Assert(err, IsNil) 301 302 err = domatch(toMatch, nil) 303 c.Check(err, IsNil) 304 305 m, err = asserts.ParseHeaders([]byte(`attrs: 306 mnt: 307 - 308 what: /dev/x\* 309 where: /foo/\* 310 options: 311 - nodev 312 - rw 313 - 314 what: /bar/\* 315 where: /baz/\* 316 options: 317 - rw 318 - bind`)) 319 c.Assert(err, IsNil) 320 321 domatchExtensive, err := asserts.CompileAttrMatcher(m["attrs"].(map[string]interface{}), nil) 322 c.Assert(err, IsNil) 323 324 err = domatchExtensive(toMatch, nil) 325 c.Check(err, IsNil) 326 327 // not matching case 328 m, err = asserts.ParseHeaders([]byte(`attrs: 329 mnt: 330 - 331 what: /dev/x\* 332 where: /foo/\* 333 options: 334 - rw 335 - 336 what: /bar/\* 337 where: /baz/\* 338 options: 339 - rw 340 - bind`)) 341 c.Assert(err, IsNil) 342 343 domatchExtensiveNoMatch, err := asserts.CompileAttrMatcher(m["attrs"].(map[string]interface{}), nil) 344 c.Assert(err, IsNil) 345 346 err = domatchExtensiveNoMatch(toMatch, nil) 347 c.Check(err, ErrorMatches, `no alternative for field "mnt\.0" matches: no alternative for field "mnt\.0.options\.1" matches:.*`) 348 } 349 350 func (s *attrMatcherSuite) TestOtherScalars(c *C) { 351 m, err := asserts.ParseHeaders([]byte(`attrs: 352 foo: 1 353 bar: true`)) 354 c.Assert(err, IsNil) 355 356 domatch, err := asserts.CompileAttrMatcher(m["attrs"].(map[string]interface{}), nil) 357 c.Assert(err, IsNil) 358 359 err = domatch(vals(` 360 foo: 1 361 bar: true 362 `), nil) 363 c.Check(err, IsNil) 364 365 values := map[string]interface{}{ 366 "foo": int64(1), 367 "bar": true, 368 } 369 err = domatch(values, nil) 370 c.Check(err, IsNil) 371 } 372 373 func (s *attrMatcherSuite) TestCompileErrors(c *C) { 374 _, err := asserts.CompileAttrMatcher(1, nil) 375 c.Check(err, ErrorMatches, `top constraint must be a key-value map, regexp or a list of alternative constraints: 1`) 376 377 _, err = asserts.CompileAttrMatcher(map[string]interface{}{ 378 "foo": 1, 379 }, nil) 380 c.Check(err, ErrorMatches, `constraint "foo" must be a key-value map, regexp or a list of alternative constraints: 1`) 381 382 _, err = asserts.CompileAttrMatcher(map[string]interface{}{ 383 "foo": "[", 384 }, nil) 385 c.Check(err, ErrorMatches, `cannot compile "foo" constraint "\[": error parsing regexp:.*`) 386 387 _, err = asserts.CompileAttrMatcher(map[string]interface{}{ 388 "foo": []interface{}{"foo", "["}, 389 }, nil) 390 c.Check(err, ErrorMatches, `cannot compile "foo/alt#2/" constraint "\[": error parsing regexp:.*`) 391 392 _, err = asserts.CompileAttrMatcher(map[string]interface{}{ 393 "foo": []interface{}{"foo", []interface{}{"bar", "baz"}}, 394 }, nil) 395 c.Check(err, ErrorMatches, `cannot nest alternative constraints directly at "foo/alt#2/"`) 396 397 _, err = asserts.CompileAttrMatcher("FOO", nil) 398 c.Check(err, ErrorMatches, `first level of non alternative constraints must be a set of key-value contraints`) 399 400 _, err = asserts.CompileAttrMatcher([]interface{}{"FOO"}, nil) 401 c.Check(err, ErrorMatches, `first level of non alternative constraints must be a set of key-value contraints`) 402 403 _, err = asserts.CompileAttrMatcher(map[string]interface{}{ 404 "foo": "$FOO()", 405 }, nil) 406 c.Check(err, ErrorMatches, `cannot compile "foo" constraint "\$FOO\(\)": no \$OP\(\) constraints supported`) 407 408 wrongDollarConstraints := []string{ 409 "$", 410 "$FOO(a)", 411 "$SLOT", 412 "$SLOT()", 413 "$SLOT(x,y)", 414 "$SLOT(x,y,z)", 415 } 416 417 for _, wrong := range wrongDollarConstraints { 418 _, err := asserts.CompileAttrMatcher(map[string]interface{}{ 419 "foo": wrong, 420 }, []string{"SLOT", "OP"}) 421 if wrong != "$SLOT(x,y)" { 422 c.Check(err, ErrorMatches, fmt.Sprintf(`cannot compile "foo" constraint "%s": not a valid \$SLOT\(\)/\$OP\(\) constraint`, regexp.QuoteMeta(wrong))) 423 } else { 424 c.Check(err, ErrorMatches, fmt.Sprintf(`cannot compile "foo" constraint "%s": \$SLOT\(\) constraint expects 1 argument`, regexp.QuoteMeta(wrong))) 425 } 426 427 } 428 } 429 430 func (s *attrMatcherSuite) TestMatchingListsSimple(c *C) { 431 m, err := asserts.ParseHeaders([]byte(`attrs: 432 foo: /foo/.*`)) 433 c.Assert(err, IsNil) 434 435 domatch, err := asserts.CompileAttrMatcher(m["attrs"].(map[string]interface{}), nil) 436 c.Assert(err, IsNil) 437 438 err = domatch(vals(` 439 foo: ["/foo/x", "/foo/y"] 440 `), nil) 441 c.Check(err, IsNil) 442 443 err = domatch(vals(` 444 foo: ["/foo/x", "/foo"] 445 `), nil) 446 c.Check(err, ErrorMatches, `field "foo\.1" value "/foo" does not match \^\(/foo/\.\*\)\$`) 447 } 448 449 func (s *attrMatcherSuite) TestMissingCheck(c *C) { 450 m, err := asserts.ParseHeaders([]byte(`attrs: 451 foo: $MISSING`)) 452 c.Assert(err, IsNil) 453 454 domatch, err := asserts.CompileAttrMatcher(m["attrs"].(map[string]interface{}), nil) 455 c.Assert(err, IsNil) 456 457 err = domatch(vals(` 458 bar: baz 459 `), nil) 460 c.Check(err, IsNil) 461 462 err = domatch(vals(` 463 foo: ["x"] 464 `), nil) 465 c.Check(err, ErrorMatches, `field "foo" is constrained to be missing but is set`) 466 } 467 468 func (s *attrMatcherSuite) TestEvalCheck(c *C) { 469 // TODO: consider rewriting once we have $WITHIN 470 m, err := asserts.ParseHeaders([]byte(`attrs: 471 foo: $SLOT(foo) 472 bar: $PLUG(bar.baz)`)) 473 c.Assert(err, IsNil) 474 475 domatch, err := asserts.CompileAttrMatcher(m["attrs"].(map[string]interface{}), []string{"SLOT", "PLUG"}) 476 c.Assert(err, IsNil) 477 478 err = domatch(vals(` 479 foo: foo 480 bar: bar 481 `), nil) 482 c.Check(err, ErrorMatches, `field "(foo|bar)" cannot be matched without context`) 483 484 calls := make(map[[2]string]bool) 485 comp1 := func(op string, arg string) (interface{}, error) { 486 calls[[2]string{op, arg}] = true 487 return arg, nil 488 } 489 490 err = domatch(vals(` 491 foo: foo 492 bar: bar.baz 493 `), testEvalAttr{comp1}) 494 c.Check(err, IsNil) 495 496 c.Check(calls, DeepEquals, map[[2]string]bool{ 497 {"slot", "foo"}: true, 498 {"plug", "bar.baz"}: true, 499 }) 500 501 comp2 := func(op string, arg string) (interface{}, error) { 502 if op == "plug" { 503 return nil, fmt.Errorf("boom") 504 } 505 return arg, nil 506 } 507 508 err = domatch(vals(` 509 foo: foo 510 bar: bar.baz 511 `), testEvalAttr{comp2}) 512 c.Check(err, ErrorMatches, `field "bar" constraint \$PLUG\(bar\.baz\) cannot be evaluated: boom`) 513 514 comp3 := func(op string, arg string) (interface{}, error) { 515 if op == "slot" { 516 return "other-value", nil 517 } 518 return arg, nil 519 } 520 521 err = domatch(vals(` 522 foo: foo 523 bar: bar.baz 524 `), testEvalAttr{comp3}) 525 c.Check(err, ErrorMatches, `field "foo" does not match \$SLOT\(foo\): foo != other-value`) 526 } 527 528 func (s *attrMatcherSuite) TestMatchingListsMap(c *C) { 529 m, err := asserts.ParseHeaders([]byte(`attrs: 530 foo: 531 p: /foo/.*`)) 532 c.Assert(err, IsNil) 533 534 domatch, err := asserts.CompileAttrMatcher(m["attrs"].(map[string]interface{}), nil) 535 c.Assert(err, IsNil) 536 537 err = domatch(vals(` 538 foo: [{p: "/foo/x"}, {p: "/foo/y"}] 539 `), nil) 540 c.Check(err, IsNil) 541 542 err = domatch(vals(` 543 foo: [{p: "zzz"}, {p: "/foo/y"}] 544 `), nil) 545 c.Check(err, ErrorMatches, `field "foo\.0\.p" value "zzz" does not match \^\(/foo/\.\*\)\$`) 546 } 547 548 type deviceScopeConstraintSuite struct { 549 testutil.BaseTest 550 } 551 552 var _ = Suite(&deviceScopeConstraintSuite{}) 553 554 func (s *deviceScopeConstraintSuite) TestCompile(c *C) { 555 tests := []struct { 556 m map[string]interface{} 557 exp *asserts.DeviceScopeConstraint 558 err string 559 }{ 560 {m: nil, exp: nil}, 561 {m: map[string]interface{}{"on-store": []interface{}{"foo", "bar"}}, exp: &asserts.DeviceScopeConstraint{Store: []string{"foo", "bar"}}}, 562 {m: map[string]interface{}{"on-brand": []interface{}{"foo", "bar"}}, exp: &asserts.DeviceScopeConstraint{Brand: []string{"foo", "bar"}}}, 563 {m: map[string]interface{}{"on-model": []interface{}{"foo/model1", "bar/model-2"}}, exp: &asserts.DeviceScopeConstraint{Model: []string{"foo/model1", "bar/model-2"}}}, 564 { 565 m: map[string]interface{}{ 566 "on-brand": []interface{}{"foo", "bar"}, 567 "on-model": []interface{}{"foo/model1", "bar/model-2"}, 568 "on-store": []interface{}{"foo", "bar"}, 569 }, 570 exp: &asserts.DeviceScopeConstraint{ 571 Store: []string{"foo", "bar"}, 572 Brand: []string{"foo", "bar"}, 573 Model: []string{"foo/model1", "bar/model-2"}, 574 }, 575 }, 576 {m: map[string]interface{}{"on-store": ""}, err: `on-store in constraint must be a list of strings`}, 577 {m: map[string]interface{}{"on-brand": "foo"}, err: `on-brand in constraint must be a list of strings`}, 578 {m: map[string]interface{}{"on-model": map[string]interface{}{"brand": "x"}}, err: `on-model in constraint must be a list of strings`}, 579 } 580 581 for _, t := range tests { 582 dsc, err := asserts.CompileDeviceScopeConstraint(t.m, "constraint") 583 if t.err == "" { 584 c.Check(err, IsNil) 585 c.Check(dsc, DeepEquals, t.exp) 586 } else { 587 c.Check(err, ErrorMatches, t.err) 588 c.Check(dsc, IsNil) 589 } 590 } 591 } 592 593 func (s *deviceScopeConstraintSuite) TestValidOnStoreBrandModel(c *C) { 594 tests := []struct { 595 constr string 596 value string 597 valid bool 598 }{ 599 {"on-store", "", false}, 600 {"on-store", "foo", true}, 601 {"on-store", "F_o-O88", true}, 602 {"on-store", "foo!", false}, 603 {"on-store", "foo.", false}, 604 {"on-store", "foo/", false}, 605 {"on-brand", "", false}, 606 // custom set brands (length 2-28) 607 {"on-brand", "dwell", true}, 608 {"on-brand", "Dwell", false}, 609 {"on-brand", "dwell-88", true}, 610 {"on-brand", "dwell_88", false}, 611 {"on-brand", "dwell.88", false}, 612 {"on-brand", "dwell:88", false}, 613 {"on-brand", "dwell!88", false}, 614 {"on-brand", "a", false}, 615 {"on-brand", "ab", true}, 616 {"on-brand", "0123456789012345678901234567", true}, 617 // snappy id brands (fixed length 32) 618 {"on-brand", "01234567890123456789012345678", false}, 619 {"on-brand", "012345678901234567890123456789", false}, 620 {"on-brand", "0123456789012345678901234567890", false}, 621 {"on-brand", "01234567890123456789012345678901", true}, 622 {"on-brand", "abcdefghijklmnopqrstuvwxyz678901", true}, 623 {"on-brand", "ABCDEFGHIJKLMNOPQRSTUVWCYZ678901", true}, 624 {"on-brand", "ABCDEFGHIJKLMNOPQRSTUVWCYZ678901X", false}, 625 {"on-brand", "ABCDEFGHIJKLMNOPQ!STUVWCYZ678901", false}, 626 {"on-brand", "ABCDEFGHIJKLMNOPQ_STUVWCYZ678901", false}, 627 {"on-brand", "ABCDEFGHIJKLMNOPQ-STUVWCYZ678901", false}, 628 {"on-model", "", false}, 629 {"on-model", "/", false}, 630 {"on-model", "dwell/dwell1", true}, 631 {"on-model", "dwell", false}, 632 {"on-model", "dwell/", false}, 633 {"on-model", "dwell//dwell1", false}, 634 {"on-model", "dwell/-dwell1", false}, 635 {"on-model", "dwell/dwell1-", false}, 636 {"on-model", "dwell/dwell1-23", true}, 637 {"on-model", "dwell/dwell1!", false}, 638 {"on-model", "dwell/dwe_ll1", false}, 639 {"on-model", "dwell/dwe.ll1", false}, 640 } 641 642 check := func(constr, value string, valid bool) { 643 cMap := map[string]interface{}{ 644 constr: []interface{}{value}, 645 } 646 647 _, err := asserts.CompileDeviceScopeConstraint(cMap, "constraint") 648 if valid { 649 c.Check(err, IsNil, Commentf("%v", cMap)) 650 } else { 651 c.Check(err, ErrorMatches, fmt.Sprintf(`%s in constraint contains an invalid element: %q`, constr, value), Commentf("%v", cMap)) 652 } 653 } 654 655 for _, t := range tests { 656 check(t.constr, t.value, t.valid) 657 658 if t.constr == "on-brand" { 659 // reuse and double check all brands also in the context of on-model! 660 661 check("on-model", t.value+"/foo", t.valid) 662 } 663 } 664 } 665 666 func (s *deviceScopeConstraintSuite) TestCheck(c *C) { 667 a, err := asserts.Decode([]byte(`type: model 668 authority-id: my-brand 669 series: 16 670 brand-id: my-brand 671 model: my-model1 672 store: store1 673 architecture: armhf 674 kernel: krnl 675 gadget: gadget 676 timestamp: 2018-09-12T12:00:00Z 677 sign-key-sha3-384: Jv8_JiHiIzJVcO9M55pPdqSDWUvuhfDIBJUS-3VW7F_idjix7Ffn5qMxB21ZQuij 678 679 AXNpZw==`)) 680 c.Assert(err, IsNil) 681 myModel1 := a.(*asserts.Model) 682 683 a, err = asserts.Decode([]byte(`type: model 684 authority-id: my-brand-subbrand 685 series: 16 686 brand-id: my-brand-subbrand 687 model: my-model2 688 store: store2 689 architecture: armhf 690 kernel: krnl 691 gadget: gadget 692 timestamp: 2018-09-12T12:00:00Z 693 sign-key-sha3-384: Jv8_JiHiIzJVcO9M55pPdqSDWUvuhfDIBJUS-3VW7F_idjix7Ffn5qMxB21ZQuij 694 695 AXNpZw==`)) 696 c.Assert(err, IsNil) 697 myModel2 := a.(*asserts.Model) 698 699 a, err = asserts.Decode([]byte(`type: model 700 authority-id: my-brand 701 series: 16 702 brand-id: my-brand 703 model: my-model3 704 store: substore1 705 architecture: armhf 706 kernel: krnl 707 gadget: gadget 708 timestamp: 2018-09-12T12:00:00Z 709 sign-key-sha3-384: Jv8_JiHiIzJVcO9M55pPdqSDWUvuhfDIBJUS-3VW7F_idjix7Ffn5qMxB21ZQuij 710 711 AXNpZw==`)) 712 c.Assert(err, IsNil) 713 myModel3 := a.(*asserts.Model) 714 715 a, err = asserts.Decode([]byte(`type: store 716 store: substore1 717 authority-id: canonical 718 operator-id: canonical 719 friendly-stores: 720 - a-store 721 - store1 722 - store2 723 timestamp: 2018-09-12T12:00:00Z 724 sign-key-sha3-384: Jv8_JiHiIzJVcO9M55pPdqSDWUvuhfDIBJUS-3VW7F_idjix7Ffn5qMxB21ZQuij 725 726 AXNpZw==`)) 727 c.Assert(err, IsNil) 728 substore1 := a.(*asserts.Store) 729 730 tests := []struct { 731 m map[string]interface{} 732 model *asserts.Model 733 store *asserts.Store 734 useFriendlyStores bool 735 err string 736 }{ 737 {m: map[string]interface{}{"on-store": []interface{}{"store1"}}, model: myModel1}, 738 {m: map[string]interface{}{"on-store": []interface{}{"a-store", "store1"}}, model: myModel1}, 739 {m: map[string]interface{}{"on-store": []interface{}{"store1"}}, model: myModel2, err: "on-store mismatch"}, 740 {m: map[string]interface{}{"on-store": []interface{}{"store1"}}, model: myModel3, store: substore1, useFriendlyStores: true}, 741 {m: map[string]interface{}{"on-store": []interface{}{"store1"}}, model: myModel3, store: substore1, useFriendlyStores: false, err: "on-store mismatch"}, 742 {m: map[string]interface{}{"on-store": []interface{}{"store1"}}, model: nil, err: `cannot match on-store/on-brand/on-model without model`}, 743 {m: map[string]interface{}{"on-store": []interface{}{"store1"}}, model: myModel2, store: substore1, err: `store assertion and model store must match`, useFriendlyStores: true}, 744 {m: map[string]interface{}{"on-store": []interface{}{"other-store"}}, model: myModel3, store: substore1, err: "on-store mismatch"}, 745 {m: map[string]interface{}{"on-brand": []interface{}{"my-brand"}}, model: myModel1}, 746 {m: map[string]interface{}{"on-brand": []interface{}{"my-brand", "my-brand-subbrand"}}, model: myModel2}, 747 {m: map[string]interface{}{"on-brand": []interface{}{"other-brand"}}, model: myModel2, err: "on-brand mismatch"}, 748 {m: map[string]interface{}{"on-model": []interface{}{"my-brand/my-model1"}}, model: myModel1}, 749 {m: map[string]interface{}{"on-model": []interface{}{"my-brand/other-model"}}, model: myModel1, err: "on-model mismatch"}, 750 {m: map[string]interface{}{"on-model": []interface{}{"my-brand/my-model", "my-brand-subbrand/my-model2", "other-brand/other-model"}}, model: myModel2}, 751 { 752 m: map[string]interface{}{ 753 "on-store": []interface{}{"store2"}, 754 "on-brand": []interface{}{"my-brand", "my-brand-subbrand"}, 755 "on-model": []interface{}{"my-brand/my-model3", "my-brand-subbrand/my-model2"}, 756 }, 757 model: myModel2, 758 }, { 759 m: map[string]interface{}{ 760 "on-store": []interface{}{"store2"}, 761 "on-brand": []interface{}{"my-brand", "my-brand-subbrand"}, 762 "on-model": []interface{}{"my-brand/my-model3", "my-brand-subbrand/my-model2"}, 763 }, 764 model: myModel3, store: substore1, 765 useFriendlyStores: true, 766 }, { 767 m: map[string]interface{}{ 768 "on-store": []interface{}{"other-store"}, 769 "on-brand": []interface{}{"my-brand", "my-brand-subbrand"}, 770 "on-model": []interface{}{"my-brand/my-model3", "my-brand-subbrand/my-model2"}, 771 }, 772 model: myModel3, store: substore1, 773 useFriendlyStores: true, 774 err: "on-store mismatch", 775 }, { 776 m: map[string]interface{}{ 777 "on-store": []interface{}{"store2"}, 778 "on-brand": []interface{}{"other-brand", "my-brand-subbrand"}, 779 "on-model": []interface{}{"my-brand/my-model3", "my-brand-subbrand/my-model2"}, 780 }, 781 model: myModel3, store: substore1, 782 useFriendlyStores: true, 783 err: "on-brand mismatch", 784 }, { 785 m: map[string]interface{}{ 786 "on-store": []interface{}{"store2"}, 787 "on-brand": []interface{}{"my-brand", "my-brand-subbrand"}, 788 "on-model": []interface{}{"my-brand/my-model1", "my-brand-subbrand/my-model2"}, 789 }, 790 model: myModel3, store: substore1, 791 useFriendlyStores: true, 792 err: "on-model mismatch", 793 }, { 794 m: map[string]interface{}{ 795 "on-store": []interface{}{"store2"}, 796 "on-brand": []interface{}{"my-brand", "my-brand-subbrand"}, 797 "on-model": []interface{}{"my-brand/my-model1", "my-brand-subbrand/my-model2"}, 798 }, 799 model: myModel3, store: substore1, 800 useFriendlyStores: false, 801 err: "on-store mismatch", 802 }, 803 } 804 805 for _, t := range tests { 806 constr, err := asserts.CompileDeviceScopeConstraint(t.m, "constraint") 807 c.Assert(err, IsNil) 808 809 var opts *asserts.DeviceScopeConstraintCheckOptions 810 if t.useFriendlyStores { 811 opts = &asserts.DeviceScopeConstraintCheckOptions{ 812 UseFriendlyStores: true, 813 } 814 } 815 err = constr.Check(t.model, t.store, opts) 816 if t.err == "" { 817 c.Check(err, IsNil, Commentf("%v", t.m)) 818 } else { 819 c.Check(err, ErrorMatches, t.err) 820 } 821 } 822 }