Example #1
0
def _UserContext(user_obj: LoggedInUser) -> Iterator[None]:
    """Managing authenticated user context

    After the user has been authenticated, initialize the global user object."""
    old_user = request_local_attr().user
    try:
        request_local_attr().user = user_obj
        yield
    finally:
        request_local_attr().user = old_user
Example #2
0
def _translation() -> Optional[Translation]:
    try:
        return request_local_attr().translation
    except RuntimeError:
        # TODO: Once we cleaned up all wrong _() to _l(), we can clean this up
        pass
    return None
Example #3
0
    def begin_form(
        self,
        name: str,
        action: Optional[str] = None,
        method: str = "GET",
        onsubmit: Optional[str] = None,
        add_transid: bool = True,
    ) -> None:
        self.form_name = name
        self.form_vars = []
        self.form_has_submit_button = False

        if action is None:
            action = requested_file_name(self.request) + ".py"
        self.open_form(
            id_="form_%s" % name,
            name=name,
            class_=name,
            action=action,
            method=method,
            onsubmit=onsubmit,
            enctype="multipart/form-data" if method.lower() == "post" else None,
        )

        session = request_local_attr("session")
        if hasattr(session, "session_info"):
            self.hidden_field("csrf_token", session.session_info.csrf_token)

        self.hidden_field("filled_in", name, add_var=True)
        if add_transid:
            self.hidden_field(
                "_transid",
                str(transactions.get()),
                add_var=True,
            )
Example #4
0
 def _set_js_csrf_token(self) -> None:
     session = request_local_attr("session")
     # session is LocalProxy, only on access it is None, so we cannot test on 'is None'
     if not hasattr(session, "session_info"):
         return
     self.javascript(
         "var global_csrf_token = %s;" % (json.dumps(session.session_info.csrf_token))
     )
Example #5
0
def localize(lang: Optional[str]) -> None:
    if lang is None:
        unlocalize()
        return

    gettext_translation = _init_language(lang)
    if not gettext_translation:
        unlocalize()
        return

    request_local_attr().translation = Translation(
        translation=gettext_translation, name=lang)
Example #6
0
def test_get_start_url_user_config(
        monkeypatch: MonkeyPatch,
        request_context: RequestContextFixture) -> None:
    monkeypatch.setattr(active_config, "start_url", "bla.py")

    class MockUser:
        @property
        def start_url(self) -> str:
            return "user_url.py"

    local = request_local_attr()
    monkeypatch.setattr(local, "user", MockUser())

    assert cmk.gui.main._get_start_url() == "user_url.py"
Example #7
0
def load_config() -> None:
    # Set default values for all user-changable configuration settings
    raw_config = get_default_config()

    # Initialize sites with default site configuration. Need to do it here to
    # override possibly deleted sites
    raw_config["sites"] = default_single_site_configuration()

    # Load assorted experimental parameters if any
    experimental_config = cmk.utils.paths.make_experimental_config_file()
    if experimental_config.exists():
        _load_config_file_to(str(experimental_config), raw_config)

    # First load main file
    _load_config_file_to(cmk.utils.paths.default_config_dir + "/multisite.mk", raw_config)

    # Load also recursively all files below multisite.d
    conf_dir = cmk.utils.paths.default_config_dir + "/multisite.d"
    filelist = []
    if os.path.isdir(conf_dir):
        for root, _directories, files in os.walk(conf_dir):
            for filename in files:
                if filename.endswith(".mk"):
                    filelist.append(root + "/" + filename)

    filelist.sort()
    for p in filelist:
        _load_config_file_to(p, raw_config)

    raw_config["sites"] = prepare_raw_site_config(raw_config["sites"])
    raw_config["tags"] = cmk.utils.tags.get_effective_tag_config(raw_config["wato_tags"])

    # TODO: Temporary local hack to transform the values to the correct type. This needs
    # to be done in make_config_object() in the next step.
    if "agent_signature_keys" in raw_config:
        raw_config["agent_signature_keys"] = {
            key_id: Key.parse_obj(raw_key)
            for key_id, raw_key in raw_config["agent_signature_keys"].items()
        }

    # Make sure, builtin roles are present, even if not modified and saved with WATO.
    for br in builtin_role_ids:
        raw_config["roles"].setdefault(br, {})

    request_local_attr().config = make_config_object(raw_config)

    execute_post_config_load_hooks()
