使用policykit 的程序一般都有一个dbus daemon程序来完成相关操作,这个dbus daemon 会在系统注册一个system bus 服务名,用于响应要求root privileged的操作,当dbus请求到达时会先验证请求程序是否有相应的权限来调用这个操作(方法),而这是在.conf文件中定义的(后面说明)。

首先定义个System Dbus daemon,写一个.service文件来启动我们的daemon

org.example.foo.service

文件放置目录:/usr/share/dbus-1/system-services

1 [D-BUS Service]
2 Name=org.example.foo
3 Exec=/usr/local/libexec/policykit_dbus_foo_daemon.py
4 User=root

其中Name是注册的SystemBus 服务名

Exec 是daemon 程序所在路径

我们以root权限启动

当有程序请求org.example.foo服务时,系统会自动以root启动我们的daemon。

相关信息看这里D-Bus system bus activation

注: SessionBus 的 'org.freedesktop.PolicyKit.AuthenticationAgent' 的服务,只有在请求认证的时候才自动启动,打开过一段时间会自动关闭。

再看我们的daemon程序

policykit_dbus_foo_daemon.py

文件放置目录:/usr/local/libexec


 1 #!/usr/bin/python
2 # -*- coding: UTF-8 -*-
3 """
4 Author: joe.zhou
5 """
6 import os
7 import sys
8 import gobject
9 import dbus
10 import dbus.service
11 import dbus.mainloop.glib
12
13 class NotPrivilegedException (dbus.DBusException):
14 _dbus_error_name = "org.example.foo.dbus.service.PolKit.NotPrivilegedException"
15 def __init__ (self, action_id, *p, **k):
16 self._dbus_error_name = self.__class__._dbus_error_name + "." + action_id
17 super (NotPrivilegedException, self).__init__ (*p, **k)
18
19 def require_auth (action_id):
20 def require_auth_decorator(func):
21 def _func(*args,**kwds):
22 revoke_if_one_shot = True
23 system_bus = dbus.SystemBus()
24 auth_obj = system_bus.get_object('org.freedesktop.PolicyKit','/')
25 auth_interface = dbus.Interface(auth_obj,'org.freedesktop.PolicyKit')
26 try:
27 dbus_name = kwds['sender_keyword']
28 except:
29 raise NotPrivilegedException (action_id)
30 granted = auth_interface.IsSystemBusNameAuthorized(action_id,dbus_name,revoke_if_one_shot)
31 if granted != 'yes':
32 raise NotPrivilegedException (action_id)
33
34 return func(*args,**kwds)
35
36 _func.func_name = func.func_name
37 _func.__name__ = func.__name__
38 _func.__doc__ = func.__doc__
39 return _func
40 return require_auth_decorator
41
42 '''
43 A D-Bus service that PolicyKit controls access to.
44 '''
45 class PolicyKitFooMechanism(dbus.service.Object):
46 SERVICE_NAME = 'org.example.foo'
47 SERVICE_PATH = '/org/example/foo'
48 INTERFACE_NAME = 'org.example.foo'
49
50 def __init__(self, conn, object_path=SERVICE_PATH):
51 dbus.service.Object.__init__(self, conn, object_path)
52
53 @dbus.service.method(dbus_interface=INTERFACE_NAME, in_signature='ss',out_signature='s',sender_keyword='sender')
54 def WriteFile(self, filepath, contents,sender=None):
55 '''
56 Write the contents to a file that requires sudo/root access to do so.
57 PolicyKit will not allow this function to be called without sudo/root
58 access, and will ask the user to authenticate if necessary, when
59 the application calls PolicyKit's ObtainAuthentication().
60 '''
61 @require_auth('org.example.foo.modify')
62 def _write_file(filepath,contents,sender_keyword = None):
63 f = open(filepath, 'w')
64 f.write(contents)
65 f.close()
66 return 'done'
67 return _write_file(filepath,contents,sender_keyword = sender)
68
69 @dbus.service.method(dbus_interface=INTERFACE_NAME, in_signature='s',out_signature='as',sender_keyword='sender')
70 def RunCmd(self, cmdStr, sender=None):
71 @require_auth('org.example.foo.sys')
72 def _run_cmd(cmdStr,sender_keyword = None):
73 f = os.popen(cmdStr)
74 output = f.readlines()
75 f.close()
76 return output
77 return _run_cmd(cmdStr,sender_keyword = sender)
78
79 @dbus.service.method(dbus_interface=INTERFACE_NAME,in_signature='', out_signature='',sender_keyword='sender')
80 def Exit(self, sender=None):
81 @require_auth('org.example.foo.sys')
82 def _exit(sender_keyword = None):
83 loop.quit()
84 return _exit(sender_keyword = sender)
85
86 @dbus.service.method(dbus_interface=INTERFACE_NAME,in_signature='', out_signature='s')
87 def hello(self):
88 return 'hello'
89
90 if __name__ == '__main__':
91 dbus.mainloop.glib.DBusGMainLoop(set_as_default=True)
92 bus = dbus.SystemBus()
93 name = dbus.service.BusName(PolicyKitFooMechanism.SERVICE_NAME, bus)
94 the_object = PolicyKitFooMechanism(bus)
95 loop = gobject.MainLoop()
96 loop.run()
97
98

