test_permission_matrix.py 9.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254
  1. def test_fixed_role_permission_matrix_is_monotonic():
  2. from app.core.system.permissions import ROLE_PERMISSIONS
  3. viewer = ROLE_PERMISSIONS["viewer"]
  4. editor = ROLE_PERMISSIONS["editor"]
  5. admin = ROLE_PERMISSIONS["admin"]
  6. assert viewer < editor < admin
  7. assert "governance:read" in viewer
  8. assert "governance:edit" in editor
  9. assert "users:manage" in admin
  10. assert "workflow:activate" in admin
  11. assert "ingestion:run" in editor
  12. assert "data-elements:edit" in editor
  13. assert "ontologies:edit" in editor
  14. assert "governance:responsibilities:read" in viewer
  15. assert "ingestion:admin" in admin
  16. assert "evidence:download" in admin
  17. assert "data-elements:publish" in admin
  18. assert "ontologies:publish" in admin
  19. assert "governance:responsibilities:manage" in admin
  20. assert "device-assets:edit" in editor
  21. assert "device-semantics:edit" in editor
  22. assert "device-semantics:review" not in editor
  23. assert "device-semantics:review" in admin
  24. assert "device-entities:edit" in editor
  25. assert "device-entities:review" not in editor
  26. assert "device-entities:review" in admin
  27. assert "device-quality:edit" in editor
  28. assert "device-quality:execute" in editor
  29. assert "device-quality:publish" not in editor
  30. assert "device-quality:publish" in admin
  31. assert "quality-issues:edit" in editor
  32. assert "quality-issues:review" not in editor
  33. assert "quality-issues:review" in admin
  34. def test_data_development_paths_have_specific_write_policies():
  35. from app.core.system.permissions import permission_for_request
  36. assert permission_for_request(
  37. "/api/development/v1/ingestion-jobs", "POST"
  38. ) == ("ingestion:run",)
  39. assert permission_for_request(
  40. "/api/development/v1/ingestion-jobs/job-1/retry", "POST"
  41. ) == ("ingestion:admin",)
  42. assert permission_for_request(
  43. "/api/development/v1/ingestion-jobs/job-1/cancel", "POST"
  44. ) == ("ingestion:run",)
  45. assert permission_for_request(
  46. "/api/development/v1/ingestion-jobs/job-1/execute", "POST"
  47. ) == ("ingestion:run",)
  48. assert permission_for_request(
  49. "/api/development/v1/ingestion-jobs/job-1/catalog-snapshots", "GET"
  50. ) == ("governance:read",)
  51. assert permission_for_request(
  52. "/api/development/v1/ingestion-jobs/job-1/evidence", "GET"
  53. ) == ("governance:read",)
  54. assert permission_for_request(
  55. "/api/development/v1/data-elements", "POST"
  56. ) == ("data-elements:edit",)
  57. assert permission_for_request(
  58. "/api/development/v1/data-elements/element-1/transitions", "POST"
  59. ) == ("data-elements:edit",)
  60. assert permission_for_request(
  61. "/api/development/v1/candidate-decisions", "POST"
  62. ) == ("data-elements:edit",)
  63. assert permission_for_request(
  64. "/api/development/v1/device-assets", "GET"
  65. ) == ("governance:read",)
  66. assert permission_for_request(
  67. "/api/development/v1/device-assets/asset-1", "GET"
  68. ) == ("governance:read",)
  69. assert permission_for_request(
  70. "/api/development/v1/device-assets/import", "POST"
  71. ) == ("device-assets:edit",)
  72. assert permission_for_request(
  73. "/api/development/v1/device-semantics/profile", "GET"
  74. ) == ("governance:read",)
  75. assert permission_for_request(
  76. "/api/development/v1/device-semantics/bootstrap", "POST"
  77. ) == ("device-semantics:edit",)
  78. assert permission_for_request(
  79. "/api/development/v1/device-semantics/codes/code-1/submit",
  80. "POST",
  81. ) == ("device-semantics:edit",)
  82. assert permission_for_request(
  83. "/api/development/v1/device-semantics/codes/code-1/review",
  84. "POST",
  85. ) == ("device-semantics:review",)
  86. assert permission_for_request(
  87. "/api/development/v1/device-entities/candidates", "GET"
  88. ) == ("governance:read",)
  89. assert permission_for_request(
  90. "/api/development/v1/device-entities/candidates/generate", "POST"
  91. ) == ("device-entities:edit",)
  92. assert permission_for_request(
  93. "/api/development/v1/device-entities/candidates/candidate-1/review",
  94. "POST",
  95. ) == ("device-entities:review",)
  96. assert permission_for_request(
  97. "/api/development/v1/device-entities/merges/merge-1/rollback",
  98. "POST",
  99. ) == ("device-entities:review",)
  100. assert permission_for_request(
  101. "/api/development/v1/device-quality/profile", "GET"
  102. ) == ("governance:read",)
  103. assert permission_for_request(
  104. "/api/development/v1/device-quality/bootstrap", "POST"
  105. ) == ("device-quality:edit",)
  106. assert permission_for_request(
  107. "/api/development/v1/device-quality/profile/versions/version-1/publish",
  108. "POST",
  109. ) == ("device-quality:publish",)
  110. assert permission_for_request(
  111. "/api/development/v1/device-quality/runs", "POST"
  112. ) == ("device-quality:execute",)
  113. assert permission_for_request(
  114. "/api/development/v1/device-quality/issues", "GET"
  115. ) == ("governance:read",)
  116. assert permission_for_request(
  117. "/api/development/v1/device-quality/issues/import", "POST"
  118. ) == ("quality-issues:edit",)
  119. assert permission_for_request(
  120. "/api/development/v1/device-quality/issues/issue-1/review",
  121. "POST",
  122. ) == ("quality-issues:review",)
  123. def test_business_domain_read_endpoints_are_available_to_viewers():
  124. from app.core.system.permissions import permission_for_request
  125. assert permission_for_request("/api/bd/list", "POST") == (
  126. "governance:read",
  127. )
  128. assert permission_for_request("/api/bd/detail", "POST") == (
  129. "governance:read",
  130. )
  131. assert permission_for_request("/api/bd/save", "POST") == (
  132. "governance:edit",
  133. )
  134. def test_permissions_are_derived_server_side_from_roles():
  135. from app.core.system.permissions import permissions_for_roles
  136. permissions = permissions_for_roles(["viewer", "editor"])
  137. assert "governance:read" in permissions
  138. assert "governance:edit" in permissions
  139. assert "users:manage" not in permissions
  140. def test_every_api_path_has_an_explicit_policy():
  141. from app import create_app
  142. from app.core.system.permissions import permission_for_request
  143. app = create_app()
  144. for rule in app.url_map.iter_rules():
  145. if not rule.rule.startswith("/api/"):
  146. continue
  147. for method in rule.methods - {"HEAD", "OPTIONS"}:
  148. assert permission_for_request(rule.rule, method) is not None, (
  149. method,
  150. rule.rule,
  151. )
  152. def test_knowledge_search_and_ask_posts_are_read_only():
  153. from app.core.system.permissions import READ_GOVERNANCE, permission_for_request
  154. assert permission_for_request("/api/knowledge/search", "POST") == (
  155. READ_GOVERNANCE,
  156. )
  157. assert permission_for_request("/api/knowledge/ask", "POST") == (READ_GOVERNANCE,)
  158. def test_knowledge_admin_routes_require_dedicated_manage_permission():
  159. from app.core.system.permissions import KNOWLEDGE_MANAGE, permission_for_request
  160. assert permission_for_request("/api/knowledge/admin/sync", "GET") == (
  161. KNOWLEDGE_MANAGE,
  162. )
  163. assert permission_for_request(
  164. "/api/knowledge/admin/change-sets/id/retry", "POST"
  165. ) == (KNOWLEDGE_MANAGE,)
  166. def test_data_rule_routes_separate_read_authoring_and_release_permissions():
  167. from app.core.system.permissions import (
  168. DATAFLOW_RELEASE,
  169. RULES_EDIT,
  170. RULES_PUBLISH,
  171. RULES_READ,
  172. permission_for_request,
  173. )
  174. assert permission_for_request("/api/rules/capabilities", "GET") == (
  175. RULES_READ,
  176. )
  177. assert permission_for_request("/api/rules/interpret", "POST") == (
  178. RULES_EDIT,
  179. )
  180. assert permission_for_request("/api/rules/validate", "POST") == (
  181. RULES_EDIT,
  182. )
  183. assert permission_for_request(
  184. "/api/rules/versions/id/publish", "POST"
  185. ) == (RULES_PUBLISH,)
  186. assert permission_for_request(
  187. "/api/rules/production-lines/id/release", "POST"
  188. ) == (DATAFLOW_RELEASE,)
  189. def test_data_factory_actions_have_independent_permissions():
  190. from app.core.system.permissions import (
  191. DATAFLOW_ACTIVATE,
  192. DATAFLOW_CANARY,
  193. DATAFLOW_DEPLOY,
  194. DATAFLOW_ROLLBACK,
  195. RULES_EXECUTE,
  196. permission_for_request,
  197. )
  198. assert permission_for_request("/api/rules/deployments", "POST") == (
  199. DATAFLOW_DEPLOY,
  200. )
  201. assert permission_for_request(
  202. "/api/rules/deployments/id/deploy-disabled", "POST"
  203. ) == (DATAFLOW_DEPLOY,)
  204. assert permission_for_request(
  205. "/api/rules/deployments/id/canary", "POST"
  206. ) == (DATAFLOW_CANARY, RULES_EXECUTE)
  207. assert permission_for_request(
  208. "/api/rules/deployments/id/execute", "POST"
  209. ) == (RULES_EXECUTE,)
  210. assert permission_for_request(
  211. "/api/rules/deployments/id/activate", "POST"
  212. ) == (DATAFLOW_ACTIVATE,)
  213. assert permission_for_request(
  214. "/api/rules/deployments/id/rollback", "POST"
  215. ) == (DATAFLOW_ROLLBACK,)
  216. assert permission_for_request(
  217. "/api/rules/deployments/id/reconcile-deploy", "POST"
  218. ) == (DATAFLOW_DEPLOY,)
  219. assert permission_for_request(
  220. "/api/rules/deployments/id/reconcile-activate", "POST"
  221. ) == (DATAFLOW_ACTIVATE,)
  222. assert permission_for_request(
  223. "/api/rules/deployments/id/reconcile-rollback", "POST"
  224. ) == (DATAFLOW_ROLLBACK,)
  225. assert permission_for_request(
  226. "/api/rules/deployments/id/reconcile-execute", "POST"
  227. ) == (RULES_EXECUTE,)