gopkg.in/ubuntu-core/snappy.v0@v0.0.0-20210902073436-25a8614f10a6/asserts/ifacedecls_test.go (about) 1 // -*- Mode: Go; indent-tabs-mode: t -*- 2 3 /* 4 * Copyright (C) 2015-2017 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 "strings" 26 27 . "gopkg.in/check.v1" 28 "gopkg.in/yaml.v2" 29 30 "github.com/snapcore/snapd/asserts" 31 "github.com/snapcore/snapd/snap" 32 "github.com/snapcore/snapd/testutil" 33 ) 34 35 var ( 36 _ = Suite(&attrConstraintsSuite{}) 37 _ = Suite(&nameConstraintsSuite{}) 38 _ = Suite(&plugSlotRulesSuite{}) 39 ) 40 41 type attrConstraintsSuite struct { 42 testutil.BaseTest 43 } 44 45 type attrerObject map[string]interface{} 46 47 func (o attrerObject) Lookup(path string) (interface{}, bool) { 48 v, ok := o[path] 49 return v, ok 50 } 51 52 func attrs(yml string) *attrerObject { 53 var attrs map[string]interface{} 54 err := yaml.Unmarshal([]byte(yml), &attrs) 55 if err != nil { 56 panic(err) 57 } 58 snapYaml, err := yaml.Marshal(map[string]interface{}{ 59 "name": "sample", 60 "plugs": map[string]interface{}{ 61 "plug": attrs, 62 }, 63 }) 64 if err != nil { 65 panic(err) 66 } 67 68 // NOTE: it's important to go through snap yaml here even though we're really interested in Attrs only, 69 // as InfoFromSnapYaml normalizes yaml values. 70 info, err := snap.InfoFromSnapYaml(snapYaml) 71 if err != nil { 72 panic(err) 73 } 74 75 ao := attrerObject(info.Plugs["plug"].Attrs) 76 return &ao 77 } 78 79 func (s *attrConstraintsSuite) SetUpTest(c *C) { 80 s.BaseTest.SetUpTest(c) 81 s.BaseTest.AddCleanup(snap.MockSanitizePlugsSlots(func(snapInfo *snap.Info) {})) 82 } 83 84 func (s *attrConstraintsSuite) TearDownTest(c *C) { 85 s.BaseTest.TearDownTest(c) 86 } 87 88 func (s *attrConstraintsSuite) TestSimple(c *C) { 89 m, err := asserts.ParseHeaders([]byte(`attrs: 90 foo: FOO 91 bar: BAR`)) 92 c.Assert(err, IsNil) 93 94 cstrs, err := asserts.CompileAttributeConstraints(m["attrs"].(map[string]interface{})) 95 c.Assert(err, IsNil) 96 97 plug := attrerObject(map[string]interface{}{ 98 "foo": "FOO", 99 "bar": "BAR", 100 "baz": "BAZ", 101 }) 102 err = cstrs.Check(plug, nil) 103 c.Check(err, IsNil) 104 105 plug = attrerObject(map[string]interface{}{ 106 "foo": "FOO", 107 "bar": "BAZ", 108 "baz": "BAZ", 109 }) 110 err = cstrs.Check(plug, nil) 111 c.Check(err, ErrorMatches, `attribute "bar" value "BAZ" does not match \^\(BAR\)\$`) 112 113 plug = attrerObject(map[string]interface{}{ 114 "foo": "FOO", 115 "baz": "BAZ", 116 }) 117 err = cstrs.Check(plug, nil) 118 c.Check(err, ErrorMatches, `attribute "bar" has constraints but is unset`) 119 } 120 121 func (s *attrConstraintsSuite) TestSimpleAnchorsVsRegexpAlt(c *C) { 122 m, err := asserts.ParseHeaders([]byte(`attrs: 123 bar: BAR|BAZ`)) 124 c.Assert(err, IsNil) 125 126 cstrs, err := asserts.CompileAttributeConstraints(m["attrs"].(map[string]interface{})) 127 c.Assert(err, IsNil) 128 129 plug := attrerObject(map[string]interface{}{ 130 "bar": "BAR", 131 }) 132 err = cstrs.Check(plug, nil) 133 c.Check(err, IsNil) 134 135 plug = attrerObject(map[string]interface{}{ 136 "bar": "BARR", 137 }) 138 err = cstrs.Check(plug, nil) 139 c.Check(err, ErrorMatches, `attribute "bar" value "BARR" does not match \^\(BAR|BAZ\)\$`) 140 141 plug = attrerObject(map[string]interface{}{ 142 "bar": "BBAZ", 143 }) 144 err = cstrs.Check(plug, nil) 145 c.Check(err, ErrorMatches, `attribute "bar" value "BAZZ" does not match \^\(BAR|BAZ\)\$`) 146 147 plug = attrerObject(map[string]interface{}{ 148 "bar": "BABAZ", 149 }) 150 err = cstrs.Check(plug, nil) 151 c.Check(err, ErrorMatches, `attribute "bar" value "BABAZ" does not match \^\(BAR|BAZ\)\$`) 152 153 plug = attrerObject(map[string]interface{}{ 154 "bar": "BARAZ", 155 }) 156 err = cstrs.Check(plug, nil) 157 c.Check(err, ErrorMatches, `attribute "bar" value "BARAZ" does not match \^\(BAR|BAZ\)\$`) 158 } 159 160 func (s *attrConstraintsSuite) TestNested(c *C) { 161 m, err := asserts.ParseHeaders([]byte(`attrs: 162 foo: FOO 163 bar: 164 bar1: BAR1 165 bar2: BAR2`)) 166 c.Assert(err, IsNil) 167 168 cstrs, err := asserts.CompileAttributeConstraints(m["attrs"].(map[string]interface{})) 169 c.Assert(err, IsNil) 170 171 err = cstrs.Check(attrs(` 172 foo: FOO 173 bar: 174 bar1: BAR1 175 bar2: BAR2 176 bar3: BAR3 177 baz: BAZ 178 `), nil) 179 c.Check(err, IsNil) 180 181 err = cstrs.Check(attrs(` 182 foo: FOO 183 bar: BAZ 184 baz: BAZ 185 `), nil) 186 c.Check(err, ErrorMatches, `attribute "bar" must be a map`) 187 188 err = cstrs.Check(attrs(` 189 foo: FOO 190 bar: 191 bar1: BAR1 192 bar2: BAR22 193 bar3: BAR3 194 baz: BAZ 195 `), nil) 196 c.Check(err, ErrorMatches, `attribute "bar\.bar2" value "BAR22" does not match \^\(BAR2\)\$`) 197 198 err = cstrs.Check(attrs(` 199 foo: FOO 200 bar: 201 bar1: BAR1 202 bar2: 203 bar22: true 204 bar3: BAR3 205 baz: BAZ 206 `), nil) 207 c.Check(err, ErrorMatches, `attribute "bar\.bar2" must be a scalar or list`) 208 } 209 210 func (s *attrConstraintsSuite) TestAlternative(c *C) { 211 m, err := asserts.ParseHeaders([]byte(`attrs: 212 - 213 foo: FOO 214 bar: BAR 215 - 216 foo: FOO 217 bar: BAZ`)) 218 c.Assert(err, IsNil) 219 220 cstrs, err := asserts.CompileAttributeConstraints(m["attrs"].([]interface{})) 221 c.Assert(err, IsNil) 222 223 plug := attrerObject(map[string]interface{}{ 224 "foo": "FOO", 225 "bar": "BAR", 226 "baz": "BAZ", 227 }) 228 err = cstrs.Check(plug, nil) 229 c.Check(err, IsNil) 230 231 plug = attrerObject(map[string]interface{}{ 232 "foo": "FOO", 233 "bar": "BAZ", 234 "baz": "BAZ", 235 }) 236 err = cstrs.Check(plug, nil) 237 c.Check(err, IsNil) 238 239 plug = attrerObject(map[string]interface{}{ 240 "foo": "FOO", 241 "bar": "BARR", 242 "baz": "BAR", 243 }) 244 err = cstrs.Check(plug, nil) 245 c.Check(err, ErrorMatches, `no alternative matches: attribute "bar" value "BARR" does not match \^\(BAR\)\$`) 246 } 247 248 func (s *attrConstraintsSuite) TestNestedAlternative(c *C) { 249 m, err := asserts.ParseHeaders([]byte(`attrs: 250 foo: FOO 251 bar: 252 bar1: BAR1 253 bar2: 254 - BAR2 255 - BAR22`)) 256 c.Assert(err, IsNil) 257 258 cstrs, err := asserts.CompileAttributeConstraints(m["attrs"].(map[string]interface{})) 259 c.Assert(err, IsNil) 260 261 err = cstrs.Check(attrs(` 262 foo: FOO 263 bar: 264 bar1: BAR1 265 bar2: BAR2 266 `), nil) 267 c.Check(err, IsNil) 268 269 err = cstrs.Check(attrs(` 270 foo: FOO 271 bar: 272 bar1: BAR1 273 bar2: BAR22 274 `), nil) 275 c.Check(err, IsNil) 276 277 err = cstrs.Check(attrs(` 278 foo: FOO 279 bar: 280 bar1: BAR1 281 bar2: BAR3 282 `), nil) 283 c.Check(err, ErrorMatches, `no alternative for attribute "bar\.bar2" matches: attribute "bar\.bar2" value "BAR3" does not match \^\(BAR2\)\$`) 284 } 285 286 func (s *attrConstraintsSuite) TestOtherScalars(c *C) { 287 m, err := asserts.ParseHeaders([]byte(`attrs: 288 foo: 1 289 bar: true`)) 290 c.Assert(err, IsNil) 291 292 cstrs, err := asserts.CompileAttributeConstraints(m["attrs"].(map[string]interface{})) 293 c.Assert(err, IsNil) 294 295 err = cstrs.Check(attrs(` 296 foo: 1 297 bar: true 298 `), nil) 299 c.Check(err, IsNil) 300 301 plug := attrerObject(map[string]interface{}{ 302 "foo": int64(1), 303 "bar": true, 304 }) 305 err = cstrs.Check(plug, nil) 306 c.Check(err, IsNil) 307 } 308 309 func (s *attrConstraintsSuite) TestCompileErrors(c *C) { 310 _, err := asserts.CompileAttributeConstraints(map[string]interface{}{ 311 "foo": "[", 312 }) 313 c.Check(err, ErrorMatches, `cannot compile "foo" constraint "\[": error parsing regexp:.*`) 314 315 _, err = asserts.CompileAttributeConstraints(map[string]interface{}{ 316 "foo": []interface{}{"foo", "["}, 317 }) 318 c.Check(err, ErrorMatches, `cannot compile "foo/alt#2/" constraint "\[": error parsing regexp:.*`) 319 320 _, err = asserts.CompileAttributeConstraints(map[string]interface{}{ 321 "foo": []interface{}{"foo", []interface{}{"bar", "baz"}}, 322 }) 323 c.Check(err, ErrorMatches, `cannot nest alternative constraints directly at "foo/alt#2/"`) 324 325 _, err = asserts.CompileAttributeConstraints("FOO") 326 c.Check(err, ErrorMatches, `first level of non alternative constraints must be a set of key-value contraints`) 327 328 _, err = asserts.CompileAttributeConstraints([]interface{}{"FOO"}) 329 c.Check(err, ErrorMatches, `first level of non alternative constraints must be a set of key-value contraints`) 330 331 wrongDollarConstraints := []string{ 332 "$", 333 "$FOO(a)", 334 "$SLOT", 335 "$SLOT()", 336 } 337 338 for _, wrong := range wrongDollarConstraints { 339 _, err := asserts.CompileAttributeConstraints(map[string]interface{}{ 340 "foo": wrong, 341 }) 342 c.Check(err, ErrorMatches, fmt.Sprintf(`cannot compile "foo" constraint "%s": not a valid \$SLOT\(\)/\$PLUG\(\) constraint`, regexp.QuoteMeta(wrong))) 343 344 } 345 } 346 347 func (s *attrConstraintsSuite) TestMatchingListsSimple(c *C) { 348 m, err := asserts.ParseHeaders([]byte(`attrs: 349 foo: /foo/.*`)) 350 c.Assert(err, IsNil) 351 352 cstrs, err := asserts.CompileAttributeConstraints(m["attrs"].(map[string]interface{})) 353 c.Assert(err, IsNil) 354 355 err = cstrs.Check(attrs(` 356 foo: ["/foo/x", "/foo/y"] 357 `), nil) 358 c.Check(err, IsNil) 359 360 err = cstrs.Check(attrs(` 361 foo: ["/foo/x", "/foo"] 362 `), nil) 363 c.Check(err, ErrorMatches, `attribute "foo\.1" value "/foo" does not match \^\(/foo/\.\*\)\$`) 364 } 365 366 func (s *attrConstraintsSuite) TestMissingCheck(c *C) { 367 m, err := asserts.ParseHeaders([]byte(`attrs: 368 foo: $MISSING`)) 369 c.Assert(err, IsNil) 370 371 cstrs, err := asserts.CompileAttributeConstraints(m["attrs"].(map[string]interface{})) 372 c.Assert(err, IsNil) 373 c.Check(asserts.RuleFeature(cstrs, "dollar-attr-constraints"), Equals, true) 374 375 err = cstrs.Check(attrs(` 376 bar: baz 377 `), nil) 378 c.Check(err, IsNil) 379 380 err = cstrs.Check(attrs(` 381 foo: ["x"] 382 `), nil) 383 c.Check(err, ErrorMatches, `attribute "foo" is constrained to be missing but is set`) 384 } 385 386 type testEvalAttr struct { 387 comp func(side string, arg string) (interface{}, error) 388 } 389 390 func (ca testEvalAttr) SlotAttr(arg string) (interface{}, error) { 391 return ca.comp("slot", arg) 392 } 393 394 func (ca testEvalAttr) PlugAttr(arg string) (interface{}, error) { 395 return ca.comp("plug", arg) 396 } 397 398 func (s *attrConstraintsSuite) TestEvalCheck(c *C) { 399 m, err := asserts.ParseHeaders([]byte(`attrs: 400 foo: $SLOT(foo) 401 bar: $PLUG(bar.baz)`)) 402 c.Assert(err, IsNil) 403 404 cstrs, err := asserts.CompileAttributeConstraints(m["attrs"].(map[string]interface{})) 405 c.Assert(err, IsNil) 406 c.Check(asserts.RuleFeature(cstrs, "dollar-attr-constraints"), Equals, true) 407 408 err = cstrs.Check(attrs(` 409 foo: foo 410 bar: bar 411 `), nil) 412 c.Check(err, ErrorMatches, `attribute "(foo|bar)" cannot be matched without context`) 413 414 calls := make(map[[2]string]bool) 415 comp1 := func(op string, arg string) (interface{}, error) { 416 calls[[2]string{op, arg}] = true 417 return arg, nil 418 } 419 420 err = cstrs.Check(attrs(` 421 foo: foo 422 bar: bar.baz 423 `), testEvalAttr{comp1}) 424 c.Check(err, IsNil) 425 426 c.Check(calls, DeepEquals, map[[2]string]bool{ 427 {"slot", "foo"}: true, 428 {"plug", "bar.baz"}: true, 429 }) 430 431 comp2 := func(op string, arg string) (interface{}, error) { 432 if op == "plug" { 433 return nil, fmt.Errorf("boom") 434 } 435 return arg, nil 436 } 437 438 err = cstrs.Check(attrs(` 439 foo: foo 440 bar: bar.baz 441 `), testEvalAttr{comp2}) 442 c.Check(err, ErrorMatches, `attribute "bar" constraint \$PLUG\(bar\.baz\) cannot be evaluated: boom`) 443 444 comp3 := func(op string, arg string) (interface{}, error) { 445 if op == "slot" { 446 return "other-value", nil 447 } 448 return arg, nil 449 } 450 451 err = cstrs.Check(attrs(` 452 foo: foo 453 bar: bar.baz 454 `), testEvalAttr{comp3}) 455 c.Check(err, ErrorMatches, `attribute "foo" does not match \$SLOT\(foo\): foo != other-value`) 456 } 457 458 func (s *attrConstraintsSuite) TestMatchingListsMap(c *C) { 459 m, err := asserts.ParseHeaders([]byte(`attrs: 460 foo: 461 p: /foo/.*`)) 462 c.Assert(err, IsNil) 463 464 cstrs, err := asserts.CompileAttributeConstraints(m["attrs"].(map[string]interface{})) 465 c.Assert(err, IsNil) 466 467 err = cstrs.Check(attrs(` 468 foo: [{p: "/foo/x"}, {p: "/foo/y"}] 469 `), nil) 470 c.Check(err, IsNil) 471 472 err = cstrs.Check(attrs(` 473 foo: [{p: "zzz"}, {p: "/foo/y"}] 474 `), nil) 475 c.Check(err, ErrorMatches, `attribute "foo\.0\.p" value "zzz" does not match \^\(/foo/\.\*\)\$`) 476 } 477 478 func (s *attrConstraintsSuite) TestAlwaysMatchAttributeConstraints(c *C) { 479 c.Check(asserts.AlwaysMatchAttributes.Check(nil, nil), IsNil) 480 } 481 482 func (s *attrConstraintsSuite) TestNeverMatchAttributeConstraints(c *C) { 483 c.Check(asserts.NeverMatchAttributes.Check(nil, nil), NotNil) 484 } 485 486 type nameConstraintsSuite struct{} 487 488 func (s *nameConstraintsSuite) TestCompileErrors(c *C) { 489 _, err := asserts.CompileNameConstraints("slot-names", "true") 490 c.Check(err, ErrorMatches, `slot-names constraints must be a list of regexps and special \$ values`) 491 492 _, err = asserts.CompileNameConstraints("slot-names", []interface{}{map[string]interface{}{"foo": "bar"}}) 493 c.Check(err, ErrorMatches, `slot-names constraint entry must be a regexp or special \$ value`) 494 495 _, err = asserts.CompileNameConstraints("plug-names", []interface{}{"["}) 496 c.Check(err, ErrorMatches, `cannot compile plug-names constraint entry "\[":.*`) 497 498 _, err = asserts.CompileNameConstraints("plug-names", []interface{}{"$"}) 499 c.Check(err, ErrorMatches, `plug-names constraint entry special value "\$" is invalid`) 500 501 _, err = asserts.CompileNameConstraints("slot-names", []interface{}{"$12"}) 502 c.Check(err, ErrorMatches, `slot-names constraint entry special value "\$12" is invalid`) 503 504 _, err = asserts.CompileNameConstraints("plug-names", []interface{}{"a b"}) 505 c.Check(err, ErrorMatches, `plug-names constraint entry regexp contains unexpected spaces`) 506 } 507 508 func (s *nameConstraintsSuite) TestCheck(c *C) { 509 nc, err := asserts.CompileNameConstraints("slot-names", []interface{}{"foo[0-9]", "bar"}) 510 c.Assert(err, IsNil) 511 512 for _, matching := range []string{"foo0", "foo1", "bar"} { 513 c.Check(nc.Check("slot name", matching, nil), IsNil) 514 } 515 516 for _, notMatching := range []string{"baz", "fooo", "foo12"} { 517 c.Check(nc.Check("slot name", notMatching, nil), ErrorMatches, fmt.Sprintf(`slot name %q does not match constraints`, notMatching)) 518 } 519 520 } 521 522 func (s *nameConstraintsSuite) TestCheckSpecial(c *C) { 523 nc, err := asserts.CompileNameConstraints("slot-names", []interface{}{"$INTERFACE"}) 524 c.Assert(err, IsNil) 525 526 c.Check(nc.Check("slot name", "foo", nil), ErrorMatches, `slot name "foo" does not match constraints`) 527 c.Check(nc.Check("slot name", "foo", map[string]string{"$INTERFACE": "foo"}), IsNil) 528 c.Check(nc.Check("slot name", "bar", map[string]string{"$INTERFACE": "foo"}), ErrorMatches, `slot name "bar" does not match constraints`) 529 } 530 531 type plugSlotRulesSuite struct{} 532 533 func checkAttrs(c *C, attrs *asserts.AttributeConstraints, witness, expected string) { 534 plug := attrerObject(map[string]interface{}{ 535 witness: "XYZ", 536 }) 537 c.Check(attrs.Check(plug, nil), ErrorMatches, fmt.Sprintf(`attribute "%s".*does not match.*`, witness)) 538 plug = attrerObject(map[string]interface{}{ 539 witness: expected, 540 }) 541 c.Check(attrs.Check(plug, nil), IsNil) 542 } 543 544 var ( 545 sideArityAny = asserts.SideArityConstraint{N: -1} 546 sideArityOne = asserts.SideArityConstraint{N: 1} 547 ) 548 549 func checkBoolPlugConnConstraints(c *C, subrule string, cstrs []*asserts.PlugConnectionConstraints, always bool) { 550 expected := asserts.NeverMatchAttributes 551 if always { 552 expected = asserts.AlwaysMatchAttributes 553 } 554 c.Assert(cstrs, HasLen, 1) 555 cstrs1 := cstrs[0] 556 c.Check(cstrs1.PlugAttributes, Equals, expected) 557 c.Check(cstrs1.SlotAttributes, Equals, expected) 558 if strings.HasPrefix(subrule, "deny-") { 559 undef := asserts.SideArityConstraint{} 560 c.Check(cstrs1.SlotsPerPlug, Equals, undef) 561 c.Check(cstrs1.PlugsPerSlot, Equals, undef) 562 } else { 563 c.Check(cstrs1.PlugsPerSlot, Equals, sideArityAny) 564 if strings.HasSuffix(subrule, "-auto-connection") { 565 c.Check(cstrs1.SlotsPerPlug, Equals, sideArityOne) 566 } else { 567 c.Check(cstrs1.SlotsPerPlug, Equals, sideArityAny) 568 } 569 } 570 c.Check(cstrs1.SlotSnapIDs, HasLen, 0) 571 c.Check(cstrs1.SlotPublisherIDs, HasLen, 0) 572 c.Check(cstrs1.SlotSnapTypes, HasLen, 0) 573 } 574 575 func checkBoolSlotConnConstraints(c *C, subrule string, cstrs []*asserts.SlotConnectionConstraints, always bool) { 576 expected := asserts.NeverMatchAttributes 577 if always { 578 expected = asserts.AlwaysMatchAttributes 579 } 580 c.Assert(cstrs, HasLen, 1) 581 cstrs1 := cstrs[0] 582 c.Check(cstrs1.PlugAttributes, Equals, expected) 583 c.Check(cstrs1.SlotAttributes, Equals, expected) 584 if strings.HasPrefix(subrule, "deny-") { 585 undef := asserts.SideArityConstraint{} 586 c.Check(cstrs1.SlotsPerPlug, Equals, undef) 587 c.Check(cstrs1.PlugsPerSlot, Equals, undef) 588 } else { 589 c.Check(cstrs1.PlugsPerSlot, Equals, sideArityAny) 590 if strings.HasSuffix(subrule, "-auto-connection") { 591 c.Check(cstrs1.SlotsPerPlug, Equals, sideArityOne) 592 } else { 593 c.Check(cstrs1.SlotsPerPlug, Equals, sideArityAny) 594 } 595 } 596 c.Check(cstrs1.PlugSnapIDs, HasLen, 0) 597 c.Check(cstrs1.PlugPublisherIDs, HasLen, 0) 598 c.Check(cstrs1.PlugSnapTypes, HasLen, 0) 599 } 600 601 func (s *plugSlotRulesSuite) TestCompilePlugRuleAllAllowDenyStanzas(c *C) { 602 m, err := asserts.ParseHeaders([]byte(`iface: 603 allow-installation: 604 plug-attributes: 605 a1: A1 606 deny-installation: 607 plug-attributes: 608 a2: A2 609 allow-connection: 610 plug-attributes: 611 pa3: PA3 612 slot-attributes: 613 sa3: SA3 614 deny-connection: 615 plug-attributes: 616 pa4: PA4 617 slot-attributes: 618 sa4: SA4 619 allow-auto-connection: 620 plug-attributes: 621 pa5: PA5 622 slot-attributes: 623 sa5: SA5 624 deny-auto-connection: 625 plug-attributes: 626 pa6: PA6 627 slot-attributes: 628 sa6: SA6`)) 629 c.Assert(err, IsNil) 630 631 rule, err := asserts.CompilePlugRule("iface", m["iface"].(map[string]interface{})) 632 c.Assert(err, IsNil) 633 634 c.Check(rule.Interface, Equals, "iface") 635 // install subrules 636 c.Assert(rule.AllowInstallation, HasLen, 1) 637 checkAttrs(c, rule.AllowInstallation[0].PlugAttributes, "a1", "A1") 638 c.Assert(rule.DenyInstallation, HasLen, 1) 639 checkAttrs(c, rule.DenyInstallation[0].PlugAttributes, "a2", "A2") 640 // connection subrules 641 c.Assert(rule.AllowConnection, HasLen, 1) 642 checkAttrs(c, rule.AllowConnection[0].PlugAttributes, "pa3", "PA3") 643 checkAttrs(c, rule.AllowConnection[0].SlotAttributes, "sa3", "SA3") 644 c.Assert(rule.DenyConnection, HasLen, 1) 645 checkAttrs(c, rule.DenyConnection[0].PlugAttributes, "pa4", "PA4") 646 checkAttrs(c, rule.DenyConnection[0].SlotAttributes, "sa4", "SA4") 647 // auto-connection subrules 648 c.Assert(rule.AllowAutoConnection, HasLen, 1) 649 checkAttrs(c, rule.AllowAutoConnection[0].PlugAttributes, "pa5", "PA5") 650 checkAttrs(c, rule.AllowAutoConnection[0].SlotAttributes, "sa5", "SA5") 651 c.Assert(rule.DenyAutoConnection, HasLen, 1) 652 checkAttrs(c, rule.DenyAutoConnection[0].PlugAttributes, "pa6", "PA6") 653 checkAttrs(c, rule.DenyAutoConnection[0].SlotAttributes, "sa6", "SA6") 654 } 655 656 func (s *plugSlotRulesSuite) TestCompilePlugRuleAllAllowDenyOrStanzas(c *C) { 657 m, err := asserts.ParseHeaders([]byte(`iface: 658 allow-installation: 659 - 660 plug-attributes: 661 a1: A1 662 - 663 plug-attributes: 664 a1: A1alt 665 deny-installation: 666 - 667 plug-attributes: 668 a2: A2 669 - 670 plug-attributes: 671 a2: A2alt 672 allow-connection: 673 - 674 plug-attributes: 675 pa3: PA3 676 slot-attributes: 677 sa3: SA3 678 - 679 plug-attributes: 680 pa3: PA3alt 681 deny-connection: 682 - 683 plug-attributes: 684 pa4: PA4 685 slot-attributes: 686 sa4: SA4 687 - 688 plug-attributes: 689 pa4: PA4alt 690 allow-auto-connection: 691 - 692 plug-attributes: 693 pa5: PA5 694 slot-attributes: 695 sa5: SA5 696 - 697 plug-attributes: 698 pa5: PA5alt 699 deny-auto-connection: 700 - 701 plug-attributes: 702 pa6: PA6 703 slot-attributes: 704 sa6: SA6 705 - 706 plug-attributes: 707 pa6: PA6alt`)) 708 c.Assert(err, IsNil) 709 710 rule, err := asserts.CompilePlugRule("iface", m["iface"].(map[string]interface{})) 711 c.Assert(err, IsNil) 712 713 c.Check(rule.Interface, Equals, "iface") 714 // install subrules 715 c.Assert(rule.AllowInstallation, HasLen, 2) 716 checkAttrs(c, rule.AllowInstallation[0].PlugAttributes, "a1", "A1") 717 checkAttrs(c, rule.AllowInstallation[1].PlugAttributes, "a1", "A1alt") 718 c.Assert(rule.DenyInstallation, HasLen, 2) 719 checkAttrs(c, rule.DenyInstallation[0].PlugAttributes, "a2", "A2") 720 checkAttrs(c, rule.DenyInstallation[1].PlugAttributes, "a2", "A2alt") 721 // connection subrules 722 c.Assert(rule.AllowConnection, HasLen, 2) 723 checkAttrs(c, rule.AllowConnection[0].PlugAttributes, "pa3", "PA3") 724 checkAttrs(c, rule.AllowConnection[0].SlotAttributes, "sa3", "SA3") 725 checkAttrs(c, rule.AllowConnection[1].PlugAttributes, "pa3", "PA3alt") 726 c.Assert(rule.DenyConnection, HasLen, 2) 727 checkAttrs(c, rule.DenyConnection[0].PlugAttributes, "pa4", "PA4") 728 checkAttrs(c, rule.DenyConnection[0].SlotAttributes, "sa4", "SA4") 729 checkAttrs(c, rule.DenyConnection[1].PlugAttributes, "pa4", "PA4alt") 730 // auto-connection subrules 731 c.Assert(rule.AllowAutoConnection, HasLen, 2) 732 checkAttrs(c, rule.AllowAutoConnection[0].PlugAttributes, "pa5", "PA5") 733 checkAttrs(c, rule.AllowAutoConnection[0].SlotAttributes, "sa5", "SA5") 734 checkAttrs(c, rule.AllowAutoConnection[1].PlugAttributes, "pa5", "PA5alt") 735 c.Assert(rule.DenyAutoConnection, HasLen, 2) 736 checkAttrs(c, rule.DenyAutoConnection[0].PlugAttributes, "pa6", "PA6") 737 checkAttrs(c, rule.DenyAutoConnection[0].SlotAttributes, "sa6", "SA6") 738 checkAttrs(c, rule.DenyAutoConnection[1].PlugAttributes, "pa6", "PA6alt") 739 } 740 741 func (s *plugSlotRulesSuite) TestCompilePlugRuleShortcutTrue(c *C) { 742 rule, err := asserts.CompilePlugRule("iface", "true") 743 c.Assert(err, IsNil) 744 745 c.Check(rule.Interface, Equals, "iface") 746 // install subrules 747 c.Assert(rule.AllowInstallation, HasLen, 1) 748 c.Check(rule.AllowInstallation[0].PlugAttributes, Equals, asserts.AlwaysMatchAttributes) 749 c.Assert(rule.DenyInstallation, HasLen, 1) 750 c.Check(rule.DenyInstallation[0].PlugAttributes, Equals, asserts.NeverMatchAttributes) 751 // connection subrules 752 checkBoolPlugConnConstraints(c, "allow-connection", rule.AllowConnection, true) 753 checkBoolPlugConnConstraints(c, "deny-connection", rule.DenyConnection, false) 754 // auto-connection subrules 755 checkBoolPlugConnConstraints(c, "allow-auto-connection", rule.AllowAutoConnection, true) 756 checkBoolPlugConnConstraints(c, "deny-auto-connection", rule.DenyAutoConnection, false) 757 } 758 759 func (s *plugSlotRulesSuite) TestCompilePlugRuleShortcutFalse(c *C) { 760 rule, err := asserts.CompilePlugRule("iface", "false") 761 c.Assert(err, IsNil) 762 763 // install subrules 764 c.Assert(rule.AllowInstallation, HasLen, 1) 765 c.Check(rule.AllowInstallation[0].PlugAttributes, Equals, asserts.NeverMatchAttributes) 766 c.Assert(rule.DenyInstallation, HasLen, 1) 767 c.Check(rule.DenyInstallation[0].PlugAttributes, Equals, asserts.AlwaysMatchAttributes) 768 // connection subrules 769 checkBoolPlugConnConstraints(c, "allow-connection", rule.AllowConnection, false) 770 checkBoolPlugConnConstraints(c, "deny-connection", rule.DenyConnection, true) 771 // auto-connection subrules 772 checkBoolPlugConnConstraints(c, "allow-auto-connection", rule.AllowAutoConnection, false) 773 checkBoolPlugConnConstraints(c, "deny-auto-connection", rule.DenyAutoConnection, true) 774 } 775 776 func (s *plugSlotRulesSuite) TestCompilePlugRuleDefaults(c *C) { 777 rule, err := asserts.CompilePlugRule("iface", map[string]interface{}{ 778 "deny-auto-connection": "true", 779 }) 780 c.Assert(err, IsNil) 781 782 // everything follows the defaults... 783 784 // install subrules 785 c.Assert(rule.AllowInstallation, HasLen, 1) 786 c.Check(rule.AllowInstallation[0].PlugAttributes, Equals, asserts.AlwaysMatchAttributes) 787 c.Assert(rule.DenyInstallation, HasLen, 1) 788 c.Check(rule.DenyInstallation[0].PlugAttributes, Equals, asserts.NeverMatchAttributes) 789 // connection subrules 790 checkBoolPlugConnConstraints(c, "allow-connection", rule.AllowConnection, true) 791 checkBoolPlugConnConstraints(c, "deny-connection", rule.DenyConnection, false) 792 // auto-connection subrules 793 checkBoolPlugConnConstraints(c, "allow-auto-connection", rule.AllowAutoConnection, true) 794 // ... but deny-auto-connection is on 795 checkBoolPlugConnConstraints(c, "deny-auto-connection", rule.DenyAutoConnection, true) 796 } 797 798 func (s *plugSlotRulesSuite) TestCompilePlugRuleInstalationConstraintsIDConstraints(c *C) { 799 rule, err := asserts.CompilePlugRule("iface", map[string]interface{}{ 800 "allow-installation": map[string]interface{}{ 801 "plug-snap-type": []interface{}{"core", "kernel", "gadget", "app"}, 802 }, 803 }) 804 c.Assert(err, IsNil) 805 806 c.Assert(rule.AllowInstallation, HasLen, 1) 807 cstrs := rule.AllowInstallation[0] 808 c.Check(cstrs.PlugSnapTypes, DeepEquals, []string{"core", "kernel", "gadget", "app"}) 809 } 810 811 func (s *plugSlotRulesSuite) TestCompilePlugRuleInstallationConstraintsOnClassic(c *C) { 812 m, err := asserts.ParseHeaders([]byte(`iface: 813 allow-installation: true`)) 814 c.Assert(err, IsNil) 815 816 rule, err := asserts.CompilePlugRule("iface", m["iface"].(map[string]interface{})) 817 c.Assert(err, IsNil) 818 819 c.Check(rule.AllowInstallation[0].OnClassic, IsNil) 820 821 m, err = asserts.ParseHeaders([]byte(`iface: 822 allow-installation: 823 on-classic: false`)) 824 c.Assert(err, IsNil) 825 826 rule, err = asserts.CompilePlugRule("iface", m["iface"].(map[string]interface{})) 827 c.Assert(err, IsNil) 828 829 c.Check(rule.AllowInstallation[0].OnClassic, DeepEquals, &asserts.OnClassicConstraint{}) 830 831 m, err = asserts.ParseHeaders([]byte(`iface: 832 allow-installation: 833 on-classic: true`)) 834 c.Assert(err, IsNil) 835 836 rule, err = asserts.CompilePlugRule("iface", m["iface"].(map[string]interface{})) 837 c.Assert(err, IsNil) 838 839 c.Check(rule.AllowInstallation[0].OnClassic, DeepEquals, &asserts.OnClassicConstraint{Classic: true}) 840 841 m, err = asserts.ParseHeaders([]byte(`iface: 842 allow-installation: 843 on-classic: 844 - ubuntu 845 - debian`)) 846 c.Assert(err, IsNil) 847 848 rule, err = asserts.CompilePlugRule("iface", m["iface"].(map[string]interface{})) 849 c.Assert(err, IsNil) 850 851 c.Check(rule.AllowInstallation[0].OnClassic, DeepEquals, &asserts.OnClassicConstraint{Classic: true, SystemIDs: []string{"ubuntu", "debian"}}) 852 } 853 854 func (s *plugSlotRulesSuite) TestCompilePlugRuleInstallationConstraintsDeviceScope(c *C) { 855 m, err := asserts.ParseHeaders([]byte(`iface: 856 allow-installation: true`)) 857 c.Assert(err, IsNil) 858 859 rule, err := asserts.CompilePlugRule("iface", m["iface"].(map[string]interface{})) 860 c.Assert(err, IsNil) 861 862 c.Check(rule.AllowInstallation[0].DeviceScope, IsNil) 863 864 tests := []struct { 865 rule string 866 expected asserts.DeviceScopeConstraint 867 }{ 868 {`iface: 869 allow-installation: 870 on-store: 871 - my-store`, asserts.DeviceScopeConstraint{Store: []string{"my-store"}}}, 872 {`iface: 873 allow-installation: 874 on-store: 875 - my-store 876 - other-store`, asserts.DeviceScopeConstraint{Store: []string{"my-store", "other-store"}}}, 877 {`iface: 878 allow-installation: 879 on-brand: 880 - my-brand 881 - s9zGdwb16ysLeRW6nRivwZS5r9puP8JT`, asserts.DeviceScopeConstraint{Brand: []string{"my-brand", "s9zGdwb16ysLeRW6nRivwZS5r9puP8JT"}}}, 882 {`iface: 883 allow-installation: 884 on-model: 885 - my-brand/bar 886 - s9zGdwb16ysLeRW6nRivwZS5r9puP8JT/baz`, asserts.DeviceScopeConstraint{Model: []string{"my-brand/bar", "s9zGdwb16ysLeRW6nRivwZS5r9puP8JT/baz"}}}, 887 {`iface: 888 allow-installation: 889 on-store: 890 - store1 891 - store2 892 on-brand: 893 - my-brand 894 on-model: 895 - my-brand/bar 896 - s9zGdwb16ysLeRW6nRivwZS5r9puP8JT/baz`, asserts.DeviceScopeConstraint{ 897 Store: []string{"store1", "store2"}, 898 Brand: []string{"my-brand"}, 899 Model: []string{"my-brand/bar", "s9zGdwb16ysLeRW6nRivwZS5r9puP8JT/baz"}}}, 900 } 901 902 for _, t := range tests { 903 m, err = asserts.ParseHeaders([]byte(t.rule)) 904 c.Assert(err, IsNil) 905 906 rule, err = asserts.CompilePlugRule("iface", m["iface"].(map[string]interface{})) 907 c.Assert(err, IsNil) 908 909 c.Check(rule.AllowInstallation[0].DeviceScope, DeepEquals, &t.expected) 910 } 911 } 912 913 func (s *plugSlotRulesSuite) TestCompilePlugRuleInstallationConstraintsPlugNames(c *C) { 914 m, err := asserts.ParseHeaders([]byte(`iface: 915 allow-installation: true`)) 916 c.Assert(err, IsNil) 917 918 rule, err := asserts.CompilePlugRule("iface", m["iface"].(map[string]interface{})) 919 c.Assert(err, IsNil) 920 921 c.Check(rule.AllowInstallation[0].PlugNames, IsNil) 922 923 tests := []struct { 924 rule string 925 matching []string 926 notMatching []string 927 }{ 928 {`iface: 929 allow-installation: 930 plug-names: 931 - foo`, []string{"foo"}, []string{"bar"}}, 932 {`iface: 933 allow-installation: 934 plug-names: 935 - foo 936 - bar`, []string{"foo", "bar"}, []string{"baz"}}, 937 {`iface: 938 allow-installation: 939 plug-names: 940 - foo[0-9] 941 - bar`, []string{"foo0", "foo1", "bar"}, []string{"baz", "fooo", "foo12"}}, 942 } 943 for _, t := range tests { 944 m, err = asserts.ParseHeaders([]byte(t.rule)) 945 c.Assert(err, IsNil) 946 947 rule, err = asserts.CompilePlugRule("iface", m["iface"].(map[string]interface{})) 948 c.Assert(err, IsNil) 949 950 for _, matching := range t.matching { 951 c.Check(rule.AllowInstallation[0].PlugNames.Check("plug name", matching, nil), IsNil) 952 } 953 for _, notMatching := range t.notMatching { 954 c.Check(rule.AllowInstallation[0].PlugNames.Check("plug name", notMatching, nil), NotNil) 955 } 956 } 957 } 958 959 func (s *plugSlotRulesSuite) TestCompilePlugRuleConnectionConstraintsIDConstraints(c *C) { 960 rule, err := asserts.CompilePlugRule("iface", map[string]interface{}{ 961 "allow-connection": map[string]interface{}{ 962 "slot-snap-type": []interface{}{"core", "kernel", "gadget", "app"}, 963 "slot-snap-id": []interface{}{"snapidsnapidsnapidsnapidsnapid01", "snapidsnapidsnapidsnapidsnapid02"}, 964 "slot-publisher-id": []interface{}{"pubidpubidpubidpubidpubidpubid09", "canonical", "$SAME"}, 965 }, 966 }) 967 c.Assert(err, IsNil) 968 969 c.Assert(rule.AllowConnection, HasLen, 1) 970 cstrs := rule.AllowConnection[0] 971 c.Check(cstrs.SlotSnapTypes, DeepEquals, []string{"core", "kernel", "gadget", "app"}) 972 c.Check(cstrs.SlotSnapIDs, DeepEquals, []string{"snapidsnapidsnapidsnapidsnapid01", "snapidsnapidsnapidsnapidsnapid02"}) 973 c.Check(cstrs.SlotPublisherIDs, DeepEquals, []string{"pubidpubidpubidpubidpubidpubid09", "canonical", "$SAME"}) 974 975 } 976 977 func (s *plugSlotRulesSuite) TestCompilePlugRuleConnectionConstraintsOnClassic(c *C) { 978 m, err := asserts.ParseHeaders([]byte(`iface: 979 allow-connection: true`)) 980 c.Assert(err, IsNil) 981 982 rule, err := asserts.CompilePlugRule("iface", m["iface"].(map[string]interface{})) 983 c.Assert(err, IsNil) 984 985 c.Check(rule.AllowConnection[0].OnClassic, IsNil) 986 987 m, err = asserts.ParseHeaders([]byte(`iface: 988 allow-connection: 989 on-classic: false`)) 990 c.Assert(err, IsNil) 991 992 rule, err = asserts.CompilePlugRule("iface", m["iface"].(map[string]interface{})) 993 c.Assert(err, IsNil) 994 995 c.Check(rule.AllowConnection[0].OnClassic, DeepEquals, &asserts.OnClassicConstraint{}) 996 997 m, err = asserts.ParseHeaders([]byte(`iface: 998 allow-connection: 999 on-classic: true`)) 1000 c.Assert(err, IsNil) 1001 1002 rule, err = asserts.CompilePlugRule("iface", m["iface"].(map[string]interface{})) 1003 c.Assert(err, IsNil) 1004 1005 c.Check(rule.AllowConnection[0].OnClassic, DeepEquals, &asserts.OnClassicConstraint{Classic: true}) 1006 1007 m, err = asserts.ParseHeaders([]byte(`iface: 1008 allow-connection: 1009 on-classic: 1010 - ubuntu 1011 - debian`)) 1012 c.Assert(err, IsNil) 1013 1014 rule, err = asserts.CompilePlugRule("iface", m["iface"].(map[string]interface{})) 1015 c.Assert(err, IsNil) 1016 1017 c.Check(rule.AllowConnection[0].OnClassic, DeepEquals, &asserts.OnClassicConstraint{Classic: true, SystemIDs: []string{"ubuntu", "debian"}}) 1018 } 1019 1020 func (s *plugSlotRulesSuite) TestCompilePlugRuleConnectionConstraintsDeviceScope(c *C) { 1021 m, err := asserts.ParseHeaders([]byte(`iface: 1022 allow-connection: true`)) 1023 c.Assert(err, IsNil) 1024 1025 rule, err := asserts.CompilePlugRule("iface", m["iface"].(map[string]interface{})) 1026 c.Assert(err, IsNil) 1027 1028 c.Check(rule.AllowInstallation[0].DeviceScope, IsNil) 1029 1030 tests := []struct { 1031 rule string 1032 expected asserts.DeviceScopeConstraint 1033 }{ 1034 {`iface: 1035 allow-connection: 1036 on-store: 1037 - my-store`, asserts.DeviceScopeConstraint{Store: []string{"my-store"}}}, 1038 {`iface: 1039 allow-connection: 1040 on-store: 1041 - my-store 1042 - other-store`, asserts.DeviceScopeConstraint{Store: []string{"my-store", "other-store"}}}, 1043 {`iface: 1044 allow-connection: 1045 on-brand: 1046 - my-brand 1047 - s9zGdwb16ysLeRW6nRivwZS5r9puP8JT`, asserts.DeviceScopeConstraint{Brand: []string{"my-brand", "s9zGdwb16ysLeRW6nRivwZS5r9puP8JT"}}}, 1048 {`iface: 1049 allow-connection: 1050 on-model: 1051 - my-brand/bar 1052 - s9zGdwb16ysLeRW6nRivwZS5r9puP8JT/baz`, asserts.DeviceScopeConstraint{Model: []string{"my-brand/bar", "s9zGdwb16ysLeRW6nRivwZS5r9puP8JT/baz"}}}, 1053 {`iface: 1054 allow-connection: 1055 on-store: 1056 - store1 1057 - store2 1058 on-brand: 1059 - my-brand 1060 on-model: 1061 - my-brand/bar 1062 - s9zGdwb16ysLeRW6nRivwZS5r9puP8JT/baz`, asserts.DeviceScopeConstraint{ 1063 Store: []string{"store1", "store2"}, 1064 Brand: []string{"my-brand"}, 1065 Model: []string{"my-brand/bar", "s9zGdwb16ysLeRW6nRivwZS5r9puP8JT/baz"}}}, 1066 } 1067 1068 for _, t := range tests { 1069 m, err = asserts.ParseHeaders([]byte(t.rule)) 1070 c.Assert(err, IsNil) 1071 1072 rule, err = asserts.CompilePlugRule("iface", m["iface"].(map[string]interface{})) 1073 c.Assert(err, IsNil) 1074 1075 c.Check(rule.AllowConnection[0].DeviceScope, DeepEquals, &t.expected) 1076 } 1077 } 1078 1079 func (s *plugSlotRulesSuite) TestCompilePlugRuleConnectionConstraintsPlugNamesSlotNames(c *C) { 1080 m, err := asserts.ParseHeaders([]byte(`iface: 1081 allow-connection: true`)) 1082 c.Assert(err, IsNil) 1083 1084 rule, err := asserts.CompilePlugRule("iface", m["iface"].(map[string]interface{})) 1085 c.Assert(err, IsNil) 1086 1087 c.Check(rule.AllowConnection[0].PlugNames, IsNil) 1088 c.Check(rule.AllowConnection[0].SlotNames, IsNil) 1089 1090 tests := []struct { 1091 rule string 1092 matching []string 1093 notMatching []string 1094 }{ 1095 {`iface: 1096 allow-connection: 1097 plug-names: 1098 - Pfoo 1099 slot-names: 1100 - Sfoo`, []string{"foo"}, []string{"bar"}}, 1101 {`iface: 1102 allow-connection: 1103 plug-names: 1104 - Pfoo 1105 - Pbar 1106 slot-names: 1107 - Sfoo 1108 - Sbar`, []string{"foo", "bar"}, []string{"baz"}}, 1109 {`iface: 1110 allow-connection: 1111 plug-names: 1112 - Pfoo[0-9] 1113 - Pbar 1114 slot-names: 1115 - Sfoo[0-9] 1116 - Sbar`, []string{"foo0", "foo1", "bar"}, []string{"baz", "fooo", "foo12"}}, 1117 } 1118 for _, t := range tests { 1119 m, err = asserts.ParseHeaders([]byte(t.rule)) 1120 c.Assert(err, IsNil) 1121 1122 rule, err = asserts.CompilePlugRule("iface", m["iface"].(map[string]interface{})) 1123 c.Assert(err, IsNil) 1124 1125 for _, matching := range t.matching { 1126 c.Check(rule.AllowConnection[0].PlugNames.Check("plug name", "P"+matching, nil), IsNil) 1127 1128 c.Check(rule.AllowConnection[0].SlotNames.Check("slot name", "S"+matching, nil), IsNil) 1129 } 1130 1131 for _, notMatching := range t.notMatching { 1132 c.Check(rule.AllowConnection[0].SlotNames.Check("plug name", "P"+notMatching, nil), NotNil) 1133 1134 c.Check(rule.AllowConnection[0].SlotNames.Check("slot name", "S"+notMatching, nil), NotNil) 1135 } 1136 } 1137 } 1138 1139 func (s *plugSlotRulesSuite) TestCompilePlugRuleConnectionConstraintsSideArityConstraints(c *C) { 1140 m, err := asserts.ParseHeaders([]byte(`iface: 1141 allow-auto-connection: true`)) 1142 c.Assert(err, IsNil) 1143 1144 rule, err := asserts.CompilePlugRule("iface", m["iface"].(map[string]interface{})) 1145 c.Assert(err, IsNil) 1146 1147 // defaults 1148 c.Check(rule.AllowAutoConnection[0].SlotsPerPlug, Equals, asserts.SideArityConstraint{N: 1}) 1149 c.Check(rule.AllowAutoConnection[0].PlugsPerSlot.Any(), Equals, true) 1150 1151 c.Check(rule.AllowConnection[0].SlotsPerPlug.Any(), Equals, true) 1152 c.Check(rule.AllowConnection[0].PlugsPerSlot.Any(), Equals, true) 1153 1154 // test that the arity constraints get normalized away to any 1155 // under allow-connection 1156 // see https://forum.snapcraft.io/t/plug-slot-declaration-rules-greedy-plugs/12438 1157 allowConnTests := []string{ 1158 `iface: 1159 allow-connection: 1160 slots-per-plug: 1 1161 plugs-per-slot: 2`, 1162 `iface: 1163 allow-connection: 1164 slots-per-plug: * 1165 plugs-per-slot: 1`, 1166 `iface: 1167 allow-connection: 1168 slots-per-plug: 2 1169 plugs-per-slot: *`, 1170 } 1171 1172 for _, t := range allowConnTests { 1173 m, err = asserts.ParseHeaders([]byte(t)) 1174 c.Assert(err, IsNil) 1175 1176 rule, err = asserts.CompilePlugRule("iface", m["iface"].(map[string]interface{})) 1177 c.Assert(err, IsNil) 1178 1179 c.Check(rule.AllowConnection[0].SlotsPerPlug.Any(), Equals, true) 1180 c.Check(rule.AllowConnection[0].PlugsPerSlot.Any(), Equals, true) 1181 } 1182 1183 // test that under allow-auto-connection: 1184 // slots-per-plug can be * (any) or otherwise gets normalized to 1 1185 // plugs-per-slot gets normalized to any (*) 1186 // see https://forum.snapcraft.io/t/plug-slot-declaration-rules-greedy-plugs/12438 1187 allowAutoConnTests := []struct { 1188 rule string 1189 slotsPerPlug asserts.SideArityConstraint 1190 }{ 1191 {`iface: 1192 allow-auto-connection: 1193 slots-per-plug: 1 1194 plugs-per-slot: 2`, sideArityOne}, 1195 {`iface: 1196 allow-auto-connection: 1197 slots-per-plug: * 1198 plugs-per-slot: 1`, sideArityAny}, 1199 {`iface: 1200 allow-auto-connection: 1201 slots-per-plug: 2 1202 plugs-per-slot: *`, sideArityOne}, 1203 } 1204 1205 for _, t := range allowAutoConnTests { 1206 m, err = asserts.ParseHeaders([]byte(t.rule)) 1207 c.Assert(err, IsNil) 1208 1209 rule, err = asserts.CompilePlugRule("iface", m["iface"].(map[string]interface{})) 1210 c.Assert(err, IsNil) 1211 1212 c.Check(rule.AllowAutoConnection[0].SlotsPerPlug, Equals, t.slotsPerPlug) 1213 c.Check(rule.AllowAutoConnection[0].PlugsPerSlot.Any(), Equals, true) 1214 } 1215 } 1216 1217 func (s *plugSlotRulesSuite) TestCompilePlugRuleConnectionConstraintsAttributesDefault(c *C) { 1218 rule, err := asserts.CompilePlugRule("iface", map[string]interface{}{ 1219 "allow-connection": map[string]interface{}{ 1220 "slot-snap-id": []interface{}{"snapidsnapidsnapidsnapidsnapid01"}, 1221 }, 1222 }) 1223 c.Assert(err, IsNil) 1224 1225 // attributes default to always matching here 1226 cstrs := rule.AllowConnection[0] 1227 c.Check(cstrs.PlugAttributes, Equals, asserts.AlwaysMatchAttributes) 1228 c.Check(cstrs.SlotAttributes, Equals, asserts.AlwaysMatchAttributes) 1229 } 1230 1231 func (s *plugSlotRulesSuite) TestCompilePlugRuleErrors(c *C) { 1232 tests := []struct { 1233 stanza string 1234 err string 1235 }{ 1236 {`iface: foo`, `plug rule for interface "iface" must be a map or one of the shortcuts 'true' or 'false'`}, 1237 {`iface: 1238 - allow`, `plug rule for interface "iface" must be a map or one of the shortcuts 'true' or 'false'`}, 1239 {`iface: 1240 allow-installation: foo`, `allow-installation in plug rule for interface "iface" must be a map or one of the shortcuts 'true' or 'false'`}, 1241 {`iface: 1242 deny-installation: foo`, `deny-installation in plug rule for interface "iface" must be a map or one of the shortcuts 'true' or 'false'`}, 1243 {`iface: 1244 allow-connection: foo`, `allow-connection in plug rule for interface "iface" must be a map or one of the shortcuts 'true' or 'false'`}, 1245 {`iface: 1246 allow-connection: 1247 - foo`, `alternative 1 of allow-connection in plug rule for interface "iface" must be a map`}, 1248 {`iface: 1249 allow-connection: 1250 - true`, `alternative 1 of allow-connection in plug rule for interface "iface" must be a map`}, 1251 {`iface: 1252 allow-installation: 1253 plug-attributes: 1254 a1: [`, `cannot compile plug-attributes in allow-installation in plug rule for interface "iface": cannot compile "a1" constraint .*`}, 1255 {`iface: 1256 allow-connection: 1257 slot-attributes: 1258 a2: [`, `cannot compile slot-attributes in allow-connection in plug rule for interface "iface": cannot compile "a2" constraint .*`}, 1259 {`iface: 1260 allow-connection: 1261 slot-snap-id: 1262 - 1263 foo: 1`, `slot-snap-id in allow-connection in plug rule for interface "iface" must be a list of strings`}, 1264 {`iface: 1265 allow-connection: 1266 slot-snap-id: 1267 - foo`, `slot-snap-id in allow-connection in plug rule for interface "iface" contains an invalid element: "foo"`}, 1268 {`iface: 1269 allow-connection: 1270 slot-snap-type: 1271 - foo`, `slot-snap-type in allow-connection in plug rule for interface "iface" contains an invalid element: "foo"`}, 1272 {`iface: 1273 allow-connection: 1274 slot-snap-type: 1275 - xapp`, `slot-snap-type in allow-connection in plug rule for interface "iface" contains an invalid element: "xapp"`}, 1276 {`iface: 1277 allow-connection: 1278 slot-snap-ids: 1279 - foo`, `allow-connection in plug rule for interface "iface" must specify at least one of plug-names, slot-names, plug-attributes, slot-attributes, slot-snap-type, slot-publisher-id, slot-snap-id, slots-per-plug, plugs-per-slot, on-classic, on-store, on-brand, on-model`}, 1280 {`iface: 1281 deny-connection: 1282 slot-snap-ids: 1283 - foo`, `deny-connection in plug rule for interface "iface" must specify at least one of plug-names, slot-names, plug-attributes, slot-attributes, slot-snap-type, slot-publisher-id, slot-snap-id, slots-per-plug, plugs-per-slot, on-classic, on-store, on-brand, on-model`}, 1284 {`iface: 1285 allow-auto-connection: 1286 slot-snap-ids: 1287 - foo`, `allow-auto-connection in plug rule for interface "iface" must specify at least one of plug-names, slot-names, plug-attributes, slot-attributes, slot-snap-type, slot-publisher-id, slot-snap-id, slots-per-plug, plugs-per-slot, on-classic, on-store, on-brand, on-model`}, 1288 {`iface: 1289 deny-auto-connection: 1290 slot-snap-ids: 1291 - foo`, `deny-auto-connection in plug rule for interface "iface" must specify at least one of plug-names, slot-names, plug-attributes, slot-attributes, slot-snap-type, slot-publisher-id, slot-snap-id, slots-per-plug, plugs-per-slot, on-classic, on-store, on-brand, on-model`}, 1292 {`iface: 1293 allow-connect: true`, `plug rule for interface "iface" must specify at least one of allow-installation, deny-installation, allow-connection, deny-connection, allow-auto-connection, deny-auto-connection`}, 1294 {`iface: 1295 allow-installation: 1296 on-store: true`, `on-store in allow-installation in plug rule for interface \"iface\" must be a list of strings`}, 1297 {`iface: 1298 allow-installation: 1299 on-store: store1`, `on-store in allow-installation in plug rule for interface \"iface\" must be a list of strings`}, 1300 {`iface: 1301 allow-installation: 1302 on-store: 1303 - zoom!`, `on-store in allow-installation in plug rule for interface \"iface\" contains an invalid element: \"zoom!\"`}, 1304 {`iface: 1305 allow-connection: 1306 on-brand: true`, `on-brand in allow-connection in plug rule for interface \"iface\" must be a list of strings`}, 1307 {`iface: 1308 allow-connection: 1309 on-brand: brand1`, `on-brand in allow-connection in plug rule for interface \"iface\" must be a list of strings`}, 1310 {`iface: 1311 allow-connection: 1312 on-brand: 1313 - zoom!`, `on-brand in allow-connection in plug rule for interface \"iface\" contains an invalid element: \"zoom!\"`}, 1314 {`iface: 1315 allow-auto-connection: 1316 on-model: true`, `on-model in allow-auto-connection in plug rule for interface \"iface\" must be a list of strings`}, 1317 {`iface: 1318 allow-auto-connection: 1319 on-model: foo/bar`, `on-model in allow-auto-connection in plug rule for interface \"iface\" must be a list of strings`}, 1320 {`iface: 1321 allow-auto-connection: 1322 on-model: 1323 - foo/!qz`, `on-model in allow-auto-connection in plug rule for interface \"iface\" contains an invalid element: \"foo/!qz"`}, 1324 {`iface: 1325 allow-installation: 1326 slots-per-plug: 1`, `allow-installation in plug rule for interface "iface" cannot specify a slots-per-plug constraint, they apply only to allow-\*connection`}, 1327 {`iface: 1328 deny-connection: 1329 slots-per-plug: 1`, `deny-connection in plug rule for interface "iface" cannot specify a slots-per-plug constraint, they apply only to allow-\*connection`}, 1330 {`iface: 1331 allow-auto-connection: 1332 plugs-per-slot: any`, `plugs-per-slot in allow-auto-connection in plug rule for interface "iface" must be an integer >=1 or \*`}, 1333 {`iface: 1334 allow-auto-connection: 1335 slots-per-plug: 00`, `slots-per-plug in allow-auto-connection in plug rule for interface "iface" has invalid prefix zeros: 00`}, 1336 {`iface: 1337 allow-auto-connection: 1338 slots-per-plug: 99999999999999999999`, `slots-per-plug in allow-auto-connection in plug rule for interface "iface" is out of range: 99999999999999999999`}, 1339 {`iface: 1340 allow-auto-connection: 1341 slots-per-plug: 0`, `slots-per-plug in allow-auto-connection in plug rule for interface "iface" must be an integer >=1 or \*`}, 1342 {`iface: 1343 allow-auto-connection: 1344 slots-per-plug: 1345 what: 1`, `slots-per-plug in allow-auto-connection in plug rule for interface "iface" must be an integer >=1 or \*`}, 1346 {`iface: 1347 allow-auto-connection: 1348 plug-names: true`, `plug-names constraints must be a list of regexps and special \$ values`}, 1349 {`iface: 1350 allow-auto-connection: 1351 slot-names: true`, `slot-names constraints must be a list of regexps and special \$ values`}, 1352 } 1353 1354 for _, t := range tests { 1355 m, err := asserts.ParseHeaders([]byte(t.stanza)) 1356 c.Assert(err, IsNil, Commentf(t.stanza)) 1357 1358 _, err = asserts.CompilePlugRule("iface", m["iface"]) 1359 c.Check(err, ErrorMatches, t.err, Commentf(t.stanza)) 1360 } 1361 } 1362 1363 var ( 1364 deviceScopeConstrs = map[string][]interface{}{ 1365 "on-store": {"store"}, 1366 "on-brand": {"brand"}, 1367 "on-model": {"brand/model"}, 1368 } 1369 ) 1370 1371 func (s *plugSlotRulesSuite) TestPlugRuleFeatures(c *C) { 1372 combos := []struct { 1373 subrule string 1374 constraintsPrefixes []string 1375 }{ 1376 {"allow-installation", []string{"plug-"}}, 1377 {"deny-installation", []string{"plug-"}}, 1378 {"allow-connection", []string{"plug-", "slot-"}}, 1379 {"deny-connection", []string{"plug-", "slot-"}}, 1380 {"allow-auto-connection", []string{"plug-", "slot-"}}, 1381 {"deny-auto-connection", []string{"plug-", "slot-"}}, 1382 } 1383 1384 for _, combo := range combos { 1385 for _, attrConstrPrefix := range combo.constraintsPrefixes { 1386 attrConstraintMap := map[string]interface{}{ 1387 "a": "ATTR", 1388 "other": []interface{}{"x", "y"}, 1389 } 1390 ruleMap := map[string]interface{}{ 1391 combo.subrule: map[string]interface{}{ 1392 attrConstrPrefix + "attributes": attrConstraintMap, 1393 }, 1394 } 1395 1396 rule, err := asserts.CompilePlugRule("iface", ruleMap) 1397 c.Assert(err, IsNil) 1398 c.Check(asserts.RuleFeature(rule, "dollar-attr-constraints"), Equals, false, Commentf("%v", ruleMap)) 1399 1400 c.Check(asserts.RuleFeature(rule, "device-scope-constraints"), Equals, false, Commentf("%v", ruleMap)) 1401 c.Check(asserts.RuleFeature(rule, "name-constraints"), Equals, false, Commentf("%v", ruleMap)) 1402 1403 attrConstraintMap["a"] = "$MISSING" 1404 rule, err = asserts.CompilePlugRule("iface", ruleMap) 1405 c.Assert(err, IsNil) 1406 c.Check(asserts.RuleFeature(rule, "dollar-attr-constraints"), Equals, true, Commentf("%v", ruleMap)) 1407 1408 // covers also alternation 1409 attrConstraintMap["a"] = []interface{}{"$SLOT(a)"} 1410 rule, err = asserts.CompilePlugRule("iface", ruleMap) 1411 c.Assert(err, IsNil) 1412 c.Check(asserts.RuleFeature(rule, "dollar-attr-constraints"), Equals, true, Commentf("%v", ruleMap)) 1413 1414 c.Check(asserts.RuleFeature(rule, "device-scope-constraints"), Equals, false, Commentf("%v", ruleMap)) 1415 c.Check(asserts.RuleFeature(rule, "name-constraints"), Equals, false, Commentf("%v", ruleMap)) 1416 1417 } 1418 1419 for deviceScopeConstr, value := range deviceScopeConstrs { 1420 ruleMap := map[string]interface{}{ 1421 combo.subrule: map[string]interface{}{ 1422 deviceScopeConstr: value, 1423 }, 1424 } 1425 1426 rule, err := asserts.CompilePlugRule("iface", ruleMap) 1427 c.Assert(err, IsNil) 1428 c.Check(asserts.RuleFeature(rule, "device-scope-constraints"), Equals, true, Commentf("%v", ruleMap)) 1429 } 1430 1431 for _, nameConstrPrefix := range combo.constraintsPrefixes { 1432 ruleMap := map[string]interface{}{ 1433 combo.subrule: map[string]interface{}{ 1434 nameConstrPrefix + "names": []interface{}{"foo"}, 1435 }, 1436 } 1437 1438 rule, err := asserts.CompilePlugRule("iface", ruleMap) 1439 c.Assert(err, IsNil) 1440 c.Check(asserts.RuleFeature(rule, "name-constraints"), Equals, true, Commentf("%v", ruleMap)) 1441 } 1442 } 1443 } 1444 1445 func (s *plugSlotRulesSuite) TestCompileSlotRuleAllAllowDenyStanzas(c *C) { 1446 m, err := asserts.ParseHeaders([]byte(`iface: 1447 allow-installation: 1448 slot-attributes: 1449 a1: A1 1450 deny-installation: 1451 slot-attributes: 1452 a2: A2 1453 allow-connection: 1454 plug-attributes: 1455 pa3: PA3 1456 slot-attributes: 1457 sa3: SA3 1458 deny-connection: 1459 plug-attributes: 1460 pa4: PA4 1461 slot-attributes: 1462 sa4: SA4 1463 allow-auto-connection: 1464 plug-attributes: 1465 pa5: PA5 1466 slot-attributes: 1467 sa5: SA5 1468 deny-auto-connection: 1469 plug-attributes: 1470 pa6: PA6 1471 slot-attributes: 1472 sa6: SA6`)) 1473 c.Assert(err, IsNil) 1474 1475 rule, err := asserts.CompileSlotRule("iface", m["iface"].(map[string]interface{})) 1476 c.Assert(err, IsNil) 1477 1478 c.Check(rule.Interface, Equals, "iface") 1479 // install subrules 1480 c.Assert(rule.AllowInstallation, HasLen, 1) 1481 checkAttrs(c, rule.AllowInstallation[0].SlotAttributes, "a1", "A1") 1482 c.Assert(rule.DenyInstallation, HasLen, 1) 1483 checkAttrs(c, rule.DenyInstallation[0].SlotAttributes, "a2", "A2") 1484 // connection subrules 1485 c.Assert(rule.AllowConnection, HasLen, 1) 1486 checkAttrs(c, rule.AllowConnection[0].PlugAttributes, "pa3", "PA3") 1487 checkAttrs(c, rule.AllowConnection[0].SlotAttributes, "sa3", "SA3") 1488 c.Assert(rule.DenyConnection, HasLen, 1) 1489 checkAttrs(c, rule.DenyConnection[0].PlugAttributes, "pa4", "PA4") 1490 checkAttrs(c, rule.DenyConnection[0].SlotAttributes, "sa4", "SA4") 1491 // auto-connection subrules 1492 c.Assert(rule.AllowAutoConnection, HasLen, 1) 1493 checkAttrs(c, rule.AllowAutoConnection[0].PlugAttributes, "pa5", "PA5") 1494 checkAttrs(c, rule.AllowAutoConnection[0].SlotAttributes, "sa5", "SA5") 1495 c.Assert(rule.DenyAutoConnection, HasLen, 1) 1496 checkAttrs(c, rule.DenyAutoConnection[0].PlugAttributes, "pa6", "PA6") 1497 checkAttrs(c, rule.DenyAutoConnection[0].SlotAttributes, "sa6", "SA6") 1498 } 1499 1500 func (s *plugSlotRulesSuite) TestCompileSlotRuleAllAllowDenyOrStanzas(c *C) { 1501 m, err := asserts.ParseHeaders([]byte(`iface: 1502 allow-installation: 1503 - 1504 slot-attributes: 1505 a1: A1 1506 - 1507 slot-attributes: 1508 a1: A1alt 1509 deny-installation: 1510 - 1511 slot-attributes: 1512 a2: A2 1513 - 1514 slot-attributes: 1515 a2: A2alt 1516 allow-connection: 1517 - 1518 plug-attributes: 1519 pa3: PA3 1520 slot-attributes: 1521 sa3: SA3 1522 - 1523 slot-attributes: 1524 sa3: SA3alt 1525 deny-connection: 1526 - 1527 plug-attributes: 1528 pa4: PA4 1529 slot-attributes: 1530 sa4: SA4 1531 - 1532 slot-attributes: 1533 sa4: SA4alt 1534 allow-auto-connection: 1535 - 1536 plug-attributes: 1537 pa5: PA5 1538 slot-attributes: 1539 sa5: SA5 1540 - 1541 slot-attributes: 1542 sa5: SA5alt 1543 deny-auto-connection: 1544 - 1545 plug-attributes: 1546 pa6: PA6 1547 slot-attributes: 1548 sa6: SA6 1549 - 1550 slot-attributes: 1551 sa6: SA6alt`)) 1552 c.Assert(err, IsNil) 1553 1554 rule, err := asserts.CompileSlotRule("iface", m["iface"].(map[string]interface{})) 1555 c.Assert(err, IsNil) 1556 1557 c.Check(rule.Interface, Equals, "iface") 1558 // install subrules 1559 c.Assert(rule.AllowInstallation, HasLen, 2) 1560 checkAttrs(c, rule.AllowInstallation[0].SlotAttributes, "a1", "A1") 1561 checkAttrs(c, rule.AllowInstallation[1].SlotAttributes, "a1", "A1alt") 1562 c.Assert(rule.DenyInstallation, HasLen, 2) 1563 checkAttrs(c, rule.DenyInstallation[0].SlotAttributes, "a2", "A2") 1564 checkAttrs(c, rule.DenyInstallation[1].SlotAttributes, "a2", "A2alt") 1565 // connection subrules 1566 c.Assert(rule.AllowConnection, HasLen, 2) 1567 checkAttrs(c, rule.AllowConnection[0].PlugAttributes, "pa3", "PA3") 1568 checkAttrs(c, rule.AllowConnection[0].SlotAttributes, "sa3", "SA3") 1569 checkAttrs(c, rule.AllowConnection[1].SlotAttributes, "sa3", "SA3alt") 1570 c.Assert(rule.DenyConnection, HasLen, 2) 1571 checkAttrs(c, rule.DenyConnection[0].PlugAttributes, "pa4", "PA4") 1572 checkAttrs(c, rule.DenyConnection[0].SlotAttributes, "sa4", "SA4") 1573 checkAttrs(c, rule.DenyConnection[1].SlotAttributes, "sa4", "SA4alt") 1574 // auto-connection subrules 1575 c.Assert(rule.AllowAutoConnection, HasLen, 2) 1576 checkAttrs(c, rule.AllowAutoConnection[0].PlugAttributes, "pa5", "PA5") 1577 checkAttrs(c, rule.AllowAutoConnection[0].SlotAttributes, "sa5", "SA5") 1578 checkAttrs(c, rule.AllowAutoConnection[1].SlotAttributes, "sa5", "SA5alt") 1579 c.Assert(rule.DenyAutoConnection, HasLen, 2) 1580 checkAttrs(c, rule.DenyAutoConnection[0].PlugAttributes, "pa6", "PA6") 1581 checkAttrs(c, rule.DenyAutoConnection[0].SlotAttributes, "sa6", "SA6") 1582 checkAttrs(c, rule.DenyAutoConnection[1].SlotAttributes, "sa6", "SA6alt") 1583 } 1584 1585 func (s *plugSlotRulesSuite) TestCompileSlotRuleShortcutTrue(c *C) { 1586 rule, err := asserts.CompileSlotRule("iface", "true") 1587 c.Assert(err, IsNil) 1588 1589 c.Check(rule.Interface, Equals, "iface") 1590 // install subrules 1591 c.Assert(rule.AllowInstallation, HasLen, 1) 1592 c.Check(rule.AllowInstallation[0].SlotAttributes, Equals, asserts.AlwaysMatchAttributes) 1593 c.Assert(rule.DenyInstallation, HasLen, 1) 1594 c.Check(rule.DenyInstallation[0].SlotAttributes, Equals, asserts.NeverMatchAttributes) 1595 // connection subrules 1596 checkBoolSlotConnConstraints(c, "allow-connection", rule.AllowConnection, true) 1597 checkBoolSlotConnConstraints(c, "deny-connection", rule.DenyConnection, false) 1598 // auto-connection subrules 1599 checkBoolSlotConnConstraints(c, "allow-auto-connection", rule.AllowAutoConnection, true) 1600 checkBoolSlotConnConstraints(c, "deny-auto-connection", rule.DenyAutoConnection, false) 1601 } 1602 1603 func (s *plugSlotRulesSuite) TestCompileSlotRuleShortcutFalse(c *C) { 1604 rule, err := asserts.CompileSlotRule("iface", "false") 1605 c.Assert(err, IsNil) 1606 1607 // install subrules 1608 c.Assert(rule.AllowInstallation, HasLen, 1) 1609 c.Check(rule.AllowInstallation[0].SlotAttributes, Equals, asserts.NeverMatchAttributes) 1610 c.Assert(rule.DenyInstallation, HasLen, 1) 1611 c.Check(rule.DenyInstallation[0].SlotAttributes, Equals, asserts.AlwaysMatchAttributes) 1612 // connection subrules 1613 checkBoolSlotConnConstraints(c, "allwo-connection", rule.AllowConnection, false) 1614 checkBoolSlotConnConstraints(c, "deny-connection", rule.DenyConnection, true) 1615 // auto-connection subrules 1616 checkBoolSlotConnConstraints(c, "allow-auto-connection", rule.AllowAutoConnection, false) 1617 checkBoolSlotConnConstraints(c, "deny-auto-connection", rule.DenyAutoConnection, true) 1618 } 1619 1620 func (s *plugSlotRulesSuite) TestCompileSlotRuleDefaults(c *C) { 1621 rule, err := asserts.CompileSlotRule("iface", map[string]interface{}{ 1622 "deny-auto-connection": "true", 1623 }) 1624 c.Assert(err, IsNil) 1625 1626 // everything follows the defaults... 1627 1628 // install subrules 1629 c.Assert(rule.AllowInstallation, HasLen, 1) 1630 c.Check(rule.AllowInstallation[0].SlotAttributes, Equals, asserts.AlwaysMatchAttributes) 1631 c.Assert(rule.DenyInstallation, HasLen, 1) 1632 c.Check(rule.DenyInstallation[0].SlotAttributes, Equals, asserts.NeverMatchAttributes) 1633 // connection subrules 1634 checkBoolSlotConnConstraints(c, "allow-connection", rule.AllowConnection, true) 1635 checkBoolSlotConnConstraints(c, "deny-connection", rule.DenyConnection, false) 1636 // auto-connection subrules 1637 checkBoolSlotConnConstraints(c, "allow-auto-connection", rule.AllowAutoConnection, true) 1638 // ... but deny-auto-connection is on 1639 checkBoolSlotConnConstraints(c, "deny-auto-connection", rule.DenyAutoConnection, true) 1640 } 1641 1642 func (s *plugSlotRulesSuite) TestCompileSlotRuleInstallationConstraintsIDConstraints(c *C) { 1643 rule, err := asserts.CompileSlotRule("iface", map[string]interface{}{ 1644 "allow-installation": map[string]interface{}{ 1645 "slot-snap-type": []interface{}{"core", "kernel", "gadget", "app"}, 1646 }, 1647 }) 1648 c.Assert(err, IsNil) 1649 1650 c.Assert(rule.AllowInstallation, HasLen, 1) 1651 cstrs := rule.AllowInstallation[0] 1652 c.Check(cstrs.SlotSnapTypes, DeepEquals, []string{"core", "kernel", "gadget", "app"}) 1653 } 1654 1655 func (s *plugSlotRulesSuite) TestCompileSlotRuleInstallationConstraintsOnClassic(c *C) { 1656 m, err := asserts.ParseHeaders([]byte(`iface: 1657 allow-installation: true`)) 1658 c.Assert(err, IsNil) 1659 1660 rule, err := asserts.CompileSlotRule("iface", m["iface"].(map[string]interface{})) 1661 c.Assert(err, IsNil) 1662 1663 c.Check(rule.AllowInstallation[0].OnClassic, IsNil) 1664 1665 m, err = asserts.ParseHeaders([]byte(`iface: 1666 allow-installation: 1667 on-classic: false`)) 1668 c.Assert(err, IsNil) 1669 1670 rule, err = asserts.CompileSlotRule("iface", m["iface"].(map[string]interface{})) 1671 c.Assert(err, IsNil) 1672 1673 c.Check(rule.AllowInstallation[0].OnClassic, DeepEquals, &asserts.OnClassicConstraint{}) 1674 1675 m, err = asserts.ParseHeaders([]byte(`iface: 1676 allow-installation: 1677 on-classic: true`)) 1678 c.Assert(err, IsNil) 1679 1680 rule, err = asserts.CompileSlotRule("iface", m["iface"].(map[string]interface{})) 1681 c.Assert(err, IsNil) 1682 1683 c.Check(rule.AllowInstallation[0].OnClassic, DeepEquals, &asserts.OnClassicConstraint{Classic: true}) 1684 1685 m, err = asserts.ParseHeaders([]byte(`iface: 1686 allow-installation: 1687 on-classic: 1688 - ubuntu 1689 - debian`)) 1690 c.Assert(err, IsNil) 1691 1692 rule, err = asserts.CompileSlotRule("iface", m["iface"].(map[string]interface{})) 1693 c.Assert(err, IsNil) 1694 1695 c.Check(rule.AllowInstallation[0].OnClassic, DeepEquals, &asserts.OnClassicConstraint{Classic: true, SystemIDs: []string{"ubuntu", "debian"}}) 1696 } 1697 1698 func (s *plugSlotRulesSuite) TestCompileSlotRuleInstallationConstraintsDeviceScope(c *C) { 1699 m, err := asserts.ParseHeaders([]byte(`iface: 1700 allow-installation: true`)) 1701 c.Assert(err, IsNil) 1702 1703 rule, err := asserts.CompileSlotRule("iface", m["iface"].(map[string]interface{})) 1704 c.Assert(err, IsNil) 1705 1706 c.Check(rule.AllowInstallation[0].DeviceScope, IsNil) 1707 1708 tests := []struct { 1709 rule string 1710 expected asserts.DeviceScopeConstraint 1711 }{ 1712 {`iface: 1713 allow-installation: 1714 on-store: 1715 - my-store`, asserts.DeviceScopeConstraint{Store: []string{"my-store"}}}, 1716 {`iface: 1717 allow-installation: 1718 on-store: 1719 - my-store 1720 - other-store`, asserts.DeviceScopeConstraint{Store: []string{"my-store", "other-store"}}}, 1721 {`iface: 1722 allow-installation: 1723 on-brand: 1724 - my-brand 1725 - s9zGdwb16ysLeRW6nRivwZS5r9puP8JT`, asserts.DeviceScopeConstraint{Brand: []string{"my-brand", "s9zGdwb16ysLeRW6nRivwZS5r9puP8JT"}}}, 1726 {`iface: 1727 allow-installation: 1728 on-model: 1729 - my-brand/bar 1730 - s9zGdwb16ysLeRW6nRivwZS5r9puP8JT/baz`, asserts.DeviceScopeConstraint{Model: []string{"my-brand/bar", "s9zGdwb16ysLeRW6nRivwZS5r9puP8JT/baz"}}}, 1731 {`iface: 1732 allow-installation: 1733 on-store: 1734 - store1 1735 - store2 1736 on-brand: 1737 - my-brand 1738 on-model: 1739 - my-brand/bar 1740 - s9zGdwb16ysLeRW6nRivwZS5r9puP8JT/baz`, asserts.DeviceScopeConstraint{ 1741 Store: []string{"store1", "store2"}, 1742 Brand: []string{"my-brand"}, 1743 Model: []string{"my-brand/bar", "s9zGdwb16ysLeRW6nRivwZS5r9puP8JT/baz"}}}, 1744 } 1745 1746 for _, t := range tests { 1747 m, err = asserts.ParseHeaders([]byte(t.rule)) 1748 c.Assert(err, IsNil) 1749 1750 rule, err = asserts.CompileSlotRule("iface", m["iface"].(map[string]interface{})) 1751 c.Assert(err, IsNil) 1752 1753 c.Check(rule.AllowInstallation[0].DeviceScope, DeepEquals, &t.expected) 1754 } 1755 } 1756 1757 func (s *plugSlotRulesSuite) TestCompileSlotRuleInstallationConstraintsSlotNames(c *C) { 1758 m, err := asserts.ParseHeaders([]byte(`iface: 1759 allow-installation: true`)) 1760 c.Assert(err, IsNil) 1761 1762 rule, err := asserts.CompileSlotRule("iface", m["iface"].(map[string]interface{})) 1763 c.Assert(err, IsNil) 1764 1765 c.Check(rule.AllowInstallation[0].SlotNames, IsNil) 1766 1767 tests := []struct { 1768 rule string 1769 matching []string 1770 notMatching []string 1771 }{ 1772 {`iface: 1773 allow-installation: 1774 slot-names: 1775 - foo`, []string{"foo"}, []string{"bar"}}, 1776 {`iface: 1777 allow-installation: 1778 slot-names: 1779 - foo 1780 - bar`, []string{"foo", "bar"}, []string{"baz"}}, 1781 {`iface: 1782 allow-installation: 1783 slot-names: 1784 - foo[0-9] 1785 - bar`, []string{"foo0", "foo1", "bar"}, []string{"baz", "fooo", "foo12"}}, 1786 } 1787 for _, t := range tests { 1788 m, err = asserts.ParseHeaders([]byte(t.rule)) 1789 c.Assert(err, IsNil) 1790 1791 rule, err = asserts.CompileSlotRule("iface", m["iface"].(map[string]interface{})) 1792 c.Assert(err, IsNil) 1793 1794 for _, matching := range t.matching { 1795 c.Check(rule.AllowInstallation[0].SlotNames.Check("slot name", matching, nil), IsNil) 1796 } 1797 for _, notMatching := range t.notMatching { 1798 c.Check(rule.AllowInstallation[0].SlotNames.Check("slot name", notMatching, nil), NotNil) 1799 } 1800 } 1801 } 1802 1803 func (s *plugSlotRulesSuite) TestCompileSlotRuleConnectionConstraintsIDConstraints(c *C) { 1804 rule, err := asserts.CompileSlotRule("iface", map[string]interface{}{ 1805 "allow-connection": map[string]interface{}{ 1806 "plug-snap-type": []interface{}{"core", "kernel", "gadget", "app"}, 1807 "plug-snap-id": []interface{}{"snapidsnapidsnapidsnapidsnapid01", "snapidsnapidsnapidsnapidsnapid02"}, 1808 "plug-publisher-id": []interface{}{"pubidpubidpubidpubidpubidpubid09", "canonical", "$SAME"}, 1809 }, 1810 }) 1811 c.Assert(err, IsNil) 1812 1813 c.Assert(rule.AllowConnection, HasLen, 1) 1814 cstrs := rule.AllowConnection[0] 1815 c.Check(cstrs.PlugSnapTypes, DeepEquals, []string{"core", "kernel", "gadget", "app"}) 1816 c.Check(cstrs.PlugSnapIDs, DeepEquals, []string{"snapidsnapidsnapidsnapidsnapid01", "snapidsnapidsnapidsnapidsnapid02"}) 1817 c.Check(cstrs.PlugPublisherIDs, DeepEquals, []string{"pubidpubidpubidpubidpubidpubid09", "canonical", "$SAME"}) 1818 } 1819 1820 func (s *plugSlotRulesSuite) TestCompileSlotRuleConnectionConstraintsOnClassic(c *C) { 1821 m, err := asserts.ParseHeaders([]byte(`iface: 1822 allow-connection: true`)) 1823 c.Assert(err, IsNil) 1824 1825 rule, err := asserts.CompileSlotRule("iface", m["iface"].(map[string]interface{})) 1826 c.Assert(err, IsNil) 1827 1828 c.Check(rule.AllowConnection[0].OnClassic, IsNil) 1829 1830 m, err = asserts.ParseHeaders([]byte(`iface: 1831 allow-connection: 1832 on-classic: false`)) 1833 c.Assert(err, IsNil) 1834 1835 rule, err = asserts.CompileSlotRule("iface", m["iface"].(map[string]interface{})) 1836 c.Assert(err, IsNil) 1837 1838 c.Check(rule.AllowConnection[0].OnClassic, DeepEquals, &asserts.OnClassicConstraint{}) 1839 1840 m, err = asserts.ParseHeaders([]byte(`iface: 1841 allow-connection: 1842 on-classic: true`)) 1843 c.Assert(err, IsNil) 1844 1845 rule, err = asserts.CompileSlotRule("iface", m["iface"].(map[string]interface{})) 1846 c.Assert(err, IsNil) 1847 1848 c.Check(rule.AllowConnection[0].OnClassic, DeepEquals, &asserts.OnClassicConstraint{Classic: true}) 1849 1850 m, err = asserts.ParseHeaders([]byte(`iface: 1851 allow-connection: 1852 on-classic: 1853 - ubuntu 1854 - debian`)) 1855 c.Assert(err, IsNil) 1856 1857 rule, err = asserts.CompileSlotRule("iface", m["iface"].(map[string]interface{})) 1858 c.Assert(err, IsNil) 1859 1860 c.Check(rule.AllowConnection[0].OnClassic, DeepEquals, &asserts.OnClassicConstraint{Classic: true, SystemIDs: []string{"ubuntu", "debian"}}) 1861 } 1862 1863 func (s *plugSlotRulesSuite) TestCompileSlotRuleConnectionConstraintsDeviceScope(c *C) { 1864 m, err := asserts.ParseHeaders([]byte(`iface: 1865 allow-connection: true`)) 1866 c.Assert(err, IsNil) 1867 1868 rule, err := asserts.CompileSlotRule("iface", m["iface"].(map[string]interface{})) 1869 c.Assert(err, IsNil) 1870 1871 c.Check(rule.AllowConnection[0].DeviceScope, IsNil) 1872 1873 tests := []struct { 1874 rule string 1875 expected asserts.DeviceScopeConstraint 1876 }{ 1877 {`iface: 1878 allow-connection: 1879 on-store: 1880 - my-store`, asserts.DeviceScopeConstraint{Store: []string{"my-store"}}}, 1881 {`iface: 1882 allow-connection: 1883 on-store: 1884 - my-store 1885 - other-store`, asserts.DeviceScopeConstraint{Store: []string{"my-store", "other-store"}}}, 1886 {`iface: 1887 allow-connection: 1888 on-brand: 1889 - my-brand 1890 - s9zGdwb16ysLeRW6nRivwZS5r9puP8JT`, asserts.DeviceScopeConstraint{Brand: []string{"my-brand", "s9zGdwb16ysLeRW6nRivwZS5r9puP8JT"}}}, 1891 {`iface: 1892 allow-connection: 1893 on-model: 1894 - my-brand/bar 1895 - s9zGdwb16ysLeRW6nRivwZS5r9puP8JT/baz`, asserts.DeviceScopeConstraint{Model: []string{"my-brand/bar", "s9zGdwb16ysLeRW6nRivwZS5r9puP8JT/baz"}}}, 1896 {`iface: 1897 allow-connection: 1898 on-store: 1899 - store1 1900 - store2 1901 on-brand: 1902 - my-brand 1903 on-model: 1904 - my-brand/bar 1905 - s9zGdwb16ysLeRW6nRivwZS5r9puP8JT/baz`, asserts.DeviceScopeConstraint{ 1906 Store: []string{"store1", "store2"}, 1907 Brand: []string{"my-brand"}, 1908 Model: []string{"my-brand/bar", "s9zGdwb16ysLeRW6nRivwZS5r9puP8JT/baz"}}}, 1909 } 1910 1911 for _, t := range tests { 1912 m, err = asserts.ParseHeaders([]byte(t.rule)) 1913 c.Assert(err, IsNil) 1914 1915 rule, err = asserts.CompileSlotRule("iface", m["iface"].(map[string]interface{})) 1916 c.Assert(err, IsNil) 1917 1918 c.Check(rule.AllowConnection[0].DeviceScope, DeepEquals, &t.expected) 1919 } 1920 } 1921 1922 func (s *plugSlotRulesSuite) TestCompileSlotRuleConnectionConstraintsPlugNamesSlotNames(c *C) { 1923 m, err := asserts.ParseHeaders([]byte(`iface: 1924 allow-connection: true`)) 1925 c.Assert(err, IsNil) 1926 1927 rule, err := asserts.CompileSlotRule("iface", m["iface"].(map[string]interface{})) 1928 c.Assert(err, IsNil) 1929 1930 c.Check(rule.AllowConnection[0].PlugNames, IsNil) 1931 c.Check(rule.AllowConnection[0].SlotNames, IsNil) 1932 1933 tests := []struct { 1934 rule string 1935 matching []string 1936 notMatching []string 1937 }{ 1938 {`iface: 1939 allow-connection: 1940 plug-names: 1941 - Pfoo 1942 slot-names: 1943 - Sfoo`, []string{"foo"}, []string{"bar"}}, 1944 {`iface: 1945 allow-connection: 1946 plug-names: 1947 - Pfoo 1948 - Pbar 1949 slot-names: 1950 - Sfoo 1951 - Sbar`, []string{"foo", "bar"}, []string{"baz"}}, 1952 {`iface: 1953 allow-connection: 1954 plug-names: 1955 - Pfoo[0-9] 1956 - Pbar 1957 slot-names: 1958 - Sfoo[0-9] 1959 - Sbar`, []string{"foo0", "foo1", "bar"}, []string{"baz", "fooo", "foo12"}}, 1960 } 1961 for _, t := range tests { 1962 m, err = asserts.ParseHeaders([]byte(t.rule)) 1963 c.Assert(err, IsNil) 1964 1965 rule, err = asserts.CompileSlotRule("iface", m["iface"].(map[string]interface{})) 1966 c.Assert(err, IsNil) 1967 1968 for _, matching := range t.matching { 1969 c.Check(rule.AllowConnection[0].PlugNames.Check("plug name", "P"+matching, nil), IsNil) 1970 1971 c.Check(rule.AllowConnection[0].SlotNames.Check("slot name", "S"+matching, nil), IsNil) 1972 } 1973 1974 for _, notMatching := range t.notMatching { 1975 c.Check(rule.AllowConnection[0].SlotNames.Check("plug name", "P"+notMatching, nil), NotNil) 1976 1977 c.Check(rule.AllowConnection[0].SlotNames.Check("slot name", "S"+notMatching, nil), NotNil) 1978 } 1979 } 1980 } 1981 1982 func (s *plugSlotRulesSuite) TestCompileSlotRuleConnectionConstraintsSideArityConstraints(c *C) { 1983 m, err := asserts.ParseHeaders([]byte(`iface: 1984 allow-auto-connection: true`)) 1985 c.Assert(err, IsNil) 1986 1987 rule, err := asserts.CompileSlotRule("iface", m["iface"].(map[string]interface{})) 1988 c.Assert(err, IsNil) 1989 1990 // defaults 1991 c.Check(rule.AllowAutoConnection[0].SlotsPerPlug, Equals, asserts.SideArityConstraint{N: 1}) 1992 c.Check(rule.AllowAutoConnection[0].PlugsPerSlot.Any(), Equals, true) 1993 1994 c.Check(rule.AllowConnection[0].SlotsPerPlug.Any(), Equals, true) 1995 c.Check(rule.AllowConnection[0].PlugsPerSlot.Any(), Equals, true) 1996 1997 // test that the arity constraints get normalized away to any 1998 // under allow-connection 1999 // see https://forum.snapcraft.io/t/plug-slot-declaration-rules-greedy-plugs/12438 2000 allowConnTests := []string{ 2001 `iface: 2002 allow-connection: 2003 slots-per-plug: 1 2004 plugs-per-slot: 2`, 2005 `iface: 2006 allow-connection: 2007 slots-per-plug: * 2008 plugs-per-slot: 1`, 2009 `iface: 2010 allow-connection: 2011 slots-per-plug: 2 2012 plugs-per-slot: *`, 2013 } 2014 2015 for _, t := range allowConnTests { 2016 m, err = asserts.ParseHeaders([]byte(t)) 2017 c.Assert(err, IsNil) 2018 2019 rule, err = asserts.CompileSlotRule("iface", m["iface"].(map[string]interface{})) 2020 c.Assert(err, IsNil) 2021 2022 c.Check(rule.AllowConnection[0].SlotsPerPlug.Any(), Equals, true) 2023 c.Check(rule.AllowConnection[0].PlugsPerSlot.Any(), Equals, true) 2024 } 2025 2026 // test that under allow-auto-connection: 2027 // slots-per-plug can be * (any) or otherwise gets normalized to 1 2028 // plugs-per-slot gets normalized to any (*) 2029 // see https://forum.snapcraft.io/t/plug-slot-declaration-rules-greedy-plugs/12438 2030 allowAutoConnTests := []struct { 2031 rule string 2032 slotsPerPlug asserts.SideArityConstraint 2033 }{ 2034 {`iface: 2035 allow-auto-connection: 2036 slots-per-plug: 1 2037 plugs-per-slot: 2`, sideArityOne}, 2038 {`iface: 2039 allow-auto-connection: 2040 slots-per-plug: * 2041 plugs-per-slot: 1`, sideArityAny}, 2042 {`iface: 2043 allow-auto-connection: 2044 slots-per-plug: 2 2045 plugs-per-slot: *`, sideArityOne}, 2046 } 2047 2048 for _, t := range allowAutoConnTests { 2049 m, err = asserts.ParseHeaders([]byte(t.rule)) 2050 c.Assert(err, IsNil) 2051 2052 rule, err = asserts.CompileSlotRule("iface", m["iface"].(map[string]interface{})) 2053 c.Assert(err, IsNil) 2054 2055 c.Check(rule.AllowAutoConnection[0].SlotsPerPlug, Equals, t.slotsPerPlug) 2056 c.Check(rule.AllowAutoConnection[0].PlugsPerSlot.Any(), Equals, true) 2057 } 2058 } 2059 2060 func (s *plugSlotRulesSuite) TestCompileSlotRuleErrors(c *C) { 2061 tests := []struct { 2062 stanza string 2063 err string 2064 }{ 2065 {`iface: foo`, `slot rule for interface "iface" must be a map or one of the shortcuts 'true' or 'false'`}, 2066 {`iface: 2067 - allow`, `slot rule for interface "iface" must be a map or one of the shortcuts 'true' or 'false'`}, 2068 {`iface: 2069 allow-installation: foo`, `allow-installation in slot rule for interface "iface" must be a map or one of the shortcuts 'true' or 'false'`}, 2070 {`iface: 2071 deny-installation: foo`, `deny-installation in slot rule for interface "iface" must be a map or one of the shortcuts 'true' or 'false'`}, 2072 {`iface: 2073 allow-connection: foo`, `allow-connection in slot rule for interface "iface" must be a map or one of the shortcuts 'true' or 'false'`}, 2074 {`iface: 2075 allow-connection: 2076 - foo`, `alternative 1 of allow-connection in slot rule for interface "iface" must be a map`}, 2077 {`iface: 2078 allow-connection: 2079 - true`, `alternative 1 of allow-connection in slot rule for interface "iface" must be a map`}, 2080 {`iface: 2081 allow-installation: 2082 slot-attributes: 2083 a1: [`, `cannot compile slot-attributes in allow-installation in slot rule for interface "iface": cannot compile "a1" constraint .*`}, 2084 {`iface: 2085 allow-connection: 2086 plug-attributes: 2087 a2: [`, `cannot compile plug-attributes in allow-connection in slot rule for interface "iface": cannot compile "a2" constraint .*`}, 2088 {`iface: 2089 allow-connection: 2090 plug-snap-id: 2091 - 2092 foo: 1`, `plug-snap-id in allow-connection in slot rule for interface "iface" must be a list of strings`}, 2093 {`iface: 2094 allow-connection: 2095 plug-snap-id: 2096 - foo`, `plug-snap-id in allow-connection in slot rule for interface "iface" contains an invalid element: "foo"`}, 2097 {`iface: 2098 allow-connection: 2099 plug-snap-type: 2100 - foo`, `plug-snap-type in allow-connection in slot rule for interface "iface" contains an invalid element: "foo"`}, 2101 {`iface: 2102 allow-connection: 2103 plug-snap-type: 2104 - xapp`, `plug-snap-type in allow-connection in slot rule for interface "iface" contains an invalid element: "xapp"`}, 2105 {`iface: 2106 allow-connection: 2107 on-classic: 2108 x: 1`, `on-classic in allow-connection in slot rule for interface \"iface\" must be 'true', 'false' or a list of operating system IDs`}, 2109 {`iface: 2110 allow-connection: 2111 on-classic: 2112 - zoom!`, `on-classic in allow-connection in slot rule for interface \"iface\" contains an invalid element: \"zoom!\"`}, 2113 {`iface: 2114 allow-connection: 2115 plug-snap-ids: 2116 - foo`, `allow-connection in slot rule for interface "iface" must specify at least one of plug-names, slot-names, plug-attributes, slot-attributes, plug-snap-type, plug-publisher-id, plug-snap-id, slots-per-plug, plugs-per-slot, on-classic, on-store, on-brand, on-model`}, 2117 {`iface: 2118 deny-connection: 2119 plug-snap-ids: 2120 - foo`, `deny-connection in slot rule for interface "iface" must specify at least one of plug-names, slot-names, plug-attributes, slot-attributes, plug-snap-type, plug-publisher-id, plug-snap-id, slots-per-plug, plugs-per-slot, on-classic, on-store, on-brand, on-model`}, 2121 {`iface: 2122 allow-auto-connection: 2123 plug-snap-ids: 2124 - foo`, `allow-auto-connection in slot rule for interface "iface" must specify at least one of plug-names, slot-names, plug-attributes, slot-attributes, plug-snap-type, plug-publisher-id, plug-snap-id, slots-per-plug, plugs-per-slot, on-classic, on-store, on-brand, on-model`}, 2125 {`iface: 2126 deny-auto-connection: 2127 plug-snap-ids: 2128 - foo`, `deny-auto-connection in slot rule for interface "iface" must specify at least one of plug-names, slot-names, plug-attributes, slot-attributes, plug-snap-type, plug-publisher-id, plug-snap-id, slots-per-plug, plugs-per-slot, on-classic, on-store, on-brand, on-model`}, 2129 {`iface: 2130 allow-connect: true`, `slot rule for interface "iface" must specify at least one of allow-installation, deny-installation, allow-connection, deny-connection, allow-auto-connection, deny-auto-connection`}, 2131 {`iface: 2132 allow-installation: 2133 on-store: true`, `on-store in allow-installation in slot rule for interface \"iface\" must be a list of strings`}, 2134 {`iface: 2135 allow-installation: 2136 on-store: store1`, `on-store in allow-installation in slot rule for interface \"iface\" must be a list of strings`}, 2137 {`iface: 2138 allow-installation: 2139 on-store: 2140 - zoom!`, `on-store in allow-installation in slot rule for interface \"iface\" contains an invalid element: \"zoom!\"`}, 2141 {`iface: 2142 allow-connection: 2143 on-brand: true`, `on-brand in allow-connection in slot rule for interface \"iface\" must be a list of strings`}, 2144 {`iface: 2145 allow-connection: 2146 on-brand: brand1`, `on-brand in allow-connection in slot rule for interface \"iface\" must be a list of strings`}, 2147 {`iface: 2148 allow-connection: 2149 on-brand: 2150 - zoom!`, `on-brand in allow-connection in slot rule for interface \"iface\" contains an invalid element: \"zoom!\"`}, 2151 {`iface: 2152 allow-auto-connection: 2153 on-model: true`, `on-model in allow-auto-connection in slot rule for interface \"iface\" must be a list of strings`}, 2154 {`iface: 2155 allow-auto-connection: 2156 on-model: foo/bar`, `on-model in allow-auto-connection in slot rule for interface \"iface\" must be a list of strings`}, 2157 {`iface: 2158 allow-auto-connection: 2159 on-model: 2160 - foo//bar`, `on-model in allow-auto-connection in slot rule for interface \"iface\" contains an invalid element: \"foo//bar"`}, 2161 {`iface: 2162 allow-installation: 2163 slots-per-plug: 1`, `allow-installation in slot rule for interface "iface" cannot specify a slots-per-plug constraint, they apply only to allow-\*connection`}, 2164 {`iface: 2165 deny-auto-connection: 2166 slots-per-plug: 1`, `deny-auto-connection in slot rule for interface "iface" cannot specify a slots-per-plug constraint, they apply only to allow-\*connection`}, 2167 {`iface: 2168 allow-auto-connection: 2169 plugs-per-slot: any`, `plugs-per-slot in allow-auto-connection in slot rule for interface "iface" must be an integer >=1 or \*`}, 2170 {`iface: 2171 allow-auto-connection: 2172 slots-per-plug: 00`, `slots-per-plug in allow-auto-connection in slot rule for interface "iface" has invalid prefix zeros: 00`}, 2173 {`iface: 2174 allow-auto-connection: 2175 slots-per-plug: 99999999999999999999`, `slots-per-plug in allow-auto-connection in slot rule for interface "iface" is out of range: 99999999999999999999`}, 2176 {`iface: 2177 allow-auto-connection: 2178 slots-per-plug: 0`, `slots-per-plug in allow-auto-connection in slot rule for interface "iface" must be an integer >=1 or \*`}, 2179 {`iface: 2180 allow-auto-connection: 2181 slots-per-plug: 2182 what: 1`, `slots-per-plug in allow-auto-connection in slot rule for interface "iface" must be an integer >=1 or \*`}, 2183 {`iface: 2184 allow-auto-connection: 2185 plug-names: true`, `plug-names constraints must be a list of regexps and special \$ values`}, 2186 {`iface: 2187 allow-auto-connection: 2188 slot-names: true`, `slot-names constraints must be a list of regexps and special \$ values`}, 2189 } 2190 2191 for _, t := range tests { 2192 m, err := asserts.ParseHeaders([]byte(t.stanza)) 2193 c.Assert(err, IsNil, Commentf(t.stanza)) 2194 _, err = asserts.CompileSlotRule("iface", m["iface"]) 2195 c.Check(err, ErrorMatches, t.err, Commentf(t.stanza)) 2196 } 2197 } 2198 2199 func (s *plugSlotRulesSuite) TestSlotRuleFeatures(c *C) { 2200 combos := []struct { 2201 subrule string 2202 constraintsPrefixes []string 2203 }{ 2204 {"allow-installation", []string{"slot-"}}, 2205 {"deny-installation", []string{"slot-"}}, 2206 {"allow-connection", []string{"plug-", "slot-"}}, 2207 {"deny-connection", []string{"plug-", "slot-"}}, 2208 {"allow-auto-connection", []string{"plug-", "slot-"}}, 2209 {"deny-auto-connection", []string{"plug-", "slot-"}}, 2210 } 2211 2212 for _, combo := range combos { 2213 for _, attrConstrPrefix := range combo.constraintsPrefixes { 2214 attrConstraintMap := map[string]interface{}{ 2215 "a": "ATTR", 2216 } 2217 ruleMap := map[string]interface{}{ 2218 combo.subrule: map[string]interface{}{ 2219 attrConstrPrefix + "attributes": attrConstraintMap, 2220 }, 2221 } 2222 2223 rule, err := asserts.CompileSlotRule("iface", ruleMap) 2224 c.Assert(err, IsNil) 2225 c.Check(asserts.RuleFeature(rule, "dollar-attr-constraints"), Equals, false, Commentf("%v", ruleMap)) 2226 2227 c.Check(asserts.RuleFeature(rule, "device-scope-constraints"), Equals, false, Commentf("%v", ruleMap)) 2228 2229 attrConstraintMap["a"] = "$PLUG(a)" 2230 rule, err = asserts.CompileSlotRule("iface", ruleMap) 2231 c.Assert(err, IsNil) 2232 c.Check(asserts.RuleFeature(rule, "dollar-attr-constraints"), Equals, true, Commentf("%v", ruleMap)) 2233 2234 c.Check(asserts.RuleFeature(rule, "device-scope-constraints"), Equals, false, Commentf("%v", ruleMap)) 2235 } 2236 2237 for deviceScopeConstr, value := range deviceScopeConstrs { 2238 ruleMap := map[string]interface{}{ 2239 combo.subrule: map[string]interface{}{ 2240 deviceScopeConstr: value, 2241 }, 2242 } 2243 2244 rule, err := asserts.CompileSlotRule("iface", ruleMap) 2245 c.Assert(err, IsNil) 2246 c.Check(asserts.RuleFeature(rule, "device-scope-constraints"), Equals, true, Commentf("%v", ruleMap)) 2247 } 2248 2249 for _, nameConstrPrefix := range combo.constraintsPrefixes { 2250 ruleMap := map[string]interface{}{ 2251 combo.subrule: map[string]interface{}{ 2252 nameConstrPrefix + "names": []interface{}{"foo"}, 2253 }, 2254 } 2255 2256 rule, err := asserts.CompileSlotRule("iface", ruleMap) 2257 c.Assert(err, IsNil) 2258 c.Check(asserts.RuleFeature(rule, "name-constraints"), Equals, true, Commentf("%v", ruleMap)) 2259 } 2260 2261 } 2262 } 2263 2264 func (s *plugSlotRulesSuite) TestValidOnStoreBrandModel(c *C) { 2265 tests := []struct { 2266 constr string 2267 value string 2268 valid bool 2269 }{ 2270 {"on-store", "", false}, 2271 {"on-store", "foo", true}, 2272 {"on-store", "F_o-O88", true}, 2273 {"on-store", "foo!", false}, 2274 {"on-store", "foo.", false}, 2275 {"on-store", "foo/", false}, 2276 {"on-brand", "", false}, 2277 // custom set brands (length 2-28) 2278 {"on-brand", "dwell", true}, 2279 {"on-brand", "Dwell", false}, 2280 {"on-brand", "dwell-88", true}, 2281 {"on-brand", "dwell_88", false}, 2282 {"on-brand", "dwell.88", false}, 2283 {"on-brand", "dwell:88", false}, 2284 {"on-brand", "dwell!88", false}, 2285 {"on-brand", "a", false}, 2286 {"on-brand", "ab", true}, 2287 {"on-brand", "0123456789012345678901234567", true}, 2288 // snappy id brands (fixed length 32) 2289 {"on-brand", "01234567890123456789012345678", false}, 2290 {"on-brand", "012345678901234567890123456789", false}, 2291 {"on-brand", "0123456789012345678901234567890", false}, 2292 {"on-brand", "01234567890123456789012345678901", true}, 2293 {"on-brand", "abcdefghijklmnopqrstuvwxyz678901", true}, 2294 {"on-brand", "ABCDEFGHIJKLMNOPQRSTUVWCYZ678901", true}, 2295 {"on-brand", "ABCDEFGHIJKLMNOPQRSTUVWCYZ678901X", false}, 2296 {"on-brand", "ABCDEFGHIJKLMNOPQ!STUVWCYZ678901", false}, 2297 {"on-brand", "ABCDEFGHIJKLMNOPQ_STUVWCYZ678901", false}, 2298 {"on-brand", "ABCDEFGHIJKLMNOPQ-STUVWCYZ678901", false}, 2299 {"on-model", "", false}, 2300 {"on-model", "/", false}, 2301 {"on-model", "dwell/dwell1", true}, 2302 {"on-model", "dwell", false}, 2303 {"on-model", "dwell/", false}, 2304 {"on-model", "dwell//dwell1", false}, 2305 {"on-model", "dwell/-dwell1", false}, 2306 {"on-model", "dwell/dwell1-", false}, 2307 {"on-model", "dwell/dwell1-23", true}, 2308 {"on-model", "dwell/dwell1!", false}, 2309 {"on-model", "dwell/dwe_ll1", false}, 2310 {"on-model", "dwell/dwe.ll1", false}, 2311 } 2312 2313 check := func(constr, value string, valid bool) { 2314 ruleMap := map[string]interface{}{ 2315 "allow-auto-connection": map[string]interface{}{ 2316 constr: []interface{}{value}, 2317 }, 2318 } 2319 2320 _, err := asserts.CompilePlugRule("iface", ruleMap) 2321 if valid { 2322 c.Check(err, IsNil, Commentf("%v", ruleMap)) 2323 } else { 2324 c.Check(err, ErrorMatches, fmt.Sprintf(`%s in allow-auto-connection in plug rule for interface "iface" contains an invalid element: %q`, constr, value), Commentf("%v", ruleMap)) 2325 } 2326 } 2327 2328 for _, t := range tests { 2329 check(t.constr, t.value, t.valid) 2330 2331 if t.constr == "on-brand" { 2332 // reuse and double check all brands also in the context of on-model! 2333 2334 check("on-model", t.value+"/foo", t.valid) 2335 } 2336 } 2337 }