python4cpm 1.1.3__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.3
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,105 +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):
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
- =============================
128
- REQUIRED METHODS
129
- =============================
130
- verify(), logon(), change(), prereconcile(), reconcile()
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.
131
95
  """
132
96
 
97
+
133
98
  def verify(self):
99
+ """
100
+ REQUIRED METHOD
101
+ """
134
102
  # TODO: use account objects for your logic
135
103
  self.close_success()
136
104
 
137
105
  def logon(self):
106
+ """
107
+ REQUIRED METHOD
108
+ """
138
109
  # TODO: use account objects for your logic
139
110
  self.close_success()
140
111
 
141
112
  def change(self):
113
+ """
114
+ REQUIRED METHOD
115
+ """
142
116
  # TODO: use account objects for your logic
143
117
  self.close_success()
144
118
 
145
119
  def prereconcile(self):
120
+ """
121
+ REQUIRED METHOD
122
+ """
146
123
  # TODO: use account objects for your logic
147
124
  self.close_success()
148
125
 
149
126
  def reconcile(self):
127
+ """
128
+ REQUIRED METHOD
129
+ """
150
130
  # TODO: use account objects for your logic
151
131
  self.close_success()
152
132
 
153
133
 
154
134
  if __name__ == "__main__":
155
- 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.
156
136
  ```
157
137
  (*) More realistic examples can be found [here](https://github.com/gonatienza/python4cpm/blob/main/examples).
158
138
 
159
139
  When doing `verify`, `change` or `reconcile` from Privilege Cloud/PVWA:
160
- 1. Verify -> the sciprt will be executed once running the `MyRotator.verify()` method.
161
- 2. Change -> the sciprt will be executed twice, running first the `MyRotator.logon()` method and secondly the `MyRotator.change()` method.
162
- - 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)`.
163
- 3. Reconcile -> the sciprt will be executed twice, running first the `MyRotator.prereconcile()` method and secondly the `MyRotator.reconcile()` method.
164
- - 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)`.
165
- 4. When calling `MyRotator.verify()`, `MyRotator.logon()` or `MyRotator.prereconcile()`: `self.target_account.new_password` will always return `None`.
166
- 5. If a logon account is not linked, `self.logon_account` will return `None`.
167
- 6. If a reconcile account is not linked, `self.reconcile_account` will return `None`.
168
- 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`.
169
149
 
170
150
 
171
151
  ### Installing dependencies in python venv
@@ -189,30 +169,24 @@ For dev purposes, `NETHelper` is a companion helper to test your scripts without
189
169
 
190
170
  ```python
191
171
  from python4cpm import NETHelper, Python4CPM, Python4CPMHandler
192
- from getpass import getpass
193
-
194
- # Get secrets for your password, logon account password, reconcile account password and new password
195
- # You may set to None any argument that does not apply or simply leaving it to its default None value.
196
- target_password = getpass("password: ") # password from account
197
- logon_password = getpass("logon_password: ") # password from linked logon account
198
- reconcile_password = getpass("reconcile_password: ") # password from linked reconcile account
199
- target_new_password = getpass("new_password: ") # new password for the rotation
200
172
 
201
173
  NETHelper.set(
202
174
  action=Python4CPM.ACTION_CHANGE, # use actions from Python4CPM.ACTION_*
203
- target_username="jdoe", # -> will fall under MyRotator.target_account.username
204
- target_address="myapp.corp.local", # -> will fall under MyRotator.target_account.address
205
- target_port="8443", # -> will fall under MyRotator.target_account.port
206
- logon_username="ldoe", # -> will fall under MyRotator.logon_account.username
207
- reconcile_username="rdoe", # -> will fall under MyRotator.reconcile_account.username
208
- logging_level="debug", # "critical", "error", "warning", "info" or "debug"
209
- target_password=target_password, # -> will fall under MyRotator.target_account.password.get()
210
- logon_password=logon_password, # -> will fall under MyRotator.logon_account.password.get()
211
- reconcile_password=reconcile_password, # -> will fall under MyRotator.reconcile_account.password.get()
212
- 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
213
187
  )
214
188
 
215
- class MyRotator(Python4CPMHandler):
189
+ class CredManager(Python4CPMHandler):
216
190
  def verify(self):
217
191
  # TODO: Add your logic here
218
192
  self.close_success()
@@ -233,11 +207,10 @@ class MyRotator(Python4CPMHandler):
233
207
  # TODO: Add your logic here
234
208
  self.close_success()
235
209
 
236
- MyRotator().run()
210
+ CredManager().run()
237
211
  ```
238
212
 
239
213
  #### Remember for your final script:
240
214
 
241
215
  - Remove the import of `NETHelper`.
242
216
  - Remove the `NETHelper.set()` call.
243
- - 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,105 +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):
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
- =============================
117
- REQUIRED METHODS
118
- =============================
119
- verify(), logon(), change(), prereconcile(), reconcile()
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.
120
84
  """
