diff options
author | Olle Lundberg <geek@nerd.sh> | 2012-10-16 13:54:23 +0200 |
---|---|---|
committer | Olle Lundberg <geek@nerd.sh> | 2012-10-16 13:57:05 +0200 |
commit | ad587fa0ef32eeeb633245c7ae68759765c0e439 (patch) | |
tree | 6081670aa1b7608a2e6a4d1b0b4305494a50d077 | |
parent | f33481cc44fb57a788b5726b529383f300d06b36 (diff) |
Add host negation support to paramiko config.
This is a rewrite of the SSHConfig class to
conform with the rules specified by the
manpage for ssh_config.
This change also adds support for negation
according to the rules introduced by
OpenSSH 5.9. Reference:
http://www.openssh.com/txt/release-5.9
-rw-r--r-- | paramiko/config.py | 80 |
1 files changed, 50 insertions, 30 deletions
diff --git a/paramiko/config.py b/paramiko/config.py index 458d5dd0..802d9155 100644 --- a/paramiko/config.py +++ b/paramiko/config.py @@ -1,4 +1,5 @@ # Copyright (C) 2006-2007 Robey Pointer <robeypointer@gmail.com> +# Copyright (C) 2012 Olle Lundberg <geek@nerd.sh> # # This file is part of paramiko. # @@ -41,7 +42,8 @@ class SSHConfig (object): """ Create a new OpenSSH config object. """ - self._config = [ { 'host': '*' } ] + self._config = [] + def parse(self, file_obj): """ @@ -50,7 +52,7 @@ class SSHConfig (object): @param file_obj: a file-like object to read the config file from @type file_obj: file """ - configs = [self._config[0]] + host = {"host":['*'],"config":{}} for line in file_obj: line = line.rstrip('\n').lstrip() if (line == '') or (line[0] == '#'): @@ -69,20 +71,20 @@ class SSHConfig (object): value = line[i:].lstrip() if key == 'host': - del configs[:] - # the value may be multiple hosts, space-delimited - for host in value.split(): - # do we have a pre-existing host config to append to? - matches = [c for c in self._config if c['host'] == host] - if len(matches) > 0: - configs.append(matches[0]) - else: - config = { 'host': host } - self._config.append(config) - configs.append(config) - else: - for config in configs: - config[key] = value + self._config.append(host) + value = value.split() + host = {key:value,'config':{}} + #identitifile is a special case, since it is allowed to be + # specified multiple times and they should be tried in order + # of specification. + elif key == 'identityfile': + if key in host['config']: + host['config']['identityfile'].append(value) + else: + host['config']['identityfile'] = [value] + elif key not in host['config']: + host['config'].update({key:value}) + self._config.append(host) def lookup(self, hostname): """ @@ -97,31 +99,45 @@ class SSHConfig (object): will win out. The keys in the returned dict are all normalized to lowercase (look for - C{"port"}, not C{"Port"}. No other processing is done to the keys or - values. + C{"port"}, not C{"Port"}. The values are processed according to the + rules for substitution variable expansion in C{ssh_config}. @param hostname: the hostname to lookup @type hostname: str """ - matches = [x for x in self._config if fnmatch.fnmatch(hostname, x['host'])] - # Move * to the end - _star = matches.pop(0) - matches.append(_star) + + matches = [ config for config in self._config if + self._allowed(hostname,config['host']) ] + ret = {} - for m in matches: - for k,v in m.iteritems(): - if not k in ret: - ret[k] = v + for match in matches: + for key in match['config']: + value = match['config'][key] + if key == 'identityfile': + if key in ret: + ret['identityfile'].extend(value) + else: + ret['identityfile'] = value + elif key not in ret: + ret[key] = value ret = self._expand_variables(ret, hostname) - del ret['host'] return ret - def _expand_variables(self, config, hostname ): + def _allowed(self, hostname, hosts): + match = False + for host in hosts: + if host.startswith('!') and fnmatch.fnmatch(hostname, host[1:]): + return False + elif fnmatch.fnmatch(hostname, host): + match = True + return match + + def _expand_variables(self, config, hostname): """ Return a dict of config options with expanded substitutions for a given hostname. - Please refer to man ssh_config(5) for the parameters that + Please refer to man C{ssh_config} for the parameters that are replaced. @param config: the config for the hostname @@ -172,5 +188,9 @@ class SSHConfig (object): for k in config: if k in replacements: for find, replace in replacements[k]: - config[k] = config[k].replace(find, str(replace)) + if isinstance(config[k],list): + for item in range(len(config[k])): + config[k][item] = config[k][item].replace(find, str(replace)) + else: + config[k] = config[k].replace(find, str(replace)) return config |