Esempio n. 1
0
    def unpublish_service(self, service, token, multicast_groups=conf.MULTICAST_ADDRS, delete_file=False):
        """
        Removes a service from the offered services structures and all associated files, upon request.

        :param tuple address: A tuple with the requesting address and port.

        :param str service: The id of the service to delete.

        :param int uid: The uid of the requesting user.

        :param list multicast_groups: The list of multicast_groups to delete the service from.\
         If not defined, the service is removed from all groups.

        :param bool delete_file: If set to ``True`` and the service is of type permanent,\
         the service file is deleted. If the service is not permanent the parameter is ignored. 
        """
        logging.debug("Unpublishing service")
        if len(str(token)) == 0:

            self.write_error("Bad token")
            return

        uid = tokenprovider.decrypt_token(token, self.secret)
        try:
            if uid is None or int(uid) < 0:
                self.write_error("Bad token")
                return
        except ValueError as e:
            self.write_error("Bad token")
            return

        if len(multicast_groups) < 1:
            multicast_groups = conf.MULTICAST_ADDRS
        # Determine whether it is a root or a user service
        # The IP addresses must be represented in valid dot notation and belong to the range 224-239
        error = False
        reason = ""

        for ip in multicast_groups:
            # The IP must be a string
            error, faulty_ip, reason = utils.verify_ip(ip, multicast_groups)

            if error == True:
                logging.debug(reason)
                try:
                    self.write_error("Invalid multicast group address '%s': %s" % (str(faulty_ip), reason))
                except Exception:
                    self.write_error("Invalid multicast group address")
                return

        if error != False:
            self.write_error("Invalid multicast group address '%s': %s" % (str(faulty_ip), reason))
            return

        if len(set(multicast_groups) - (set(conf.MULTICAST_ADDRS) & set(multicast_groups))) > 0:
            self.write_error("The group %s is not available" % multicast_groups[0])
            return

        user = self.validate_user(uid)

        if user is None:
            self.write_error("wrong user")
            return

        if self.verify.match(service):
            # user service
            try:
                user, service_name = self.verify.match(service).groups()
                user_pwd = pwd.getpwnam(user)
            except (IndexError, ValueError):
                self.write_error("Invalid formatting")
                return
            if user_pwd is None:
                self.write_error("Invalid user")
                return
            for group in multicast_groups:
                if self.user_services[group].get(user, None) is not None:
                    match = next((s for s in self.user_services[group][user] if s["id"] == service_name), None)
                    if match:
                        is_permanent = match.get("permanent", False)

                        if delete_file and is_permanent:
                            folder = user_pwd.pw_dir
                            deploy_folder = path.join(folder, conf.POLO_USER_DIR)
                            if path.exists(deploy_folder) and isfile(path.join(deploy_folder, service_name)):

                                try:
                                    os.remove(path.join(deploy_folder, service_name))
                                except Exception as e:
                                    pass
                            else:
                                self.write_error("Could not find service file")
                        try:
                            self.user_services[group][user].remove(match)
                        except ValueError:
                            pass
                    else:
                        self.write_error("Could not find service")
                        return
                else:
                    self.write_error("Could not find service")
                    return
            else:
                self.write_ok(0)
                return
        else:
            # root service
            error_str = ""
            for group in multicast_groups:
                match = next((s for s in self.offered_services[group] if s["id"] == service), None)
                if match:
                    is_permanent = match.get("permanent", False)

                    if delete_file and is_permanent:
                        folder = path.join(conf.CONF_DIR, conf.SERVICES_DIR)

                        if path.exists(folder) and isfile(path.join(folder, service)):
                            with open(path.join(folder, service), "r+") as f:
                                file_dict = json.load(f)
                                if len(file_dict.get("groups", [])) == 0:
                                    try:
                                        f.close()
                                        os.remove(path.join(folder, service))
                                    except Exception as e:
                                        self.write_error("Internal error during processing of file")
                                else:
                                    groups = file_dict.get("groups", [])
                                    if len(groups) > 0:
                                        try:
                                            groups.remove(group)
                                        except ValueError:
                                            pass

                                    if len(groups) == 0:
                                        try:
                                            f.close()
                                            os.remove(path.join(folder, service))
                                        except Exception as e:
                                            self.write_error("Internal error during processing of file")
                                    else:
                                        f.seek(0, 0)
                                        f.truncate()
                                        json.dump(file_dict, f)

                        else:
                            self.write_error("Could not find service file")
                    try:
                        self.offered_services[group].remove(match)
                    except ValueError:
                        pass
                else:
                    if delete_file:
                        folder = path.join(conf.CONF_DIR, conf.SERVICES_DIR)
                        if path.exists(folder) and isfile(path.join(folder, service)):
                            with open(path.join(folder, service), "r+") as f:
                                file_dict = json.load(f)
                                if len(file_dict.get("groups", [])) < 1:
                                    try:
                                        f.close()
                                        os.remove(path.join(folder, service))
                                    except Exception as e:
                                        self.write_error("Internal error during processing of file")
                                else:
                                    groups = file_dict.get("groups", [])
                                    if len(groups) > 0:
                                        try:
                                            groups.remove(group)
                                        except ValueError:
                                            pass
                                    f.seek(0, 0)
                                    f.truncate()
                                    json.dump(file_dict, f)

                        else:
                            self.write_error("Could not find service")
                            return
                try:
                    self.offered_services[group].remove(match)
                except ValueError as e:
                    pass
            else:
                try:
                    self.write_ok(0)
                except ValueError:
                    pass
