python4cpm 1.1.3__py3-none-any.whl → 1.1.5__py3-none-any.whl

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.

Potentially problematic release.


This version of python4cpm might be problematic. Click here for more details.

python4cpm/__init__.py CHANGED
@@ -1,6 +1,6 @@
1
1
  from python4cpm.python4cpm import Python4CPM
2
2
  from python4cpm.python4cpmhandler import Python4CPMHandler
3
- from python4cpm.nethelper import NETHelper
3
+ from python4cpm.devhelper import DevHelper
4
4
  from importlib.metadata import version as __version
5
5
 
6
6
  __version__ = __version(__name__)
@@ -8,5 +8,5 @@ __version__ = __version(__name__)
8
8
  __all__ = [
9
9
  Python4CPM,
10
10
  Python4CPMHandler,
11
- NETHelper
11
+ DevHelper
12
12
  ]
python4cpm/accounts.py CHANGED
@@ -31,10 +31,22 @@ 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
+ "safe_name",
37
+ "object_name",
38
+ "username",
39
+ "password",
40
+ "address",
41
+ "port",
42
+ "new_password"
43
+ )
35
44
 
36
45
  def __init__(
37
46
  self,
47
+ policy_id: str | None,
48
+ safe_name: str | None,
49
+ object_name: str | None,
38
50
  username: str | None,
39
51
  password: str | None,
40
52
  address: str | None,
@@ -45,10 +57,25 @@ class TargetAccount(BaseAccount):
45
57
  username,
46
58
  password
47
59
  )
60
+ self._policy_id = policy_id
61
+ self._safe_name = safe_name
62
+ self._object_name = object_name
48
63
  self._address = address
49
64
  self._port = port
50
65
  self._new_password = Secret.from_env_var(new_password)
51
66
 
67
+ @property
68
+ def policy_id(self) -> str | None:
69
+ return self._policy_id
70
+
71
+ @property
72
+ def safe_name(self) -> str | None:
73
+ return self._safe_name
74
+
75
+ @property
76
+ def object_name(self) -> str | None:
77
+ return self._object_name
78
+
52
79
  @property
53
80
  def address(self) -> str | None:
54
81
  return self._address
python4cpm/args.py CHANGED
@@ -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
@@ -1,16 +1,18 @@
1
- from python4cpm.python4cpm import Python4CPM
2
1
  from python4cpm.args import Args
3
2
  from python4cpm.accounts import TargetAccount, LogonAccount, ReconcileAccount
4
3
  from python4cpm.crypto import Crypto
5
4
  import os
6
5
 
7
6
 
8
- class NETHelper:
7
+ class DevHelper:
9
8
  @classmethod
10
9
  def set(
11
10
  cls,
12
11
  action: str | None = None,
13
12
  logging_level: str | None = None,
13
+ target_policy_id: str | None = None,
14
+ target_safe_name: 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,9 @@ 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.safe_name),
36
+ TargetAccount.get_key(TargetAccount.PROPS.object_name),
32
37
  TargetAccount.get_key(TargetAccount.PROPS.username),
33
38
  TargetAccount.get_key(TargetAccount.PROPS.address),
34
39
  TargetAccount.get_key(TargetAccount.PROPS.port),
@@ -42,6 +47,9 @@ class NETHelper:
42
47
  values = (
43
48
  action,
44
49
  logging_level,
50
+ target_policy_id,
51
+ target_safe_name,
52
+ target_object_name,
45
53
  target_username,
46
54
  target_address,
47
55
  target_port,
@@ -55,7 +63,3 @@ class NETHelper:
55
63
  for i, key in enumerate(keys):
56
64
  if values[i] is not None:
57
65
  os.environ.update({key: values[i]})
58
-
59
- @classmethod
60
- def get(cls) -> Python4CPM:
61
- return Python4CPM(cls.__name__)
python4cpm/logger.py CHANGED
@@ -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,21 +21,21 @@ 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 or cls._DEFAULT_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}_{uid}.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,
35
35
  backupCount=1
