· 7 years ago · Dec 16, 2018, 05:34 PM
1# -*- coding: utf-8 -*-
2
3"""
4 A real simple app for using webapp2 with auth and session.
5
6 It just covers the basics. Creating a user, login, logout and a decorator for protecting certain handlers.
7
8 PRE-REQUIREMENTS:
9
10 Set at secret_key in webapp2 config:
11 webapp2_config = {}
12 webapp2_config['webapp2_extras.sessions'] = {
13 'secret_key': 'Im_an_alien',
14 }
15
16 You need to either set upp the following routes:
17
18 app = webapp2.WSGIApplication([
19 webapp2.Route(r'/login/', handler=LoginHandler, name='login'),
20 webapp2.Route(r'/logout/', handler=LogoutHandler, name='logout'),
21 webapp2.Route(r'/login/', handler=SecureRequestHandler, name='secure'),
22 webapp2.Route(r'/secure/', handler=CreateUserHandler, name='create-user'),
23
24 ])
25
26 OR:
27
28 Change the urls in BaseHandler.auth_config to match LoginHandler/LogoutHandler
29 And also change the url in the post method of the LoginHandler to redirect to to a page requiring a user session
30"""
31
32import webapp2
33from webapp2_extras import auth
34from webapp2_extras import sessions
35from webapp2_extras.auth import InvalidAuthIdError
36from webapp2_extras.auth import InvalidPasswordError
37
38def user_required(handler):
39 """
40 Decorator for checking if there's a user associated with the current session.
41 Will also fail if there's no session present.
42 """
43 def check_login(self, *args, **kwargs):
44 auth = self.auth
45 if not auth.get_user_by_session():
46 # If handler has no login_url specified invoke a 403 error
47 try:
48 self.redirect(self.auth_config['login_url'], abort=True)
49 except (AttributeError, KeyError), e:
50 self.abort(403)
51 else:
52 return handler(self, *args, **kwargs)
53
54 return check_login
55
56class BaseHandler(webapp2.RequestHandler):
57 """
58 BaseHandler for all requests
59
60 Holds the auth and session properties so they are reachable for all requests
61 """
62 def dispatch(self):
63 """
64 Save the sessions for preservation across requests
65 """
66 try:
67 response = super(BaseHandler, self).dispatch()
68 self.response.write(response)
69 finally:
70 self.session_store.save_sessions(self.response)
71
72 @webapp2.cached_property
73 def auth(self):
74 return auth.get_auth()
75
76 @webapp2.cached_property
77 def session_store(self):
78 return sessions.get_store(request=self.request)
79
80 @webapp2.cached_property
81 def auth_config(self):
82 """
83 Dict to hold urls for login/logout
84 """
85 return {
86 'login_url': self.uri_for('login'),
87 'logout_url': self.uri_for('logout')
88 }
89
90
91class LoginHandler(BaseHandler):
92 def get(self):
93 """
94 Returns a simple HTML form for login
95 """
96 return """
97 <!DOCTYPE hml>
98 <html>
99 <head>
100 <title>webapp2 auth example</title>
101 </head>
102 <body>
103 <form action="%s" method="post">
104 <fieldset>
105 <legend>Login form</legend>
106 <label>Username <input type="text" name="username" placeholder="Your username" /></label>
107 <label>Password <input type="password" name="password" placeholder="Your password" /></label>
108 </fieldset>
109 <button>Login</button>
110 </form>
111 </html>
112 """ % self.request.url
113
114 def post(self):
115 """
116 username: Get the username from POST dict
117 password: Get the password from POST dict
118 """
119 username = self.request.POST.get('username')
120 password = self.request.POST.get('password')
121 # Try to login user with password
122 # Raises InvalidAuthIdError if user is not found
123 # Raises InvalidPasswordError if provided password doesn't match with specified user
124 try:
125 self.auth.get_user_by_password(username, password)
126 self.redirect('/secure')
127 except (InvalidAuthIdError, InvalidPasswordError), e:
128 # Returns error message to self.response.write in the BaseHandler.dispatcher
129 # Currently no message is attached to the exceptions
130 return e
131
132class CreateUserHandler(BaseHandler):
133 def get(self):
134 """
135 Returns a simple HTML form for create a new user
136 """
137 return """
138 <!DOCTYPE hml>
139 <html>
140 <head>
141 <title>webapp2 auth example</title>
142 </head>
143 <body>
144 <form action="%s" method="post">
145 <fieldset>
146 <legend>Create user form</legend>
147 <label>Username <input type="text" name="username" placeholder="Your username" /></label>
148 <label>Password <input type="password" name="password" placeholder="Your password" /></label>
149 </fieldset>
150 <button>Create user</button>
151 </form>
152 </html>
153 """ % self.request.url
154
155 def post(self):
156 """
157 username: Get the username from POST dict
158 password: Get the password from POST dict
159 """
160 username = self.request.POST.get('username')
161 password = self.request.POST.get('password')
162 # Passing password_raw=password so password will be hashed
163 # Returns a tuple, where first value is BOOL. If True ok, If False no new user is created
164 user = self.auth.store.user_model.create_user(username, password_raw=password)
165 if not user[0]: #user is a tuple
166 return user[1] # Error message
167 else:
168 # User is created, let's try redirecting to login page
169 try:
170 self.redirect(self.auth_config['login_url'], abort=True)
171 except (AttributeError, KeyError), e:
172 self.abort(403)
173
174
175class LogoutHandler(BaseHandler):
176 """
177 Destroy user session and redirect to login
178 """
179 def get(self):
180 self.auth.unset_session()
181 # User is logged out, let's try redirecting to login page
182 try:
183 self.redirect(self.auth_config['login_url'])
184 except (AttributeError, KeyError), e:
185 return "User is logged out"
186
187
188class SecureRequestHandler(BaseHandler):
189 """
190 Only accessible to users that are logged in
191 """
192 @user_required
193 def get(self, **kwargs):
194 a = self.app.config.get('foo')
195 try:
196 return "Secure zone %s <a href='%s'>Logout</a>" % (a, self.auth_config['logout_url'])
197 except (AttributeError, KeyError), e:
198 return "Secure zone"