Esempio n. 2
0
    def publish_service(
        self, service, token, params={}, multicast_groups=conf.MULTICAST_ADDRS, permanent=False, root=False
    ):
        """
        Registers a service during execution time.

        :param tuple address: A tuple with the requesting address and port
        
        :param str service: Indicates the unique identifier of the service.
        
            If `root` is true, the published service will have the same identifier as the value of the parameter. Otherwise, the name of the user will be prepended (`<user>:<service>`).
        
        :param int uid: The unique user identifier

        :param set multicast_groups: Indicates the groups where the service shall be published.
        
            Note that the groups must be defined in the polo.conf file, or otherwise the method will throw an exception.
        
        :param bool permanent: If set to true a file will be created and the service will be permanently offered until the file is deleted.
        
        :param bool root: Stores the file in the marcopolo configuration directory.
        
            This feature is only available to privileged users, by default root and users in the marcopolo group.
        """
        if len(str(token)) == 0:
            self.write_error("Need a token")
            return

        uid = tokenprovider.decrypt_token(token, self.secret)
        try:
            if uid is None or int(uid) < 0:
                self.write_error("Bad token. Could not find user")
                return
        except ValueError as e:
            self.write_error("Bad token")
            return

        error = False  # Python does not allow throwing an exception insided an exception, so we use a flag
        reason = ""

        # Verification of services
        if not isinstance(service, six.string_types):
            error = True
            reason = "Service must be a string"

        # The service must be something larger than 1 character
        if service is None or len(service) < 1:
            error = True
            reason = "Must be larger than 1"

        if error:
            logging.debug(error)
            self.write_error("The name of the service %s is invalid: %s" % (service, reason))
            return

        error = False
        faulty_ip = ""
        # The IP addresses must be represented in valid dot notation and belong to the range 224-239
        for ip in multicast_groups:
            # The IP must be a string
            error, faulty_ip, reason = utils.verify_ip(ip, multicast_groups)

            if error == True:
                logging.debug(reason)
                try:
                    self.write_error("Invalid multicast group address '%s': %s" % (str(faulty_ip), reason))
                except Exception:
                    self.write_error("Invalid multicast group address")
                return

        if type(permanent) is not bool:
            logging.debug("Permanent must be boolean")
            self.write_error("permanent must be boolean")
            return

        if type(root) is not bool:
            self.write_error("root must be boolean")
            return

        # UID verification
        user = self.validate_user(uid)
        if user is None:
            self.write_error("wrong user")
            return

        # Final entry with all service parameters
        service_dict = {}
        service_dict["id"] = service
        service_dict["params"] = params
        service_dict["groups"] = multicast_groups
        service_dict["disabled"] = False

        # Root service
        error = False
        error_reason = ""

        if len(multicast_groups) < 1:
            multicast_groups = conf.MULTICAST_ADDRS

        if root is True:
            if not self.is_superuser(user):
                self.write_error("Permission denied")
                return

            for group in multicast_groups:
                # Only root or members of the `marcopolo` group can publish root services

                if service in [s["id"] for s in self.offered_services[group]]:
                    error_reason = "Service %s already exists" % service
                    error = True
                    continue

                if permanent is True:

                    services_dir = path.join(conf.CONF_DIR, conf.SERVICES_DIR)
                    if not path.exists(services_dir):
                        makedirs(services_dir)
                        os.chown(services_dir, 0, grp.getgrnam(conf.GROUP_NAME).gr_gid)

                    service_file = sanitize_path(service)
                    if path.isfile(path.join(services_dir, service_file)):
                        error_reason = "Permanent service %s already exists" % service
                        error = True
                        continue

                    try:
                        f = open(path.join(services_dir, service_file), "w")
                        f.write(json.dumps(service_dict))
                        os.fchown(f.fileno(), user.pw_uid, user.pw_gid)
                        f.close()
                        service_dict["file"] = service_file
                    except Exception as e:
                        error_reason = "Could not write file"
                        error = True
                        continue

                self.offered_services[group].append(
                    {"id": service, "permanent": permanent, "disabled": False, "params": params}
                )
            else:
                service_dict["permanent"] = permanent
                if not error:
                    self.write_ok(service)
                else:
                    self.write_error(error_reason)
            return

        else:
            error = False
            for group in multicast_groups:

                if self.user_services[group].get(user.pw_name, None):
                    if service in [s["id"] for s in self.user_services[group][user.pw_name]]:
                        error = True
                        continue

                folder = user.pw_dir
                deploy_folder = path.join(folder, conf.POLO_USER_DIR)

                if permanent is True:
                    if not path.exists(deploy_folder):
                        makedirs(deploy_folder)
                        os.chown(deploy_folder, user.pw_uid, user.pw_gid)

                    service_file = sanitize_path(service)
                    if path.isfile(path.join(deploy_folder, service_file)):

                        error = True
                        continue

                    try:
                        f = open(path.join(deploy_folder, service_file), "w")
                        f.write(json.dumps(service_dict))
                        os.fchown(f.fileno(), user.pw_uid, user.pw_gid)
                        f.close()
                    except Exception as e:
                        logging.debug(e)
                        self.write_error("Could not write service file %s" % str(e))
                        error = True
                        return

                if self.user_services[group].get(user.pw_name, None) is None:
                    self.user_services[group][user.pw_name] = []

                self.user_services[group][user.pw_name].append(
                    {"id": service, "permanent": permanent, "disabled": False, "params": params}
                )
                logging.debug("Publishing user service %s in group %s" % (service, group))
            else:
                if not error:
                    self.write_ok(user.pw_name + ":" + service)
                else:
                    self.write_error("Service already exists")