Esempio n. 1
0
    def __call__(self, parser, namespace, values, option_string=None):
        parser_name = values[0]

        try:
            # Check for deprecated command name
            correct_name = self._deprecated_command_map[parser_name]
        except KeyError:
            pass
        else:
            # Warn the user about deprecated command
            if correct_name is None:
                logger.warning(
                    m18n.g("deprecated_command", prog=parser.prog, command=parser_name)
                )
            else:
                logger.warning(
                    m18n.g(
                        "deprecated_command_alias",
                        old=parser_name,
                        new=correct_name,
                        prog=parser.prog,
                    )
                )
                values[0] = correct_name

        return super(_ExtendedSubParsersAction, self).__call__(
            parser, namespace, values, option_string
        )
Esempio n. 2
0
 def display(self, message, style="info"):  # i18n: info
     """Display a message"""
     if style == "success":
         print("{} {}".format(colorize(m18n.g("success"), "green"),
                              message))
     elif style == "warning":
         print("{} {}".format(colorize(m18n.g("warning"), "yellow"),
                              message))
     elif style == "error":
         print("{} {}".format(colorize(m18n.g("error"), "red"), message))
     else:
         print(message)
Esempio n. 3
0
    def logout(self):

        profile = request.params.get("profile", self.actionsmap.default_authentication)
        authenticator = self.actionsmap.get_authenticator(profile)

        try:
            authenticator.get_session_cookie()
        except KeyError:
            raise HTTPResponse(m18n.g("not_logged_in"), 401)
        else:
            # Delete cookie and clean the session
            authenticator.delete_session_cookie()
            return m18n.g("logged_out")
Esempio n. 4
0
    def login(self):
        """Log in to an authenticator

        Attempt to authenticate to the default authenticator and
        register it with the current session - a new one will be created
        if needed.

        """

        if "credentials" not in request.params:
            raise HTTPResponse("Missing credentials parameter", 400)
        credentials = request.params["credentials"]

        profile = request.params.get("profile", self.actionsmap.default_authentication)
        authenticator = self.actionsmap.get_authenticator(profile)

        try:
            auth_infos = authenticator.authenticate_credentials(credentials)
        except MoulinetteError as e:
            try:
                self.logout()
            except Exception:
                pass
            raise HTTPResponse(e.strerror, 401)
        else:
            authenticator.set_session_cookie(auth_infos)
            return m18n.g("logged_in")
Esempio n. 5
0
    def __call__(self, arguments, arg_name, arg_value):
        pattern, message = (arguments[0], arguments[1])

        # Use temporarly utf-8 encoded value
        try:
            v = str(arg_value, "utf-8")
        except Exception:
            v = arg_value

        if v and not re.match(pattern, v or "", re.UNICODE):
            logger.warning(
                "argument value '%s' for '%s' doesn't match pattern '%s'",
                v,
                arg_name,
                pattern,
            )

            # Attempt to retrieve message translation
            msg = m18n.n(message)
            if msg == message:
                msg = m18n.g(message)

            raise MoulinetteValidationError(
                "invalid_argument", argument=arg_name, error=msg
            )
        return arg_value
Esempio n. 6
0
def tools_maindomain(new_main_domain=None):
    from yunohost.domain import domain_main_domain
    logger.warning(
        m18n.g("deprecated_command_alias",
               prog="yunohost",
               old="tools maindomain",
               new="domain main-domain"))
    return domain_main_domain(new_main_domain=new_main_domain)
Esempio n. 7
0
def test_read_file_missing_file():
    bad_file = "doesnt-exist"

    with pytest.raises(MoulinetteError) as exception:
        read_file(bad_file)

    translation = m18n.g("file_not_exist", path=bad_file)
    expected_msg = translation.format(path=bad_file)
    assert expected_msg in str(exception)
Esempio n. 8
0
    def authenticate(self, authenticator):

        try:
            session_infos = authenticator.get_session_cookie()
        except Exception:
            msg = m18n.g("authentication_required")
            raise HTTPResponse(msg, 401)

        return session_infos
Esempio n. 9
0
def test_required_paremeter_missing_value(iface, caplog):
    required = RequiredParameter(iface)
    with pytest.raises(MoulinetteError) as exception:
        required(True, "a", "")

    translation = m18n.g("argument_required", argument="a")
    expected_msg = translation.format(argument="a")
    assert expected_msg in str(exception)
    assert any("is required" in message for message in caplog.messages)
