Default authorized sites for new users

Hi all, we have:

  • a multisite dashboard instance (Check MK 2.2.0p30) which connects to some remote sites,
  • we use SAML authentication and have 3 roles mapped on it’s attributes, guest, normal and admin,
  • The users are internal and external and are not replicated to the remote sites.
    The idea is that specific users may only see information for site(s) that they are allowed to see.
    Now by default a new user is authorized for all sites but i’d like that behavior changed to no sites until one of our admin users authorize 1 or more sites for viewing.
    Does anyone had a similar use case?
    I could probably get this behavior by modifying the backend code but that would mean after every patch i have to reapply that same modification, suggestions?
1 Like

Take your feature wish to https://ideas.checkmk.com/.

Do you mean that this would be a new feature not possible currently?

Yes, this is currently not possible, AFAIK.

There is no option in the GUI to set ‘Authorized sites’ to set it to ‘No Sites’ as far as I know.

You can limit the hosts the user will see based on contact groups. contact Groups could be synced from your directory service.

For assigning ‘Authorized sites’ automatically based on AD group membership we use a hook but yes, you have to maintain the code.

regards

Mike

So our central dashboard site is a checkmk 2.2.0p30 instance.
To get what i want i modified lib/python3/cmk/gui/cee/userdb/saml2/connector.py
In the definition of the _create_user function, just before the save_users call, i added the line:

users[user_id]["authorized_sites"]=["",]

So the complete function looks like this:

    def _create_user(self, user_id: UserId, user_profile: AuthenticatedUser, users: Users) -> None:
        new_user_entry = _populate_user_with_defaults(
            user_id=user_id,
            default_user_profile=active_config.default_user_profile,
            connection_id=self.config.id,
        )
        users[user_id] = _update_user_spec(
            user_profile=user_profile,
            current_user_entry=new_user_entry,
            checkmk_contact_groups=set(load_contact_group_information()),
            roles_mapping=self.config.role_membership_mapping,
            contact_groups_mapping=self.config.contact_groups_mapping,
            configured_user_attributes=self.config.interface_config.user_attributes,
        )
        users[user_id]["authorized_sites"]=["",]
        save_users(users, datetime.now())  # releases the lock implicitly
        add_change("edit-user", _("Added user %s") % str(user_id))
1 Like

Could you elaborate about that hook?
So first i modified /omd/versions/2.3.0p27.cee/lib/python3/cmk/gui/cee/saml2/_connector.py
For class Connector i modified method _create_user.
This change works, however if cmk is upgraded this change won’t replicate, so i tried overriding this method by creating
${OMDSITE}/local/lib/python3/cmk/gui/cee/saml2/_connector.py
but that is not beeing picked up, i’m looking for a cleaner method…

Here is the code from local/share/check_mk/web/plugins/userdb/ldap_groups_to_sites.py
I removed the internal content but the general code shoudl be clar.

#!/usr/bin/env python3

from cmk.gui.log import logger
import logging


# NOTE: We use LDAPBuiltinAttributePlugin here since this plugin sets the builtin attribute authorized sites
from cmk.gui.plugins.userdb.ldap_connector import ldap_attribute_plugin_registry, LDAPBuiltinAttributePlugin
from cmk.gui.valuespec import Dictionary
from cmk.gui.sites import sitenames


def ldap_sync_groups_to_site(connection, plugin, params, user_id, ldap_user, user):
    # user = {'authorized_sites' : ['site1']} --> the user is restricted to site1 only
    # user = {"authorized_sites":None} --> the user can see all sites (authorized_sites is not set here)
    # returns only the attribute to be changed. dict.update([other]) is finally used to update the attribute
    # on the calling side of this function.

    logger = logging.getLogger(f"GroupToSite({user_id})")
    authorized_sites = []
    for cg_name in user.get("contactgroups", []):
        if cg_name is None:
            continue
        cg_name_lower = cg_name.lower()
        if cg_name_lower in [ "group1", "group2", "group3" ]:
            logger.debug(f"User {user_id} in group {cg_name} may see all sites")
            return {"authorized_sites":None}
        elif cg_name_lower.endswith("something"):
        
            try:
                _country, site, _ = cg_name.split("-", 2)
            except:
                logger.debug(f"Group {cg_name_lower} doesnt contain a site")
                continue
            if len(site) != 3 or not site.isupper():
                logger.debug(f"Site {site} is not a valid  site")
                continue # Skip groups not relevant for us
            if site not in sitenames():
                logger.debug(f"Site: {site} not configured ")
                continue # Skip unknown sites
            if site not in authorized_sites:
                logger.debug(f"Asssign site {site} because of group {cg_name}")
                # adds the site to "authorized_sites" if not already in the list
                authorized_sites.append(site)
            else:
                logger.debug(f"Site {site} for Group {cg_name} already assigned")
    logger.debug(f"Assigned sites: {authorized_sites}")
    #returns the attribute to let upstream code updating the user dict
    return {"authorized_sites": authorized_sites}


@ldap_attribute_plugin_registry.register
class LDAPAttributeGroupsToSites(LDAPBuiltinAttributePlugin):
    @property
    def ident(self):
        return "groups_to_site"

    @property
    def title(self):
        return _('Groups to site attribute')

    @property
    def help(self):
        return _('xxx') +\
                ('yyy')

    def lock_attributes(self, params):
        return ['authorized_sites']

    def needed_attributes(self, connection, params):
        return ['authorized_sites']

    def sync_func(self, connection, plugin, params, user_id, ldap_user, user):
        return ldap_sync_groups_to_site(connection, plugin, params, user_id, ldap_user, user)

    def parameters(self, connection):
        return Dictionary(
            title=self.title,
            help=self.help,
            elements=[],
        )

This topic was automatically closed 365 days after the last reply. New replies are no longer allowed. Contact an admin if you think this should be re-opened.