1
00:00:01,330 --> 00:00:02,910
Hi guys and welcome back.

2
00:00:02,910 --> 00:00:04,580
In this video, we're going to start learning

3
00:00:04,580 --> 00:00:06,320
about decorators.

4
00:00:06,320 --> 00:00:08,210
Decorators are great in Python

5
00:00:08,210 --> 00:00:12,390
because they allow us to very easily modify functions.

6
00:00:12,390 --> 00:00:13,940
So let's learn more about them.

7
00:00:15,090 --> 00:00:18,490
Here we've got a user dictionary that has a username and

8
00:00:18,490 --> 00:00:20,890
an access level of guest.

9
00:00:20,890 --> 00:00:24,167
Let's say that guests can't access this function here

10
00:00:24,167 --> 00:00:25,350
get_admin_password

11
00:00:25,350 --> 00:00:28,310
which returns the password to your admin panel.

12
00:00:28,310 --> 00:00:29,143
But as you can see,

13
00:00:29,143 --> 00:00:31,230
if we print out get_admin_password

14
00:00:31,230 --> 00:00:32,960
at the moment, even though we're a guest

15
00:00:32,960 --> 00:00:35,210
we're gonna get out 1234.

16
00:00:35,210 --> 00:00:36,310
There's nothing in this function

17
00:00:36,310 --> 00:00:37,933
to prevent us from doing that.

18
00:00:38,910 --> 00:00:42,360
So, the output of this video should be

19
00:00:43,280 --> 00:00:46,430
to get this function secured so that

20
00:00:46,430 --> 00:00:50,093
people that are guests cannot get the value out.

21
00:00:51,270 --> 00:00:54,380
Now of course, the clear thing that you can do

22
00:00:54,380 --> 00:00:56,320
is to put an if statement in here.

23
00:00:56,320 --> 00:01:01,320
If the user's access level is == to admin,

24
00:01:01,460 --> 00:01:03,970
then you can print out this function.

25
00:01:03,970 --> 00:01:05,550
Otherwise, you can't.

26
00:01:05,550 --> 00:01:06,424
So this will work

27
00:01:06,424 --> 00:01:09,140
if you run that, you don't get anything,

28
00:01:09,140 --> 00:01:10,360
but of course as you know,

29
00:01:10,360 --> 00:01:13,810
get_admin_password is still un-secure

30
00:01:13,810 --> 00:01:15,830
so you can still print that out

31
00:01:15,830 --> 00:01:17,650
and it'll still work.

32
00:01:17,650 --> 00:01:18,860
Because what we've done is

33
00:01:18,860 --> 00:01:23,860
we have protected this specific call to get_admin_password,

34
00:01:23,900 --> 00:01:27,290
but we haven't protected this one.

35
00:01:27,290 --> 00:01:30,383
So, this is probably not exactly what we want to do.

36
00:01:31,760 --> 00:01:32,593
The next thing we can do

37
00:01:32,593 --> 00:01:35,610
is we can define a secure function.

38
00:01:35,610 --> 00:01:37,863
For example, secure_get_admin.

39
00:01:38,926 --> 00:01:41,810
And in this one, if the user's access level is admin

40
00:01:44,240 --> 00:01:47,420
then we can return the password 1234.

41
00:01:47,420 --> 00:01:49,040
Now, a couple of things.

42
00:01:49,040 --> 00:01:51,630
Clearly get_admin_password is still defined,

43
00:01:51,630 --> 00:01:53,810
so that is not going to work,

44
00:01:53,810 --> 00:01:58,760
but secure_get_admin does protect the password.

45
00:01:58,760 --> 00:02:01,790
Running that gives you None, first of all

46
00:02:01,790 --> 00:02:03,230
when you run secure_get_admin

47
00:02:03,230 --> 00:02:05,350
because we didn't return 1234.

48
00:02:05,350 --> 00:02:07,600
Instead, we returned None which is a default.

49
00:02:07,600 --> 00:02:09,743
Then you get 1234 the next time around.

50
00:02:10,920 --> 00:02:12,580
So of course, what you may want to do

51
00:02:12,580 --> 00:02:14,719
is delete this one entirely.

52
00:02:14,719 --> 00:02:17,633
But, that poses a small problem.

53
00:02:17,633 --> 00:02:20,960
Which is that all the functions

54
00:02:20,960 --> 00:02:22,560
where you want it to be secure,

