github.com/shashidharatd/test-infra@v0.0.0-20171006011030-71304e1ca560/gubernator/github_auth.py (about) 1 # Copyright 2016 The Kubernetes Authors. 2 # 3 # Licensed under the Apache License, Version 2.0 (the "License"); 4 # you may not use this file except in compliance with the License. 5 # You may obtain a copy of the License at 6 # 7 # http://www.apache.org/licenses/LICENSE-2.0 8 # 9 # Unless required by applicable law or agreed to in writing, software 10 # distributed under the License is distributed on an "AS IS" BASIS, 11 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 # See the License for the specific language governing permissions and 13 # limitations under the License. 14 15 # Copyright 2016 The Kubernetes Authors. 16 # 17 # Licensed under the Apache License, Version 2.0 (the "License"); 18 # you may not use this file except in compliance with the License. 19 # You may obtain a copy of the License at 20 # 21 # http://www.apache.org/licenses/LICENSE-2.0 22 # 23 # Unless required by applicable law or agreed to in writing, software 24 # distributed under the License is distributed on an "AS IS" BASIS, 25 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 26 # See the License for the specific language governing permissions and 27 # limitations under the License. 28 29 import json 30 import logging 31 import urllib 32 33 from webapp2_extras import security 34 35 from google.appengine.api import urlfetch 36 37 import view_base 38 39 40 class Endpoint(view_base.BaseHandler): 41 def github_client(self): 42 client = self.app.config['github_client'] 43 if not client: 44 self.abort(500) 45 return client['id'], client['secret'] 46 47 def maybe_redirect(self, target): 48 ''' 49 Redirect to a given URL if it's determined to be safe. 50 ''' 51 if target.startswith('/pr'): 52 self.redirect(target) 53 54 def get(self, arg): 55 # Documentation here: https://developer.github.com/v3/oauth/ 56 57 client_id, client_secret = self.github_client() 58 59 if arg.endswith('/done'): 60 target, done = arg[:-len('/done')], True 61 else: 62 target, done = arg, False 63 64 if not done: 65 # 1) Redirect users to request GitHub access 66 if self.session.get('user'): 67 # User already logged in, no need to continue. 68 self.maybe_redirect(target) 69 return 70 71 state = security.generate_random_string(entropy=128) 72 args = { 73 'client_id': client_id, 74 'redirect_uri': self.request.url + '/done', 75 'scope': '', # Username only needs "public data" permission! 76 'state': state 77 } 78 self.session['gh_state'] = state 79 self.redirect('https://github.com/login/oauth/authorize?' 80 + urllib.urlencode(args)) 81 else: 82 # 2) GitHub redirects back to your site 83 code = self.request.get('code') 84 state = self.request.get('state') 85 session_state = self.session.pop('gh_state', '') 86 87 if not state or not code: 88 self.abort(400) 89 if not security.compare_hashes(state, session_state): 90 # States must match to avoid CSRF. 91 # Full attack details here: 92 # http://homakov.blogspot.com/2012/07/saferweb-most-common-oauth2.html 93 self.abort(400) 94 95 # 3) Use the access token to access the API 96 params = { 97 'client_id': client_id, 98 'client_secret': client_secret, 99 'code': code, 100 'state': session_state, 101 } 102 resp = urlfetch.fetch( 103 'https://github.com/login/oauth/access_token', 104 payload=urllib.urlencode(params), 105 method='POST', 106 headers={'Accept': 'application/json'} 107 ) 108 if resp.status_code != 200: 109 logging.error('failed to vend token: status %d', resp.status_code) 110 self.abort(500) 111 vended = json.loads(resp.content) 112 113 resp = urlfetch.fetch( 114 'https://api.github.com/user', 115 headers={'Authorization': 'token ' + vended['access_token']}) 116 117 if resp.status_code != 200: 118 logging.error('failed to get user name: status %d', resp.status_code) 119 self.abort(500) 120 121 user_info = json.loads(resp.content) 122 login = user_info['login'] 123 logging.info('successful login for %s', login) 124 125 # Save the GitHub username to the session. 126 # Note: we intentionally discard the access_token here, 127 # since we don't need it for anything more. 128 self.session['user'] = login 129 self.response.write('<h1>Welcome, %s</h1>' % login) 130 131 self.maybe_redirect(target)