36
36
  )
37
37
  fmt = (
38
- "%(asctime)s.%(msecs)03d | %(levelname)s | %(name)s | "
38
+ "%(asctime)s.%(msecs)03d | %(levelname)s | "
39
39
  "%(module)s | %(funcName)s | %(message)s"
40
40
  )
41
41
  datefmt = "%Y-%m-%d %H:%M:%S"
python4cpm/python4cpm.py CHANGED
@@ -24,13 +24,12 @@ 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 = self._get_logger()
34
33
  self._logger.debug("Initiating...")
35
34
  self._log_obj(self._args)
36
35
  self._verify_action()
@@ -60,6 +59,15 @@ class Python4CPM:
60
59
  def reconcile_account(self) -> ReconcileAccount:
61
60
  return self._reconcile_account
62
61
 
62
+ def _get_logger(self) -> logging.Logger:
63
+ name_parts = (
64
+ self._target_account.policy_id,
65
+ self._target_account.safe_name,
66
+ self._target_account.object_name
67
+ )
68
+ name = "-".join(name_parts)
69
+ return Logger.get_logger(name, self._args.logging_level)
70
+
63
71
  def _verify_action(self) -> None:
64
72
  if self._args.action not in self._VALID_ACTIONS:
65
73
  self._logger.warning(f"Unkonwn action -> '{self._args.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.5
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,83 @@ 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
+ .username (str): Account username.
75
+ .address (str): Target address.
76
+ .port (str): Target port.
77
+ .password (Secret): Current password. Call .get() to retrieve value.
78
+ .new_password (Secret): Replacement password. Call .get() to retrieve value.
79
+
80
+ logon_account (LogonAccount): Linked Logon Account.
81
+ .username (str): Account username.
82
+ .password (Secret): Logon password. Call .get() to retrieve value.
83
+
84
+ reconcile_account (ReconcileAccount): Linked Reconcile Account.
85
+ .username (str): Account username.
86
+ .password (Secret): Reconcile password. Call .get() to retrieve value.
87
+
88
+ logger (logging.Logger): Logger instance.
89
+
90
+ Methods:
91
+ close_success(): Signal successful completion and terminate.
92
+ close_fail(): Signal failed completion and terminate.
131
93
  """
132
94
 
95
+
133
96
  def verify(self):
97
+ """
98
+ REQUIRED METHOD
99
+ """
134
100
  # TODO: use account objects for your logic
135
101
  self.close_success()
136
102
 
137
103
  def logon(self):
104
+ """
105
+ REQUIRED METHOD
106
+ """
138
107
  # TODO: use account objects for your logic
139
108
  self.close_success()
140
109
 
141
110
  def change(self):
111
+ """
112
+ REQUIRED METHOD
113
+ """
142
114
  # TODO: use account objects for your logic
143
115
  self.close_success()
144
116
 
145
117
  def prereconcile(self):
118
+ """
119
+ REQUIRED METHOD
120
+ """
146
121
  # TODO: use account objects for your logic
147
122
  self.close_success()
148
123
 
149
124
  def reconcile(self):
125
+ """
126
+ REQUIRED METHOD
127
+ """
150
128
  # TODO: use account objects for your logic
151
129
  self.close_success()
152
130
 
153
131
 
154
132
  if __name__ == "__main__":
155
- MyRotator().run() # initializes the class and calls the action that was requested from CPM/SRS.
133
+ CredManager().run() # initializes the class and calls the action that was requested from CPM/SRS.
156
134
  ```
157
135
  (*) More realistic examples can be found [here](https://github.com/gonatienza/python4cpm/blob/main/examples).
158
136
 
159
137
  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`).
138
+ 1. Verify -> the script will be executed once running the `verify()` method.
139
+ 2. Change -> the script will be executed twice: first `logon()`, then `change()`.
140
+ 3. Reconcile -> the script will be executed twice: first `prereconcile()`, then `reconcile()`.
141
+ 4. When calling `verify()`, `logon()` or `prereconcile()`: `target_account.new_password` will always return `None`.
142
+ 5. If a logon account is not linked, `logon_account` will return `None`.
143
+ 6. If a reconcile account is not linked, `reconcile_account` will return `None`.
144
+ 7. Always use the `close_success` or `close_fail` methods to signal the proper termination for all actions.
145
+ - 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.
146
+ 8. The python `Logger` places its logs in the `Logs/ThirdParty` directory.
169
147
 
170
148
 
171
149
  ### Installing dependencies in python venv
@@ -179,40 +157,35 @@ As with any python venv, you can install dependencies in your venv.
179
157
 
180
158
  ## Dev Helper:
181
159
 
182
- 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.
160
+ For dev purposes, `DevHelper` 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.
183
161
 
184
- **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.
162
+ **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 `DevHelper` will be in plaintext. In windows dev workstations, `DevHelper` 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.
185
163
 
186
164
  ### Example:
187
165
 
188
166
  #### Set your arguments and secrets:
189
167
 
190
168
  ```python
191
- 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
169
+ from python4cpm import DevHelper, Python4CPM, Python4CPMHandler
200
170
 
201
- NETHelper.set(
171
+ DevHelper.set(
202
172
  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()
173
+ logging_level="debug",
174
+ target_policy_id="DevHelper", # name of platform
175
+ target_safe_name="Safename", # name of safe
176
+ target_object_name="Objectname", # name of account
177
+ target_username="jdoe",
178
+ target_address="myapp.corp.local",
179
+ target_port="8443",
180
+ logon_username="ldoe",
181
+ reconcile_username="rdoe",
182
+ target_password="", # str value of target password
183
+ logon_password="", # str value of logon password
184
+ reconcile_password="", # str value of reconcile password
185
+ target_new_password="" # str value of new password
213
186
  )
214
187
 
215
- class MyRotator(Python4CPMHandler):
188
+ class CredManager(Python4CPMHandler):
216
189
  def verify(self):
217
190
  # TODO: Add your logic here
218
191
  self.close_success()
@@ -233,11 +206,10 @@ class MyRotator(Python4CPMHandler):
233
206
  # TODO: Add your logic here
234
207
  self.close_success()
235
208
 
236
- MyRotator().run()
209
+ CredManager().run()
237
210
  ```
238
211
 
239
212
  #### Remember for your final script:
240
213
 
241
- - Remove the import of `NETHelper`.
242
- - Remove the `NETHelper.set()` call.
243
- - Remove any secrets prompting or interactive interruptions.
214
+ - Remove the import of `DevHelper`.
215
+ - Remove the `DevHelper.set()` call.
@@ -0,0 +1,15 @@
1
+ python4cpm/__init__.py,sha256=GQvsXZY2escF3zucv3d68s2ydqHSIYi5tnNDCkhTjcM,302
2
+ python4cpm/accounts.py,sha256=tzLr6kFeHD6jDF-dZXjp2FeoABFsWn2hp4bO5uYN2Zg,2201
3
+ python4cpm/args.py,sha256=PQDDlMakP-YsDLd3HdwIZtxVpIM4m4aHl2GuRj9kHpI,504
4
+ python4cpm/crypto.py,sha256=AKjmCVkuQRxomhuQJ8b5IN5QPc8n9aAMui99ZEgnGAg,2388
5
+ python4cpm/devhelper.py,sha256=iv5zHSMyMNMdksGkkTh_8c_ObocJfQormQ4S3nZzo0Y,2609
6
+ python4cpm/envhandler.py,sha256=mNDOaYVemmxKREXyOf7IuQdFDXrvZ7f_7sNaqWV7TsI,678
7
+ python4cpm/logger.py,sha256=XmuCY1Guxyo5aNKfnueLZY55_UePtH-FFysxXiTPjTE,1456
8
+ python4cpm/python4cpm.py,sha256=-zgvLHnnnvwnmSxTwizdtHwKJPK7DcsUZIHES5Q6sR8,3449
9
+ python4cpm/python4cpmhandler.py,sha256=SXKsnFAwUp8cHEhv2TygpNpptUkN3bVQZt1i-3TWql4,935
10
+ python4cpm/secret.py,sha256=Z2K8H406lViOLPJKklffScug6sdWoxt0T5jhX0EA_gI,572
11
+ python4cpm-1.1.5.dist-info/licenses/LICENSE,sha256=vF2wZA3GchZjH5lUmhonEwd4snuBc6o0ytpZNxVFyCw,1077
12
+ python4cpm-1.1.5.dist-info/METADATA,sha256=UD0JHnDBBH-6lT34jN4TyJL6K7n7m2AzsZ_ThQTfS8g,11193
13
+ python4cpm-1.1.5.dist-info/WHEEL,sha256=aeYiig01lYGDzBgS8HxWXOg3uV61G9ijOsup-k9o1sk,91
14
+ python4cpm-1.1.5.dist-info/top_level.txt,sha256=EhbshwpANmL0wYXaET8_6V9ZENtSwIyAd-kM3jGhUlE,11
15
+ python4cpm-1.1.5.dist-info/RECORD,,
@@ -1,15 +0,0 @@
1
- python4cpm/__init__.py,sha256=Z5UI6SPWKeegUkQ_YqMKTuYNIiDvgg5BmynCMJxpPaI,302
2
- python4cpm/accounts.py,sha256=zCyPpbqAECj5_mak7PsRnsWA5-a9j7BwOVHcEbDdEVU,1624
3
- python4cpm/args.py,sha256=2Td9rxbe7KDRv234l0yVtIYezd5X2DE7Jo9EseDrlKM,497
4
- python4cpm/crypto.py,sha256=AKjmCVkuQRxomhuQJ8b5IN5QPc8n9aAMui99ZEgnGAg,2388
5
- python4cpm/envhandler.py,sha256=mNDOaYVemmxKREXyOf7IuQdFDXrvZ7f_7sNaqWV7TsI,678
6
- python4cpm/logger.py,sha256=H8yoJ0GrI9OJful0nT3t1HwBbn-BVEn2q7Z2CDjOs1o,1475
7
- python4cpm/nethelper.py,sha256=iGbqQ8yt1T_GQSebG6u46caJzqE--dyCcws1XeCE99A,2315
8
- python4cpm/python4cpm.py,sha256=BpyPdhRftkcPWZwank8FAsM8x3mY4AwCGyA5xW79cZo,3210
9
- python4cpm/python4cpmhandler.py,sha256=zIhDVLinwnRqdef2Dz9wWPdDWFwOere6CZoAhgeHPoU,1018
10
- python4cpm/secret.py,sha256=Z2K8H406lViOLPJKklffScug6sdWoxt0T5jhX0EA_gI,572
11
- python4cpm-1.1.3.dist-info/licenses/LICENSE,sha256=vF2wZA3GchZjH5lUmhonEwd4snuBc6o0ytpZNxVFyCw,1077
12
- python4cpm-1.1.3.dist-info/METADATA,sha256=lKEQaRuJ4U-wzmTzz7zLCpqdGGg6vB8jBvype21BUio,13308
13
- python4cpm-1.1.3.dist-info/WHEEL,sha256=aeYiig01lYGDzBgS8HxWXOg3uV61G9ijOsup-k9o1sk,91
14
- python4cpm-1.1.3.dist-info/top_level.txt,sha256=EhbshwpANmL0wYXaET8_6V9ZENtSwIyAd-kM3jGhUlE,11
15
- python4cpm-1.1.3.dist-info/RECORD,,