mcpField
F
frappe-core-permissions

frappe-core-permissions

github.com/impertio-studioセキュリティ198124 回インストール30 週間前に更新
リポジトリ

frappe-core-permissions について

Frappe 開発者向けの権限システムのリファレンススキルです。複数の権限レイヤー、API、フック、安全なクエリパターンを解説します。

主な機能

  1. 015 層の権限モデル
  2. 02権限チェックと共有 API
  3. 03レコード・フィールド単位のアクセス制御
  4. 04権限フックとクエリフィルタリング
  5. 05データマスキングのルール
  6. 06安全なクエリの実践

活用シーン

  1. 01DocType のアクセスルール設計
  2. 02ユーザーに見せるレコードやフィールドの制限
  3. 03リストクエリへの権限フィルター適用

インストール

コマンドをコピーしてターミナルで実行します。

$npx skills add https://github.com/impertio-studio/frappe_claude_skill_package --skill frappe-core-permissions

インストール後、Claude Code で/frappe-core-permissionsを入力して読み込まれたか確認します。

同梱ファイル

SKILL.md と一緒にインストールされる参考資料やスクリプト。

references/
anti-patterns.md
examples.md
permission-api-reference.md
permission-hooks-reference.md
permission-types-reference.md

詳細

Frappe の権限

5 層からなる Frappe 権限システムの決定論的なパターンです。


権限レイヤー

レイヤー制御する内容設定方法バージョン
ロール権限ユーザーが実行できる操作DocType の権限テーブルすべて
ユーザー権限ユーザーに表示されるレコードUser Permission DocTypeすべて
権限レベルユーザーに表示・編集されるフィールドフィールドの permlevel プロパティすべて
権限フックカスタムの拒否ロジックhooks.pyすべて
データマスキングマスクされたフィールド値フィールドの mask プロパティ[v16+]

判断ツリー

Need to control access?
├── Who can Create/Read/Write/Delete a DocType? → Role Permissions
├── Which specific records can a user see? → User Permissions
├── Which fields should be hidden? → Perm Levels (permlevel 1+)
├── Which fields show masked values? → Data Masking [v16+]
├── Custom runtime deny logic? → has_permission hook
├── Filter list queries dynamically? → permission_query_conditions hook
└── Share one document with one user? → frappe.share

Checking permissions in code?
├── Before action → frappe.has_permission() or doc.has_permission()
├── Raise on denial → doc.check_permission() or throw=True
├── System bypass → doc.flags.ignore_permissions = True (ALWAYS document why)
└── List query → ALWAYS use frappe.get_list() for user-facing data

権限の種類

種類API チェック適用対象
readfrappe.has_permission(dt, "read")すべての DocType
writefrappe.has_permission(dt, "write")すべての DocType
createfrappe.has_permission(dt, "create")すべての DocType
deletefrappe.has_permission(dt, "delete")すべての DocType
submitfrappe.has_permission(dt, "submit")送信可能な DocType のみ
cancelfrappe.has_permission(dt, "cancel")送信可能な DocType のみ
amendfrappe.has_permission(dt, "amend")送信可能な DocType のみ
selectfrappe.has_permission(dt, "select")リンクフィールド [v14+]
report該当なしレポートビルダーへのアクセス
export該当なしExcel/CSV エクスポート
import該当なしデータインポートツール
share該当なし他のユーザーとの共有
print該当なし印刷/PDF 生成
email該当なしメール送信
maskマスクされていない表示に対するロール権限データマスキング [v16+]

自動ロール

ロール割り当て先備考
Guest全員(匿名ユーザーを含む)公開ページ
All登録済みユーザー全員基本的な認証済みアクセス
AdministratorAdministrator ユーザーのみ常にすべての権限を持ちます
Desk Userシステムユーザーのみ[v15+]

基本 API

権限を確認する
# DocType-level
frappe.has_permission("Sales Order", "write")

# Document-level (by name or object)
frappe.has_permission("Sales Order", "write", "SO-00001")
frappe.has_permission("Sales Order", "write", doc=doc)

# For specific user
frappe.has_permission("Sales Order", "read", user="[email protected]")

