github.com/rigado/snapd@v2.42.5-go-mod+incompatible/interfaces/policy/basedeclaration_test.go (about) 1 // -*- Mode: Go; indent-tabs-mode: t -*- 2 3 /* 4 * Copyright (C) 2016-2018 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 policy_test 21 22 import ( 23 "fmt" 24 "strings" 25 26 . "gopkg.in/check.v1" 27 28 "github.com/snapcore/snapd/asserts" 29 "github.com/snapcore/snapd/interfaces" 30 "github.com/snapcore/snapd/interfaces/builtin" 31 "github.com/snapcore/snapd/interfaces/policy" 32 "github.com/snapcore/snapd/release" 33 "github.com/snapcore/snapd/snap" 34 "github.com/snapcore/snapd/snap/snaptest" 35 "github.com/snapcore/snapd/strutil" 36 "github.com/snapcore/snapd/testutil" 37 ) 38 39 type baseDeclSuite struct { 40 baseDecl *asserts.BaseDeclaration 41 restoreSanitize func() 42 } 43 44 var _ = Suite(&baseDeclSuite{}) 45 46 func (s *baseDeclSuite) SetUpSuite(c *C) { 47 s.restoreSanitize = snap.MockSanitizePlugsSlots(func(snapInfo *snap.Info) {}) 48 s.baseDecl = asserts.BuiltinBaseDeclaration() 49 } 50 51 func (s *baseDeclSuite) TearDownSuite(c *C) { 52 s.restoreSanitize() 53 } 54 55 func (s *baseDeclSuite) connectCand(c *C, iface, slotYaml, plugYaml string) *policy.ConnectCandidate { 56 if slotYaml == "" { 57 slotYaml = fmt.Sprintf(`name: slot-snap 58 version: 0 59 slots: 60 %s: 61 `, iface) 62 } 63 if plugYaml == "" { 64 plugYaml = fmt.Sprintf(`name: plug-snap 65 version: 0 66 plugs: 67 %s: 68 `, iface) 69 } 70 slotSnap := snaptest.MockInfo(c, slotYaml, nil) 71 plugSnap := snaptest.MockInfo(c, plugYaml, nil) 72 return &policy.ConnectCandidate{ 73 Plug: interfaces.NewConnectedPlug(plugSnap.Plugs[iface], nil, nil), 74 Slot: interfaces.NewConnectedSlot(slotSnap.Slots[iface], nil, nil), 75 BaseDeclaration: s.baseDecl, 76 } 77 } 78 79 func (s *baseDeclSuite) installSlotCand(c *C, iface string, snapType snap.Type, yaml string) *policy.InstallCandidate { 80 if yaml == "" { 81 yaml = fmt.Sprintf(`name: install-slot-snap 82 version: 0 83 type: %s 84 slots: 85 %s: 86 `, snapType, iface) 87 } 88 snap := snaptest.MockInfo(c, yaml, nil) 89 return &policy.InstallCandidate{ 90 Snap: snap, 91 BaseDeclaration: s.baseDecl, 92 } 93 } 94 95 func (s *baseDeclSuite) installPlugCand(c *C, iface string, snapType snap.Type, yaml string) *policy.InstallCandidate { 96 if yaml == "" { 97 yaml = fmt.Sprintf(`name: install-plug-snap 98 version: 0 99 type: %s 100 plugs: 101 %s: 102 `, snapType, iface) 103 } 104 snap := snaptest.MockInfo(c, yaml, nil) 105 return &policy.InstallCandidate{ 106 Snap: snap, 107 BaseDeclaration: s.baseDecl, 108 } 109 } 110 111 const declTempl = `type: snap-declaration 112 authority-id: canonical 113 series: 16 114 snap-name: @name@ 115 snap-id: @snapid@ 116 publisher-id: @publisher@ 117 @plugsSlots@ 118 timestamp: 2016-09-30T12:00:00Z 119 sign-key-sha3-384: Jv8_JiHiIzJVcO9M55pPdqSDWUvuhfDIBJUS-3VW7F_idjix7Ffn5qMxB21ZQuij 120 121 AXNpZw==` 122 123 func (s *baseDeclSuite) mockSnapDecl(c *C, name, snapID, publisher string, plugsSlots string) *asserts.SnapDeclaration { 124 encoded := strings.Replace(declTempl, "@name@", name, 1) 125 encoded = strings.Replace(encoded, "@snapid@", snapID, 1) 126 encoded = strings.Replace(encoded, "@publisher@", publisher, 1) 127 if plugsSlots != "" { 128 encoded = strings.Replace(encoded, "@plugsSlots@", strings.TrimSpace(plugsSlots), 1) 129 } else { 130 encoded = strings.Replace(encoded, "@plugsSlots@\n", "", 1) 131 } 132 a, err := asserts.Decode([]byte(encoded)) 133 c.Assert(err, IsNil) 134 return a.(*asserts.SnapDeclaration) 135 } 136 137 func (s *baseDeclSuite) TestAutoConnection(c *C) { 138 all := builtin.Interfaces() 139 140 // these have more complex or in flux policies and have their 141 // own separate tests 142 snowflakes := map[string]bool{ 143 "content": true, 144 "core-support": true, 145 "home": true, 146 "lxd-support": true, 147 "multipass-support": true, 148 "packagekit-control": true, 149 "snapd-control": true, 150 "dummy": true, 151 } 152 153 // these simply auto-connect, anything else doesn't 154 autoconnect := map[string]bool{ 155 "audio-playback": true, 156 "browser-support": true, 157 "desktop": true, 158 "desktop-legacy": true, 159 "gsettings": true, 160 "media-hub": true, 161 "mir": true, 162 "network": true, 163 "network-bind": true, 164 "network-status": true, 165 "online-accounts-service": true, 166 "opengl": true, 167 "optical-drive": true, 168 "pulseaudio": true, 169 "screen-inhibit-control": true, 170 "ubuntu-download-manager": true, 171 "unity7": true, 172 "unity8": true, 173 "upower-observe": true, 174 "wayland": true, 175 "x11": true, 176 } 177 178 for _, iface := range all { 179 if snowflakes[iface.Name()] { 180 continue 181 } 182 expected := autoconnect[iface.Name()] 183 comm := Commentf(iface.Name()) 184 185 // check base declaration 186 cand := s.connectCand(c, iface.Name(), "", "") 187 err := cand.CheckAutoConnect() 188 if expected { 189 c.Check(err, IsNil, comm) 190 } else { 191 c.Check(err, NotNil, comm) 192 } 193 } 194 } 195 196 func (s *baseDeclSuite) TestAutoConnectPlugSlot(c *C) { 197 all := builtin.Interfaces() 198 199 // these have more complex or in flux policies and have their 200 // own separate tests 201 snowflakes := map[string]bool{ 202 "classic-support": true, 203 "content": true, 204 "home": true, 205 "lxd-support": true, 206 } 207 208 for _, iface := range all { 209 if snowflakes[iface.Name()] { 210 continue 211 } 212 c.Check(iface.AutoConnect(nil, nil), Equals, true) 213 } 214 } 215 216 func (s *baseDeclSuite) TestInterimAutoConnectionHome(c *C) { 217 restore := release.MockOnClassic(true) 218 defer restore() 219 cand := s.connectCand(c, "home", "", "") 220 err := cand.CheckAutoConnect() 221 c.Check(err, IsNil) 222 223 release.OnClassic = false 224 err = cand.CheckAutoConnect() 225 c.Check(err, ErrorMatches, `auto-connection denied by slot rule of interface \"home\"`) 226 } 227 228 func (s *baseDeclSuite) TestHomeReadAll(c *C) { 229 const plugYaml = `name: plug-snap 230 version: 0 231 plugs: 232 home: 233 read: all 234 ` 235 restore := release.MockOnClassic(true) 236 defer restore() 237 cand := s.connectCand(c, "home", "", plugYaml) 238 err := cand.Check() 239 c.Check(err, NotNil) 240 241 err = cand.CheckAutoConnect() 242 c.Check(err, NotNil) 243 244 release.OnClassic = false 245 err = cand.Check() 246 c.Check(err, NotNil) 247 248 err = cand.CheckAutoConnect() 249 c.Check(err, NotNil) 250 } 251 252 func (s *baseDeclSuite) TestHomeReadDefault(c *C) { 253 const plugYaml = `name: plug-snap 254 version: 0 255 plugs: 256 home: null 257 ` 258 restore := release.MockOnClassic(true) 259 defer restore() 260 cand := s.connectCand(c, "home", "", plugYaml) 261 err := cand.Check() 262 c.Check(err, IsNil) 263 264 // Same as TestInterimAutoConnectionHome() 265 err = cand.CheckAutoConnect() 266 c.Check(err, IsNil) 267 268 release.OnClassic = false 269 err = cand.Check() 270 c.Check(err, IsNil) 271 272 // Same as TestInterimAutoConnectionHome() 273 err = cand.CheckAutoConnect() 274 c.Check(err, NotNil) 275 } 276 277 func (s *baseDeclSuite) TestAutoConnectionSnapdControl(c *C) { 278 cand := s.connectCand(c, "snapd-control", "", "") 279 err := cand.CheckAutoConnect() 280 c.Check(err, NotNil) 281 c.Assert(err, ErrorMatches, "auto-connection denied by plug rule of interface \"snapd-control\"") 282 283 plugsSlots := ` 284 plugs: 285 snapd-control: 286 allow-auto-connection: true 287 ` 288 289 lxdDecl := s.mockSnapDecl(c, "some-snap", "J60k4JY0HppjwOjW8dZdYc8obXKxujRu", "canonical", plugsSlots) 290 cand.PlugSnapDeclaration = lxdDecl 291 err = cand.CheckAutoConnect() 292 c.Check(err, IsNil) 293 } 294 295 func (s *baseDeclSuite) TestAutoConnectionContent(c *C) { 296 // random snaps cannot connect with content 297 // (Sanitize* will now also block this) 298 cand := s.connectCand(c, "content", "", "") 299 err := cand.CheckAutoConnect() 300 c.Check(err, NotNil) 301 302 slotDecl1 := s.mockSnapDecl(c, "slot-snap", "slot-snap-id", "pub1", "") 303 plugDecl1 := s.mockSnapDecl(c, "plug-snap", "plug-snap-id", "pub1", "") 304 plugDecl2 := s.mockSnapDecl(c, "plug-snap", "plug-snap-id", "pub2", "") 305 306 // same publisher, same content 307 cand = s.connectCand(c, "stuff", ` 308 name: slot-snap 309 version: 0 310 slots: 311 stuff: 312 interface: content 313 content: mk1 314 `, ` 315 name: plug-snap 316 version: 0 317 plugs: 318 stuff: 319 interface: content 320 content: mk1 321 `) 322 cand.SlotSnapDeclaration = slotDecl1 323 cand.PlugSnapDeclaration = plugDecl1 324 err = cand.CheckAutoConnect() 325 c.Check(err, IsNil) 326 327 // different publisher, same content 328 cand.SlotSnapDeclaration = slotDecl1 329 cand.PlugSnapDeclaration = plugDecl2 330 err = cand.CheckAutoConnect() 331 c.Check(err, NotNil) 332 333 // same publisher, different content 334 cand = s.connectCand(c, "stuff", `name: slot-snap 335 version: 0 336 slots: 337 stuff: 338 interface: content 339 content: mk1 340 `, ` 341 name: plug-snap 342 version: 0 343 plugs: 344 stuff: 345 interface: content 346 content: mk2 347 `) 348 cand.SlotSnapDeclaration = slotDecl1 349 cand.PlugSnapDeclaration = plugDecl1 350 err = cand.CheckAutoConnect() 351 c.Check(err, NotNil) 352 } 353 354 func (s *baseDeclSuite) TestAutoConnectionLxdSupportOverride(c *C) { 355 // by default, don't auto-connect 356 cand := s.connectCand(c, "lxd-support", "", "") 357 err := cand.CheckAutoConnect() 358 c.Check(err, NotNil) 359 360 plugsSlots := ` 361 plugs: 362 lxd-support: 363 allow-auto-connection: true 364 ` 365 366 lxdDecl := s.mockSnapDecl(c, "lxd", "J60k4JY0HppjwOjW8dZdYc8obXKxujRu", "canonical", plugsSlots) 367 cand.PlugSnapDeclaration = lxdDecl 368 err = cand.CheckAutoConnect() 369 c.Check(err, IsNil) 370 } 371 372 func (s *baseDeclSuite) TestAutoConnectionLxdSupportOverrideRevoke(c *C) { 373 cand := s.connectCand(c, "lxd-support", "", "") 374 plugsSlots := ` 375 plugs: 376 lxd-support: 377 allow-auto-connection: false 378 ` 379 380 lxdDecl := s.mockSnapDecl(c, "notlxd", "J60k4JY0HppjwOjW8dZdYc8obXKxujRu", "canonical", plugsSlots) 381 cand.PlugSnapDeclaration = lxdDecl 382 err := cand.CheckAutoConnect() 383 c.Check(err, NotNil) 384 c.Assert(err, ErrorMatches, "auto-connection not allowed by plug rule of interface \"lxd-support\" for \"notlxd\" snap") 385 } 386 387 func (s *baseDeclSuite) TestAutoConnectionKernelModuleControlOverride(c *C) { 388 cand := s.connectCand(c, "kernel-module-control", "", "") 389 err := cand.CheckAutoConnect() 390 c.Check(err, NotNil) 391 c.Assert(err, ErrorMatches, "auto-connection denied by plug rule of interface \"kernel-module-control\"") 392 393 plugsSlots := ` 394 plugs: 395 kernel-module-control: 396 allow-auto-connection: true 397 ` 398 399 snapDecl := s.mockSnapDecl(c, "some-snap", "J60k4JY0HppjwOjW8dZdYc8obXKxujRu", "canonical", plugsSlots) 400 cand.PlugSnapDeclaration = snapDecl 401 err = cand.CheckAutoConnect() 402 c.Check(err, IsNil) 403 } 404 405 func (s *baseDeclSuite) TestAutoConnectionDockerSupportOverride(c *C) { 406 cand := s.connectCand(c, "docker-support", "", "") 407 err := cand.CheckAutoConnect() 408 c.Check(err, NotNil) 409 c.Assert(err, ErrorMatches, "auto-connection denied by plug rule of interface \"docker-support\"") 410 411 plugsSlots := ` 412 plugs: 413 docker-support: 414 allow-auto-connection: true 415 ` 416 417 snapDecl := s.mockSnapDecl(c, "some-snap", "J60k4JY0HppjwOjW8dZdYc8obXKxujRu", "canonical", plugsSlots) 418 cand.PlugSnapDeclaration = snapDecl 419 err = cand.CheckAutoConnect() 420 c.Check(err, IsNil) 421 } 422 423 func (s *baseDeclSuite) TestAutoConnectionClassicSupportOverride(c *C) { 424 cand := s.connectCand(c, "classic-support", "", "") 425 err := cand.CheckAutoConnect() 426 c.Check(err, NotNil) 427 c.Assert(err, ErrorMatches, "auto-connection denied by plug rule of interface \"classic-support\"") 428 429 plugsSlots := ` 430 plugs: 431 classic-support: 432 allow-auto-connection: true 433 ` 434 435 snapDecl := s.mockSnapDecl(c, "classic", "J60k4JY0HppjwOjW8dZdYc8obXKxujRu", "canonical", plugsSlots) 436 cand.PlugSnapDeclaration = snapDecl 437 err = cand.CheckAutoConnect() 438 c.Check(err, IsNil) 439 } 440 441 func (s *baseDeclSuite) TestAutoConnectionKubernetesSupportOverride(c *C) { 442 cand := s.connectCand(c, "kubernetes-support", "", "") 443 err := cand.CheckAutoConnect() 444 c.Check(err, NotNil) 445 c.Assert(err, ErrorMatches, "auto-connection denied by plug rule of interface \"kubernetes-support\"") 446 447 plugsSlots := ` 448 plugs: 449 kubernetes-support: 450 allow-auto-connection: true 451 ` 452 453 snapDecl := s.mockSnapDecl(c, "some-snap", "J60k4JY0HppjwOjW8dZdYc8obXKxujRu", "canonical", plugsSlots) 454 cand.PlugSnapDeclaration = snapDecl 455 err = cand.CheckAutoConnect() 456 c.Check(err, IsNil) 457 } 458 459 func (s *baseDeclSuite) TestAutoConnectionGreengrassSupportOverride(c *C) { 460 cand := s.connectCand(c, "greengrass-support", "", "") 461 err := cand.CheckAutoConnect() 462 c.Check(err, NotNil) 463 c.Assert(err, ErrorMatches, "auto-connection denied by plug rule of interface \"greengrass-support\"") 464 465 plugsSlots := ` 466 plugs: 467 greengrass-support: 468 allow-auto-connection: true 469 ` 470 471 snapDecl := s.mockSnapDecl(c, "some-snap", "J60k4JY0HppjwOjW8dZdYc8obXKxujRu", "canonical", plugsSlots) 472 cand.PlugSnapDeclaration = snapDecl 473 err = cand.CheckAutoConnect() 474 c.Check(err, IsNil) 475 } 476 477 func (s *baseDeclSuite) TestAutoConnectionMultipassSupportOverride(c *C) { 478 cand := s.connectCand(c, "multipass-support", "", "") 479 err := cand.CheckAutoConnect() 480 c.Check(err, NotNil) 481 c.Assert(err, ErrorMatches, "auto-connection denied by plug rule of interface \"multipass-support\"") 482 483 plugsSlots := ` 484 plugs: 485 multipass-support: 486 allow-auto-connection: true 487 ` 488 489 snapDecl := s.mockSnapDecl(c, "multipass-snap", "J60k4JY0HppjwOjW8dZdYc8obXKxujRu", "canonical", plugsSlots) 490 cand.PlugSnapDeclaration = snapDecl 491 err = cand.CheckAutoConnect() 492 c.Check(err, IsNil) 493 } 494 495 func (s *baseDeclSuite) TestAutoConnectionBlockDevicesOverride(c *C) { 496 cand := s.connectCand(c, "block-devices", "", "") 497 err := cand.CheckAutoConnect() 498 c.Check(err, NotNil) 499 c.Assert(err, ErrorMatches, "auto-connection denied by plug rule of interface \"block-devices\"") 500 501 plugsSlots := ` 502 plugs: 503 block-devices: 504 allow-auto-connection: true 505 ` 506 507 snapDecl := s.mockSnapDecl(c, "some-snap", "J60k4JY0HppjwOjW8dZdYc8obXKxujRu", "canonical", plugsSlots) 508 cand.PlugSnapDeclaration = snapDecl 509 err = cand.CheckAutoConnect() 510 c.Check(err, IsNil) 511 } 512 513 func (s *baseDeclSuite) TestAutoConnectionPackagekitControlOverride(c *C) { 514 cand := s.connectCand(c, "packagekit-control", "", "") 515 err := cand.CheckAutoConnect() 516 c.Check(err, NotNil) 517 c.Assert(err, ErrorMatches, "auto-connection denied by plug rule of interface \"packagekit-control\"") 518 519 plugsSlots := ` 520 plugs: 521 packagekit-control: 522 allow-auto-connection: true 523 ` 524 525 snapDecl := s.mockSnapDecl(c, "some-snap", "J60k4JY0HppjwOjW8dZdYc8obXKxujRu", "canonical", plugsSlots) 526 cand.PlugSnapDeclaration = snapDecl 527 err = cand.CheckAutoConnect() 528 c.Check(err, IsNil) 529 } 530 531 func (s *baseDeclSuite) TestAutoConnectionOverrideMultiple(c *C) { 532 plugsSlots := ` 533 plugs: 534 network-bind: 535 allow-auto-connection: true 536 network-control: 537 allow-auto-connection: true 538 kernel-module-control: 539 allow-auto-connection: true 540 system-observe: 541 allow-auto-connection: true 542 hardware-observe: 543 allow-auto-connection: true 544 ` 545 546 snapDecl := s.mockSnapDecl(c, "some-snap", "J60k4JY0HppjwOjW8dZdYc8obXKxujRu", "canonical", plugsSlots) 547 548 all := builtin.Interfaces() 549 // these are a mixture interfaces that the snap plugs 550 plugged := map[string]bool{ 551 "network-bind": true, 552 "network-control": true, 553 "kernel-module-control": true, 554 "system-observe": true, 555 "hardware-observe": true, 556 } 557 for _, iface := range all { 558 if !plugged[iface.Name()] { 559 continue 560 } 561 562 cand := s.connectCand(c, iface.Name(), "", "") 563 cand.PlugSnapDeclaration = snapDecl 564 err := cand.CheckAutoConnect() 565 c.Check(err, IsNil) 566 } 567 } 568 569 // describe installation rules for slots succinctly for cross-checking, 570 // if an interface is not mentioned here a slot of its type can only 571 // be installed by a core snap (and this was taken care by 572 // BeforePrepareSlot), 573 // otherwise the entry for the interface is the list of snap types it 574 // can be installed by (using the declaration naming); 575 // ATM a nil entry means even stricter rules that would need be tested 576 // separately and whose implementation is in flux for now 577 var ( 578 unconstrained = []string{"core", "kernel", "gadget", "app"} 579 580 slotInstallation = map[string][]string{ 581 // other 582 "adb-support": {"core"}, 583 "audio-playback": {"app", "core"}, 584 "audio-record": {"app", "core"}, 585 "autopilot-introspection": {"core"}, 586 "avahi-control": {"app", "core"}, 587 "avahi-observe": {"app", "core"}, 588 "bluez": {"app", "core"}, 589 "bool-file": {"core", "gadget"}, 590 "browser-support": {"core"}, 591 "content": {"app", "gadget"}, 592 "core-support": {"core"}, 593 "dbus": {"app"}, 594 "docker-support": {"core"}, 595 "fwupd": {"app"}, 596 "gpio": {"core", "gadget"}, 597 "gpio-control": {"core"}, 598 "greengrass-support": {"core"}, 599 "hidraw": {"core", "gadget"}, 600 "i2c": {"core", "gadget"}, 601 "iio": {"core", "gadget"}, 602 "kubernetes-support": {"core"}, 603 "location-control": {"app"}, 604 "location-observe": {"app"}, 605 "lxd-support": {"core"}, 606 "maliit": {"app"}, 607 "media-hub": {"app", "core"}, 608 "mir": {"app"}, 609 "modem-manager": {"app", "core"}, 610 "mpris": {"app"}, 611 "network-manager": {"app", "core"}, 612 "network-manager-observe": {"app", "core"}, 613 "network-status": {"app"}, 614 "ofono": {"app", "core"}, 615 "online-accounts-service": {"app"}, 616 "ppp": {"core"}, 617 "pulseaudio": {"app", "core"}, 618 "serial-port": {"core", "gadget"}, 619 "spi": {"core", "gadget"}, 620 "storage-framework-service": {"app"}, 621 "dummy": {"app"}, 622 "thumbnailer-service": {"app"}, 623 "ubuntu-download-manager": {"app"}, 624 "udisks2": {"app", "core"}, 625 "uhid": {"core"}, 626 "unity8": {"app"}, 627 "unity8-calendar": {"app"}, 628 "unity8-contacts": {"app"}, 629 "upower-observe": {"app", "core"}, 630 "wayland": {"app", "core"}, 631 "x11": {"app", "core"}, 632 // snowflakes 633 "classic-support": nil, 634 "docker": nil, 635 "lxd": nil, 636 } 637 638 restrictedPlugInstallation = map[string][]string{ 639 "core-support": {"core"}, 640 } 641 642 snapTypeMap = map[string]snap.Type{ 643 "core": snap.TypeOS, 644 "app": snap.TypeApp, 645 "kernel": snap.TypeKernel, 646 "gadget": snap.TypeGadget, 647 } 648 ) 649 650 func (s *baseDeclSuite) TestSlotInstallation(c *C) { 651 all := builtin.Interfaces() 652 653 for _, iface := range all { 654 types, ok := slotInstallation[iface.Name()] 655 if !ok { // common ones, only core can install them, 656 types = []string{"core"} 657 } 658 659 if types == nil { 660 // snowflake needs to be tested specially 661 continue 662 } 663 for name, snapType := range snapTypeMap { 664 ok := strutil.ListContains(types, name) 665 ic := s.installSlotCand(c, iface.Name(), snapType, ``) 666 err := ic.Check() 667 comm := Commentf("%s by %s snap", iface.Name(), name) 668 if ok { 669 c.Check(err, IsNil, comm) 670 } else { 671 c.Check(err, NotNil, comm) 672 } 673 } 674 } 675 676 // test docker specially 677 ic := s.installSlotCand(c, "docker", snap.TypeApp, ``) 678 err := ic.Check() 679 c.Assert(err, Not(IsNil)) 680 c.Assert(err, ErrorMatches, "installation not allowed by \"docker\" slot rule of interface \"docker\"") 681 682 // test lxd specially 683 ic = s.installSlotCand(c, "lxd", snap.TypeApp, ``) 684 err = ic.Check() 685 c.Assert(err, Not(IsNil)) 686 c.Assert(err, ErrorMatches, "installation not allowed by \"lxd\" slot rule of interface \"lxd\"") 687 } 688 689 func (s *baseDeclSuite) TestPlugInstallation(c *C) { 690 all := builtin.Interfaces() 691 692 restricted := map[string]bool{ 693 "block-devices": true, 694 "classic-support": true, 695 "docker-support": true, 696 "greengrass-support": true, 697 "gpio-control": true, 698 "kernel-module-control": true, 699 "kubernetes-support": true, 700 "lxd-support": true, 701 "multipass-support": true, 702 "packagekit-control": true, 703 "personal-files": true, 704 "snapd-control": true, 705 "system-files": true, 706 "unity8": true, 707 } 708 709 for _, iface := range all { 710 types, ok := restrictedPlugInstallation[iface.Name()] 711 // If plug installation is restricted to specific snap types we 712 // need to make sure this is really the case here. If that is not 713 // the case we continue as normal. 714 if ok { 715 for name, snapType := range snapTypeMap { 716 ok := strutil.ListContains(types, name) 717 ic := s.installPlugCand(c, iface.Name(), snapType, ``) 718 err := ic.Check() 719 comm := Commentf("%s by %s snap", iface.Name(), name) 720 if ok { 721 c.Check(err, IsNil, comm) 722 } else { 723 c.Check(err, NotNil, comm) 724 } 725 } 726 } else { 727 ic := s.installPlugCand(c, iface.Name(), snap.TypeApp, ``) 728 err := ic.Check() 729 comm := Commentf("%s", iface.Name()) 730 if restricted[iface.Name()] { 731 c.Check(err, NotNil, comm) 732 } else { 733 c.Check(err, IsNil, comm) 734 } 735 } 736 } 737 } 738 739 func (s *baseDeclSuite) TestConnection(c *C) { 740 all := builtin.Interfaces() 741 742 // connecting with these interfaces needs to be allowed on 743 // case-by-case basis 744 noconnect := map[string]bool{ 745 "content": true, 746 "docker": true, 747 "fwupd": true, 748 "location-control": true, 749 "location-observe": true, 750 "lxd": true, 751 "maliit": true, 752 "mir": true, 753 "network-status": true, 754 "online-accounts-service": true, 755 "storage-framework-service": true, 756 "thumbnailer-service": true, 757 "ubuntu-download-manager": true, 758 "unity8-calendar": true, 759 "unity8-contacts": true, 760 } 761 762 for _, iface := range all { 763 expected := !noconnect[iface.Name()] 764 comm := Commentf(iface.Name()) 765 766 // check base declaration 767 cand := s.connectCand(c, iface.Name(), "", "") 768 err := cand.Check() 769 770 if expected { 771 c.Check(err, IsNil, comm) 772 } else { 773 c.Check(err, NotNil, comm) 774 } 775 } 776 } 777 778 func (s *baseDeclSuite) TestConnectionOnClassic(c *C) { 779 restore := release.MockOnClassic(false) 780 defer restore() 781 782 all := builtin.Interfaces() 783 784 // connecting with these interfaces needs to be allowed on 785 // case-by-case basis when not on classic 786 noconnect := map[string]bool{ 787 "audio-record": true, 788 "modem-manager": true, 789 "network-manager": true, 790 "ofono": true, 791 "pulseaudio": true, 792 "upower-observe": true, 793 } 794 795 for _, onClassic := range []bool{true, false} { 796 release.OnClassic = onClassic 797 for _, iface := range all { 798 if !noconnect[iface.Name()] { 799 continue 800 } 801 expected := onClassic 802 comm := Commentf(iface.Name()) 803 804 // check base declaration 805 cand := s.connectCand(c, iface.Name(), "", "") 806 err := cand.Check() 807 808 if expected { 809 c.Check(err, IsNil, comm) 810 } else { 811 c.Check(err, NotNil, comm) 812 } 813 } 814 } 815 } 816 817 func (s *baseDeclSuite) TestSanity(c *C) { 818 all := builtin.Interfaces() 819 820 // these interfaces have rules both for the slots and plugs side 821 // given how the rules work this can be delicate, 822 // listed here to make sure that was a conscious decision 823 bothSides := map[string]bool{ 824 "block-devices": true, 825 "audio-playback": true, 826 "classic-support": true, 827 "core-support": true, 828 "docker-support": true, 829 "greengrass-support": true, 830 "gpio-control": true, 831 "kernel-module-control": true, 832 "kubernetes-support": true, 833 "lxd-support": true, 834 "multipass-support": true, 835 "packagekit-control": true, 836 "personal-files": true, 837 "snapd-control": true, 838 "system-files": true, 839 "udisks2": true, 840 "unity8": true, 841 "wayland": true, 842 } 843 844 for _, iface := range all { 845 plugRule := s.baseDecl.PlugRule(iface.Name()) 846 slotRule := s.baseDecl.SlotRule(iface.Name()) 847 if plugRule == nil && slotRule == nil { 848 c.Logf("%s is not considered in the base declaration", iface.Name()) 849 c.Fail() 850 } 851 if plugRule != nil && slotRule != nil { 852 if !bothSides[iface.Name()] { 853 c.Logf("%s have both a base declaration slot rule and plug rule, make sure that's intended and correct", iface.Name()) 854 c.Fail() 855 } 856 } 857 } 858 } 859 860 func (s *baseDeclSuite) TestConnectionContent(c *C) { 861 // we let connect explicitly as long as content matches (or is absent on both sides) 862 863 // random (Sanitize* will now also block this) 864 cand := s.connectCand(c, "content", "", "") 865 err := cand.Check() 866 c.Check(err, NotNil) 867 868 slotDecl1 := s.mockSnapDecl(c, "slot-snap", "slot-snap-id", "pub1", "") 869 plugDecl1 := s.mockSnapDecl(c, "plug-snap", "plug-snap-id", "pub1", "") 870 plugDecl2 := s.mockSnapDecl(c, "plug-snap", "plug-snap-id", "pub2", "") 871 872 // same publisher, same content 873 cand = s.connectCand(c, "stuff", `name: slot-snap 874 version: 0 875 slots: 876 stuff: 877 interface: content 878 content: mk1 879 `, ` 880 name: plug-snap 881 version: 0 882 plugs: 883 stuff: 884 interface: content 885 content: mk1 886 `) 887 cand.SlotSnapDeclaration = slotDecl1 888 cand.PlugSnapDeclaration = plugDecl1 889 err = cand.Check() 890 c.Check(err, IsNil) 891 892 // different publisher, same content 893 cand.SlotSnapDeclaration = slotDecl1 894 cand.PlugSnapDeclaration = plugDecl2 895 err = cand.Check() 896 c.Check(err, IsNil) 897 898 // same publisher, different content 899 cand = s.connectCand(c, "stuff", ` 900 name: slot-snap 901 version: 0 902 slots: 903 stuff: 904 interface: content 905 content: mk1 906 `, ` 907 name: plug-snap 908 version: 0 909 plugs: 910 stuff: 911 interface: content 912 content: mk2 913 `) 914 cand.SlotSnapDeclaration = slotDecl1 915 cand.PlugSnapDeclaration = plugDecl1 916 err = cand.Check() 917 c.Check(err, NotNil) 918 } 919 920 func (s *baseDeclSuite) TestComposeBaseDeclaration(c *C) { 921 decl, err := policy.ComposeBaseDeclaration(nil) 922 c.Assert(err, IsNil) 923 c.Assert(string(decl), testutil.Contains, ` 924 type: base-declaration 925 authority-id: canonical 926 series: 16 927 revision: 0 928 `) 929 } 930 931 func (s *baseDeclSuite) TestDoesNotPanic(c *C) { 932 // In case there are any issues in the actual interfaces we'd get a panic 933 // on snapd startup. This test prevents this from happing unnoticed. 934 _, err := policy.ComposeBaseDeclaration(builtin.Interfaces()) 935 c.Assert(err, IsNil) 936 } 937 938 func (s *baseDeclSuite) TestBrowserSupportAllowSandbox(c *C) { 939 const plugYaml = `name: plug-snap 940 version: 0 941 plugs: 942 browser-support: 943 allow-sandbox: true 944 ` 945 cand := s.connectCand(c, "browser-support", "", plugYaml) 946 err := cand.Check() 947 c.Check(err, NotNil) 948 949 err = cand.CheckAutoConnect() 950 c.Check(err, NotNil) 951 } 952 953 func (s *baseDeclSuite) TestOpticalDriveWrite(c *C) { 954 type options struct { 955 readonlyYamls []string 956 writableYamls []string 957 } 958 959 opts := &options{ 960 readonlyYamls: []string{ 961 // Non-specified "write" attribute 962 `name: plug-snap 963 version: 0 964 plugs: 965 optical-drive: null 966 `, 967 // Undefined "write" attribute 968 `name: plug-snap 969 version: 0 970 plugs: 971 optical-drive: {} 972 `, 973 // False "write" attribute 974 `name: plug-snap 975 version: 0 976 plugs: 977 optical-drive: 978 write: false 979 `, 980 }, 981 writableYamls: []string{ 982 // True "write" attribute 983 `name: plug-snap 984 version: 0 985 plugs: 986 optical-drive: 987 write: true 988 `, 989 }, 990 } 991 992 checkOpticalDriveAutoConnect := func(plugYaml string, checker Checker) { 993 cand := s.connectCand(c, "optical-drive", "", plugYaml) 994 err := cand.Check() 995 c.Check(err, checker) 996 err = cand.CheckAutoConnect() 997 c.Check(err, checker) 998 } 999 1000 for _, plugYaml := range opts.readonlyYamls { 1001 checkOpticalDriveAutoConnect(plugYaml, IsNil) 1002 } 1003 for _, plugYaml := range opts.writableYamls { 1004 checkOpticalDriveAutoConnect(plugYaml, NotNil) 1005 } 1006 }