Created
August 9, 2015 17:56
-
-
Save corytodd/6509d4fb0733600f6022 to your computer and use it in GitHub Desktop.
Python Bitbucket helper
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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