121
85
 
86
+
122
87
  def verify(self):
88
+ """
89
+ REQUIRED METHOD
90
+ """
123
91
  # TODO: use account objects for your logic
124
92
  self.close_success()
125
93
 
126
94
  def logon(self):
95
+ """
96
+ REQUIRED METHOD
97
+ """
127
98
  # TODO: use account objects for your logic
128
99
  self.close_success()
129
100
 
130
101
  def change(self):
102
+ """
103
+ REQUIRED METHOD
104
+ """
131
105
  # TODO: use account objects for your logic
132
106
  self.close_success()
133
107
 
134
108
  def prereconcile(self):
109
+ """
110
+ REQUIRED METHOD
111
+ """
135
112
  # TODO: use account objects for your logic
136
113
  self.close_success()
137
114
 
138
115
  def reconcile(self):
116
+ """
117
+ REQUIRED METHOD
118
+ """
139
119
  # TODO: use account objects for your logic
140
120
  self.close_success()
141
121
 
142
122
 
143
123
  if __name__ == "__main__":
144
- 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.
145
125
  ```
146
126
  (*) More realistic examples can be found [here](https://github.com/gonatienza/python4cpm/blob/main/examples).
147
127
 
148
128
  When doing `verify`, `change` or `reconcile` from Privilege Cloud/PVWA:
149
- 1. Verify -> the sciprt will be executed once running the `MyRotator.verify()` method.
150
- 2. Change -> the sciprt will be executed twice, running first the `MyRotator.logon()` method and secondly the `MyRotator.change()` method.
151
- - 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)`.
152
- 3. Reconcile -> the sciprt will be executed twice, running first the `MyRotator.prereconcile()` method and secondly the `MyRotator.reconcile()` method.
153
- - 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)`.
154
- 4. When calling `MyRotator.verify()`, `MyRotator.logon()` or `MyRotator.prereconcile()`: `self.target_account.new_password` will always return `None`.
155
- 5. If a logon account is not linked, `self.logon_account` will return `None`.
156
- 6. If a reconcile account is not linked, `self.reconcile_account` will return `None`.
157
- 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`.
158
138
 
159
139
 
160
140
  ### Installing dependencies in python venv
@@ -178,30 +158,24 @@ For dev purposes, `NETHelper` is a companion helper to test your scripts without
178
158
 
179
159
  ```python
180
160
  from python4cpm import NETHelper, Python4CPM, Python4CPMHandler
181
- from getpass import getpass
182
-
183
- # Get secrets for your password, logon account password, reconcile account password and new password
184
- # You may set to None any argument that does not apply or simply leaving it to its default None value.
185
- target_password = getpass("password: ") # password from account
186
- logon_password = getpass("logon_password: ") # password from linked logon account
187
- reconcile_password = getpass("reconcile_password: ") # password from linked reconcile account
188
- target_new_password = getpass("new_password: ") # new password for the rotation
189
161
 
190
162
  NETHelper.set(
191
163
  action=Python4CPM.ACTION_CHANGE, # use actions from Python4CPM.ACTION_*
192
- target_username="jdoe", # -> will fall under MyRotator.target_account.username
193
- target_address="myapp.corp.local", # -> will fall under MyRotator.target_account.address
194
- target_port="8443", # -> will fall under MyRotator.target_account.port
195
- logon_username="ldoe", # -> will fall under MyRotator.logon_account.username
196
- reconcile_username="rdoe", # -> will fall under MyRotator.reconcile_account.username
197
- logging_level="debug", # "critical", "error", "warning", "info" or "debug"
198
- target_password=target_password, # -> will fall under MyRotator.target_account.password.get()
199
- logon_password=logon_password, # -> will fall under MyRotator.logon_account.password.get()
200
- reconcile_password=reconcile_password, # -> will fall under MyRotator.reconcile_account.password.get()
201
- 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
202
176
  )
203
177
 
204
- class MyRotator(Python4CPMHandler):
178
+ class CredManager(Python4CPMHandler):
205
179
  def verify(self):
206
180
  # TODO: Add your logic here
207
181
  self.close_success()
@@ -222,11 +196,10 @@ class MyRotator(Python4CPMHandler):
222
196
  # TODO: Add your logic here
223
197
  self.close_success()
224
198
 
225
- MyRotator().run()
199
+ CredManager().run()
226
200
  ```
