Beispiel #1
0
    def getUserPPDs(self, user):
        index = PluginRegistry.getInstance("ObjectIndex")
        res = index.search({"_type": "User", "uid": user}, {"dn": 1})
        if len(res) == 0:
            raise EntryNotFound(C.make_error("USER_NOT_FOUND", topic=user))

        object = ObjectProxy(res[0]["dn"])
        printer_cns = []
        if object.is_extended_by("GotoEnvironment"):
            printer_cns.append(object.gotoPrinters)
        if object.is_extended_by("PosixUser"):
            for group_cn in object.groupMembership:
                group = ObjectProxy(group_cn)
                if group.is_extended_by("GotoEnvironment"):
                    printer_cns.append(group.gotoPrinters)
        # collect all PPDs
        res = index.search({
            "_type": "GotoPrinter",
            "cn": {
                "in_": printer_cns
            }
        }, {"gotoPrinterPPD": 1})
        ppds = []
        for r in res:
            ppds.append(r["gotoPrinterPPD"])
        return ppds
Beispiel #2
0
    def applyClientRights(self, device_uuid):
        # check rights
        acl = PluginRegistry.getInstance("ACLResolver")
        if not acl.check(
                device_uuid, "%s.%s.%s" %
            (self.env.domain, "command", "preUserSession"), "x"):
            role_name = "$$ClientDevices"
            # create AclRole for joining if not exists
            index = PluginRegistry.getInstance("ObjectIndex")
            res = index.search({
                "_type": "AclRole",
                "name": role_name
            }, {"dn": 1})
            if len(res) == 0:
                # create
                role = ObjectProxy(self.env.base, "AclRole")
                role.name = role_name

                # create rule
                aclentry = {
                    "priority":
                    0,
                    "scope":
                    "sub",
                    "actions": [{
                        "topic":
                        "%s\.command\.(joinClient|preUserSession|postUserSession|getMethods)"
                        % self.env.domain,
                        "acl":
                        "x",
                        "options": {}
                    }]
                }
                role.AclRoles = [aclentry]
                role.commit()

            # check if device has role
            found = False
            base = ObjectProxy(self.env.base)
            if base.is_extended_by("Acl"):
                for entry in base.AclSets:
                    if entry["rolename"] == role_name and device_uuid in entry[
                            "members"]:
                        found = True
                        break
            else:
                base.extend("Acl")

            if found is False:
                acl_entry = {
                    "priority": 0,
                    "members": [device_uuid],
                    "rolename": role_name
                }
                base.AclSets.append(acl_entry)
                base.commit()
Beispiel #3
0
    def getUserPPDs(self, user):
        index = PluginRegistry.getInstance("ObjectIndex")
        res = index.search({"_type": "User", "uid": user}, {"dn": 1})
        if len(res) == 0:
            raise EntryNotFound(C.make_error("USER_NOT_FOUND", topic=user))

        object = ObjectProxy(res[0]["dn"])
        printer_cns = []
        if object.is_extended_by("GotoEnvironment"):
            printer_cns.append(object.gotoPrinters)
        if object.is_extended_by("PosixUser"):
            for group_cn in object.groupMembership:
                group = ObjectProxy(group_cn)
                if group.is_extended_by("GotoEnvironment"):
                    printer_cns.append(group.gotoPrinters)
        # collect all PPDs
        res = index.search({"_type": "GotoPrinter", "cn": {"in_": printer_cns}}, {"gotoPrinterPPD": 1})
        ppds = []
        for r in res:
            ppds.append(r["gotoPrinterPPD"])
        return ppds
Beispiel #4
0
    def __maintain_user_session(self, client_id, user_name):
        # save login time and system<->user references
        client = self.__open_device(client_id)
        client.gotoLastUser = user_name
        self.systemSetStatus(client, "+B")

        index = PluginRegistry.getInstance("ObjectIndex")
        res = index.search({"_type": "User", "uid": user_name}, {"dn": 1})
        for u in res:
            user = ObjectProxy(u["dn"])
            if not user.is_extended_by("GosaAccount"):
                user.extend("GosaAccount")
            user.gotoLastSystemLogin = datetime.datetime.now()
            user.gotoLastSystem = client.dn
            user.commit()
Beispiel #5
0
    def __maintain_user_session(self, client_id, user_name):
        # save login time and system<->user references
        client = self.__open_device(client_id)
        client.gotoLastUser = user_name
        self.systemSetStatus(client, "+B")

        index = PluginRegistry.getInstance("ObjectIndex")
        res = index.search({"_type": "User", "uid": user_name}, {"dn": 1})
        for u in res:
            user = ObjectProxy(u["dn"])
            if not user.is_extended_by("GosaAccount"):
                user.extend("GosaAccount")
            user.gotoLastSystemLogin = datetime.datetime.now()
            user.gotoLastSystem = client.dn
            user.commit()
