예제 #1
0
    def load(cls):
        cls.clear_instances()

        # First load builtin pages. Set username to ''
        for name, page_dict in cls.builtin_pages().items():
            page_dict["owner"] = UserId(u'')  # might have been forgotten on copy action
            page_dict["public"] = True
            page_dict["name"] = name
            new_page = cls(page_dict)
            cls.add_instance(("", name), new_page)

        # Now scan users subdirs for files "user_$type_name.mk"
        for user_dir in os.listdir(config.config_dir):
            user = UserId(ensure_unicode(user_dir))
            try:
                path = "%s/%s/user_%ss.mk" % (config.config_dir, six.ensure_str(user),
                                              cls.type_name())
                if not os.path.exists(path):
                    continue

                if not userdb.user_exists(user):
                    continue

                user_pages = store.load_object_from_file(path, default={})
                for name, page_dict in user_pages.items():
                    page_dict["owner"] = user
                    page_dict["name"] = name
                    cls.add_instance((user, name), cls(page_dict))

            except SyntaxError as e:
                raise MKGeneralException(
                    _("Cannot load %s from %s: %s") % (cls.type_name(), path, e))

        cls._load()
        cls._declare_instance_permissions()
예제 #2
0
파일: users.py 프로젝트: majma24/checkmk
    def _from_vars(self):
        # TODO: Should we turn the both fields below into Optional[UserId]?
        self._user_id = html.request.get_unicode_input(
            "edit")  # missing -> new user
        # This is needed for the breadcrumb computation:
        # When linking from user notification rules page the request variable is "user"
        # instead of "edit". We should also change that variable to "user" on this page,
        # then we can simply use self._user_id.
        if not self._user_id and html.request.has_var("user"):
            self._user_id = html.request.get_str_input_mandatory("user")

        self._cloneid = html.request.get_unicode_input(
            "clone")  # Only needed in 'new' mode
        # TODO: Nuke the field below? It effectively hides facts about _user_id for mypy.
        self._is_new_user = self._user_id is None
        self._users = userdb.load_users(lock=html.is_transaction())
        new_user = userdb.new_user_template('htpasswd')
        if self._user_id is not None:
            self._user = self._users.get(UserId(self._user_id), new_user)
        elif self._cloneid:
            self._user = self._users.get(UserId(self._cloneid), new_user)
        else:
            self._user = new_user
        self._locked_attributes = userdb.locked_attributes(
            self._user.get('connector'))
예제 #3
0
    def __init__(self, user_id):
        self.id = UserId(user_id)

        self._load_confdir()
        self._load_roles()
        self._load_attributes()
        self._load_permissions()
        self._load_site_config()
        self._button_counts = None
예제 #4
0
def fixture_test_config(tmp_path):
    file_path = tmp_path / "htpasswd"
    htpwd = htpasswd.Htpasswd(file_path)

    htpwd.save({
        UserId("non-unicode"): "non-unicode",
        UserId("abcä"): "bbbä",
    })

    return htpwd
예제 #5
0
def _verify_user(environ: WSGIEnvironment, now: datetime) -> RFC7662:
    verified: List[RFC7662] = []

    auth_header = environ.get("HTTP_AUTHORIZATION", "")
    basic_user = None
    if auth_header:
        auth_type, _ = auth_header.split(None, 1)
        if auth_type == "Bearer":
            user_id, secret = user_from_bearer_header(auth_header)
            automation_user = automation_auth(user_id, secret)
            if automation_user:
                verified.append(automation_user)
            else:
                # GUI user and Automation users are mutually exclusive. Checking only once is less
                # work for the system.
                gui_user = gui_user_auth(user_id, secret, now)
                if gui_user:
                    verified.append(gui_user)
        elif auth_type == "Basic":
            # We store this for sanity checking below, once we get a REMOTE_USER key.
            # If we don't get a REMOTE_USER key, this value will be ignored.
            basic_user = user_from_basic_header(auth_header)
        else:
            raise MKAuthException(f"Unsupported Auth Type: {auth_type}")

    remote_user = environ.get("REMOTE_USER", "")
    if remote_user and userdb.user_exists(UserId(remote_user)):
        if basic_user and basic_user[0] != remote_user:
            raise MKAuthException("Mismatch in authentication headers.")
        verified.append(rfc7662_subject(UserId(remote_user), "web_server"))

    cookie = Request(environ).cookies.get(f"auth_{omd_site()}")
    if cookie:
        user_id, session_id, cookie_hash = user_from_cookie(cookie)
        check_parsed_auth_cookie(user_id, session_id, cookie_hash)
        verified.append(rfc7662_subject(user_id, "cookie"))

    if not verified:
        raise MKAuthException(
            "You need to be authenticated to use the REST API.")

    # We pick the first successful authentication method, which means the precedence is the same
    # as the order in the code.
    final_candidate = verified[0]
    user_id = final_candidate["sub"]
    if not userdb.is_customer_user_allowed_to_login(user_id):
        raise MKAuthException(f"{user_id} may not log in here.")

    if userdb.user_locked(user_id):
        raise MKAuthException(f"{user_id} not authorized.")

    if change_reason := userdb.need_to_change_pw(user_id, now):
        raise MKAuthException(
            f"{user_id} needs to change the password ({change_reason}).")
