PageRenderTime 47ms CodeModel.GetById 17ms app.highlight 24ms RepoModel.GetById 1ms app.codeStats 0ms

/tools/jirashell.py

https://bitbucket.org/jonEbird/jira-python
Python | 190 lines | 179 code | 5 blank | 6 comment | 0 complexity | b06974c8d0d76a570359e0ddc321b8c8 MD5 | raw file
  1#!/usr/bin/env python
  2
  3"""
  4Starts an interactive JIRA session in an ipython terminal. Script arguments
  5support changing the server and a persistent authentication over HTTP BASIC.
  6"""
  7import ConfigParser
  8
  9import argparse
 10from getpass import getpass
 11from sys import exit
 12import os
 13import requests
 14from jira.packages.requests_oauth.hook import OAuthHook
 15from urlparse import parse_qsl
 16import webbrowser
 17from jira.client import JIRA
 18from jira import __version__
 19
 20CONFIG_PATH = os.path.join(os.path.expanduser('~'), '.jira-python', 'jirashell.ini')
 21
 22def oauth_dance(server, consumer_key, key_cert_data, print_tokens=False):
 23    verify = server.startswith('https')
 24
 25    # step 1: get request tokens
 26    request_oauth_hook = OAuthHook(consumer_key=consumer_key, consumer_secret='',
 27                                   key_cert=key_cert_data, header_auth=True)
 28    r = requests.post(server + '/plugins/servlet/oauth/request-token', verify=verify,
 29                      hooks={'pre_request': request_oauth_hook})
 30    request = dict(parse_qsl(r.text))
 31    request_token = request['oauth_token']
 32    request_token_secret = request['oauth_token_secret']
 33    if print_tokens:
 34        print "Request tokens received."
 35        print "    Request token:        {}".format(request_token)
 36        print "    Request token secret: {}".format(request_token_secret)
 37
 38    # step 2: prompt user to validate
 39    auth_url = '{}/plugins/servlet/oauth/authorize?oauth_token={}'.format(server, request_token)
 40    webbrowser.open_new(auth_url)
 41    print "Your browser is opening the OAuth authorization for this client session."
 42    approved = raw_input('Have you authorized this program to connect on your behalf to {}? (y/n)'.format(server))
 43
 44    if approved.lower() != 'y':
 45        exit('Abandoning OAuth dance. Your partner faceplants. The audience boos. You feel shame.')
 46
 47    # step 3: get access tokens for validated user
 48    access_oauth_hook = OAuthHook(access_token=request_token, access_token_secret=request_token_secret,
 49                                  consumer_key=consumer_key, consumer_secret='',
 50                                  key_cert=key_cert_data, header_auth=True)
 51    r = requests.post(server + '/plugins/servlet/oauth/access-token', verify=verify,
 52                      hooks={'pre_request': access_oauth_hook})
 53    access = dict(parse_qsl(r.text))
 54
 55    if print_tokens:
 56        print "Access tokens received."
 57        print "    Access token:        {}".format(access['oauth_token'])
 58        print "    Access token secret: {}".format(access['oauth_token_secret'])
 59
 60    return {
 61        'access_token': access['oauth_token'],
 62        'access_token_secret': access['oauth_token_secret'],
 63        'consumer_key': consumer_key,
 64        'key_cert': key_cert_data,
 65    }
 66
 67def process_config():
 68    parser = ConfigParser.SafeConfigParser()
 69    try:
 70        parser.read(CONFIG_PATH)
 71    except ConfigParser.ParsingError, err:
 72        print "Couldn't read config file at path: " + CONFIG_PATH + "; reverting to command line"
 73        return process_command_line()
 74
 75    if parser.has_section('options'):
 76        options = dict(parser.items('options'))
 77    else:
 78        options = {}
 79    if parser.has_section('basic_auth'):
 80        basic_auth = dict(parser.items('basic_auth'))
 81    else:
 82        basic_auth = {}
 83    if parser.has_section('oauth'):
 84        oauth = dict(parser.items('oauth'))
 85    else:
 86        oauth = {}
 87
 88    return options, basic_auth, oauth
 89
 90def process_command_line():
 91    parser = argparse.ArgumentParser(description='Start an interactive JIRA shell with the REST API.')
 92    jira_group = parser.add_argument_group('JIRA server connection options')
 93    jira_group.add_argument('-s', '--server',
 94                            help='The JIRA instance to connect to, including context path.')
 95    jira_group.add_argument('-r', '--rest-path',
 96                            help='The root path of the REST API to use.')
 97    jira_group.add_argument('-v', '--rest-api-version',
 98                            help='The version of the API under the specified name.')
 99
