Building data-driven web apps with Flask and SQLAlchemy Transcripts
Chapter: User input and HTML forms
Lecture: Creating the user
0:00 Well we've almost finished the registration process
0:02 and our interaction with forms are working really well.
0:04 But, let's go work on this other bit here.
0:07 Let's actually go and create the user.
0:09 So let's do this.
0:10 Let's say the user's going to be our user_service
0:13 remember we're doing lots of stuff with the database
0:15 and users, so we created our user_service over here.
0:19 So we can just import that.
0:22 We're going to add a function, not get_user_count
0:24 but create_user.
0:26 We're going to have a bunch more things as well
0:27 and we can just pass the values: name, email and password.
0:33 Now, this doesn't exist yet
0:34 but, you know what, we can have PyCharm write it for us
0:37 and this can be a string and a string and a string.
0:44 And let's actually tell it here
0:46 that it's going to return not a user
0:48 but an optional user. Why optional?
0:52 Well you'll see in a second.
0:53 But we definitely want to return
0:54 some kind of user here.
0:56 That's just the end of the line, okay.
0:59 Cool, cool, okay.
1:00 So what we need to do here is
1:01 we should also do some validation
1:03 on name and email and so on
1:04 but we're doing that at the top as well.
1:06 Let's just trust it for the moment
1:08 but in a real site we would add that.
1:09 So how do we do this?
1:10 We're going to create a user, like so.
1:15 And we're going to set some values.
1:17 Now, be super, super careful here.
1:20 Do you do this? Maybe? No.
1:24 Bad, bad, don't do that.
1:25 You never, ever, ever want to store
1:28 your password directly in the database.
1:31 It's such a bad idea to store the password in plain text.
1:34 If for some reason you have a SQL injection attack
1:37 or your database gets lost, or whatever
1:40 all sorts of bad things are going to happen.
1:42 So we never, ever, ever want to do this.
1:43 So instead, let me show you how we're going to
1:45 handle passwords in our app.
1:47 So come over here, and let's check out
1:49 this thing called passlib.
1:50 This is one of my favorite of all time modules for Python
1:53 because it just takes this tricky problem
1:56 and makes it super, super nice.
1:58 So the way that it works is this thing will go
2:01 you can import it and it will go and hash up
2:04 some kind of thing like, I don't know, a password.
2:07 And then it will verify it.
2:09 Now, of course you could use MD5 or SHA512 or whatever
2:14 but this does a lot more.
2:15 It also creates random salt, that is like
2:17 words that are mixed in with the password.
2:20 And it folds it many, many times.
2:23 Now what I mean by that, instead of just
2:24 taking the value and hashing it
2:25 it takes that result and hashes it again
2:27 and then it hashes it again and hashes it again
2:29 like 150,000 times.
2:32 So this is a much, much harder to break password
2:35 if for some reason someone were able to access your database
2:38 and get a hold of the emails and the passwords.
2:42 So this is pretty easy for us to use.
2:44 We have to add passlib to our requirements
2:51 down here like so.
2:54 Make sure those get installed.
2:56 So this is all good
2:57 and then up here at the top
2:59 we need to go and import some stuff.
3:00 But we're going to go over here
3:01 and we're going to say
3:07 from passlib.handlers.sha2_crypt import sha512_crypt as crypto
3:08 We're going to import SHA512
3:12 and let's do this as crypto.
3:16 That way when we use it below
3:17 if for some reason we decide we want
3:18 to change our handler or something
3:20 we can do that pretty easily.
3:22 So now I'm just going to drop two functions down here.
3:24 One is to do the hashing and one is to do the verification.
3:28 So we have hash.txt, which takes plain text and encrypts it
3:32 and we're setting the rounds
3:33 to be something kind of random but really high.
3:35 171,204 iterations, that's going to make it
3:39 extremely computationally challenging to break.
3:43 And then we want to go and verify it.
3:45 You can't directly compare these
3:47 because each encryption result results in using random salt
3:51 but once one of these strings exists
3:53 it knows how to verify it.
3:54 So what we're going to do here
3:55 is just a super simple hash.txt
3:57 and give it the password.
3:59 There we go. That's much better.
4:01 And now we're going to need one of these sessions, like so.
4:04 I'm going to say session.commit when we're done
4:07 and we're going to return the user.
4:09 All right, and then before we actually call commit
4:12 we need to do session.add, remember that?
4:14 And that'll add it here.
4:15 Now, this all looks pretty good.
4:17 There's one other issue, we may have a problem here.
4:20 So let's just write one more function
4:21 and you'll see why.
4:23 Def find_user_by_email.
4:32 And it's going to do something super simple
4:33 but it's going to come down here and say
4:36 return session of query.
4:38 And so what we're going to do is find
4:40 and see if somebody's already registered
4:42 with this email address
4:43 because that would be bad to re-register them, right?
4:53 All right, so that's going to just give us the one user back
4:56 and let's say if find_user_by_email
5:01 this email address, return None.
5:05 So we don't want to create the user
5:07 otherwise this user has not registered with us yet
5:09 we're going to create their account
5:11 we're going to set their values, hash their password
5:14 put them in the database, commit it and return it.
5:17 Phew, all right.
5:18 So that seems like a lot, but I think we're good.
5:20 Let's do a quick test, if not user
5:21 that meant we couldn't create it.
5:23 Let's do something like this. Here we go.
5:28 So if for some reason the thing already exists
5:31 we're going to bail, otherwise we'll redirect.
5:33 Let's go and actually do a quick query, look in our database
5:35 and see if this user already exists, or not.
5:39 It shouldn't exist. Go here.
5:44 Say select star from users.
5:47 What your email is, firstname.lastname@example.org.
5:53 We run that, look, zero rows.
5:54 All right, now, now let's go try to run this, here.
5:59 Make sure everything's working, good, good.
6:01 Now let's go try to register again.
6:03 So I'm going to be Michael.
6:09 Right, here we go.
6:10 It should go through, actually let's not
6:12 put the password yet. Some fields are missing.
6:15 Let's go and try to register.
6:17 Boom, it went through. It didn't crash.
6:20 All of these are really good signs.
6:22 Let's go to the database and ask that question again.
6:25 Look at that, user 85 is now registered.
6:28 Michael, michael@talkpython.
6:29 Here's that crazy password.
6:32 Like, that's really not very much
6:34 at all like what I typed, right?
6:36 That's not the letter a.
6:37 Down here we have our logins, our created_date
6:40 we didn't set the profile image, that's all fine.
6:42 Let's do one more test.
6:43 Let's go back and try to register again.
6:45 I'm Jeff. And my email address is that.
6:50 Let's try again. A user with that email already exists.
6:53 Okay, great, I think our forms
6:55 and our user interaction is pretty good.
6:57 At least on the registration level.