Example #8
0
        self._response_stack: List[Response] = [response]

    def write(self, data: bytes) -> None:
        self._response_stack[-1].stream.write(data)

    @contextmanager
    def plugged(self) -> Iterator[None]:
        self._response_stack.append(Response())
        try:
            yield
        finally:
            response = self._response_stack.pop()
            # Rest of popped response is written to now topmost request.
            # TODO: Investigate call sites whether or not this is a used feature
            self.write(response.get_data())

    def _is_plugged(self) -> bool:
        return len(self._response_stack) > 1

    def drain(self) -> str:
        """Return the content of the topmost response object"""
        if not self._is_plugged():
            return ""

        text = self._response_stack.pop().get_data(as_text=True)
        self._response_stack.append(Response())
        return text


output_funnel: OutputFunnel = request_local_attr("output_funnel")
Example #9
0
import cmk.gui.permissions as permissions
import cmk.gui.site_config as site_config
from cmk.gui.config import active_config, builtin_role_ids
from cmk.gui.ctx_stack import request_local_attr
from cmk.gui.exceptions import MKAuthException
from cmk.gui.http import request
from cmk.gui.i18n import _
from cmk.gui.utils.roles import may_with_roles, roles_of_user
from cmk.gui.utils.transaction_manager import TransactionManager

if TYPE_CHECKING:
    # Cyclic import!
    from cmk.gui.plugins.openapi.restful_objects import Endpoint

endpoint: Endpoint = request_local_attr("endpoint")


class LoggedInUser:
    """Manage the currently logged in user

    This objects intention is currently only to handle the currently logged in user after
    authentication.
    """
    def __init__(self, user_id: Optional[str]) -> None:
        self.id = UserId(user_id) if user_id else None
        self.transactions = TransactionManager(request, self)

        self.confdir = _confdir_for_user_id(self.id)
        self.role_ids = self._gather_roles(self.id)
        baserole_ids = _baserole_ids_from_role_ids(self.role_ids)
Example #10
0
    if "admin" in baserole_ids:
        return "admin"
    if "user" in baserole_ids:
        return "user"
    return "guest"


def _initial_permission_cache(user_id: Optional[UserId]) -> Dict[str, bool]:
    if user_id is None:
        return {}

    # Prepare cache of already computed permissions
    # Make sure, admin can restore permissions in any case!
    if user_id in active_config.admin_users:
        return {
            "general.use": True,  # use Multisite
            "wato.use": True,  # enter WATO
            "wato.edit": True,  # make changes in WATO...
            "wato.users": True,  # ... with access to user management
        }
    return {}


def save_user_file(name: str, data: Any, user_id: UserId) -> None:
    path = cmk.utils.paths.profile_dir.joinpath(user_id, name + ".mk")
    store.mkdir(path.parent)
    store.save_object_to_file(path, data)


user: LoggedInUser = request_local_attr("user")
Example #11
0
    client.

    It is possible to disable the applications request timeout (temoporarily)
    or totally for specific calls, but the timeout to the client will always
    be applied by the system webserver. So the client will always get a error
    page while the site apache continues processing the request (until the
    first try to write anything to the client) which will result in an
    exception.
    """

    def enable_timeout(self, duration: int) -> None:
        def handle_request_timeout(signum: int, frame: Optional[FrameType]) -> None:
            raise RequestTimeout(
                _(
                    "Your request timed out after %d seconds. This issue may be "
                    "related to a local configuration problem or a request which works "
                    "with a too large number of objects. But if you think this "
                    "issue is a bug, please send a crash report."
                )
                % duration
            )

        signal.signal(signal.SIGALRM, handle_request_timeout)
        signal.alarm(duration)

    def disable_timeout(self) -> None:
        signal.alarm(0)


timeout_manager: TimeoutManager = request_local_attr("timeout_manager")
Example #12
0
    def check_transaction(self) -> bool:
        """called by page functions in order to check, if this was a reload or the original form submission.

        Increases the transid of the user, if the latter was the case.

        There are three return codes:

        True:  -> positive confirmation by the user
        False: -> not yet confirmed, question is being shown
        None:  -> a browser reload or a negative confirmation
        """
        if self.transaction_valid():
            transid = self._request.var("_transid")
            if transid and transid != "-1":
                self._invalidate(transid)
            return True
        return False

    def _invalidate(self, used_id: str) -> None:
        """Remove the used transid from the list of valid ones"""
        valid_ids = self._user.transids(lock=True)
        try:
            valid_ids.remove(used_id)
        except ValueError:
            return
        self._user.save_transids(valid_ids)


transactions: TransactionManager = request_local_attr("transactions")
Example #13
0
page.
"""

from typing import Dict, Iterator, Mapping, Optional

from cmk.gui.ctx_stack import request_local_attr
from cmk.gui.exceptions import MKUserError


