Запрос на повышение прав контроля учетных записей из скрипта Python?



Я хочу, чтобы мой скрипт Python копировал файлы на Vista. Когда я запускаю его из нормального cmd.exe окно, ошибки не генерируются, но файлы не копируются. Если я бегу cmd.exe "как администратор", а затем запустить мой скрипт, он отлично работает.



это имеет смысл, так как Контроль учетных записей пользователей (UAC) обычно предотвращает многие действия файловой системы.



есть ли способ, которым я могу из скрипта Python вызвать запрос на повышение UAC (те диалоги, которые говорят что-то вроде " такой-то приложение нуждается в доступе администратора, это нормально?")



Если это невозможно, есть ли способ, которым мой скрипт может хотя бы обнаружить, что он не повышен, чтобы он мог изящно завершиться?

918   10  

10 ответов:

по состоянию на 2017 год, простой способ достичь этого заключается в следующем:

import ctypes, sys

def is_admin():
    try:
        return ctypes.windll.shell32.IsUserAnAdmin()
    except:
        return False

if is_admin():
    # Code of your program here
else:
    # Re-run the program with admin rights
    ctypes.windll.shell32.ShellExecuteW(None, "runas", sys.executable, __file__, None, 1)

если вы используете Python 2.x, то вы должны заменить последнюю строку для:

ctypes.windll.shell32.ShellExecuteW(None, u"runas", unicode(sys.executable), unicode(__file__), None, 1)

также обратите внимание, что если вы преобразовали скрипт python в исполняемый файл (используя такие инструменты, как py2exe,cx_freeze,pyinstaller) затем вы должны заменить четвертый параметр для пустой строки ("").

некоторые из преимуществ здесь несколько:

  • никаких внешних библиотек требуется (ни Python для расширения Windows). Он использует только ctypes из стандартной библиотеки.
  • работает как на Python 2, так и на Python 3.
  • нет необходимости изменять файловые ресурсы или создавать файл манифеста.
  • если вы не добавляете код ниже оператора if/else, код никогда не будет выполняться дважды.
  • вы можете легко изменить его, чтобы иметь специальное поведение, если пользователь отклоняет приглашение UAC.
  • вы можете указать аргументы изменение четвертого параметра.
  • можно указать метод отображения, изменяющий шестой параметр.

документация для базового вызова ShellExecute является здесь.

Мне потребовалось некоторое время, чтобы получить ответ dguaraglia, поэтому в интересах экономии времени других, вот что я сделал, чтобы реализовать эту идею:

import os
import sys
import win32com.shell.shell as shell
ASADMIN = 'asadmin'

if sys.argv[-1] != ASADMIN:
    script = os.path.abspath(sys.argv[0])
    params = ' '.join([script] + sys.argv[1:] + [ASADMIN])
    shell.ShellExecuteEx(lpVerb='runas', lpFile=sys.executable, lpParameters=params)
    sys.exit(0)

кажется, что нет никакого способа повысить привилегии приложения на некоторое время для выполнения конкретной задачи. Windows должна знать при запуске программы, требует ли приложение определенных привилегий, и попросит пользователя подтвердить, когда приложение выполняет любые задачи, которые нужно эти привилегии. Есть два способа сделать это:

  1. напишите файл манифеста, который сообщает Windows, что приложение может потребовать некоторых привилегии
  2. запустите приложение с повышенными привилегиями из другой программы

этой двастатьи объясните более подробно как это работает.

Что бы я сделал, если вы не хотите писать неприятную оболочку ctypes для API CreateElevatedProcess, используйте трюк ShellExecuteEx, описанный в статье проекта кода (Pywin32 поставляется с оболочкой для ShellExecute). Как? Что-то вроде это:

когда ваша программа запускается, она проверяет, есть ли у нее права администратора, если это не так, она запускается сама с помощью трюка ShellExecute и немедленно завершает работу, если это так, она выполняет задачу под рукой.

поскольку вы описываете свою программу как" сценарий", я полагаю, что этого достаточно для ваших нужд.

Ура.

признавая, что этот вопрос был задан много лет назад, я думаю, что более элегантное решение предлагается на github по frmdstryr с помощью своего модуля pyminutils:

выдержка:

import pythoncom
from win32com.shell import shell,shellcon

def copy(src,dst,flags=shellcon.FOF_NOCONFIRMATION):
    """ Copy files using the built in Windows File copy dialog

    Requires absolute paths. Does NOT create root destination folder if it doesn't exist.
    Overwrites and is recursive by default 
    @see http://msdn.microsoft.com/en-us/library/bb775799(v=vs.85).aspx for flags available
    """
    # @see IFileOperation
    pfo = pythoncom.CoCreateInstance(shell.CLSID_FileOperation,None,pythoncom.CLSCTX_ALL,shell.IID_IFileOperation)

    # Respond with Yes to All for any dialog
    # @see http://msdn.microsoft.com/en-us/library/bb775799(v=vs.85).aspx
    pfo.SetOperationFlags(flags)

    # Set the destionation folder
    dst = shell.SHCreateItemFromParsingName(dst,None,shell.IID_IShellItem)

    if type(src) not in (tuple,list):
        src = (src,)

    for f in src:
        item = shell.SHCreateItemFromParsingName(f,None,shell.IID_IShellItem)
        pfo.CopyItem(item,dst) # Schedule an operation to be performed

    # @see http://msdn.microsoft.com/en-us/library/bb775780(v=vs.85).aspx
    success = pfo.PerformOperations()

    # @see sdn.microsoft.com/en-us/library/bb775769(v=vs.85).aspx
    aborted = pfo.GetAnyOperationsAborted()
    return success is None and not aborted    