100    basic_auth_group = parser.add_argument_group('BASIC auth options')
101    basic_auth_group.add_argument('-u', '--username',
102                                  help='The username to connect to this JIRA instance with.')
103    basic_auth_group.add_argument('-p', '--password',
104                                  help='The password associated with this user.')
105    basic_auth_group.add_argument('-P', '--prompt-for-password', action='store_true',
106                                  help='Prompt for the password at the command line.')
107
108    oauth_group = parser.add_argument_group('OAuth options')
109    oauth_group.add_argument('-od', '--oauth-dance', action='store_true',
110                             help='Start a 3-legged OAuth authentication dance with JIRA.')
111    oauth_group.add_argument('-ck', '--consumer-key',
112                             help='OAuth consumer key.')
113    oauth_group.add_argument('-k', '--key-cert',
114                             help='Private key to sign OAuth requests with (should be the pair of the public key\
115                                   configured in the JIRA application link)')
116    oauth_group.add_argument('-pt', '--print-tokens', action='store_true',
117                             help='Print the negotiated OAuth tokens as they are retrieved.')
118
119    oauth_already_group = parser.add_argument_group('OAuth options for already-authenticated access tokens')
120    oauth_already_group.add_argument('-at', '--access-token',
121                             help='OAuth access token for the user.')
122    oauth_already_group.add_argument('-ats', '--access-token-secret',
123                             help='Secret for the OAuth access token.')
124
125    args = parser.parse_args()
126
127    options = {}
128    if args.server:
129        options['server'] = args.server
130    if args.rest_path:
131        options['rest_path'] = args.rest_path
132    if args.rest_api_version:
133        options['rest_api_version'] = args.rest_api_version
134
135    if args.prompt_for_password:
136        args.password = getpass()
137
138    basic_auth = (args.username, args.password) if args.username and args.password else ()
139
140    key_cert_data = None
141    if args.key_cert:
142        with open(args.key_cert, 'r') as key_cert_file:
143            key_cert_data = key_cert_file.read()
144
145    oauth = None
146    if args.oauth_dance:
147        oauth = oauth_dance(args.server, args.consumer_key, key_cert_data, args.print_tokens)
148    elif args.access_token and args.access_token_secret and args.consumer_key and args.key_cert:
149        oauth = {
150            'access_token': args.access_token,
151            'access_token_secret': args.access_token_secret,
152            'consumer_key': args.consumer_key,
153            'key_cert': key_cert_data,
154        }
155
156    return options, basic_auth, oauth
157
158def get_config():
159    if os.path.exists(CONFIG_PATH):
160        options, basic_auth, oauth = process_config()
161
162    cmd_options, cmd_basic_auth, cmd_oauth = process_command_line()
163
164    options.update(cmd_options)
165    basic_auth.update(cmd_basic_auth)
166    oauth.update(cmd_oauth)
167
168    return options, basic_auth, oauth
169
170def main():
171    try:
172        get_ipython
173    except NameError:
174        pass
175    else:
176        exit("Running ipython inside ipython isn't supported. :(")
177
178    options, basic_auth, oauth = get_config()
179
180    jira = JIRA(options=options, basic_auth=basic_auth, oauth=oauth)
181
182    from IPython.frontend.terminal.embed import InteractiveShellEmbed
183
184    ipshell = InteractiveShellEmbed(banner1='<JIRA Shell ' + __version__ + ' (' + jira.client_info() + ')>')
185    ipshell("*** JIRA shell active; client is in 'jira'."
186            ' Press Ctrl-D to exit.')
187
188if __name__ == '__main__':
189    status = main()
190    exit(status)