Beispiel #6
0
    def get_object(self, object_type, oid, create=True, data=None):
        backend_attributes = self.factory.getObjectBackendProperties(object_type)
        foreman_object = None

        if "Foreman" not in backend_attributes:
            self.log.warning("no foreman backend attributes found for '%s' object" % object_type)
            return

        types = self.factory.getObjectTypes()[object_type]
        base_type = object_type if types["base"] is True else types["extends"][0]

        index = PluginRegistry.getInstance("ObjectIndex")

        # check if the object already exists
        query = {
            '_type': base_type,
            backend_attributes["Foreman"]["_uuidAttribute"]: str(oid)
        }
        if types["base"] is False:
            query["extension"] = object_type

        res = index.search(query, {'dn': 1})

        if len(res) == 0:
            if create is True:
                # no object found -> create one
                self.log.debug(">>> creating new %s" % object_type)
                base_dn = self.env.base
                if object_type == "ForemanHost":
                    # get the IncomingDevice-Container
                    res = index.search({"_type": "IncomingDeviceContainer", "_parent_dn": self.type_bases["ForemanHost"]}, {"dn": 1})
                    if len(res) > 0:
                        base_dn = res[0]["dn"]
                    else:
                        base_dn = self.type_bases["ForemanHost"]
                elif object_type in self.type_bases:
                    base_dn = self.type_bases[object_type]
                foreman_object = ObjectProxy(base_dn, base_type)
                uuid_extension = foreman_object.get_extension_off_attribute(backend_attributes["Foreman"]["_uuidAttribute"])
                if base_type != uuid_extension and not foreman_object.is_extended_by(uuid_extension):
                    foreman_object.extend(uuid_extension)
                setattr(foreman_object, backend_attributes["Foreman"]["_uuidAttribute"], str(oid))
        else:
            # open existing object
            self.log.debug(">>> open existing %s with DN: %s" % (object_type, res[0]["dn"]))
            foreman_object = ObjectProxy(res[0]["dn"], data={object_type: {"Foreman": data}} if data is not None else None)

        return foreman_object
Beispiel #7
0
    async def test_provision_host(self, m_get, m_del, m_put, m_post):
        """ convert a discovered host to a 'real' host  """
        self._test_dn = GosaTestCase.create_test_data()
        container = ObjectProxy(self._test_dn, "IncomingDeviceContainer")
        container.commit()

        mocked_foreman = MockForeman()
        m_get.side_effect = mocked_foreman.get
        m_del.side_effect = mocked_foreman.delete
        m_put.side_effect = mocked_foreman.put
        m_post.side_effect = mocked_foreman.post

        # create the discovered host + foremanHostgroup
        d_host = ObjectProxy(container.dn, "Device")
        d_host.cn = "mac00262df16a2c"
        d_host.extend("ForemanHost")
        d_host.status = "discovered"
        d_host.extend("ieee802Device")
        d_host.macAddress = "00:26:2d:f1:6a:2c"
        d_host.extend("IpHost")
        d_host.ipHostNumber = "192.168.0.1"
        d_host.commit()

        hostgroup = ObjectProxy("%s" % self._test_dn, "GroupOfNames")
        hostgroup.extend("ForemanHostGroup")
        hostgroup.cn = "Test"
        hostgroup.foremanGroupId = "4"
        hostgroup.commit()

        # add host to group
        logging.getLogger("test.foreman-integration").info("########### START: Add Host to group ############# %s" % AsyncHTTPTestCase.get_url(self, "/hooks/"))
        d_host = ObjectProxy("cn=mac00262df16a2c,%s" % container.dn)

        def check():
            logging.getLogger("test.foreman-integration").info("check condition: %s, %s" % (d_host.cn, d_host.status))
            return d_host.cn == "mac00262df16a2c" and d_host.status == "discovered"

        def check2():
            logging.getLogger("test.foreman-integration").info("check2 condition: %s" % d_host.cn)
            return d_host.cn == "Testhost"

        base_dir = os.path.join(os.path.dirname(os.path.realpath(__file__)), "data")
        with open(os.path.join(base_dir, "discovered_hosts", "mac00262df16a2c.json")) as f:
            mocked_foreman.register_conditional_response("http://localhost:8000/api/v2/discovered_hosts/mac00262df16a2c",
                                                         "get",
                                                         check,
                                                         f.read())
        with open(os.path.join(base_dir, "conditional", "Testhost.json")) as f:
            mocked_foreman.register_conditional_response("http://localhost:8000/api/v2/hosts/Testhost",
                                                         "get",
                                                         check2,
                                                         f.read())

        def activate(**kwargs):
            return True

        mocked_foreman.register_trigger("http://localhost:8000/api/v2/discovered_hosts/mac00262df16a2c",
                                        "put",
                                        activate,
                                        self.execute)

        with make_session() as session:
            assert session.query(ObjectInfoIndex.dn)\
                       .join(ObjectInfoIndex.properties)\
                       .filter(and_(KeyValueIndex.key == "cn", KeyValueIndex.value == "Testhost"))\
                       .count() == 0

        d_host.cn = "Testhost"
        d_host.groupMembership = hostgroup.dn
        d_host.commit()
        logging.getLogger("test.foreman-integration").info("waiting for 2 seconds")
        await asyncio.sleep(2)

        logging.getLogger("test.foreman-integration").info("########### END: Add Host to group #############")

        # now move the host to the final destination
        d_host = ObjectProxy("cn=Testhost,ou=incoming,%s" % self._test_dn)
        assert d_host.status != "discovered"
        assert d_host.name == "Testhost"
        assert d_host.hostgroup_id == "4"
        assert d_host.is_extended_by("RegisteredDevice") is True
        assert len(d_host.userPassword[0]) > 0
        assert d_host.deviceUUID is not None

        with make_session() as session:
            assert session.query(ObjectInfoIndex.dn) \
                       .join(ObjectInfoIndex.properties) \
                       .filter(and_(KeyValueIndex.key == "cn", KeyValueIndex.value == "Testhost")) \
                       .count() == 1

        logging.getLogger("test.foreman-integration").info("########### START: moving host #############")
        d_host.move("%s" % self._test_dn)
        logging.getLogger("test.foreman-integration").info("########### END: moving host #############")

        # lets check if everything is fine in the database
        d_host = ObjectProxy("cn=Testhost,ou=devices,%s" % self._test_dn, read_only=True)
        assert d_host is not None
        assert d_host.status == "unknown"
        assert d_host.groupMembership == hostgroup.dn
