Ejemplo n.º 1
0
 def test_noninteractive(self, mock_util):
     mock_util().menu.side_effect = errors.MissingCommandlineFlag(
         "no vhost default")
     try:
         self._call(self.vhosts)
     except errors.MissingCommandlineFlag as e:
         self.assertTrue("VirtualHost directives" in e.message)
Ejemplo n.º 2
0
def _vhost_menu(domain, vhosts):
    """Select an appropriate Apache Vhost.

    :param vhosts: Available Apache Virtual Hosts
    :type vhosts: :class:`list` of type `~obj.Vhost`

    :returns: Display tuple - ('code', tag')
    :rtype: `tuple`

    """
    # Free characters in the line of display text (9 is for ' | ' formatting)
    free_chars = display_util.WIDTH - len("HTTPS") - len("Enabled") - 9

    if free_chars < 2:
        logger.debug("Display size is too small for "
                     "letsencrypt_apache.display_ops._vhost_menu()")
        # This runs the edge off the screen, but it doesn't cause an "error"
        filename_size = 1
        disp_name_size = 1
    else:
        # Filename is a bit more important and probably longer with 000-*
        filename_size = int(free_chars * .6)
        disp_name_size = free_chars - filename_size

    choices = []
    for vhost in vhosts:
        if len(vhost.get_names()) == 1:
            disp_name = next(iter(vhost.get_names()))
        elif len(vhost.get_names()) == 0:
            disp_name = ""
        else:
            disp_name = "Multiple Names"

        choices.append("{fn:{fn_size}s} | {name:{name_size}s} | {https:5s} | "
                       "{active:7s}".format(
                           fn=os.path.basename(vhost.filep)[:filename_size],
                           name=disp_name[:disp_name_size],
                           https="HTTPS" if vhost.ssl else "",
                           active="Enabled" if vhost.enabled else "",
                           fn_size=filename_size,
                           name_size=disp_name_size))

    try:
        code, tag = zope.component.getUtility(interfaces.IDisplay).menu(
            "We were unable to find a vhost with a ServerName "
            "or Address of {0}.{1}Which virtual host would you "
            "like to choose?\n(note: conf files with multiple "
            "vhosts are not yet supported)".format(domain, os.linesep),
            choices,
            help_label="More Info",
            ok_label="Select")
    except errors.MissingCommandlineFlag as e:
        msg = ("Failed to run Apache plugin non-interactively{1}{0}{1}"
               "(The best solution is to add ServerName or ServerAlias "
               "entries to the VirtualHost directives of your apache "
               "configuration files.)".format(e, os.linesep))
        raise errors.MissingCommandlineFlag(msg)

    return code, tag
Ejemplo n.º 3
0
 def _interaction_fail(self, message, cli_flag, extra=""):
     "Error out in case of an attempt to interact in noninteractive mode"
     msg = "Missing command line flag or config entry for this setting:\n"
     msg += message
     if extra:
         msg += "\n" + extra
     if cli_flag:
         msg += "\n\n(You can set this with the {0} flag)".format(cli_flag)
     raise errors.MissingCommandlineFlag(msg)
Ejemplo n.º 4
0
def pick_plugin(config, default, plugins, question, ifaces):
    """Pick plugin.

    :param letsencrypt.interfaces.IConfig: Configuration
    :param str default: Plugin name supplied by user or ``None``.
    :param letsencrypt.plugins.disco.PluginsRegistry plugins:
        All plugins registered as entry points.
    :param str question: Question to be presented to the user in case
        multiple candidates are found.
    :param list ifaces: Interfaces that plugins must provide.

    :returns: Initialized plugin.
    :rtype: IPlugin

    """
    if default is not None:
        # throw more UX-friendly error if default not in plugins
        filtered = plugins.filter(lambda p_ep: p_ep.name == default)
    else:
        if config.noninteractive_mode:
            # it's really bad to auto-select the single available plugin in
            # non-interactive mode, because an update could later add a second
            # available plugin
            raise errors.MissingCommandlineFlag(
                "Missing command line flags. For non-interactive execution, "
                "you will need to specify a plugin on the command line.  Run "
                "with '--help plugins' to see a list of options, and see "
                "https://eff.org/letsencrypt-plugins for more detail on what "
                "the plugins do and how to use them.")

        filtered = plugins.visible().ifaces(ifaces)

    filtered.init(config)
    verified = filtered.verify(ifaces)
    verified.prepare()
    prepared = verified.available()

    if len(prepared) > 1:
        logger.debug("Multiple candidate plugins: %s", prepared)
        plugin_ep = choose_plugin(prepared.values(), question)
        if plugin_ep is None:
            return None
        else:
            return plugin_ep.init()
    elif len(prepared) == 1:
        plugin_ep = prepared.values()[0]
        logger.debug("Single candidate plugin: %s", plugin_ep)
        if plugin_ep.misconfigured:
            return None
        return plugin_ep.init()
    else:
        logger.debug("No candidate plugin")
        return None
