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.

Reply via email to