Validating captcha in Flask - python

I am creating a captcha just for an exercise. The creation of the captcha images seem fine. But every time I try validating the captcha challenge entered by the user, the validation is done against the next captcha. I am stuck at how to go with this.
Function for creating captcha images- captcha.py
import random
import Image
import ImageFont
import ImageDraw
import ImageFilter
import JpegImagePlugin
import PngImagePlugin
def gen_captcha(text, fnt, fnt_sz, file_name, fmt='JPEG'):
fgcolor = random.randint(0,0xff0000)
bgcolor = fgcolor ^ 0xffffff
font = ImageFont.truetype(fnt,fnt_sz)
dim = font.getsize(text)
im = Image.new('RGB', (dim[0]+5,dim[1]+5), bgcolor)
d = ImageDraw.Draw(im)
x, y = im.size
r = random.randint
for num in range(100):
d.rectangle((r(0,x),r(0,y),r(0,x),r(0,y)),fill=r(0,0xffff00))
d.text((3,3), text, font=font, fill=fgcolor)
im = im.filter(ImageFilter.EDGE_ENHANCE_MORE)
im.save(file_name)
signup function from views.py
#app.route('/signup', methods = ['GET', 'POST'])
def signup():
if g.user is not None and g.user.is_authenticated():
return redirect(url_for('index'))
words = open('app/corncob_caps.txt').readlines()
captcha_word = words[random.randint(1,len(words))]
captcha_filename = ''.join(random.choice(string.ascii_uppercase + string.digits) for x in range(10)) + '.jpg'
captcha.gen_captcha(captcha_word.strip(), 'app/os.ttf', 25, 'app/static/' + captcha_filename + '')
form = SignUpForm(captcha_word)
if form.validate_on_submit() == False:
return render_template('signup.html', form = form, filename = captcha_filename)
else:
user = User(form.email.data, form.password.data)
db.session.add(user)
db.session.commit()
flash('You have successfully signed up.')
flash('You may login now.')
return redirect(url_for('login'))
return render_template('signup.html', form = form, filename = captcha_filename)
I am passing the captcha_word to my form class. The form class is:
class SignUpForm(Form):
email = EmailField('Email Address', validators = [email()])
password = PasswordField('Password', validators = [Required('Please enter a valid password between 8 and 30 characters.'), Length(min = 8, max = 30)])
captcha = TextField('Captcha', validators = [Required('You must enter the challenge captcha.')])
submit = SubmitField('Create Account')
captcha_word = ''
def __init__(self, word, *args, **kwargs):
Form.__init__(self, *args, **kwargs)
self.get_word(word)
def get_word(self, word):
self.captcha_word = word
def validate(self):
if not Form.validate(self):
return False
elif self.captcha_word != self.captcha.data.upper():
print self.captcha_word
print self.captcha.data.upper()
self.captcha.errors.append("Wrong captcha!")
return False
user = self.get_user()
if user:
self.email.errors.append("That email is already taken.")
return False
else:
return True
def get_user(self):
return User.query.filter_by(email = self.email.data.lower()).first()
I inserted the two print statements inside to see why the comparison was coming wrong. The first print showed the next captcha whereas the print self.captcha.data.upper() displayed the user entered data.
I am not sure, but it seems the signup route is being called twice. But I don't know how to fix this. Any ideas?

If you need to use a captcha, you can use the feature that's already built into Flask-WTF and save yourself reinventing the wheel.
If you do want to reinvent the wheel, then the main problem you're having is that you're recreating the captcha when the user submits the form, you have no way to remember and refer to the old value.
So this is how it's working at the moment:
User goes to sign in, you generate a captcha, then because they haven't submitted a form, it shows the sign in form including the captcha picture.
User fills in the form and hits the submit button- this loads the signup view again,creates a new random captcha, then goes down the form submitted
logic path, so when you compare the user captcha data to the current captcha data, it doesn't match.
So you're missing persistence, the captcha you generate the first time round doesn't get held anywhere, so when the user submits you've got no way to refer back to it. So you need to store that captcha word somewhere. You could simply just store that captcha word in the user's session and use that to validate against when you need to, or perhaps sign it with itsdangerous and store it in the form itself as a hidden field?
Code Example:
This just takes your code and adjusts it a little to store the value in the session-- not tested, and can definitely been improved, but should work:
#app.route('/signup', methods = ['GET', 'POST'])
def signup():
if g.user is not None and g.user.is_authenticated():
return redirect(url_for('index'))
if request.method == 'post':
captcha_word = session["captcha"]
else:
words = open('app/corncob_caps.txt').readlines()
captcha_word = words[random.randint(1,len(words))]
session["captcha"] = captcha_word
captcha_filename = ''.join(random.choice(string.ascii_uppercase + string.digits) for x in range(10)) + '.jpg'
captcha.gen_captcha(captcha_word.strip(), 'app/os.ttf', 25, 'app/static/' + captcha_filename + '')
form = SignUpForm(captcha_word)
if form.validate_on_submit() == False:
return render_template('signup.html', form = form, filename = captcha_filename)
else:
user = User(form.email.data, form.password.data)
db.session.add(user)
db.session.commit()
flash('You have successfully signed up.')
flash('You may login now.')
return redirect(url_for('login'))
return render_template('signup.html', form = form, filename = captcha_filename)

