Copilot commented on code in PR #64436:
URL: https://github.com/apache/airflow/pull/64436#discussion_r3025332530
##########
airflow-core/docs/installation/upgrading_to_airflow3.rst:
##########
@@ -225,6 +225,65 @@ TaskInstances, Variables, Connections, XComs, and more.
.. note::
If you need functionality that is not available via the Airflow Python
Client, consider requesting new API endpoints or Task SDK features. The Airflow
community prioritizes adding missing API capabilities over enabling direct
database access.
+Recommended Approach: Use Built-in Local REST Client (In-Process)
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+For code running inside trusted Airflow processes that have direct access to
+the metadata database — the scheduler, DAG processor, triggerer, or plugins —
+Airflow provides a built-in local REST client that calls the Core REST API
+in-process with no network overhead and zero credential configuration.
+
+.. code-block:: python
+
+ from airflow.api.client import get_local_rest_client
+
+ client = get_local_rest_client()
+
+ # Pools
+ client.pools.create(name="my_pool", slots=5)
+ pools = client.pools.list()
+
+ # DAGs
+ dags = client.dags.list()
+ client.dags.pause("my_dag")
+
+ # Connections
+ client.connections.create(conn_id="my_conn", conn_type="http",
host="example.com")
Review Comment:
Docs example uses `client.connections.create(conn_id=...)`, but the
LocalRESTClient Connections sub-client expects `connection_id` (matching the
Core API field name). Update the snippet to avoid a copy/paste runtime error
for users following the migration guide.
```suggestion
client.connections.create(connection_id="my_conn", conn_type="http",
host="example.com")
```
##########
airflow-core/src/airflow/api/client/local_rest_client.py:
##########
@@ -0,0 +1,189 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+"""
+Local REST API client for trusted Airflow processes.
+
+Provides a zero-configuration way for code running inside trusted Airflow
+processes (scheduler, DAG processor, triggerer, plugins) to call Core API
+endpoints without needing user credentials. The calling process must have
+access to the Airflow metadata database.
+
+Example usage::
+
+ from airflow.api.client import get_local_rest_client
+
+ client = get_local_rest_client()
+ client.pools.create(name="my_pool", slots=5)
+ pools = client.pools.list()
+"""
+
+from __future__ import annotations
+
+import json
+import logging
+import os
+from functools import cached_property
+
+import httpx
+
+from airflow import settings
+from airflow.api.client.in_process_core_api import InProcessCoreAPI
+from airflow.api.client.resources.assets import AssetsClient
+from airflow.api.client.resources.config import ConfigClient
+from airflow.api.client.resources.connections import ConnectionsClient
+from airflow.api.client.resources.dag_runs import DagRunsClient
+from airflow.api.client.resources.dags import DagsClient
+from airflow.api.client.resources.pools import PoolsClient
+from airflow.api.client.resources.task_instances import TaskInstancesClient
+from airflow.api.client.resources.variables import VariablesClient
+from airflow.configuration import AIRFLOW_HOME, conf
+
+_log = logging.getLogger(__name__)
+
+
+class LocalRESTClient:
+ """
+ REST API client for use inside trusted Airflow processes.
+
+ Uses an in-process ASGI transport to call the Core API without network
+ overhead, with authentication bypassed via a ``SystemUser`` override.
+
+ :param process_type: Identifier for the calling process, used in audit
logs.
+ Defaults to ``"unknown"``.
+ """
+
+ def __init__(self, *, process_type: str = "unknown") -> None:
+ self._process_type = process_type
+ self._in_process_api = InProcessCoreAPI(process_type=process_type)
+
+ @cached_property
+ def _http(self) -> httpx.Client:
+ if self._is_orm_access_blocked():
+ base_url = self._get_http_fallback_base_url()
+ token = self._get_http_bearer_token(base_url=base_url)
+ if not token:
+ raise RuntimeError(
+ "LocalRESTClient cannot use in-process Core API because
ORM access is blocked in this "
+ "process, and it could not obtain an auth token for HTTP
fallback."
+ )
+ return httpx.Client(
+ base_url=base_url,
+ headers={"Authorization": f"Bearer {token}"},
Review Comment:
LocalRESTClient’s HTTP fallback attempts to auto-acquire a Core API token
(including by reading SimpleAuthManager credentials from disk) when ORM access
is blocked. This makes it possible for code running in task/worker contexts
(where DB access is intentionally blocked) to still obtain a privileged token
and call Core API endpoints, which undermines the Airflow 3 security boundary.
Consider removing this implicit fallback (raise with clear guidance), or
require an explicit opt-in (e.g., `mode="http"` + caller-provided token) and
ensure task contexts cannot leverage admin credentials.
```suggestion
# In Airflow 3, contexts where ORM access is blocked (e.g.
task/worker
# processes and Task SDK execution-time contexts) must not
escalate to
# privileged Core API access via LocalRESTClient.
# Instead, such code should use the public Execution API client
# (e.g. airflow.sdk or an HTTP client configured with a user
token).
raise RuntimeError(
"LocalRESTClient cannot be used from contexts where ORM
access is blocked "
"(for example, task or worker processes). Use the public
Execution API "
"client (airflow.sdk) or an explicitly configured HTTP
client instead."
```
##########
airflow-core/src/airflow/api/client/in_process_core_api.py:
##########
@@ -0,0 +1,266 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+"""In-process Core API helper for trusted Airflow processes."""
+
+from __future__ import annotations
+
+import logging
+from contextlib import AsyncExitStack
+from functools import cached_property
+from typing import TYPE_CHECKING, Any
+
+import attrs
+from fastapi import FastAPI, Request
+from fastapi.responses import JSONResponse
+
+from airflow.api_fastapi.auth.managers.models.system_user import SystemUser
+
+if TYPE_CHECKING:
+ import httpx
+
+ from airflow.api_fastapi.auth.managers.models.base_user import BaseUser
+
+_log = logging.getLogger(__name__)
+
+
+class _SystemAccessAuthManagerProxy:
+ """
+ Auth manager proxy that grants full access for all authorization checks.
+
+ Used by :class:`InProcessCoreAPI` to bypass authorization for trusted
system
+ processes while still delegating non-auth methods (e.g. ``init``,
``get_url_login``)
+ to the real auth manager.
+ """
+
+ def __init__(self, real_manager: Any) -> None:
+ object.__setattr__(self, "_real", real_manager)
+
+ def __getattr__(self, name: str) -> Any:
+ return getattr(self._real, name)
+
+ # -- All is_authorized_* methods return True unconditionally --
+
+ def is_authorized_configuration(self, **kwargs: Any) -> bool:
+ return True
+
+ def is_authorized_connection(self, **kwargs: Any) -> bool:
+ return True
+
+ def is_authorized_dag(self, **kwargs: Any) -> bool:
+ return True
+
+ def is_authorized_asset(self, **kwargs: Any) -> bool:
+ return True
+
+ def is_authorized_asset_alias(self, **kwargs: Any) -> bool:
+ return True
+
+ def is_authorized_pool(self, **kwargs: Any) -> bool:
+ return True
+
+ def is_authorized_team(self, **kwargs: Any) -> bool:
+ return True
+
+ def is_authorized_variable(self, **kwargs: Any) -> bool:
+ return True
+
+ def is_authorized_view(self, **kwargs: Any) -> bool:
+ return True
+
+ def is_authorized_custom_view(self, **kwargs: Any) -> bool:
+ return True
+
+ def is_authorized_hitl_task(self, **kwargs: Any) -> bool:
+ return True
+
+ # -- Batch methods --
+
+ def batch_is_authorized_connection(self, *args: Any, **kwargs: Any) ->
bool:
+ return True
+
+ def batch_is_authorized_dag(self, *args: Any, **kwargs: Any) -> bool:
+ return True
+
+ def batch_is_authorized_pool(self, *args: Any, **kwargs: Any) -> bool:
+ return True
+
+ def batch_is_authorized_variable(self, *args: Any, **kwargs: Any) -> bool:
+ return True
+
+ # -- Filter / get_authorized methods must return full sets to avoid
empty-set filtering --
+
+ def filter_authorized_menu_items(self, menu_items: list, **kwargs: Any) ->
list:
+ return menu_items
+
+ def filter_authorized_pools(self, *, pool_names: set[str], **kwargs: Any)
-> set[str]:
+ return pool_names
+
+ def filter_authorized_connections(self, *, conn_ids: set[str], **kwargs:
Any) -> set[str]:
+ return conn_ids
+
+ def filter_authorized_variables(self, *, variable_keys: set[str],
**kwargs: Any) -> set[str]:
+ return variable_keys
+
+ def filter_authorized_dags(self, *, dag_ids: set[str], **kwargs: Any) ->
set[str]:
+ return dag_ids
+
+ def get_authorized_pools(self, **kwargs: Any) -> set[str]:
+ from sqlalchemy import select
+
+ from airflow.models.pool import Pool
+
+ session = kwargs.get("session")
+ if session is None:
+ from airflow.utils.session import create_session
+
+ with create_session() as session:
+ return {row[0] for row in
session.execute(select(Pool.pool)).all()}
+ return {row[0] for row in session.execute(select(Pool.pool)).all()}
+
+ def get_authorized_dag_ids(self, **kwargs: Any) -> set[str]:
+ from sqlalchemy import select
+
+ from airflow.models.dag import DagModel
+
+ session = kwargs.get("session")
+ if session is None:
+ from airflow.utils.session import create_session
+
+ with create_session() as session:
+ return {row[0] for row in
session.execute(select(DagModel.dag_id)).all()}
+ return {row[0] for row in
session.execute(select(DagModel.dag_id)).all()}
+
+ def get_authorized_connections(self, **kwargs: Any) -> set[str]:
+ from sqlalchemy import select
+
+ from airflow.models import Connection
+
+ session = kwargs.get("session")
+ if session is None:
+ from airflow.utils.session import create_session
+
+ with create_session() as session:
+ return {row[0] for row in
session.execute(select(Connection.conn_id)).all()}
+ return {row[0] for row in
session.execute(select(Connection.conn_id)).all()}
+
+ def get_authorized_variables(self, **kwargs: Any) -> set[str]:
+ from sqlalchemy import select
+
+ from airflow.models.variable import Variable
+
+ session = kwargs.get("session")
+ if session is None:
+ from airflow.utils.session import create_session
+
+ with create_session() as session:
+ return {row[0] for row in
session.execute(select(Variable.key)).all()}
+ return {row[0] for row in session.execute(select(Variable.key)).all()}
+
+
[email protected]()
+class InProcessCoreAPI:
+ """
+ Run the Core API in-process with auth bypassed for system calls.
+
+ Follows the same pattern as ``InProcessExecutionAPI`` in the execution API,
+ but targets the Core API (``/api/v2/``) endpoints.
+
+ The sync version uses a2wsgi to run the async FastAPI app in a separate
thread,
+ allowing use with the sync ``httpx.Client``.
+ """
+
+ _app: FastAPI | None = None
+ _cm: AsyncExitStack | None = None
+ _process_type: str = "unknown"
+
+ @cached_property
+ def app(self) -> FastAPI:
+ if not self._app:
+ # Lazy imports to avoid circular dependencies and heavy imports at
module level
+ from airflow.api_fastapi.app import _AuthManagerState,
init_auth_manager
+ from airflow.api_fastapi.common.dagbag import create_dag_bag
+ from airflow.api_fastapi.core_api.app import init_config,
init_error_handlers, init_views
+ from airflow.api_fastapi.core_api.security import
auth_manager_from_app, get_user
+
+ self._app = FastAPI(title="Airflow Core API (in-process)")
+ self._app.state.dag_bag = create_dag_bag()
+ real_am = init_auth_manager(self._app)
+ init_views(self._app)
+ init_error_handlers(self._app)
+ init_config(self._app)
+
+ # Wrap the real auth manager with a proxy that grants full access.
+ # This is needed because requires_access_* dependencies call
+ # get_auth_manager() (the global singleton) internally.
+ proxy = _SystemAccessAuthManagerProxy(real_am)
+ _AuthManagerState.instance = proxy
+ self._app.state.auth_manager = proxy
+
Review Comment:
InProcessCoreAPI mutates the global auth-manager singleton
(`_AuthManagerState.instance = proxy`). This can inadvertently bypass
authorization checks for the entire process (e.g., if used inside the API
server or webserver process, subsequent requests may see the permissive proxy).
Prefer avoiding global state mutation: either add proper `SystemUser` handling
to auth managers / `get_user()` so the real manager can remain in place, or
refactor `requires_access_*` to use the per-app `auth_manager_from_app`
dependency instead of `get_auth_manager()`.
##########
airflow-core/src/airflow/api/client/in_process_core_api.py:
##########
@@ -0,0 +1,266 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+"""In-process Core API helper for trusted Airflow processes."""
+
+from __future__ import annotations
+
+import logging
+from contextlib import AsyncExitStack
+from functools import cached_property
+from typing import TYPE_CHECKING, Any
+
+import attrs
+from fastapi import FastAPI, Request
+from fastapi.responses import JSONResponse
+
+from airflow.api_fastapi.auth.managers.models.system_user import SystemUser
+
+if TYPE_CHECKING:
+ import httpx
+
+ from airflow.api_fastapi.auth.managers.models.base_user import BaseUser
+
+_log = logging.getLogger(__name__)
+
+
+class _SystemAccessAuthManagerProxy:
+ """
+ Auth manager proxy that grants full access for all authorization checks.
+
+ Used by :class:`InProcessCoreAPI` to bypass authorization for trusted
system
+ processes while still delegating non-auth methods (e.g. ``init``,
``get_url_login``)
+ to the real auth manager.
+ """
+
+ def __init__(self, real_manager: Any) -> None:
+ object.__setattr__(self, "_real", real_manager)
+
+ def __getattr__(self, name: str) -> Any:
+ return getattr(self._real, name)
+
+ # -- All is_authorized_* methods return True unconditionally --
+
+ def is_authorized_configuration(self, **kwargs: Any) -> bool:
+ return True
+
+ def is_authorized_connection(self, **kwargs: Any) -> bool:
+ return True
+
+ def is_authorized_dag(self, **kwargs: Any) -> bool:
+ return True
+
+ def is_authorized_asset(self, **kwargs: Any) -> bool:
+ return True
+
+ def is_authorized_asset_alias(self, **kwargs: Any) -> bool:
+ return True
+
+ def is_authorized_pool(self, **kwargs: Any) -> bool:
+ return True
+
+ def is_authorized_team(self, **kwargs: Any) -> bool:
+ return True
+
+ def is_authorized_variable(self, **kwargs: Any) -> bool:
+ return True
+
+ def is_authorized_view(self, **kwargs: Any) -> bool:
+ return True
+
+ def is_authorized_custom_view(self, **kwargs: Any) -> bool:
+ return True
+
+ def is_authorized_hitl_task(self, **kwargs: Any) -> bool:
+ return True
+
+ # -- Batch methods --
+
+ def batch_is_authorized_connection(self, *args: Any, **kwargs: Any) ->
bool:
+ return True
+
+ def batch_is_authorized_dag(self, *args: Any, **kwargs: Any) -> bool:
+ return True
+
+ def batch_is_authorized_pool(self, *args: Any, **kwargs: Any) -> bool:
+ return True
+
+ def batch_is_authorized_variable(self, *args: Any, **kwargs: Any) -> bool:
+ return True
+
+ # -- Filter / get_authorized methods must return full sets to avoid
empty-set filtering --
+
+ def filter_authorized_menu_items(self, menu_items: list, **kwargs: Any) ->
list:
+ return menu_items
+
+ def filter_authorized_pools(self, *, pool_names: set[str], **kwargs: Any)
-> set[str]:
+ return pool_names
+
+ def filter_authorized_connections(self, *, conn_ids: set[str], **kwargs:
Any) -> set[str]:
+ return conn_ids
+
+ def filter_authorized_variables(self, *, variable_keys: set[str],
**kwargs: Any) -> set[str]:
+ return variable_keys
+
+ def filter_authorized_dags(self, *, dag_ids: set[str], **kwargs: Any) ->
set[str]:
+ return dag_ids
+
+ def get_authorized_pools(self, **kwargs: Any) -> set[str]:
+ from sqlalchemy import select
+
+ from airflow.models.pool import Pool
+
+ session = kwargs.get("session")
+ if session is None:
+ from airflow.utils.session import create_session
+
+ with create_session() as session:
+ return {row[0] for row in
session.execute(select(Pool.pool)).all()}
+ return {row[0] for row in session.execute(select(Pool.pool)).all()}
Review Comment:
Several functions in `_SystemAccessAuthManagerProxy` perform imports inside
the method body (e.g. `from sqlalchemy import select`, `from
airflow.models...`). Airflow’s codebase guidelines generally keep imports at
module scope; if these imports are needed for circular-dependency avoidance,
please add an explicit comment explaining why. Otherwise, move them to the top
of the module to make dependencies explicit and avoid repeated import-path
overhead.
##########
airflow-core/src/airflow/api/client/in_process_core_api.py:
##########
@@ -0,0 +1,266 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+"""In-process Core API helper for trusted Airflow processes."""
+
+from __future__ import annotations
+
+import logging
+from contextlib import AsyncExitStack
+from functools import cached_property
+from typing import TYPE_CHECKING, Any
+
+import attrs
+from fastapi import FastAPI, Request
+from fastapi.responses import JSONResponse
+
+from airflow.api_fastapi.auth.managers.models.system_user import SystemUser
+
+if TYPE_CHECKING:
+ import httpx
+
+ from airflow.api_fastapi.auth.managers.models.base_user import BaseUser
+
+_log = logging.getLogger(__name__)
+
+
+class _SystemAccessAuthManagerProxy:
+ """
+ Auth manager proxy that grants full access for all authorization checks.
+
+ Used by :class:`InProcessCoreAPI` to bypass authorization for trusted
system
+ processes while still delegating non-auth methods (e.g. ``init``,
``get_url_login``)
+ to the real auth manager.
+ """
+
+ def __init__(self, real_manager: Any) -> None:
+ object.__setattr__(self, "_real", real_manager)
+
+ def __getattr__(self, name: str) -> Any:
+ return getattr(self._real, name)
+
+ # -- All is_authorized_* methods return True unconditionally --
+
+ def is_authorized_configuration(self, **kwargs: Any) -> bool:
+ return True
+
+ def is_authorized_connection(self, **kwargs: Any) -> bool:
+ return True
+
+ def is_authorized_dag(self, **kwargs: Any) -> bool:
+ return True
+
+ def is_authorized_asset(self, **kwargs: Any) -> bool:
+ return True
+
+ def is_authorized_asset_alias(self, **kwargs: Any) -> bool:
+ return True
+
+ def is_authorized_pool(self, **kwargs: Any) -> bool:
+ return True
+
+ def is_authorized_team(self, **kwargs: Any) -> bool:
+ return True
+
+ def is_authorized_variable(self, **kwargs: Any) -> bool:
+ return True
+
+ def is_authorized_view(self, **kwargs: Any) -> bool:
+ return True
+
+ def is_authorized_custom_view(self, **kwargs: Any) -> bool:
+ return True
+
+ def is_authorized_hitl_task(self, **kwargs: Any) -> bool:
+ return True
+
+ # -- Batch methods --
+
+ def batch_is_authorized_connection(self, *args: Any, **kwargs: Any) ->
bool:
+ return True
+
+ def batch_is_authorized_dag(self, *args: Any, **kwargs: Any) -> bool:
+ return True
+
+ def batch_is_authorized_pool(self, *args: Any, **kwargs: Any) -> bool:
+ return True
+
+ def batch_is_authorized_variable(self, *args: Any, **kwargs: Any) -> bool:
+ return True
+
+ # -- Filter / get_authorized methods must return full sets to avoid
empty-set filtering --
+
+ def filter_authorized_menu_items(self, menu_items: list, **kwargs: Any) ->
list:
+ return menu_items
+
+ def filter_authorized_pools(self, *, pool_names: set[str], **kwargs: Any)
-> set[str]:
+ return pool_names
+
+ def filter_authorized_connections(self, *, conn_ids: set[str], **kwargs:
Any) -> set[str]:
+ return conn_ids
+
+ def filter_authorized_variables(self, *, variable_keys: set[str],
**kwargs: Any) -> set[str]:
+ return variable_keys
+
+ def filter_authorized_dags(self, *, dag_ids: set[str], **kwargs: Any) ->
set[str]:
+ return dag_ids
+
+ def get_authorized_pools(self, **kwargs: Any) -> set[str]:
+ from sqlalchemy import select
+
+ from airflow.models.pool import Pool
+
+ session = kwargs.get("session")
+ if session is None:
+ from airflow.utils.session import create_session
+
+ with create_session() as session:
+ return {row[0] for row in
session.execute(select(Pool.pool)).all()}
+ return {row[0] for row in session.execute(select(Pool.pool)).all()}
+
+ def get_authorized_dag_ids(self, **kwargs: Any) -> set[str]:
+ from sqlalchemy import select
+
+ from airflow.models.dag import DagModel
+
+ session = kwargs.get("session")
+ if session is None:
+ from airflow.utils.session import create_session
+
+ with create_session() as session:
+ return {row[0] for row in
session.execute(select(DagModel.dag_id)).all()}
+ return {row[0] for row in
session.execute(select(DagModel.dag_id)).all()}
+
+ def get_authorized_connections(self, **kwargs: Any) -> set[str]:
+ from sqlalchemy import select
+
+ from airflow.models import Connection
+
+ session = kwargs.get("session")
+ if session is None:
+ from airflow.utils.session import create_session
+
+ with create_session() as session:
+ return {row[0] for row in
session.execute(select(Connection.conn_id)).all()}
+ return {row[0] for row in
session.execute(select(Connection.conn_id)).all()}
+
Review Comment:
`get_authorized_connections()` (and similar methods) currently loads *all*
IDs from the metadata DB to satisfy the permission-filter dependencies. On
large installations this can be very expensive and adds an extra full-table
query to many requests. For system/process access, it would be better to bypass
these permitted-ID filters entirely (e.g., make the permitted-filter deps no-op
for `SystemUser`/system access) rather than enumerating every
connection/variable/pool key.
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: [email protected]
For queries about this service, please contact Infrastructure at:
[email protected]