Beispiel #8
0
    def __collect_user_configuration(self, client_id, users):
        """
        :param client_id: deviceUUID or hostname
        :param users: list of currently logged in users on the client
        """
        if isinstance(client_id, ObjectProxy):
            client = client_id
        else:
            client = self.__open_device(client_id, read_only=True)
        group = None
        index = PluginRegistry.getInstance("ObjectIndex")
        res = index.search({"_type": "GroupOfNames", "member": client.dn}, {"dn": 1})
        if len(res) > 0:
            group = ObjectProxy(res[0]["dn"], read_only=True)
        config = {}

        resolution = None
        if group is not None and group.is_extended_by("GotoEnvironment") and group.gotoXResolution is not None:
            resolution = group.gotoXResolution

        if client.is_extended_by("GotoEnvironment") and client.gotoXResolution is not None:
            resolution = client.gotoXResolution

        release = None
        if client.is_extended_by("GotoMenu"):
            release = client.getReleaseName()
        elif group is not None and group.is_extended_by("ForemanHostGroup"):
            release = group.getReleaseName()
            parent_group = group
            while release is None and parent_group is not None and parent_group.parent_id is not None:
                res = index.search({"_type": "GroupOfNames", "extension": "ForemanHostGroup", "foremanGroupId": parent_group.parent_id}, {"dn": 1})
                if len(res) == 0:
                    break
                else:
                    parent_group = ObjectProxy(res[0]["dn"], read_only=True)
                    release = parent_group.getReleaseName()

        if release is None:
            self.log.error("no release found for client/user combination (%s/%s)" % (client_id, users))

        client_menu = None

        if hasattr(client, "gotoMenu") and client.gotoMenu is not None:
            client_menu = loads(client.gotoMenu)

        # collect users DNs
        query_result = index.search({"_type": "User", "uid": {"in_": users}}, {"dn": 1})
        for entry in query_result:
            user = ObjectProxy(entry["dn"], read_only=True)
            config[user.uid] = {}

            if release is not None:
                menus = []
                if client_menu is not None:
                    menus.append(client_menu)

                # get all groups the user is member of which have a menu for the given release
                query = {'_type': 'GroupOfNames', "member": user.dn, "extension": "GotoMenu", "gotoLsbName": release}

                for res in index.search(query, {"gotoMenu": 1}):
                    # collect user menus
                    for m in res.get("gotoMenu", []):
                        menus.append(loads(m))

                if len(menus):
                    user_menu = None
                    for menu_entry in menus:
                        if user_menu is None:
                            user_menu = self.get_submenu(menu_entry)
                        else:
                            self.merge_submenu(user_menu, self.get_submenu(menu_entry))
                    config[user.uid]["menu"] = user_menu

            # collect printer settings for user, starting with the clients printers
            settings = self.__collect_printer_settings(group)
            printer_names = [x["cn"] for x in settings["printers"]]
            # get all GroupOfNames with GotoEnvironment the user or client is member of
            for res in index.search({'_type': 'GroupOfNames', "member": {"in_": [user.dn, client.dn]}, "extension": "GotoEnvironment"},
                                    {"dn": 1}):
                user_group = ObjectProxy(res["dn"], read_only=True)
                if group is not None and user_group.dn == group.dn:
                    # this group has already been handled
                    continue
                s = self.__collect_printer_settings(user_group)

                if user_group.gotoXResolution is not None:
                    resolution = user_group.gotoXResolution

                for p in s["printers"]:
                    if p["cn"] not in printer_names:
                        settings["printers"].append(p)
                        printer_names.append(p["cn"])

                if s["defaultPrinter"] is not None:
                    settings["defaultPrinter"] = s["defaultPrinter"]

            # override group environment settings if the client has one
            s = self.__collect_printer_settings(client)
            if len(s["printers"]) > 0:
                settings["printers"] = s["printers"]
                settings["defaultPrinter"] = s["defaultPrinter"]

            if user.is_extended_by("GosaAccount") and user.gosaDefaultPrinter is not None:
                # check if the users default printer is send to the client
                found = False
                for printer_settings in settings["printers"]:
                    if printer_settings["cn"] == user.gosaDefaultPrinter:
                        found = True
                        break

                def process(res):
                    if len(res) == 0:
                        self.log.warning("users defaultPrinter not found: %s" % user.gosaDefaultPrinter)
                        return None
                    elif len(res) == 1:
                        # add this one to the result set
                        printer = ObjectProxy(res[0]["dn"], read_only=True)
                        p_conf = {}
                        for attr in self.printer_attributes:
                            p_conf[attr] = getattr(printer, attr)
                        return p_conf
                    return False

                if found is False:
                    # find the printer and add it to the settings
                    res = index.search({"_type": "GotoPrinter", "cn": user.gosaDefaultPrinter}, {"dn": 1})
                    printer_config = process(res)
                    if printer_config is False:
                        # more than 1 printers found by this CN, try to look in the users subtree
                        res = index.search({
                            "_type": "GotoPrinter",
                            "cn": user.gosaDefaultPrinter,
                            "_adjusted_parent_dn": user.get_adjusted_parent_dn()
                        }, {"dn": 1})
                        printer_config = process(res)

                    if isinstance(printer_config, dict):
                        settings["printers"].append(printer_config)
                        settings["defaultPrinter"] = user.gosaDefaultPrinter
                    else:
                        self.log.warning("users defaultPrinter not found: %s" % user.gosaDefaultPrinter)
                else:
                    settings["defaultPrinter"] = user.gosaDefaultPrinter

            config[user.uid]["printer-setup"] = settings
            config[user.uid]["resolution"] = None

            if resolution is not None:
                config[user.uid]["resolution"] = [int(x) for x in resolution.split("x")]

            # TODO: collect and send login scripts to client
        return config