Related

how do i use values of if-block outside if-block? [duplicate]

This question already has answers here:
Python: Assign Value if None Exists
(9 answers)
Closed 1 year ago.
I want to access couple of variables from if-block, outside in the function
eg: i want to use signin, user
#app.route('/signup', methods=['GET', 'POST'])
def register():
form1 = Signup()
if form1.validate_on_submit():
signin = auth_py.create_user_with_email_and_password(form1.email_address.data, form1.password.data)
user = auth_py.send_email_verification(signin['idToken']) # for email verification
name = form1.name.data
username = form1.username.data
email_address = form1.email_address.data
return redirect(url_for('home_page'))
if form1.errors != {}: # if no errors occur from validators
for err_msg in form1.errors.values():
print(f'there was an error creating the user {err_msg}')
database(username, name, email_address) # problem is here
return render_template('sign-up.html', form1=form1)
I want to use name, username and email_address from form1.validate_on_submit() inside database() and i don't know how can i do that.
NOTE: there is some code yet to be written in main program so I cannot call database() function inside form1.validate_on_submit()
signin = None;
user = None;
def register():
form1 = Signup()
if form1.validate_on_submit():
signin = auth_py.create_user_with_email_and_password(form1.email_address.data, form1.password.data)
user = auth_py.send_email_verification(signin['idToken']) # for email verification
name = form1.name.data
username = form1.username.data
email_address = form1.email_address.data
return redirect(url_for('home_page'))
if form1.errors != {}:`enter code here`
Define variable outside your if-block, this way you can use the
variables outside the if-block.

python flask wtf form return value from function

Trying to retrieve an int from a function which get the data from a form in wtform in Flask.
form.py
class CoilsSlittingMetric(FlaskForm):
masterCoil = IntegerField('Master Coil Size [mm]', validators=[DataRequired()])
slitOne = IntegerField('First Coil Size [mm]', validators=[DataRequired()])
slitTwo = IntegerField('Second Coil Size [mm]', validators=[DataRequired()])
slitThree = IntegerField('Third Coil Size [mm]', validators=[DataRequired()])
slitFour = IntegerField('Fourth Coil Size [mm]', validators=[DataRequired()])
remember = BooleanField('Remember my current entries?')
submit = SubmitField('Calculate!')
when I run the routes.py without the return a statement it shows the flash message just fine
but when I try to return the (myproblem) integer it returns the following error
TypeError: The view function did not return a valid response. The return type must be a string, dict, tuple,
here is the function
#app.route('/slitter', methods=['GET', 'POST'])
#login_required
def slitter():
form = CoilsSlittingMetric()
if form.validate_on_submit():
myproblem = form.masterCoil.data
flash(myproblem)
flash(f'Calculating {form.masterCoil.data} '
f'and resulting data {form.slitOne.data}, {form.slitTwo.data}, '
f'{form.slitThree.data}, {form.slitFour.data}')
return myproblem
return redirect(url_for('slitter_results'))
return render_template('slitter.html', title="Slitter Calculator - Metric Sizes", form=form)
I sort of understand what is says in the error message but I need to use the integers of each of the values (testing just with one of them to simplify) but I don't know how to do it.
I tried to search for similar issues in here but did find only one solution which did not work for me.
EDIT:
I try to use this value(s) later in another function
#app.route('/slitter_results', methods=['GET', 'POST'])
#login_required
myproblem = slitter()
flash(myproblem)
I think you are looking for something like this:
First, get the value from the form submitted and then pass the value in slitter_results function
#app.route('/slitter', methods=['GET', 'POST'])
#login_required
def slitter():
form = CoilsSlittingMetric()
if form.validate_on_submit():
myproblem = form.masterCoil.data
flash(f'Calculating {form.masterCoil.data} '
f'and resulting data {form.slitOne.data}, {form.slitTwo.data}, '
f'{form.slitThree.data}, {form.slitFour.data}')
return redirect(url_for('slitter_results', param_int=myproblem))
return render_template('slitter.html', title="Slitter Calculator - Metric Sizes", form=form)
slitter_results function will look like this:
#app.route('/slitter_results/<int: param_int>', methods=['GET', 'POST'])
#login_required
def slitter_results(param_int)
myproblem = param_int
flash(myproblem)
return "Yay" #just so you don't get an error, you can render or redirect to anywhere from here!
For more info have a look at Variable Rules and URL Building

