Make env and plugins commands local again

During thin client refactoring, LocalOrRemote class implementation of `run`
method was overriden by default Command implementation during instantiation of
client plugins from schema. This caused these commands to always forward this
request to IPA master.

This patch restores the original behavior: unless `--server` option was
specified, the commands will always print out local config.

https://fedorahosted.org/freeipa/ticket/6490

Reviewed-By: Stanislav Laznicka <slaznick@redhat.com>
This commit is contained in:
Martin Babinsky
2016-11-28 14:50:57 +01:00
parent 42307ae2dc
commit 0ae7bebb76
3 changed files with 133 additions and 121 deletions

View File

@@ -2,14 +2,15 @@
# Copyright (C) 2016 FreeIPA Contributors see COPYING for license
#
from ipaclient.frontend import CommandOverride
from ipalib.misc import env as _env
from ipalib.misc import plugins as _plugins
from ipalib.plugable import Registry
register = Registry()
@register(override=True, no_fail=True)
class env(CommandOverride):
class env(_env):
def output_for_cli(self, textui, output, *args, **options):
output = dict(output)
output.pop('count', None)
@@ -20,7 +21,7 @@ class env(CommandOverride):
@register(override=True, no_fail=True)
class plugins(CommandOverride):
class plugins(_plugins):
def output_for_cli(self, textui, output, *args, **options):
options['all'] = True
return super(plugins, self).output_for_cli(textui, output,

124
ipalib/misc.py Normal file
View File

@@ -0,0 +1,124 @@
#
# Copyright (C) 2016 FreeIPA Contributors see COPYING for license
#
import re
from ipalib import LocalOrRemote, _, ngettext
from ipalib.output import Output, summary
from ipalib import Flag
from ipalib.plugable import Registry
register = Registry()
# FIXME: We should not let env return anything in_server
# when mode == 'production'. This would allow an attacker to see the
# configuration of the server, potentially revealing compromising
# information. However, it's damn handy for testing/debugging.
class env(LocalOrRemote):
__doc__ = _('Show environment variables.')
msg_summary = _('%(count)d variables')
takes_args = (
'variables*',
)
takes_options = LocalOrRemote.takes_options + (
Flag('all',
cli_name='all',
doc=_('retrieve and print all attributes from the server. Affects command output.'),
exclude='webui',
flags=['no_option', 'no_output'],
default=True,
),
)
has_output = (
Output('result',
type=dict,
doc=_('Dictionary mapping variable name to value'),
),
Output('total',
type=int,
doc=_('Total number of variables env (>= count)'),
flags=['no_display'],
),
Output('count',
type=int,
doc=_('Number of variables returned (<= total)'),
flags=['no_display'],
),
summary,
)
def __find_keys(self, variables):
keys = set()
for query in variables:
if '*' in query:
pat = re.compile(query.replace('*', '.*') + '$')
for key in self.env:
if pat.match(key):
keys.add(key)
elif query in self.env:
keys.add(query)
return keys
def execute(self, variables=None, **options):
if variables is None:
keys = self.env
else:
keys = self.__find_keys(variables)
ret = dict(
result=dict(
(key, self.env[key]) for key in keys
),
count=len(keys),
total=len(self.env),
)
if len(keys) > 1:
ret['summary'] = self.msg_summary % ret
else:
ret['summary'] = None
return ret
class plugins(LocalOrRemote):
__doc__ = _('Show all loaded plugins.')
msg_summary = ngettext(
'%(count)d plugin loaded', '%(count)d plugins loaded', 0
)
takes_options = LocalOrRemote.takes_options + (
Flag('all',
cli_name='all',
doc=_('retrieve and print all attributes from the server. Affects command output.'),
exclude='webui',
flags=['no_option', 'no_output'],
default=True,
),
)
has_output = (
Output('result', dict, 'Dictionary mapping plugin names to bases'),
Output('count',
type=int,
doc=_('Number of plugins loaded'),
),
summary,
)
def execute(self, **options):
result = {}
for namespace in self.api:
for plugin in self.api[namespace]():
cls = type(plugin)
key = '{}.{}'.format(cls.__module__, cls.__name__)
result.setdefault(key, []).append(namespace)
return dict(
result=result,
)

View File

@@ -17,129 +17,16 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
import re
from ipalib import LocalOrRemote, _, ngettext
from ipalib.output import Output, summary
from ipalib import Flag
from ipalib import _
from ipalib.misc import env, plugins
from ipalib.plugable import Registry
__doc__ = _("""
Misc plug-ins
""")
register = Registry()
# FIXME: We should not let env return anything in_server
# when mode == 'production'. This would allow an attacker to see the
# configuration of the server, potentially revealing compromising
# information. However, it's damn handy for testing/debugging.
@register()
class env(LocalOrRemote):
__doc__ = _('Show environment variables.')
msg_summary = _('%(count)d variables')
takes_args = (
'variables*',
)
takes_options = LocalOrRemote.takes_options + (
Flag('all',
cli_name='all',
doc=_('retrieve and print all attributes from the server. Affects command output.'),
exclude='webui',
flags=['no_option', 'no_output'],
default=True,
),
)
has_output = (
Output('result',
type=dict,
doc=_('Dictionary mapping variable name to value'),
),
Output('total',
type=int,
doc=_('Total number of variables env (>= count)'),
flags=['no_display'],
),
Output('count',
type=int,
doc=_('Number of variables returned (<= total)'),
flags=['no_display'],
),
summary,
)
def __find_keys(self, variables):
keys = set()
for query in variables:
if '*' in query:
pat = re.compile(query.replace('*', '.*') + '$')
for key in self.env:
if pat.match(key):
keys.add(key)
elif query in self.env:
keys.add(query)
return keys
def execute(self, variables=None, **options):
if variables is None:
keys = self.env
else:
keys = self.__find_keys(variables)
ret = dict(
result=dict(
(key, self.env[key]) for key in keys
),
count=len(keys),
total=len(self.env),
)
if len(keys) > 1:
ret['summary'] = self.msg_summary % ret
else:
ret['summary'] = None
return ret
@register()
class plugins(LocalOrRemote):
__doc__ = _('Show all loaded plugins.')
msg_summary = ngettext(
'%(count)d plugin loaded', '%(count)d plugins loaded', 0
)
takes_options = LocalOrRemote.takes_options + (
Flag('all',
cli_name='all',
doc=_('retrieve and print all attributes from the server. Affects command output.'),
exclude='webui',
flags=['no_option', 'no_output'],
default=True,
),
)
has_output = (
Output('result', dict, 'Dictionary mapping plugin names to bases'),
Output('count',
type=int,
doc=_('Number of plugins loaded'),
),
summary,
)
def execute(self, **options):
result = {}
for namespace in self.api:
for plugin in self.api[namespace]():
cls = type(plugin)
key = '{}.{}'.format(cls.__module__, cls.__name__)
result.setdefault(key, []).append(namespace)
return dict(
result=result,
)
env = register()(env)
plugins = register()(plugins)