python3-cyberfusion-file-support 1.1.1.2__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.2 → python3_cyberfusion_file_support-1.2}/PKG-INFO +1 -1
- {python3_cyberfusion_file_support-1.1.1.2 → python3_cyberfusion_file_support-1.2}/pyproject.toml +1 -1
- {python3_cyberfusion_file_support-1.1.1.2 → python3_cyberfusion_file_support-1.2}/src/cyberfusion/FileSupport/__init__.py +58 -24
- 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.2 → python3_cyberfusion_file_support-1.2}/src/python3_cyberfusion_file_support.egg-info/PKG-INFO +1 -1
- {python3_cyberfusion_file_support-1.1.1.2 → python3_cyberfusion_file_support-1.2}/src/python3_cyberfusion_file_support.egg-info/SOURCES.txt +2 -0
- {python3_cyberfusion_file_support-1.1.1.2 → python3_cyberfusion_file_support-1.2}/README.md +0 -0
- {python3_cyberfusion_file_support-1.1.1.2 → python3_cyberfusion_file_support-1.2}/setup.cfg +0 -0
- {python3_cyberfusion_file_support-1.1.1.2 → 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.2 → python3_cyberfusion_file_support-1.2}/src/python3_cyberfusion_file_support.egg-info/requires.txt +0 -0
- {python3_cyberfusion_file_support-1.1.1.2 → python3_cyberfusion_file_support-1.2}/src/python3_cyberfusion_file_support.egg-info/top_level.txt +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
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
5
|
Author-email: Cyberfusion <support@cyberfusion.io>
|
|
6
6
|
Project-URL: 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
|
|
@@ -84,22 +111,32 @@ class DestinationFileReplacement:
|
|
|
84
111
|
|
|
85
112
|
return default_comment + self._contents
|
|
86
113
|
|
|
87
|
-
def
|
|
88
|
-
"""Write contents to
|
|
89
|
-
|
|
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:
|
|
90
119
|
open_mode = "wb"
|
|
120
|
+
|
|
121
|
+
contents = encrypt_file(
|
|
122
|
+
self.encryption_properties,
|
|
123
|
+
self.contents,
|
|
124
|
+
)
|
|
91
125
|
else:
|
|
92
126
|
open_mode = "w"
|
|
93
127
|
|
|
94
|
-
|
|
95
|
-
|
|
128
|
+
contents = self.contents
|
|
129
|
+
|
|
130
|
+
with open(path, open_mode) as f:
|
|
131
|
+
f.write(contents)
|
|
96
132
|
|
|
97
133
|
@property
|
|
98
134
|
def changed(self) -> bool:
|
|
99
135
|
"""Get if destination file will change."""
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
136
|
+
if not self.destination_file.exists:
|
|
137
|
+
return True
|
|
138
|
+
|
|
139
|
+
return self.destination_file.contents != self.contents
|
|
103
140
|
|
|
104
141
|
@property
|
|
105
142
|
def differences(self) -> List[str]:
|
|
@@ -107,9 +144,6 @@ class DestinationFileReplacement:
|
|
|
107
144
|
|
|
108
145
|
No differences are returned when contents is not string.
|
|
109
146
|
"""
|
|
110
|
-
if not isinstance(self._contents, str):
|
|
111
|
-
return []
|
|
112
|
-
|
|
113
147
|
results = []
|
|
114
148
|
|
|
115
149
|
for line in difflib.unified_diff(
|
|
@@ -118,7 +152,7 @@ class DestinationFileReplacement:
|
|
|
118
152
|
if self.destination_file.contents
|
|
119
153
|
else []
|
|
120
154
|
),
|
|
121
|
-
self.contents.splitlines(),
|
|
155
|
+
self.contents.splitlines(),
|
|
122
156
|
fromfile=self.tmp_path,
|
|
123
157
|
tofile=self.destination_file.path,
|
|
124
158
|
lineterm="",
|
|
@@ -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,6 +1,6 @@
|
|
|
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
5
|
Author-email: Cyberfusion <support@cyberfusion.io>
|
|
6
6
|
Project-URL: Source, https://github.com/CyberfusionIO/python3-cyberfusion-file-support
|
|
@@ -2,6 +2,8 @@ README.md
|
|
|
2
2
|
pyproject.toml
|
|
3
3
|
setup.cfg
|
|
4
4
|
src/cyberfusion/FileSupport/__init__.py
|
|
5
|
+
src/cyberfusion/FileSupport/encryption.py
|
|
6
|
+
src/cyberfusion/FileSupport/exceptions.py
|
|
5
7
|
src/python3_cyberfusion_file_support.egg-info/PKG-INFO
|
|
6
8
|
src/python3_cyberfusion_file_support.egg-info/SOURCES.txt
|
|
7
9
|
src/python3_cyberfusion_file_support.egg-info/dependency_links.txt
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|