2121)
2222
2323
24- try :
25- # Python 3
24+ if sys .version_info [0 ] == 3 :
2625 from configparser import ConfigParser , RawConfigParser
27- except ImportError :
28- # Python 2
26+ JSONDecodeError = json . JSONDecodeError
27+ elif sys . version_info [ 0 ] == 2 :
2928 from ConfigParser import ConfigParser , RawConfigParser
30-
31- try :
32- # Python 3
33- JSONDecodeError = json .decode .JSONDecodeError
34- except AttributeError :
35- # Python 2
3629 JSONDecodeError = ValueError
37-
38- try :
39- input = raw_input
40- except NameError :
41- pass
42-
43- if sys .version_info [0 ] == 2 :
4430 FileNotFoundError = IOError
4531
4632logger = logging .getLogger (__name__ )
@@ -132,8 +118,48 @@ def parse(self):
132118 self .parser .add_option (
133119 '--ignore-ssl-errors' , action = 'store_true' , dest = 'ignore_ssl_certificate_errors' , default = False ,
134120 help = textwrap .dedent ('''
135- Ignore any SSL certificate that may be encountered. Note that it is
136- recommended to resolve certificate errors for production.
121+ Ignore any SSL certificate that may be encountered. Note that
122+ it is recommended to resolve certificate errors for production.
123+ This option makes the `ca-bundle` flag ignored.
124+ ''' ),
125+ )
126+
127+ self .parser .add_option (
128+ '--ca-bundle' ,
129+ dest = 'ca_bundle' ,
130+ default = None ,
131+ help = textwrap .dedent ('''
132+ The path to a file of concatenated CA certificates in PEM
133+ format, or a directory of such files.
134+ ''' ),
135+ )
136+
137+ self .parser .add_option (
138+ '--client-cert' ,
139+ dest = 'client_cert' ,
140+ default = None ,
141+ help = textwrap .dedent ('''
142+ Client side certificate to send with requests. Should be a path
143+ to a single file in PEM format containing the certificate
144+ as well as any number of CA certificates needed to establish
145+ the certificate’s authenticity.
146+
147+ If `--client-cert-key` is not given, this file must also contain
148+ the private key of the client certificate.
149+ ''' ),
150+ )
151+
152+ self .parser .add_option (
153+ '--client-cert-key' ,
154+ dest = 'client_cert_key' ,
155+ default = None ,
156+ help = textwrap .dedent ('''
157+ Private key for the client side certificate given in
158+ `--client-cert`.
159+
160+ If `--client-cert` is given but this argument is not, then the
161+ client cert file given with `--client-cert` must contain the
162+ private key.
137163 ''' ),
138164 )
139165
@@ -145,6 +171,9 @@ def parse(self):
145171 s = repr (toggled_options ).strip ("[]" )
146172 self .parser .error ("Options %s are mutually exclusive" % s )
147173
174+ if self .options .client_cert_key and not self .options .client_cert :
175+ self .parser .error ("'--client-cert-key' given without '--client-cert'" )
176+
148177 def add_globalopts (self ):
149178 '''
150179 Misc global options
@@ -377,6 +406,10 @@ def get_login_details(self):
377406 'SALTAPI_USER' : None ,
378407 'SALTAPI_PASS' : None ,
379408 'SALTAPI_EAUTH' : 'auto' ,
409+
410+ 'SALTAPI_CA_BUNDLE' : None ,
411+ 'SALTAPI_CLIENT_CERT' : None ,
412+ 'SALTAPI_CLIENT_CERT_KEY' : None ,
380413 }
381414
382415 try :
@@ -396,33 +429,37 @@ def get_login_details(self):
396429 for key , value in list (results .items ()):
397430 results [key ] = os .environ .get (key , results [key ])
398431
399- if results ['SALTAPI_EAUTH' ] == 'kerberos' :
400- results ['SALTAPI_PASS' ] = None
432+ ret = {}
401433
402- if self .options .eauth :
403- results ['SALTAPI_EAUTH' ] = self .options .eauth
404- if self .options .token_expire :
405- results ['SALTAPI_TOKEN_EXPIRE' ] = self .options .token_expire
406- if self .options .username is None and results ['SALTAPI_USER' ] is None :
434+ if not self .options .username and not results .get ('SALTAPI_USER' ):
407435 if self .options .interactive :
408- results [ 'SALTAPI_USER ' ] = input ('Username: ' )
436+ ret [ 'username ' ] = input ('Username: ' )
409437 else :
410438 raise PepperAuthException ("SALTAPI_USER required" )
411439 else :
412- if self .options .username is not None :
413- results [ 'SALTAPI_USER' ] = self . options . username
414- if self .options .password is None and \
415- results ['SALTAPI_PASS' ] is None and \
440+ ret [ 'username' ] = self .options .username or results [ "SALTAPI_USER" ]
441+
442+ if not self .options .password and \
443+ not results ['SALTAPI_PASS' ] and \
416444 results ['SALTAPI_EAUTH' ] != 'kerberos' :
417445 if self .options .interactive :
418- results [ 'SALTAPI_PASS ' ] = getpass .getpass (prompt = 'Password: ' )
446+ ret [ 'password ' ] = getpass .getpass (prompt = 'Password: ' )
419447 else :
420448 raise PepperAuthException ("SALTAPI_PASS required" )
421449 else :
422- if self .options .password is not None :
423- results ['SALTAPI_PASS' ] = self .options .password
450+ ret ['password' ] = self .options .password or results ['SALTAPI_PASS' ]
451+
452+ if results ['SALTAPI_EAUTH' ] == 'kerberos' :
453+ ret ['password' ] = None
424454
425- return results
455+ ret ['eauth' ] = self .options .eauth or results .get ('SALTAPI_EAUTH' )
456+ ret ['token_expire' ] = self .options .token_expire or results .get ('SALTAPI_TOKEN_EXPIRE' )
457+ ret ['token_expire' ] = ret ['token_expire' ] and int (ret ['token_expire' ])
458+ ret ['ca_bundle' ] = self .options .ca_bundle or results .get ('SALTAPI_CA_BUNDLE' )
459+ ret ['client_cert' ] = self .options .client_cert or results .get ('SALTAPI_CLIENT_CERT' )
460+ ret ['client_cert_key' ] = self .options .client_cert_key or results .get ('SALTAPI_CLIENT_CERT_KEY' )
461+
462+ return ret
426463
427464 def parse_url (self ):
428465 '''
@@ -451,25 +488,6 @@ def parse_url(self):
451488
452489 return url
453490
454- def parse_login (self ):
455- '''
456- Extract the authentication credentials
457- '''
458- login_details = self .get_login_details ()
459-
460- # Auth values placeholder; grab interactively at CLI or from config
461- username = login_details ['SALTAPI_USER' ]
462- password = login_details ['SALTAPI_PASS' ]
463- eauth = login_details ['SALTAPI_EAUTH' ]
464-
465- ret = dict (username = username , password = password , eauth = eauth )
466-
467- token_expire = login_details .get ('SALTAPI_TOKEN_EXPIRE' , None )
468- if token_expire :
469- ret ['token_expire' ] = int (token_expire )
470-
471- return ret
472-
473491 def parse_cmd (self , api ):
474492 '''
475493 Extract the low data for a command from the passed CLI params
@@ -604,23 +622,24 @@ def poll_for_returns(self, api, load):
604622 if failed :
605623 yield exit_code , [{'Failed' : failed }]
606624
607- def login (self , api ):
625+ def login (self , api , login_details ):
608626 login = api .token if self .options .userun else api .login
609627
610628 if self .options .mktoken :
611629 token_file = self .options .cache
612630 try :
613631 with open (token_file , 'rt' ) as f :
614632 auth = json .load (f )
615- if auth ['expire' ] < time .time ()+ 30 :
633+ if auth ['expire' ] < time .time () + 30 :
616634 logger .error ('Login token expired' )
617635 raise Exception ('Login token expired' )
618636 except Exception as e :
619637 if e .args [0 ] != 2 :
620638 logger .error ('Unable to load login token from {0} {1}' .format (token_file , str (e )))
621639 if os .path .isfile (token_file ):
622640 os .remove (token_file )
623- auth = login (** self .parse_login ())
641+ auth = login (** login_details )
642+
624643 try :
625644 oldumask = os .umask (0 )
626645 fdsc = os .open (token_file , os .O_WRONLY | os .O_CREAT , 0o600 )
@@ -631,7 +650,7 @@ def login(self, api):
631650 finally :
632651 os .umask (oldumask )
633652 else :
634- auth = login (** self . parse_login () )
653+ auth = login (** login_details )
635654
636655 api .auth = auth
637656 self .auth = auth
@@ -662,12 +681,18 @@ def run(self):
662681 rootLogger .addHandler (logging .StreamHandler ())
663682 rootLogger .setLevel (max (logging .ERROR - (self .options .verbose * 10 ), 1 ))
664683
684+ login_details = self .get_login_details ()
685+
665686 api = pepper .Pepper (
666687 self .parse_url (),
667688 debug_http = self .options .debug_http ,
668- ignore_ssl_errors = self .options .ignore_ssl_certificate_errors )
689+ ignore_ssl_errors = self .options .ignore_ssl_certificate_errors ,
690+ ca_bundle = login_details .get ("ca_bundle" ),
691+ client_cert = login_details .get ("client_cert" ),
692+ client_cert_key = login_details .get ("client_cert_key" ),
693+ )
669694
670- self .login (api )
695+ self .login (api , login_details )
671696
672697 load = self .parse_cmd (api )
673698
0 commit comments