在daemon程序中定义了三个需要权限的操作,一个不需要权限的操作,也定义了操作(方法)要求验证的action id,程序請求写文件操作时需先向org.freedesktop.PolicyKit.AuthenticationAgent 请求对应的 action id权限才能再进行调用,

否则会提示没有权限,hello操作不需要操作权限,两者区别在于来请求时是否先向 org.freedesktop.Policykit 检测这个 dbus 請求是否有 previleged。

具体是调用 IsSystemBusNameAuthorized 方法来验证,通过则返回'yes',否则 返回其它字符串

使用命令以下命令查看方法的 IsSystemBusNameAuthorized 详细 introspec

dbus-send --system --print-reply --dest=org.freedesktop.PolicyKit / org.freedesktop.DBus.Introspectable.Introspect

在这里值得注意的是如果你定义了一系列的系统级调用操作(以root方式启动前面的程序,但去除了前面的@require_auth 部分),你必须保证每个操作要进行权限验证,即加上这个东西@require_auth('org.example.foo.sys')

如果你定义了写文件的dbus操作,但是没有进行权限验证的话,一个普通用户的dbus 调用請求也会调用通过,即普通用户可以随意改写任何文件,这是很危险的

你也可以尝试把前面的@require_auth部分去掉,再启动服务,用d-feet 就可以调用WriteFile方法随意地在根目录上写入文件

--题外话——

本想将程序写成这种形式的

1 @require_auth('org.example.foo.sys')
2 @dbus.service.method(dbus_interface=INTERFACE_NAME,in_signature='', out_signature='',sender_keyword='sender')
3 def Exit(self, sender=None):
4 loop.quit()

这样写,用d-feet 看了下,服务起不来,调了很久也找不出原因,无奈写成这种冗余的方式 --!,看能否有高手指点下,不尽感激!!

接着定义谁可以调用这些操作(方法),在.conf 文件定义

org.example.foo.conf

文件放置目录:/etc/dbus-1/system.d


 1 <?xml version="1.0" encoding="UTF-8"?> <!-- -*- XML -*- -->
2
3 <!DOCTYPE busconfig PUBLIC
4 "-//freedesktop//DTD D-BUS Bus Configuration 1.0//EN"
5 "http://www.freedesktop.org/standards/dbus/1.0/busconfig.dtd">
6 <busconfig>
7
8 <!-- Only root can own the service -->
9 <policy user="root">
10 <allow own="org.example.foo"/>
11 <allow send_interface="org.example.foo"/>
12 </policy>
13
14 <!-- allow Introspectable --><!-- 任何人都可以调用,在后面使用.policy进行約束-->
15 <policy context="default">
16 <allow send_interface="org.example.foo"/>
17 <allow send_interface="org.freedesktop.DBus.Introspectable"/>
18 </policy>
19
20 </busconfig>
21

再跟着是定义相关的 action id了,在.policy 文件定义

其中定义授权认证的方式和时效可以有以下几种


no
auth_self$$
auth_admin$$
yes 其中加$$的可以附加后缀 _one_shot,_keep_session,_keep_always
其意义字面已经很清楚了

另外也可以看看 man policykit.conf

不会写?参照/usr/share/PolicyKit/policy 目录下一堆 .policy文件总会了吧

写好后可以用工具 polkit-policy-file-validate 验证下是否有效

org.example.foo.policy

文件放置目录:/usr/share/PolicyKit/policy


 1 <?xml version="1.0" encoding="UTF-8"?>
2 <!DOCTYPE policyconfig PUBLIC
3 "-//freedesktop//DTD PolicyKit Policy Configuration 1.0//EN"
4 "http://www.freedesktop.org/standards/PolicyKit/1.0/policyconfig.dtd">
5 <policyconfig>
6
7 <vendor>Example Application</vendor>
8 <vendor_url>http://fedoraproject.org/example</vendor_url>
9
10 <action id="org.example.foo.modify">
11 <description>Example Write Access</description>
12 <message>System policy prevents write access to the Example service</message>
13 <defaults>
14 <allow_inactive>no</allow_inactive>
15 <allow_active>auth_admin</allow_active>
16 </defaults>
17 </action>
18
19 <action id="org.example.foo.sys">
20 <description>Example system action</description>
21 <message>System policy prevents do system action to the Example service</message>
22 <defaults>
23 <allow_inactive>no</allow_inactive>
24 <allow_active>auth_admin</allow_active>
25 </defaults>
26 </action>
27
28
29 </policyconfig>