Ejemplo n.º 5
0
def get_email(more=False, invalid=False):
    """Prompt for valid email address.

    :param bool more: explain why the email is strongly advisable, but how to
        skip it
    :param bool invalid: true if the user just typed something, but it wasn't
        a valid-looking email

    :returns: Email or ``None`` if cancelled by user.
    :rtype: str

    """
    msg = "Enter email address (used for urgent notices and lost key recovery)"
    if invalid:
        msg = "There seem to be problems with that address. " + msg
    if more:
        msg += (
            '\n\nIf you really want to skip this, you can run the client with '
            '--register-unsafely-without-email but make sure you backup your '
            'account key from /etc/letsencrypt/accounts\n\n')
    try:
        code, email = zope.component.getUtility(interfaces.IDisplay).input(msg)
    except errors.MissingCommandlineFlag:
        msg = (
            "You should register before running non-interactively, or provide --agree-tos"
            " and --email <email_address> flags")
        raise errors.MissingCommandlineFlag(msg)

    if code == display_util.OK:
        if le_util.safe_email(email):
            return email
        else:
            # TODO catch the server's ACME invalid email address error, and
            # make a similar call when that happens
            return get_email(more=True, invalid=(email != ""))
    else:
        return None
Ejemplo n.º 6
0
def choose_configurator_plugins(config, plugins, verb):
    """
    Figure out which configurator we're going to use, modifies
    config.authenticator and config.installer strings to reflect that choice if
    necessary.

    :raises errors.PluginSelectionError if there was a problem

    :returns: (an `IAuthenticator` or None, an `IInstaller` or None)
    :rtype: tuple
    """

    req_auth, req_inst = cli_plugin_requests(config)

    # Which plugins do we need?
    if verb == "run":
        need_inst = need_auth = True
        from letsencrypt.cli import cli_command
        if req_auth in noninstaller_plugins and not req_inst:
            msg = (
                'With the {0} plugin, you probably want to use the "certonly" command, eg:{1}'
                '{1}    {2} certonly --{0}{1}{1}'
                '(Alternatively, add a --installer flag. See https://eff.org/letsencrypt-plugins'
                '{1} and "--help plugins" for more information.)'.format(
                    req_auth, os.linesep, cli_command))

            raise errors.MissingCommandlineFlag(msg)
    else:
        need_inst = need_auth = False
    if verb == "certonly":
        need_auth = True
    if verb == "install":
        need_inst = True
        if config.authenticator:
            logger.warn(
                "Specifying an authenticator doesn't make sense in install mode"
            )

    # Try to meet the user's request and/or ask them to pick plugins
    authenticator = installer = None
    if verb == "run" and req_auth == req_inst:
        # Unless the user has explicitly asked for different auth/install,
        # only consider offering a single choice
        authenticator = installer = pick_configurator(config, req_inst,
                                                      plugins)
    else:
        if need_inst or req_inst:
            installer = pick_installer(config, req_inst, plugins)
        if need_auth:
            authenticator = pick_authenticator(config, req_auth, plugins)
    logger.debug("Selected authenticator %s and installer %s", authenticator,
                 installer)

    # Report on any failures
    if need_inst and not installer:
        diagnose_configurator_problem("installer", req_inst, plugins)
    if need_auth and not authenticator:
        diagnose_configurator_problem("authenticator", req_auth, plugins)

    record_chosen_plugins(config, plugins, authenticator, installer)
    return installer, authenticator