pypsrp 0.8.0rc1__tar.gz → 0.9.0rc1__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (121) hide show
  1. {pypsrp-0.8.0rc1 → pypsrp-0.9.0rc1}/CHANGELOG.md +16 -1
  2. {pypsrp-0.8.0rc1/src/pypsrp.egg-info → pypsrp-0.9.0rc1}/PKG-INFO +59 -28
  3. {pypsrp-0.8.0rc1 → pypsrp-0.9.0rc1}/README.md +27 -13
  4. {pypsrp-0.8.0rc1 → pypsrp-0.9.0rc1}/build_helpers/lib.sh +7 -14
  5. {pypsrp-0.8.0rc1 → pypsrp-0.9.0rc1}/build_helpers/win-setup.ps1 +97 -75
  6. pypsrp-0.9.0rc1/pyproject.toml +110 -0
  7. pypsrp-0.9.0rc1/setup.cfg +4 -0
  8. {pypsrp-0.8.0rc1 → pypsrp-0.9.0rc1}/src/pypsrp/_utils.py +0 -1
  9. {pypsrp-0.8.0rc1 → pypsrp-0.9.0rc1}/src/pypsrp/client.py +0 -1
  10. {pypsrp-0.8.0rc1 → pypsrp-0.9.0rc1}/src/pypsrp/complex_objects.py +3 -3
  11. {pypsrp-0.8.0rc1 → pypsrp-0.9.0rc1}/src/pypsrp/encryption.py +9 -77
  12. {pypsrp-0.8.0rc1 → pypsrp-0.9.0rc1}/src/pypsrp/messages.py +3 -2
  13. {pypsrp-0.8.0rc1 → pypsrp-0.9.0rc1}/src/pypsrp/negotiate.py +3 -6
  14. {pypsrp-0.8.0rc1 → pypsrp-0.9.0rc1}/src/pypsrp/powershell.py +59 -8
  15. {pypsrp-0.8.0rc1 → pypsrp-0.9.0rc1}/src/pypsrp/serializer.py +23 -20
  16. {pypsrp-0.8.0rc1 → pypsrp-0.9.0rc1}/src/pypsrp/shell.py +3 -3
  17. {pypsrp-0.8.0rc1 → pypsrp-0.9.0rc1}/src/pypsrp/wsman.py +78 -34
  18. {pypsrp-0.8.0rc1 → pypsrp-0.9.0rc1/src/pypsrp.egg-info}/PKG-INFO +59 -28
  19. pypsrp-0.9.0rc1/src/pypsrp.egg-info/SOURCES.txt +113 -0
  20. pypsrp-0.9.0rc1/src/pypsrp.egg-info/requires.txt +25 -0
  21. {pypsrp-0.8.0rc1/tests → pypsrp-0.9.0rc1/tests/tests_pypsrp}/conftest.py +1 -1
  22. pypsrp-0.9.0rc1/tests/tests_pypsrp/responses/test_psrp_multiple_invocations.yml +21 -0
  23. pypsrp-0.9.0rc1/tests/tests_pypsrp/responses/test_psrp_no_profile.yml +13 -0
  24. {pypsrp-0.8.0rc1/tests → pypsrp-0.9.0rc1/tests/tests_pypsrp}/responses/test_winrs_noprofile.yml +1 -1
  25. {pypsrp-0.8.0rc1/tests → pypsrp-0.9.0rc1/tests/tests_pypsrp}/test_client.py +12 -12
  26. {pypsrp-0.8.0rc1/tests → pypsrp-0.9.0rc1/tests/tests_pypsrp}/test_complex_objects.py +0 -4
  27. {pypsrp-0.8.0rc1/tests → pypsrp-0.9.0rc1/tests/tests_pypsrp}/test_encryption.py +27 -54
  28. {pypsrp-0.8.0rc1/tests → pypsrp-0.9.0rc1/tests/tests_pypsrp}/test_host.py +11 -12
  29. {pypsrp-0.8.0rc1/tests → pypsrp-0.9.0rc1/tests/tests_pypsrp}/test_integration.py +5 -5
  30. {pypsrp-0.8.0rc1/tests → pypsrp-0.9.0rc1/tests/tests_pypsrp}/test_negotiate.py +40 -40
  31. {pypsrp-0.8.0rc1/tests → pypsrp-0.9.0rc1/tests/tests_pypsrp}/test_powershell.py +72 -21
  32. {pypsrp-0.8.0rc1/tests → pypsrp-0.9.0rc1/tests/tests_pypsrp}/test_serializer.py +1 -1
  33. {pypsrp-0.8.0rc1/tests → pypsrp-0.9.0rc1/tests/tests_pypsrp}/test_shell.py +4 -4
  34. {pypsrp-0.8.0rc1/tests → pypsrp-0.9.0rc1/tests/tests_pypsrp}/test_utils.py +7 -7
  35. {pypsrp-0.8.0rc1/tests → pypsrp-0.9.0rc1/tests/tests_pypsrp}/test_wsman.py +1 -1
  36. pypsrp-0.8.0rc1/pyproject.toml +0 -80
  37. pypsrp-0.8.0rc1/requirements-dev.txt +0 -18
  38. pypsrp-0.8.0rc1/setup.cfg +0 -53
  39. pypsrp-0.8.0rc1/src/pypsrp.egg-info/SOURCES.txt +0 -114
  40. pypsrp-0.8.0rc1/src/pypsrp.egg-info/requires.txt +0 -9
  41. pypsrp-0.8.0rc1/src/pypsrp.egg-info/zip-safe +0 -1
  42. {pypsrp-0.8.0rc1 → pypsrp-0.9.0rc1}/LICENSE +0 -0
  43. {pypsrp-0.8.0rc1 → pypsrp-0.9.0rc1}/MANIFEST.in +0 -0
  44. {pypsrp-0.8.0rc1 → pypsrp-0.9.0rc1}/build_helpers/JEARole.psrc +0 -0
  45. {pypsrp-0.8.0rc1 → pypsrp-0.9.0rc1}/build_helpers/JEARoleSettings.pssc +0 -0
  46. {pypsrp-0.8.0rc1 → pypsrp-0.9.0rc1}/build_helpers/check-winrm.py +0 -0
  47. {pypsrp-0.8.0rc1 → pypsrp-0.9.0rc1}/build_helpers/run-ci.sh +0 -0
  48. {pypsrp-0.8.0rc1 → pypsrp-0.9.0rc1}/src/pypsrp/__init__.py +0 -0
  49. {pypsrp-0.8.0rc1 → pypsrp-0.9.0rc1}/src/pypsrp/exceptions.py +0 -0
  50. {pypsrp-0.8.0rc1 → pypsrp-0.9.0rc1}/src/pypsrp/host.py +0 -0
  51. {pypsrp-0.8.0rc1 → pypsrp-0.9.0rc1}/src/pypsrp/pwsh_scripts/__init__.py +0 -0
  52. {pypsrp-0.8.0rc1 → pypsrp-0.9.0rc1}/src/pypsrp/pwsh_scripts/copy.ps1 +0 -0
  53. {pypsrp-0.8.0rc1 → pypsrp-0.9.0rc1}/src/pypsrp/pwsh_scripts/fetch.ps1 +0 -0
  54. {pypsrp-0.8.0rc1 → pypsrp-0.9.0rc1}/src/pypsrp/py.typed +0 -0
  55. {pypsrp-0.8.0rc1 → pypsrp-0.9.0rc1}/src/pypsrp.egg-info/dependency_links.txt +0 -0
  56. {pypsrp-0.8.0rc1 → pypsrp-0.9.0rc1}/src/pypsrp.egg-info/top_level.txt +0 -0
  57. {pypsrp-0.8.0rc1/tests → pypsrp-0.9.0rc1/tests/tests_pypsrp}/__init__.py +0 -0
  58. {pypsrp-0.8.0rc1/tests → pypsrp-0.9.0rc1/tests/tests_pypsrp}/data/test_sanitise_clixml_with_error.xml +0 -0
  59. {pypsrp-0.8.0rc1/tests → pypsrp-0.9.0rc1/tests/tests_pypsrp}/data/test_sanitise_clixml_with_no_errors.xml +0 -0
  60. {pypsrp-0.8.0rc1/tests → pypsrp-0.9.0rc1/tests/tests_pypsrp}/responses/_template.yml +0 -0
  61. {pypsrp-0.8.0rc1/tests → pypsrp-0.9.0rc1/tests/tests_pypsrp}/responses/test_client_copy_expand_vars.yml +0 -0
  62. {pypsrp-0.8.0rc1/tests → pypsrp-0.9.0rc1/tests/tests_pypsrp}/responses/test_client_copy_file.yml +0 -0
  63. {pypsrp-0.8.0rc1/tests → pypsrp-0.9.0rc1/tests/tests_pypsrp}/responses/test_client_copy_file_empty.yml +0 -0
  64. {pypsrp-0.8.0rc1/tests → pypsrp-0.9.0rc1/tests/tests_pypsrp}/responses/test_client_copy_file_failure.yml +0 -0
  65. {pypsrp-0.8.0rc1/tests → pypsrp-0.9.0rc1/tests/tests_pypsrp}/responses/test_client_copy_file_really_large.yml +0 -0
  66. {pypsrp-0.8.0rc1/tests → pypsrp-0.9.0rc1/tests/tests_pypsrp}/responses/test_client_copy_file_warning.yml +0 -0
  67. {pypsrp-0.8.0rc1/tests → pypsrp-0.9.0rc1/tests/tests_pypsrp}/responses/test_client_execute_cmd.yml +0 -0
  68. {pypsrp-0.8.0rc1/tests → pypsrp-0.9.0rc1/tests/tests_pypsrp}/responses/test_client_execute_cmd_environment.yml +0 -0
  69. {pypsrp-0.8.0rc1/tests → pypsrp-0.9.0rc1/tests/tests_pypsrp}/responses/test_client_execute_ps.yml +0 -0
  70. {pypsrp-0.8.0rc1/tests → pypsrp-0.9.0rc1/tests/tests_pypsrp}/responses/test_client_execute_ps_environment.yml +0 -0
  71. {pypsrp-0.8.0rc1/tests → pypsrp-0.9.0rc1/tests/tests_pypsrp}/responses/test_client_execute_ps_failure.yml +0 -0
  72. {pypsrp-0.8.0rc1/tests → pypsrp-0.9.0rc1/tests/tests_pypsrp}/responses/test_client_fetch_file.yml +0 -0
  73. {pypsrp-0.8.0rc1/tests → pypsrp-0.9.0rc1/tests/tests_pypsrp}/responses/test_client_fetch_file_expand_vars.yml +0 -0
  74. {pypsrp-0.8.0rc1/tests → pypsrp-0.9.0rc1/tests/tests_pypsrp}/responses/test_client_fetch_file_fail_dir.yml +0 -0
  75. {pypsrp-0.8.0rc1/tests → pypsrp-0.9.0rc1/tests/tests_pypsrp}/responses/test_client_fetch_file_fail_missing.yml +0 -0
  76. {pypsrp-0.8.0rc1/tests → pypsrp-0.9.0rc1/tests/tests_pypsrp}/responses/test_client_fetch_file_hash_mismatch.yml +0 -0
  77. {pypsrp-0.8.0rc1/tests → pypsrp-0.9.0rc1/tests/tests_pypsrp}/responses/test_psrp_application_args.yml +0 -0
  78. {pypsrp-0.8.0rc1/tests → pypsrp-0.9.0rc1/tests/tests_pypsrp}/responses/test_psrp_clear_commands.yml +0 -0
  79. {pypsrp-0.8.0rc1/tests → pypsrp-0.9.0rc1/tests/tests_pypsrp}/responses/test_psrp_disconnect_runspaces.yml +0 -0
  80. {pypsrp-0.8.0rc1/tests → pypsrp-0.9.0rc1/tests/tests_pypsrp}/responses/test_psrp_disconnected_commands.yml +0 -0
  81. {pypsrp-0.8.0rc1/tests → pypsrp-0.9.0rc1/tests/tests_pypsrp}/responses/test_psrp_error_failed.yml +0 -0
  82. {pypsrp-0.8.0rc1/tests → pypsrp-0.9.0rc1/tests/tests_pypsrp}/responses/test_psrp_get_command_metadata.yml +0 -0
  83. {pypsrp-0.8.0rc1/tests → pypsrp-0.9.0rc1/tests/tests_pypsrp}/responses/test_psrp_key_exchange_timeout.yml +0 -0
  84. {pypsrp-0.8.0rc1/tests → pypsrp-0.9.0rc1/tests/tests_pypsrp}/responses/test_psrp_long_running_cmdlet.yml +0 -0
  85. {pypsrp-0.8.0rc1/tests → pypsrp-0.9.0rc1/tests/tests_pypsrp}/responses/test_psrp_merge_commands.yml +0 -0
  86. {pypsrp-0.8.0rc1/tests → pypsrp-0.9.0rc1/tests/tests_pypsrp}/responses/test_psrp_multiple_commands.yml +0 -0
  87. {pypsrp-0.8.0rc1/tests → pypsrp-0.9.0rc1/tests/tests_pypsrp}/responses/test_psrp_nested_command.yml +0 -0
  88. {pypsrp-0.8.0rc1/tests → pypsrp-0.9.0rc1/tests/tests_pypsrp}/responses/test_psrp_open_runspace.yml +0 -0
  89. {pypsrp-0.8.0rc1/tests → pypsrp-0.9.0rc1/tests/tests_pypsrp}/responses/test_psrp_pshost_methods.yml +0 -0
  90. {pypsrp-0.8.0rc1/tests → pypsrp-0.9.0rc1/tests/tests_pypsrp}/responses/test_psrp_pshost_raw_ui_mocked_methods.yml +0 -0
  91. {pypsrp-0.8.0rc1/tests → pypsrp-0.9.0rc1/tests/tests_pypsrp}/responses/test_psrp_pshost_ui_mocked_methods.yml +0 -0
  92. {pypsrp-0.8.0rc1/tests → pypsrp-0.9.0rc1/tests/tests_pypsrp}/responses/test_psrp_receive_failure.yml +0 -0
  93. {pypsrp-0.8.0rc1/tests → pypsrp-0.9.0rc1/tests/tests_pypsrp}/responses/test_psrp_reset_runspace_state.yml +0 -0
  94. {pypsrp-0.8.0rc1/tests → pypsrp-0.9.0rc1/tests/tests_pypsrp}/responses/test_psrp_reset_runspace_state_fail.yml +0 -0
  95. {pypsrp-0.8.0rc1/tests → pypsrp-0.9.0rc1/tests/tests_pypsrp}/responses/test_psrp_run_protocol_version_2.1.yml +0 -0
  96. {pypsrp-0.8.0rc1/tests → pypsrp-0.9.0rc1/tests/tests_pypsrp}/responses/test_psrp_run_protocol_version_2.2.yml +0 -0
  97. {pypsrp-0.8.0rc1/tests → pypsrp-0.9.0rc1/tests/tests_pypsrp}/responses/test_psrp_run_protocol_version_2.3.yml +0 -0
  98. {pypsrp-0.8.0rc1/tests → pypsrp-0.9.0rc1/tests/tests_pypsrp}/responses/test_psrp_set_runspaces.yml +0 -0
  99. {pypsrp-0.8.0rc1/tests → pypsrp-0.9.0rc1/tests/tests_pypsrp}/responses/test_psrp_small_msg_size.yml +0 -0
  100. {pypsrp-0.8.0rc1/tests → pypsrp-0.9.0rc1/tests/tests_pypsrp}/responses/test_psrp_stream_no_output_invocation.yml +0 -0
  101. {pypsrp-0.8.0rc1/tests → pypsrp-0.9.0rc1/tests/tests_pypsrp}/responses/test_psrp_stream_output_invocation.yml +0 -0
  102. {pypsrp-0.8.0rc1/tests → pypsrp-0.9.0rc1/tests/tests_pypsrp}/responses/test_psrp_with_history.yml +0 -0
  103. {pypsrp-0.8.0rc1/tests → pypsrp-0.9.0rc1/tests/tests_pypsrp}/responses/test_psrp_with_input.yml +0 -0
  104. {pypsrp-0.8.0rc1/tests → pypsrp-0.9.0rc1/tests/tests_pypsrp}/responses/test_psrp_with_jea_configuration.yml +0 -0
  105. {pypsrp-0.8.0rc1/tests → pypsrp-0.9.0rc1/tests/tests_pypsrp}/responses/test_psrp_with_no_history.yml +0 -0
  106. {pypsrp-0.8.0rc1/tests → pypsrp-0.9.0rc1/tests/tests_pypsrp}/responses/test_winrs_bad_cmd_id.yml +0 -0
  107. {pypsrp-0.8.0rc1/tests → pypsrp-0.9.0rc1/tests/tests_pypsrp}/responses/test_winrs_environment.yml +0 -0
  108. {pypsrp-0.8.0rc1/tests → pypsrp-0.9.0rc1/tests/tests_pypsrp}/responses/test_winrs_extra_opts.yml +0 -0
  109. {pypsrp-0.8.0rc1/tests → pypsrp-0.9.0rc1/tests/tests_pypsrp}/responses/test_winrs_fail_poll_process.yml +0 -0
  110. {pypsrp-0.8.0rc1/tests → pypsrp-0.9.0rc1/tests/tests_pypsrp}/responses/test_winrs_no_cmd_shell.yml +0 -0
  111. {pypsrp-0.8.0rc1/tests → pypsrp-0.9.0rc1/tests/tests_pypsrp}/responses/test_winrs_open_already_opened.yml +0 -0
  112. {pypsrp-0.8.0rc1/tests → pypsrp-0.9.0rc1/tests/tests_pypsrp}/responses/test_winrs_operation_timeout.yml +0 -0
  113. {pypsrp-0.8.0rc1/tests → pypsrp-0.9.0rc1/tests/tests_pypsrp}/responses/test_winrs_send.yml +0 -0
  114. {pypsrp-0.8.0rc1/tests → pypsrp-0.9.0rc1/tests/tests_pypsrp}/responses/test_winrs_standard.yml +0 -0
  115. {pypsrp-0.8.0rc1/tests → pypsrp-0.9.0rc1/tests/tests_pypsrp}/responses/test_winrs_stderr_rc.yml +0 -0
  116. {pypsrp-0.8.0rc1/tests → pypsrp-0.9.0rc1/tests/tests_pypsrp}/responses/test_winrs_unicode.yml +0 -0
  117. {pypsrp-0.8.0rc1/tests → pypsrp-0.9.0rc1/tests/tests_pypsrp}/responses/test_wsman_update_envelope_size_150.yml +0 -0
  118. {pypsrp-0.8.0rc1/tests → pypsrp-0.9.0rc1/tests/tests_pypsrp}/responses/test_wsman_update_envelope_size_4096.yml +0 -0
  119. {pypsrp-0.8.0rc1/tests → pypsrp-0.9.0rc1/tests/tests_pypsrp}/responses/test_wsman_update_envelope_size_500.yml +0 -0
  120. {pypsrp-0.8.0rc1/tests → pypsrp-0.9.0rc1/tests/tests_pypsrp}/test_exceptions.py +0 -0
  121. {pypsrp-0.8.0rc1/tests → pypsrp-0.9.0rc1/tests/tests_pypsrp}/test_messages.py +0 -0
