diff options
Diffstat (limited to 'src/lib/Bcfg2/Server/Plugin/helpers.py')
-rw-r--r-- | src/lib/Bcfg2/Server/Plugin/helpers.py | 99 |
1 files changed, 81 insertions, 18 deletions
diff --git a/src/lib/Bcfg2/Server/Plugin/helpers.py b/src/lib/Bcfg2/Server/Plugin/helpers.py index 41c450b4e..399ab6679 100644 --- a/src/lib/Bcfg2/Server/Plugin/helpers.py +++ b/src/lib/Bcfg2/Server/Plugin/helpers.py @@ -18,25 +18,17 @@ from Bcfg2.Server.Plugin.exceptions import SpecificityError, \ PluginExecutionError try: + import Bcfg2.Encryption + HAS_CRYPTO = True +except ImportError: + HAS_CRYPTO = False + +try: import django # pylint: disable=W0611 HAS_DJANGO = True except ImportError: HAS_DJANGO = False -#: A dict containing default metadata for Path entries from bcfg2.conf -DEFAULT_FILE_METADATA = Bcfg2.Options.OptionParser(dict( - configfile=Bcfg2.Options.CFILE, - owner=Bcfg2.Options.MDATA_OWNER, - group=Bcfg2.Options.MDATA_GROUP, - mode=Bcfg2.Options.MDATA_MODE, - secontext=Bcfg2.Options.MDATA_SECONTEXT, - important=Bcfg2.Options.MDATA_IMPORTANT, - paranoid=Bcfg2.Options.MDATA_PARANOID, - sensitive=Bcfg2.Options.MDATA_SENSITIVE)) -DEFAULT_FILE_METADATA.parse([Bcfg2.Options.CFILE.cmd, Bcfg2.Options.CFILE]) -del DEFAULT_FILE_METADATA['args'] -del DEFAULT_FILE_METADATA['configfile'] - LOGGER = logging.getLogger(__name__) #: a compiled regular expression for parsing info and :info files @@ -51,7 +43,20 @@ INFO_REGEX = re.compile('owner:(\s)*(?P<owner>\S+)|' + 'mtime:(\s)*(?P<mtime>\w+)|') -def bind_info(entry, metadata, infoxml=None, default=DEFAULT_FILE_METADATA): +def default_path_metadata(): + """ Get the default Path entry metadata from the config. + + :returns: dict of metadata attributes and their default values + """ + attrs = Bcfg2.Options.PATH_METADATA_OPTIONS.keys() + setup = Bcfg2.Options.get_option_parser() + if not set(attrs).issubset(setup.keys()): + setup.add_options(Bcfg2.Options.PATH_METADATA_OPTIONS) + setup.reparse(argv=[Bcfg2.Options.CFILE.cmd, Bcfg2.Options.CFILE]) + return dict([(k, setup[k]) for k in attrs]) + + +def bind_info(entry, metadata, infoxml=None, default=None): """ Bind the file metadata in the given :class:`Bcfg2.Server.Plugin.helpers.InfoXML` object to the given entry. @@ -68,6 +73,8 @@ def bind_info(entry, metadata, infoxml=None, default=DEFAULT_FILE_METADATA): :returns: None :raises: :class:`Bcfg2.Server.Plugin.exceptions.PluginExecutionError` """ + if default is None: + default = default_path_metadata() for attr, val in list(default.items()): entry.set(attr, val) if infoxml: @@ -570,13 +577,69 @@ class XMLFileBacked(FileBacked): class StructFile(XMLFileBacked): """ StructFiles are XML files that contain a set of structure file formatting logic for handling ``<Group>`` and ``<Client>`` - tags. """ + tags. + + .. ----- + .. autoattribute:: __identifier__ + """ #: If ``__identifier__`` is not None, then it must be the name of #: an XML attribute that will be required on the top-level tag of #: the file being cached __identifier__ = None + #: Whether or not encryption support is enabled in this file + encryption = True + + def __init__(self, filename, fam=None, should_monitor=False): + XMLFileBacked.__init__(self, filename, fam=fam, + should_monitor=should_monitor) + self.setup = Bcfg2.Options.get_option_parser() + + def Index(self): + XMLFileBacked.Index(self) + if self.encryption: + strict = self.xdata.get( + "decrypt", + self.setup.cfp.get(Bcfg2.Encryption.CFG_SECTION, "decrypt", + default="strict")) == "strict" + for el in self.xdata.xpath("//*[@encrypted]"): + if not HAS_CRYPTO: + raise PluginExecutionError("%s: M2Crypto is not available" + % self.name) + try: + el.text = self._decrypt(el).encode('ascii', + 'xmlcharrefreplace') + except UnicodeDecodeError: + LOGGER.info("%s: Decrypted %s to gibberish, skipping" % + (self.name, el.tag)) + except Bcfg2.Encryption.EVPError: + msg = "Failed to decrypt %s element in %s" % (el.tag, + self.name) + if strict: + raise PluginExecutionError(msg) + else: + LOGGER.warning(msg) + Index.__doc__ = XMLFileBacked.Index.__doc__ + + def _decrypt(self, element): + """ Decrypt a single encrypted properties file element """ + if not element.text or not element.text.strip(): + return + passes = Bcfg2.Encryption.get_passphrases() + try: + passphrase = passes[element.get("encrypted")] + try: + return Bcfg2.Encryption.ssl_decrypt(element.text, passphrase) + except Bcfg2.Encryption.EVPError: + # error is raised below + pass + except KeyError: + # bruteforce_decrypt raises an EVPError with a sensible + # error message, so we just let it propagate up the stack + return Bcfg2.Encryption.bruteforce_decrypt(element.text) + raise Bcfg2.Encryption.EVPError("Failed to decrypt") + def _include_element(self, item, metadata): """ determine if an XML element matches the metadata """ if isinstance(item, lxml.etree._Comment): # pylint: disable=W0212 @@ -1156,7 +1219,7 @@ class EntrySet(Debuggable): self.path = path self.entry_type = entry_type self.entries = {} - self.metadata = DEFAULT_FILE_METADATA.copy() + self.metadata = default_path_metadata() self.infoxml = None self.encoding = encoding @@ -1378,7 +1441,7 @@ class EntrySet(Debuggable): if event.filename == 'info.xml': self.infoxml = None elif event.filename in [':info', 'info']: - self.metadata = DEFAULT_FILE_METADATA.copy() + self.metadata = default_path_metadata() def bind_info_to_entry(self, entry, metadata): """ Shortcut to call :func:`bind_info` with the base |