Skip to content

Commit

Permalink
Extract the part that depends on FAB from the standalone command (apa…
Browse files Browse the repository at this point in the history
  • Loading branch information
vandonr-amz authored Sep 26, 2023
1 parent 2b57f39 commit d6db11e
Show file tree
Hide file tree
Showing 3 changed files with 51 additions and 31 deletions.
39 changes: 39 additions & 0 deletions airflow/auth/managers/fab/security_manager/override.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@
import base64
import json
import logging
import os
import random
import uuid
import warnings
from functools import cached_property
Expand Down Expand Up @@ -332,6 +334,43 @@ def create_builtin_roles(self):
"""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.
Expand Down
36 changes: 5 additions & 31 deletions airflow/cli/commands/standalone_command.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@

import logging
import os
import random
import socket
import subprocess
import threading
Expand All @@ -28,7 +27,7 @@

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
Expand Down Expand Up @@ -179,39 +178,14 @@ def initialize_database(self):
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):
"""
Expand Down
7 changes: 7 additions & 0 deletions airflow/www/security_manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -603,6 +603,13 @@ def update_admin_permission(self) -> None:

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.
Expand Down

0 comments on commit d6db11e

Please sign in to comment.