Это использует интерфейс COM и автоматически указывает, что права администратора необходимы с помощью знакомого диалогового окна, которое вы увидите, если вы копируете в каталог, где требуются права администратора, а также предоставляет типичный диалог хода выполнения файла во время операция копирования.

Это может не полностью ответить на ваш вопрос, но вы также можете попробовать использовать команду Elevate Powertoy для запуска сценария с повышенными привилегиями UAC.

http://technet.microsoft.com/en-us/magazine/2008.06.elevation.aspx

Я думаю, что если вы используете его, это будет выглядеть как ' elevate python yourscript.py -

следующий пример основан на Мартин де ла Фуэнте Сааведра отличная работа и принято отвечать. В частности, вводятся два перечисления. Первый позволяет легко определить, как должна быть открыта повышенная программа, а второй помогает, когда ошибки должны быть легко идентифицированы. Обратите внимание, что если вы хотите, чтобы все аргументы командной строки передавались новому процессу,sys.argv[0] вероятно, следует заменить вызовом функции:subprocess.list2cmdline(sys.argv).

#! /usr/bin/env python3
import ctypes
import enum
import sys


# Reference:
# msdn.microsoft.com/en-us/library/windows/desktop/bb762153(v=vs.85).aspx


class SW(enum.IntEnum):

    HIDE = 0
    MAXIMIZE = 3
    MINIMIZE = 6
    RESTORE = 9
    SHOW = 5
    SHOWDEFAULT = 10
    SHOWMAXIMIZED = 3
    SHOWMINIMIZED = 2
    SHOWMINNOACTIVE = 7
    SHOWNA = 8
    SHOWNOACTIVATE = 4
    SHOWNORMAL = 1


class ERROR(enum.IntEnum):

    ZERO = 0
    FILE_NOT_FOUND = 2
    PATH_NOT_FOUND = 3
    BAD_FORMAT = 11
    ACCESS_DENIED = 5
    ASSOC_INCOMPLETE = 27
    DDE_BUSY = 30
    DDE_FAIL = 29
    DDE_TIMEOUT = 28
    DLL_NOT_FOUND = 32
    NO_ASSOC = 31
    OOM = 8
    SHARE = 26


def bootstrap():
    if ctypes.windll.shell32.IsUserAnAdmin():
        main()
    else:
        hinstance = ctypes.windll.shell32.ShellExecuteW(
            None, 'runas', sys.executable, sys.argv[0], None, SW.SHOWNORMAL
        )
        if hinstance <= 32:
            raise RuntimeError(ERROR(hinstance))


def main():
    # Your Code Here
    print(input('Echo: '))


if __name__ == '__main__':
    bootstrap()

Если ваш скрипт всегда требует прав администратора, то:

runas /user:Administrator "python your_script.py"

вы можете сделать ярлык где-то и в качестве целевого использования: python yourscript.py затем в разделе свойства и дополнительно выберите Запуск от имени администратора.

когда пользователь выполняет ярлык, он попросит их поднять приложение.

вариант работы Джоренко выше позволяет повышенному процессу использовать ту же консоль (но см. Мой комментарий ниже):

def spawn_as_administrator():
    """ Spawn ourself with administrator rights and wait for new process to exit
        Make the new process use the same console as the old one.
          Raise Exception() if we could not get a handle for the new re-run the process
          Raise pywintypes.error() if we could not re-spawn
        Return the exit code of the new process,
          or return None if already running the second admin process. """
    #pylint: disable=no-name-in-module,import-error
    import win32event, win32api, win32process
    import win32com.shell.shell as shell
    if '--admin' in sys.argv:
        return None
    script = os.path.abspath(sys.argv[0])
    params = ' '.join([script] + sys.argv[1:] + ['--admin'])
    SEE_MASK_NO_CONSOLE = 0x00008000
    SEE_MASK_NOCLOSE_PROCESS = 0x00000040
    process = shell.ShellExecuteEx(lpVerb='runas', lpFile=sys.executable, lpParameters=params, fMask=SEE_MASK_NO_CONSOLE|SEE_MASK_NOCLOSE_PROCESS)
    hProcess = process['hProcess']
    if not hProcess:
        raise Exception("Could not identify administrator process to install drivers")
    # It is necessary to wait for the elevated process or else
    #  stdin lines are shared between 2 processes: they get one line each
    INFINITE = -1
    win32event.WaitForSingleObject(hProcess, INFINITE)
    exitcode = win32process.GetExitCodeProcess(hProcess)
    win32api.CloseHandle(hProcess)
    return exitcode

это в основном обновление ответа Jorenko, которое позволяет использовать параметры с пробелами в Windows, но также должно работать довольно хорошо на Linux :) Кроме того, будет работать с cx_freeze или py2exe, так как мы не используем __file__ но sys.argv[0] как исполняемый

import sys,ctypes,platform

def is_admin():
    try:
        return ctypes.windll.shell32.IsUserAnAdmin()
    except:
        raise False

if __name__ == '__main__':

    if platform.system() == "Windows":
        if is_admin():
            main(sys.argv[1:])
        else:
            # Re-run the program with admin rights, don't use __file__ since py2exe won't know about it
            # Use sys.argv[0] as script path and sys.argv[1:] as arguments, join them as lpstr, quoting each parameter or spaces will divide parameters
            lpParameters = ""
            # Litteraly quote all parameters which get unquoted when passed to python
            for i, item in enumerate(sys.argv[0:]):
                lpParameters += '"' + item + '" '
            try:
                ctypes.windll.shell32.ShellExecuteW(None, "runas", sys.executable, lpParameters , None, 1)
            except:
                sys.exit(1)
    else:
        main(sys.argv[1:])

Comments

    Ничего не найдено.