znote 1.0.0__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.
- znote/__init__.py +1 -0
- znote/app/display.py +93 -0
- znote/app/input.py +46 -0
- znote/client.py +568 -0
- znote/core/auth.py +71 -0
- znote/core/client/connect.py +52 -0
- znote/core/client/settings.py +142 -0
- znote/core/client/token.py +17 -0
- znote/core/config.py +73 -0
- znote/core/editor.py +132 -0
- znote/core/gen.py +21 -0
- znote/core/pack.py +72 -0
- znote/core/server/note.py +20 -0
- znote/core/server/settings.py +148 -0
- znote/core/workspace.py +67 -0
- znote/main.py +2 -0
- znote/model/models.py +25 -0
- znote/server.py +734 -0
- znote-1.0.0.dist-info/METADATA +143 -0
- znote-1.0.0.dist-info/RECORD +24 -0
- znote-1.0.0.dist-info/WHEEL +5 -0
- znote-1.0.0.dist-info/entry_points.txt +3 -0
- znote-1.0.0.dist-info/licenses/LICENSE +21 -0
- znote-1.0.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
import __main__
|
|
2
|
+
import impmagic
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
class Settings():
|
|
6
|
+
@impmagic.loader(
|
|
7
|
+
{'module': '__main__'},
|
|
8
|
+
{'module': 'os'},
|
|
9
|
+
{'module': 'pyotp'},
|
|
10
|
+
{'module': 'app.display','submodule': ['logs']},
|
|
11
|
+
{'module': 'Crypto','submodule': ['Random']},
|
|
12
|
+
{'module': 'Crypto.PublicKey','submodule': ['RSA']},
|
|
13
|
+
{'module': 'core.config','submodule': ['Config']},
|
|
14
|
+
{'module': 'core.client.token','submodule': ['get_token']}
|
|
15
|
+
)
|
|
16
|
+
def __init__(self, config_file):
|
|
17
|
+
self.config_dir = os.path.abspath(os.path.expanduser('~/.config/znote'))
|
|
18
|
+
|
|
19
|
+
self.token_file = os.path.join(self.config_dir, ".token")
|
|
20
|
+
|
|
21
|
+
if not config_file:
|
|
22
|
+
config_file = os.path.join(self.config_dir, "config.yml")
|
|
23
|
+
|
|
24
|
+
self.config = Config(f"{config_file}")
|
|
25
|
+
|
|
26
|
+
self.verbose = self.config.get('client.verbose', default=False, auto_set=True)
|
|
27
|
+
|
|
28
|
+
self.host = self.config.get('client.host', default="127.0.0.1", auto_set=True)
|
|
29
|
+
self.port = self.config.get('client.port', default=40017, auto_set=True)
|
|
30
|
+
self.key_size = self.config.get('client.key_size', default=2048, auto_set=True)
|
|
31
|
+
self.key_otp = self.config.get('client.key_otp', default='JBSWY3DPEHPK3PXP', auto_set=True)
|
|
32
|
+
self.totp = pyotp.TOTP(self.key_otp)
|
|
33
|
+
|
|
34
|
+
self.PubServ = None
|
|
35
|
+
self.keyC = None
|
|
36
|
+
|
|
37
|
+
self.token = get_token(self.token_file)
|
|
38
|
+
self.pubserv_set = None
|
|
39
|
+
self.authenticated = False
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
@impmagic.loader(
|
|
43
|
+
{'module': '__main__'},
|
|
44
|
+
{'module': 'os'},
|
|
45
|
+
{'module': 'hashlib'},
|
|
46
|
+
{'module': 'Crypto.PublicKey', 'submodule': ['RSA']},
|
|
47
|
+
{'module': 'Crypto', 'submodule': ['Random']},
|
|
48
|
+
{'module': 'app.display', 'submodule': ['logs']},
|
|
49
|
+
)
|
|
50
|
+
def load_key(self):
|
|
51
|
+
privkey_path = os.path.join(self.config_dir, "client.key")
|
|
52
|
+
pubkey_path = os.path.join(self.config_dir, "client.pem")
|
|
53
|
+
|
|
54
|
+
if not os.path.exists(privkey_path) or not os.path.exists(pubkey_path):
|
|
55
|
+
try:
|
|
56
|
+
#Génération de la paire de clé pour les échanges avec le client
|
|
57
|
+
logs("Création de la paire de clé", "info")
|
|
58
|
+
rng = Random.new().read
|
|
59
|
+
rsa_key = RSA.generate(self.key_size, rng)
|
|
60
|
+
rsa_priv = rsa_key.exportKey("PEM")
|
|
61
|
+
|
|
62
|
+
self.PrivKey = rsa_key.exportKey('PEM', pkcs=1)
|
|
63
|
+
|
|
64
|
+
with open(privkey_path, "wb") as f:
|
|
65
|
+
f.write(self.PrivKey)
|
|
66
|
+
|
|
67
|
+
key_pub = rsa_key.publickey()
|
|
68
|
+
self.PubKey = key_pub.exportKey("PEM")
|
|
69
|
+
|
|
70
|
+
with open(pubkey_path, "wb") as f:
|
|
71
|
+
f.write(self.PubKey)
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
logs("Paire de clé créée", "success")
|
|
75
|
+
|
|
76
|
+
except Exception as err:
|
|
77
|
+
logs(f"Erreur lors de la création de la paire de clé: {err}", "error")
|
|
78
|
+
|
|
79
|
+
else:
|
|
80
|
+
try:
|
|
81
|
+
logs("Chargement de la paire de clé", "info")
|
|
82
|
+
|
|
83
|
+
with open(privkey_path, "rb") as f:
|
|
84
|
+
self.PrivKey = f.read()
|
|
85
|
+
|
|
86
|
+
with open(pubkey_path, "rb") as f:
|
|
87
|
+
self.PubKey = f.read()
|
|
88
|
+
|
|
89
|
+
logs("Paire de clé chargée", "success")
|
|
90
|
+
|
|
91
|
+
except Exception as err:
|
|
92
|
+
logs(f"Erreur lors du chargement de la paire de clé: {err}", "error")
|
|
93
|
+
|
|
94
|
+
self.key = RSA.importKey(self.PrivKey)
|
|
95
|
+
|
|
96
|
+
#Calcul du hash de la clé du client
|
|
97
|
+
self.clienthash = hashlib.sha256(self.PubKey).hexdigest()
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
#Chargement de la clé serveur stocké
|
|
101
|
+
serverpub_path = os.path.join(self.config_dir, "server.pub")
|
|
102
|
+
if os.path.exists(serverpub_path):
|
|
103
|
+
try:
|
|
104
|
+
logs("Chargement de la clé du serveur", "info")
|
|
105
|
+
|
|
106
|
+
with open(serverpub_path, "rb") as f:
|
|
107
|
+
content = f.read()
|
|
108
|
+
|
|
109
|
+
self.pubserv_set = True
|
|
110
|
+
self.keyC = RSA.importKey(content)
|
|
111
|
+
#Calcul du hash de la clé du serveur
|
|
112
|
+
self.serverhash = hashlib.sha256(content).hexdigest()
|
|
113
|
+
|
|
114
|
+
except Exception as err:
|
|
115
|
+
logs(f"Erreur lors du chargement de la clé du serveur: {err}", "error")
|
|
116
|
+
|
|
117
|
+
|
|
118
|
+
@impmagic.loader(
|
|
119
|
+
{'module': 'zpp_color', 'submodule': ['fg', 'attr']},
|
|
120
|
+
)
|
|
121
|
+
def init_settings(self):
|
|
122
|
+
host = input(f"{fg('dark_gray')}host [{fg('cyan')}{self.host}{fg('dark_gray')}]: ")
|
|
123
|
+
port = input(f"{fg('dark_gray')}port [{fg('cyan')}{self.port}{fg('dark_gray')}]: ")
|
|
124
|
+
key_size = input(f"{fg('dark_gray')}key_size [{fg('cyan')}{self.key_size}{fg('dark_gray')}]: ")
|
|
125
|
+
key_otp = input(f"{fg('dark_gray')}key_otp [{fg('cyan')}{self.key_otp}{fg('dark_gray')}]: ")
|
|
126
|
+
verbose = input(f"{fg('dark_gray')}verbose [{fg('cyan')}{self.verbose}{fg('dark_gray')}]: ")
|
|
127
|
+
|
|
128
|
+
if host:
|
|
129
|
+
self.config.set('server.host', host)
|
|
130
|
+
|
|
131
|
+
if port and isinstance(port, int):
|
|
132
|
+
self.config.set('server.port', int(port))
|
|
133
|
+
|
|
134
|
+
if key_size and isinstance(key_size, int):
|
|
135
|
+
self.config.set('server.key_size', int(key_size))
|
|
136
|
+
|
|
137
|
+
if key_otp:
|
|
138
|
+
self.config.set('server.key_otp', key_otp)
|
|
139
|
+
|
|
140
|
+
if verbose:
|
|
141
|
+
self.config.set('server.verbose', verbose)
|
|
142
|
+
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import __main__
|
|
2
|
+
import impmagic
|
|
3
|
+
|
|
4
|
+
def set_token(token):
|
|
5
|
+
with open(__main__.settings.token_file, 'w') as f:
|
|
6
|
+
f.write(token)
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
@impmagic.loader(
|
|
10
|
+
{'module': 'os'},
|
|
11
|
+
)
|
|
12
|
+
def get_token(token_file):
|
|
13
|
+
if os.path.exists(token_file):
|
|
14
|
+
with open(token_file, 'r') as f:
|
|
15
|
+
return f.read()
|
|
16
|
+
else:
|
|
17
|
+
return False
|
znote/core/config.py
ADDED
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import os
|
|
2
|
+
import yaml
|
|
3
|
+
|
|
4
|
+
class Config:
|
|
5
|
+
def __init__(self, path):
|
|
6
|
+
self.path = os.path.expanduser(path)
|
|
7
|
+
self.data = {}
|
|
8
|
+
self._load()
|
|
9
|
+
|
|
10
|
+
def _load(self):
|
|
11
|
+
if os.path.exists(self.path):
|
|
12
|
+
with open(self.path, "r", encoding="utf-8") as f:
|
|
13
|
+
self.data = yaml.safe_load(f) or {}
|
|
14
|
+
else:
|
|
15
|
+
self.data = {}
|
|
16
|
+
|
|
17
|
+
def _save(self):
|
|
18
|
+
directory = os.path.dirname(self.path)
|
|
19
|
+
if directory:
|
|
20
|
+
os.makedirs(directory, exist_ok=True)
|
|
21
|
+
|
|
22
|
+
with open(self.path, "w", encoding="utf-8") as f:
|
|
23
|
+
yaml.safe_dump(self.data, f, default_flow_style=False, sort_keys=False)
|
|
24
|
+
|
|
25
|
+
def _get_recursive(self, data, keys):
|
|
26
|
+
if len(keys) == 1:
|
|
27
|
+
return data.get(keys[0])
|
|
28
|
+
if keys[0] in data:
|
|
29
|
+
return self._get_recursive(data[keys[0]], keys[1:])
|
|
30
|
+
return None
|
|
31
|
+
|
|
32
|
+
def _set_recursive(self, data, keys, value):
|
|
33
|
+
if len(keys) == 1:
|
|
34
|
+
data[keys[0]] = value
|
|
35
|
+
return data
|
|
36
|
+
if keys[0] not in data or not isinstance(data[keys[0]], dict):
|
|
37
|
+
data[keys[0]] = {}
|
|
38
|
+
data[keys[0]] = self._set_recursive(data[keys[0]], keys[1:], value)
|
|
39
|
+
return data
|
|
40
|
+
|
|
41
|
+
def get(self, dotted_key, default=None, auto_set=False):
|
|
42
|
+
keys = dotted_key.split(".")
|
|
43
|
+
result = self._get_recursive(self.data, keys)
|
|
44
|
+
|
|
45
|
+
if result is not None:
|
|
46
|
+
return result
|
|
47
|
+
|
|
48
|
+
if auto_set:
|
|
49
|
+
self.set(dotted_key, default)
|
|
50
|
+
return default
|
|
51
|
+
|
|
52
|
+
return default
|
|
53
|
+
|
|
54
|
+
def set(self, dotted_key, value):
|
|
55
|
+
keys = dotted_key.split(".")
|
|
56
|
+
self.data = self._set_recursive(self.data, keys, value)
|
|
57
|
+
self._save()
|
|
58
|
+
|
|
59
|
+
def delete(self, dotted_key):
|
|
60
|
+
keys = dotted_key.split(".")
|
|
61
|
+
parent = self._get_recursive(self.data, keys[:-1])
|
|
62
|
+
if parent and keys[-1] in parent:
|
|
63
|
+
del parent[keys[-1]]
|
|
64
|
+
# Nettoie récursivement si parent vide :
|
|
65
|
+
while keys[:-1]:
|
|
66
|
+
keys = keys[:-1]
|
|
67
|
+
parent = self._get_recursive(self.data, keys[:-1])
|
|
68
|
+
if parent and not parent.get(keys[-1]):
|
|
69
|
+
del parent[keys[-1]]
|
|
70
|
+
self._save()
|
|
71
|
+
|
|
72
|
+
def has(self, dotted_key):
|
|
73
|
+
return self.get(dotted_key) is not None
|
znote/core/editor.py
ADDED
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
|
|
3
|
+
import sys
|
|
4
|
+
import asyncio
|
|
5
|
+
import os # Import os for checking file existence
|
|
6
|
+
|
|
7
|
+
from prompt_toolkit import Application
|
|
8
|
+
from prompt_toolkit.layout import Layout, HSplit, Window
|
|
9
|
+
from prompt_toolkit.widgets import TextArea, Label
|
|
10
|
+
from prompt_toolkit.buffer import Buffer
|
|
11
|
+
from prompt_toolkit.key_binding import KeyBindings
|
|
12
|
+
from prompt_toolkit.filters import Condition
|
|
13
|
+
|
|
14
|
+
from prompt_toolkit.layout.containers import ConditionalContainer
|
|
15
|
+
from prompt_toolkit.shortcuts import message_dialog
|
|
16
|
+
from prompt_toolkit.layout.controls import BufferControl
|
|
17
|
+
|
|
18
|
+
from zpp_ManagedFile import ManagedFile
|
|
19
|
+
|
|
20
|
+
class Editor:
|
|
21
|
+
def __init__(self, filename, file):
|
|
22
|
+
# --- État de l'application ---
|
|
23
|
+
self.file = file # Default filename
|
|
24
|
+
self.filename = filename
|
|
25
|
+
|
|
26
|
+
# --- UI Elements ---
|
|
27
|
+
self.text_area = TextArea(
|
|
28
|
+
multiline=True,
|
|
29
|
+
wrap_lines=True,
|
|
30
|
+
scrollbar=True,
|
|
31
|
+
line_numbers=True,
|
|
32
|
+
)
|
|
33
|
+
# Initial status bar text will be set after loading
|
|
34
|
+
self.status_bar = Label(text="") # Start empty, will be set by load_file
|
|
35
|
+
# --- Layout ---
|
|
36
|
+
self.layout = Layout(
|
|
37
|
+
HSplit([
|
|
38
|
+
self.text_area,
|
|
39
|
+
self.status_bar,
|
|
40
|
+
]),
|
|
41
|
+
)
|
|
42
|
+
|
|
43
|
+
# --- Key Bindings ---
|
|
44
|
+
self.kb = KeyBindings()
|
|
45
|
+
|
|
46
|
+
@self.kb.add("c-q")
|
|
47
|
+
def _(event):
|
|
48
|
+
""" Global shortcut to quit """
|
|
49
|
+
asyncio.get_event_loop().create_task(self.quit())
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
@self.kb.add("c-s") # Active only in editor mode
|
|
53
|
+
def _(event):
|
|
54
|
+
""" Ctrl+S shortcut to save """
|
|
55
|
+
# Schedule save_file as it's an async method
|
|
56
|
+
asyncio.get_event_loop().create_task(self.save_file())
|
|
57
|
+
|
|
58
|
+
# --- Application ---
|
|
59
|
+
self.app = Application(
|
|
60
|
+
layout=self.layout,
|
|
61
|
+
key_bindings=self.kb,
|
|
62
|
+
full_screen=True,
|
|
63
|
+
# style...
|
|
64
|
+
)
|
|
65
|
+
|
|
66
|
+
# --- Initial Actions ---
|
|
67
|
+
self.load_file(self.file) # Load file content on startup
|
|
68
|
+
self.app.layout.focus(self.text_area) # Set initial focus
|
|
69
|
+
|
|
70
|
+
# --- File Loading ---
|
|
71
|
+
def load_file(self, filename):
|
|
72
|
+
""" Loads content from a file into the text area on startup """
|
|
73
|
+
try:
|
|
74
|
+
if isinstance(self.file,ManagedFile) and hasattr(self.file,'read'):
|
|
75
|
+
self.file.seek(0)
|
|
76
|
+
self.text_area.text = self.file.read()
|
|
77
|
+
self.status_bar.text = f"Fichier chargé."
|
|
78
|
+
else:
|
|
79
|
+
if os.path.exists(filename):
|
|
80
|
+
with open(filename, "r", encoding='utf-8') as f:
|
|
81
|
+
content = f.read()
|
|
82
|
+
self.text_area.text = content
|
|
83
|
+
self.status_bar.text = f"Fichier '{filename}' chargé."
|
|
84
|
+
else:
|
|
85
|
+
self.status_bar.text = f"Nouveau fichier '{filename}'."
|
|
86
|
+
except Exception as e:
|
|
87
|
+
# Basic error handling for loading
|
|
88
|
+
self.status_bar.text = f"Erreur de chargement : {e}"
|
|
89
|
+
# Status messages set here are static until the first save/reset
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
# --- File Saving ---
|
|
93
|
+
# save_file must be async because it's awaited in save_and_quit
|
|
94
|
+
async def save_file(self):
|
|
95
|
+
""" Saves content to the default file """
|
|
96
|
+
filename = self.file # Use attribute
|
|
97
|
+
try:
|
|
98
|
+
if isinstance(self.file,ManagedFile) and hasattr(self.file,'read'):
|
|
99
|
+
self.file.seek(0)
|
|
100
|
+
self.file.truncate(0)
|
|
101
|
+
self.file.write(self.text_area.text)
|
|
102
|
+
else:
|
|
103
|
+
# Synchronous file writing
|
|
104
|
+
with open(self.file, "w", encoding='utf-8') as f:
|
|
105
|
+
f.write(self.text_area.text)
|
|
106
|
+
|
|
107
|
+
# Update status bar on success and schedule reset
|
|
108
|
+
self.status_bar.text = f"Fichier '{filename}' sauvegardé."
|
|
109
|
+
return True
|
|
110
|
+
except Exception as e:
|
|
111
|
+
# Update status bar on error and schedule reset
|
|
112
|
+
self.status_bar.text = f"Erreur de sauvegarde : {e}"
|
|
113
|
+
return False
|
|
114
|
+
|
|
115
|
+
# --- Quit ---
|
|
116
|
+
# async because it awaits the message_dialog
|
|
117
|
+
async def quit(self, event=None):
|
|
118
|
+
""" Asks for confirmation and quits the application """
|
|
119
|
+
# Use the message_dialog, which is awaited
|
|
120
|
+
self.app.exit()
|
|
121
|
+
|
|
122
|
+
# Entry point
|
|
123
|
+
if __name__ == '__main__':
|
|
124
|
+
try:
|
|
125
|
+
file = ManagedFile(mode='a', typefile='stringio')
|
|
126
|
+
editor = Editor(file)
|
|
127
|
+
editor.app.run()
|
|
128
|
+
file.seek(0)
|
|
129
|
+
print(file.read())
|
|
130
|
+
except Exception as e:
|
|
131
|
+
print(f"An unexpected error occurred: {e}")
|
|
132
|
+
sys.exit(1)
|
znote/core/gen.py
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import impmagic
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
@impmagic.loader(
|
|
5
|
+
{'module': 'string'},
|
|
6
|
+
{'module': 'secrets'}
|
|
7
|
+
)
|
|
8
|
+
def gen_password(size):
|
|
9
|
+
alphabet = string.ascii_letters + string.digits + string.punctuation
|
|
10
|
+
pwd = ''
|
|
11
|
+
for i in range(size):
|
|
12
|
+
pwd += ''.join(secrets.choice(alphabet))
|
|
13
|
+
|
|
14
|
+
return pwd.encode()
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
@impmagic.loader(
|
|
18
|
+
{'module': 'uuid','submodule': ['uuid4']}
|
|
19
|
+
)
|
|
20
|
+
def UUID():
|
|
21
|
+
return str(uuid4()).replace("-","")
|
znote/core/pack.py
ADDED
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import impmagic
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
#Création de la signature d'une donnée
|
|
5
|
+
@impmagic.loader(
|
|
6
|
+
{'module': 'Crypto.Hash', 'submodule': ['SHA256']},
|
|
7
|
+
{'module': 'Crypto.Signature','submodule': ['PKCS1_v1_5']}
|
|
8
|
+
)
|
|
9
|
+
def sign(data, privKey):
|
|
10
|
+
if not isinstance(data, bytes):
|
|
11
|
+
data = data.encode()
|
|
12
|
+
|
|
13
|
+
digest = SHA256.new(data)
|
|
14
|
+
|
|
15
|
+
signature = PKCS1_v1_5.new(privKey).sign(digest)
|
|
16
|
+
|
|
17
|
+
return signature
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
#Vérification de la signature d'une donnée
|
|
21
|
+
@impmagic.loader(
|
|
22
|
+
{'module': 'Crypto.Hash', 'submodule': ['SHA256']},
|
|
23
|
+
{'module': 'Crypto.Signature','submodule': ['PKCS1_v1_5']}
|
|
24
|
+
)
|
|
25
|
+
def verify(data, signature, pubKey):
|
|
26
|
+
if not isinstance(data, bytes):
|
|
27
|
+
data = data.encode()
|
|
28
|
+
|
|
29
|
+
digest = SHA256.new(data)
|
|
30
|
+
|
|
31
|
+
verifier = PKCS1_v1_5.new(pubKey)
|
|
32
|
+
return verifier.verify(digest, signature)
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
#Création du package à envoyer
|
|
36
|
+
@impmagic.loader(
|
|
37
|
+
{'module': 'dill', 'submodule': ['dumps']}
|
|
38
|
+
)
|
|
39
|
+
def pack(data, privKey):
|
|
40
|
+
#Transformation d'un objet en binaire
|
|
41
|
+
data = dumps(data)
|
|
42
|
+
|
|
43
|
+
#Création de la signature de ce binaire
|
|
44
|
+
signature = sign(data, privKey)
|
|
45
|
+
|
|
46
|
+
return signature+data
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
#Transformation du package en objet
|
|
50
|
+
@impmagic.loader(
|
|
51
|
+
{'module': 'dill', 'submodule': ['loads']}
|
|
52
|
+
)
|
|
53
|
+
def unpack(data, pubKey):
|
|
54
|
+
if len(data)>256:
|
|
55
|
+
#Récupération de la signature dans le bloc de données
|
|
56
|
+
signature, data = data[0:256], data[256:]
|
|
57
|
+
|
|
58
|
+
#Vérifie si la signature est valide et provient du serveur
|
|
59
|
+
if verify(data, signature, pubKey):
|
|
60
|
+
try:
|
|
61
|
+
#Si la signature est valide, conversion du binaire en objet
|
|
62
|
+
return loads(data)
|
|
63
|
+
except:
|
|
64
|
+
print("Pickle invalid")
|
|
65
|
+
return
|
|
66
|
+
else:
|
|
67
|
+
print("Signature check: Bad return")
|
|
68
|
+
return
|
|
69
|
+
else:
|
|
70
|
+
print("Pickle invalid")
|
|
71
|
+
return
|
|
72
|
+
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import __main__
|
|
2
|
+
import impmagic
|
|
3
|
+
|
|
4
|
+
@impmagic.loader(
|
|
5
|
+
{'module': 'pony.orm', 'submodule': ['db_session', 'select']}
|
|
6
|
+
)
|
|
7
|
+
def get_note(user_id, workspace=None):
|
|
8
|
+
notes_list = []
|
|
9
|
+
|
|
10
|
+
with db_session:
|
|
11
|
+
query = __main__.settings.orm.Note.select().where("n.creator_id == user_id", {"user_id": user_id})
|
|
12
|
+
|
|
13
|
+
if workspace:
|
|
14
|
+
query = query.where("n.workspace == workspace", {"workspace": workspace})
|
|
15
|
+
|
|
16
|
+
result = query[:]
|
|
17
|
+
|
|
18
|
+
return result
|
|
19
|
+
|
|
20
|
+
return notes_list
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
import impmagic
|
|
2
|
+
|
|
3
|
+
class Settings:
|
|
4
|
+
@impmagic.loader(
|
|
5
|
+
{'module': '__main__'},
|
|
6
|
+
{'module': 're'},
|
|
7
|
+
{'module': 'os'},
|
|
8
|
+
{'module': 'pyotp'},
|
|
9
|
+
{'module': 'app.display', 'submodule': ['logs']},
|
|
10
|
+
{'module': 'core.config', 'submodule': ['Config']},
|
|
11
|
+
)
|
|
12
|
+
def __init__(self, config_file=None):
|
|
13
|
+
self.config_dir = os.path.abspath(os.path.expanduser('~/.config/znote'))
|
|
14
|
+
|
|
15
|
+
if not os.path.exists(self.config_dir):
|
|
16
|
+
os.makedirs(self.config_dir, exist_ok=True)
|
|
17
|
+
|
|
18
|
+
if not config_file:
|
|
19
|
+
config_file = os.path.join(self.config_dir, "config.yml")
|
|
20
|
+
#config_file = "config.toml"
|
|
21
|
+
|
|
22
|
+
self.config = Config(config_file)
|
|
23
|
+
|
|
24
|
+
self.verbose = self.config.get('server.verbose', default=True, auto_set=True)
|
|
25
|
+
|
|
26
|
+
self.host = self.config.get('server.host', default="127.0.0.1", auto_set=True)
|
|
27
|
+
self.port = self.config.get('server.port', default=40017, auto_set=True)
|
|
28
|
+
self.key_size = self.config.get('server.key_size', default=2048, auto_set=True)
|
|
29
|
+
self.key_otp = self.config.get('server.key_otp', default='JBSWY3DPEHPK3PXP', auto_set=True)
|
|
30
|
+
|
|
31
|
+
#Initialisation de l'OTP pour la connexion initiale
|
|
32
|
+
if not re.match(r'^[A-Z0-9]{16}$', self.key_otp):
|
|
33
|
+
print("Clé OTP invalide. Passage sur la clé par défaut", "warning")
|
|
34
|
+
self.key_otp = 'JBSWY3DPEHPK3PXP'
|
|
35
|
+
self.totp = pyotp.TOTP(self.key_otp)
|
|
36
|
+
|
|
37
|
+
self.data_dir = self.config.get('server.data_dir', default=None, auto_set=False)
|
|
38
|
+
|
|
39
|
+
if not self.data_dir:
|
|
40
|
+
self.data_dir = os.path.join(self.config_dir, "znote.db")
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
@impmagic.loader(
|
|
44
|
+
{'module': '__main__'},
|
|
45
|
+
{'module': 'os'},
|
|
46
|
+
{'module': 'hashlib'},
|
|
47
|
+
{'module': 'Crypto.PublicKey', 'submodule': ['RSA']},
|
|
48
|
+
{'module': 'Crypto', 'submodule': ['Random']},
|
|
49
|
+
{'module': 'app.display', 'submodule': ['logs']},
|
|
50
|
+
)
|
|
51
|
+
def load_key(self):
|
|
52
|
+
privkey_path = os.path.join(self.config_dir, "server.key")
|
|
53
|
+
pubkey_path = os.path.join(self.config_dir, "server.pem")
|
|
54
|
+
|
|
55
|
+
if not os.path.exists(privkey_path) or not os.path.exists(pubkey_path):
|
|
56
|
+
try:
|
|
57
|
+
#Génération de la paire de clé pour les échanges avec le client
|
|
58
|
+
logs("Création de la paire de clé", "info")
|
|
59
|
+
rng = Random.new().read
|
|
60
|
+
rsa_key = RSA.generate(self.key_size, rng)
|
|
61
|
+
rsa_priv = rsa_key.exportKey("PEM")
|
|
62
|
+
|
|
63
|
+
self.PrivKey = rsa_key.exportKey('PEM', pkcs=1)
|
|
64
|
+
|
|
65
|
+
with open(privkey_path, "wb") as f:
|
|
66
|
+
f.write(self.PrivKey)
|
|
67
|
+
|
|
68
|
+
key_pub = rsa_key.publickey()
|
|
69
|
+
self.PubKey = key_pub.exportKey("PEM")
|
|
70
|
+
|
|
71
|
+
with open(pubkey_path, "wb") as f:
|
|
72
|
+
f.write(self.PubKey)
|
|
73
|
+
|
|
74
|
+
logs("Paire de clé créée", "success")
|
|
75
|
+
|
|
76
|
+
except Exception as err:
|
|
77
|
+
logs(f"Erreur lors de la création de la paire de clé: {err}", "error")
|
|
78
|
+
exit()
|
|
79
|
+
|
|
80
|
+
else:
|
|
81
|
+
try:
|
|
82
|
+
logs("Chargement de la paire de clé", "info")
|
|
83
|
+
|
|
84
|
+
with open(privkey_path, "rb") as f:
|
|
85
|
+
self.PrivKey = f.read()
|
|
86
|
+
|
|
87
|
+
with open(pubkey_path, "rb") as f:
|
|
88
|
+
self.PubKey = f.read()
|
|
89
|
+
|
|
90
|
+
logs("Paire de clé chargée", "success")
|
|
91
|
+
|
|
92
|
+
except Exception as err:
|
|
93
|
+
logs(f"Erreur lors du chargement de la paire de clé: {err}", "error")
|
|
94
|
+
exit()
|
|
95
|
+
|
|
96
|
+
#Calcul du hash de la clé du client
|
|
97
|
+
self.serverhash = hashlib.sha256(self.PubKey).hexdigest()
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
@impmagic.loader(
|
|
101
|
+
{'module': '__main__'},
|
|
102
|
+
{'module': 'os'},
|
|
103
|
+
{'module': 'model', 'submodule': ['models']},
|
|
104
|
+
{'module': 'pony.orm','submodule': ['Database']},
|
|
105
|
+
{'module': 'app.display', 'submodule': ['logs']},
|
|
106
|
+
)
|
|
107
|
+
def load_db(self):
|
|
108
|
+
try:
|
|
109
|
+
self.orm = Database()
|
|
110
|
+
models.Table(self.orm)
|
|
111
|
+
self.orm.bind(provider='sqlite', filename=self.data_dir, create_db=True)
|
|
112
|
+
self.orm.generate_mapping(create_tables=True)
|
|
113
|
+
|
|
114
|
+
logs("Base de données initialisée", "success")
|
|
115
|
+
|
|
116
|
+
except Exception as err:
|
|
117
|
+
logs(f"Erreur lors de l'initialisation de la base de données: {err}", "error")
|
|
118
|
+
exit()
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
@impmagic.loader(
|
|
122
|
+
{'module': 'zpp_color', 'submodule': ['fg', 'attr']},
|
|
123
|
+
)
|
|
124
|
+
def init_settings(self):
|
|
125
|
+
host = input(f"{fg('dark_gray')}host [{fg('cyan')}{self.host}{fg('dark_gray')}]: ")
|
|
126
|
+
port = input(f"{fg('dark_gray')}port [{fg('cyan')}{self.port}{fg('dark_gray')}]: ")
|
|
127
|
+
key_size = input(f"{fg('dark_gray')}key_size [{fg('cyan')}{self.key_size}{fg('dark_gray')}]: ")
|
|
128
|
+
key_otp = input(f"{fg('dark_gray')}key_otp [{fg('cyan')}{self.key_otp}{fg('dark_gray')}]: ")
|
|
129
|
+
data_dir = input(f"{fg('dark_gray')}data_dir [{fg('cyan')}{self.data_dir}{fg('dark_gray')}]: ")
|
|
130
|
+
verbose = input(f"{fg('dark_gray')}verbose [{fg('cyan')}{self.verbose}{fg('dark_gray')}]: ")
|
|
131
|
+
|
|
132
|
+
if host:
|
|
133
|
+
self.config.set('server.host', host)
|
|
134
|
+
|
|
135
|
+
if port and isinstance(port, int):
|
|
136
|
+
self.config.set('server.port', int(port))
|
|
137
|
+
|
|
138
|
+
if key_size and isinstance(key_size, int):
|
|
139
|
+
self.config.set('server.key_size', int(key_size))
|
|
140
|
+
|
|
141
|
+
if key_otp:
|
|
142
|
+
self.config.set('server.key_otp', key_otp)
|
|
143
|
+
|
|
144
|
+
if data_dir:
|
|
145
|
+
self.config.set('server.data_dir', data_dir)
|
|
146
|
+
|
|
147
|
+
if verbose:
|
|
148
|
+
self.config.set('server.verbose', verbose)
|