1
1

00:00:01,330  -->  00:00:02,910
<v ->Hi guys and welcome back.</v>
2

2

00:00:02,910  -->  00:00:04,580
In this video, we're going to start learning
3

3

00:00:04,580  -->  00:00:06,320
about decorators.
4

4

00:00:06,320  -->  00:00:08,210
Decorators are great in Python
5

5

00:00:08,210  -->  00:00:12,390
because they allow us to very easily modify functions.
6

6

00:00:12,390  -->  00:00:13,940
So let's learn more about them.
7

7

00:00:15,090  -->  00:00:18,490
Here we've got a user dictionary that has a username and
8

8

00:00:18,490  -->  00:00:20,890
an access level of guest.
9

9

00:00:20,890  -->  00:00:24,167
Let's say that guests can't access this function here
10

10

00:00:24,167  -->  00:00:25,350
get_admin_password
11

11

00:00:25,350  -->  00:00:28,310
which returns the password to your admin panel.
12

12

00:00:28,310  -->  00:00:29,143
But as you can see,
13

13

00:00:29,143  -->  00:00:31,230
if we print out get_admin_password
14

14

00:00:31,230  -->  00:00:32,960
at the moment, even though we're a guest
15

15

00:00:32,960  -->  00:00:35,210
we're gonna get out 1234.
16

16

00:00:35,210  -->  00:00:36,310
There's nothing in this function
17

17

00:00:36,310  -->  00:00:37,933
to prevent us from doing that.
18

18

00:00:38,910  -->  00:00:42,360
So, the output of this video should be
19

19

00:00:43,280  -->  00:00:46,430
to get this function secured so that
20

20

00:00:46,430  -->  00:00:50,093
people that are guests cannot get the value out.
21

21

00:00:51,270  -->  00:00:54,380
Now of course, the clear thing that you can do
22

22

00:00:54,380  -->  00:00:56,320
is to put an if statement in here.
23

23

00:00:56,320  -->  00:01:01,320
If the user's access level is == to admin,
24

24

00:01:01,460  -->  00:01:03,970
then you can print out this function.
25

25

00:01:03,970  -->  00:01:05,550
Otherwise, you can't.
26

26

00:01:05,550  -->  00:01:06,424
So this will work
27

27

00:01:06,424  -->  00:01:09,140
if you run that, you don't get anything,
28

28

00:01:09,140  -->  00:01:10,360
but of course as you know,
29

29

00:01:10,360  -->  00:01:13,810
get_admin_password is still un-secure
30

30

00:01:13,810  -->  00:01:15,830
so you can still print that out
31

31

00:01:15,830  -->  00:01:17,650
and it'll still work.
32

32

00:01:17,650  -->  00:01:18,860
Because what we've done is
33

33

00:01:18,860  -->  00:01:23,860
we have protected this specific call to get_admin_password,
34

34

00:01:23,900  -->  00:01:27,290
but we haven't protected this one.
35

35

00:01:27,290  -->  00:01:30,383
So, this is probably not exactly what we want to do.
36

36

00:01:31,760  -->  00:01:32,593
The next thing we can do
37

37

00:01:32,593  -->  00:01:35,610
is we can define a secure function.
38

38

00:01:35,610  -->  00:01:37,863
For example, secure_get_admin.
39

39

00:01:38,926  -->  00:01:41,810
And in this one, if the user's access level is admin
40

40

00:01:44,240  -->  00:01:47,420
then we can return the password 1234.
41

41

00:01:47,420  -->  00:01:49,040
Now, a couple of things.
42

42

00:01:49,040  -->  00:01:51,630
Clearly get_admin_password is still defined,
43

43

00:01:51,630  -->  00:01:53,810
so that is not going to work,
44

44

00:01:53,810  -->  00:01:58,760
but secure_get_admin does protect the password.
45

45

00:01:58,760  -->  00:02:01,790
Running that gives you None, first of all
46

46

00:02:01,790  -->  00:02:03,230
when you run secure_get_admin
47

47

00:02:03,230  -->  00:02:05,350
because we didn't return 1234.
48

48

00:02:05,350  -->  00:02:07,600
Instead, we returned None which is a default.
49

49

00:02:07,600  -->  00:02:09,743
Then you get 1234 the next time around.
50

50

00:02:10,920  -->  00:02:12,580
So of course, what you may want to do
51

51

00:02:12,580  -->  00:02:14,719
is delete this one entirely.
52

52

00:02:14,719  -->  00:02:17,633
But, that poses a small problem.
53

53

00:02:17,633  -->  00:02:20,960
Which is that all the functions
54

54

