@appaflytech/zeyrek-ai-mcp 0.0.30 → 0.0.32

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (203) hide show
  1. package/.env.example +0 -15
  2. package/README.md +5 -5
  3. package/dist/config.js +0 -37
  4. package/dist/index.js +0 -2
  5. package/dist/knowledge/backendConstraints.js +1 -18
  6. package/dist/knowledge/boxSections.js +184 -0
  7. package/dist/knowledge/componentCatalog.js +232 -562
  8. package/dist/knowledge/componentEvents.js +21 -141
  9. package/dist/knowledge/layoutSummary.js +2 -140
  10. package/dist/knowledge/normalize.js +67 -236
  11. package/dist/knowledge/outlines.js +0 -36
  12. package/dist/knowledge/policy.js +4 -18
  13. package/dist/knowledge/reservedTables.js +0 -79
  14. package/dist/knowledge/rules.js +21 -319
  15. package/dist/knowledge/schemaGuard.js +0 -11
  16. package/dist/knowledge/styleResolve.js +38 -55
  17. package/dist/knowledge/themeResolve.js +3 -3
  18. package/dist/observability/trace.js +3 -6
  19. package/dist/platform/diagnostics.js +4 -10
  20. package/dist/platform/domSummary.js +1 -1
  21. package/dist/platform/errors.js +10 -162
  22. package/dist/platform/http.js +8 -29
  23. package/dist/platform/responses.js +5 -39
  24. package/dist/platform/{capture/capture.js → screenshot.js} +519 -105
  25. package/dist/platform/secrets.js +1 -17
  26. package/dist/platform/strictArguments.js +12 -68
  27. package/dist/platform/tokenVault.js +4 -187
  28. package/dist/resources/examples/layout-app-shell.json +707 -229
  29. package/dist/resources/examples/page-detail-profile.json +1 -2
  30. package/dist/resources/examples/page-dynamic-form.json +2 -3
  31. package/dist/resources/examples/page-flex-sections.json +1 -1
  32. package/dist/resources/examples/page-html-embed.json +1 -1
  33. package/dist/resources/examples/workflow-basic.json +2 -8
  34. package/dist/resources/guides/component.md +4 -91
  35. package/dist/resources/guides/entity.md +8 -50
  36. package/dist/resources/guides/expressions.md +5 -14
  37. package/dist/resources/guides/functions.md +1 -1
  38. package/dist/resources/guides/getting-started.md +1 -38
  39. package/dist/resources/guides/layout.md +58 -55
  40. package/dist/resources/guides/microservice.md +2 -81
  41. package/dist/resources/guides/middleware.md +2 -44
  42. package/dist/resources/guides/page.md +75 -454
  43. package/dist/resources/guides/theme.md +2 -8
  44. package/dist/resources/guides/translation.md +1 -1
  45. package/dist/resources/guides/workflow.md +1 -58
  46. package/dist/resources/rules.js +5 -11
  47. package/dist/resources/schema-fingerprint.json +5 -7
  48. package/dist/resources/schemas/CreateEntityCommand.json +0 -118
  49. package/dist/resources/schemas/CreateMicroserviceCommand.json +1 -13
  50. package/dist/resources/schemas/CreateQueryCommand.json +0 -4
  51. package/dist/resources/schemas/CreateWorkflowCommand.json +1 -143
  52. package/dist/resources/schemas/DataJobDto.json +1 -6
  53. package/dist/resources/schemas/GlobalFunctionUpdateResultDto.json +1 -21
  54. package/dist/resources/schemas/MicroserviceDto.json +3 -15
  55. package/dist/resources/schemas/PreviewMicroserviceCommand.json +1 -10
  56. package/dist/resources/schemas/PreviewWorkflowCommand.json +1 -22
  57. package/dist/resources/schemas/StepDto.json +1 -22
  58. package/dist/resources/schemas/UpdateEntityCommand.json +0 -118
  59. package/dist/resources/schemas/UpdateMicroserviceCommand.json +1 -13
  60. package/dist/resources/schemas/UpdateQueryCommand.json +0 -4
  61. package/dist/resources/schemas/UpdateViewCommand.json +2 -0
  62. package/dist/resources/schemas/UpdateWorkflowCommand.json +1 -143
  63. package/dist/server.js +1 -27
  64. package/dist/tools/access.js +2 -369
  65. package/dist/tools/applications.js +3 -60
  66. package/dist/tools/common.js +0 -7
  67. package/dist/tools/components.js +14 -118
  68. package/dist/tools/datasources.js +3 -3
  69. package/dist/tools/dependencies.js +1 -1
  70. package/dist/tools/deployments.js +2 -2
  71. package/dist/tools/designSchemaPatch.js +18 -34
  72. package/dist/tools/dtos.js +5 -36
  73. package/dist/tools/entities.js +12 -79
  74. package/dist/tools/externalComponents.js +5 -26
  75. package/dist/tools/flows.js +14 -122
  76. package/dist/tools/folders.js +1 -1
  77. package/dist/tools/imports.js +2 -5
  78. package/dist/tools/layouts.js +17 -50
  79. package/dist/tools/merges.js +1 -1
  80. package/dist/tools/microservices.js +25 -77
  81. package/dist/tools/pages.js +112 -465
  82. package/dist/tools/platformInfo.js +8 -27
  83. package/dist/tools/projects.js +9 -71
  84. package/dist/tools/rows.js +13 -69
  85. package/dist/tools/schemaOps.js +1 -61
  86. package/dist/tools/screenWrite.js +8 -37
  87. package/dist/tools/screens.js +5 -95
  88. package/dist/tools/templates.js +1 -26
  89. package/dist/tools/translations.js +1 -1
  90. package/dist/tools/variables.js +5 -11
  91. package/dist/tools/workflows.js +5 -10
  92. package/package.json +2 -2
  93. package/dist/knowledge/access.js +0 -802
  94. package/dist/knowledge/case.js +0 -41
  95. package/dist/knowledge/connectDataBind.js +0 -819
  96. package/dist/knowledge/connectDataDefinition.js +0 -341
  97. package/dist/knowledge/connectDataExecute.js +0 -93
  98. package/dist/knowledge/connectDataRules.js +0 -128
  99. package/dist/knowledge/decision.js +0 -121
  100. package/dist/knowledge/expressionPrelude.js +0 -59
  101. package/dist/knowledge/microserviceParameters.js +0 -58
  102. package/dist/knowledge/mining.js +0 -94
  103. package/dist/knowledge/mobileComponentCatalog.js +0 -859
  104. package/dist/knowledge/mobileComponentEvents.js +0 -239
  105. package/dist/knowledge/platform/lint.js +0 -385
  106. package/dist/knowledge/platform/mobile.js +0 -142
  107. package/dist/knowledge/platform/mobileLayoutLint.js +0 -324
  108. package/dist/knowledge/platform/mobileRules.js +0 -231
  109. package/dist/knowledge/platform/mobileSpacing.js +0 -82
  110. package/dist/knowledge/platform/mobileTabs.js +0 -108
  111. package/dist/knowledge/platform/nativeBoxModel.js +0 -44
  112. package/dist/knowledge/platform/profile.js +0 -25
  113. package/dist/knowledge/platform/resolve.js +0 -122
  114. package/dist/knowledge/platform/web.js +0 -39
  115. package/dist/knowledge/process.js +0 -510
  116. package/dist/knowledge/rowFilter.js +0 -43
  117. package/dist/knowledge/styleResolveNative.js +0 -527
  118. package/dist/platform/argumentAliases.js +0 -128
  119. package/dist/platform/bodyFile.js +0 -103
  120. package/dist/platform/capture/actions.js +0 -333
  121. package/dist/platform/capture/browser.js +0 -1
  122. package/dist/platform/capture/console-log.js +0 -122
  123. package/dist/platform/capture/index.js +0 -11
  124. package/dist/platform/capture/readiness.js +0 -103
  125. package/dist/platform/capture/summary.js +0 -77
  126. package/dist/platform/capture/target.js +0 -5
  127. package/dist/platform/capture/targets/studio-mobile.js +0 -15
  128. package/dist/platform/capture/targets/studio-web.js +0 -5
  129. package/dist/platform/impersonation.js +0 -45
  130. package/dist/platform/jsonArray.js +0 -24
  131. package/dist/platform/schemaReadMemory.js +0 -60
  132. package/dist/resources/examples/bpm-gorev-kontrol.json +0 -294
  133. package/dist/resources/examples/bpm-iso-dokuman.json +0 -228
  134. package/dist/resources/examples/bpm-satinalma.json +0 -376
  135. package/dist/resources/examples/connect-data-convert-verify.json +0 -107
  136. package/dist/resources/examples/connect-data-dropdown-roles.json +0 -66
  137. package/dist/resources/examples/connect-data-edit-m2m.json +0 -73
  138. package/dist/resources/examples/connect-data-query-params.json +0 -50
  139. package/dist/resources/examples/connect-data-table-entity.json +0 -147
  140. package/dist/resources/examples/connect-data-workflow-microservice.json +0 -65
  141. package/dist/resources/examples/mobile-app-shell-drawer.json +0 -243
  142. package/dist/resources/examples/mobile-app-shell-stack.json +0 -157
  143. package/dist/resources/examples/mobile-app-shell-tab.json +0 -208
  144. package/dist/resources/examples/mobile-form-screen.json +0 -365
  145. package/dist/resources/examples/mobile-list-detail-form.json +0 -672
  146. package/dist/resources/guides/access.md +0 -796
  147. package/dist/resources/guides/bpm.md +0 -686
  148. package/dist/resources/guides/case.md +0 -153
  149. package/dist/resources/guides/decision.md +0 -185
  150. package/dist/resources/guides/mobile-layout.md +0 -193
  151. package/dist/resources/guides/mobile.md +0 -495
  152. package/dist/resources/read.js +0 -41
  153. package/dist/resources/schemas/AccessExplanationDto.json +0 -144
  154. package/dist/resources/schemas/AccessMatrixDto.json +0 -170
  155. package/dist/resources/schemas/AccessPermissionDto.json +0 -57
  156. package/dist/resources/schemas/AccessPolicyDto.json +0 -223
  157. package/dist/resources/schemas/AccessResourceDto.json +0 -86
  158. package/dist/resources/schemas/AppSessionDto.json +0 -53
  159. package/dist/resources/schemas/ConnectDataDescribeDto.json +0 -344
  160. package/dist/resources/schemas/ConnectDataRequestModel.json +0 -202
  161. package/dist/resources/schemas/ConnectDataResultDto.json +0 -421
  162. package/dist/resources/schemas/ConnectorCredentialDto.json +0 -70
  163. package/dist/resources/schemas/ConvertConnectDataToMicroserviceCommand.json +0 -37
  164. package/dist/resources/schemas/CreateAccessPolicyCommand.json +0 -209
  165. package/dist/resources/schemas/CreateDecisionCommand.json +0 -221
  166. package/dist/resources/schemas/CreateProcessDefinitionCommand.json +0 -1337
  167. package/dist/resources/schemas/DecisionDto.json +0 -302
  168. package/dist/resources/schemas/DetachConnectDataMicroserviceCommand.json +0 -28
  169. package/dist/resources/schemas/ExplainAccessQuery.json +0 -33
  170. package/dist/resources/schemas/GenerateProcessDocumentCommand.json +0 -116
  171. package/dist/resources/schemas/OrganizationProjectDto.json +0 -335
  172. package/dist/resources/schemas/OrganizationSchemaDto.json +0 -196
  173. package/dist/resources/schemas/OrganizationSchemaVersionDto.json +0 -207
  174. package/dist/resources/schemas/PatchProcessDefinitionCommand.json +0 -76
  175. package/dist/resources/schemas/ProcessDefinitionDto.json +0 -1480
  176. package/dist/resources/schemas/ProcessSimulationDto.json +0 -96
  177. package/dist/resources/schemas/SaveConnectorCredentialCommand.json +0 -90
  178. package/dist/resources/schemas/SaveProcessEventSourceCommand.json +0 -61
  179. package/dist/resources/schemas/SaveProcessMailSourceCommand.json +0 -89
  180. package/dist/resources/schemas/SignProcessTaskCommand.json +0 -73
  181. package/dist/resources/schemas/SimulateProcessDefinitionQuery.json +0 -92
  182. package/dist/resources/schemas/UpdateAccessPolicyCommand.json +0 -209
  183. package/dist/resources/schemas/UpdateDecisionCommand.json +0 -216
  184. package/dist/resources/schemas/UpdateProcessDefinitionCommand.json +0 -1357
  185. package/dist/resources/schemas/ValidateProcessDefinitionQuery.json +0 -1339
  186. package/dist/tools/accessOperations.js +0 -135
  187. package/dist/tools/accessPolicies.js +0 -170
  188. package/dist/tools/accessRuntime.js +0 -203
  189. package/dist/tools/bpm.js +0 -343
  190. package/dist/tools/bpmGovernance.js +0 -266
  191. package/dist/tools/bpmOperations.js +0 -230
  192. package/dist/tools/bpmReporting.js +0 -154
  193. package/dist/tools/bpmRouting.js +0 -150
  194. package/dist/tools/bpmRuntime.js +0 -396
  195. package/dist/tools/case.js +0 -178
  196. package/dist/tools/caseRuntime.js +0 -89
  197. package/dist/tools/connectData.js +0 -449
  198. package/dist/tools/connectors.js +0 -193
  199. package/dist/tools/decisions.js +0 -206
  200. package/dist/tools/mobile.js +0 -221
  201. package/dist/tools/modules.js +0 -48
  202. package/dist/tools/organizationStructure.js +0 -137
  203. package/dist/tools/profileWrite.js +0 -92
