PageRenderTime 58ms CodeModel.GetById 32ms RepoModel.GetById 1ms app.codeStats 0ms

/velruse/providers/bitbucket.py

https://github.com/wt/velruse
Python | 118 lines | 79 code | 25 blank | 14 comment | 6 complexity | a1ef8207705ec45cc0e89b5b23c7603c MD5 | raw file
  1. """Bitbucket Authentication Views
  2. http://confluence.atlassian.com/display/BITBUCKET/OAuth+on+Bitbucket
  3. """
  4. import json
  5. from urlparse import parse_qs
  6. import oauth2 as oauth
  7. import requests
  8. from pyramid.httpexceptions import HTTPFound
  9. from pyramid.settings import asbool
  10. from velruse.api import AuthenticationComplete
  11. from velruse.exceptions import AuthenticationDenied
  12. from velruse.exceptions import ThirdPartyFailure
  13. REQUEST_URL = 'https://bitbucket.org/api/1.0/oauth/request_token/'
  14. ACCESS_URL = 'https://bitbucket.org/api/1.0/oauth/access_token/'
  15. USER_URL = 'https://bitbucket.org/api/1.0/user'
  16. SIGMETHOD = oauth.SignatureMethod_HMAC_SHA1()
  17. class BitbucketAuthenticationComplete(AuthenticationComplete):
  18. """Bitbucket auth complete"""
  19. provider = 'bitbucket'
  20. def includeme(config):
  21. config.add_route("bitbucket_login", "/bitbucket/login")
  22. config.add_route("bitbucket_process", "/bitbucket/process",
  23. use_global_views=True,
  24. factory=bitbucket_process)
  25. config.add_view(bitbucket_login, route_name="bitbucket_login")
  26. def bitbucket_login(request):
  27. """Initiate a bitbucket login"""
  28. config = request.registry.settings
  29. # Create the consumer and client, make the request
  30. consumer = oauth.Consumer(config['velruse.bitbucket.consumer_key'],
  31. config['velruse.bitbucket.consumer_secret'])
  32. params = {'oauth_callback': request.route_url('bitbucket_process')}
  33. # We go through some shennanigans here to specify a callback url
  34. oauth_request = oauth.Request.from_consumer_and_token(consumer,
  35. http_url=REQUEST_URL, parameters=params)
  36. oauth_request.sign_request(SIGMETHOD, consumer, None)
  37. r = requests.get(REQUEST_URL, headers=oauth_request.to_header())
  38. if r.status_code != 200:
  39. raise ThirdPartyFailure("Status %s: %s" % (r.status_code, r.content))
  40. request_token = oauth.Token.from_string(r.content)
  41. request.session['token'] = r.content
  42. # Send the user to bitbucket now for authorization
  43. # there doesnt seem to be separate url for this on BB
  44. if asbool(config.get('velruse.bitbucket.authorize')):
  45. req_url = 'https://bitbucket.org/api/1.0/oauth/authenticate/'
  46. else:
  47. req_url = 'https://bitbucket.org/api/1.0/oauth/authenticate/'
  48. oauth_request = oauth.Request.from_token_and_callback(token=request_token,
  49. http_url=req_url)
  50. return HTTPFound(location=oauth_request.to_url())
  51. def bitbucket_process(request):
  52. """Process the bitbucket redirect"""
  53. if 'denied' in request.GET:
  54. return AuthenticationDenied("User denied authentication")
  55. config = request.registry.settings
  56. request_token = oauth.Token.from_string(request.session['token'])
  57. verifier = request.GET.get('oauth_verifier')
  58. if not verifier:
  59. raise ThirdPartyFailure("No oauth_verifier returned")
  60. request_token.set_verifier(verifier)
  61. # Create the consumer and client, make the request
  62. consumer = oauth.Consumer(config['velruse.bitbucket.consumer_key'],
  63. config['velruse.bitbucket.consumer_secret'])
  64. client = oauth.Client(consumer, request_token)
  65. resp, content = client.request(ACCESS_URL, "POST")
  66. if resp['status'] != '200':
  67. raise ThirdPartyFailure("Status %s: %s" % (resp['status'], content))
  68. access_token = dict(parse_qs(content))
  69. cred = {'oauthAccessToken': access_token['oauth_token'][0],
  70. 'oauthAccessTokenSecret': access_token['oauth_token_secret'][0]}
  71. # Make a request with the data for more user info
  72. token = oauth.Token(key=cred['oauthAccessToken'],
  73. secret=cred['oauthAccessTokenSecret'])
  74. client = oauth.Client(consumer, token)
  75. resp, content = client.request(USER_URL)
  76. user_data = json.loads(content)
  77. data = user_data['user']
  78. # Setup the normalized contact info
  79. profile = {}
  80. profile['accounts'] = [{
  81. 'domain':'bitbucket.com',
  82. 'username':data['username']
  83. }]
  84. profile['preferredUsername'] = data['username']
  85. profile['name'] = {
  86. 'formatted': '%s %s' % (data['first_name'], data['last_name']),
  87. 'givenName': data['first_name'],
  88. 'familyName': data['last_name'],
  89. }
  90. profile['displayName'] = profile['name']['formatted']
  91. return BitbucketAuthenticationComplete(profile=profile,
  92. credentials=cred)