RESTful and HTTP APIs in Pyramid Transcripts
Chapter: Authenticated services
Lecture: Isolating the API auth features
0:01 As we saw, having all this header parsing, database querying, API at the beginning of every one of our functions is super bad,
0:09 now we could slightly improve this by putting this into a function, right, we could come over here and say extract a method
0:16 and if I grab the right stuff and it didn't really like these return values I could store those values and check it, whatever, right,
0:23 I could make this work no problem. But, we can do better than this, so let's go add a new section of our web app here
0:30 let's go to infrastructure and over in this section, we'll probably end up with a number of other things
0:36 but for now, let's just put a thing we're going to call auth. And that method that we wrote, let's say def parse_api_key,
0:45 so let's break this into two things, request okay, so let's go over here and cut this out for a minute,
0:55 so what do we have, we have auth header, again, spelling matters, so we are going to get the authorization, if not this, we're going to return,
1:03 let's make a copy really quick of this, so we're going to return none, no auth header.
1:11 Actually, I am wondering if we should maybe pass the error message back as well, so let's go over here and say none and error is no auth header.
1:21 Let's go over here and say none, and the reason is invalid here got to import this, this is going to be none and the reason is invalid auth key,
1:32 and then finally, here we're going to return a user,no error so we're going to say given request let's go find the API key here,
1:42 so it has to live in authorization thing, there's none of that obviously we have no user and the error is this, if it's invalid, no user error is this.
1:52 No user, no user and the error is this, otherwise, we found these and there are no errors, so that's how we're going to use this little bit here.
2:00 Now, we don't really need this anymore, the next thing I want to do is I would like to be able to go over here
2:05 and just go something like this, at require auth, API auth, okay. So what we're going to do is build a decorator,
2:15 just a basic decorator that we can wrap on this and it's going to intercept the call here, and it won't even let it get to it,
2:21 if there's no authorization, right, it's going to manage all the stuff that you saw us do, and we go back here and turn this back off,
2:28 because we're not going to need this anymore, instead we're going to have this So, how do we do that? Let's start really simple,
2:36 so decorators are just functions, okay, and decorators are just functions that take a function,
2:41 what function are they going to take, the thing that you put them before
2:44 so in our example here, if I say at this, let's be a little more explicit up here, so let's say this, [typing code] import that as auth, okay,
3:02 so let's make it kind of explicit where it's coming from @ auth.require.api_auth, okay, so it's going to take this function
3:10 and that is a function that takes one thing, a request and it returns a response, so we need to tell this thing to return a function
3:18 which takes a request and returns a response, right, so I am going to come down here and we'll have a def wrapped takes a request
3:28 and it returns some kind of response, and often this will just be by delegating to this inner function,
3:36 this wrapped function here, I am going to need to return wrapped, without calling it, now let's just do a print, before the wrapped func
3:46 and let's go ahead and try to print it here maybe it will tell us what it is func ; okay, so do we have it over here, we do, and restart,
3:57 before the wrap function and here are all autos at this address perfect, now did it work, did it still run— it did, look, down here at the bottom,
4:05 we've got all this stuff, and that part happened right here, this is the actual execution of that API call.
4:15 So this before part came before, then right, here's where we can do that validation so we'll say user error equals parse api key with this request,
4:28 and we'll say if error then return response from pyramid with a status just like before, 403, and the body being the error, like so.
4:45 Now, maybe this car, remember what we had up here is we had a little print statement, print listing cars for
4:54 we give a name here, we said . format( user.name) what is the user, what if we actually need to provide the user,
5:01 not just make sure they can call it or don't call it, so we can do that step right here, before we call the function
5:08 but we have already verified there is a user, we can say request.api_user = user.
5:15 Now, we could plug into the pyramid all stuff and try to make that user but I kind of feel like just making it super explicit like this is the user;
5:24 So let's go over here, you can choose to do that however you want request.api_user.name and this has to be
5:33 again put back request now that we want to use it an the idea is, if this lets the function run there will be a user,
5:40 if there's no user, this function doesn't run, so you don't need to worry about whether or not this is there,
5:45 right, it's going to be there; now let's try this again, this should be awesome, are you ready?
5:50 I'm over here, who is our user, this is the authorization I believe I got it for jeff and first of all, let's not send it, see what happens,
5:56 boom 403 forbidden no auth header, so you could say like maybe a nicer message, like you must specify an authorization header, something like this,
6:12 but whatever the error is, there is no auth header, now if I pass it in, and maybe I get this wrong
6:18 I'll put an extra f on the end, first of all let's mess this up make the api thing wrong, so it's invalid,
6:24 make it valid, now it's valid but the value corresponds to no user, there's no user with that account, take the f off,
6:30 now, so notice, none of this crashed, right, this was never run at all, because the outer shell was guarding it, right
6:42 this required auth API that we created is guarding it now it should pass through that, and on the way through
6:48 it should set the API user to jeff, let's try— boom, so it works fine over in our log files, well ''log files'', in our print
6:57 we have listing cars for jeff, what do you guys think, is that killer or what? So just to review, we come over here, we wrote two functions
7:05 one that knows how to suck this authorization header apart, break it into its pieces and give us the API key,
7:10 and then there's a very very simple decorator that takes basically right here what turns out to be the thing that gets executed,
7:17 wrapping around the API call, it checks for the user and if there's no user, return error, don't ever call the function,
7:25 but if it is good, set the API user and call the function.