Beispiel #9
0
    def joinClient(self, user, device_uuid, mac, info=None):
        """
        TODO
        """

        index = PluginRegistry.getInstance("ObjectIndex")

        uuid_check = re.compile(r"^[0-9a-f]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}$", re.IGNORECASE)
        if not uuid_check.match(device_uuid):
            raise ValueError(C.make_error("CLIENT_UUID_INVALID", uuid=device_uuid))

        # Handle info, if present
        more_info = []
        extensions = ["simpleSecurityObject", "ieee802Device"]

        if info:
            # Check string entries
            for entry in filter(lambda x: x in info, ["serialNumber", "ou", "o", "l", "description"]):

                if not re.match(r"^[\w\s]+$", info[entry]):
                    raise ValueError(C.make_error("CLIENT_DATA_INVALID", client=device_uuid, entry=entry, data=info[entry]))

                more_info.append((entry, info[entry]))

            if "ipHostNumber" in info:
                if re.match(r"^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}", info["ipHostNumber"]):
                    more_info.append(("ipHostNumber", info["ipHostNumber"]))
                    extensions.append("IpHost")
                else:
                    raise ValueError(C.make_error("CLIENT_DATA_INVALID", client=device_uuid, entry="ipHostNumber", data=info["ipHostNumber"]))

            if "hostname" in info:
                allowed = re.compile("(?!-)[A-Z\d-]{1,63}(?<!-)$", re.IGNORECASE)
                if all(allowed.match(x) for x in info["hostname"].split(".")):
                    more_info.append(("description", info["hostname"]))

            # Check desired device type if set
            if "deviceType" in info:
                if re.match(r"^(terminal|workstation|server|sipphone|switch|router|printer|scanner)$", info["deviceType"]):

                    more_info.append(("deviceType", info["deviceType"]))
                else:
                    raise ValueError(C.make_error("CLIENT_TYPE_INVALID", client=device_uuid, type=info["deviceType"]))

            # Check owner for presence
            if "owner" in info:
                # Take a look at the directory to see if there's  such an owner DN
                res = index.search({'_dn': info["owner"]}, {'_dn': 1})
                if len(res) == 0:
                    raise ValueError(C.make_error("CLIENT_OWNER_NOT_FOUND", client=device_uuid, owner=info["owner"]))
                more_info.append(("owner", info["owner"]))

        # Generate random client key
        h, key, salt = generate_random_key()

        # Take a look at the directory to see if there's already a joined client with this uuid
        res = index.search({'_type': 'Device', 'macAddress': mac, 'extension': 'RegisteredDevice'},
                           {'dn': 1})

        if len(res) > 0:
            record = ObjectProxy(res[0]['dn'])
            for ext in extensions:
                if not record.is_extended_by(ext):
                    record.extend(ext)

            if record.is_extended_by("ForemanHost") and record.otp is not None:
                record.otp = None

            record.userPassword = ["{SSHA}" + encode(h.digest() + salt).decode()]
            for k, value in more_info:
                setattr(record, k, value)
            cn = record.deviceUUID
            record.status_Online = False
            record.status_Offline = True
            record.status_InstallationInProgress = False

            record.commit()
            self.log.info("UUID '%s' joined as %s" % (device_uuid, record.dn))
        else:

            # While the client is going to be joined, generate a random uuid and an encoded join key
            cn = str(uuid4())
            device_key = encrypt_key(device_uuid.replace("-", ""), cn + key)

            # Resolve manager
            res = index.search({'_type': 'User', 'uid': user},
                               {'dn': 1})

            if len(res) != 1:
                raise GOtoException(C.make_error("USER_NOT_UNIQUE" if res else "UNKNOWN_USER", user=user))
            manager = res[0]['dn']

            # Create new machine entry
            dn = ",".join([self.env.config.get("goto.machine-rdn", default="ou=systems"), self.env.base])
            record = ObjectProxy(dn, "Device")
            record.extend("RegisteredDevice")
            for ext in extensions:
                record.extend(ext)
            record.deviceUUID = cn
            record.deviceKey = Binary(device_key)
            record.cn = "mac%s" % mac.replace(":", "")
            record.manager = manager
            record.status_Offline = True
            record.macAddress = mac.encode("ascii", "ignore")
            record.userPassword = ["{SSHA}" + encode(h.digest() + salt).decode()]
            for k, value in more_info:
                setattr(record, k, value)

            record.commit()
            self.log.info("UUID '%s' joined as %s" % (device_uuid, record.dn))

        # make sure the client has the access rights he needs
        self.applyClientRights(cn)

        return [key, cn]