예제 #6
0
파일: sites.py 프로젝트: mahdi7839/checkmk
def _livestatus_auth_user(user: LoggedInUser, force_authuser: Optional[UserId]) -> Optional[UserId]:
    if not user.may("general.see_all"):
        return user.id
    if force_authuser == UserId("1"):
        return user.id
    if force_authuser == UserId("0"):
        return None
    if force_authuser:
        return force_authuser  # set a different user
    if user.get_attribute("force_authuser"):
        return user.id
    return None
예제 #7
0
def _check_auth_automation() -> UserId:
    secret = html.request.get_str_input_mandatory("_secret", "").strip()
    user_id = html.request.get_unicode_input_mandatory("_username", "")

    user_id = UserId(user_id.strip())
    html.del_var_from_env('_username')
    html.del_var_from_env('_secret')

    if verify_automation_secret(user_id, secret):
        # Auth with automation secret succeeded - mark transid as unneeded in this case
        html.transaction_manager.ignore()
        set_auth_type("automation")
        return user_id
    raise MKAuthException(_("Invalid automation secret for user %s") % user_id)
예제 #8
0
def test_save(tmp_path):
    file_path = tmp_path / "htpasswd"
    htpwd = htpasswd.Htpasswd(file_path)

    htpwd.save({
        UserId("non-unicode"): "non-unicode",
        UserId("abcä"): "bbbä",
    })

    loaded = htpwd.load()
    assert loaded == {
        UserId("non-unicode"): "non-unicode",
        UserId("abcä"): "bbbä",
    }
예제 #9
0
 def _from_vars(self):
     # TODO: Should we turn the both fields below into Optional[UserId]?
     self._user_id = html.request.get_unicode_input("edit")  # missing -> new user
     self._cloneid = html.request.get_unicode_input("clone")  # Only needed in 'new' mode
     # TODO: Nuke the field below? It effectively hides facts about _user_id for mypy.
     self._is_new_user = self._user_id is None
     self._users = userdb.load_users(lock=html.is_transaction())
     new_user = userdb.new_user_template('htpasswd')
     if self._user_id is not None:
         self._user = self._users.get(UserId(self._user_id), new_user)
     elif self._cloneid:
         self._user = self._users.get(UserId(self._cloneid), new_user)
     else:
         self._user = new_user
     self._locked_attributes = userdb.locked_attributes(self._user.get('connector'))
