如何&在基于 macOS Python 的应用程序上通过本机 GUI 检索 sudo 密码的最佳位置 -(同时维护交互式输出流 (stdout)) [英] How & where to best retrieve sudo password via a native GUI on a macOS Python-based app - (while maintaining an interactive output stream (stdout))

查看:13
本文介绍了如何&在基于 macOS Python 的应用程序上通过本机 GUI 检索 sudo 密码的最佳位置 -(同时维护交互式输出流 (stdout))的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

好的,所以情况是这样的:我正在使用 Python 和 wx (wxphoenix) 构建一个 macOS GUI 应用程序.用户可以使用 GUI(例如:script1)启动文件删除过程(包含在 script2 中).为了成功运行 script2 需要以 sudo 权限运行.

Ok, so the situation is this: I am building a macOS GUI App using Python and wx (wxphoenix). The user can use the GUI (say: script1) to launch a file-deletion process (contained in script2). In order to run successfully script2 needs to run with sudo rights.

script2 将遍历一长串文件并删除它们.但是我需要它在每一轮之后与 script1 中包含的 GUI 进行通信,以便 script1 可以更新进度条.

script2 will itterate over a long list of files and delete them. But I need it to communicate with the GUI contained in script1 after each round so that script1 can update the progressbar.

在绝对最基本的形式中,我当前的工作设置如下所示:

In it's absolute most basic form my current working setup looks like this:

脚本 1:

import io
from threading import Thread
import subprocess

import wx

# a whole lot of wx GUI stuff 

def get_password():
    """Retrieve user password via a GUI"""

    # A wx solution using wx.PasswordEntryDialog()
    # Store password in a variable

    return variable

class run_script_with_sudo(Thread):
    """Launch a script with administrator privileges"""

    def __init__(self, path_to_script, wx_pubsub_sendmessage):
        """Set variables to self"""
        self.path = path_to_script
        self.sender = wx_pubsub_sendmessage
        self.password = get_password()
        Thread.__init__(self)
        self.start()

    def run(self):
        """Run thread"""

        prepare_script = subprocess.Popen(["echo", password], stdout=subprocess.PIPE)
        prepare_script.wait()
        launch_script = subprocess.Popen(['sudo', '-S', '/usr/local/bin/python3.6', '-u', self.path], stdin=prepare_script.stdout, stdout=subprocess.PIPE)
        for line in io.TextIOWrapper(launch_script.stdout, encoding="utf-8"):
            print("Received line: ", line.rstrip())
            # Tell progressbar to add another step:
            wx.CallAfter(self.sender, "update", msg="")

脚本 2:

import time

# This is a test setup, just a very simple loop that produces an output.

for i in range(25):
    time.sleep(1)
    print(i)

上述设置的工作原理是 script1 实时接收 script2 的输出并对其进行操作.(因此在给定的示例中:每秒钟 script1 向进度条添加一个步骤,直到达到 25 个步骤).

The above setup works in that script1 receives the output of script2 in real-time and acts on it. (So in the given example: after each second script1 adds another step to the progress bar until it reaches 25 steps).

我想要实现的目标 = 不将密码存储在变量中,而是使用 macOS 的本机 GUI 来检索密码.

What I want to achieve = not storing the password in a variable and using macOS it's native GUI to retrieve the password.

但是当我改变时:

prepare_script = subprocess.Popen(["echo", password], stdout=subprocess.PIPE)
prepare_script.wait()
launch_script = subprocess.Popen(['sudo', '-S', '/usr/local/bin/python3.6', '-u', self.path], stdin=prepare_script.stdout, stdout=subprocess.PIPE)
for line in io.TextIOWrapper(launch_script.stdout, encoding="utf-8"):
                print("Received line: ", line.rstrip())
                # Tell progressbar to add another step:
                wx.CallAfter(self.sender, "update", msg="")

进入:

command = r"""/usr/bin/osascript -e 'do shell script "/usr/local/bin/python3.6 -u """ + self.path + """ with prompt "Sart Deletion Process " with administrator privileges'"""
command_list = shlex.split(command)

