Source code for plainbox.impl.secure.qualifiers

# This file is part of Checkbox.
#
# Copyright 2013 Canonical Ltd.
# Written by:
#   Zygmunt Krynicki <zygmunt.krynicki@canonical.com>
#
# Checkbox is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License version 3,
# as published by the Free Software Foundation.

#
# Checkbox is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with Checkbox.  If not, see <http://www.gnu.org/licenses/>.

"""
:mod:`plainbox.impl.secure.qualifiers` -- Job Qualifiers
========================================================

Qualifiers are callable objects that can be used to 'match' a job definition to
some set of rules.
"""

import os
import re

from plainbox.abc import IJobQualifier


[docs]class RegExpJobQualifier(IJobQualifier): """ A JobQualifier that designates jobs by matching their name to a regular expression """ def __init__(self, pattern): """ Initialize a new RegExpJobQualifier with the specified pattern. """ self._pattern = re.compile(pattern) self._pattern_text = pattern @property
[docs] def pattern_text(self): """ text of the regular expression embedded in this qualifier """ return self._pattern_text
[docs] def designates(self, job): return self._pattern.match(job.name)
def __repr__(self): return "<{0} pattern:{1!r}>".format( self.__class__.__name__, self._pattern_text)
[docs]class NameJobQualifier(IJobQualifier): """ A JobQualifier that designates a single job with a particular name """ def __init__(self, name): self._name = name
[docs] def designates(self, job): return self._name == job.name
def __repr__(self): return "<{0} name:{1!r}>".format( self.__class__.__name__, self._name)
[docs]class CompositeQualifier(IJobQualifier): """ A JobQualifier that has qualifies jobs matching any inclusive qualifiers while not matching all of the exclusive qualifiers """ def __init__(self, inclusive_qualifier_list, exclusive_qualifier_list): self.inclusive_qualifier_list = inclusive_qualifier_list self.exclusive_qualifier_list = exclusive_qualifier_list
[docs] def designates(self, job): # First reject stuff that is excluded for qualifier in self.exclusive_qualifier_list: if qualifier.designates(job): return False # Then allow stuff that is included for qualifier in self.inclusive_qualifier_list: if qualifier.designates(job): return True # Lastly reject by default return False # NOTE: using CompositeQualifier seems strange but it's a tested proven # component so all we have to ensure is that we read the whitelist files # correctly.
[docs]class WhiteList(CompositeQualifier): """ A qualifier that understands checkbox whitelist files. A whitelist file is a plain text, line oriented file. Each line represents a regular expression pattern that can be matched against the name of a job. The file can contain simple shell-style comments that begin with the pound or hash key (#). Those are ignored. Comments can span both a fraction of a line as well as the whole line. For historical reasons each pattern has an implicit '^' and '$' prepended and appended (respectively) to the actual pattern specified in the file. """ def __init__(self, pattern_list, name=None): """ Initialize a whitelist object with the specified list of patterns. The patterns must be already mangled with '^' and '$'. """ inclusive = [RegExpJobQualifier(pattern) for pattern in pattern_list] exclusive = () super(WhiteList, self).__init__(inclusive, exclusive) self._name = name def __repr__(self): return "<{} name:{!r}>".format(self.__class__.__name__, self.name) @property
[docs] def name(self): """ name of this WhiteList (might be None) """ return self._name
@classmethod
[docs] def from_file(cls, pathname): """ Load and initialize the WhiteList object from the specified file. :param pathname: file to load :returns: a fresh WhiteList object """ pattern_list = cls._load_patterns(pathname) name = os.path.splitext(os.path.basename(pathname))[0] return cls(pattern_list, name=name)
@classmethod def _load_patterns(self, pathname): """ Load whitelist patterns from the specified file """ pattern_list = [] # Load the file with open(pathname, "rt", encoding="UTF-8") as stream: for line in stream: # Strip shell-style comments if there are any try: index = line.index("#") except ValueError: pass else: line = line[:index] # Strip whitespace line = line.strip() # Skip empty lines (especially after stripping comments) if line == "": continue # Surround the pattern with ^ and $ # so that it wont just match a part of the job name. regexp_pattern = r"^{pattern}$".format(pattern=line) # Accumulate patterns into the list pattern_list.append(regexp_pattern) return pattern_list