@@ -1,6 +1,21 @@
1
1
  # Changelog
2
2
 
3
- ## 0.8.0 - TBD
3
+ ## 0.9.0 - TBD
4
+
5
+ * Raised minimum Python version to 3.10
6
+ * Added retry handler for WSMan `Receive` operations that can attempt to recover from a network disconnect during a command operation
7
+ * Added `no_profile` option to `RunspacePool` to skip loading the user profile on the remote shell
8
+ * Made an `__enter__` and `__exit__` method for `PowerShell` to allow it to be used with a `with PowerShell(runspace) as ps:` syntax
9
+ * On `__exit__` the pipeline will be closed using the `TERMINATE` signal to clean up any resources on the server end
10
+ * `ps.close()` can also be called manually to clean up any resources and to prep the pipeline to be run again
11
+ * Added `clear_streams()` onto a `PowerShell` object to clear the output and `streams` values so it is ready for a subsequent run
12
+
13
+ ## 0.8.1 - 2022-02-22
14
+
15
+ * Bump `requests-credssp` minimum to new version to support newer encryption format and simpler dependencies
16
+
17
+
18
+ ## 0.8.0 - 2022-02-01
4
19
 
5
20
  * The `CommandParameter` class now uses named keyword arguments
6
21
  * The `cmd` parameter for `Command` class is now a positional argument
