def execute_command_request()

in gui/backend/gui_plugin/core/ShellGuiWebSocketHandler.py [0:0]


    def execute_command_request(self, json_msg):
        request_id = json_msg.get('request_id')
        try:
            cmd = json_msg.get('command')
            if not cmd:
                raise Exception(
                    'No command given. Please provide the command.')

            # Check if user is allowed to execute this command
            allowed = False
            res = self.db.execute(
                '''SELECT p.name, p.access_pattern
                FROM privilege p
                    INNER JOIN role_has_privilege r_p
                        ON p.id = r_p.privilege_id
                    INNER JOIN user_has_role u_r
                        ON r_p.role_id = u_r.role_id
                WHERE u_r.user_id = ? AND p.privilege_type_id = 1''',
                (self.session_user_id,)).fetch_all()
            for row in res:
                p = re.compile(row['access_pattern'])
                m = p.match(cmd)
                if not m:
                    raise Exception(f'This user account has no privileges to '
                                    f'execute the command {cmd}')
                allowed = True
                break

            if not allowed:
                raise Exception(f'This user does not have the necessary '
                                f'privileges to execute the command {cmd}.')

            # Argument need to be passed in a dict using the argument names as
            # the keys
            args = json_msg.get('args', {})
            kwargs = json_msg.get('kwargs', {})

            kwargs = {**args, **kwargs}

            # Inspect the function arguments and check if there are arguments
            # named user_id, profile_id, web_session, request_id,
            # module_session, async_web_session or session.
            # If so, replace them with session variables
            f_args = []

            # Loop over all chained objects/functions of the given cmd and find
            # the function to call
            matches = re.findall(r'(\w+)\.', cmd + '.')
            parent_obj = None
            func = None

            if len(matches) < 2:
                raise Exception(
                    f"The command '{cmd}' is using wrong format. "
                    "Use <global>[.<object>]*.<function>")

            # Last entry is a function name
            function_name = matches[-1]

            # Rest is a chain of objects
            objects = matches[:-1]

            found_objects = []

            # Selects the parent object
            if objects[0] == 'gui':
                parent_obj = gui
                objects = objects[1:]
                found_objects.append('gui')
            else:
                parent_obj = mysqlsh.globals

            # Searches the object hierarchy
            for object in objects:
                try:
                    # Convert from camelCase to snake_case
                    object = re.sub(r'(?<!^)(?=[A-Z])', '_', object).lower()

                    child = getattr(parent_obj, object)

                    # Set the parent_obj for the next object evaluation
                    parent_obj = child
                    found_objects.append(object)
                except:
                    if len(found_objects) == 0:
                        raise Exception(
                            f"The '{object}' global object does not exist")
                    else:
                        raise Exception(
                            f"Object '{'.'.join(found_objects)}' has no member named '{object}'")

            # Searches the target function
            try:
                func = getattr(parent_obj, function_name)
            except:
                raise Exception(
                    f"Object '{'.'.join(found_objects)}' has no member function named '{function_name}'")

            f_args = {}
            if func:
                f_args = self.get_function_arguments(
                    func=func, mod=parent_obj, mod_cmd=function_name)

            lock_session = False

            if found_objects[0] == 'gui':
                # This is the `user_id` that needs to be provided by the user
                # like for the function `add_profile(user_id, profile)`
                if "user_id" in f_args:
                    # Return error if user_id does not match self.session_user_id
                    if self.session_user_id is None or "user_id" not in kwargs \
                            or kwargs["user_id"] != self.session_user_id:
                        raise Exception(f'The function argument user_id must not '
                                        f'be set to a different user_id than the '
                                        f'one used in the '
                                        f'authenticated session.')

                    kwargs.update({"user_id": self.session_user_id})

                # The `_user_id` here is for internal use only
                # it's not exposed to the user and it's replaced
                # always by session_user_id
                if "_user_id" in f_args and not self.is_local_session:
                    kwargs.update({"_user_id": self.session_user_id})

                if "profile_id" in f_args:
                    if "profile_id" not in kwargs:
                        kwargs.update({"profile_id":
                                       self.session_active_profile_id})

                if "web_session" in f_args:
                    raise Exception(
                        f'Argument web_session not allowed for function: {cmd}.')

                if "request_id" in f_args:
                    raise Exception(
                        f'Argument request_id not allowed for function: {cmd}.')

                if "be_session" in f_args:
                    kwargs.update({"be_session": self.db})

                if "db_connection_id" in f_args and self.single_server is not None:
                    if self._single_server_conn_id is None:
                        if self.session_uuid in self.get_cache():
                            self._single_server_conn_id = self.get_cache()[
                                self.session_uuid][1]
                            del self.get_cache()[
                                self.session_uuid]

                    kwargs.update({"db_connection_id": self._single_server_conn_id})

            if "interactive" in f_args:
                kwargs.update({"interactive": False})

            # If the function may receive a message handler, adds the kwarg so the
            # RequestHandler properly sets the callback
            if "send_gui_message" in f_args:
                kwargs.update({"send_gui_message": True})

            lock_session = False
            if "session" in f_args:
                # If the called function requires a session parameter,
                # get it from the given module_session
                if not 'module_session_id' in kwargs:
                    raise Exception(
                        f'The function {cmd} requires the module_session_id '
                        'argument to be set.')
                module_session = self.get_module_session_object(
                    kwargs['module_session_id'])
                if not isinstance(module_session, DbModuleSession):
                    raise Exception(
                        f'The function {cmd} needs a module_session_id '
                        'argument set to a DbModuleSession.')

                user_session_functions = ["gui.sql_editor.execute",
                                          "gui.sql_editor.default_user_schema", "gui.sql_editor.get_current_schema",
                                          "gui.sql_editor.set_current_schema", "gui.sql_editor.get_auto_commit",
                                          "gui.sql_editor.set_auto_commit"]
                if isinstance(module_session, SqlEditorModuleSession) and cmd in user_session_functions:
                    db_module_session = module_session._db_user_session
                else:
                    db_module_session = module_session._db_service_session
                if not isinstance(db_module_session, DbSession):
                    raise Exception(
                        f'The function {cmd} needs a module_session_id '
                        'argument set to a DbSession.')

                self.register_module_request(
                    request_id, kwargs['module_session_id'])

                kwargs.update({"session": db_module_session})

                # The plugins written for the Shell that work with standard Shell session fall on this branch,
                # the session must be locked while the function is executed to avoid race conditions that may
                # lead to shell failures
                if found_objects[0] != 'gui':
                    lock_session = True

                del kwargs['module_session_id']

            module_session = None
            if "module_session" in f_args:
                if "module_session_id" not in kwargs:
                    raise Exception('No module_session_id given. Please '
                                    'provide the module_session_id.')

                # swap 'module_session_id' with 'module_session'
                module_session = self.get_module_session_object(
                    kwargs['module_session_id'])
                kwargs.update({"module_session": module_session})
                self.register_module_request(
                    request_id, kwargs['module_session_id'])
                del kwargs['module_session_id']

            thread = RequestHandler(
                request_id, func, kwargs, self, lock_session=lock_session)
            thread.start()
            result = None

        except Exception as e:
            logger.exception(e)
            result = Response.exception(e)

        if result is not None:
            self.send_command_response(request_id, result)