summaryrefslogtreecommitdiffstats
path: root/src/lib/Bcfg2/Server/Plugin/helpers.py
diff options
context:
space:
mode:
Diffstat (limited to 'src/lib/Bcfg2/Server/Plugin/helpers.py')
-rw-r--r--src/lib/Bcfg2/Server/Plugin/helpers.py99
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