Flask-AppBuilder (FAB) security integration component within Apache Airflow core, providing authentication, authorization, and security management features
—
Quality
Pending
Does it follow best practices?
Impact
Pending
No eval scenarios have been run
SQLAlchemy models representing the security schema including users, roles, permissions, actions, resources, and their relationships. These models provide the data layer foundation for all security operations.
Represents an Airflow user with authentication information, profile data, and role assignments.
class User(Model):
"""
Represents an Airflow user which has roles assigned to it.
Attributes:
- id: Integer primary key
- first_name: String(64), user's first name
- last_name: String(64), user's last name
- username: String(256), unique username
- password: String(256), hashed password
- active: Boolean, whether user account is active
- email: String(256), unique email address
- last_login: DateTime, timestamp of last login
- login_count: Integer, total number of logins
- fail_login_count: Integer, number of failed login attempts
- roles: Relationship to Role objects
- created_on: DateTime, account creation timestamp
- changed_on: DateTime, last modification timestamp
- created_by_fk: Foreign key to User who created this account
- changed_by_fk: Foreign key to User who last modified this account
- created_by: Relationship to User (creator)
- changed_by: Relationship to User (modifier)
"""
@classmethod
def get_user_id(cls) -> int | None:
"""Get current user ID from Flask context."""
@property
def is_authenticated(self) -> bool:
"""Check if user is authenticated (always True for User objects)."""
@property
def is_active(self) -> bool:
"""Check if user account is active."""
@property
def is_anonymous(self) -> bool:
"""Check if user is anonymous (always False for User objects)."""
@property
def perms(self) -> set[tuple[str, str]]:
"""Get user's permissions as set of (action, resource) tuples."""
def get_id(self) -> int:
"""Get user ID for session management."""
def get_full_name(self) -> str:
"""Get formatted full name (first_name last_name)."""Represents a user role that can be assigned permissions and associated with users.
class Role(Model):
"""
Represents a user role to which permissions can be assigned.
Attributes:
- id: Integer primary key
- name: String(64), unique role name
- permissions: Relationship to Permission objects assigned to this role
"""Represents a permission pairing an action with a resource for granular access control.
class Permission(Model):
"""
Permission pair comprised of an Action + Resource combo.
Attributes:
- id: Integer primary key
- action_id: Foreign key to Action
- action: Relationship to Action object
- resource_id: Foreign key to Resource
- resource: Relationship to Resource object
"""Represents permission actions that define what operations can be performed.
class Action(Model):
"""
Represents permission actions such as 'can_read', 'can_edit', etc.
Attributes:
- id: Integer primary key
- name: String(100), unique action name
"""Represents permission objects/resources that can be protected by permissions.
class Resource(Model):
"""
Represents permission objects such as 'User', 'DAG', 'Connection', etc.
Attributes:
- id: Integer primary key
- name: String(250), unique resource name
"""
def __eq__(self, other) -> bool:
"""Check equality based on resource name."""
def __neq__(self, other) -> bool:
"""Check inequality based on resource name."""Represents a user registration request for user self-registration workflows.
class RegisterUser(Model):
"""
Represents a user registration request.
Attributes:
- id: Integer primary key
- first_name: String(64), requested first name
- last_name: String(64), requested last name
- username: String(256), requested unique username
- password: String(256), hashed password
- email: String(256), email address
- registration_date: DateTime, when registration was requested
- registration_hash: String(256), unique hash for registration verification
"""Many-to-many relationship tables connecting the core models.
assoc_permission_role = Table(
"ab_permission_view_role",
Model.metadata,
Column("id", Integer, primary_key=True),
Column("permission_view_id", Integer, ForeignKey("ab_permission_view.id")),
Column("role_id", Integer, ForeignKey("ab_role.id")),
UniqueConstraint("permission_view_id", "role_id")
)
"""Many-to-many relationship table between Permission and Role."""
assoc_user_role = Table(
"ab_user_role",
Model.metadata,
Column("id", Integer, primary_key=True),
Column("user_id", Integer, ForeignKey("ab_user.id")),
Column("role_id", Integer, ForeignKey("ab_role.id")),
UniqueConstraint("user_id", "role_id")
)
"""Many-to-many relationship table between User and Role."""The models map to specific database tables:
User → ab_userRole → ab_rolePermission → ab_permission_viewAction → ab_permissionResource → ab_view_menuRegisterUser → ab_register_userassoc_user_roleassoc_permission_rolefrom airflow.www.fab_security.sqla.models import User, Role
# Create user instance
user = User()
user.username = "john_doe"
user.email = "john@example.com"
user.first_name = "John"
user.last_name = "Doe"
user.active = True
# Check user properties
if user.is_active:
print(f"Active user: {user.get_full_name()}")
# Access user permissions
for action, resource in user.perms:
print(f"Permission: {action} on {resource}")from airflow.www.fab_security.sqla.models import Role, Permission, Action, Resource
# Create role
role = Role(name="DataAnalyst")
# Create permission components
action = Action(name="can_read")
resource = Resource(name="Reports")
permission = Permission(action=action, resource=resource)
# Assign permission to role
role.permissions.append(permission)
# Access role permissions
for perm in role.permissions:
print(f"Role {role.name} can {perm.action.name} on {perm.resource.name}")import uuid
from datetime import datetime
from airflow.www.fab_security.sqla.models import RegisterUser
# Create registration request
registration = RegisterUser()
registration.username = "new_user"
registration.first_name = "New"
registration.last_name = "User"
registration.email = "new@example.com"
registration.registration_date = datetime.now()
registration.registration_hash = str(uuid.uuid1())
print(f"Registration hash: {registration.registration_hash}")from sqlalchemy.orm import sessionmaker
from airflow.www.fab_security.sqla.models import User, Role
# Query users by role
admin_role = session.query(Role).filter_by(name="Admin").first()
admin_users = session.query(User).filter(User.roles.contains(admin_role)).all()
# Query permissions for user
user = session.query(User).filter_by(username="john_doe").first()
if user:
# Get all permissions through roles
all_permissions = []
for role in user.roles:
all_permissions.extend(role.permissions)User.username: Must be unique across all usersUser.email: Must be unique across all usersRole.name: Must be unique across all rolesAction.name: Must be unique across all actionsResource.name: Must be unique across all resourcesPermission(action_id, resource_id): Unique combination constraintPermission.action_id → Action.idPermission.resource_id → Resource.idUser.created_by_fk → User.id (self-reference)User.changed_by_fk → User.id (self-reference)All models inherit from Flask-AppBuilder's Model class and use Airflow's base metadata for table creation and management, ensuring compatibility with Airflow's database migration system.
Install with Tessl CLI
npx tessl i tessl/pypi-apache-airflow-fab-security