Example #1
0
def perform_registration(acme, config):
    """
    Actually register new account, trying repeatedly if there are email
    problems

    :param .IConfig config: Client configuration.
    :param acme.client.Client client: ACME client object.

    :returns: Registration Resource.
    :rtype: `acme.messages.RegistrationResource`
    """
    try:
        return acme.register(messages.NewRegistration.from_data(email=config.email))
    except messages.Error as e:
        if e.code == "invalidEmail" or e.code == "invalidContact":
            if config.noninteractive_mode:
                msg = ("The ACME server believes %s is an invalid email address. "
                       "Please ensure it is a valid email and attempt "
                       "registration again." % config.email)
                raise errors.Error(msg)
            else:
                config.email = display_ops.get_email(invalid=True)
                return perform_registration(acme, config)
        else:
            raise
Example #2
0
def _determine_account(config):
    """Determine which account to use.

    If ``config.account`` is ``None``, it will be updated based on the
    user input. Same for ``config.email``.

    :param config: Configuration object
    :type config: interfaces.IConfig

    :returns: Account and optionally ACME client API (biproduct of new
        registration).
    :rtype: tuple of :class:`certbot._internal.account.Account` and :class:`acme.client.Client`

    :raises errors.Error: If unable to register an account with ACME server

    """
    def _tos_cb(terms_of_service):
        if config.tos:
            return True
        msg = ("Please read the Terms of Service at {0}. You "
               "must agree in order to register with the ACME "
               "server at {1}".format(terms_of_service, config.server))
        obj = zope.component.getUtility(interfaces.IDisplay)
        result = obj.yesno(msg,
                           "Agree",
                           "Cancel",
                           cli_flag="--agree-tos",
                           force_interactive=True)
        if not result:
            raise errors.Error("Registration cannot proceed without accepting "
                               "Terms of Service.")
        return None

    account_storage = account.AccountFileStorage(config)
    acme = None

    if config.account is not None:
        acc = account_storage.load(config.account)
    else:
        accounts = account_storage.find_all()
        if len(accounts) > 1:
            acc = display_ops.choose_account(accounts)
        elif len(accounts) == 1:
            acc = accounts[0]
        else:  # no account registered yet
            if config.email is None and not config.register_unsafely_without_email:
                config.email = display_ops.get_email()
            try:
                acc, acme = client.register(config,
                                            account_storage,
                                            tos_cb=_tos_cb)
            except errors.MissingCommandlineFlag:
                raise
            except errors.Error:
                logger.debug("", exc_info=True)
                raise errors.Error(
                    "Unable to register an account with ACME server")

    config.account = acc.id
    return acc, acme
Example #3
0
def perform_registration(acme, config, tos_cb):
    """
    Actually register new account, trying repeatedly if there are email
    problems

    :param .IConfig config: Client configuration.
    :param acme.client.Client client: ACME client object.

    :returns: Registration Resource.
    :rtype: `acme.messages.RegistrationResource`
    """
    try:
        return acme.new_account_and_tos(
            messages.NewRegistration.from_data(email=config.email), tos_cb)
    except messages.Error as e:
        if e.code == "invalidEmail" or e.code == "invalidContact":
            if config.noninteractive_mode:
                msg = (
                    "The ACME server believes %s is an invalid email address. "
                    "Please ensure it is a valid email and attempt "
                    "registration again." % config.email)
                raise errors.Error(msg)
            else:
                config.email = display_ops.get_email(invalid=True)
                return perform_registration(acme, config, tos_cb)
        else:
            raise