Beispiel #10
0
    def applyClientRights(self, device_uuid):
        # check rights
        acl = PluginRegistry.getInstance("ACLResolver")
        allowed_commands = [
            'joinClient',
            'preUserSession',
            'postUserSession',
            'getMethods',
            'getDestinationIndicator'
        ]
        missing = [x for x in allowed_commands if not acl.check(device_uuid, "%s.%s.%s" % (self.env.domain, "command", x), "x")]
        reload = False
        role_name = "$$ClientDevices"

        if len(missing) > 0:
            # create AclRole for joining if not exists
            index = PluginRegistry.getInstance("ObjectIndex")
            res = index.search({"_type": "AclRole", "name": role_name}, {"dn": 1})
            if len(res) == 0:
                # create
                role = ObjectProxy(self.env.base, "AclRole")
                role.name = role_name
            else:
                role = ObjectProxy(res[0]['dn'])

            # create rule
            aclentry = {
                "priority": 0,
                "scope": "sub",
                "actions": [
                    {
                        "topic": "%s\.command\.(%s)" % (self.env.domain, "|".join(allowed_commands)),
                        "acl": "x",
                        "options": {}
                    }
                ]}
            role.AclRoles = [aclentry]
            role.commit()
            reload = True

        # check if device has role
        found = False
        base = ObjectProxy(self.env.base)
        if base.is_extended_by("Acl"):
            for entry in base.AclSets:
                if entry["rolename"] == role_name and device_uuid in entry["members"]:
                    found = True
                    break
        else:
            base.extend("Acl")

        if found is False:
            acl_entry = {"priority": 0,
                         "members": [device_uuid],
                         "rolename": role_name}
            base.AclSets.append(acl_entry)
            if self.env.mode != "proxy":
                self.__acl_change_checks.append({"role": role_name, "member": device_uuid})
            base.commit()
            reload = True

        if reload is True:
            # reload acls to make sure that they are applied in the current instance
            acl.load_acls()