做好以上工作,我们可以写我们的调用端程序了


 1 #!/usr/bin/python
2 # -*- coding: UTF-8 -*-
3 import os
4 import sys
5 import gobject
6 import dbus
7 import dbus.service
8 import dbus.mainloop.glib
9 import traceback
10
11 def auth_proxy (func):
12 DBUSNAME = 'org.freedesktop.PolicyKit.AuthenticationAgent'
13 DBUSPATH = '/'
14 DBUSINTERFACE = 'org.freedesktop.PolicyKit.AuthenticationAgent'
15 EXC_NAME = "org.example.foo.dbus.service.PolKit.NotPrivilegedException"
16 def auth_proxy_wrapper (*args, **kwds):
17 try:
18 return func (*args, **kwds)
19 except dbus.DBusException, e:
20 exc_name = e.get_dbus_name ()
21 if exc_name.startswith (EXC_NAME + "."):
22 session_bus = dbus.SessionBus ()
23 auth_obj = session_bus.get_object (DBUSNAME, DBUSPATH)
24 auth_interface = dbus.Interface(auth_obj,DBUSINTERFACE)
25 action_id = exc_name[len (EXC_NAME)+1:]
26 granted = auth_interface.ObtainAuthorization (action_id, dbus.UInt32 (0),dbus.UInt32 (os.getpid ()))
27 if not granted:
28 raise
29 else:
30 raise
31
32 return func(*args, **kwds)
33 return auth_proxy_wrapper
34
35 class DbusTestProxy:
36 SERVICE_NAME = 'org.example.foo'
37 SERVICE_PATH = '/org/example/foo'
38 INTERFACE_NAME = 'org.example.foo'
39 def __init__(self):
40 self.bus = dbus.SystemBus()
41 self.o = self.bus.get_object(self.SERVICE_NAME,self.SERVICE_PATH)
42 self.i = dbus.Interface(self.o,self.INTERFACE_NAME)
43
44 @auth_proxy
45 def WriteFileWithAuth(self,filePath,contents):
46 return self.i.WriteFile(filePath,contents)
47
48 def WriteFileWithoutAuth(self,filePath,contents):
49 return self.i.WriteFile(filePath,contents)
50
51 @auth_proxy
52 def RunCmd(self,cmdStr):
53 return self.i.RunCmd(cmdStr)
54
55 @auth_proxy
56 def Exit(self):
57 return self.i.Exit()
58
59 #do not need to auth
60 def hello(self):
61 return self.i.hello()
62
63
64 if __name__ == "__main__":
65 p = DbusTestProxy()
66 #print p.RunCmd('ls -al')
67 print p.WriteFileWithAuth('/text','test\n')
68 #print p.WriteFileWithoutAuth('/text','test\n')
69 #p.Exit()
70 print p.hello()

运行上面的程序尝试WriteFileWithAuth 方法会弹出验证的对话框,口令正确的话会在根目录写入文件,调用WriteFileWithoutAuth会因为没有调用权限验证

而返回没有privileged的 异常,因为WriteFile操作是需要权限的。

以上程序相当的简单,因为我也是python新手,相信你也看得明白。

最后打个包,点击下载 policykit_dbus_foo.7z


#install
sudo ./install.sh install #remove
sudo ./install.sh uninstall #test
./policykit_dbus_foo_client.py

以上系统环境为ubuntu 8.04

Reference:

http://hal.freedesktop.org/docs/PolicyKit

Policy, Mechanism and Time zones

http://dbus.freedesktop.org/doc/dbus-specification.html

http://dbus.freedesktop.org/doc/dbus-python/doc/tutorial.html

python Decorator for functions and methods

最后发现了个开源项目 python-slip

其中 slip.dbus.polkit 部分,封装了policykit, 能使你在项目中使用policykit 更容易些.(我觉得python已经够简单的了Orz)

注:转载注明出处

http://www.cnblogs.com/joe2k8/archive/2009/05/24/1488074.html

