python4cpm 1.0.20__tar.gz → 1.0.22__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.
- python4cpm-1.0.22/PKG-INFO +404 -0
- python4cpm-1.0.22/README.md +372 -0
- {python4cpm-1.0.20 → python4cpm-1.0.22}/pyproject.toml +1 -1
- {python4cpm-1.0.20 → python4cpm-1.0.22}/src/python4cpm/__init__.py +2 -7
- {python4cpm-1.0.20 → python4cpm-1.0.22}/src/python4cpm/logger.py +4 -4
- {python4cpm-1.0.20 → python4cpm-1.0.22}/src/python4cpm/nethelper.py +9 -4
- {python4cpm-1.0.20 → python4cpm-1.0.22}/src/python4cpm/python4cpm.py +3 -8
- python4cpm-1.0.22/src/python4cpm/python4cpmhandler.py +41 -0
- python4cpm-1.0.22/src/python4cpm.egg-info/PKG-INFO +404 -0
- {python4cpm-1.0.20 → python4cpm-1.0.22}/src/python4cpm.egg-info/SOURCES.txt +1 -0
- python4cpm-1.0.20/PKG-INFO +0 -244
- python4cpm-1.0.20/README.md +0 -212
- python4cpm-1.0.20/src/python4cpm.egg-info/PKG-INFO +0 -244
- {python4cpm-1.0.20 → python4cpm-1.0.22}/LICENSE +0 -0
- {python4cpm-1.0.20 → python4cpm-1.0.22}/setup.cfg +0 -0
- {python4cpm-1.0.20 → python4cpm-1.0.22}/src/python4cpm/args.py +0 -0
- {python4cpm-1.0.20 → python4cpm-1.0.22}/src/python4cpm/crypto.py +0 -0
- {python4cpm-1.0.20 → python4cpm-1.0.22}/src/python4cpm/secrets.py +0 -0
- {python4cpm-1.0.20 → python4cpm-1.0.22}/src/python4cpm.egg-info/dependency_links.txt +0 -0
- {python4cpm-1.0.20 → python4cpm-1.0.22}/src/python4cpm.egg-info/top_level.txt +0 -0
|
@@ -0,0 +1,404 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: python4cpm
|
|
3
|
+
Version: 1.0.22
|
|
4
|
+
Summary: Python for CPM
|
|
5
|
+
Author-email: Gonzalo Atienza Rela <gonatienza@gmail.com>
|
|
6
|
+
License: MIT License
|
|
7
|
+
|
|
8
|
+
Copyright (c) 2026 Gonzalo Atienza Rela
|
|
9
|
+
|
|
10
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
11
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
12
|
+
in the Software without restriction, including without limitation the rights
|
|
13
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
14
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
15
|
+
furnished to do so, subject to the following conditions:
|
|
16
|
+
|
|
17
|
+
The above copyright notice and this permission notice shall be included in
|
|
18
|
+
all copies or substantial portions of the Software.
|
|
19
|
+
|
|
20
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
21
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
22
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
23
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
24
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
25
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
26
|
+
THE SOFTWARE.
|
|
27
|
+
|
|
28
|
+
Requires-Python: >=3.10
|
|
29
|
+
Description-Content-Type: text/markdown
|
|
30
|
+
License-File: LICENSE
|
|
31
|
+
Dynamic: license-file
|
|
32
|
+
|
|
33
|
+
# Python4CPM
|
|
34
|
+
|
|
35
|
+
A simple way of using python scripts with CyberArk CPM rotations. This module leverages the [Credential Management .NET SDK](https://docs.cyberark.com/privilege-cloud-standard/latest/en/content/pasimp/plug-in-netinvoker.htm) from CyberArk to securely offload a password rotation logic into a python script.
|
|
36
|
+
|
|
37
|
+
This platform allows you to duplicate it multiple times, simply changing its settings from Privilege Cloud/PVWA to point to different python scripts leveraging the module `python4cpm`.
|
|
38
|
+
|
|
39
|
+
## Installation
|
|
40
|
+
|
|
41
|
+
### Preparing Python
|
|
42
|
+
|
|
43
|
+
1. Install Python in CPM.
|
|
44
|
+
- **Python must be installed for all users**. Follow the custom install steps from the installation wizard to check the checkbox.
|
|
45
|
+
3. Create a venv in CPM, by running `py -m venv c:\venv`. If desired, use a custom location and adjust any future references.
|
|
46
|
+
4. Install `python4cpm` in your venv:
|
|
47
|
+
- If your CPM can connect to the internet, install with `c:\venv\Scripts\pip install python4cpm`.
|
|
48
|
+
- If your CPM cannot connect to the internet:
|
|
49
|
+
- Download the latest `python4cpm-*.whl` file from the [pypi project files](https://pypi.org/project/python4cpm/#files).
|
|
50
|
+
- Copy the file to CPM and extract to a temporary directory called `python4cpm-wheel`.
|
|
51
|
+
- From the parent directory of `python4cpm-wheel` run `c:\venv\Scripts\pip install --no-index --find-links=.\python4cpm-wheel python4cpm`.
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
### Importing the platform
|
|
55
|
+
|
|
56
|
+
#### If you are using CPM (SaaS or Self-Hosted):
|
|
57
|
+
1. Download the latest [Credential Management .NET SDK](https://community.cyberark.com/marketplace/s/#a3550000000EkA0AAK-a3950000000jjoOAAQ) and place its content in the bin folder of CPM (`C:\Program Files (x86)\CyberArk\Password Manager\bin`). The files for this may already be present.
|
|
58
|
+
2. Download the `python4cpm-platform-*.zip` asset from the [release](https://github.com/gonatienza/python4cpm/releases).
|
|
59
|
+
3. Import the platform zip file into Privilege Cloud/PVWA `(Administration -> Platform Management -> Import platform)`.
|
|
60
|
+
4. Craft your python script and place it within a folder in CPM (e.g., `C:\python4cpm-scripts`).
|
|
61
|
+
5. Duplicate the imported platform in Privilege Cloud/PVWA `(Administration -> Platform Management -> Application -> Python for CPM)` and name it after your application (e.g., My App).
|
|
62
|
+
6. Edit the duplicated platform and specify the path of your script, under `Target Account Platform -> Automatic Platform Management -> Additional Policy Settings -> Parameters -> PythonScriptPath -> Value` (e.g., `C:\python4cpm-scripts\myapp.py`).
|
|
63
|
+
7. Also update `Target Account Platform -> Automatic Platform Management -> Additional Policy Settings -> Parameters -> PythonExePath -> Value` with the custom path for the venv's `python.exe` file (e.g., `c:\venv\Scripts\python.exe`).
|
|
64
|
+
8. If you want to disable logging, update `Target Account Platform -> Automatic Platform Management -> Additional Policy Settings -> Parameters -> PythonLogging -> Value` to `no`.
|
|
65
|
+
9. If you want to change the logging level to `debug`, update `Target Account Platform -> Automatic Platform Management -> Additional Policy Settings -> Parameters -> PythonLoggingLevel -> Value` to `debug`.
|
|
66
|
+
10. For new applications repeat steps from 4 to 9.
|
|
67
|
+
|
|
68
|
+
#### If you are using SRS (SaaS only):
|
|
69
|
+
1. Download the `python4cpm-platform-*.zip` asset from the [release](https://github.com/gonatienza/python4cpm/releases).
|
|
70
|
+
2. Import the platform zip file into Privilege Cloud `(Administration -> Platform Management -> Import platform)`.
|
|
71
|
+
3. Craft your python script and place it within a folder in the Cloud Connector (where the SRS Management Agent runs) (e.g., `C:\python4cpm-scripts`).
|
|
72
|
+
4. Duplicate the imported platform in Privilege Cloud/PVWA `(Administration -> Platform Management -> Application -> Python for CPM)` and name it after your application (e.g., My App).
|
|
73
|
+
5. Edit the duplicated platform and specify the path of your script, under `Plugin Settings -> Additional Parameters -> PythonScriptPath` (e.g., `C:\python4cpm-scripts\myapp.py`).
|
|
74
|
+
6. Also update `Plugin Settings -> Additional Parameters -> PythonExePath` with the custom path for the venv's `python.exe` file (e.g., `c:\venv\Scripts\python.exe`).
|
|
75
|
+
7. If you want to disable logging, update `Plugin Settings -> Additional Parameters -> PythonLogging` to `no`.
|
|
76
|
+
8. If you want to change the logging level to `debug`, update `Plugin Settings -> Additional Parameters -> PythonLoggingLevel -> Value` to `debug`.
|
|
77
|
+
9. For new applications repeat steps from 3 to 8.
|
|
78
|
+
|
|
79
|
+
## Python Script
|
|
80
|
+
|
|
81
|
+
### Using the handler (recommended):
|
|
82
|
+
|
|
83
|
+
```python
|
|
84
|
+
from python4cpm import Python4CPMHandler
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
class MyApp(Python4CPMHandler): # create a subclass for the Handler
|
|
88
|
+
"""
|
|
89
|
+
These are the usable properties and methods from Python4CPMHandler:
|
|
90
|
+
|
|
91
|
+
self.args.action # action requested from CPM/SRS
|
|
92
|
+
self.args.address # address from the account address field
|
|
93
|
+
self.args.username # username from the account username field
|
|
94
|
+
self.args.reconcile_username # reconcile username from the linked reconcile account
|
|
95
|
+
self.args.logon_username # logon username from the linked logon account
|
|
96
|
+
self.args.logging # used to carry the platform logging settings for python
|
|
97
|
+
self.secrets.password.get() # get str from password received from the vault
|
|
98
|
+
self.secrets.new_password.get() # get str from new password in case of a rotation
|
|
99
|
+
self.secrets.logon_password.get() # get str from linked logon account password
|
|
100
|
+
self.secrets.reconcile_password.get() # get str from linked reconcile account password
|
|
101
|
+
|
|
102
|
+
Logging methods -> Will only log if PythonLogging (platform parameters) is set to yes (default is yes)
|
|
103
|
+
|
|
104
|
+
self.log_error("this is an error message") # logs error into Logs/ThirdParty/MyApp.log
|
|
105
|
+
self.log_warning("this is a warning message") # logs warning into Logs/ThirdParty/MyApp.log
|
|
106
|
+
self.log_info("this is an info message") # logs info into Logs/ThirdParty/MyApp.log
|
|
107
|
+
|
|
108
|
+
Logging level -> Will only log debug messages if PythonLoggingLevel (platform parameters) is set to debug (default is info)
|
|
109
|
+
|
|
110
|
+
self.log_debug("this is an debug message") # logs info into Logs/ThirdParty/MyApp.log if logging level is set to debug
|
|
111
|
+
|
|
112
|
+
=============================
|
|
113
|
+
REQUIRED TERMINATION SIGNALS
|
|
114
|
+
=============================
|
|
115
|
+
Terminate signals -> MUST use one of the following three signals to terminate the script:
|
|
116
|
+
|
|
117
|
+
self.close_success() # terminate with success state
|
|
118
|
+
self.close_fail() # terminate with recoverable failed state
|
|
119
|
+
self.close_fail(unrecoverable=True) # terminate with unrecoverable failed state
|
|
120
|
+
|
|
121
|
+
When calling a signal sys.exit is invoked and the script is terminated. If no signal is called, and the script finishes without any exception, it will behave like p4cpm.close_fail(unrecoverable=True) and log an error message.
|
|
122
|
+
=============================
|
|
123
|
+
=============================
|
|
124
|
+
"""
|
|
125
|
+
|
|
126
|
+
# =============================
|
|
127
|
+
# REQUIRED METHODS (MUST DEFINE)
|
|
128
|
+
# =============================
|
|
129
|
+
# verify(), logon(), change(), prereconcile(), reconcile()
|
|
130
|
+
|
|
131
|
+
def verify(self):
|
|
132
|
+
self._verify()
|
|
133
|
+
self.log_info("verification successful")
|
|
134
|
+
self.close_success()
|
|
135
|
+
|
|
136
|
+
def logon(self):
|
|
137
|
+
self.close_success() # terminate with success state if nothing needs to be done with a given action.
|
|
138
|
+
|
|
139
|
+
def change(self):
|
|
140
|
+
self._change()
|
|
141
|
+
self.log_error("something went wrong")
|
|
142
|
+
self.close_fail()
|
|
143
|
+
|
|
144
|
+
def prereconcile(self):
|
|
145
|
+
self._verify(from_reconcile=True)
|
|
146
|
+
self.close_success()
|
|
147
|
+
|
|
148
|
+
def reconcile(self):
|
|
149
|
+
self._change(from_reconcile=True)
|
|
150
|
+
self.close_success()
|
|
151
|
+
|
|
152
|
+
def _verify(self, from_reconcile=False):
|
|
153
|
+
if from_reconcile is False:
|
|
154
|
+
pass
|
|
155
|
+
# TODO: use self.args.address, self.args.username, self.secrets.password.get()
|
|
156
|
+
# for your logic in a verification
|
|
157
|
+
else:
|
|
158
|
+
pass
|
|
159
|
+
# TODO: use self.args.address, self.args.reconcile_username, self.secrets.reconcile_password.get()
|
|
160
|
+
# for your logic in a verification
|
|
161
|
+
result = True
|
|
162
|
+
if result is True:
|
|
163
|
+
self.log_info("verification successful") # logs info message into Logs/ThirdParty/MyApp.log
|
|
164
|
+
else:
|
|
165
|
+
self.log_error("something went wrong") # logs error message Logs/ThirdParty/MyApp.log
|
|
166
|
+
self.close_fail()
|
|
167
|
+
|
|
168
|
+
def _change(self, from_reconcile=False):
|
|
169
|
+
if from_reconcile is False:
|
|
170
|
+
pass
|
|
171
|
+
# TODO: use self.args.address, self.args.username, self.secrets.password.get()
|
|
172
|
+
# and self.secrets.new_password.get() for your logic in a rotation
|
|
173
|
+
else:
|
|
174
|
+
pass
|
|
175
|
+
# TODO: use self.args.address, self.args.username, self.args.reconcile_username,
|
|
176
|
+
# self.secrets.reconcile_password.get() and self.secrets.new_password.get() for your logic in a reconciliation
|
|
177
|
+
result = True
|
|
178
|
+
if result is True:
|
|
179
|
+
self.log_info("rotation successful") # logs info message into Logs/ThirdParty/MyApp.log
|
|
180
|
+
else:
|
|
181
|
+
self.log_error("something went wrong") # logs error message Logs/ThirdParty/MyApp.log
|
|
182
|
+
self.close_fail()
|
|
183
|
+
|
|
184
|
+
|
|
185
|
+
if __name__ == "__main__":
|
|
186
|
+
MyApp().run() # initializes the class and calls the action that was requested from CPM/SRS.
|
|
187
|
+
```
|
|
188
|
+
(*) More realistic examples can be found [here](https://github.com/gonatienza/python4cpm/blob/main/examples/python4cpmhandler).
|
|
189
|
+
|
|
190
|
+
When doing `verify`, `change` or `reconcile` from Privilege Cloud/PVWA:
|
|
191
|
+
1. Verify -> the sciprt will be executed once with the `MyApp.verify()` method.
|
|
192
|
+
2. Change -> the sciprt will be executed twice, once with the action `MyApp.logon()` method and once as `MyApp.change()` method.
|
|
193
|
+
- If all actions are not terminated with `self.close_success()` and the scripts terminates without any exception, CPM/SRS will see this as a `self.close_fail(unrecoverable=True)`.
|
|
194
|
+
3. Reconcile -> the sciprt will be executed twice, once with the action `MyApp.prereconcile()` method and once as `MyApp.reconcile()` method.
|
|
195
|
+
- If all actions are not terminated with `self.close_success()` and the scripts terminates without any exception, CPM/SRS will see this as a `self.close_fail(unrecoverable=True)`.
|
|
196
|
+
4. When calling `MyApp.logon()` or `MyApp.prereconcile()`: `self.secrets.new_password.get()` will always return an empty string.
|
|
197
|
+
5. If a logon account is not linked, `self.args.logon_username` and `self.secrets.logon_password.get()` will return an empty string.
|
|
198
|
+
6. If a reconcile account is not linked, `self.args.reconcile_username` and `self.secrets.reconcile_password.get()` will return an empty string.
|
|
199
|
+
|
|
200
|
+
|
|
201
|
+
### Using Python4CPM properties and methods directly (for low level controls):
|
|
202
|
+
|
|
203
|
+
```python
|
|
204
|
+
from python4cpm import Python4CPM
|
|
205
|
+
|
|
206
|
+
|
|
207
|
+
p4cpm = Python4CPM("MyApp") # this instantiates the object and grabs all arguments and secrets shared by the .NET SDK
|
|
208
|
+
|
|
209
|
+
# These are the usable properties and related methods from the object:
|
|
210
|
+
p4cpm.args.action # action requested from CPM/SRS
|
|
211
|
+
p4cpm.args.address # address from the account address field
|
|
212
|
+
p4cpm.args.username # username from the account username field
|
|
213
|
+
p4cpm.args.reconcile_username # reconcile username from the linked reconcile account
|
|
214
|
+
p4cpm.args.logon_username # logon username from the linked logon account
|
|
215
|
+
p4cpm.args.logging # used to carry the platform logging settings for python
|
|
216
|
+
p4cpm.secrets.password.get() # get str from password received from the vault
|
|
217
|
+
p4cpm.secrets.new_password.get() # get str from new password in case of a rotation
|
|
218
|
+
p4cpm.secrets.logon_password.get() # get str from linked logon account password
|
|
219
|
+
p4cpm.secrets.reconcile_password.get() # get str from linked reconcile account password
|
|
220
|
+
|
|
221
|
+
# Logging methods -> Will only log if PythonLogging (platform parameters) is set to yes (default is yes)
|
|
222
|
+
p4cpm.log_error("this is an error message") # logs error into Logs/ThirdParty/MyApp.log
|
|
223
|
+
p4cpm.log_warning("this is a warning message") # logs warning into Logs/ThirdParty/MyApp.log
|
|
224
|
+
p4cpm.log_info("this is an info message") # logs info into Logs/ThirdParty/MyApp.log
|
|
225
|
+
# Logging level -> Will only log debug messages if PythonLoggingLevel (platform parameters) is set to debug (default is info)
|
|
226
|
+
p4cpm.log_debug("this is an debug message") # logs info into Logs/ThirdParty/MyApp.log if logging level is set to debug
|
|
227
|
+
|
|
228
|
+
# Terminate signals -> MUST use one of the following three signals to terminate the script:
|
|
229
|
+
## p4cpm.close_success() # terminate with success state
|
|
230
|
+
## p4cpm.close_fail() # terminate with recoverable failed state
|
|
231
|
+
## p4cpm.close_fail(unrecoverable=True) # terminate with unrecoverable failed state
|
|
232
|
+
# When calling a signal sys.exit is invoked and the script is terminated. If no signal is called, and the script finishes without any exception, it will behave like p4cpm.close_fail(unrecoverable=True) and log an error message.
|
|
233
|
+
|
|
234
|
+
|
|
235
|
+
# Verification example -> verify the username and password are valid
|
|
236
|
+
def verify(from_reconcile=False):
|
|
237
|
+
if from_reconcile is False:
|
|
238
|
+
pass
|
|
239
|
+
# TODO: use p4cpm.args.address, p4cpm.args.username, p4cpm.secrets.password.get()
|
|
240
|
+
# for your logic in a verification
|
|
241
|
+
else:
|
|
242
|
+
pass
|
|
243
|
+
# TODO: use p4cpm.args.address, p4cpm.args.reconcile_username, p4cpm.secrets.reconcile_password.get()
|
|
244
|
+
# for your logic in a verification
|
|
245
|
+
result = True
|
|
246
|
+
if result is True:
|
|
247
|
+
p4cpm.log_info("verification successful")
|
|
248
|
+
else:
|
|
249
|
+
p4cpm.log_error("something went wrong")
|
|
250
|
+
raise Exception("verify failed") # raise to trigger failed termination signal
|
|
251
|
+
|
|
252
|
+
|
|
253
|
+
# Rotation example -> rotate the password of the account
|
|
254
|
+
def change(from_reconcile=False):
|
|
255
|
+
if from_reconcile is False:
|
|
256
|
+
pass
|
|
257
|
+
# TODO: use p4cpm.args.address, p4cpm.args.username, p4cpm.secrets.password.get()
|
|
258
|
+
# and p4cpm.secrets.new_password.get() for your logic in a rotation
|
|
259
|
+
else:
|
|
260
|
+
pass
|
|
261
|
+
# TODO: use p4cpm.args.address, p4cpm.args.username, p4cpm.args.reconcile_username,
|
|
262
|
+
# p4cpm.secrets.reconcile_password.get() and p4cpm.secrets.new_password.get() for your logic in a reconciliation
|
|
263
|
+
result = True
|
|
264
|
+
if result is True:
|
|
265
|
+
p4cpm.log_info("rotation successful")
|
|
266
|
+
else:
|
|
267
|
+
p4cpm.log_error("something went wrong")
|
|
268
|
+
raise Exception("change failed") # raise to trigger failed termination signal
|
|
269
|
+
|
|
270
|
+
|
|
271
|
+
if __name__ == "__main__":
|
|
272
|
+
try:
|
|
273
|
+
if p4cpm.args.action == Python4CPM.ACTION_VERIFY: # class attribute ACTION_VERIFY holds the verify action value
|
|
274
|
+
verify()
|
|
275
|
+
p4cpm.close_success()
|
|
276
|
+
elif p4cpm.args.action == Python4CPM.ACTION_LOGON: # class attribute ACTION_LOGON holds the logon action value
|
|
277
|
+
p4cpm.close_success() # terminate with success state if nothing needs to be done with a given action.
|
|
278
|
+
elif p4cpm.args.action == Python4CPM.ACTION_CHANGE: # class attribute ACTION_CHANGE holds the password change action value
|
|
279
|
+
change()
|
|
280
|
+
p4cpm.close_success()
|
|
281
|
+
elif p4cpm.args.action == Python4CPM.ACTION_PRERECONCILE: # class attribute ACTION_PRERECONCILE holds the pre-reconcile action value
|
|
282
|
+
verify(from_reconcile=True)
|
|
283
|
+
p4cpm.close_success()
|
|
284
|
+
# Alternatively ->
|
|
285
|
+
## p4cpm.log_error("reconciliation is not supported") # let the logs know that reconciliation is not supported
|
|
286
|
+
## p4cpm.close_fail() # let CPM/SRS know to check the logs
|
|
287
|
+
elif p4cpm.args.action == Python4CPM.ACTION_RECONCILE: # class attribute ACTION_RECONCILE holds the reconcile action value
|
|
288
|
+
change(from_reconcile=True)
|
|
289
|
+
p4cpm.close_success()
|
|
290
|
+
# Alternatively ->
|
|
291
|
+
## p4cpm.log_error("reconciliation is not supported") # let the logs know that reconciliation is not supported
|
|
292
|
+
## p4cpm.close_fail() # let CPM/SRS know to check the logs
|
|
293
|
+
except Exception as e:
|
|
294
|
+
p4cpm.log_error(f"{type(e).__name__}: {e}")
|
|
295
|
+
raise e # CPM/SRS will see any Exception as a p4cpm.close_fail(unrecoverable=True)
|
|
296
|
+
```
|
|
297
|
+
(*) More realistic examples can be found [here](https://github.com/gonatienza/python4cpm/blob/main/examples/python4cpm).
|
|
298
|
+
|
|
299
|
+
When doing `verify`, `change` or `reconcile` from Privilege Cloud/PVWA:
|
|
300
|
+
1. Verify -> the sciprt will be executed once with the `p4cpm.args.action` as `Python4CPM.ACTION_VERIFY`.
|
|
301
|
+
2. Change -> the sciprt will be executed twice, once with the action `p4cpm.args.action` as `Python4CPM.ACTION_LOGON` and once as `Python4CPM.ACTION_CHANGE`.
|
|
302
|
+
- If all actions are not terminated with `p4cpm.close_success()` and the scripts terminates without any exception, CPM/SRS will see this as a `p4cpm.close_fail(unrecoverable=True)`.
|
|
303
|
+
3. Reconcile -> the sciprt will be executed twice, once with the `p4cpm.args.action` as `Python4CPM.ACTION_PRERECONCILE` and once as `Python4CPM.ACTION_RECONCILE`.
|
|
304
|
+
- If all actions are not terminated with `p4cpm.close_success()` and the scripts terminates without any exception, CPM/SRS will see this as a `p4cpm.close_fail(unrecoverable=True)`.
|
|
305
|
+
4. When `p4cpm.args.action` comes as `Python4CPM.ACTION_VERIFY`, `Python4CPM.ACTION_LOGON` or `Python4CPM.ACTION_PRERECONCILE`: `p4cpm.secrets.new_password.get()` will always return an empty string.
|
|
306
|
+
5. If a logon account is not linked, `p4cpm.args.logon_username` and `p4cpm.secrets.logon_password.get()` will return an empty string.
|
|
307
|
+
6. If a reconcile account is not linked, `p4cpm.args.reconcile_username` and `p4cpm.secrets.reconcile_password.get()` will return an empty string.
|
|
308
|
+
|
|
309
|
+
|
|
310
|
+
### Installing dependencies in python venv
|
|
311
|
+
|
|
312
|
+
As with any python venv, you can install dependencies in your venv.
|
|
313
|
+
1. If your CPM can connect to the internet:
|
|
314
|
+
- You can use regular pip install commands (e.g., `c:\venv\Scripts\pip.exe install requests`).
|
|
315
|
+
2. If your CPM cannot connect to the internet:
|
|
316
|
+
- You can download packages for an offline install. More info [here](https://pip.pypa.io/en/stable/cli/pip_download/).
|
|
317
|
+
|
|
318
|
+
|
|
319
|
+
## Dev Helper:
|
|
320
|
+
|
|
321
|
+
For dev purposes, `NETHelper` is a companion helper that simplifies the instantiation of the `Python4CPM` or `Python4CPMHandler` objects by simulating how the plugin passes arguments and secrets to the modules.
|
|
322
|
+
Install this module (in a dev workstation) with:
|
|
323
|
+
|
|
324
|
+
```bash
|
|
325
|
+
pip install python4cpm
|
|
326
|
+
```
|
|
327
|
+
|
|
328
|
+
**Note**: As CPM runs in Windows, the plugin was built to pass secrets securely to the `Python4CPM.crypto` module using the Data Protection API (DPAPI). For dev purposes in Linux/Mac dev workstations, those secrets will appear as plaintext in the environment of the process. This is informational only, the module will use its encryption/decryption capabilities automatically in Windows and you do not have to do anything specific to enable it.
|
|
329
|
+
|
|
330
|
+
### Example:
|
|
331
|
+
|
|
332
|
+
```python
|
|
333
|
+
from python4cpm import NETHelper, Python4CPM, Python4CPMHandler
|
|
334
|
+
from getpass import getpass
|
|
335
|
+
|
|
336
|
+
# Get secrets for your password, logon account password, reconcile account password and new password
|
|
337
|
+
# You can use an empty string if it does not apply
|
|
338
|
+
password = getpass("password: ") # password from account
|
|
339
|
+
logon_password = getpass("logon_password: ") # password from linked logon account
|
|
340
|
+
reconcile_password = getpass("reconcile_password: ") # password from linked reconcile account
|
|
341
|
+
new_password = getpass("new_password: ") # new password for the rotation
|
|
342
|
+
```
|
|
343
|
+
|
|
344
|
+
#### Set your arguments and secrets:
|
|
345
|
+
|
|
346
|
+
```python
|
|
347
|
+
NETHelper.set(
|
|
348
|
+
action=Python4CPM.ACTION_LOGON, # use actions from Python4CPM.ACTION_*
|
|
349
|
+
address="myapp.corp.local", # populate with the address from your account properties
|
|
350
|
+
username="jdoe", # populate with the username from your account properties
|
|
351
|
+
logon_username="ldoe", # populate with the logon account username from your linked logon account
|
|
352
|
+
reconcile_username="rdoe", # ppopulate with the reconcile account username from your linked logon account
|
|
353
|
+
logging="yes", # populate with the PythonLogging parameter from the platform: "yes" or "no"
|
|
354
|
+
logging_level="info", # populate with the PythonLoggingLevel parameter from the platform: "info" or "debug"
|
|
355
|
+
password=password,
|
|
356
|
+
logon_password=logon_password,
|
|
357
|
+
reconcile_password=reconcile_password,
|
|
358
|
+
new_password=new_password
|
|
359
|
+
)
|
|
360
|
+
```
|
|
361
|
+
|
|
362
|
+
#### Using the handler (recommended):
|
|
363
|
+
|
|
364
|
+
```python
|
|
365
|
+
class MyApp(Python4CPMHandler):
|
|
366
|
+
def verify(self):
|
|
367
|
+
# TODO: Add your logic here
|
|
368
|
+
self.close_success()
|
|
369
|
+
|
|
370
|
+
def logon(self):
|
|
371
|
+
# TODO: Add your logic here
|
|
372
|
+
self.close_success()
|
|
373
|
+
|
|
374
|
+
def change(self):
|
|
375
|
+
# TODO: Add your logic here
|
|
376
|
+
self.close_success()
|
|
377
|
+
|
|
378
|
+
def prereconcile(self):
|
|
379
|
+
# TODO: Add your logic here
|
|
380
|
+
self.close_success()
|
|
381
|
+
|
|
382
|
+
def reconcile(self):
|
|
383
|
+
# TODO: Add your logic here
|
|
384
|
+
self.close_success()
|
|
385
|
+
|
|
386
|
+
MyApp().run()
|
|
387
|
+
```
|
|
388
|
+
|
|
389
|
+
#### Using Python4CPM properties and methods directly:
|
|
390
|
+
|
|
391
|
+
```python
|
|
392
|
+
p4cpm = NETHelper.get()
|
|
393
|
+
|
|
394
|
+
# TODO: use the p4cpm object during dev to build your script logic
|
|
395
|
+
assert password == p4cpm.secrets.password.get()
|
|
396
|
+
p4cpm.log_info("success!")
|
|
397
|
+
p4cpm.close_success()
|
|
398
|
+
```
|
|
399
|
+
|
|
400
|
+
#### Remember for your final script:
|
|
401
|
+
|
|
402
|
+
- Change the definition of `p4cpm` from `p4cpm = NETHelper.get()` to `p4cpm = Python4CPM("MyApp")` if you are using the properties and methods directly.
|
|
403
|
+
- Remove any secrets prompting or interactive interruptions.
|
|
404
|
+
- Remove the import of `NETHelper`.
|