Is there a way to return a value from function after redirect() in Flask?

Here I am sharing an example code of what i am trying to achieve.
my_bluprint = Blueprint('index',
__name__,
template_folder=template_dir)
#my_bluprint.route('/print', methods=['GET', 'POST'])
def my_function_2():
print('i was redirected')
if request.method == 'POST':
age = request.form.get('age')
print('AGE: ', age)
return render_template("form2.html")
#my_bluprint.route('/test', methods=['GET', 'POST'])
def my_function():
if request.method == 'POST':
print('my_function')
# do something
name = request.form.get('name')
print('my_name', name)
if name == 'root':
print('name is ', name)
return redirect(url_for("index.my_function_2"))
# age = request.form.get('age')
print('AGE: ',age) # I need input age from my_function_2 here
age_trans = my_module.ageMul(age)
print('TransAGE: ', age_trans)
return render_template("result.html", result=age_trans)
return render_template("form1.html")
I need to render different templates depending on the input from previous template.
On run time my program will use the input from one template to decide which template to redirect next. I tried rendering the templates the way i have used in my example code but I need inputs from previous templates for further processing combined with inputs of all the other templates which were rendered.
EDIT
i am able to redirect to other routes in my code. but i need input from the template i am using in my_function_2() in my_function() after the execution of my_function_2(). which means i need to come back to main route i-e /test with data from my_function_2. I need help in understanding how can I do that
Another Idea
is there a way to render different templates using conditions in same function lets say my_function(). If input from one html form makes the condition true render template A else render template B ? meanwhile retaining the inputs from first template rendered in my_function()? in this way i can have only one route rendering multiple templates ? but i want to keep data from all the templates previously rendered
what would be the good approach of doing this ?
EIDT 2
i tried this
#my_bluprint.route('/test', methods=['GET', 'POST'])
def my_function():
if request.method == 'POST':
#name = request.form.get('name')
session['name'] = request.form['name']
print('my_name', session['name'])
if session['name'] == 'root':
print('name is ', session['name'])
#return redirect(url_for("index.my_function_2"))
return render_template("form2.html")
age = request.form.get('age')
print('AGE ',age)
age_trans = my_module.ageMul(age)
print('TransAGE: ', age_trans)
print(session['name'])
return render_template("result.html", result=age_trans)
return render_template("form1.html")
First run goes smooth till return render_template("form2.html") after the form submitted here control goes back to if request.method == 'POST': and execute it from the start and gives me error here session['name'] = request.form['name'] but i want to the program control to always move forward with the values it got in first run of render_template("form1.html") until the page(url) is refreshed
You can pass it in query param in redirect or store it in session.

Flask-SQLAlchemy changes don't persist

