Skip to main content
Frappe Helpdesk uses Frappe Framework’s role-based permission system combined with custom permission logic to control access to tickets, knowledge base articles, and administrative functions.

Role Hierarchy

Frappe Helpdesk defines several roles with different permission levels:

Customer

  • Can create tickets via customer portal
  • Can view their own tickets
  • Can comment on their tickets
  • Can view public knowledge base articles
  • Cannot access agent dashboard or settings

Agent

  • Can view assigned tickets
  • Can respond to and update tickets
  • Can create saved replies (personal or global)
  • Can view knowledge base articles
  • Can access agent dashboard
  • Limited to tickets within their permission scope

Agent Manager

  • All Agent permissions
  • Can manage other agents
  • Can create and edit teams
  • Can configure assignment rules
  • Can access helpdesk settings
  • Can view all tickets (subject to team restrictions)
  • Includes System Manager role for administrative access

System Manager

  • Full administrative access
  • Can configure all helpdesk settings
  • Can manage users and roles
  • Can customize doctypes and workflows
  • Can access all data without restrictions

Team-Based Permissions

When team restrictions are enabled, permissions are further controlled:
restrict_tickets_by_agent_group
boolean
default:"false"
Enable team-based access control for tickets.When enabled:
  • Agents can only view tickets assigned to their team(s)
  • Ticket queries are filtered by team membership
  • Cross-team visibility requires explicit permission or Manager role
do_not_restrict_tickets_without_an_agent_group
boolean
default:"false"
Allow agents to see tickets that don’t have a team assigned.Only applies when restrict_tickets_by_agent_group is enabled.
assign_within_team
boolean
default:"false"
Restrict ticket assignment to team members only.When enabled, agents can only assign tickets to members of the ticket’s team.

Permission Logic

The system implements custom permission checks in doctypes:
# From HD Ticket doctype
def has_permission(doc, user=None, permission_type=None):
    if not user:
        user = frappe.session.user
    
    # System managers have full access
    if "System Manager" in frappe.get_roles(user):
        return True
    
    # Customers can only see their own tickets
    if doc.raised_by == user:
        return True
    
    # Check if user is an agent
    if not frappe.db.exists("HD Agent", {"user": user, "is_active": 1}):
        return False
    
    # If team restrictions are enabled
    if frappe.db.get_single_value("HD Settings", "restrict_tickets_by_agent_group"):
        # Check if ticket has a team
        if doc.agent_group:
            # Check if user is in the ticket's team
            return is_user_in_team(user, doc.agent_group)
        else:
            # Check if unassigned tickets are visible
            return frappe.db.get_single_value(
                "HD Settings",
                "do_not_restrict_tickets_without_an_agent_group"
            )
    
    # No restrictions - all agents can see
    return True

Guest Ticket Creation

Control whether unauthenticated users can create tickets:
allow_anyone_to_create_tickets
boolean
default:"false"
Allow guest users to create tickets without authentication.When enabled:
  • Adds “create” permission for Guest role on HD Ticket
  • Useful for webform integrations
  • Automatically removed when disabled
Implementation:
# From hd_settings.py
def update_ticket_permissions(self):
    if self.allow_anyone_to_create_tickets:
        set_guest_ticket_creation_permission()
    else:
        remove_guest_ticket_creation_permission()

Saved Replies Permissions

disable_saved_replies_global_scope
boolean
default:"false"
Disable global saved replies when team restrictions are enabled.When enabled:
  • Agents can only create team-scoped saved replies
  • Existing global replies are hidden
  • Useful for organizations with strict team separation

Query Filtering

Permissions are enforced at the database query level using Frappe Query Builder:
import frappe
from frappe.query_builder import Field

def get_permitted_tickets(user):
    """Get tickets visible to the current user"""
    
    Ticket = frappe.qb.DocType("HD Ticket")
    query = frappe.qb.get_query(
        "HD Ticket",
        fields=["name", "subject", "status"],
        ignore_permissions=False  # Enforce permissions
    )
    
    # Additional filters based on role
    if "Customer" in frappe.get_roles(user):
        query = query.where(Ticket.raised_by == user)
    
    if frappe.db.get_single_value("HD Settings", "restrict_tickets_by_agent_group"):
        # Filter by team membership
        user_teams = get_user_teams(user)
        query = query.where(Ticket.agent_group.isin(user_teams))
    
    return query.run(as_dict=True)

Permission API

Check User Permissions

import frappe

# Check if user has permission on a document
has_perm = frappe.has_permission(
    "HD Ticket",
    ptype="read",
    doc="TICKET-001",
    user="[email protected]"
)

# Check role membership
has_role = frappe.has_role("Agent Manager", user="[email protected]")

# Get user's roles
roles = frappe.get_roles("[email protected]")

Grant Permissions

import frappe

# Add role to user
user = frappe.get_doc("User", "[email protected]")
user.add_roles("Agent", "Agent Manager")

# Remove role from user
user.remove_roles("Agent Manager")

Permission Levels

Frappe supports different permission types:
  • Read: View the document
  • Write: Edit the document
  • Create: Create new documents
  • Delete: Delete documents
  • Submit: Submit documents (if submittable)
  • Cancel: Cancel submitted documents
  • Amend: Amend cancelled documents
Configure these in DocType permissions or through custom has_permission methods.

Field-Level Permissions

Restrict access to specific fields based on roles:
# In doctype JSON definition
{
    "fieldname": "internal_notes",
    "fieldtype": "Text",
    "label": "Internal Notes",
    "permlevel": 1  # Requires separate permission level
}
Configure permission levels in DocType Permissions.

Best Practices

  1. Principle of Least Privilege: Grant minimum permissions needed
  2. Use Teams: Organize agents into teams for natural permission boundaries
  3. Regular Audits: Review role assignments quarterly
  4. Test Restrictions: Verify team restrictions work as expected before enabling
  5. Document Custom Logic: Comment custom permission code thoroughly
  6. Monitor Access: Log permission-related issues for security review
  7. Avoid Bypassing: Use ignore_permissions=True sparingly and only when necessary

Custom Permission Rules

Extend permissions with custom logic:
# In custom_permissions.py
import frappe

@frappe.whitelist()
def can_assign_ticket(ticket_name, assignee):
    """Check if current user can assign ticket to specified agent"""
    
    ticket = frappe.get_doc("HD Ticket", ticket_name)
    
    # Managers can assign to anyone
    if frappe.has_role("Agent Manager"):
        return True
    
    # If team restrictions enabled, check team membership
    if frappe.db.get_single_value("HD Settings", "assign_within_team"):
        if ticket.agent_group:
            return is_user_in_team(assignee, ticket.agent_group)
    
    return True

Security Considerations

  1. SQL Injection: Always use Query Builder or parameterized queries
  2. XSS Prevention: Sanitize user input in custom fields
  3. CSRF Protection: Frappe automatically handles this for whitelisted methods
  4. Session Hijacking: Use secure cookies and HTTPS in production
  5. Data Leakage: Test permission filters thoroughly to prevent unauthorized access

Troubleshooting

  • Permission Denied: Check user’s roles and team membership
  • Can’t See Tickets: Verify team restrictions settings and team assignments
  • Assignment Fails: Check assign_within_team setting and team membership
  • Guest Creation Not Working: Verify allow_anyone_to_create_tickets is enabled
  • Custom Permissions Not Applied: Clear cache and restart server after permission changes

Build docs developers (and LLMs) love