Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

authorized_key module: rewrite options to dict parser, fixes #5032 #5107

Merged
merged 1 commit into from
Dec 2, 2013
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
58 changes: 35 additions & 23 deletions library/system/authorized_key
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,7 @@ import os
import pwd
import os.path
import tempfile
import re
import shlex

def keyfile(module, user, write=False, path=None, manage_dir=True):
Expand Down Expand Up @@ -170,33 +171,44 @@ def keyfile(module, user, write=False, path=None, manage_dir=True):

return keysfile

def parseoptions(options):
def parseoptions(module, options):
'''
reads a string containing ssh-key options
and returns a dictionary of those options
'''
options_dict = {}
if options:
lex = shlex.shlex(options)
lex.quotes = ["'", '"']
lex.whitespace_split = True
opt_parts = list(lex)

#options_list = options.strip().split(",")
options_list = opt_parts
for option in options_list:
# happen when there is comma at the end
if option == '':
continue
if option.find("=") != -1:
(arg,val) = option.split("=", 1)
token_exp = [
# matches separator
(r',+', False),
# matches option with value, e.g. from="x,y"
(r'([a-z0-9-]+)="((?:[^"\\]|\\.)*)"', True),
# matches single option, e.g. no-agent-forwarding
(r'[a-z0-9-]+', True)
]

pos = 0
while pos < len(options):
match = None
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:
arg = option
val = None
options_dict[arg] = val.replace('"', '').replace("'", "")
pos = match.end(0)

return options_dict

def parsekey(raw_key):
def parsekey(module, raw_key):
'''
parses a key, which may or may not contain a list
of ssh-key options at the beginning
Expand Down Expand Up @@ -239,7 +251,7 @@ def parsekey(raw_key):
options = key_parts[0]

# parse the options (if any)
options = parseoptions(options)
options = parseoptions(module, options)

# get key after the type index
key = key_parts[(type_index + 1)]
Expand All @@ -250,15 +262,15 @@ def parsekey(raw_key):

return (key, key_type, options, comment)

def readkeys(filename):
def readkeys(module, filename):

if not os.path.isfile(filename):
return {}

keys = {}
f = open(filename)
for line in f.readlines():
key_data = parsekey(line)
key_data = parsekey(module, line)
if key_data:
# use key as identifier
keys[key_data[0]] = key_data
Expand Down Expand Up @@ -314,14 +326,14 @@ def enforce_state(module, params):
# check current state -- just get the filename, don't create file
do_write = False
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.
for new_key in key:
if key_options is not None:
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:
module.fail_json(msg="invalid key specified: %s" % new_key)

Expand Down