| OLD | NEW |
| 1 #!/usr/bin/env python | 1 #!/usr/bin/env python |
| 2 | 2 |
| 3 # Copyright (c) 2011 The Chromium Authors. All rights reserved. | 3 # Copyright (c) 2011 The Chromium Authors. All rights reserved. |
| 4 # Use of this source code is governed by a BSD-style license that can be | 4 # Use of this source code is governed by a BSD-style license that can be |
| 5 # found in the LICENSE file. | 5 # found in the LICENSE file. |
| 6 | 6 |
| 7 """Provides authentcation related utilities and endpoint handlers. | 7 """Provides authentcation related utilities and endpoint handlers. |
| 8 | 8 |
| 9 All authentication code for the webapp should go through this module. In | 9 All authentication code for the webapp should go through this module. In |
| 10 general, credentials should be used server-side. The URL endpoints are for | 10 general, credentials should be used server-side. The URL endpoints are for |
| (...skipping 25 matching lines...) Expand all Loading... |
| 36 CLIENT_ID = ('440925447803-d9u05st5jjm3gbe865l0jeaujqfrufrn.' | 36 CLIENT_ID = ('440925447803-d9u05st5jjm3gbe865l0jeaujqfrufrn.' |
| 37 'apps.googleusercontent.com') | 37 'apps.googleusercontent.com') |
| 38 CLIENT_SECRET = 'Nl4vSQEgDpPMP-1rDEsgs3V7' | 38 CLIENT_SECRET = 'Nl4vSQEgDpPMP-1rDEsgs3V7' |
| 39 | 39 |
| 40 | 40 |
| 41 class NotAuthenticated(Exception): | 41 class NotAuthenticated(Exception): |
| 42 """API requiring authentication is called with credentials.""" | 42 """API requiring authentication is called with credentials.""" |
| 43 pass | 43 pass |
| 44 | 44 |
| 45 | 45 |
| 46 class XmppToken(db.Model): | 46 class ClientLoginToken(db.Model): |
| 47 auth_token = db.StringProperty() | 47 auth_token = db.StringProperty() |
| 48 | 48 |
| 49 | 49 |
| 50 class OAuth2Tokens(db.Model): | 50 class OAuth2Tokens(db.Model): |
| 51 """Stores the Refresh and Access token information for OAuth2.""" | 51 """Stores the Refresh and Access token information for OAuth2.""" |
| 52 refresh_token = db.StringProperty() | 52 refresh_token = db.StringProperty() |
| 53 access_token = db.StringProperty() | 53 access_token = db.StringProperty() |
| 54 access_token_expiration = db.IntegerProperty() | 54 access_token_expiration = db.IntegerProperty() |
| 55 | 55 |
| 56 | 56 |
| 57 def HasOAuth2Tokens(throws=True): | 57 def HasOAuth2Tokens(throws=True): |
| 58 oauth2_tokens = OAuth2Tokens.get_or_insert(GetUserId()) | 58 oauth2_tokens = OAuth2Tokens.get_or_insert(GetUserId()) |
| 59 if oauth2_tokens.refresh_token: | 59 if oauth2_tokens.refresh_token: |
| 60 return True; | 60 return True; |
| 61 return False; | 61 return False; |
| 62 | 62 |
| 63 | 63 |
| 64 def GetAccessToken(throws=True): | 64 def GetOAuth2AccessToken(throws=True): |
| 65 oauth2_tokens = OAuth2Tokens.get_or_insert(GetUserId()) | 65 oauth2_tokens = OAuth2Tokens.get_or_insert(GetUserId()) |
| 66 | 66 |
| 67 if not oauth2_tokens.refresh_token: | 67 if not oauth2_tokens.refresh_token: |
| 68 raise NotAuthenticated() | 68 raise NotAuthenticated() |
| 69 | 69 |
| 70 if time.time() > oauth2_tokens.access_token_expiration: | 70 if time.time() > oauth2_tokens.access_token_expiration: |
| 71 form_fields = { | 71 form_fields = { |
| 72 'client_id' : CLIENT_ID, | 72 'client_id' : CLIENT_ID, |
| 73 'client_secret' : CLIENT_SECRET, | 73 'client_secret' : CLIENT_SECRET, |
| 74 'refresh_token' : oauth2_tokens.refresh_token, | 74 'refresh_token' : oauth2_tokens.refresh_token, |
| (...skipping 11 matching lines...) Expand all Loading... |
| 86 oauth_json = json.loads(result.content) | 86 oauth_json = json.loads(result.content) |
| 87 oauth2_tokens.access_token = oauth_json['access_token'] | 87 oauth2_tokens.access_token = oauth_json['access_token'] |
| 88 # Give us 30 second buffer to hackily account for RTT on network request. | 88 # Give us 30 second buffer to hackily account for RTT on network request. |
| 89 oauth2_tokens.access_token_expiration = ( | 89 oauth2_tokens.access_token_expiration = ( |
| 90 int(oauth_json['expires_in'] + time.time() - 30)) | 90 int(oauth_json['expires_in'] + time.time() - 30)) |
| 91 oauth2_tokens.put() | 91 oauth2_tokens.put() |
| 92 | 92 |
| 93 return oauth2_tokens.access_token | 93 return oauth2_tokens.access_token |
| 94 | 94 |
| 95 | 95 |
| 96 def GetXmppToken(throws=True): | 96 def GetClientLoginToken(throws=True): |
| 97 """Retrieves the XMPP for Chromoting. | 97 """Retrieves the ClientLogin for Chromoting. |
| 98 | 98 |
| 99 Args: | 99 Args: |
| 100 throws: bool (optional) Default is True. Throws if no token. | 100 throws: bool (optional) Default is True. Throws if no token. |
| 101 | 101 |
| 102 Returns: | 102 Returns: |
| 103 The auth token for the current user. | 103 The auth token for the current user. |
| 104 """ | 104 """ |
| 105 xmpp_token = XmppToken.get_or_insert(GetUserId()) | 105 clientlogin_token = ClientLoginToken.get_or_insert(GetUserId()) |
| 106 if throws and not xmpp_token.auth_token: | 106 if throws and not clientlogin_token.auth_token: |
| 107 raise NotAuthenticated() | 107 raise NotAuthenticated() |
| 108 return xmpp_token.auth_token | 108 return clientlogin_token.auth_token |
| 109 | 109 |
| 110 | 110 |
| 111 def ClearXmppToken(): | 111 def ClearClientLoginToken(): |
| 112 """Clears all Chromoting ClientLogin token state from the datastore.""" | 112 """Clears all Chromoting ClientLogin token state from the datastore.""" |
| 113 db.delete(db.Key.from_path('XmppToken', GetUserId())) | 113 db.delete(db.Key.from_path('ClientLoginToken', GetUserId())) |
| 114 | 114 |
| 115 | 115 |
| 116 def ClearOAuth2Token(): | 116 def ClearOAuth2Token(): |
| 117 """Clears all Chromoting ClientLogin token state from the datastore.""" | 117 """Clears all Chromoting ClientLogin token state from the datastore.""" |
| 118 db.delete(db.Key.from_path('OAuth2Tokens', GetUserId())) | 118 db.delete(db.Key.from_path('OAuth2Tokens', GetUserId())) |
| 119 | 119 |
| 120 | 120 |
| 121 def GetUserId(): | 121 def GetUserId(): |
| 122 """Retrieves the user id for the current user. | 122 """Retrieves the user id for the current user. |
| 123 | 123 |
| 124 Returns: | 124 Returns: |
| 125 A string with the user id of the logged in user. | 125 A string with the user id of the logged in user. |
| 126 | 126 |
| 127 Raises: | 127 Raises: |
| 128 NotAuthenticated if the user is not logged in, or missing an id. | 128 NotAuthenticated if the user is not logged in, or missing an id. |
| 129 """ | 129 """ |
| 130 user = users.get_current_user() | 130 user = users.get_current_user() |
| 131 if not user: | 131 if not user: |
| 132 raise NotAuthenticated() | 132 raise NotAuthenticated() |
| 133 | 133 |
| 134 if not user.user_id(): | 134 if not user.user_id(): |
| 135 raise NotAuthenticated('no e-mail with google account!') | 135 raise NotAuthenticated('no e-mail with google account!') |
| 136 | 136 |
| 137 return user.user_id() | 137 return user.user_id() |
| 138 | 138 |
| 139 | 139 |
| 140 class XmppAuthHandler(webapp.RequestHandler): | 140 class ClientLoginAuthHandler(webapp.RequestHandler): |
| 141 """Prompts Google Accounts credentials and retrieves a ClientLogin token. | 141 """Prompts Google Accounts credentials and retrieves a ClientLogin token. |
| 142 | 142 |
| 143 This class takes the user's plaintext username and password, and then | 143 This class takes the user's plaintext username and password, and then |
| 144 posts a request to ClientLogin to get the access token. | 144 posts a request to ClientLogin to get the access token. |
| 145 | 145 |
| 146 THIS CLASS SHOULD NOT EXIST. | 146 THIS CLASS SHOULD NOT EXIST. |
| 147 | 147 |
| 148 We should NOT be taking a user's Google Accounts credentials in our webapp. | 148 We should NOT be taking a user's Google Accounts credentials in our webapp. |
| 149 However, we need a ClientLogin token for jingle, and this is currently the | 149 However, we need a ClientLogin token for jingle, and this is currently the |
| 150 only known workaround. | 150 only known workaround. |
| 151 """ | 151 """ |
| 152 @login_required | 152 @login_required |
| 153 def get(self): | 153 def get(self): |
| 154 ClearXmppToken() | 154 ClearClientLoginToken() |
| 155 path = os.path.join(os.path.dirname(__file__), 'client_login.html') | 155 path = os.path.join(os.path.dirname(__file__), 'client_login.html') |
| 156 self.response.out.write(template.render(path, {})) | 156 self.response.out.write(template.render(path, {})) |
| 157 | 157 |
| 158 def post(self): | 158 def post(self): |
| 159 email = self.request.get('username') | 159 email = self.request.get('username') |
| 160 password = self.request.get('password') | 160 password = self.request.get('password') |
| 161 form_fields = { | 161 form_fields = { |
| 162 'accountType' : 'HOSTED_OR_GOOGLE', | 162 'accountType' : 'HOSTED_OR_GOOGLE', |
| 163 'Email' : self.request.get('username'), | 163 'Email' : self.request.get('username'), |
| 164 'Passwd' : self.request.get('password'), | 164 'Passwd' : self.request.get('password'), |
| 165 'service' : 'chromiumsync', | 165 'service' : 'chromiumsync', |
| 166 'source' : 'chromoplex' | 166 'source' : 'chromoplex' |
| 167 } | 167 } |
| 168 form_data = urllib.urlencode(form_fields) | 168 form_data = urllib.urlencode(form_fields) |
| 169 result = urlfetch.fetch( | 169 result = urlfetch.fetch( |
| 170 url = 'https://www.google.com/accounts/ClientLogin', | 170 url = 'https://www.google.com/accounts/ClientLogin', |
| 171 payload = form_data, | 171 payload = form_data, |
| 172 method = urlfetch.POST, | 172 method = urlfetch.POST, |
| 173 headers = {'Content-Type': 'application/x-www-form-urlencoded'}) | 173 headers = {'Content-Type': 'application/x-www-form-urlencoded'}) |
| 174 if result.status_code != 200: | 174 if result.status_code != 200: |
| 175 self.response.out.write(result.content) | 175 self.response.out.write(result.content) |
| 176 for i in result.headers: | 176 for i in result.headers: |
| 177 self.response.headers[i] = result.headers[i] | 177 self.response.headers[i] = result.headers[i] |
| 178 self.response.set_status(result.status_code) | 178 self.response.set_status(result.status_code) |
| 179 return | 179 return |
| 180 | 180 |
| 181 xmpp_token = XmppToken(key_name = GetUserId()) | 181 clientlogin_token = ClientLoginToken(key_name = GetUserId()) |
| 182 xmpp_token.auth_token = re.search("Auth=(.*)", result.content).group(1) | 182 clientlogin_token.auth_token = re.search( |
| 183 xmpp_token.put() | 183 "Auth=(.*)", result.content).group(1) |
| 184 clientlogin_token.put() |
| 184 self.redirect('/') | 185 self.redirect('/') |
| 185 | 186 |
| 186 | 187 |
| 187 class ClearXmppTokenHandler(webapp.RequestHandler): | 188 class ClearClientLoginTokenHandler(webapp.RequestHandler): |
| 188 """Endpoint for dropping the user's Xmpp token.""" | 189 """Endpoint for dropping the user's ClientLogin token.""" |
| 189 @login_required | 190 @login_required |
| 190 def get(self): | 191 def get(self): |
| 191 ClearXmppToken() | 192 ClearClientLoginToken() |
| 192 self.redirect('/') | 193 self.redirect('/') |
| 193 | 194 |
| 194 | 195 |
| 195 class ClearOAuth2TokenHandler(webapp.RequestHandler): | 196 class ClearOAuth2TokenHandler(webapp.RequestHandler): |
| 196 """Endpoint for dropping the user's OAuth2 token.""" | 197 """Endpoint for dropping the user's OAuth2 token.""" |
| 197 @login_required | 198 @login_required |
| 198 def get(self): | 199 def get(self): |
| 199 ClearOAuth2Token() | 200 ClearOAuth2Token() |
| 200 self.redirect('/') | 201 self.redirect('/') |
| 201 | 202 |
| (...skipping 39 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 241 | 242 |
| 242 if state: | 243 if state: |
| 243 self.redirect(state) | 244 self.redirect(state) |
| 244 else: | 245 else: |
| 245 self.redirect('/') | 246 self.redirect('/') |
| 246 | 247 |
| 247 | 248 |
| 248 def main(): | 249 def main(): |
| 249 application = webapp.WSGIApplication( | 250 application = webapp.WSGIApplication( |
| 250 [ | 251 [ |
| 251 ('/auth/xmpp_auth', XmppAuthHandler), | 252 ('/auth/clientlogin_auth', ClientLoginAuthHandler), |
| 252 ('/auth/clear_xmpp_token', ClearXmppTokenHandler), | 253 ('/auth/clear_clientlogin_token', ClearClientLoginTokenHandler), |
| 253 ('/auth/clear_oauth2_token', ClearOAuth2TokenHandler), | 254 ('/auth/clear_oauth2_token', ClearOAuth2TokenHandler), |
| 254 ('/auth/oauth2_return', OAuth2ReturnHandler) | 255 ('/auth/oauth2_return', OAuth2ReturnHandler) |
| 255 ], | 256 ], |
| 256 debug=True) | 257 debug=True) |
| 257 util.run_wsgi_app(application) | 258 util.run_wsgi_app(application) |
| 258 | 259 |
| 259 | 260 |
| 260 if __name__ == '__main__': | 261 if __name__ == '__main__': |
| 261 main() | 262 main() |
| OLD | NEW |