1
1

00:00:01,210  -->  00:00:03,000
<v Jose>Hi guys, and welcome back.</v>
2

2

00:00:03,000  -->  00:00:04,520
In this video we're going to talk about
3

3

00:00:04,520  -->  00:00:08,423
how to limit certain actions in our app to admins.
4

4

00:00:09,520  -->  00:00:13,870
We're going to do this by defining who the admins are
5

5

00:00:13,870  -->  00:00:17,130
and then checking whether the people that are accessing
6

6

00:00:17,130  -->  00:00:19,780
certain endpoints or trying to do certain things
7

7

00:00:19,780  -->  00:00:21,613
are admins or not.
8

8

00:00:23,340  -->  00:00:25,980
If you've ever used environment variables before
9

9

00:00:25,980  -->  00:00:28,180
you'll know that an environment variable is
10

10

00:00:28,180  -->  00:00:30,000
a piece of data that is stored
11

11

00:00:30,000  -->  00:00:32,030
and then your application can access it
12

12

00:00:32,030  -->  00:00:33,900
and it can take information from it,
13

13

00:00:33,900  -->  00:00:37,270
but the environment variables usually disappear
14

14

00:00:37,270  -->  00:00:39,920
once a session is closed.
15

15

00:00:39,920  -->  00:00:42,730
And when I say session here I don't mean a session
16

16

00:00:42,730  -->  00:00:46,030
as in the data we store about a logged in user.
17

17

00:00:46,030  -->  00:00:48,060
What I mean is that an environment variable
18

18

00:00:48,060  -->  00:00:51,823
is only alive while an application runs.
19

19

00:00:52,730  -->  00:00:55,090
So we can set some environment variables
20

20

00:00:55,980  -->  00:00:58,440
and we can make sure that they're only existing
21

21

00:00:58,440  -->  00:01:00,920
while our application is running.
22

22

00:01:00,920  -->  00:01:02,460
When our application finishes running,
23

23

00:01:02,460  -->  00:01:03,673
we can clear them out.
24

24

00:01:04,530  -->  00:01:06,350
Environment variables are used frequently
25

25

00:01:06,350  -->  00:01:09,780
in Linux and Mac OS, and Windows as well.
26

26

00:01:09,780  -->  00:01:13,510
And often they're used for, in a much larger sense,
27

27

00:01:13,510  -->  00:01:15,600
so they are often alive for
28

28

00:01:15,600  -->  00:01:18,520
the entire time your computer's on.
29

29

00:01:18,520  -->  00:01:21,383
But in this case we're only gonna use them for this app.
30

30

00:01:22,370  -->  00:01:26,410
We can get Flask to load an environment variable file
31

31

00:01:27,410  -->  00:01:28,907
if we call it ".env".
32

32

00:01:29,980  -->  00:01:33,450
If we create a file called ".env" in our app,
33

33

00:01:33,450  -->  00:01:36,207
we can here say that the admin is going to be
34

34

00:01:36,207  -->  00:01:39,350
"jose@tecladocode.com".
35

35

00:01:39,350  -->  00:01:41,200
Whenever you create a ".env" file
36

36

00:01:41,200  -->  00:01:43,040
you should also create a new file
37

37

00:01:43,040  -->  00:01:45,970
called ".env.example".
38

38

00:01:45,970  -->  00:01:48,730
And here I'm just gonna put "admin="
39

39

00:01:48,730  -->  00:01:52,060
and "example@example.com".
40

40

00:01:52,060  -->  00:01:54,010
Why do I do this?
41

41

00:01:54,010  -->  00:01:57,340
If you ever were to share your code with other people
42

42

00:01:57,340  -->  00:02:01,363
you do not want your ".env" file to be shared with them.
43

43

00:02:02,230  -->  00:02:04,930
You want only your ".env.example" file
44

44

00:02:04,930  -->  00:02:06,400
to be shared with them
45

45

00:02:06,400  -->  00:02:07,960
because you could potentially have
46

46

00:02:07,960  -->  00:02:09,800
some sensitive information in here,
47

47

00:02:09,800  -->  00:02:11,760
like your email address.
48

48

00:02:11,760  -->  00:02:13,560
Then we're gonna go over to "app.py"
49

49

00:02:14,640  -->  00:02:18,890
and we're gonna make use of that environment variable.
50

50

