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