Esempio n. 10
0
 def authenticate(self, authenticator):
     # Hmpf we have no-use case in yunohost anymore where we need to auth
     # because everything is run as root ...
     # I guess we could imagine some yunohost-independant use-case where
     # moulinette is used to create a CLI for non-root user that needs to
     # auth somehow but hmpf -.-
     msg = m18n.g("password")
     credentials = self.prompt(msg, True, False, color="yellow")
     return authenticator.authenticate_credentials(credentials=credentials)
Esempio n. 11
0
        def _prompt(message):

            if not is_multiline:

                import prompt_toolkit
                from prompt_toolkit.completion import WordCompleter
                from prompt_toolkit.styles import Style

                autocomplete_ = WordCompleter(autocomplete)
                style = Style.from_dict({
                    "": "",
                    "message": f"#ansi{color} bold",
                })

                if help:

                    def bottom_toolbar():
                        return [("class:", help)]

                else:
                    bottom_toolbar = None

                colored_message = [
                    ("class:message", message),
                    ("class:", ": "),
                ]

                return prompt_toolkit.prompt(
                    colored_message,
                    bottom_toolbar=bottom_toolbar,
                    style=style,
                    default=prefill,
                    completer=autocomplete_,
                    complete_while_typing=True,
                    is_password=is_password,
                )

            else:
                while True:
                    value = input(
                        colorize(m18n.g("edit_text_question", message), color))
                    value = value.lower().strip()
                    if value in ["", "n", "no"]:
                        return prefill
                    elif value in ["y", "yes"]:
                        break

                initial_message = prefill.encode("utf-8")

                with tempfile.NamedTemporaryFile(suffix=".tmp") as tf:
                    tf.write(initial_message)
                    tf.flush()
                    call(["editor", tf.name])
                    tf.seek(0)
                    edited_message = tf.read()
                return edited_message.decode("utf-8")
Esempio n. 12
0
def test_read_yaml_cannot_read(test_yaml, mocker):
    error = "foobar"

    mocker.patch("yaml.safe_load", side_effect=Exception(error))
    with pytest.raises(MoulinetteError) as exception:
        read_yaml(str(test_yaml))

    translation = m18n.g("corrupted_yaml", ressource=str(test_yaml), error=error)
    expected_msg = translation.format(ressource=str(test_yaml), error=error)
    assert expected_msg in str(exception)
Esempio n. 13
0
def test_read_file_cannot_read_exception(test_file, mocker):
    error = "foobar"

    mocker.patch("builtins.open", side_effect=Exception(error))
    with pytest.raises(MoulinetteError) as exception:
        read_file(str(test_file))

    translation = m18n.g("unknown_error_reading_file", file=str(test_file), error=error)
    expected_msg = translation.format(file=str(test_file), error=error)
    assert expected_msg in str(exception)
Esempio n. 14
0
def test_remove_file_bad_perms(test_file, mocker):
    error = "foobar"

    mocker.patch("os.remove", side_effect=OSError(error))
    with pytest.raises(MoulinetteError) as exception:
        rm(str(test_file))

    translation = m18n.g("error_removing", path=str(test_file), error=error)
    expected_msg = translation.format(path=str(test_file), error=error)
    assert expected_msg in str(exception)
Esempio n. 15
0
def test_write_to_yaml_bad_perms(test_yaml, mocker):
    error = "foobar"

    mocker.patch("builtins.open", side_effect=IOError(error))
    with pytest.raises(MoulinetteError) as exception:
        write_to_yaml(str(test_yaml), {"a": 1})

    translation = m18n.g("cannot_write_file", file=str(test_yaml), error=error)
    expected_msg = translation.format(file=str(test_yaml), error=error)
    assert expected_msg in str(exception)
Esempio n. 16
0
def test_write_to_file_exception(test_file, mocker):
    error = "foobar"

    mocker.patch("builtins.open", side_effect=Exception(error))
    with pytest.raises(MoulinetteError) as exception:
        write_to_file(str(test_file), "yolo\nswag")

    translation = m18n.g("error_writing_file", file=str(test_file), error=error)
    expected_msg = translation.format(file=str(test_file), error=error)
    assert expected_msg in str(exception)
Esempio n. 17
0
def test_read_json_cannot_read(test_json, mocker):
    error = "foobar"

    mocker.patch("json.loads", side_effect=ValueError(error))
    with pytest.raises(MoulinetteError) as exception:
        read_json(str(test_json))

    translation = m18n.g("corrupted_json", ressource=str(test_json), error=error)
    expected_msg = translation.format(ressource=str(test_json), error=error)
    assert expected_msg in str(exception)
