113113from ansible .module_utils .urls import fetch_url
114114
115115
116- gpg_bin = None
117- grep_bin = None
118116apt_key_bin = None
119117
120118
121119def find_needed_binaries (module ):
122- global gpg_bin
123- global grep_bin
124120 global apt_key_bin
125121
126- gpg_bin = module .get_bin_path ('gpg' , required = True )
127- grep_bin = module .get_bin_path ('grep' , required = True )
128122 apt_key_bin = module .get_bin_path ('apt-key' , required = True )
129123
124+ ### FIXME: Is there a reason that gpg and grep are checked? Is it just
125+ # cruft or does the apt .deb package not require them (and if they're not
126+ # installed, /usr/bin/apt-key fails?)
127+ module .get_bin_path ('gpg' , required = True )
128+ module .get_bin_path ('grep' , required = True )
129+
130+
131+ def parse_key_id (key_id ):
132+ """validate the key_id and break it into segments
133+
134+ :arg key_id: The key_id as supplied by the user. A valid key_id will be
135+ 8, 16, or more hexadecimal chars with an optional leading ``0x``.
136+ :returns: The portion of key_id suitable for apt-key del, the portion
137+ suitable for comparisons with --list-public-keys, and the portion that
138+ can be used with --recv-key. If key_id is long enough, these will be
139+ the last 8 characters of key_id, the last 16 characters, and all of
140+ key_id. If key_id is not long enough, some of the values will be the
141+ same.
142+
143+ * apt-key del <= 1.10 has a bug with key_id != 8 chars
144+ * apt-key adv --list-public-keys prints 16 chars
145+ * apt-key adv --recv-key can take more chars
146+
147+ """
148+ # Make sure the key_id is valid hexadecimal
149+ int (key_id , 16 )
150+
151+ key_id = key_id .upper ()
152+ if key_id .startswith ('0X' ):
153+ key_id = key_id [2 :]
154+
155+ key_id_len = len (key_id )
156+ if (key_id_len != 8 or key_id_len != 16 ) and key_id_len <= 16 :
157+ raise ValueError ('key_id must be 8, 16, or 16+ hexadecimal characters in length' )
158+
159+ short_key_id = key_id [- 8 :]
160+
161+ fingerprint = key_id
162+ if key_id_len > 16 :
163+ fingerprint = key_id [- 16 :]
164+
165+ return short_key_id , fingerprint , key_id
166+
130167
131168def all_keys (module , keyring , short_format ):
132169 if keyring :
133- cmd = "apt-key --keyring %s adv --list-public-keys --keyid-format=long" % keyring
170+ cmd = "%s --keyring %s adv --list-public-keys --keyid-format=long" % ( apt_key_bin , keyring )
134171 else :
135- cmd = "apt-key adv --list-public-keys --keyid-format=long"
172+ cmd = "%s adv --list-public-keys --keyid-format=long" % apt_key_bin
136173 (rc , out , err ) = module .run_command (cmd )
137174 results = []
138175 lines = to_native (out ).split ('\n ' )
@@ -176,9 +213,9 @@ def download_key(module, url):
176213
177214def import_key (module , keyring , keyserver , key_id ):
178215 if keyring :
179- cmd = "apt-key --keyring %s adv --keyserver %s --recv %s" % (keyring , keyserver , key_id )
216+ cmd = "%s --keyring %s adv --keyserver %s --recv %s" % (apt_key_bin , keyring , keyserver , key_id )
180217 else :
181- cmd = "apt-key adv --keyserver %s --recv %s" % (keyserver , key_id )
218+ cmd = "%s adv --keyserver %s --recv %s" % (apt_key_bin , keyserver , key_id )
182219 for retry in range (5 ):
183220 lang_env = dict (LANG = 'C' , LC_ALL = 'C' , LC_MESSAGES = 'C' )
184221 (rc , out , err ) = module .run_command (cmd , environ_update = lang_env )
@@ -198,25 +235,25 @@ def import_key(module, keyring, keyserver, key_id):
198235def add_key (module , keyfile , keyring , data = None ):
199236 if data is not None :
200237 if keyring :
201- cmd = "apt-key --keyring %s add -" % keyring
238+ cmd = "%s --keyring %s add -" % ( apt_key_bin , keyring )
202239 else :
203- cmd = "apt-key add -"
240+ cmd = "%s add -" % apt_key_bin
204241 (rc , out , err ) = module .run_command (cmd , data = data , check_rc = True , binary_data = True )
205242 else :
206243 if keyring :
207- cmd = "apt-key --keyring %s add %s" % (keyring , keyfile )
244+ cmd = "%s --keyring %s add %s" % (apt_key_bin , keyring , keyfile )
208245 else :
209- cmd = "apt-key add %s" % (keyfile )
246+ cmd = "%s add %s" % (apt_key_bin , keyfile )
210247 (rc , out , err ) = module .run_command (cmd , check_rc = True )
211248 return True
212249
213250
214251def remove_key (module , key_id , keyring ):
215252 # FIXME: use module.run_command, fail at point of error and don't discard useful stdin/stdout
216253 if keyring :
217- cmd = 'apt-key --keyring %s del %s' % (keyring , key_id )
254+ cmd = '%s --keyring %s del %s' % (apt_key_bin , keyring , key_id )
218255 else :
219- cmd = 'apt-key del %s' % key_id
256+ cmd = '%s del %s' % ( apt_key_bin , key_id )
220257 (rc , out , err ) = module .run_command (cmd , check_rc = True )
221258 return True
222259
@@ -234,7 +271,8 @@ def main():
234271 keyserver = dict (required = False ),
235272 state = dict (required = False , choices = ['present' , 'absent' ], default = 'present' )
236273 ),
237- supports_check_mode = True
274+ supports_check_mode = True ,
275+ mutually_exclusive = (('filename' , 'keyserver' , 'data' , 'url' ),),
238276 )
239277
240278 key_id = module .params ['id' ]
@@ -246,61 +284,62 @@ def main():
246284 keyserver = module .params ['keyserver' ]
247285 changed = False
248286
287+ fingerprint = short_key_id = key_id
288+ short_format = False
249289 if key_id :
250290 try :
251- int (key_id , 16 )
252- if key_id .startswith ('0x' ):
253- key_id = key_id [2 :]
254- key_id = key_id .upper ()
255- short_key_id = key_id [- 8 :]
291+ key_id , fingerprint , short_key_id = parse_key_id (key_id )
256292 except ValueError :
257- module .fail_json (msg = " Invalid key_id" , id = key_id )
293+ module .fail_json (msg = ' Invalid key_id' , id = key_id )
258294
259- find_needed_binaries (module )
295+ if len (fingerprint ) == 8 :
296+ short_format = True
260297
261- short_format = False
262- if key_id == short_key_id :
263- short_format = True
298+ find_needed_binaries (module )
264299
265300 keys = all_keys (module , keyring , short_format )
266301 return_values = {}
267302
268303 if state == 'present' :
269- if key_id and key_id in keys :
304+ if fingerprint and fingerprint in keys :
270305 module .exit_json (changed = False )
306+ elif fingerprint and fingerprint not in keys and module .check_mode :
307+ ### TODO: Someday we could go further -- write keys out to
308+ # a temporary file and then extract the key id from there via gpg
309+ # to decide if the key is installed or not.
310+ module .exit_json (changed = True )
271311 else :
272312 if not filename and not data and not keyserver :
273313 data = download_key (module , url )
274- if key_id and key_id in keys :
275- module .exit_json (changed = False )
314+
315+ if filename :
316+ add_key (module , filename , keyring )
317+ elif keyserver :
318+ import_key (module , keyring , keyserver , key_id )
276319 else :
277- if module .check_mode :
278- module .exit_json (changed = True )
279- if filename :
280- add_key (module , filename , keyring )
281- elif keyserver :
282- import_key (module , keyring , keyserver , key_id )
283- else :
284- add_key (module , "-" , keyring , data )
285- changed = False
286- keys2 = all_keys (module , keyring , short_format )
287- if len (keys ) != len (keys2 ):
288- changed = True
289- if key_id and key_id not in keys2 :
290- module .fail_json (msg = "key does not seem to have been added" , id = key_id )
291- module .exit_json (changed = changed )
320+ add_key (module , "-" , keyring , data )
321+
322+ changed = False
323+ keys2 = all_keys (module , keyring , short_format )
324+ if len (keys ) != len (keys2 ):
325+ changed = True
326+
327+ if fingerprint and fingerprint not in keys2 :
328+ module .fail_json (msg = "key does not seem to have been added" , id = key_id )
329+ module .exit_json (changed = changed )
330+
292331 elif state == 'absent' :
293332 if not key_id :
294333 module .fail_json (msg = "key is required" )
295- if key_id in keys :
334+ if fingerprint in keys :
296335 if module .check_mode :
297336 module .exit_json (changed = True )
298337
299338 # we use the "short" id: key_id[-8:], short_format=True
300339 # it's a workaround for https://bugs.launchpad.net/ubuntu/+source/apt/+bug/1481871
301340 if remove_key (module , short_key_id , keyring ):
302341 keys = all_keys (module , keyring , short_format )
303- if key_id in keys :
342+ if fingerprint in keys :
304343 module .fail_json (msg = "apt-key del did not return an error but the key was not removed (check that the id is correct and *not* a subkey)" , id = key_id )
305344 changed = True
306345 else :
0 commit comments