0
# Constants and Exception Handling
1
2
Framework constants, authentication types, API configuration, and comprehensive exception classes for error handling. This module provides all the constants and exception types used throughout Flask-AppBuilder for configuration, error handling, and system integration.
3
4
## Capabilities
5
6
### Authentication Constants
7
8
Constants defining supported authentication types and methods for configuring Flask-AppBuilder's security system.
9
10
```python { .api }
11
from flask_appbuilder.const import (
12
AUTH_OID, AUTH_DB, AUTH_LDAP, AUTH_REMOTE_USER, AUTH_OAUTH
13
)
14
15
# Authentication type constants
16
AUTH_OID = 0 # OpenID authentication
17
AUTH_DB = 1 # Database authentication (default)
18
AUTH_LDAP = 2 # LDAP/Active Directory authentication
19
AUTH_REMOTE_USER = 3 # Remote user authentication (via headers)
20
AUTH_OAUTH = 4 # OAuth authentication (Google, GitHub, etc.)
21
22
# Usage in application configuration
23
# Set authentication method in config.py
24
AUTH_TYPE = AUTH_DB # Use database authentication
25
26
# Database authentication configuration
27
AUTH_TYPE = AUTH_DB
28
AUTH_USER_REGISTRATION = True # Allow user self-registration
29
AUTH_USER_REGISTRATION_ROLE = "Public" # Default role for new users
30
31
# LDAP authentication configuration
32
AUTH_TYPE = AUTH_LDAP
33
AUTH_LDAP_SERVER = "ldap://your-ldap-server.com"
34
AUTH_LDAP_USE_TLS = True
35
AUTH_LDAP_BIND_USER = "cn=admin,dc=company,dc=com"
36
AUTH_LDAP_BIND_PASSWORD = "ldap-password"
37
AUTH_LDAP_SEARCH = "ou=people,dc=company,dc=com"
38
AUTH_LDAP_UID_FIELD = "uid"
39
AUTH_LDAP_FIRSTNAME_FIELD = "givenName"
40
AUTH_LDAP_LASTNAME_FIELD = "sn"
41
AUTH_LDAP_EMAIL_FIELD = "mail"
42
43
# OAuth authentication configuration
44
AUTH_TYPE = AUTH_OAUTH
45
OAUTH_PROVIDERS = [
46
{
47
'name': 'google',
48
'icon': 'fa-google',
49
'token_key': 'access_token',
50
'remote_app': {
51
'client_id': 'your-google-client-id',
52
'client_secret': 'your-google-client-secret',
53
'server_metadata_url': 'https://accounts.google.com/.well-known/openid_configuration',
54
'client_kwargs': {
55
'scope': 'openid email profile'
56
}
57
}
58
},
59
{
60
'name': 'github',
61
'icon': 'fa-github',
62
'token_key': 'access_token',
63
'remote_app': {
64
'client_id': 'your-github-client-id',
65
'client_secret': 'your-github-client-secret',
66
'access_token_url': 'https://github.com/login/oauth/access_token',
67
'authorize_url': 'https://github.com/login/oauth/authorize',
68
'api_base_url': 'https://api.github.com/',
69
'client_kwargs': {'scope': 'user:email'}
70
}
71
}
72
]
73
74
# Remote user authentication configuration
75
AUTH_TYPE = AUTH_REMOTE_USER
76
AUTH_REMOTE_USER_VAR = "REMOTE_USER" # Header containing username
77
```
78
79
### API Constants
80
81
Constants for API configuration, versioning, and request/response handling in Flask-AppBuilder's REST API system.
82
83
```python { .api }
84
from flask_appbuilder.const import (
85
API_SECURITY_VERSION, PERMISSION_PREFIX, API_URI_RIS_KEY,
86
API_RESULT_RIS_KEY, API_ORDER_COLUMNS_RIS_KEY, API_ORDER_DIRECTION_RIS_KEY,
87
API_FILTERS_RIS_KEY, API_PAGE_INDEX_RIS_KEY, API_PAGE_SIZE_RIS_KEY,
88
API_SELECT_COLUMNS_RIS_KEY, API_SELECT_KEYS_RIS_KEY
89
)
90
91
# API versioning
92
API_SECURITY_VERSION = "v1" # Default API version
93
94
# Permission system
95
PERMISSION_PREFIX = "can_" # Prefix for all permissions ("can_list", "can_show", etc.)
96
97
# API request parameter keys (Rison format)
98
API_URI_RIS_KEY = "q" # Main query parameter key
99
API_RESULT_RIS_KEY = "result" # Result data key
100
API_ORDER_COLUMNS_RIS_KEY = "order_column" # Ordering column key
101
API_ORDER_DIRECTION_RIS_KEY = "order_direction" # Ordering direction key
102
API_FILTERS_RIS_KEY = "filters" # Filters array key
103
API_PAGE_INDEX_RIS_KEY = "page" # Page index key
104
API_PAGE_SIZE_RIS_KEY = "page_size" # Page size key
105
API_SELECT_COLUMNS_RIS_KEY = "columns" # Column selection key
106
API_SELECT_KEYS_RIS_KEY = "keys" # Primary key selection key
107
108
# Usage in API requests
109
"""
110
GET /api/v1/person/?q=(filters:!((col:name,opr:ct,value:John)),page:0,page_size:20)
111
112
Decoded Rison:
113
{
114
"filters": [{"col": "name", "opr": "ct", "value": "John"}],
115
"page": 0,
116
"page_size": 20
117
}
118
"""
119
120
# API response format constants
121
API_RESPONSE_SCHEMA = {
122
"count": "int", # Total number of records
123
"description_columns": "dict", # Column descriptions
124
"ids": "list", # List of primary keys
125
"label_columns": "dict", # Column labels
126
"list_columns": "list", # Available list columns
127
"order_columns": "list", # Orderable columns
128
"page": "int", # Current page
129
"page_size": "int", # Items per page
130
"result": "list" # Actual data records
131
}
132
133
# HTTP status codes used by API
134
API_STATUS_CODES = {
135
200: "OK",
136
201: "Created",
137
400: "Bad Request",
138
401: "Unauthorized",
139
403: "Forbidden",
140
404: "Not Found",
141
422: "Unprocessable Entity",
142
500: "Internal Server Error"
143
}
144
145
# API configuration constants
146
FAB_API_MAX_PAGE_SIZE = 100 # Maximum page size allowed
147
FAB_API_SWAGGER_UI = True # Enable Swagger UI
148
FAB_API_SWAGGER_TEMPLATE = "appbuilder/swagger/swagger.html"
149
FAB_API_SHOW_STACKTRACE = False # Show stack traces in API responses
150
```
151
152
### Log Message Constants
153
154
Predefined log message templates for consistent logging throughout Flask-AppBuilder applications.
155
156
```python { .api }
157
from flask_appbuilder.const import (
158
LOGMSG_ERR_SEC_ADD_PERMISSION, LOGMSG_ERR_SEC_ADD_VIEWMENU,
159
LOGMSG_ERR_SEC_ADD_PERMVIEW, LOGMSG_ERR_SEC_DEL_PERMISSION,
160
LOGMSG_WAR_SEC_LOGIN_FAILED, LOGMSG_WAR_SEC_NO_USER,
161
LOGMSG_INF_SEC_ADD_ROLE, LOGMSG_INF_SEC_UPD_ROLE
162
)
163
164
# Security error messages
165
LOGMSG_ERR_SEC_ADD_PERMISSION = "Add Permission: {0}"
166
LOGMSG_ERR_SEC_ADD_VIEWMENU = "Add View Menu Error: {0}"
167
LOGMSG_ERR_SEC_ADD_PERMVIEW = "Add Permission View Error: {0}"
168
LOGMSG_ERR_SEC_DEL_PERMISSION = "Del Permission Error: {0}"
169
LOGMSG_ERR_SEC_ADD_REGISTER_USER = "Add Register User Error: {0}"
170
LOGMSG_ERR_SEC_UPD_REGISTER_USER = "Update Register User Error: {0}"
171
172
# Security warning messages
173
LOGMSG_WAR_SEC_LOGIN_FAILED = "Login Failed for user: {0}"
174
LOGMSG_WAR_SEC_NO_USER = "No user yet created, use flask fab create-admin"
175
LOGMSG_WAR_SEC_NOAPIKEY = "API key not found on request"
176
177
# Security info messages
178
LOGMSG_INF_SEC_ADD_ROLE = "Added Role: {0}"
179
LOGMSG_INF_SEC_UPD_ROLE = "Updated Role: {0}"
180
LOGMSG_INF_SEC_ADD_PERMISSION = "Added Permission: {0}"
181
LOGMSG_INF_SEC_ADD_VIEWMENU = "Added View Menu: {0}"
182
LOGMSG_INF_SEC_ADD_PERMVIEW = "Added Permission View: {0}"
183
184
# Database messages
185
LOGMSG_ERR_DBI_ADD_GENERIC = "Add record error: {0}"
186
LOGMSG_ERR_DBI_EDIT_GENERIC = "Edit record error: {0}"
187
LOGMSG_ERR_DBI_DEL_GENERIC = "Delete record error: {0}"
188
189
# Usage in custom logging
190
import logging
191
from flask_appbuilder.const import LOGMSG_WAR_SEC_LOGIN_FAILED
192
193
logger = logging.getLogger(__name__)
194
195
def log_failed_login(username):
196
logger.warning(LOGMSG_WAR_SEC_LOGIN_FAILED.format(username))
197
198
def log_permission_added(permission_name):
199
logger.info(LOGMSG_INF_SEC_ADD_PERMISSION.format(permission_name))
200
```
201
202
### Flash Message Constants
203
204
Constants for user interface flash messages providing consistent user feedback across Flask-AppBuilder applications.
205
206
```python { .api }
207
from flask_appbuilder.const import (
208
FLAMSG_ERR_SEC_ACCESS_DENIED, FLAMSG_SUCESSFUL_ADD_RECORD,
209
FLAMSG_SUCESSFUL_UPD_RECORD, FLAMSG_SUCESSFUL_DEL_RECORD
210
)
211
212
# Error flash messages
213
FLAMSG_ERR_SEC_ACCESS_DENIED = "Access is Denied"
214
FLAMSG_ERR_DBI_ADD_GENERIC = "Add record error. {0}"
215
FLAMSG_ERR_DBI_EDIT_GENERIC = "Edit record error. {0}"
216
FLAMSG_ERR_DBI_DEL_GENERIC = "Delete record error. {0}"
217
218
# Success flash messages
219
FLAMSG_SUCESSFUL_ADD_RECORD = "Added Record"
220
FLAMSG_SUCESSFUL_UPD_RECORD = "Changed Record"
221
FLAMSG_SUCESSFUL_DEL_RECORD = "Deleted Record"
222
223
# Usage in views
224
from flask import flash
225
from flask_appbuilder.const import FLAMSG_SUCESSFUL_ADD_RECORD
226
227
class PersonModelView(ModelView):
228
def post_add(self, item):
229
flash(FLAMSG_SUCESSFUL_ADD_RECORD, "success")
230
231
def post_update(self, item):
232
flash(FLAMSG_SUCESSFUL_UPD_RECORD, "success")
233
```
234
235
### Exception Classes
236
237
Comprehensive exception hierarchy for handling various error conditions in Flask-AppBuilder applications.
238
239
```python { .api }
240
from flask_appbuilder.exceptions import (
241
FABException, InvalidColumnFilterFABException, InvalidOperationFilterFABException,
242
InvalidOrderByColumnFABException, InvalidColumnArgsFABException,
243
InterfaceQueryWithoutSession, PasswordComplexityValidationError,
244
ApplyFilterException, OAuthProviderUnknown, InvalidLoginAttempt,
245
DeleteGroupWithUsersException, DeleteRoleWithUsersException
246
)
247
248
# Base exception class
249
class FABException(Exception):
250
"""
251
Base Flask-AppBuilder exception class.
252
All custom exceptions inherit from this class.
253
"""
254
pass
255
256
# Database and model exceptions
257
class InvalidColumnFilterFABException(FABException):
258
"""
259
Invalid column specified for filtering operation.
260
261
Raised when:
262
- Column name doesn't exist on model
263
- Column is not searchable/filterable
264
- Invalid column reference in filter expression
265
"""
266
pass
267
268
class InvalidOperationFilterFABException(FABException):
269
"""
270
Invalid filter operation for column type.
271
272
Raised when:
273
- Using string operations on numeric columns
274
- Using numeric operations on string columns
275
- Unsupported filter operation for column type
276
"""
277
pass
278
279
class InvalidOrderByColumnFABException(FABException):
280
"""
281
Invalid column specified for ordering.
282
283
Raised when:
284
- Column name doesn't exist on model
285
- Column is not orderable
286
- Invalid sort direction specified
287
"""
288
pass
289
290
class InvalidColumnArgsFABException(FABException):
291
"""
292
Invalid column arguments provided.
293
294
Raised when:
295
- Column configuration is malformed
296
- Missing required column properties
297
- Conflicting column settings
298
"""
299
pass
300
301
class InterfaceQueryWithoutSession(FABException):
302
"""
303
Database query attempted without active session.
304
305
Raised when:
306
- SQLAlchemy session is None
307
- Session has been closed
308
- Database connection is unavailable
309
"""
310
pass
311
312
# Security exceptions
313
class PasswordComplexityValidationError(FABException):
314
"""
315
Password doesn't meet complexity requirements.
316
317
Raised when:
318
- Password is too short
319
- Password lacks required character types
320
- Password fails custom validation rules
321
"""
322
pass
323
324
class InvalidLoginAttempt(FABException):
325
"""
326
Login attempt with invalid credentials.
327
328
Raised when:
329
- Username doesn't exist
330
- Password is incorrect
331
- Account is disabled
332
- Maximum login attempts exceeded
333
"""
334
pass
335
336
class OAuthProviderUnknown(FABException):
337
"""
338
Unknown or unconfigured OAuth provider.
339
340
Raised when:
341
- OAuth provider not in OAUTH_PROVIDERS config
342
- Provider configuration is incomplete
343
- Provider authentication endpoint unreachable
344
"""
345
pass
346
347
# Filter and query exceptions
348
class ApplyFilterException(FABException):
349
"""
350
Error applying filter to query.
351
352
Raised when:
353
- Filter syntax is invalid
354
- Filter references non-existent columns
355
- Filter values are wrong type
356
- Database error during filter application
357
"""
358
pass
359
360
# User management exceptions
361
class DeleteGroupWithUsersException(FABException):
362
"""
363
Attempted to delete group that still has users.
364
365
Raised when:
366
- Group has active user associations
367
- CASCADE delete is not enabled
368
- Business rules prevent group deletion
369
"""
370
pass
371
372
class DeleteRoleWithUsersException(FABException):
373
"""
374
Attempted to delete role that still has users.
375
376
Raised when:
377
- Role has active user assignments
378
- Role is marked as protected/system role
379
- CASCADE delete is not enabled
380
"""
381
pass
382
383
# Usage examples
384
def validate_filter_column(self, column_name):
385
"""Validate that column can be used for filtering."""
386
if column_name not in self.search_columns:
387
raise InvalidColumnFilterFABException(
388
f"Column '{column_name}' is not searchable"
389
)
390
391
def apply_custom_filter(self, query, filter_spec):
392
"""Apply custom filter with error handling."""
393
try:
394
# Apply filter logic
395
return query.filter(filter_spec)
396
except Exception as e:
397
raise ApplyFilterException(
398
f"Failed to apply filter: {str(e)}"
399
) from e
400
401
def delete_user_role(self, role_id):
402
"""Delete role with validation."""
403
role = self.get_role(role_id)
404
405
if role.users:
406
raise DeleteRoleWithUsersException(
407
f"Cannot delete role '{role.name}' - {len(role.users)} users assigned"
408
)
409
410
self.delete_role(role)
411
412
# Custom exception handling in views
413
class PersonModelView(ModelView):
414
datamodel = SQLAInterface(Person)
415
416
def _list(self, **kwargs):
417
"""Override list with custom error handling."""
418
try:
419
return super()._list(**kwargs)
420
except InvalidColumnFilterFABException as e:
421
flash(f"Filter error: {str(e)}", "error")
422
return redirect(url_for('PersonModelView.list'))
423
except ApplyFilterException as e:
424
flash(f"Search error: {str(e)}", "error")
425
return redirect(url_for('PersonModelView.list'))
426
427
# Exception handling in API
428
from flask_appbuilder.api import ModelRestApi
429
430
class PersonApi(ModelRestApi):
431
datamodel = SQLAInterface(Person)
432
433
def get_list(self, **kwargs):
434
"""Override with custom exception handling."""
435
try:
436
return super().get_list(**kwargs)
437
except InvalidColumnFilterFABException as e:
438
return self.response_400(f"Invalid filter column: {str(e)}")
439
except InvalidOperationFilterFABException as e:
440
return self.response_400(f"Invalid filter operation: {str(e)}")
441
except ApplyFilterException as e:
442
return self.response_500(f"Filter application error: {str(e)}")
443
444
# Global exception handler
445
@app.errorhandler(FABException)
446
def handle_fab_exception(error):
447
"""Global handler for Flask-AppBuilder exceptions."""
448
logger.error(f"FAB Exception: {str(error)}")
449
450
if isinstance(error, InvalidLoginAttempt):
451
return redirect(url_for('AuthDBView.login'))
452
elif isinstance(error, PasswordComplexityValidationError):
453
flash(f"Password error: {str(error)}", "error")
454
return redirect(request.referrer or '/')
455
else:
456
flash(f"Application error: {str(error)}", "error")
457
return redirect(url_for('IndexView.index'))
458
```
459
460
### Configuration Constants
461
462
Constants for configuring Flask-AppBuilder behavior, templates, and feature flags.
463
464
```python { .api }
465
# Flask-AppBuilder configuration constants
466
467
# Application configuration
468
FAB_APP_NAME = "Flask AppBuilder" # Default app name
469
FAB_APP_ICON = None # App icon path
470
FAB_APP_THEME = "" # Bootstrap theme
471
FAB_BASE_TEMPLATE = "appbuilder/baselayout.html" # Base template
472
FAB_STATIC_FOLDER = "static/appbuilder" # Static files folder
473
FAB_STATIC_URL_PATH = "/appbuilder" # Static URL path
474
475
# Security configuration
476
FAB_SECURITY_MANAGER_CLASS = "flask_appbuilder.security.manager.SecurityManager"
477
FAB_UPDATE_PERMS = True # Auto-update permissions
478
FAB_PASSWORD_COMPLEXITY_ENABLED = False # Enable password complexity
479
FAB_ROLES_MAPPING = {} # Role name mapping
480
FAB_AUTH_LIMIT_PER_USER = 0 # Login attempts limit (0 = unlimited)
481
482
# API configuration
483
FAB_API_MAX_PAGE_SIZE = 100 # Maximum API page size
484
FAB_API_SWAGGER_UI = True # Enable Swagger UI
485
FAB_API_SHOW_STACKTRACE = False # Show stack traces in API errors
486
487
# UI configuration
488
FAB_ADDON_MANAGERS = [] # List of addon managers
489
FAB_ADD_SECURITY_VIEWS = True # Add default security views
490
FAB_ADD_SECURITY_PERMISSION_VIEW = False # Add permission management view
491
FAB_ADD_SECURITY_VIEW_MENU_VIEW = False # Add view menu management
492
FAB_ADD_SECURITY_PERMISSION_VIEWS_VIEW = False # Add permission-view management
493
494
# Menu configuration
495
FAB_MENU_OPEN_SUBMENU_ON_CLICK = True # Open submenus on click
496
FAB_ICON_FONT_NAME = "font-awesome" # Icon font name
497
498
# File upload configuration
499
FAB_UPLOAD_FOLDER = "uploads/" # Upload directory
500
FAB_IMG_UPLOAD_FOLDER = "uploads/images/" # Image upload directory
501
FAB_IMG_UPLOAD_URL = "/uploads/images/" # Image URL path
502
FAB_IMG_SIZE = (150, 150, True) # Image resize dimensions
503
504
# Rate limiting
505
RATELIMIT_ENABLED = False # Enable rate limiting
506
RATELIMIT_STORAGE_URL = "memory://" # Rate limit storage backend
507
RATELIMIT_HEADERS_ENABLED = True # Include rate limit headers
508
509
# Usage in configuration
510
class Config:
511
# Basic configuration
512
FAB_APP_NAME = "My Application"
513
FAB_APP_THEME = "cerulean.css"
514
FAB_APP_ICON = "/static/img/logo.png"
515
516
# Security
517
FAB_UPDATE_PERMS = True
518
FAB_PASSWORD_COMPLEXITY_ENABLED = True
519
FAB_AUTH_LIMIT_PER_USER = 5 # Max 5 login attempts
520
521
# API
522
FAB_API_MAX_PAGE_SIZE = 50
523
FAB_API_SHOW_STACKTRACE = False # Hide in production
524
525
# File uploads
526
FAB_UPLOAD_FOLDER = "/var/uploads/"
527
FAB_IMG_SIZE = (200, 200, True) # Larger thumbnails
528
529
# Rate limiting
530
RATELIMIT_ENABLED = True
531
RATELIMIT_STORAGE_URL = "redis://localhost:6379"
532
533
# Feature flags
534
class FeatureFlags:
535
"""Feature flags for conditional functionality."""
536
537
# UI features
538
ENABLE_CHARTS = True # Enable chart views
539
ENABLE_FILE_UPLOADS = True # Enable file upload fields
540
ENABLE_BULK_ACTIONS = True # Enable bulk actions on lists
541
ENABLE_EXPORT = True # Enable data export functionality
542
543
# Security features
544
ENABLE_PASSWORD_HISTORY = False # Track password history
545
ENABLE_SESSION_TIMEOUT = True # Enable session timeout
546
ENABLE_LOGIN_AUDIT = True # Log all login attempts
547
548
# API features
549
ENABLE_API_RATE_LIMITING = True # Enable API rate limiting
550
ENABLE_API_CACHING = False # Enable API response caching
551
ENABLE_OPENAPI_DOCS = True # Enable OpenAPI documentation
552
553
# Environment-specific constants
554
class ProductionConfig(Config):
555
FAB_API_SHOW_STACKTRACE = False
556
RATELIMIT_ENABLED = True
557
FAB_PASSWORD_COMPLEXITY_ENABLED = True
558
559
class DevelopmentConfig(Config):
560
FAB_API_SHOW_STACKTRACE = True
561
RATELIMIT_ENABLED = False
562
FAB_UPDATE_PERMS = True
563
564
class TestingConfig(Config):
565
TESTING = True
566
FAB_UPDATE_PERMS = False
567
WTF_CSRF_ENABLED = False
568
```