00:02:18,890  -->  00:02:21,270
Underneath "app.secret_key"
51

51

00:02:21,270  -->  00:02:24,063
type "app.config.update"
52

52

00:02:25,550  -->  00:02:26,987
and then admin is gonna be
53

53

00:02:26,987  -->  00:02:30,410
"=os.environ.get('ADMIN')".
54

54

00:02:32,300  -->  00:02:35,790
So here's where something's going on behind the scenes.
55

55

00:02:35,790  -->  00:02:39,250
Whenever Flask, at least in the newer versions of Flask,
56

56

00:02:39,250  -->  00:02:41,700
if you're using Flask zero point something,
57

57

00:02:41,700  -->  00:02:43,340
make sure to update.
58

58

00:02:43,340  -->  00:02:44,630
In the newer versions of Flask,
59

59

00:02:44,630  -->  00:02:48,220
Flask will automatically load the ".env" file
60

60

00:02:48,220  -->  00:02:53,220
and it will populate "os.environ" for environment.
61

61

00:02:53,340  -->  00:02:56,510
You can then get any value that is in your ".env" file
62

62

00:02:56,510  -->  00:02:58,447
by doing ".get".
63

63

00:02:59,360  -->  00:03:00,193
So what this is gonna do,
64

64

00:03:00,193  -->  00:03:02,990
is it's going to update our app config
65

65

00:03:02,990  -->  00:03:06,960
which is a variable that is sort of held in our app
66

66

00:03:06,960  -->  00:03:09,390
throughout this entire lifetime.
67

67

00:03:09,390  -->  00:03:13,343
It's gonna update that with this key, admin and this value.
68

68

00:03:15,000  -->  00:03:16,060
What does that mean for us?
69

69

00:03:16,060  -->  00:03:19,453
Well it means we can access that app's config anywhere else.
70

70

00:03:20,740  -->  00:03:23,470
Before the ".env" file is automatically
71

71

00:03:23,470  -->  00:03:24,920
read by Flask though,
72

72

00:03:24,920  -->  00:03:28,000
we do have to instal a new library.
73

73

00:03:28,000  -->  00:03:30,690
So go over to your preferences panel.
74

74

00:03:30,690  -->  00:03:32,100
Project Interpreter
75

75

00:03:32,100  -->  00:03:34,610
and click at the little plus icon.
76

76

00:03:34,610  -->  00:03:36,860
And then we're gonna instal "python-dotenv".
77

77

00:03:38,880  -->  00:03:41,880
This package is used to read ".env" files
78

78

00:03:41,880  -->  00:03:45,320
and automatically load their contents into
79

79

00:03:45,320  -->  00:03:48,210
the operating system's environment variable set
80

80

00:03:48,210  -->  00:03:50,130
and Flask uses it, if it's installed
81

81

00:03:50,130  -->  00:03:52,123
in order to read the ".env" files.
82

82

00:03:53,780  -->  00:03:58,780
So what we can do is we can go over to our user decorators
83

83

00:04:00,730  -->  00:04:03,620
and we can create a new decorator
84

84

00:04:03,620  -->  00:04:05,900
which I'm gonna call "requires_admin"
85

85

00:04:05,900  -->  00:04:08,620
and once again it's gonna take in a callable,
86

86

00:04:08,620  -->  00:04:10,170
and return a callable.
87

87

00:04:10,170  -->  00:04:14,620
And this one is going to do something very similar
88

88

00:04:14,620  -->  00:04:15,773
to the one above.
89

89

00:04:17,670  -->  00:04:20,630
But it is only going to check
90

90

00:04:20,630  -->  00:04:23,400
whether session email is set
91

91

00:04:23,400  -->  00:04:28,240
and it matches the current "app.config.get('ADMIN')".
92

92

00:04:30,990  -->  00:04:32,790
So we do need to import current app.
93

93

00:04:33,800  -->  00:04:36,040
And what current app does in Flask is
94

94

00:04:36,040  -->  00:04:39,240
if there is an app that's currently loaded
95

95

00:04:39,240  -->  00:04:42,723
and you are currently serving the user a request,
96

96

00:04:43,990  -->  00:04:47,270
then current app will have the value of the app
97

97

00:04:47,270  -->  00:04:50,063
that you're currently using to respond to that user.
98

98

00:04:51,640  -->  00:04:56,640
So if the email is equal to the admin property of our config
99

