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)