You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何通过PolicyKit获取权限实现PyQt5程序的文件写入操作

PyQt5 + DBus + PolicyKit 无库提权读写文件实现

一、先理清PolicyKit配置文件的作用

你之前放置的三个文件各司其职:

  • .policy:定义权限动作规则,告诉PolicyKit你的程序需要什么权限、触发验证的条件,放在/usr/share/polkit-1/actions/
  • .conf:自定义授权规则(比如免密码),优先级高于系统默认规则,放在/etc/polkit-1/localauthority/50-local.d/
  • .service:DBus系统服务定义,指定以root身份运行的特权程序路径和服务名,放在/usr/share/dbus-1/system-services/

配置文件示例

1. 权限动作定义(com.example.FileWriter.policy)

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE policyconfig PUBLIC
 "-//freedesktop//DTD PolicyKit Policy Configuration 1.0//EN"
 "http://www.freedesktop.org/standards/PolicyKit/1/policyconfig.dtd">
<policyconfig>
  <action id="com.example.FileWriter.write">
    <description>写入受保护文件</description>
    <message>需要管理员权限才能写入该文件</message>
    <defaults>
      <allow_any>auth_admin</allow_any>
      <allow_inactive>auth_admin</allow_inactive>
      <allow_active>auth_admin_keep</allow_active>
    </defaults>
  </action>
</policyconfig>
  • auth_admin:需要管理员密码验证;auth_admin_keep:验证一次后保持权限一段时间
  • 将文件放到/usr/share/polkit-1/actions/,权限设为644

2. DBus系统服务定义(com.example.FileWriter.service)

[D-BUS Service]
Name=com.example.FileWriter
Exec=/usr/bin/example-file-writer
User=root
SystemdService=example-file-writer.service
  • Name:DBus服务的唯一标识;Exec:特权操作程序的绝对路径;User=root:以root身份运行

3. 可选免密码授权规则(50-example-file-writer.conf)

如果需要特定用户免密码执行,创建这个文件:

[com.example.FileWriter.write]
Identity=unix-user:your_username
Action=com.example.FileWriter.write
ResultAny=yes
ResultInactive=yes
ResultActive=yes

替换your_username为实际用户名,放到/etc/polkit-1/localauthority/50-local.d/

二、编写特权操作程序

不能让PyQt5主程序直接提权,必须把需要root权限的操作(写文件)封装成独立的DBus服务程序,以root身份运行。

创建/usr/bin/example-file-writer:

#!/usr/bin/env python3
import dbus
import dbus.service
from dbus.mainloop.glib import DBusGMainLoop
import os
from gi.repository import GLib

class FileWriterService(dbus.service.Object):
    def __init__(self):
        bus_name = dbus.service.BusName('com.example.FileWriter', dbus.SystemBus())
        super().__init__(bus_name, '/com/example/FileWriter')

    @dbus.service.method(
        'com.example.FileWriter',
        in_signature='ss',
        out_signature='b',
        sender_keyword='sender'
    )
    def WriteToFile(self, file_path, content, sender=None):
        try:
            with open(file_path, 'w', encoding='utf-8') as f:
                f.write(content)
            return True
        except Exception as e:
            print(f"写入失败: {str(e)}", file=os.stderr)
            return False

if __name__ == '__main__':
    DBusGMainLoop(set_as_default=True)
    service = FileWriterService()
    loop = GLib.MainLoop()
    loop.run()

给文件加执行权限:sudo chmod +x /usr/bin/example-file-writer

用systemd管理特权服务(可选)

创建/etc/systemd/system/example-file-writer.service:

[Unit]
Description=文件写入特权DBus服务
After=dbus.service

[Service]
Type=dbus
BusName=com.example.FileWriter
ExecStart=/usr/bin/example-file-writer
User=root
Restart=on-failure

[Install]
WantedBy=multi-user.target

启用并启动服务:

sudo systemctl daemon-reload
sudo systemctl enable --now example-file-writer.service

三、PyQt5主程序整合逻辑

主程序需要完成:检查本地权限 → 无权限则请求PolicyKit授权 → 授权成功调用特权DBus服务。

核心代码片段

from PyQt5.QtWidgets import QApplication, QMainWindow, QPushButton, QMessageBox
import os
import dbus

def has_write_permission(file_path):
    """检查当前用户是否有文件写入权限"""
    return os.path.exists(file_path) and os.access(file_path, os.W_OK)

def request_policykit_auth(action_id):
    """通过DBus调用PolicyKit请求权限"""
    try:
        bus = dbus.SystemBus()
        polkit_auth = bus.get_object(
            'org.freedesktop.PolicyKit1',
            '/org/freedesktop/PolicyKit1/Authority'
        )
        auth_interface = dbus.Interface(
            polkit_auth,
            'org.freedesktop.PolicyKit1.Authority'
        )
        # 构造进程主题,标识当前程序
        subject = ('unix-process', {
            'pid': dbus.UInt32(os.getpid()),
            'start-time': dbus.UInt64(0)
        })
        # 请求授权,允许弹窗交互
        result = auth_interface.CheckAuthorization(
            subject, action_id, {}, dbus.UInt32(1), ''
        )
        return result[0]
    except dbus.DBusException as e:
        print(f"PolicyKit请求失败: {str(e)}")
        return False

def call_privileged_write(file_path, content):
    """调用DBus特权服务写入文件"""
    try:
        bus = dbus.SystemBus()
        service = bus.get_object('com.example.FileWriter', '/com/example/FileWriter')
        write_interface = dbus.Interface(service, 'com.example.FileWriter')
        return write_interface.WriteToFile(file_path, content)
    except dbus.DBusException as e:
        print(f"特权服务调用失败: {str(e)}")
        return False

class MainWindow(QMainWindow):
    def __init__(self):
        super().__init__()
        self.init_ui()

    def init_ui(self):
        self.setWindowTitle("特权文件写入工具")
        self.write_btn = QPushButton("写入受保护文件", self)
        self.write_btn.clicked.connect(self.handle_write)
        self.setCentralWidget(self.write_btn)
        self.resize(300, 100)

    def handle_write(self):
        target_file = '/etc/test_privileged.txt'
        content = "PyQt5通过PolicyKit提权写入的内容\n"

        if has_write_permission(target_file):
            # 直接写入
            try:
                with open(target_file, 'a', encoding='utf-8') as f:
                    f.write(content)
                QMessageBox.information(self, "成功", "文件写入完成")
            except Exception as e:
                QMessageBox.critical(self, "失败", f"写入错误: {str(e)}")
        else:
            # 请求提权
            if request_policykit_auth('com.example.FileWriter.write'):
                if call_privileged_write(target_file, content):
                    QMessageBox.information(self, "成功", "已通过提权写入文件")
                else:
                    QMessageBox.critical(self, "失败", "特权写入操作失败")
            else:
                QMessageBox.critical(self, "失败", "权限验证未通过")

if __name__ == '__main__':
    app = QApplication([])
    window = MainWindow()
    window.show()
    app.exec_()

四、部署验证

  1. 把所有配置文件放到对应目录,重启DBus:sudo systemctl restart dbus
  2. 启动特权服务(如果用systemd)
  3. 运行PyQt5程序,点击写入按钮,无权限时会弹出PolicyKit验证窗口,输入密码后即可完成写入

内容的提问来源于stack exchange,提问作者omen

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.05 15:19:57