permissions-expert
Frappe Permissions Expert
This skill provides comprehensive guidance for understanding and extending Frappe's permission system.
Overview
Frappe's permission system is multi-layered and evaluates permissions in a specific order:
- Administrator Check: Administrator user bypasses all permission checks
- Role-Based Permissions: DocType permissions configured through Permission Manager
- Controller Permissions: Custom
has_permissionhooks in doctypes - User Permissions: Document-level restrictions based on user-specific rules
- Permission Query Conditions: SQL-based filters for list views and reports
- Share Permissions: Explicit document sharing between users
Permission Types
Frappe supports the following permission types (ptypes):
rights = (
"select", # View in list (limited fields)
"read", # Full read access
"write", # Edit existing documents
"create", # Create new documents
"delete", # Delete documents
"submit", # Submit submittable documents
"cancel", # Cancel submitted documents
"amend", # Amend cancelled documents
"print", # Print documents
"email", # Email documents
"report", # Access reports
"import", # Import documents
"export", # Export documents
"share", # Share documents with others
)
Quick Reference
Main Permission Check Function
frappe.has_permission(
doctype="DocType Name",
ptype="read", # Permission type to check
doc=None, # Optional: specific document instance
user=None, # Optional: defaults to current user
raise_exception=True, # Display error message if False
parent_doctype=None, # Required for child doctypes
debug=False, # Enable debug logging
ignore_share_permissions=False
)
Key Concepts
- Child Tables: Don't have their own permissions; permissions are checked on the parent document
- Virtual DocTypes: Permission query conditions don't apply; only
has_permissionhook and role permissions apply - Permission Levels: Provide field-level access control within a document (permlevel 0, 1, 2, etc.)
Extension Hooks
Frappe provides seven main hooks for extending permission logic:
1. has_permission - Controller Permission Check
Purpose: Implement custom document-level permission logic
Location: In your doctype's .py file or registered in hooks.py
Signature:
def has_permission(doc, ptype=None, user=None, debug=False):
"""
Returns:
bool or None:
- True: Explicitly grant permission
- False: Explicitly deny permission
- None: No opinion, continue with other checks
"""
Important Notes:
- Return
Noneto defer to other permission checks (recommended default) - Return
Falseto explicitly deny permission (overrides role permissions) - Return
Trueto explicitly grant permission (use with caution)
When to read: See references/has-permission-hook.md when implementing document-level permission logic with 7 detailed examples covering owner access, team hierarchies, status restrictions, and more.
2. permission_query_conditions - List View Filtering and Document Access
Purpose: Return SQL WHERE conditions to filter documents in list views and validate individual document access
Location: In your doctype's .py file or registered in hooks.py
Signature:
def get_permission_query_conditions(user=None, doctype=None):
"""
Returns:
str: SQL WHERE clause without the "WHERE" keyword
Returns empty string "" to allow all documents
"""
Security: Always escape dynamic values with frappe.db.escape()
Important: These conditions are checked both in list views AND when accessing individual documents via has_permission() for read/select operations.
When to read: See references/permission-query-conditions-hook.md when implementing list filtering or document access validation with 7 examples including company filtering, role-based regions, time-based access, and multi-tenant patterns.
3. write_permission_query_conditions - Post-Write Validation
Purpose: Validate that saved/updated documents satisfy custom conditions before committing to database
Location: In your doctype's .py file or registered in hooks.py
Signature:
def get_write_permission_query_conditions(user=None, doctype=None, ptype="write"):
"""
Return SQL WHERE conditions to validate written documents.
This is automatically called during has_permission() checks
for write/create/submit/cancel/delete operations.
Checked AFTER database write but BEFORE commit.
If validation fails, transaction is rolled back.
"""
When to read: See references/write-permission-query-conditions-hook.md when validating writes before commit with examples for regional restrictions, document age limits, and status-based validation.
4. Server Scripts - Permission Query
Purpose: Define permission query conditions using Server Scripts (Python code in the UI)
Location: Created through Frappe UI at /app/server-script
Script Type: "Permission Query"
When to read: See references/server-scripts.md when prototyping permission logic via UI before moving to code, with examples for department filtering and role-based access.
5. has_website_permission - Website/Portal Access
Purpose: Control access to documents on the website/portal (not desk)
Location: In your doctype's .py file or registered in hooks.py
When to read: See references/has-website-permission-hook.md when implementing portal/website access control with examples for customer orders, published content, and contact relationships.
Workflow Permission Hooks
Frappe provides two additional hooks specifically for workflow-based permissions:
6. filter_workflow_transitions - Custom Transition Filtering
Purpose: Filter and customize the list of available workflow transitions based on custom logic
Location: Registered in hooks.py
Use Cases:
- Hide specific transitions based on document field values
- Apply time-based or date-based restrictions
- Implement dynamic transition visibility
7. has_workflow_action_permission - Action-Level Permission
Purpose: Control which users should receive workflow action notifications and have permission to execute specific actions
Location: Registered in hooks.py
Use Cases:
- Implement approval hierarchies
- Department or region-based approval routing
- Amount-based approval limits
When to read: See references/workflow-permission-hooks.md when implementing workflow transition filtering or approval routing with 10+ examples covering hierarchical approvals, time restrictions, and regional routing.
User Permissions
User Permissions restrict access to specific document values for link fields.
Creating User Permissions:
from frappe.permissions import add_user_permission
add_user_permission(
doctype="Company",
name="Company A",
user="user@example.com",
applicable_for="Sales Order", # Optional
is_default=1, # Optional
ignore_permissions=True # Optional
)
Use Cases:
- Restrict sales users to their own territory
- Limit employees to their branch/department
- Multi-company access control
When to read: See references/user-permissions.md when implementing document-level restrictions using User Permissions with comprehensive examples for multi-company, territory, and department-based access.
Share Permissions
Documents can be explicitly shared with specific users:
frappe.share.add(
doctype="Sales Order",
name="SO-0001",
user="user@example.com",
read=1,
write=1,
share=0,
submit=0,
notify=1
)
When to read: See references/share-permissions.md when implementing explicit document sharing between users with examples for collaboration, temporary access, and cross-department workflows.
Permission Levels
Permission levels provide field-level access control:
- Each field can have a permlevel (0, 1, 2, etc.)
- Users must have role permission with that permlevel to see/edit the field
- Permlevel 0 is default and always checked
When to read: See references/permission-levels.md when implementing field-level access control with examples for hiding pricing, cost fields, and internal notes from specific roles.
Best Practices
Security
- Always Escape User Input: Use
frappe.db.escape()when building SQL conditions - Fail Secure: Default to denying access when in doubt
- Validate Hook Returns: Ensure hooks return expected types
- Test Permission Boundaries: Test with users having minimal permissions
- Avoid Side Effects: Permission checks should be read-only
Performance
- Optimize SQL Conditions: Use indexed columns in WHERE clauses
- Cache User Data: Cache frequently accessed user properties
- Minimize Hook Complexity: Keep permission logic simple and fast
- Use Appropriate Hooks:
- Use
permission_query_conditionsfor list filtering - Use
has_permissionfor complex document-specific logic
- Use
Maintainability
- Document Permission Logic: Add docstrings explaining the rules
- Separate Concerns: Keep permission logic separate from business logic
- Use Constants: Define permission-related constants
- Consistent Return Values: Be explicit about what you're returning
When to read: See references/best-practices.md when writing production-ready permission code with comprehensive guidelines for security, performance, and maintainability.
Debugging Permissions
Enable Debug Mode
# In Python
result = frappe.has_permission("DocType", "read", doc, debug=True)
# Logs will show the permission evaluation flow
# Or enable globally
frappe.conf.developer_mode = 1
Check Permission Debug Logs
# After permission check with debug=True
logs = frappe.local.permission_debug_log
for log in logs:
print(log)
When to read: See references/debugging.md when troubleshooting permission issues with comprehensive debugging workflows, common scenarios, and logging techniques.
Common Issues and Solutions
When to read: See references/common-issues.md when facing permission problems with detailed troubleshooting for:
- User Can't See Documents in List View
- Can See Document in List but Can't Open
- Permission Query Hook Not Working
- Write Operations Fail Silently
- Virtual DocType Permission Issues
- Share Permissions Not Working
- Administrator Not Seeing All Documents
Common Patterns
When to read: See references/common-patterns.md for 15 ready-to-use permission patterns including:
- Owner-Only Access
- Role-Based Region Filtering
- Hierarchical Access (Team/Department)
- Status-Based Restrictions
- Time-Based Access
- Multi-Tenant Access
- Permission Level Filtering
- Child Table Permissions
- Conditional Field Visibility
- Combined Role and Territory Access
Testing Permission Hooks
When to read: See references/testing.md when writing unit and integration tests for permission logic with comprehensive examples and patterns.
Migration Guide
When to read: See references/migration-guide.md for a step-by-step guide when adding custom permissions to existing DocTypes.
Core Implementation Files
Key files in Frappe codebase:
/frappe/permissions.py- Main permission system/frappe/model/db_query.py- Permission query integration/frappe/model/document.py- Document lifecycle and permission checks/frappe/core/doctype/user_permission/- User permission management
Usage
When working with permissions:
- Identify the requirement: What access control is needed?
- Choose appropriate hook: Select based on use case
- Reference appropriate guide: Use reference files for detailed patterns
- Follow best practices: Security, performance, maintainability
- Test thoroughly: With different roles and edge cases
- Debug when needed: Use debug mode and logging
Important Notes
- Administrator always bypasses all permission checks
- Permission query conditions are applied to both list views AND individual document access
- Virtual doctypes don't support permission query conditions
- Child tables inherit parent document permissions
- Always escape user input in SQL conditions to prevent SQL injection
- Permission checks should be read-only operations
More from kehwar/frappe_tweaks
frappe-tweaks-power-query-expert
Expert guidance for connecting Power Query (Power BI, Excel) to Frappe apps and reports. Use when building Power Query M code for Frappe data access, integrating Frappe reports with Power BI/Excel, implementing authentication for Power Query connections, handling heavy/long-running reports with report_long_polling API to avoid timeouts, applying column types and transformations, or troubleshooting Power Query caching and connection issues.
6open-observe-api-expert
Expert guidance for OpenObserve API integration in Frappe Tweaks. Use when creating, configuring, or troubleshooting OpenObserve API DocType, implementing send_logs() or search_logs() functionality, integrating with Server Scripts/Business Logic/Client-side code, debugging connection issues, or implementing logging, monitoring, error tracking, performance metrics, or audit trail use cases.
5frappe-ci-expert
Expert guidance for setting up CI/CD tests for Frappe apps. Use when users ask about GitHub Actions workflows, CI test setup, continuous integration for Frappe apps, running tests in CI environments, database setup for CI, bench configuration in CI, or automating tests for Frappe/ERPNext applications.
4workflow-expert
Expert guidance on Frappe Workflow system including workflow structure, states and transitions, workflow actions, email notifications, permission hooks (before_transition, after_transition, filter_workflow_transitions, has_workflow_action_permission), and best practices. Use when creating workflows, implementing workflow logic, understanding state transitions, working with workflow actions, configuring email notifications, or troubleshooting workflow-related issues.
4report-expert
Expert guidance on Frappe reports including report types, structure, creation workflow, and best practices. Use when creating standard script reports, query reports, understanding report structure, working with columns and filters, or troubleshooting report-related issues.
4api-reviewer
Security review and analysis for Frappe API endpoints decorated with @frappe.whitelist(). Use when reviewing API security, checking for permission vulnerabilities, scanning for unprotected endpoints, validating role restrictions, or auditing API endpoints for security best practices. Helps identify missing frappe.only_for(), frappe.has_permission(), or frappe.get_list() usage.
4