Beispiel #11
0
    def handle_request(self, request_handler):
        foreman = PluginRegistry.getInstance("Foreman")
        data = loads(request_handler.request.body)
        self.log.debug(data)

        # TODO disable hook logging to file
        with open("foreman-log.json", "a") as f:
            f.write("%s,\n" % dumps(data, indent=4, sort_keys=True))

        if data["event"] in ForemanHookReceiver.skip_next_event and data["object"] in ForemanHookReceiver.skip_next_event[data["event"]]:
            ForemanHookReceiver.skip_next_event[data["event"]].remove(data["object"])
            self.log.info("skipped '%s' event for object: '%s'" % (data["event"], data["object"]))
            return

        data_keys = list(data['data'].keys())
        if len(data_keys) == 1:
            type = data_keys[0]
        else:
            # no type given -> skipping this event as other might come with more information
            self.log.warning("skipping event '%s' for object '%s' as no type information is given in data: '%s'" % (data["event"],
                                                                                                                  data["object"],
                                                                                                                  data["data"]))
            return

        # search for real data
        if len(data['data'][type].keys()) == 1:
            # something like {data: 'host': {host: {...}}}
            #             or {data: 'discovered_host': {host: {...}}}
            payload_data = data['data'][type][list(data['data'][type].keys())[0]]
        else:
            payload_data = data['data'][type]

        if type == "operatingsystem":
            with make_session() as session:
                foreman.sync_release_name(payload_data, session, event=data['event'])
                session.commit()
                return

        factory = ObjectFactory.getInstance()
        foreman_type = type
        if type == "discovered_host":
            type = "host"

        object_types = factory.getObjectNamesWithBackendSetting("Foreman", "type", "%ss" % type)
        object_type = object_types[0] if len(object_types) else None

        backend_attributes = factory.getObjectBackendProperties(object_type) if object_type is not None else None
        self.log.debug("Hookevent: '%s' for '%s' (%s)" % (data['event'], data['object'], object_type))

        uuid_attribute = None
        if "Foreman" in backend_attributes:
            uuid_attribute = backend_attributes["Foreman"]["_uuidSourceAttribute"] \
                if '_uuidSourceAttribute' in backend_attributes["Foreman"] else backend_attributes["Foreman"]["_uuidAttribute"]

        ForemanBackend.modifier = "foreman"
        update_data = {}

        if data['event'] in ["update", "create"] and foreman_type == "host":
            id = payload_data["id"] if "id" in payload_data else None
            try:
                foreman.write_parameters(id if id is not None else data['object'])
            except:
                foreman.mark_for_parameter_setting(data['object'], {
                    "status": "created",
                    "use_id": id
                })

        if data['event'] == "after_commit" or data['event'] == "update" or data['event'] == "after_create" or data['event'] == "create":
            host = None
            if data['event'] == "update" and foreman_type == "host" and "mac" in payload_data and payload_data["mac"] is not None:
                # check if we have an discovered host for this mac
                index = PluginRegistry.getInstance("ObjectIndex")
                res = index.search({
                    "_type": "Device",
                    "extension": ["ForemanHost", "ieee802Device"],
                    "macAddress": payload_data["mac"],
                    "status": "discovered"
                }, {"dn": 1})

                if len(res):
                    self.log.debug("update received for existing host with dn: %s" % res[0]["dn"])
                    host = ObjectProxy(res[0]["dn"])

                    if foreman_type != "discovered_host" and host.is_extended_by("ForemanHost"):
                        host.status = "unknown"

            foreman_object = foreman.get_object(object_type, payload_data[uuid_attribute], create=host is None)
            if foreman_object and host:
                if foreman_object != host:
                    self.log.debug("using known host instead of creating a new one")
                    # host is the formerly discovered host, which might have been changed in GOsa for provisioning
                    # so we want to use this one, foreman_object is the joined one, so copy the credentials from foreman_object to host
                    if not host.is_extended_by("RegisteredDevice"):
                        host.extend("RegisteredDevice")
                    if not host.is_extended_by("simpleSecurityObject"):
                        host.extend("simpleSecurityObject")
                    host.deviceUUID = foreman_object.deviceUUID
                    host.userPassword = foreman_object.userPassword
                    host.otp = foreman_object.otp
                    host.cn = foreman_object.cn

                    # now delete the formerly joined host
                    foreman_object.remove()
                    foreman_object = host

            elif foreman_object is None and host is not None:
                foreman_object = host

            elif foreman_type == "discovered_host":
                self.log.debug("setting discovered state for %s" % payload_data[uuid_attribute])
                if not foreman_object.is_extended_by("ForemanHost"):
                    foreman_object.extend("ForemanHost")
                foreman_object.status = "discovered"

            if foreman_type == "host":
                old_build_state = foreman_object.build

            foreman.update_type(object_type, foreman_object, payload_data, uuid_attribute, update_data=update_data)
            if foreman_type == "host" and old_build_state is True and foreman_object.build is False and \
                            foreman_object.status == "ready":
                # send notification
                e = EventMaker()
                ev = e.Event(e.Notification(
                    e.Title(N_("Host ready")),
                    e.Body(N_("Host '%s' has been successfully build." % foreman_object.cn)),
                    e.Icon("@Ligature/pc"),
                    e.Timeout("10000")
                ))
                event_object = objectify.fromstring(etree.tostring(ev, pretty_print=True).decode('utf-8'))
                SseHandler.notify(event_object)

        elif data['event'] == "after_destroy":
            # print("Payload: %s" % payload_data)
            foreman.remove_type(object_type, payload_data[uuid_attribute])

            # because foreman sends the after_commit event after the after_destroy event
            # we need to skip this event, otherwise the host would be re-created
            if "after_commit" not in ForemanHookReceiver.skip_next_event:
                ForemanHookReceiver.skip_next_event["after_commit"] = [data['object']]
            else:
                ForemanHookReceiver.skip_next_event["after_commit"].append(data['object'])

            # add garbage collection for skip
            sobj = PluginRegistry.getInstance("SchedulerService")
            sobj.getScheduler().add_date_job(self.cleanup_event_skipper,
                                             datetime.datetime.now() + datetime.timedelta(minutes=1),
                                             args=("after_commit", data['object']),
                                             tag='_internal', jobstore='ram')

        else:
            self.log.info("unhandled hook event '%s' received for '%s'" % (data['event'], type))

        ForemanBackend.modifier = None
Beispiel #12
0
    def add_host(self, hostname, base=None):

        # create dn
        if base is None:
            index = PluginRegistry.getInstance("ObjectIndex")
            # get the IncomingDevice-Container
            res = index.search({"_type": "IncomingDeviceContainer", "_parent_dn": self.type_bases["ForemanHost"]}, {"dn": 1})
            if len(res) > 0:
                base = res[0]["dn"]
            else:
                base = self.type_bases["ForemanHost"]

        ForemanBackend.modifier = "foreman"

        device = self.get_object("ForemanHost", hostname, create=False)
        if device is None:
            self.log.debug("Realm request: creating new host with hostname: %s" % hostname)
            device = ObjectProxy(base, "Device")
            device.extend("ForemanHost")
            device.cn = hostname
            # commit now to get a uuid
            device.commit()

            # re-open to get a clean object
            device = ObjectProxy(device.dn)
        else:
            self.log.debug("Realm request: use existing host with hostname: %s" % hostname)

        try:

            if not device.is_extended_by("ForemanHost"):
                device.extend("ForemanHost")

            # Generate random client key
            h, key, salt = generate_random_key()

            # While the client is going to be joined, generate a random uuid and an encoded join key
            if not device.is_extended_by("RegisteredDevice"):
                device.extend("RegisteredDevice")
            if not device.is_extended_by("simpleSecurityObject"):
                device.extend("simpleSecurityObject")
            if device.deviceUUID is None:
                cn = str(uuid.uuid4())
                device.deviceUUID = cn
            else:
                cn = device.deviceUUID

            device.status = "pending"
            device.status_InstallationInProgress = True

            if device.userPassword is None:
                device.userPassword = []
            elif device.otp is not None:
                device.userPassword.remove(device.otp)
            device.userPassword.append("{SSHA}" + encode(h.digest() + salt).decode())

            # make sure the client has the access rights he needs
            client_service = PluginRegistry.getInstance("ClientService")
            client_service.applyClientRights(device.deviceUUID)

            device.commit()
            self.mark_for_parameter_setting(hostname, {"status": "added"})
            return "%s|%s" % (key, cn)

        except Exception as e:
            # remove created device again because something went wrong
            # self.remove_type("ForemanHost", hostname)
            self.log.error(str(e))
            raise e

        finally:
            ForemanBackend.modifier = None
