test_permission_matrix.py 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299
  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. assert "device-observability:edit" in editor
  35. assert "governance-audit:read" not in editor
  36. assert "governance-audit:read" in admin
  37. assert "governance-audit:seal" in admin
  38. def test_data_development_paths_have_specific_write_policies():
  39. from app.core.system.permissions import permission_for_request
  40. assert permission_for_request(
  41. "/api/development/v1/ingestion-jobs", "POST"
  42. ) == ("ingestion:run",)
  43. assert permission_for_request(
  44. "/api/development/v1/ingestion-jobs/job-1/retry", "POST"
  45. ) == ("ingestion:admin",)
  46. assert permission_for_request(
  47. "/api/development/v1/ingestion-jobs/job-1/cancel", "POST"
  48. ) == ("ingestion:run",)
  49. assert permission_for_request(
  50. "/api/development/v1/ingestion-jobs/job-1/execute", "POST"
  51. ) == ("ingestion:run",)
  52. assert permission_for_request(
  53. "/api/development/v1/ingestion-jobs/job-1/catalog-snapshots", "GET"
  54. ) == ("governance:read",)
  55. assert permission_for_request(
  56. "/api/development/v1/ingestion-jobs/job-1/evidence", "GET"
  57. ) == ("governance:read",)
  58. assert permission_for_request(
  59. "/api/development/v1/data-elements", "POST"
  60. ) == ("data-elements:edit",)
  61. assert permission_for_request(
  62. "/api/development/v1/data-elements/element-1/transitions", "POST"
  63. ) == ("data-elements:edit",)
  64. assert permission_for_request(
  65. "/api/development/v1/candidate-decisions", "POST"
  66. ) == ("data-elements:edit",)
  67. assert permission_for_request(
  68. "/api/development/v1/device-assets", "GET"
  69. ) == ("governance:read",)
  70. assert permission_for_request(
  71. "/api/development/v1/device-assets/asset-1", "GET"
  72. ) == ("governance:read",)
  73. assert permission_for_request(
  74. "/api/development/v1/device-assets/import", "POST"
  75. ) == ("device-assets:edit",)
  76. assert permission_for_request(
  77. "/api/development/v1/device-semantics/profile", "GET"
  78. ) == ("governance:read",)
  79. assert permission_for_request(
  80. "/api/development/v1/device-semantics/bootstrap", "POST"
  81. ) == ("device-semantics:edit",)
  82. assert permission_for_request(
  83. "/api/development/v1/device-semantics/codes/code-1/submit",
  84. "POST",
  85. ) == ("device-semantics:edit",)
  86. assert permission_for_request(
  87. "/api/development/v1/device-semantics/codes/code-1/review",
  88. "POST",
  89. ) == ("device-semantics:review",)
  90. assert permission_for_request(
  91. "/api/development/v1/device-entities/candidates", "GET"
  92. ) == ("governance:read",)
  93. assert permission_for_request(
  94. "/api/development/v1/device-entities/candidates/generate", "POST"
  95. ) == ("device-entities:edit",)
  96. assert permission_for_request(
  97. "/api/development/v1/device-entities/candidates/candidate-1/review",
  98. "POST",
  99. ) == ("device-entities:review",)
  100. assert permission_for_request(
  101. "/api/development/v1/device-entities/merges/merge-1/rollback",
  102. "POST",
  103. ) == ("device-entities:review",)
  104. assert permission_for_request(
  105. "/api/development/v1/device-quality/profile", "GET"
  106. ) == ("governance:read",)
  107. assert permission_for_request(
  108. "/api/development/v1/device-quality/bootstrap", "POST"
  109. ) == ("device-quality:edit",)
  110. assert permission_for_request(
  111. "/api/development/v1/device-quality/profile/versions/version-1/publish",
  112. "POST",
  113. ) == ("device-quality:publish",)
  114. assert permission_for_request(
  115. "/api/development/v1/device-quality/runs", "POST"
  116. ) == ("device-quality:execute",)
  117. assert permission_for_request(
  118. "/api/development/v1/device-quality/issues", "GET"
  119. ) == ("governance:read",)
  120. assert permission_for_request(
  121. "/api/development/v1/device-quality/issues/import", "POST"
  122. ) == ("quality-issues:edit",)
  123. assert permission_for_request(
  124. "/api/development/v1/device-quality/issues/issue-1/review",
  125. "POST",
  126. ) == ("quality-issues:review",)
  127. assert permission_for_request(
  128. "/api/development/v1/device-observability/events",
  129. "GET",
  130. ) == ("governance:read",)
  131. assert permission_for_request(
  132. "/api/development/v1/device-observability/graph",
  133. "GET",
  134. ) == ("governance:read",)
  135. assert permission_for_request(
  136. "/api/development/v1/device-observability/root-cause",
  137. "GET",
  138. ) == ("governance:read",)
  139. assert permission_for_request(
  140. "/api/development/v1/device-observability/import",
  141. "POST",
  142. ) == ("device-observability:edit",)
  143. assert permission_for_request(
  144. "/api/development/v1/governance-metrics/summary",
  145. "GET",
  146. ) == ("governance:read",)
  147. assert permission_for_request(
  148. "/api/development/v1/governance-metrics/details",
  149. "GET",
  150. ) == ("governance:read",)
  151. assert permission_for_request(
  152. "/api/system/governance-audit/events",
  153. "GET",
  154. ) == ("governance-audit:read",)
  155. assert permission_for_request(
  156. "/api/system/governance-audit/seals",
  157. "POST",
  158. ) == ("governance-audit:seal",)
  159. def test_business_domain_read_endpoints_are_available_to_viewers():
  160. from app.core.system.permissions import permission_for_request
  161. assert permission_for_request("/api/bd/list", "POST") == (
  162. "governance:read",
  163. )
  164. assert permission_for_request("/api/bd/detail", "POST") == (
  165. "governance:read",
  166. )
  167. assert permission_for_request("/api/bd/save", "POST") == (
  168. "governance:edit",
  169. )
  170. def test_permissions_are_derived_server_side_from_roles():
  171. from app.core.system.permissions import permissions_for_roles
  172. permissions = permissions_for_roles(["viewer", "editor"])
  173. assert "governance:read" in permissions
  174. assert "governance:edit" in permissions
  175. assert "users:manage" not in permissions
  176. def test_every_api_path_has_an_explicit_policy():
  177. from app import create_app
  178. from app.core.system.permissions import permission_for_request
  179. app = create_app()
  180. for rule in app.url_map.iter_rules():
  181. if not rule.rule.startswith("/api/"):
  182. continue
  183. for method in rule.methods - {"HEAD", "OPTIONS"}:
  184. assert permission_for_request(rule.rule, method) is not None, (
  185. method,
  186. rule.rule,
  187. )
  188. def test_knowledge_search_and_ask_posts_are_read_only():
  189. from app.core.system.permissions import READ_GOVERNANCE, permission_for_request
  190. assert permission_for_request("/api/knowledge/search", "POST") == (
  191. READ_GOVERNANCE,
  192. )
  193. assert permission_for_request("/api/knowledge/ask", "POST") == (READ_GOVERNANCE,)
  194. def test_knowledge_admin_routes_require_dedicated_manage_permission():
  195. from app.core.system.permissions import KNOWLEDGE_MANAGE, permission_for_request
  196. assert permission_for_request("/api/knowledge/admin/sync", "GET") == (
  197. KNOWLEDGE_MANAGE,
  198. )
  199. assert permission_for_request(
  200. "/api/knowledge/admin/change-sets/id/retry", "POST"
  201. ) == (KNOWLEDGE_MANAGE,)
  202. assert permission_for_request(
  203. "/api/knowledge/admin/device-sources", "GET"
  204. ) == (KNOWLEDGE_MANAGE,)
  205. assert permission_for_request(
  206. "/api/knowledge/admin/device-sources/id/scope", "PUT"
  207. ) == (KNOWLEDGE_MANAGE,)
  208. assert permission_for_request(
  209. "/api/knowledge/admin/query-audits", "GET"
  210. ) == (KNOWLEDGE_MANAGE,)
  211. def test_data_rule_routes_separate_read_authoring_and_release_permissions():
  212. from app.core.system.permissions import (
  213. DATAFLOW_RELEASE,
  214. RULES_EDIT,
  215. RULES_PUBLISH,
  216. RULES_READ,
  217. permission_for_request,
  218. )
  219. assert permission_for_request("/api/rules/capabilities", "GET") == (
  220. RULES_READ,
  221. )
  222. assert permission_for_request("/api/rules/interpret", "POST") == (
  223. RULES_EDIT,
  224. )
  225. assert permission_for_request("/api/rules/validate", "POST") == (
  226. RULES_EDIT,
  227. )
  228. assert permission_for_request(
  229. "/api/rules/versions/id/publish", "POST"
  230. ) == (RULES_PUBLISH,)
  231. assert permission_for_request(
  232. "/api/rules/production-lines/id/release", "POST"
  233. ) == (DATAFLOW_RELEASE,)
  234. def test_data_factory_actions_have_independent_permissions():
  235. from app.core.system.permissions import (
  236. DATAFLOW_ACTIVATE,
  237. DATAFLOW_CANARY,
  238. DATAFLOW_DEPLOY,
  239. DATAFLOW_ROLLBACK,
  240. RULES_EXECUTE,
  241. permission_for_request,
  242. )
  243. assert permission_for_request("/api/rules/deployments", "POST") == (
  244. DATAFLOW_DEPLOY,
  245. )
  246. assert permission_for_request(
  247. "/api/rules/deployments/id/deploy-disabled", "POST"
  248. ) == (DATAFLOW_DEPLOY,)
  249. assert permission_for_request(
  250. "/api/rules/deployments/id/canary", "POST"
  251. ) == (DATAFLOW_CANARY, RULES_EXECUTE)
  252. assert permission_for_request(
  253. "/api/rules/deployments/id/execute", "POST"
  254. ) == (RULES_EXECUTE,)
  255. assert permission_for_request(
  256. "/api/rules/deployments/id/activate", "POST"
  257. ) == (DATAFLOW_ACTIVATE,)
  258. assert permission_for_request(
  259. "/api/rules/deployments/id/rollback", "POST"
  260. ) == (DATAFLOW_ROLLBACK,)
  261. assert permission_for_request(
  262. "/api/rules/deployments/id/reconcile-deploy", "POST"
  263. ) == (DATAFLOW_DEPLOY,)
  264. assert permission_for_request(
  265. "/api/rules/deployments/id/reconcile-activate", "POST"
  266. ) == (DATAFLOW_ACTIVATE,)
  267. assert permission_for_request(
  268. "/api/rules/deployments/id/reconcile-rollback", "POST"
  269. ) == (DATAFLOW_ROLLBACK,)
  270. assert permission_for_request(
  271. "/api/rules/deployments/id/reconcile-execute", "POST"
  272. ) == (RULES_EXECUTE,)