diff options
| author | Michael Vogt <mvo@ubuntu.com> | 2019-06-07 13:21:10 +0200 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2019-06-07 13:21:10 +0200 |
| commit | 47a007b51438e63f9aec020de90d6c9d07c54d38 (patch) | |
| tree | 15143136f333872d22481b4fbfe482f600343244 | |
| parent | 9d4e5903d00c24fb7a08aa7c0081e4b88af7fbfe (diff) | |
| parent | 2159125f423998f1f440c07214f3174fabb637a2 (diff) | |
Merge pull request #6325 from kubiko/avahi-on-classic
Allowing avahi-observer/control slots from app snap also on classic
| -rw-r--r-- | interfaces/builtin/avahi_control.go | 23 | ||||
| -rw-r--r-- | interfaces/builtin/avahi_control_test.go | 99 | ||||
| -rw-r--r-- | interfaces/builtin/avahi_observe.go | 23 | ||||
| -rw-r--r-- | interfaces/builtin/avahi_observe_test.go | 99 | ||||
| -rw-r--r-- | interfaces/builtin/export_test.go | 2 | ||||
| -rw-r--r-- | interfaces/builtin/utils.go | 24 | ||||
| -rw-r--r-- | interfaces/builtin/utils_test.go | 38 |
7 files changed, 224 insertions, 84 deletions
diff --git a/interfaces/builtin/avahi_control.go b/interfaces/builtin/avahi_control.go index d20f85acfb..3b91439ce8 100644 --- a/interfaces/builtin/avahi_control.go +++ b/interfaces/builtin/avahi_control.go @@ -25,7 +25,6 @@ import ( "github.com/snapcore/snapd/interfaces" "github.com/snapcore/snapd/interfaces/apparmor" "github.com/snapcore/snapd/interfaces/dbus" - "github.com/snapcore/snapd/release" "github.com/snapcore/snapd/snap" ) @@ -119,10 +118,12 @@ func (iface *avahiControlInterface) StaticInfo() interfaces.StaticInfo { func (iface *avahiControlInterface) AppArmorConnectedPlug(spec *apparmor.Specification, plug *interfaces.ConnectedPlug, slot *interfaces.ConnectedSlot) error { old := "###SLOT_SECURITY_TAGS###" var new string - if release.OnClassic { - // If we're running on classic Avahi will be part - // of the OS snap and will run unconfined. - new = "unconfined" + // If we're running on classic, Avahi may be installed either as a snap of + // as part of the OS. If it is part of the OS, it will not have a security + // label like it would when installed as a snap. + if implicitSystemConnectedSlot(slot) { + // avahi from the OS is typically unconfined but known to sometimes be confined + new = "\"{unconfined,/usr/sbin/avahi-daemon}\"" } else { new = slotAppLabelExpr(slot) } @@ -135,7 +136,9 @@ func (iface *avahiControlInterface) AppArmorConnectedPlug(spec *apparmor.Specifi } func (iface *avahiControlInterface) AppArmorPermanentSlot(spec *apparmor.Specification, slot *snap.SlotInfo) error { - if !release.OnClassic { + // Only apply slot snippet when running as application snap + // on classic, slot side can be system or application + if !implicitSystemPermanentSlot(slot) { // NOTE: this is using avahi-observe permanent slot as it contains // base declarations for running as the avahi service. spec.AddSnippet(avahiObservePermanentSlotAppArmor) @@ -144,7 +147,9 @@ func (iface *avahiControlInterface) AppArmorPermanentSlot(spec *apparmor.Specifi } func (iface *avahiControlInterface) AppArmorConnectedSlot(spec *apparmor.Specification, plug *interfaces.ConnectedPlug, slot *interfaces.ConnectedSlot) error { - if !release.OnClassic { + // Only apply slot snippet when running as application snap + // on classic, slot side can be system or application + if !implicitSystemConnectedSlot(slot) { old := "###PLUG_SECURITY_TAGS###" new := plugAppLabelExpr(plug) // avahi-control implies avahi-observe, so add snippets for both here @@ -157,7 +162,9 @@ func (iface *avahiControlInterface) AppArmorConnectedSlot(spec *apparmor.Specifi } func (iface *avahiControlInterface) DBusPermanentSlot(spec *dbus.Specification, slot *snap.SlotInfo) error { - if !release.OnClassic { + // Only apply slot snippet when running as application snap + // on classic, slot side can be system or application + if !implicitSystemPermanentSlot(slot) { // NOTE: this is using avahi-observe permanent slot as it contains // base declarations for running as the avahi service. spec.AddSnippet(avahiObservePermanentSlotDBus) diff --git a/interfaces/builtin/avahi_control_test.go b/interfaces/builtin/avahi_control_test.go index 71f4c3cec0..ae42bd62b7 100644 --- a/interfaces/builtin/avahi_control_test.go +++ b/interfaces/builtin/avahi_control_test.go @@ -32,13 +32,15 @@ import ( ) type AvahiControlInterfaceSuite struct { - iface interfaces.Interface - plug *interfaces.ConnectedPlug - plugInfo *snap.PlugInfo - appSlot *interfaces.ConnectedSlot - appSlotInfo *snap.SlotInfo - coreSlot *interfaces.ConnectedSlot - coreSlotInfo *snap.SlotInfo + iface interfaces.Interface + plug *interfaces.ConnectedPlug + plugInfo *snap.PlugInfo + appSlot *interfaces.ConnectedSlot + appSlotInfo *snap.SlotInfo + coreSlot *interfaces.ConnectedSlot + coreSlotInfo *snap.SlotInfo + snapdSlot *interfaces.ConnectedSlot + snapdSlotInfo *snap.SlotInfo } var _ = Suite(&AvahiControlInterfaceSuite{ @@ -61,6 +63,14 @@ apps: const avahiControlCoreYaml = `name: core version: 0 +type: os +slots: + avahi-control: +` + +const avahiControlSnapdYaml = `name: snapd +version: 0 +type: snapd slots: avahi-control: ` @@ -69,6 +79,7 @@ func (s *AvahiControlInterfaceSuite) SetUpTest(c *C) { s.plug, s.plugInfo = MockConnectedPlug(c, avahiControlConsumerYaml, nil, "avahi-control") s.appSlot, s.appSlotInfo = MockConnectedSlot(c, avahiControlProducerYaml, nil, "avahi-control") s.coreSlot, s.coreSlotInfo = MockConnectedSlot(c, avahiControlCoreYaml, nil, "avahi-control") + s.snapdSlot, s.snapdSlotInfo = MockConnectedSlot(c, avahiControlSnapdYaml, nil, "avahi-control") } func (s *AvahiControlInterfaceSuite) TestName(c *C) { @@ -91,14 +102,11 @@ func (s *AvahiControlInterfaceSuite) TestSanitizePlug(c *C) { c.Assert(interfaces.BeforePreparePlug(s.iface, s.plugInfo), IsNil) } -func (s *AvahiControlInterfaceSuite) TestAppArmorSpec(c *C) { - // on a core system with avahi slot coming from a regular app snap. - restore := release.MockOnClassic(false) - defer restore() - +func (s *AvahiControlInterfaceSuite) testAppArmorSpecWithProducer(c *C, + slot *interfaces.ConnectedSlot, slotInfo *snap.SlotInfo) { // connected plug to app slot spec := &apparmor.Specification{} - c.Assert(spec.AddConnectedPlug(s.iface, s.plug, s.appSlot), IsNil) + c.Assert(spec.AddConnectedPlug(s.iface, s.plug, slot), IsNil) c.Assert(spec.SecurityTags(), DeepEquals, []string{"snap.consumer.app"}) c.Assert(spec.SnippetForTag("snap.consumer.app"), testutil.Contains, "name=org.freedesktop.Avahi") c.Assert(spec.SnippetForTag("snap.consumer.app"), testutil.Contains, `peer=(label="snap.producer.app"),`) @@ -114,7 +122,7 @@ func (s *AvahiControlInterfaceSuite) TestAppArmorSpec(c *C) { // connected app slot to plug spec = &apparmor.Specification{} - c.Assert(spec.AddConnectedSlot(s.iface, s.plug, s.appSlot), IsNil) + c.Assert(spec.AddConnectedSlot(s.iface, s.plug, slot), IsNil) c.Assert(spec.SecurityTags(), DeepEquals, []string{"snap.producer.app"}) c.Assert(spec.SnippetForTag("snap.producer.app"), testutil.Contains, `interface=org.freedesktop.Avahi`) c.Assert(spec.SnippetForTag("snap.producer.app"), testutil.Contains, `peer=(label="snap.consumer.app"),`) @@ -128,22 +136,21 @@ func (s *AvahiControlInterfaceSuite) TestAppArmorSpec(c *C) { // permanent app slot spec = &apparmor.Specification{} - c.Assert(spec.AddPermanentSlot(s.iface, s.appSlotInfo), IsNil) + c.Assert(spec.AddPermanentSlot(s.iface, slotInfo), IsNil) c.Assert(spec.SecurityTags(), DeepEquals, []string{"snap.producer.app"}) c.Assert(spec.SnippetForTag("snap.producer.app"), testutil.Contains, `dbus (bind) bus=system name="org.freedesktop.Avahi",`) +} - // on a classic system with avahi slot coming from the core snap. - restore = release.MockOnClassic(true) - defer restore() - +func (s *AvahiControlInterfaceSuite) testAppArmorSpecFromSystem(c *C, + slot *interfaces.ConnectedSlot, slotInfo *snap.SlotInfo) { // connected plug to core slot - spec = &apparmor.Specification{} - c.Assert(spec.AddConnectedPlug(s.iface, s.plug, s.coreSlot), IsNil) + spec := &apparmor.Specification{} + c.Assert(spec.AddConnectedPlug(s.iface, s.plug, slot), IsNil) c.Assert(spec.SecurityTags(), DeepEquals, []string{"snap.consumer.app"}) c.Assert(spec.SnippetForTag("snap.consumer.app"), testutil.Contains, "name=org.freedesktop.Avahi") - c.Assert(spec.SnippetForTag("snap.consumer.app"), testutil.Contains, "peer=(label=unconfined),") + c.Assert(spec.SnippetForTag("snap.consumer.app"), testutil.Contains, "peer=(label=\"{unconfined,/usr/sbin/avahi-daemon}\"),") // make sure control includes also observe capabilities c.Assert(spec.SnippetForTag("snap.consumer.app"), testutil.Contains, `interface=org.freedesktop.Avahi.AddressResolver`) c.Assert(spec.SnippetForTag("snap.consumer.app"), testutil.Contains, `interface=org.freedesktop.Avahi.HostNameResolver`) @@ -155,34 +162,68 @@ func (s *AvahiControlInterfaceSuite) TestAppArmorSpec(c *C) { // connected core slot to plug spec = &apparmor.Specification{} - c.Assert(spec.AddConnectedSlot(s.iface, s.plug, s.coreSlot), IsNil) + c.Assert(spec.AddConnectedSlot(s.iface, s.plug, slot), IsNil) c.Assert(spec.SecurityTags(), HasLen, 0) // permanent core slot spec = &apparmor.Specification{} - c.Assert(spec.AddPermanentSlot(s.iface, s.coreSlotInfo), IsNil) + c.Assert(spec.AddPermanentSlot(s.iface, slotInfo), IsNil) c.Assert(spec.SecurityTags(), HasLen, 0) } -func (s *AvahiControlInterfaceSuite) TestDBusSpec(c *C) { +func (s *AvahiControlInterfaceSuite) TestAppArmorSpec(c *C) { // on a core system with avahi slot coming from a regular app snap. restore := release.MockOnClassic(false) defer restore() + s.testAppArmorSpecWithProducer(c, s.appSlot, s.appSlotInfo) + + // on a classic system with avahi slot coming from the system by core snap. + restore = release.MockOnClassic(true) + defer restore() + s.testAppArmorSpecWithProducer(c, s.appSlot, s.appSlotInfo) + + // on a classic system with avahi slot coming from the system by core snap. + restore = release.MockOnClassic(true) + defer restore() + s.testAppArmorSpecFromSystem(c, s.coreSlot, s.coreSlotInfo) + + // on a classic system with avahi slot coming from the system by snapd snap. + restore = release.MockOnClassic(true) + defer restore() + s.testAppArmorSpecFromSystem(c, s.snapdSlot, s.snapdSlotInfo) +} + +func (s *AvahiControlInterfaceSuite) testDBusSpecSlotByApp(c *C, classic bool) { + restore := release.MockOnClassic(classic) + defer restore() spec := &dbus.Specification{} c.Assert(spec.AddPermanentSlot(s.iface, s.appSlotInfo), IsNil) c.Assert(spec.SecurityTags(), DeepEquals, []string{"snap.producer.app"}) c.Assert(spec.SnippetForTag("snap.producer.app"), testutil.Contains, `<allow own="org.freedesktop.Avahi"/>`) +} - // on a classic system with avahi slot coming from the core snap. - restore = release.MockOnClassic(true) +func (s *AvahiControlInterfaceSuite) testDBusSpecSlotBySystem(c *C, slotInfo *snap.SlotInfo) { + restore := release.MockOnClassic(true) defer restore() - spec = &dbus.Specification{} - c.Assert(spec.AddPermanentSlot(s.iface, s.coreSlotInfo), IsNil) + spec := &dbus.Specification{} + c.Assert(spec.AddPermanentSlot(s.iface, slotInfo), IsNil) c.Assert(spec.SecurityTags(), HasLen, 0) } +func (s *AvahiControlInterfaceSuite) TestDBusSpecSlot(c *C) { + // on a core system with avahi slot coming from a regular app snap. + s.testDBusSpecSlotByApp(c, false) + // on a classic system with avahi slot coming from a regular app snap. + s.testDBusSpecSlotByApp(c, true) + + // on a classic system with avahi slot coming from the core snap. + s.testDBusSpecSlotBySystem(c, s.coreSlotInfo) + // on a classic system with avahi slot coming from the snapd snap. + s.testDBusSpecSlotBySystem(c, s.snapdSlotInfo) +} + func (s *AvahiControlInterfaceSuite) TestStaticInfo(c *C) { si := interfaces.StaticInfoOf(s.iface) c.Assert(si.ImplicitOnCore, Equals, false) diff --git a/interfaces/builtin/avahi_observe.go b/interfaces/builtin/avahi_observe.go index c767d7e68f..362109b763 100644 --- a/interfaces/builtin/avahi_observe.go +++ b/interfaces/builtin/avahi_observe.go @@ -25,7 +25,6 @@ import ( "github.com/snapcore/snapd/interfaces" "github.com/snapcore/snapd/interfaces/apparmor" "github.com/snapcore/snapd/interfaces/dbus" - "github.com/snapcore/snapd/release" "github.com/snapcore/snapd/snap" ) @@ -430,10 +429,12 @@ func (iface *avahiObserveInterface) StaticInfo() interfaces.StaticInfo { func (iface *avahiObserveInterface) AppArmorConnectedPlug(spec *apparmor.Specification, plug *interfaces.ConnectedPlug, slot *interfaces.ConnectedSlot) error { old := "###SLOT_SECURITY_TAGS###" var new string - if release.OnClassic { - // If we're running on classic Avahi will be part - // of the OS snap and will run unconfined. - new = "unconfined" + // If we're running on classic, Avahi may be installed either as a snap of + // as part of the OS. If it is part of the OS, it will not have a security + // label like it would when installed as a snap. + if implicitSystemConnectedSlot(slot) { + // avahi from the OS is typically unconfined but known to sometimes be confined + new = "\"{unconfined,/usr/sbin/avahi-daemon}\"" } else { new = slotAppLabelExpr(slot) } @@ -443,14 +444,18 @@ func (iface *avahiObserveInterface) AppArmorConnectedPlug(spec *apparmor.Specifi } func (iface *avahiObserveInterface) AppArmorPermanentSlot(spec *apparmor.Specification, slot *snap.SlotInfo) error { - if !release.OnClassic { + // Only apply slot snippet when running as application snap + // on classic, slot side can be system or application + if !implicitSystemPermanentSlot(slot) { spec.AddSnippet(avahiObservePermanentSlotAppArmor) } return nil } func (iface *avahiObserveInterface) AppArmorConnectedSlot(spec *apparmor.Specification, plug *interfaces.ConnectedPlug, slot *interfaces.ConnectedSlot) error { - if !release.OnClassic { + // Only apply slot snippet when running as application snap + // on classic, slot side can be system or application + if !implicitSystemConnectedSlot(slot) { old := "###PLUG_SECURITY_TAGS###" new := plugAppLabelExpr(plug) snippet := strings.Replace(avahiObserveConnectedSlotAppArmor, old, new, -1) @@ -460,7 +465,9 @@ func (iface *avahiObserveInterface) AppArmorConnectedSlot(spec *apparmor.Specifi } func (iface *avahiObserveInterface) DBusPermanentSlot(spec *dbus.Specification, slot *snap.SlotInfo) error { - if !release.OnClassic { + // Only apply slot snippet when running as application snap + // on classic, slot side can be system or application + if !implicitSystemPermanentSlot(slot) { spec.AddSnippet(avahiObservePermanentSlotDBus) } return nil diff --git a/interfaces/builtin/avahi_observe_test.go b/interfaces/builtin/avahi_observe_test.go index 650e25e7ed..d428558651 100644 --- a/interfaces/builtin/avahi_observe_test.go +++ b/interfaces/builtin/avahi_observe_test.go @@ -32,13 +32,15 @@ import ( ) type AvahiObserveInterfaceSuite struct { - iface interfaces.Interface - plug *interfaces.ConnectedPlug - plugInfo *snap.PlugInfo - appSlot *interfaces.ConnectedSlot - appSlotInfo *snap.SlotInfo - coreSlot *interfaces.ConnectedSlot - coreSlotInfo *snap.SlotInfo + iface interfaces.Interface + plug *interfaces.ConnectedPlug + plugInfo *snap.PlugInfo + appSlot *interfaces.ConnectedSlot + appSlotInfo *snap.SlotInfo + coreSlot *interfaces.ConnectedSlot + coreSlotInfo *snap.SlotInfo + snapdSlot *interfaces.ConnectedSlot + snapdSlotInfo *snap.SlotInfo } var _ = Suite(&AvahiObserveInterfaceSuite{ @@ -61,6 +63,14 @@ apps: const avahiObserveCoreYaml = `name: core version: 0 +type: os +slots: + avahi-observe: +` + +const avahiObserveSnapdYaml = `name: snapd +version: 0 +type: snapd slots: avahi-observe: ` @@ -69,6 +79,7 @@ func (s *AvahiObserveInterfaceSuite) SetUpTest(c *C) { s.plug, s.plugInfo = MockConnectedPlug(c, avahiObserveConsumerYaml, nil, "avahi-observe") s.appSlot, s.appSlotInfo = MockConnectedSlot(c, avahiObserveProducerYaml, nil, "avahi-observe") s.coreSlot, s.coreSlotInfo = MockConnectedSlot(c, avahiObserveCoreYaml, nil, "avahi-observe") + s.snapdSlot, s.snapdSlotInfo = MockConnectedSlot(c, avahiObserveSnapdYaml, nil, "avahi-observe") } func (s *AvahiObserveInterfaceSuite) TestName(c *C) { @@ -91,14 +102,11 @@ func (s *AvahiObserveInterfaceSuite) TestSanitizePlug(c *C) { c.Assert(interfaces.BeforePreparePlug(s.iface, s.plugInfo), IsNil) } -func (s *AvahiObserveInterfaceSuite) TestAppArmorSpec(c *C) { - // on a core system with avahi slot coming from a regular app snap. - restore := release.MockOnClassic(false) - defer restore() - +func (s *AvahiObserveInterfaceSuite) testAppArmorSpecWithProducer(c *C, + slot *interfaces.ConnectedSlot, slotInfo *snap.SlotInfo) { // connected plug to app slot spec := &apparmor.Specification{} - c.Assert(spec.AddConnectedPlug(s.iface, s.plug, s.appSlot), IsNil) + c.Assert(spec.AddConnectedPlug(s.iface, s.plug, slot), IsNil) c.Assert(spec.SecurityTags(), DeepEquals, []string{"snap.consumer.app"}) c.Assert(spec.SnippetForTag("snap.consumer.app"), testutil.Contains, "name=org.freedesktop.Avahi") c.Assert(spec.SnippetForTag("snap.consumer.app"), testutil.Contains, `peer=(label="snap.producer.app"),`) @@ -114,7 +122,7 @@ func (s *AvahiObserveInterfaceSuite) TestAppArmorSpec(c *C) { // connected app slot to plug spec = &apparmor.Specification{} - c.Assert(spec.AddConnectedSlot(s.iface, s.plug, s.appSlot), IsNil) + c.Assert(spec.AddConnectedSlot(s.iface, s.plug, slot), IsNil) c.Assert(spec.SecurityTags(), DeepEquals, []string{"snap.producer.app"}) c.Assert(spec.SnippetForTag("snap.producer.app"), testutil.Contains, `interface=org.freedesktop.Avahi`) c.Assert(spec.SnippetForTag("snap.producer.app"), testutil.Contains, `peer=(label="snap.consumer.app"),`) @@ -128,22 +136,21 @@ func (s *AvahiObserveInterfaceSuite) TestAppArmorSpec(c *C) { // permanent app slot spec = &apparmor.Specification{} - c.Assert(spec.AddPermanentSlot(s.iface, s.appSlotInfo), IsNil) + c.Assert(spec.AddPermanentSlot(s.iface, slotInfo), IsNil) c.Assert(spec.SecurityTags(), DeepEquals, []string{"snap.producer.app"}) c.Assert(spec.SnippetForTag("snap.producer.app"), testutil.Contains, `dbus (bind) bus=system name="org.freedesktop.Avahi",`) +} - // on a classic system with avahi slot coming from the core snap. - restore = release.MockOnClassic(true) - defer restore() - +func (s *AvahiObserveInterfaceSuite) testAppArmorSpecFromSystem(c *C, + slot *interfaces.ConnectedSlot, slotInfo *snap.SlotInfo) { // connected plug to core slot - spec = &apparmor.Specification{} - c.Assert(spec.AddConnectedPlug(s.iface, s.plug, s.coreSlot), IsNil) + spec := &apparmor.Specification{} + c.Assert(spec.AddConnectedPlug(s.iface, s.plug, slot), IsNil) c.Assert(spec.SecurityTags(), DeepEquals, []string{"snap.consumer.app"}) c.Assert(spec.SnippetForTag("snap.consumer.app"), testutil.Contains, "name=org.freedesktop.Avahi") - c.Assert(spec.SnippetForTag("snap.consumer.app"), testutil.Contains, "peer=(label=unconfined),") + c.Assert(spec.SnippetForTag("snap.consumer.app"), testutil.Contains, "peer=(label=\"{unconfined,/usr/sbin/avahi-daemon}\"),") // make sure observe does have observe but not control capabilities c.Assert(spec.SnippetForTag("snap.consumer.app"), testutil.Contains, `interface=org.freedesktop.Avahi.AddressResolver`) c.Assert(spec.SnippetForTag("snap.consumer.app"), testutil.Contains, `interface=org.freedesktop.Avahi.HostNameResolver`) @@ -155,34 +162,68 @@ func (s *AvahiObserveInterfaceSuite) TestAppArmorSpec(c *C) { // connected core slot to plug spec = &apparmor.Specification{} - c.Assert(spec.AddConnectedSlot(s.iface, s.plug, s.coreSlot), IsNil) + c.Assert(spec.AddConnectedSlot(s.iface, s.plug, slot), IsNil) c.Assert(spec.SecurityTags(), HasLen, 0) // permanent core slot spec = &apparmor.Specification{} - c.Assert(spec.AddPermanentSlot(s.iface, s.coreSlotInfo), IsNil) + c.Assert(spec.AddPermanentSlot(s.iface, slotInfo), IsNil) c.Assert(spec.SecurityTags(), HasLen, 0) } -func (s *AvahiObserveInterfaceSuite) TestDBusSpec(c *C) { +func (s *AvahiObserveInterfaceSuite) TestAppArmorSpec(c *C) { // on a core system with avahi slot coming from a regular app snap. restore := release.MockOnClassic(false) defer restore() + s.testAppArmorSpecWithProducer(c, s.appSlot, s.appSlotInfo) + + // on a classic system with avahi slot coming from the system by core snap. + restore = release.MockOnClassic(true) + defer restore() + s.testAppArmorSpecWithProducer(c, s.appSlot, s.appSlotInfo) + + // on a classic system with avahi slot coming from the system by core snap. + restore = release.MockOnClassic(true) + defer restore() + s.testAppArmorSpecFromSystem(c, s.coreSlot, s.coreSlotInfo) + + // on a classic system with avahi slot coming from the system by snapd snap. + restore = release.MockOnClassic(true) + defer restore() + s.testAppArmorSpecFromSystem(c, s.snapdSlot, s.snapdSlotInfo) +} + +func (s *AvahiObserveInterfaceSuite) testDBusSpecSlotByApp(c *C, classic bool) { + restore := release.MockOnClassic(classic) + defer restore() spec := &dbus.Specification{} c.Assert(spec.AddPermanentSlot(s.iface, s.appSlotInfo), IsNil) c.Assert(spec.SecurityTags(), DeepEquals, []string{"snap.producer.app"}) c.Assert(spec.SnippetForTag("snap.producer.app"), testutil.Contains, `<allow own="org.freedesktop.Avahi"/>`) +} - // on a classic system with avahi slot coming from the core snap. - restore = release.MockOnClassic(true) +func (s *AvahiObserveInterfaceSuite) testDBusSpecSlotBySystem(c *C, slotInfo *snap.SlotInfo) { + restore := release.MockOnClassic(true) defer restore() - spec = &dbus.Specification{} - c.Assert(spec.AddPermanentSlot(s.iface, s.coreSlotInfo), IsNil) + spec := &dbus.Specification{} + c.Assert(spec.AddPermanentSlot(s.iface, slotInfo), IsNil) c.Assert(spec.SecurityTags(), HasLen, 0) } +func (s *AvahiObserveInterfaceSuite) TestDBusSpecSlot(c *C) { + // on a core system with avahi slot coming from a regular app snap. + s.testDBusSpecSlotByApp(c, false) + // on a classic system with avahi slot coming from a regular app snap. + s.testDBusSpecSlotByApp(c, true) + + // on a classic system with avahi slot coming from the core snap. + s.testDBusSpecSlotBySystem(c, s.coreSlotInfo) + // on a classic system with avahi slot coming from the snapd snap. + s.testDBusSpecSlotBySystem(c, s.snapdSlotInfo) +} + func (s *AvahiObserveInterfaceSuite) TestStaticInfo(c *C) { si := interfaces.StaticInfoOf(s.iface) c.Assert(si.ImplicitOnCore, Equals, false) diff --git a/interfaces/builtin/export_test.go b/interfaces/builtin/export_test.go index 2315e924be..756016504b 100644 --- a/interfaces/builtin/export_test.go +++ b/interfaces/builtin/export_test.go @@ -35,6 +35,8 @@ var ( SanitizeSlotReservedForOS = sanitizeSlotReservedForOS SanitizeSlotReservedForOSOrGadget = sanitizeSlotReservedForOSOrGadget SanitizeSlotReservedForOSOrApp = sanitizeSlotReservedForOSOrApp + ImplicitSystemPermanentSlot = implicitSystemPermanentSlot + ImplicitSystemConnectedSlot = implicitSystemConnectedSlot ) func MprisGetName(iface interfaces.Interface, attribs map[string]interface{}) (string, error) { diff --git a/interfaces/builtin/utils.go b/interfaces/builtin/utils.go index ae690a3292..b071964824 100644 --- a/interfaces/builtin/utils.go +++ b/interfaces/builtin/utils.go @@ -25,6 +25,7 @@ import ( "sort" "github.com/snapcore/snapd/interfaces" + "github.com/snapcore/snapd/release" "github.com/snapcore/snapd/snap" ) @@ -96,3 +97,26 @@ func sanitizeSlotReservedForOSOrApp(iface interfaces.Interface, slot *snap.SlotI } return nil } + +// determine if permanent slot side is provided by the system +// on classic system some implicit slots can be provided by system or by +// application snap e.g. avahi (it can be installed as deb or snap) +// - slot owned by the system (core,snapd snap) usually requires no action +// - slot owned by application snap typically requires rules update +func implicitSystemPermanentSlot(slot *snap.SlotInfo) bool { + if release.OnClassic && + (slot.Snap.Type == snap.TypeOS || slot.Snap.Type == snap.TypeSnapd) { + return true + } + return false +} + +// determine if connected slot side is provided by the system +// as for isPermanentSlotSystemSlot() slot can be owned by app or system +func implicitSystemConnectedSlot(slot *interfaces.ConnectedSlot) bool { + if release.OnClassic && + (slot.Snap().Type == snap.TypeOS || slot.Snap().Type == snap.TypeSnapd) { + return true + } + return false +} diff --git a/interfaces/builtin/utils_test.go b/interfaces/builtin/utils_test.go index 4f6fb556c0..17958f7923 100644 --- a/interfaces/builtin/utils_test.go +++ b/interfaces/builtin/utils_test.go @@ -32,19 +32,25 @@ import ( ) type utilsSuite struct { - iface interfaces.Interface - slotOS *snap.SlotInfo - slotApp *snap.SlotInfo - slotSnapd *snap.SlotInfo - slotGadget *snap.SlotInfo + iface interfaces.Interface + slotOS *snap.SlotInfo + slotApp *snap.SlotInfo + slotSnapd *snap.SlotInfo + slotGadget *snap.SlotInfo + conSlotOS *interfaces.ConnectedSlot + conSlotSnapd *interfaces.ConnectedSlot + conSlotApp *interfaces.ConnectedSlot } var _ = Suite(&utilsSuite{ - iface: &ifacetest.TestInterface{InterfaceName: "iface"}, - slotOS: &snap.SlotInfo{Snap: &snap.Info{Type: snap.TypeOS}}, - slotApp: &snap.SlotInfo{Snap: &snap.Info{Type: snap.TypeApp}}, - slotSnapd: &snap.SlotInfo{Snap: &snap.Info{Type: snap.TypeApp, SuggestedName: "snapd"}}, - slotGadget: &snap.SlotInfo{Snap: &snap.Info{Type: snap.TypeGadget}}, + iface: &ifacetest.TestInterface{InterfaceName: "iface"}, + slotOS: &snap.SlotInfo{Snap: &snap.Info{Type: snap.TypeOS}}, + slotApp: &snap.SlotInfo{Snap: &snap.Info{Type: snap.TypeApp}}, + slotSnapd: &snap.SlotInfo{Snap: &snap.Info{Type: snap.TypeSnapd, SuggestedName: "snapd"}}, + slotGadget: &snap.SlotInfo{Snap: &snap.Info{Type: snap.TypeGadget}}, + conSlotOS: interfaces.NewConnectedSlot(&snap.SlotInfo{Snap: &snap.Info{Type: snap.TypeOS}}, nil, nil), + conSlotSnapd: interfaces.NewConnectedSlot(&snap.SlotInfo{Snap: &snap.Info{Type: snap.TypeSnapd}}, nil, nil), + conSlotApp: interfaces.NewConnectedSlot(&snap.SlotInfo{Snap: &snap.Info{Type: snap.TypeApp}}, nil, nil), }) func (s *utilsSuite) TestSanitizeSlotReservedForOS(c *C) { @@ -70,6 +76,18 @@ func (s *utilsSuite) TestSanitizeSlotReservedForOSOrApp(c *C) { c.Assert(builtin.SanitizeSlotReservedForOSOrApp(s.iface, s.slotGadget), ErrorMatches, errmsg) } +func (s *utilsSuite) TestIsSlotSystemSlot(c *C) { + c.Assert(builtin.ImplicitSystemPermanentSlot(s.slotApp), Equals, false) + c.Assert(builtin.ImplicitSystemPermanentSlot(s.slotOS), Equals, true) + c.Assert(builtin.ImplicitSystemPermanentSlot(s.slotSnapd), Equals, true) +} + +func (s *utilsSuite) TestImplicitSystemConnectedSlot(c *C) { + c.Assert(builtin.ImplicitSystemConnectedSlot(s.conSlotApp), Equals, false) + c.Assert(builtin.ImplicitSystemConnectedSlot(s.conSlotOS), Equals, true) + c.Assert(builtin.ImplicitSystemConnectedSlot(s.conSlotSnapd), Equals, true) +} + func MockPlug(c *C, yaml string, si *snap.SideInfo, plugName string) *snap.PlugInfo { return builtin.MockPlug(c, yaml, si, plugName) } |