Esempio n. 18
0
def test_write_yaml_to_file_exception(test_file, mocker):
    error = "foobar"

    dummy_dict = {"foo": 42, "bar": ["a", "b", "c"]}

    mocker.patch("builtins.open", side_effect=Exception(error))
    with pytest.raises(MoulinetteError) as exception:
        write_to_yaml(str(test_file), dummy_dict)

    translation = m18n.g("error_writing_file", file=str(test_file), error=error)
    expected_msg = translation.format(file=str(test_file), error=error)
    assert expected_msg in str(exception)
Esempio n. 19
0
def test_chmod_exception(test_file, mocker):
    error = "foobar"

    mocker.patch("os.chmod", side_effect=Exception(error))
    with pytest.raises(MoulinetteError) as exception:
        chmod(str(test_file), 0o000)

    translation = m18n.g(
        "error_changing_file_permissions", path=str(test_file), error=str(error)
    )
    expected_msg = translation.format(path=str(test_file), error=str(error))
    assert expected_msg in str(exception)
Esempio n. 20
0
def test_chown(test_file):
    with pytest.raises(ValueError):
        chown(str(test_file))

    current_uid = os.getuid()
    current_gid = os.getgid()
    chown(str(test_file), current_uid, current_gid)

    assert os.stat(str(test_file)).st_uid == current_uid
    assert os.stat(str(test_file)).st_gid == current_gid

    current_gid = os.getgid()
    chown(str(test_file), uid=None, gid=current_gid)

    assert os.stat(str(test_file)).st_gid == current_gid

    current_uid = pwd.getpwuid(os.getuid())[0]
    current_gid = grp.getgrgid(os.getgid())[0]
    chown(str(test_file), current_uid, current_gid)

    assert os.stat(str(test_file)).st_uid == os.getuid()
    assert os.stat(str(test_file)).st_gid == os.getgid()

    fake_user = "******"
    with pytest.raises(MoulinetteError) as exception:
        chown(str(test_file), fake_user)

    translation = m18n.g("unknown_user", user=fake_user)
    expected_msg = translation.format(user=fake_user)
    assert expected_msg in str(exception)

    fake_grp = "nogrplol"
    with pytest.raises(MoulinetteError) as exception:
        chown(str(test_file), gid=fake_grp)

    translation = m18n.g("unknown_group", group=fake_grp)
    expected_msg = translation.format(group=fake_grp)
    assert expected_msg in str(exception)
Esempio n. 21
0
def check_command_is_valid_before_postinstall(args):

    allowed_if_not_postinstalled = [
        'tools postinstall', 'tools versions', 'backup list', 'backup restore',
        'log display'
    ]

    if (len(args) < 2
            or (args[0] + ' ' + args[1] not in allowed_if_not_postinstalled)):
        init_i18n()
        print(
            colorize(m18n.g('error'), 'red') + " " +
            m18n.n('yunohost_not_installed'))
        sys.exit(1)
Esempio n. 22
0
def test_pattern_parameter(iface, caplog, mocker):
    pattern = PatternParameter(iface)
    arg = pattern(["foo", "foobar"], "foo_name", "foo_value")
    assert arg == "foo_value"

    error = "error_message"
    mocker.patch("moulinette.Moulinette18n.n", return_value=error)
    with pytest.raises(MoulinetteError) as exception:
        pattern(["foo", "message"], "foo_name", "not_match")

    translation = m18n.g("invalid_argument", argument="foo_name", error=error)
    expected_msg = translation.format(argument="foo_name", error=error)
    assert expected_msg in str(exception)
    assert any("doesn't match pattern" in message
               for message in caplog.messages)
Esempio n. 23
0
def check_command_is_valid_before_postinstall(args):

    allowed_if_not_postinstalled = [
        "tools postinstall",
        "tools versions",
        "tools shell",
        "backup list",
        "backup restore",
        "log display",
    ]

    if len(args) < 2 or (args[0] + " " + args[1] not in allowed_if_not_postinstalled):
        init_i18n()
        print(colorize(m18n.g("error"), "red") + " " + m18n.n("yunohost_not_installed"))
        sys.exit(1)
Esempio n. 24
0
def mkdir(path, mode=0o0777, parents=False, uid=None, gid=None, force=False):
    """Create a directory with optional features

    Create a directory and optionaly set its permissions to mode and its
    owner and/or group. If path refers to an existing path, nothing is done
    unless force is True.

    Keyword arguments:
        - path -- The directory to create
        - mode -- Numeric path mode to set
        - parents -- Make parent directories as needed
        - uid -- Numeric uid or user name
        - gid -- Numeric gid or group name
        - force -- Force directory creation and owning even if the path exists

    """
    if os.path.exists(path) and not force:
        raise OSError(errno.EEXIST, m18n.g("folder_exists", path=path))

    if parents:
        # Create parents directories as needed
        head, tail = os.path.split(path)
        if not tail:
            head, tail = os.path.split(head)
        if head and tail and not os.path.exists(head):
            try:
                mkdir(head, mode, parents, uid, gid, force)
            except OSError as e:
                if e.errno != errno.EEXIST:
                    raise
            if tail == os.curdir:
                return

    # Create directory and set permissions
    try:
        oldmask = os.umask(000)
        os.mkdir(path, mode)
        os.umask(oldmask)
    except OSError:
        # mimic Python3.2+ os.makedirs exist_ok behaviour
        if not force or not os.path.isdir(path):
            raise

    if uid is not None or gid is not None:
        chown(path, uid, gid)