Beispiel #13
0
    def __collect_user_configuration(self, client_id, users):
        """
        :param client_id: deviceUUID or hostname
        :param users: list of currently logged in users on the client
        """
        if isinstance(client_id, ObjectProxy):
            client = client_id
        else:
            client = self.__open_device(client_id)
        group = None
        index = PluginRegistry.getInstance("ObjectIndex")
        res = index.search({
            "_type": "GroupOfNames",
            "member": client.dn
        }, {"dn": 1})
        if len(res) > 0:
            group = ObjectProxy(res[0]["dn"])
        config = {}

        resolution = None
        if group is not None and group.is_extended_by(
                "GotoEnvironment") and group.gotoXResolution is not None:
            resolution = group.gotoXResolution

        if client.is_extended_by(
                "GotoEnvironment") and client.gotoXResolution is not None:
            resolution = client.gotoXResolution

        release = None
        if client.is_extended_by("GotoMenu"):
            release = client.getReleaseName()
        elif group is not None and group.is_extended_by("ForemanHostGroup"):
            release = group.getReleaseName()
            parent_group = group
            while release is None and parent_group is not None and parent_group.parent_id is not None:
                res = index.search(
                    {
                        "_type": "GroupOfNames",
                        "extension": "ForemanHostGroup",
                        "foremanGroupId": parent_group.parent_id
                    }, {"dn": 1})
                if len(res) == 0:
                    break
                else:
                    parent_group = ObjectProxy(res[0]["dn"])
                    release = parent_group.getReleaseName()

        if release is None:
            self.log.error(
                "no release found for client/user combination (%s/%s)" %
                (client_id, users))

        client_menu = None

        if hasattr(client, "gotoMenu") and client.gotoMenu is not None:
            client_menu = loads(client.gotoMenu)

        # collect users DNs
        query_result = index.search({
            "_type": "User",
            "uid": {
                "in_": users
            }
        }, {"dn": 1})
        for entry in query_result:
            user = ObjectProxy(entry["dn"])
            config[user.uid] = {}

            if release is not None:
                menus = []
                if client_menu is not None:
                    menus.append(client_menu)

                # get all groups the user is member of which have a menu for the given release
                query = {
                    '_type': 'GroupOfNames',
                    "member": user.dn,
                    "extension": "GotoMenu",
                    "gotoLsbName": release
                }

                for res in index.search(query, {"gotoMenu": 1}):
                    # collect user menus
                    for m in res["gotoMenu"]:
                        menus.append(loads(m))

                if len(menus):
                    user_menu = None
                    for menu_entry in menus:
                        if user_menu is None:
                            user_menu = self.get_submenu(menu_entry)
                        else:
                            self.merge_submenu(user_menu,
                                               self.get_submenu(menu_entry))
                    config[user.uid]["menu"] = user_menu

            # collect printer settings for user, starting with the clients printers
            settings = self.__collect_printer_settings(group)
            printer_names = [x["cn"] for x in settings["printers"]]
            for res in index.search(
                {
                    '_type': 'GroupOfNames',
                    "member": user.dn,
                    "extension": "GotoEnvironment"
                }, {"dn": 1}):
                user_group = ObjectProxy(res["dn"])
                if user_group.dn == group.dn:
                    continue
                s = self.__collect_printer_settings(user_group)

                if user_group.gotoXResolution is not None:
                    resolution = user_group.gotoXResolution

                for p in s["printers"]:
                    if p["cn"] not in printer_names:
                        settings["printers"].append(p)
                        printer_names.append(p["cn"])

                if s["defaultPrinter"] is not None:
                    settings["defaultPrinter"] = s["defaultPrinter"]

            # override group environment settings if the client has one
            s = self.__collect_printer_settings(client)
            if len(s["printers"]) > 0:
                settings["printers"] = s["printers"]
                settings["defaultPrinter"] = s["defaultPrinter"]

            if user.is_extended_by(
                    "GosaAccount") and user.gosaDefaultPrinter is not None:
                # check if the users default printer is send to the client
                found = False
                for printer_settings in settings["printers"]:
                    if printer_settings["cn"] == user.gosaDefaultPrinter:
                        found = True
                        break

                def process(res):
                    if len(res) == 0:
                        self.log.warning("users defaultPrinter not found: %s" %
                                         user.gosaDefaultPrinter)
                        return None
                    elif len(res) == 1:
                        # add this one to the result set
                        printer = ObjectProxy(res[0]["dn"])
                        p_conf = {}
                        for attr in self.printer_attributes:
                            p_conf[attr] = getattr(printer, attr)
                        return p_conf
                    return False

                if found is False:
                    # find the printer and add it to the settings
                    res = index.search(
                        {
                            "_type": "GotoPrinter",
                            "cn": user.gosaDefaultPrinter
                        }, {"dn": 1})
                    printer_config = process(res)
                    if printer_config is False:
                        # more than 1 printers found by this CN, try to look in the users subtree
                        res = index.search(
                            {
                                "_type":
                                "GotoPrinter",
                                "cn":
                                user.gosaDefaultPrinter,
                                "_adjusted_parent_dn":
                                user.get_adjusted_parent_dn()
                            }, {"dn": 1})
                        printer_config = process(res)

                    if isinstance(printer_config, dict):
                        settings["printers"].append(printer_config)
                        settings["defaultPrinter"] = user.gosaDefaultPrinter
                    else:
                        self.log.warning("users defaultPrinter not found: %s" %
                                         user.gosaDefaultPrinter)
                else:
                    settings["defaultPrinter"] = user.gosaDefaultPrinter

            config[user.uid]["printer-setup"] = settings
            config[user.uid]["resolution"] = None

            if resolution is not None:
                config[user.uid]["resolution"] = [
                    int(x) for x in resolution.split("x")
                ]

            # TODO: collect and send login scripts to client
        return config