99

00:04:58,170  -->  00:05:00,310
and by the way, this means that if there is
100

100

00:05:00,310  -->  00:05:03,930
no admin property in our config, use empty string
101

101

00:05:03,930  -->  00:05:05,820
as the default.
102

102

00:05:05,820  -->  00:05:07,733
So if they are the same,
103

103

00:05:08,900  -->  00:05:11,660
sorry, if they're not the same, there we go.
104

104

00:05:11,660  -->  00:05:13,507
Then we're gonna flash a message and say,
105

105

00:05:13,507  -->  00:05:18,477
"You need to be an administrator to access this page."
106

106

00:05:21,150  -->  00:05:26,150
And we're gonna return them to the login endpoint.
107

107

00:05:29,430  -->  00:05:33,310
Otherwise, we're gonna return the actual function
108

108

00:05:33,310  -->  00:05:34,290
that we wanna use
109

109

00:05:36,440  -->  00:05:39,900
and finally we will return the decorated function,
110

110

00:05:39,900  -->  00:05:42,550
but make sure to not evaluate the function.
111

111

00:05:42,550  -->  00:05:44,150
Just return the function itself.
112

112

00:05:45,400  -->  00:05:47,920
This requires admin decorator will make sure
113

113

00:05:47,920  -->  00:05:51,130
that the user is the admin
114

114

00:05:51,130  -->  00:05:55,240
before allowing them to access any particular endpoint.
115

115

00:05:55,240  -->  00:05:58,093
And we're going to use that in our stores.
116

116

00:06:01,010  -->  00:06:04,920
So going over to the stores views here,
117

117

00:06:04,920  -->  00:06:07,160
we're gonna say that from models,
118

118

00:06:07,160  -->  00:06:10,690
the user we're gonna import requires admin
119

119

00:06:10,690  -->  00:06:13,020
and we do need to go over to the init
120

120

00:06:14,320  -->  00:06:17,840
and make sure that that is loaded there as well.
121

121

00:06:17,840  -->  00:06:21,050
Okay, and now we can apply this requires admin
122

122

00:06:21,050  -->  00:06:23,810
to any endpoint that we choose.
123

123

00:06:23,810  -->  00:06:26,370
And what I'm gonna do is I'm going to apply it,
124

124

00:06:26,370  -->  00:06:28,280
not to the index,
125

125

00:06:28,280  -->  00:06:29,863
but to all the others.
126

126

00:06:31,550  -->  00:06:34,143
And I'll show you why in just a moment.
127

127

00:06:38,420  -->  00:06:40,970
The reason for it is because now we're also gonna
128

128

00:06:40,970  -->  00:06:43,500
make some changes to the store index.
129

129

00:06:43,500  -->  00:06:44,630
Here's the thing.
130

130

00:06:44,630  -->  00:06:46,900
You should not do what I've just done
131

131

00:06:46,900  -->  00:06:50,080
if any user can go to the index
132

132

00:06:50,080  -->  00:06:55,080
and any user can see these buttons.
133

133

00:06:55,650  -->  00:06:57,330
Why should you not?
134

134

00:06:57,330  -->  00:07:00,960
Because any user that isn't an admin can click the button
135

135

00:07:00,960  -->  00:07:02,090
and then they will be told
136

136

00:07:02,090  -->  00:07:04,990
they need to be an admin in order to access this endpoint.
137

137

00:07:05,930  -->  00:07:09,740
Not the best experience for a user that doesn't realise
138

138

00:07:09,740  -->  00:07:12,003
that only admins are allowed to do this.
139

139

00:07:13,350  -->  00:07:18,323
Also, this should be permanently delete store.
140

140

00:07:20,100  -->  00:07:22,453
I just realised that this is incorrect there.
141

141

00:07:24,120  -->  00:07:28,200
We wanna hide this away from users who are not admins.
142

142

00:07:28,200  -->  00:07:30,448
So that they're not confused by thinking that they can
143

143

00:07:30,448  -->  00:07:33,830
click it, but really they can't.
144

144

00:07:33,830  -->  00:07:35,987
The way we do that in Jinja is we'll say
145

145

00:07:35,987  -->  00:07:38,990
"if session('email') == config.ADMIN"
146

146

00:07:43,600  -->  00:07:44,843
then we will show that.
147

147