예제 #10
0
def _verify_user(environ) -> RFC7662:
    verified: List[RFC7662] = []

    auth_header = environ.get('HTTP_AUTHORIZATION', '')
    basic_user = None
    if auth_header:
        auth_type, _ = auth_header.split(None, 1)
        if auth_type == 'Bearer':
            user_id, secret = user_from_bearer_header(auth_header)
            automation_user = automation_auth(user_id, secret)
            if automation_user:
                verified.append(automation_user)

            gui_user = gui_user_auth(user_id, secret)
            if gui_user:
                verified.append(gui_user)
        elif auth_type == 'Basic':
            # We store this for sanity checking below, once we get a REMOTE_USER key.
            # If we don't get a REMOTE_USER key, this value will be ignored.
            basic_user = user_from_basic_header(auth_header)
        else:
            raise MKAuthException(f"Unsupported Auth Type: {auth_type}")

    remote_user = environ.get('REMOTE_USER', '')
    if remote_user and userdb.user_exists(UserId(remote_user)):
        if basic_user and basic_user[0] != remote_user:
            raise MKAuthException("Mismatch in authentication headers.")
        verified.append(rfc7662_subject(UserId(remote_user), 'webserver'))

    cookie = Request(environ).cookies.get(f"auth_{omd_site()}")
    if cookie:
        user_id, session_id, cookie_hash = user_from_cookie(cookie)
        check_parsed_auth_cookie(user_id, session_id, cookie_hash)
        verified.append(rfc7662_subject(user_id, 'cookie'))

    if not verified:
        raise MKAuthException("You need to be authenticated to use the REST API.")

    # We pick the first successful authentication method, which means the precedence is the same
    # as the oder in the code.
    final_candidate = verified[0]
    if not userdb.is_customer_user_allowed_to_login(final_candidate['sub']):
        raise MKAuthException(f"{final_candidate['sub']} may not log in here.")

    if userdb.user_locked(final_candidate['sub']):
        raise MKAuthException(f"{final_candidate['sub']} not authorized.")

    return final_candidate
예제 #11
0
파일: auth.py 프로젝트: mahdi7839/checkmk
def bearer_auth(auth_header: str) -> RFC7662:
    try:
        _, token = auth_header.split("Bearer", 1)
    except ValueError:
        raise MKAuthException(None, "Not a valid Bearer token.")

    try:
        user_id, secret = token.strip().split(' ', 1)
    except ValueError:
        raise MKAuthException("No user/password combination in Bearer token.")

    if not secret:
        raise MKAuthException("Empty password not allowed.")

    if not user_id:
        raise MKAuthException("Empty user not allowed.")

    if "/" in user_id:
        raise MKAuthException("No slashes / allowed in username.")

    if not verify_automation_secret(UserId(ensure_str(user_id)), secret):
        raise MKAuthException("Not authenticated.")

    # Auth with automation secret succeeded - mark transid as unneeded in this case
    return rfc7662_subject(user_id, 'automation')
예제 #12
0
def add_change(
    action_name: str,
    text: LogMessage,
    object_ref: Optional[ObjectRef] = None,
    diff_text: Optional[str] = None,
    add_user: bool = True,
    need_sync: Optional[bool] = None,
    need_restart: Optional[bool] = None,
    domains: Optional[List[Type[ABCConfigDomain]]] = None,
    sites: Optional[List[SiteId]] = None,
    domain_settings: Optional[DomainSettings] = None,
) -> None:

    log_audit(
        action=action_name,
        message=text,
        object_ref=object_ref,
        user_id=user.id if add_user else UserId(""),
        diff_text=diff_text,
    )
    cmk.gui.watolib.sidebar_reload.need_sidebar_reload()

    search.update_index_background(action_name)

    ActivateChangesWriter().add_change(
        action_name,
        text,
        object_ref,
        add_user,
        need_sync,
        need_restart,
        domains,
        sites,
        domain_settings,
    )
예제 #13
0
def test_managed_global_internal(monkeypatch):
    # this test uses the internal mechanics of the user endpoint
    monkeypatch.setattr(
        "cmk.gui.watolib.global_settings.rulebased_notifications_enabled",
        lambda: True)

    edit_users({
        "user": {
            "attributes": {
                "ui_theme": None,
                "ui_sidebar_position": None,
                "nav_hide_icons_title": None,
                "icons_per_item": None,
                "show_mode": None,
                "start_url": None,
                "force_authuser": False,
                "enforce_pw_change": False,
                "alias": "User Name",
                "locked": False,
                "pager": "",
                "roles": [],
                "contactgroups": [],
                "customer": None,  # None represents global internally
                "email": "",
                "fallback_contact": False,
                "disable_notifications": {},
            },
            "is_new_user": True,
        }
    })
    user_internal = _load_user(UserId("user"))
    user_endpoint_attrs = complement_customer(
        _internal_to_api_format(user_internal))
    assert user_endpoint_attrs["customer"] == "global"
