python3-cyberfusion-file-support 1.1.1.1__tar.gz → 1.2__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.
- {python3-cyberfusion-file-support-1.1.1.1 → python3_cyberfusion_file_support-1.2}/PKG-INFO +5 -11
- {python3-cyberfusion-file-support-1.1.1.1 → python3_cyberfusion_file_support-1.2}/README.md +0 -6
- python3_cyberfusion_file_support-1.2/pyproject.toml +19 -0
- {python3-cyberfusion-file-support-1.1.1.1 → python3_cyberfusion_file_support-1.2}/src/cyberfusion/FileSupport/__init__.py +62 -28
- python3_cyberfusion_file_support-1.2/src/cyberfusion/FileSupport/encryption.py +65 -0
- python3_cyberfusion_file_support-1.2/src/cyberfusion/FileSupport/exceptions.py +13 -0
- {python3-cyberfusion-file-support-1.1.1.1 → python3_cyberfusion_file_support-1.2}/src/python3_cyberfusion_file_support.egg-info/PKG-INFO +5 -11
- {python3-cyberfusion-file-support-1.1.1.1 → python3_cyberfusion_file_support-1.2}/src/python3_cyberfusion_file_support.egg-info/SOURCES.txt +2 -1
- python3-cyberfusion-file-support-1.1.1.1/pyproject.toml +0 -21
- python3-cyberfusion-file-support-1.1.1.1/setup.py +0 -25
- {python3-cyberfusion-file-support-1.1.1.1 → python3_cyberfusion_file_support-1.2}/setup.cfg +0 -0
- {python3-cyberfusion-file-support-1.1.1.1 → python3_cyberfusion_file_support-1.2}/src/python3_cyberfusion_file_support.egg-info/dependency_links.txt +0 -0
- {python3-cyberfusion-file-support-1.1.1.1 → python3_cyberfusion_file_support-1.2}/src/python3_cyberfusion_file_support.egg-info/requires.txt +0 -0
- {python3-cyberfusion-file-support-1.1.1.1 → python3_cyberfusion_file_support-1.2}/src/python3_cyberfusion_file_support.egg-info/top_level.txt +0 -0
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: python3-cyberfusion-file-support
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.2
|
|
4
4
|
Summary: Library for idempotent writing to files.
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
Author-email: support@cyberfusion.io
|
|
8
|
-
Platform: linux
|
|
5
|
+
Author-email: Cyberfusion <support@cyberfusion.io>
|
|
6
|
+
Project-URL: Source, https://github.com/CyberfusionIO/python3-cyberfusion-file-support
|
|
9
7
|
Description-Content-Type: text/markdown
|
|
8
|
+
Requires-Dist: python3-cyberfusion-common~=2.0
|
|
9
|
+
Requires-Dist: python3-cyberfusion-queue-support~=1.1
|
|
10
10
|
|
|
11
11
|
# python3-cyberfusion-file-support
|
|
12
12
|
|
|
@@ -20,12 +20,6 @@ Run the following command to install the package from PyPI:
|
|
|
20
20
|
|
|
21
21
|
pip3 install python3-cyberfusion-file-support
|
|
22
22
|
|
|
23
|
-
## Generic
|
|
24
|
-
|
|
25
|
-
Run the following command to create a source distribution:
|
|
26
|
-
|
|
27
|
-
python3 setup.py sdist
|
|
28
|
-
|
|
29
23
|
## Debian
|
|
30
24
|
|
|
31
25
|
Run the following commands to build a Debian package:
|
|
@@ -10,12 +10,6 @@ Run the following command to install the package from PyPI:
|
|
|
10
10
|
|
|
11
11
|
pip3 install python3-cyberfusion-file-support
|
|
12
12
|
|
|
13
|
-
## Generic
|
|
14
|
-
|
|
15
|
-
Run the following command to create a source distribution:
|
|
16
|
-
|
|
17
|
-
python3 setup.py sdist
|
|
18
|
-
|
|
19
13
|
## Debian
|
|
20
14
|
|
|
21
15
|
Run the following commands to build a Debian package:
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "python3-cyberfusion-file-support"
|
|
7
|
+
version = "1.2"
|
|
8
|
+
description = "Library for idempotent writing to files."
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
authors = [
|
|
11
|
+
{ name = "Cyberfusion", email = "support@cyberfusion.io" },
|
|
12
|
+
]
|
|
13
|
+
dependencies = [
|
|
14
|
+
"python3-cyberfusion-common~=2.0",
|
|
15
|
+
"python3-cyberfusion-queue-support~=1.1",
|
|
16
|
+
]
|
|
17
|
+
|
|
18
|
+
[project.urls]
|
|
19
|
+
"Source" = "https://github.com/CyberfusionIO/python3-cyberfusion-file-support"
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
"""Classes for files."""
|
|
2
2
|
|
|
3
3
|
import difflib
|
|
4
|
-
|
|
4
|
+
|
|
5
5
|
import os
|
|
6
|
+
|
|
6
7
|
from typing import List, Optional, Union
|
|
7
8
|
|
|
8
9
|
from cyberfusion.Common import get_tmp_file
|
|
@@ -11,13 +12,27 @@ from cyberfusion.QueueSupport.items.command import CommandItem
|
|
|
11
12
|
from cyberfusion.QueueSupport.items.copy import CopyItem
|
|
12
13
|
from cyberfusion.QueueSupport.items.unlink import UnlinkItem
|
|
13
14
|
|
|
15
|
+
from cyberfusion.FileSupport.encryption import (
|
|
16
|
+
EncryptionProperties,
|
|
17
|
+
encrypt_file,
|
|
18
|
+
decrypt_file,
|
|
19
|
+
)
|
|
20
|
+
from cyberfusion.FileSupport.exceptions import DecryptionError
|
|
21
|
+
|
|
14
22
|
|
|
15
23
|
class _DestinationFile:
|
|
16
24
|
"""Represents destination file."""
|
|
17
25
|
|
|
18
|
-
def __init__(
|
|
19
|
-
|
|
26
|
+
def __init__(
|
|
27
|
+
self, *, path: str, encryption_properties: Optional[EncryptionProperties] = None
|
|
28
|
+
) -> None:
|
|
29
|
+
"""Set attributes.
|
|
30
|
+
|
|
31
|
+
If 'encryption_properties' is specified, and the destination file already
|
|
32
|
+
exists, it must be encrypted using the same properties (it is decrypted).
|
|
33
|
+
"""
|
|
20
34
|
self.path = path
|
|
35
|
+
self.encryption_properties = encryption_properties
|
|
21
36
|
|
|
22
37
|
@property
|
|
23
38
|
def exists(self) -> bool:
|
|
@@ -27,11 +42,19 @@ class _DestinationFile:
|
|
|
27
42
|
@property
|
|
28
43
|
def contents(self) -> Optional[str]:
|
|
29
44
|
"""Get contents."""
|
|
30
|
-
if self.exists:
|
|
45
|
+
if not self.exists:
|
|
46
|
+
return None
|
|
47
|
+
|
|
48
|
+
if not self.encryption_properties:
|
|
31
49
|
with open(self.path, "r") as f:
|
|
32
50
|
return f.read()
|
|
33
51
|
|
|
34
|
-
|
|
52
|
+
try:
|
|
53
|
+
return decrypt_file(self.encryption_properties, self.path)
|
|
54
|
+
except DecryptionError as e:
|
|
55
|
+
raise DecryptionError(
|
|
56
|
+
f"Decrypting the destination file at '{self.path}' failed. Note that the file must already be encrypted using the specified encryption properties."
|
|
57
|
+
) from e
|
|
35
58
|
|
|
36
59
|
|
|
37
60
|
class DestinationFileReplacement:
|
|
@@ -41,33 +64,37 @@ class DestinationFileReplacement:
|
|
|
41
64
|
self,
|
|
42
65
|
queue: Queue,
|
|
43
66
|
*,
|
|
44
|
-
contents:
|
|
67
|
+
contents: str,
|
|
45
68
|
destination_file_path: str,
|
|
46
69
|
default_comment_character: Optional[str] = None,
|
|
47
70
|
command: Optional[List[str]] = None,
|
|
48
71
|
reference: Optional[str] = None,
|
|
72
|
+
encryption_properties: Optional[EncryptionProperties] = None,
|
|
49
73
|
) -> None:
|
|
50
74
|
"""Set attributes.
|
|
51
75
|
|
|
52
76
|
'default_comment_character' has no effect when 'contents' is not string.
|
|
77
|
+
|
|
78
|
+
If 'encryption_properties' is specified, and the destination file already
|
|
79
|
+
exists, it must be encrypted using the same properties (it is decrypted).
|
|
53
80
|
"""
|
|
54
81
|
self.queue = queue
|
|
55
82
|
self._contents = contents
|
|
56
83
|
self.default_comment_character = default_comment_character
|
|
57
84
|
self.command = command
|
|
58
85
|
self.reference = reference
|
|
86
|
+
self.encryption_properties = encryption_properties
|
|
59
87
|
|
|
60
88
|
self.tmp_path = get_tmp_file()
|
|
61
|
-
self.destination_file = _DestinationFile(
|
|
89
|
+
self.destination_file = _DestinationFile(
|
|
90
|
+
path=destination_file_path, encryption_properties=encryption_properties
|
|
91
|
+
)
|
|
62
92
|
|
|
63
|
-
self.
|
|
93
|
+
self.write_to_file(self.tmp_path)
|
|
64
94
|
|
|
65
95
|
@property
|
|
66
|
-
def contents(self) ->
|
|
96
|
+
def contents(self) -> str:
|
|
67
97
|
"""Get contents."""
|
|
68
|
-
if not isinstance(self._contents, str):
|
|
69
|
-
return self._contents
|
|
70
|
-
|
|
71
98
|
if self._contents != "" and not self._contents.endswith(
|
|
72
99
|
"\n"
|
|
73
100
|
): # Some programs require newline to consider last line completed
|
|
@@ -77,27 +104,39 @@ class DestinationFileReplacement:
|
|
|
77
104
|
return self._contents
|
|
78
105
|
|
|
79
106
|
default_comment = f"{self.default_comment_character} Update this file via your management interface.\n"
|
|
80
|
-
default_comment +=
|
|
107
|
+
default_comment += (
|
|
108
|
+
f"{self.default_comment_character} Your changes will be overwritten.\n"
|
|
109
|
+
)
|
|
81
110
|
default_comment += "\n"
|
|
82
111
|
|
|
83
112
|
return default_comment + self._contents
|
|
84
113
|
|
|
85
|
-
def
|
|
86
|
-
"""Write contents to
|
|
87
|
-
|
|
114
|
+
def write_to_file(self, path: str) -> None:
|
|
115
|
+
"""Write contents to file."""
|
|
116
|
+
contents: Union[str, bytes]
|
|
117
|
+
|
|
118
|
+
if self.encryption_properties:
|
|
88
119
|
open_mode = "wb"
|
|
120
|
+
|
|
121
|
+
contents = encrypt_file(
|
|
122
|
+
self.encryption_properties,
|
|
123
|
+
self.contents,
|
|
124
|
+
)
|
|
89
125
|
else:
|
|
90
126
|
open_mode = "w"
|
|
91
127
|
|
|
92
|
-
|
|
93
|
-
|
|
128
|
+
contents = self.contents
|
|
129
|
+
|
|
130
|
+
with open(path, open_mode) as f:
|
|
131
|
+
f.write(contents)
|
|
94
132
|
|
|
95
133
|
@property
|
|
96
134
|
def changed(self) -> bool:
|
|
97
135
|
"""Get if destination file will change."""
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
136
|
+
if not self.destination_file.exists:
|
|
137
|
+
return True
|
|
138
|
+
|
|
139
|
+
return self.destination_file.contents != self.contents
|
|
101
140
|
|
|
102
141
|
@property
|
|
103
142
|
def differences(self) -> List[str]:
|
|
@@ -105,9 +144,6 @@ class DestinationFileReplacement:
|
|
|
105
144
|
|
|
106
145
|
No differences are returned when contents is not string.
|
|
107
146
|
"""
|
|
108
|
-
if not isinstance(self._contents, str):
|
|
109
|
-
return []
|
|
110
|
-
|
|
111
147
|
results = []
|
|
112
148
|
|
|
113
149
|
for line in difflib.unified_diff(
|
|
@@ -116,7 +152,7 @@ class DestinationFileReplacement:
|
|
|
116
152
|
if self.destination_file.contents
|
|
117
153
|
else []
|
|
118
154
|
),
|
|
119
|
-
self.contents.splitlines(),
|
|
155
|
+
self.contents.splitlines(),
|
|
120
156
|
fromfile=self.tmp_path,
|
|
121
157
|
tofile=self.destination_file.path,
|
|
122
158
|
lineterm="",
|
|
@@ -146,9 +182,7 @@ class DestinationFileReplacement:
|
|
|
146
182
|
|
|
147
183
|
if self.command:
|
|
148
184
|
self.queue.add(
|
|
149
|
-
CommandItem(
|
|
150
|
-
command=self.command, reference=self.reference
|
|
151
|
-
),
|
|
185
|
+
CommandItem(command=self.command, reference=self.reference),
|
|
152
186
|
)
|
|
153
187
|
|
|
154
188
|
self.queue.add(
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
"""Utilities for file encryption."""
|
|
2
|
+
|
|
3
|
+
import subprocess
|
|
4
|
+
from dataclasses import dataclass
|
|
5
|
+
from enum import Enum
|
|
6
|
+
|
|
7
|
+
from cyberfusion.FileSupport.exceptions import EncryptionError, DecryptionError
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class MessageDigestEnum(str, Enum):
|
|
11
|
+
"""Message digests supported by OpenSSL."""
|
|
12
|
+
|
|
13
|
+
MD2 = "md2"
|
|
14
|
+
MD5 = "md5"
|
|
15
|
+
SHA = "sha"
|
|
16
|
+
SHA1 = "sha1"
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
@dataclass
|
|
20
|
+
class EncryptionProperties:
|
|
21
|
+
"""Properties to encrypt files, needed by OpenSSL."""
|
|
22
|
+
|
|
23
|
+
cipher_name: str # Get options with `openssl list -cipher-algorithms`
|
|
24
|
+
message_digest: MessageDigestEnum
|
|
25
|
+
password_file_path: str # Create password with `openssl rand -hex 128`
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def encrypt_file(encryption_properties: EncryptionProperties, contents: str) -> bytes:
|
|
29
|
+
"""Get contents for file to encrypt."""
|
|
30
|
+
try:
|
|
31
|
+
return subprocess.check_output(
|
|
32
|
+
[
|
|
33
|
+
"openssl",
|
|
34
|
+
"enc",
|
|
35
|
+
"-" + encryption_properties.cipher_name,
|
|
36
|
+
"-md",
|
|
37
|
+
encryption_properties.message_digest,
|
|
38
|
+
"-pass",
|
|
39
|
+
"file:" + encryption_properties.password_file_path,
|
|
40
|
+
],
|
|
41
|
+
input=contents.encode(),
|
|
42
|
+
)
|
|
43
|
+
except subprocess.CalledProcessError as e:
|
|
44
|
+
raise EncryptionError from e
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
def decrypt_file(encryption_properties: EncryptionProperties, path: str) -> str:
|
|
48
|
+
"""Get contents of encrypted file."""
|
|
49
|
+
try:
|
|
50
|
+
return subprocess.check_output(
|
|
51
|
+
[
|
|
52
|
+
"openssl",
|
|
53
|
+
"enc",
|
|
54
|
+
"-d",
|
|
55
|
+
"-" + encryption_properties.cipher_name,
|
|
56
|
+
"-md",
|
|
57
|
+
encryption_properties.message_digest,
|
|
58
|
+
"-pass",
|
|
59
|
+
"file:" + encryption_properties.password_file_path,
|
|
60
|
+
"-in",
|
|
61
|
+
path,
|
|
62
|
+
]
|
|
63
|
+
).decode()
|
|
64
|
+
except subprocess.CalledProcessError as e:
|
|
65
|
+
raise DecryptionError from e
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: python3-cyberfusion-file-support
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.2
|
|
4
4
|
Summary: Library for idempotent writing to files.
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
Author-email: support@cyberfusion.io
|
|
8
|
-
Platform: linux
|
|
5
|
+
Author-email: Cyberfusion <support@cyberfusion.io>
|
|
6
|
+
Project-URL: Source, https://github.com/CyberfusionIO/python3-cyberfusion-file-support
|
|
9
7
|
Description-Content-Type: text/markdown
|
|
8
|
+
Requires-Dist: python3-cyberfusion-common~=2.0
|
|
9
|
+
Requires-Dist: python3-cyberfusion-queue-support~=1.1
|
|
10
10
|
|
|
11
11
|
# python3-cyberfusion-file-support
|
|
12
12
|
|
|
@@ -20,12 +20,6 @@ Run the following command to install the package from PyPI:
|
|
|
20
20
|
|
|
21
21
|
pip3 install python3-cyberfusion-file-support
|
|
22
22
|
|
|
23
|
-
## Generic
|
|
24
|
-
|
|
25
|
-
Run the following command to create a source distribution:
|
|
26
|
-
|
|
27
|
-
python3 setup.py sdist
|
|
28
|
-
|
|
29
23
|
## Debian
|
|
30
24
|
|
|
31
25
|
Run the following commands to build a Debian package:
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
README.md
|
|
2
2
|
pyproject.toml
|
|
3
3
|
setup.cfg
|
|
4
|
-
setup.py
|
|
5
4
|
src/cyberfusion/FileSupport/__init__.py
|
|
5
|
+
src/cyberfusion/FileSupport/encryption.py
|
|
6
|
+
src/cyberfusion/FileSupport/exceptions.py
|
|
6
7
|
src/python3_cyberfusion_file_support.egg-info/PKG-INFO
|
|
7
8
|
src/python3_cyberfusion_file_support.egg-info/SOURCES.txt
|
|
8
9
|
src/python3_cyberfusion_file_support.egg-info/dependency_links.txt
|
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
[tool.isort]
|
|
2
|
-
profile = "black"
|
|
3
|
-
line_length = 79
|
|
4
|
-
known_first_party = ["cyberfusion"]
|
|
5
|
-
default_section = "THIRDPARTY"
|
|
6
|
-
|
|
7
|
-
[tool.black]
|
|
8
|
-
line-length = 79
|
|
9
|
-
exclude = '''
|
|
10
|
-
(
|
|
11
|
-
/(
|
|
12
|
-
\.eggs # exclude a few common directories in the
|
|
13
|
-
| \.git # root of the project
|
|
14
|
-
| \.hg
|
|
15
|
-
| \.mypy_cache
|
|
16
|
-
| \.tox
|
|
17
|
-
| \.venv
|
|
18
|
-
| venv
|
|
19
|
-
)/
|
|
20
|
-
)
|
|
21
|
-
'''
|
|
@@ -1,25 +0,0 @@
|
|
|
1
|
-
"""A setuptools based setup module."""
|
|
2
|
-
|
|
3
|
-
from setuptools import setup
|
|
4
|
-
|
|
5
|
-
with open("README.md", "r", encoding="utf-8") as fh:
|
|
6
|
-
long_description = fh.read()
|
|
7
|
-
|
|
8
|
-
setup(
|
|
9
|
-
name="python3-cyberfusion-file-support",
|
|
10
|
-
version="1.1.1.1",
|
|
11
|
-
description="Library for idempotent writing to files.",
|
|
12
|
-
long_description=long_description,
|
|
13
|
-
long_description_content_type="text/markdown",
|
|
14
|
-
author="Cyberfusion",
|
|
15
|
-
author_email="support@cyberfusion.io",
|
|
16
|
-
url="https://github.com/CyberfusionIO/python3-cyberfusion-file-support",
|
|
17
|
-
platforms=["linux"],
|
|
18
|
-
packages=["cyberfusion.FileSupport"],
|
|
19
|
-
package_dir={"": "src"},
|
|
20
|
-
data_files=[],
|
|
21
|
-
install_requires=[
|
|
22
|
-
"python3-cyberfusion-common~=2.0",
|
|
23
|
-
"python3-cyberfusion-queue-support~=1.1",
|
|
24
|
-
],
|
|
25
|
-
)
|
|
File without changes
|
|
File without changes
|
|
File without changes
|