227
201
 
228
202
  #### Remember for your final script:
229
203
 
230
204
  - Remove the import of `NETHelper`.
231
205
  - Remove the `NETHelper.set()` call.
232
- - 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.3"
7
+ version = "1.1.4"
8
8
  description = "Python for CPM"
9
9
  authors = [
10
10
  { name = "Gonzalo Atienza Rela", email = "gonatienza@gmail.com" }
@@ -31,10 +31,20 @@ class BaseAccount(EnvHandler):
31
31
 
32
32
  class TargetAccount(BaseAccount):
33
33
  OBJ_PREFIX = "target_"
34
- PROPS = Props("username", "password", "address", "port", "new_password")
34
+ PROPS = Props(
35
+ "policy_id",
36
+ "object_name",
37
+ "username",
38
+ "password",
39
+ "address",
40
+ "port",
41
+ "new_password"
42
+ )
35
43
 
36
44
  def __init__(
37
45
  self,
46
+ policy_id: str | None,
47
+ object_name: str | None,
38
48
  username: str | None,
39
49
  password: str | None,
40
50
  address: str | None,
@@ -45,10 +55,20 @@ class TargetAccount(BaseAccount):
45
55
  username,
46
56
  password
47
57
  )
58
+ self._policy_id = policy_id
59
+ self._object_name = object_name
48
60
  self._address = address
49
61
  self._port = port
50
62
  self._new_password = Secret.from_env_var(new_password)
51
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
+
52
72
  @property
53
73
  def address(self) -> str | None:
54
74
  return self._address
@@ -14,7 +14,7 @@ class Args(EnvHandler):
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
@@ -12,7 +12,7 @@ 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(
@@ -21,14 +21,14 @@ class Logger:
21
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
- is_logging_level_str = isinstance(logging_level, str)
28
- if is_logging_level_str and logging_level.lower() in cls._LOGGING_LEVELS:
29
- logger.setLevel(cls._LOGGING_LEVELS[logging_level.lower()])
30
- else:
31
- 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)
32
32
  handler = RotatingFileHandler(
33
33
  filename=logs_file,
34
34
  maxBytes=1024 ** 2,
@@ -11,6 +11,8 @@ class NETHelper:
11
11
  cls,
12
12
  action: str | None = None,
13
13
  logging_level: str | None = None,
14
+ target_policy_id: str | None = None,
15
+ target_object_name: str | None = None,
14
16
  target_username: str | None = None,
15
17
  target_address: str | None = None,
16
18
  target_port: str | None = None,
@@ -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,
@@ -58,4 +64,4 @@ class NETHelper:
58
64
 
59
65
  @classmethod
60
66
  def get(cls) -> Python4CPM:
61
- return Python4CPM(cls.__name__)
67
+ return Python4CPM()
@@ -24,13 +24,15 @@ class Python4CPM:
24
24
  _FAILED_RECOVERABLE_CODE = 81
25
25
  _FAILED_UNRECOVERABLE_CODE = 89
26
26
 
27
- def __init__(self, name: str) -> None:
28
- self._name = name
27
+ def __init__(self) -> None:
29
28
  self._args = Args.get()
30
29
  self._target_account = TargetAccount.get()
31
30
  self._logon_account = LogonAccount.get()
32
31
  self._reconcile_account = ReconcileAccount.get()
33
- 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
+ )
34
36
  self._logger.debug("Initiating...")
35
37
  self._log_obj(self._args)
36
38
  self._verify_action()
@@ -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,
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: python4cpm
3
- Version: 1.1.3
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,105 +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):
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
- =============================
128
- REQUIRED METHODS
129
- =============================
130
- verify(), logon(), change(), prereconcile(), reconcile()
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.
131
95
  """
132
96
 
97
+
133
98
  def verify(self):
99
+ """
100
+ REQUIRED METHOD
101
+ """
134
102
  # TODO: use account objects for your logic
135
103
  self.close_success()
136
104
 
137
105
  def logon(self):
106
+ """
107
+ REQUIRED METHOD
108
+ """
138
109
  # TODO: use account objects for your logic
139
110
  self.close_success()
140
111
 
141
112
  def change(self):
113
+ """
114
+ REQUIRED METHOD
115
+ """
142
116
  # TODO: use account objects for your logic
143
117
  self.close_success()
144
118
 
145
119
  def prereconcile(self):
120
+ """
121
+ REQUIRED METHOD
122
+ """
146
123
  # TODO: use account objects for your logic
147
124
  self.close_success()
148
125
 
149
126
  def reconcile(self):
127
+ """
128
+ REQUIRED METHOD
129
+ """
150
130
  # TODO: use account objects for your logic
151
131
  self.close_success()
152
132
 
153
133
 
154
134
  if __name__ == "__main__":
155
- 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.
156
136
  ```
157
137
  (*) More realistic examples can be found [here](https://github.com/gonatienza/python4cpm/blob/main/examples).
158
138
 
159
139
  When doing `verify`, `change` or `reconcile` from Privilege Cloud/PVWA:
160
- 1. Verify -> the sciprt will be executed once running the `MyRotator.verify()` method.
161
- 2. Change -> the sciprt will be executed twice, running first the `MyRotator.logon()` method and secondly the `MyRotator.change()` method.
162
- - 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)`.
163
- 3. Reconcile -> the sciprt will be executed twice, running first the `MyRotator.prereconcile()` method and secondly the `MyRotator.reconcile()` method.
164
- - 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)`.
165
- 4. When calling `MyRotator.verify()`, `MyRotator.logon()` or `MyRotator.prereconcile()`: `self.target_account.new_password` will always return `None`.
166
- 5. If a logon account is not linked, `self.logon_account` will return `None`.
167
- 6. If a reconcile account is not linked, `self.reconcile_account` will return `None`.
168
- 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`.
169
149
 
170
150
 
171
151
  ### Installing dependencies in python venv
@@ -189,30 +169,24 @@ For dev purposes, `NETHelper` is a companion helper to test your scripts without
189
169
 
190
170
  ```python
191
171
  from python4cpm import NETHelper, Python4CPM, Python4CPMHandler
192
- from getpass import getpass
193
-
194
- # Get secrets for your password, logon account password, reconcile account password and new password
195
- # You may set to None any argument that does not apply or simply leaving it to its default None value.
196
- target_password = getpass("password: ") # password from account
197
- logon_password = getpass("logon_password: ") # password from linked logon account
198
- reconcile_password = getpass("reconcile_password: ") # password from linked reconcile account
199
- target_new_password = getpass("new_password: ") # new password for the rotation
200
172
 
201
173
  NETHelper.set(
202
174
  action=Python4CPM.ACTION_CHANGE, # use actions from Python4CPM.ACTION_*
203
- target_username="jdoe", # -> will fall under MyRotator.target_account.username
204
- target_address="myapp.corp.local", # -> will fall under MyRotator.target_account.address
205
- target_port="8443", # -> will fall under MyRotator.target_account.port
206
- logon_username="ldoe", # -> will fall under MyRotator.logon_account.username
207
- reconcile_username="rdoe", # -> will fall under MyRotator.reconcile_account.username
208
- logging_level="debug", # "critical", "error", "warning", "info" or "debug"
209
- target_password=target_password, # -> will fall under MyRotator.target_account.password.get()
210
- logon_password=logon_password, # -> will fall under MyRotator.logon_account.password.get()
211
- reconcile_password=reconcile_password, # -> will fall under MyRotator.reconcile_account.password.get()
212
- 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
213
187
  )
214
188
 
215
- class MyRotator(Python4CPMHandler):
189
+ class CredManager(Python4CPMHandler):
216
190
  def verify(self):
217
191
  # TODO: Add your logic here
218
192
  self.close_success()
@@ -233,11 +207,10 @@ class MyRotator(Python4CPMHandler):
233
207
  # TODO: Add your logic here
234
208
  self.close_success()
235
209
 
236
- MyRotator().run()
210
+ CredManager().run()
237
211
  ```
238
212
 
239
213
  #### Remember for your final script:
240
214
 
241
215
  - Remove the import of `NETHelper`.
242
216
  - Remove the `NETHelper.set()` call.
243
- - Remove any secrets prompting or interactive interruptions.
File without changes
File without changes