launch_script = subprocess.Popen(command_list, stdout=subprocess.PIPE)
for line in io.TextIOWrapper(launch_script.stdout, encoding="utf-8"):
    print("Received line: ", line.rstrip())
    # Tell progressbar to add another step:
    wx.CallAfter(self.sender, "update", msg="")

它停止工作是因为 osascript 显然在非交互式 shell 中运行.这意味着 script2 在完全完成之前不会发送任何输出,从而导致 script1 中的进度条停滞.

It stops working because osascript apparently runs in a non-interactive shell. This means script2 doesn't sent any output until it is fully finished, causing the progress bar in script1 to stall.

我的问题变成了:如何确保使用 macOS 本机 GUI 来询问 sudo 密码,从而避免必须将其存储在变量中,同时仍然保持捕获该密码的可能性交互式/实时流中特权脚本的标准输出.

My question thus becomes: How can I make sure to use macOS native GUI to ask for the sudo password, thus preventing having to store it in a variable, while still maintaining the possibility to catch the stdout from the privileged script in an interactive / real-time stream.

希望这是有道理的.

希望得到任何见解!

推荐答案

我的问题就变成了:如何确保使用 macOS 原生 GUI要求提供 sudo 密码,从而避免必须将其存储在变量,同时仍然保持捕获标准输出的可能性来自交互式/实时流中的特权脚本.

My question thus becomes: How can I make sure to use macOS native GUI to ask for the sudo password, thus preventing having to store it in a variable, while still maintaining the possibility to catch the stdout from the privileged script in an interactive / real-time stream.

我自己找到了一个解决方案,使用了命名管道(os.mkfifo()).

I have found a solution myself, using a named pipe (os.mkfifo()).

这样,您可以让 2 个 Python 脚本相互通信,而其中 1 个通过 osascript 以特权权限启动(意思是:您将获得一个要求用户输入 sudo 密码的本机 GUI 窗口).

That way, you can have 2 python scripts communicate with each other while 1 of them is launched with privileged rights via osascript (meaning: you get a native GUI window that asks for the users sudo password).

可行的解决方案:

mainscript.py

import os
from pathlib import Path
import shlex
import subprocess
import sys
from threading import Thread
import time

class LaunchDeletionProcess(Thread):

    def __init__(self):

        Thread.__init__(self)

    def run(self):

        launch_command = r"""/usr/bin/osascript -e 'do shell script "/usr/local/bin/python3.6 -u /path/to/priviliged_script.py" with prompt "Sart Deletion Process " with administrator privileges'"""
        split_command = shlex.split(launch_command)

        print("Thread 1 started")
        testprogram = subprocess.Popen(split_command)
        testprogram.wait()
        print("Thread1 Finished")

class ReadStatus(Thread):

    def __init__(self):

        Thread.__init__(self)

    def run(self):

        while not os.path.exists(os.path.expanduser("~/p1")):
            time.sleep(0.1)

        print("Thread 2 started")

        self.wfPath = os.path.expanduser("~/p1")

        rp = open(self.wfPath, 'r')
        response = rp.read()

        self.try_pipe(response)

    def try_pipe(self, response):
        rp = open(self.wfPath, 'r')
        response = rp.read()
        print("Receiving response: ", response)
        rp.close()
        if response == str(self.nr_of_steps-1):
            print("Got to end")
            os.remove(os.path.expanduser("~/p1"))
        else:
            time.sleep(1)
            self.try_pipe(response)

if __name__ == "__main__":

    thread1 = LaunchDeletionProcess()
    thread2 = ReadStatus()
    thread1.start()
    thread2.start()

priviliged_script.py

import os
import time
import random

wfPath = os.path.expanduser("~/p1")

try:

    os.mkfifo(wfPath)

except OSError:

    print("error")
    pass

result = 10

nr = 0 

while nr < result:

    random_nr = random.random()

    wp = open(wfPath, 'w')
    print("writing new number: ", random_nr)
    wp.write("Number: " + str(random_nr))       
    wp.close()

    time.sleep(1)
    nr += 1

wp = open(wfPath, 'w')
wp.write("end")     
wp.close()

这篇关于如何&amp;在基于 macOS Python 的应用程序上通过本机 GUI 检索 sudo 密码的最佳位置 -(同时维护交互式输出流 (stdout))的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