@@ -1,25 +1,44 @@
1
- Metadata-Version: 2.1
1
+ Metadata-Version: 2.4
2
2
  Name: pypsrp
3
- Version: 0.8.0rc1
3
+ Version: 0.9.0rc1
4
4
  Summary: PowerShell Remoting Protocol and WinRM for Python
5
- Home-page: https://github.com/jborean93/pypsrp
6
- Author: Jordan Borean
7
- Author-email: jborean93@gmail.com
8
- License: MIT
5
+ Author-email: Jordan Borean <jborean93@gmail.com>
6
+ License-Expression: MIT
7
+ Project-URL: homepage, https://github.com/jborean93/pypsrp
9
8
  Keywords: winrm,psrp,winrs,windows,powershell
10
- Platform: UNKNOWN
11
9
  Classifier: Development Status :: 4 - Beta
12
- Classifier: License :: OSI Approved :: MIT License
13
10
  Classifier: Programming Language :: Python :: 3
14
- Classifier: Programming Language :: Python :: 3.6
15
- Classifier: Programming Language :: Python :: 3.7
16
- Classifier: Programming Language :: Python :: 3.8
17
- Classifier: Programming Language :: Python :: 3.9
18
11
  Classifier: Programming Language :: Python :: 3.10
12
+ Classifier: Programming Language :: Python :: 3.11
13
+ Classifier: Programming Language :: Python :: 3.12
14
+ Classifier: Programming Language :: Python :: 3.13
15
+ Classifier: Programming Language :: Python :: 3.14
16
+ Requires-Python: >=3.10
19
17
  Description-Content-Type: text/markdown