예제 #14
0
def test_log_audit_with_object_diff(request_context):
    old = {
        "a": "b",
        "b": "c",
    }
    new = {
        "b": "c",
    }

    with on_time("2018-04-15 16:50", "CET"):
        log_audit(
            object_ref=None,
            action="bla",
            message="Message",
            user_id=UserId("calvin"),
            diff_text=make_diff_text(old, new),
        )

    store = AuditLogStore(AuditLogStore.make_path())
    assert store.read() == [
        AuditLogStore.Entry(
            time=1523811000,
            object_ref=None,
            user_id="calvin",
            action="bla",
            text="Message",
            diff_text='Attribute "a" with value "b" removed.',
        ),
    ]
예제 #15
0
    def _automation_push_profile(self):
        site_id = html.request.var("siteid")
        if not site_id:
            raise MKGeneralException(_("Missing variable siteid"))

        user_id = html.request.var("user_id")
        if not user_id:
            raise MKGeneralException(_("Missing variable user_id"))

        our_id = config.omd_site()

        if our_id is not None and our_id != site_id:
            raise MKGeneralException(
                _("Site ID mismatch. Our ID is '%s', but you are saying we are '%s'."
                  ) % (our_id, site_id))

        profile = html.request.var("profile")
        if not profile:
            raise MKGeneralException(
                _('Invalid call: The profile is missing.'))

        users = userdb.load_users(lock=True)
        users[UserId(user_id)] = watolib.mk_eval(profile)
        userdb.save_users(users)

        return True
예제 #16
0
    def _do_login(self) -> None:
        """handle the sent login form"""
        if not html.request.var('_login'):
            return

        try:
            if not config.user_login:
                raise MKUserError(None,
                                  _('Login is not allowed on this site.'))

            username_var = html.request.get_unicode_input('_username', '')
            assert username_var is not None
            username = UserId(username_var.rstrip())
            if not username:
                raise MKUserError('_username', _('No username given.'))

            password = html.request.var('_password', '')
            if not password:
                raise MKUserError('_password', _('No password given.'))

            default_origtarget = config.url_prefix() + "check_mk/"
            origtarget = html.get_url_input("_origtarget", default_origtarget)

            # Disallow redirections to:
            #  - logout.py: Happens after login
            #  - side.py: Happens when invalid login is detected during sidebar refresh
            if "logout.py" in origtarget or 'side.py' in origtarget:
                origtarget = default_origtarget

            result = userdb.check_credentials(username, password)
            if result:
                # use the username provided by the successful login function, this function
                # might have transformed the username provided by the user. e.g. switched
                # from mixed case to lower case.
                username = result

                session_id = userdb.on_succeeded_login(username)

                # The login succeeded! Now:
                # a) Set the auth cookie
                # b) Unset the login vars in further processing
                # c) Redirect to really requested page
                _create_auth_session(username, session_id)

                # Never use inplace redirect handling anymore as used in the past. This results
                # in some unexpected situations. We simpy use 302 redirects now. So we have a
                # clear situation.
                # userdb.need_to_change_pw returns either False or the reason description why the
                # password needs to be changed
                change_pw_result = userdb.need_to_change_pw(username)
                if change_pw_result:
                    raise HTTPRedirect(
                        'user_change_pw.py?_origtarget=%s&reason=%s' %
                        (html.urlencode(origtarget), change_pw_result))
                raise HTTPRedirect(origtarget)

            userdb.on_failed_login(username)
            raise MKUserError(None, _('Invalid credentials.'))
        except MKUserError as e:
            html.add_user_error(e.varname, e)
예제 #17
0
파일: sites.py 프로젝트: tribe29/checkmk
def _ensure_connected(user: Optional[LoggedInUser],
                      force_authuser: Optional[UserId]) -> None:
    """Build up a connection to livestatus to either a single site or multiple sites."""
    if "live" in g:
        return

    if user is None:
        user = global_user

    if force_authuser is None:
        request_force_authuser = request.get_str_input("force_authuser")
        force_authuser = UserId(
            request_force_authuser) if request_force_authuser else None

    logger.debug(
        "Initializing livestatus connections as user %s (forced auth user: %s)",
        user.id,
        force_authuser,
    )

    g.site_status = {}
    _connect_multiple_sites(user)
    _set_livestatus_auth(user, force_authuser)

    logger.debug("Site states: %r", g.site_status)
예제 #18
0
def test_parse_auth_cookie_allow_current(current_cookie, with_user,
                                         session_id):
    assert login.user_from_cookie(login._fetch_cookie(current_cookie)) == (
        UserId(with_user[0]),
        session_id,
        login._generate_auth_hash(with_user[0], session_id),
    )
예제 #19
0
def user_from_bearer_header(auth_header: str) -> Tuple[UserId, str]:
    """

    Examples:

        >>> user_from_bearer_header("Bearer username password")
        ('username', 'password')

    Args:
        auth_header:

    Returns:

    """
    try:
        _, token = auth_header.split("Bearer ", 1)
    except ValueError:
        raise MKAuthException(f"Not a valid Bearer token: {auth_header}")
    try:
        user_id, secret = token.strip().split(" ", 1)
    except ValueError:
        raise MKAuthException("No user/password combination in Bearer token.")
    if not secret:
        raise MKAuthException("Empty password not allowed.")
    if not user_id:
        raise MKAuthException("Empty user not allowed.")
    if "/" in user_id:
        raise MKAuthException("No slashes / allowed in username.")

    return UserId(user_id), secret
예제 #20
0
def add_change(action_name: str,
               text: LogMessage,
               object_ref: Optional[ObjectRef] = None,
               diff_text: Optional[str] = None,
               add_user: bool = True,
               need_sync: Optional[bool] = None,
               need_restart: Optional[bool] = None,
               domains: Optional[List[Type[ABCConfigDomain]]] = None,
               sites: Optional[List[SiteId]] = None) -> None:

    log_audit(action=action_name,
              message=text,
              object_ref=object_ref,
              user_id=config.user.id if add_user else UserId(''),
              diff_text=diff_text)
    cmk.gui.watolib.sidebar_reload.need_sidebar_reload()

    search.update_index_background(action_name)

    # On each change to the Checkmk configuration mark the agents to be rebuild
    # TODO: Really? Why?
    #if has_agent_bakery():
    #    import cmk.gui.cee.agent_bakery as agent_bakery
    #    agent_bakery.mark_need_to_bake_agents()

    ActivateChangesWriter().add_change(action_name, text, object_ref, add_user,
                                       need_sync, need_restart, domains, sites)
예제 #21
0
파일: login.py 프로젝트: selten/checkmk
def _parse_auth_cookie(cookie_name: str) -> Tuple[UserId, float, str]:
    raw_cookie = html.request.cookie(cookie_name, "::")
    assert raw_cookie is not None

    raw_value = ensure_str(raw_cookie)
    username, issue_time, cookie_hash = raw_value.split(':', 2)
    return UserId(username), float(
        issue_time) if issue_time else 0.0, ensure_str(cookie_hash)
예제 #22
0
def _create_host_info(row: Mapping[str, Any]) -> HostInfo:
    return HostInfo(
        name=row['name'],
        alias=row['alias'],
        address=row['address'],
        custom_variables=row['custom_variables'],
        contacts={UserId(c) for c in row['contacts']},
        contact_groups=set(row['contact_groups']),
    )
예제 #23
0
 def execute(self) -> Iterator[ACResult]:
     if (cmk.gui.plugins.userdb.htpasswd.HtpasswdUserConnector(
         {}).check_credentials(UserId("omdadmin"), "omd") == "omdadmin"):
         yield ACResultCRIT(
             _("Found <tt>omdadmin</tt> with default password. "
               "It is highly recommended to change this password."))
     else:
         yield ACResultOK(
             _("Found <tt>omdadmin</tt> using custom password."))
예제 #24
0
def _create_host_info(row: Mapping[str, Any]) -> HostInfo:
    return HostInfo(
        name=row["name"],
        alias=row["alias"],
        address=row["address"],
        custom_variables=row["custom_variables"],
        contacts={UserId(c) for c in row["contacts"]},
        contact_groups=set(row["contact_groups"]),
    )
