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