18
+ License-File: LICENSE
19
+ Requires-Dist: cryptography>=3.1
20
+ Requires-Dist: pyspnego<1.0.0,>=0.7.0
21
+ Requires-Dist: requests>=2.9.1
20
22
  Provides-Extra: credssp
23
+ Requires-Dist: requests-credssp>=2.0.0; extra == "credssp"
21
24
  Provides-Extra: kerberos
22
- License-File: LICENSE
25
+ Requires-Dist: pyspnego[kerberos]; extra == "kerberos"
26
+ Provides-Extra: dev
27
+ Requires-Dist: black==25.11.0; extra == "dev"
28
+ Requires-Dist: build; extra == "dev"
29
+ Requires-Dist: isort==6.1.0; extra == "dev"
30
+ Requires-Dist: mypy==1.19.0; extra == "dev"
31
+ Requires-Dist: pre-commit; extra == "dev"
32
+ Requires-Dist: pyspnego[kerberos]; extra == "dev"
33
+ Requires-Dist: pytest; extra == "dev"
34
+ Requires-Dist: pytest-cov; extra == "dev"
35
+ Requires-Dist: pytest-mock; extra == "dev"
36
+ Requires-Dist: PyYAML; extra == "dev"
37
+ Requires-Dist: requests-credssp; extra == "dev"
38
+ Requires-Dist: types-requests; extra == "dev"
39
+ Requires-Dist: types-PyYAML; extra == "dev"
40
+ Requires-Dist: xmldiff; extra == "dev"
41
+ Dynamic: license-file
23
42
 
24
43
  # pypsrp - Python PowerShell Remoting Protocol Client library
25
44
 
@@ -64,7 +83,7 @@ libraries to be installed.
64
83
 
65
84
  See `How to Install` for more details
66
85
 
