This is an automated email from the ASF dual-hosted git repository.
vincbeck 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 d6db11e763 Extract the part that depends on FAB from the standalone
command (#33676)
d6db11e763 is described below
commit d6db11e7634953579ca4737b20aa405f65869834
Author: Raphaƫl Vandon <[email protected]>
AuthorDate: Tue Sep 26 06:18:04 2023 -0700
Extract the part that depends on FAB from the standalone command (#33676)
---
.../auth/managers/fab/security_manager/override.py | 39 ++++++++++++++++++++++
airflow/cli/commands/standalone_command.py | 36 +++-----------------
airflow/www/security_manager.py | 7 ++++
3 files changed, 51 insertions(+), 31 deletions(-)
diff --git a/airflow/auth/managers/fab/security_manager/override.py
b/airflow/auth/managers/fab/security_manager/override.py
index 4b884c303a..2e5bf313d9 100644
--- a/airflow/auth/managers/fab/security_manager/override.py
+++ b/airflow/auth/managers/fab/security_manager/override.py
@@ -20,6 +20,8 @@ from __future__ import annotations
import base64
import json
import logging
+import os
+import random
import uuid
import warnings
from functools import cached_property
@@ -332,6 +334,43 @@ class
FabAirflowSecurityManagerOverride(AirflowSecurityManagerV2):
"""Returns FAB builtin roles."""
return self.appbuilder.app.config.get("FAB_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")
+ role = self.find_role("Admin")
+ assert role is not None
+ # 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.
diff --git a/airflow/cli/commands/standalone_command.py
b/airflow/cli/commands/standalone_command.py
index 7821d1d8c1..1eca350f58 100644
--- a/airflow/cli/commands/standalone_command.py
+++ b/airflow/cli/commands/standalone_command.py
@@ -18,7 +18,6 @@ from __future__ import annotations
import logging
import os
-import random
import socket
import subprocess
import threading
@@ -28,7 +27,7 @@ from typing import TYPE_CHECKING
from termcolor import colored
-from airflow.configuration import AIRFLOW_HOME, conf,
make_group_other_inaccessible
+from airflow.configuration import conf
from airflow.executors import executor_constants
from airflow.executors.executor_loader import ExecutorLoader
from airflow.jobs.job import most_recent_job
@@ -179,39 +178,14 @@ class StandaloneCommand:
self.print_output("standalone", "Checking database is initialized")
db.initdb()
self.print_output("standalone", "Database ready")
- # See if a user needs creating
- # 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.
+
+ # Then create a "default" admin user if necessary
from airflow.auth.managers.fab.cli_commands.utils import
get_application_builder
with get_application_builder() as appbuilder:
- user_exists = appbuilder.sm.find_user("admin")
- password_path = os.path.join(AIRFLOW_HOME,
"standalone_admin_password.txt")
- 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:
- self.print_output("standalone", "Creating admin user")
- role = appbuilder.sm.find_role("Admin")
- assert role is not None
- # 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)
- appbuilder.sm.add_user("admin", "Admin", "User",
"[email protected]", role, password)
- self.print_output("standalone", "Created admin 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
+ user_name, password = appbuilder.sm.create_admin_standalone()
# Store what we know about the user for printing later in startup
- self.user_info = {"username": "admin", "password": password}
+ self.user_info = {"username": user_name, "password": password}
def is_ready(self):
"""
diff --git a/airflow/www/security_manager.py b/airflow/www/security_manager.py
index 0797260f86..87f845bb4f 100644
--- a/airflow/www/security_manager.py
+++ b/airflow/www/security_manager.py
@@ -603,6 +603,13 @@ class AirflowSecurityManagerV2(SecurityManager,
LoggingMixin):
session.commit()
+ def create_admin_standalone(self) -> tuple[str | None, str | None]:
+ """Perform the required steps when initializing airflow for standalone
mode.
+
+ If necessary, returns the username and password to be printed in the
console for users to log in.
+ """
+ return None, None
+
def sync_roles(self) -> None:
"""
Initialize default and custom roles with related permissions.