55
00:02:22,560 --> 00:02:25,120
you're going to have to add this if statement.

56
00:02:25,120 --> 00:02:27,750
And up 'til now, that is what you would have done

57
00:02:27,750 --> 00:02:29,760
if you didn't know about decorators.

58
00:02:29,760 --> 00:02:33,571
But, a decorator will allow us to modify this function

59
00:02:33,571 --> 00:02:36,620
to secure it without having to replace

60
00:02:36,620 --> 00:02:40,800
all of our functions by its secure counterparts.

61
00:02:40,800 --> 00:02:42,943
So, we don't want to do that either.

62
00:02:44,960 --> 00:02:46,050
Instead what we could do

63
00:02:46,050 --> 00:02:49,880
is define another function, secure_function

64
00:02:49,880 --> 00:02:51,170
that takes in a function.

65
00:02:51,170 --> 00:02:53,990
We've learned about first class functions,

66
00:02:53,990 --> 00:02:56,730
so we can take it in as a function there,

67
00:02:56,730 --> 00:03:01,590
and we'll say if the user's access level is == to admin

68
00:03:03,890 --> 00:03:06,040
then we will return func.

69
00:03:06,040 --> 00:03:07,440
Otherwise we'll return none.

70
00:03:07,440 --> 00:03:09,240
And now what you may be tempted to do

71
00:03:09,240 --> 00:03:10,470
is to do something like

72
00:03:10,470 --> 00:03:13,877
get_admin_password = secure function(get_admin_password).

73
00:03:17,850 --> 00:03:19,363
So, will this work?

74
00:03:20,480 --> 00:03:23,030
Well, let's run through the code.

75
00:03:23,030 --> 00:03:25,040
We've got secure_function being called

76
00:03:25,040 --> 00:03:26,970
which takes in get_admin_password,

77
00:03:26,970 --> 00:03:29,110
and when this line runs,

78
00:03:29,110 --> 00:03:31,680
we're going to check the user's access level

79
00:03:31,680 --> 00:03:32,860
and see if it's admin.

80
00:03:32,860 --> 00:03:35,793
And if it is, we're going to return func.

81
00:03:37,685 --> 00:03:39,035
So, let's see what happens.

82
00:03:41,310 --> 00:03:43,010
Now you get an error.

83
00:03:43,010 --> 00:03:45,560
NoneType object is not callable.

84
00:03:45,560 --> 00:03:48,700
Because of course, when we ran this code

85
00:03:48,700 --> 00:03:50,140
i.e., this line here,

86
00:03:50,140 --> 00:03:52,930
the user's access level was guest not admin.

87
00:03:52,930 --> 00:03:54,520
So we didn't return func,

88
00:03:54,520 --> 00:03:56,450
we returned none which is the default.

89
00:03:56,450 --> 00:03:59,270
So get_admin_password = none.

90
00:03:59,270 --> 00:04:02,320
Then we're trying to run none as if it were a function,

91
00:04:02,320 --> 00:04:04,520
but you can't do that so you get an error.

92
00:04:04,520 --> 00:04:05,580
What you would have to do is

93
00:04:05,580 --> 00:04:07,620
make sure that your user is an admin

94
00:04:07,620 --> 00:04:09,780
before you run any of your code.

95
00:04:09,780 --> 00:04:12,800
And then it will work and you will get 1234.

96
00:04:12,800 --> 00:04:16,653
So this is a step closer to what we want,

97
00:04:17,540 --> 00:04:21,100
but alas it requires that our user be an admin

98
00:04:21,100 --> 00:04:24,080
before we secure our functions.

99
00:04:24,080 --> 00:04:26,440
Ideally we would like to do something that checks

100
00:04:26,440 --> 00:04:29,480
the user's access level when you call the function,

101
00:04:29,480 --> 00:04:31,033
not when you define it.

102
00:04:32,900 --> 00:04:34,740
So, here's what we're gonna do.

103
00:04:34,740 --> 00:04:36,790
We're still going to take in a function

104
00:04:36,790 --> 00:04:40,360
but now we're going to define another function,

105
00:04:40,360 --> 00:04:42,270
which I'm going to call secure_function.

106
00:04:42,270 --> 00:04:44,603
This one I'm gonna call make_secure.

107
00:04:45,710 --> 00:04:48,820
This one doesn't take any parameters,