67
- * CPython 3.6+
86
+ * CPython 3.10+
68
87
  * [cryptography](https://github.com/pyca/cryptography)
69
88
  * [pyspnego](https://github.com/jborean93/pyspnego)
70
89
  * [requests](https://github.com/requests/requests)
@@ -242,7 +261,7 @@ configure a `WinRS` shell;
242
261
  * `idle_time_out`: THe idle timeout in seconds of the shell
243
262
  * `lifetime`: The total lifetime of the shell
244
263
  * `name`: The name (description only) of the shell
245
- * `no_profile`: Whether to create the shell with the user profile loaded or not
264
+ * `no_profile`: Whether to create the shell with the user profile loaded or not. This no longer works on Server 2012/Windows 8 or newer
246
265
  * `working_directory`: The default working directory of the created shell
247
266
 
248
267
  `RunspacePool` is a shell used by the PSRP protocol, it is designed to be a
@@ -259,6 +278,7 @@ Here are the options that can be used to configure a `RunspacePool` shell;
259
278
  * `min_runspaces`: The minimuum number of runspaces that a pool can hold, default is 1
260
279
  * `max_runspaces`: The maximum number of runspaces that a pool can hold. Each PowerShell pipeline is run in a single Runspace, default is 1
261
280
  * `session_key_timeout_ms`: The maximum time to wait for a session key transfer from the server
281
+ * `no_profile`: Do not load the user profile on the remote Runspace Pool
262
282
 
263
283
  ### Process
264
284
 
@@ -364,9 +384,8 @@ from pypsrp.wsman import WSMan
364
384
  # creates a https connection with explicit kerberos auth and implicit credentials
365
385
  wsman = WSMan("server", auth="kerberos", cert_validation=False))
366
386
 
367
- with wsman, RunspacePool(wsman) as pool:
387
+ with wsman, RunspacePool(wsman) as pool, PowerShell(pool) as ps:
368
388
  # execute 'Get-Process | Select-Object Name'
369
- ps = PowerShell(pool)
370
389
  ps.add_cmdlet("Get-Process").add_cmdlet("Select-Object").add_argument("Name")
371
390
  output = ps.invoke()
372
391
 
@@ -397,6 +416,19 @@ with wsman, RunspacePool(wsman) as pool:
397
416
  ps.invoke(["string", 1])
398
417
  print(ps.output)
399
418
  print(ps.streams.debug)
419
+
420
+ # It is possible to run the PowerShell pipeline again with invoke() but it
421
+ # needs to be explicitly closed first and the commands/streams optionally
422
+ # cleared if desired.
423
+ ps.close()
424
+
425
+ # Clears out ps.output and ps.streams to a blank value. Not required but
426
+ # nice if the output should be separate from a previous run
427
+ ps.clear_streams()
428
+
429
+ # Removes all existing commands. Not required but needed if re-using the
430
+ # same pipeline with a different set of commands
431
+ ps.clear_commands()
400
432
  ```
401
433
 
402
434
 
@@ -450,17 +482,18 @@ information and should only be used for debugging purposes._
450
482
  ## Testing
451
483
 
452
484
  Any changes are more than welcome in pull request form, you can run the current
453
- test suite with tox like so;
485
+ test suite with:
454
486
 
455
487
  ```bash
456
- # make sure tox is installed
457
- pip install tox
458
-
459
- # run the tox suite
460
- tox
461
-
462
- # or run the test manually for the current Python environment
463
- py.test -v --pep8 --cov pypsrp --cov-report term-missing
488
+ pip install -e .[dev]
489
+
490
+ python -m pytest \
491
+ tests/tests_pypsrp \
492
+ --verbose \
493
+ --junitxml junit/test-results.xml \
494
+ --cov pypsrp \
495
+ --cov-report xml \
496
+ --cov-report term-missing
464
497
  ```
465
498
 
466
499
  A lot of the tests either simulate a remote Windows host but you can also run a
@@ -530,5 +563,3 @@ tests.
530
563
  * Add Ansible playbook for better integration tests
531
564
  * Improved serialization between Python and .NET objects
532
565
  * Live interactive console for PSRP
533
-
534
-
@@ -41,7 +41,7 @@ libraries to be installed.
41
41
 
42
42
  See `How to Install` for more details
43
43
 
44
- * CPython 3.6+
44
+ * CPython 3.10+
45
45
  * [cryptography](https://github.com/pyca/cryptography)
46
46
  * [pyspnego](https://github.com/jborean93/pyspnego)
47
47
  * [requests](https://github.com/requests/requests)
@@ -219,7 +219,7 @@ configure a `WinRS` shell;
219
219
  * `idle_time_out`: THe idle timeout in seconds of the shell
220
220
  * `lifetime`: The total lifetime of the shell
221
221
  * `name`: The name (description only) of the shell
222
- * `no_profile`: Whether to create the shell with the user profile loaded or not
222
+ * `no_profile`: Whether to create the shell with the user profile loaded or not. This no longer works on Server 2012/Windows 8 or newer
223
223
  * `working_directory`: The default working directory of the created shell
224
224
 
225
225
  `RunspacePool` is a shell used by the PSRP protocol, it is designed to be a
@@ -236,6 +236,7 @@ Here are the options that can be used to configure a `RunspacePool` shell;
236
236
  * `min_runspaces`: The minimuum number of runspaces that a pool can hold, default is 1
237
237
  * `max_runspaces`: The maximum number of runspaces that a pool can hold. Each PowerShell pipeline is run in a single Runspace, default is 1
238
238
  * `session_key_timeout_ms`: The maximum time to wait for a session key transfer from the server
239
+ * `no_profile`: Do not load the user profile on the remote Runspace Pool
239
240
 
240
241
  ### Process
241
242
 
@@ -341,9 +342,8 @@ from pypsrp.wsman import WSMan
341
342
  # creates a https connection with explicit kerberos auth and implicit credentials
342
343
  wsman = WSMan("server", auth="kerberos", cert_validation=False))
343
344
 
344
- with wsman, RunspacePool(wsman) as pool:
345
+ with wsman, RunspacePool(wsman) as pool, PowerShell(pool) as ps:
345
346
  # execute 'Get-Process | Select-Object Name'
346
- ps = PowerShell(pool)
347
347
  ps.add_cmdlet("Get-Process").add_cmdlet("Select-Object").add_argument("Name")
348
348
  output = ps.invoke()
349
349
 
@@ -374,6 +374,19 @@ with wsman, RunspacePool(wsman) as pool:
374
374
  ps.invoke(["string", 1])
375
375
  print(ps.output)
376
376
  print(ps.streams.debug)
377
+
378
+ # It is possible to run the PowerShell pipeline again with invoke() but it
379
+ # needs to be explicitly closed first and the commands/streams optionally
380
+ # cleared if desired.
381
+ ps.close()
382
+
383
+ # Clears out ps.output and ps.streams to a blank value. Not required but
384
+ # nice if the output should be separate from a previous run
385
+ ps.clear_streams()
386
+
387
+ # Removes all existing commands. Not required but needed if re-using the
388
+ # same pipeline with a different set of commands
389
+ ps.clear_commands()
377
390
  ```
378
391
 
379
392
 
@@ -427,17 +440,18 @@ information and should only be used for debugging purposes._
427
440
  ## Testing
428
441
 
429
442
  Any changes are more than welcome in pull request form, you can run the current
430
- test suite with tox like so;
443
+ test suite with:
431
444
 
432
445
  ```bash
433
- # make sure tox is installed
434
- pip install tox
435
-
436
- # run the tox suite
437
- tox
438
-
439
- # or run the test manually for the current Python environment
440
- py.test -v --pep8 --cov pypsrp --cov-report term-missing
446
+ pip install -e .[dev]
447
+
448
+ python -m pytest \
449
+ tests/tests_pypsrp \
450
+ --verbose \
451
+ --junitxml junit/test-results.xml \
452
+ --cov pypsrp \
453
+ --cov-report xml \
454
+ --cov-report term-missing
441
455
  ```
442
456
 
443
457
  A lot of the tests either simulate a remote Windows host but you can also run a
@@ -62,25 +62,17 @@ lib::setup::python_requirements() {
62
62
  echo "::group::Installing Python Requirements"
63
63
  fi
64
64
 
65
- python -m pip install --upgrade pip setuptools wheel
65
+ # Getting the version is important so that pip prioritises our local dist
66
+ python -m pip install build
67
+ PSRP_VERSION="$( python -c "import build.util; print(build.util.project_wheel_metadata('.').get('Version'))" )"
66
68
 
67
69
  echo "Installing pypsrp"
68
- if [ "$(expr substr $(uname -s) 1 5)" == "MINGW" ]; then
69
- DIST_LINK_PATH="$( echo "${PWD}/dist" | sed -e 's/^\///' -e 's/\//\\/g' -e 's/^./\0:/' )"
70
- else
71
- DIST_LINK_PATH="${PWD}/dist"
72
- fi
73
-
74
- python -m pip install pypsrp \
75
- --no-index \
76
- --find-links "file://${DIST_LINK_PATH}" \
77
- --no-build-isolation \
78
- --no-dependencies \
70
+ python -m pip install pypsrp[credssp,kerberos]=="${PSRP_VERSION}" \
71
+ --find-links dist \
79
72
  --verbose
80
- python -m pip install pypsrp[credssp,kerberos]
81
73
 
82
74
  echo "Installing dev dependencies"
83
- python -m pip install -r requirements-dev.txt
75
+ python -m pip install .[dev]
84
76
 
85
77
  if [ x"${GITHUB_ACTIONS}" = "xtrue" ]; then
86
78
  echo "::endgroup::"
@@ -114,6 +106,7 @@ lib::tests::run() {
114
106
  fi
115
107
 
116
108
  python -m pytest \
109
+ tests/tests_pypsrp \
117
110
  --verbose \
118
111
  --junitxml junit/test-results.xml \
119
112
  --cov pypsrp \
@@ -120,13 +120,13 @@ function New-WinRMFirewallRule {
120
120
 
121
121
  foreach ($rule in $rules) {
122
122
  $rule_details = @{
123
- LocalPorts = $Port
124
- RemotePorts = "*"
125
- LocalAddresses = "*"
126
- Enabled = $true
127
- Direction = 1
128
- Action = 1
129
- Grouping = "Windows Remote Management"
123
+ LocalPorts = $Port
124
+ RemotePorts = "*"
125
+ LocalAddresses = "*"
126
+ Enabled = $true
127
+ Direction = 1
128
+ Action = 1
129
+ Grouping = "Windows Remote Management"
130
130
  ApplicationName = "System"
131
131
  }
132
132
  $rule.Protocol = 6
@@ -171,33 +171,28 @@ function Reset-WinRMConfig {
171
171
  Write-Verbose "Removing all existing WinRM listeners"
172
172
  Get-ChildItem -LiteralPath WSMan:\localhost\Listener | Remove-Item -Force -Recurse
173
173
 
174
- if (-not $CertificateThumbprint) {
175
- Write-Verbose "Removing all existing certificate in the personal store"
176
- Remove-Item -Path Cert:\LocalMachine\My\* -Force -Recurse
177
- }
178
-
179
174
  Write-Information -MessageData "Creating HTTP listener"
180
- $selector_set = @{
175
+ $selectorSet = @{
181
176
  Transport = "HTTP"
182
- Address = "*"
177
+ Address = "*"
183
178
  }
184
- $value_set = @{
179
+ $valueSet = @{
185
180
  Enabled = $true
186
181
  }
187
- New-WSManInstance -ResourceURI winrm/config/listener -SelectorSet $selector_set -ValueSet $value_set > $null
182
+ New-WSManInstance -ResourceURI winrm/config/listener -SelectorSet $selectorSet -ValueSet $valueSet > $null
188
183
 
189
184
  $certificate = New-LegacySelfSignedCert -Subject $env:COMPUTERNAME -ValidDays 1095
190
- $selector_set = @{
185
+ $selectorSet = @{
191
186
  Transport = "HTTPS"
192
- Address = "*"
187
+ Address = "*"
193
188
  }
194
- $value_set = @{
189
+ $valueSet = @{
195
190
  CertificateThumbprint = $certificate.Thumbprint
196
- Enabled = $true
191
+ Enabled = $true
197
192
  }
198
193
 
199
194
  Write-Information -MessageData "Creating HTTPS listener"
200
- New-WSManInstance -ResourceURI "winrm/config/Listener" -SelectorSet $selector_set -ValueSet $value_set > $null
195
+ New-WSManInstance -ResourceURI "winrm/config/Listener" -SelectorSet $selectorSet -ValueSet $valueSet > $null
201
196
 
202
197
  Write-Verbose "Enabling PowerShell Remoting"
203
198
  Enable-PSRemoting -Force > $null
@@ -211,8 +206,8 @@ function Reset-WinRMConfig {
211
206
  Write-Information -MessageData "Enabling CredSSP authentication"
212
207
  Enable-WSManCredSSP -Role Server -Force > $null
213
208
 
214
- Write-Information -MessageData "Setting AllowUnencrypted to False"
215
- Set-Item -Path WSMan:\localhost\Service\AllowUnencrypted -Value $false
209
+ Write-Information -MessageData "Setting AllowUnencrypted to True"
210
+ Set-Item -Path WSMan:\localhost\Service\AllowUnencrypted -Value $true
216
211
 
217
212
  Write-Information -MessageData "Configuring WinRM HTTPS firewall rule"
218
213
  New-WinRMFirewallRule -Port 5986 -Protocol HTTPS
@@ -222,11 +217,11 @@ function Reset-WinRMConfig {
222
217
 
223
218
  Write-Information -MessageData "Allow local admins over network auth"
224
219
  $regInfo = @{
225
- Path = "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\System"
226
- Name = "LocalAccountTokenFilterPolicy"
227
- Value = 1
220
+ Path = "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\System"
221
+ Name = "LocalAccountTokenFilterPolicy"
222
+ Value = 1
228
223
  PropertyType = "DWord"
229
- Force = $true
224
+ Force = $true
230
225
  }
231
226
  New-ItemProperty @regInfo
232
227
 
@@ -245,45 +240,61 @@ Function New-CertificateAuthBinding {
245
240
 
246
241
  Write-Information -MessageData "Generating self signed certificate for authentication of user $Name"
247
242
  $certInfo = @{
248
- Type = "Custom"
249
- Subject = "CN=$Name"
243
+ Subject = "CN=$Name"
244
+ KeyUsage = "DigitalSignature", "KeyEncipherment"
245
+ KeyAlgorithm = "RSA"
246
+ KeyLength = 2048
250
247
  TextExtension = @("2.5.29.37={text}1.3.6.1.5.5.7.3.2", "2.5.29.17={text}upn=$Name@localhost")
251
- KeyUsage = "DigitalSignature", "KeyEncipherment"
252
- KeyAlgorithm = "RSA"
253
- KeyLength = 2048
248
+ Type = "Custom"
249
+ CertStoreLocation = "Cert:\CurrentUser\My"
254
250
  }
255
251
  $cert = New-SelfSignedCertificate @certInfo
256
252
 
257
- Write-Information -MessageData "Exporting public key of cert"
258
- $pem_output = @()
259
- $pem_output += "-----BEGIN CERTIFICATE-----"
260
- $pem_output += [System.Convert]::ToBase64String($cert.RawData) -replace ".{64}", "$&`n"
261
- $pem_output += "-----END CERTIFICATE-----"
262
- [System.IO.File]::WriteAllLines("$CertPath\cert.pem", $pem_output)
263
-
264
253
  Write-Information -MessageData "Exporting private key in a PFX file"
265
254
  [System.IO.File]::WriteAllBytes("$CertPath\cert.pfx", $cert.Export("Pfx"))
266
255
 
267
256
  Write-Information -MessageData "Converting private key to PEM format with openssl"
268
- &"C:\Program Files\OpenSSL\bin\openssl.exe" @("pkcs12", "-in", "$CertPath\cert.pfx", "-nocerts", "-nodes", "-out", "$CertPath\cert_key.pem", "-passin", "pass:", "-passout", "pass:")
257
+ $out = openssl.exe @(
258
+ "pkcs12",
259
+ "-in", "$CertPath\cert.pfx",
260
+ "-nocerts",
261
+ "-nodes",
262
+ "-out", "$CertPath\cert_key.pem",
263
+ "-passin", "pass:",
264
+ "-passout", "pass:"
265
+ ) 2>&1
266
+ if ($LASTEXITCODE) {
267
+ throw "Failed to extract key from PEM:`n$out"
268
+ }
269
+ Remove-Item -Path "$CertPath\cert.pfx" -Force
270
+
271
+ # WinRM seems to be very picky about the type of cert in the trusted root and people store. Make sure this is set
272
+ # to the cert and not cert + key.
273
+ $cert = [System.Security.Cryptography.X509Certificates.X509Certificate2]::new($cert.RawData)
274
+
275
+ Write-Information -MessageData "Exporting cert and key of user certificate"
276
+ $key_pem = Get-Content -Path "$CertPath\cert_key.pem"
277
+ Remove-Item -Path "$CertPath\cert_key.pem" -Force
278
+ [System.IO.File]::WriteAllLines("$CertPath\cert.pem", @(
279
+ $key_pem
280
+ "-----BEGIN CERTIFICATE-----"
281
+ [System.Convert]::ToBase64String($cert.RawData) -replace ".{64}", "$&`n"
282
+ "-----END CERTIFICATE-----"
283
+ ))
269
284
 
270
285
  Write-Information -MessageData "Importing cert into LocalMachine\Root"
271
- $store_name = [System.Security.Cryptography.X509Certificates.StoreName]::Root
272
- $store_location = [System.Security.Cryptography.X509Certificates.StoreLocation]::LocalMachine
273
- $store = New-Object -TypeName System.Security.Cryptography.X509Certificates.X509Store -ArgumentList $store_name, $store_location
286
+ $store = Get-Item -Path Cert:\LocalMachine\Root
274
287
  $store.Open("MaxAllowed")
275
288
  $store.Add($cert)
276
289
  $store.Close()
277
290
 
278
291
  Write-Information -MessageData "Importing cert into LocalMachine\TrustedPeople"
279
- $store_name = [System.Security.Cryptography.X509Certificates.StoreName]::TrustedPeople
280
- $store_location = [System.Security.Cryptography.X509Certificates.StoreLocation]::LocalMachine
281
- $store = New-Object -TypeName System.Security.Cryptography.X509Certificates.X509Store -ArgumentList $store_name, $store_location
292
+ $store = Get-Item -Path Cert:\LocalMachine\TrustedPeople
282
293
  $store.Open("MaxAllowed")
283
294
  $store.Add($cert)
284
295
  $store.Close()
285
296
 
286
- $cert.Thumbprint
297
+ $cert
287
298
  }
288
299
 
289
300
  Function New-JEAConfiguration {
@@ -296,31 +307,31 @@ Function New-JEAConfiguration {
296
307
  $JEAConfigPath
297
308
  )
298
309
 
299
- $module_path = Join-Path -Path $env:ProgramFiles -ChildPath "WindowsPowerShell\Modules\$Name"
300
- Write-Information -MessageData "Setting up JEA PowerShell module path at '$module_path'"
301
- if (-not (Test-Path -Path $module_path)) {
302
- New-Item -Path $module_path -ItemType Directory
310
+ $modulePath = Join-Path -Path $env:ProgramFiles -ChildPath "WindowsPowerShell\Modules\$Name"
311
+ Write-Information -MessageData "Setting up JEA PowerShell module path at '$modulePath'"
312
+ if (-not (Test-Path -Path $modulePath)) {
313
+ New-Item -Path $modulePath -ItemType Directory
303
314
  }
304
315
 
305
- $functions_path = Join-Path -Path $module_path -ChildPath "$($Name)Functions.psm1"
306
- if (-not (Test-Path -Path $functions_path)) {
307
- New-Item -Path $functions_path -ItemType File
316
+ $functionsPath = Join-Path -Path $modulePath -ChildPath "$($Name)Functions.psm1"
317
+ if (-not (Test-Path -Path $functionsPath)) {
318
+ New-Item -Path $functionsPath -ItemType File
308
319
  }
309
320
 
310
- $manifest_path = Join-Path -Path $module_path -ChildPath "$($Name).psd1"
311
- if (-not (Test-Path -Path $manifest_path)) {
312
- New-ModuleManifest -Path $manifest_path -RootModule "$($Name)Functions.psm1"
321
+ $manifestPath = Join-Path -Path $modulePath -ChildPath "$($Name).psd1"
322
+ if (-not (Test-Path -Path $manifestPath)) {
323
+ New-ModuleManifest -Path $manifestPath -RootModule "$($Name)Functions.psm1"
313
324
  }
314
325
 
315
- $role_path = Join-Path -Path $module_path -ChildPath "RoleCapabilities"
316
- if (-not (Test-Path -Path $role_path)) {
317
- New-Item -Path $role_path -ItemType Directory
326
+ $rolePath = Join-Path -Path $modulePath -ChildPath "RoleCapabilities"
327
+ if (-not (Test-Path -Path $rolePath)) {
328
+ New-Item -Path $rolePath -ItemType Directory
318
329
  }
319
330
 
320
- $jea_role_src = Join-Path -Path $JEAConfigPath -ChildPath "$($Name).psrc"
331
+ $jeaRoleSrc = Join-Path -Path $JEAConfigPath -ChildPath "$($Name).psrc"
321
332
 
322
- Write-Information -MessageData "Copying across JEA role configuration from '$jea_role_src'"
323
- Copy-Item -Path $jea_role_src -Destination $role_path
333
+ Write-Information -MessageData "Copying across JEA role configuration from '$jeaRoleSrc'"
334
+ Copy-Item -Path $jeaRoleSrc -Destination $rolePath
324
335
 
325
336
  if (Get-PSSessionConfiguration | Where-Object { $_.Name -eq $name }) {
326
337
  Write-Information -MessageData "JEA role $Name already registered, removing to ensure we start fresh"
@@ -330,6 +341,7 @@ Function New-JEAConfiguration {
330
341
 
331
342
  $secPassword = ConvertTo-SecureString -String $Password -AsPlainText -Force
332
343
  $userCredential = New-Object -TypeName System.Management.Automation.PSCredential -ArgumentList $env:COMPUTERNAME\$UserName, $secPassword
344
+ $userCertCredential = New-Object -TypeName System.Management.Automation.PSCredential -ArgumentList $UserName, $secPassword
333
345
 
334
346
  Enable-PSRemoting -Force
335
347
  Start-Service -Name WinRM
@@ -338,17 +350,19 @@ Reset-WinRMConfig
338
350
  $localUser = New-LocalUser -Name $UserName -Password $secPassword -AccountNeverExpires -PasswordNeverExpires
339
351
  Add-LocalGroupMember -Group Administrators -Member $localUser
340
352
 
341
- # Cert auth is disabled in these tests due to some unknown failure
342
- # $thumbprint = New-CertificateAuthBinding -Name $UserName -CertPath $CertPath
343
- # $credBinding = @{
344
- # Path = "WSMan:\localhost\ClientCertificate"
345
- # Subject = "$UserName@localhost"
346
- # URI = "*"
347
- # Issuer = $thumbprint
348
- # Credential = $userCredential
349
- # Force = $true
350
- # }
351
- # New-Item @credBinding
353
+ $clientCertificate = New-CertificateAuthBinding -Name $UserName -CertPath $CertPath
354
+ $certChain = [Security.Cryptography.X509Certificates.X509Chain]::new()
355
+ [void]$certChain.Build($clientCertificate)
356
+
357
+ $credBinding = @{
358
+ Path = "WSMan:\localhost\ClientCertificate"
359
+ Subject = "$UserName@localhost"
360
+ URI = "*"
361
+ Issuer = $certChain.ChainElements.Certificate[-1].Thumbprint
362
+ Credential = $userCertCredential
363
+ Force = $true
364
+ }
365
+ New-Item @credBinding
352
366
 
353
367
  New-JEAConfiguration -Name JEARole -JEAConfigPath $PSScriptRoot
354
368
  Register-PSSessionConfiguration -Path "$PSScriptRoot\JEARoleSettings.pssc" -Name JEARole -Force
@@ -356,4 +370,12 @@ Register-PSSessionConfiguration -Path "$PSScriptRoot\JEARoleSettings.pssc" -Name
356
370
  Restart-Service -Name winrm
357
371
 
358
372
  Write-Information -MessageData "Testing WinRM connection"
359
- Invoke-Command -ComputerName localhost -ScriptBlock { whoami.exe /all } -Credential $userCredential
373
+ $invokeParams = @{
374
+ ComputerName = 'localhost'
375
+ ScriptBlock = { whoami.exe }
376
+ SessionOption = (New-PSSessionOption -SkipCACheck -SkipCNCheck -SkipRevocationCheck)
377
+ }
378
+ Invoke-Command @invokeParams -Credential $userCredential
379
+
380
+ # Write-Information -MessageData "Testing WinRM connection with certificates"
381
+ # Invoke-Command @invokeParams -CertificateThumbprint $thumbprint