github.com/rigado/snapd@v2.42.5-go-mod+incompatible/interfaces/builtin/desktop_test.go (about)

     1  // -*- Mode: Go; indent-tabs-mode: t -*-
     2  
     3  /*
     4   * Copyright (C) 2017 Canonical Ltd
     5   *
     6   * This program is free software: you can redistribute it and/or modify
     7   * it under the terms of the GNU General Public License version 3 as
     8   * published by the Free Software Foundation.
     9   *
    10   * This program is distributed in the hope that it will be useful,
    11   * but WITHOUT ANY WARRANTY; without even the implied warranty of
    12   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    13   * GNU General Public License for more details.
    14   *
    15   * You should have received a copy of the GNU General Public License
    16   * along with this program.  If not, see <http://www.gnu.org/licenses/>.
    17   *
    18   */
    19  
    20  package builtin_test
    21  
    22  import (
    23  	"os"
    24  	"path/filepath"
    25  	"strings"
    26  
    27  	. "gopkg.in/check.v1"
    28  
    29  	"github.com/snapcore/snapd/dirs"
    30  	"github.com/snapcore/snapd/interfaces"
    31  	"github.com/snapcore/snapd/interfaces/apparmor"
    32  	"github.com/snapcore/snapd/interfaces/builtin"
    33  	"github.com/snapcore/snapd/interfaces/mount"
    34  	"github.com/snapcore/snapd/release"
    35  	"github.com/snapcore/snapd/snap"
    36  	"github.com/snapcore/snapd/testutil"
    37  )
    38  
    39  type DesktopInterfaceSuite struct {
    40  	iface        interfaces.Interface
    41  	coreSlotInfo *snap.SlotInfo
    42  	coreSlot     *interfaces.ConnectedSlot
    43  	plugInfo     *snap.PlugInfo
    44  	plug         *interfaces.ConnectedPlug
    45  }
    46  
    47  var _ = Suite(&DesktopInterfaceSuite{
    48  	iface: builtin.MustInterface("desktop"),
    49  })
    50  
    51  const desktopConsumerYaml = `name: consumer
    52  version: 0
    53  apps:
    54   app:
    55    plugs: [desktop]
    56  `
    57  
    58  const desktopCoreYaml = `name: core
    59  version: 0
    60  type: os
    61  slots:
    62    desktop:
    63  `
    64  
    65  func (s *DesktopInterfaceSuite) SetUpTest(c *C) {
    66  	s.plug, s.plugInfo = MockConnectedPlug(c, desktopConsumerYaml, nil, "desktop")
    67  	s.coreSlot, s.coreSlotInfo = MockConnectedSlot(c, desktopCoreYaml, nil, "desktop")
    68  }
    69  
    70  func (s *DesktopInterfaceSuite) TearDownTest(c *C) {
    71  	dirs.SetRootDir("/")
    72  }
    73  
    74  func (s *DesktopInterfaceSuite) TestName(c *C) {
    75  	c.Assert(s.iface.Name(), Equals, "desktop")
    76  }
    77  
    78  func (s *DesktopInterfaceSuite) TestSanitizeSlot(c *C) {
    79  	c.Assert(interfaces.BeforePrepareSlot(s.iface, s.coreSlotInfo), IsNil)
    80  }
    81  
    82  func (s *DesktopInterfaceSuite) TestSanitizePlug(c *C) {
    83  	c.Assert(interfaces.BeforePreparePlug(s.iface, s.plugInfo), IsNil)
    84  }
    85  
    86  func (s *DesktopInterfaceSuite) TestAppArmorSpec(c *C) {
    87  	tmpdir := c.MkDir()
    88  	dirs.SetRootDir(tmpdir)
    89  	c.Assert(os.MkdirAll(filepath.Join(tmpdir, "/usr/share/fonts"), 0777), IsNil)
    90  	c.Assert(os.MkdirAll(filepath.Join(tmpdir, "/usr/local/share/fonts"), 0777), IsNil)
    91  	c.Assert(os.MkdirAll(filepath.Join(tmpdir, "/var/cache/fontconfig"), 0777), IsNil)
    92  	restore := release.MockOnClassic(false)
    93  	defer restore()
    94  
    95  	// connected plug to core slot
    96  	spec := &apparmor.Specification{}
    97  	c.Assert(spec.AddConnectedPlug(s.iface, s.plug, s.coreSlot), IsNil)
    98  	c.Assert(spec.SecurityTags(), DeepEquals, []string{"snap.consumer.app"})
    99  	c.Assert(spec.SnippetForTag("snap.consumer.app"), testutil.Contains, "# Description: Can access basic graphical desktop resources")
   100  	c.Assert(spec.SnippetForTag("snap.consumer.app"), testutil.Contains, "#include <abstractions/fonts>")
   101  	c.Assert(spec.SnippetForTag("snap.consumer.app"), testutil.Contains, "/etc/gtk-3.0/settings.ini r,")
   102  	c.Assert(spec.SnippetForTag("snap.consumer.app"), testutil.Contains, "# Allow access to xdg-desktop-portal and xdg-document-portal")
   103  
   104  	// On an all-snaps system, the only UpdateNS rule is for the
   105  	// document portal.
   106  	updateNS := spec.UpdateNS()
   107  	profile0 := `  # Mount the document portal
   108    mount options=(bind) /run/user/[0-9]*/doc/by-app/snap.consumer/ -> /run/user/[0-9]*/doc/,
   109    umount /run/user/[0-9]*/doc/,
   110  
   111  `
   112  	c.Assert(strings.Join(updateNS, ""), Equals, profile0)
   113  
   114  	// On a classic system, there are UpdateNS rules for the host
   115  	// system font mounts
   116  	restore = release.MockOnClassic(true)
   117  	defer restore()
   118  	spec = &apparmor.Specification{}
   119  	c.Assert(spec.AddConnectedPlug(s.iface, s.plug, s.coreSlot), IsNil)
   120  	updateNS = spec.UpdateNS()
   121  	c.Check(updateNS, testutil.Contains, "  # Mount the document portal\n")
   122  	c.Check(updateNS, testutil.Contains, "  # Read-only access to /usr/share/fonts\n")
   123  	c.Check(updateNS, testutil.Contains, "  # Read-only access to /usr/local/share/fonts\n")
   124  	c.Check(updateNS, testutil.Contains, "  # Read-only access to /var/cache/fontconfig\n")
   125  
   126  	// connected plug to core slot
   127  	spec = &apparmor.Specification{}
   128  	c.Assert(spec.AddConnectedSlot(s.iface, s.plug, s.coreSlot), IsNil)
   129  	c.Assert(spec.SecurityTags(), HasLen, 0)
   130  }
   131  
   132  func (s *DesktopInterfaceSuite) TestMountSpec(c *C) {
   133  	tmpdir := c.MkDir()
   134  	dirs.SetRootDir(tmpdir)
   135  	c.Assert(os.MkdirAll(filepath.Join(tmpdir, "/usr/share/fonts"), 0777), IsNil)
   136  	c.Assert(os.MkdirAll(filepath.Join(tmpdir, "/usr/local/share/fonts"), 0777), IsNil)
   137  	c.Assert(os.MkdirAll(filepath.Join(tmpdir, "/var/cache/fontconfig"), 0777), IsNil)
   138  
   139  	restore := release.MockOnClassic(false)
   140  	defer restore()
   141  
   142  	// On all-snaps systems, the font related mount entries are missing
   143  	spec := &mount.Specification{}
   144  	c.Assert(spec.AddConnectedPlug(s.iface, s.plug, s.coreSlot), IsNil)
   145  	c.Check(spec.MountEntries(), HasLen, 0)
   146  
   147  	entries := spec.UserMountEntries()
   148  	c.Check(entries, HasLen, 1)
   149  	c.Check(entries[0].Name, Equals, "$XDG_RUNTIME_DIR/doc/by-app/snap.consumer")
   150  	c.Check(entries[0].Dir, Equals, "$XDG_RUNTIME_DIR/doc")
   151  	c.Check(entries[0].Options, DeepEquals, []string{"bind", "rw", "x-snapd.ignore-missing"})
   152  
   153  	// On classic systems, a number of font related directories
   154  	// are bind mounted from the host system if they exist.
   155  	restore = release.MockOnClassic(true)
   156  	defer restore()
   157  	spec = &mount.Specification{}
   158  	c.Assert(spec.AddConnectedPlug(s.iface, s.plug, s.coreSlot), IsNil)
   159  
   160  	entries = spec.MountEntries()
   161  	c.Assert(entries, HasLen, 3)
   162  
   163  	const hostfs = "/var/lib/snapd/hostfs"
   164  	c.Check(entries[0].Name, Equals, hostfs+dirs.SystemFontsDir)
   165  	c.Check(entries[0].Dir, Equals, "/usr/share/fonts")
   166  	c.Check(entries[0].Options, DeepEquals, []string{"bind", "ro"})
   167  
   168  	c.Check(entries[1].Name, Equals, hostfs+dirs.SystemLocalFontsDir)
   169  	c.Check(entries[1].Dir, Equals, "/usr/local/share/fonts")
   170  	c.Check(entries[1].Options, DeepEquals, []string{"bind", "ro"})
   171  
   172  	c.Check(entries[2].Name, Equals, hostfs+dirs.SystemFontconfigCacheDirs[0])
   173  	c.Check(entries[2].Dir, Equals, "/var/cache/fontconfig")
   174  	c.Check(entries[2].Options, DeepEquals, []string{"bind", "ro"})
   175  
   176  	entries = spec.UserMountEntries()
   177  	c.Assert(entries, HasLen, 1)
   178  	c.Check(entries[0].Dir, Equals, "$XDG_RUNTIME_DIR/doc")
   179  
   180  	// Fedora is a little special with their fontconfig cache location(s)
   181  	restore = release.MockReleaseInfo(&release.OS{ID: "fedora"})
   182  	defer restore()
   183  
   184  	tmpdir = c.MkDir()
   185  	dirs.SetRootDir(tmpdir)
   186  	c.Assert(dirs.SystemFontconfigCacheDirs, DeepEquals, []string{filepath.Join(tmpdir, "/var/cache/fontconfig"), filepath.Join(tmpdir, "/usr/lib/fontconfig/cache")})
   187  	c.Assert(os.MkdirAll(filepath.Join(tmpdir, "/usr/share/fonts"), 0777), IsNil)
   188  	c.Assert(os.MkdirAll(filepath.Join(tmpdir, "/usr/local/share/fonts"), 0777), IsNil)
   189  	c.Assert(os.MkdirAll(filepath.Join(tmpdir, "/usr/lib/fontconfig/cache"), 0777), IsNil)
   190  	c.Assert(os.MkdirAll(filepath.Join(tmpdir, "/var/cache/fontconfig"), 0777), IsNil)
   191  	spec = &mount.Specification{}
   192  	c.Assert(spec.AddConnectedPlug(s.iface, s.plug, s.coreSlot), IsNil)
   193  	entries = spec.MountEntries()
   194  	c.Assert(entries, HasLen, 4)
   195  
   196  	c.Check(entries[2].Name, Equals, hostfs+dirs.SystemFontconfigCacheDirs[0])
   197  	c.Check(entries[2].Dir, Equals, "/var/cache/fontconfig")
   198  	c.Check(entries[2].Options, DeepEquals, []string{"bind", "ro"})
   199  
   200  	c.Check(entries[3].Name, Equals, hostfs+dirs.SystemFontconfigCacheDirs[1])
   201  	c.Check(entries[3].Dir, Equals, "/usr/lib/fontconfig/cache")
   202  	c.Check(entries[3].Options, DeepEquals, []string{"bind", "ro"})
   203  }
   204  
   205  func (s *DesktopInterfaceSuite) TestStaticInfo(c *C) {
   206  	si := interfaces.StaticInfoOf(s.iface)
   207  	c.Assert(si.ImplicitOnCore, Equals, false)
   208  	c.Assert(si.ImplicitOnClassic, Equals, true)
   209  	c.Assert(si.Summary, Equals, `allows access to basic graphical desktop resources`)
   210  	c.Assert(si.BaseDeclarationSlots, testutil.Contains, "desktop")
   211  }
   212  
   213  func (s *DesktopInterfaceSuite) TestInterfaces(c *C) {
   214  	c.Check(builtin.Interfaces(), testutil.DeepContains, s.iface)
   215  }