MindsDB 25.8.3.0__py3-none-any.whl → 25.9.1.1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of MindsDB might be problematic. Click here for more details.

Files changed (109) hide show
  1. mindsdb/__about__.py +1 -1
  2. mindsdb/__main__.py +3 -45
  3. mindsdb/api/a2a/__init__.py +52 -0
  4. mindsdb/api/a2a/agent.py +11 -12
  5. mindsdb/api/a2a/common/server/server.py +17 -36
  6. mindsdb/api/a2a/common/server/task_manager.py +14 -28
  7. mindsdb/api/a2a/task_manager.py +20 -21
  8. mindsdb/api/a2a/utils.py +1 -1
  9. mindsdb/api/common/middleware.py +106 -0
  10. mindsdb/api/executor/utilities/mysql_to_duckdb_functions.py +466 -18
  11. mindsdb/api/executor/utilities/sql.py +9 -31
  12. mindsdb/api/http/initialize.py +34 -43
  13. mindsdb/api/http/namespaces/auth.py +6 -14
  14. mindsdb/api/http/namespaces/config.py +0 -2
  15. mindsdb/api/http/namespaces/default.py +74 -106
  16. mindsdb/api/http/namespaces/file.py +9 -3
  17. mindsdb/api/http/namespaces/handlers.py +77 -87
  18. mindsdb/api/http/start.py +29 -47
  19. mindsdb/api/litellm/start.py +11 -10
  20. mindsdb/api/mcp/__init__.py +165 -0
  21. mindsdb/api/mysql/mysql_proxy/mysql_proxy.py +33 -64
  22. mindsdb/api/postgres/postgres_proxy/postgres_proxy.py +86 -85
  23. mindsdb/integrations/handlers/autogluon_handler/requirements.txt +1 -1
  24. mindsdb/integrations/handlers/autosklearn_handler/requirements.txt +1 -1
  25. mindsdb/integrations/handlers/crate_handler/crate_handler.py +3 -7
  26. mindsdb/integrations/handlers/derby_handler/derby_handler.py +32 -34
  27. mindsdb/integrations/handlers/documentdb_handler/requirements.txt +1 -0
  28. mindsdb/integrations/handlers/dummy_data_handler/dummy_data_handler.py +12 -13
  29. mindsdb/integrations/handlers/flaml_handler/requirements.txt +1 -1
  30. mindsdb/integrations/handlers/google_books_handler/google_books_handler.py +45 -44
  31. mindsdb/integrations/handlers/google_calendar_handler/google_calendar_handler.py +101 -95
  32. mindsdb/integrations/handlers/google_content_shopping_handler/google_content_shopping_handler.py +129 -129
  33. mindsdb/integrations/handlers/google_fit_handler/google_fit_handler.py +59 -43
  34. mindsdb/integrations/handlers/google_search_handler/google_search_handler.py +38 -39
  35. mindsdb/integrations/handlers/informix_handler/informix_handler.py +5 -18
  36. mindsdb/integrations/handlers/lightfm_handler/requirements.txt +1 -1
  37. mindsdb/integrations/handlers/lightwood_handler/requirements.txt +4 -4
  38. mindsdb/integrations/handlers/maxdb_handler/maxdb_handler.py +22 -28
  39. mindsdb/integrations/handlers/monetdb_handler/monetdb_handler.py +3 -7
  40. mindsdb/integrations/handlers/mongodb_handler/mongodb_handler.py +53 -67
  41. mindsdb/integrations/handlers/mongodb_handler/requirements.txt +1 -0
  42. mindsdb/{api/mongo/utilities → integrations/handlers/mongodb_handler/utils}/mongodb_ast.py +43 -68
  43. mindsdb/{api/mongo/utilities → integrations/handlers/mongodb_handler/utils}/mongodb_parser.py +17 -25
  44. mindsdb/{api/mongo/utilities → integrations/handlers/mongodb_handler/utils}/mongodb_query.py +10 -16
  45. mindsdb/integrations/handlers/mongodb_handler/utils/mongodb_render.py +43 -69
  46. mindsdb/integrations/handlers/tpot_handler/requirements.txt +1 -1
  47. mindsdb/integrations/libs/base.py +1 -1
  48. mindsdb/integrations/libs/llm/config.py +15 -0
  49. mindsdb/integrations/libs/llm/utils.py +15 -0
  50. mindsdb/interfaces/agents/constants.py +1 -0
  51. mindsdb/interfaces/agents/langchain_agent.py +4 -0
  52. mindsdb/interfaces/agents/providers.py +20 -0
  53. mindsdb/interfaces/knowledge_base/controller.py +25 -7
  54. mindsdb/utilities/config.py +15 -158
  55. mindsdb/utilities/log.py +0 -25
  56. mindsdb/utilities/render/sqlalchemy_render.py +7 -1
  57. mindsdb/utilities/starters.py +0 -39
  58. {mindsdb-25.8.3.0.dist-info → mindsdb-25.9.1.1.dist-info}/METADATA +269 -267
  59. {mindsdb-25.8.3.0.dist-info → mindsdb-25.9.1.1.dist-info}/RECORD +62 -105
  60. mindsdb/api/a2a/__main__.py +0 -144
  61. mindsdb/api/a2a/run_a2a.py +0 -86
  62. mindsdb/api/common/check_auth.py +0 -42
  63. mindsdb/api/http/gunicorn_wrapper.py +0 -17
  64. mindsdb/api/mcp/start.py +0 -205
  65. mindsdb/api/mongo/__init__.py +0 -0
  66. mindsdb/api/mongo/classes/__init__.py +0 -5
  67. mindsdb/api/mongo/classes/query_sql.py +0 -19
  68. mindsdb/api/mongo/classes/responder.py +0 -45
  69. mindsdb/api/mongo/classes/responder_collection.py +0 -34
  70. mindsdb/api/mongo/classes/scram.py +0 -86
  71. mindsdb/api/mongo/classes/session.py +0 -23
  72. mindsdb/api/mongo/functions/__init__.py +0 -19
  73. mindsdb/api/mongo/responders/__init__.py +0 -73
  74. mindsdb/api/mongo/responders/add_shard.py +0 -13
  75. mindsdb/api/mongo/responders/aggregate.py +0 -90
  76. mindsdb/api/mongo/responders/buildinfo.py +0 -17
  77. mindsdb/api/mongo/responders/coll_stats.py +0 -63
  78. mindsdb/api/mongo/responders/company_id.py +0 -25
  79. mindsdb/api/mongo/responders/connection_status.py +0 -22
  80. mindsdb/api/mongo/responders/count.py +0 -21
  81. mindsdb/api/mongo/responders/db_stats.py +0 -32
  82. mindsdb/api/mongo/responders/delete.py +0 -105
  83. mindsdb/api/mongo/responders/describe.py +0 -23
  84. mindsdb/api/mongo/responders/end_sessions.py +0 -13
  85. mindsdb/api/mongo/responders/find.py +0 -175
  86. mindsdb/api/mongo/responders/get_cmd_line_opts.py +0 -18
  87. mindsdb/api/mongo/responders/get_free_monitoring_status.py +0 -14
  88. mindsdb/api/mongo/responders/get_parameter.py +0 -23
  89. mindsdb/api/mongo/responders/getlog.py +0 -14
  90. mindsdb/api/mongo/responders/host_info.py +0 -28
  91. mindsdb/api/mongo/responders/insert.py +0 -270
  92. mindsdb/api/mongo/responders/is_master.py +0 -20
  93. mindsdb/api/mongo/responders/is_master_lower.py +0 -13
  94. mindsdb/api/mongo/responders/list_collections.py +0 -55
  95. mindsdb/api/mongo/responders/list_databases.py +0 -37
  96. mindsdb/api/mongo/responders/list_indexes.py +0 -22
  97. mindsdb/api/mongo/responders/ping.py +0 -13
  98. mindsdb/api/mongo/responders/recv_chunk_start.py +0 -13
  99. mindsdb/api/mongo/responders/replsetgetstatus.py +0 -13
  100. mindsdb/api/mongo/responders/sasl_continue.py +0 -34
  101. mindsdb/api/mongo/responders/sasl_start.py +0 -33
  102. mindsdb/api/mongo/responders/update_range_deletions.py +0 -12
  103. mindsdb/api/mongo/responders/whatsmyuri.py +0 -18
  104. mindsdb/api/mongo/server.py +0 -388
  105. mindsdb/api/mongo/start.py +0 -15
  106. mindsdb/api/mongo/utilities/__init__.py +0 -0
  107. {mindsdb-25.8.3.0.dist-info → mindsdb-25.9.1.1.dist-info}/WHEEL +0 -0
  108. {mindsdb-25.8.3.0.dist-info → mindsdb-25.9.1.1.dist-info}/licenses/LICENSE +0 -0
  109. {mindsdb-25.8.3.0.dist-info → mindsdb-25.9.1.1.dist-info}/top_level.txt +0 -0
