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
znote/client.py
ADDED
|
@@ -0,0 +1,568 @@
|
|
|
1
|
+
import __main__
|
|
2
|
+
import impmagic
|
|
3
|
+
import threading
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
@impmagic.loader(
|
|
7
|
+
{'module': 'chardet'},
|
|
8
|
+
)
|
|
9
|
+
def decode(message):
|
|
10
|
+
if chardet.detect(message)['encoding']!=None:
|
|
11
|
+
return message.decode(chardet.detect(message)['encoding'])
|
|
12
|
+
else:
|
|
13
|
+
return message.decode()
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class Client():
|
|
17
|
+
def __init__(self, conn):
|
|
18
|
+
threading.Thread.__init__(self)
|
|
19
|
+
self.connexion = conn
|
|
20
|
+
self.killed = False
|
|
21
|
+
self.well_come = False
|
|
22
|
+
|
|
23
|
+
@impmagic.loader(
|
|
24
|
+
{'module': 'Crypto.Cipher', 'submodule': ['PKCS1_OAEP']},
|
|
25
|
+
{'module': 'cryptography.hazmat.backends', 'submodule': ['default_backend']},
|
|
26
|
+
{'module': 'cryptography.hazmat.primitives.ciphers', 'submodule': ['Cipher', 'algorithms', 'modes']}
|
|
27
|
+
)
|
|
28
|
+
def decrypt_data(self, message):
|
|
29
|
+
iv = message[:16]
|
|
30
|
+
sym_key_encrypted = message[16:272] # Taille de la clé RSA (2048 bits) / 8
|
|
31
|
+
ciphertext = message[272:]
|
|
32
|
+
|
|
33
|
+
# Déchiffrement de la clé symétrique avec RSA
|
|
34
|
+
decryptor = PKCS1_OAEP.new(__main__.settings.key)
|
|
35
|
+
sym_key = decryptor.decrypt(sym_key_encrypted)
|
|
36
|
+
|
|
37
|
+
# Déchiffrement du fichier avec AES en mode CFB
|
|
38
|
+
cipher = Cipher(algorithms.AES(sym_key), modes.CFB(iv), backend=default_backend())
|
|
39
|
+
decryptor = cipher.decryptor()
|
|
40
|
+
plaintext = decryptor.update(ciphertext) + decryptor.finalize()
|
|
41
|
+
|
|
42
|
+
return plaintext
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
#Réception des données
|
|
46
|
+
@impmagic.loader(
|
|
47
|
+
{'module': 'select'}
|
|
48
|
+
)
|
|
49
|
+
def receive(self):
|
|
50
|
+
data = b""
|
|
51
|
+
while True:
|
|
52
|
+
#Pour éviter les exceptions avec le socket non bloquant
|
|
53
|
+
#ready_to_read, _, _ = select.select([self.connexion], [], [], 1)
|
|
54
|
+
#if ready_to_read:
|
|
55
|
+
chunk = self.connexion.recv(2048)
|
|
56
|
+
|
|
57
|
+
if chunk and len(chunk)<2048:
|
|
58
|
+
return data+chunk
|
|
59
|
+
|
|
60
|
+
if not chunk:
|
|
61
|
+
return data
|
|
62
|
+
|
|
63
|
+
data += chunk
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
@impmagic.loader(
|
|
67
|
+
{'module': 'os'},
|
|
68
|
+
{'module': 'core.gen', 'submodule': ['gen_password']},
|
|
69
|
+
{'module': 'Crypto.Cipher', 'submodule': ['PKCS1_OAEP']},
|
|
70
|
+
{'module': 'cryptography.hazmat.backends', 'submodule': ['default_backend']},
|
|
71
|
+
{'module': 'cryptography.hazmat.primitives.ciphers', 'submodule': ['Cipher', 'algorithms', 'modes']}
|
|
72
|
+
)
|
|
73
|
+
def send(self, message):
|
|
74
|
+
if not isinstance(message, bytes):
|
|
75
|
+
message = message.encode()
|
|
76
|
+
|
|
77
|
+
# Génération de la clé symétrique (AES) et de l'IV
|
|
78
|
+
#sym_key = os.urandom(32) # 256 bits
|
|
79
|
+
sym_key = gen_password(32) # 256 bits
|
|
80
|
+
iv = os.urandom(16) # IV de 128 bits pour AES
|
|
81
|
+
|
|
82
|
+
# Chiffrement du fichier avec AES en mode CFB
|
|
83
|
+
plaintext = message
|
|
84
|
+
|
|
85
|
+
cipher = Cipher(algorithms.AES(sym_key), modes.CFB(iv), backend=default_backend())
|
|
86
|
+
encryptor = cipher.encryptor()
|
|
87
|
+
ciphertext = encryptor.update(plaintext) + encryptor.finalize()
|
|
88
|
+
|
|
89
|
+
# Chiffrement de la clé symétrique avec RSA
|
|
90
|
+
encryptor = PKCS1_OAEP.new(__main__.settings.keyC)
|
|
91
|
+
sym_key_encrypted = encryptor.encrypt(sym_key)
|
|
92
|
+
|
|
93
|
+
# Sauvegarde du IV, de la clé symétrique chiffrée et du fichier chiffré
|
|
94
|
+
message_cipher = iv + sym_key_encrypted + ciphertext
|
|
95
|
+
|
|
96
|
+
self.connexion.send(message_cipher)
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
#Premier appel avec le serveur
|
|
100
|
+
@impmagic.loader(
|
|
101
|
+
{'module': 'hashlib'},
|
|
102
|
+
{'module': 'app.display', 'submodule': ['logs', 'notify']},
|
|
103
|
+
{'module': 'core.client.connect', 'submodule': ['encrypt', 'decrypt']},
|
|
104
|
+
{'module': 'Crypto.PublicKey', 'submodule': ['RSA']},
|
|
105
|
+
)
|
|
106
|
+
def welcome(self):
|
|
107
|
+
if __main__.settings.token:
|
|
108
|
+
payload = {'method': 'welcome', 'token': __main__.settings.token, 'serverhash': __main__.settings.serverhash, 'clienthash': __main__.settings.clienthash}
|
|
109
|
+
logs("Envoi du welcome au serveur", component="client")
|
|
110
|
+
|
|
111
|
+
else:
|
|
112
|
+
payload = {'method': 'sendKey', 'content': __main__.settings.PubKey.decode()}
|
|
113
|
+
logs("Envoi de la clé public au serveur", component="client")
|
|
114
|
+
|
|
115
|
+
self.connexion.send(encrypt(json.dumps(payload).encode()))
|
|
116
|
+
|
|
117
|
+
try:
|
|
118
|
+
message_recu = self.receive()
|
|
119
|
+
|
|
120
|
+
except:
|
|
121
|
+
if self.killed:
|
|
122
|
+
logs(f"Arrêt du thread demandé", component="client")
|
|
123
|
+
else:
|
|
124
|
+
logs(f"Erreur avec le client. Arret de la connexion", "error", component="client")
|
|
125
|
+
|
|
126
|
+
if message_recu!=b'':
|
|
127
|
+
msg = decrypt(decode(message_recu))
|
|
128
|
+
msg = json.loads(msg)
|
|
129
|
+
|
|
130
|
+
if msg['method']=="sendKey":
|
|
131
|
+
__main__.settings.PubServ = msg['content'].encode()
|
|
132
|
+
try:
|
|
133
|
+
#print("Connexion réussi")
|
|
134
|
+
__main__.settings.keyC = RSA.importKey(__main__.settings.PubServ)
|
|
135
|
+
|
|
136
|
+
#Enregistrement de la clé public du serveur
|
|
137
|
+
pubkey_path = os.path.join(__main__.settings.config_dir, "server.pub")
|
|
138
|
+
with open(pubkey_path, "wb") as f:
|
|
139
|
+
f.write(__main__.settings.PubServ)
|
|
140
|
+
|
|
141
|
+
#Calcul du hash de la clé du serveur
|
|
142
|
+
__main__.settings.serverhash = hashlib.sha256(__main__.settings.PubServ).hexdigest()
|
|
143
|
+
|
|
144
|
+
__main__.settings.pubserv_set = True
|
|
145
|
+
|
|
146
|
+
except Exception as err:
|
|
147
|
+
__main__.settings.PubServ=None
|
|
148
|
+
__main__.settings.pubserv_set = False
|
|
149
|
+
|
|
150
|
+
elif msg['method']=="welcome":
|
|
151
|
+
if 'message' in msg:
|
|
152
|
+
notify(msg['message'], "error")
|
|
153
|
+
|
|
154
|
+
if 'status' in msg:
|
|
155
|
+
if msg['status']==0:
|
|
156
|
+
__main__.settings.pubserv_set = False
|
|
157
|
+
#self.welcome()
|
|
158
|
+
|
|
159
|
+
elif msg['status']==2:
|
|
160
|
+
if os.path.exists(__main__.settings.token_file):
|
|
161
|
+
os.remove(__main__.settings.token_file)
|
|
162
|
+
exit()
|
|
163
|
+
|
|
164
|
+
|
|
165
|
+
@impmagic.loader(
|
|
166
|
+
{'module': '__main__'},
|
|
167
|
+
)
|
|
168
|
+
def get_note_content(self, note):
|
|
169
|
+
command = {
|
|
170
|
+
'method': 'note',
|
|
171
|
+
'action': 'get',
|
|
172
|
+
'note': note,
|
|
173
|
+
'token': __main__.settings.token
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
message = self.call_server(command)
|
|
177
|
+
if message['method']=="note":
|
|
178
|
+
if 'content' in message:
|
|
179
|
+
return message['content']
|
|
180
|
+
|
|
181
|
+
return ""
|
|
182
|
+
|
|
183
|
+
|
|
184
|
+
#Envoie une commande au serveur et attends une réponse
|
|
185
|
+
@impmagic.loader(
|
|
186
|
+
{'module': '__main__'},
|
|
187
|
+
{'module': 'app.display', 'submodule': ['logs']},
|
|
188
|
+
{'module': 'core.pack', 'submodule': ['pack', 'unpack']},
|
|
189
|
+
)
|
|
190
|
+
def call_server(self, command):
|
|
191
|
+
if not self.well_come:
|
|
192
|
+
self.welcome()
|
|
193
|
+
self.well_come = True
|
|
194
|
+
|
|
195
|
+
message = None
|
|
196
|
+
|
|
197
|
+
#if not __main__.settings.pubserv_set:
|
|
198
|
+
#if not __main__.settings.serverhash:
|
|
199
|
+
|
|
200
|
+
if not __main__.settings.pubserv_set:
|
|
201
|
+
return None
|
|
202
|
+
#exit()
|
|
203
|
+
#try:
|
|
204
|
+
self.send(pack(command, __main__.settings.key))
|
|
205
|
+
|
|
206
|
+
try:
|
|
207
|
+
message_recu = self.receive()
|
|
208
|
+
|
|
209
|
+
except:
|
|
210
|
+
if self.killed:
|
|
211
|
+
logs(f"Arrêt du thread demandé", component="client")
|
|
212
|
+
else:
|
|
213
|
+
logs(f"Erreur avec le client. Arret de la connexion", "error", component="client")
|
|
214
|
+
exit()
|
|
215
|
+
|
|
216
|
+
if message_recu!=b'':
|
|
217
|
+
message = self.decrypt_data(message_recu)
|
|
218
|
+
message = unpack(message, __main__.settings.keyC)
|
|
219
|
+
|
|
220
|
+
return message
|
|
221
|
+
|
|
222
|
+
|
|
223
|
+
@impmagic.loader(
|
|
224
|
+
{'module': '__main__'},
|
|
225
|
+
{'module': 'json'},
|
|
226
|
+
{'module': 'os'},
|
|
227
|
+
{'module': 'hashlib'},
|
|
228
|
+
{'module': 'core.editor', 'submodule': ['Editor']},
|
|
229
|
+
{'module': 'app.display', 'submodule': ['logs', 'notify', 'print_nxs', 'SaveScreen', 'RestoreScreen']},
|
|
230
|
+
{'module': 'core.pack', 'submodule': ['pack']},
|
|
231
|
+
{'module': 'zpp_color', 'submodule': ['fg', 'attr']},
|
|
232
|
+
{'module': 'app.input', 'submodule': ['getpass']},
|
|
233
|
+
{'module': 'zpp_ManagedFile', 'submodule': ['ManagedFile']}
|
|
234
|
+
)
|
|
235
|
+
def run(self, argument, parameter):
|
|
236
|
+
command = None
|
|
237
|
+
|
|
238
|
+
if argument!=None and len(parameter):
|
|
239
|
+
instruction = parameter[0]
|
|
240
|
+
|
|
241
|
+
if instruction=="login":
|
|
242
|
+
username = input("Username: ")
|
|
243
|
+
password = getpass("Password: ")
|
|
244
|
+
|
|
245
|
+
command = {
|
|
246
|
+
'method': 'authentication',
|
|
247
|
+
'username': username,
|
|
248
|
+
'password': password
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
__main__.settings.token = None
|
|
252
|
+
if os.path.exists(__main__.settings.token_file):
|
|
253
|
+
os.remove(__main__.settings.token_file)
|
|
254
|
+
|
|
255
|
+
elif instruction=="status":
|
|
256
|
+
command = {
|
|
257
|
+
'method': 'status',
|
|
258
|
+
'token': __main__.settings.token
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
elif instruction=="logout":
|
|
262
|
+
command = {
|
|
263
|
+
'method': 'logout',
|
|
264
|
+
'token': __main__.settings.token
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
if os.path.exists(__main__.settings.token_file):
|
|
268
|
+
os.remove(__main__.settings.token_file)
|
|
269
|
+
|
|
270
|
+
elif instruction=="workspace":
|
|
271
|
+
command = {
|
|
272
|
+
'method': 'workspace',
|
|
273
|
+
'token': __main__.settings.token
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
if len(parameter)>1:
|
|
277
|
+
command['workspace'] = parameter[1]
|
|
278
|
+
|
|
279
|
+
if argument.create:
|
|
280
|
+
command['action'] = 'create'
|
|
281
|
+
|
|
282
|
+
elif argument.remove:
|
|
283
|
+
command['action'] = 'remove'
|
|
284
|
+
|
|
285
|
+
elif instruction=="add" or instruction=="edit":
|
|
286
|
+
if len(parameter)>1:
|
|
287
|
+
command = {
|
|
288
|
+
'method': 'note',
|
|
289
|
+
'action': instruction,
|
|
290
|
+
'note': parameter[1],
|
|
291
|
+
'token': __main__.settings.token
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
try:
|
|
295
|
+
file = ManagedFile(mode='a', typefile='stringio')
|
|
296
|
+
|
|
297
|
+
if instruction=="edit":
|
|
298
|
+
file.write(self.get_note_content(parameter[1]))
|
|
299
|
+
file.seek(0)
|
|
300
|
+
|
|
301
|
+
SaveScreen()
|
|
302
|
+
editor = Editor(filename=parameter[1], file=file)
|
|
303
|
+
editor.app.run()
|
|
304
|
+
RestoreScreen()
|
|
305
|
+
file.seek(0)
|
|
306
|
+
|
|
307
|
+
command['content'] = file.read()
|
|
308
|
+
|
|
309
|
+
except Exception as e:
|
|
310
|
+
command = None
|
|
311
|
+
logs(f"An unexpected error occurred: {e}", "error")
|
|
312
|
+
|
|
313
|
+
elif instruction=="view" or instruction=="pull":
|
|
314
|
+
if len(parameter)>1:
|
|
315
|
+
content_note = self.get_note_content(parameter[1])
|
|
316
|
+
|
|
317
|
+
if instruction=="view":
|
|
318
|
+
print(content_note)
|
|
319
|
+
|
|
320
|
+
elif instruction=="pull":
|
|
321
|
+
if len(parameter)>2:
|
|
322
|
+
filename = parameter[2]
|
|
323
|
+
|
|
324
|
+
if os.path.exists(filename):
|
|
325
|
+
notify(f"Le fichier {filename} existe déjà", "error")
|
|
326
|
+
|
|
327
|
+
with open(filename, "w") as f:
|
|
328
|
+
f.write(content_note)
|
|
329
|
+
|
|
330
|
+
elif instruction=="push":
|
|
331
|
+
if len(parameter)>1:
|
|
332
|
+
filename = parameter[1]
|
|
333
|
+
|
|
334
|
+
if os.path.exists(filename):
|
|
335
|
+
with open(filename, "r") as f:
|
|
336
|
+
content_note = f.read()
|
|
337
|
+
|
|
338
|
+
if len(parameter)>2:
|
|
339
|
+
note_name = parameter[2]
|
|
340
|
+
else:
|
|
341
|
+
note_name = os.path.basename(filename)
|
|
342
|
+
|
|
343
|
+
|
|
344
|
+
command = {
|
|
345
|
+
'method': 'note',
|
|
346
|
+
'action': "add",
|
|
347
|
+
'note': note_name,
|
|
348
|
+
'content': content_note,
|
|
349
|
+
'token': __main__.settings.token
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
elif instruction=="remove":
|
|
353
|
+
if len(parameter)>1:
|
|
354
|
+
command = {
|
|
355
|
+
'method': 'note',
|
|
356
|
+
'action': "remove",
|
|
357
|
+
'note': parameter[1],
|
|
358
|
+
'token': __main__.settings.token
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
elif instruction=="list":
|
|
362
|
+
command = {
|
|
363
|
+
'method': 'note',
|
|
364
|
+
'action': 'list',
|
|
365
|
+
'note': '',
|
|
366
|
+
'token': __main__.settings.token
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
elif instruction=="info":
|
|
370
|
+
if len(parameter)>1:
|
|
371
|
+
command = {
|
|
372
|
+
'method': 'note',
|
|
373
|
+
'action': 'info',
|
|
374
|
+
'note': parameter[1],
|
|
375
|
+
'token': __main__.settings.token
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
elif instruction=="find":
|
|
379
|
+
if len(parameter)>1:
|
|
380
|
+
command = {
|
|
381
|
+
'method': 'note',
|
|
382
|
+
'action': 'find',
|
|
383
|
+
'pattern': parameter[1],
|
|
384
|
+
'token': __main__.settings.token
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
elif instruction=="protect":
|
|
388
|
+
if len(parameter)>1:
|
|
389
|
+
password = getpass("Password: ")
|
|
390
|
+
note_password = hashlib.sha256(password.encode()).hexdigest()
|
|
391
|
+
|
|
392
|
+
command = {
|
|
393
|
+
'method': 'note',
|
|
394
|
+
'action': 'protect',
|
|
395
|
+
'note': parameter[1],
|
|
396
|
+
'password_hash': note_password,
|
|
397
|
+
'token': __main__.settings.token
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
if command:
|
|
401
|
+
self.send_and_response(command)
|
|
402
|
+
|
|
403
|
+
|
|
404
|
+
@impmagic.loader(
|
|
405
|
+
{'module': '__main__'},
|
|
406
|
+
{'module': 'app.display', 'submodule': ['logs', 'notify', 'print_nxs']},
|
|
407
|
+
{'module': 'core.pack', 'submodule': ['pack']},
|
|
408
|
+
{'module': 'core.client.token', 'submodule': ['set_token']},
|
|
409
|
+
{'module': 'app.input', 'submodule': ['getpass']},
|
|
410
|
+
)
|
|
411
|
+
def send_and_response(self, command):
|
|
412
|
+
message = self.call_server(command)
|
|
413
|
+
|
|
414
|
+
if message['method']=="setToken":
|
|
415
|
+
if 'content' in message:
|
|
416
|
+
__main__.settings.token = message['content']
|
|
417
|
+
set_token(__main__.settings.token)
|
|
418
|
+
logs(f"Reception du token", component="client")
|
|
419
|
+
notify("Connecté", "success")
|
|
420
|
+
else:
|
|
421
|
+
logs("Token invalide", "error")
|
|
422
|
+
|
|
423
|
+
elif message['method']=="workspace":
|
|
424
|
+
if 'content' in message:
|
|
425
|
+
for element in message['content']:
|
|
426
|
+
if 'active' in message and element==message['active']:
|
|
427
|
+
print(f"* {element}")
|
|
428
|
+
else:
|
|
429
|
+
print(f" {element}")
|
|
430
|
+
|
|
431
|
+
elif message['method']=="status":
|
|
432
|
+
for element in ['connected', 'workspace', 'notes']:
|
|
433
|
+
if element in message:
|
|
434
|
+
print_nxs(f"{element}: ", color='dark_gray',nojump=True)
|
|
435
|
+
|
|
436
|
+
if isinstance(message[element], bool):
|
|
437
|
+
if message[element]==True:
|
|
438
|
+
print_nxs(message[element], color="green")
|
|
439
|
+
else:
|
|
440
|
+
print_nxs(message[element], color="light_red")
|
|
441
|
+
|
|
442
|
+
else:
|
|
443
|
+
print_nxs(message[element])
|
|
444
|
+
|
|
445
|
+
elif message['method']=="list":
|
|
446
|
+
if 'notes' in message:
|
|
447
|
+
for note in message['notes']:
|
|
448
|
+
print(f" - {note}")
|
|
449
|
+
|
|
450
|
+
elif message['method']=="info":
|
|
451
|
+
if 'note' in message:
|
|
452
|
+
for key, value in message['note'].items():
|
|
453
|
+
print_nxs(f"{key}: ", color='dark_gray',nojump=True)
|
|
454
|
+
print_nxs(value)
|
|
455
|
+
|
|
456
|
+
elif message['method']=="find":
|
|
457
|
+
if 'content' in message:
|
|
458
|
+
for note in message['content']:
|
|
459
|
+
print(f" - {note}")
|
|
460
|
+
|
|
461
|
+
|
|
462
|
+
elif message['method']=="notify":
|
|
463
|
+
if 'message' in message and 'status_code' in message:
|
|
464
|
+
notify(message['message'], message['status_code'])
|
|
465
|
+
|
|
466
|
+
elif message['method']=="password_required" and 'payload' in message:
|
|
467
|
+
password = getpass("Password: ")
|
|
468
|
+
payload = message['payload']
|
|
469
|
+
|
|
470
|
+
payload['password_hash'] = hashlib.sha256(password.encode()).hexdigest()
|
|
471
|
+
|
|
472
|
+
self.send_and_response(payload)
|
|
473
|
+
|
|
474
|
+
else:
|
|
475
|
+
print(message)
|
|
476
|
+
|
|
477
|
+
self.send(pack({'method': 'endSession'}, __main__.settings.key))
|
|
478
|
+
"""
|
|
479
|
+
except Exception as err:
|
|
480
|
+
logs(err, "error", component="client")
|
|
481
|
+
|
|
482
|
+
finally:
|
|
483
|
+
self.connexion.close()
|
|
484
|
+
"""
|
|
485
|
+
|
|
486
|
+
|
|
487
|
+
#Récupération de l'id du thread en cours
|
|
488
|
+
def get_id(self):
|
|
489
|
+
if hasattr(self, '_thread_id'):
|
|
490
|
+
return self._thread_id
|
|
491
|
+
for id, thread in threading._active.items():
|
|
492
|
+
if thread is self:
|
|
493
|
+
return id
|
|
494
|
+
|
|
495
|
+
|
|
496
|
+
#Permet l'arrêt du thread en cours
|
|
497
|
+
@impmagic.loader(
|
|
498
|
+
{'module': 'ctypes'},
|
|
499
|
+
{'module': 'app.display', 'submodule': ['logs']},
|
|
500
|
+
)
|
|
501
|
+
def kill_thread(self):
|
|
502
|
+
logs("Requête d'arrêt reçue", "info")
|
|
503
|
+
self.killed = True
|
|
504
|
+
thread_id = self.get_id()
|
|
505
|
+
res = ctypes.pythonapi.PyThreadState_SetAsyncExc(thread_id,
|
|
506
|
+
ctypes.py_object(SystemExit))
|
|
507
|
+
if res > 1:
|
|
508
|
+
ctypes.pythonapi.PyThreadState_SetAsyncExc(thread_id, 0)
|
|
509
|
+
#print('Exception raise failure')
|
|
510
|
+
self.connexion.close()
|
|
511
|
+
#th_E.kill_thread()
|
|
512
|
+
|
|
513
|
+
|
|
514
|
+
@impmagic.loader(
|
|
515
|
+
{'module': '__main__'},
|
|
516
|
+
{'module': 'zpp_args'},
|
|
517
|
+
{'module': 'app.display', 'submodule': ['logs']},
|
|
518
|
+
{'module': 'core.client.settings', 'submodule': ['Settings']},
|
|
519
|
+
{'module': 'core.client.connect', 'submodule': ['connect_server']},
|
|
520
|
+
)
|
|
521
|
+
def run_client():
|
|
522
|
+
parse = zpp_args.parser()
|
|
523
|
+
parse.set_description("znote")
|
|
524
|
+
parse.set_argument(longname="config", description="Spécifier le chemin du fichier de config", store="value", default=None)
|
|
525
|
+
parse.set_argument(longname="init", description="Initialiser les paramètres de l'application", default=None)
|
|
526
|
+
parse.set_argument(longname="create", description="Création d'un workspace", default=False, category="workspace")
|
|
527
|
+
parse.set_argument(longname="remove", description="Suppression d'un workspace", default=False, category="workspace")
|
|
528
|
+
parse.set_argument(longname="info", description="Afficher les informations de connexion", default=False)
|
|
529
|
+
#parse.set_parameter("command", description="commande à lancer")
|
|
530
|
+
parse.set_parameter("login", "Connexion à un serveur de note")
|
|
531
|
+
parse.set_parameter("logout", "Déconnexion d'un serveur de note")
|
|
532
|
+
parse.set_parameter("status", "Affichage du statut de l'application")
|
|
533
|
+
parse.set_parameter("workspace", "Création/Suppression/Changement de workspace")
|
|
534
|
+
parse.set_parameter("add", "Ajouter une note")
|
|
535
|
+
parse.set_parameter("edit", "Editer une note")
|
|
536
|
+
parse.set_parameter("view", "Voir une note")
|
|
537
|
+
parse.set_parameter("pull", "Récupérer une note")
|
|
538
|
+
parse.set_parameter("push", "Envoyer une note")
|
|
539
|
+
parse.set_parameter("remove", "Supprimer une note")
|
|
540
|
+
parse.set_parameter("list", "Lister les notes")
|
|
541
|
+
parse.set_parameter("info", "Afficher les informations d'une note")
|
|
542
|
+
parse.set_parameter("find", "Rechercher un contenu dans les notes")
|
|
543
|
+
parse.set_parameter("protect", "Protéger une note par mot de passe")
|
|
544
|
+
parse.disable_check()
|
|
545
|
+
parameter, argument = parse.load()
|
|
546
|
+
|
|
547
|
+
if argument!=None:
|
|
548
|
+
__main__.settings = Settings(argument.config)
|
|
549
|
+
|
|
550
|
+
if argument.init:
|
|
551
|
+
__main__.settings.init_settings()
|
|
552
|
+
|
|
553
|
+
elif argument.info:
|
|
554
|
+
logs(f"{__main__.settings.host}:{__main__.settings.port}")
|
|
555
|
+
|
|
556
|
+
else:
|
|
557
|
+
__main__.settings.load_key()
|
|
558
|
+
connexion = connect_server()
|
|
559
|
+
|
|
560
|
+
if connexion==0:
|
|
561
|
+
logs("Impossible d'accéder au serveur", "error")
|
|
562
|
+
else:
|
|
563
|
+
__main__.th_R = Client(connexion)
|
|
564
|
+
__main__.th_R.run(argument, parameter)
|
|
565
|
+
|
|
566
|
+
|
|
567
|
+
if __name__ == "__main__":
|
|
568
|
+
run_client()
|
znote/core/auth.py
ADDED
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import __main__
|
|
2
|
+
import impmagic
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
@impmagic.loader(
|
|
6
|
+
{'module': '__main__'},
|
|
7
|
+
{'module': 'base64'},
|
|
8
|
+
{'module': 'secrets'}
|
|
9
|
+
)
|
|
10
|
+
def get_token(username):
|
|
11
|
+
length = 32
|
|
12
|
+
# Génère un token binaire aléatoire
|
|
13
|
+
token_bytes = secrets.token_bytes(length)
|
|
14
|
+
# Encode le token en base64 pour qu'il soit lisible et transmissible
|
|
15
|
+
token = base64.urlsafe_b64encode(token_bytes).rstrip(b'=').decode('utf-8')
|
|
16
|
+
|
|
17
|
+
user_data = {
|
|
18
|
+
'username': username,
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
__main__.server.USER[token] = user_data
|
|
22
|
+
|
|
23
|
+
return token
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
@impmagic.loader(
|
|
27
|
+
{'module': 'hashlib', 'submodule': ['sha256']}
|
|
28
|
+
)
|
|
29
|
+
def hash_password(password):
|
|
30
|
+
return sha256(password.encode("utf-8")).hexdigest()
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
@impmagic.loader(
|
|
34
|
+
{'module': '__main__'},
|
|
35
|
+
{'module': 'pony.orm', 'submodule': ['db_session']}
|
|
36
|
+
)
|
|
37
|
+
def authenticate(username, password):
|
|
38
|
+
with db_session:
|
|
39
|
+
user = __main__.settings.orm.User.get(username=username)
|
|
40
|
+
if user and user.password_hash == hash_password(password):
|
|
41
|
+
return True
|
|
42
|
+
return False
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
@impmagic.loader(
|
|
46
|
+
{'module': '__main__'},
|
|
47
|
+
{'module': 'pony.orm', 'submodule': ['db_session']}
|
|
48
|
+
)
|
|
49
|
+
def get_userid(username):
|
|
50
|
+
with db_session:
|
|
51
|
+
user = __main__.settings.orm.User.get(username=username)
|
|
52
|
+
if user:
|
|
53
|
+
return user.id
|
|
54
|
+
return None
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
#Création d'un nouvel utilisateur
|
|
58
|
+
@impmagic.loader(
|
|
59
|
+
{'module': '__main__'},
|
|
60
|
+
{'module': 'datetime', 'submodule': ['datetime']},
|
|
61
|
+
{'module': 'pony.orm', 'submodule': ['db_session', 'commit']}
|
|
62
|
+
)
|
|
63
|
+
def create_user(username, password):
|
|
64
|
+
with db_session:
|
|
65
|
+
if not __main__.settings.orm.User.get(username=username):
|
|
66
|
+
__main__.settings.orm.User(username=username, password_hash=hash_password(password), created_at=datetime.utcnow())
|
|
67
|
+
commit()
|
|
68
|
+
|
|
69
|
+
return ""
|
|
70
|
+
else:
|
|
71
|
+
return "User already exist"
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import __main__
|
|
2
|
+
import impmagic
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
@impmagic.loader(
|
|
6
|
+
{'module': 'socket'},
|
|
7
|
+
{'module': 'app.display', 'submodule': ['logs']},
|
|
8
|
+
)
|
|
9
|
+
def connect_server():
|
|
10
|
+
connexion = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
|
11
|
+
try:
|
|
12
|
+
connexion.connect((__main__.settings.host, __main__.settings.port))
|
|
13
|
+
except socket.error:
|
|
14
|
+
return 0
|
|
15
|
+
logs("Connexion établie avec le serveur.", "success")
|
|
16
|
+
return connexion
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
@impmagic.loader(
|
|
20
|
+
{'module': 'base64','submodule': ['b64encode']},
|
|
21
|
+
{'module': 'Crypto.Cipher','submodule': ['AES']},
|
|
22
|
+
{'module': 'Crypto.Hash','submodule': ['SHA256']},
|
|
23
|
+
{'module': 'Crypto','submodule': ['Random']}
|
|
24
|
+
)
|
|
25
|
+
def encrypt(source, encode=True):
|
|
26
|
+
key = __main__.settings.totp.now()
|
|
27
|
+
key = SHA256.new(key.encode()).digest()
|
|
28
|
+
IV = Random.new().read(AES.block_size)
|
|
29
|
+
encryptor = AES.new(key, AES.MODE_CBC, IV)
|
|
30
|
+
padding = AES.block_size - len(source) % AES.block_size
|
|
31
|
+
source += bytes([padding]) * padding
|
|
32
|
+
data = IV + encryptor.encrypt(source)
|
|
33
|
+
return b64encode(data) if encode else data
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
@impmagic.loader(
|
|
37
|
+
{'module': 'base64','submodule': ['b64decode']},
|
|
38
|
+
{'module': 'Crypto.Cipher','submodule': ['AES']},
|
|
39
|
+
{'module': 'Crypto.Hash','submodule': ['SHA256']}
|
|
40
|
+
)
|
|
41
|
+
def decrypt(source, decode=True):
|
|
42
|
+
key = __main__.settings.totp.now()
|
|
43
|
+
if decode:
|
|
44
|
+
source = b64decode(source)
|
|
45
|
+
key = SHA256.new(key.encode()).digest()
|
|
46
|
+
IV = source[:AES.block_size]
|
|
47
|
+
decryptor = AES.new(key, AES.MODE_CBC, IV)
|
|
48
|
+
data = decryptor.decrypt(source[AES.block_size:])
|
|
49
|
+
padding = data[-1]
|
|
50
|
+
if data[-padding:] != bytes([padding]) * padding:
|
|
51
|
+
raise ValueError("Invalid padding...")
|
|
52
|
+
return data[:-padding].decode("Utf8")
|