@@ -1,796 +0,0 @@
1
- # Erişim Rehberi — organizasyon şeması, erişim politikası, alan koruması
2
-
3
- Kompleks projenin (`project_create` `accessModel: 'complex'`) yetki modeli: kim hangi kaydı görür,
4
- hangi eylemi yapar, hangi alanı maskeli ya da açık görür. Basit projede izin kataloğu boştur ve
5
- policy yazılmaz; alan koruması ise iki türde de kullanılır.
6
-
7
- Araçlar: `organization_schema_get` · `access_permission_list` · `access_resource_list` ·
8
- `access_policy_list` · `access_policy_get` · `access_policy_create` · `access_policy_update` ·
9
- `access_policy_delete` · `entity_protection_update` · `access_preview` · `access_session_create`.
10
- Gateway kuralları: ORG-001…003, ACC-001…010, PROT-001…004 — her biri aşağıda ilgili başlıkta.
11
-
12
- ## Kavramlar
13
-
14
- | Terim | Ne | Nerede okunur |
15
- |---|---|---|
16
- | Birim | Organizasyon ağacının düğümü; tür holding · company · branch · region · department · team · other; `function_code` birimin işlevi (it, hr, finance…) | `row_list('organization_units')` |
17
- | Pozisyon | Birimdeki koltuk; idari üst `parent_position_id`, işlevsel üst `functional_parent_position_id`; her pozisyonun bir rolü var; `is_multi_seat` (varsayılan false: aynı anda tek kalıcı kişi) | `organization_positions` |
18
- | Rol | Benzersiz unvan (Satış Müdürü); kompleks projede organizasyon şemasından gelir | `roles` |
19
- | Kullanıcı ataması | Kim hangi koltukta oturuyor; dönemli, `is_primary`, vekâlet için `assignment_type`; tek koltuklu pozisyonda çakışan ikinci kalıcı atama 409 `OrganizationPositionSeatOccupied`, vekâlet (`acting`) sınıra girmez | `user_positions` |
20
- | Kaynak | Yetkinin bağlandığı şey: entity, alan, microservice, sayfa, workflow… — katalogdaki anahtar | `access_resource_list` |
21
- | İzin | `<tip>.<eylem>`: `entity.read`, `field.read`, `microservice.execute`… | `access_permission_list` |
22
- | Grant | İzin × kaynak × etki (allow/deny) + opsiyonel kapsam, koşul, alan erişimi | politika gövdesi |
23
- | Politika | Grant'ler + atamalar paketi | `access_policy_list` |
24
- | Atama | Politikayı özneye bağlar: rol · pozisyon · birim · kullanıcı | politika gövdesi |
25
- | Özne | İsteği yapan uygulama kullanıcısı: id'si, pozisyonları, birimleri, şirketleri, rol slug'ları, işlev kodları | runtime |
26
-
27
- ## Organizasyon ağacı
28
-
29
- Organizasyonun tek bir rol yönetim şeması vardır; yönetim konsolunda düzenlenir ve yayınlanır
30
- (`organization_schema_get` taslağı, `version` ile bir yayını gösterir). Kompleks proje açılınca
31
- yayınlı son sürümün **kopyası** projenin her ortamına yazılır (ORG-002); yayın yoksa tablolar boştur.
32
-
33
- - **Abonelik:** `project_get` → `organizationSchema`. Yeni yayın abone projeye senkronla gelir.
34
- Projede yapı değişikliği (birim ekle/sil/taşı, pozisyon ya da organizasyondan gelen rol) aboneliği
35
- kapatır; `customizedAt` görünür ve sonraki sürümler gelmez. Kişi atamaları aboneliği etkilemez.
36
- - **Okuma:** `row_list` ile hazır entity'ler. Alt ağaç kapanış tablolarındadır:
37
- `organization_unit_closure` (`ancestor_id` → `descendant_id`, `depth` 0 düğümün kendisi),
38
- `organization_position_closure` (ayrıca `line_type`: administrative · functional).
39
- - **Yazma yok (ORG-001, block).** Organizasyon ve erişim tablolarına `row_create` / `row_update` /
40
- `row_delete`, table kaynaklı uç ve table step `400 SystemEntityWriteNotAllowed` alır; gateway
41
- isteği göndermez. Projeye özel birim/pozisyon ve kullanıcı ataması Studio › Organizasyon
42
- ekranındadır; dal ortamında yapı yazımı zaten 409'dur.
43
- - **Birim yöneticisi** bir kişi değil pozisyondur (`manager_position_id`). **Matris organizasyon:**
44
- destek departmanı (IT) idari olarak şirkete, işlevsel olarak holding direktörüne bağlanır — ikinci
45
- hat pozisyondaki işlevsel üsttür, birim ağacı tek ebeveynli kalır.
46
-
47
- ## İzin kataloğu
48
-
49
- Katalog yalnız kompleks projede tohumlanır; `access_permission_list` boş dönüyorsa proje basittir.
50
-
51
- | Tip | İzinler | Kapsam alır | Alan erişimi alır |
52
- |---|---|---|---|
53
- | entity | read · create · update · delete · export · import · reveal · reassign · audit | read · update · delete · export · reveal · reassign | — |
54
- | field | read · write · filter | — | yalnız `field.read` |
55
- | row | read · update · delete · export · reveal | — | — |
56
- | çalıştırma | `microservice.execute` · `page.view` · `workflow.execute` · `ui-flow.run` · `function.execute` · `microfunction.execute` | — | — |
57
- | veri | `query.execute` · `query.unrestricted` · `view.read` | — | — |
58
- | dosya | `file.read` · `file.upload` · `file.delete` · `folder.read` · `folder.upload` · `folder.delete` | — | — |
59
- | organizasyon | `organization-unit.read` · `organization-unit.manage` · `organization-unit.assign` | — | — |
60
- | sistem | `system.access.manage` · `system.access.explain` | — | — |
61
- | süreç | `process.start` · `process.manage` · `process.view-all` (anahtar `process:<tanımId>`) | yalnız `view-all` | — |
62
- | görev | `task.complete` · `task.reassign` (süreç kaynağına bağlanır) | — | — |
63
-
64
- Otomasyon katalogda **yoktur** ve ona grant yazılmaz: satır yazımı otomasyonu yazanın yetkisinden
65
- bağımsız tetikler; adımların hangi kimlikle koştuğunu otomasyonun `runAs`'ı seçer
66
- (`platform_guide('workflow')` §Kompleks projede özne ve runAs).
67
-
68
- ## Kaynak anahtarı
69
-
70
- Grant kaynağa id ile değil **anahtarla** bağlanır; anahtar yeniden kurma, deployment ve dal boyunca
71
- kararlıdır.
72
-
73
- - Biçim: `entity:<entityId>` · `field:<entityId>:<fieldId>` · diğer tiplerde `<tip>:<id>`. Anahtar
74
- verilmeyen grant iznin tipinin **tamamına** uygulanır.
75
- - Anahtar iznin tipiyle başlar: `entity.read` → `entity:…`, `field.read` → `field:…` (ACC-001).
76
- - `access_resource_list { type, search, parentId }` — `key` alanı anahtardır. `parentId` bir
77
- **katalog** id'sidir (listedeki `id`), entity id değil: bir entity'nin alanları için önce entity
78
- satırını bul, onun `id`'siyle çağır.
79
- - Katalogda türetilmiş alanlar vardır (`isDerived`), korumalı alanlar işaretlidir (`isProtected`);
80
- gizli sistem alanları (sahiplik alanları dahil) **yoktur**.
81
- - Kaynak silinince grant kalır ve `orphaned: true` görünür. Güncelleme var olan yetim anahtara
82
- toleranslıdır; **yeni** gelen bilinmeyen anahtar `400 AccessResourceUnknown`.
83
-
84
- ## Kapsam
85
-
86
- Kapsam satırın **sahiplik alanlarına** göre SQL'de süzülür (`sys_owner_user_id`, `sys_owner_unit_id`).
87
-
88
- | scope | Satır görünür, eğer |
89
- |---|---|
90
- | `own` | sahip kullanıcı öznenin kendisi |
91
- | `unit` | sahip birim öznenin birimlerinden biri |
92
- | `unit-tree` | sahip birim öznenin birimi ya da onun alt birimi |
93
- | `subordinates` | sahip kullanıcı, öznenin pozisyonlarının altındaki bir pozisyonda şu an oturuyor; hat `subordinatesLine`: `administrative` (varsayılan) · `functional` · `both` |
94
- | `function` | sahip birimin işlev kodu öznenin birimlerinin işlev kodlarından biri (IT direktörü üç şirketin IT kayıtlarını görür) |
95
- | `company` | sahip birim öznenin şirketinin altında |
96
- | `all` | tümü |
97
-
98
- - Öznenin birimi ya da pozisyonu yoksa ilgili kapsam **hiçbir satır** döndürmez.
99
- - Kapsamlı grant entity'de sahiplik alanı ister (ACC-003, warn — platform 400
100
- `AccessScopeRequiresOwnerFields`). Kompleks projede yeni entity iki alanı kendiliğinden alır; eski
101
- entity için `POST /{proje}/DataJobs/owner-fields/{datasourceId}` işi kolonları ekler.
102
- - Var olan satırların sahibi geri doldurulmaz: sahipsiz satırı yalnız `all` kapsamı görür. Yeni
103
- satırda sahip yazma yolunda dolar; gövdede gönderilen değer yok sayılır.
104
-
105
- ## Grant kuralları (ACC-001 · ACC-002 · ACC-003)
106
-
107
- Gateway politika gövdesini istek göndermeden denetler (ACC-001 block, ACC-002 block, ACC-003 warn):
108
-
109
- - **Etki zorunlu.** Allow'lar toplanır, **kapsamsız deny kazanır**. Deny kapsam (ve kapsamla
110
- birlikte koşul) taşıyabilir: kapsamlı deny eylemi kesmez, yalnız kapsamındaki satırları süzer —
111
- `rowScope` metninde `except` olarak görünür. **İstisna koşulsuz `scope: 'all'`:** koşulsuz `all`
112
- daraltıcı bir kapsam değildir, "bütün satırlar hariç" demektir; böyle bir deny **kapsamsız sayılır
113
- ve eylemi keser** (`explicit-deny`, izde `outcome: applied` — TB-132). `scope: 'all'` + koşul ise
114
- yalnız koşula uyan satırları dışlar (`all except condition(…)`). Kapsam **atamanın öznesine** göre çözülür: birim
115
- atamasında o birim, pozisyon atamasında pozisyonun birimi, rol/kullanıcı atamasında öznenin
116
- birimleri. Deny alan erişimi taşımaz (`AccessDenyFieldAccessNotSupported`).
117
- - **Kapsam** yalnız altı entity izninde ve `process.view-all`'da (tablo yukarıda); `subordinatesLine` yalnız
118
- `scope: 'subordinates'` ile.
119
- - **Alan erişimi** yalnız `field.read`'te: `hidden` · `masked` · `plain`. Birden çok allow'da en
120
- genişi kazanır (`plain` > `masked` > `hidden`); korumalı alanın varsayılanı maskelidir.
121
- **`masked` ancak korumalı alanda maskeler:** maske alanın depolama korumasından gelir. Korumasız alana
122
- `fieldAccess: 'masked'` verilirse değer DÜZ döner (ölçüldü 38/M-3) — önce `entity_protection_update`
123
- ile alanı koruyun ya da `hidden` verin. Gateway bunu `access_policy_create/update`'te uyarır (ACC-017).
124
- - **Bir alanı kapatmanın iki yolu aynı şey değildir.** Kapsamsız `deny` ile `allow + hidden`
125
- okumada farklı davranır, yazmada aynı davranır — ikinci bir rol eklendiğinde fark ortaya çıkar
126
- (ölçüldü 2026-09-20, S2-16):
127
-
128
- | Yazım | Tek başına | Kullanıcıya `plain` veren ikinci bir rol de eklendiğinde |
129
- |---|---|---|
130
- | kapsamsız `deny` | alan satırda yok | **yine yok** — kapsamsız deny her allow'u ezer |
131
- | `allow` + `fieldAccess: hidden` | alan satırda yok | **görünür olur** — `plain` `hidden`'ı yener |
132
-
133
- "Bu rol bunu asla görmesin" istiyorsanız `deny`; "bu rolün varsayılanı kapalı olsun ama başka bir
134
- rol açabilsin" istiyorsanız `hidden`. İkisi de alanı **yazılamaz** yapar.
135
- - **Koşul** kapsama AND ile eklenir; kapsamsız koşul yazılmaz — saf koşul için `scope: 'all'`.
136
- Koşul yalnız anahtarlı grant'te çalışır ve alanları **o entity'nin katalogdaki korumasız
137
- alanlarıdır** (gizli ve sistem alanı yok).
138
- - **Koşulda korumalı alan yok (ACC-002).** Ana kolonda maske durur; sorgulanabilir parça
139
- türetilmiş alanla kurulur.
140
- - Aynı izin + anahtar + etki bir politikada tek grant'tir.
141
-
142
- Koşul düğümü: `{ all: [...] }`, `{ any: [...] }` ya da `{ field, op, value }`; derinlik ≤ 5, düğüm ≤ 50.
143
-
144
- | Değer | İşleçler |
145
- |---|---|
146
- | sabit (`'onaylı'`, `100`) | `eq` · `neq` · `lt` · `lte` · `gt` · `gte` |
147
- | dizi (`['a','b']`) | `in` · `notin` |
148
- | `{ subject: 'userId' }` | `eq` · `neq` |
149
- | `{ subject: 'unitIds' \| 'positionIds' \| 'companyIds' \| 'roleSlugs' \| 'functionCodes' }` | `in` · `notin` |
150
- | `{ now: true }` | `eq` · `neq` · `lt` · `lte` · `gt` · `gte` |
151
- | yok | `isnull` · `isnotnull` |
152
-
153
- **Koşul dili alanlar arası hesap yapmaz.** Sağ taraf ya sabit ya `{ subject }` ya `{ now }`;
154
- `alanA + alanB <= 100` yazılamaz. Limit türetilmiş bir büyüklüğe bakacaksa (ör. "etkili iskonto =
155
- nominal iskonto + aksesuar + takas farkı") o büyüklüğü **entity'de hesaplanan bir alan** olarak
156
- tutup koşulu ona kurun — yoksa kullanıcı nominal alanı limitin altında bırakıp gerçekte limiti
157
- aşabilir (ölçüldü 2026-09-20, S2-13: nominal %2,5 görünen teklif gerçekte %6,98 idi ve limiti %3
158
- olan kişi onayladı).
159
-
160
- Hesaplanan alanı kurmanın iki yolu vardır ve ikisi de koşul dilinden ÖNCE gelir:
161
-
162
- | Kaynak | Nasıl | Ne zaman |
163
- |---|---|---|
164
- | Aynı satırın başka bir alanından **türetiliyor** | `fields[].derivedFromFieldId` + `derivationRule` (§Türetilmiş sorgu alanı) | Değer tek bir alanın dilimi/deseni ise |
165
- | Birkaç alandan **hesaplanıyor** (toplam, yüzde, fark) | Kalıcı bir alan açın ve satır yazımında doldurun: yazan ucun `fields[]` listesine `source: 'expression'` ile ekleyin ya da entity'ye `automation` bağlayın | Değer aritmetik ise — koşul o alana kurulur |
166
-
167
- Kural: **limiti gerçekten belirleyen sayı satırda bir kolon olarak durmalıdır.** Koşul yalnız
168
- kolon okur; ekranda hesaplanan bir toplam politika için yok hükmündedir.
169
-
170
- ```jsonc
171
- // "Birimi ve alt birimleri — yalnız onaylı olanlar"
172
- { "permission": "entity.read", "resourceKey": "entity:<musteriler id>", "effect": "allow",
173
- "scope": "unit-tree", "condition": { "field": "durum", "op": "eq", "value": "onaylı" } }
174
- ```
175
-
176
- ## Atama
177
-
178
- - `subjectType`: `role` · `position` · `unit` · `user`; `subjectId` ilgili tablonun satır id'si
179
- (`row_list('roles')`, `organization_positions`, `organization_units`, `users`).
180
- - `includeDescendants` yalnız birimde (alt birimlerdekiler de politikayı alır).
181
- - `startsAt` verilmezse şimdi; `endsAt` sonra olmalı. Aynı özne bir politikada bir kez atanır.
182
- - `overridesDeny: true` yalnız `subjectType: 'user'` ve **dolu** `endsAt` ile yazılır
183
- (`AccessOverrideRequiresUserWithEndDate`); atama bütün deny'leri aşar — kapsamlısını da
184
- kapsamsızını da. Yazmak `access.override.manage` iznini ister (var olan bir istisnanın süresini
185
- uzatmak ya da öznesini — `subjectId`/`subjectType` — değiştirmek de yazmaktır); izinsiz istek
186
- 403 `AccessOverridePermissionRequiredException` döner ve hiçbir şey yazılmaz.
187
- **İstisna taşıyan politikanın grant'lerine dokunmak da aynı izni ister** — ekleme, silme ve
188
- değiştirme, `access_policy_update` ve `access_matrix_save` yollarının ikisinde de: istisna o
189
- grant kümesi kadar yasak aşar, küme değişince istisnanın sınırı da değişir. Ölçüt istisnanın
190
- **süresinin dolmamış** olmasıdır (`endsAt` gelecekte), şu an yürürlükte olması değil: `startsAt`'i
191
- ileri tarihli, henüz başlamamış bir istisna da izin ister. Grant listesini **id'siz** göndermek
192
- kümenin tamamını değiştirmek sayılır — aynı grant'leri id'siz yeniden göndermek de izin ister,
193
- o yüzden `access_policy_get`'ten gelen `id`'leri koru. İzin istemeyenler: istisnayı kapatmak,
194
- süresini kısaltmak, atamayı silmek. `access_preview` kararı
195
- `allowed-by-override`, ezilen deny izleri `outcome: 'overridden'` olur.
196
- - **Atamasız politika hiç kimseye uygulanmaz.**
197
- - **Yeni kompleks proje yetkili bir özneyle doğar.** Sistem politikası `system-admin` iki atama
198
- taşır: Super Admin **rolü** ve projeyi açan **kullanıcı** (`subjectType: 'user'`). İkincisi
199
- 2026-09-22'de eklendi, çünkü S-15'ten sonra kurucunun Super Admin rolü kompleks projede özneye
200
- hiç girmiyor ve koltuğu olmadığı için proje yetkili bir öznesiz doğuyordu. Aynı atama basit →
201
- kompleks dönüşümünde de yazılır. Bu, "yetki koltuktan gelir" ilkesinin iz bırakan istisnasıdır:
202
- gerçek yönetici koltuğu kurulduğunda `access_policy_update` ile **silinmesi beklenir**.
203
-
204
- ## Hiyerarşi yetki vermez
205
-
206
- Ağaçta üstte olmak ya da bir birimin yönetici koltuğunda oturmak **tek başına hiçbir kaydı açmaz**.
207
- Yetki yalnız grant ve atamadan gelir:
208
-
209
- - "Müdür ekibinin kayıtlarını görür" → müdür pozisyonuna (ya da rolüne) `entity.read` +
210
- `subordinates` ya da `unit-tree`.
211
- - "Bölge müdürü okur ama güncelleyemez" → `entity.read` allow `unit-tree` + `entity.update` **deny**.
212
- - "Herkes kendi kaydını düzenler" → `entity.update` allow `own`, bir role ya da en üst birime
213
- `includeDescendants` ile.
214
-
215
- ## Politika araçları
216
-
217
- - **Veri bağlama (Connect Data) politikayı her istekte uygular:** `connect_data_describe` alan başına
218
- efektif `access` ve izinli `operators`'ı döner; gizli alan yanıtta ve kolonda yoktur, korumalı alanda
219
- yalnız `eq` · `neq` · `in` · `notin` çalışır (CD-002). `asUserId` ile başka kullanıcının gözüyle okunur.
220
-
221
- - `access_policy_create` — ad, slug (projede tekil), grant'ler, atamalar tek gövdede.
222
- - `access_policy_update` — platform ucu **tam değiştirmedir**. Gateway kaydı önce okur: verilmeyen
223
- alan taşınır ve bu `gatewayNormalized` ile söylenir. `grants` / `assignments` verilirse **tam
224
- listedir**: listede olmayan silinir; var olanı değiştirmek için `id`'sini koru.
225
- - `access_policy_delete` — grant ve atamalarıyla birlikte.
226
- - `access_matrix_get` / `access_matrix_save` — Studio'nun yetki matrisinin okuma ve yazma yüzü.
227
- Matris tek bir kaynak tipini tablolar (satır = kaynak, sütun = izin, hücre = o politikadaki grant).
228
- `access_policy_update`'ten farkı: grant listesinin tamamı değil **yalnız değişen hücreler** gider
229
- (`changes` yazar/günceller, `removedGrantIds` siler). Atamalara dokunmaz.
230
- - `access_resource_rebuild` — kaynak kataloğunu projenin güncel tanımlarından yeniden üretir; yeni
231
- entity/alan/uç katalogda yoksa ya da `orphaned: true` satırlar kaldıysa bunu düzeltir.
232
- - **Tasarım eseridir:** development'ta ya da dalda yazılır, production'a deployment taşır, merge'de
233
- izlenir. Dikey çözümün production'da değiştirdiği politika `origin: 'runtime'` olur ve deployment
234
- ona dokunmaz.
235
- - `origin`: `system` · `project` · `runtime` · `module`.
236
-
237
- ## Örnek: CZA Holding — Müşteriler
238
-
239
- `musteriler` entity'si: `ad` (display), `tckn` (maskeli+şifreli, son 4), `telefon` (maskeli),
240
- `telefon_operator` (telefondan türetilmiş), sahiplik alanları. Id'ler `access_resource_list` ve
241
- `row_list` ile okunur.
242
-
243
- ```jsonc
244
- // 1) Satış personeli — yalnız kendi kayıtları, TCKN maskeli (varsayılan)
245
- { "name": "Satış personeli", "slug": "satis-personeli",
246
- "grants": [
247
- { "permission": "entity.read", "resourceKey": "entity:<musteriler>", "effect": "allow", "scope": "own" },
248
- { "permission": "entity.update", "resourceKey": "entity:<musteriler>", "effect": "allow", "scope": "own" } ],
249
- "assignments": [ { "subjectType": "role", "subjectId": "<satis-personeli rol id>" } ] }
250
-
251
- // 2) İstanbul Müdürü — departmanlarının kayıtları, TCKN açık, tek hücre açma (reveal)
252
- { "name": "İstanbul müdürü", "slug": "istanbul-muduru",
253
- "grants": [
254
- { "permission": "entity.read", "resourceKey": "entity:<musteriler>", "effect": "allow", "scope": "unit-tree" },
255
- { "permission": "entity.update", "resourceKey": "entity:<musteriler>", "effect": "allow", "scope": "unit-tree" },
256
- { "permission": "entity.reveal", "resourceKey": "entity:<musteriler>", "effect": "allow", "scope": "unit-tree" },
257
- { "permission": "field.read", "resourceKey": "field:<musteriler>:<tckn>", "effect": "allow", "fieldAccess": "plain" } ],
258
- "assignments": [ { "subjectType": "position", "subjectId": "<İstanbul Müdürü pozisyon id>" } ] }
259
-
260
- // 3) Bölge müdürleri — alt ağacı okur, güncelleyemez
261
- { "name": "Bölge müdürü", "slug": "bolge-muduru",
262
- "grants": [
263
- { "permission": "entity.read", "resourceKey": "entity:<musteriler>", "effect": "allow", "scope": "unit-tree" },
264
- { "permission": "entity.update", "resourceKey": "entity:<musteriler>", "effect": "deny" } ],
265
- "assignments": [ { "subjectType": "role", "subjectId": "<bolge-muduru rol id>" } ] }
266
- ```
267
-
268
- CEO için `scope: 'company'`. Kapsam dışı bir kaydın id'siyle okuma/güncelleme runtime'da 404,
269
- izin yoksa 403 döner.
270
-
271
- **Tek kayıt ucu.** Kompleks projede `isSingle` uç bulunamayan **ya da** kapsam dışı id için
272
- `404 NotFoundException` döner — modtan bağımsız, okuma ve yazma aynı. Basit projede aynı uç
273
- boş gövdeyle `204 No Content` döndürür; iki proje türü arasında bu fark bilinçlidir (TB-97).
274
-
275
- ## Alan koruması
276
-
277
- Tanım yalnız platformda (entity alanında) yapılır; **kimin** maskeli ya da açık gördüğünü politika
278
- (`field.read` + `fieldAccess`) belirler.
279
-
280
- | mode | Ana kolon | Gizli kolonlar | Anahtar |
281
- |---|---|---|---|
282
- | `none` | düz değer | — | — |
283
- | `masked` | maske metni (`*******1234`) | `sys_<alan>_plain` | gerekmez |
284
- | `encrypted` | boş | `sys_<alan>_enc` · `sys_<alan>_kv` (+ `sys_<alan>_bidx`) | gerekir |
285
- | `masked-encrypted` | maske metni | `sys_<alan>_enc` · `sys_<alan>_kv` (+ `sys_<alan>_bidx`) | gerekir |
286
-
287
- - Gizli kolonlar son kullanıcıya ve kataloğa görünmez. Şifreleme AES-256-GCM, tam eşleşme için
288
- HMAC blind index (`exactSearch: true`); `normalize` (`digits` · `lower` · `trim`) eşleşmeden önce
289
- uygulanır — TCKN için `digits`.
290
- - Maske türü (`mask.type`): `last-n` (`visible` > 0) · `first-n` · `first-last` · `email` ·
291
- `phone` (varsayılan son 2 hane; `mask.char` ayırıcı ya da boşluk olamaz — `+ - ( ) . /`) · `full` ·
292
- `pattern` (`#` açık karakter) · `first-n-last-n`
293
- (`visible` baştan, `visibleEnd` sondan, ikisi de > 0 — `{ type: 'first-n-last-n', visible: 3,
294
- visibleEnd: 2 }`: `10000000146` → `100******46`). `masked` ve `masked-encrypted` modunda maske
295
- zorunludur. Değer maskenin görünen hanelerinden kısaysa tamamı maskelenir.
296
- - **Maskeli değer geri yazılırsa yok sayılır — her türde.** Satırı okuyup gövdeyi olduğu gibi geri
297
- gönderen bir istemci korumalı alanı ezmez: platform gelen değer kendi maskesine eşitse alanı ve
298
- ondan türetilen alanı değişmemiş sayar (maskeler idempotenttir; 2026-09-23'e kadar `phone` değildi
299
- ve maskeli telefon gerçek numaranın yerine yazılıyordu — VK-1). `visibleEnd` yalnız
300
- `first-n-last-n`'de saklanır; başka türe gönderilirse platform onu 0 sayar.
301
- - **Hangi alan:** yalnız düz metin; display, birincil anahtar, identity, çok dilli ve ilişki alanı
302
- korunamaz (PROT-003, block). Yalnız şifreli modda ana kolon boş kalır: NOT NULL alan bu modla
303
- korunursa yazma düşer — alanı nullable yapın.
304
- - **Yeni alan:** korumayı `entity_create` / `entity_patch` / `entity_update` gövdesinde
305
- `fields[].protection` ile alır.
306
- - **Var olan alan:** yalnız `entity_protection_update`. Güncelleme araçları var olan alanın
307
- korumasını değiştiremez (`400 ProtectionChangeNotAllowed`; `entity_patch` bunu istek göndermeden
308
- durdurur). Dolu satırlar arka plan işiyle dönüşür: yanıt `jobId`, ilerleme `import_status`. İş
309
- sürerken aynı entity'ye yazım `409 ProtectionMigrationInProgress`; bu arada işlenmiş satırlar
310
- maskeli, işlenmemişler düz görünebilir. İş hata verirse **aynı çağrıyı aynı gövdeyle** tekrarlamak
311
- kaldığı yerden sürdürür.
312
- - Ortamda anahtar yapılandırılmamışsa şifreli mod `ProtectionKeyUnavailable` ile düşer — anahtar
313
- kurulumu insan işidir.
314
- - `row_list` (tasarım ucu) ana kolonu döner: maskeli modlarda maske metni, yalnız şifreli modda boş.
315
- Düz değer MCP'den okunmaz.
316
- - **Korumalı alana YAZMAK ayrı bir grant ister.** `entity.create` / `entity.update` izni olan bir
317
- rol korumalı bir alana değer yazamaz: `403 AccessFieldWriteDenied`. Karar motoru yazma hakkını
318
- `level != hidden && (field.write ALLOW || (entity.update && alan KORUMASIZ))` diye kuruyor, yani
319
- korumalı alan için **açık `field.write` grant'i şarttır** — korumasız alanlarda `entity.update`
320
- yeter. Fikstür yükleyen ilk çağrı çoğunlukla burada düşer (S-14, ölçüldü 2026-09-19:
321
- `tckn` alanına 20/20 satır 403 aldı, `field.write` verilince 20/20 geçti).
322
- - **Denetim izi korumalı alanın düz/şifreli kopyasını tutmaz** (2026-09-20'den beri): `sys_<alan>_plain`,
323
- `_enc`, `_bidx`, `_kv` yardımcı kolonları denetim kaydına hiç girmez; defterde yalnız maskeli değer
324
- durur. Silme kaydı da öyle — önce silinen satırın tam şifreli metni defterde kalıyordu (S2-4).
325
- - **Denetim izini açmak iki alandır.** `isAuditTrailActive: true` **tek başına hiçbir şey yazmaz**;
326
- `auditTrailType` (`simple` | `detailed`) verilmezse kayıt sessizce oluşmaz ve hata da dönmez.
327
- Geçmişe telafi yok: bayrak açıkken tipi boş geçen dönemde yapılan yazmalar kalıcı olarak izsizdir
328
- (ölçüldü 2026-09-20, S2-3). Fikstür kurarken ikisini birlikte yazın ve ilk yazmadan sonra
329
- `entity_audit_trails` ile bir satır görün.
330
- - Bilinen sınır: template'ten kurulan projede korumalı kolonlar kaynak projenin anahtarıyla gelir ve
331
- hedefte yeniden şifrelenmez.
332
-
333
- ## Türetilmiş sorgu alanı
334
-
335
- Korumalı alanın sorgulanabilir parçası ayrı bir **metin** alanda saklanır; platform yazma yolunda
336
- hesaplar.
337
-
338
- - `fields[].derivedFromFieldId` + `derivationRule { type, start, length, pattern }` birlikte verilir
339
- (PROT-004, block); alan kendisinden türetilemez.
340
- - `slice` — `start` (≥ 0) ve `length` (> 0): telefon `05321234567` → `{ type: 'slice', start: 1, length: 3 }` → `532`.
341
- - `regex` — `pattern`; ilk yakalama grubu, grup yoksa eşleşmenin tamamı. Platform .NET Regex'le derler.
342
- - `domain` — e-postanın alan adı, küçük harf.
343
- - Türetilmiş alan katalogdadır; filtrede ve policy koşulunda kullanılabilir. Kaynak korumalı olsa da
344
- türetilmiş değer **düz** saklanır — ne açığa çıkardığını seçerken düşün.
345
- - Değer üç yerde hesaplanır: her satır yazımında, koruma dönüşüm işinde (`entity_protection_update`)
346
- ve türetilmiş alan eklendiğinde ya da kaynağı/kuralı değiştiğinde. Sonuncusu `entity_update` /
347
- `entity_patch` kaydından sonra açılan `derived-fill` işidir: var olan satırları kaynaktan (şifreliyse
348
- çözerek) doldurur, tabloyu kilitlemez (yazım 409 almaz), yanıt iş numarası taşımaz. Bitişi
349
- `import_history { entityId }` ile izleyin — en yeni satır `kind: 'derived-fill'`, `status: 'completed'`
350
- olunca değerler yerindedir; o ana kadar yeni kolona filtre 0 satır dönebilir. Kaynak alan o sırada
351
- dönüşümdeyse doldurma onu atlar; dönüşüm bitince (`protection-*` `completed`) platform aynı tabloya
352
- bir `derived-fill` işi daha açar ve dönüşümün önceden işlediği satırları da doldurur — değerleri o
353
- işin bitişinden sonra okuyun. Yalnız ad değişikliği ve silme iş açmaz. Bilinen eksik:
354
- deployment/merge ya da şablon kurulumuyla hedef ortama gelen türetilmiş alan orada kendiliğinden
355
- dolmaz (platform bulgusu VK-21).
356
-
357
- ## Arama ve sıralama (PROT-001 · PROT-002)
358
-
359
- - Korumalı alanda yalnız `eq` · `neq` · `in` · `notin` · `isnull` · `isnotnull`. `contains`,
360
- `startswith`, `endswith` (ve büyük/küçük harf duyarsız eşleri), `lt`/`gt` karşılaştırmaları
361
- `400 ProtectionOperatorNotAllowed` (PROT-001, block — `row_list` istek göndermeden durur).
362
- - Şifreli modda eşitlik yalnız `exactSearch: true` ile çalışır (blind index); yoksa `eq` de reddedilir.
363
- - Korumalı alana göre sıralama `400 ProtectionSortNotAllowed` (PROT-002, block).
364
- - Denetim kök entity'nin alanındadır (`tckn` ya da `musteriler.tckn`); ilişki join'i üzerinden
365
- korumalı alana filtre yazmayın — o yol denetlenmez.
366
- - Kısmi arama ihtiyacı türetilmiş alanla karşılanır (`telefon_operator eq 532`).
367
-
368
- ## Runtime: uygulama oturumu ve özne
369
-
370
- Kompleks projede kimliği platform yönetir; uygulamanın kendi kimlik middleware'i gerekmez (ACC-006).
371
-
372
- | Uç | Ne |
373
- |---|---|
374
- | `POST /{proje}/runtime/{app}/auth/sign-in` | `{ email, password }` → erişim jetonu (15 dk) + üç çerez: `ZY_App_{proje}` (HttpOnly), `ZY_AppRefresh_{proje}` (30 gün), `ZY_Csrf_{proje}` |
375
- | `POST …/auth/refresh` | yenileme çerezi (ya da gövdede `refreshToken`) → yeni jetonlar; eski yenileme jetonu tekrar gelirse bütün aile iptal, 401 |
376
- | `POST …/auth/sign-out` | çerezleri siler |
377
- | `GET …/auth/me` | kullanıcı, roller, pozisyonlar, birimler, şirketler |
378
- | `GET /{proje}/runtime/{app}/access` | izin özeti: `mode`, izinli sayfa ve uç slug'ları, entity başına eylemler ve alan düzeyleri (arayüzde gizleme için; `enforce` dışında her şey açık döner) |
379
-
380
- - Jeton önce çerezden, yoksa `Authorization: Bearer`'dan okunur. Çerezle gelen **durum değiştiren**
381
- istek `X-ZY-CSRF` başlığında `ZY_Csrf_{proje}` değerini ister (yoksa `403 CsrfTokenMismatch`);
382
- Bearer'lı istek CSRF istemez. Giriş hız sınırı 5 dk'da 10 deneme (429).
383
- - Kullanıcı projenin `users` tablosundadır; parola biçimi değişmedi (`Hasher.Hash`).
384
- - **Özne:** kullanıcı id'si, pozisyonları, birimleri, şirketleri, rol slug'ları, işlev kodları.
385
- **Kompleks projede rol YALNIZ pozisyondan gelir** (2026-09-21, S-15): `users` ekranından verilen
386
- doğrudan rol (`user_roles`) özneye girmez ve hiçbir yetki vermez — koltuğu olmayan kullanıcı
387
- rolsüzdür ve her kapsamlı grant'ten reddedilir. Doğrudan rol birleşimi yalnız **basit** projede
388
- sürer. Dönüşümden önce kimin yetki kaybedeceğini `access_model_preflight` `usersWithDirectRoles`
389
- sayar. Workflow
390
- bağlamında `user` değişkeni **önceden dolar**: `id`, `email`, `roles`, `roles_csv`, `positions`, `units`.
391
- Salt okunur `subject` nesnesi ve `Can(izin, anahtar)` yardımcısı **henüz yok** — yetkiyi ifadede yeniden
392
- kurmayın; uç, sayfa, satır ve alan kararı platformdadır.
393
- - **Anonim uç işareti yok:** kompleks + `enforce` ortamda oturumsuz her microservice ve sayfa isteği
394
- `401 AppSessionRequired` döner. İstisna, koruması `anonymous` olan system operation'dır
395
- (`access.session.create`; uç `isAnonymous: true` olmak zorunda).
396
- - **Sayfa akışı bu uçlara ham `fetch` atmaz (FLOW-028).** Giriş `access.session.create`, kullanıcı
397
- kartı `access.session.get` system microservice'iyle `microservice` adımından çağrılır: runtime
398
- `X-Environment`'ı, CSRF'i ve 401'de tek yenilemeyi kendisi yapar, giriş gövdesi jeton taşımaz.
399
- Yanlış parola `401 AppSignInFailedException` döner (oturum hatası değildir, yenileme tetiklemez).
400
-
401
- ## Zorlama modu
402
-
403
- | Mod | Ne olur |
404
- |---|---|
405
- | `off` | Karar motoru devrede değil; kimlik ve yetki bugünkü gibi middleware'de. **Korumalı alanlar AÇIK döner** (`__protected: { <alan>: 'plain' }`), reveal 403 (açacak bir şey yok) |
406
- | `shadow` | Karar motoru her istekte çalışır ama **engellemez**; yanıt `off` ile aynıdır (korumalı alanlar açık), ayrıca "reddedilirdi / süzülürdü / maskelenirdi" kaydı karar günlüğüne yazılır |
407
- | `enforce` | İzin yoksa 403, kapsam dışı kayıt 404, satırlar SQL'de süzülür, alanlar gizli ya da maskeli döner |
408
-
409
- - Mod **ortam başına** bir ayardır: `project_get` → `environments[].enforcementMode`. Yeni kompleks proje
410
- `enforce` başlar, dal ortamı üst ortamın modunu alır. Değişiklik runtime'a en geç ~30 sn'de yansır.
411
- - Eski rol listelerini (`page.roleIds`, `microservice.roles`) platform **hiçbir modda** karar için kullanmaz:
412
- liste yalnız `triggerSource.roles`'a geçer ve `Authorized(user.roles)` çağıran middleware okur. Uç ve
413
- sayfa izninin kapısı `shadow`/`enforce`'ta erişim politikasıdır; bağlı middleware'ler ise her modda koşar
414
- ve reddedebilir (ACC-006).
415
-
416
- ## Zorlama modunu kim değiştirir (ACC-007)
417
-
418
- Mod değişikliği **ölçülerek** yapılır; MCP'de artık hem ölçen hem yazan araç var:
419
-
420
- 1. `access_decision_log_summary` / `access_decision_log_users` — gölge modun biriktirdiği
421
- "reddedilirdi" kayıtlarını izin × kaynak ve kullanıcı kırılımında okur; `access_decision_log_list`
422
- tek tek kayıtları verir. Platform üç karar yazar: `would-deny` (istek reddedilirdi),
423
- `would-filter` (istek geçerdi, satırlar süzülürdü) ve `would-mask` (alan maskeli ya da gizli
424
- dönerdi — `permission: 'field.read'`, `resourceKey: 'field:<entityId>:<fieldId>'`,
425
- `reason: 'masked' | 'hidden'`). `decision` süzgeci **opsiyoneldir** — vermezseniz hepsi gelir.
426
- `filteredRowCount` yalnız **liste okuma** yolundaki `would-filter` kayıtlarında doludur (gerçek
427
- sorgunun toplamı − kapsamlı toplam); güncelleme ve silme yolunda `null` kalır. Kayıt dakikalık
428
- pencerede toplanır, bu yüzden alan **pencere toplamıdır**: istek başına düşen satır sayısı
429
- `filteredRowCount / count`'tur (ölçüldü 2026-09-19: `count: 22`, `filteredRowCount: 352` →
430
- istek başına 16). Studio'nun gölge raporu bu bölmeyi kendisi yapar ve istek başına değeri
431
- gösterir; ham toplamı okuyan başka bir istemci bölmeyi kendi yapmalıdır. `would-mask`
432
- istek başına entity başına bir kez **çözülür**, ama maskeli/gizli **her alan** için bir kayıt
433
- düşer ve yalnız sorgu satır döndürdüğünde yazılır.
434
- 2. `access_enforcement_readiness { days }` — pencerede kaç "reddedilirdi" olduğunu sayar.
435
- `isReady: false` ise `enforce` bugün çalışan bir şeyi keser. `true` olması hiçbir şeyin
436
- kesilmeyeceğini **kanıtlamaz**: yalnız o pencerede denenen yolları kapsar, az kullanılan uçları
437
- `access_preview` ile ayrıca sınayın.
438
- 3. `access_enforcement_mode_set { projectId, environmentId, enforcementMode, confirm }` — modu
439
- değiştirir. Platform iki durumda `confirm` ister: production ortamında **her** değişiklik (409
440
- `ProductionEnforcementChangeRequiresConfirmation`) ve **her ortamda `off`'a geçiş** (409
441
- `EnforcementOffRequiresConfirmation`). `off` süzmeyi kaldırmakla kalmaz, korumalı alanları da
442
- herkese açık döndürür: karar kullanıcınındır, aracın değil — hangi ortam ve neden olduğunu
443
- söyleyin.
444
-
445
- Değişiklik runtime'a en geç ~30 sn'de yansır.
446
-
447
- ## Runtime'da kapsam, alan erişimi ve reveal
448
-
449
- - **Table kaynaklı uç, table step ve dışa aktarma** satırları öznenin kapsamıyla SQL'de süzer. Kapsam dışı
450
- id ile okuma/güncelleme/silme **404** (tek kayıt ucunda da 404; basit projede 204); eylem izni yoksa
451
- **403** `ForbiddenException` (mesaj hangi izin olduğunu söylemez — `access_preview`).
452
- - **Alan erişimi:** `hidden` alan satırdan tamamen çıkar; korumalı alan varsa satırda
453
- `__protected: { <alan>: 'masked' | 'plain' }` bulunur. Maskeli değer bir metindir — forma değer olarak
454
- bağlanmaz, geri yazılmaz.
455
- - Yazma yetkisi olmayan alana değer → `403 AccessFieldWriteDenied`; filtre yetkisi olmayan alana filtre →
456
- `403 AccessFilterDenied`; sıralama yetkisi olmayan alana sıralama → `403 AccessSortDenied`. Korumalı
457
- alanda tam eşleşme araması ayrı `field.filter` iznidir ("TCKN ile bul, maskeli gör"). Gizli alan bu iki
458
- uçta da **sızmaz**: süzme ve sıralama denemesi 403 döner, boş sonuç değil.
459
- - **Yazma denetimine yalnız satıra gerçekten değer yazılan alan girer.** Bir table-source yazma
460
- ucunun `fields[]` listesinde `writeOn: always` ile duran alan, kaynağı gövde/başlık/query ise ve
461
- istekte **hiç gönderilmemişse** satıra anahtar olarak girmez, yazma kararına da sokulmaz
462
- (TB-110, 2026-09-20). Sabit değeri ya da ifadesi olan alan yazılıyor sayılır ve denetlenir; gövdede
463
- açıkça `null` gönderilen alan da yazılıyor sayılır. Eskiden gönderilmeyen alan da satıra girdiği
464
- için kapalı tek bir alan **bütün ucu** `403 AccessFieldWriteDenied` ile kapatıyordu (ölçüldü
465
- 2026-09-20, S2-15).
466
- - **`deny` ile `allow + fieldAccess: hidden` okumada farklı, yazmada aynıdır:** ikisi de alanı
467
- **yazılamaz** yapar. Okumada `deny` alanı hiç getirmez, `hidden` alanı satırdan çıkarır; yazmada
468
- ikisi de `403 AccessFieldWriteDenied` verir.
469
- - **Reveal:** `access_reveal { application, asUser, entityId, rowId, fieldSlug, reason }` →
470
- `POST /{proje}/runtime/{app}/reveal`. Kapsamlı `entity.reveal` izni ister; gerekçe zorunlu
471
- (≤ 1000 karakter) ve denetim kaydına düşer; hız sınırı ayrıdır (varsayılan 5 dk'da 20 → 429).
472
- Proje ayarı `revealReasonMinLength` (varsayılan 0, `project_get` / `project_update`) daha kısa
473
- gerekçeyi `400 RevealReasonTooShort` ile reddeder: deneme hız sınırını tüketir ve denetim
474
- defterine `REVEAL-DENIED` düşer. Gateway asgariyi projeden okur ve kısa gerekçeyi hiç göndermez
475
- (ACC-010); kasıtlı olumsuz ölçümde `expectFailure: true` ön denetimi atlatır.
476
- Basit projede ve `off` modda 403. Değer istemcide kısa süre gösterilip atılır.
477
- **Reddedilen deneme de kayda geçer** (2026-09-19'dan beri): izin yoksa, satır kapsam dışıysa ya da
478
- satır bulunamadıysa denetim defterine `REVEAL-DENIED` satırı yazılır — gerekçe `currentValue`'da,
479
- ret sebebi `previousValue`'da. Kayıt isteğin transaction'ı geri alınsa da kalır; başarılı
480
- `REVEAL` satırında `previousValue` null olduğu için iki durum ayrılır.
481
- - **Kullanıcının bütün yüzeyi:** `access_runtime_get { application, asUser }` →
482
- `GET /{proje}/runtime/{app}/access`: zorlama modu, görülebilen sayfa ve uç slug'ları, entity başına
483
- eylemler ve alan erişimleri. `access_preview` tek kararı açıklar, bu araç haritanın tamamını verir.
484
- İkisi de `asUser` ister — gateway'in servis jetonu bir uygulama kullanıcısı değildir.
485
- - İlişki join'iyle gelen alanlar süzülmez: ilişkinin görünen adı her zaman döner.
486
-
487
- ## Kısıtsız sorgu ve runAs (ACC-004 · ACC-005)
488
-
489
- - **Ham SQL** (query kaynaklı uç, query step): sorgu metni sahiplik alanlı ya da korumalı bir entity'nin
490
- slug'ını geçiriyorsa `403 AccessQueryNotScoped`. Eşleşme sözcük düzeyindedir — slug kolon ya da takma ad
491
- olarak geçse de sayılır. `isUnrestricted: true` sorgu çalışır ve **yalnız satır kapsamını**
492
- kaldırır; yazması `query.unrestricted` iznini ister (ACC-005, warn).
493
- **Koruma yardımcı sütunları sorgu metninde yasaktır** (TB-103, 2026-09-20): `sys_<alan>_plain`,
494
- `_enc`, `_bidx`, `_kv` geçen bir sorgu kayıtta **400** `QueryProtectionColumnNotAllowed`, koşuda
495
- **403** `AccessQueryProtectionColumnDenied` verir — `isUnrestricted` olsun ya da olmasın.
496
- Her `isUnrestricted` koşusu karar günlüğüne **`unrestricted`** kaydı yazar (enforce modda da);
497
- `access_decision_log_list { decision: "unrestricted" }` ile okunur. **Sınır:** kayıt, sorgu korumalı
498
- ya da sahiplik alanlı bir entity'ye dokunduğunda düşer — kapsız bir tabloyu okuyan `isUnrestricted`
499
- sorgusu zaten bir şey atlamadığı için iz bırakmaz. **İkinci sınır:** yasak sütun taraması sorgu
500
- metnindeki ADLARA bakar; `select *` ile gelen koruma sütunlarını göremez — rapor ucunda sütunları
501
- açıkça saydırın.
502
- Eskiden ölçülen açık (2026-09-20, S2-8): kapsamı üç satır olan bir kullanıcı aynı uçla on bir
503
- satırın hepsini aldı ve `sys_telefon_plain` kolonunu takma adla seçerek `masked` bir alanın **düz
504
- değerini** okudu.
505
- - **Adım kimliği** (table, query ve process step, `rule.runAs`): `inherit` (varsayılan, çağıranın kapsamı) ·
506
- `system` (kapsam ve alan erişimi atlanır, denetim kaydı yok — ACC-004 warn; gerekçeyi adımın
507
- `caution`'ına yazın) · `triggering-user` (bastırma geri alınır ve adım **tetikleyen kullanıcının**
508
- kapsamıyla koşar; tetikleyen bilinmiyorsa hiçbir şey değişmez). `workflow_step_schema` bu alanı
509
- göstermez; gövdeye yazılır.
510
- **`runAs: system` bir yetki yükseltmesidir ve `workflow.runas.manage` platform iznini ister**
511
- (TB-105, 2026-09-20): akışın kendisine ya da herhangi bir adımına `system` yazan `workflow_create` /
512
- `workflow_update` isteği izin yoksa **403** `WorkflowRunAsPermissionRequired` alır; `inherit`'e
513
- dönmek serbesttir. Uçların kendisi 2026-09-21'den beri **`workflow.write`** platform izniyle de
514
- korunur (RA-11): izni olmayan kullanıcı gövdesiz **403** alır (middleware), izni olup
515
- `workflow.runas.manage` olmayan kullanıcı uca ulaşır ve gövdeli **403
516
- `WorkflowRunAsPermissionRequired`** görür. Okuma uçları (`Get` tekil ve liste ·
517
- `GenerateStepSchema` · `GetMiddlewareVariables` · `GetDependencies`) 2026-09-21'den beri
518
- **`workflow.read`** ister (RA-15): önceden öznitelik taşımadıkları için super-admin'e kilitliydiler
519
- ve `workflow.write` verilen kullanıcı akışı yazabiliyor ama okuyamıyordu. `Preview` ucu
520
- **`workflow.write`** ister (RA-16, 2026-09-21): uç kayıtlı bir akışı değil **istemcinin gövdede
521
- yazdığı adımları** koşturur, yani veri önizleme değil akış yazma sınıfındadır. Query, table ve
522
- process adımlarının her sistem koşusu karar günlüğüne **`elevated`** kaydı yazar
523
- — akış id/slug'ı, akışı yazan kullanıcı ve adına koşulan kullanıcı orada durur
524
- (`access_decision_log_list { decision: "elevated" }`); `process` adımı 2026-09-21'e kadar bu kaydı
525
- yazmıyordu (RA-17, düzeltildi). Ölçülen açık (2026-09-20, S2-10): onay limiti
526
- 200 bin olan kullanıcı `runAs: system` bir adımla 6 M TL'lik kaydı onayladı; aynı iş `inherit` iken
527
- `ForbiddenException` alıyordu. Kullanıcı isteğiyle tetiklenen akışlarda `inherit` bırakın.
528
- - **Tasarım zamanı: Studio erişimi veri erişimidir ve `project.data.preview` ile daraltılır.**
529
- `microservice_preview` ile Studio satır ızgarası (`row_list`) çağıranın kapsamıyla değil, kapsamsız
530
- koşar: bütün satırlar ve kapsamsız `deny` ile kapatılmış alanlar **düz** döner; yalnız alan koruması
531
- (masked/encrypted) depolama katmanında olduğu için durur (ölçüldü 2026-09-20, S2-20). Bu yüzden iki
532
- uç da **`project.data.preview` platform iznini** ister (TB-114, 2026-09-20); izni olmayan platform
533
- kullanıcısı **403** alır ve Studio ızgarasında "Veri önizlemesi için izin gerekir" boş durumunu
534
- görür. İzin verilen tasarımcı hâlâ kapsamsız görür — bir rolün ne göreceğini önizlemeyle değil
535
- `access_preview` / `access_runtime_get` / `asUser` ile ölçün.
536
- **Akış önizlemesinde zorlama hiç açılmaz** (ölçüldü 2026-09-21, RA-19): `Activate(...)` yalnız
537
- microservice / sayfa / connect-data / debug yollarında çağrılır, `POST workflows/{app}/preview`
538
- yolunda çağrılmaz — kompleks projede bile önizleme `Mode = Off` ile koşar, adım kapsamı
539
- uygulanmaz ve `runAs: system` adımı `elevated` yazmaz. Önizlemenin kapısı bu yüzden zorlama değil
540
- **izindir** (`workflow.write`).
541
- - **Workflow kimliği** (`workflow.runAs`, otomasyonlarda): `inherit` (varsayılan) **tetikleyen çağrının
542
- kapsamıdır** — otomasyon, satırı değiştiren kullanıcı neyi yapabiliyorsa onu yapar; `system` bir
543
- yükseltmedir, `workflow.runas.manage` ister ve `elevated` kaydı yazar; `triggering-user` iç içe
544
- bastırmayı da geri alır (üst düzeyde `inherit` ile aynı sonucu verir). Aynı üç değer, aynı anlam —
545
- adım düzeyi workflow düzeyini ezer.
546
- **Ölçülen açık kapandı (RA-10, 2026-09-21):** `inherit` 2026-09-21 öncesinde zorlamayı **tamamen
547
- bastırıyordu**, yani `runAs` yazmadan otomasyon açan herkes `workflow.runas.manage` kapısını atlayarak
548
- yükseliyor ve geriye `elevated` kaydı bile kalmıyordu. Ölçüm: kapsamı `own` olan bir müşteri
549
- temsilcisi kendi satırını güncelledi, otomasyon **başkasının** satırını değiştirdi ve karar günlüğü
550
- boş kaldı. Artık kapsam dışına yazan `inherit` otomasyon reddedilir; **satır içi** otomasyonda bu
551
- tetikleyen satır yazımını da düşürür (`ForbiddenException` akış içinden bugün 500 döner — TB-124),
552
- kuyruk otomasyonunda `automation_histories` `failed` olur ve satır yazımı etkilenmez. Akış içinden
553
- gelen ret 2026-09-21'den beri **doğru HTTP kodunu** taşır (TB-124): `ForbiddenException` → **403**,
554
- kapsam dışı satırda `NotFoundException` → **404**; gövde yine `WorkflowStepErrorResponse`'tur
555
- (`code` · `message` · `step`). **Sınır:** akışın kendi global hata dalı varsa kodu o dalın
556
- `response` adımı belirler — gateway'in her akışa eklediği `gatewayOnError` adımı 500 döndürür.
557
- **Sınır:** aktörsüz kuyruk olayında (zamanlanmış mikroservis, deployment içe aktarımı) özne hiç
558
- kurulmaz, bağlam pasiftir ve otomasyon eskisi gibi zorlamasız koşar. `shadow` ortamda reddetmez,
559
- `would-deny` yazar.
560
- - **Otomasyon aktörü:** satır olayındaki kullanıcı hem satır içi hem kuyruk otomasyonunda taşınır;
561
- kuyruk tüketicisi özneyi kurup ortamın modunu etkinleştirir. Aktör otomasyon geçmişinde
562
- `queued_events.actor_user_id` kolonunda durur.
563
- - **Workflow JS'inde özne:** her adımın JS bağlamında dondurulmuş `subject` nesnesi vardır
564
- (`userId` · `roleSlugs` · `positionIds` · `unitIds` · `companyUnitIds` · `functionCodes`) ve
565
- `Can(permission, resourceKey)` karar motoruna yan etkisiz sorar. Zorlama kapalıyken `Can` her zaman
566
- `true` döner — "kapalıyken de reddet" davranışı istiyorsanız kendiniz kontrol edin.
567
-
568
- ## Açıklama ve sınama
569
-
570
- - **Neden?** → `access_preview { userId, permission, resourceKey, rowId? }`: karar, gerekçe, satır
571
- kapsamı, alan erişimi ve iz (atama → politika → grant). Yan etkisizdir ve zorlama modundan bağımsız
572
- hesaplanır — gölge moddaki "reddedilirdi" kaydını da böyle okuyun. `rowId` verildiğinde `isAllowed`
573
- **tablonun** cevabı olarak kalır; satırın cevabı `rowInScope` (özet: `summary.row`
574
- `in-scope` / `out-of-scope`). Kapsam dışı satır çalışma zamanında 404 döner.
575
- - **Kullanıcı gözüyle sına (parolasız)** → runtime araçlarına `asUserId: '<users satır id'si>'`:
576
- `connect_data_execute`, `microservice_execute`, `microservice_debug`, `page_runtime_get`, `page_screenshot`
577
- (ayrıca Studio uçları `connect_data_describe/preview/get/bind`). İstek gateway kimliği + `impersonated-user-id`
578
- başlığıyla gider; platform özneyi o kullanıcı kurar (`AppSessionMiddleware`) — politika, kapsam, maske onun.
579
- Yalnız development/dal (production'da başlık düşer, gateway isteği göndermez); kullanıcı projede olmalı.
580
- Parolasız açılan test kullanıcısı için yeterlidir; sayfayı açabilmesi için politikasında `page.view` olmalı.
581
- **Parolasız kullanıcı `user_create` ile AÇILMAZ** — şeması `password`'ü zorunlu tutar ve AI hiçbir alana parola
582
- yazmaz. Yol: `row_create { entityId: 'users', rows: [{ email, first_name, last_name }] }` → dönen satır id'si
583
- `asUserId`'dir; pozisyon/politika ataması `user_position_replace` ve `access_policy_create` ile aynı kalır
584
- (ölçüldü 2026-09-21, 39/05 D-19: iki test kullanıcısı böyle açıldı, `asUserId` ile 200/403 beklendiği gibi).
585
- - **Oturum açılmış üye olarak sına** → `access_session_create { application, email, password }`; yanıttaki
586
- `tokenRefs` referansını `microservice_execute` / `microservice_debug` / `page_runtime_get` `asUser`
587
- alanına verin. Jeton Bearer olarak gider (CSRF gerekmez), ne yanıta ne günlüğe düz yazılır.
588
- - `microservice_debug` ve `page_runtime_get` kompleks projede yanıta `accessDecision` ekler: `decision`
589
- (allow · deny · not-enforced · unknown), `mode`, hata kodu, izin, `reason`, `next`.
590
- - `microservice_preview` bir Studio ucudur: uygulama jetonuyla sınanamaz — uygulama kullanıcısı olarak
591
- `microservice_execute` / `microservice_debug`.
592
-
593
- ## Basit ↔ kompleks dönüşüm
594
-
595
- 1. `access_model_preflight { projectId }` — ortam ortam ölçer: entity ve satır sayısı, sahiplik alanı
596
- olmayan entity'ler, dış datasource'lu entity'ler, sorgu ve kısıtsız sorgu sayısı, rol sayısı,
597
- **rezerve tablo adı çakışmaları**, `sys_created_user_id`'den doldurulabilir satır sayısı,
598
- middleware'e bağlı uç ve sayfa listesi, pozisyonsuz kullanıcı sayısı, kapsamlı tabloya dokunan ham
599
- SQL sorguları. `slugCollisions` boş değilse dönüşüm yapılmamalıdır: kompleks model o adlarla kendi
600
- sistem tablolarını kurar.
601
- 2. `access_model_convert { projectId, target, enforcementMode }` — `enforce` kabul edilmez; yeni
602
- kompleks proje önce `shadow`'da izlenir. Yanıt "başladı" demektir.
603
- 3. `access_model_conversion_tasks { projectId }` — ortam başına görev: `pending` · `processing` ·
604
- `completed` · `error`, deneme sayısı ve hata metniyle. **Bittiğinin tek kanıtı budur**; `error`
605
- kalan ortam dönüştürülmemiştir.
606
-
607
- ## Organizasyon yapısını yazmak
608
-
609
- Kompleks projede elle (row API'siyle) yazılması kilitli olan tablolar **üçtür**: `organization_units` ·
610
- `organization_positions` · `user_positions` (ORG-001). **`roles` bu listede değildir** — sıradan bir
611
- entity'dir ve kompleks projede de `row_create` ile yazılabilir; proje rolleri fikstür kurarken böyle
612
- açılır (ölçüldü 2026-09-20, S2-32).
613
-
614
- Projenin kendi birim/pozisyon ağacı `organization_tree_get` ile okunur; `organization_unit_create` ·
615
- `organization_unit_update` · `organization_position_create` · `organization_position_update` ile
616
- yazılır. Kullanıcının rolü, birimi ve şirketi pozisyonundan gelir: `user_position_list` /
617
- `user_position_replace` (liste **tam değiştirmedir**, boş dizi kullanıcıyı pozisyonsuz bırakır).
618
-
619
- Proje organizasyon şemasına aboneyse buradan yazmak projeyi "özelleştirilmiş" işaretler ve sonraki
620
- yayın otomatik inmez; hizalamak için `organization_schema_resubscribe_preview` →
621
- `organization_schema_resubscribe`. Yayın tarafı: `organization_schema_versions` ·
622
- `organization_schema_diff` · `organization_schema_subscribers` · `organization_schema_publish`
623
- (geri alınamaz ve abone projelerin **production** şemasına da yazar) ·
624
- `project_organization_schema_tasks` (yayının o projeye indiğinin kanıtı).
625
-
626
- ### Organizasyon şeması: taslağı AI yazar, yayını insan yapar
627
-
628
- Merkezi şemanın **taslağına** `organization_schema_unit_create` · `organization_schema_unit_update` ·
629
- `organization_schema_position_create` · `organization_schema_position_update` ile yazılır. Bu dört
630
- araç yalnız taslağı değiştirir: hiçbir projeye inmez, production'a dokunmaz ve verilmeyen alanı
631
- korur (gateway taslağı okur, tek düğümü değiştirir, listeyi geri yazar — platformda düğüm başına
632
- uç yoktur, yazma tek parçadır; arada başkası yazdıysa onun değişikliği ezilir).
633
-
634
- **Yayın AI'ın işi değildir.** `organization_schema_publish` geri alınamaz, bütün abone projelere
635
- senkronize olur ve abone projenin **production** şemasına da yazar; repo anayasası AI'ın hiçbir
636
- ortamda production'a yazmasını yasaklar. Taslağı hazırlayın, farkı `organization_schema_diff`
637
- (`to` verilmezse **taslak ↔ verilen yayın** karşılaştırılır) ile gösterin ve yayını insana bırakın.
638
-
639
- Ağacın bugünkü kuralları — hepsi 2026-09-20'de ölçüldü (S2-17 · S2-18 · S2-30):
640
-
641
- - **Tek kök zorunlu.** İkinci bir kök açmak ya da bir birimi köke taşımak `400` döner. Proje kendi
642
- kökünü açamaz; yayımlanmış şemanın köküne eklenir.
643
- - **`unitType` yalnız etikettir.** `holding` · `company` · `branch` · `region` · `department` ·
644
- `team` · `other` arasında izinli-ebeveyn kuralı **yok**: şube doğrudan şirketin altına alınabilir
645
- (200). Denetlenen tek şey döngü ve kod tekilliği. Anlamlı hiyerarşiyi siz koruyun. Bu bilinçli bir
646
- karardır (TB-111): sabit bir tip matrisi gerçek organizasyonlara uymuyor, şema düzeyinde
647
- yapılandırılabilir kural faz 2 adayıdır. Tipin tek işlevsel etkisi `company`'dir — kapsam
648
- hesabında şirket sınırı odur.
649
- - **Şemadan gelen düğüm (`origin: "organization"`) kilitli değildir.** Proje onu taşıyabilir,
650
- yeniden adlandırabilir, **arşivleyebilir** ve içine kendi rolüyle koltuk açabilir. `origin`
651
- denetimi hiçbir yazma yolunda yok; tek koruma dal ortamıdır.
652
- - **Arşivleme geri alınabilir** (2026-09-21, TB-112). `organization_unit_activate` ve
653
- `organization_position_activate` pasifleştirilmiş düğümü geri açar; id, kod ve alt ağaç korunur.
654
- Kural kökten yaprağa işler: üst düğüm pasifken alt düğüm açılmaz — `400`
655
- `OrganizationStructureParentInactive`. Pozisyonda üç ayrı sebep sayılır (birimi pasif, idari üst
656
- pozisyonu pasif, işlevsel üst pozisyonu pasif). Koltuk **boş** açılır: eski atama geri gelmez,
657
- `user_position_replace` ile yeniden oturtulur. Güncelleme araçları hâlâ `isActive` almaz.
658
- - **Sapma raporu `organization_schema_drift`'tir** (2026-09-21, TB-112). `organization_schema_diff`
659
- iki **yayımlanmış sürümü** karşılaştırır ve projenin kopyasını hiç görmez; `organization_schema_drift`
660
- tam tersine projeye bakar ve ortam başına iki ekseni **ayırır**: `driftFromApplied` = projenin
661
- uyguladığı sürüme göre yerel kopyadaki fark (`updated` proje düğümü yeniden adlandırdı/taşıdı,
662
- `removed` proje onu **arşivledi**), `newerInSchema` = aynı sürümden son yayına kadar şemada olanlar.
663
- `customNodes` projenin kendi (`origin: "project"`) düğümleridir ve sapma sayılmaz. Salt okunur;
664
- basit projede `400`.
665
- - **Abonelik hep-ya-hiçtir**: abone proje şemadaki **bütün** birimleri görür (başka iş kollarınınkini
666
- de); alt ağaca abone olma yolu yok. Projenin kendi (`origin: "project"`) birimleri ise yalnız kendi
667
- veritabanında kalır, projeler arasında karışmaz.
668
- - **Pasifleştirme araçları:** `organization_unit_deactivate` · `organization_position_deactivate`
669
- (2026-09-20'de eklendi; uygulama oturumu istemez). Kapatma sırası yapraktan köke: kullanıcıyı
670
- koltuktan al (`user_position_replace`) → pozisyonu pasifleştir → alt birimler → birimi
671
- pasifleştir. Ara adımda `400 OrganizationStructureNodeInUse` alırsınız ve gateway hangi sıranın
672
- izleneceğini mesaja ekler. Pasifleştirilen birimin sahiplendiği satırlar kimsenin kapsamına
673
- girmez — `row_reassign` ile devredin (§Sahiplik ve devir).
674
- - **Dizin (LDAP/AD/SCIM) entegrasyonu yoktur.** Kullanıcı yalnız elle (`user_create` /
675
- yönetim konsolu) açılır; toplu içe aktarma ya da dizinden eşitleme operasyonu da yok.
676
-
677
- ## Toplu işlem
678
-
679
- Kapsam ve koşul her satırda ayrı ayrı uygulanır; dolayısıyla "hepsini birden onayla" bir POLİTİKA
680
- sorusu değil bir UÇ sorusudur ve table-source `update` bunu **filtreyle yapamaz** — tam olarak bir
681
- tekil alan filtresi ister (`InvalidFilterCountException` · `FilterFieldMustUniqueException`,
682
- ölçüldü 2026-09-20, S2-22). Doğru kurulum: anahtarlar elindeyse `bulk-update` ucu (tek çağrı, tek
683
- transaction), satır başına sonuç ve satır başına yetki kararı gerekiyorsa **iş akışı + döngü**
684
- (liste → her satır için `microservice` adımı → özet). Akış içinden yazmak yetki YÜKSELTMEZ: her
685
- satır çağıranın kimliğiyle denetlenir ve reddedilen satır ötekileri geri almaz. Reçete:
686
- `platform_guide('microservice')` §"Toplu güncelleme FİLTREYLE kurulmaz".
687
-
688
- ## Sahiplik ve devir (ORG-006)
689
-
690
- Sahiplik alanları (`sys_owner_user_id` · `sys_owner_unit_id`) satır kapsamının dayanağıdır ve
691
- **satır yazma yolundan yazılmaz**: `row_create` bunları çağırandan ve pozisyonundan kendisi
692
- doldurur, `row_update` / toplu güncelleme gövdesinde görürse istek `400
693
- OwnershipFieldNotWritable` alır (2026-09-20 öncesinde sessizce düşüyordu, S2-7).
694
-
695
- Sahip değiştirmenin tek yolu **`row_reassign`**'dır:
696
-
697
- - Hedef satırlar `rowIds` ya da `filter` ile seçilir; hedef sahip `ownerUnitId` ve/veya
698
- `ownerUserId`'dir. Yalnız `ownerUserId` verilirse birim o kişinin aktif birincil pozisyonundan
699
- türetilir (pozisyonu yoksa satırın birimi olduğu gibi kalır).
700
- - Her satıra `REASSIGN` denetim kaydı düşer (eski sahip → yeni sahip); `entity_audit_trails` ile
701
- okunur.
702
- - Hedef birim/kullanıcı var olmalı (birim ayrıca aktif olmalı), yoksa 404; tek çağrıda en çok 1000
703
- satır devredilir.
704
-
705
- **Kimlik iki yol ayırır.** `row_reassign` öteki `row_*` araçları gibi bir **Studio** ucudur ve
706
- gateway'in platform kimliğiyle koşar — uygulama jetonu (`asUser`) orada geçmez, 401 alır. İzin
707
- `entity.reassign` ve **kapsamlıdır**, ama o kapsam ancak istekte bir uygulama öznesi varken
708
- hesaplanır. Uygulama kullanıcısının kendi kapsamıyla devretmesi için (ve kapsamı gerçekten
709
- ölçmek için) sistem operasyonunu kullanın: `systemOperation: 'entity.row.reassign'` olan bir uç
710
- açıp `microservice_execute { asUser }` ile çağırın. Kapsam kuralı orada işler ve kapsamı `unit` ya
711
- da `unit-tree` olan çağıran için **sahipsiz satır** (`sys_owner_unit_id` NULL) de kapsam içi
712
- sayılır — şubesi kapanan kayda başka türlü kimse ulaşamazdı.
713
-
714
- **Sistem operasyonunun izni** katalogdaki `permission`'dır (`microservice_system_operation_list`);
715
- çalıştırıcı onu işlemden önce karar motoruna sorar. `permission` **boş** olan işlemde izni işlemin
716
- çağırdığı servis verir: süreç çalışma zamanı (`process.*`) görevi, bildirimi, vekaleti kişiye ve kayda
717
- göre çözer ve REST `bpm/*` ucuyla aynı görünürlüğü verir; yalnız `process.override.*` `process.manage`
718
- (kaynak `process:<id>`) ister. Modül kurulumu "Modül kullanıcısı" politikasına yalnız işaretli
719
- işlemlerin iznini yazar — `process.manage`'ı yazmaz, açık bir politikayla verin.
720
-
721
- **Kullanıcı silinince** satırları sarkık kalmaz: `user.delete` sahiplik taşıyan her entity'de o
722
- kullanıcının satırlarının `sys_owner_user_id` alanını NULL'a çeker, `sys_owner_unit_id`'yi
723
- **korur** (birimin sahipsiz kaydı olurlar, birim yöneticisi görmeye devam eder) ve entity başına bir
724
- `OWNER-RELEASED` denetim kaydı yazar (kaç satır, hangi kullanıcı). Silme çağrısına
725
- `transferToUserId` verilirse satırlar bunun yerine devralan kişiye ve birimine geçer ve kayıt
726
- `REASSIGN` olur; devralan var olmalı (404) ve silinen kişinin kendisi olamaz (400). Devralanın
727
- birimi, o kişinin **birincil aktif pozisyonunun** biriminden okunur — aktif pozisyonu yoksa
728
- `sys_owner_unit_id` hiç değişmez. Ajan yolu `user_delete { transferToUserId }`; aynı seçenek
729
- REST yolunda da vardır: `DELETE /{proje}/users/{id}?transferToUserId=…`. Sahipliğin çözülmesi
730
- kompleks projeye özgüdür: basit modelde silme çalışır ama bu adım hiç işlemez.
731
-
732
- ## Kompleks projede kimlik middleware'i (ACC-006)
733
-
734
- Set-user'lı kimlik middleware'i (`platform_guide('middleware')` Tarif 1) kompleks projede gereksizdir, bağlı
735
- kalırsa da etkisiz değildir: platform oturumu kurar ve uç yolunda `user`'ı doldurur, ama bağlı middleware'ler
736
- zorlama modundan bağımsız **her modda koşar** ve reddedebilir; set-user adımı ya da yanıttaki `user`
737
- platformun `user`'ını **ezer**. Karar motoru özneyi oturumdan okur; ezilen `user` ise ifadelerde ikinci bir
738
- doğruluk kaynağı olur. Kimlik middleware'ini uçların ve sayfaların `middlewareIds`'inden çıkarın. Gateway
739
- `set-user` taşıyan middleware yazımında uyarır (warn). Ayar setter'ı gibi kimlik dışı middleware'ler serbesttir.
740
-
741
- ## Eski rol listeleri (ACC-008)
742
-
743
- `page.roleIds` ve `microservice.roles` kompleks projede **karar girdisi değildir**: `enforce`'ta tek kapı
744
- karar motorudur ve platform bu listeyi hiçbir modda yetki için okumaz — liste yalnız `triggerSource.roles`'a
745
- geçer, yani ona anlam veren tek şey `Authorized(user.roles)` çağıran bir middleware'dir (K-29). Yetkiyi
746
- `access_policy_create` + `access_matrix_save` ile verin ve listeyi boş bırakın; gateway dolu listede uyarır
747
- (warn, çünkü `off` ve `shadow` modunda eski davranış sürer) ve Studio kompleks projede rol seçicisini zaten
748
- çizmez.
749
-
750
- **Kullanıcıya DOĞRUDAN atanan rol de yetki vermez** (2026-09-21, S-15). Özne kurulurken roller
751
- kompleks projede **yalnız pozisyonlardan** gelir (`AppSubjectResolver.ResolveAsync`): `user_roles`
752
- satırı — `PUT /{proje}/users/{id}` ile verilen eski yol — özneye hiç girmez, `auth/me`'de rol olarak
753
- sayılmaz ve o rolün politikaları uygulanmaz. "Kullanıcının rolü pozisyonundan gelir" cümlesi artık
754
- bu yolu da kapsıyor: koltuğu gişe olan bir kullanıcıya temsilci rolü atanırsa **yalnız gişe** sayılır,
755
- hiç koltuğu olmayan kullanıcı ise rolsüzdür ve her kapsamlı grant'ten reddedilir. Birleşim yalnız
756
- **basit** projede sürer. 2026-09-19'da ölçülen eski davranış (ikisi de sayılıyordu) artık geçerli
757
- değildir. Basit → kompleks dönüşümünde kimin yetki kaybedeceğini `access_model_preflight`
758
- `usersWithDirectRoles` sayar; o kullanıcılar dönüşümden önce `user_position_replace` ile koltuğa
759
- oturtulur.
760
-
761
- Bunun tek istisnası **projeyi açan kullanıcıdır**: koltuğu olmasa da yetkilidir, çünkü `system-admin`
762
- politikası ona ayrıca `subjectType: 'user'` ile atanır (§Atama). Kurucu dışında koltuksuz kimse
763
- yetkili değildir — "neden göremiyor?" sorusunda ilk bakılacak yer hâlâ `user_position_list`'tir.
764
-
765
- ## Hatalar
766
-
767
- | Anahtar | Durum | Ne yapmalı |
768
- |---|---|---|
769
- | `SystemEntityWriteNotAllowed` | 400 | Organizasyon/erişim tablosuna genel satır yazımı (ORG-001) — Studio ya da `access_policy_*` |
770
- | `AccessPermissionUnknown` | 400 | İzin slug'ı katalogda yok — `access_permission_list` |
771
- | `AccessResourceTypeMismatch` | 400 | Anahtar iznin tipiyle başlamıyor |
772
- | `AccessResourceUnknown` | 400 | Yeni gelen anahtar katalogda yok — `access_resource_list` |
773
- | `AccessScopeNotSupported` · `AccessFieldAccessNotSupported` | 400 | Kapsam/alan erişimi o izinde yok (ACC-001) |
774
- | `AccessDenyFieldAccessNotSupported` · `AccessConditionRequiresScope` · `AccessSubordinatesLineRequiresSubordinatesScope` | 400 | Grant biçimi (ACC-001) — deny kapsam taşıyabilir, alan erişimi taşıyamaz |
775
- | `AccessOverrideRequiresUserWithEndDate` | 400 | `overridesDeny` yalnız `user` öznesi + dolu `endsAt` (ACC-009) |
776
- | `AccessOverridePermissionRequiredException` | 403 | İstisnayı açmak/genişletmek ya da istisna taşıyan politikanın grant'lerine dokunmak `access.override.manage` ister (ACC-009) |
777
- | `AccessConditionInvalidException` | 400 | Koşul alanı/işleci/değeri (ACC-001 · ACC-002) |
778
- | `AccessScopeRequiresOwnerFields` | 400 | Entity'de sahiplik alanı yok (ACC-003) |
779
- | `AccessAssignmentDescendantsRequiresUnit` · `AccessAssignmentPeriodInvalid` · `AccessSubjectMissing` | 400 / 422 | Atama biçimi |
780
- | `AccessPolicySlugDuplicate` | 409 | Slug projede tekil |
781
- | `OrganizationPositionSeatOccupied` | 409 | Tek koltuklu pozisyonda çakışan ikinci kalıcı atama (ORG-003) — vekâlet için `assignmentType: 'acting'`, kalıcı ikinci kişi için pozisyonu `isMultiSeat` yapın |
782
- | `RevealReasonTooShort` | 400 | Gerekçe projenin asgarisinin altında (`revealReasonMinLength`, ACC-010); deneme `REVEAL-DENIED` olarak kaydedilir |
783
- | `EnforcementOffRequiresConfirmation` | 409 | `off`'a geçiş korumalı alanları herkese açar — bilinçliyse `confirm: true` |
784
- | `ProtectionFieldTypeNotSupported` · `ProtectionDisplayFieldNotAllowed` | 400 / 422 | Korunamayan alan (PROT-003) |
785
- | `ProtectionDerivedSourceInvalid` · `ProtectionDerivedRuleInvalid` | 400 / 422 | Türetme kuralı (PROT-004) |
786
- | `ProtectionOperatorNotAllowed` · `ProtectionSortNotAllowed` | 400 | Korumalı alanda arama/sıralama (PROT-001 · PROT-002) |
787
- | `ProtectionChangeNotAllowed` | 400 | Var olan alanın koruması — `entity_protection_update` |
788
- | `ProtectionMigrationInProgress` | 409 | Koruma işi sürüyor — `import_status`, gerekirse aynı çağrıyı tekrarla |
789
- | `ProtectionKeyUnavailable` | — | Ortamda anahtar yok (insan işi) |
790
- | `AppSessionRequiredException` | 401 | Oturum yok (kompleks + `enforce`) — parolasız `asUserId` (development) ya da `access_session_create` + `asUser` |
791
- | `AppSessionInvalidException` | 401 | Jeton geçersiz ya da süresi dolmuş — yeniden giriş |
792
- | `AppSessionRateLimitedException` | 429 | Giriş ya da reveal hız sınırı — bekleyin |
793
- | `CsrfTokenMismatchException` | 403 | Çerezli durum değiştiren istekte `X-ZY-CSRF` yok — Bearer (`asUser`) |
794
- | `ForbiddenException` | 403 | İzin reddi (mesaj izni adıyla söylemez) — `access_preview` |
795
- | `AccessQueryNotScopedException` | 403 | Ham SQL kapsamlı/korumalı tabloya dokunuyor — table kaynağı ya da `isUnrestricted` (ACC-005) |
796
- | `AccessFieldWriteDeniedException` · `AccessFilterDeniedException` · `AccessRevealDeniedException` | 403 | Alan erişimi — `access_preview` `fieldAccess` |