Repository: mlflow/mlflow

Issue:
[BUG]  basic-auth alembic migration crash on remote database
### Issues Policy acknowledgement

- [X] I have read and agree to submit bug reports in accordance with the [issues policy](https://www.github.com/mlflow/mlflow/blob/master/ISSUE_POLICY.md)

### Willingness to contribute

Yes. I can contribute a fix for this bug independently.

### MLflow version

- Client: 1.5.0
- Tracking server: 1.5.0


### System information

- **Debian GNU/Linux 10 (buster)**:
- **Python 3.8.17 **:

- **SQLAlchemy version: 2.0.19**

### Describe the problem

Alembic migration for the basic-auth app crashes due to obfuscated password being passed to the alembic config

This bug seems to be related to the SQLAlchemy version, migration works with SQLAlchemy 1.4.19 but not with 2.0.19, seems like they change the behaviour of the str representation of url's.

### Tracking information

<!-- PLEASE KEEP BACKTICKS AND CHECK PREVIEW -->
```shell
REPLACE_ME
```


### Code to reproduce issue

<!-- PLEASE KEEP BACKTICKS AND CHECK PREVIEW -->
```
python -m mlflow.server.auth db upgrade --url 'postgresql://database_user:database_password@host:5432/database_name'
```


### Stack trace

<!-- PLEASE KEEP BACKTICKS AND CHECK PREVIEW -->
```
Traceback (most recent call last):
  File "/home/turuania/mlflow_test/env/lib/python3.8/site-packages/sqlalchemy/engine/base.py", line 145, in __init__
    self._dbapi_connection = engine.raw_connection()
  File "/home/turuania/mlflow_test/env/lib/python3.8/site-packages/sqlalchemy/engine/base.py", line 3288, in raw_connection
    return self.pool.connect()
  File "/home/turuania/mlflow_test/env/lib/python3.8/site-packages/sqlalchemy/pool/base.py", line 452, in connect
    return _ConnectionFairy._checkout(self)
  File "/home/turuania/mlflow_test/env/lib/python3.8/site-packages/sqlalchemy/pool/base.py", line 1267, in _checkout
    fairy = _ConnectionRecord.checkout(pool)
  File "/home/turuania/mlflow_test/env/lib/python3.8/site-packages/sqlalchemy/pool/base.py", line 716, in checkout
    rec = pool._do_get()
  File "/home/turuania/mlflow_test/env/lib/python3.8/site-packages/sqlalchemy/pool/impl.py", line 284, in _do_get
    return self._create_connection()
  File "/home/turuania/mlflow_test/env/lib/python3.8/site-packages/sqlalchemy/pool/base.py", line 393, in _create_connection
    return _ConnectionRecord(self)
  File "/home/turuania/mlflow_test/env/lib/python3.8/site-packages/sqlalchemy/pool/base.py", line 678, in __init__
    self.__connect()
  File "/home/turuania/mlflow_test/env/lib/python3.8/site-packages/sqlalchemy/pool/base.py", line 903, in __connect
    pool.logger.debug("Error on connect(): %s", e)
  File "/home/turuania/mlflow_test/env/lib/python3.8/site-packages/sqlalchemy/util/langhelpers.py", line 147, in __exit__
    raise exc_value.with_traceback(exc_tb)
  File "/home/turuania/mlflow_test/env/lib/python3.8/site-packages/sqlalchemy/pool/base.py", line 898, in __connect
    self.dbapi_connection = connection = pool._invoke_creator(self)
  File "/home/turuania/mlflow_test/env/lib/python3.8/site-packages/sqlalchemy/engine/create.py", line 637, in connect
    return dialect.connect(*cargs, **cparams)
  File "/home/turuania/mlflow_test/env/lib/python3.8/site-packages/sqlalchemy/engine/default.py", line 615, in connect
    return self.loaded_dbapi.connect(*cargs, **cparams)
  File "/home/turuania/mlflow_test/env/lib/python3.8/site-packages/psycopg2/__init__.py", line 122, in connect
    conn = _connect(dsn, connection_factory=connection_factory, **kwasync)
sqlalchemy.exc.OperationalError: (psycopg2.OperationalError) connection to server at "host" (host ip), port 5432 failed: FATAL:  password authentication failed for user "db_user"
connection to server at "host" (host_ip), port 5432 failed: FATAL:  password authentication failed for user "db_user"


The above exception was the direct cause of the following exception:

Traceback (most recent call last):
  File "/usr/local/lib/python3.8/runpy.py", line 194, in _run_module_as_main
    return _run_code(code, main_globals, None,
  File "/usr/local/lib/python3.8/runpy.py", line 87, in _run_code
    exec(code, run_globals)
  File "/home/turuania/mlflow_test/env/lib/python3.8/site-packages/mlflow/server/auth/__main__.py", line 5, in <module>
    commands()
  File "/home/turuania/mlflow_test/env/lib/python3.8/site-packages/click/core.py", line 1157, in __call__
    return self.main(*args, **kwargs)
  File "/home/turuania/mlflow_test/env/lib/python3.8/site-packages/click/core.py", line 1078, in main
    rv = self.invoke(ctx)
  File "/home/turuania/mlflow_test/env/lib/python3.8/site-packages/click/core.py", line 1688, in invoke
    return _process_result(sub_ctx.command.invoke(sub_ctx))
  File "/home/turuania/mlflow_test/env/lib/python3.8/site-packages/click/core.py", line 1688, in invoke
    return _process_result(sub_ctx.command.invoke(sub_ctx))
  File "/home/turuania/mlflow_test/env/lib/python3.8/site-packages/click/core.py", line 1434, in invoke
    return ctx.invoke(self.callback, **ctx.params)
  File "/home/turuania/mlflow_test/env/lib/python3.8/site-packages/click/core.py", line 783, in invoke
    return __callback(*args, **kwargs)
  File "/home/turuania/mlflow_test/env/lib/python3.8/site-packages/mlflow/server/auth/db/cli.py", line 19, in upgrade
    utils.migrate(engine, revision)
  File "/home/turuania/mlflow_test/env/lib/python3.8/site-packages/mlflow/server/auth/db/utils.py", line 28, in migrate
    upgrade(alembic_cfg, revision)
  File "/home/turuania/mlflow_test/env/lib/python3.8/site-packages/alembic/command.py", line 385, in upgrade
    script.run_env()
  File "/home/turuania/mlflow_test/env/lib/python3.8/site-packages/alembic/script/base.py", line 578, in run_env
    util.load_python_file(self.dir, "env.py")
  File "/home/turuania/mlflow_test/env/lib/python3.8/site-packages/alembic/util/pyfiles.py", line 93, in load_python_file
    module = load_module_py(module_id, path)
  File "/home/turuania/mlflow_test/env/lib/python3.8/site-packages/alembic/util/pyfiles.py", line 109, in load_module_py
    spec.loader.exec_module(module)  # type: ignore
  File "<frozen importlib._bootstrap_external>", line 843, in exec_module
  File "<frozen importlib._bootstrap>", line 219, in _call_with_frames_removed
  File "/home/turuania/mlflow_test/env/lib/python3.8/site-packages/mlflow/server/auth/db/migrations/env.py", line 79, in <module>
    run_migrations_online()
  File "/home/turuania/mlflow_test/env/lib/python3.8/site-packages/mlflow/server/auth/db/migrations/env.py", line 69, in run_migrations_online
    with connectable.connect() as connection:
  File "/home/turuania/mlflow_test/env/lib/python3.8/site-packages/sqlalchemy/engine/base.py", line 3264, in connect
    return self._connection_cls(self)
  File "/home/turuania/mlflow_test/env/lib/python3.8/site-packages/sqlalchemy/engine/base.py", line 147, in __init__
    Connection._handle_dbapi_exception_noconnection(
  File "/home/turuania/mlflow_test/env/lib/python3.8/site-packages/sqlalchemy/engine/base.py", line 2426, in _handle_dbapi_exception_noconnection
    raise sqlalchemy_exception.with_traceback(exc_info[2]) from e
  File "/home/turuania/mlflow_test/env/lib/python3.8/site-packages/sqlalchemy/engine/base.py", line 145, in __init__
    self._dbapi_connection = engine.raw_connection()
  File "/home/turuania/mlflow_test/env/lib/python3.8/site-packages/sqlalchemy/engine/base.py", line 3288, in raw_connection
    return self.pool.connect()
  File "/home/turuania/mlflow_test/env/lib/python3.8/site-packages/sqlalchemy/pool/base.py", line 452, in connect
    return _ConnectionFairy._checkout(self)
  File "/home/turuania/mlflow_test/env/lib/python3.8/site-packages/sqlalchemy/pool/base.py", line 1267, in _checkout
    fairy = _ConnectionRecord.checkout(pool)
  File "/home/turuania/mlflow_test/env/lib/python3.8/site-packages/sqlalchemy/pool/base.py", line 716, in checkout
    rec = pool._do_get()
  File "/home/turuania/mlflow_test/env/lib/python3.8/site-packages/sqlalchemy/pool/impl.py", line 284, in _do_get
    return self._create_connection()
  File "/home/turuania/mlflow_test/env/lib/python3.8/site-packages/sqlalchemy/pool/base.py", line 393, in _create_connection
    return _ConnectionRecord(self)
  File "/home/turuania/mlflow_test/env/lib/python3.8/site-packages/sqlalchemy/pool/base.py", line 678, in __init__
    self.__connect()
  File "/home/turuania/mlflow_test/env/lib/python3.8/site-packages/sqlalchemy/pool/base.py", line 903, in __connect
    pool.logger.debug("Error on connect(): %s", e)
  File "/home/turuania/mlflow_test/env/lib/python3.8/site-packages/sqlalchemy/util/langhelpers.py", line 147, in __exit__
    raise exc_value.with_traceback(exc_tb)
  File "/home/turuania/mlflow_test/env/lib/python3.8/site-packages/sqlalchemy/pool/base.py", line 898, in __connect
    self.dbapi_connection = connection = pool._invoke_creator(self)
  File "/home/turuania/mlflow_test/env/lib/python3.8/site-packages/sqlalchemy/engine/create.py", line 637, in connect
    return dialect.connect(*cargs, **cparams)
  File "/home/turuania/mlflow_test/env/lib/python3.8/site-packages/sqlalchemy/engine/default.py", line 615, in connect
    return self.loaded_dbapi.connect(*cargs, **cparams)
  File "/home/turuania/mlflow_test/env/lib/python3.8/site-packages/psycopg2/__init__.py", line 122, in connect
    conn = _connect(dsn, connection_factory=connection_factory, **kwasync)
sqlalchemy.exc.OperationalError: (psycopg2.OperationalError) connection to server at "host" (host ip), port 5432 failed: FATAL:  password authentication failed for user "db_user"
connection to server at "host" (host_ip), port 5432 failed: FATAL:  password authentication failed for user "db_user"

(Background on this error at: https://sqlalche.me/e/20/e3q8)
```


### Other info / logs

<!-- PLEASE KEEP BACKTICKS AND CHECK PREVIEW -->
```
REPLACE_ME
```


### What component(s) does this bug affect?

- [ ] `area/artifacts`: Artifact stores and artifact logging
- [ ] `area/build`: Build and test infrastructure for MLflow
- [ ] `area/docs`: MLflow documentation pages
- [ ] `area/examples`: Example code
- [ ] `area/gateway`: AI Gateway service, Gateway client APIs, third-party Gateway integrations
- [ ] `area/model-registry`: Model Registry service, APIs, and the fluent client calls for Model Registry
- [ ] `area/models`: MLmodel format, model serialization/deserialization, flavors
- [ ] `area/recipes`: Recipes, Recipe APIs, Recipe configs, Recipe Templates
- [ ] `area/projects`: MLproject format, project running backends
- [ ] `area/scoring`: MLflow Model server, model deployment tools, Spark UDFs
- [X] `area/server-infra`: MLflow Tracking server backend
- [ ] `area/tracking`: Tracking Service, tracking client APIs, autologging

### What interface(s) does this bug affect?

- [ ] `area/uiux`: Front-end, user experience, plotting, JavaScript, JavaScript dev server
- [ ] `area/docker`: Docker use across MLflow's components, such as MLflow Projects and MLflow Models
- [X] `area/sqlalchemy`: Use of SQLAlchemy in the Tracking Service or Model Registry
- [ ] `area/windows`: Windows support

### What language(s) does this bug affect?

- [ ] `language/r`: R APIs and clients
- [ ] `language/java`: Java APIs and clients
- [ ] `language/new`: Proposals for new client languages

### What integration(s) does this bug affect?

- [ ] `integrations/azure`: Azure and Azure ML integrations
- [ ] `integrations/sagemaker`: SageMaker integrations
- [ ] `integrations/databricks`: Databricks integrations


Discussion:
Could be fixed by replacing `alembic_cfg = _get_alembic_config(str(engine.url))` by `alembic_cfg = _get_alembic_config(engine.url.render_as_string(hide_password=False))` in mlflow/server/auth/db/utils.py
Hi @aloahPGF thank you for reporting this issue. If you're still interested in filing a PR to fix this configuration issue, we will happily review it. 
Is this configuration change compatible with older versions of SQLAlchemy or does version validation logic need to be introduced?
Hello, From what I tested It is compatible with 1.4.19 for sure, didn't test on older ones, don't really know up to which version I should check

Resolving patch:
diff --git a/mlflow/server/auth/db/utils.py b/mlflow/server/auth/db/utils.py
--- a/mlflow/server/auth/db/utils.py
+++ b/mlflow/server/auth/db/utils.py
@@ -21,7 +21,7 @@ def _get_alembic_config(url: str) -> Config:
 
 
 def migrate(engine: Engine, revision: str) -> None:
-    alembic_cfg = _get_alembic_config(str(engine.url))
+    alembic_cfg = _get_alembic_config(engine.url.render_as_string(hide_password=False))
     with engine.begin() as conn:
         alembic_cfg.attributes["connection"] = conn
         upgrade(alembic_cfg, revision)


Test patch: