blob: 40703c5109207dc774e87a97bc92ce8e3b121209 [file] [log] [blame]
Vadim Shtayurac4c76b62014-01-13 15:05:41 -08001#!/usr/bin/env python
2# Copyright 2013 The Swarming Authors. All rights reserved.
3# Use of this source code is governed under the Apache License, Version 2.0 that
4# can be found in the LICENSE file.
5
6"""Client tool to perform various authentication related tasks."""
7
8__version__ = '0.3'
9
10import optparse
11import sys
12
13from third_party import colorama
14from third_party.depot_tools import fix_encoding
15from third_party.depot_tools import subcommand
16
17from utils import net
18from utils import oauth
19from utils import tools
20
21
22def add_auth_options(parser):
23 """Adds command line options related to authentication."""
24 parser.auth_group = optparse.OptionGroup(parser, 'Authentication')
25 parser.auth_group.add_option(
Vadim Shtayura5d1efce2014-02-04 10:55:43 -080026 '--auth-method',
27 metavar='METHOD',
28 default='bot' if tools.is_headless() else 'oauth',
29 help='Authentication method to use: %s. [default: %%default]' %
30 ', '.join(net.AUTH_METHODS))
Vadim Shtayurac4c76b62014-01-13 15:05:41 -080031 parser.add_option_group(parser.auth_group)
32 oauth.add_oauth_options(parser)
33
34
Vadim Shtayura5d1efce2014-02-04 10:55:43 -080035def process_auth_options(parser, options):
Vadim Shtayurac4c76b62014-01-13 15:05:41 -080036 """Configures process-wide authentication parameters based on |options|."""
Vadim Shtayura5d1efce2014-02-04 10:55:43 -080037 if options.auth_method not in net.AUTH_METHODS:
38 parser.error('Invalid --auth-method value: %s' % options.auth_method)
39 net.configure_auth(options.auth_method, oauth_options=options)
Vadim Shtayurac4c76b62014-01-13 15:05:41 -080040
41
42class AuthServiceError(Exception):
43 """Unexpected response from authentication service."""
44
45
46class AuthService(object):
47 """Represents remote Authentication service."""
48
49 def __init__(self, url):
50 self._service = net.get_http_service(url)
51
Vadim Shtayurae34e13a2014-02-02 11:23:26 -080052 def login(self, allow_user_interaction):
Vadim Shtayurac4c76b62014-01-13 15:05:41 -080053 """Refreshes cached access token or creates a new one."""
Vadim Shtayurae34e13a2014-02-02 11:23:26 -080054 return self._service.login(allow_user_interaction)
Vadim Shtayurac4c76b62014-01-13 15:05:41 -080055
56 def logout(self):
57 """Purges cached access token."""
58 return self._service.logout()
59
60 def get_current_identity(self):
61 """Returns identity associated with currently used credentials.
62
63 Identity is a string:
64 user:<email> - if using OAuth or cookie based authentication.
65 bot:<id> - if using HMAC based authentication.
66 anonymous:anonymous - if not authenticated.
67 """
68 identity = self._service.json_request('GET', '/auth/api/v1/accounts/self')
69 if not identity:
70 raise AuthServiceError('Failed to fetch identity')
71 return identity['identity']
72
73
74@subcommand.usage('[options]')
75def CMDlogin(parser, args):
Vadim Shtayurae34e13a2014-02-02 11:23:26 -080076 """Runs interactive login flow and stores auth token/cookie on disk."""
Vadim Shtayurac4c76b62014-01-13 15:05:41 -080077 (options, args) = parser.parse_args(args)
Vadim Shtayura5d1efce2014-02-04 10:55:43 -080078 process_auth_options(parser, options)
Vadim Shtayurac4c76b62014-01-13 15:05:41 -080079 service = AuthService(options.service)
Vadim Shtayurae34e13a2014-02-02 11:23:26 -080080 if service.login(True):
Vadim Shtayurac4c76b62014-01-13 15:05:41 -080081 print 'Logged in as \'%s\'.' % service.get_current_identity()
82 return 0
83 else:
84 print 'Login failed or canceled.'
85 return 1
86
87
88@subcommand.usage('[options]')
89def CMDlogout(parser, args):
Vadim Shtayurae34e13a2014-02-02 11:23:26 -080090 """Purges cached auth token/cookie."""
Vadim Shtayurac4c76b62014-01-13 15:05:41 -080091 (options, args) = parser.parse_args(args)
Vadim Shtayura5d1efce2014-02-04 10:55:43 -080092 process_auth_options(parser, options)
Vadim Shtayurac4c76b62014-01-13 15:05:41 -080093 service = AuthService(options.service)
94 service.logout()
Vadim Shtayurae34e13a2014-02-02 11:23:26 -080095 return 0
Vadim Shtayurac4c76b62014-01-13 15:05:41 -080096
97
98@subcommand.usage('[options]')
99def CMDcheck(parser, args):
Vadim Shtayurae34e13a2014-02-02 11:23:26 -0800100 """Shows identity associated with currently cached auth token/cookie."""
Vadim Shtayurac4c76b62014-01-13 15:05:41 -0800101 (options, args) = parser.parse_args(args)
Vadim Shtayura5d1efce2014-02-04 10:55:43 -0800102 process_auth_options(parser, options)
Vadim Shtayurac4c76b62014-01-13 15:05:41 -0800103 service = AuthService(options.service)
Vadim Shtayurae34e13a2014-02-02 11:23:26 -0800104 service.login(False)
Vadim Shtayurac4c76b62014-01-13 15:05:41 -0800105 print service.get_current_identity()
Vadim Shtayurae34e13a2014-02-02 11:23:26 -0800106 return 0
Vadim Shtayurac4c76b62014-01-13 15:05:41 -0800107
108
109class OptionParserAuth(tools.OptionParserWithLogging):
110 def __init__(self, **kwargs):
111 tools.OptionParserWithLogging.__init__(self, prog='auth.py', **kwargs)
112 self.server_group = tools.optparse.OptionGroup(self, 'Server')
113 self.server_group.add_option(
114 '-S', '--service',
115 metavar='URL', default='',
116 help='Service to use')
117 self.add_option_group(self.server_group)
118 add_auth_options(self)
119
120 def parse_args(self, *args, **kwargs):
121 options, args = tools.OptionParserWithLogging.parse_args(
122 self, *args, **kwargs)
123 options.service = options.service.rstrip('/')
124 if not options.service:
125 self.error('--service is required.')
126 return options, args
127
128
129def main(args):
130 dispatcher = subcommand.CommandDispatcher(__name__)
131 try:
132 return dispatcher.execute(OptionParserAuth(version=__version__), args)
133 except Exception as e:
134 tools.report_error(e)
135 return 1
136
137
138if __name__ == '__main__':
139 fix_encoding.fix_encoding()
140 tools.disable_buffering()
141 colorama.init()
142 sys.exit(main(sys.argv[1:]))