Beispiel #14
0
    def joinClient(self, user, device_uuid, mac, info=None):
        """
        TODO
        """

        index = PluginRegistry.getInstance("ObjectIndex")

        uuid_check = re.compile(
            r"^[0-9a-f]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}$",
            re.IGNORECASE)
        if not uuid_check.match(device_uuid):
            raise ValueError(C.make_error("CLIENT_UUID_INVALID", device_uuid))

        # Handle info, if present
        more_info = []

        if info:
            # Check string entries
            for entry in filter(
                    lambda x: x in info,
                ["serialNumber", "ou", "o", "l", "description"]):

                if not re.match(r"^[\w\s]+$", info[entry]):
                    raise ValueError(
                        C.make_error("CLIENT_DATA_INVALID",
                                     device_uuid,
                                     entry=entry,
                                     data=info[entry]))

                more_info.append((entry, info[entry]))

            # Check desired device type if set
            if "deviceType" in info:
                if re.match(
                        r"^(terminal|workstation|server|sipphone|switch|router|printer|scanner)$",
                        info["deviceType"]):

                    more_info.append(("deviceType", info["deviceType"]))
                else:
                    raise ValueError(
                        C.make_error("CLIENT_TYPE_INVALID",
                                     device_uuid,
                                     type=info["deviceType"]))

            # Check owner for presence
            if "owner" in info:
                # Take a look at the directory to see if there's  such an owner DN
                res = index.search({'_dn': info["owner"]}, {'_dn': 1})
                if len(res) == 0:
                    raise ValueError(
                        C.make_error("CLIENT_OWNER_NOT_FOUND",
                                     device_uuid,
                                     owner=info["owner"]))
                more_info.append(("owner", info["owner"]))

        # Generate random client key
        h, key, salt = generate_random_key()

        # Take a look at the directory to see if there's already a joined client with this uuid
        res = index.search(
            {
                '_type': 'Device',
                'macAddress': mac,
                'extension': 'RegisteredDevice'
            }, {'dn': 1})

        if len(res) > 0:
            record = ObjectProxy(res[0]['dn'])
            for ext in ["simpleSecurityObject", "ieee802Device"]:
                if not record.is_extended_by(ext):
                    record.extend(ext)

            if record.is_extended_by("ForemanHost") and record.otp is not None:
                record.otp = None

            record.userPassword = [
                "{SSHA}" + encode(h.digest() + salt).decode()
            ]
            for k, value in more_info:
                setattr(record, k, value)
            cn = record.deviceUUID
            record.status_Online = False
            record.status_Offline = True
            record.status_InstallationInProgress = False

            record.commit()
            self.log.info("UUID '%s' joined as %s" % (device_uuid, record.dn))
        else:

            # While the client is going to be joined, generate a random uuid and an encoded join key
            cn = str(uuid4())
            device_key = encrypt_key(device_uuid.replace("-", ""), cn + key)

            # Resolve manager
            res = index.search({'_type': 'User', 'uid': user}, {'dn': 1})

            if len(res) != 1:
                raise GOtoException(
                    C.make_error("USER_NOT_UNIQUE" if res else "UNKNOWN_USER",
                                 target=user))
            manager = res[0]['dn']

            # Create new machine entry
            dn = ",".join([
                self.env.config.get("goto.machine-rdn", default="ou=systems"),
                self.env.base
            ])
            record = ObjectProxy(dn, "Device")
            record.extend("RegisteredDevice")
            record.extend("ieee802Device")
            record.extend("simpleSecurityObject")
            record.deviceUUID = cn
            record.deviceKey = Binary(device_key)
            record.cn = "mac%s" % mac.replace(":", "")
            record.manager = manager
            record.status_Offline = True
            record.macAddress = mac.encode("ascii", "ignore")
            record.userPassword = [
                "{SSHA}" + encode(h.digest() + salt).decode()
            ]
            for k, value in more_info:
                setattr(record, k, value)

            record.commit()
            self.log.info("UUID '%s' joined as %s" % (device_uuid, record.dn))

        # make sure the client has the access rights he needs
        self.applyClientRights(cn)

        return [key, cn]