Example #4
0
def _determine_account(config):
    """Determine which account to use.

    In order to make the renewer (configuration de/serialization) happy,
    if ``config.account`` is ``None``, it will be updated based on the
    user input. Same for ``config.email``.

    :param argparse.Namespace config: CLI arguments
    :param certbot.interface.IConfig config: Configuration object
    :param .AccountStorage account_storage: Account storage.

    :returns: Account and optionally ACME client API (biproduct of new
        registration).
    :rtype: `tuple` of `certbot.account.Account` and
        `acme.client.Client`

    """
    account_storage = account.AccountFileStorage(config)
    acme = None

    if config.account is not None:
        acc = account_storage.load(config.account)
    else:
        accounts = account_storage.find_all()
        if len(accounts) > 1:
            acc = display_ops.choose_account(accounts)
        elif len(accounts) == 1:
            acc = accounts[0]
        else:  # no account registered yet
            if config.email is None and not config.register_unsafely_without_email:
                config.email = display_ops.get_email()

            def _tos_cb(regr):
                if config.tos:
                    return True
                msg = ("Please read the Terms of Service at {0}. You "
                       "must agree in order to register with the ACME "
                       "server at {1}".format(regr.terms_of_service,
                                              config.server))
                obj = zope.component.getUtility(interfaces.IDisplay)
                return obj.yesno(msg,
                                 "Agree",
                                 "Cancel",
                                 cli_flag="--agree-tos",
                                 force_interactive=True)

            try:
                acc, acme = client.register(config,
                                            account_storage,
                                            tos_cb=_tos_cb)
            except errors.MissingCommandlineFlag:
                raise
            except errors.Error as error:
                logger.debug(error, exc_info=True)
                raise errors.Error(
                    "Unable to register an account with ACME server")

    config.account = acc.id
    return acc, acme
Example #5
0
def register(config, unused_plugins):
    """Create or modify accounts on the server.

    :param config: Configuration object
    :type config: interfaces.IConfig

    :param unused_plugins: List of plugins (deprecated)
    :type unused_plugins: `list` of `str`

    :returns: `None` or a string indicating and error
    :rtype: None or str

    """
    # Portion of _determine_account logic to see whether accounts already
    # exist or not.
    account_storage = account.AccountFileStorage(config)
    accounts = account_storage.find_all()
    reporter_util = zope.component.getUtility(interfaces.IReporter)
    add_msg = lambda m: reporter_util.add_message(
        m, reporter_util.MEDIUM_PRIORITY)

    # registering a new account
    if not config.update_registration:
        if len(accounts) > 0:
            # TODO: add a flag to register a duplicate account (this will
            #       also require extending _determine_account's behavior
            #       or else extracting the registration code from there)
            return ("There is an existing account; registration of a "
                    "duplicate account with this command is currently "
                    "unsupported.")
        # _determine_account will register an account
        _determine_account(config)
        return

    # --update-registration
    if len(accounts) == 0:
        return "Could not find an existing account to update."
    if config.email is None:
        if config.register_unsafely_without_email:
            return ("--register-unsafely-without-email provided, however, a "
                    "new e-mail address must\ncurrently be provided when "
                    "updating a registration.")
        config.email = display_ops.get_email(optional=False)

    acc, acme = _determine_account(config)
    cb_client = client.Client(config, acc, None, None, acme=acme)
    # We rely on an exception to interrupt this process if it didn't work.
    acc_contacts = ['mailto:' + email for email in config.email.split(',')]
    prev_regr_uri = acc.regr.uri
    acc.regr = cb_client.acme.update_registration(
        acc.regr.update(body=acc.regr.body.update(contact=acc_contacts)))
    # A v1 account being used as a v2 account will result in changing the uri to
    # the v2 uri. Since it's the same object on disk, put it back to the v1 uri
    # so that we can also continue to use the account object with acmev1.
    acc.regr = acc.regr.update(uri=prev_regr_uri)
    account_storage.save_regr(acc, cb_client.acme)
    eff.handle_subscription(config)
    add_msg("Your e-mail address was updated to {0}.".format(config.email))
