|
|
@ -111,6 +111,7 @@ import os
|
|
|
|
import pwd
|
|
|
|
import pwd
|
|
|
|
import os.path
|
|
|
|
import os.path
|
|
|
|
import tempfile
|
|
|
|
import tempfile
|
|
|
|
|
|
|
|
import re
|
|
|
|
import shlex
|
|
|
|
import shlex
|
|
|
|
|
|
|
|
|
|
|
|
def keyfile(module, user, write=False, path=None, manage_dir=True):
|
|
|
|
def keyfile(module, user, write=False, path=None, manage_dir=True):
|
|
|
@ -170,33 +171,44 @@ def keyfile(module, user, write=False, path=None, manage_dir=True):
|
|
|
|
|
|
|
|
|
|
|
|
return keysfile
|
|
|
|
return keysfile
|
|
|
|
|
|
|
|
|
|
|
|
def parseoptions(options):
|
|
|
|
def parseoptions(module, options):
|
|
|
|
'''
|
|
|
|
'''
|
|
|
|
reads a string containing ssh-key options
|
|
|
|
reads a string containing ssh-key options
|
|
|
|
and returns a dictionary of those options
|
|
|
|
and returns a dictionary of those options
|
|
|
|
'''
|
|
|
|
'''
|
|
|
|
options_dict = {}
|
|
|
|
options_dict = {}
|
|
|
|
if options:
|
|
|
|
if options:
|
|
|
|
lex = shlex.shlex(options)
|
|
|
|
token_exp = [
|
|
|
|
lex.quotes = ["'", '"']
|
|
|
|
# matches separator
|
|
|
|
lex.whitespace_split = True
|
|
|
|
(r',+', False),
|
|
|
|
opt_parts = list(lex)
|
|
|
|
# matches option with value, e.g. from="x,y"
|
|
|
|
|
|
|
|
(r'([a-z0-9-]+)="((?:[^"\\]|\\.)*)"', True),
|
|
|
|
#options_list = options.strip().split(",")
|
|
|
|
# matches single option, e.g. no-agent-forwarding
|
|
|
|
options_list = opt_parts
|
|
|
|
(r'[a-z0-9-]+', True)
|
|
|
|
for option in options_list:
|
|
|
|
]
|
|
|
|
# happen when there is comma at the end
|
|
|
|
|
|
|
|
if option == '':
|
|
|
|
pos = 0
|
|
|
|
continue
|
|
|
|
while pos < len(options):
|
|
|
|
if option.find("=") != -1:
|
|
|
|
match = None
|
|
|
|
(arg,val) = option.split("=", 1)
|
|
|
|
for pattern, is_valid_option in token_exp:
|
|
|
|
|
|
|
|
regex = re.compile(pattern, re.IGNORECASE)
|
|
|
|
|
|
|
|
match = regex.match(options, pos)
|
|
|
|
|
|
|
|
if match:
|
|
|
|
|
|
|
|
text = match.group(0)
|
|
|
|
|
|
|
|
if is_valid_option:
|
|
|
|
|
|
|
|
if len(match.groups()) == 2:
|
|
|
|
|
|
|
|
options_dict[match.group(1)] = match.group(2)
|
|
|
|
|
|
|
|
else:
|
|
|
|
|
|
|
|
options_dict[text] = None
|
|
|
|
|
|
|
|
break
|
|
|
|
|
|
|
|
if not match:
|
|
|
|
|
|
|
|
module.fail_json(msg="invalid option string: %s" % options)
|
|
|
|
else:
|
|
|
|
else:
|
|
|
|
arg = option
|
|
|
|
pos = match.end(0)
|
|
|
|
val = None
|
|
|
|
|
|
|
|
options_dict[arg] = val.replace('"', '').replace("'", "")
|
|
|
|
|
|
|
|
return options_dict
|
|
|
|
return options_dict
|
|
|
|
|
|
|
|
|
|
|
|
def parsekey(raw_key):
|
|
|
|
def parsekey(module, raw_key):
|
|
|
|
'''
|
|
|
|
'''
|
|
|
|
parses a key, which may or may not contain a list
|
|
|
|
parses a key, which may or may not contain a list
|
|
|
|
of ssh-key options at the beginning
|
|
|
|
of ssh-key options at the beginning
|
|
|
@ -239,7 +251,7 @@ def parsekey(raw_key):
|
|
|
|
options = key_parts[0]
|
|
|
|
options = key_parts[0]
|
|
|
|
|
|
|
|
|
|
|
|
# parse the options (if any)
|
|
|
|
# parse the options (if any)
|
|
|
|
options = parseoptions(options)
|
|
|
|
options = parseoptions(module, options)
|
|
|
|
|
|
|
|
|
|
|
|
# get key after the type index
|
|
|
|
# get key after the type index
|
|
|
|
key = key_parts[(type_index + 1)]
|
|
|
|
key = key_parts[(type_index + 1)]
|
|
|
@ -250,7 +262,7 @@ def parsekey(raw_key):
|
|
|
|
|
|
|
|
|
|
|
|
return (key, key_type, options, comment)
|
|
|
|
return (key, key_type, options, comment)
|
|
|
|
|
|
|
|
|
|
|
|
def readkeys(filename):
|
|
|
|
def readkeys(module, filename):
|
|
|
|
|
|
|
|
|
|
|
|
if not os.path.isfile(filename):
|
|
|
|
if not os.path.isfile(filename):
|
|
|
|
return {}
|
|
|
|
return {}
|
|
|
@ -258,7 +270,7 @@ def readkeys(filename):
|
|
|
|
keys = {}
|
|
|
|
keys = {}
|
|
|
|
f = open(filename)
|
|
|
|
f = open(filename)
|
|
|
|
for line in f.readlines():
|
|
|
|
for line in f.readlines():
|
|
|
|
key_data = parsekey(line)
|
|
|
|
key_data = parsekey(module, line)
|
|
|
|
if key_data:
|
|
|
|
if key_data:
|
|
|
|
# use key as identifier
|
|
|
|
# use key as identifier
|
|
|
|
keys[key_data[0]] = key_data
|
|
|
|
keys[key_data[0]] = key_data
|
|
|
@ -314,14 +326,14 @@ def enforce_state(module, params):
|
|
|
|
# check current state -- just get the filename, don't create file
|
|
|
|
# check current state -- just get the filename, don't create file
|
|
|
|
do_write = False
|
|
|
|
do_write = False
|
|
|
|
params["keyfile"] = keyfile(module, user, do_write, path, manage_dir)
|
|
|
|
params["keyfile"] = keyfile(module, user, do_write, path, manage_dir)
|
|
|
|
existing_keys = readkeys(params["keyfile"])
|
|
|
|
existing_keys = readkeys(module, params["keyfile"])
|
|
|
|
|
|
|
|
|
|
|
|
# Check our new keys, if any of them exist we'll continue.
|
|
|
|
# Check our new keys, if any of them exist we'll continue.
|
|
|
|
for new_key in key:
|
|
|
|
for new_key in key:
|
|
|
|
if key_options is not None:
|
|
|
|
if key_options is not None:
|
|
|
|
new_key = "%s %s" % (key_options, new_key)
|
|
|
|
new_key = "%s %s" % (key_options, new_key)
|
|
|
|
|
|
|
|
|
|
|
|
parsed_new_key = parsekey(new_key)
|
|
|
|
parsed_new_key = parsekey(module, new_key)
|
|
|
|
if not parsed_new_key:
|
|
|
|
if not parsed_new_key:
|
|
|
|
module.fail_json(msg="invalid key specified: %s" % new_key)
|
|
|
|
module.fail_json(msg="invalid key specified: %s" % new_key)
|
|
|
|
|
|
|
|
|
|
|
|