如何在我的Python应用程序中弹出特权提升对话框?我想要Windows上的UAC对话框和Mac上的密码验证对话框。
基本上,我需要部分应用程序的root权限,我需要通过GUI获得这些权限。我正在使用wxPython。有什么想法吗?
答案 0 :(得分:4)
在Windows上,如果不启动新进程,就无法获得UAC对话框,甚至无法使用CreateProcess启动该进程。
可以通过运行具有相应清单文件的另一个应用程序来实现UAC对话框 - 有关如何使用py2exe执行此操作的示例,请参阅Running compiled python (py2exe) as administrator in Vista。
你也可以使用win32 api ShellExecute http://msdn.microsoft.com/en-us/library/bb762153(v=vs.85).aspx以编程方式使用runas动词 - 你可以使用ctypes http://python.net/crew/theller/ctypes/来调用它,它是python 2.5+ iirc上标准库的一部分。
抱歉不了解Mac。如果您提供有关您希望在Windows上完成的内容的更多详细信息,我可以提供更具体的帮助。
答案 1 :(得分:2)
我知道帖子有点旧,但我写了以下内容作为我的问题的解决方案(在Linux和OS X上以root身份运行python脚本)。
我编写了以下bash脚本来执行具有管理员权限的bash / python脚本(适用于Linux和OS X系统):
#!/bin/bash
if [ -z "$1" ]; then
echo "Specify executable"
exit 1
fi
EXE=$1
available(){
which $1 >/dev/null 2>&1
}
platform=`uname`
if [ "$platform" == "Darwin" ]; then
MESSAGE="Please run $1 as root with sudo or install osascript (should be installed by default)"
else
MESSAGE="Please run $1 as root with sudo or install gksu / kdesudo!"
fi
if [ `whoami` != "root" ]; then
if [ "$platform" == "Darwin" ]; then
# Apple
if available osascript
then
SUDO=`which osascript`
fi
else # assume Linux
# choose either gksudo or kdesudo
# if both are avilable check whoch desktop is running
if available gksudo
then
SUDO=`which gksudo`
fi
if available kdesudo
then
SUDO=`which kdesudo`
fi
if ( available gksudo && available kdesudo )
then
if [ $XDG_CURRENT_DESKTOP = "KDE" ]; then
SUDO=`which kdesudo`;
else
SUDO=`which gksudo`
fi
fi
# prefer polkit if available
if available pkexec
then
SUDO=`which pkexec`
fi
fi
if [ -z $SUDO ]; then
if available zenity; then
zenity --info --text "$MESSAGE"
exit 0
elif available notify-send; then
notify-send "$MESSAGE"
exit 0
elif available xmessage notify-send; then
xmessage -buttons Ok:0 "$MESSAGE"
exit 0
else
echo "$MESSAGE"
fi
fi
fi
if [ "$platform" == "Darwin" ]
then
$SUDO -e "do shell script \"$*\" with administrator privileges"
else
$SUDO $@
fi
基本上,我设置系统的方式是将子文件夹保存在bin目录中(例如/ usr / local / bin中的/ usr / local / bin / pyscripts),并创建指向可执行文件的符号链接。这对我有三个好处:
(1)如果我有不同的版本,我可以通过更改符号链接轻松切换执行哪个版本,并使bin目录保持清洁(例如/usr/local/bin/gcc-versions/4.9/,/ usr /local/bin/gcc-versions/4.8/,/ usr / local / bin / gcc - > gcc-versions / 4.8 / gcc)
(2)我可以使用它们的扩展名存储脚本(有助于IDE中的语法高亮显示),但可执行文件不包含它们,因为我喜欢它(例如svn-tools - > pyscripts / svn-tools的.py)
(3)我将在下面展示的原因:
我将脚本命名为“run-as-root-wrapper”并将其放在一个非常常见的路径中(例如/ usr / local / bin),因此python不需要任何特殊的东西来定位它。然后我有以下run_command.py模块:
import os
import sys
from distutils.spawn import find_executable
#===========================================================================#
def wrap_to_run_as_root(exe_install_path, true_command, expand_path = True):
run_as_root_path = find_executable("run-as-root-wrapper")
if(not run_as_root_path):
return False
else:
if(os.path.exists(exe_install_path)):
os.unlink(exe_install_path)
if(expand_path):
true_command = os.path.realpath(true_command)
true_command = os.path.abspath(true_command)
true_command = os.path.normpath(true_command)
f = open(exe_install_path, 'w')
f.write("#!/bin/bash\n\n")
f.write(run_as_root_path + " " + true_command + " $@\n\n")
f.close()
os.chmod(exe_install_path, 0755)
return True
在我的实际python脚本中,我有以下功能:
def install_cmd(args):
exe_install_path = os.path.join(args.prefix,
os.path.join("bin", args.name))
if(not run_command.wrap_to_run_as_root(exe_install_path, sys.argv[0])):
os.symlink(os.path.realpath(sys.argv[0]), exe_install_path)
因此,如果我有一个名为TrackingBlocker.py的脚本(我用来修改/ etc / hosts文件以将已知跟踪域重新路由到127.0.0.1的实际脚本),当我调用“sudo / usr / local / bin”时/pyscripts/TrackingBlocker.py --prefix / usr / local --name ModifyTrackingBlocker install“(通过argparse模块处理的参数),它安装”/ usr / local / bin / ModifyTrackingBlocker“,这是一个执行的bash脚本
/usr/local/bin/run-as-root-wrapper /usr/local/bin/pyscripts/TrackingBlocker.py [args]
e.g。
ModifyTrackingBlocker add tracker.ads.com
执行:
/usr/local/bin/run-as-root-wrapper /usr/local/bin/pyscripts/TrackingBlocker.py add tracker.ads.com
然后显示获取添加权限所需的身份验证对话框:
127.0.0.1 tracker.ads.com
到我的hosts文件(只能由超级用户写入)。
如果您想简化/修改它以仅以root身份运行某些命令,您只需将其添加到您的脚本中(使用上面提到的必要导入+ import subprocess):
def run_as_root(command, args, expand_path = True):
run_as_root_path = find_executable("run-as-root-wrapper")
if(not run_as_root_path):
return 1
else:
if(expand_path):
command = os.path.realpath(command)
command = os.path.abspath(command)
command = os.path.normpath(command)
cmd = []
cmd.append(run_as_root_path)
cmd.append(command)
cmd.extend(args)
return subprocess.call(' '.join(cmd), shell=True)
使用上面的(在run_command模块中):
>>> ret = run_command.run_as_root("/usr/local/bin/pyscripts/TrackingBlocker.py", ["status", "display"])
>>> /etc/hosts is blocking approximately 16147 domains
答案 2 :(得分:1)
我在Mac OS X上遇到同样的问题。我有一个可行的解决方案,但它并不是最佳的。我将在这里解释我的解决方案并继续寻找更好的解决方案。
在程序开始时,我通过执行
检查我是否是rootdef _elevate():
"""Elevate user permissions if needed"""
if platform.system() == 'Darwin':
try:
os.setuid(0)
except OSError:
_mac_elevate()
如果我还不是root用户,那么os.setuid(0)将会失败,并且会触发_mac_elevate(),它会在管理员的帮助下从osascript开始重新启动我的程序。 osascript可用于执行applescript和其他东西。我这样用它:
def _mac_elevate():
"""Relaunch asking for root privileges."""
print "Relaunching with root permissions"
applescript = ('do shell script "./my_program" '
'with administrator privileges')
exit_code = subprocess.call(['osascript', '-e', applescript])
sys.exit(exit_code)
这个问题是如果我使用subprocess.call,我保持当前进程运行,我的应用程序运行的两个实例将提供两个停靠图标。如果我改为使用subprocess.Popen并让非特权进程立即死亡,我就无法使用退出代码,也无法获取stdout / stderr流并从原始进程传播到终端。