Example #6
0
def _determine_account(config):
    """Determine which account to use.

    If ``config.account`` is ``None``, it will be updated based on the
    user input. Same for ``config.email``.

    :param config: Configuration object
    :type config: interfaces.IConfig

    :returns: Account and optionally ACME client API (biproduct of new
        registration).
    :rtype: tuple of :class:`certbot.account.Account` and :class:`acme.client.Client`

    :raises errors.Error: If unable to register an account with ACME server

    """
    def _tos_cb(terms_of_service):
        if config.tos:
            return True
        msg = ("Please read the Terms of Service at {0}. You "
               "must agree in order to register with the ACME "
               "server at {1}".format(
                   terms_of_service, config.server))
        obj = zope.component.getUtility(interfaces.IDisplay)
        result = obj.yesno(msg, "Agree", "Cancel",
                         cli_flag="--agree-tos", force_interactive=True)
        if not result:
            raise errors.Error(
                "Registration cannot proceed without accepting "
                "Terms of Service.")

    account_storage = account.AccountFileStorage(config)
    acme = None

    if config.account is not None:
        acc = account_storage.load(config.account)
    else:
        accounts = account_storage.find_all()
        if len(accounts) > 1:
            acc = display_ops.choose_account(accounts)
        elif len(accounts) == 1:
            acc = accounts[0]
        else:  # no account registered yet
            if config.email is None and not config.register_unsafely_without_email:
                config.email = display_ops.get_email()
            try:
                acc, acme = client.register(
                    config, account_storage, tos_cb=_tos_cb)
            except errors.MissingCommandlineFlag:
                raise
            except errors.Error:
                logger.debug("", exc_info=True)
                raise errors.Error(
                    "Unable to register an account with ACME server")

    config.account = acc.id
    return acc, acme
Example #7
0
def register(config, unused_plugins):
    """Create or modify accounts on the server.

    :param config: Configuration object
    :type config: interfaces.IConfig

    :param unused_plugins: List of plugins (deprecated)
    :type unused_plugins: `list` of `str`

    :returns: `None` or a string indicating and error
    :rtype: None or str

    """
    # Portion of _determine_account logic to see whether accounts already
    # exist or not.
    account_storage = account.AccountFileStorage(config)
    accounts = account_storage.find_all()
    reporter_util = zope.component.getUtility(interfaces.IReporter)
    add_msg = lambda m: reporter_util.add_message(m, reporter_util.MEDIUM_PRIORITY)

    # registering a new account
    if not config.update_registration:
        if len(accounts) > 0:
            # TODO: add a flag to register a duplicate account (this will
            #       also require extending _determine_account's behavior
            #       or else extracting the registration code from there)
            return ("There is an existing account; registration of a "
                    "duplicate account with this command is currently "
                    "unsupported.")
        # _determine_account will register an account
        _determine_account(config)
        return

    # --update-registration
    if len(accounts) == 0:
        return "Could not find an existing account to update."
    if config.email is None:
        if config.register_unsafely_without_email:
            return ("--register-unsafely-without-email provided, however, a "
                    "new e-mail address must\ncurrently be provided when "
                    "updating a registration.")
        config.email = display_ops.get_email(optional=False)

    acc, acme = _determine_account(config)
    cb_client = client.Client(config, acc, None, None, acme=acme)
    # We rely on an exception to interrupt this process if it didn't work.
    acc_contacts = ['mailto:' + email for email in config.email.split(',')]
    prev_regr_uri = acc.regr.uri
    acc.regr = cb_client.acme.update_registration(acc.regr.update(
        body=acc.regr.body.update(contact=acc_contacts)))
    # A v1 account being used as a v2 account will result in changing the uri to
    # the v2 uri. Since it's the same object on disk, put it back to the v1 uri
    # so that we can also continue to use the account object with acmev1.
    acc.regr = acc.regr.update(uri=prev_regr_uri)
    account_storage.save_regr(acc, cb_client.acme)
    eff.handle_subscription(config)
    add_msg("Your e-mail address was updated to {0}.".format(config.email))
