@@ -100,6 +100,7 @@ def build(self):
100100
101101 def on_start (self ):
102102 self .update_passwords ()
103+ self .check_alpha ()
103104
104105 # ================================
105106 # Context menus and dialogs
@@ -165,31 +166,107 @@ def wip_info(self):
165166 ).alert ()
166167 info_dialog .open ()
167168
169+ # ================================
170+ # Checks
171+ # ================================
172+
173+ def check_alpha (self ):
174+ """Checks if beta password is set. If so, checks if alpha password is set.
175+ That's because beta password is required to save alpha password."""
176+ Logger .debug ('Called: check_alpha' )
177+ ok_beta = self .check_beta ()
178+ if ok_beta is True :
179+ try :
180+ open (appdata (Files .alpha_key ))
181+ except FileNotFoundError :
182+ alert_dialog = MDDialog (
183+ title = 'Missing alpha password' ,
184+ text = 'I\' ve noticed, that you have not set alpha password. It\' s needed for safe password storing.'
185+ ' Do you want to provide it by yourself, or let program to randomize it?' ,
186+ auto_dismiss = False ,
187+ buttons = [
188+ MDFillRoundFlatIconButton (
189+ text = 'Set password' ,
190+ icon = 'account-key-outline' ,
191+ on_release = lambda x : self .dismiss_and_back (alert_dialog , 'settings' )
192+ ),
193+ MDRoundFlatIconButton (
194+ text = 'Randomize' ,
195+ icon = 'dice-multiple-outline' ,
196+ on_release = lambda x : [
197+ self .change_alpha (rand_password ()),
198+ alert_dialog .dismiss ()
199+ ]
200+ )
201+ ]
202+ )
203+ alert_dialog .open ()
204+ return False
205+ else :
206+ return True
207+
208+ def check_beta (self ):
209+ """Checks if beta password is set."""
210+ Logger .debug ('Called: check_beta' )
211+ try :
212+ open (appdata (Files .beta_key ))
213+ except FileNotFoundError :
214+ alert_dialog = MDDialog (
215+ title = 'Missing beta password' ,
216+ text = 'I\' ve noticed, that you have not set beta password. It\' s needed for safe password storing.'
217+ ' Do you want to provide it by yourself, or let program to randomize it?' ,
218+ auto_dismiss = False ,
219+ buttons = [
220+ MDFillRoundFlatIconButton (
221+ text = 'Set password' ,
222+ icon = 'account-key-outline' ,
223+ on_release = lambda x : self .dismiss_and_back (alert_dialog , 'settings' )
224+ ),
225+ MDRoundFlatIconButton (
226+ text = 'Randomize' ,
227+ icon = 'dice-multiple-outline' ,
228+ on_release = lambda x : [
229+ self .reset_beta (),
230+ alert_dialog .dismiss ()
231+ ]
232+ )
233+ ]
234+ )
235+ alert_dialog .open ()
236+ return False
237+ else :
238+ return True
239+
168240 # ================================
169241 # Passwords managing
170242 # ================================
171243
172244 def add_password (self ):
173245 """Adding passwords to database. Invoked by button in ``passwords`` menu."""
174246 Logger .trace ('Called: add_password' )
175- alias_box = self .root .ids .password_alias
176- value_box = self .root .ids .password_value
247+ try :
248+ open (appdata (Files .alpha_key ))
249+ except FileNotFoundError :
250+ self .check_alpha ()
251+ else :
252+ alias_box = self .root .ids .password_alias
253+ value_box = self .root .ids .password_value
177254
178- self .validate_input (alias_box , 3 )
179- self .validate_input (value_box , 6 )
255+ self .validate_input (alias_box , 3 )
256+ self .validate_input (value_box , 6 )
180257
181- ok_alias = self .validate_input (alias_box , 3 )
182- ok_value = self .validate_input (value_box , 6 )
258+ ok_alias = self .validate_input (alias_box , 3 )
259+ ok_value = self .validate_input (value_box , 6 )
183260
184- password_alias = alias_box .text .capitalize ()
185- password_value = value_box .text
261+ password_alias = alias_box .text .capitalize ()
262+ password_value = value_box .text
186263
187- if ok_alias is True and ok_value is True :
188- try :
189- query (
190- 'INSERT INTO passwords (`name`, `password`) VALUES (?, ?);' ,
191- [password_alias , encrypt (password_value )]
192- )
264+ if ok_alias is True and ok_value is True :
265+ try :
266+ query (
267+ 'INSERT INTO passwords (`name`, `password`) VALUES (?, ?);' ,
268+ [password_alias , encrypt (password_value )]
269+ )
193270
194271 except sqlite3 .IntegrityError :
195272 Logger .info (f'Passwords: Tried to save "{ password_alias } " but already exists.' )
@@ -206,22 +283,15 @@ def add_password(self):
206283 ).alert ()
207284
208285 else :
209- Logger .info (f'Passwords: Password "{ password_alias } " saved.' )
210286 result_dialog = CustomDialog (
211287 title = 'Whoops!' ,
212288 text = 'The entered values are too short or invalid.'
213289 ).alert ()
214290
215- else :
216- result_dialog = CustomDialog (
217- title = 'Whoops!' ,
218- text = 'The entered data is invalid.'
219- ).alert ()
220-
221- result_dialog .open ()
222- alias_box .text = ''
223- value_box .text = ''
224- self .update_passwords ()
291+ result_dialog .open ()
292+ alias_box .text = ''
293+ value_box .text = ''
294+ self .update_passwords ()
225295
226296 def copy_password (self , password = None ):
227297 """
@@ -279,45 +349,56 @@ def del_password(self, password=None):
279349
280350 self .root .ids .del_password_alias .text = ''
281351
282- to_del = query (
283- 'SELECT `password` FROM `passwords` WHERE `name` LIKE ?;' ,
284- [password ]
285- )
286- if to_del is not None :
287- to_del = to_del [0 ][0 ]
288- if type (to_del ) is bytes :
289- result_dialog = MDDialog (
290- title = 'Attention' ,
291- text = f'Do you really want to delete "{ password } " password?' ,
292- auto_dismiss = False ,
293- buttons = [
294- MDFillRoundFlatIconButton (
295- text = 'Yes' ,
296- icon = 'check-circle-outline' ,
297- on_release = lambda x : [
298- self .del_password_confirm (password = password ),
299- result_dialog .dismiss ()
300- ]
301- ),
302- MDRoundFlatIconButton (
303- text = 'No' ,
304- icon = 'close-circle-outline' ,
305- on_release = lambda x : self .dismiss_and_back (result_dialog )
306- )
307- ]
308- )
352+ if len (password ) == 0 :
353+ result_dialog = CustomDialog (
354+ title = 'Whoops' ,
355+ text = 'Please provide password alias at first.'
356+ ).alert ()
357+ elif len (password ) < 3 :
358+ result_dialog = CustomDialog (
359+ title = 'Whoops' ,
360+ text = 'Password alias has to be at least 3 characters long.'
361+ ).alert ()
362+ else :
363+ to_del = query (
364+ 'SELECT `password` FROM `passwords` WHERE `name` LIKE ?;' ,
365+ [password ]
366+ )
367+ if len (to_del ) > 0 :
368+ to_del = to_del [0 ][0 ]
369+ if type (to_del ) is bytes :
370+ result_dialog = MDDialog (
371+ title = 'Attention' ,
372+ text = f'Do you really want to delete "{ password } " password?' ,
373+ auto_dismiss = False ,
374+ buttons = [
375+ MDFillRoundFlatIconButton (
376+ text = 'Yes' ,
377+ icon = 'check-circle-outline' ,
378+ on_release = lambda x : [
379+ self .del_password_confirm (password = password ),
380+ result_dialog .dismiss ()
381+ ]
382+ ),
383+ MDRoundFlatIconButton (
384+ text = 'No' ,
385+ icon = 'close-circle-outline' ,
386+ on_release = lambda x : self .dismiss_and_back (result_dialog )
387+ )
388+ ]
389+ )
390+ else :
391+ Logger .critical (
392+ f'Database: Password "{ password } " is stored as "{ type (to_del )} " type instead of "byte" type' )
393+ result_dialog = CustomDialog (
394+ title = 'Warning!' ,
395+ text = 'An critical error has occurred. Passwords are saved to local database in wrong way.'
396+ ).alert ()
309397 else :
310- Logger .critical (
311- f'Database: Password "{ password } " is stored as "{ type (to_del )} " type instead of "byte" type' )
312398 result_dialog = CustomDialog (
313399 title = 'Warning!' ,
314- text = 'An critical error has occurred. Passwords are saved to local database in wrong way. '
400+ text = 'That password do not exists in database '
315401 ).alert ()
316- else :
317- result_dialog = CustomDialog (
318- title = 'Warning!' ,
319- text = 'That password do not exists in database'
320- ).alert ()
321402 result_dialog .open ()
322403
323404 def del_password_confirm (self , password ):
@@ -347,12 +428,14 @@ def del_password_confirm(self, password):
347428 def change_alpha (self , preset = None ):
348429 """Changing alpha password based on text input or random value from ``reset_alpha`` method."""
349430 Logger .trace ('Called: change_alpha' )
431+ ok_beta = self .check_beta ()
350432 password_box = self .root .ids .alpha_change
433+ if ok_beta is True :
351434
352- if preset is None :
353- password = password_box .text
354- else :
355- password = preset
435+ if preset is None :
436+ password = password_box .text
437+ else :
438+ password = preset
356439
357440 if len (password ) < 6 :
358441 result_dialog = CustomDialog (
@@ -361,38 +444,38 @@ def change_alpha(self, preset=None):
361444 ).alert ()
362445 password_box .error = True
363446
364- else :
365- password_box .error = False
366- try :
367- open (appdata (Files .beta_key ))
368- except FileNotFoundError :
369- generate_salt ()
370- finally :
447+ else :
448+ password_box .error = False
371449 try :
372- open (appdata (Files .alpha_key ))
450+ open (appdata (Files .beta_key ))
373451 except FileNotFoundError :
374- open (appdata (Files .alpha_key ), 'x' )
375- finally :
376- with open (appdata (Files .beta_key ), 'rb' ) as f :
377- kdf = PBKDF2HMAC (
378- algorithm = hashes .SHA256 (),
379- length = 32 ,
380- salt = f .read (),
381- iterations = 100000 ,
382- backend = default_backend ()
383- )
384- with open (appdata (Files .alpha_key ), 'wb' ) as f :
385- key = base64 .urlsafe_b64encode (kdf .derive (password .encode ()))
386- f .write (key )
387- Logger .info ('Passwords: Alpha password has changed' )
388-
389- result_dialog = CustomDialog (
390- title = 'Success!' ,
391- text = 'New alpha password successfully saved.'
392- ).alert ()
452+ self .check_beta ()
453+ return
454+ else :
455+ try :
456+ open (appdata (Files .alpha_key ))
457+ except FileNotFoundError :
458+ open (appdata (Files .alpha_key ), 'x' )
459+ finally :
460+ with open (appdata (Files .beta_key ), 'rb' ) as f :
461+ kdf = PBKDF2HMAC (
462+ algorithm = hashes .SHA256 (),
463+ length = 32 ,
464+ salt = f .read (),
465+ iterations = 100000 ,
466+ backend = default_backend ()
467+ )
468+ with open (appdata (Files .alpha_key ), 'wb' ) as f :
469+ key = base64 .urlsafe_b64encode (kdf .derive (password .encode ()))
470+ f .write (key )
471+ Logger .info ('Passwords: Alpha password has changed' )
472+ result_dialog = CustomDialog (
473+ title = 'Success!' ,
474+ text = 'New alpha password successfully saved.'
475+ ).alert ()
393476
477+ result_dialog .open ()
394478 password_box .text = ''
395- result_dialog .open ()
396479
397480 def reset_alpha (self ):
398481 """Changing alpha password to ``random_password`` function value."""
@@ -526,8 +609,24 @@ def update_passwords(self):
526609 count = len (self .passwords )
527610 if count == 0 :
528611 text = 'There are no passwords in database.'
612+ self .root .ids .passwords_list .add_widget (
613+ NewbieTip (
614+ text = 'Welcome to Python Password' ,
615+ secondary_text = 'Save your first password, by entering' ,
616+ tertiary_text = 'it\' s data on the right' ,
617+ icon = 'arrow-right-bold-circle'
618+ )
619+ )
529620 elif count == 1 :
530621 text = 'There\' s only 1 password in database.'
622+ self .root .ids .passwords_list .add_widget (
623+ NewbieTip (
624+ text = 'Congratulations!' ,
625+ secondary_text = 'You have saved your\' s 1st password.' ,
626+ tertiary_text = 'Preview it just by clicking it.' ,
627+ icon = 'arrow-up-bold-circle'
628+ )
629+ )
531630 else :
532631 text = f'There are { count } passwords in database.'
533632 self .root .ids .passwords_count .text = text
0 commit comments