00:02:20,960  -->  00:02:22,560
where you want it to be secure,
55

55

00:02:22,560  -->  00:02:25,120
you're going to have to add this if statement.
56

56

00:02:25,120  -->  00:02:27,750
And up 'til now, that is what you would have done
57

57

00:02:27,750  -->  00:02:29,760
if you didn't know about decorators.
58

58

00:02:29,760  -->  00:02:33,571
But, a decorator will allow us to modify this function
59

59

00:02:33,571  -->  00:02:36,620
to secure it without having to replace
60

60

00:02:36,620  -->  00:02:40,800
all of our functions by its secure counterparts.
61

61

00:02:40,800  -->  00:02:42,943
So, we don't want to do that either.
62

62

00:02:44,960  -->  00:02:46,050
Instead what we could do
63

63

00:02:46,050  -->  00:02:49,880
is define another function, secure_function
64

64

00:02:49,880  -->  00:02:51,170
that takes in a function.
65

65

00:02:51,170  -->  00:02:53,990
We've learned about first class functions,
66

66

00:02:53,990  -->  00:02:56,730
so we can take it in as a function there,
67

67

00:02:56,730  -->  00:03:01,590
and we'll say if the user's access level is == to admin
68

68

00:03:03,890  -->  00:03:06,040
then we will return func.
69

69

00:03:06,040  -->  00:03:07,440
Otherwise we'll return none.
70

70

00:03:07,440  -->  00:03:09,240
And now what you may be tempted to do
71

71

00:03:09,240  -->  00:03:10,470
is to do something like
72

72

00:03:10,470  -->  00:03:13,877
get_admin_password = secure function(get_admin_password).
73

73

00:03:17,850  -->  00:03:19,363
So, will this work?
74

74

00:03:20,480  -->  00:03:23,030
Well, let's run through the code.
75

75

00:03:23,030  -->  00:03:25,040
We've got secure_function being called
76

76

00:03:25,040  -->  00:03:26,970
which takes in get_admin_password,
77

77

00:03:26,970  -->  00:03:29,110
and when this line runs,
78

78

00:03:29,110  -->  00:03:31,680
we're going to check the user's access level
79

79

00:03:31,680  -->  00:03:32,860
and see if it's admin.
80

80

00:03:32,860  -->  00:03:35,793
And if it is, we're going to return func.
81

81

00:03:37,685  -->  00:03:39,035
So, let's see what happens.
82

82

00:03:41,310  -->  00:03:43,010
Now you get an error.
83

83

00:03:43,010  -->  00:03:45,560
NoneType object is not callable.
84

84

00:03:45,560  -->  00:03:48,700
Because of course, when we ran this code
85

85

00:03:48,700  -->  00:03:50,140
i.e., this line here,
86

86

00:03:50,140  -->  00:03:52,930
the user's access level was guest not admin.
87

87

00:03:52,930  -->  00:03:54,520
So we didn't return func,
88

88

00:03:54,520  -->  00:03:56,450
we returned none which is the default.
89

89

00:03:56,450  -->  00:03:59,270
So get_admin_password = none.
90

90

00:03:59,270  -->  00:04:02,320
Then we're trying to run none as if it were a function,
91

91

00:04:02,320  -->  00:04:04,520
but you can't do that so you get an error.
92

92

00:04:04,520  -->  00:04:05,580
What you would have to do is
93

93

00:04:05,580  -->  00:04:07,620
make sure that your user is an admin
94

94

00:04:07,620  -->  00:04:09,780
before you run any of your code.
95

95

00:04:09,780  -->  00:04:12,800
And then it will work and you will get 1234.
96

96

00:04:12,800  -->  00:04:16,653
So this is a step closer to what we want,
97

97

00:04:17,540  -->  00:04:21,100
but alas it requires that our user be an admin
98

98

00:04:21,100  -->  00:04:24,080
before we secure our functions.
99

99

00:04:24,080  -->  00:04:26,440
Ideally we would like to do something that checks
100

100

00:04:26,440  -->  00:04:29,480
the user's access level when you call the function,
101

101

00:04:29,480  -->  00:04:31,033
not when you define it.
102

102

00:04:32,900  -->  00:04:34,740
So, here's what we're gonna do.
103

103

00:04:34,740  -->  00:04:36,790
We're still going to take in a function
104

104

00:04:36,790  -->  00:04:40,360
but now we're going to define another function,
105

105

00:04:40,360  -->  00:04:42,270
which I'm going to call secure_function.
106

106

00:04:42,270  -->  00:04:44,603
This one I'm gonna call make_secure.
107

107

00:04:45,710  -->  00:04:48,820
This one doesn't take any parameters,
108

