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  }