# Throw on denial
frappe.has_permission("Sales Order", "delete", throw=True)

# Debug mode — prints evaluation steps
frappe.has_permission("Sales Order", "read", debug=True)
print(frappe.local.permission_debug_log)
ドキュメントインスタンスのメソッド
doc = frappe.get_doc("Sales Order", "SO-00001")

# Returns bool
if doc.has_permission("write"):
    doc.status = "Approved"
    doc.save()

# Raises frappe.PermissionError if denied
doc.check_permission("write")
実効権限を取得する
from frappe.permissions import get_doc_permissions

perms = get_doc_permissions(doc)
# {'read': 1, 'write': 1, 'create': 0, 'delete': 0, ...}

perms = get_doc_permissions(doc, user="[email protected]")

ユーザー権限(レコードレベル)

ユーザーが特定のリンクフィールド値(特定の会社、地域など)だけにアクセスできるよう制限します。

from frappe.permissions import add_user_permission, remove_user_permission

# Restrict user to one company
add_user_permission(
    doctype="Company",
    name="My Company",
    user="[email protected]",
    is_default=1,            # auto-fill in new documents
    applicable_for="Sales Order"  # only for this DocType (optional)
)

# Remove restriction
remove_user_permission("Company", "My Company", "[email protected]")

# Query current restrictions
from frappe.permissions import get_user_permissions
perms = get_user_permissions("[email protected]")
# {"Company": [{"doc": "My Company", "is_default": 1}], ...}

共有(ドキュメントレベル)

特定のユーザーに単一のドキュメントへのアクセス権を付与します。

from frappe.share import add as add_share, remove as remove_share

add_share("Sales Order", "SO-00001", "[email protected]",
          read=1, write=1, share=0, notify=1)

remove_share("Sales Order", "SO-00001", "[email protected]")

# Share with everyone
add_share("Sales Order", "SO-00001", everyone=1, read=1)

フィールドレベルの権限(権限レベル)

フィールドを permlevel(0~9)でグループ化します。上位レベルを付与する前に、必ずレベル 0 を付与する必要があります。

{
  "fields": [
    {"fieldname": "employee_name", "permlevel": 0},
    {"fieldname": "salary",        "permlevel": 1}
  ],
  "permissions": [
    {"role": "Employee",   "permlevel": 0, "read": 1},
    {"role": "HR Manager", "permlevel": 0, "read": 1, "write": 1},
    {"role": "HR Manager", "permlevel": 1, "read": 1, "write": 1}
  ]
}

ルール:レベルは階層を意味しません。レベル 2 がレベル 1 より「上位」というわけではありません。それぞれ独立したフィールドグループです。


データマスキング [v16+]

mask=1 のフィールドは、mask 権限のないユーザーにはマスクされた値(例:****、+91-811XXXXXXX)を表示します。

{
  "fieldname": "phone_number", "fieldtype": "Data", "mask": 1
}

マスクされていない値を表示する必要があるロールには、mask 権限を付与します。

{"role": "HR Manager", "permlevel": 0, "read": 1, "mask": 1}

重大な注意:データマスキングは frappe.db.sql() や、未加工の SQL を使用するクエリレポートには適用されません。カスタム SQL クエリでは、必ず手動でマスクしてください。


権限フック

has_permission:カスタム拒否ロジック

アクセスを拒否することしかできません。許可するために True を返すことは絶対にありません。標準チェックを続行するには、常に None を返します。

# hooks.py
has_permission = {
    "Sales Order": "myapp.permissions.check_order_permission"
}
# myapp/permissions.py
def check_order_permission(doc, ptype, user):
    if ptype == "write" and doc.docstatus == 2:
        if "Sales Manager" not in frappe.get_roles(user):
            return False
    return None  # ALWAYS return None by default
permission_query_conditions:リストクエリをフィルタリングする

SQL の WHERE 句の一部を返します。影響するのは get_list() のみで、get_all() には影響しません。

# hooks.py
permission_query_conditions = {
    "Customer": "myapp.permissions.customer_query"
}
def customer_query(user):
    if not user:
        user = frappe.session.user
    if "Sales Manager" in frappe.get_roles(user):
        return ""
    return f"`tabCustomer`.owner = {frappe.db.escape(user)}"

