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