Esempio n. 25
0
 def format(self, record):
     """Enhance message with level and colors if supported."""
     msg = record.getMessage()
     if self.supports_color():
         level = ""
         if self.level <= log.DEBUG:
             # add level name before message
             level = "%s " % record.levelname
         elif record.levelname in ["SUCCESS", "WARNING", "ERROR", "INFO"]:
             # add translated level name before message
             level = "%s " % m18n.g(record.levelname.lower())
         color = self.LEVELS_COLOR.get(record.levelno, "white")
         msg = "{}{}{}{}".format(colors_codes[color], level, END_CLI_COLOR,
                                 msg)
     if self.formatter:
         # use user-defined formatter
         record.__dict__[self.message_key] = msg
         return self.formatter.format(record)
     return msg
Esempio n. 26
0
    def messages(self):
        """Listen to the messages WebSocket stream

        Retrieve the WebSocket stream and send to it each messages displayed by
        the display method. They are JSON encoded as a dict { style: message }.
        """

        profile = request.params.get("profile", self.actionsmap.default_authentication)
        authenticator = self.actionsmap.get_authenticator(profile)

        s_id = authenticator.get_session_cookie()["id"]
        try:
            queue = self.log_queues[s_id]
        except KeyError:
            # Create a new queue for the session
            queue = Queue()
            self.log_queues[s_id] = queue

        wsock = request.environ.get("wsgi.websocket")
        if not wsock:
            raise HTTPResponse(m18n.g("websocket_request_expected"), 500)

        while True:
            item = queue.get()
            try:
                # Retrieve the message
                style, message = item
            except TypeError:
                if item == StopIteration:
                    # Delete the current queue and break
                    del self.log_queues[s_id]
                    break
                logger.exception("invalid item in the messages queue: %r", item)
            else:
                try:
                    # Send the message
                    wsock.send(json_encode({style: message}))
                except WebSocketError:
                    break
            sleep(0)
Esempio n. 27
0
    def prompt(
        self,
        message,
        is_password=False,
        confirm=False,
        color="blue",
        prefill="",
        is_multiline=False,
        autocomplete=[],
        help=None,
    ):
        """Prompt for a value

        Keyword arguments:
            - color -- The color to use for prompting message
        """

        if not os.isatty(1):
            raise MoulinetteError("Not a tty, can't do interactive prompts",
                                  raw_msg=True)

        def _prompt(message):

            if not is_multiline:

                import prompt_toolkit
                from prompt_toolkit.completion import WordCompleter
                from prompt_toolkit.styles import Style

                autocomplete_ = WordCompleter(autocomplete)
                style = Style.from_dict({
                    "": "",
                    "message": f"#ansi{color} bold",
                })

                if help:

                    def bottom_toolbar():
                        return [("class:", help)]

                else:
                    bottom_toolbar = None

                colored_message = [
                    ("class:message", message),
                    ("class:", ": "),
                ]

                return prompt_toolkit.prompt(
                    colored_message,
                    bottom_toolbar=bottom_toolbar,
                    style=style,
                    default=prefill,
                    completer=autocomplete_,
                    complete_while_typing=True,
                    is_password=is_password,
                )

            else:
                while True:
                    value = input(
                        colorize(m18n.g("edit_text_question", message), color))
                    value = value.lower().strip()
                    if value in ["", "n", "no"]:
                        return prefill
                    elif value in ["y", "yes"]:
                        break

                initial_message = prefill.encode("utf-8")

                with tempfile.NamedTemporaryFile(suffix=".tmp") as tf:
                    tf.write(initial_message)
                    tf.flush()
                    call(["editor", tf.name])
                    tf.seek(0)
                    edited_message = tf.read()
                return edited_message.decode("utf-8")

        value = _prompt(message)

        if confirm:
            m = message[0].lower() + message[1:]
            if _prompt(m18n.g("confirm", prompt=m)) != value:
                raise MoulinetteValidationError("values_mismatch")

        return value