mirror of https://gitee.com/openkylin/gvfs.git
197 lines
7.4 KiB
Python
Executable File
197 lines
7.4 KiB
Python
Executable File
#!/usr/bin/env python3
|
|
# (C) 2011 Sebastian Heinlein
|
|
# (C) 2012 Canonical Ltd.
|
|
# Authors:
|
|
# Sebastian Heinlein <sebi@glatzor.de>
|
|
# Martin Pitt <martin.pitt@ubuntu.com>
|
|
#
|
|
# This program is free software; you can redistribute it and/or
|
|
# modify it under the terms of the GNU Lesser General Public
|
|
# License as published by the Free Software Foundation; either
|
|
# version 2 of the License, or (at your option) any later version.
|
|
|
|
'''Simple mock polkit daemon for test suites.
|
|
|
|
This also provides some convenience API for launching the daemon and for
|
|
writing unittest test cases involving polkit operations.
|
|
'''
|
|
|
|
import sys
|
|
import os
|
|
import argparse
|
|
import unittest
|
|
import signal
|
|
import time
|
|
|
|
import dbus
|
|
import dbus.service
|
|
from gi.repository import GLib, Gio
|
|
|
|
# ----------------------------------------------------------------------------
|
|
|
|
class TestPolicyKitDaemon(dbus.service.Object):
|
|
def __init__(self, allowed_actions, on_bus=None, replace=False):
|
|
'''Initialize test polkit daemon.
|
|
|
|
@allowed_actions is a list of PolicyKit action IDs which will be
|
|
allowed (active/inactive sessions or user IDs will not be considered);
|
|
all actions not in that list will be denied. If 'all' is an element of
|
|
@allowed_actions, all actions will be allowed.
|
|
|
|
When @on_bus string is given, the daemon will run on that D-BUS
|
|
address, otherwise on the system D-BUS.
|
|
|
|
If @replace is True, this will replace an already running polkit daemon
|
|
on the D-BUS.
|
|
'''
|
|
self.allowed_actions = allowed_actions
|
|
if on_bus:
|
|
bus = dbus.bus.BusConnection(on_bus)
|
|
else:
|
|
bus = dbus.SystemBus()
|
|
bus_name = dbus.service.BusName('org.freedesktop.PolicyKit1',
|
|
bus, do_not_queue=True,
|
|
replace_existing=replace,
|
|
allow_replacement=True)
|
|
bus.add_signal_receiver(self.on_disconnected, signal_name='Disconnected')
|
|
|
|
dbus.service.Object.__init__(self, bus_name,
|
|
'/org/freedesktop/PolicyKit1/Authority')
|
|
self.loop = GLib.MainLoop()
|
|
|
|
def run(self):
|
|
self.loop.run()
|
|
|
|
@dbus.service.method('org.freedesktop.PolicyKit1.Authority',
|
|
in_signature='(sa{sv})sa{ss}us',
|
|
out_signature='(bba{ss})')
|
|
def CheckAuthorization(self, subject, action_id, details, flags,
|
|
cancellation_id):
|
|
if 'all' in self.allowed_actions:
|
|
allowed = True
|
|
else:
|
|
allowed = action_id in self.allowed_actions
|
|
challenged = False
|
|
details = {'test': 'test'}
|
|
return (allowed, challenged, details)
|
|
|
|
@dbus.service.method('org.freedesktop.PolicyKit1.Authority',
|
|
in_signature='', out_signature='')
|
|
def Quit(self):
|
|
GLib.idle_add(self.loop.quit)
|
|
|
|
def on_disconnected(self):
|
|
print('disconnected from D-BUS, terminating')
|
|
self.Quit()
|
|
|
|
# ----------------------------------------------------------------------------
|
|
|
|
class PolkitTestCase(unittest.TestCase):
|
|
'''Convenient test cases involving polkit.
|
|
|
|
Call start_polkitd() with the list of allowed actions in your test cases.
|
|
The daemon will be automatically terminated when the test case exits.
|
|
'''
|
|
|
|
def __init__(self, methodName='runTest'):
|
|
unittest.TestCase.__init__(self, methodName)
|
|
self.polkit_pid = None
|
|
|
|
def start_polkitd(self, allowed_actions, on_bus=None):
|
|
'''Start test polkitd.
|
|
|
|
This should be called in your test cases before the exercised code
|
|
makes any polkit query. The daemon will be stopped automatically when
|
|
the test case ends (regardless of whether its successful or failed). If
|
|
you want to test multiple different action sets in one test case, you
|
|
have to call stop_polkitd() before starting a new one.
|
|
|
|
@allowed_actions is a list of PolicyKit action IDs which will be
|
|
allowed (active/inactive sessions or user IDs will not be considered);
|
|
all actions not in that list will be denied. If 'all' is an element of
|
|
@allowed_actions, all actions will be allowed.
|
|
|
|
When @on_bus string is given, the daemon will run on that D-BUS
|
|
address, otherwise on the system D-BUS.
|
|
'''
|
|
assert self.polkit_pid is None, \
|
|
'can only launch one polkitd at a time; write a separate test case or call stop_polkitd()'
|
|
self.polkit_pid = spawn(allowed_actions, on_bus)
|
|
self.addCleanup(self.stop_polkitd)
|
|
|
|
def stop_polkitd(self):
|
|
'''Stop test polkitd.
|
|
|
|
This happens automatically when a test case ends, but is required when
|
|
you want to test multiple different action sets in one test case.
|
|
'''
|
|
assert self.polkit_pid is not None, 'polkitd is not running'
|
|
os.kill(self.polkit_pid, signal.SIGTERM)
|
|
os.waitpid(self.polkit_pid, 0)
|
|
self.polkit_pid = None
|
|
|
|
# ----------------------------------------------------------------------------
|
|
|
|
def _run(allowed_actions, bus_address, replace=False):
|
|
# Set up the DBus main loop
|
|
import dbus.mainloop.glib
|
|
dbus.mainloop.glib.DBusGMainLoop(set_as_default=True)
|
|
|
|
polkitd = TestPolicyKitDaemon(allowed_actions, bus_address, replace)
|
|
polkitd.run()
|
|
|
|
def spawn(allowed_actions, on_bus=None):
|
|
'''Run a TestPolicyKitDaemon instance in a separate process.
|
|
|
|
@allowed_actions is a list of PolicyKit action IDs which will be
|
|
allowed (active/inactive sessions or user IDs will not be considered);
|
|
all actions not in that list will be denied. If 'all' is an element of
|
|
@allowed_actions, all actions will be allowed.
|
|
|
|
When @on_bus string is given, the daemon will run on that D-BUS address,
|
|
otherwise on the system D-BUS.
|
|
|
|
The daemon will terminate automatically when the @on_bus D-BUS goes down.
|
|
If that does not happen (e. g. you test on the actual system/session bus),
|
|
you need to kill it manually.
|
|
|
|
Returns the process ID of the spawned daemon.
|
|
'''
|
|
pid = os.fork()
|
|
if pid == 0:
|
|
# child
|
|
_run(allowed_actions, on_bus)
|
|
os._exit(0)
|
|
|
|
# wait until the daemon is up on the bus
|
|
if on_bus:
|
|
bus = dbus.bus.BusConnection(on_bus)
|
|
elif 'DBUS_SYSTEM_BUS_ADDRESS' in os.environ:
|
|
# dbus.SystemBus() does not recognize this env var, so we have to
|
|
# handle that manually
|
|
bus = dbus.bus.BusConnection(os.environ['DBUS_SYSTEM_BUS_ADDRESS'])
|
|
else:
|
|
bus = dbus.SystemBus()
|
|
timeout = 50
|
|
while timeout > 0 and not bus.name_has_owner('org.freedesktop.PolicyKit1'):
|
|
timeout -= 1
|
|
time.sleep(0.1)
|
|
assert timeout > 0, 'test polkitd failed to start up'
|
|
|
|
return pid
|
|
|
|
def main():
|
|
parser = argparse.ArgumentParser(description='Simple mock polkit daemon for test suites')
|
|
parser.add_argument('-a', '--allowed-actions', metavar='ACTION[,ACTION,...]',
|
|
default='', help='Comma separated list of allowed action ids')
|
|
parser.add_argument('-b', '--bus-address',
|
|
help='D-BUS address to listen on (if not given, listen on system D-BUS)')
|
|
parser.add_argument('-r', '--replace', action='store_true',
|
|
help='Replace existing polkit daemon on the bus')
|
|
args = parser.parse_args()
|
|
|
|
_run(args.allowed_actions.split(','), args.bus_address, args.replace)
|
|
|
|
if __name__ == '__main__':
|
|
main()
|