Skip to content

Connection Test in UI fails for team-scoped connections: get_connection_from_secrets ignores team_name and falls back to masked password "***" #70192

Description

@s-manzhak

Under which category would you file this issue?

Airflow Core

Apache Airflow version

3.2.2

What happened and how to reproduce it?

Issue Description

With multi_team = True, connections that have team_name set (e.g. Portal) cannot be tested from the UI.

Airflow 3.1.7+ includes a fix for masked passwords (***) in /connections/test (#59643): the API tries to load the existing connection via Connection.get_connection_from_secrets(conn_id) and then merges the form body with update_orm_from_pydantic, restoring the real password when the UI sends ***.

That lookup does not pass team_name. For team-scoped connections, get_connection_from_secrets raises AirflowNotFoundException even though the connection exists in the metastore. The API then falls back to constructing a connection from the request body only, which still contains password="***", so the test authenticates with the literal masked placeholder and fails.

This happens for both admin and team users. The stored credentials are correct: the same connection works with PostgresHook / DAG runtime when the team context is available.

Steps to reproduce:

  1. Enable multi-team mode ([core] multi_team = True) with at least one team (e.g. Portal) and a DAG bundle with team_name="Portal".
  2. Create a Postgres connection in the UI:
    • conn_id: recruitment_crm_db (any id)
    • valid host/login/password
    • assign team_name = Portal
  3. Confirm the row exists in the metadata DB with team_name='Portal'.
  4. Open the connection in the UI (as admin or as a Portal user) and click Test without re-entering the password (password field shows masked ***).
  5. Observe authentication failure (e.g. password authentication failed).

Repro evidence (same deployment):

from airflow.models.connection import Connection
from airflow.secrets.metastore import MetastoreBackend
from airflow.providers.postgres.hooks.postgres import PostgresHook
from airflow.settings import Session

c = Session().query(Connection).filter(Connection.conn_id == "recruitment_crm_db").one()
# team_name == "Portal", password decrypts correctly

PostgresHook(connection=c).test_connection()
# -> (True, "Connection successfully tested")

MetastoreBackend().get_connection("recruitment_crm_db")
# -> None

MetastoreBackend().get_connection("recruitment_crm_db", team_name="Portal")
# -> Connection(...)

Connection.get_connection_from_secrets("recruitment_crm_db")
# -> AirflowNotFoundException: The conn_id `recruitment_crm_db` isn't defined

### What you think should happen instead?

---

### What you think should happen instead?

```markdown
`/connections/test` should successfully test team-scoped connections when the caller is allowed to access that team.

Expected behavior:
1. Resolve the existing connection using the correct team context (or otherwise include team-scoped metastore rows), so `update_orm_from_pydantic` can restore the real password when the UI sends the masked placeholder `***`.
2. Alternatively, detect `password == "***"` (masked value) and never treat it as a real credential.
3. UI Test should succeed for a valid team-owned Postgres connection without requiring the user to re-type the password.

Current behavior is erroneous because:
- the connection exists and credentials are valid;
- DAG/hook usage works;
- only UI Test fails due to secrets lookup without `team_name`, then auth with literal `***`.

### Operating System

Debian GNU/Linux 12 (bookworm)  # inside official apache/airflow container

### Deployment

Docker-Compose

### Apache Airflow Provider(s)

_No response_

### Versions of Apache Airflow Providers

apache-airflow-providers-postgres==6.7.0
apache-airflow-providers-common-sql==2.0.0
apache-airflow-providers-common-compat==1.15.0
apache-airflow-providers-common-io==1.7.2
apache-airflow-providers-common-messaging==2.0.3

### Official Helm Chart version

Not Applicable

### Kubernetes Version

Not Applicable

### Helm Chart configuration

Not Applicable

### Docker Image customizations

Based on official image:

```dockerfile
FROM apache/airflow:3.2.2
USER root
COPY requirements.txt /requirements.txt
RUN chown -R airflow:root /requirements.txt
USER airflow
RUN pip install --no-cache-dir -r /requirements.txt

requirements.txt only adds DAG libs (jira, pymysql, python-gitlab) — not related to this bug. Relevant config:

[core]
multi_team = True
test_connection = Enabled
DAG bundle example:

[{"name":"company-dags","classpath":"airflow.dag_processing.bundles.local.LocalDagBundle","kwargs":{"path":"/opt/airflow/dags/company"},"team_name":"Company"}]

### Anything else?

### Anything else?
```markdown
Every time for any saved connection with non-null `team_name` when using UI Test without re-entering the password.

Workarounds:
1. Re-type the real password in the UI form before clicking Test.
2. Temporarily set `team_name = NULL` on the connection (loses team isolation).
3. Validate with PostgresHook / DAG instead of UI Test.

Related:
- #59298 / #59643 / #61670 — masked password fix (works for non-team connections)
- This report is specifically about the remaining gap when `multi_team=True` and connection.team_name is set.

### Are you willing to submit PR?

- [ ] Yes I am willing to submit a PR!

### Code of Conduct

- [x] I agree to follow this project's [Code of Conduct](https://github.com/apache/airflow/blob/main/CODE_OF_CONDUCT.md)

Metadata

Metadata

Assignees

No one assigned

    Labels

    area:APIAirflow's REST/HTTP APIarea:UIRelated to UI/UX. For Frontend Developers.kind:bugThis is a clearly a bugneeds-triagelabel for new issues that we didn't triage yet

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions