Skip to content

Commit

Permalink
Finished release v0.1.7.
Browse files Browse the repository at this point in the history
* release/v0.1.7:
  fixing the recursion in a different way
  Revert "patching subprocess can yield to maximum recursion depth"
  • Loading branch information
grzn committed May 18, 2014
2 parents 98e5c00 + 31e5848 commit 67a7632
Show file tree
Hide file tree
Showing 2 changed files with 27 additions and 13 deletions.
33 changes: 20 additions & 13 deletions src/infi/run_as/__init__.py
@@ -1,33 +1,34 @@
__import__("pkg_resources").declare_namespace(__name__)

from infi.winver import Windows
from infi.pyutils.contexts import contextmanager
from mock import patch, MagicMock
from logging import getLogger
from os import environ, path
from sys import argv, stderr, stdout, exit
from .c_api import Environment, StartupInfoW, ProcessInformation, CreateProcessWithLogonW, WaitForInputIdle, Handle
from .c_api import create_buffer, create_unicode_buffer, Ctypes, get_token, CreateProcessAsUserW, INFINITE


logger = getLogger(__name__)


class CreateProcess(object):
def __init__(self, username, password):
super(CreateProcess, self).__init__()
self.username = username
self.password = password
self.method = self._CreateProcessWithLogon if \
self._can_we_user_create_process_with_login() else \
self._CreateProcessAsUser

def _can_we_user_create_process_with_login(self):
return Windows().is_windows_2003() and environ.get('USERNAME', 'SYSTEM') == 'SYSTEM'

def create_process_as_administrator(self, *args, **kwargs):
""" A drop-in replacement for _subprocess.CreateProcess that creates the process as an administrator
:returns: a 4-tuple (proc_handle, thread_handle, pid, tid)"""
if Windows().is_windows_2003() and environ.get('USERNAME', 'SYSTEM') == 'SYSTEM':
# http://msdn.microsoft.com/en-us/library/windows/desktop/ms682431(v=vs.85).aspx
# Windows XP with SP2 and Windows Server 2003:
# You cannot call CreateProcessWithLogonW from a process that is running under the "LocalSystem" account,
# because the function uses the logon SID in the caller token,
# and the token for the "LocalSystem" account does not contain this SID.
# As an alternative, use the CreateProcessAsUser and LogonUser functions.
return self._CreateProcessAsUser(*args, **kwargs)
return self._CreateProcessWithLogon(*args, **kwargs)
return self.method(*args, **kwargs)

def _CreateProcessWithLogon(self, app_name, cmd_line, proc_attrs, thread_attrs, inherit,
flags, env_mapping, curdir, startup_info):
Expand Down Expand Up @@ -87,12 +88,18 @@ def _CreateProcessAsUser(self, app_name, cmd_line, proc_attrs, thread_attrs, inh
return result


@contextmanager
def subprocess_runas_context(username, password):
side_effect = CreateProcess(username, password).create_process_as_administrator
with patch("_subprocess.CreateProcess", new=side_effect):
yield


def run_as(argv=argv[1:]):
from infi.execute.runner import LocalRunner
from infi.execute import execute
username, password, args = argv[0], argv[1], argv[2:]
runner = LocalRunner()
runner.popen = CreateProcess(username, password).create_process_as_administrator
pid = runner.execute(args)
with subprocess_runas_context(username, password):
pid = execute(args)
stdout.write(pid.get_stdout())
stdout.flush()
stderr.write(pid.get_stderr())
Expand Down
7 changes: 7 additions & 0 deletions tests/test__run_as.py
Expand Up @@ -3,6 +3,13 @@
from os import name, environ

class RunAsTestCase(TestCase):
def test__on_unix_with_mock(self):
if name == 'nt':
raise SkipTest("unix only")
with patch("infi.run_as.subprocess_runas_context"):
from infi.run_as import run_as
self.assertEquals(0, run_as(["Administrator", "Password", "ifconfig"]))

def test__on_windows(self):
from infi.run_as import run_as
if name != 'nt':
Expand Down

0 comments on commit 67a7632

Please sign in to comment.