gitee.com/mysnapcore/mysnapd@v0.1.0/interfaces/builtin/polkit_test.go (about) 1 // -*- Mode: Go; indent-tabs-mode: t -*- 2 3 /* 4 * Copyright (C) 2021 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 "io/ioutil" 24 "os" 25 "path/filepath" 26 "strings" 27 28 . "gopkg.in/check.v1" 29 30 "gitee.com/mysnapcore/mysnapd/dirs" 31 "gitee.com/mysnapcore/mysnapd/interfaces" 32 "gitee.com/mysnapcore/mysnapd/interfaces/apparmor" 33 "gitee.com/mysnapcore/mysnapd/interfaces/builtin" 34 "gitee.com/mysnapcore/mysnapd/interfaces/polkit" 35 "gitee.com/mysnapcore/mysnapd/osutil" 36 "gitee.com/mysnapcore/mysnapd/snap" 37 "gitee.com/mysnapcore/mysnapd/snap/snaptest" 38 "gitee.com/mysnapcore/mysnapd/testutil" 39 ) 40 41 type polkitInterfaceSuite struct { 42 testutil.BaseTest 43 44 iface interfaces.Interface 45 slot *interfaces.ConnectedSlot 46 slotInfo *snap.SlotInfo 47 plug *interfaces.ConnectedPlug 48 plugInfo *snap.PlugInfo 49 } 50 51 var _ = Suite(&polkitInterfaceSuite{ 52 iface: builtin.MustInterface("polkit"), 53 }) 54 55 func (s *polkitInterfaceSuite) SetUpTest(c *C) { 56 s.BaseTest.SetUpTest(c) 57 dirs.SetRootDir(c.MkDir()) 58 s.AddCleanup(func() { 59 dirs.SetRootDir("/") 60 }) 61 62 const mockPlugSnapInfo = `name: other 63 version: 1.0 64 plugs: 65 polkit: 66 action-prefix: org.example.foo 67 apps: 68 app: 69 command: foo 70 plugs: [polkit] 71 ` 72 s.slotInfo = &snap.SlotInfo{ 73 Snap: &snap.Info{SuggestedName: "core", SnapType: snap.TypeOS}, 74 Name: "polkit", 75 Interface: "polkit", 76 } 77 s.slot = interfaces.NewConnectedSlot(s.slotInfo, nil, nil) 78 plugSnap := snaptest.MockSnap(c, mockPlugSnapInfo, &snap.SideInfo{ 79 RealName: "other", 80 Revision: snap.R(1), 81 }) 82 s.plugInfo = plugSnap.Plugs["polkit"] 83 s.plug = interfaces.NewConnectedPlug(s.plugInfo, nil, nil) 84 } 85 86 func (s *polkitInterfaceSuite) TestName(c *C) { 87 c.Assert(s.iface.Name(), Equals, "polkit") 88 } 89 90 func (s *polkitInterfaceSuite) TestConnectedPlugAppArmor(c *C) { 91 apparmorSpec := &apparmor.Specification{} 92 err := apparmorSpec.AddConnectedPlug(s.iface, s.plug, s.slot) 93 c.Assert(err, IsNil) 94 c.Assert(apparmorSpec.SecurityTags(), DeepEquals, []string{"snap.other.app"}) 95 c.Check(apparmorSpec.SnippetForTag("snap.other.app"), testutil.Contains, "# Description: Can talk to polkitd's CheckAuthorization API") 96 c.Check(apparmorSpec.SnippetForTag("snap.other.app"), testutil.Contains, `member="{,Cancel}CheckAuthorization"`) 97 } 98 99 func (s *polkitInterfaceSuite) TestConnectedPlugPolkit(c *C) { 100 const samplePolicy1 = `<policyconfig> 101 <action id="org.example.foo.some-action"> 102 <description>Some action</description> 103 <message>Authentication is required to do some action</message> 104 <defaults> 105 <allow_any>no</allow_any> 106 <allow_inactive>no</allow_inactive> 107 <allow_active>auth_admin</allow_active> 108 </defaults> 109 </action> 110 </policyconfig>` 111 const samplePolicy2 = `<policyconfig/>` 112 113 c.Assert(os.MkdirAll(filepath.Join(s.plugInfo.Snap.MountDir(), "meta/polkit"), 0755), IsNil) 114 policyPath := filepath.Join(s.plugInfo.Snap.MountDir(), "meta/polkit/polkit.foo.policy") 115 c.Assert(ioutil.WriteFile(policyPath, []byte(samplePolicy1), 0644), IsNil) 116 policyPath = filepath.Join(s.plugInfo.Snap.MountDir(), "meta/polkit/polkit.bar.policy") 117 c.Assert(ioutil.WriteFile(policyPath, []byte(samplePolicy2), 0644), IsNil) 118 119 polkitSpec := &polkit.Specification{} 120 err := polkitSpec.AddConnectedPlug(s.iface, s.plug, s.slot) 121 c.Assert(err, IsNil) 122 123 c.Check(polkitSpec.Policies(), DeepEquals, map[string]polkit.Policy{ 124 "polkit.foo": polkit.Policy(samplePolicy1), 125 "polkit.bar": polkit.Policy(samplePolicy2), 126 }) 127 } 128 129 func (s *polkitInterfaceSuite) TestConnectedPlugPolkitMissing(c *C) { 130 polkitSpec := &polkit.Specification{} 131 err := polkitSpec.AddConnectedPlug(s.iface, s.plug, s.slot) 132 c.Check(err, ErrorMatches, `cannot find any policy files for plug "polkit"`) 133 } 134 135 func (s *polkitInterfaceSuite) TestConnectedPlugPolkitNotFile(c *C) { 136 c.Assert(os.MkdirAll(filepath.Join(s.plugInfo.Snap.MountDir(), "meta/polkit"), 0755), IsNil) 137 policyPath := filepath.Join(s.plugInfo.Snap.MountDir(), "meta/polkit/polkit.foo.policy") 138 c.Assert(os.Mkdir(policyPath, 0755), IsNil) 139 140 polkitSpec := &polkit.Specification{} 141 err := polkitSpec.AddConnectedPlug(s.iface, s.plug, s.slot) 142 c.Check(err, ErrorMatches, `cannot read file ".*/meta/polkit/polkit.foo.policy": read .*: is a directory`) 143 } 144 145 func (s *polkitInterfaceSuite) TestConnectedPlugPolkitBadXML(c *C) { 146 const samplePolicy = `<malformed` 147 c.Assert(os.MkdirAll(filepath.Join(s.plugInfo.Snap.MountDir(), "meta/polkit"), 0755), IsNil) 148 policyPath := filepath.Join(s.plugInfo.Snap.MountDir(), "meta/polkit/polkit.foo.policy") 149 c.Assert(ioutil.WriteFile(policyPath, []byte(samplePolicy), 0644), IsNil) 150 151 polkitSpec := &polkit.Specification{} 152 err := polkitSpec.AddConnectedPlug(s.iface, s.plug, s.slot) 153 c.Check(err, ErrorMatches, `cannot validate policy file ".*/meta/polkit/polkit.foo.policy": XML syntax error on line 1: unexpected EOF`) 154 } 155 156 func (s *polkitInterfaceSuite) TestConnectedPlugPolkitBadAction(c *C) { 157 const samplePolicy = `<policyconfig> 158 <action id="org.freedesktop.systemd1.manage-units"> 159 <description>A conflict with systemd's polkit actions</description> 160 <message>Manage system services</message> 161 <defaults> 162 <allow_any>yes</allow_any> 163 </defaults> 164 </action> 165 </policyconfig>` 166 c.Assert(os.MkdirAll(filepath.Join(s.plugInfo.Snap.MountDir(), "meta/polkit"), 0755), IsNil) 167 policyPath := filepath.Join(s.plugInfo.Snap.MountDir(), "meta/polkit/polkit.foo.policy") 168 c.Assert(ioutil.WriteFile(policyPath, []byte(samplePolicy), 0644), IsNil) 169 170 polkitSpec := &polkit.Specification{} 171 err := polkitSpec.AddConnectedPlug(s.iface, s.plug, s.slot) 172 c.Check(err, ErrorMatches, `policy file ".*/meta/polkit/polkit.foo.policy" contains unexpected action ID "org.freedesktop.systemd1.manage-units"`) 173 } 174 175 func (s *polkitInterfaceSuite) TestConnectedPlugPolkitBadImplies(c *C) { 176 const samplePolicy = `<policyconfig> 177 <action id="org.example.foo.some-action"> 178 <description>Some action</description> 179 <message>Allow "some action" (and also managing system services for some reason)</message> 180 <defaults> 181 <allow_any>yes</allow_any> 182 </defaults> 183 <annotate key="org.freedesktop.policykit.imply">org.freedesktop.systemd1.manage-units</annotate> 184 </action> 185 </policyconfig>` 186 c.Assert(os.MkdirAll(filepath.Join(s.plugInfo.Snap.MountDir(), "meta/polkit"), 0755), IsNil) 187 policyPath := filepath.Join(s.plugInfo.Snap.MountDir(), "meta/polkit/polkit.foo.policy") 188 c.Assert(ioutil.WriteFile(policyPath, []byte(samplePolicy), 0644), IsNil) 189 190 polkitSpec := &polkit.Specification{} 191 err := polkitSpec.AddConnectedPlug(s.iface, s.plug, s.slot) 192 c.Check(err, ErrorMatches, `policy file ".*/meta/polkit/polkit.foo.policy" contains unexpected action ID "org.freedesktop.systemd1.manage-units"`) 193 } 194 195 func (s *polkitInterfaceSuite) TestSanitizeSlot(c *C) { 196 c.Assert(interfaces.BeforePrepareSlot(s.iface, s.slotInfo), IsNil) 197 } 198 199 func (s *polkitInterfaceSuite) TestSanitizePlug(c *C) { 200 c.Assert(interfaces.BeforePreparePlug(s.iface, s.plugInfo), IsNil) 201 } 202 203 func (s *polkitInterfaceSuite) TestSanitizePlugHappy(c *C) { 204 const mockSnapYaml = `name: polkit-plug-snap 205 version: 1.0 206 plugs: 207 polkit: 208 action-prefix: org.example.bar 209 ` 210 info := snaptest.MockInfo(c, mockSnapYaml, nil) 211 plug := info.Plugs["polkit"] 212 c.Assert(interfaces.BeforePreparePlug(s.iface, plug), IsNil) 213 } 214 215 func (s *polkitInterfaceSuite) TestSanitizePlugUnhappy(c *C) { 216 const mockSnapYaml = `name: polkit-plug-snap 217 version: 1.0 218 plugs: 219 polkit: 220 $t 221 ` 222 var testCases = []struct { 223 inp string 224 errStr string 225 }{ 226 {"", `snap "polkit-plug-snap" does not have attribute "action-prefix" for interface "polkit"`}, 227 {`action-prefix: true`, `snap "polkit-plug-snap" has interface "polkit" with invalid value type bool for "action-prefix" attribute: \*string`}, 228 {`action-prefix: 42`, `snap "polkit-plug-snap" has interface "polkit" with invalid value type int64 for "action-prefix" attribute: \*string`}, 229 {`action-prefix: []`, `snap "polkit-plug-snap" has interface "polkit" with invalid value type \[\]interface {} for "action-prefix" attribute: \*string`}, 230 {`action-prefix: {}`, `snap "polkit-plug-snap" has interface "polkit" with invalid value type map\[string\]interface {} for "action-prefix" attribute: \*string`}, 231 232 {`action-prefix: ""`, `plug has invalid action-prefix: ""`}, 233 {`action-prefix: "org"`, `plug has invalid action-prefix: "org"`}, 234 {`action-prefix: "a+b"`, `plug has invalid action-prefix: "a\+b"`}, 235 {`action-prefix: "org.example\n"`, `plug has invalid action-prefix: "org.example\\n"`}, 236 {`action-prefix: "com.example "`, `plug has invalid action-prefix: "com.example "`}, 237 {`action-prefix: "123.foo.bar"`, `plug has invalid action-prefix: "123.foo.bar"`}, 238 } 239 240 for _, t := range testCases { 241 yml := strings.Replace(mockSnapYaml, "$t", t.inp, -1) 242 info := snaptest.MockInfo(c, yml, nil) 243 plug := info.Plugs["polkit"] 244 245 c.Check(interfaces.BeforePreparePlug(s.iface, plug), ErrorMatches, t.errStr, Commentf("unexpected error for %q", t.inp)) 246 } 247 } 248 249 func (s *polkitInterfaceSuite) TestConnectedPlugPolkitInternalError(c *C) { 250 const mockPlugSnapInfo = `name: other 251 version: 1.0 252 plugs: 253 polkit: 254 action-prefix: false 255 apps: 256 app: 257 command: foo 258 plugs: [polkit] 259 ` 260 s.slotInfo = &snap.SlotInfo{ 261 Snap: &snap.Info{SuggestedName: "core", SnapType: snap.TypeOS}, 262 Name: "polkit", 263 Interface: "polkit", 264 } 265 s.slot = interfaces.NewConnectedSlot(s.slotInfo, nil, nil) 266 plugSnap := snaptest.MockInfo(c, mockPlugSnapInfo, nil) 267 s.plugInfo = plugSnap.Plugs["polkit"] 268 s.plug = interfaces.NewConnectedPlug(s.plugInfo, nil, nil) 269 270 polkitSpec := &polkit.Specification{} 271 err := polkitSpec.AddConnectedPlug(s.iface, s.plug, s.slot) 272 c.Assert(err, ErrorMatches, `snap "other" has interface "polkit" with invalid value type bool for "action-prefix" attribute: \*string`) 273 } 274 275 func (s *polkitInterfaceSuite) TestStaticInfo(c *C) { 276 si := interfaces.StaticInfoOf(s.iface) 277 c.Check(si.ImplicitOnCore, Equals, osutil.IsExecutable("/usr/libexec/polkitd")) 278 c.Check(si.ImplicitOnClassic, Equals, true) 279 c.Check(si.Summary, Equals, "allows access to polkitd to check authorisation") 280 c.Check(si.BaseDeclarationPlugs, testutil.Contains, "polkit") 281 c.Check(si.BaseDeclarationSlots, testutil.Contains, "polkit") 282 } 283 284 func (s *polkitInterfaceSuite) TestInterfaces(c *C) { 285 c.Check(builtin.Interfaces(), testutil.DeepContains, s.iface) 286 }