The User Permission Doctype in Frappe is a powerful tool for implementing record-level security. It allows administrators to grant specific users access to individual documents or sets of documents based on matching values in linked fields, overriding or supplementing standard role-based permissions. This is often used for scenarios like restricting access based on Territory, Branch, Company, or any other linked master Doctype.
- To provide fine-grained, document-level access control for specific users.
- To allow users to access only those documents that are linked to a specific value they are permitted for (e.g., User A can only see Customers in "Territory North").
- To enable default value setting based on these permissions (e.g., when User A creates a new Customer, "Territory North" is automatically set).
- To control access across multiple related Doctypes if
apply_to_all_doctypesis checked. - To manage visibility of descendant records in tree structures based on permissions to parent nodes.
user_permission.json: Defines the schema (fields, properties, permissions) of the User Permission Doctype itself.user_permission.py: Contains the server-side Python controller logic, handling validation, cache management, real-time updates, and providing utility functions for fetching and applying user permissions.user_permission.js: Handles client-side JavaScript for the User Permission form, making it dynamic and user-friendly.user_permission_help.html: Likely contains static HTML content used for help text or guidance within the User Permission tool or documentation.user_permission_list.js: Customizes the list view for User Permission records.test_user_permission.py: Contains unit tests for the User Permission logic.
This JSON file outlines the structure for each User Permission record.
- Key Fields:
user(Link to User, Required, In List View): The user to whom this specific permission applies.allow(Link to DocType, Required, In List View): The Doctype for which the permission is being defined (e.g., "Territory", "Customer", "Sales Order"). This is the Doctype that contains the field whose value will be matched.for_value(Dynamic Link, Options:allow, Required, In List View): The specific record name (ID) or value within theallowDoctype that theuseris permitted to access. For example, ifallowis "Territory",for_valuecould be "North America".is_default(Check, Default: 0): If checked, thisfor_valuecan be used as a default value for theuserwhen they create new documents of theallowDoctype or related Doctypes that link toallow.apply_to_all_doctypes(Check, Default: 1):- If checked, this User Permission will apply to all Doctypes that have a Link field pointing to the
allowDoctype. For instance, ifallow="Territory" andfor_value="North", the user can access Sales Orders, Customers, etc., that are linked to the "North" territory. - If unchecked, the permission's scope is further restricted by the
applicable_forfield.
- If checked, this User Permission will apply to all Doctypes that have a Link field pointing to the
applicable_for(Link to DocType, Depends on!apply_to_all_doctypes): Ifapply_to_all_doctypesis unchecked, this field specifies a single target Doctype where this User Permission will be effective. The target Doctype must have a Link field pointing to theallowDoctype.hide_descendants(Check, Default: 0, Hidden): If theallowDoctype is a tree (nested set), this option (when visible and checked) would restrict the user from seeing descendant records of thefor_valuenode.
- Permissions (for "User Permission" Doctype itself):
- Only "System Manager" role has full CRUD permissions on User Permission records.
title_field: "user": The list view primarily displays the user.track_changes: 1: Changes to User Permission records are versioned.- Module: "Core"
The Python controller is crucial for validating, caching, and applying User Permissions.
UserPermission(Document)Class:validate():- Checks for duplicate User Permission entries (same user, allow, for_value, and scope).
- Validates that a user doesn't have multiple
is_defaultentries for the sameallowDoctype and scope, preventing ambiguity.
on_update()andon_trash():- Clear the cached user permissions for the affected
user(frappe.cache.hdel("user_permissions", self.user)). - Publish a real-time event
update_user_permissionsto notify active sessions of the permission change.
- Clear the cached user permissions for the affected
send_user_permissions(bootinfo)(Hooked into boot process):- Fetches the current user's processed permissions using
get_user_permissions()and adds them to thebootinfoobject, making them available on the client-side upon login.
- Fetches the current user's processed permissions using
get_user_permissions(user=None)(Whitelisted):- The core function for retrieving and structuring a user's effective permissions based on "User Permission" records.
- It's cached for performance.
- For each "User Permission" record for the user:
- It adds the
for_valueto a list associated with theallowDoctype. - Nested Set Handling: If
allowis a tree Doctype andhide_descendantsis not checked, it recursively adds all descendant records offor_valueto the permitted list.
- It adds the
- Returns a dictionary where keys are Doctype names, and values are lists of permitted document names (or link values) for that Doctype, along with
applicable_forandis_defaultflags.
- Utility Functions:
user_permission_exists(): Checks if a similar permission already exists.get_applicable_for_doctype_list()(Whitelisted): Provides a filtered list of Doctypes for the "Applicable For" dropdown, showing Doctypes linked to the selected "Allow" Doctype.get_permitted_documents(): Returns a list of document names a user can access for a given Doctype based on their User Permissions.- Whitelisted functions (
check_applicable_doc_perm,clear_user_permissions,add_user_permissions) provide the backend for the "User Permissions" management tool/page, allowing administrators to manage these permissions in bulk or with more complex scoping.
This script enhances the "User Permission" form's usability.
setup(frm):- Sets dynamic queries for
allow(filters for non-single, non-table Doctypes) andapplicable_for(uses server callget_applicable_for_doctype_listbased on selectedallowDoctype).
- Sets dynamic queries for
refresh(frm):- Adds a "View Permitted Documents" button, linking to a report showing documents accessible to the selected user.
- Manages the visibility and mandatory status of
applicable_forbased onapply_to_all_doctypes. - Manages visibility of
hide_descendantsbased on whetherallowis a tree Doctype.
- Field Change Handlers (
allow,apply_to_all_doctypes):- Clears
for_valueifallowchanges. - Dynamically updates UI constraints for
applicable_forandhide_descendants.
- Clears
- Definition: A System Manager creates "User Permission" records. For example:
- User: "sales_rep_john@example.com"
- Allow: "Territory"
- For Value: "North Region"
- Apply To All DocTypes: Checked
- Login/Cache: When "sales_rep_john" logs in,
get_user_permissions()is called, and his permissions (including access to "North Region" Territory and, by extension, any document linked to "North Region") are cached. - List View/Report Filtering: When John views a list of "Customers", the list query is automatically restricted. If "Customer" has a field
territory(Link to Territory), the list will only show Customers whereterritoryis "North Region" or one of its descendants (ifhide_descendantsis off for "North Region" and it's a tree). - Form Access: If John tries to open a "Customer" document directly whose
territoryis not "North Region" (or its descendant), he will be denied access (unless other role permissions grant broader access). - Default Values: If a User Permission has
is_default = 1(e.g., for "Territory" = "North Region"), when John creates a new "Customer", the "Territory" field might automatically be set to "North Region".
User Permissions are a powerful mechanism for implementing record-level security and data segmentation in Frappe, often used in conjunction with role-based permissions to achieve complex access control scenarios.