import string
import random
import keyring
from kivymd.app import MDApp
from kivy.lang.builder
import Builder
from kivy.uix.screenmanager
import ScreenManager
, Screen
from kivy.properties
import ObjectProperty
, StringProperty
from kivymd.uix.button
import MDFlatButton
from kivymd.uix.boxlayout
import MDBoxLayout
from kivymd.uix.dialog
import MDDialog
from kivy.uix.popup
import Popup
from kivymd.uix.list
import TwoLineIconListItem
, IconLeftWidget
import sqlite3
service_id =
'PassWordGenerator'
# List reference for how to populate password field from generator
alphabet =
list(string.ascii_letters)
digits =
list(string.digits)
special =
list(
"!@#$%^&*()")
chars =
list(string.ascii_letters + string.digits +
"!@#$%^&*()")
# This is a popup to ensure validation
class Splash(Screen):
pass
class LogIn(Screen):
# Ensures that there is a user within stored local encryption that matches credentials
def verify(
self):
close = MDFlatButton(
text=
'Close', on_press=
self.dismiss)
self.dialog = MDDialog(
title=
'Error Encountered', text=
'This user does not exist! \n\nPlease create an account',
buttons=[close])
user =
self.ids.user.text
password =
self.ids.userpass.text
if keyring.get_password(service_id
, user) == password:
self.manager.current =
'pass'
self.manager.transition.direction =
'left'
else:
self.dialog.open()
# Helper to dismiss popup
def dismiss(
self, obj):
self.dialog.dismiss()
self.ids.user.text =
''
self.ids.userpass.text =
''
class DeleteAccount(Screen):
# Function to delete user/account from database
def delete_pass(
self):
user =
self.ids.user.text
# Dialog if removal failed
closeFail = MDFlatButton(
text=
'Close', on_press=
self.dismissFail)
closeSuccess = MDFlatButton(
text=
'Close', on_press=
self.dismissSuccess)
# Dialog if removal succeeds
self.fail = MDDialog(
title=
'Error Encountered', text=
'This user does not exist!', buttons=[closeFail])
self.success = MDDialog(
title=
'Success!', text=
'This user has been deleted from the database',
buttons=[closeSuccess])
# Try/Except block to catch error and call dialog box correctly
try:
keyring.delete_password(service_id
, user)
self.success.open()
except keyring.errors.PasswordDeleteError:
self.fail.open()
user =
''
# Helper to dismiss success dialog box
def dismissSuccess(
self, obj):
self.success.dismiss()
self.manager.current =
'login'
self.manager.transition.direction =
'up'
# Helper to dismiss success dialog box
def dismissFail(
self, obj):
self.fail.dismiss()
self.ids.user.text =
''
class CreateAccount(Screen):
# Function to ensure passwords match
# Don't want user to enter two different passwords by accident (stop human error)
def verify_pass(
self):
# Dialog box to be opened if error encountered
close = MDFlatButton(
text=
'Close', on_press=
self.dismiss)
self.dialog = MDDialog(
title=
'Error Encountered', text=
'Please ensure that passwords match!', buttons=[close])
# Grab necessary variables
user =
self.ids.username.text
first =
self.ids.userpass.text
second =
self.ids.second.text
# Conditional to check if passwords match
# If not open dialog for user to notify them of error
if first != second:
self.dialog.open()
else:
keyring.set_password(service_id
, user
, first)
self.manager.current =
'login'
# Helper to dismiss dialog box
def dismiss(
self, obj):
self.dialog.dismiss()
self.ids.userpass.text =
''
self.ids.second.text =
''
# TODO: There is stacking of dialog boxes: "Enter only Digits" and "Filling in gaps"
# Both of these appear upon a user entering in a letter instead of a digit in boxes
class PasswordScreen(Screen):
# This is main function within the application to generate our password for the user
# Uses predetermined global lists at top of file for alphabet, digits, and special characters
# Based upon a random.choice application of each list dependent on the users preferences
def buildPassword(
self):
# Create dialog box to handle errors!
close = MDFlatButton(
text=
'Close', on_press=
self.dismissDigit)
self.digit = MDDialog(
title=
'Error Encountered', text=
'Fill all fields and only use digits', buttons=[close]
,
auto_dismiss=
False)
# Give intial assignment and value attribute
pass_length =
0
a_count =
0
d_count =
0
s_count =
0
total_count =
0
errorFound =
False
# Grab initial counts
try:
pass_length =
int(
self.ids.length.text)
a_count =
int(
self.ids.pass_letter.text)
d_count =
int(
self.ids.pass_number.text)
s_count =
int(
self.ids.pass_special.text)
# Handle error if they enter anything OTHER than a digit (due to INT casting)
except ValueError:
errorFound =
True
self.digit.open()
# Calculate total
total_count = a_count + d_count + s_count
# Holder for our password, will be populated by following loops
password = []
# 3 loops to add in each amount of digits, chars, and specials
for i
in range(a_count):
password.append(random.choice(alphabet))
# Populate with alphabet
for i
in range(d_count):
password.append(random.choice(digits))
# Populate with digits
for i
in range(s_count):
password.append(random.choice(special))
# Populate with special chars
# Shuffle to ensure randomness of password
random.shuffle(password)
# Dialog box to inform user we are strengthening their password to fill in remaining space
# Triggers if user desired length > total # of each char type
close = MDFlatButton(
text=
'Close', on_press=
self.dismissFill)
self.fill = MDDialog(
title=
'Filling in the gaps',
text=
'Adding in additional random characters to satisfy length', buttons=[close]
,
auto_dismiss=
False)
# Check to ensure that total count is less than the password length
if (total_count < pass_length):
if total_count ==
0:
# Ensure no overlap of error messages
pass
else:
random.shuffle(chars)
# Shuffle the created password
for i
in range(pass_length - total_count):
password.append(random.choice(chars))
self.fill.open()
# Open dialog to inform user we are filling in gaps, if their entered numbers less than total desired length
# Dialog box to handle error that total counts from digit, char, and special !> total length
close = MDFlatButton(
text=
'Close', on_press=
self.dismissLength)
self.length = MDDialog(
title=
'Error Encountered', text=
'Please ensure entered char total less than length',
buttons=[close]
, auto_dismiss=
False)
# Conditional to ensure this does not happen
if (total_count > pass_length):
errorFound =
True
self.length.open()
# Another random shuffle and turn list into string to be printed
random.shuffle(password)
holder =
"".join(password)
# Conditional to ensure that if error is found we don't print anything to generated password box
# IF no errors than print password
# If any errors, reset all text boxes and allow user to try again
if errorFound ==
False:
self.ids.generated.text = holder
else:
self.resetText()
# Function called to clear passed information from user
def clear(
self):
self.manager.get_screen(
'login').ids.user.text =
''
self.manager.get_screen(
'login').ids.userpass.text =
''
# Method to add generated password to next pages list
def save(
self):
pic = IconLeftWidget(
icon=
'eye-off')
name =
self.ids.ID.text
password =
self.ids.generated.text
# Ensure user clicked the generate password button so we grab something
if password ==
'':
close = MDFlatButton(
text=
'Close', on_press=
self.dismissGen)
self.gen = MDDialog(
title=
'Error Encountered', text=
'Remember to click generate!', buttons=[close]
,
auto_dismiss=
False)
self.gen.open()
else:
# Create list item based upon entries
item = TwoLineIconListItem(
text=name
, secondary_text=password)
item.add_widget(pic)
# Adds item to next page list
self.manager.get_screen(
'save').ids.main.add_widget(item)
self.resetText()
# Helpers for all dialog boxes to ensure dismissal
def dismissGen(
self, obj):
self.gen.dismiss()
def dismissLength(
self, obj):
self.length.dismiss()
def dismissDigit(
self, obj):
self.digit.dismiss()
def dismissFill(
self, obj):
self.fill.dismiss()
def resetText(
self):
self.ids.ID.text =
''
self.ids.generated.text =
''
self.ids.length.text =
''
self.ids.pass_number.text =
''
self.ids.pass_letter.text =
''
self.ids.pass_special.text =
''
class Saved(Screen):
pass
class WindowManager(ScreenManager):
pass
class LoginPage(MDApp):
def build(
self):
self.theme_cls.theme_style =
'Light' # Background color
self.theme_cls.accent_palette =
'BlueGray'
screen = Builder.load_file(
'log.kv')
return screen
LoginPage().run()
this is my source code and i will my kv file also ..
WindowManager:
Splash:
name: 'splash'
LogIn:
name: 'login'
DeleteAccount:
name: 'delete'
CreateAccount:
name: 'create'
PasswordScreen:
name: 'pass'
Saved:
name: 'save'
<Splash>:
canvas:
Rectangle:
pos: self.pos
size: self.size
source: 'C:\\Users\\jaimi\\Downloads\\star.jpg'
MDCard:
size_hint: None, None
border_radius: 20
radius: [15]
size: 500, 500
elevation: 100
pos_hint: {'center_x':.5, 'center_y':.5}
orientation: 'vertical'
MDLabel:
id: my_label
markup: True
text: "[u]Password Generator 3000[/u]"
font_name: 'Arialbd'
font_size: 32
color: 35/255, 83/255, 161/255, 1
halign: 'center'
Image:
source: 'C:\\Users\\jaimi\\Downloads\\locksplash.jpg'
allow_stretch: True
Widget:
size_hint_y: None
MDBoxLayout:
size_hint_y: None
height: 30
orientation: 'horizontal'
Widget:
MDFillRoundFlatButton:
text: "Quit"
on_press: app.stop()
pos_hint: {'center_x':.5}
theme_text_color: "Custom"
md_bg_color: 35/255, 83/255, 161/255, 1
text_color: 0, 0, 0, 1
Widget:
MDFillRoundFlatButton:
text: "Proceed"
on_press: root.manager.current = 'login'
pos_hint: {'center_x': .5}
theme_text_color: "Custom"
md_bg_color: 35/255, 83/255, 161/255, 1
text_color: 0, 0, 0, 1
Widget:
Widget:
size_hint_y: None
height: 20
<LogIn>:
canvas:
Rectangle:
pos: self.pos
size: self.size
source: 'C:\\Users\\jaimi\\Downloads\\star.jpg'
MDCard:
border_radius: 20
radius: [15]
size_hint: None, None
size: 500, 500
elevation: 10
pos_hint: {'center_x':.5, 'center_y': .5}
orientation: 'vertical'
MDBoxLayout:
orientation: 'vertical'
size: root.width, root.height
spacing: 20
MDTopAppBar:
title: 'Welcome Back!'
md_bg_color: (35/255, 83/255, 161/255, 1)
icon_right: 'account'
specific_text_color: (1, 1, 1, 1)
MDLabel:
id: log
font_size: 32
text: "Please log in below!"
text_font: 'Arialbd'
halign: 'center'
MDTextField:
id: user
hint_text: "Username"
normal_color: app.theme_cls.accent_color
icon_right: 'account'
write_tab: False
size_hint_x: None
width: 300
font_size: 18
pos_hint: {'center_x':.5}
MDTextField:
id: userpass
hint_text: "Password"
normal_color: app.theme_cls.accent_color
icon_right: 'eye-off'
write_tab: False
size_hint_x: None
width: 300
font_size: 18
pos_hint: {'center_x':.5}
password: True
on_text_validate: root.verify()
Widget:
size_hint_y: None
height: 15
MDFillRoundFlatButton:
text: "Log In"
pos_hint: {'center_x': .5}
theme_text_color: "Custom"
md_bg_color: 35/255, 83/255, 161/255, 1
text_color: 0, 0, 0, 1
on_press: root.verify()
MDTextButton:
markup: True
text: "[i]Create new account[/i]"
pos_hint: {'center_x': .5}
on_press:
root.manager.current = 'create'
root.manager.transition.direction = 'down'
MDTextButton:
markup: True
text: "[i]Delete Account[/i]"
pos_hint: {'center_x': .5}
on_press:
root.manager.current = 'delete'
root.manager.transition.direction = 'up'
Widget:
size_hint_y: None
height: 40
<DeleteAccount>:
canvas:
Rectangle:
pos: self.pos
size: self.size
source: 'C:\\Users\\jaimi\\Downloads\\star.jpg'
MDCard:
size_hint: None, None
border_radius: 20
radius: [15]
size: 500, 500
elevation: 100
pos_hint: {'center_x':.5, 'center_y':.5}
orientation: 'vertical'
MDBoxLayout:
orientation: 'vertical'
size: root.width, root.height
spacing: 20
MDTopAppBar:
title: "Delete Existing Account"
md_bg_color: (35/255, 83/255, 161/255, 1)
icon_right: 'account'
specific_text_color: (1, 1, 1, 1)
Widget:
size_hint_y: None
height: 10
MDLabel:
markup: True
text: "[b]Enter username you wish to delete below[/b]"
halign: 'center'
pos_hint: {'center_x':.5}
MDTextField:
id: user
normal_color: app.theme_cls.accent_color
hint_text: "Username"
icon_right: 'account'
write_tab: False
size_hint_x: None
width: 300
font_size: 18
pos_hint: {'center_x':.5}
Widget:
size_hint_y: None
height: 10
MDFillRoundFlatButton:
text: 'Delete Account'
pos_hint: {'center_x': .5}
theme_text_color: "Custom"
md_bg_color: 35/255, 83/255, 161/255, 1
text_color: 0, 0, 0, 1
on_press: root.delete_pass()
MDFillRoundFlatButton:
text: 'Return'
pos_hint: {'center_x': .5}
theme_text_color: "Custom"
md_bg_color: 35/255, 83/255, 161/255, 1
text_color: 0, 0, 0, 1
on_press:
root.manager.current = 'login'
root.manager.transition.direction = 'down'
Widget:
#Spacing
<CreateAccount>:
canvas:
Rectangle:
pos: self.pos
size: self.size
source: 'C:\\Users\\jaimi\\Downloads\\star.jpg'
MDCard:
border_radius: 20
radius: [15]
size_hint: None, None
size: 500, 500
elevation: 10
pos_hint: {'center_x':.5, 'center_y': .5}
orientation: 'vertical'
MDBoxLayout:
orientation: 'vertical'
size: root.width, root.height
spacing: 20
MDTopAppBar:
title: 'Welcome new user!'
md_bg_color: (35/255, 83/255, 161/255, 1)
specific_text_color: (1, 1, 1, 1)
MDLabel:
markup: True
text: "[b]Please enter account details below[/b]"
halign: 'center'
pos_hint: {'center_x':.5}
MDTextField:
id: username
normal_color: app.theme_cls.accent_color
hint_text: "Username"
icon_right: 'account'
write_tab: False
size_hint_x: None
width: 300
font_size: 18
pos_hint: {'center_x':.5}
MDTextField:
id: userpass
hint_text: "Password"
normal_color: app.theme_cls.accent_color
icon_right: 'eye-off'
write_tab: False
size_hint_x: None
width: 300
font_size: 18
pos_hint: {'center_x':.5}
password: True
MDTextField:
id: second
hint_text: "Re-enter Password"
normal_color: app.theme_cls.accent_color
icon_right: 'eye-off'
write_tab: False
size_hint_x: None
width: 300
font_size: 18
pos_hint: {'center_x':.5}
password: True
on_text_validate: root.verify_pass()
Widget:
size_hint_y: None
height: 20
MDFillRoundFlatButton:
text: 'Create Account'
pos_hint: {'center_x': .5}
theme_text_color: "Custom"
md_bg_color: 35/255, 83/255, 161/255, 1
text_color: 0, 0, 0, 1
on_press: root.verify_pass()
MDFillRoundFlatButton:
text: 'Return'
pos_hint: {'center_x': .5}
theme_text_color: "Custom"
md_bg_color: 35/255, 83/255, 161/255, 1
text_color: 0, 0, 0, 1
on_press:
root.manager.current = 'login'
root.manager.transition.direction = 'up'
Widget:
#TODO: Add textinput to allow user to specify what site this is tied to
# Make this a global variable that can be accessed on the next page?
# Need to make function that pulls generated password and site name, creates new list item with that information
<PasswordScreen>:
canvas:
Rectangle:
pos: self.pos
size: self.size
source: 'C:\\Users\\jaimi\\Downloads\\star.jpg'
MDCard:
border_radius: 20
radius: [15]
id: card
size_hint: None, None
size: 700, 575
pos_hint: {'center_x':.5, 'center_y': .5}
orientation: 'vertical'
MDBoxLayout:
orientation: 'vertical'
type_height: 'small'
MDTopAppBar:
md_bg_color: (35/255, 83/255, 161/255, 1)
specific_text_color: (1, 1, 1, 1)
title: 'Welcome to your password manager' #Maybe include in title the name of the user?
MDBoxLayout:
spacing: 10
orientation: 'vertical'
MDLabel:
font_name: 'Arialbd'
text: "Generated Password Below"
halign: 'center'
font_size: 18
MDTextFieldRect:
id: generated
size_hint: None, None
size: 300, 40
pos_hint: {'center_x': .5}
normal_color: app.theme_cls.accent_color
hint_text: 'Max Length 25 characters'
MDBoxLayout:
orientation: 'horizontal'
#Grab length from user
Widget:
size_hint_x: None
width: 40
MDLabel:
text: 'Enter desired length'
Widget:
#Spacing
MDLabel:
text: 'Enter ID for this password'
Widget:
size_hint_x: None
width: 10
MDBoxLayout:
orientation: 'horizontal'
Widget:
size_hint_x: None
width: 60
MDTextFieldRect:
id: length
size_hint: None, None
width: 100
pos_hint: {'center_x': .2}
height: '30dp'
normal_color: app.theme_cls.accent_color
required: True
Widget:
#Spacing
MDTextFieldRect:
id: ID
size_hint: None, None
width: 100
pos_hint: {'center_x': .7}
normal_color: app.theme_cls.accent_color
height: '30dp'
required: True
Widget:
size_hint_x:None
width: 80
#Below we get to the textboxes for the desired # of each char type
MDBoxLayout:
orientation: 'horizontal'
spacing: 0
size: card.width, card.height
MDLabel:
text: 'How many characters to include?'
halign: 'center'
MDTextField:
id: pass_letter
text_hint: "Enter # chars desired"
normal_color: app.theme_cls.accent_color
size_hint_x: None
width: 100
Widget:
MDBoxLayout:
orientation: 'horizontal'
size: card.width, card.height
MDLabel:
text: 'How many digits to include?'
halign: 'center'
MDTextField:
id: pass_number
text_hint: "Enter # digits desired"
normal_color: app.theme_cls.accent_color
size_hint_x: None
width: 100
Widget:
MDBoxLayout:
orientation: 'horizontal'
size: card.width, card.height
MDLabel:
text: 'How many specials to include?'
halign: 'center'
MDTextField:
id: pass_special
text_hint: "Enter # specials desired"
normal_color: app.theme_cls.accent_color
size_hint_x: None
width: 100
Widget:
size_hint_x: None
width: 160
MDFillRoundFlatButton:
text: "Save Password"
theme_text_color: "Custom"
md_bg_color: 35/255, 83/255, 161/255, 1
text_color: 0, 0, 0, 1
on_press: root.save()
Widget:
size_hint_x: None
width: 20
MDBoxLayout:
orientation: 'horizontal'
size: root.width, root.height
Widget:
size_hint_x: None
width: 15
MDFillRoundFlatButton:
text: "Log Out"
theme_text_color: "Custom"
md_bg_color: 35/255, 83/255, 161/255, 1
text_color: 0, 0, 0, 1
on_press:
root.clear()
root.manager.current = 'login'
root.manager.transition.direction = 'right'
Widget:
size_hint_x: None
width: 200
MDFillRoundFlatButton:
text: "Generate"
theme_text_color: "Custom"
md_bg_color: 35/255, 83/255, 161/255, 1
text_color: 0, 0, 0, 1
on_press: root.buildPassword()
Widget:
#Spacing
MDFillRoundFlatButton:
text: "View Saved Passwords"
theme_text_color: "Custom"
md_bg_color: 35/255, 83/255, 161/255, 1
text_color: 0, 0, 0, 1
on_press:
root.manager.current = 'save'
root.manager.transition.direction = 'left'
Widget:
size_hint_x: None
width: 15
Widget:
size_hint_y: None
height: 5
<Saved>:
canvas:
Rectangle:
pos: self.pos
size: self.size
source: 'C:\\Users\\jaimi\\Downloads\\star.jpg'
MDCard:
size_hint: None, None
border_radius: 20
radius: [15]
size: 500, 500
elevation: 100
pos_hint: {'center_x':.5, 'center_y':.5}
orientation: 'vertical'
MDTopAppBar:
title: 'Saved Passwords'
type_height: 'small'
md_bg_color: (35/255, 83/255, 161/255, 1)
specific_text_color: (1, 1, 1, 1)
MDBoxLayout:
orientation: 'vertical'
ScrollView:
MDList:
id: main
MDBoxLayout:
size_hint_y: None
height: 140
orientation: 'horizontal'
Widget:
size_hint_x: None
width: 15
MDFillRoundFlatButton:
text: "Generate Another"
theme_text_color: "Custom"
md_bg_color: 35/255, 83/255, 161/255, 1
text_color: 0, 0, 0, 1
on_press:
root.manager.transition.direction = 'right'
root.manager.current = 'pass'
Widget:
#Spacing
MDFillRoundFlatButton:
text: "Exit"
theme_text_color: "Custom"
md_bg_color: 35/255, 83/255, 161/255, 1
text_color: 0, 0, 0, 1
on_press: app.stop()
Widget:
size_hint_x: None
width: 15
Widget:
size_hint_y:None
height: 8