00:07:46,120  -->  00:07:48,800
Otherwise, we won't show it at all.
148

148

00:07:48,800  -->  00:07:50,643
Similarly for the create new.
149

149

00:07:55,790  -->  00:07:57,920
By the way, you may be wondering,
150

150

00:07:57,920  -->  00:07:59,737
what is config?
151

151

00:07:59,737  -->  00:08:03,480
Jinja2 has access to the app's config property,
152

152

00:08:03,480  -->  00:08:07,670
just because Jinja2 and Flask are so closely related
153

153

00:08:07,670  -->  00:08:09,090
that you can access it like this.
154

154

00:08:09,090  -->  00:08:10,610
It's already given to you
155

155

00:08:10,610  -->  00:08:11,940
when you are rendering the template.
156

156

00:08:11,940  -->  00:08:14,840
So you don't have to worry about getting access to config.
157

157

00:08:16,270  -->  00:08:19,254
Now here we're using session email.
158

158

00:08:19,254  -->  00:08:23,450
Jinja2 also has access to the session property from Flask.
159

159

00:08:23,450  -->  00:08:25,610
But it is possible that the user
160

160

00:08:25,610  -->  00:08:27,683
doesn't have a session email.
161

161

00:08:28,520  -->  00:08:31,060
E.G. if they haven't logged in yet.
162

162

00:08:31,060  -->  00:08:34,410
So do go over to your stores blueprint
163

163

00:08:34,410  -->  00:08:37,140
and make sure to do requires login
164

164

00:08:39,120  -->  00:08:42,820
and make sure to make this one a requires login as well.
165

165

00:08:42,820  -->  00:08:46,560
Just so that we will be able to render that template
166

166

00:08:46,560  -->  00:08:48,313
in case the user is not logged in.
167

167

00:08:49,700  -->  00:08:51,823
Let's run our app and give this a go.
168

168

00:08:54,750  -->  00:08:58,977
We're gonna login as "bob@example.com".
169

169

00:08:59,970  -->  00:09:01,410
There we go.
170

170

00:09:01,410  -->  00:09:06,410
And, Bob doesn't have any alerts.
171

171

00:09:07,690  -->  00:09:09,970
But if we go to the stores,
172

172

00:09:09,970  -->  00:09:12,610
you can see that he cannot see any buttons.
173

173

00:09:12,610  -->  00:09:16,530
He can't edit or delete or create new stores.
174

174

00:09:16,530  -->  00:09:18,527
If we sign up again, but this time as
175

175

00:09:18,527  -->  00:09:20,683
"jose@tecladocode.com"
176

176

00:09:25,410  -->  00:09:28,350
then we can see that we get the buttons
177

177

00:09:28,350  -->  00:09:30,543
because this is an admin.
178

178

00:09:32,330  -->  00:09:35,010
All right, so I hope all of this makes sense.
179

179

00:09:35,010  -->  00:09:36,290
What we've looked at is
180

180

00:09:36,290  -->  00:09:39,830
how to limit certain actions to admins.
181

181

00:09:39,830  -->  00:09:44,740
And also how to hide and show some elements
182

182

00:09:44,740  -->  00:09:47,710
of our page depending on a conditional.
183

183

00:09:47,710  -->  00:09:50,710
And that conditional has been also admin-related
184

184

00:09:50,710  -->  00:09:53,110
but it could be anything else if you want it to.
185

185

00:09:54,370  -->  00:09:57,740
Again, remember, just because we're hiding the buttons,
186

186

00:09:57,740  -->  00:10:02,180
that does not mean that users cannot reach these endpoints.
187

187

00:10:02,180  -->  00:10:05,180
It makes it more difficult, but they still can,
188

188

00:10:05,180  -->  00:10:07,460
which is why you still have to do
189

189

00:10:07,460  -->  00:10:09,840
this requires admin decorator.
190

190

00:10:09,840  -->  00:10:11,340
If you don't do that,
191

191

00:10:11,340  -->  00:10:14,490
users could just go to "/stores/new"
192

192

00:10:14,490  -->  00:10:16,740
to create a new store without being an admin.
193

193

00:10:18,210  -->  00:10:19,610
I hope all of this makes sense.
194

194

00:10:19,610  -->  00:10:20,840
Thank you for joining me.
195

195

00:10:20,840  -->  00:10:22,463
I'll see you on the next one.
