This is an automated email from the ASF dual-hosted git repository.
jedcunningham pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/airflow.git
The following commit(s) were added to refs/heads/main by this push:
new 1428890c2b3 Use SimpleAuthManager for standalone (#48036)
1428890c2b3 is described below
commit 1428890c2b3fa5b03add4f17498ec4273af4b0fa
Author: Jed Cunningham <[email protected]>
AuthorDate: Fri Mar 21 09:29:02 2025 -0600
Use SimpleAuthManager for standalone (#48036)
This fixes the standalone command.
At the same time, it switches standalone to use SimpleAuthManager.
Similar to before, if it's the first time you run, the username and
password will be printed out - now just at the very top of the output
(which honestly is a bit easier to see before a ton of lines start
flying by). However, it will only output the password once. On
subsequent starts, it will print the file that contains the file so you
can go grab the password yourself if you need it. This was done since
there isn't an easy way to both use the same password with standalone and
api-server directly and make sure we aren't outputting a password you've
populated yourself.
---
.../commands/local_commands/standalone_command.py | 51 +++++++++++++++-------
.../fab/auth_manager/security_manager/override.py | 39 -----------------
2 files changed, 36 insertions(+), 54 deletions(-)
diff --git
a/airflow-core/src/airflow/cli/commands/local_commands/standalone_command.py
b/airflow-core/src/airflow/cli/commands/local_commands/standalone_command.py
index 7188411969c..3e89b8f14fe 100644
--- a/airflow-core/src/airflow/cli/commands/local_commands/standalone_command.py
+++ b/airflow-core/src/airflow/cli/commands/local_commands/standalone_command.py
@@ -27,6 +27,7 @@ from typing import TYPE_CHECKING
from termcolor import colored
+from airflow.api_fastapi.app import create_auth_manager
from airflow.configuration import conf
from airflow.executors import executor_constants
from airflow.executors.executor_loader import ExecutorLoader
@@ -58,7 +59,6 @@ class StandaloneCommand:
def __init__(self):
self.subcommands = {}
self.output_queue = deque()
- self.user_info = {}
self.ready_time = None
self.ready_delay = 3
@@ -69,6 +69,7 @@ class StandaloneCommand:
logging.getLogger("").setLevel(logging.WARNING)
# Startup checks and prep
env = self.calculate_env()
+ self.find_user_info()
self.initialize_database()
# Set up commands to run
self.subcommands["scheduler"] = SubCommand(
@@ -179,8 +180,42 @@ class StandaloneCommand:
else:
self.print_output("standalone", "Forcing executor to
LocalExecutor")
env["AIRFLOW__CORE__EXECUTOR"] =
executor_constants.LOCAL_EXECUTOR
+
+ # Make sure we're using SimpleAuthManager
+ simple_auth_manager_classpath = (
+
"airflow.api_fastapi.auth.managers.simple.simple_auth_manager.SimpleAuthManager"
+ )
+ if conf.get("core", "auth_manager") != simple_auth_manager_classpath:
+ self.print_output("standalone", "Forcing auth manager to
SimpleAuthManager")
+ env["AIRFLOW__CORE__AUTH_MANAGER"] = simple_auth_manager_classpath
+ os.environ["AIRFLOW__CORE__AUTH_MANAGER"] =
simple_auth_manager_classpath # also in this process!
+
return env
+ def find_user_info(self):
+ if conf.get("core", "simple_auth_manager_all_admins").lower() ==
"true":
+ # If we have no auth anyways, no need to print or do anything
+ return
+ if conf.get("core", "simple_auth_manager_users") != "admin:admin":
+ self.print_output(
+ "standalone",
+ "Not outputting user passwords - `[core]
simple_auth_manager_users` is already set.",
+ )
+ return
+
+ am = create_auth_manager()
+
+ password_file = am.get_generated_password_file()
+ if os.path.exists(password_file):
+ self.print_output(
+ "standalone",
+ f"Password for the admin user has been previously generated in
{password_file}. Not echoing it here.",
+ )
+ return
+
+ # this generates the password and prints it
+ am.init()
+
def initialize_database(self):
"""Make sure all the tables are created."""
# Set up DB tables
@@ -188,15 +223,6 @@ class StandaloneCommand:
db.initdb()
self.print_output("standalone", "Database ready")
- # Then create a "default" admin user if necessary
- from airflow.providers.fab.auth_manager.cli_commands.utils import
get_application_builder
-
- with get_application_builder() as appbuilder:
- if hasattr(appbuilder.sm, "create_admin_standalone"):
- user_name, password = appbuilder.sm.create_admin_standalone()
- # Store what we know about the user for printing later in startup
- self.user_info = {"username": user_name, "password": password}
-
def is_ready(self):
"""
Detect when all Airflow components are ready to serve.
@@ -244,11 +270,6 @@ class StandaloneCommand:
"""
self.print_output("standalone", "")
self.print_output("standalone", "Airflow is ready")
- if self.user_info["password"]:
- self.print_output(
- "standalone",
- f"Login with username: {self.user_info['username']} password:
{self.user_info['password']}",
- )
self.print_output(
"standalone",
"Airflow Standalone is for development purposes only. Do not use
this in production!",
diff --git
a/providers/fab/src/airflow/providers/fab/auth_manager/security_manager/override.py
b/providers/fab/src/airflow/providers/fab/auth_manager/security_manager/override.py
index 7a7f59e7323..48ea2d00614 100644
---
a/providers/fab/src/airflow/providers/fab/auth_manager/security_manager/override.py
+++
b/providers/fab/src/airflow/providers/fab/auth_manager/security_manager/override.py
@@ -21,8 +21,6 @@ import copy
import datetime
import itertools
import logging
-import os
-import random
import uuid
from collections.abc import Collection, Iterable, Mapping
from typing import TYPE_CHECKING, Any
@@ -740,43 +738,6 @@ class
FabAirflowSecurityManagerOverride(AirflowSecurityManagerV2):
"""Get the builtin roles."""
return self._builtin_roles
- def create_admin_standalone(self) -> tuple[str | None, str | None]:
- """Create an Admin user with a random password so that users can
access airflow."""
- from airflow.configuration import AIRFLOW_HOME,
make_group_other_inaccessible
-
- user_name = "admin"
-
- # We want a streamlined first-run experience, but we do not want to
- # use a preset password as people will inevitably run this on a public
- # server. Thus, we make a random password and store it in AIRFLOW_HOME,
- # with the reasoning that if you can read that directory, you can see
- # the database credentials anyway.
- password_path = os.path.join(AIRFLOW_HOME,
"standalone_admin_password.txt")
-
- user_exists = self.find_user(user_name) is not None
- we_know_password = os.path.isfile(password_path)
-
- # If the user does not exist, make a random password and make it
- if not user_exists:
- print(f"FlaskAppBuilder Authentication Manager: Creating
{user_name} user")
- if (role := self.find_role("Admin")) is None:
- raise AirflowException("Unable to find role 'Admin'")
- # password does not contain visually similar characters: ijlIJL1oO0
- password =
"".join(random.choices("abcdefghkmnpqrstuvwxyzABCDEFGHKMNPQRSTUVWXYZ23456789",
k=16))
- with open(password_path, "w") as file:
- file.write(password)
- make_group_other_inaccessible(password_path)
- self.add_user(user_name, "Admin", "User", "[email protected]",
role, password)
- print(f"FlaskAppBuilder Authentication Manager: Created
{user_name} user")
- # If the user does exist, and we know its password, read the password
- elif user_exists and we_know_password:
- with open(password_path) as file:
- password = file.read().strip()
- # Otherwise we don't know the password
- else:
- password = None
- return user_name, password
-
def _init_config(self):
"""
Initialize config.