python4cpm 1.1.2__tar.gz → 1.1.4__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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: python4cpm
3
- Version: 1.1.2
3
+ Version: 1.1.4
4
4
  Summary: Python for CPM
5
5
  Author-email: Gonzalo Atienza Rela <gonatienza@gmail.com>
6
6
  License-Expression: MIT
@@ -19,7 +19,7 @@ This module leverages the [Credential Management .NET SDK](https://docs.cyberark
19
19
 
20
20
  All objects are collected from the SDK and sent as environment context to be picked up by the `python4cpm` module during the subprocess execution of python. All secrets of such environment are protected and encrypted by [Data Protection API (DPAPI)](https://learn.microsoft.com/en-us/dotnet/standard/security/how-to-use-data-protection), until they are explicitely retrieved in your python script runtime, invoking the `Secret.get()` method. Finally, python controls the termination signal sent back to the SDK, which is consequently used as the return code to CPM/SRS. Such as a successful or failed (recoverable or not) result of the requested action.
21
21
 
22
- This platform allows you to duplicate it multiple times, simply changing its settings (with regular day two operations from Privilege Cloud/PVWA) to point to different venvs and/or python scripts.
22
+ This platform allows you to duplicate it multiple times, simply changing its settings (from Privilege Cloud/PVWA) to point to different venvs and/or python scripts.
23
23
 
24
24
  ## Installation
25
25
 
@@ -67,136 +67,85 @@ This platform allows you to duplicate it multiple times, simply changing its set
67
67
  from python4cpm import Python4CPMHandler
68
68
 
69
69
 
70
- class MyRotator(Python4CPMHandler): # create a subclass for the Handler
70
+ class CredManager(Python4CPMHandler):
71
71
  """
72
- These are the usable properties and methods from Python4CPMHandler:
73
-
74
- self.args.action
75
- # action requested from CPM/SRS
76
-
77
- ## Target Account
78
-
79
- self.target_account.username
80
- # address from account
81
-
82
- self.target_account.address
83
- # address from account
84
-
85
- self.target_account.port
86
- # port from account
87
-
88
- self.target_account.password.get()
89
- # get plaintext str from password object
90
-
91
- ## Logon Account
92
- self.logon_account.username
93
- self.logon_account.password.get()
94
-
95
- ## Reconcile Account
96
- self.reconcile_account.username
97
- self.reconcile_account.password.get()
98
-
99
- ## Logging
100
-
101
- self.logger.critical("this is critical message")
102
- self.logger.error("this is an error message")
103
- self.logger.warning("this is a warning message")
104
- self.logger.info("this is an info message")
105
- self.logger.debug("this is a debug message")
106
-
107
- # The logging level comes from PythonLoggingLevel (platform parameters) (default is error)
108
-
109
- =============================
110
- REQUIRED TERMINATION SIGNALS
111
- =============================
112
- Terminate signals -> MUST use one of the following three signals to terminate the script:
113
-
114
- self.close_success()
115
- # terminate and provide CPM/SRS with a success state
116
-
117
- self.close_fail()
118
- # terminate and provide CPM/SRS with a failed recoverable state
119
-
120
- self.close_fail(unrecoverable=True)
121
- # terminate and provide CPM/SRS with a failed unrecoverable state
122
-
123
- When calling a signal sys.exit is invoked and the script is terminated.
124
- If no signal is called, and the script finishes without any exception,
125
- it will behave like p4cpm.close_fail(unrecoverable=True) and log an error message.
126
- =============================
127
- =============================
72
+ Properties:
73
+ target_account (TargetAccount): Account being managed.
74
+ .policy_id (str): Platform name.
75
+ .object_name (str): Account name.
76
+ .username (str): Account username.
77
+ .address (str): Target address.
78
+ .port (str): Target port.
79
+ .password (Secret): Current password. Call .get() to retrieve value.
80
+ .new_password (Secret): Replacement password. Call .get() to retrieve value.
81
+
82
+ logon_account (LogonAccount): Linked Logon Account.
83
+ .username (str): Account username.
84
+ .password (Secret): Logon password. Call .get() to retrieve value.
85
+
86
+ reconcile_account (ReconcileAccount): Linked Reconcile Account.
87
+ .username (str): Account username.
88
+ .password (Secret): Reconcile password. Call .get() to retrieve value.
89
+
90
+ logger (logging.Logger): Logger instance.
91
+
92
+ Methods:
93
+ close_success(): Signal successful completion and terminate.
94
+ close_fail(): Signal failed completion and terminate.
128
95
  """
129
96
 
130
- # =============================
131
- # REQUIRED METHODS (MUST DEFINE)
132
- # =============================
133
- # verify(), logon(), change(), prereconcile(), reconcile()
134
97
 
135
98
  def verify(self):
136
- self._verify()
137
- self.log_info("verification successful")
99
+ """
100
+ REQUIRED METHOD
101
+ """
102
+ # TODO: use account objects for your logic
138
103
  self.close_success()
139
104
 
140
105
  def logon(self):
106
+ """
107
+ REQUIRED METHOD
108
+ """
109
+ # TODO: use account objects for your logic
141
110
  self.close_success()
142
111
 
143
112
  def change(self):
144
- self._change()
145
- self.log_error("something went wrong")
146
- self.close_fail()
113
+ """
114
+ REQUIRED METHOD
115
+ """
116
+ # TODO: use account objects for your logic
117
+ self.close_success()
147
118
 
148
119
  def prereconcile(self):
149
- self._verify(from_reconcile=True)
120
+ """
121
+ REQUIRED METHOD
122
+ """
123
+ # TODO: use account objects for your logic
150
124
  self.close_success()
151
125
 
152
126
  def reconcile(self):
153
- self._change(from_reconcile=True)
127
+ """
128
+ REQUIRED METHOD
129
+ """
130
+ # TODO: use account objects for your logic
154
131
  self.close_success()
155
132
 
156
- def _verify(self, from_reconcile=False):
157
- if from_reconcile is False:
158
- pass
159
- # TODO: use account objects for your logic
160
- else:
161
- pass
162
- # TODO: use account objects for your logic
163
- result = True
164
- if result is True:
165
- self.log_info("verification successful")
166
- else:
167
- self.log_error("something went wrong")
168
- self.close_fail()
169
-
170
- def _change(self, from_reconcile=False):
171
- if from_reconcile is False:
172
- pass
173
- # TODO: use account objects for your logic
174
- else:
175
- pass
176
- # TODO: use account objects for your logic
177
- result = True
178
- if result is True:
179
- self.log_info("rotation successful")
180
- else:
181
- self.log_error("something went wrong")
182
- self.close_fail()
183
-
184
133
 
185
134
  if __name__ == "__main__":
186
- MyRotator().run() # initializes the class and calls the action that was requested from CPM/SRS.
135
+ CredManager().run() # initializes the class and calls the action that was requested from CPM/SRS.
187
136
  ```
188
137
  (*) More realistic examples can be found [here](https://github.com/gonatienza/python4cpm/blob/main/examples).
189
138
 
190
139
  When doing `verify`, `change` or `reconcile` from Privilege Cloud/PVWA:
191
- 1. Verify -> the sciprt will be executed once running the `MyRotator.verify()` method.
192
- 2. Change -> the sciprt will be executed twice, running first the `MyRotator.logon()` method and secondly the `MyRotator.change()` method.
193
- - 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)`.
194
- 3. Reconcile -> the sciprt will be executed twice, running first the `MyRotator.prereconcile()` method and secondly the `MyRotator.reconcile()` method.
195
- - 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)`.
196
- 4. When calling `MyRotator.verify()`, `MyRotator.logon()` or `MyRotator.prereconcile()`: `self.target_account.new_password.get()` will always return an empty string.
197
- 5. If a logon account is not linked, `self.logon_account.username` and `self.logon_account.password.get()` will return empty strings.
198
- 6. If a reconcile account is not linked, `self.reconcile_account.username` and `self.reconcile_account.password.get()` will return empty strings.
199
- 7. The python `Logger` places its logs in the `Logs/ThirdParty` directory. The filename will be based on the name of the subclass created (e.g., `MyRotator`).
140
+ 1. Verify -> the script will be executed once running the `verify()` method.
141
+ 2. Change -> the script will be executed twice: first `logon()`, then `change()`.
142
+ 3. Reconcile -> the script will be executed twice: first `prereconcile()`, then `reconcile()`.
143
+ 4. When calling `verify()`, `logon()` or `prereconcile()`: `target_account.new_password` will always return `None`.
144
+ 5. If a logon account is not linked, `logon_account` will return `None`.
145
+ 6. If a reconcile account is not linked, `reconcile_account` will return `None`.
146
+ 7. Always use the `close_success` or `close_fail` methods to signal the proper termination for all actions.
147
+ - If any action is not terminated with a termination method, CPM/SRS will see this as a `close_fail(unrecoverable=True)`, even if no exceptions are raised.
148
+ 8. The python `Logger` places its logs in the `Logs/ThirdParty` directory. The filename will be based on `target_account.policy_id` and `target_account.object_name`.
200
149
 
201
150
 
202
151
  ### Installing dependencies in python venv
@@ -210,7 +159,7 @@ As with any python venv, you can install dependencies in your venv.
210
159
 
211
160
  ## Dev Helper:
212
161
 
213
- For dev purposes, `NETHelper` is a companion helper to test your scripts before shipping to CPM/SRS. It simplifies the instantiation of the `Python4CPM` or `Python4CPMHandler` objects by simulating how the plugin creates the environment context for the python module.
162
+ For dev purposes, `NETHelper` is a companion helper to test your scripts without CPM/SRS. It simplifies the instantiation of the `Python4CPM` or `Python4CPMHandler` objects by simulating how the plugin creates the environment context for the python module.
214
163
 
215
164
  **Note**: As CPM and the SRS management agent run in Windows, the plugin was built to encrypt secrets using DPAPI (a windows only library). For dev purposes in Linux/Mac dev workstations, those secrets put in the environment context by `NETHelper` will be in plaintext. In windows dev workstations, `NETHelper` encrypts the secrets as the .NET plugin does. This is informational only, **the module will use its encryption/decryption capabilities automatically based on the platform** it is running on and you do not have to do anything specific to enable it.
216
165
 
@@ -220,30 +169,24 @@ For dev purposes, `NETHelper` is a companion helper to test your scripts before
220
169
 
221
170
  ```python
222
171
  from python4cpm import NETHelper, Python4CPM, Python4CPMHandler
223
- from getpass import getpass
224
-
225
- # Get secrets for your password, logon account password, reconcile account password and new password
226
- # You can use an empty string if it does not apply
227
- target_password = getpass("password: ") # password from account
228
- logon_password = getpass("logon_password: ") # password from linked logon account
229
- reconcile_password = getpass("reconcile_password: ") # password from linked reconcile account
230
- target_new_password = getpass("new_password: ") # new password for the rotation
231
172
 
232
173
  NETHelper.set(
233
174
  action=Python4CPM.ACTION_CHANGE, # use actions from Python4CPM.ACTION_*
234
- target_username="jdoe", # -> will fall under MyRotator.target_account.username
235
- target_address="myapp.corp.local", # -> will fall under MyRotator.target_account.address
236
- target_port="8443", # -> will fall under MyRotator.target_account.port
237
- logon_username="ldoe", # -> will fall under MyRotator.logon_account.username
238
- reconcile_username="rdoe", # -> will fall under MyRotator.reconcile_account.username
239
- logging_level="debug", # "critical", "error", "warning", "info" or "debug"
240
- target_password=target_password, # -> will fall under MyRotator.target_account.password.get()
241
- logon_password=logon_password, # -> will fall under MyRotator.logon_account.password.get()
242
- reconcile_password=reconcile_password, # -> will fall under MyRotator.reconcile_account.password.get()
243
- target_new_password=target_new_password # -> will fall under MyRotator.target_account.new_password.get()
175
+ logging_level="debug",
176
+ target_policy_id="NETHelper",
177
+ target_object_name="Objectname",
178
+ target_username="jdoe",
179
+ target_address="myapp.corp.local",
180
+ target_port="8443",
181
+ logon_username="ldoe",
182
+ reconcile_username="rdoe",
183
+ target_password="", # str value of target password
184
+ logon_password="", # str value of logon password
185
+ reconcile_password="", # str value of reconcile password
186
+ target_new_password="" # str value of new password
244
187
  )
245
188
 
246
- class MyRotator(Python4CPMHandler):
189
+ class CredManager(Python4CPMHandler):
247
190
  def verify(self):
248
191
  # TODO: Add your logic here
249
192
  self.close_success()
@@ -264,12 +207,10 @@ class MyRotator(Python4CPMHandler):
264
207
  # TODO: Add your logic here
265
208
  self.close_success()
266
209
 
267
- MyRotator().run()
210
+ CredManager().run()
268
211
  ```
269
212
 
270
213
  #### Remember for your final script:
271
214
 
272
215
  - Remove the import of `NETHelper`.
273
216
  - Remove the `NETHelper.set()` call.
274
- - If applicable, change the definition of `p4cpm` from `p4cpm = NETHelper.get()` to `p4cpm = Python4CPM("MyApp")`.
275
- - Remove any secrets prompting or interactive interruptions.
@@ -8,7 +8,7 @@ This module leverages the [Credential Management .NET SDK](https://docs.cyberark
8
8
 
9
9
  All objects are collected from the SDK and sent as environment context to be picked up by the `python4cpm` module during the subprocess execution of python. All secrets of such environment are protected and encrypted by [Data Protection API (DPAPI)](https://learn.microsoft.com/en-us/dotnet/standard/security/how-to-use-data-protection), until they are explicitely retrieved in your python script runtime, invoking the `Secret.get()` method. Finally, python controls the termination signal sent back to the SDK, which is consequently used as the return code to CPM/SRS. Such as a successful or failed (recoverable or not) result of the requested action.
10
10
 
11
- This platform allows you to duplicate it multiple times, simply changing its settings (with regular day two operations from Privilege Cloud/PVWA) to point to different venvs and/or python scripts.
11
+ This platform allows you to duplicate it multiple times, simply changing its settings (from Privilege Cloud/PVWA) to point to different venvs and/or python scripts.
12
12
 
13
13
  ## Installation
14
14
 
@@ -56,136 +56,85 @@ This platform allows you to duplicate it multiple times, simply changing its set
56
56
  from python4cpm import Python4CPMHandler
57
57
 
58
58
 
59
- class MyRotator(Python4CPMHandler): # create a subclass for the Handler
59
+ class CredManager(Python4CPMHandler):
60
60
  """
61
- These are the usable properties and methods from Python4CPMHandler:
62
-
63
- self.args.action
64
- # action requested from CPM/SRS
65
-
66
- ## Target Account
67
-
68
- self.target_account.username
69
- # address from account
70
-
71
- self.target_account.address
72
- # address from account
73
-
74
- self.target_account.port
75
- # port from account
76
-
77
- self.target_account.password.get()
78
- # get plaintext str from password object
79
-
80
- ## Logon Account
81
- self.logon_account.username
82
- self.logon_account.password.get()
83
-
84
- ## Reconcile Account
85
- self.reconcile_account.username
86
- self.reconcile_account.password.get()
87
-
88
- ## Logging
89
-
90
- self.logger.critical("this is critical message")
91
- self.logger.error("this is an error message")
92
- self.logger.warning("this is a warning message")
93
- self.logger.info("this is an info message")
94
- self.logger.debug("this is a debug message")
95
-
96
- # The logging level comes from PythonLoggingLevel (platform parameters) (default is error)
97
-
98
- =============================
99
- REQUIRED TERMINATION SIGNALS
100
- =============================
101
- Terminate signals -> MUST use one of the following three signals to terminate the script:
102
-
103
- self.close_success()
104
- # terminate and provide CPM/SRS with a success state
105
-
106
- self.close_fail()
107
- # terminate and provide CPM/SRS with a failed recoverable state
108
-
109
- self.close_fail(unrecoverable=True)
110
- # terminate and provide CPM/SRS with a failed unrecoverable state
111
-
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,
114
- it will behave like p4cpm.close_fail(unrecoverable=True) and log an error message.
115
- =============================
116
- =============================
61
+ Properties:
62
+ target_account (TargetAccount): Account being managed.
63
+ .policy_id (str): Platform name.
64
+ .object_name (str): Account name.
65
+ .username (str): Account username.
66
+ .address (str): Target address.
67
+ .port (str): Target port.
68
+ .password (Secret): Current password. Call .get() to retrieve value.
69
+ .new_password (Secret): Replacement password. Call .get() to retrieve value.
70
+
71
+ logon_account (LogonAccount): Linked Logon Account.
72
+ .username (str): Account username.
73
+ .password (Secret): Logon password. Call .get() to retrieve value.
74
+
75
+ reconcile_account (ReconcileAccount): Linked Reconcile Account.
76
+ .username (str): Account username.
77
+ .password (Secret): Reconcile password. Call .get() to retrieve value.
78
+
79
+ logger (logging.Logger): Logger instance.
80
+
81
+ Methods:
82
+ close_success(): Signal successful completion and terminate.
83
+ close_fail(): Signal failed completion and terminate.
117
84
  """
118
85
 
119
- # =============================
120
- # REQUIRED METHODS (MUST DEFINE)
121
- # =============================
122
- # verify(), logon(), change(), prereconcile(), reconcile()
123
86
 
124
87
  def verify(self):
125
- self._verify()
126
- self.log_info("verification successful")
88
+ """
89
+ REQUIRED METHOD
90
+ """
91
+ # TODO: use account objects for your logic
127
92
  self.close_success()
128
93
 
129
94
  def logon(self):
95
+ """
96
+ REQUIRED METHOD
97
+ """
98
+ # TODO: use account objects for your logic
130
99
  self.close_success()
131
100
 
132
101
  def change(self):
133
- self._change()
134
- self.log_error("something went wrong")
135
- self.close_fail()
102
+ """
103
+ REQUIRED METHOD
104
+ """
105
+ # TODO: use account objects for your logic
106
+ self.close_success()
136
107
 
137
108
  def prereconcile(self):
138
- self._verify(from_reconcile=True)
109
+ """
110
+ REQUIRED METHOD
111
+ """
112
+ # TODO: use account objects for your logic
139
113
  self.close_success()
140
114
 
141
115
  def reconcile(self):
142
- self._change(from_reconcile=True)
116
+ """
117
+ REQUIRED METHOD
118
+ """
119
+ # TODO: use account objects for your logic
143
120
  self.close_success()
144
121
 
145
- def _verify(self, from_reconcile=False):
146
- if from_reconcile is False:
147
- pass
148
- # TODO: use account objects for your logic
149
- else:
150
- pass
151
- # TODO: use account objects for your logic
152
- result = True
153
- if result is True:
154
- self.log_info("verification successful")
155
- else:
156
- self.log_error("something went wrong")
157
- self.close_fail()
158
-
159
- def _change(self, from_reconcile=False):
160
- if from_reconcile is False:
161
- pass
162
- # TODO: use account objects for your logic
163
- else:
164
- pass
165
- # TODO: use account objects for your logic
166
- result = True
167
- if result is True:
168
- self.log_info("rotation successful")
169
- else:
170
- self.log_error("something went wrong")
171
- self.close_fail()
172
-
173
122
 
174
123
  if __name__ == "__main__":
175
- MyRotator().run() # initializes the class and calls the action that was requested from CPM/SRS.
124
+ CredManager().run() # initializes the class and calls the action that was requested from CPM/SRS.
176
125
  ```
177
126
  (*) More realistic examples can be found [here](https://github.com/gonatienza/python4cpm/blob/main/examples).
178
127
 
179
128
  When doing `verify`, `change` or `reconcile` from Privilege Cloud/PVWA:
180
- 1. Verify -> the sciprt will be executed once running the `MyRotator.verify()` method.
181
- 2. Change -> the sciprt will be executed twice, running first the `MyRotator.logon()` method and secondly the `MyRotator.change()` method.
182
- - 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)`.
183
- 3. Reconcile -> the sciprt will be executed twice, running first the `MyRotator.prereconcile()` method and secondly the `MyRotator.reconcile()` method.
184
- - 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)`.
185
- 4. When calling `MyRotator.verify()`, `MyRotator.logon()` or `MyRotator.prereconcile()`: `self.target_account.new_password.get()` will always return an empty string.
186
- 5. If a logon account is not linked, `self.logon_account.username` and `self.logon_account.password.get()` will return empty strings.
187
- 6. If a reconcile account is not linked, `self.reconcile_account.username` and `self.reconcile_account.password.get()` will return empty strings.
188
- 7. The python `Logger` places its logs in the `Logs/ThirdParty` directory. The filename will be based on the name of the subclass created (e.g., `MyRotator`).
129
+ 1. Verify -> the script will be executed once running the `verify()` method.
130
+ 2. Change -> the script will be executed twice: first `logon()`, then `change()`.
131
+ 3. Reconcile -> the script will be executed twice: first `prereconcile()`, then `reconcile()`.
132
+ 4. When calling `verify()`, `logon()` or `prereconcile()`: `target_account.new_password` will always return `None`.
133
+ 5. If a logon account is not linked, `logon_account` will return `None`.
134
+ 6. If a reconcile account is not linked, `reconcile_account` will return `None`.
135
+ 7. Always use the `close_success` or `close_fail` methods to signal the proper termination for all actions.
136
+ - If any action is not terminated with a termination method, CPM/SRS will see this as a `close_fail(unrecoverable=True)`, even if no exceptions are raised.
137
+ 8. The python `Logger` places its logs in the `Logs/ThirdParty` directory. The filename will be based on `target_account.policy_id` and `target_account.object_name`.
189
138
 
190
139
 
191
140
  ### Installing dependencies in python venv
@@ -199,7 +148,7 @@ As with any python venv, you can install dependencies in your venv.
199
148
 
200
149
  ## Dev Helper:
201
150
 
202
- For dev purposes, `NETHelper` is a companion helper to test your scripts before shipping to CPM/SRS. It simplifies the instantiation of the `Python4CPM` or `Python4CPMHandler` objects by simulating how the plugin creates the environment context for the python module.
151
+ For dev purposes, `NETHelper` is a companion helper to test your scripts without CPM/SRS. It simplifies the instantiation of the `Python4CPM` or `Python4CPMHandler` objects by simulating how the plugin creates the environment context for the python module.
203
152
 
204
153
  **Note**: As CPM and the SRS management agent run in Windows, the plugin was built to encrypt secrets using DPAPI (a windows only library). For dev purposes in Linux/Mac dev workstations, those secrets put in the environment context by `NETHelper` will be in plaintext. In windows dev workstations, `NETHelper` encrypts the secrets as the .NET plugin does. This is informational only, **the module will use its encryption/decryption capabilities automatically based on the platform** it is running on and you do not have to do anything specific to enable it.
205
154
 
@@ -209,30 +158,24 @@ For dev purposes, `NETHelper` is a companion helper to test your scripts before
209
158
 
210
159
  ```python
211
160
  from python4cpm import NETHelper, Python4CPM, Python4CPMHandler
212
- from getpass import getpass
213
-
214
- # Get secrets for your password, logon account password, reconcile account password and new password
215
- # You can use an empty string if it does not apply
216
- target_password = getpass("password: ") # password from account
217
- logon_password = getpass("logon_password: ") # password from linked logon account
218
- reconcile_password = getpass("reconcile_password: ") # password from linked reconcile account
219
- target_new_password = getpass("new_password: ") # new password for the rotation
220
161
 
221
162
  NETHelper.set(
222
163
  action=Python4CPM.ACTION_CHANGE, # use actions from Python4CPM.ACTION_*
223
- target_username="jdoe", # -> will fall under MyRotator.target_account.username
224
- target_address="myapp.corp.local", # -> will fall under MyRotator.target_account.address
225
- target_port="8443", # -> will fall under MyRotator.target_account.port
226
- logon_username="ldoe", # -> will fall under MyRotator.logon_account.username
227
- reconcile_username="rdoe", # -> will fall under MyRotator.reconcile_account.username
228
- logging_level="debug", # "critical", "error", "warning", "info" or "debug"
229
- target_password=target_password, # -> will fall under MyRotator.target_account.password.get()
230
- logon_password=logon_password, # -> will fall under MyRotator.logon_account.password.get()
231
- reconcile_password=reconcile_password, # -> will fall under MyRotator.reconcile_account.password.get()
232
- target_new_password=target_new_password # -> will fall under MyRotator.target_account.new_password.get()
164
+ logging_level="debug",
165
+ target_policy_id="NETHelper",
166
+ target_object_name="Objectname",
167
+ target_username="jdoe",
168
+ target_address="myapp.corp.local",
169
+ target_port="8443",
170
+ logon_username="ldoe",
171
+ reconcile_username="rdoe",
172
+ target_password="", # str value of target password
173
+ logon_password="", # str value of logon password
174
+ reconcile_password="", # str value of reconcile password
175
+ target_new_password="" # str value of new password
233
176
  )
234
177
 
235
- class MyRotator(Python4CPMHandler):
178
+ class CredManager(Python4CPMHandler):
236
179
  def verify(self):
237
180
  # TODO: Add your logic here
238
181
  self.close_success()
@@ -253,12 +196,10 @@ class MyRotator(Python4CPMHandler):
253
196
  # TODO: Add your logic here
254
197
  self.close_success()
255
198
 
256
- MyRotator().run()
199
+ CredManager().run()
257
200
  ```
258
201
 
259
202
  #### Remember for your final script:
260
203
 
261
204
  - Remove the import of `NETHelper`.
262
205
  - Remove the `NETHelper.set()` call.
263
- - If applicable, change the definition of `p4cpm` from `p4cpm = NETHelper.get()` to `p4cpm = Python4CPM("MyApp")`.
264
- - Remove any secrets prompting or interactive interruptions.
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "python4cpm"
7
- version = "1.1.2"
7
+ version = "1.1.4"
8
8
  description = "Python for CPM"
9
9
  authors = [
10
10
  { name = "Gonzalo Atienza Rela", email = "gonatienza@gmail.com" }
@@ -0,0 +1,90 @@
1
+ from python4cpm.envhandler import EnvHandler, Props
2
+ from python4cpm.secret import Secret
3
+
4
+
5
+ class BaseAccount(EnvHandler):
6
+ PROPS = Props("username", "password")
7
+
8
+ def __init__(
9
+ self,
10
+ username: str | None,
11
+ password: str | None
12
+ ) -> None:
13
+ self._username = username
14
+ self._password = Secret.from_env_var(password)
15
+
16
+ @classmethod
17
+ def get(cls) -> object | None:
18
+ kwargs = cls.get_kwargs()
19
+ if all(value is None for value in kwargs.values()):
20
+ return None
21
+ return cls(**kwargs)
22
+
23
+ @property
24
+ def username(self) -> str:
25
+ return self._username
26
+
27
+ @property
28
+ def password(self) -> Secret:
29
+ return self._password
30
+
31
+
32
+ class TargetAccount(BaseAccount):
33
+ OBJ_PREFIX = "target_"
34
+ PROPS = Props(
35
+ "policy_id",
36
+ "object_name",
37
+ "username",
38
+ "password",
39
+ "address",
40
+ "port",
41
+ "new_password"
42
+ )
43
+
44
+ def __init__(
45
+ self,
46
+ policy_id: str | None,
47
+ object_name: str | None,
48
+ username: str | None,
49
+ password: str | None,
50
+ address: str | None,
51
+ port: str | None,
52
+ new_password: str | None
53
+ ) -> None:
54
+ super().__init__(
55
+ username,
56
+ password
57
+ )
58
+ self._policy_id = policy_id
59
+ self._object_name = object_name
60
+ self._address = address
61
+ self._port = port
62
+ self._new_password = Secret.from_env_var(new_password)
63
+
64
+ @property
65
+ def policy_id(self) -> str | None:
66
+ return self._policy_id
67
+
68
+ @property
69
+ def object_name(self) -> str | None:
70
+ return self._object_name
71
+
72
+ @property
73
+ def address(self) -> str | None:
74
+ return self._address
75
+
76
+ @property
77
+ def port(self) -> str | None:
78
+ return self._port
79
+
80
+ @property
81
+ def new_password(self) -> Secret:
82
+ return self._new_password
83
+
84
+
85
+ class LogonAccount(BaseAccount):
86
+ OBJ_PREFIX = "logon_"
87
+
88
+
89
+ class ReconcileAccount(BaseAccount):
90
+ OBJ_PREFIX = "reconcile_"
@@ -7,16 +7,16 @@ class Args(EnvHandler):
7
7
 
8
8
  def __init__(
9
9
  self,
10
- action: str,
11
- logging_level: str
10
+ action: str | None,
11
+ logging_level: str | None
12
12
  ) -> None:
13
13
  self._action = action
14
14
  self._logging_level = logging_level
15
15
 
16
16
  @property
17
- def action(self) -> str:
17
+ def action(self) -> str | None:
18
18
  return self._action
19
19
 
20
20
  @property
21
- def logging_level(self) -> str:
21
+ def logging_level(self) -> str | None:
22
22
  return self._logging_level
@@ -20,10 +20,13 @@ class EnvHandler:
20
20
  env_key = f"{cls.PREFIX}{cls.OBJ_PREFIX}{key}"
21
21
  return env_key.upper()
22
22
 
23
+ @classmethod
24
+ def get_kwargs(cls) -> dict:
25
+ return {
26
+ prop: os.environ.get(cls.get_key(prop))
27
+ for prop in cls.PROPS
28
+ }
29
+
23
30
  @classmethod
24
31
  def get(cls) -> object:
25
- kwargs = {}
26
- for prop in cls.PROPS:
27
- value = os.environ.get(cls.get_key(prop))
28
- kwargs[prop] = value if value is not None else ""
29
- return cls(**kwargs)
32
+ return cls(**cls.get_kwargs())
@@ -12,22 +12,23 @@ class Logger:
12
12
  "info": logging.INFO,
13
13
  "debug": logging.DEBUG
14
14
  }
15
- _DEFAULT_LEVEL = _LOGGING_LEVELS["error"]
15
+ _DEFAULT_LEVEL = "error"
16
16
 
17
17
  @classmethod
18
18
  def get_logger(
19
19
  cls,
20
20
  name: str,
21
- logging_level: str
21
+ logging_level: str | None
22
22
  ) -> logging.Logger:
23
23
  os.makedirs(cls._LOGS_DIR, exist_ok=True)
24
- logs_file = os.path.join(cls._LOGS_DIR, f"{__name__}-{name}.log")
25
- _id = os.urandom(4).hex()
26
- logger = logging.getLogger(_id)
27
- if logging_level.lower() in cls._LOGGING_LEVELS:
28
- logger.setLevel(cls._LOGGING_LEVELS[logging_level.lower()])
29
- else:
30
- logger.setLevel(cls._DEFAULT_LEVEL)
24
+ uid = os.urandom(4).hex()
25
+ logger = logging.getLogger(uid)
26
+ _logging_level = logging_level.lower()
27
+ if _logging_level not in cls._LOGGING_LEVELS:
28
+ _logging_level = cls._DEFAULT_LEVEL
29
+ logger.setLevel(cls._LOGGING_LEVELS[_logging_level])
30
+ file_name = f"{__name__}_{_logging_level}_{name}.log"
31
+ logs_file = os.path.join(cls._LOGS_DIR, file_name)
31
32
  handler = RotatingFileHandler(
32
33
  filename=logs_file,
33
34
  maxBytes=1024 ** 2,
@@ -9,17 +9,19 @@ class NETHelper:
9
9
  @classmethod
10
10
  def set(
11
11
  cls,
12
- action: str = "",
13
- logging_level: str = "",
14
- target_username: str = "",
15
- target_address: str = "",
16
- target_port: str = "",
17
- logon_username: str = "",
18
- reconcile_username: str = "",
19
- target_password: str = "",
20
- logon_password: str = "",
21
- reconcile_password: str = "",
22
- target_new_password: str = ""
12
+ action: str | None = None,
13
+ logging_level: str | None = None,
14
+ target_policy_id: str | None = None,
15
+ target_object_name: str | None = None,
16
+ target_username: str | None = None,
17
+ target_address: str | None = None,
18
+ target_port: str | None = None,
19
+ logon_username: str | None = None,
20
+ reconcile_username: str | None = None,
21
+ target_password: str | None = None,
22
+ logon_password: str | None = None,
23
+ reconcile_password: str | None = None,
24
+ target_new_password: str | None = None
23
25
  ) -> None:
24
26
  if Crypto.ENABLED:
25
27
  target_password = Crypto.encrypt(target_password)
@@ -29,6 +31,8 @@ class NETHelper:
29
31
  keys = (
30
32
  Args.get_key(Args.PROPS.action),
31
33
  Args.get_key(Args.PROPS.logging_level),
34
+ TargetAccount.get_key(TargetAccount.PROPS.policy_id),
35
+ TargetAccount.get_key(TargetAccount.PROPS.object_name),
32
36
  TargetAccount.get_key(TargetAccount.PROPS.username),
33
37
  TargetAccount.get_key(TargetAccount.PROPS.address),
34
38
  TargetAccount.get_key(TargetAccount.PROPS.port),
@@ -42,6 +46,8 @@ class NETHelper:
42
46
  values = (
43
47
  action,
44
48
  logging_level,
49
+ target_policy_id,
50
+ target_object_name,
45
51
  target_username,
46
52
  target_address,
47
53
  target_port,
@@ -53,8 +59,9 @@ class NETHelper:
53
59
  target_new_password
54
60
  )
55
61
  for i, key in enumerate(keys):
56
- os.environ.update({key: values[i]})
62
+ if values[i] is not None:
63
+ os.environ.update({key: values[i]})
57
64
 
58
65
  @classmethod
59
66
  def get(cls) -> Python4CPM:
60
- return Python4CPM(cls.__name__)
67
+ return Python4CPM()
@@ -3,7 +3,6 @@ import atexit
3
3
  import logging
4
4
  from python4cpm.secret import Secret
5
5
  from python4cpm.args import Args
6
- from python4cpm.crypto import Crypto
7
6
  from python4cpm.logger import Logger
8
7
  from python4cpm.accounts import TargetAccount, LogonAccount, ReconcileAccount
9
8
 
@@ -25,13 +24,15 @@ class Python4CPM:
25
24
  _FAILED_RECOVERABLE_CODE = 81
26
25
  _FAILED_UNRECOVERABLE_CODE = 89
27
26
 
28
- def __init__(self, name: str) -> None:
29
- self._name = name
27
+ def __init__(self) -> None:
30
28
  self._args = Args.get()
31
29
  self._target_account = TargetAccount.get()
32
30
  self._logon_account = LogonAccount.get()
33
31
  self._reconcile_account = ReconcileAccount.get()
34
- self._logger = Logger.get_logger(self._name, self._args.logging_level)
32
+ self._logger = Logger.get_logger(
33
+ f"{self._target_account.policy_id}-{self._target_account.object_name}",
34
+ self._args.logging_level
35
+ )
35
36
  self._logger.debug("Initiating...")
36
37
  self._log_obj(self._args)
37
38
  self._verify_action()
@@ -65,20 +66,18 @@ class Python4CPM:
65
66
  if self._args.action not in self._VALID_ACTIONS:
66
67
  self._logger.warning(f"Unkonwn action -> '{self._args.action}'")
67
68
 
68
- def _log_obj(self, obj: object) -> None:
69
- for key, value in vars(obj).items():
70
- _key = f"{obj.__class__.__name__}.{key.strip('_')}"
71
- if value:
72
- if not isinstance(value, Secret):
73
- logging_value = f"'{value}'"
74
- else:
75
- if Crypto.ENABLED is True:
76
- logging_value = "[ENCRYPTED]"
69
+ def _log_obj(self, obj: object | None) -> None:
70
+ if obj is not None:
71
+ for key, value in vars(obj).items():
72
+ _key = f"{obj.__class__.__name__}.{key.removeprefix('_')}"
73
+ if value is not None:
74
+ if not isinstance(value, Secret):
75
+ logging_value = f"'{value}'"
77
76
  else:
78
- logging_value = "[SET]"
79
- else:
80
- logging_value = "[NOT SET]"
81
- self._logger.debug(f"{_key} -> {logging_value}")
77
+ logging_value = str(value)
78
+ else:
79
+ logging_value = "[NOT SET]"
80
+ self._logger.debug(f"{_key} -> {logging_value}")
82
81
 
83
82
  def close_fail(self, unrecoverable: bool = False) -> None:
84
83
  if unrecoverable is False:
@@ -3,9 +3,6 @@ from python4cpm.python4cpm import Python4CPM
3
3
 
4
4
 
5
5
  class Python4CPMHandler(ABC, Python4CPM):
6
- def __init__(self) -> None:
7
- super().__init__(self.__class__.__name__)
8
-
9
6
  def run(self) -> None:
10
7
  actions = {
11
8
  self.ACTION_VERIFY: self.verify,
@@ -18,7 +15,7 @@ class Python4CPMHandler(ABC, Python4CPM):
18
15
  if action is not None:
19
16
  action()
20
17
  else:
21
- raise ValueError(f"Unknown action: {self._args.action}")
18
+ raise ValueError(f"Unknown action: '{self._args.action}'")
22
19
 
23
20
  @abstractmethod
24
21
  def verify(self) -> None:
@@ -0,0 +1,24 @@
1
+ from python4cpm.crypto import Crypto
2
+
3
+
4
+ class Secret:
5
+ def __init__(self, secret: str) -> None:
6
+ self._secret = secret
7
+
8
+ @classmethod
9
+ def from_env_var(cls, secret: str | None) -> object | None:
10
+ if secret is not None:
11
+ return cls(secret)
12
+ return None
13
+
14
+ def __str__(self) -> str:
15
+ if Crypto.ENABLED:
16
+ return "[ENCRYPTED]"
17
+ else:
18
+ return "[SET]"
19
+
20
+ def get(self) -> str:
21
+ if Crypto.ENABLED:
22
+ return Crypto.decrypt(self._secret)
23
+ else:
24
+ return self._secret
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: python4cpm
3
- Version: 1.1.2
3
+ Version: 1.1.4
4
4
  Summary: Python for CPM
5
5
  Author-email: Gonzalo Atienza Rela <gonatienza@gmail.com>
6
6
  License-Expression: MIT
@@ -19,7 +19,7 @@ This module leverages the [Credential Management .NET SDK](https://docs.cyberark
19
19
 
20
20
  All objects are collected from the SDK and sent as environment context to be picked up by the `python4cpm` module during the subprocess execution of python. All secrets of such environment are protected and encrypted by [Data Protection API (DPAPI)](https://learn.microsoft.com/en-us/dotnet/standard/security/how-to-use-data-protection), until they are explicitely retrieved in your python script runtime, invoking the `Secret.get()` method. Finally, python controls the termination signal sent back to the SDK, which is consequently used as the return code to CPM/SRS. Such as a successful or failed (recoverable or not) result of the requested action.
21
21
 
22
- This platform allows you to duplicate it multiple times, simply changing its settings (with regular day two operations from Privilege Cloud/PVWA) to point to different venvs and/or python scripts.
22
+ This platform allows you to duplicate it multiple times, simply changing its settings (from Privilege Cloud/PVWA) to point to different venvs and/or python scripts.
23
23
 
24
24
  ## Installation
25
25
 
@@ -67,136 +67,85 @@ This platform allows you to duplicate it multiple times, simply changing its set
67
67
  from python4cpm import Python4CPMHandler
68
68
 
69
69
 
70
- class MyRotator(Python4CPMHandler): # create a subclass for the Handler
70
+ class CredManager(Python4CPMHandler):
71
71
  """
72
- These are the usable properties and methods from Python4CPMHandler:
73
-
74
- self.args.action
75
- # action requested from CPM/SRS
76
-
77
- ## Target Account
78
-
79
- self.target_account.username
80
- # address from account
81
-
82
- self.target_account.address
83
- # address from account
84
-
85
- self.target_account.port
86
- # port from account
87
-
88
- self.target_account.password.get()
89
- # get plaintext str from password object
90
-
91
- ## Logon Account
92
- self.logon_account.username
93
- self.logon_account.password.get()
94
-
95
- ## Reconcile Account
96
- self.reconcile_account.username
97
- self.reconcile_account.password.get()
98
-
99
- ## Logging
100
-
101
- self.logger.critical("this is critical message")
102
- self.logger.error("this is an error message")
103
- self.logger.warning("this is a warning message")
104
- self.logger.info("this is an info message")
105
- self.logger.debug("this is a debug message")
106
-
107
- # The logging level comes from PythonLoggingLevel (platform parameters) (default is error)
108
-
109
- =============================
110
- REQUIRED TERMINATION SIGNALS
111
- =============================
112
- Terminate signals -> MUST use one of the following three signals to terminate the script:
113
-
114
- self.close_success()
115
- # terminate and provide CPM/SRS with a success state
116
-
117
- self.close_fail()
118
- # terminate and provide CPM/SRS with a failed recoverable state
119
-
120
- self.close_fail(unrecoverable=True)
121
- # terminate and provide CPM/SRS with a failed unrecoverable state
122
-
123
- When calling a signal sys.exit is invoked and the script is terminated.
124
- If no signal is called, and the script finishes without any exception,
125
- it will behave like p4cpm.close_fail(unrecoverable=True) and log an error message.
126
- =============================
127
- =============================
72
+ Properties:
73
+ target_account (TargetAccount): Account being managed.
74
+ .policy_id (str): Platform name.
75
+ .object_name (str): Account name.
76
+ .username (str): Account username.
77
+ .address (str): Target address.
78
+ .port (str): Target port.
79
+ .password (Secret): Current password. Call .get() to retrieve value.
80
+ .new_password (Secret): Replacement password. Call .get() to retrieve value.
81
+
82
+ logon_account (LogonAccount): Linked Logon Account.
83
+ .username (str): Account username.
84
+ .password (Secret): Logon password. Call .get() to retrieve value.
85
+
86
+ reconcile_account (ReconcileAccount): Linked Reconcile Account.
87
+ .username (str): Account username.
88
+ .password (Secret): Reconcile password. Call .get() to retrieve value.
89
+
90
+ logger (logging.Logger): Logger instance.
91
+
92
+ Methods:
93
+ close_success(): Signal successful completion and terminate.
94
+ close_fail(): Signal failed completion and terminate.
128
95
  """
129
96
 
130
- # =============================
131
- # REQUIRED METHODS (MUST DEFINE)
132
- # =============================
133
- # verify(), logon(), change(), prereconcile(), reconcile()
134
97
 
135
98
  def verify(self):
136
- self._verify()
137
- self.log_info("verification successful")
99
+ """
100
+ REQUIRED METHOD
101
+ """
102
+ # TODO: use account objects for your logic
138
103
  self.close_success()
139
104
 
140
105
  def logon(self):
106
+ """
107
+ REQUIRED METHOD
108
+ """
109
+ # TODO: use account objects for your logic
141
110
  self.close_success()
142
111
 
143
112
  def change(self):
144
- self._change()
145
- self.log_error("something went wrong")
146
- self.close_fail()
113
+ """
114
+ REQUIRED METHOD
115
+ """
116
+ # TODO: use account objects for your logic
117
+ self.close_success()
147
118
 
148
119
  def prereconcile(self):
149
- self._verify(from_reconcile=True)
120
+ """
121
+ REQUIRED METHOD
122
+ """
123
+ # TODO: use account objects for your logic
150
124
  self.close_success()
151
125
 
152
126
  def reconcile(self):
153
- self._change(from_reconcile=True)
127
+ """
128
+ REQUIRED METHOD
129
+ """
130
+ # TODO: use account objects for your logic
154
131
  self.close_success()
155
132
 
156
- def _verify(self, from_reconcile=False):
157
- if from_reconcile is False:
158
- pass
159
- # TODO: use account objects for your logic
160
- else:
161
- pass
162
- # TODO: use account objects for your logic
163
- result = True
164
- if result is True:
165
- self.log_info("verification successful")
166
- else:
167
- self.log_error("something went wrong")
168
- self.close_fail()
169
-
170
- def _change(self, from_reconcile=False):
171
- if from_reconcile is False:
172
- pass
173
- # TODO: use account objects for your logic
174
- else:
175
- pass
176
- # TODO: use account objects for your logic
177
- result = True
178
- if result is True:
179
- self.log_info("rotation successful")
180
- else:
181
- self.log_error("something went wrong")
182
- self.close_fail()
183
-
184
133
 
185
134
  if __name__ == "__main__":
186
- MyRotator().run() # initializes the class and calls the action that was requested from CPM/SRS.
135
+ CredManager().run() # initializes the class and calls the action that was requested from CPM/SRS.
187
136
  ```
188
137
  (*) More realistic examples can be found [here](https://github.com/gonatienza/python4cpm/blob/main/examples).
189
138
 
190
139
  When doing `verify`, `change` or `reconcile` from Privilege Cloud/PVWA:
191
- 1. Verify -> the sciprt will be executed once running the `MyRotator.verify()` method.
192
- 2. Change -> the sciprt will be executed twice, running first the `MyRotator.logon()` method and secondly the `MyRotator.change()` method.
193
- - 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)`.
194
- 3. Reconcile -> the sciprt will be executed twice, running first the `MyRotator.prereconcile()` method and secondly the `MyRotator.reconcile()` method.
195
- - 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)`.
196
- 4. When calling `MyRotator.verify()`, `MyRotator.logon()` or `MyRotator.prereconcile()`: `self.target_account.new_password.get()` will always return an empty string.
197
- 5. If a logon account is not linked, `self.logon_account.username` and `self.logon_account.password.get()` will return empty strings.
198
- 6. If a reconcile account is not linked, `self.reconcile_account.username` and `self.reconcile_account.password.get()` will return empty strings.
199
- 7. The python `Logger` places its logs in the `Logs/ThirdParty` directory. The filename will be based on the name of the subclass created (e.g., `MyRotator`).
140
+ 1. Verify -> the script will be executed once running the `verify()` method.
141
+ 2. Change -> the script will be executed twice: first `logon()`, then `change()`.
142
+ 3. Reconcile -> the script will be executed twice: first `prereconcile()`, then `reconcile()`.
143
+ 4. When calling `verify()`, `logon()` or `prereconcile()`: `target_account.new_password` will always return `None`.
144
+ 5. If a logon account is not linked, `logon_account` will return `None`.
145
+ 6. If a reconcile account is not linked, `reconcile_account` will return `None`.
146
+ 7. Always use the `close_success` or `close_fail` methods to signal the proper termination for all actions.
147
+ - If any action is not terminated with a termination method, CPM/SRS will see this as a `close_fail(unrecoverable=True)`, even if no exceptions are raised.
148
+ 8. The python `Logger` places its logs in the `Logs/ThirdParty` directory. The filename will be based on `target_account.policy_id` and `target_account.object_name`.
200
149
 
201
150
 
202
151
  ### Installing dependencies in python venv
@@ -210,7 +159,7 @@ As with any python venv, you can install dependencies in your venv.
210
159
 
211
160
  ## Dev Helper:
212
161
 
213
- For dev purposes, `NETHelper` is a companion helper to test your scripts before shipping to CPM/SRS. It simplifies the instantiation of the `Python4CPM` or `Python4CPMHandler` objects by simulating how the plugin creates the environment context for the python module.
162
+ For dev purposes, `NETHelper` is a companion helper to test your scripts without CPM/SRS. It simplifies the instantiation of the `Python4CPM` or `Python4CPMHandler` objects by simulating how the plugin creates the environment context for the python module.
214
163
 
215
164
  **Note**: As CPM and the SRS management agent run in Windows, the plugin was built to encrypt secrets using DPAPI (a windows only library). For dev purposes in Linux/Mac dev workstations, those secrets put in the environment context by `NETHelper` will be in plaintext. In windows dev workstations, `NETHelper` encrypts the secrets as the .NET plugin does. This is informational only, **the module will use its encryption/decryption capabilities automatically based on the platform** it is running on and you do not have to do anything specific to enable it.
216
165
 
@@ -220,30 +169,24 @@ For dev purposes, `NETHelper` is a companion helper to test your scripts before
220
169
 
221
170
  ```python
222
171
  from python4cpm import NETHelper, Python4CPM, Python4CPMHandler
223
- from getpass import getpass
224
-
225
- # Get secrets for your password, logon account password, reconcile account password and new password
226
- # You can use an empty string if it does not apply
227
- target_password = getpass("password: ") # password from account
228
- logon_password = getpass("logon_password: ") # password from linked logon account
229
- reconcile_password = getpass("reconcile_password: ") # password from linked reconcile account
230
- target_new_password = getpass("new_password: ") # new password for the rotation
231
172
 
232
173
  NETHelper.set(
233
174
  action=Python4CPM.ACTION_CHANGE, # use actions from Python4CPM.ACTION_*
234
- target_username="jdoe", # -> will fall under MyRotator.target_account.username
235
- target_address="myapp.corp.local", # -> will fall under MyRotator.target_account.address
236
- target_port="8443", # -> will fall under MyRotator.target_account.port
237
- logon_username="ldoe", # -> will fall under MyRotator.logon_account.username
238
- reconcile_username="rdoe", # -> will fall under MyRotator.reconcile_account.username
239
- logging_level="debug", # "critical", "error", "warning", "info" or "debug"
240
- target_password=target_password, # -> will fall under MyRotator.target_account.password.get()
241
- logon_password=logon_password, # -> will fall under MyRotator.logon_account.password.get()
242
- reconcile_password=reconcile_password, # -> will fall under MyRotator.reconcile_account.password.get()
243
- target_new_password=target_new_password # -> will fall under MyRotator.target_account.new_password.get()
175
+ logging_level="debug",
176
+ target_policy_id="NETHelper",
177
+ target_object_name="Objectname",
178
+ target_username="jdoe",
179
+ target_address="myapp.corp.local",
180
+ target_port="8443",
181
+ logon_username="ldoe",
182
+ reconcile_username="rdoe",
183
+ target_password="", # str value of target password
184
+ logon_password="", # str value of logon password
185
+ reconcile_password="", # str value of reconcile password
186
+ target_new_password="" # str value of new password
244
187
  )
245
188
 
246
- class MyRotator(Python4CPMHandler):
189
+ class CredManager(Python4CPMHandler):
247
190
  def verify(self):
248
191
  # TODO: Add your logic here
249
192
  self.close_success()
@@ -264,12 +207,10 @@ class MyRotator(Python4CPMHandler):
264
207
  # TODO: Add your logic here
265
208
  self.close_success()
266
209
 
267
- MyRotator().run()
210
+ CredManager().run()
268
211
  ```
269
212
 
270
213
  #### Remember for your final script:
271
214
 
272
215
  - Remove the import of `NETHelper`.
273
216
  - Remove the `NETHelper.set()` call.
274
- - If applicable, change the definition of `p4cpm` from `p4cpm = NETHelper.get()` to `p4cpm = Python4CPM("MyApp")`.
275
- - Remove any secrets prompting or interactive interruptions.
@@ -1,63 +0,0 @@
1
- from python4cpm.envhandler import EnvHandler, Props
2
- from python4cpm.secret import Secret
3
-
4
-
5
- class BaseAccount(EnvHandler):
6
- PROPS = Props("username", "password")
7
-
8
- def __init__(
9
- self,
10
- username: str,
11
- password: str
12
- ) -> None:
13
- self._username = username
14
- self._password = Secret(password)
15
-
16
- @property
17
- def username(self) -> str:
18
- return self._username
19
-
20
- @property
21
- def password(self) -> Secret:
22
- return self._password
23
-
24
-
25
- class TargetAccount(BaseAccount):
26
- OBJ_PREFIX = "target_"
27
- PROPS = Props("username", "password", "address", "port", "new_password")
28
-
29
- def __init__(
30
- self,
31
- username: str,
32
- password: str,
33
- address: str,
34
- port: str,
35
- new_password: str
36
- ) -> None:
37
- super().__init__(
38
- username,
39
- password
40
- )
41
- self._address = address
42
- self._port = port
43
- self._new_password = Secret(new_password)
44
-
45
- @property
46
- def address(self) -> str:
47
- return self._address
48
-
49
- @property
50
- def port(self) -> str:
51
- return self._port
52
-
53
- @property
54
- def new_password(self) -> Secret:
55
- return self._new_password
56
-
57
-
58
- class LogonAccount(BaseAccount):
59
- OBJ_PREFIX = "logon_"
60
-
61
-
62
- class ReconcileAccount(BaseAccount):
63
- OBJ_PREFIX = "reconcile_"
@@ -1,15 +0,0 @@
1
- from python4cpm.crypto import Crypto
2
-
3
-
4
- class Secret:
5
- def __init__(self, secret: str) -> None:
6
- self._secret = secret
7
-
8
- def __bool__(self) -> bool:
9
- return bool(self._secret)
10
-
11
- def get(self) -> str:
12
- if Crypto.ENABLED and self._secret:
13
- return Crypto.decrypt(self._secret)
14
- else:
15
- return self._secret
File without changes
File without changes