def parse_logging_config(log_config_spec) -> Dict[Optional[str], LogConfig]: if not isinstance(log_config_spec, dict): raise ConfigurationError('logging config should be a dictionary') root_logger_level = _retrieve_log_level( log_config_spec, 'root-level', default=DEFAULT_ROOT_LOGGER_LEVEL ) root_logger_output = get_and_apply( log_config_spec, 'root-output', LogConfig.parse_output_spec, default=StdLogOutput.STDERR ) log_config = {None: LogConfig(root_logger_level, root_logger_output)} logging_by_module = log_config_spec.get('by-module', {}) if not isinstance(logging_by_module, dict): raise ConfigurationError('logging.by-module should be a dict') for module, module_logging_settings in logging_by_module.items(): if not isinstance(module, str): raise ConfigurationError( "Keys in logging.by-module should be strings" ) level_spec = _retrieve_log_level(module_logging_settings, 'level') output_spec = get_and_apply( module_logging_settings, 'output', LogConfig.parse_output_spec, default=StdLogOutput.STDERR ) log_config[module] = LogConfig(level=level_spec, output=output_spec) return log_config
def process_entries(cls, config_dict): super().process_entries(config_dict) other_certs = config_dict.get('other_certs', ()) if isinstance(other_certs, str): other_certs = (other_certs, ) config_dict['other_certs'] = list(load_certs_from_pemder(other_certs)) if 'token_label' not in config_dict and 'slot_no' not in config_dict: raise ConfigurationError( "Either 'slot_no' or 'token_label' must be provided in " "PKCS#11 setup") cert_file = config_dict.get('signing_certificate', None) if cert_file is not None: config_dict['signing_certificate'] \ = load_cert_from_pemder(cert_file) if 'key_id' in config_dict: config_dict['key_id'] \ = _process_pkcs11_id_value(config_dict['key_id']) elif 'key_label' not in config_dict and 'cert_label' not in config_dict: raise ConfigurationError( "Either 'key_id', 'key_label' or 'cert_label' must be provided " "in PKCS#11 setup") if 'cert_id' in config_dict: config_dict['cert_id'] \ = _process_pkcs11_id_value(config_dict['cert_id']) elif 'cert_label' not in config_dict \ and 'signing_certificate' not in config_dict: raise ConfigurationError( "Either 'cert_id', 'cert_label' or 'signing_certificate' " "must be provided in PKCS#11 setup")
def get_stamp_style(self, name=None) -> TextStampStyle: name = name or self.default_stamp_style try: style_config = dict(self.stamp_styles[name]) except KeyError: raise ConfigurationError( f"There is no stamp style named '{name}'.") except TypeError as e: raise ConfigurationError(e) cls = STAMP_STYLE_TYPES[style_config.pop('type', 'text')] return cls.from_config(style_config)
def _retrieve_log_level(settings_dict, key, default=None) -> Union[int, str]: try: level_spec = settings_dict[key] except KeyError: if default is not None: return default raise ConfigurationError( f"Logging config for '{key}' does not define a log level.") if not isinstance(level_spec, (int, str)): raise ConfigurationError( f"Log levels must be int or str, not {type(level_spec)}") return level_spec
def init_validation_context_kwargs(*, trust, trust_replace, other_certs, retroactive_revinfo=False, time_tolerance=None): if not isinstance(time_tolerance, timedelta): if time_tolerance is None: time_tolerance = timedelta(seconds=DEFAULT_TIME_TOLERANCE) elif isinstance(time_tolerance, int): time_tolerance = timedelta(seconds=time_tolerance) else: raise ConfigurationError( "time-tolerance parameter must be specified in seconds" ) vc_kwargs = {'time_tolerance': time_tolerance} if retroactive_revinfo: vc_kwargs['retroactive_revinfo'] = True if trust: if isinstance(trust, str): trust = (trust,) # add trust roots to the validation context, or replace them trust_certs = list(load_certs_from_pemder(trust)) if trust_replace: vc_kwargs['trust_roots'] = trust_certs else: vc_kwargs['extra_trust_roots'] = trust_certs if other_certs: if isinstance(other_certs, str): other_certs = (other_certs,) vc_kwargs['other_certs'] = list(load_certs_from_pemder(other_certs)) return vc_kwargs
def _get_validation_settings_raw(self, name=None): name = name or self.default_validation_context try: return self.validation_contexts[name] except KeyError: raise ConfigurationError( f"There is no validation context named '{name}'.")
def get_pemder_config(self, name): try: setup = self.pemder_setups[name] except KeyError: raise ConfigurationError( f"There's no PEM/DER setup named '{name}'") return PemDerSignatureConfig.from_config(setup)
def get_pkcs12_config(self, name): try: setup = self.pkcs12_setups[name] except KeyError: raise ConfigurationError( f"There's no PKCS#12 setup named '{name}'") return PKCS12SignatureConfig.from_config(setup)
def process_config_dict(config_dict: dict) -> dict: # validation context config vcs = {DEFAULT_VALIDATION_CONTEXT: {}} try: vc_specs = config_dict['validation-contexts'] vcs.update(vc_specs) except KeyError: pass # stamp style config # TODO this style is obviously not suited for non-signing scenarios # (but it'll do for now) stamp_configs = { DEFAULT_STAMP_STYLE: { 'stamp-text': DEFAULT_SIGNING_STAMP_STYLE.stamp_text, 'background': '__stamp__' } } try: stamp_specs = config_dict['stamp-styles'] stamp_configs.update(stamp_specs) except KeyError: pass # logging config log_config_spec = config_dict.get('logging', {}) log_config = parse_logging_config(log_config_spec) # TODO type check! pkcs11_setups = config_dict.get('pkcs11-setups', {}) pkcs12_setups = config_dict.get('pkcs12-setups', {}) pemder_setups = config_dict.get('pemder-setups', {}) beid_module_path = config_dict.get('beid-module-path', None) # some misc settings default_vc = config_dict.get( 'default-validation-context', DEFAULT_VALIDATION_CONTEXT ) default_stamp_style = config_dict.get( 'default-stamp-style', DEFAULT_STAMP_STYLE ) time_tolerance_seconds = config_dict.get( 'time-tolerance', DEFAULT_TIME_TOLERANCE ) if not isinstance(time_tolerance_seconds, int): raise ConfigurationError( "time-tolerance parameter must be specified in seconds" ) time_tolerance = timedelta(seconds=time_tolerance_seconds) retroactive_revinfo = bool(config_dict.get('retroactive-revinfo', False)) return dict( validation_contexts=vcs, default_validation_context=default_vc, time_tolerance=time_tolerance, retroactive_revinfo=retroactive_revinfo, stamp_styles=stamp_configs, default_stamp_style=default_stamp_style, log_config=log_config, pkcs11_setups=pkcs11_setups, pkcs12_setups=pkcs12_setups, pemder_setups=pemder_setups, beid_module_path=beid_module_path )
def get_validation_context(self, name=None, as_dict=False): name = name or self.default_validation_context try: vc_config = self.validation_contexts[name] except KeyError: raise ConfigurationError( f"There is no validation context named '{name}'.") vc_kwargs = parse_trust_config(vc_config, self.time_tolerance) return vc_kwargs if as_dict else ValidationContext(**vc_kwargs)
def instantiate(self, provided_pfx_passphrase: Optional[bytes] = None) \ -> SimpleSigner: passphrase = self.pfx_passphrase or provided_pfx_passphrase result = SimpleSigner.load_pkcs12( pfx_file=self.pfx_file, passphrase=passphrase, other_certs=self.other_certs, prefer_pss=self.prefer_pss ) if result is None: raise ConfigurationError("Error while loading key material") return result
def instantiate(self, provided_key_passphrase: Optional[bytes] = None) \ -> SimpleSigner: key_passphrase = self.key_passphrase or provided_key_passphrase result = SimpleSigner.load( key_file=self.key_file, cert_file=self.cert_file, other_certs=self.other_certs, prefer_pss=self.prefer_pss, key_passphrase=key_passphrase, ) if result is None: raise ConfigurationError("Error while loading key material") return result
def parse_output_spec(spec) -> Union[StdLogOutput, str]: if not isinstance(spec, str): raise ConfigurationError( "Log output must be specified as a string.") spec_l = spec.lower() if spec_l == 'stderr': return StdLogOutput.STDERR elif spec_l == 'stdout': return StdLogOutput.STDOUT else: return spec
def process_entries(cls, config_dict): super().process_entries(config_dict) try: fc = config_dict['font'] if not isinstance(fc, str) or \ not (fc.endswith('.otf') or fc.endswith('.ttf')): raise ConfigurationError( "'font' must be a path to an OpenType font file.") config_dict['font'] = GlyphAccumulatorFactory(fc) except KeyError: pass
def _get_background_content(bg_spec) -> content.PdfContent: if not isinstance(bg_spec, str): raise ConfigurationError("Background specification must be a string") # 'special' value to use the stamp vector image baked into # the module if bg_spec == '__stamp__': return STAMP_ART_CONTENT elif bg_spec.endswith('.pdf'): # import first page of PDF as background return content.ImportedPdfPage(bg_spec) else: from PIL import Image from pyhanko.pdf_utils.images import PdfImage img = Image.open(bg_spec) # Setting the writer can be delayed return PdfImage(img, writer=None)
def parse_cli_config(yaml_str): config_dict = yaml.safe_load(yaml_str) or {} # validation context config vcs = {DEFAULT_VALIDATION_CONTEXT: {}} try: vc_specs = config_dict['validation-contexts'] vcs.update(vc_specs) except KeyError: pass # stamp style config # TODO this style is obviously not suited for non-signing scenarios # (but it'll do for now) stamp_configs = {DEFAULT_STAMP_STYLE: DEFAULT_SIGNING_STAMP_STYLE} try: stamp_specs = config_dict['stamp-styles'] stamp_configs.update(stamp_specs) except KeyError: pass # logging config log_config_spec = config_dict.get('logging', {}) log_config = parse_logging_config(log_config_spec) # some misc settings default_vc = config_dict.get('default-validation-context', DEFAULT_VALIDATION_CONTEXT) default_stamp_style = config_dict.get('default-stamp-style', DEFAULT_STAMP_STYLE) time_tolerance_seconds = config_dict.get('time-tolerance', DEFAULT_TIME_TOLERANCE) if not isinstance(time_tolerance_seconds, int): raise ConfigurationError( "time-tolerance parameter must be specified in seconds") time_tolerance = timedelta(seconds=time_tolerance_seconds) retroactive_revinfo = bool(config_dict.get('retroactive-revinfo', False)) return CLIConfig(validation_contexts=vcs, default_validation_context=default_vc, time_tolerance=time_tolerance, retroactive_revinfo=retroactive_revinfo, stamp_styles=stamp_configs, default_stamp_style=default_stamp_style, log_config=log_config)
def from_y_align(cls, align_str: str) -> 'AxisAlignment': """ Convert from a vertical alignment config string. :param align_str: A string: 'bottom', 'mid' or 'top'. :return: An :class:`.AxisAlignment` value. :raise ConfigurationError: on unexpected string inputs. """ try: return { 'bottom': AxisAlignment.ALIGN_MIN, 'mid': AxisAlignment.ALIGN_MID, 'top': AxisAlignment.ALIGN_MAX }[align_str.lower()] except KeyError: raise ConfigurationError( f"'{align_str}' is not a valid vertical alignment; valid " f"values are 'bottom', 'mid', 'top'.")
def from_config(cls, config_str) -> 'QRPosition': """ Convert from a configuration string. :param config_str: A string: 'left', 'right', 'top', 'bottom' :return: An :class:`.QRPosition` value. :raise ConfigurationError: on unexpected string inputs. """ try: return { 'left': QRPosition.LEFT_OF_TEXT, 'right': QRPosition.RIGHT_OF_TEXT, 'top': QRPosition.ABOVE_TEXT, 'bottom': QRPosition.BELOW_TEXT }[config_str.lower()] except KeyError: raise ConfigurationError( f"'{config_str}' is not a valid QR position setting; valid " f"values are 'left', 'right', 'top', 'bottom'")
def from_config(cls, config_str: str) -> 'InnerScaling': """ Convert from a configuration string. :param config_str: A string: 'none', 'stretch-fill', 'stretch-to-fit', 'shrink-to-fit' :return: An :class:`.InnerScaling` value. :raise ConfigurationError: on unexpected string inputs. """ try: return { 'none': InnerScaling.NO_SCALING, 'stretch-fill': InnerScaling.STRETCH_FILL, 'stretch-to-fit': InnerScaling.STRETCH_TO_FIT, 'shrink-to-fit': InnerScaling.SHRINK_TO_FIT }[config_str.lower()] except KeyError: raise ConfigurationError( f"'{config_str}' is not a valid inner scaling setting; valid " f"values are 'none', 'stretch-fill', 'stretch-to-fit', " f"'shrink-to-fit'.")