Simple and rapid application development framework, built on top of Flask, with detailed security, auto CRUD generation, and comprehensive UI components.
—
Quality
Pending
Does it follow best practices?
Impact
Pending
No eval scenarios have been run
Authentication, authorization, and security decorators providing comprehensive user management, role-based permissions, and access control for both traditional views and REST APIs. The security system supports multiple authentication backends and fine-grained permission control.
Decorators that enforce security permissions and access control for view methods and API endpoints.
from flask_appbuilder.security.decorators import (
has_access, has_access_api, protect, permission_name, limit, no_cache
)
@has_access
def has_access(f):
"""
Enable granular security permissions for view methods.
Redirects to login page on access denied.
Usage:
@expose('/admin/')
@has_access
def admin_view(self):
return self.render_template('admin.html')
"""
@has_access_api
def has_access_api(f):
"""
Enable security permissions for API methods.
Returns HTTP 401/403 on access denied instead of redirect.
Usage:
@expose('/api/data/')
@has_access_api
def get_data(self):
return self.response(200, data={"result": "success"})
"""
@protect(allow_browser_login=False)
def protect(allow_browser_login=False):
"""
Enable security permissions for API methods with JWT support.
Parameters:
- allow_browser_login: Allow Flask-Login session cookies
Usage:
@expose('/api/secure/')
@protect()
def secure_api(self):
return {"message": "JWT authenticated"}
@expose('/api/browser-ok/')
@protect(allow_browser_login=True)
def browser_api(self):
return {"message": "JWT or session authenticated"}
"""
@permission_name("custom_permission")
def permission_name(name):
"""
Override the permission name for a method.
Useful for aggregating methods under single permission.
Parameters:
- name: Custom permission name
Usage:
@expose('/reports/sales/')
@has_access
@permission_name("can_access_reports")
def sales_report(self):
return self.render_template('sales.html')
@expose('/reports/inventory/')
@has_access
@permission_name("can_access_reports")
def inventory_report(self):
return self.render_template('inventory.html')
"""
@limit("100/hour", key_func=lambda: request.remote_addr)
def limit(limit_value, key_func=None, per_method=False, methods=None,
error_message=None, exempt_when=None, override_defaults=True,
deduct_when=None, on_breach=None, cost=1):
"""
Rate limiting decorator for individual routes.
Parameters:
- limit_value: Rate limit string (e.g., "100/hour", "10/minute")
- key_func: Function to generate rate limit key
- per_method: Apply limit per HTTP method
- methods: HTTP methods to apply limit to
- error_message: Custom error message
- exempt_when: Function to determine exemption
- override_defaults: Override default rate limits
- deduct_when: Function to determine when to deduct
- on_breach: Callback on rate limit breach
- cost: Cost per request (default 1)
Usage:
@expose('/api/expensive/')
@limit("10/minute")
@has_access_api
def expensive_operation(self):
return {"result": "expensive computation"}
"""
@no_cache
def no_cache(view):
"""
Add no-cache headers to response.
Usage:
@expose('/sensitive-data/')
@has_access
@no_cache
def sensitive_data(self):
return self.render_template('sensitive.html')
"""Constants defining supported authentication types and methods.
from flask_appbuilder.const import (
AUTH_OID, AUTH_DB, AUTH_LDAP, AUTH_REMOTE_USER, AUTH_OAUTH
)
# Authentication type constants
AUTH_OID = 0 # OpenID authentication
AUTH_DB = 1 # Database authentication
AUTH_LDAP = 2 # LDAP authentication
AUTH_REMOTE_USER = 3 # Remote user authentication (headers)
AUTH_OAUTH = 4 # OAuth authentication
# Usage in configuration
AUTH_TYPE = AUTH_DB # Use database authentication
AUTH_USER_REGISTRATION = True # Allow user self-registration
AUTH_USER_REGISTRATION_ROLE = "Public" # Default role for new users
# LDAP configuration example
AUTH_TYPE = AUTH_LDAP
AUTH_LDAP_SERVER = "ldap://ldap.server.com"
AUTH_LDAP_BIND_USER = "cn=admin,dc=example,dc=com"
AUTH_LDAP_BIND_PASSWORD = "password"
AUTH_LDAP_SEARCH = "ou=people,dc=example,dc=com"
AUTH_LDAP_UID_FIELD = "uid"
# OAuth configuration example
AUTH_TYPE = AUTH_OAUTH
OAUTH_PROVIDERS = [
{
'name': 'google',
'token_key': 'access_token',
'icon': 'fa-google',
'remote_app': {
'client_id': 'your-google-client-id',
'client_secret': 'your-google-client-secret',
'server_metadata_url': 'https://accounts.google.com/.well-known/openid_configuration',
'client_kwargs': {'scope': 'openid email profile'}
}
}
]The security manager handles user authentication, authorization, and permission management.
# Accessing security manager from AppBuilder
from flask_appbuilder import AppBuilder
appbuilder = AppBuilder(app, db.session)
security_manager = appbuilder.sm
# Common security manager methods
def get_user_by_id(user_id):
"""Get user by ID."""
def auth_user_db(username, password):
"""Authenticate user with database credentials."""
def auth_user_ldap(username, password):
"""Authenticate user with LDAP credentials."""
def auth_user_oauth(userinfo):
"""Authenticate user with OAuth provider info."""
def add_role(name):
"""Add new role to system."""
def add_permission(name):
"""Add new permission to system."""
def add_permissions_view(permissions_list, view_menu_name):
"""Add permissions for a view."""
def add_permissions_menu(permission_name):
"""Add menu permission."""
def security_cleanup():
"""Remove unused permissions."""
def get_user_menu_access(menu_names=None):
"""Get menu items user has access to."""
# User model properties (when using AUTH_DB)
class User(Model):
id = Column(Integer, primary_key=True)
username = Column(String(64), unique=True, nullable=False)
first_name = Column(String(64), nullable=False)
last_name = Column(String(64), nullable=False)
email = Column(String(120), unique=True, nullable=False)
password = Column(String(256))
active = Column(Boolean, default=True)
created_on = Column(DateTime, default=datetime.datetime.now)
changed_on = Column(DateTime, default=datetime.datetime.now)Flask-AppBuilder uses a comprehensive permission system based on roles and permissions.
# Permission naming convention
# Format: "can_<method_name>" on "<ViewClassName>"
# Examples of auto-generated permissions:
# - "can_list" on "PersonModelView"
# - "can_show" on "PersonModelView"
# - "can_add" on "PersonModelView"
# - "can_edit" on "PersonModelView"
# - "can_delete" on "PersonModelView"
# Custom permissions can be defined:
from flask_appbuilder.security.decorators import permission_name
class MyView(BaseView):
@expose('/custom/')
@has_access
@permission_name("can_access_custom")
def custom_method(self):
return "Custom functionality"
# Role management
class Role(Model):
id = Column(Integer, primary_key=True)
name = Column(String(64), unique=True, nullable=False)
permissions = relationship("Permission", secondary="ab_permission_view_role")
# Permission model
class Permission(Model):
id = Column(Integer, primary_key=True)
name = Column(String(100), unique=True, nullable=False)
# View menu model
class ViewMenu(Model):
id = Column(Integer, primary_key=True)
name = Column(String(250), unique=True, nullable=False)
# Permission view role association
class PermissionViewRole(Model):
id = Column(Integer, primary_key=True)
permission_view_id = Column(Integer, ForeignKey('ab_permission_view.id'))
role_id = Column(Integer, ForeignKey('ab_role.id'))Support for JSON Web Token authentication for API access.
# JWT configuration
JWT_SECRET_KEY = 'your-jwt-secret-key' # Required for JWT
JWT_ACCESS_TOKEN_EXPIRES = datetime.timedelta(hours=1)
JWT_REFRESH_TOKEN_EXPIRES = datetime.timedelta(days=30)
# JWT authentication endpoints (automatically available)
# POST /api/v1/security/login - Get JWT tokens
# POST /api/v1/security/refresh - Refresh JWT token
# Using JWT in API requests
# Authorization: Bearer <jwt-token>
# Example API client usage
import requests
# Login to get tokens
login_response = requests.post('/api/v1/security/login', json={
'username': 'admin',
'password': 'password'
})
tokens = login_response.json()
# Use access token for API calls
headers = {'Authorization': f"Bearer {tokens['access_token']}"}
api_response = requests.get('/api/v1/myapi/', headers=headers)
# Refresh token when expired
refresh_response = requests.post('/api/v1/security/refresh', json={
'refresh_token': tokens['refresh_token']
})
new_tokens = refresh_response.json()Key configuration settings for customizing the security system behavior.
# Authentication configuration
AUTH_TYPE = AUTH_DB # Authentication method
AUTH_USER_REGISTRATION = False # Allow user self-registration
AUTH_USER_REGISTRATION_ROLE = "Public" # Default role for new users
AUTH_ROLES_SYNC_AT_LOGIN = True # Sync roles at each login (LDAP/OAuth)
# Password policy
AUTH_PASSWORD_COMPLEXITY_ENABLED = True
AUTH_PASSWORD_COMPLEXITY_VALIDATOR = r"^(?=.*[a-z])(?=.*[A-Z])(?=.*\d).{8,}$"
# Session configuration
PERMANENT_SESSION_LIFETIME = datetime.timedelta(hours=1)
SESSION_COOKIE_HTTPONLY = True
SESSION_COOKIE_SECURE = True # HTTPS only
SESSION_COOKIE_SAMESITE = 'Lax'
# CSRF protection
WTF_CSRF_ENABLED = True
WTF_CSRF_TIME_LIMIT = 3600 # 1 hour
# Rate limiting
RATELIMIT_ENABLED = True
RATELIMIT_STORAGE_URL = "redis://localhost:6379"
# OAuth specific settings
AUTH_OAUTH_USER_INFO_KEY_USERNAME = "email" # Map OAuth field to username
AUTH_OAUTH_USER_INFO_KEY_EMAIL = "email"
AUTH_OAUTH_USER_INFO_KEY_FIRST_NAME = "given_name"
AUTH_OAUTH_USER_INFO_KEY_LAST_NAME = "family_name"
# API security
FAB_API_SWAGGER_UI = True # Enable Swagger UI
FAB_API_SHOW_STACKTRACE = False # Hide stacktraces in productionInstall with Tessl CLI
npx tessl i tessl/pypi-flask-appbuilder