예제 #25
0
파일: login.py 프로젝트: selten/checkmk
def check_auth_web_server(request: Request) -> UserId:
    """Try to get the authenticated user from the HTTP request

    The user may have configured (basic) authentication by the web server. In
    case a user is provided, we trust that user.
    """
    user = request.remote_user
    if user is not None:
        set_auth_type("web_server")
        return UserId(ensure_str(user))
예제 #26
0
def test_key_mgmt_create_key(monkeypatch: pytest.MonkeyPatch, tmp_path: Path) -> None:
    monkeypatch.setattr(time, "time", lambda: 123)

    key = key_mgmt.generate_key("älias", "passphra$e", UserId("dingdöng"), SiteId("test-site"))
    assert isinstance(key, key_mgmt.Key)
    assert key.alias == "älias"
    assert key.date == 123
    assert key.owner == "dingdöng"
    assert key.certificate.startswith("-----BEGIN CERTIFICATE---")
    assert key.private_key.startswith("-----BEGIN ENCRYPTED PRIVATE KEY---")
예제 #27
0
def _check_auth_http_header(auth_by_http_header: str) -> Optional[UserId]:
    """When http header auth is enabled, try to read the user_id from the var"""
    user_id = request.get_request_header(auth_by_http_header)
    if not user_id:
        return None

    user_id = UserId(user_id)
    set_auth_type("http_header")

    return user_id
예제 #28
0
def _verify_user(environ) -> RFC7662:
    verified: List[RFC7662] = []

    auth_header = environ.get('HTTP_AUTHORIZATION', '')
    if auth_header:
        user_id, secret = user_from_bearer_header(auth_header)
        automation_user = automation_auth(user_id, secret)
        gui_user = gui_user_auth(user_id, secret)

        if not (automation_user or gui_user):
            raise MKAuthException(f"{user_id} not authorized.")

        if automation_user:
            verified.append(automation_user)

        if gui_user:
            verified.append(gui_user)

    remote_user = environ.get('REMOTE_USER', '')
    if remote_user and userdb.user_exists(UserId(remote_user)):
        verified.append(rfc7662_subject(UserId(remote_user), 'webserver'))

    cookie = Request(environ).cookies.get(f"auth_{omd_site()}")
    if cookie:
        user_id, session_id, cookie_hash = user_from_cookie(cookie)
        check_parsed_auth_cookie(user_id, session_id, cookie_hash)
        verified.append(rfc7662_subject(user_id, 'cookie'))

    if not verified:
        raise MKAuthException(
            "You need to be authenticated to use the REST API.")

    # We pick the first successful authentication method, which means the precedence is the same
    # as the oder in the code.
    final_candidate = verified[0]
    if not userdb.is_customer_user_allowed_to_login(final_candidate['sub']):
        raise MKAuthException(f"{final_candidate['sub']} may not log in here.")

    if userdb.user_locked(final_candidate['sub']):
        raise MKAuthException(f"{final_candidate['sub']} not authorized.")

    return final_candidate
예제 #29
0
def query_contactgroups_members(group_names: Iterable[ContactgroupName]) -> set[UserId]:
    query = "GET contactgroups\nColumns: members"
    num_group_names = 0
    for group_name in group_names:
        query += f"\nFilter: name = {group_name}"
        num_group_names += 1
    query += f"\nOr: {num_group_names}"
    contact_lists: list[list[str]] = (
        LocalConnection().query_column(query) if num_group_names else []
    )
    return {UserId(contact) for contact_list in contact_lists for contact in contact_list}
예제 #30
0
파일: login.py 프로젝트: mahdi7839/checkmk
def _check_auth_http_header() -> Optional[UserId]:
    """When http header auth is enabled, try to read the user_id from the var"""
    assert isinstance(config.auth_by_http_header, str)
    user_id = html.request.get_request_header(config.auth_by_http_header)
    if not user_id:
        return None

    user_id = UserId(ensure_str(user_id))
    set_auth_type("http_header")

    return user_id