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.
- {python4cpm-1.1.3/src/python4cpm.egg-info → python4cpm-1.1.4}/PKG-INFO +66 -93
- {python4cpm-1.1.3 → python4cpm-1.1.4}/README.md +65 -92
- {python4cpm-1.1.3 → python4cpm-1.1.4}/pyproject.toml +1 -1
- {python4cpm-1.1.3 → python4cpm-1.1.4}/src/python4cpm/accounts.py +21 -1
- {python4cpm-1.1.3 → python4cpm-1.1.4}/src/python4cpm/args.py +1 -1
- {python4cpm-1.1.3 → python4cpm-1.1.4}/src/python4cpm/logger.py +9 -9
- {python4cpm-1.1.3 → python4cpm-1.1.4}/src/python4cpm/nethelper.py +7 -1
- {python4cpm-1.1.3 → python4cpm-1.1.4}/src/python4cpm/python4cpm.py +5 -3
- {python4cpm-1.1.3 → python4cpm-1.1.4}/src/python4cpm/python4cpmhandler.py +0 -3
- {python4cpm-1.1.3 → python4cpm-1.1.4/src/python4cpm.egg-info}/PKG-INFO +66 -93
- {python4cpm-1.1.3 → python4cpm-1.1.4}/LICENSE +0 -0
- {python4cpm-1.1.3 → python4cpm-1.1.4}/setup.cfg +0 -0
- {python4cpm-1.1.3 → python4cpm-1.1.4}/src/python4cpm/__init__.py +0 -0
- {python4cpm-1.1.3 → python4cpm-1.1.4}/src/python4cpm/crypto.py +0 -0
- {python4cpm-1.1.3 → python4cpm-1.1.4}/src/python4cpm/envhandler.py +0 -0
- {python4cpm-1.1.3 → python4cpm-1.1.4}/src/python4cpm/secret.py +0 -0
- {python4cpm-1.1.3 → python4cpm-1.1.4}/src/python4cpm.egg-info/SOURCES.txt +0 -0
- {python4cpm-1.1.3 → python4cpm-1.1.4}/src/python4cpm.egg-info/dependency_links.txt +0 -0
- {python4cpm-1.1.3 → python4cpm-1.1.4}/src/python4cpm.egg-info/top_level.txt +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: python4cpm
|
|
3
|
-
Version: 1.1.
|
|
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 (
|
|
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
|
|
70
|
+
class CredManager(Python4CPMHandler):
|
|
71
71
|
"""
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
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
|
-
|
|
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
|
|
161
|
-
2. Change -> the
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
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
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
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
|
|
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
|
-
|
|
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 (
|
|
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
|
|
59
|
+
class CredManager(Python4CPMHandler):
|
|
60
60
|
"""
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
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
|
-
|
|
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
|
|
150
|
-
2. Change -> the
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
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
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
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
|
|
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
|
-
|
|
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.
|
|
@@ -31,10 +31,20 @@ class BaseAccount(EnvHandler):
|
|
|
31
31
|
|
|
32
32
|
class TargetAccount(BaseAccount):
|
|
33
33
|
OBJ_PREFIX = "target_"
|
|
34
|
-
PROPS = Props(
|
|
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
|
|
@@ -12,7 +12,7 @@ class Logger:
|
|
|
12
12
|
"info": logging.INFO,
|
|
13
13
|
"debug": logging.DEBUG
|
|
14
14
|
}
|
|
15
|
-
_DEFAULT_LEVEL =
|
|
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
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
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(
|
|
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
|
|
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(
|
|
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()
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: python4cpm
|
|
3
|
-
Version: 1.1.
|
|
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 (
|
|
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
|
|
70
|
+
class CredManager(Python4CPMHandler):
|
|
71
71
|
"""
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
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
|
-
|
|
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
|
|
161
|
-
2. Change -> the
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
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
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
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
|
|
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
|
-
|
|
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
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|