108
00:04:48,820 --> 00:04:50,906
and it is the one that checks.

109
00:04:50,906 --> 00:04:55,700
And, it returns calling the original function.

110
00:04:55,700 --> 00:04:59,310
Then, here we return secure_function.

111
00:04:59,310 --> 00:05:01,360
So this is a decorator.

112
00:05:01,360 --> 00:05:02,550
What happens now, and by the way

113
00:05:02,550 --> 00:05:04,450
this should be make_secure.

114
00:05:04,450 --> 00:05:06,760
What happens now is that get_admin_password,

115
00:05:06,760 --> 00:05:08,933
this function that we want to secure,

116
00:05:10,120 --> 00:05:12,810
is passed to the make_secure function.

117
00:05:12,810 --> 00:05:14,220
This one defines another function,

118
00:05:14,220 --> 00:05:16,070
and by the way, in Python you can define functions

119
00:05:16,070 --> 00:05:18,820
inside a function, so that's totally fine.

120
00:05:18,820 --> 00:05:22,050
And this function here, when called,

121
00:05:22,050 --> 00:05:23,940
will check the user's access level

122
00:05:23,940 --> 00:05:26,350
and return calling the original function

123
00:05:26,350 --> 00:05:27,620
which is get_admin_password.

124
00:05:27,620 --> 00:05:32,340
So it will return 1234 if the user's access level is admin.

125
00:05:32,340 --> 00:05:33,760
So that's this function here,

126
00:05:33,760 --> 00:05:35,680
and then we return the function itself.

127
00:05:35,680 --> 00:05:39,080
Not the function call, but the function itself.

128
00:05:39,080 --> 00:05:43,910
get_admin_password will be equal to this function.

129
00:05:43,910 --> 00:05:47,210
Which calls seemingly itself, but this is okay.

130
00:05:47,210 --> 00:05:49,423
It calls get_admin_password from up here.

131
00:05:50,556 --> 00:05:53,350
So when you call get_admin_password what you're gonna do

132
00:05:53,350 --> 00:05:55,670
is you're going to check the user's access level,

133
00:05:55,670 --> 00:05:58,120
and then you're going to return the result

134
00:05:58,120 --> 00:06:00,250
of calling the function originally,

135
00:06:00,250 --> 00:06:01,863
which will give you 1234.

136
00:06:05,709 --> 00:06:10,080
At the moment you get none, but of course if you

137
00:06:10,080 --> 00:06:13,290
set the user's access level to admin down here

138
00:06:13,290 --> 00:06:14,343
and you run it again,

139
00:06:15,250 --> 00:06:16,510
then you get 1234.

140
00:06:16,510 --> 00:06:17,860
Which is exactly what we wanted.

141
00:06:17,860 --> 00:06:20,800
So this is a simple decorator.

142
00:06:20,800 --> 00:06:23,820
This simple decorator will create a function

143
00:06:23,820 --> 00:06:27,730
and replace the original function with this secure one.

144
00:06:27,730 --> 00:06:31,870
So that you can no longer call get_admin_password

145
00:06:31,870 --> 00:06:34,963
without having the admin access level.

146
00:06:35,910 --> 00:06:38,240
Now if you wanted to add a little bit of error handling

147
00:06:38,240 --> 00:06:39,640
you can do an else here

148
00:06:39,640 --> 00:06:41,197
and you can return something like

149
00:06:41,197 --> 00:06:45,880
"No admin permissions for {user['username']}."

150
00:06:47,600 --> 00:06:50,490
and that'll give you a nicer idea of what's going on

151
00:06:50,490 --> 00:06:52,290
if you don't have admin permissions.

152
00:06:54,330 --> 00:06:55,500
That's everything for this video.

153
00:06:55,500 --> 00:06:57,770
This is a simple decorator,

154
00:06:57,770 --> 00:06:59,910
and now in the next one we're going to learn more

155
00:06:59,910 --> 00:07:02,700
about using the @ syntax for decorators

156
00:07:02,700 --> 00:07:05,520
that makes this much simpler, and much easier

157
00:07:05,520 --> 00:07:07,740
to re-use and secure multiple functions

158
00:07:07,740 --> 00:07:09,500
if that's what you want to do.

159
00:07:09,500 --> 00:07:10,390
Thanks for joining me,

160
00:07:10,390 --> 00:07:12,140
and I'll see you in the next video.