@@ -1,86 +0,0 @@
1
- #!/usr/bin/env python
2
- import os
3
- import sys
4
- import logging
5
- from dotenv import load_dotenv
6
- from typing import Dict, Any, Optional
7
-
8
- # Configure logging
9
- logging.basicConfig(level=logging.INFO, format="%(asctime)s - %(name)s - %(levelname)s - %(message)s")
10
- logger = logging.getLogger(__name__)
11
-
12
-
13
- def setup_python_path():
14
- """Ensure the repository root directory is on *sys.path* so that the
15
- ``mindsdb`` package (located at <repo>/mindsdb) can be imported when this
16
- helper script is executed from inside *mindsdb/api/a2a*.
17
- """
18
-
19
- # Absolute path to *this* file
20
- this_file_dir = os.path.dirname(os.path.abspath(__file__))
21
-
22
- # Walk three levels up: a2a/ -> api/ -> mindsdb/ -> <repo_root>
23
- repo_root = os.path.abspath(os.path.join(this_file_dir, os.pardir, os.pardir, os.pardir))
24
-
25
- # Prepend to PYTHONPATH if not already present
26
- if repo_root not in sys.path:
27
- sys.path.insert(0, repo_root)
28
-
29
- # Add the a2a directory to the Python path so that 'common' can be imported
30
- if this_file_dir not in sys.path:
31
- sys.path.insert(0, this_file_dir)
32
-
33
- logger.info("Added %s to PYTHONPATH", repo_root)
34
-
35
-
36
- def main(config_override: Optional[Dict[str, Any]] = None, *args, **kwargs):
37
- """
38
- Run the a2a module with the correct Python path.
39
- First set up the Python path, then import and run __main__.py
40
-
41
- Args:
42
- config_override: Optional configuration dictionary to override settings
43
- args: Additional positional arguments
44
- kwargs: Additional keyword arguments
45
- """
46
- # Set up Python path first
47
- setup_python_path()
48
-
49
- # Load environment variables
50
- load_dotenv()
51
-
52
- try:
53
- # Import the main_with_config function from __main__.py
54
- from mindsdb.api.a2a.__main__ import main_with_config
55
- from mindsdb.utilities.config import config
56
-
57
- logger.info("Successfully imported a2a module")
58
-
59
- # Get configuration from config system or use provided override
60
- a2a_config = config_override if config_override is not None else config.get("api", {}).get("a2a", {})
61
-
62
- # Set log level if specified
63
- if a2a_config.get("log_level"):
64
- log_level = getattr(logging, a2a_config["log_level"].upper(), None)
65
- if log_level:
66
- logger.setLevel(log_level)
67
- logger.info(f"Set log level to {a2a_config['log_level'].upper()}")
68
-
69
- logger.info(f"Starting A2A with configuration: {a2a_config}")
70
-
71
- # Call the main_with_config function with the configuration
72
- main_with_config(a2a_config, *args, **kwargs)
73
-
74
- except ImportError as e:
75
- logger.error(f"Error importing a2a module: {e}")
76
- sys.exit(1)
77
- except Exception as e:
78
- logger.error(f"Error running a2a module: {e}")
79
- import traceback
80
-
81
- logger.error(traceback.format_exc())
82
- sys.exit(1)
83
-
84
-
85
- if __name__ == "__main__":
86
- main()
@@ -1,42 +0,0 @@
1
- import traceback
2
-
3
- from mindsdb.utilities import log
4
-
5
- logger = log.getLogger(__name__)
6
-
7
-
8
- def check_auth(username, password, scramble_func, salt, company_id, config):
9
- try:
10
- hardcoded_user = config['auth'].get('username')
11
- hardcoded_password = config['auth'].get('password')
12
- if hardcoded_password is None:
13
- hardcoded_password = ''
14
- hardcoded_password_hash = scramble_func(hardcoded_password, salt)
15
- hardcoded_password = hardcoded_password.encode()
16
-
17
- if password is None:
18
- password = ''
19
- if isinstance(password, str):
20
- password = password.encode()
21
-
22
- if username != hardcoded_user:
23
- logger.warning(f'Check auth, user={username}: user mismatch')
24
- return {
25
- 'success': False
26
- }
27
-
28
- if password != hardcoded_password and password != hardcoded_password_hash:
29
- logger.warning(f'check auth, user={username}: password mismatch')
30
- return {
31
- 'success': False
32
- }
33
-
34
- logger.info(f'Check auth, user={username}: Ok')
35
- return {
36
- 'success': True,
37
- 'username': username
38
- }
39
- except Exception as e:
40
- logger.error(f'Check auth, user={username}: ERROR')
41
- logger.error(e)
42
- logger.error(traceback.format_exc())
@@ -1,17 +0,0 @@
1
- import gunicorn.app.base
2
-
3
-
4
- class StandaloneApplication(gunicorn.app.base.BaseApplication):
5
- def __init__(self, app, options=None):
6
- self.options = options or {}
7
- self.application = app
8
- super().__init__()
9
-
10
- def load_config(self):
11
- config = {key: value for key, value in self.options.items()
12
- if key in self.cfg.settings and value is not None}
13
- for key, value in config.items():
14
- self.cfg.set(key.lower(), value)
15
-
16
- def load(self):
17
- return self.application
mindsdb/api/mcp/start.py DELETED
@@ -1,205 +0,0 @@
1
- import os
2
- from typing import Any
3
- from textwrap import dedent
4
- from contextlib import asynccontextmanager
5
- from collections.abc import AsyncIterator
6
- from dataclasses import dataclass
7
-
8
- import uvicorn
9
- import anyio
10
- from mcp.server.fastmcp import FastMCP
11
- from starlette.middleware.base import BaseHTTPMiddleware
12
- from starlette.requests import Request
13
- from starlette.responses import Response
14
-
15
- from mindsdb.api.mysql.mysql_proxy.classes.fake_mysql_proxy import FakeMysqlProxy
16
- from mindsdb.api.executor.data_types.response_type import RESPONSE_TYPE as SQL_RESPONSE_TYPE
17
- from mindsdb.utilities import log
18
- from mindsdb.utilities.log import get_uvicorn_logging_config
19
- from mindsdb.utilities.config import Config
20
- from mindsdb.interfaces.storage import db
21
-
22
- logger = log.getLogger(__name__)
23
-
24
-
25
- @dataclass
26
- class AppContext:
27
- db: Any
28
-
29
-
30
- @asynccontextmanager
31
- async def app_lifespan(server: FastMCP) -> AsyncIterator[AppContext]:
32
- """Manage application lifecycle with type-safe context"""
33
- # Initialize on startup
34
- db.init()
35
- try:
36
- yield AppContext(db=db)
37
- finally:
38
- # TODO: We need better way to handle this in storage/db.py
39
- pass
40
-
41
-
42
- # Configure server with lifespan
43
- mcp = FastMCP(
44
- "MindsDB",
45
- lifespan=app_lifespan,
46
- dependencies=["mindsdb"], # Add any additional dependencies
47
- )
48
- # MCP Queries
49
- LISTING_QUERY = "SHOW DATABASES"
50
-
51
-
52
- query_tool_description = dedent("""\
53
- Executes a SQL query against MindsDB.
54
-
55
- A database must be specified either in the `context` parameter or directly in the query string (e.g., `SELECT * FROM my_database.my_table`). Queries like `SELECT * FROM my_table` will fail without a `context`.
56
-
57
- Args:
58
- query (str): The SQL query to execute.
59
- context (dict, optional): The default database context. For example, `{"db": "my_postgres"}`.
60
-
61
- Returns:
62
- A dictionary describing the result.
63
- - For a successful query with no data to return (e.g., an `UPDATE` statement), the response is `{"type": "ok"}`.
64
- - If the query returns tabular data, the response is a dictionary containing `data` (a list of rows) and `column_names` (a list of column names). For example: `{"type": "table", "data": [[1, "a"], [2, "b"]], "column_names": ["column_a", "column_b"]}`.
65
- - In case of an error, a response is `{"type": "error", "error_message": "the error message"}`.
66
- """)
67
-
68
-
69
- @mcp.tool(name="query", description=query_tool_description)
70
- def query(query: str, context: dict | None = None) -> dict[str, Any]:
71
- """Execute a SQL query against MindsDB
72
-
73
- Args:
74
- query: The SQL query to execute
75
- context: Optional context parameters for the query
76
-
77
- Returns:
78
- Dict containing the query results or error information
79
- """
80
-
81
- if context is None:
82
- context = {}
83
-
84
- logger.debug(f"Incoming MCP query: {query}")
85
-
86
- mysql_proxy = FakeMysqlProxy()
87
- mysql_proxy.set_context(context)
88
-
89
- try:
90
- result = mysql_proxy.process_query(query)
91
-
92
- if result.type == SQL_RESPONSE_TYPE.OK:
93
- return {"type": SQL_RESPONSE_TYPE.OK}
94
-
95
- if result.type == SQL_RESPONSE_TYPE.TABLE:
96
- return {
97
- "type": SQL_RESPONSE_TYPE.TABLE,
98
- "data": result.result_set.to_lists(json_types=True),
99
- "column_names": [column.alias or column.name for column in result.result_set.columns],
100
- }
101
- else:
102
- return {"type": SQL_RESPONSE_TYPE.ERROR, "error_code": 0, "error_message": "Unknown response type"}
103
-
104
- except Exception as e:
105
- logger.error(f"Error processing query: {str(e)}")
106
- return {"type": SQL_RESPONSE_TYPE.ERROR, "error_code": 0, "error_message": str(e)}
107
-
108
-
109
- list_databases_tool_description = (
110
- "Returns a list of all database connections currently available in MindsDB. "
111
- + "The tool takes no parameters and responds with a list of database names, "
112
- + 'for example: ["my_postgres", "my_mysql", "test_db"].'
113
- )
114
-
115
-
116
- @mcp.tool(name="list_databases", description=list_databases_tool_description)
117
- def list_databases() -> list[str]:
118
- """
119
- List all databases in MindsDB
120
-
121
- Returns:
122
- list[str]: list of databases
123
- """
124
-
125
- mysql_proxy = FakeMysqlProxy()
126
-
127
- try:
128
- result = mysql_proxy.process_query(LISTING_QUERY)
129
- if result.type == SQL_RESPONSE_TYPE.ERROR:
130
- return {
131
- "type": "error",
132
- "error_code": result.error_code,
133
- "error_message": result.error_message,
134
- }
135
-
136
- elif result.type == SQL_RESPONSE_TYPE.OK:
137
- return {"type": "ok"}
138
-
139
- elif result.type == SQL_RESPONSE_TYPE.TABLE:
140
- data = result.result_set.to_lists(json_types=True)
141
- data = [val[0] for val in data]
142
- return data
143
-
144
- except Exception as e:
145
- return {
146
- "type": "error",
147
- "error_code": 0,
148
- "error_message": str(e),
149
- }
150
-
151
-
152
- class CustomAuthMiddleware(BaseHTTPMiddleware):
153
- """Custom middleware to handle authentication basing on header 'Authorization'"""
154
-
155
- async def dispatch(self, request: Request, call_next):
156
- mcp_access_token = os.environ.get("MINDSDB_MCP_ACCESS_TOKEN")
157
- if mcp_access_token is not None:
158
- auth_token = request.headers.get("Authorization", "").partition("Bearer ")[-1]
159
- if mcp_access_token != auth_token:
160
- return Response(status_code=401, content="Unauthorized", media_type="text/plain")
161
-
162
- response = await call_next(request)
163
-
164
- return response
165
-
166
-
167
- async def run_sse_async() -> None:
168
- """Run the server using SSE transport."""
169
- starlette_app = mcp.sse_app()
170
- starlette_app.add_middleware(CustomAuthMiddleware)
171
-
172
- config = uvicorn.Config(
173
- starlette_app,
174
- host=mcp.settings.host,
175
- port=mcp.settings.port,
176
- log_level=mcp.settings.log_level.lower(),
177
- log_config=get_uvicorn_logging_config("uvicorn_mcp"),
178
- )
179
- server = uvicorn.Server(config)
180
- await server.serve()
181
-
182
-
183
- def start(*args, **kwargs):
184
- """Start the MCP server
185
- Args:
186
- host (str): Host to bind to
187
- port (int): Port to listen on
188
- """
189
- config = Config()
190
- port = int(config["api"].get("mcp", {}).get("port", 47337))
191
- host = config["api"].get("mcp", {}).get("host", "127.0.0.1")
192
-
193
- logger.info(f"Starting MCP server on {host}:{port}")
194
- mcp.settings.host = host
195
- mcp.settings.port = port
196
-
197
- try:
198
- anyio.run(run_sse_async)
199
- except Exception as e:
200
- logger.error(f"Error starting MCP server: {str(e)}")
201
- raise
202
-
203
-
204
- if __name__ == "__main__":
205
- start()
File without changes
@@ -1,5 +0,0 @@
1
- from .responder_collection import RespondersCollection
2
- from .responder import Responder
3
- from .session import Session
4
-
5
- __all__ = ['RespondersCollection', 'Responder', 'Session']
@@ -1,19 +0,0 @@
1
- from mindsdb.api.executor.controllers import SessionController
2
- from mindsdb.api.executor.command_executor import ExecuteCommands
3
- from mindsdb.utilities.config import config
4
-
5
-
6
- def run_sql_command(request_env, ast_query):
7
- sql_session = SessionController()
8
- sql_session.database = request_env.get('database', config.get('default_project'))
9
-
10
- command_executor = ExecuteCommands(sql_session)
11
- ret = command_executor.execute_command(ast_query)
12
- if ret.error_code is not None:
13
- raise Exception(ret.error_message)
14
-
15
- if ret.data is None:
16
- # return no data
17
- return []
18
-
19
- return list(ret.data.get_records())
@@ -1,45 +0,0 @@
1
- class Responder():
2
- def __init__(self, when=None, result=None):
3
- if when is not None:
4
- self.when = when
5
- if result is not None:
6
- self.result = result
7
- if not hasattr(self, 'when') or (not isinstance(self.when, dict) and not callable(self.when)):
8
- raise ValueError("Responder attr 'when' must be dict or function.")
9
- if not hasattr(self, 'result') or (not isinstance(self.result, dict) and not callable(self.result)):
10
- raise ValueError("Responder attr 'result' must be dict or function.")
11
-
12
- def match(self, query):
13
- """ check, if this 'responder' can be used to answer or current request
14
-
15
- query (dict): request document
16
-
17
- return bool
18
- """
19
- if isinstance(self.when, dict):
20
- for key, value in self.when.items():
21
- if key not in query:
22
- return False
23
- if callable(value):
24
- if not value(query[key]):
25
- return False
26
- elif value != query[key]:
27
- return False
28
- return True
29
- else:
30
- return self.when(query)
31
-
32
- def handle(self, query, args, env, session):
33
- """ making answer based on params:
34
-
35
- query (dict): document(s) from request
36
- args (dict): all other significant information from request: flags, collection name, rows to return, etc
37
- env (dict): config, model_controller instance, and other mindsdb related stuff
38
- session (object): current session
39
-
40
- returns documents as dict or list of dicts
41
- """
42
- if isinstance(self.result, dict):
43
- return self.result
44
- else:
45
- return self.result(query, args, env, session)
@@ -1,34 +0,0 @@
1
- from .responder import Responder
2
- from mindsdb.utilities import log
3
-
4
- logger = log.getLogger(__name__)
5
-
6
-
7
- class RespondersCollection():
8
- def __init__(self):
9
- self.responders = []
10
-
11
- def find_match(self, query):
12
- for r in self.responders:
13
- if r.match(query):
14
- return r
15
-
16
- msg = f'Is not responder for query: {query}'
17
-
18
- class ErrorResponder(Responder):
19
- when = {}
20
-
21
- result = {
22
- "ok": 0.0,
23
- "errmsg": msg,
24
- "code": 59,
25
- "codeName": "CommandNotFound"
26
- }
27
-
28
- logger.error(msg)
29
- return ErrorResponder()
30
-
31
- def add(self, when, result):
32
- self.responders.append(
33
- Responder(when, result)
34
- )
@@ -1,86 +0,0 @@
1
- # https://stackoverflow.com/questions/29298346/xmpp-sasl-scram-sha1-authentication
2
- # https://tools.ietf.org/html/rfc5802
3
- # https://tools.ietf.org/html/rfc7677
4
-
5
- import base64
6
- import hashlib
7
- import hmac
8
- import os
9
-
10
- from pymongo.auth import _password_digest, _xor, saslprep
11
-
12
-
13
- class Scram():
14
- ''' implementation of server-side SCRAM-SHA-1 and SCRAM-SHA-256 auth for mongodb
15
- '''
16
-
17
- def __init__(self, method='sha1', get_salted_password=None):
18
- self.get_salted_password = get_salted_password
19
- self.snonce = base64.b64encode(os.urandom(24)).decode()
20
- self.iterations = 4096
21
- self.messages = []
22
-
23
- if method == 'sha1':
24
- self.method_str = 'sha1'
25
- self.method_func = hashlib.sha1
26
- self.salt = base64.b64encode(os.urandom(16))
27
- elif method == 'sha256':
28
- self.method_str = 'sha256'
29
- self.method_func = hashlib.sha256
30
- self.salt = base64.b64encode(os.urandom(28))
31
-
32
- def process_client_first_message(self, payload):
33
- payload = payload[3:]
34
- payload_parts = self._split_payload(payload)
35
- self.client_user = payload_parts['n']
36
-
37
- if self.get_salted_password is not None:
38
- salt_bytes, self.salted_password = self.get_salted_password(self.client_user, self.method_str)
39
- self.salt = base64.b64encode(salt_bytes)
40
- else:
41
- self.salted_password = self.salt_password()
42
-
43
- self.unonce = payload_parts['r']
44
- self.messages.append(payload)
45
-
46
- responce_msg = f"r={self.unonce}{self.snonce},s={self.salt.decode()},i={self.iterations}"
47
- self.messages.append(responce_msg)
48
- return responce_msg
49
-
50
- def process_client_second_message(self, payload):
51
- self.messages.append(payload[:payload.rfind(',p=')]) # without 'p' part
52
-
53
- messages = ','.join(self.messages)
54
- server_key = self._hmac(self.salted_password, b'Server Key')
55
- server_signature = self._hmac(server_key, messages.encode('utf-8'))
56
-
57
- client_key = self._hmac(self.salted_password, b'Client Key')
58
- stored_key = self.method_func(client_key).digest()
59
- client_signature = self._hmac(stored_key, messages.encode('utf-8'))
60
- expected_client_proof = base64.b64encode(_xor(client_key, client_signature)).decode()
61
-
62
- income_client_proof = payload[payload.rfind(',p=') + 3:]
63
-
64
- if expected_client_proof != income_client_proof:
65
- raise Exception('wrong password')
66
-
67
- return f'v={base64.b64encode(server_signature).decode()}'
68
-
69
- def _hmac(self, key, msg):
70
- return hmac.new(key, msg, digestmod=self.method_func).digest()
71
-
72
- def salt_password(self, user, password):
73
- if self.method_str == 'sha1':
74
- password = _password_digest(user, password).encode("utf-8")
75
- elif self.method_str == 'sha256':
76
- password = saslprep(password).encode("utf-8")
77
-
78
- return hashlib.pbkdf2_hmac(
79
- self.method_str, password, base64.b64decode(self.salt), self.iterations
80
- )
81
-
82
- def _split_payload(self, payload):
83
- parts = {}
84
- for part in [x for x in payload.split(',') if len(x) > 0]:
85
- parts[part[0]] = part[2:]
86
- return parts
@@ -1,23 +0,0 @@
1
- import base64
2
-
3
- from mindsdb.api.mongo.classes.scram import Scram
4
-
5
-
6
- class Session():
7
- def __init__(self, server_mindsdb_env):
8
- self.config = server_mindsdb_env['config']
9
- self.mindsdb_env = {'company_id': None}
10
- self.mindsdb_env.update(server_mindsdb_env)
11
-
12
- def init_scram(self, method):
13
- self.scram = Scram(method=method, get_salted_password=self.get_salted_password)
14
-
15
- def get_salted_password(self, username, method=None):
16
- real_user = self.config['auth'].get('username', '')
17
- password = self.config['auth'].get('password', '')
18
- if username != real_user:
19
- raise Exception(f'Wrong username {username}')
20
-
21
- salted_password = self.scram.salt_password(real_user, password)
22
-
23
- return base64.b64decode(self.scram.salt), salted_password
@@ -1,19 +0,0 @@
1
- import bson
2
-
3
-
4
- def is_true(val):
5
- return bool(val) is True
6
-
7
-
8
- def is_false(val):
9
- return bool(val) is False
10
-
11
-
12
- def int_to_objectid(n):
13
- s = str(n)
14
- s = '0' * (24 - len(s)) + s
15
- return bson.ObjectId(s)
16
-
17
-
18
- def objectid_to_int(obj):
19
- return int(str(obj))
@@ -1,73 +0,0 @@
1
- from .whatsmyuri import responder as responder_whatsmyuri
2
- from .buildinfo import responder as responder_buildinfo
3
- from .is_master import responder as responder_is_master
4
- from .is_master_lower import responder as responder_is_master_lower
5
- from .replsetgetstatus import responder as responder_replsetgetstatus
6
- from .getlog import responder as responder_getlog
7
- from .add_shard import responder as responder_add_shard
8
- from .update_range_deletions import responder as responder_update_range_deletions
9
- from .recv_chunk_start import responder as responder_recv_chunk_start
10
- from .connection_status import responder as responder_connection_status
11
- from .get_cmd_line_opts import responder as responder_get_cmd_line_opts
12
- from .host_info import responder as responder_host_info
13
- from .db_stats import responder as responder_db_stats
14
- from .coll_stats import responder as responder_coll_stats
15
- from .count import responder as responder_count
16
- from .aggregate import responder as responder_aggregate
17
- from .get_free_monitoring_status import responder as responder_get_free_monitoring_status
18
- from .end_sessions import responder as responder_end_sessions
19
- from .ping import responder as responder_ping
20
- from .get_parameter import responder as responder_get_parameter
21
-
22
- from .list_indexes import responder as responder_list_indexes
23
- from .list_collections import responder as responder_list_collections
24
- from .list_databases import responder as responder_list_databases
25
-
26
- from .find import responder as responder_find
27
- from .insert import responder as responder_insert
28
- from .delete import responder as responder_delete
29
- from .describe import responder as responder_describe
30
-
31
- from .sasl_start import responder as sasl_start
32
- from .sasl_continue import responder as sasl_continue
33
-
34
- from .company_id import responder as responder_company_id
35
-
36
-
37
- responders = [
38
- # service queries
39
- responder_whatsmyuri,
40
- responder_buildinfo,
41
- responder_is_master,
42
- responder_is_master_lower,
43
- responder_replsetgetstatus,
44
- responder_getlog,
45
- responder_add_shard, # 4.4
46
- responder_update_range_deletions, # 4.4
47
- responder_recv_chunk_start, # 4.4
48
- responder_connection_status,
49
- responder_get_cmd_line_opts,
50
- responder_host_info,
51
- responder_db_stats,
52
- responder_coll_stats,
53
- responder_count,
54
- responder_aggregate,
55
- responder_get_free_monitoring_status,
56
- responder_end_sessions,
57
- responder_ping,
58
- responder_get_parameter,
59
-
60
- # user queries
61
- responder_list_indexes,
62
- responder_list_collections,
63
- responder_list_databases,
64
- responder_find,
65
- responder_insert,
66
- responder_delete,
67
- responder_describe,
68
- # auth
69
- sasl_start,
70
- sasl_continue,
71
- # cloud
72
- responder_company_id
73
- ]
@@ -1,13 +0,0 @@
1
- from mindsdb.api.mongo.classes import Responder
2
- import mindsdb.api.mongo.functions as helpers
3
-
4
-
5
- class Responce(Responder):
6
- when = {'_addShard': helpers.is_true}
7
-
8
- result = {
9
- "ok": 1
10
- }
11
-
12
-
13
- responder = Responce()