Skip to content

Instantly share code, notes, and snippets.

@corytodd
Created August 9, 2015 17:56
Show Gist options
  • Save corytodd/6509d4fb0733600f6022 to your computer and use it in GitHub Desktop.
Save corytodd/6509d4fb0733600f6022 to your computer and use it in GitHub Desktop.
Python Bitbucket helper
from rauth import OAuth1Service
import BaseHTTPServer
import threading,time
import webbrowser
import urlparse
class Config(object):
def __init__(self):
# Create a new consumer at https://bitbucket.org/account/user/{username}/api
self.consumer_key = <YOUR_KEY>
self.consumer_secret = <YOUR_SECRET>
# API URLs from https://confluence.atlassian.com/display/BITBUCKET/oauth+Endpoint
self.request_token_url = 'https://bitbucket.org/!api/1.0/oauth/request_token'
self.access_token_url = 'https://bitbucket.org/!api/1.0/oauth/access_token'
self.authorize_url = 'https://bitbucket.org/!api/1.0/oauth/authenticate'
self.team = <YOUR_TEAM>
self.host_name = "localhost"
self.port_number = 8080
self.call_back = 'http://{:s}:{:d}?dump'.format(self.host_name, self.port_number)
# TODO support more endpoints
self.url = 'https://bitbucket.org/!api/2.0/repositories/%s' % (self.team)
class Session(object):
def __init__(self, config):
"""
Creates a new bitbucket oauth session using a config object
and endpoints as defined in the Bitbucket oauth docs. Initializing
a session object spins up a local webserver to capture the oauth
verification token. Once the token has been captured, the webserver
shuts down and the request may continue.
Simply call make_request on this object when ready
Args:
-----
config : Config
configuration object containing key, secret, and callback URL
"""
self.config = config
# Create the oauth1 service
self.bitbucket = OAuth1Service(name='bitbucket',
consumer_key=self.config.consumer_key,
consumer_secret=self.config.consumer_secret,
request_token_url=self.config.request_token_url,
access_token_url=self.config.access_token_url,
authorize_url=self.config.authorize_url)
# These will be filled in throughout the verification process
self.verifier = None
self.rtoken = None
self.rtoken_secret = None
# webserver kill switch
self.keep_running = True
self.results = None
# Spin up a web server thread to capture the verification code
self.server_thread = threading.Thread(target=self.run_server_while_needed)
self.server_thread.setDaemon(True)
self.server_thread.start()
def make_request(self):
"""
Request authorization for the appropriate scopes. Note that this uses your default web browser.
In the case of Chrome, whichever profile you have open will handle the request. So, if you have
a profile open with a Bitbucket account that did not generate your oauth key/secret, this will
fail.
"""
# Make the request for a token, include the callback URL.
self.rtoken, self.rtoken_secret = self.bitbucket.get_request_token(params={'oauth_callback': self.config.call_back})
# Use the token to rquest an authorization URL.
authorize_url = self.bitbucket.get_authorize_url(self.rtoken)
# Request authorization for this oauth exchange
webbrowser.open(authorize_url)
def get_results(self):
"""Returns any results generated by make_request. May be None"""
return self.results
def __get_results(self, verifier=None):
"""Using the authorized session, complete the specified request"""
url = self.config.url
self.results = self.__get_session(verifier).get(url).json()
# Stop the server and script
self.keep_running = False
def __get_session(self, verifier=None):
"""Returns an authenticated Bitbucket session using the verification code captured by the webserver"""
if self.__is_configured(verifier):
return self.bitbucket.get_auth_session(self.rtoken, self.rtoken_secret, data={'oauth_verifier': verifier})
else:
print "Bitbucket session is not correctly configured: verifier->{:s},rtoken->{:s},rtoken_secret->{:s}".format(verifier, self.rtoken, self.rtoken_secret)
def __is_configured(self, verifier):
"""Validates that all oauth session variables are not None"""
if verifier is None:
return False
if self.rtoken is None:
return False
if self.rtoken_secret is None:
return False
return True
def run_server_while_needed(self, server_class=BaseHTTPServer.HTTPServer,
handler_class=BaseHTTPServer.BaseHTTPRequestHandler):
"""
This assumes that keep_running() is a function of no arguments which
is tested initially and after each request. If its return value
is true, the server continues.
"""
GetHandler.call_back = self.__get_results
httpd = server_class((self.config.host_name, self.config.port_number), GetHandler)
while self.keep_running:
httpd.handle_request()
httpd.server_close()
class GetHandler(BaseHTTPServer.BaseHTTPRequestHandler):
"""Handler to extract the oauth verification code"""
# Static variables
call_back = None
done = False
lock = threading.Lock()
def do_GET(self):
query = urlparse.urlparse(self.path).query
qs = urlparse.parse_qs(query)
self.send_response(200)
self.end_headers()
# Only allow the request to be made once per execution
GetHandler.lock.acquire()
if not GetHandler.done and qs.has_key('oauth_verifier'):
GetHandler.call_back(qs['oauth_verifier'])
GetHandler.done = True
GetHandler.lock.release()
if __name__ == '__main__':
config = Config()
session = Session(config)
# Makes the oauth request and the webserver should capture the verification code
# and fire the final
session.make_request()
# Wait for request to complete, up to 5 seconds
session.server_thread.join(5000)
print session.get_results()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment