Skip to content

Latest commit

 

History

History
94 lines (77 loc) · 8.7 KB

File metadata and controls

94 lines (77 loc) · 8.7 KB

Frappe Core Doctype: User Permission

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.

Core Purpose

  • 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_doctypes is checked.
  • To manage visibility of descendant records in tree structures based on permissions to parent nodes.

Key Files Defining User Permission

  • 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.

user_permission.json - Schema Definition

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 the allow Doctype that the user is permitted to access. For example, if allow is "Territory", for_value could be "North America".
    • is_default (Check, Default: 0): If checked, this for_value can be used as a default value for the user when they create new documents of the allow Doctype or related Doctypes that link to allow.
    • 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 allow Doctype. For instance, if allow="Territory" and for_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_for field.
    • applicable_for (Link to DocType, Depends on !apply_to_all_doctypes): If apply_to_all_doctypes is 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 the allow Doctype.
    • hide_descendants (Check, Default: 0, Hidden): If the allow Doctype is a tree (nested set), this option (when visible and checked) would restrict the user from seeing descendant records of the for_value node.
  • 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"

user_permission.py - Server-Side Logic

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_default entries for the same allow Doctype and scope, preventing ambiguity.
    • on_update() and on_trash():
      • Clear the cached user permissions for the affected user (frappe.cache.hdel("user_permissions", self.user)).
      • Publish a real-time event update_user_permissions to notify active sessions of the permission change.
  • send_user_permissions(bootinfo) (Hooked into boot process):
    • Fetches the current user's processed permissions using get_user_permissions() and adds them to the bootinfo object, making them available on the client-side upon login.
  • 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_value to a list associated with the allow Doctype.
      • Nested Set Handling: If allow is a tree Doctype and hide_descendants is not checked, it recursively adds all descendant records of for_value to the permitted list.
    • 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_for and is_default flags.
  • 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.

user_permission.js - Client-Side Logic

This script enhances the "User Permission" form's usability.

  • setup(frm):
    • Sets dynamic queries for allow (filters for non-single, non-table Doctypes) and applicable_for (uses server call get_applicable_for_doctype_list based on selected allow Doctype).
  • 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_for based on apply_to_all_doctypes.
    • Manages visibility of hide_descendants based on whether allow is a tree Doctype.
  • Field Change Handlers (allow, apply_to_all_doctypes):
    • Clears for_value if allow changes.
    • Dynamically updates UI constraints for applicable_for and hide_descendants.

How User Permissions Are Applied

  1. Definition: A System Manager creates "User Permission" records. For example:
  2. 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.
  3. 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 where territory is "North Region" or one of its descendants (if hide_descendants is off for "North Region" and it's a tree).
  4. Form Access: If John tries to open a "Customer" document directly whose territory is not "North Region" (or its descendant), he will be denied access (unless other role permissions grant broader access).
  5. 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.