Example #8
0
def perform_registration(
        acme: acme_client.ClientV2, config: configuration.NamespaceConfig,
        tos_cb: Optional[Callable[[str],
                                  None]]) -> messages.RegistrationResource:
    """
    Actually register new account, trying repeatedly if there are email
    problems

    :param acme.client.Client acme: ACME client object.
    :param certbot.configuration.NamespaceConfig config: Client configuration.
    :param Callable tos_cb: a callback to handle Term of Service agreement.

    :returns: Registration Resource.
    :rtype: `acme.messages.RegistrationResource`
    """

    eab_credentials_supplied = config.eab_kid and config.eab_hmac_key
    eab: Optional[Dict[str, Any]]
    if eab_credentials_supplied:
        account_public_key = acme.net.key.public_key()
        eab = messages.ExternalAccountBinding.from_data(
            account_public_key=account_public_key,
            kid=config.eab_kid,
            hmac_key=config.eab_hmac_key,
            directory=acme.directory)
    else:
        eab = None

    if acme.external_account_required():
        if not eab_credentials_supplied:
            msg = ("Server requires external account binding."
                   " Please use --eab-kid and --eab-hmac-key.")
            raise errors.Error(msg)

    try:
        newreg = messages.NewRegistration.from_data(
            email=config.email, external_account_binding=eab)
        # Until ACME v1 support is removed from Certbot, we actually need the provided
        # ACME client to be a wrapper of type BackwardsCompatibleClientV2.
        # TODO: Remove this cast and rewrite the logic when the client is actually a ClientV2
        try:
            return cast(acme_client.BackwardsCompatibleClientV2,
                        acme).new_account_and_tos(newreg, tos_cb)
        except AttributeError:
            raise errors.Error("The ACME client must be an instance of "
                               "acme.client.BackwardsCompatibleClientV2")
    except messages.Error as e:
        if e.code in ("invalidEmail", "invalidContact"):
            if config.noninteractive_mode:
                msg = (
                    "The ACME server believes %s is an invalid email address. "
                    "Please ensure it is a valid email and attempt "
                    "registration again." % config.email)
                raise errors.Error(msg)
            config.email = display_ops.get_email(invalid=True)
            return perform_registration(acme, config, tos_cb)
        raise
Example #9
0
def _determine_account(config):
    """Determine which account to use.

    In order to make the renewer (configuration de/serialization) happy,
    if ``config.account`` is ``None``, it will be updated based on the
    user input. Same for ``config.email``.

    :param argparse.Namespace config: CLI arguments
    :param certbot.interface.IConfig config: Configuration object
    :param .AccountStorage account_storage: Account storage.

    :returns: Account and optionally ACME client API (biproduct of new
        registration).
    :rtype: `tuple` of `certbot.account.Account` and
        `acme.client.Client`

    """
    account_storage = account.AccountFileStorage(config)
    acme = None

    if config.account is not None:
        acc = account_storage.load(config.account)
    else:
        accounts = account_storage.find_all()
        if len(accounts) > 1:
            acc = display_ops.choose_account(accounts)
        elif len(accounts) == 1:
            acc = accounts[0]
        else:  # no account registered yet
            if config.email is None and not config.register_unsafely_without_email:
                config.namespace.email = display_ops.get_email()

            def _tos_cb(regr):
                if config.tos:
                    return True
                msg = ("Please read the Terms of Service at {0}. You "
                       "must agree in order to register with the ACME "
                       "server at {1}".format(
                           regr.terms_of_service, config.server))
                obj = zope.component.getUtility(interfaces.IDisplay)
                return obj.yesno(msg, "Agree", "Cancel",
                                 cli_flag="--agree-tos", force_interactive=True)

            try:
                acc, acme = client.register(
                    config, account_storage, tos_cb=_tos_cb)
            except errors.MissingCommandlineFlag:
                raise
            except errors.Error as error:
                logger.debug(error, exc_info=True)
                raise errors.Error(
                    "Unable to register an account with ACME server")

    config.namespace.account = acc.id
    return acc, acme
Example #10
0
def update_account(config, unused_plugins):
    """Modify accounts on the server.

    :param config: Configuration object
    :type config: interfaces.IConfig

    :param unused_plugins: List of plugins (deprecated)
    :type unused_plugins: `list` of `str`

    :returns: `None` or a string indicating and error
    :rtype: None or str

    """
    # Portion of _determine_account logic to see whether accounts already
    # exist or not.
    account_storage = account.AccountFileStorage(config)
    accounts = account_storage.find_all()
    reporter_util = zope.component.getUtility(interfaces.IReporter)
    add_msg = lambda m: reporter_util.add_message(
        m, reporter_util.MEDIUM_PRIORITY)

    if not accounts:
        return "Could not find an existing account to update."
    if config.email is None and not config.register_unsafely_without_email:
        config.email = display_ops.get_email(optional=False)

    acc, acme = _determine_account(config)
    cb_client = client.Client(config, acc, None, None, acme=acme)
    # Empty list of contacts in case the user is removing all emails

    acc_contacts = ()  # type: Iterable[str]
    if config.email:
        acc_contacts = ['mailto:' + email for email in config.email.split(',')]
    # We rely on an exception to interrupt this process if it didn't work.
    prev_regr_uri = acc.regr.uri
    acc.regr = cb_client.acme.update_registration(
        acc.regr.update(body=acc.regr.body.update(contact=acc_contacts)))
    # A v1 account being used as a v2 account will result in changing the uri to
    # the v2 uri. Since it's the same object on disk, put it back to the v1 uri
    # so that we can also continue to use the account object with acmev1.
    acc.regr = acc.regr.update(uri=prev_regr_uri)
    account_storage.update_regr(acc, cb_client.acme)

    if config.email is None:
        add_msg(
            "Any contact information associated with this account has been removed."
        )
    else:
        eff.prepare_subscription(config, acc)
        add_msg("Your e-mail address was updated to {0}.".format(config.email))

    return None
Example #11
0
def perform_registration(acme, config, tos_cb):
    """
    Actually register new account, trying repeatedly if there are email
    problems

    :param acme.client.Client client: ACME client object.
    :param certbot.configuration.NamespaceConfig config: Client configuration.
    :param Callable tos_cb: a callback to handle Term of Service agreement.

    :returns: Registration Resource.
    :rtype: `acme.messages.RegistrationResource`
    """

    eab_credentials_supplied = config.eab_kid and config.eab_hmac_key
    eab: Optional[Dict[str, Any]]
    if eab_credentials_supplied:
        account_public_key = acme.client.net.key.public_key()
        eab = messages.ExternalAccountBinding.from_data(
            account_public_key=account_public_key,
            kid=config.eab_kid,
            hmac_key=config.eab_hmac_key,
            directory=acme.client.directory)
    else:
        eab = None

    if acme.external_account_required():
        if not eab_credentials_supplied:
            msg = ("Server requires external account binding."
                   " Please use --eab-kid and --eab-hmac-key.")
            raise errors.Error(msg)

    try:
        # TODO: Remove the cast once certbot package is fully typed
        newreg = messages.NewRegistration.from_data(
            email=config.email,
            external_account_binding=cast(
                Optional[messages.ExternalAccountBinding], eab))
        return acme.new_account_and_tos(newreg, tos_cb)
    except messages.Error as e:
        if e.code in ('invalidEmail', 'invalidContact'):
            if config.noninteractive_mode:
                msg = (
                    "The ACME server believes %s is an invalid email address. "
                    "Please ensure it is a valid email and attempt "
                    "registration again." % config.email)
                raise errors.Error(msg)
            config.email = display_ops.get_email(invalid=True)
            return perform_registration(acme, config, tos_cb)
        raise
Example #12
0
def perform_registration(acme, config, tos_cb):
    """
    Actually register new account, trying repeatedly if there are email
    problems

    :param acme.client.Client client: ACME client object.
    :param .IConfig config: Client configuration.
    :param Callable tos_cb: a callback to handle Term of Service agreement.

    :returns: Registration Resource.
    :rtype: `acme.messages.RegistrationResource`
    """

    eab_credentials_supplied = config.eab_kid and config.eab_hmac_key
    if eab_credentials_supplied:
        account_public_key = acme.client.net.key.public_key()
        eab = messages.ExternalAccountBinding.from_data(account_public_key=account_public_key,
                                                        kid=config.eab_kid,
                                                        hmac_key=config.eab_hmac_key,
                                                        directory=acme.client.directory)
    else:
        eab = None

    if acme.external_account_required():
        if not eab_credentials_supplied:
            msg = ("Server requires external account binding."
                   " Please use --eab-kid and --eab-hmac-key.")
            raise errors.Error(msg)

    try:
        newreg = messages.NewRegistration.from_data(email=config.email,
                                                    external_account_binding=eab)
        return acme.new_account_and_tos(newreg, tos_cb)
    except messages.Error as e:
        if e.code == "invalidEmail" or e.code == "invalidContact":
            if config.noninteractive_mode:
                msg = ("The ACME server believes %s is an invalid email address. "
                       "Please ensure it is a valid email and attempt "
                       "registration again." % config.email)
                raise errors.Error(msg)
            else:
                config.email = display_ops.get_email(invalid=True)
                return perform_registration(acme, config, tos_cb)
        else:
            raise