class UserErrors(Mapping[Optional[str], str]):
    def __init__(self) -> None:
        self._errors: Dict[Optional[str], str] = {}

    def add(self, error: MKUserError) -> None:
        self._errors[error.varname] = str(error)

    def __bool__(self) -> bool:
        return bool(self._errors)

    def __getitem__(self, key: Optional[str]) -> str:
        return self._errors.__getitem__(key)

    def __iter__(self) -> Iterator[Optional[str]]:
        return self._errors.__iter__()

    def __len__(self) -> int:
        return len(self._errors)


user_errors: UserErrors = request_local_attr("user_errors")
Example #14
0
                    (json_request, e))

        for key, val in self.itervars():
            if key not in ["request", "output_format"] + exclude_vars:
                request_[key] = val

        return request_


class Response(werkzeug.Response):
    # NOTE: Currently we rely on a *relative* Location header in redirects!
    autocorrect_location_header = False

    default_mimetype = "text/html"

    def set_http_cookie(self, key: str, value: str, *, secure: bool) -> None:
        super().set_cookie(key,
                           value,
                           secure=secure,
                           httponly=True,
                           samesite="Lax")

    def set_content_type(self, mime_type: str) -> None:
        self.headers["Content-type"] = get_content_type(
            mime_type, self.charset)


# From request context
request: Request = request_local_attr("request")
response: Response = request_local_attr("response")
Example #15
0
    The `decorators` module. The other modules will be called from there.

The modules:

  * The `request_schemas` and `response_schemas` modules are special, as
    they only contain abstract models of the request and response formats
    and will be used in validation (currently only response).

  * The `specification` module is the entry point for the OpenAPI
    spec generation code and contains mostly boilerplate code and some
    useful parameter definitions.

  * The `constructors` module contains helpers which generate parts
    of the nested JSON struct that is specified by the Restful Objects
    specification.

  * The `code_examples` module contains a helper which renders Jinja2
    templates with source code examples. These are put into the spec
    by the decorator. The examples are specific to the Redoc documentation
    generator library.