dbus 和 policykit 实例篇(python) ()转的更多相关文章

  1. 实例解析Python设计模式编程之桥接模式的运用

    实例解析Python设计模式编程之桥接模式的运用 这篇文章主要介绍了Python设计模式编程之桥接模式的运用,桥接模式主张把抽象部分与它的实现部分分离,需要的朋友可以参考下 我们先来看一个例子: #e ...

  2. ASP.NET MVC 4 插件化架构简单实现-实例篇

    先回顾一下上篇决定的做法: 1.定义程序集搜索目录(临时目录). 2.将要使用的各种程序集(插件)复制到该目录. 3.加载临时目录中的程序集. 4.定义模板引擎的搜索路径. 5.在模板引擎的查找页面方 ...

  3. MVC 4 插件化架构简单实现实例篇

    ASP.NET MVC 4 插件化架构简单实现-实例篇   先回顾一下上篇决定的做法: 1.定义程序集搜索目录(临时目录). 2.将要使用的各种程序集(插件)复制到该目录. 3.加载临时目录中的程序集 ...

  4. Asp.Net MVC2.0 Url 路由入门---实例篇

    本篇主要讲述Routing组件的作用,以及举几个实例来学习Asp.Net MVC2.0 Url路由技术. 接着上一篇开始讲,我们在Global.asax中注册一条路由后,我们的请求是怎么转到相应的Vi ...

  5. Flex 布局教程:实例篇【转】

    Flex 布局教程:实例篇   作者: 阮一峰 日期: 2015年7月14日 原文:http://www.ruanyifeng.com/blog/2015/07/flex-examples.html ...

  6. 【0728 | 预习】第三篇 Python基础

    第三篇 Python基础预习 Part 1 变量 一.什么是变量? 二.为什么要有变量? 三.定义变量 四.变量的组成 五.变量名的命名规范 六.变量名的两种风格 Part 2 常量 Part 3 P ...

  7. 第二篇 python进阶

    目录 第二篇 python进阶 一 数字类型内置方法 二 字符串类型内置方法 三 列表类型内置方法(list) 四 元组类型内置方法(tuple) 五 字典内置方法 六 集合类型内置方法(self) ...

  8. 看完100篇Python技术精华文章,平均涨薪30%!

    一个以技术为立身根基的教育机构做出来的微信号,干货程度会有多高? 马哥Linux运维公众号运营五年,从一开始的定位就是给技术人分享加薪干货的地方.这五年里,公众号运营最重的任务就是做内容.内容并不好做 ...

  9. 第五篇python进阶之深浅拷贝

    目录 第五篇python进阶之深浅拷贝 一.引言 1.1可变 和不可变 二.拷贝(只针对可变数据类型) 三.浅拷贝 四.深拷贝 第五篇python进阶之深浅拷贝 一.引言 1.1可变 和不可变 id不 ...

随机推荐

  1. C++易vector

    很长一段时间没有动手编写C++计划.无非就是模仿后STL对,虽然达不到标准STL该程序.但简单的功能来实现. STL事实上,深刻:泛型编程.容器.算法.适配器...有的是内容能够学.以下是依据STL源 ...

  2. poj3461 Oulipo (KMP模板题~) 前面哪些也是模板题 O.O

    # include <stdio.h> # include <algorithm> # include <string.h> using namespace std ...

  3. [Windows Phone学习笔记]UserControl的使用

    UserControl的使用 开发过程中,多个UI控件需要协同工作,相互交互之后,才可完成一个完整的业务需求,此时可把这些控件封装成为一个整体,相互之间的交互逻辑封装其中,外部调用可无需关心内部逻辑, ...

  4. 明晚8点,捷微团队QQ群公开课,解说jeewx2.0版本号maven环境的搭建入门!

    2014-08-13号晚8点,捷微团队QQ群公开课,解说jeewx2.0版本号maven环境的搭建入门! 讲师:刘强(团队成员) QQ群:287090836 (JAVA版本号微信开源项目) http: ...

  5. Virtualbox mouse move in and out and file share with windows

    How to use Virstalbox to share files with Linux and Windows, and to move the mouse in and out Virtua ...

  6. IO流的总结

    流操作的基本规律: 最痛苦的就是流对象有非常多.不知道该用那一个. 通过明白来完毕. (1).明白源和目的 源:输入流InputStream Reader 目的:输出流OutputStream Wri ...

  7. python语言学习3 ——第一个python程序

    输入exit即退出,这样写的缺点是没有保存已经写的代码,下次需要重新写

  8. hdu5176(并查集)

    传送门:The Experience of Love 题意:一个叫Gorwin的女孩和一个叫Vivin的男孩是一对情侣.他们来到一个叫爱情的国家,这个国家由N个城市组成而且只有N−1条小道(像一棵树) ...

  9. java中怎么终止一个线程的执行----个人学习心得

    参考了一下两个网站的介绍: ①:http://blog.csdn.net/liuhanhan512/article/details/7077601 ②:http://www.blogjava.net/ ...

  10. 【Swift】—— 中国课程

    Apple宣布更换oc武器语言 Swift ,在牛叉的中国开发者社区9几天之内,以完成.这是福音许多中国的开发商啊,兴奋的发现之后,写存储在日志,马上防止忘记: GitHub开源项目:<The ...