Example #13
0
def register(config, unused_plugins):
    """Create or modify accounts on the server."""

    # Portion of _determine_account logic to see whether accounts already
    # exist or not.
    account_storage = account.AccountFileStorage(config)
    accounts = account_storage.find_all()
    reporter_util = zope.component.getUtility(interfaces.IReporter)
    add_msg = lambda m: reporter_util.add_message(
        m, reporter_util.MEDIUM_PRIORITY)

    # registering a new account
    if not config.update_registration:
        if len(accounts) > 0:
            # TODO: add a flag to register a duplicate account (this will
            #       also require extending _determine_account's behavior
            #       or else extracting the registration code from there)
            return ("There is an existing account; registration of a "
                    "duplicate account with this command is currently "
                    "unsupported.")
        # _determine_account will register an account
        _determine_account(config)
        return

    # --update-registration
    if len(accounts) == 0:
        return "Could not find an existing account to update."
    if config.email is None:
        if config.register_unsafely_without_email:
            return ("--register-unsafely-without-email provided, however, a "
                    "new e-mail address must\ncurrently be provided when "
                    "updating a registration.")
        config.email = display_ops.get_email(optional=False)

    acc, acme = _determine_account(config)
    cb_client = client.Client(config, acc, None, None, acme=acme)
    # We rely on an exception to interrupt this process if it didn't work.
    acc.regr = cb_client.acme.update_registration(
        acc.regr.update(body=acc.regr.body.update(contact=('mailto:' +
                                                           config.email, ))))
    account_storage.save_regr(acc, cb_client.acme)
    eff.handle_subscription(config)
    add_msg("Your e-mail address was updated to {0}.".format(config.email))
Example #14
0
def register(config, unused_plugins):
    """Create or modify accounts on the server."""

    # Portion of _determine_account logic to see whether accounts already
    # exist or not.
    account_storage = account.AccountFileStorage(config)
    accounts = account_storage.find_all()
    reporter_util = zope.component.getUtility(interfaces.IReporter)
    add_msg = lambda m: reporter_util.add_message(m, reporter_util.MEDIUM_PRIORITY)

    # registering a new account
    if not config.update_registration:
        if len(accounts) > 0:
            # TODO: add a flag to register a duplicate account (this will
            #       also require extending _determine_account's behavior
            #       or else extracting the registration code from there)
            return ("There is an existing account; registration of a "
                    "duplicate account with this command is currently "
                    "unsupported.")
        # _determine_account will register an account
        _determine_account(config)
        return

    # --update-registration
    if len(accounts) == 0:
        return "Could not find an existing account to update."
    if config.email is None:
        if config.register_unsafely_without_email:
            return ("--register-unsafely-without-email provided, however, a "
                    "new e-mail address must\ncurrently be provided when "
                    "updating a registration.")
        config.namespace.email = display_ops.get_email(optional=False)

    acc, acme = _determine_account(config)
    acme_client = client.Client(config, acc, None, None, acme=acme)
    # We rely on an exception to interrupt this process if it didn't work.
    acc.regr = acme_client.acme.update_registration(acc.regr.update(
        body=acc.regr.body.update(contact=('mailto:' + config.email,))))
    account_storage.save_regr(acc)
    eff.handle_subscription(config)
    add_msg("Your e-mail address was updated to {0}.".format(config.email))
Example #15
0
def perform_registration(acme, config):
    """
    Actually register new account, trying repeatedly if there are email
    problems

    :param .IConfig config: Client configuration.
    :param acme.client.Client client: ACME client object.

    :returns: Registration Resource.
    :rtype: `acme.messages.RegistrationResource`

    :raises .UnexpectedUpdate:
    """
    try:
        return acme.register(messages.NewRegistration.from_data(email=config.email))
    except messages.Error as e:
        if e.typ == "urn:acme:error:invalidEmail":
            config.namespace.email = display_ops.get_email(more=True, invalid=True)
            return perform_registration(acme, config)
        else:
            raise
Example #16
0
 def _call(cls, **kwargs):
     from certbot.display.ops import get_email
     return get_email(**kwargs)