"""

from cmk.gui.ctx_stack import request_local_attr
from cmk.gui.plugins.openapi.restful_objects.decorators import Endpoint
from cmk.gui.plugins.openapi.restful_objects.specification import SPEC

__all__ = ["Endpoint", "SPEC", "endpoint"]

endpoint: Endpoint = request_local_attr("endpoint")
Example #16
0
            Path(cmk.utils.paths.web_dir), cmk.utils.paths.local_web_dir
    ]:
        if not base_dir.exists():
            continue

        theme_base_dir = base_dir / "htdocs" / "themes"
        if not theme_base_dir.exists():
            continue

        for theme_dir in theme_base_dir.iterdir():
            meta_file = theme_dir / "theme.json"
            if not meta_file.exists():
                continue

            try:
                theme_meta = json.loads(
                    meta_file.open(encoding="utf-8").read())
            except ValueError:
                # Ignore broken meta files and show the directory name as title
                theme_meta = {
                    "title": theme_dir.name,
                }

            assert isinstance(theme_meta["title"], str)
            themes[theme_dir.name] = theme_meta["title"]

    return sorted(themes.items())


theme: Theme = request_local_attr("theme")
Example #17
0
        _check_auth_cookie(cookie_name)
    except MKAuthException:
        # Suppress cookie validation errors from other sites cookies
        auth_logger.debug("Exception while checking cookie %s: %s" %
                          (cookie_name, traceback.format_exc()))
    except Exception:
        auth_logger.debug("Exception while checking cookie %s: %s" %
                          (cookie_name, traceback.format_exc()))


def set_auth_type(_auth_type: AuthType) -> None:
    request_local_attr().auth_type = _auth_type


auth_type: Union[AuthType, LocalProxy] = LocalProxy(
    lambda: request_local_attr().auth_type)


@page_registry.register_page("login")
class LoginPage(Page):
    def __init__(self) -> None:
        super().__init__()
        self._no_html_output = False

    def set_no_html_output(self, no_html_output: bool) -> None:
        self._no_html_output = no_html_output

    def page(self) -> None:
        # Initialize the cmk.gui.i18n for the login dialog. This might be
        # overridden later after user login
        cmk.gui.i18n.localize(
Example #18
0
def set_auth_type(_auth_type: AuthType) -> None:
    request_local_attr().auth_type = _auth_type
Example #19
0
                drop_handler="function(index){return cmk.element_dragging.url_drop_handler(%s, index);})"
                % json.dumps(base_url),
            )
        )

    def element_dragger_js(
        self, dragging_tag: str, drop_handler: str, handler_args: Mapping[str, Any]
    ) -> None:
        self.write_html(
            HTMLGenerator.render_element_dragger(
                dragging_tag,
                drop_handler="function(new_index){return %s(%s, new_index);})"
                % (drop_handler, json.dumps(handler_args)),
            )
        )

    # Currently only tested with tables. But with some small changes it may work with other
    # structures too.
    @staticmethod
    def render_element_dragger(dragging_tag: str, drop_handler: str) -> HTML:
        return HTMLWriter.render_a(
            HTMLGenerator.render_icon("drag", _("Move this entry")),
            href="javascript:void(0)",
            class_=["element_dragger"],
            onmousedown="cmk.element_dragging.start(event, this, %s, %s"
            % (json.dumps(dragging_tag.upper()), drop_handler),
        )


html: HTMLGenerator = request_local_attr("html")
Example #20
0
######################################################################
# TODO: This should live somewhere else...
class PrependURLFilter(logging.Filter):
    def filter(self, record):
        if record.levelno >= logging.ERROR:
            record.msg = "%s %s" % (request.requested_url, record.msg)
        return True


# From app context
current_app = LocalProxy(partial(_lookup_app_object, "app"))
g: Any = LocalProxy(partial(_lookup_app_object, "g"))

# NOTE: All types FOO below are actually a Union[Foo, LocalProxy], but
# LocalProxy is meant as a transparent proxy, so we leave it out to de-confuse
# mypy. LocalProxy uses a lot of reflection magic, which can't be understood by
# tools in general.

# From request context
request: http.Request = request_local_attr("request")
response: http.Response = request_local_attr("response")
output_funnel: OutputFunnel = request_local_attr("output_funnel")
active_config: Config = request_local_attr("config")
endpoint: Endpoint = request_local_attr("endpoint")
user_errors: UserErrors = request_local_attr("user_errors")

html: htmllib.html = request_local_attr("html")
timeout_manager: TimeoutManager = request_local_attr("timeout_manager")
theme: Theme = request_local_attr("theme")
display_options: DisplayOptions = request_local_attr("display_options")
Example #21
0
@dataclass
class Config(CREConfig, CEEConfig, CMEConfig):
    """Holds the loaded configuration during GUI processing

    The loaded configuration is then accessible through `from cmk.gui.globals import config`.
    For builtin config variables type checking and code completion works.

    This class is extended by `load_config` to support custom config variables which may
    be introduced by 3rd party extensions. For these variables we don't have the features
    mentioned above. But that's fine for now.
    """

    tags: cmk.utils.tags.TagConfig = cmk.utils.tags.TagConfig()


active_config: Config = request_local_attr("config")


# .
#   .--Functions-----------------------------------------------------------.
#   |             _____                 _   _                              |
#   |            |  ___|   _ _ __   ___| |_(_) ___  _ __  ___              |
#   |            | |_ | | | | '_ \ / __| __| |/ _ \| '_ \/ __|             |
#   |            |  _|| |_| | | | | (__| |_| | (_) | | | \__ \             |
#   |            |_|   \__,_|_| |_|\___|\__|_|\___/|_| |_|___/             |
#   |                                                                      |
#   +----------------------------------------------------------------------+
#   |  Helper functions for config parsing, login, etc.                    |
#   '----------------------------------------------------------------------'

Example #22
0
        # links). These links need to know about the provided display_option parameter. The links
        # could use "display_options.options" but this contains the implicit options which should
        # not be added to the URLs. So the real parameters need to be preserved for this case.
        self.title_options = request.get_ascii_input("display_options")

        # If display option 'M' is set, then all links are targetet to the 'main'
        # frame. Also the display options are removed since the view in the main
        # frame should be displayed in standard mode.
        if self.disabled(self.M):
            html.link_target = "main"
            request.del_var("display_options")

    # If all display_options are upper case assume all not given values default
    # to lower-case. Vice versa when all display_options are lower case.
    # When the display_options are mixed case assume all unset options to be enabled
    def _merge_with_defaults(self, opts: str) -> str:
        do_defaults = self.all_off() if opts.isupper() else self.all_on()
        for c in do_defaults:
            if c.lower() not in opts.lower():
                opts += c
        return opts

    def enabled(self, opt: str) -> bool:
        return opt in self.options

    def disabled(self, opt: str) -> bool:
        return opt not in self.options


display_options: DisplayOptions = request_local_attr("display_options")
Example #23
0
def unlocalize() -> None:
    request_local_attr().translation = None