108

00:04:48,820  -->  00:04:50,906
and it is the one that checks.
109

109

00:04:50,906  -->  00:04:55,700
And, it returns calling the original function.
110

110

00:04:55,700  -->  00:04:59,310
Then, here we return secure_function.
111

111

00:04:59,310  -->  00:05:01,360
So this is a decorator.
112

112

00:05:01,360  -->  00:05:02,550
What happens now, and by the way
113

113

00:05:02,550  -->  00:05:04,450
this should be make_secure.
114

114

00:05:04,450  -->  00:05:06,760
What happens now is that get_admin_password,
115

115

00:05:06,760  -->  00:05:08,933
this function that we want to secure,
116

116

00:05:10,120  -->  00:05:12,810
is passed to the make_secure function.
117

117

00:05:12,810  -->  00:05:14,220
This one defines another function,
118

118

00:05:14,220  -->  00:05:16,070
and by the way, in Python you can define functions
119

119

00:05:16,070  -->  00:05:18,820
inside a function, so that's totally fine.
120

120

00:05:18,820  -->  00:05:22,050
And this function here, when called,
121

121

00:05:22,050  -->  00:05:23,940
will check the user's access level
122

122

00:05:23,940  -->  00:05:26,350
and return calling the original function
123

123

00:05:26,350  -->  00:05:27,620
which is get_admin_password.
124

124

00:05:27,620  -->  00:05:32,340
So it will return 1234 if the user's access level is admin.
125

125

00:05:32,340  -->  00:05:33,760
So that's this function here,
126

126

00:05:33,760  -->  00:05:35,680
and then we return the function itself.
127

127

00:05:35,680  -->  00:05:39,080
Not the function call, but the function itself.
128

128

00:05:39,080  -->  00:05:43,910
get_admin_password will be equal to this function.
129

129

00:05:43,910  -->  00:05:47,210
Which calls seemingly itself, but this is okay.
130

130

00:05:47,210  -->  00:05:49,423
It calls get_admin_password from up here.
131

131

00:05:50,556  -->  00:05:53,350
So when you call get_admin_password what you're gonna do
132

132

00:05:53,350  -->  00:05:55,670
is you're going to check the user's access level,
133

133

00:05:55,670  -->  00:05:58,120
and then you're going to return the result
134

134

00:05:58,120  -->  00:06:00,250
of calling the function originally,
135

135

00:06:00,250  -->  00:06:01,863
which will give you 1234.
136

136

00:06:05,709  -->  00:06:10,080
At the moment you get none, but of course if you
137

137

00:06:10,080  -->  00:06:13,290
set the user's access level to admin down here
138

138

00:06:13,290  -->  00:06:14,343
and you run it again,
139

139

00:06:15,250  -->  00:06:16,510
then you get 1234.
140

140

00:06:16,510  -->  00:06:17,860
Which is exactly what we wanted.
141

141

00:06:17,860  -->  00:06:20,800
So this is a simple decorator.
142

142

00:06:20,800  -->  00:06:23,820
This simple decorator will create a function
143

143

00:06:23,820  -->  00:06:27,730
and replace the original function with this secure one.
144

144

00:06:27,730  -->  00:06:31,870
So that you can no longer call get_admin_password
145

145

00:06:31,870  -->  00:06:34,963
without having the admin access level.
146

146

00:06:35,910  -->  00:06:38,240
Now if you wanted to add a little bit of error handling
147

147

00:06:38,240  -->  00:06:39,640
you can do an else here
148

148

00:06:39,640  -->  00:06:41,197
and you can return something like
149

149

00:06:41,197  -->  00:06:45,880
"No admin permissions for {user['username']}."
150

150

00:06:47,600  -->  00:06:50,490
and that'll give you a nicer idea of what's going on
151

151

00:06:50,490  -->  00:06:52,290
if you don't have admin permissions.
152

152

00:06:54,330  -->  00:06:55,500
That's everything for this video.
153

153

00:06:55,500  -->  00:06:57,770
This is a simple decorator,
154

154

00:06:57,770  -->  00:06:59,910
and now in the next one we're going to learn more
155

155

00:06:59,910  -->  00:07:02,700
about using the @ syntax for decorators
156

156

00:07:02,700  -->  00:07:05,520
that makes this much simpler, and much easier
157

157

00:07:05,520  -->  00:07:07,740
to re-use and secure multiple functions
158

158

00:07:07,740  -->  00:07:09,500
if that's what you want to do.
159

159

00:07:09,500  -->  00:07:10,390
Thanks for joining me,
160

160

00:07:10,390  -->  00:07:12,140
and I'll see you in the next video.
