as you can see in this code I have imported my pyqt5 .ui file and I take inputs from users. then when I want to insert it inside the table that I have created using sqlite3, I want the email address to be unique and for that, I wanna check all the rows and find where the email address equals the input I got from the user. if yes then it will show a message. But the code is not working. conn.execute("select * from users where email=?", str(email)), this the part where I wanna check if I have the same email address inside the database or not and the problem is here it ain't working.
def signup(self):
conn = sqlite3.connect("inventory.bd")
conn.execute('''CREATE TABLE IF NOT EXISTS users(
id integer primary key AUTOINCREMENT,
username text not null,
email text not null unique,
password text not null
)''')
username = self.lineEdit.text()
email = self.lineEdit_3.text()
password = self.lineEdit_2.text()
confirm_pass = self.lineEdit_4.text()
res = conn.execute("select * from users where email=?", str(email))
if username == "" or email == "" or password == "":
QMessageBox.about(Dialog,"warning","Please make sure that you have filled all the information")
#QMessageBox.setStandardButtons(QMessageBox.Ok | QMessageBox.Cancel)
elif (len(res.fetchall())>0):
QMessageBox.about(Dialog, "warning", "The email you have entered has already been used")
elif password != confirm_pass:
QMessageBox.about(Dialog, "warning", "The password does not match!")
else:
conn.execute("insert into users(username,email,password) values(?, ?, ?)", (username, email, password))
conn.commit()
conn.close()
QMessageBox.about(Dialog,'welcome','Your account has been created successfully')
Instead of :
res = conn.execute("select * from users where email=?", str(email))
Try this style of formatting :
res = conn.execute("select * from users where email=%s", email)
Works very well with database queries....
Theory :
Python uses C-style string formatting to create new, formatted strings. The "%" operator is used to format a set of variables enclosed in a "tuple" (a fixed size list), together with a format string, which contains normal text together with "argument specifiers", special symbols like "%s" and "%d".
Here are some basic argument specifiers you should know:
%s - String (or any object with a string representation, like numbers)
%d - Integers
%f - Floating point numbers
Related
I am using Python and MySql to handle user authentication. I have added the users thru python with the exact same method, but when I try to do the "login"/authentication it does not match.
This is my code for authentication:
# Collecting data from users.
username=data['username']
password=data['password']
cur=mysql.connection.cursor()
# Checks if username exists.
check_username = cur.execute("SELECT * FROM `users` WHERE `username`=%s",[username])
if check_username > 0:
# Retrieves the correct salt of the user.
cur.execute("SELECT `salt` FROM `users` WHERE `username`=%s",[username])
get_salt = cur.fetchone()
# Generates the hashed password of the user.
hashed_password=(hashlib.sha256((str(password[0:2])+str(get_salt[0])+str(password[2:])).encode('utf-8')).hexdigest()).lower()
# Retrieves the hashed password of the user from the database.
cur.execute("SELECT `password` FROM `users` WHERE `username`=%s",[username])
get_password = cur.fetchone()
# Checks if identical.
if get_password[0] == hashed_password:
return jsonify("Authentication successful!"),201
else: return jsonify("Authentication failed!"),401
else: return 'Incorrect username. Please try again.'
The hashed_password does not return the same hashed password that is stored in the database.
And this is the code I used to insert the users.
username=data['username']
password=data['password']
salt=data['salt']
cur=mysql.connection.cursor()
# Generates the hashed password of the user.
hashed_password=(hashlib.sha256((str(password[0:2])+str(salt[0])+str(password[2:])).encode('utf-8')).hexdigest()).lower()
# Adds user to database.
add_user = cur.execute(" INSERT INTO `users` (`username`, `password`, `salt`) VALUES (%s, %s, %s);",[username,hashed_password, salt])
Does anyone see what is causing this?
Looking at the insertion code, you seem to treat salt like the get_salt tuple, get first item, not knowing what it is originally, that might be the source of your issues as I would not expect the first salt you get to be in a tuple.
Here is a version that works, it's using SQLite rather than MySQL, but the changes are minimal besides formatting.
Also, I recommend you use hashlib.hash.update() rather than your large and complicated one step hashing. And by default hexdigest is lowercase, so no need to .lower() it.
# db setup
import hashlib
import sqlite3
con = sqlite3.connect(":memory:")
cur = con.cursor()
cur.execute("CREATE TABLE users (username TEXT, salt TEXT, password TEXT)")
in_username = "ljmc"
in_salt = "verysecuresalt"
in_password = "p4assW0rD1!"
h = hashlib.sha256()
h.update(str(in_password[:2]).encode("utf-8"))
h.update(str(in_salt).encode("utf-8")) # here salt[0] is just b"v" not the whole salt
h.update(str(in_password[2:]).encode("utf-8"))
in_hashed_password = h.hexdigest()
cur.execute(
"INSERT INTO users VALUES (?, ?, ?)",
(in_username, in_salt, in_hashed_password), # then store whole salt
)
con.commit()
# user check
username = in_username
password = good_password
# Checks if username exists.
check_username = cur.execute(
"SELECT COUNT(*) FROM users WHERE username = ?", [username]
).fetchone()
if check_username[0] > 0:
# Retrieves the correct salt of the user.
cur.execute("SELECT salt FROM users WHERE username=?", [username])
get_salt = cur.fetchone() # get the whole salt
# Generates the hashed password of the user.
hashed_password = (
hashlib.sha256(
(str(password[0:2]) + str(get_salt[0]) + str(password[2:])).encode(
"utf-8"
)
).hexdigest()
).lower() # use the whole salt
# Retrieves the hashed password of the user from the database.
cur.execute("SELECT password FROM users WHERE username=?", [username])
get_password = cur.fetchone()
# Checks if identical.
if get_password[0] == hashed_password:
print("Authentication successful!")
else:
print("Authentication failed!")
As pointed out by other comments, it would also be a lot better to fetch all the user data in one query and then apply the logic, so the check block would look like this.
cur.execute(
"SELECT username, salt, password FROM users WHERE username = ?",
(username,),
)
if user := cur.fetchone():
out_username, out_salt, out_password = user
h = hashlib.sha256()
h.update(str(password[:2]).encode("utf-8"))
h.update(str(out_salt).encode("utf-8"))
h.update(str(password[2:]).encode("utf-8"))
print(f"is the password {password} ?", out_password == h.hexdigest())
I'm trying to create a contact book as a personal project. In the 'find_contact()' function, when I use the 'emaiL' variable to perform a query, it error message says that the data (I pre-recorded in the table) doesn't exist. But when I changed the variable to be used to query to 'phoneNum' (which is a number in text form) the query worked. How do I go about this please?
import sqlite3
conn = sqlite3.connect('contactBook.db')
cur = conn.cursor()
records = cur.fetchall()
#create table
cur.execute("""CREATE TABLE IF NOT EXISTS contacts (
first_name TEXT NOT NULL,
last_name TEXT,
phone_number TEXT NOT NULL PRIMARY KEY,
email,
address TEXT,
UNIQUE(phone_number, email)
)""")
#conn.close()
def save_contact():
save_contact.firstName = input("First name of contact: ")
lastName = input("Last name of contact: ")
phoneNumber = input("Phone number of contact: ")
email_ = input("Email of contact: ")
address_ = input("Address of contact: ")
cur.execute("INSERT OR IGNORE INTO contacts (first_name, last_name,phone_number,
email,address) VALUES (?, ?, ?, ?, ?)",
(save_contact.firstName, lastName, phoneNumber, email_, address_))
conn.commit()
def find_contact():
emaiL = input('Enter email: ')
query = f'''SELECT * FROM contacts WHERE email = {emaiL}'''
lua = f'''SELECT first_name, phone_number FROM contacts WHERE email = {emaiL}'''
#cur.execute("SELECT * FROM contacts (email) VALUES (?)", (email,))
cur.execute(query)
#conn.commit()
print(cur.execute(lua))
req = input("Hello, would you like to save or search for a contact: ")
if str.lower(req) == 'save':
save_contact()
x = save_contact.firstName
print("You have successfully saved " + x + "'s details")
elif str.lower(req) == 'search':
find_contact()
The test run was:
Hello, would you like to save, search or update for a contact:
search
Enter email: mine
The traceback:
Traceback (most recent call last):
File "c:\Users\GLORIA\Desktop\MINE\db.py", line 60, in <module>
find_contact()
File "c:\Users\GLORIA\Desktop\MINE\db.py", line 33, in
find_contact
cur.execute(query)
sqlite3.OperationalError: no such column: mine
query = f'''SELECT * FROM contacts WHERE email = {emaiL}'''
If the value of the variable emaiL is the string 'mine', this creates the SQL statement
SELECT * FROM contacts WHERE email = mine
but in this statement, mine would be interpreted by SQLite as a column name. If you want it to be interpreted as a string, quotes would need to be added:
SELECT * FROM contacts WHERE email = "mine"
However, don't try to adjust the string formatting line, query = f'''...''' to add the quotes, instead use a parameterized statement with ? as a placeholder, like you did for the other SQL statements in your code.
query = 'SELECT * FROM contacts WHERE email = ?'
cur.execute(query, (emaiL,))
See How to use variables in SQL statement in Python? for reasons to do so.
In the query query = f'''SELECT * FROM contacts WHERE last_name = {emaiL}''' is it not supposed to be f'''SELECT * FROM contacts WHERE email = {emaiL}'''?
It's probably because your {emaiL} doesn't have quotes for the email in the query, like this:
f'''SELECT * FROM contacts WHERE email = '{emaiL}' '''
If you print out your current query variable, you'll get SELECT * FROM contacts WHERE email = mine, which isn't valid. You want to get the string SELECT * FROM contacts WHERE email = 'mine'.
I am trying to get the rowid of a username in sqlite3, i have got the basics of it but when ever i run it i get somthing like 'sqlite3.Cursor object at 0x03885660' and it changes every time i run it with the same username. I know it is because i print rowid but i cant find an alternative way.
here is my code:
def sign_in():
username = input("What is your username?")
password = input("What is your password?")
c.execute("SELECT username FROM stuffToPlot")
names = {name[0] for name in c.fetchall()}
if username in names:
rowid = c.execute("SELECT rowid, * FROM stuffToPlot WHERE username = (username)")
print(rowid)
You got to the point where your cursor executes that query, but then you need to tell it what to return from it. Return the first match with that query? return every record? Fetch the data depending on what you need. You can use fetchone(), fetchall() or many other ways to get it.
if username in names:
c.execute("SELECT rowid, * FROM stuffToPlot WHERE username = (username)")
rowid = c.fetchone()
print(rowid)
Guys I'm using sqlite3 with python tkinter as front end. The database is a simple one with two fields, username and password. I want to make a registration sign up page. where data given in the two fields to be stored in a sqlite3 database. Data is inserting properly. But I want to display a messagebox when the username provided already exist in the database. I tried the below code.
MY CODE:
def signup():
userID = username.get()
passwd = password.get()
conn = sqlite3.connect('test.db')
c = conn.cursor()
result = c.execute("SELECT * FROM userstable")
for i in result:
if i[0] == userID:
messagebox.showerror("DUPLICATE", "USER ALREADY EXISTS!")
else:
conn = sqlite3.connect('test.db')
c = conn.cursor()
c.execute("INSERT INTO userstable VALUES (?, ?)", (userID, passwd))
conn.commit()
c.close()
conn.close()
username.delete(0,END)
password.delete(0,END)
username.focus()
messagebox.showinfo("SUCCESS", "USER CREATED SUCCESSFULLY")
This works but still the duplicate data is being stored after the error message. My requirement is to throw the error and stop executing if the username is already available. If the username is not available already it should insert the data.
Where am I going wrong? could some one explain me by pointing out or is there any other way I can achieve this? It seems I need to modify something to my function. Please guide me.
EDIT 1
If i try to use three conditions the break is not working.
MY CODE
def data_entry():
conn = sqlite3.connect('test.db')
c = conn.cursor()
c.execute('CREATE TABLE IF NOT EXISTS userstable(username TEXT, password TEXT)')
username = uname.get()
password = passwd.get()
result = c.execute("SELECT * FROM userstable")
if username != '' or password != '':
for i in result:
if i[0] == username:
tkinter.messagebox.showerror("DUPLICATE", "USER ALREADY EXISTS!")
break
else:
c.execute('INSERT INTO userstable (username, password) VALUES(?, ?)',(username,password))
conn.commit()
c.close()
conn.close()
another_clear()
tkinter.messagebox.showinfo("Success", "User Created Successfully,\nPlease restart application.")
else:
tkinter.messagebox.showerror("ERROR", "Fill both fields!")
A better way to approach this would be to create a unique constraint (index, in sqlite) on the table to prevent the insertion of a duplicate username. That way, you can try/except the insert statement instead of looping through a list of all users to see if it already exists (that's not scalable). This would also prevent you from having to "select *", which is generally bad practice (try to be explicit).
https://sqlite.org/lang_createtable.html
So, you could add the constraint either as a unique index, or as a primary key. If you only have 2 columns in this table, or if you have more than 2 but no additional IDs, your username can be your primary key. If you are introducing a system ID for your users, I'd use that as your primary key and username as a unique index. Either way, you'll need to alter your table to add the constraint.
CREATE UNIQUE INDEX username_uidx ON userstable (username);
Again, because you're not explicitly letting us know the column names, you'll have to fill that in.
After that:
try:
conn = sqlite3.connect('test.db')
c = conn.cursor()
c.execute("INSERT INTO userstable VALUES (?, ?)", (userID, passwd))
conn.commit()
except: # I'm not sure the exact error that's raised by SQLite
messagebox.showerror("DUPLICATE", "USER ALREADY EXISTS!")
finally:
c.close()
conn.close()
I typically wrap my cursor and connections in a finally so that they close even if there's an exception. That's not 100% of what you need, but it should get you there in one step with better DB design to enforce the uniqueness on a user.
I advise against using a loop with an else statement, it can be confusing.
See this post why-does-python-use-else-after-for-and-while-loops for more info.
If you want to use for - else you can add a break, so else will not be executed :
for i in result:
if i[0] == userID:
messagebox.showerror("DUPLICATE", "USER ALREADY EXISTS!")
break
else:
...
Or you can use a true / false flag :
user_exists = False
for i in result:
if i[0] == userID:
messagebox.showerror("DUPLICATE", "USER ALREADY EXISTS!")
user_exists = True
if not user_exists :
...
The aim of this code is to validate a password that is entered using the passwords and usernames stored in a database. The table contains username (nick) and password, passwords are encrypted using db.crypt.
My code at the moment consists of:
def check_login(db, username, password):
"""returns True if password matches stored"""
cursor = db.cursor()
pass1 = db.crypt(password)
cursor.execute("SELECT password FROM users WHERE nick=?", (username,))
passcheck = cursor.fetchone()
if passcheck == pass1:
return True
else:
return False
But i keep getting an assertion error when running a unit test:
line 29, in test_check_login
self.assertTrue(users.check_login(self.db, username, password), "Password check failed for username%s" % username)
AssertionError: False is not true : Password check failed for nick jake
When I use print to show what passcheck is retrieving it prints the correct encrypted pass word but inside of ('') tags (as a tuple, I believe). But when I print cursor.fetchone() it says None. I'm unsure what is going wrong here, I encrypted the password being sent into the function so it can be correctly matched to that of the password stored in the DB. I assume passcheck would retrieve the password that corresponds to the user nick, that is passed through the function.
Any and all help is much appreciated.
cursor.fetchone()
fetches a row as a tuple:
passcheck == ("hashed password", )
You must compare agains passcheck[0] or unpack the tuple:
passcheck, = cursor.fetchone()
Edit: Let the database do the comparing:
cursor.execute("SELECT * FROM users WHERE username= ? and password= ?",
(username, pass1))
found = cursor.fetchone()
if found:
# user exists and password matches
else:
# user does not exist or password does not match