I'm making a web application, where user action can affect some bar graphs (seen in the gif below). Sometimes, changes aren't saved. When I reload the page, the changed bar graphs are shown (indicating that the user's action's were saved). When I reload the page again, sometimes the updated bar graphs and corresponding list are shown. Other times, they are not.
Here's the code for the view:
#app.route('/')
def home():
'''homepage for application'''
# redirect users who go to home page but aren't logged in
if not current_user.is_authenticated:
return redirect(url_for("landing"))
# reset the session
db.session.flush()
# get most recent entered weight
try:
last_weight = WeightEntry.query.filter_by(user_id = current_user.user_id).order_by(WeightEntry.date)[-1]
except IndexError:
return error("No weight recorded. Please contact support.")
return render_template("home.html",
today= Today(current_user.user_id),
foods = [food for food in FoodEntry.query.filter_by(user_id=current_user.user_id).all() if food.is_today() == True],
options = sorted(Food.query.filter(Food.user_id==current_user.user_id).all(), key=lambda x: x.name),
last_weight = last_weight)
I added the db.session.flush() in an attempt to solve the problem, but that didn't work.
The changes (logged foods) are stored here:
#app.route("/log_food", methods=["POST", "GET"])
#login_required
def log_food():
# add foods given by the user
if request.method == "POST":
for food in request.form.getlist("logged_food"):
try:
added_food = Food.query.filter_by(user_id=current_user.user_id, name=food).first()
x = FoodEntry(
food_id = added_food.food_id,
user_id = current_user.user_id)
db.session.add(x)
db.session.commit()
except:
return error("Unable to log food.")
return redirect(url_for("home"))
I'd appreciate any help I can get.
Thanks!
I fixed it by adding db.session.commit() to the page functions.
For example, for the homepage: I did:
#app.route('/')
def home():
'''homepage for application'''
db.session.commit()
...

how can test u=g.user in Flask

Lets say I have flask Application Now I want to test u=g.user.be_friend(user) which make a user friend with eachother if they werene't friend befor But I don't know how I can test g.user and most importantly g.user.be_friend would you please help thanx ?
view.py:
user = Users.query.filter_by(name = name).first()
if user is None:
flash('User %s not found.' % name)
return redirect(url_for('layout.user'))
if user == g.user:
flash('You can\'t Friend yourself!')
return redirect(url_for('layout.user',page=1, sortby='normal'))
u = g.user.be_friend(user)
mytest_app.py:
def test_friend_main(self):
e1 = Users(name='admine1', email='admine2#gmail.com', age=25)
e2 = Users(name='teste2', email='teste2#gmail.com', age=27)
db.session.add_all([e1, e2])
db.session.commit()
with self.client:
self.assertTrue(g.user =='admine1')
e1.be_friend(e2)
response=self.client.get('/friend/admine1', follow_redirects=True)
self.assertTrue('You are now Friend with !',response.data)
You need flask-login module.
from flask.ext.login import current_user
#lm.user_loader # you should have same function
def load_user(user_id):
return User.get_user(user_id)
#flask_app.before_request
def before_request():
g.user = current_user # save current user in request context.
Sadly, but I didn't find any easy way to get current g value in test code. Only hard way with test_request_context. Anyway, you can test current_user, if it passed to template:
def test_something(self)
self.assertEqual(self.get_context_variable('current_user').name, name_should_be)
But at first, you should login:
response = self.client.post('/login', data=dict(name='admine1', email='admine2#gmail.com'))
User model can be improved: class User(UserMixin, db.Model):
Also, in test you shouldn't execute e1.be_friend(e2) directly. You should execute it from your production code.
I have implemented a unit test for the "followers" feature of the Flasky application that I feature in my book. Here is the test, you can probably take some ideas from it:
def test_follows(self):
u1 = User(email='john#example.com', password='cat')
u2 = User(email='susan#example.org', password='dog')
db.session.add(u1)
db.session.add(u2)
db.session.commit()
self.assertFalse(u1.is_following(u2))
self.assertFalse(u1.is_followed_by(u2))
timestamp_before = datetime.utcnow()
u1.follow(u2)
db.session.add(u1)
db.session.commit()
timestamp_after = datetime.utcnow()
self.assertTrue(u1.is_following(u2))
self.assertFalse(u1.is_followed_by(u2))
self.assertTrue(u2.is_followed_by(u1))
self.assertTrue(u1.followed.count() == 2)
self.assertTrue(u2.followers.count() == 2)
f = u1.followed.all()[-1]
self.assertTrue(f.followed == u2)
self.assertTrue(timestamp_before <= f.timestamp <= timestamp_after)
f = u2.followers.all()[-1]
self.assertTrue(f.follower == u1)
u1.unfollow(u2)
db.session.add(u1)
db.session.commit()
self.assertTrue(u1.followed.count() == 1)
self.assertTrue(u2.followers.count() == 1)
self.assertTrue(Follow.query.count() == 2)
u2.follow(u1)
db.session.add(u1)
db.session.add(u2)
db.session.commit()
db.session.delete(u2)
db.session.commit()
self.assertTrue(Follow.query.count() == 1)
When you look at this code, keep in mind that this application makes users followers of themselves, so for example, the follower count when a user just follows another user is two, not one.

Categories