CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/pypi-flask-appbuilder

Simple and rapid application development framework, built on top of Flask, with detailed security, auto CRUD generation, and comprehensive UI components.

Pending

Quality

Pending

Does it follow best practices?

Impact

Pending

No eval scenarios have been run

Overview
Eval results
Files

security.mddocs/

Security System

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.

Capabilities

Security Decorators

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')
    """

Authentication Constants

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'}
        }
    }
]

Security Manager Integration

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)

Permission System

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'))

JWT Authentication

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()

Security Configuration Options

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 production

Install with Tessl CLI

npx tessl i tessl/pypi-flask-appbuilder

docs

actions-hooks.md

charts.md

cli-tools.md

constants-exceptions.md

core-framework.md

database-models.md

forms-fields.md

index.md

rest-api.md

security.md

views-crud.md

tile.json