必ず frappe.db.escape() を使用してください。未加工のユーザー入力を文字列連結に使用することは絶対に避けてください。


get_list と get_all の違い

メソッドユーザー権限クエリフック用途
frappe.get_list()適用される適用されるユーザー向けのクエリ
frappe.get_all()無視される無視されるシステム/バックグラウンドのクエリ

ユーザーにデータを返す場合は、必ず get_list() を使用してください。get_all() はすべての権限フィルタリングを回避します。


よくあるパターン

所有者のみ編集可能
{"role": "Sales User", "read": 1, "write": 1, "create": 1, "if_owner": 1}
ロール制限付きエンドポイント
@frappe.whitelist()
def sensitive_action():
    frappe.only_for(["Manager", "Administrator"])
    # Only reaches here if user has one of these roles
権限を回避する(理由を必ず記載!)
# On document — ALWAYS add a comment explaining the reason
doc.flags.ignore_permissions = True
doc.save()

# On method call
doc.save(ignore_permissions=True)
doc.insert(ignore_permissions=True)

重要なルール

  1. 必ずアクセス制御には frappe.has_permission() を使用してください。ロールを直接確認してはいけません
  2. 必ずユーザー向けのクエリには frappe.get_list() を使用してください。get_all() を使用してはいけません
  3. 必ずクエリフック内の SQL をエスケープしてください — frappe.db.escape(user)
  4. 必ずクエリフックではテーブル名にプレフィックスを付けてください — `tabDocType`.fieldname
  5. 必ずhas_permission フックではデフォルトで None を返してください。True を返してはいけません
  6. 必ず権限を変更した後はキャッシュをクリアしてください — frappe.clear_cache()
  7. 必ずignore_permissions の使用箇所にはコメントで説明を記載してください
  8. 絶対にhas_permission フックでエラーをスローしてはいけません。拒否するには False を返してください
  9. 絶対にpermlevel 0 を許可せずに permlevel 1 以上を許可してはいけません
  10. 絶対にデータマスキングがカスタム SQL クエリにも適用されると想定してはいけません[v16 以降]

アンチパターン

してはいけないこと代わりに行うこと
アクセス制御に if "Role" in frappe.get_roles() を使うfrappe.has_permission(dt, ptype)
ユーザー向けのクエリに frappe.get_all() を使うfrappe.get_list()
has_permission フックで return True を使うreturn None
SQL 内で f"owner = '{user}'" を使うf"owner = {frappe.db.escape(user)}"
権限フック内で frappe.throw() を使うreturn False
ユーザー向けの更新に frappe.db.set_value() を使う権限チェックを行ったうえで doc.save() を使う
エラーメッセージに機密データを含める汎用的な frappe.PermissionError を使う

バージョンごとの差異

機能v14v15v16
select 権限ありありあり
Desk User ロールなしありあり
データマスキング(mask フィールド)なしなしあり
mask 権限タイプなしなしあり
カスタム権限タイプなしなし試験的

権限の優先順位

  1. Administrator — 必ずすべての権限を持ちます(制限できません)
  2. ロール権限 — 割り当てられたロールに基づきます
  3. ユーザー権限 — 特定のドキュメント値に制限します
  4. has_permission フック — 拒否のみ可能です(いずれかが False なら拒否されます)
  5. 共有 — 共有されたドキュメントへのアクセスを許可します
  6. if_owner — 所有するドキュメントにさらに制限します

参考ファイル

ファイル内容
permission-types-reference.mdオプションを含むすべての権限タイプ
permission-api-reference.mdすべてのシグネチャを含む API 全体
permission-hooks-reference.mdフックのパターンと例
examples.md動作する実装例
anti-patterns.mdよくある間違いと修正方法

関連スキル

  • frappe-core-database — 権限を考慮したデータベース操作
  • frappe-core-api — 権限チェックを備えた API エンドポイント
  • frappe-syntax-controllers — コントローラーでの権限検証
  • frappe-syntax-hooks — フック設定のパターン

Frappe のドキュメントに基づき検証済み:2026-03-20 | Frappe v14/v15/v16

コメント