python4cpm 1.0.21__tar.gz → 1.0.23__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.21/src/python4cpm.egg-info → python4cpm-1.0.23}/PKG-INFO +202 -76
- {python4cpm-1.0.21 → python4cpm-1.0.23}/README.md +200 -53
- {python4cpm-1.0.21 → python4cpm-1.0.23}/pyproject.toml +3 -2
- {python4cpm-1.0.21 → python4cpm-1.0.23}/src/python4cpm/__init__.py +2 -7
- python4cpm-1.0.23/src/python4cpm/logger.py +44 -0
- {python4cpm-1.0.21 → python4cpm-1.0.23}/src/python4cpm/nethelper.py +9 -4
- {python4cpm-1.0.21 → python4cpm-1.0.23}/src/python4cpm/python4cpm.py +24 -33
- python4cpm-1.0.23/src/python4cpm/python4cpmhandler.py +41 -0
- {python4cpm-1.0.21 → python4cpm-1.0.23/src/python4cpm.egg-info}/PKG-INFO +202 -76
- {python4cpm-1.0.21 → python4cpm-1.0.23}/src/python4cpm.egg-info/SOURCES.txt +1 -0
- python4cpm-1.0.21/src/python4cpm/logger.py +0 -40
- {python4cpm-1.0.21 → python4cpm-1.0.23}/LICENSE +0 -0
- {python4cpm-1.0.21 → python4cpm-1.0.23}/setup.cfg +0 -0
- {python4cpm-1.0.21 → python4cpm-1.0.23}/src/python4cpm/args.py +0 -0
- {python4cpm-1.0.21 → python4cpm-1.0.23}/src/python4cpm/crypto.py +0 -0
- {python4cpm-1.0.21 → python4cpm-1.0.23}/src/python4cpm/secrets.py +0 -0
- {python4cpm-1.0.21 → python4cpm-1.0.23}/src/python4cpm.egg-info/dependency_links.txt +0 -0
- {python4cpm-1.0.21 → python4cpm-1.0.23}/src/python4cpm.egg-info/top_level.txt +0 -0
|
@@ -1,30 +1,9 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: python4cpm
|
|
3
|
-
Version: 1.0.
|
|
3
|
+
Version: 1.0.23
|
|
4
4
|
Summary: Python for CPM
|
|
5
5
|
Author-email: Gonzalo Atienza Rela <gonatienza@gmail.com>
|
|
6
|
-
License: MIT
|
|
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
|
-
|
|
6
|
+
License-Expression: MIT
|
|
28
7
|
Requires-Python: >=3.10
|
|
29
8
|
Description-Content-Type: text/markdown
|
|
30
9
|
License-File: LICENSE
|
|
@@ -32,7 +11,7 @@ Dynamic: license-file
|
|
|
32
11
|
|
|
33
12
|
# Python4CPM
|
|
34
13
|
|
|
35
|
-
A simple way of using python scripts with CyberArk CPM rotations. This module
|
|
14
|
+
A simple way of using python scripts with CyberArk CPM/SRS 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
15
|
|
|
37
16
|
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
17
|
|
|
@@ -40,14 +19,14 @@ This platform allows you to duplicate it multiple times, simply changing its set
|
|
|
40
19
|
|
|
41
20
|
### Preparing Python
|
|
42
21
|
|
|
43
|
-
1. Install Python
|
|
22
|
+
1. Install Python along CPM or the SRS Connector Management Agent.
|
|
44
23
|
- **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
|
|
24
|
+
3. Create a venv in the server, by running `py -m venv c:\venv`. If desired, use a custom location and adjust any future references.
|
|
46
25
|
4. Install `python4cpm` in your venv:
|
|
47
26
|
- If your CPM can connect to the internet, install with `c:\venv\Scripts\pip install python4cpm`.
|
|
48
27
|
- If your CPM cannot connect to the internet:
|
|
49
28
|
- Download the latest `python4cpm-*.whl` file from the [pypi project files](https://pypi.org/project/python4cpm/#files).
|
|
50
|
-
- Copy the file to
|
|
29
|
+
- Copy the file to the server into a temporary directory called `python4cpm-wheel`.
|
|
51
30
|
- From the parent directory of `python4cpm-wheel` run `c:\venv\Scripts\pip install --no-index --find-links=.\python4cpm-wheel python4cpm`.
|
|
52
31
|
|
|
53
32
|
|
|
@@ -78,7 +57,127 @@ This platform allows you to duplicate it multiple times, simply changing its set
|
|
|
78
57
|
|
|
79
58
|
## Python Script
|
|
80
59
|
|
|
81
|
-
###
|
|
60
|
+
### Using the handler (recommended):
|
|
61
|
+
|
|
62
|
+
```python
|
|
63
|
+
from python4cpm import Python4CPMHandler
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
class MyRotator(Python4CPMHandler): # create a subclass for the Handler
|
|
67
|
+
"""
|
|
68
|
+
These are the usable properties and methods from Python4CPMHandler:
|
|
69
|
+
|
|
70
|
+
self.args.action # action requested from CPM/SRS
|
|
71
|
+
self.args.address # address from the account address field
|
|
72
|
+
self.args.username # username from the account username field
|
|
73
|
+
self.args.reconcile_username # reconcile username from the linked reconcile account
|
|
74
|
+
self.args.logon_username # logon username from the linked logon account
|
|
75
|
+
self.args.logging # used to carry the platform logging settings for python
|
|
76
|
+
self.secrets.password.get() # get str from password received from the vault
|
|
77
|
+
self.secrets.new_password.get() # get str from new password in case of a rotation
|
|
78
|
+
self.secrets.logon_password.get() # get str from linked logon account password
|
|
79
|
+
self.secrets.reconcile_password.get() # get str from linked reconcile account password
|
|
80
|
+
|
|
81
|
+
Logging methods -> Will only log if PythonLogging (platform parameters) is set to yes (default is yes)
|
|
82
|
+
|
|
83
|
+
self.log_error("this is an error message") # logs error into Logs/ThirdParty/MyRotator.log
|
|
84
|
+
self.log_warning("this is a warning message") # logs warning into Logs/ThirdParty/MyRotator.log
|
|
85
|
+
self.log_info("this is an info message") # logs info into Logs/ThirdParty/MyRotator.log
|
|
86
|
+
|
|
87
|
+
Logging level -> Will only log debug messages if PythonLoggingLevel (platform parameters) is set to debug (default is info)
|
|
88
|
+
|
|
89
|
+
self.log_debug("this is an debug message") # logs info into Logs/ThirdParty/MyRotator.log if logging level is set to debug
|
|
90
|
+
|
|
91
|
+
=============================
|
|
92
|
+
REQUIRED TERMINATION SIGNALS
|
|
93
|
+
=============================
|
|
94
|
+
Terminate signals -> MUST use one of the following three signals to terminate the script:
|
|
95
|
+
|
|
96
|
+
self.close_success() # terminate with success state
|
|
97
|
+
self.close_fail() # terminate with recoverable failed state
|
|
98
|
+
self.close_fail(unrecoverable=True) # terminate with unrecoverable failed state
|
|
99
|
+
|
|
100
|
+
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.
|
|
101
|
+
=============================
|
|
102
|
+
=============================
|
|
103
|
+
"""
|
|
104
|
+
|
|
105
|
+
# =============================
|
|
106
|
+
# REQUIRED METHODS (MUST DEFINE)
|
|
107
|
+
# =============================
|
|
108
|
+
# verify(), logon(), change(), prereconcile(), reconcile()
|
|
109
|
+
|
|
110
|
+
def verify(self):
|
|
111
|
+
self._verify()
|
|
112
|
+
self.log_info("verification successful")
|
|
113
|
+
self.close_success()
|
|
114
|
+
|
|
115
|
+
def logon(self):
|
|
116
|
+
self.close_success() # terminate with success state if nothing needs to be done with a given action.
|
|
117
|
+
|
|
118
|
+
def change(self):
|
|
119
|
+
self._change()
|
|
120
|
+
self.log_error("something went wrong")
|
|
121
|
+
self.close_fail()
|
|
122
|
+
|
|
123
|
+
def prereconcile(self):
|
|
124
|
+
self._verify(from_reconcile=True)
|
|
125
|
+
self.close_success()
|
|
126
|
+
|
|
127
|
+
def reconcile(self):
|
|
128
|
+
self._change(from_reconcile=True)
|
|
129
|
+
self.close_success()
|
|
130
|
+
|
|
131
|
+
def _verify(self, from_reconcile=False):
|
|
132
|
+
if from_reconcile is False:
|
|
133
|
+
pass
|
|
134
|
+
# TODO: use self.args.address, self.args.username, self.secrets.password.get()
|
|
135
|
+
# for your logic in a verification
|
|
136
|
+
else:
|
|
137
|
+
pass
|
|
138
|
+
# TODO: use self.args.address, self.args.reconcile_username, self.secrets.reconcile_password.get()
|
|
139
|
+
# for your logic in a verification
|
|
140
|
+
result = True
|
|
141
|
+
if result is True:
|
|
142
|
+
self.log_info("verification successful") # logs info message into Logs/ThirdParty/MyRotator.log
|
|
143
|
+
else:
|
|
144
|
+
self.log_error("something went wrong") # logs error message Logs/ThirdParty/MyRotator.log
|
|
145
|
+
self.close_fail()
|
|
146
|
+
|
|
147
|
+
def _change(self, from_reconcile=False):
|
|
148
|
+
if from_reconcile is False:
|
|
149
|
+
pass
|
|
150
|
+
# TODO: use self.args.address, self.args.username, self.secrets.password.get()
|
|
151
|
+
# and self.secrets.new_password.get() for your logic in a rotation
|
|
152
|
+
else:
|
|
153
|
+
pass
|
|
154
|
+
# TODO: use self.args.address, self.args.username, self.args.reconcile_username,
|
|
155
|
+
# self.secrets.reconcile_password.get() and self.secrets.new_password.get() for your logic in a reconciliation
|
|
156
|
+
result = True
|
|
157
|
+
if result is True:
|
|
158
|
+
self.log_info("rotation successful") # logs info message into Logs/ThirdParty/MyRotator.log
|
|
159
|
+
else:
|
|
160
|
+
self.log_error("something went wrong") # logs error message Logs/ThirdParty/MyRotator.log
|
|
161
|
+
self.close_fail()
|
|
162
|
+
|
|
163
|
+
|
|
164
|
+
if __name__ == "__main__":
|
|
165
|
+
MyRotator().run() # initializes the class and calls the action that was requested from CPM/SRS.
|
|
166
|
+
```
|
|
167
|
+
(*) More realistic examples can be found [here](https://github.com/gonatienza/python4cpm/blob/main/examples/python4cpmhandler).
|
|
168
|
+
|
|
169
|
+
When doing `verify`, `change` or `reconcile` from Privilege Cloud/PVWA:
|
|
170
|
+
1. Verify -> the sciprt will be executed once running the `MyRotator.verify()` method.
|
|
171
|
+
2. Change -> the sciprt will be executed twice, running first the `MyRotator.logon()` method and secondly the `MyRotator.change()` method.
|
|
172
|
+
- If both 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)`.
|
|
173
|
+
3. Reconcile -> the sciprt will be executed twice, running first the `MyRotator.prereconcile()` method and secondly the `MyRotator.reconcile()` method.
|
|
174
|
+
- If both 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)`.
|
|
175
|
+
4. When calling `MyRotator.verify()`, `MyRotator.logon()` or `MyRotator.prereconcile()`: `self.secrets.new_password.get()` will always return an empty string.
|
|
176
|
+
5. If a logon account is not linked, `self.args.logon_username` and `self.secrets.logon_password.get()` will return empty strings.
|
|
177
|
+
6. If a reconcile account is not linked, `self.args.reconcile_username` and `self.secrets.reconcile_password.get()` will return empty strings.
|
|
178
|
+
|
|
179
|
+
|
|
180
|
+
### Using Python4CPM properties and methods directly (for low level controls):
|
|
82
181
|
|
|
83
182
|
```python
|
|
84
183
|
from python4cpm import Python4CPM
|
|
@@ -87,7 +186,7 @@ from python4cpm import Python4CPM
|
|
|
87
186
|
p4cpm = Python4CPM("MyApp") # this instantiates the object and grabs all arguments and secrets shared by the .NET SDK
|
|
88
187
|
|
|
89
188
|
# These are the usable properties and related methods from the object:
|
|
90
|
-
p4cpm.args.action # action requested from CPM
|
|
189
|
+
p4cpm.args.action # action requested from CPM/SRS
|
|
91
190
|
p4cpm.args.address # address from the account address field
|
|
92
191
|
p4cpm.args.username # username from the account username field
|
|
93
192
|
p4cpm.args.reconcile_username # reconcile username from the linked reconcile account
|
|
@@ -98,36 +197,35 @@ p4cpm.secrets.new_password.get() # get str from new password in case of a rotati
|
|
|
98
197
|
p4cpm.secrets.logon_password.get() # get str from linked logon account password
|
|
99
198
|
p4cpm.secrets.reconcile_password.get() # get str from linked reconcile account password
|
|
100
199
|
|
|
101
|
-
# Logging methods -> Will only log if
|
|
102
|
-
p4cpm.log_error("this is an error message") # logs error into Logs/ThirdParty/
|
|
103
|
-
p4cpm.log_warning("this is a warning message") # logs warning into Logs/ThirdParty/
|
|
104
|
-
p4cpm.log_info("this is an info message") # logs info into Logs/ThirdParty/
|
|
105
|
-
# Logging level -> Will only log debug messages if
|
|
106
|
-
p4cpm.log_debug("this is an debug message") # logs info into Logs/ThirdParty/
|
|
200
|
+
# Logging methods -> Will only log if PythonLogging (platform parameters) is set to yes (default is yes)
|
|
201
|
+
p4cpm.log_error("this is an error message") # logs error into Logs/ThirdParty/MyApp.log
|
|
202
|
+
p4cpm.log_warning("this is a warning message") # logs warning into Logs/ThirdParty/MyApp.log
|
|
203
|
+
p4cpm.log_info("this is an info message") # logs info into Logs/ThirdParty/MyApp.log
|
|
204
|
+
# Logging level -> Will only log debug messages if PythonLoggingLevel (platform parameters) is set to debug (default is info)
|
|
205
|
+
p4cpm.log_debug("this is an debug message") # logs info into Logs/ThirdParty/MyApp.log if logging level is set to debug
|
|
107
206
|
|
|
108
207
|
# Terminate signals -> MUST use one of the following three signals to terminate the script:
|
|
109
208
|
## p4cpm.close_success() # terminate with success state
|
|
110
209
|
## p4cpm.close_fail() # terminate with recoverable failed state
|
|
111
210
|
## p4cpm.close_fail(unrecoverable=True) # terminate with unrecoverable failed state
|
|
112
|
-
# When calling a signal sys.exit is invoked and the script is terminated.
|
|
113
|
-
# 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.
|
|
211
|
+
# 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.
|
|
114
212
|
|
|
115
213
|
|
|
116
214
|
# Verification example -> verify the username and password are valid
|
|
117
215
|
def verify(from_reconcile=False):
|
|
118
216
|
if from_reconcile is False:
|
|
119
217
|
pass
|
|
120
|
-
#
|
|
218
|
+
# TODO: use p4cpm.args.address, p4cpm.args.username, p4cpm.secrets.password.get()
|
|
121
219
|
# for your logic in a verification
|
|
122
220
|
else:
|
|
123
221
|
pass
|
|
124
|
-
#
|
|
222
|
+
# TODO: use p4cpm.args.address, p4cpm.args.reconcile_username, p4cpm.secrets.reconcile_password.get()
|
|
125
223
|
# for your logic in a verification
|
|
126
224
|
result = True
|
|
127
225
|
if result is True:
|
|
128
|
-
p4cpm.log_info("verification successful")
|
|
226
|
+
p4cpm.log_info("verification successful")
|
|
129
227
|
else:
|
|
130
|
-
p4cpm.log_error("something went wrong")
|
|
228
|
+
p4cpm.log_error("something went wrong")
|
|
131
229
|
raise Exception("verify failed") # raise to trigger failed termination signal
|
|
132
230
|
|
|
133
231
|
|
|
@@ -135,17 +233,17 @@ def verify(from_reconcile=False):
|
|
|
135
233
|
def change(from_reconcile=False):
|
|
136
234
|
if from_reconcile is False:
|
|
137
235
|
pass
|
|
138
|
-
#
|
|
236
|
+
# TODO: use p4cpm.args.address, p4cpm.args.username, p4cpm.secrets.password.get()
|
|
139
237
|
# and p4cpm.secrets.new_password.get() for your logic in a rotation
|
|
140
238
|
else:
|
|
141
239
|
pass
|
|
142
|
-
#
|
|
240
|
+
# TODO: use p4cpm.args.address, p4cpm.args.username, p4cpm.args.reconcile_username,
|
|
143
241
|
# p4cpm.secrets.reconcile_password.get() and p4cpm.secrets.new_password.get() for your logic in a reconciliation
|
|
144
242
|
result = True
|
|
145
243
|
if result is True:
|
|
146
|
-
p4cpm.log_info("rotation successful")
|
|
244
|
+
p4cpm.log_info("rotation successful")
|
|
147
245
|
else:
|
|
148
|
-
p4cpm.log_error("something went wrong")
|
|
246
|
+
p4cpm.log_error("something went wrong")
|
|
149
247
|
raise Exception("change failed") # raise to trigger failed termination signal
|
|
150
248
|
|
|
151
249
|
|
|
@@ -153,48 +251,44 @@ if __name__ == "__main__":
|
|
|
153
251
|
try:
|
|
154
252
|
if p4cpm.args.action == Python4CPM.ACTION_VERIFY: # class attribute ACTION_VERIFY holds the verify action value
|
|
155
253
|
verify()
|
|
156
|
-
p4cpm.close_success()
|
|
254
|
+
p4cpm.close_success()
|
|
157
255
|
elif p4cpm.args.action == Python4CPM.ACTION_LOGON: # class attribute ACTION_LOGON holds the logon action value
|
|
158
|
-
|
|
159
|
-
p4cpm.close_success() # terminate with success state
|
|
256
|
+
p4cpm.close_success() # terminate with success state if nothing needs to be done with a given action.
|
|
160
257
|
elif p4cpm.args.action == Python4CPM.ACTION_CHANGE: # class attribute ACTION_CHANGE holds the password change action value
|
|
161
258
|
change()
|
|
162
|
-
p4cpm.close_success()
|
|
259
|
+
p4cpm.close_success()
|
|
163
260
|
elif p4cpm.args.action == Python4CPM.ACTION_PRERECONCILE: # class attribute ACTION_PRERECONCILE holds the pre-reconcile action value
|
|
164
261
|
verify(from_reconcile=True)
|
|
165
|
-
p4cpm.close_success()
|
|
262
|
+
p4cpm.close_success()
|
|
166
263
|
# Alternatively ->
|
|
167
264
|
## p4cpm.log_error("reconciliation is not supported") # let the logs know that reconciliation is not supported
|
|
168
|
-
## p4cpm.close_fail() # let CPM know to check the logs
|
|
265
|
+
## p4cpm.close_fail() # let CPM/SRS know to check the logs
|
|
169
266
|
elif p4cpm.args.action == Python4CPM.ACTION_RECONCILE: # class attribute ACTION_RECONCILE holds the reconcile action value
|
|
170
267
|
change(from_reconcile=True)
|
|
171
|
-
p4cpm.close_success()
|
|
268
|
+
p4cpm.close_success()
|
|
172
269
|
# Alternatively ->
|
|
173
270
|
## p4cpm.log_error("reconciliation is not supported") # let the logs know that reconciliation is not supported
|
|
174
|
-
## p4cpm.close_fail() # let CPM know to check the logs
|
|
175
|
-
else:
|
|
176
|
-
p4cpm.log_error(f"invalid action: '{p4cpm.args.action}'") # logs into Logs/ThirdParty/Python4CPM/MyApp.log
|
|
177
|
-
p4cpm.close_fail(unrecoverable=True) # terminate with unrecoverable failed state
|
|
271
|
+
## p4cpm.close_fail() # let CPM/SRS know to check the logs
|
|
178
272
|
except Exception as e:
|
|
179
273
|
p4cpm.log_error(f"{type(e).__name__}: {e}")
|
|
180
|
-
p4cpm.close_fail()
|
|
274
|
+
raise e # CPM/SRS will see any Exception as a p4cpm.close_fail(unrecoverable=True)
|
|
181
275
|
```
|
|
182
|
-
(*) More realistic examples can be found [here](https://github.com/gonatienza/python4cpm/blob/main/examples).
|
|
276
|
+
(*) More realistic examples can be found [here](https://github.com/gonatienza/python4cpm/blob/main/examples/python4cpm).
|
|
183
277
|
|
|
184
278
|
When doing `verify`, `change` or `reconcile` from Privilege Cloud/PVWA:
|
|
185
279
|
1. Verify -> the sciprt will be executed once with the `p4cpm.args.action` as `Python4CPM.ACTION_VERIFY`.
|
|
186
280
|
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`.
|
|
187
|
-
- If
|
|
281
|
+
- If both 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)`.
|
|
188
282
|
3. Reconcile -> the sciprt will be executed twice, once with the `p4cpm.args.action` as `Python4CPM.ACTION_PRERECONCILE` and once as `Python4CPM.ACTION_RECONCILE`.
|
|
189
|
-
- If
|
|
283
|
+
- If both 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)`.
|
|
190
284
|
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.
|
|
191
|
-
5. If a logon account is not linked, `p4cpm.args.logon_username` and `p4cpm.secrets.logon_password.get()` will return
|
|
192
|
-
6. If a reconcile account is not linked, `p4cpm.args.reconcile_username` and `p4cpm.secrets.reconcile_password.get()` will return
|
|
285
|
+
5. If a logon account is not linked, `p4cpm.args.logon_username` and `p4cpm.secrets.logon_password.get()` will return empty strings.
|
|
286
|
+
6. If a reconcile account is not linked, `p4cpm.args.reconcile_username` and `p4cpm.secrets.reconcile_password.get()` will return empty strings.
|
|
193
287
|
|
|
194
288
|
|
|
195
|
-
### Installing
|
|
289
|
+
### Installing dependencies in python venv
|
|
196
290
|
|
|
197
|
-
As with any python venv, you can install
|
|
291
|
+
As with any python venv, you can install dependencies in your venv.
|
|
198
292
|
1. If your CPM can connect to the internet:
|
|
199
293
|
- You can use regular pip install commands (e.g., `c:\venv\Scripts\pip.exe install requests`).
|
|
200
294
|
2. If your CPM cannot connect to the internet:
|
|
@@ -203,7 +297,7 @@ As with any python venv, you can install dependancies in your venv.
|
|
|
203
297
|
|
|
204
298
|
## Dev Helper:
|
|
205
299
|
|
|
206
|
-
For dev purposes, `NETHelper` is a companion helper that simplifies the instantiation of the `Python4CPM`
|
|
300
|
+
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.
|
|
207
301
|
Install this module (in a dev workstation) with:
|
|
208
302
|
|
|
209
303
|
```bash
|
|
@@ -214,8 +308,10 @@ pip install python4cpm
|
|
|
214
308
|
|
|
215
309
|
### Example:
|
|
216
310
|
|
|
311
|
+
#### Set your arguments and secrets:
|
|
312
|
+
|
|
217
313
|
```python
|
|
218
|
-
from python4cpm import NETHelper, Python4CPM
|
|
314
|
+
from python4cpm import NETHelper, Python4CPM, Python4CPMHandler
|
|
219
315
|
from getpass import getpass
|
|
220
316
|
|
|
221
317
|
# Get secrets for your password, logon account password, reconcile account password and new password
|
|
@@ -225,7 +321,7 @@ logon_password = getpass("logon_password: ") # password from linked logon accoun
|
|
|
225
321
|
reconcile_password = getpass("reconcile_password: ") # password from linked reconcile account
|
|
226
322
|
new_password = getpass("new_password: ") # new password for the rotation
|
|
227
323
|
|
|
228
|
-
|
|
324
|
+
NETHelper.set(
|
|
229
325
|
action=Python4CPM.ACTION_LOGON, # use actions from Python4CPM.ACTION_*
|
|
230
326
|
address="myapp.corp.local", # populate with the address from your account properties
|
|
231
327
|
username="jdoe", # populate with the username from your account properties
|
|
@@ -238,19 +334,49 @@ p4cpm = NETHelper.run(
|
|
|
238
334
|
reconcile_password=reconcile_password,
|
|
239
335
|
new_password=new_password
|
|
240
336
|
)
|
|
337
|
+
```
|
|
338
|
+
|
|
339
|
+
#### Using the handler (recommended):
|
|
340
|
+
|
|
341
|
+
```python
|
|
342
|
+
class MyRotator(Python4CPMHandler):
|
|
343
|
+
def verify(self):
|
|
344
|
+
# TODO: Add your logic here
|
|
345
|
+
self.close_success()
|
|
346
|
+
|
|
347
|
+
def logon(self):
|
|
348
|
+
# TODO: Add your logic here
|
|
349
|
+
self.close_success()
|
|
241
350
|
|
|
242
|
-
|
|
351
|
+
def change(self):
|
|
352
|
+
# TODO: Add your logic here
|
|
353
|
+
self.close_success()
|
|
354
|
+
|
|
355
|
+
def prereconcile(self):
|
|
356
|
+
# TODO: Add your logic here
|
|
357
|
+
self.close_success()
|
|
358
|
+
|
|
359
|
+
def reconcile(self):
|
|
360
|
+
# TODO: Add your logic here
|
|
361
|
+
self.close_success()
|
|
362
|
+
|
|
363
|
+
MyRotator().run()
|
|
364
|
+
```
|
|
365
|
+
|
|
366
|
+
#### Using Python4CPM properties and methods directly:
|
|
367
|
+
|
|
368
|
+
```python
|
|
369
|
+
p4cpm = NETHelper.get()
|
|
370
|
+
|
|
371
|
+
# TODO: use the p4cpm object during dev to build your script logic
|
|
243
372
|
assert password == p4cpm.secrets.password.get()
|
|
244
373
|
p4cpm.log_info("success!")
|
|
245
374
|
p4cpm.close_success()
|
|
246
|
-
|
|
247
|
-
# Remember for your final script:
|
|
248
|
-
## changing the definition of p4cpm from NETHelper.run() to Python4CPM("MyApp")
|
|
249
|
-
## remove any secrets prompting
|
|
250
|
-
## remove the NETHelper import
|
|
251
375
|
```
|
|
252
376
|
|
|
253
|
-
Remember for your final script:
|
|
254
|
-
|
|
255
|
-
- Remove any secrets prompting or interactive interruptions.
|
|
377
|
+
#### Remember for your final script:
|
|
378
|
+
|
|
256
379
|
- Remove the import of `NETHelper`.
|
|
380
|
+
- Remove the `NETHelper.set()` call.
|
|
381
|
+
- If applicable, change the definition of `p4cpm` from `p4cpm = NETHelper.get()` to `p4cpm = Python4CPM("MyApp")`.
|
|
382
|
+
- Remove any secrets prompting or interactive interruptions.
|