gitee.com/mysnapcore/mysnapd@v0.1.0/interfaces/builtin/mpris.go (about) 1 // -*- Mode: Go; indent-tabs-mode: t -*- 2 3 /* 4 * Copyright (C) 2016-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 21 22 import ( 23 "fmt" 24 "regexp" 25 "strings" 26 27 "gitee.com/mysnapcore/mysnapd/interfaces" 28 "gitee.com/mysnapcore/mysnapd/interfaces/apparmor" 29 "gitee.com/mysnapcore/mysnapd/release" 30 "gitee.com/mysnapcore/mysnapd/snap" 31 ) 32 33 const mprisSummary = `allows operating as an MPRIS player` 34 35 const mprisBaseDeclarationSlots = ` 36 mpris: 37 allow-installation: 38 slot-snap-type: 39 - app 40 deny-connection: 41 slot-attributes: 42 name: .+ 43 deny-auto-connection: true 44 ` 45 46 const mprisPermanentSlotAppArmor = ` 47 # Description: Allow operating as an MPRIS player. 48 49 # DBus accesses 50 #include <abstractions/dbus-session-strict> 51 52 # https://specifications.freedesktop.org/mpris-spec/latest/ 53 # allow binding to the well-known DBus mpris interface based on the snap's name 54 dbus (bind) 55 bus=session 56 name="org.mpris.MediaPlayer2.###MPRIS_NAME###{,.*}", 57 58 # register as a player 59 dbus (send) 60 bus=system 61 path=/org/freedesktop/DBus 62 interface=org.freedesktop.DBus 63 member="{Request,Release}Name" 64 peer=(name=org.freedesktop.DBus, label=unconfined), 65 66 dbus (send) 67 bus=system 68 path=/org/freedesktop/DBus 69 interface=org.freedesktop.DBus 70 member="GetConnectionUnix{ProcessID,User}" 71 peer=(name=org.freedesktop.DBus, label=unconfined), 72 73 dbus (send) 74 bus=session 75 path=/org/mpris/MediaPlayer2 76 interface=org.freedesktop.DBus.Properties 77 member="{GetAll,PropertiesChanged}" 78 peer=(name=org.freedesktop.DBus, label=unconfined), 79 80 dbus (send) 81 bus=session 82 path=/org/mpris/MediaPlayer2 83 interface="org.mpris.MediaPlayer2{,.Player}" 84 peer=(name=org.freedesktop.DBus, label=unconfined), 85 86 # we can always connect to ourselves 87 dbus (receive) 88 bus=session 89 path=/org/mpris/MediaPlayer2 90 peer=(label=@{profile_name}), 91 ` 92 93 const mprisConnectedSlotAppArmor = ` 94 # Allow connected clients to interact with the player 95 dbus (receive) 96 bus=session 97 interface=org.freedesktop.DBus.Properties 98 path=/org/mpris/MediaPlayer2 99 peer=(label=###PLUG_SECURITY_TAGS###), 100 101 dbus (receive) 102 bus=session 103 interface=org.freedesktop.DBus.Introspectable 104 peer=(label=###PLUG_SECURITY_TAGS###), 105 106 dbus (receive) 107 bus=session 108 interface="org.mpris.MediaPlayer2{,.*}" 109 path=/org/mpris/MediaPlayer2 110 peer=(label=###PLUG_SECURITY_TAGS###), 111 112 dbus (send) 113 bus=session 114 interface=org.freedesktop.DBus.Properties 115 path=/org/mpris/MediaPlayer2 116 member=PropertiesChanged 117 peer=(label=###PLUG_SECURITY_TAGS###), 118 ` 119 120 const mprisConnectedSlotAppArmorClassic = ` 121 # Allow unconfined clients to interact with the player on classic 122 dbus (receive) 123 bus=session 124 path=/org/mpris/MediaPlayer2 125 peer=(label=unconfined), 126 dbus (receive) 127 bus=session 128 interface=org.freedesktop.DBus.Introspectable 129 peer=(label=unconfined), 130 ` 131 132 const mprisConnectedPlugAppArmor = ` 133 # Description: Allow connecting to an MPRIS player. 134 135 #include <abstractions/dbus-session-strict> 136 137 # Find the mpris player 138 dbus (send) 139 bus=session 140 path=/org/freedesktop/DBus 141 interface=org.freedesktop.DBus.Introspectable 142 peer=(name="org.freedesktop.DBus", label="unconfined"), 143 dbus (send) 144 bus=session 145 path=/{,org,org/mpris,org/mpris/MediaPlayer2} 146 interface=org.freedesktop.DBus.Introspectable 147 peer=(name="org.freedesktop.DBus", label="unconfined"), 148 # This reveals all names on the session bus 149 dbus (send) 150 bus=session 151 path=/ 152 interface=org.freedesktop.DBus 153 member=ListNames 154 peer=(name="org.freedesktop.DBus", label="unconfined"), 155 156 # Communicate with the mpris player 157 dbus (send) 158 bus=session 159 path=/org/mpris/MediaPlayer2 160 peer=(label=###SLOT_SECURITY_TAGS###), 161 162 # receive signals for updated properties 163 dbus (receive) 164 bus=session 165 interface=org.freedesktop.DBus.Properties 166 path=/org/mpris/MediaPlayer2 167 member=PropertiesChanged 168 peer=(label=###SLOT_SECURITY_TAGS###), 169 ` 170 171 type mprisInterface struct{} 172 173 func (iface *mprisInterface) Name() string { 174 return "mpris" 175 } 176 177 func (iface *mprisInterface) StaticInfo() interfaces.StaticInfo { 178 return interfaces.StaticInfo{ 179 Summary: mprisSummary, 180 BaseDeclarationSlots: mprisBaseDeclarationSlots, 181 } 182 } 183 184 func (iface *mprisInterface) AppArmorConnectedPlug(spec *apparmor.Specification, plug *interfaces.ConnectedPlug, slot *interfaces.ConnectedSlot) error { 185 old := "###SLOT_SECURITY_TAGS###" 186 new := slotAppLabelExpr(slot) 187 spec.AddSnippet(strings.Replace(mprisConnectedPlugAppArmor, old, new, -1)) 188 return nil 189 } 190 191 func (iface *mprisInterface) AppArmorPermanentSlot(spec *apparmor.Specification, slot *snap.SlotInfo) error { 192 name, err := iface.getName(slot.Attrs) 193 if err != nil { 194 return err 195 } 196 197 old := "###MPRIS_NAME###" 198 new := name 199 spec.AddSnippet(strings.Replace(mprisPermanentSlotAppArmor, old, new, -1)) 200 // on classic, allow unconfined remotes to control the player 201 // (eg, indicator-sound) 202 if release.OnClassic { 203 spec.AddSnippet(mprisConnectedSlotAppArmorClassic) 204 } 205 return nil 206 } 207 208 func (iface *mprisInterface) AppArmorConnectedSlot(spec *apparmor.Specification, plug *interfaces.ConnectedPlug, slot *interfaces.ConnectedSlot) error { 209 old := "###PLUG_SECURITY_TAGS###" 210 new := plugAppLabelExpr(plug) 211 spec.AddSnippet(strings.Replace(mprisConnectedSlotAppArmor, old, new, -1)) 212 return nil 213 } 214 215 var isValidDBusElement = regexp.MustCompile("^[a-zA-Z0-9_-]*$").MatchString 216 217 func (iface *mprisInterface) getName(attribs map[string]interface{}) (string, error) { 218 // default to snap instance name if 'name' attribute not set 219 // parallel-installs: snaps utilizing the mpris interface must adjust 220 // themselves accordingly for parallel installs and use 221 // SNAP_INSTANCE_NAME as part of their well-known name. 222 mprisName := "@{SNAP_INSTANCE_NAME}" 223 for attr := range attribs { 224 if attr != "name" { 225 return "", fmt.Errorf("unknown attribute '%s'", attr) 226 } 227 raw, ok := attribs[attr] 228 if !ok { 229 return "", fmt.Errorf("cannot find attribute %q", attr) 230 } 231 name, ok := raw.(string) 232 if !ok { 233 return "", fmt.Errorf("name element %v is not a string", raw) 234 } 235 236 if !isValidDBusElement(name) { 237 return "", fmt.Errorf("invalid name element: %q", name) 238 } 239 mprisName = name 240 } 241 return mprisName, nil 242 } 243 244 func (iface *mprisInterface) BeforePrepareSlot(slot *snap.SlotInfo) error { 245 _, err := iface.getName(slot.Attrs) 246 return err 247 } 248 249 func (iface *mprisInterface) AutoConnect(*snap.PlugInfo, *snap.SlotInfo) bool { 250 // allow what declarations allowed 251 return true 252 } 253 254 func init() { 255 registerIface(&mprisInterface{}) 256 }